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': '', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(90.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'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(107.260322571, 0.0, 0.0), + '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, -1.79999995232, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'pillars', + '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': 'centerPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10006: {'type': 'nodepath', + 'name': 'frontPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(-89.9665527344, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10007: {'type': 'nodepath', + 'name': 'backPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(89.9700012207, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10008: {'type': 'nodepath', + 'name': 'outerPillars', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': 'byVaultDoor', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(127.898963928, 0.0, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10024: {'type': 'nodepath', + 'name': 'shelves', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10026: {'type': 'nodepath', + 'name': 'nearVault', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(101.76008606, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10027: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0.0, 19.3625793457, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10028: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0.0, -19.3600006104, 0.0), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'rightNear', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(-15.3249912262, -55.9873504639, 0.0), + 'hpr': Vec3(152.447189331, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10035: {'type': 'nodepath', + 'name': 'leftNear', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(-17.6631221771, 55.3191757202, 0.0), + 'hpr': Vec3(26.3582077026, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10038: {'type': 'nodepath', + 'name': 'nearEntrance', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-107.089996338, -1.71000003815, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CashbotMintPipeRoom_Battle01_Cogs.py b/toontown/coghq/CashbotMintPipeRoom_Battle01_Cogs.py new file mode 100644 index 0000000..61883ec --- /dev/null +++ b/toontown/coghq/CashbotMintPipeRoom_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/CashbotMintStomperAlley_Action00.py b/toontown/coghq/CashbotMintStomperAlley_Action00.py new file mode 100644 index 0000000..babeee7 --- /dev/null +++ b/toontown/coghq/CashbotMintStomperAlley_Action00.py @@ -0,0 +1,394 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/cashbotHQ/ZONE17a', + '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': []}, + 10020: {'type': 'attribModifier', + 'name': 'range', + 'comment': '', + 'parentEntId': 10016, + 'attribName': 'range', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '7.3'}, + 10026: {'type': 'attribModifier', + 'name': 'period', + 'comment': '', + 'parentEntId': 10016, + 'attribName': 'period', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '6'}, + 10011: {'type': 'conveyorBelt', + 'name': 'conveyorBelt', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.37029790878, 24.4200267792, 13.000869751), + 'hpr': Point3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'floorName': 'platformcollision', + 'length': 42.0, + 'speed': 2.0, + 'treadLength': 10.0, + 'treadModelPath': 'phase_9/models/cogHQ/platform1', + 'widthScale': 1.0}, + 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.2, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 8, + 'velocity': 4}, + 10025: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1.2, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 8, + 'velocity': 4}, + 10002: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-1.80931818485, 65.0541229248, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10008: {'type': 'mintShelf', + 'name': 'frontShelf', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0.75479054451, -116.741004944, 0.0), + 'hpr': Vec3(180.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10009: {'type': 'mintShelf', + 'name': 'backShelf1', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(5.09813165665, 63.6601753235, 0.0), + 'hpr': Vec3(270.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10010: {'type': 'mintShelf', + 'name': 'copy of backShelf1', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(-3.80197167397, 81.7602081299, 0.0), + 'hpr': Vec3(90.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0), + 'mintId': 12500}, + 10000: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, -9.59561252594, 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'}, + 10003: {'type': 'model', + 'name': 'downLift', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(4.43959569931, 36.9850654602, 0.880278944969), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(0.550000011921, 0.550000011921, 0.550000011921), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer.bam'}, + 10004: {'type': 'model', + 'name': 'upLift', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-3.25595784187, -35.5523605347, 0.880278944969), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(0.550000011921, 0.550000011921, 0.550000011921), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer.bam'}, + 10012: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0.0, 10.1246709824, 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'}, + 10023: {'type': 'model', + 'name': 'penaltyLift', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-4.46345901489, -13.5, 0.880278944969), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': Vec3(0.34999999404, 0.34999999404, 0.34999999404), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer.bam'}, + 10027: {'type': 'model', + 'name': 'shadow', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, -1.56577277184), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(3.5935189724, 3.5935189724, 3.5935189724), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10028: {'type': 'model', + 'name': 'shadow', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, -1.56577277184), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(3.5935189724, 3.5935189724, 3.5935189724), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10029: {'type': 'model', + 'name': 'copy of shadow', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, -2.47000002861), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(3.5935189724, 3.5935189724, 3.5935189724), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModel', + 'modelPath': 'phase_10/models/cogHQ/RoundShadow.bam'}, + 10001: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.660497009754, 0.0, 0.0133484890684), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(2.50575017929, 2.50575017929, 2.50575017929)}, + 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}, + 10016: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(5.0, 10.4380426407, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': Vec3(1.0, 1.0, 1.0)}, + 10021: {'type': 'nodepath', + 'name': 'goons', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Vec3(0.0, 0.0, 0.0), + 'scale': 1}, + 10005: {'type': 'paintMixer', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0.0, 0.0, 3.56454277039), + 'hpr': Vec3(180.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': 'noBlend', + 'offset': Point3(0.0, 0.0, 20.0), + 'period': 10.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.4}, + 10006: {'type': 'paintMixer', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0.0, 0.0, 3.56454277039), + 'hpr': Vec3(180.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': 'noBlend', + 'offset': Point3(0.0, 0.0, 20.0), + 'period': 5.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.4}, + 10024: {'type': 'paintMixer', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0.0, 0.0, 3.56454277039), + 'hpr': Vec3(180.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': 'noBlend', + 'offset': Point3(0.0, 0.0, 33.0), + 'period': 10.0, + 'phaseShift': 0.5, + 'shaftScale': 4.0, + 'waitPercent': 0.4}, + 10013: {'type': 'path', + 'name': 'right', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(4.0, 0.0, 0.0), + 'hpr': Point3(90.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 3, + 'pathScale': 1.0}, + 10022: {'type': 'path', + 'name': 'left', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-4.0, 2.0, 0.0), + 'hpr': Point3(-90.0, 0.0, 0.0), + 'scale': 1, + 'pathIndex': 3, + 'pathScale': 1.0}, + 10015: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 0.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.0, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10017: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 8.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.75, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10018: {'type': 'stomper', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 16.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.25, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10019: {'type': 'stomper', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0.0, 24.0, 0.0), + 'hpr': Point3(270.0, 0.0, 0.0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(4.0, 2.0, 4.0), + 'modelPath': 0, + 'motion': 1, + 'period': 6, + 'phaseShift': 0.5, + 'range': 7.3, + 'shaftScale': Point3(1.89999997616, 7.5, 1.89999997616), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'horizontal', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/CogDisguiseGlobals.py b/toontown/coghq/CogDisguiseGlobals.py new file mode 100644 index 0000000..27a3607 --- /dev/null +++ b/toontown/coghq/CogDisguiseGlobals.py @@ -0,0 +1,521 @@ +from toontown.suit import SuitDNA +import types +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from otp.otpbase import OTPGlobals +PartsPerSuit = (17, + 14, + 12, + 10) +PartsPerSuitBitmasks = (131071, + 130175, + 56447, + 56411) +AllBits = 131071 +MinPartLoss = 2 +MaxPartLoss = 4 +MeritsPerLevel = ((100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500), + (1100, + 1440, + 1780, + 2120, + 8900), + (1780, + 2330, + 2880, + 3430, + 14400), + (2880, + 3770, + 4660, + 5500, + 23300, + 2880, + 23300, + 2880, + 3770, + 4660, + 5500, + 23300, + 2880, + 3770, + 4660, + 5500, + 6440, + 7330, + 8220, + 9110, + 10000, + 23300, + 2880, + 3770, + 4660, + 5500, + 6440, + 7330, + 8220, + 9110, + 10000, + 23300, + 2880, + 3770, + 4660, + 5500, + 6440, + 7330, + 8220, + 9110, + 10000, + 23300, + 0), + (60, + 80, + 100, + 120, + 500), + (100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500), + (1100, + 1440, + 1780, + 2120, + 8900), + (1780, + 2330, + 2880, + 3430, + 14400, + 1780, + 14400, + 1780, + 2330, + 2880, + 3430, + 14400, + 1780, + 2330, + 2880, + 3430, + 3980, + 4530, + 5080, + 5630, + 6180, + 14400, + 1780, + 2330, + 2880, + 3430, + 3980, + 4530, + 5080, + 5630, + 6180, + 14400, + 1780, + 2330, + 2880, + 3430, + 3980, + 4530, + 5080, + 5630, + 6180, + 14400, + 0), + (40, + 50, + 60, + 70, + 300), + (60, + 80, + 100, + 120, + 500), + (100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500), + (1100, + 1440, + 1780, + 2120, + 8900, + 1100, + 8900, + 1100, + 1440, + 1780, + 2120, + 8900, + 1100, + 1440, + 1780, + 2120, + 2460, + 2800, + 3140, + 3480, + 3820, + 8900, + 1100, + 1440, + 1780, + 2120, + 2460, + 2800, + 3140, + 3480, + 3820, + 8900, + 1100, + 1440, + 1780, + 2120, + 2460, + 2800, + 3140, + 3480, + 3820, + 8900, + 0), + (20, + 30, + 40, + 50, + 200), + (40, + 50, + 60, + 70, + 300), + (60, + 80, + 100, + 120, + 500), + (100, + 130, + 160, + 190, + 800), + (160, + 210, + 260, + 310, + 1300), + (260, + 340, + 420, + 500, + 2100), + (420, + 550, + 680, + 810, + 3400), + (680, + 890, + 1100, + 1310, + 5500, + 680, + 5500, + 680, + 890, + 1100, + 1310, + 5500, + 680, + 890, + 1100, + 1310, + 1520, + 1730, + 1940, + 2150, + 2360, + 5500, + 680, + 890, + 1100, + 1310, + 1520, + 1730, + 1940, + 2150, + 2360, + 5500, + 680, + 890, + 1100, + 1310, + 1520, + 1730, + 1940, + 2150, + 2360, + 5500, + 0)) +leftLegUpper = 1 +leftLegLower = 2 +leftLegFoot = 4 +rightLegUpper = 8 +rightLegLower = 16 +rightLegFoot = 32 +torsoLeftShoulder = 64 +torsoRightShoulder = 128 +torsoChest = 256 +torsoHealthMeter = 512 +torsoPelvis = 1024 +leftArmUpper = 2048 +leftArmLower = 4096 +leftArmHand = 8192 +rightArmUpper = 16384 +rightArmLower = 32768 +rightArmHand = 65536 +upperTorso = torsoLeftShoulder +leftLegIndex = 0 +rightLegIndex = 1 +torsoIndex = 2 +leftArmIndex = 3 +rightArmIndex = 4 +PartsQueryShifts = (leftLegUpper, + rightLegUpper, + torsoLeftShoulder, + leftArmUpper, + rightArmUpper) +PartsQueryMasks = (leftLegFoot + leftLegLower + leftLegUpper, + rightLegFoot + rightLegLower + rightLegUpper, + torsoPelvis + torsoHealthMeter + torsoChest + torsoRightShoulder + torsoLeftShoulder, + leftArmHand + leftArmLower + leftArmUpper, + rightArmHand + rightArmLower + rightArmUpper) +PartNameStrings = TTLocalizer.CogPartNames +SimplePartNameStrings = TTLocalizer.CogPartNamesSimple +PartsQueryNames = ({1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[2], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[5], + 64: PartNameStrings[6], + 128: PartNameStrings[7], + 256: PartNameStrings[8], + 512: PartNameStrings[9], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[13], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[16]}, + {1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[2], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[5], + 64: SimplePartNameStrings[0], + 128: SimplePartNameStrings[0], + 256: SimplePartNameStrings[0], + 512: SimplePartNameStrings[0], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[13], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[16]}, + {1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[2], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[5], + 64: SimplePartNameStrings[0], + 128: SimplePartNameStrings[0], + 256: SimplePartNameStrings[0], + 512: SimplePartNameStrings[0], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[12], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[15]}, + {1: PartNameStrings[0], + 2: PartNameStrings[1], + 4: PartNameStrings[1], + 8: PartNameStrings[3], + 16: PartNameStrings[4], + 32: PartNameStrings[4], + 64: SimplePartNameStrings[0], + 128: SimplePartNameStrings[0], + 256: SimplePartNameStrings[0], + 512: SimplePartNameStrings[0], + 1024: PartNameStrings[10], + 2048: PartNameStrings[11], + 4096: PartNameStrings[12], + 8192: PartNameStrings[12], + 16384: PartNameStrings[14], + 32768: PartNameStrings[15], + 65536: PartNameStrings[15]}) +suitTypes = PythonUtil.Enum(('NoSuit', 'NoMerits', 'FullSuit')) + +def getNextPart(parts, partIndex, dept): + dept = dept2deptIndex(dept) + needMask = PartsPerSuitBitmasks[dept] & PartsQueryMasks[partIndex] + haveMask = parts[dept] & PartsQueryMasks[partIndex] + nextPart = ~needMask | haveMask + nextPart = nextPart ^ nextPart + 1 + nextPart = nextPart + 1 >> 1 + return nextPart + + +def getPartName(partArray): + index = 0 + for part in partArray: + if part: + return PartsQueryNames[index][part] + index += 1 + + +def isSuitComplete(parts, dept): + dept = dept2deptIndex(dept) + for p in range(len(PartsQueryMasks)): + if getNextPart(parts, p, dept): + return 0 + + return 1 + + +def isPaidSuitComplete(av, parts, dept): + isPaid = 0 + base = getBase() + if av and av.getGameAccess() == OTPGlobals.AccessFull: + isPaid = 1 + if isPaid: + if isSuitComplete(parts, dept): + return 1 + return 0 + + +def getTotalMerits(toon, index): + from toontown.battle import SuitBattleGlobals + cogIndex = toon.cogTypes[index] + SuitDNA.suitsPerDept * index + cogTypeStr = SuitDNA.suitHeadTypes[cogIndex] + cogBaseLevel = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + cogLevel = toon.cogLevels[index] - cogBaseLevel + cogLevel = max(min(cogLevel, len(MeritsPerLevel[cogIndex]) - 1), 0) + return MeritsPerLevel[cogIndex][cogLevel] + + +def getTotalParts(bitString, shiftWidth = 32): + sum = 0 + for shift in range(0, shiftWidth): + sum = sum + (bitString >> shift & 1) + + return sum + + +def asBitstring(number): + array = [] + shift = 0 + if number == 0: + array.insert(0, '0') + while pow(2, shift) <= number: + if number >> shift & 1: + array.insert(0, '1') + else: + array.insert(0, '0') + shift += 1 + + str = '' + for i in range(0, len(array)): + str = str + array[i] + + return str + + +def asNumber(bitstring): + num = 0 + for i in range(0, len(bitstring)): + if bitstring[i] == '1': + num += pow(2, len(bitstring) - 1 - i) + + return num + + +def dept2deptIndex(dept): + if type(dept) == types.StringType: + dept = SuitDNA.suitDepts.index(dept) + return dept diff --git a/toontown/coghq/CogHQBossBattle.py b/toontown/coghq/CogHQBossBattle.py new file mode 100644 index 0000000..39485a0 --- /dev/null +++ b/toontown/coghq/CogHQBossBattle.py @@ -0,0 +1,227 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.battle import BattlePlace +from toontown.suit import Suit +import math + +class CogHQBossBattle(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQBossBattle') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.bossCog = None + self.teleportInPosHpr = (0, 0, 0, 0, 0, 0) + self.fsm = ClassicFSM.ClassicFSM('CogHQBossBattle', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'movie']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'died', 'movie']), + State.State('finalBattle', self.enterFinalBattle, self.exitFinalBattle, ['walk', + 'stickerBook', + 'teleportOut', + 'died', + 'tunnelOut', + 'DFA', + 'battle', + 'movie', + 'ouch', + 'crane', + 'WaitForBattle', + 'squished']), + State.State('movie', self.enterMovie, self.exitMovie, ['walk', + 'battle', + 'finalBattle', + 'died', + 'teleportOut']), + State.State('ouch', self.enterOuch, self.exitOuch, ['walk', + 'battle', + 'finalBattle', + 'died', + 'crane']), + State.State('crane', self.enterCrane, self.exitCrane, ['walk', + 'battle', + 'finalBattle', + 'died', + 'ouch', + 'squished']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'teleportOut', + 'died', + 'tunnelOut', + 'DFA', + 'battle', + 'movie', + 'ouch', + 'crane', + 'finalBattle', + 'WaitForBattle']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'DFA', + 'WaitForBattle', + 'movie', + 'battle']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk', 'movie']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut', 'tunnelOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'final', 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['final']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('squished', self.enterSquished, self.exitSquished, ['finalBattle', + 'crane', + 'died', + 'teleportOut']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + return + + def load(self): + BattlePlace.BattlePlace.load(self) + self.parentFSM.getStateNamed('cogHQBossBattle').addChild(self.fsm) + self.townBattle = self.loader.townBattle + for i in range(1, 3): + Suit.loadSuits(i) + + def unload(self): + BattlePlace.BattlePlace.unload(self) + self.parentFSM.getStateNamed('cogHQBossBattle').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + for i in range(1, 3): + Suit.unloadSuits(i) + + def getTaskZoneId(self): + return base.cr.playGame.hood.id + + def enter(self, requestStatus, bossCog): + self.zoneId = requestStatus['zoneId'] + BattlePlace.BattlePlace.enter(self) + self.fsm.enterInitialState() + self.bossCog = bossCog + if self.bossCog: + self.bossCog.d_avatarEnter() + self._telemLimiter = TLGatherAllAvs('CogHQBossBattle', RotationLimitToH) + NametagGlobals.setMasterArrowsOn(1) + base.localAvatar.inventory.setRespectInvasions(0) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def exit(self): + self.fsm.requestFinalState() + base.localAvatar.inventory.setRespectInvasions(1) + if self.bossCog: + self.bossCog.d_avatarExit() + self.bossCog = None + self._telemLimiter.destroy() + del self._telemLimiter + BattlePlace.BattlePlace.exit(self) + return + + def enterBattle(self, event): + mult = 1 + if self.bossCog: + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(self.bossCog.battleNumber) + self.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.cantLeaveGame = 1 + + def exitBattle(self): + self.townBattle.exit() + + def enterFinalBattle(self): + self.walkStateData.enter() + self.walkStateData.fsm.request('walking') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.setTeleportAllowed(0) + base.localAvatar.cantLeaveGame = 0 + base.localAvatar.book.hideButton() + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore('enterStickerBook') + self.ignore(ToontownGlobals.OptionsPageHotkey) + + def exitFinalBattle(self): + self.walkStateData.exit() + base.localAvatar.setTeleportAllowed(1) + + def enterMovie(self, requestStatus = None): + base.localAvatar.setTeleportAvailable(0) + + def exitMovie(self): + pass + + def enterOuch(self): + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.laffMeter.start() + + def exitOuch(self): + base.localAvatar.laffMeter.stop() + + 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): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.setTeleportAllowed(0) + base.localAvatar.book.hideButton() + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore('enterStickerBook') + self.ignore(ToontownGlobals.OptionsPageHotkey) + self.ignore(self.walkDoneEvent) + + def exitWalk(self): + BattlePlace.BattlePlace.exitWalk(self) + base.localAvatar.setTeleportAllowed(1) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTeleportIn(self, requestStatus): + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(*self.teleportInPosHpr) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def enterSquished(self): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('Flattened') + + def handleSquishDone(self, extraArgs = []): + base.cr.playGame.getPlace().setState('walk') + + def exitSquished(self): + taskMgr.remove(base.localAvatar.uniqueName('finishSquishTask')) + base.localAvatar.laffMeter.stop() diff --git a/toontown/coghq/CogHQExterior.py b/toontown/coghq/CogHQExterior.py new file mode 100644 index 0000000..1d1c43b --- /dev/null +++ b/toontown/coghq/CogHQExterior.py @@ -0,0 +1,135 @@ +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 pandac.PandaModules import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs + +class CogHQExterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQExterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.fsm = ClassicFSM.ClassicFSM('CogHQExterior', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'doorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'teleportOut', + 'tunnelOut', + 'DFA', + 'doorOut', + 'died', + 'stopped', + 'WaitForBattle', + 'battle', + 'squished', + 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'stickerBook']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk', 'stopped']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'DFA', + 'WaitForBattle', + 'battle', + 'tunnelOut', + 'doorOut', + 'squished', + 'died']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut', 'tunnelOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', 'WaitForBattle', 'battle']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'final', 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['quietZone']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk', 'WaitForBattle', 'battle']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('cogHQExterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + + def unload(self): + self.parentFSM.getStateNamed('cogHQExterior').removeChild(self.fsm) + del self.fsm + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + BattlePlace.BattlePlace.enter(self) + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + self.nodeList = [self.loader.geom] + self._telemLimiter = TLGatherAllAvs('CogHQExterior', RotationLimitToH) + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + NametagGlobals.setMasterArrowsOn(1) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.nodeList, self.zoneId) + how = requestStatus['how'] + self.fsm.request(how, [requestStatus]) + + def exit(self): + self.fsm.requestFinalState() + self._telemLimiter.destroy() + del self._telemLimiter + self.loader.music.stop() + for node in self.tunnelOriginList: + node.removeNode() + + del self.tunnelOriginList + if self.loader.geom: + self.loader.geom.reparentTo(hidden) + self.ignoreAll() + BattlePlace.BattlePlace.exit(self) + + def enterTunnelOut(self, requestStatus): + fromZoneId = self.zoneId - self.zoneId % 100 + tunnelName = base.cr.hoodMgr.makeLinkTunnelName(self.loader.hood.id, fromZoneId) + requestStatus['tunnelName'] = tunnelName + BattlePlace.BattlePlace.enterTunnelOut(self, requestStatus) + + def enterTeleportIn(self, requestStatus): + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + base.localAvatar.setPosHpr(render, x, y, z, h, p, r) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus, callback = None): + if requestStatus.has_key('battle'): + self.__teleportOutDone(requestStatus) + else: + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.hoodId and zoneId == self.loader.hood.hoodId 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 exitTeleportOut(self): + BattlePlace.BattlePlace.exitTeleportOut(self) + + 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() diff --git a/toontown/coghq/CogHQLoader.py b/toontown/coghq/CogHQLoader.py new file mode 100644 index 0000000..bf5761c --- /dev/null +++ b/toontown/coghq/CogHQLoader.py @@ -0,0 +1,150 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import CogHQLobby +from toontown.hood import QuietZoneState +from toontown.hood import ZoneUtil +from toontown.town import TownBattle +from toontown.suit import Suit +from pandac.PandaModules import * + +class CogHQLoader(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.hood = hood + self.parentFSMState = parentFSMState + self.placeDoneEvent = 'cogHQLoaderPlaceDone' + self.townBattleDoneEvent = 'town-battle-done' + self.fsm = ClassicFSM.ClassicFSM('CogHQLoader', [State.State('start', None, None, ['quietZone', 'cogHQExterior', 'cogHQBossBattle']), + State.State('cogHQExterior', self.enterCogHQExterior, self.exitCogHQExterior, ['quietZone', 'cogHQLobby']), + State.State('cogHQLobby', self.enterCogHQLobby, self.exitCogHQLobby, ['quietZone', 'cogHQExterior', 'cogHQBossBattle']), + State.State('cogHQBossBattle', self.enterCogHQBossBattle, self.exitCogHQBossBattle, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['cogHQExterior', 'cogHQLobby', 'cogHQBossBattle']), + State.State('final', None, None, ['start'])], 'start', 'final') + return + + def load(self, zoneId): + self.parentFSMState.addChild(self.fsm) + self.music = base.loadMusic(self.musicFile) + self.battleMusic = base.loadMusic('phase_9/audio/bgm/encntr_suit_winning.mid') + self.townBattle = TownBattle.TownBattle(self.townBattleDoneEvent) + self.townBattle.load() + Suit.loadSuits(3) + self.loadPlaceGeom(zoneId) + + def loadPlaceGeom(self, zoneId): + pass + + def unloadPlaceGeom(self): + pass + + def unload(self): + self.unloadPlaceGeom() + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + del self.fsm + self.townBattle.unload() + self.townBattle.cleanup() + del self.townBattle + del self.battleMusic + Suit.unloadSuits(3) + Suit.unloadSkelDialog() + del self.hood + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + self.fsm.enterInitialState() + self.fsm.request(requestStatus['where'], [requestStatus]) + + def exit(self): + self.ignoreAll() + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def enterPlace(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.placeDone) + self.place = self.placeClass(self, self.fsm, self.placeDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitPlace(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def placeDone(self): + self.requestStatus = self.place.doneStatus + status = self.place.doneStatus + if status.get('shardId') == None and self.isInThisHq(status): + self.unloadPlaceGeom() + zoneId = status['zoneId'] + self.loadPlaceGeom(zoneId) + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def isInThisHq(self, status): + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterCogHQExterior(self, requestStatus): + self.placeClass = self.getExteriorPlaceClass() + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitCogHQExterior(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterCogHQLobby(self, requestStatus): + self.placeClass = CogHQLobby.CogHQLobby + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitCogHQLobby(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterCogHQBossBattle(self, requestStatus): + self.placeClass = self.getBossPlaceClass() + self.enterPlace(requestStatus) + + def exitCogHQBossBattle(self): + self.exitPlace() + self.placeClass = None + return diff --git a/toontown/coghq/CogHQLobby.py b/toontown/coghq/CogHQLobby.py new file mode 100644 index 0000000..662f530 --- /dev/null +++ b/toontown/coghq/CogHQLobby.py @@ -0,0 +1,109 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.hood import Place +from toontown.building import Elevator +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs + +class CogHQLobby(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHQLobby') + + def __init__(self, hood, parentFSM, doneEvent): + Place.Place.__init__(self, hood, doneEvent) + self.parentFSM = parentFSM + self.elevatorDoneEvent = 'elevatorDone' + self.fsm = ClassicFSM.ClassicFSM('CogHQLobby', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'doorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['elevator', + 'DFA', + 'doorOut', + 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'elevator']), + 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('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('cogHQLobby').addChild(self.fsm) + Place.Place.load(self) + + def unload(self): + self.parentFSM.getStateNamed('cogHQLobby').removeChild(self.fsm) + Place.Place.unload(self) + self.fsm = None + return + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + Place.Place.enter(self) + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + NametagGlobals.setMasterArrowsOn(1) + how = requestStatus['how'] + self.fsm.request(how, [requestStatus]) + self._telemLimiter = TLGatherAllAvs('CogHQLobby', RotationLimitToH) + + def exit(self): + self._telemLimiter.destroy() + del self._telemLimiter + self.fsm.requestFinalState() + self.ignoreAll() + self.loader.music.stop() + if self.loader.geom != None: + self.loader.geom.reparentTo(hidden) + Place.Place.exit(self) + return + + def enterWalk(self, teleportIn = 0): + Place.Place.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + 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 + distElevator.elevatorFSM = self.elevator + 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 == 'cogHQBossBattle': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def enterTeleportIn(self, requestStatus): + base.localAvatar.setPosHpr(render, 0, 0, 0, 0, 0, 0) + Place.Place.enterTeleportIn(self, requestStatus) diff --git a/toontown/coghq/CogSuitManagerAI.py b/toontown/coghq/CogSuitManagerAI.py new file mode 100644 index 0000000..c6271f7 --- /dev/null +++ b/toontown/coghq/CogSuitManagerAI.py @@ -0,0 +1,20 @@ +from otp.ai.AIBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +import random +from toontown.suit import SuitDNA +import CogDisguiseGlobals + +class CogSuitManagerAI: + notify = DirectNotifyGlobal.directNotify.newCategory('CogSuitManagerAI') + + def __init__(self, air): + self.air = air + + def recoverPart(self, av, factoryType, suitTrack, zoneId, avList): + partsRecovered = [ + 0, 0, 0, 0] + part = av.giveGenericCogPart(factoryType, suitTrack) + if part: + partsRecovered[CogDisguiseGlobals.dept2deptIndex(suitTrack)] = part + self.air.questManager.toonRecoveredCogSuitPart(av, zoneId, avList) + return partsRecovered diff --git a/toontown/coghq/ConveyorBelt.py b/toontown/coghq/ConveyorBelt.py new file mode 100644 index 0000000..f8e2685 --- /dev/null +++ b/toontown/coghq/ConveyorBelt.py @@ -0,0 +1,107 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import MovingPlatform +from otp.level import BasicEntities + +class ConveyorBelt(BasicEntities.NodePathEntity): + UseClipPlanes = 1 + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.initBelt() + + def destroy(self): + self.destroyBelt() + BasicEntities.NodePathEntity.destroy(self) + + def initBelt(self): + treadModel = loader.loadModel(self.treadModelPath) + treadModel.setSx(self.widthScale) + treadModel.flattenLight() + self.numTreads = int(self.length / self.treadLength) + 3 + self.beltNode = self.attachNewNode('belt') + self.treads = [] + for i in xrange(self.numTreads): + mp = MovingPlatform.MovingPlatform() + mp.parentingNode = render.attachNewNode('parentTarget') + mp.setupCopyModel('conv%s-%s' % (self.getParentToken(), i), treadModel, self.floorName, parentingNode=mp.parentingNode) + mp.parentingNode.reparentTo(mp) + mp.reparentTo(self.beltNode) + self.treads.append(mp) + + self.start() + + def destroyBelt(self): + self.stop() + for tread in self.treads: + tread.destroy() + tread.parentingNode.removeNode() + del tread.parentingNode + + del self.treads + self.beltNode.removeNode() + del self.beltNode + + def start(self): + startTime = self.level.startTime + treadsIval = Parallel(name='treads') + treadPeriod = self.treadLength / abs(self.speed) + startY = -self.treadLength + for i in xrange(self.numTreads): + periodsToEnd = self.numTreads - i + periodsFromStart = self.numTreads - periodsToEnd + ival = Sequence() + if periodsToEnd != 0: + ival.append(LerpPosInterval(self.treads[i], duration=treadPeriod * periodsToEnd, pos=Point3(0, startY + self.numTreads * self.treadLength, 0), startPos=Point3(0, startY + i * self.treadLength, 0), fluid=1)) + + def dumpContents(tread = self.treads[i]): + tread.releaseLocalToon() + + ival.append(Sequence(Func(dumpContents), Func(self.treads[i].setPos, Point3(0, startY + self.numTreads * self.treadLength, 0)))) + if periodsFromStart != 0: + ival.append(LerpPosInterval(self.treads[i], duration=treadPeriod * periodsFromStart, pos=Point3(0, startY + i * self.treadLength, 0), startPos=Point3(0, startY, 0), fluid=1)) + treadsIval.append(ival) + + self.beltIval = Sequence(treadsIval, name='ConveyorBelt-%s' % self.entId) + playRate = 1.0 + startT = 0.0 + endT = self.beltIval.getDuration() + if self.speed < 0.0: + playRate = -1.0 + temp = startT + startT = endT + endT = temp + self.beltIval.loop(playRate=playRate) + self.beltIval.setT(globalClock.getFrameTime() - startTime) + if ConveyorBelt.UseClipPlanes: + headClip = PlaneNode('headClip') + tailClip = PlaneNode('tailClip') + self.headClipPath = self.beltNode.attachNewNode(headClip) + self.headClipPath.setP(-90) + self.tailClipPath = self.beltNode.attachNewNode(tailClip) + self.tailClipPath.setY(self.length) + self.tailClipPath.setP(90) + self.beltNode.setClipPlane(self.headClipPath) + self.beltNode.setClipPlane(self.tailClipPath) + for tread in self.treads: + tread.parentingNode.setClipPlaneOff(self.headClipPath) + tread.parentingNode.setClipPlaneOff(self.tailClipPath) + + def stop(self): + if hasattr(self, 'beltIval'): + self.beltIval.pause() + del self.beltIval + if ConveyorBelt.UseClipPlanes: + self.headClipPath.removeNode() + del self.headClipPath + self.tailClipPath.removeNode() + del self.tailClipPath + self.clearClipPlane() + for tread in self.treads: + tread.parentingNode.clearClipPlane() + + if __dev__: + + def attribChanged(self, attrib, value): + self.destroyBelt() + self.initBelt() diff --git a/toontown/coghq/CountryClubInterior.py b/toontown/coghq/CountryClubInterior.py new file mode 100644 index 0000000..29a7ea5 --- /dev/null +++ b/toontown/coghq/CountryClubInterior.py @@ -0,0 +1,291 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import BulletinBoardWatcher +from pandac.PandaModules import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.coghq import DistributedCountryClub +from toontown.building import Elevator +import random + +class CountryClubInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('CountryClubInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = loader.countryClubId + self.elevatorDoneEvent = 'elevatorDone' + self.fsm = ClassicFSM.ClassicFSM('CountryClubInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'DFA', + 'fallDown', + 'stopped', + 'elevator']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'stickerBook']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'DFA', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walkteleportOut']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('countryClubInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + musicName = random.choice(['phase_12/audio/bgm/Bossbot_Factory_v1.mid', 'phase_12/audio/bgm/Bossbot_Factory_v2.mid', 'phase_12/audio/bgm/Bossbot_Factory_v3.mid']) + self.music = base.loadMusic(musicName) + + def unload(self): + self.parentFSM.getStateNamed('countryClubInterior').removeChild(self.fsm) + del self.music + del self.fsm + del self.parentFSM + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + base.localAvatar.inventory.setRespectInvasions(0) + base.cr.forbidCheesyEffects(1) + self._telemLimiter = TLGatherAllAvs('CountryClubInterior', RotationLimitToH) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + CountryClub = bboard.get(DistributedCountryClub.DistributedCountryClub.ReadyPost) + self.loader.hood.spawnTitleText(CountryClub.countryClubId, CountryClub.floorNum) + + self.CountryClubReadyWatcher = BulletinBoardWatcher.BulletinBoardWatcher('CountryClubReady', DistributedCountryClub.DistributedCountryClub.ReadyPost, commence) + self.CountryClubDefeated = 0 + self.acceptOnce(DistributedCountryClub.DistributedCountryClub.WinEvent, self.handleCountryClubWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send(DistributedCountryClub.DistributedCountryClub.WinEvent)) + self.confrontedBoss = 0 + + def handleConfrontedBoss(self = self): + self.confrontedBoss = 1 + + self.acceptOnce('localToonConfrontedCountryClubBoss', handleConfrontedBoss) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + bboard.remove(DistributedCountryClub.DistributedCountryClub.ReadyPost) + self._telemLimiter.destroy() + del self._telemLimiter + base.cr.forbidCheesyEffects(0) + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + del self.CountryClubReadyWatcher + + def enterStopped(self): + BattlePlace.BattlePlace.enterStopped(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + CountryClubInterior.notify.debug('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + CountryClubInterior.notify.debug('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + CountryClubInterior.notify.debug('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getCountryClubCreditMultiplier(self.zoneId) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + CountryClubInterior.notify.debug('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + CountryClubInterior.notify.debug('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + CountryClubInterior.notify.debug('__teleportOutDone()') + messenger.send('leavingCountryClub') + messenger.send('localToonLeft') + if self.CountryClubDefeated and not self.confrontedBoss: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + CountryClubInterior.notify.debug('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def handleCountryClubWinEvent(self): + CountryClubInterior.notify.debug('handleCountryClubWinEvent') + + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + + self.CountryClubDefeated = 1 + + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + + self.fsm.request('teleportOut', [{ + 'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + }]) + + def enterDied(self, requestStatus, callback = None): + CountryClubInterior.notify.debug('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingCountryClub') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + CountryClubInterior.notify.debug('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveCountryClubAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + CountryClubInterior.notify.debug('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + 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) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + + 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 == 'factoryInterior' or where == 'suitInterior': + self.doneStatus = doneStatus + self.doneEvent = 'lawOfficeFloorDone' + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') diff --git a/toontown/coghq/CountryClubLayout.py b/toontown/coghq/CountryClubLayout.py new file mode 100644 index 0000000..83fb00d --- /dev/null +++ b/toontown/coghq/CountryClubLayout.py @@ -0,0 +1,278 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from toontown.coghq import CountryClubRoomSpecs +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import normalDistrib, lerp +import random + +def printAllBossbotInfo(): + print 'roomId: roomName' + for roomId, roomName in CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName.items(): + print '%s: %s' % (roomId, roomName) + + print '\nroomId: numBattles' + for roomId, numBattles in CountryClubRoomSpecs.roomId2numBattles.items(): + print '%s: %s' % (roomId, numBattles) + + print '\ncountryClubId floor roomIds' + printCountryClubRoomIds() + print '\ncountryClubId floor numRooms' + printNumRooms() + print '\ncountryClubId floor numForcedBattles' + printNumBattles() + + +def iterateBossbotCountryClubs(func): + from toontown.toonbase import ToontownGlobals + for countryClubId in [ToontownGlobals.BossbotCountryClubIntA, ToontownGlobals.BossbotCountryClubIntB, ToontownGlobals.BossbotCountryClubIntC]: + for floorNum in xrange(ToontownGlobals.CountryClubNumFloors[countryClubId]): + func(CountryClubLayout(countryClubId, floorNum)) + + +def printCountryClubInfo(): + + def func(ml): + print ml + + iterateBossbotCountryClubs(func) + + +def printCountryClubRoomIds(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getRoomIds() + + iterateBossbotCountryClubs(func) + + +def printCountryClubRoomNames(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getRoomNames() + + iterateBossbotCountryClubs(func) + + +def printNumRooms(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getNumRooms() + + iterateBossbotCountryClubs(func) + + +def printNumBattles(): + + def func(ml): + print ml.getCountryClubId(), ml.getFloorNum(), ml.getNumBattles() + + iterateBossbotCountryClubs(func) + + +ClubLayout3_0 = [(0, 2, 5, 9, 17), (0, 2, 4, 9, 17), (0, 2, 5, 9, 18)] +ClubLayout3_1 = [(0, 2, 5, 9, 17), (0, 2, 4, 9, 17), (0, 2, 5, 9, 18)] +ClubLayout3_2 = [(0, 2, 4, 9, 17), (0, 2, 4, 9, 17), (0, 2, 6, 9, 18)] +ClubLayout6_0 = [(0, 22, 4, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 18)] +ClubLayout6_1 = [(0, 22, 4, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 4, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 4, 29, 17), + (0, 22, 6, 29, 18)] +ClubLayout6_2 = [(0, 22, 4, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 6, 29, 17), + (0, 22, 5, 29, 17), + (0, 22, 7, 29, 18)] +ClubLayout9_0 = [(0, 32, 4, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 6, 39, 18)] +ClubLayout9_1 = [(0, 32, 4, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 7, 39, 17), + (0, 32, 7, 39, 18)] +ClubLayout9_2 = [(0, 32, 5, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 5, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 6, 39, 17), + (0, 32, 7, 39, 18)] +countryClubLayouts = [ClubLayout3_0, + ClubLayout3_1, + ClubLayout3_2, + ClubLayout6_0, + ClubLayout6_1, + ClubLayout6_2, + ClubLayout9_0, + ClubLayout9_1, + ClubLayout9_2] +testLayout = [ClubLayout3_0, + ClubLayout3_0, + ClubLayout3_0, + ClubLayout6_0, + ClubLayout6_0, + ClubLayout6_0, + ClubLayout9_0, + ClubLayout9_0, + ClubLayout9_0] +countryClubLayouts = testLayout + +class CountryClubLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('CountryClubLayout') + + def __init__(self, countryClubId, floorNum, layoutIndex): + self.countryClubId = countryClubId + self.floorNum = floorNum + self.layoutIndex = layoutIndex + self.roomIds = [] + self.hallways = [] + self.numRooms = 1 + ToontownGlobals.CountryClubNumRooms[self.countryClubId][0] + self.numHallways = self.numRooms - 1 + 1 + self.roomIds = countryClubLayouts[layoutIndex][floorNum] + hallwayRng = self.getRng() + connectorRoomNames = CountryClubRoomSpecs.BossbotCountryClubConnectorRooms + for i in xrange(self.numHallways): + self.hallways.append(hallwayRng.choice(connectorRoomNames)) + + def _genFloorLayout(self): + rng = self.getRng() + startingRoomIDs = CountryClubRoomSpecs.BossbotCountryClubEntranceIDs + middleRoomIDs = CountryClubRoomSpecs.BossbotCountryClubMiddleRoomIDs + finalRoomIDs = CountryClubRoomSpecs.BossbotCountryClubFinalRoomIDs + + numBattlesLeft = ToontownGlobals.CountryClubNumBattles[self.countryClubId] + + finalRoomId = rng.choice(finalRoomIDs) + numBattlesLeft -= CountryClubRoomSpecs.getNumBattles(finalRoomId) + + middleRoomIds = [] + middleRoomsLeft = self.numRooms - 2 + + numBattles2middleRoomIds = invertDictLossless(CountryClubRoomSpecs.middleRoomId2numBattles) + + allBattleRooms = [] + for num, roomIds in numBattles2middleRoomIds.items(): + if num > 0: + allBattleRooms.extend(roomIds) + while 1: + allBattleRoomIds = list(allBattleRooms) + rng.shuffle(allBattleRoomIds) + battleRoomIds = self._chooseBattleRooms(numBattlesLeft, + allBattleRoomIds) + if battleRoomIds is not None: + break + + CountryClubLayout.notify.info('could not find a valid set of battle rooms, trying again') + + middleRoomIds.extend(battleRoomIds) + middleRoomsLeft -= len(battleRoomIds) + + if middleRoomsLeft > 0: + actionRoomIds = numBattles2middleRoomIds[0] + for i in xrange(middleRoomsLeft): + roomId = rng.choice(actionRoomIds) + actionRoomIds.remove(roomId) + middleRoomIds.append(roomId) + + roomIds = [] + + roomIds.append(rng.choice(startingRoomIDs)) + + middleRoomIds.sort() + print 'middleRoomIds=%s' % middleRoomIds + roomIds.extend(middleRoomIds) + + roomIds.append(finalRoomId) + + return roomIds + + def getNumRooms(self): + return len(self.roomIds) + + def getRoomId(self, n): + return self.roomIds[n] + + def getRoomIds(self): + return self.roomIds[:] + + def getRoomNames(self): + names = [] + for roomId in self.roomIds: + names.append(CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[roomId]) + + return names + + def getNumHallways(self): + return len(self.hallways) + + def getHallwayModel(self, n): + return self.hallways[n] + + def getNumBattles(self): + numBattles = 0 + for roomId in self.getRoomIds(): + numBattles += CountryClubRoomSpecs.roomId2numBattles[roomId] + + return numBattles + + def getCountryClubId(self): + return self.countryClubId + + def getFloorNum(self): + return self.floorNum + + def getRng(self): + return random.Random(self.countryClubId * self.floorNum) + + def _chooseBattleRooms(self, numBattlesLeft, allBattleRoomIds, baseIndex = 0, chosenBattleRooms = None): + if chosenBattleRooms is None: + chosenBattleRooms = [] + while baseIndex < len(allBattleRoomIds): + nextRoomId = allBattleRoomIds[baseIndex] + baseIndex += 1 + newNumBattlesLeft = numBattlesLeft - CountryClubRoomSpecs.middleRoomId2numBattles[nextRoomId] + if newNumBattlesLeft < 0: + continue + elif newNumBattlesLeft == 0: + chosenBattleRooms.append(nextRoomId) + return chosenBattleRooms + chosenBattleRooms.append(nextRoomId) + result = self._chooseBattleRooms(newNumBattlesLeft, allBattleRoomIds, baseIndex, chosenBattleRooms) + if result is not None: + return result + else: + del chosenBattleRooms[-1:] + else: + return + + return + + def __str__(self): + return 'CountryClubLayout: id=%s, layoutIndex=%s, floor=%s, numRooms=%s, numBattles=%s' % (self.countryClubId, + self.layoutIndex, + self.floorNum, + self.getNumRooms(), + self.getNumBattles()) + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/CountryClubManagerAI.py b/toontown/coghq/CountryClubManagerAI.py new file mode 100644 index 0000000..9516156 --- /dev/null +++ b/toontown/coghq/CountryClubManagerAI.py @@ -0,0 +1,53 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedCountryClubAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import CountryClubLayout +from direct.showbase import DirectObject +import random +CountryClubId2Layouts = {ToontownGlobals.BossbotCountryClubIntA: (0, 1, 2), ToontownGlobals.BossbotCountryClubIntB: (3, 4, 5), ToontownGlobals.BossbotCountryClubIntC: (6, 7, 8)} + +class CountryClubManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('CountryClubManagerAI') + countryClubId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createCountryClub(self, countryClubId, players): + for avId in players: + if bboard.has('countryClubId-%s' % avId): + countryClubId = bboard.get('countryClubId-%s' % avId) + break + + numFloors = 1 + layoutIndex = None + floor = 0 + for avId in players: + if bboard.has('countryClubFloor-%s' % avId): + floor = bboard.get('countryClubFloor-%s' % avId) + floor = max(0, floor) + floor = min(floor, numFloors - 1) + break + + for avId in players: + if bboard.has('countryClubRoom-%s' % avId): + roomId = bboard.get('countryClubRoom-%s' % avId) + for i in xrange(numFloors): + layout = CountryClubLayout.CountryClubLayout(countryClubId, i) + if roomId in layout.getRoomIds(): + floor = i + else: + from toontown.coghq import CountryClubRoomSpecs + roomName = CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[roomId] + CountryClubManagerAI.notify.warning('room %s (%s) not found in any floor of countryClub %s' % (roomId, roomName, countryClubId)) + + countryClubZone = self.air.allocateZone() + if layoutIndex is None: + layoutIndex = random.choice(CountryClubId2Layouts[countryClubId]) + countryClub = DistributedCountryClubAI.DistributedCountryClubAI(self.air, countryClubId, countryClubZone, floor, players, layoutIndex) + countryClub.generateWithRequired(countryClubZone) + return countryClubZone diff --git a/toontown/coghq/CountryClubRoom.py b/toontown/coghq/CountryClubRoom.py new file mode 100644 index 0000000..0b12f64 --- /dev/null +++ b/toontown/coghq/CountryClubRoom.py @@ -0,0 +1,123 @@ +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from toontown.coghq import CountryClubRoomSpecs +from direct.directnotify import DirectNotifyGlobal +import random + +class CountryClubRoom(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('CountryClubRoom') + FloorCollPrefix = 'mintFloorColl' + CashbotMintDoorFrame = 'phase_10/models/cashbotHQ/DoorFrame' + + def __init__(self, path = None): + if path is not None: + if path in CountryClubRoomSpecs.BossbotCountryClubConnectorRooms: + loadFunc = loader.loadModelCopy + else: + loadFunc = loader.loadModel + self.setGeom(loadFunc(path)) + self.localToonFSM = ClassicFSM.ClassicFSM('CountryClubRoomLocalToonPresent', [State.State('off', self.enterLtOff, self.exitLtOff, ['notPresent']), State.State('notPresent', self.enterLtNotPresent, self.exitLtNotPresent, ['present']), State.State('present', self.enterLtPresent, self.exitLtPresent, ['notPresent'])], 'notPresent', 'notPresent') + self.localToonFSM.enterInitialState() + return + + def delete(self): + del self.localToonFSM + + def enter(self): + self.localToonFSM.request('notPresent') + + def exit(self): + self.localToonFSM.requestFinalState() + + def setRoomNum(self, num): + self.roomNum = num + + def getRoomNum(self): + return self.roomNum + + def setGeom(self, geom): + if geom == None: + import pdb + pdb.set_trace() + self.__geom = geom + return + + def getGeom(self): + return self.__geom + + def _getEntrances(self): + return self.__geom.findAllMatches('**/ENTRANCE*') + + def _getExits(self): + return self.__geom.findAllMatches('**/EXIT*') + + def attachTo(self, other, rng): + otherExits = other._getExits() + entrances = self._getEntrances() + otherDoor = otherExits[0] + thisDoor = rng.choice(entrances) + geom = self.getGeom() + otherGeom = other.getGeom() + self.notify.debug('thisDoor = %s' % thisDoor) + self.notify.debug('otherDoor = %s' % otherDoor) + self.notify.debug('thisGeom = %s' % geom) + self.notify.debug('otherGeom = %s' % otherGeom) + debugAxis1 = None + if debugAxis1: + debugAxis1.reparentTo(thisDoor) + debugAxis2 = None + if debugAxis2: + debugAxis2.reparentTo(otherDoor) + debugAxis2.setColorScale(0.5, 0.5, 0.5, 1) + tempNode = otherDoor.attachNewNode('tempRotNode') + geom.reparentTo(tempNode) + geom.clearMat() + newGeomPos = Vec3(0) - thisDoor.getPos(geom) + self.notify.debug('newGeomPos = %s' % newGeomPos) + geom.setPos(newGeomPos) + newTempNodeH = -thisDoor.getH(otherDoor) + self.notify.debug('newTempNodeH =%s' % newTempNodeH) + tempNode.setH(newTempNodeH) + geom.wrtReparentTo(otherGeom.getParent()) + tempNode.removeNode() + return + + def getFloorCollName(self): + return '%s%s' % (CountryClubRoom.FloorCollPrefix, self.roomNum) + + def initFloorCollisions(self): + allColls = self.getGeom().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 = self.getFloorCollName() + others = self.getGeom().findAllMatches('**/%s' % floorCollName) + for other in others: + other.setName('%s_renamed' % floorCollName) + + for floorColl in floorColls: + floorColl.setName(floorCollName) + + def enterLtOff(self): + pass + + def exitLtOff(self): + pass + + def enterLtNotPresent(self): + pass + + def exitLtNotPresent(self): + pass + + def enterLtPresent(self): + pass + + def exitLtPresent(self): + pass diff --git a/toontown/coghq/CountryClubRoomBase.py b/toontown/coghq/CountryClubRoomBase.py new file mode 100644 index 0000000..764c0d7 --- /dev/null +++ b/toontown/coghq/CountryClubRoomBase.py @@ -0,0 +1,24 @@ +from toontown.toonbase import ToontownGlobals + +class CountryClubRoomBase: + + def __init__(self): + pass + + def setCountryClubId(self, countryClubId): + self.countryClubId = countryClubId + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(countryClubId) + + def setRoomId(self, roomId): + self.roomId = roomId + + def getCogTrack(self): + return self.cogTrack + + if __dev__: + + def getCountryClubEntityTypeReg(self): + import FactoryEntityTypes + from otp.level import EntityTypeRegistry + typeReg = EntityTypeRegistry.EntityTypeRegistry(FactoryEntityTypes) + return typeReg diff --git a/toontown/coghq/CountryClubRoomSpecs.py b/toontown/coghq/CountryClubRoomSpecs.py new file mode 100644 index 0000000..d8dbe38 --- /dev/null +++ b/toontown/coghq/CountryClubRoomSpecs.py @@ -0,0 +1,70 @@ +from direct.showbase.PythonUtil import invertDict +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BossbotCountryClubFairwayRoom_Battle00_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle00_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle01_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle02_Cogs +from toontown.coghq import BossbotCountryClubMazeRoom_Battle03_Cogs +from toontown.coghq import NullCogs +from toontown.coghq import BossbotCountryClubKartRoom_Battle00_Cogs +from toontown.coghq import BossbotCountryClubPresidentRoom_Battle00_Cogs + +def getCountryClubRoomSpecModule(roomId): + return CashbotMintSpecModules[roomId] + + +def getCogSpecModule(roomId): + roomName = BossbotCountryClubRoomId2RoomName[roomId] + return CogSpecModules.get(roomName, NullCogs) + + +def getNumBattles(roomId): + return roomId2numBattles[roomId] + + +BossbotCountryClubRoomId2RoomName = {0: 'BossbotCountryClubEntrance_Action00', + 2: 'BossbotCountryClubTeeOffRoom_Action00', + 4: 'BossbotCountryClubFairwayRoom_Battle00', + 5: 'BossbotCountryClubMazeRoom_Battle00', + 6: 'BossbotCountryClubMazeRoom_Battle01', + 7: 'BossbotCountryClubMazeRoom_Battle02', + 9: 'BossbotCountryClubGreenRoom_Action00', + 17: 'BossbotCountryClubKartRoom_Battle00', + 18: 'BossbotCountryClubPresidentRoom_Battle00', + 22: 'BossbotCountryClubTeeOffRoom_Action01', + 32: 'BossbotCountryClubTeeOffRoom_Action02', + 29: 'BossbotCountryClubGreenRoom_Action01', + 39: 'BossbotCountryClubGreenRoom_Action02'} +BossbotCountryClubRoomName2RoomId = invertDict(BossbotCountryClubRoomId2RoomName) +BossbotCountryClubEntranceIDs = (0,) +BossbotCountryClubMiddleRoomIDs = (2, 5, 6) +BossbotCountryClubFinalRoomIDs = (18,) +BossbotCountryClubConnectorRooms = ('phase_12/models/bossbotHQ/Connector_Tunnel_A', 'phase_12/models/bossbotHQ/Connector_Tunnel_B') +CashbotMintSpecModules = {} +if not isClient(): + print 'EXECWARNING CountryClubRoomSpecs: %s' % BossbotCountryClubRoomName2RoomId + printStack() +for roomName, roomId in BossbotCountryClubRoomName2RoomId.items(): + exec 'from toontown.coghq import %s' % roomName + CashbotMintSpecModules[roomId] = eval(roomName) + +CogSpecModules = {'BossbotCountryClubFairwayRoom_Battle00': BossbotCountryClubFairwayRoom_Battle00_Cogs, + 'BossbotCountryClubMazeRoom_Battle00': BossbotCountryClubMazeRoom_Battle00_Cogs, + 'BossbotCountryClubMazeRoom_Battle01': BossbotCountryClubMazeRoom_Battle01_Cogs, + 'BossbotCountryClubMazeRoom_Battle02': BossbotCountryClubMazeRoom_Battle02_Cogs, + 'BossbotCountryClubKartRoom_Battle00': BossbotCountryClubKartRoom_Battle00_Cogs, + 'BossbotCountryClubPresidentRoom_Battle00': BossbotCountryClubPresidentRoom_Battle00_Cogs} +roomId2numBattles = {} +for roomName, roomId in BossbotCountryClubRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numBattles[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numBattles[roomId] = len(cogSpecModule.BattleCells) + +name2id = BossbotCountryClubRoomName2RoomId +roomId2numBattles[name2id['BossbotCountryClubTeeOffRoom_Action00']] = 1 +del name2id +middleRoomId2numBattles = {} +for roomId in BossbotCountryClubMiddleRoomIDs: + middleRoomId2numBattles[roomId] = roomId2numBattles[roomId] diff --git a/toontown/coghq/CrateGlobals.py b/toontown/coghq/CrateGlobals.py new file mode 100644 index 0000000..20b4efc --- /dev/null +++ b/toontown/coghq/CrateGlobals.py @@ -0,0 +1,23 @@ +from pandac.PandaModules import * +CRATE_CLEAR = 0 +CRATE_POWERUP = 1 +CRATE_PUSH = 2 +CrateNormals = [Vec3(1, 0, 0), + Vec3(-1, 0, 0), + Vec3(0, 1, 0), + Vec3(0, -1, 0)] +CrateHprs = [Vec3(90, 0, 0), + Vec3(270, 0, 0), + Vec3(180, 0, 0), + Vec3(0, 0, 0)] +T_PUSH = 1.5 +T_PAUSE = 0.1 +TorsoToOffset = {'ss': 0.17, + 'ms': 0.18, + 'ls': 0.75, + 'sd': 0.17, + 'md': 0.18, + 'ld': 0.75, + 's': 0.17, + 'm': 0.18, + 'l': 0.75} diff --git a/toontown/coghq/CrusherCell.py b/toontown/coghq/CrusherCell.py new file mode 100644 index 0000000..36a0ee8 --- /dev/null +++ b/toontown/coghq/CrusherCell.py @@ -0,0 +1,8 @@ +import ActiveCell +from direct.directnotify import DirectNotifyGlobal + +class CrusherCell(ActiveCell.ActiveCell): + notify = DirectNotifyGlobal.directNotify.newCategory('CrusherCell') + + def __init__(self, cr): + ActiveCell.ActiveCell.__init__(self, cr) diff --git a/toontown/coghq/CrusherCellAI.py b/toontown/coghq/CrusherCellAI.py new file mode 100644 index 0000000..ea86425 --- /dev/null +++ b/toontown/coghq/CrusherCellAI.py @@ -0,0 +1,63 @@ +import ActiveCellAI +from direct.directnotify import DirectNotifyGlobal + +class CrusherCellAI(ActiveCellAI.ActiveCellAI): + notify = DirectNotifyGlobal.directNotify.newCategory('CrusherCellAI') + + def __init__(self, level, entId): + ActiveCellAI.ActiveCellAI.__init__(self, level, entId) + self.crushers = [] + self.crushables = [] + + def destroy(self): + self.notify.info('destroy entity %s' % self.entId) + for entId in self.crushers: + self.unregisterCrusher(entId) + + ActiveCellAI.ActiveCellAI.destroy(self) + + def registerCrusher(self, entId): + if entId not in self.crushers: + ent = self.level.entities.get(entId, None) + if ent: + self.crushers.append(entId) + self.accept(ent.crushMsg, self.doCrush) + return + + def unregisterCrusher(self, entId): + if entId in self.crushers: + self.crushers.remove(entId) + if not hasattr(self, 'level'): + self.notify.error("unregisterCrusher(%s): CrusherCellAI %s has no attrib 'level'" % (entId, self.entId)) + ent = self.level.entities.get(entId, None) + if ent: + self.ignore(ent.crushMsg) + return + + def registerCrushable(self, entId): + if entId not in self.crushables: + self.crushables.append(entId) + + def unregisterCrushable(self, entId): + if entId in self.crushables: + self.crushables.remove(entId) + + def doCrush(self, crusherId, axis): + self.notify.debug('doCrush %s' % crusherId) + for occupantId in self.occupantIds: + if occupantId in self.crushables: + crushObj = self.level.entities.get(occupantId, None) + if crushObj: + crushObj.doCrush(crusherId, axis) + else: + self.notify.warning("couldn't find crushable object %d" % self.occupantId) + + return + + def updateCrushables(self): + for id in self.crushables: + crushable = self.level.entities.get(id, None) + if crushable: + crushable.updateGrid() + + return diff --git a/toontown/coghq/DinerStatusIndicator.py b/toontown/coghq/DinerStatusIndicator.py new file mode 100644 index 0000000..9715d16 --- /dev/null +++ b/toontown/coghq/DinerStatusIndicator.py @@ -0,0 +1,113 @@ +from pandac.PandaModules import NodePath, BillboardEffect, Vec3, Point3, TextureStage, TransparencyAttrib, DecalEffect, VBase4 +from direct.fsm import FSM +from direct.gui.DirectGui import DirectFrame, DGG +from direct.interval.IntervalGlobal import LerpScaleInterval, LerpColorScaleInterval, Parallel, Sequence, Wait + +class DinerStatusIndicator(NodePath.NodePath, FSM.FSM): + + def __init__(self, parent, pos = None, scale = None): + NodePath.NodePath.__init__(self, 'DinerStatusIndicator') + if parent: + self.reparentTo(parent) + if pos: + self.setPos(pos) + if scale: + self.setScale(scale) + self.loadAssets() + FSM.FSM.__init__(self, 'DinerStatusIndicator') + self.activeIval = None + return + + def delete(self): + if self.activeIval: + self.activeIval.pause() + self.activeIval = None + self.angryIcon.removeNode() + self.hungryIcon.removeNode() + self.eatingIcon.removeNode() + self.removeNode() + return + + def loadAssets(self): + iconsFile = loader.loadModel('phase_12/models/bossbotHQ/BanquetIcons') + self.angryIcon, self.angryMeter = self.loadIcon(iconsFile, '**/Anger') + self.hungryIcon, self.hungryMeter = self.loadIcon(iconsFile, '**/Hunger') + self.eatingIcon, self.eatingMeter = self.loadIcon(iconsFile, '**/Food') + self.angryMeter.hide() + iconsFile.removeNode() + + def loadIcon(self, iconsFile, name): + retVal = iconsFile.find(name) + retVal.setBillboardAxis() + retVal.reparentTo(self) + dark = retVal.copyTo(NodePath()) + dark.reparentTo(retVal) + dark.setColor(0.5, 0.5, 0.5, 1) + retVal.setEffect(DecalEffect.make()) + retVal.setTransparency(TransparencyAttrib.MAlpha, 1) + ll, ur = dark.getTightBounds() + center = retVal.attachNewNode('center') + center.setPos(0, 0, ll[2]) + dark.wrtReparentTo(center) + dark.setTexProjector(TextureStage.getDefault(), center, retVal) + retVal.hide() + return (retVal, center) + + def enterEating(self, timeToFinishFood): + self.eatingIcon.show() + self.activeIval = self.createMeterInterval(self.eatingIcon, self.eatingMeter, timeToFinishFood) + self.activeIval.start() + + def exitEating(self): + if self.activeIval: + self.activeIval.finish() + self.activeIval = None + self.eatingIcon.hide() + return + + def enterHungry(self, timeToFinishFood): + self.hungryIcon.show() + self.activeIval = self.createMeterInterval(self.hungryIcon, self.hungryMeter, timeToFinishFood) + self.activeIval.start() + + def exitHungry(self): + if self.activeIval: + self.activeIval.finish() + self.activeIval = None + self.hungryIcon.hide() + return + + def enterAngry(self): + self.angryIcon.show() + + def exitAngry(self): + self.angryIcon.hide() + if self.activeIval: + self.activeIval.finish() + self.activeIval = None + return + + def enterDead(self): + pass + + def exitDead(self): + pass + + def enterInactive(self): + pass + + def exitInactive(self): + pass + + def createMeterInterval(self, icon, meter, time): + ivalDarkness = LerpScaleInterval(meter, time, scale=Vec3(1, 1, 1), startScale=Vec3(1, 0.001, 0.001)) + flashingTrack = Sequence() + flashDuration = 10 + if time > flashDuration: + flashingTrack.append(Wait(time - flashDuration)) + for i in xrange(10): + flashingTrack.append(Parallel(LerpColorScaleInterval(icon, 0.5, VBase4(1, 0, 0, 1)), icon.scaleInterval(0.5, 1.25))) + flashingTrack.append(Parallel(LerpColorScaleInterval(icon, 0.5, VBase4(1, 1, 1, 1)), icon.scaleInterval(0.5, 1))) + + retIval = Parallel(ivalDarkness, flashingTrack) + return retIval diff --git a/toontown/coghq/DirectionalCell.py b/toontown/coghq/DirectionalCell.py new file mode 100644 index 0000000..33574da --- /dev/null +++ b/toontown/coghq/DirectionalCell.py @@ -0,0 +1,8 @@ +import ActiveCell +from direct.directnotify import DirectNotifyGlobal + +class DirectionalCell(ActiveCell.ActiveCell): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectionalCell') + + def __init__(self, cr): + ActiveCell.ActiveCell.__init__(self, cr) diff --git a/toontown/coghq/DirectionalCellAI.py b/toontown/coghq/DirectionalCellAI.py new file mode 100644 index 0000000..5a6220f --- /dev/null +++ b/toontown/coghq/DirectionalCellAI.py @@ -0,0 +1,42 @@ +from direct.directnotify import DirectNotifyGlobal +import ActiveCellAI, CrateGlobals +from direct.task import Task + +class DirectionalCellAI(ActiveCellAI.ActiveCellAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectionalCellAI') + + def __init__(self, level, entId): + self.dir = [ + 0, 0] + ActiveCellAI.ActiveCellAI.__init__(self, level, entId) + self.moveTrack = None + return + + def delete(self): + if self.moveTrack: + self.moveTrack.pause() + del self.moveTrack + self.moveTrack = None + taskMgr.remove(self.taskName('moveTask')) + return + + def setState(self, state, objId=None): + ActiveCellAI.ActiveCellAI.setState(self, state, objId) + self.startMoveTask() + + def taskName(self, name): + return self.level.taskName(name) + '-' + str(self.entId) + + def startMoveTask(self): + taskMgr.remove(self.taskName('moveTask')) + taskMgr.doMethodLater(CrateGlobals.T_PUSH + CrateGlobals.T_PAUSE, self.moveTask, self.taskName('moveTask')) + + def moveTask(self, task): + oldPos = self.grid.getObjPos(self.occupantId) + if self.grid.doMove(self.occupantId, self.dir[0], self.dir[1]): + newPos = self.grid.getObjPos(self.occupantId) + crate = simbase.air.doId2do.get(self.occupantId) + if crate: + crate.sendUpdate('setMoveTo', [ + oldPos[0], oldPos[1], oldPos[2], newPos[0], newPos[1], newPos[2]]) + return Task.done diff --git a/toontown/coghq/DistributedBanquetTable.py b/toontown/coghq/DistributedBanquetTable.py new file mode 100644 index 0000000..a87dab3 --- /dev/null +++ b/toontown/coghq/DistributedBanquetTable.py @@ -0,0 +1,1144 @@ +import math +import random +from pandac.PandaModules import NodePath, Point3, VBase4, TextNode, Vec3, deg2Rad, CollisionSegment, CollisionHandlerQueue, CollisionNode, BitMask32, SmoothMover +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import globalClockDelta +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, ProjectileInterval, Parallel, LerpHprInterval, ActorInterval, Func, Wait, SoundInterval, LerpPosHprInterval, LerpScaleInterval +from direct.gui.DirectGui import DGG, DirectButton, DirectLabel, DirectWaitBar +from direct.task import Task +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.coghq import BanquetTableBase +from toontown.coghq import DinerStatusIndicator +from toontown.battle import MovieUtil + +class DistributedBanquetTable(DistributedObject.DistributedObject, FSM.FSM, BanquetTableBase.BanquetTableBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBanquetTable') + rotationsPerSeatIndex = [90, + 90, + 0, + 0, + -90, + -90, + 180, + 180] + pitcherMinH = -360 + pitcherMaxH = 360 + rotateSpeed = 30 + waterPowerSpeed = base.config.GetDouble('water-power-speed', 15) + waterPowerExponent = base.config.GetDouble('water-power-exponent', 0.75) + useNewAnimations = True + TugOfWarControls = False + OnlyUpArrow = True + if OnlyUpArrow: + BASELINE_KEY_RATE = 3 + else: + BASELINE_KEY_RATE = 6 + UPDATE_KEY_PRESS_RATE_TASK = 'BanquetTableUpdateKeyPressRateTask' + YELLOW_POWER_THRESHOLD = 0.75 + RED_POWER_THRESHOLD = 0.97 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedBanquetTable') + self.boss = None + self.index = -1 + self.diners = {} + self.dinerStatus = {} + self.serviceLocs = {} + self.chairLocators = {} + self.sitLocators = {} + self.activeIntervals = {} + self.dinerStatusIndicators = {} + self.preparedForPhaseFour = False + self.avId = 0 + self.toon = None + self.pitcherSmoother = SmoothMover() + self.pitcherSmoother.setSmoothMode(SmoothMover.SMOn) + self.smoothStarted = 0 + self.__broadcastPeriod = 0.2 + self.changeSeq = 0 + self.lastChangeSeq = 0 + self.pitcherAdviceLabel = None + self.fireLength = 250 + self.fireTrack = None + self.hitObject = None + self.setupPowerBar() + self.aimStart = None + self.toonPitcherPosition = Point3(0, -2, 0) + self.allowLocalRequestControl = True + self.fadeTrack = None + self.grabTrack = None + self.gotHitByBoss = False + self.keyTTL = [] + self.keyRate = 0 + self.buttons = [0, 1] + self.lastPowerFired = 0 + self.moveSound = None + self.releaseTrack = None + return + + def disable(self): + DistributedObject.DistributedObject.disable(self) + taskMgr.remove(self.triggerName) + taskMgr.remove(self.smoothName) + taskMgr.remove(self.watchControlsName) + taskMgr.remove(self.pitcherAdviceName) + taskMgr.remove(self.posHprBroadcastName) + taskMgr.remove(self.waterPowerTaskName) + if self.releaseTrack: + self.releaseTrack.finish() + self.releaseTrack = None + if self.fireTrack: + self.fireTrack.finish() + self.fireTrack = None + self.cleanupIntervals() + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.boss = None + self.ignoreAll() + for indicator in self.dinerStatusIndicators.values(): + indicator.delete() + + self.dinerStatusIndicators = {} + for diner in self.diners.values(): + diner.delete() + + self.diners = {} + self.powerBar.destroy() + self.powerBar = None + self.pitcherMoveSfx.stop() + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.loadAssets() + self.smoothName = self.uniqueName('pitcherSmooth') + self.pitcherAdviceName = self.uniqueName('pitcherAdvice') + self.posHprBroadcastName = self.uniqueName('pitcherBroadcast') + self.waterPowerTaskName = self.uniqueName('updateWaterPower') + self.triggerName = self.uniqueName('trigger') + self.watchControlsName = self.uniqueName('watchControls') + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + self.boss.setTable(self, self.index) + + def setIndex(self, index): + self.index = index + + def setState(self, state, avId, extraInfo): + self.gotHitByBoss = extraInfo + if state == 'F': + self.demand('Off') + elif state == 'N': + self.demand('On') + elif state == 'I': + self.demand('Inactive') + elif state == 'R': + self.demand('Free') + elif state == 'C': + self.demand('Controlled', avId) + elif state == 'L': + self.demand('Flat', avId) + else: + self.notify.error('Invalid state from AI: %s' % state) + + def setNumDiners(self, numDiners): + self.numDiners = numDiners + + def setDinerInfo(self, hungryDurations, eatingDurations, dinerLevels): + self.dinerInfo = {} + for i in xrange(len(hungryDurations)): + hungryDur = hungryDurations[i] + eatingDur = eatingDurations[i] + dinerLevel = dinerLevels[i] + self.dinerInfo[i] = (hungryDur, eatingDur, dinerLevel) + + def loadAssets(self): + self.tableGroup = loader.loadModel('phase_12/models/bossbotHQ/BanquetTableChairs') + tableLocator = self.boss.geom.find('**/TableLocator_%d' % (self.index + 1)) + if tableLocator.isEmpty(): + self.tableGroup.reparentTo(render) + self.tableGroup.setPos(0, 75, 0) + else: + self.tableGroup.reparentTo(tableLocator) + self.tableGeom = self.tableGroup.find('**/Geometry') + self.setupDiners() + self.setupChairCols() + self.squirtSfx = loader.loadSfx('phase_4/audio/sfx/AA_squirt_seltzer_miss.mp3') + self.hitBossSfx = loader.loadSfx('phase_5/audio/sfx/SA_watercooler_spray_only.mp3') + self.hitBossSoundInterval = SoundInterval(self.hitBossSfx, node=self.boss, volume=1.0) + self.serveFoodSfx = loader.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_bell_for_trolley.mp3') + self.pitcherMoveSfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3') + + def setupDiners(self): + for i in xrange(self.numDiners): + newDiner = self.createDiner(i) + self.diners[i] = newDiner + self.dinerStatus[i] = self.HUNGRY + + def createDiner(self, i): + diner = Suit.Suit() + diner.dna = SuitDNA.SuitDNA() + level = self.dinerInfo[i][2] + level -= 4 + diner.dna.newSuitRandom(level=level, dept='c') + diner.setDNA(diner.dna) + if self.useNewAnimations: + diner.loop('sit', fromFrame=i) + else: + diner.pose('landing', 0) + locator = self.tableGroup.find('**/chair_%d' % (i + 1)) + locatorScale = locator.getNetTransform().getScale()[0] + correctHeadingNp = locator.attachNewNode('correctHeading') + self.chairLocators[i] = correctHeadingNp + heading = self.rotationsPerSeatIndex[i] + correctHeadingNp.setH(heading) + sitLocator = correctHeadingNp.attachNewNode('sitLocator') + base.sitLocator = sitLocator + pos = correctHeadingNp.getPos(render) + if SuitDNA.getSuitBodyType(diner.dna.name) == 'c': + sitLocator.setPos(0.5, 3.65, -3.75) + else: + sitLocator.setZ(-2.4) + sitLocator.setY(2.5) + sitLocator.setX(0.5) + self.sitLocators[i] = sitLocator + diner.setScale(1.0 / locatorScale) + diner.reparentTo(sitLocator) + newLoc = NodePath('serviceLoc-%d-%d' % (self.index, i)) + newLoc.reparentTo(correctHeadingNp) + newLoc.setPos(0, 3.0, 1) + self.serviceLocs[i] = newLoc + base.serviceLoc = newLoc + head = diner.find('**/joint_head') + newIndicator = DinerStatusIndicator.DinerStatusIndicator(parent=head, pos=Point3(0, 0, 3.5), scale=5.0) + newIndicator.wrtReparentTo(diner) + self.dinerStatusIndicators[i] = newIndicator + return diner + + def setupChairCols(self): + for i in xrange(self.numDiners): + chairCol = self.tableGroup.find('**/collision_chair_%d' % (i + 1)) + colName = 'ChairCol-%d-%d' % (self.index, i) + chairCol.setTag('chairIndex', str(i)) + chairCol.setName(colName) + chairCol.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enter' + colName, self.touchedChair) + + def touchedChair(self, colEntry): + chairIndex = int(colEntry.getIntoNodePath().getTag('chairIndex')) + if chairIndex in self.dinerStatus: + status = self.dinerStatus[chairIndex] + if status in (self.HUNGRY, self.ANGRY): + self.boss.localToonTouchedChair(self.index, chairIndex) + + def serveFood(self, food, chairIndex): + self.removeFoodModel(chairIndex) + serviceLoc = self.serviceLocs.get(chairIndex) + if not food or food.isEmpty(): + foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood') + foodModel.setScale(ToontownGlobals.BossbotFoodModelScale) + foodModel.reparentTo(serviceLoc) + else: + food.wrtReparentTo(serviceLoc) + tray = food.find('**/tray') + if not tray.isEmpty(): + tray.hide() + ivalDuration = 1.5 + foodMoveIval = Parallel(SoundInterval(self.serveFoodSfx, node=food), ProjectileInterval(food, duration=ivalDuration, startPos=food.getPos(serviceLoc), endPos=serviceLoc.getPos(serviceLoc)), LerpHprInterval(food, ivalDuration, Point3(0, -360, 0))) + intervalName = 'serveFood-%d-%d' % (self.index, chairIndex) + foodMoveIval.start() + self.activeIntervals[intervalName] = foodMoveIval + + def setDinerStatus(self, chairIndex, status): + if chairIndex in self.dinerStatus: + oldStatus = self.dinerStatus[chairIndex] + self.dinerStatus[chairIndex] = status + if oldStatus != status: + if status == self.EATING: + self.changeDinerToEating(chairIndex) + elif status == self.HUNGRY: + self.changeDinerToHungry(chairIndex) + elif status == self.ANGRY: + self.changeDinerToAngry(chairIndex) + elif status == self.DEAD: + self.changeDinerToDead(chairIndex) + elif status == self.HIDDEN: + self.changeDinerToHidden(chairIndex) + + def removeFoodModel(self, chairIndex): + serviceLoc = self.serviceLocs.get(chairIndex) + if serviceLoc: + for i in xrange(serviceLoc.getNumChildren()): + serviceLoc.getChild(0).removeNode() + + def changeDinerToEating(self, chairIndex): + indicator = self.dinerStatusIndicators.get(chairIndex) + eatingDuration = self.dinerInfo[chairIndex][1] + if indicator: + indicator.request('Eating', eatingDuration) + diner = self.diners[chairIndex] + intervalName = 'eating-%d-%d' % (self.index, chairIndex) + eatInTime = 32.0 / 24.0 + eatOutTime = 21.0 / 24.0 + eatLoopTime = 19 / 24.0 + rightHand = diner.getRightHand() + waitTime = 5 + loopDuration = eatingDuration - eatInTime - eatOutTime - waitTime + serviceLoc = self.serviceLocs[chairIndex] + + def foodAttach(self = self, diner = diner): + foodModel = self.serviceLocs[chairIndex].getChild(0) + (foodModel.reparentTo(diner.getRightHand()),) + (foodModel.setHpr(Point3(0, -94, 0)),) + (foodModel.setPos(Point3(-0.15, -0.7, -0.4)),) + scaleAdj = 1 + if SuitDNA.getSuitBodyType(diner.dna.name) == 'c': + scaleAdj = 0.6 + (foodModel.setPos(Point3(0.1, -0.25, -0.31)),) + else: + scaleAdj = 0.8 + (foodModel.setPos(Point3(-0.25, -0.85, -0.34)),) + oldScale = foodModel.getScale() + newScale = oldScale * scaleAdj + foodModel.setScale(newScale) + + def foodDetach(self = self, diner = diner): + foodModel = diner.getRightHand().getChild(0) + (foodModel.reparentTo(serviceLoc),) + (foodModel.setPosHpr(0, 0, 0, 0, 0, 0),) + scaleAdj = 1 + if SuitDNA.getSuitBodyType(diner.dna.name) == 'c': + scaleAdj = 0.6 + else: + scakeAdj = 0.8 + oldScale = foodModel.getScale() + newScale = oldScale / scaleAdj + foodModel.setScale(newScale) + + eatIval = Sequence(ActorInterval(diner, 'sit', duration=waitTime), ActorInterval(diner, 'sit-eat-in', startFrame=0, endFrame=6), Func(foodAttach), ActorInterval(diner, 'sit-eat-in', startFrame=6, endFrame=32), ActorInterval(diner, 'sit-eat-loop', duration=loopDuration, loop=1), ActorInterval(diner, 'sit-eat-out', startFrame=0, endFrame=12), Func(foodDetach), ActorInterval(diner, 'sit-eat-out', startFrame=12, endFrame=21)) + eatIval.start() + self.activeIntervals[intervalName] = eatIval + + def changeDinerToHungry(self, chairIndex): + intervalName = 'eating-%d-%d' % (self.index, chairIndex) + if intervalName in self.activeIntervals: + self.activeIntervals[intervalName].finish() + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Hungry', self.dinerInfo[chairIndex][0]) + diner = self.diners[chairIndex] + if random.choice([0, 1]): + diner.loop('sit-hungry-left') + else: + diner.loop('sit-hungry-right') + + def changeDinerToAngry(self, chairIndex): + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Angry') + diner = self.diners[chairIndex] + diner.loop('sit-angry') + + def changeDinerToDead(self, chairIndex): + + def removeDeathSuit(suit, deathSuit): + if not deathSuit.isEmpty(): + deathSuit.detachNode() + suit.cleanupLoseActor() + + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Dead') + diner = self.diners[chairIndex] + deathSuit = diner + locator = self.tableGroup.find('**/chair_%d' % (chairIndex + 1)) + deathSuit = diner.getLoseActor() + ival = Sequence(Func(self.notify.debug, 'before actorinterval sit-lose'), ActorInterval(diner, 'sit-lose'), Func(self.notify.debug, 'before deathSuit.setHpr'), Func(deathSuit.setHpr, diner.getHpr()), Func(self.notify.debug, 'before diner.hide'), Func(diner.hide), Func(self.notify.debug, 'before deathSuit.reparentTo'), Func(deathSuit.reparentTo, self.chairLocators[chairIndex]), Func(self.notify.debug, 'befor ActorInterval lose'), ActorInterval(deathSuit, 'lose', duration=MovieUtil.SUIT_LOSE_DURATION), Func(self.notify.debug, 'before remove deathsuit'), Func(removeDeathSuit, diner, deathSuit, name='remove-death-suit-%d-%d' % (chairIndex, self.index)), Func(self.notify.debug, 'diner.stash'), Func(diner.stash)) + 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)) + intervalName = 'dinerDie-%d-%d' % (self.index, chairIndex) + deathIval = Parallel(ival, deathSoundTrack) + deathIval.start() + self.activeIntervals[intervalName] = deathIval + + def changeDinerToHidden(self, chairIndex): + self.removeFoodModel(chairIndex) + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Inactive') + diner = self.diners[chairIndex] + diner.hide() + + def setAllDinersToSitNeutral(self): + startFrame = 0 + for diner in self.diners.values(): + if not diner.isHidden(): + diner.loop('sit', fromFrame=startFrame) + startFrame += 1 + + def cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + + self.activeIntervals = {} + + def clearInterval(self, name, finish = 1): + if self.activeIntervals.has_key(name): + ival = self.activeIntervals[name] + if finish: + ival.finish() + else: + ival.pause() + 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 getNotDeadInfo(self): + notDeadList = [] + for i in xrange(self.numDiners): + if self.dinerStatus[i] != self.DEAD: + notDeadList.append((self.index, i, 12)) + + return notDeadList + + def enterOn(self): + pass + + def exitOn(self): + pass + + def enterInactive(self): + for chairIndex in xrange(self.numDiners): + indicator = self.dinerStatusIndicators.get(chairIndex) + if indicator: + indicator.request('Inactive') + self.removeFoodModel(chairIndex) + + def exitInactive(self): + pass + + def enterFree(self): + self.resetPowerBar() + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.prepareForPhaseFour() + if self.avId == localAvatar.doId: + self.tableGroup.setAlphaScale(0.3) + self.tableGroup.setTransparency(1) + taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName) + self.fadeTrack = Sequence(Func(self.tableGroup.setTransparency, 1), self.tableGroup.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3))) + self.fadeTrack.start() + self.allowLocalRequestControl = False + else: + self.allowLocalRequestControl = True + self.avId = 0 + return + + def exitFree(self): + pass + + def touchedTable(self, colEntry): + tableIndex = int(colEntry.getIntoNodePath().getTag('tableIndex')) + if self.state == 'Free' and self.avId == 0 and self.allowLocalRequestControl: + self.d_requestControl() + + def prepareForPhaseFour(self): + if not self.preparedForPhaseFour: + for i in xrange(8): + chair = self.tableGroup.find('**/chair_%d' % (i + 1)) + if not chair.isEmpty(): + chair.hide() + colChairs = self.tableGroup.findAllMatches('**/ChairCol*') + for i in xrange(colChairs.getNumPaths()): + col = colChairs.getPath(i) + col.stash() + + colChairs = self.tableGroup.findAllMatches('**/collision_chair*') + for i in xrange(colChairs.getNumPaths()): + col = colChairs.getPath(i) + col.stash() + + tableCol = self.tableGroup.find('**/collision_table') + colName = 'TableCol-%d' % self.index + tableCol.setTag('tableIndex', str(self.index)) + tableCol.setName(colName) + tableCol.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.BanquetTableBitmask) + self.accept('enter' + colName, self.touchedTable) + self.preparedForPhaseFour = True + self.waterPitcherModel = loader.loadModel('phase_12/models/bossbotHQ/tt_m_ara_bhq_seltzerBottle') + lampNode = self.tableGroup.find('**/lamp_med_5') + pos = lampNode.getPos(self.tableGroup) + lampNode.hide() + bottleLocator = self.tableGroup.find('**/bottle_locator') + pos = bottleLocator.getPos(self.tableGroup) + self.waterPitcherNode = self.tableGroup.attachNewNode('pitcherNode') + self.waterPitcherNode.setPos(pos) + self.waterPitcherModel.reparentTo(self.waterPitcherNode) + self.waterPitcherModel.ls() + self.nozzle = self.waterPitcherModel.find('**/nozzle_tip') + self.handLocator = self.waterPitcherModel.find('**/hand_locator') + self.handPos = self.handLocator.getPos() + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestFree(self, gotHitByBoss): + self.sendUpdate('requestFree', [gotHitByBoss]) + + def enterControlled(self, avId): + self.prepareForPhaseFour() + self.avId = avId + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.toon = toon + self.grabTrack = self.makeToonGrabInterval(toon) + self.notify.debug('grabTrack=%s' % self.grabTrack) + self.pitcherCamPos = Point3(0, -50, 40) + self.pitcherCamHpr = Point3(0, -21, 0) + if avId == localAvatar.doId: + self.boss.toMovieMode() + self.__enableControlInterface() + self.startPosHprBroadcast() + self.grabTrack = Sequence(self.grabTrack, Func(camera.wrtReparentTo, localAvatar), LerpPosHprInterval(camera, 1, self.pitcherCamPos, self.pitcherCamHpr), Func(self.boss.toCraneMode)) + if self.TugOfWarControls: + self.__spawnUpdateKeyPressRateTask() + self.accept('exitCrane', self.gotBossZapped) + else: + self.startSmooth() + toon.stopSmooth() + self.grabTrack.start() + + def exitControlled(self): + self.ignore('exitCrane') + if self.grabTrack: + self.grabTrack.finish() + self.grabTrack = None + nextState = self.getCurrentOrNextState() + self.notify.debug('nextState=%s' % nextState) + if nextState == 'Flat': + place = base.cr.playGame.getPlace() + self.notify.debug('%s' % place.fsm) + if self.avId == localAvatar.doId: + self.__disableControlInterface() + else: + if self.toon and not self.toon.isDisabled(): + self.toon.loop('neutral') + self.toon.startSmooth() + self.releaseTrack = self.makeToonReleaseInterval(self.toon) + self.stopPosHprBroadcast() + self.stopSmooth() + if self.avId == localAvatar.doId: + localAvatar.wrtReparentTo(render) + self.__disableControlInterface() + camera.reparentTo(base.localAvatar) + camera.setPos(base.localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.goToFinalBattle() + self.safeBossToFinalBattleMode() + else: + toon = base.cr.doId2do.get(self.avId) + if toon: + toon.wrtReparentTo(render) + self.releaseTrack.start() + return + + def safeBossToFinalBattleMode(self): + if self.boss: + self.boss.toFinalBattleMode() + + def goToFinalBattle(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + if place.fsm.getCurrentState().getName() == 'crane': + place.setState('finalBattle') + + def makeToonGrabInterval(self, toon): + toon.pose('leverNeutral', 0) + toon.update() + rightHandPos = toon.rightHand.getPos(toon) + self.toonPitcherPosition = Point3(self.handPos[0] - rightHandPos[0], self.handPos[1] - rightHandPos[1], 0) + destZScale = rightHandPos[2] / self.handPos[2] + grabIval = Sequence(Func(toon.wrtReparentTo, self.waterPitcherNode), Func(toon.loop, 'neutral'), Parallel(ActorInterval(toon, 'jump'), Sequence(Wait(0.43), Parallel(ProjectileInterval(toon, duration=0.9, startPos=toon.getPos(self.waterPitcherNode), endPos=self.toonPitcherPosition), LerpHprInterval(toon, 0.9, Point3(0, 0, 0)), LerpScaleInterval(self.waterPitcherModel, 0.9, Point3(1, 1, destZScale))))), Func(toon.setPos, self.toonPitcherPosition), Func(toon.loop, 'leverNeutral')) + return grabIval + + def makeToonReleaseInterval(self, toon): + temp1 = self.waterPitcherNode.attachNewNode('temp1') + temp1.setPos(self.toonPitcherPosition) + temp2 = self.waterPitcherNode.attachNewNode('temp2') + temp2.setPos(0, -10, -self.waterPitcherNode.getZ()) + startPos = temp1.getPos(render) + endPos = temp2.getPos(render) + temp1.removeNode() + temp2.removeNode() + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -10, 0)) + + if self.gotHitByBoss: + self.notify.debug('creating zap interval instead') + grabIval = Sequence(Func(toon.loop, 'neutral'), Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1))) + else: + grabIval = Sequence(Func(toon.loop, 'neutral'), Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'jump'), Sequence(Wait(0.43), ProjectileInterval(toon, duration=0.9, startPos=startPos, endPos=endPos)))) + return grabIval + + def b_clearSmoothing(self): + self.d_clearSmoothing() + self.clearSmoothing() + + def d_clearSmoothing(self): + self.sendUpdate('clearSmoothing', [0]) + + def clearSmoothing(self, bogus = None): + self.pitcherSmoother.clearPositions(1) + + def doSmoothTask(self, task): + self.pitcherSmoother.computeAndApplySmoothHpr(self.waterPitcherNode) + 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 __enableControlInterface(self): + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=2, text=TTLocalizer.BossbotPitcherLeave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(1.05, 0, -0.82), command=self.__exitPitcher) + self.accept('escape', self.__exitPitcher) + 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) + self.accept('arrow_up', self.__upArrowKeyPressed) + self.accept('arrow_down', self.__downArrowKeyPressed) + taskMgr.add(self.__watchControls, self.watchControlsName) + taskMgr.doMethodLater(5, self.__displayPitcherAdvice, self.pitcherAdviceName) + self.arrowVert = 0 + self.arrowHorz = 0 + self.powerBar.show() + return + + def __disableControlInterface(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = None + self.__cleanupPitcherAdvice() + 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.ignore('arrow_up') + self.ignore('arrow_down') + self.arrowVert = 0 + self.arrowHorz = 0 + taskMgr.remove(self.watchControlsName) + taskMgr.remove(self.waterPowerTaskName) + self.resetPowerBar() + self.aimStart = None + self.powerBar.hide() + if self.TugOfWarControls: + self.__killUpdateKeyPressRateTask() + self.keyTTL = [] + self.__setMoveSound(None) + return + + def __displayPitcherAdvice(self, task): + if self.pitcherAdviceLabel == None: + self.pitcherAdviceLabel = DirectLabel(text=TTLocalizer.BossbotPitcherAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1) + return + + def __cleanupPitcherAdvice(self): + if self.pitcherAdviceLabel: + self.pitcherAdviceLabel.destroy() + self.pitcherAdviceLabel = None + taskMgr.remove(self.pitcherAdviceName) + return + + def showExiting(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = DirectLabel(relief=None, text=TTLocalizer.BossbotPitcherLeaving, pos=(1.05, 0, -0.88), text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1)) + self.__cleanupPitcherAdvice() + return + + def __exitPitcher(self): + self.showExiting() + self.d_requestFree(False) + + def __controlPressed(self): + self.__cleanupPitcherAdvice() + if self.TugOfWarControls: + if self.power: + self.aimStart = 1 + self.__endFireWater() + elif self.state == 'Controlled': + self.__beginFireWater() + + def __controlReleased(self): + if self.TugOfWarControls: + pass + elif self.state == 'Controlled': + self.__endFireWater() + + def __upArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowHorz = 1 + elif self.arrowHorz > 0: + self.arrowHorz = 0 + + def __leftArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupPitcherAdvice() + if pressed: + self.arrowHorz = -1 + elif self.arrowHorz < 0: + self.arrowHorz = 0 + + def __incrementChangeSeq(self): + self.changeSeq = self.changeSeq + 1 & 255 + + def stopPosHprBroadcast(self): + taskName = self.posHprBroadcastName + taskMgr.remove(taskName) + + def startPosHprBroadcast(self): + taskName = self.posHprBroadcastName + self.b_clearSmoothing() + self.d_sendPitcherPos() + taskMgr.remove(taskName) + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + + def __posHprBroadcast(self, task): + self.d_sendPitcherPos() + taskName = self.posHprBroadcastName + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + return Task.done + + def d_sendPitcherPos(self): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setPitcherPos', [self.changeSeq, self.waterPitcherNode.getH(), timestamp]) + + def setPitcherPos(self, changeSeq, h, timestamp): + self.changeSeq = changeSeq + if self.smoothStarted: + now = globalClock.getFrameTime() + local = globalClockDelta.networkToLocalTime(timestamp, now) + self.pitcherSmoother.setH(h) + self.pitcherSmoother.setTimestamp(local) + self.pitcherSmoother.markPosition() + else: + self.waterPitcherNode.setH(h) + + def __watchControls(self, task): + if self.arrowHorz: + self.__movePitcher(self.arrowHorz) + else: + self.__setMoveSound(None) + return Task.cont + + def __movePitcher(self, xd): + dt = globalClock.getDt() + h = self.waterPitcherNode.getH() - xd * self.rotateSpeed * dt + h %= 360 + self.notify.debug('rotSpeed=%.2f curH=%.2f xd =%.2f, dt = %.2f, h=%.2f' % (self.rotateSpeed, + self.waterPitcherNode.getH(), + xd, + dt, + h)) + limitH = h + self.waterPitcherNode.setH(limitH) + if xd: + self.__setMoveSound(self.pitcherMoveSfx) + + def reloadPosition(self): + self.pitcherSmoother.clearPositions(0) + self.pitcherSmoother.setHpr(self.waterPitcherNode.getHpr()) + self.pitcherSmoother.setPhonyTimestamp() + + def forceToTruePosition(self): + if self.pitcherSmoother.getLatestPosition(): + self.pitcherSmoother.applySmoothHpr(self.waterPitcherNode) + self.pitcherSmoother.clearPositions(1) + + def getSprayTrack(self, color, origin, target, dScaleUp, dHold, dScaleDown, horizScale = 1.0, vertScale = 1.0, parent = render): + track = Sequence() + SPRAY_LEN = 1.5 + sprayProp = MovieUtil.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(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=Point3(0.01, 0.01, 0.01))) + track.append(Func(self.checkHitObject)) + 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, Point3(0.01, 0.01, 0.01))) + + def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool): + sprayProp.detachNode() + MovieUtil.removeProp(sprayProp) + sprayRot.removeNode() + sprayScale.removeNode() + + track.append(Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, MovieUtil.globalPropPool)) + return track + + def checkHitObject(self): + if not self.hitObject: + return + if self.avId != base.localAvatar.doId: + return + tag = self.hitObject.getNetTag('pieCode') + pieCode = int(tag) + if pieCode == ToontownGlobals.PieCodeBossCog: + self.hitBossSoundInterval.start() + self.sendUpdate('waterHitBoss', [self.index]) + if self.TugOfWarControls: + damage = 1 + if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD: + damage = 1 + elif self.lastPowerFired < self.RED_POWER_THRESHOLD: + damage = 2 + else: + damage = 3 + self.boss.d_hitBoss(damage) + else: + damage = 1 + if self.lastPowerFired < self.YELLOW_POWER_THRESHOLD: + damage = 1 + elif self.lastPowerFired < self.RED_POWER_THRESHOLD: + damage = 2 + else: + damage = 3 + self.boss.d_hitBoss(damage) + + def waterHitBoss(self, tableIndex): + if self.index == tableIndex: + self.hitBossSoundInterval.start() + + def setupPowerBar(self): + self.powerBar = DirectWaitBar(pos=(0.0, 0, -0.94), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=1, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(0.75, 0.75, 1.0, 0.8), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar.hide() + + def resetPowerBar(self): + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar['text'] = '' + self.keyTTL = [] + + def __beginFireWater(self): + if self.fireTrack and self.fireTrack.isPlaying(): + return + if self.aimStart != None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + time = globalClock.getFrameTime() + self.aimStart = time + messenger.send('wakeup') + taskMgr.add(self.__updateWaterPower, self.waterPowerTaskName) + return + + def __endFireWater(self): + if self.aimStart == None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + taskMgr.remove(self.waterPowerTaskName) + messenger.send('wakeup') + self.aimStart = None + origin = self.nozzle.getPos(render) + target = self.boss.getPos(render) + angle = deg2Rad(self.waterPitcherNode.getH() + 90) + x = math.cos(angle) + y = math.sin(angle) + fireVector = Point3(x, y, 0) + if self.power < 0.001: + self.power = 0.001 + self.lastPowerFired = self.power + fireVector *= self.fireLength * self.power + target = origin + fireVector + segment = CollisionSegment(origin[0], origin[1], origin[2], target[0], target[1], target[2]) + fromObject = render.attachNewNode(CollisionNode('pitcherColNode')) + fromObject.node().addSolid(segment) + fromObject.node().setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) + fromObject.node().setIntoCollideMask(BitMask32.allOff()) + queue = CollisionHandlerQueue() + base.cTrav.addCollider(fromObject, queue) + base.cTrav.traverse(render) + queue.sortEntries() + self.hitObject = None + if queue.getNumEntries(): + entry = queue.getEntry(0) + target = entry.getSurfacePoint(render) + self.hitObject = entry.getIntoNodePath() + base.cTrav.removeCollider(fromObject) + fromObject.removeNode() + self.d_firingWater(origin, target) + self.fireWater(origin, target) + self.resetPowerBar() + return + + def __updateWaterPower(self, task): + if not self.powerBar: + print '### no power bar!!!' + return task.done + newPower = self.__getWaterPower(globalClock.getFrameTime()) + self.power = newPower + self.powerBar['value'] = newPower + if self.power < self.YELLOW_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(0.75, 0.75, 1.0, 0.8) + elif self.power < self.RED_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(1.0, 1.0, 0.0, 0.8) + else: + self.powerBar['barColor'] = VBase4(1.0, 0.0, 0.0, 0.8) + return task.cont + + def __getWaterPower(self, time): + elapsed = max(time - self.aimStart, 0.0) + t = elapsed / self.waterPowerSpeed + exponent = self.waterPowerExponent + if t > 1: + t = t % 1 + power = 1 - math.pow(1 - t, exponent) + if power > 1.0: + power = 1.0 + return power + + def d_firingWater(self, origin, target): + self.sendUpdate('firingWater', [origin[0], + origin[1], + origin[2], + target[0], + target[1], + target[2]]) + + def firingWater(self, startX, startY, startZ, endX, endY, endZ): + origin = Point3(startX, startY, startZ) + target = Point3(endX, endY, endZ) + self.fireWater(origin, target) + + def fireWater(self, origin, target): + color = VBase4(0.75, 0.75, 1, 0.8) + dScaleUp = 0.1 + dHold = 0.3 + dScaleDown = 0.1 + horizScale = 0.1 + vertScale = 0.1 + sprayTrack = self.getSprayTrack(color, origin, target, dScaleUp, dHold, dScaleDown, horizScale, vertScale) + duration = self.squirtSfx.length() + if sprayTrack.getDuration() < duration: + duration = sprayTrack.getDuration() + soundTrack = SoundInterval(self.squirtSfx, node=self.waterPitcherModel, duration=duration) + self.fireTrack = Parallel(sprayTrack, soundTrack) + self.fireTrack.start() + + def getPos(self, wrt = render): + return self.tableGroup.getPos(wrt) + + def getLocator(self): + return self.tableGroup + + def enterFlat(self, avId): + self.prepareForPhaseFour() + self.resetPowerBar() + self.notify.debug('enterFlat %d' % self.index) + if self.avId: + toon = base.cr.doId2do.get(self.avId) + if toon: + toon.wrtReparentTo(render) + toon.setZ(0) + self.tableGroup.setScale(1, 1, 0.01) + if self.avId and self.avId == localAvatar.doId: + localAvatar.b_squish(ToontownGlobals.BossCogDamageLevels[ToontownGlobals.BossCogMoveAttack]) + + def exitFlat(self): + self.tableGroup.setScale(1.0) + if self.avId: + toon = base.cr.doId2do.get(self.avId) + if toon: + if toon == localAvatar: + self.boss.toCraneMode() + toon.b_setAnimState('neutral') + toon.setAnimState('neutral') + toon.loop('leverNeutral') + + def __allowDetect(self, task): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = Sequence(self.tableGroup.colorScaleInterval(0.2, VBase4(1, 1, 1, 1)), Func(self.tableGroup.clearColorScale), Func(self.tableGroup.clearTransparency)) + self.fadeTrack.start() + self.allowLocalRequestControl = True + + def gotBossZapped(self): + self.showExiting() + self.d_requestFree(True) + + def __upArrowKeyPressed(self): + if self.TugOfWarControls: + self.__pressHandler(0) + + def __downArrowKeyPressed(self): + if self.TugOfWarControls: + self.__pressHandler(1) + + def __pressHandler(self, index): + if index == self.buttons[0]: + self.keyTTL.insert(0, 1.0) + if not self.OnlyUpArrow: + self.buttons.reverse() + + def __spawnUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + taskMgr.doMethodLater(0.1, self.__updateKeyPressRateTask, self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __killUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __updateKeyPressRateTask(self, task): + if self.state not in 'Controlled': + return Task.done + for i in range(len(self.keyTTL)): + self.keyTTL[i] -= 0.1 + + for i in range(len(self.keyTTL)): + if self.keyTTL[i] <= 0: + a = self.keyTTL[0:i] + del self.keyTTL + self.keyTTL = a + break + + self.keyRate = len(self.keyTTL) + keyRateDiff = self.keyRate - self.BASELINE_KEY_RATE + diffPower = keyRateDiff / 300.0 + if self.power < 1 and diffPower > 0: + diffPower = diffPower * math.pow(1 - self.power, 1.25) + newPower = self.power + diffPower + if newPower > 1: + newPower = 1 + elif newPower < 0: + newPower = 0 + self.notify.debug('diffPower=%.2f keyRate = %d, newPower=%.2f' % (diffPower, self.keyRate, newPower)) + self.power = newPower + self.powerBar['value'] = newPower + if self.power < self.YELLOW_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(0.75, 0.75, 1.0, 0.8) + elif self.power < self.RED_POWER_THRESHOLD: + self.powerBar['barColor'] = VBase4(1.0, 1.0, 0.0, 0.8) + else: + self.powerBar['barColor'] = VBase4(1.0, 0.0, 0.0, 0.8) + self.__spawnUpdateKeyPressRateTask() + return Task.done + + 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) diff --git a/toontown/coghq/DistributedBanquetTableAI.py b/toontown/coghq/DistributedBanquetTableAI.py new file mode 100644 index 0000000..be4de53 --- /dev/null +++ b/toontown/coghq/DistributedBanquetTableAI.py @@ -0,0 +1,246 @@ +import random +from direct.distributed import DistributedObjectAI +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import BanquetTableBase +from toontown.toonbase import ToontownGlobals + +class DistributedBanquetTableAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM, BanquetTableBase.BanquetTableBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBanquetTableAI') + + def __init__(self, air, boss, index, numDiners, dinerLevel): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedBanquetTableAI') + self.boss = boss + self.index = index + self.numDiners = numDiners + self.numChairs = 8 + self.dinerStatus = {} + self.dinerInfo = {} + for i in xrange(self.numDiners): + self.dinerStatus[i] = self.INACTIVE + diffSettings = ToontownGlobals.BossbotBossDifficultySettings[self.boss.battleDifficulty] + hungryDuration = diffSettings[4] + eatingDuration = diffSettings[5] + hungryDuration += random.uniform(-5, 5) + eatingDuration += random.uniform(-5, 5) + level = 12 + if type(dinerLevel) == type(0): + level = dinerLevel + else: + level = random.choice(dinerLevel) + self.dinerInfo[i] = (hungryDuration, eatingDuration, level) + + self.transitionTasks = {} + self.numFoodEaten = {} + self.avId = 0 + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def getNumDiners(self): + return self.numDiners + + def getDinerStatus(self, chairIndex): + retval = self.DEAD + if chairIndex in self.dinerStatus: + retval = self.dinerStatus[chairIndex] + return retval + + def setDinerStatus(self, chairIndex, newStatus): + self.dinerStatus[chairIndex] = newStatus + + def getDinerInfo(self): + hungryDurations = [] + eatingDurations = [] + dinerLevels = [] + for i in xrange(self.numDiners): + hungryDurations.append(self.dinerInfo[i][0]) + eatingDurations.append(self.dinerInfo[i][1]) + dinerLevels.append(self.dinerInfo[i][2]) + + return (hungryDurations, eatingDurations, dinerLevels) + + def d_setDinerStatus(self, chairIndex, newStatus): + self.sendUpdate('setDinerStatus', [chairIndex, newStatus]) + + def b_setDinerStatus(self, chairIndex, newStatus): + self.setDinerStatus(chairIndex, newStatus) + self.d_setDinerStatus(chairIndex, newStatus) + + def setState(self, state): + self.request(state) + + def d_setState(self, state, avId=0, extraInfo=0): + newState = state + if state == 'On': + newState = 'N' + else: + if state == 'Off': + newState = 'F' + else: + if state == 'Inactive': + newState = 'I' + else: + if state == 'Free': + newState = 'R' + else: + if state == 'Controlled': + newState = 'C' + else: + if state == 'Flat': + newState = 'L' + self.sendUpdate('setState', [newState, avId, extraInfo]) + + def b_setState(self, state, avId=0, extraInfo=0): + if state == 'Controlled' or state == 'Flat': + self.request(state, avId) + else: + self.request(state) + self.d_setState(state, avId, extraInfo) + + def turnOn(self): + self.b_setState('On') + + def turnOff(self): + self.b_setState('Off') + + def foodServed(self, chairIndex): + self.b_setDinerStatus(chairIndex, self.EATING) + eatingDur = self.dinerInfo[chairIndex][1] + if chairIndex in self.transitionTasks: + self.removeTask(self.transitionTasks[chairIndex]) + taskName = self.uniqueName('transition-%d' % chairIndex) + newTask = self.doMethodLater(eatingDur, self.finishedEating, taskName, extraArgs=[chairIndex]) + self.transitionTasks[chairIndex] = newTask + + def finishedEating(self, chairIndex): + if chairIndex in self.transitionTasks: + self.removeTask(self.transitionTasks[chairIndex]) + self.incrementFoodEaten(chairIndex) + if self.numFoodEaten[chairIndex] >= ToontownGlobals.BossbotNumFoodToExplode: + self.b_setDinerStatus(chairIndex, self.DEAD) + self.boss.incrementDinersExploded() + else: + self.b_setDinerStatus(chairIndex, self.HUNGRY) + taskName = self.uniqueName('transition-%d' % chairIndex) + hungryDur = self.dinerInfo[chairIndex][0] + newTask = self.doMethodLater(hungryDur, self.finishedHungry, taskName, extraArgs=[chairIndex]) + self.transitionTasks[chairIndex] = newTask + + def incrementFoodEaten(self, chairIndex): + numFood = 0 + if chairIndex in self.numFoodEaten: + numFood = self.numFoodEaten[chairIndex] + self.numFoodEaten[chairIndex] = numFood + 1 + + def finishedHungry(self, chairIndex): + self.b_setDinerStatus(chairIndex, self.ANGRY) + self.numFoodEaten[chairIndex] = 0 + if chairIndex in self.transitionTasks: + self.removeTask(self.transitionTasks[chairIndex]) + + def goInactive(self): + self.b_setState('Inactive') + + def goFree(self): + self.b_setState('Free') + + def goFlat(self): + self.b_setState('Flat', self.avId) + + def getNotDeadInfo(self): + notDeadList = [] + for i in xrange(self.numDiners): + if self.dinerStatus[i] != self.DEAD: + notDeadList.append((self.index, i, self.dinerInfo[i][2])) + + return notDeadList + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0 and self.state == 'Free': + tableId = self.__getTableId(avId) + if tableId == 0: + grantRequest = True + if self.boss and not self.boss.isToonRoaming(avId): + grantRequest = False + if grantRequest: + self.b_setState('Controlled', avId) + + def forceControl(self, avId): + self.notify.debug('forceContrl tableIndex=%d avId=%d' % (self.index, avId)) + tableId = self.__getTableId(avId) + if tableId == self.doId: + if self.state == 'Flat': + self.b_setState('Controlled', avId) + else: + self.notify.warning('invalid forceControl from state %s' % self.state) + else: + self.notify.warning('tableId %d != self.doId %d ' % (tableId, self.doId)) + + def requestFree(self, gotHitByBoss): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + if self.state == 'Controlled': + self.b_setState('Free', extraInfo=gotHitByBoss) + if self.boss: + self.boss.toonLeftTable(self.index) + else: + self.notify.debug('requestFree denied in state %s' % self.state) + + def __getTableId(self, avId): + if self.boss and self.boss.tables != None: + for table in self.boss.tables: + if table.avId == avId: + return table.doId + + return 0 + + def enterOn(self): + for i in xrange(self.numDiners): + self.b_setDinerStatus(i, self.HUNGRY) + + def exitOn(slef): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterInactive(self): + for task in self.transitionTasks.values(): + self.removeTask(task) + + self.transitionTasks = {} + + def exitInactive(self): + pass + + def enterFree(self): + self.notify.debug('enterFree tableIndex=%d' % self.index) + self.avId = 0 + + def exitFree(self): + pass + + def enterControlled(self, avId): + self.notify.debug('enterControlled tableIndex=%d' % self.index) + self.avId = avId + + def exitControlled(self): + pass + + def enterFlat(self, avId): + self.notify.debug('enterFlat tableIndex=%d' % self.index) + + def exitFlat(self): + pass diff --git a/toontown/coghq/DistributedBarrelBase.py b/toontown/coghq/DistributedBarrelBase.py new file mode 100644 index 0000000..10068e9 --- /dev/null +++ b/toontown/coghq/DistributedBarrelBase.py @@ -0,0 +1,101 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.coghq import BarrelBase +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedBarrelBase(BasicEntities.DistributedNodePathEntity, BarrelBase.BarrelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBarrelBase') + + def __init__(self, cr): + self.rewardPerGrabMax = 0 + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' + self.rejectSoundPath = 'phase_4/audio/sfx/ring_miss.mp3' + self.animTrack = None + self.shadow = 0 + self.barrelScale = 0.5 + self.sphereRadius = 3.2 + self.playSoundForRemoteToons = 1 + self.gagNode = None + self.gagModel = None + self.barrel = None + return + + def disable(self): + BasicEntities.DistributedNodePathEntity.disable(self) + self.ignoreAll() + if self.animTrack: + self.animTrack.pause() + self.animTrack = None + return + + def generate(self): + BasicEntities.DistributedNodePathEntity.generate(self) + + def delete(self): + BasicEntities.DistributedNodePathEntity.delete(self) + self.gagNode.removeNode() + del self.gagNode + if self.barrel: + self.barrel.removeNode() + del self.barrel + self.barrel = None + return + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.setTag('doId', str(self.getDoId())) + self.loadModel() + self.collSphere = CollisionSphere(0, 0, 0, self.sphereRadius) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('barrelSphere')) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.barrel.attachNewNode(self.collNode) + self.collNodePath.hide() + self.applyLabel() + self.accept(self.uniqueName('enterbarrelSphere'), self.handleEnterSphere) + + def loadModel(self): + self.grabSound = base.loadSfx(self.grabSoundPath) + self.rejectSound = base.loadSfx(self.rejectSoundPath) + self.barrel = loader.loadModel('phase_4/models/cogHQ/gagTank') + self.barrel.setScale(self.barrelScale) + self.barrel.reparentTo(self) + dcsNode = self.barrel.find('**/gagLabelDCS') + dcsNode.setColor(0.15, 0.15, 0.1) + self.gagNode = self.barrel.attachNewNode('gagNode') + self.gagNode.setPosHpr(0.0, -2.62, 4.0, 0, 0, 0) + self.gagNode.setColorScale(0.7, 0.7, 0.6, 1) + + def handleEnterSphere(self, collEntry = None): + localAvId = base.localAvatar.getDoId() + self.d_requestGrab() + + def d_requestGrab(self): + self.sendUpdate('requestGrab', []) + + def setGrab(self, avId): + self.notify.debug('handleGrab %s' % avId) + self.avId = avId + if avId == base.localAvatar.doId: + self.ignore(self.uniqueName('entertreasureSphere')) + self.barrel.setColorScale(0.5, 0.5, 0.5, 1) + if self.playSoundForRemoteToons or self.avId == base.localAvatar.getDoId(): + base.playSfx(self.grabSound) + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + flytime = 1.0 + self.animTrack = Sequence(LerpScaleInterval(self.barrel, 0.2, 1.1 * self.barrelScale, blendType='easeInOut'), LerpScaleInterval(self.barrel, 0.2, self.barrelScale, blendType='easeInOut'), Func(self.resetBarrel), name=self.uniqueName('animTrack')) + self.animTrack.start() + return + + def setReject(self): + self.notify.debug('I was rejected!!!!!') + + def resetBarrel(self): + self.barrel.setScale(self.barrelScale) + self.accept(self.uniqueName('entertreasureSphere'), self.handleEnterSphere) diff --git a/toontown/coghq/DistributedBarrelBaseAI.py b/toontown/coghq/DistributedBarrelBaseAI.py new file mode 100644 index 0000000..06e3f24 --- /dev/null +++ b/toontown/coghq/DistributedBarrelBaseAI.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.level import DistributedEntityAI +from direct.task import Task +from toontown.coghq import BarrelBase + +class DistributedBarrelBaseAI(DistributedEntityAI.DistributedEntityAI, BarrelBase.BarrelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBarrelBaseAI') + + def __init__(self, level, entId): + self.rewardPerGrabMax = 0 + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.usedAvIds = [] + + def delete(self): + taskMgr.remove(self.taskName('resetGags')) + del self.usedAvIds + del self.pos + DistributedEntityAI.DistributedEntityAI.delete(self) + + def requestGrab(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestGrab %s' % avId) + if avId not in self.usedAvIds: + self.usedAvIds.append(avId) + self.d_setGrab(avId) + else: + self.sendUpdate('setReject') + + def d_setGrab(self, avId): + self.sendUpdate('setGrab', [avId]) diff --git a/toontown/coghq/DistributedBattleFactory.py b/toontown/coghq/DistributedBattleFactory.py new file mode 100644 index 0000000..dc4870e --- /dev/null +++ b/toontown/coghq/DistributedBattleFactory.py @@ -0,0 +1,47 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals + +class DistributedBattleFactory(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFactory') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('FactoryReward', self.enterFactoryReward, self.exitFactoryReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('FactoryReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('FactoryReward') + + def enterFactoryReward(self, ts): + self.notify.info('enterFactoryReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedForeman') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleFactoryRewardDone) + + def __handleFactoryRewardDone(self): + self.notify.info('Factory reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitFactoryReward(self): + self.notify.info('exitFactoryReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedBattleFactoryAI.py b/toontown/coghq/DistributedBattleFactoryAI.py new file mode 100644 index 0000000..1c41cea --- /dev/null +++ b/toontown/coghq/DistributedBattleFactoryAI.py @@ -0,0 +1,55 @@ +from toontown.coghq import DistributedLevelBattleAI +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from direct.showbase.PythonUtil import addListsByValue + +class DistributedBattleFactoryAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleFactoryAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback=None, finishCallback=None, maxSuits=4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'FactoryReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setForemanConfronted(toonId) + self.fsm.addState(State.State('FactoryReward', self.enterFactoryReward, self.exitFactoryReward, [ + 'Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('FactoryReward') + + def getTaskZoneId(self): + return self.level.factoryId + + def handleToonsWon(self, toons): + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilled, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.getTaskZoneId(), getFactoryMeritMultiplier(self.getTaskZoneId())) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful, skipping merits' % toon.doId) + if self.bossBattle: + self.toonParts[toon.doId] = self.air.cogSuitMgr.recoverPart(toon, self.level.factoryType, self.suitTrack, self.getTaskZoneId(), toons) + self.notify.debug('toonParts = %s' % self.toonParts) + + def enterFactoryReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitFactoryReward(self): + return None + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() diff --git a/toontown/coghq/DistributedBeanBarrel.py b/toontown/coghq/DistributedBeanBarrel.py new file mode 100644 index 0000000..b6ad7af --- /dev/null +++ b/toontown/coghq/DistributedBeanBarrel.py @@ -0,0 +1,32 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedBarrelBase + +class DistributedBeanBarrel(DistributedBarrelBase.DistributedBarrelBase): + + def __init__(self, cr): + DistributedBarrelBase.DistributedBarrelBase.__init__(self, cr) + self.numGags = 0 + self.gagScale = 3.0 + + def disable(self): + DistributedBarrelBase.DistributedBarrelBase.disable(self) + self.ignoreAll() + + def delete(self): + self.gagModel.removeNode() + del self.gagModel + DistributedBarrelBase.DistributedBarrelBase.delete(self) + + def applyLabel(self): + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.gagModel = purchaseModels.find('**/Jar') + self.gagModel.reparentTo(self.gagNode) + self.gagModel.setScale(self.gagScale) + self.gagModel.setPos(0, -0.1, 0) + purchaseModels.removeNode() + + def setGrab(self, avId): + DistributedBarrelBase.DistributedBarrelBase.setGrab(self, avId) diff --git a/toontown/coghq/DistributedBeanBarrelAI.py b/toontown/coghq/DistributedBeanBarrelAI.py new file mode 100644 index 0000000..12efd86 --- /dev/null +++ b/toontown/coghq/DistributedBeanBarrelAI.py @@ -0,0 +1,16 @@ +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task + +class DistributedBeanBarrelAI(DistributedBarrelBaseAI.DistributedBarrelBaseAI): + + def __init__(self, level, entityId): + x = y = z = h = 0 + DistributedBarrelBaseAI.DistributedBarrelBaseAI.__init__(self, level, entityId) + + def d_setGrab(self, avId): + self.notify.debug('d_setGrab %s' % avId) + self.sendUpdate('setGrab', [avId]) + av = self.air.doId2do.get(avId) + if av: + av.addMoney(self.rewardPerGrab) diff --git a/toontown/coghq/DistributedButton.py b/toontown/coghq/DistributedButton.py new file mode 100644 index 0000000..7d82cfc --- /dev/null +++ b/toontown/coghq/DistributedButton.py @@ -0,0 +1,137 @@ +from pandac.PandaModules import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +import MovingPlatform +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +import DistributedSwitch +from toontown.toonbase import TTLocalizer + +class DistributedButton(DistributedSwitch.DistributedSwitch): + countdownSeconds = 3.0 + + def __init__(self, cr): + self.countdownTrack = None + DistributedSwitch.DistributedSwitch.__init__(self, cr) + return + + def setSecondsOn(self, secondsOn): + self.secondsOn = secondsOn + + def avatarExit(self, avatarId): + DistributedSwitch.DistributedSwitch.avatarExit(self, avatarId) + if self.secondsOn != -1.0 and self.secondsOn > 0.0 and self.countdownSeconds > 0.0 and self.countdownSeconds < self.secondsOn and self.fsm.getCurrentState().getName() == 'playing': + track = self.switchCountdownTrack() + if track is not None: + track.start(0.0) + self.countdownTrack = track + return + + def setupSwitch(self): + model = loader.loadModel('phase_9/models/cogHQ/CogDoor_Button') + if model: + buttonBase = model.find('**/buttonBase') + change = render.attachNewNode('changePos') + buttonBase.reparentTo(change) + rootNode = render.attachNewNode(self.getName() + '-buttonBase_root') + change.reparentTo(rootNode) + self.buttonFrameNode = rootNode + self.buttonFrameNode.show() + button = model.find('**/button') + change = render.attachNewNode('change') + button.reparentTo(change) + rootNode = render.attachNewNode(self.getName() + '-button_root') + rootNode.setColor(self.color) + change.reparentTo(rootNode) + self.buttonNode = rootNode + self.buttonNode.show() + self.buttonFrameNode.reparentTo(self) + self.buttonNode.reparentTo(self) + if 1: + radius = 0.5 + cSphere = CollisionSphere(0.0, 0.0, radius, radius) + cSphere.setTangible(0) + cSphereNode = CollisionNode(self.getName()) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.cSphereNodePath = rootNode.attachNewNode(cSphereNode) + if 1: + collisionFloor = button.find('**/collision_floor') + if collisionFloor.isEmpty(): + top = 0.475 + size = 0.5 + floor = CollisionPolygon(Point3(-size, -size, top), + Point3( size, -size, top), + Point3( size, size, top), + Point3(-size, size, top)) + floor.setTangible(1) + floorNode = CollisionNode('collision_floor') + floorNode.addSolid(floor) + collisionFloor = button.attachNewNode(floorNode) + else: + change = collisionFloor.getParent().attachNewNode('changeFloor') + change.setScale(0.5, 0.5, 1.0) + collisionFloor.reparentTo(change) + collisionFloor.node().setFromCollideMask(BitMask32.allOff()) + collisionFloor.node().setIntoCollideMask(ToontownGlobals.FloorBitmask) + self.buttonFrameNode.flattenMedium() + self.buttonNode.flattenMedium() + + def delete(self): + DistributedSwitch.DistributedSwitch.delete(self) + + def enterTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.enterTrigger(self, args) + + def exitTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.exitTrigger(self, args) + + def switchOnTrack(self): + onSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_switch_pressed.mp3') + duration = 0.8 + halfDur = duration * 0.5 + pos = Vec3(0.0, 0.0, -0.2) + color = Vec4(0.0, 1.0, 0.0, 1.0) + track = Sequence(Func(self.setIsOn, 1), Parallel(SoundInterval(onSfx, node=self.node, volume=0.9), LerpPosInterval(nodePath=self.buttonNode, duration=duration, pos=pos, blendType='easeInOut'), Sequence(Wait(halfDur), LerpColorInterval(nodePath=self.buttonNode, duration=halfDur, color=color, override=1, blendType='easeOut')))) + return track + + def switchCountdownTrack(self): + wait = self.secondsOn - self.countdownSeconds + countDownSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_switch_depressed.mp3') + track = Parallel( + SoundInterval(countDownSfx), + Sequence( + Wait(wait), + Wait(0.5), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.5), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.4), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.3), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=Vec4(0.0, 1.0, 0.0, 1.0), override=1, blendType='easeOut'), + Wait(0.2), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, color=self.color, override=1, blendType='easeIn'), + LerpColorInterval(nodePath=self.buttonNode, duration=0.1, override=1, color=Vec4(0.0, 1.0, 0.0, 1.0), blendType='easeOut'), + Wait(0.1))) + return track + + def switchOffTrack(self): + offSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_switch_popup.mp3') + duration = 1.0 + halfDur = duration * 0.5 + pos = Vec3(0.0) + track = Sequence(Parallel(SoundInterval(offSfx, node=self.node, volume=1.0), LerpPosInterval(nodePath=self.buttonNode, duration=duration, pos=pos, blendType='easeInOut'), Sequence(Wait(halfDur), LerpColorInterval(nodePath=self.buttonNode, duration=halfDur, color=self.color, override=1, blendType='easeIn'))), Func(self.setIsOn, 0)) + return track + + def exitPlaying(self): + if self.countdownTrack: + self.countdownTrack.finish() + self.countdownTrack = None + DistributedSwitch.DistributedSwitch.exitPlaying(self) diff --git a/toontown/coghq/DistributedButtonAI.py b/toontown/coghq/DistributedButtonAI.py new file mode 100644 index 0000000..b87f684 --- /dev/null +++ b/toontown/coghq/DistributedButtonAI.py @@ -0,0 +1,7 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +import DistributedSwitchBase, DistributedSwitchAI + +class DistributedButtonAI(DistributedSwitchAI.DistributedSwitchAI): + setColor = DistributedSwitchBase.stubFunction + setModel = DistributedSwitchBase.stubFunction diff --git a/toontown/coghq/DistributedCashbotBossCrane.py b/toontown/coghq/DistributedCashbotBossCrane.py new file mode 100644 index 0000000..be5d3f0 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossCrane.py @@ -0,0 +1,906 @@ +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 +import random + +class DistributedCashbotBossCrane(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossCrane') + firstMagnetBit = 21 + craneMinY = 8 + craneMaxY = 25 + armMinH = -45 + armMaxH = 45 + shadowOffset = 1 + emptyFrictionCoef = 0.1 + 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, 'DistributedCashbotBossCrane') + self.boss = 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.closeButton = 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(*ToontownGlobals.CashbotBossCranePosHprs[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.boss.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.boss.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.boss.craneArm.copyTo(self.crane) + self.boss.cranes[self.index] = self + + def disable(self): + DistributedObject.DistributedObject.disable(self) + del self.boss.cranes[self.index] + self.cleanup() + + def cleanup(self): + if self.state != 'Off': + self.demand('Off') + self.boss = 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.boss.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.boss.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(self.emptyFrictionCoef) + 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.boss.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.boss.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.boss.geom.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.boss.geom) + 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) + 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.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=2, text=TTLocalizer.CashbotCraneLeave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(1.05, 0, -0.82), command=self.__exitCrane) + self.accept('escape', self.__exitCrane) + 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 + return + + def __disableControlInterface(self): + self.__turnOffMagnet() + if self.closeButton: + self.closeButton.destroy() + self.closeButton = None + 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 __exitCrane(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = DirectLabel(relief=None, text=TTLocalizer.CashbotCraneLeaving, pos=(1.05, 0, -0.88), text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1)) + self.__cleanupCraneAdvice() + self.__cleanupMagnetAdvice() + self.d_requestFree() + return + + 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.boss.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() + if np.hasNetTag('object'): + doId = int(np.getNetTag('object')) + else: + self.notify.warning("%s missing 'object' tag" % np) + return + self.notify.debug('__sniffedSomething %d' % doId) + 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(self.emptyFrictionCoef) + 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): + self.d_requestControl() + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + 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: + if len(links) > self.numLinks: + self.notify.warning('Links passed in is greater than total number of links') + return + 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.boss: + 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.boss.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() + self.accept('exitCrane', self.__exitCrane) + else: + self.startSmooth() + toon.stopSmooth() + self.grabTrack = Sequence(self.grabTrack, Func(toon.startSmooth)) + self.grabTrack.start() + + def exitControlled(self): + self.ignore('exitCrane') + 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) + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + if place.fsm.getCurrentState().getName() == 'crane': + place.setState('finalBattle') + self.boss.toFinalBattleMode() + 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() diff --git a/toontown/coghq/DistributedCashbotBossCraneAI.py b/toontown/coghq/DistributedCashbotBossCraneAI.py new file mode 100644 index 0000000..2974ae3 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossCraneAI.py @@ -0,0 +1,75 @@ +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 DistributedCashbotBossCraneAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedCashbotBossCraneAI') + self.boss = boss + self.index = index + cn = CollisionNode('controls') + cs = CollisionSphere(0, -6, 0, 6) + cn.addSolid(cs) + self.goonShield = NodePath(cn) + self.goonShield.setPosHpr(*ToontownGlobals.CashbotBossCranePosHprs[self.index]) + self.avId = 0 + self.objectId = 0 + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def d_setState(self, state, avId): + self.sendUpdate('setState', [state, avId]) + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0: + craneId = self.__getCraneId(avId) + if craneId == 0: + self.request('Controlled', avId) + + def requestFree(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.request('Free') + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getCraneId(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.cranes: + if crane.avId == avId: + return crane.doId + + return 0 + + def enterOff(self): + self.goonShield.detachNode() + + def exitOff(self): + self.goonShield.reparentTo(self.boss.scene) + + 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/coghq/DistributedCashbotBossObject.py b/toontown/coghq/DistributedCashbotBossObject.py new file mode 100644 index 0000000..c1af7b6 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossObject.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 DistributedCashbotBossObject(DistributedSmoothNode.DistributedSmoothNode, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossObject') + wantsWatchDrift = 1 + + def __init__(self, cr): + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedCashbotBossObject') + self.boss = 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.boss = 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.boss.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.boss.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.boss.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 setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + 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.boss == 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/coghq/DistributedCashbotBossObjectAI.py b/toontown/coghq/DistributedCashbotBossObjectAI.py new file mode 100644 index 0000000..196c127 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossObjectAI.py @@ -0,0 +1,140 @@ +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 DistributedCashbotBossObjectAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM): + wantsWatchDrift = 1 + + def __init__(self, air, boss): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedCashbotBossObjectAI') + self.boss = boss + self.reparentTo(self.boss.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 getBossCogId(self): + return self.boss.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) + return + + def __getCraneAndObject(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.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/coghq/DistributedCashbotBossSafe.py b/toontown/coghq/DistributedCashbotBossSafe.py new file mode 100644 index 0000000..f0df111 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossSafe.py @@ -0,0 +1,91 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +import DistributedCashbotBossObject + +class DistributedCashbotBossSafe(DistributedCashbotBossObject.DistributedCashbotBossObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossSafe') + grabPos = (0, 0, -8.2) + craneFrictionCoef = 0.2 + craneSlideSpeed = 11 + craneRotateSpeed = 16 + wantsWatchDrift = 0 + + def __init__(self, cr): + DistributedCashbotBossObject.DistributedCashbotBossObject.__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): + DistributedCashbotBossObject.DistributedCashbotBossObject.announceGenerate(self) + self.name = 'safe-%s' % self.doId + self.setName(self.name) + self.boss.safe.copyTo(self) + self.shadow = self.find('**/shadow') + self.collisionNode.setName('safe') + cs = CollisionSphere(0, 0, 4, 4) + self.collisionNode.addSolid(cs) + if self.index == 0: + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask | OTPGlobals.WallBitmask) + self.collisionNode.setFromCollideMask(ToontownGlobals.PieBitmask) + self.boss.safes[self.index] = self + self.setupPhysics('safe') + self.resetToInitialPosition() + + def disable(self): + del self.boss.safes[self.index] + DistributedCashbotBossObject.DistributedCashbotBossObject.disable(self) + + def hideShadows(self): + self.shadow.hide() + + def showShadows(self): + self.shadow.show() + + def getMinImpact(self): + if self.boss.heldObject: + return ToontownGlobals.CashbotBossSafeKnockImpact + else: + return ToontownGlobals.CashbotBossSafeNewImpact + + def doHitGoon(self, goon): + goon.b_destroyGoon() + + def resetToInitialPosition(self): + posHpr = ToontownGlobals.CashbotBossSafePosHprs[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: + DistributedCashbotBossObject.DistributedCashbotBossObject.setObjectState(self, state, avId, craneId) + + def d_requestInitial(self): + self.sendUpdate('requestInitial') + + def enterInitial(self): + self.resetToInitialPosition() + self.showShadows() + if self.index == 0: + self.stash() + + def exitInitial(self): + if self.index == 0: + self.unstash() diff --git a/toontown/coghq/DistributedCashbotBossSafeAI.py b/toontown/coghq/DistributedCashbotBossSafeAI.py new file mode 100644 index 0000000..a544977 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossSafeAI.py @@ -0,0 +1,72 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +import DistributedCashbotBossObjectAI + +class DistributedCashbotBossSafeAI(DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI): + wantsWatchDrift = 0 + + def __init__(self, air, boss, index): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.__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 = ToontownGlobals.CashbotBossSafePosHprs[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 + else: + if 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() + return + + def requestInitial(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.demand('Initial') + + def enterGrabbed(self, avId, craneId): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.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): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.enterFree(self) + self.avoidHelmet = 0 diff --git a/toontown/coghq/DistributedCashbotBossTreasure.py b/toontown/coghq/DistributedCashbotBossTreasure.py new file mode 100644 index 0000000..a1c5e18 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossTreasure.py @@ -0,0 +1,41 @@ +from toontown.safezone import DistributedSZTreasure +from toontown.toonbase import ToontownGlobals +from direct.interval.IntervalGlobal import * +from pandac.PandaModules import Point3 +Models = {ToontownGlobals.ToontownCentral: 'phase_4/models/props/icecream', + ToontownGlobals.DonaldsDock: 'phase_6/models/props/starfish_treasure', + ToontownGlobals.TheBrrrgh: 'phase_8/models/props/snowflake_treasure', + ToontownGlobals.MinniesMelodyland: 'phase_6/models/props/music_treasure', + ToontownGlobals.DaisyGardens: 'phase_8/models/props/flower_treasure', + ToontownGlobals.DonaldsDreamland: 'phase_8/models/props/zzz_treasure'} + +class DistributedCashbotBossTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' + + def setStyle(self, hoodId): + newModel = Models[hoodId] + if self.modelPath != newModel: + if self.modelPath: + self.loadModel(newModel) + self.modelPath = newModel + + def setGoonId(self, goonId): + self.goonId = goonId + + def setFinalPosition(self, x, y, z): + if not self.nodePath: + self.makeNodePath() + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + startPos = None + goon = self.cr.doId2do[self.goonId] + if goon: + startPos = goon.getPos() + lerpTime = 1 + self.treasureFlyTrack = Sequence(Func(self.collNodePath.stash), Parallel(ProjectileInterval(self.treasure, startPos=Point3(0, 0, 0), endPos=Point3(0, 0, 0), duration=lerpTime, gravityMult=2.0), LerpPosInterval(self.nodePath, lerpTime, Point3(x, y, z), startPos=startPos)), Func(self.collNodePath.unstash)) + self.treasureFlyTrack.start() + return diff --git a/toontown/coghq/DistributedCashbotBossTreasureAI.py b/toontown/coghq/DistributedCashbotBossTreasureAI.py new file mode 100644 index 0000000..70bb0d1 --- /dev/null +++ b/toontown/coghq/DistributedCashbotBossTreasureAI.py @@ -0,0 +1,50 @@ +from toontown.safezone import DistributedSZTreasureAI + +class DistributedCashbotBossTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, boss, goon, style, fx, fy, fz): + pos = goon.getPos() + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, boss, pos[0], pos[1], 0) + self.goonId = goon.doId + self.style = style + self.finalPosition = (fx, fy, fz) + + def getGoonId(self): + return self.goonId + + def setGoonId(self, goonId): + self.goonId = goonId + + def b_setGoonId(self, goonId): + self.setGoonId(goonId) + self.d_setGoonId(goonId) + + def d_setGoonId(self, goonId): + self.sendUpdate('setGoonId', [goonId]) + + def getStyle(self): + return self.style + + def setStyle(self, hoodId): + self.style = hoodId + + def b_setStyle(self, hoodId): + self.setStyle(hoodId) + self.d_setStyle(hoodId) + + def d_setStyle(self, hoodId): + self.sendUpdate('setStyle', [hoodId]) + + def getFinalPosition(self): + return self.finalPosition + + def setFinalPosition(self, x, y, z): + self.finalPosition = ( + x, y, z) + + def b_setFinalPosition(self, x, y, z): + self.setFinalPosition(x, y, z) + self.d_setFinalPosition(x, y, z) + + def d_setFinalPosition(self, x, y, z): + self.sendUpdate('setFinalPosition', [x, y, z]) diff --git a/toontown/coghq/DistributedCogHQDoor.py b/toontown/coghq/DistributedCogHQDoor.py new file mode 100644 index 0000000..62b47c8 --- /dev/null +++ b/toontown/coghq/DistributedCogHQDoor.py @@ -0,0 +1,98 @@ +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 +from toontown.building import DistributedDoor +from toontown.hood import ZoneUtil +from toontown.building import FADoorCodes +from toontown.building import DoorTypes +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TeaserPanel + +class DistributedCogHQDoor(DistributedDoor.DistributedDoor): + + def __init__(self, cr): + DistributedDoor.DistributedDoor.__init__(self, cr) + self.openSfx = base.loadSfx('phase_9/audio/sfx/CHQ_door_open.mp3') + self.closeSfx = base.loadSfx('phase_9/audio/sfx/CHQ_door_close.mp3') + + def wantsNametag(self): + return 0 + + 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 = Parallel(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)), Sequence(Wait(0.5), SoundInterval(self.closeSfx, node=rightDoor)), name=trackName) + self.doorTrack.start(ts) + if hasattr(self, 'done'): + request = self.getRequestStatus() + messenger.send('doorDoneEvent', [request]) + + def exitDoorEnterClosing(self, ts): + doorFrameHoleLeft = self.findDoorNode('doorFrameHoleLeft') + if doorFrameHoleLeft.isEmpty(): + self.notify.warning('enterOpening(): did not find flatDoors') + return + if ZoneUtil.isInterior(self.zoneId): + doorFrameHoleLeft.setColor(1.0, 1.0, 1.0, 1.0) + 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.doorExitTrack = Parallel(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)), Sequence(Wait(0.5), SoundInterval(self.closeSfx, node=leftDoor)), name=trackName) + self.doorExitTrack.start(ts) + + def setZoneIdAndBlock(self, zoneId, block): + self.zoneId = zoneId + self.block = block + canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + if canonicalZoneId in (ToontownGlobals.BossbotHQ, ToontownGlobals.BossbotLobby): + self.doorX = 1.0 + + def enterDoor(self): + if self.allowedToEnter(self.zoneId): + messenger.send('DistributedDoor_doorTrigger') + self.sendUpdate('requestEnter') + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='cogHQ', doneFunc=self.handleOkTeaser) + + def doorTrigger(self, args = None): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.boardingParty.showMe(rejectText) + return + DistributedDoor.DistributedDoor.doorTrigger(self, args) diff --git a/toontown/coghq/DistributedCogHQDoorAI.py b/toontown/coghq/DistributedCogHQDoorAI.py new file mode 100644 index 0000000..1169d66 --- /dev/null +++ b/toontown/coghq/DistributedCogHQDoorAI.py @@ -0,0 +1,60 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from toontown.building import DistributedDoorAI +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +import CogDisguiseGlobals +from toontown.building import FADoorCodes +from toontown.building import DoorTypes +from toontown.toonbase import ToontownAccessAI + +class DistributedCogHQDoorAI(DistributedDoorAI.DistributedDoorAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogHQDoorAI') + + def __init__(self, air, blockNumber, doorType, destinationZone, doorIndex=0, lockValue=FADoorCodes.SB_DISGUISE_INCOMPLETE, swing=3): + DistributedDoorAI.DistributedDoorAI.__init__(self, air, blockNumber, doorType, doorIndex, lockValue, swing) + self.destinationZone = destinationZone + + def requestEnter(self): + avatarID = self.air.getAvatarIdFromSender() + allowed = 0 + dept = ToontownGlobals.cogHQZoneId2deptIndex(self.destinationZone) + av = self.air.doId2do.get(avatarID) + if av: + if self.doorType == DoorTypes.EXT_COGHQ and self.isLockedDoor(): + parts = av.getCogParts() + if CogDisguiseGlobals.isSuitComplete(parts, dept): + allowed = 1 + else: + allowed = 0 + else: + allowed = 1 + + if not ToontownAccessAI.canAccess(avatarID, self.zoneId, 'DistributedCogHQDoorAI.requestEnter'): + allowed = 0 + + if not allowed: + self.sendReject(avatarID, self.isLockedDoor()) + else: + self.enqueueAvatarIdEnter(avatarID) + self.sendUpdateToAvatarId(avatarID, 'setOtherZoneIdAndDoId', [ + self.destinationZone, + self.otherDoor.getDoId()]) + + def requestExit(self): + avatarID = self.air.getAvatarIdFromSender() + if self.avatarsWhoAreEntering.has_key(avatarID): + del self.avatarsWhoAreEntering[avatarID] + if not self.avatarsWhoAreExiting.has_key(avatarID): + dept = ToontownGlobals.cogHQZoneId2deptIndex(self.destinationZone) + self.avatarsWhoAreExiting[avatarID] = 1 + self.sendUpdate('avatarExit', [avatarID]) + self.openDoor(self.exitDoorFSM) + if self.lockedDoor: + av = self.air.doId2do[avatarID] + if self.doorType == DoorTypes.EXT_COGHQ: + av.b_setCogIndex(-1) + else: + av.b_setCogIndex(dept) diff --git a/toontown/coghq/DistributedCogKart.py b/toontown/coghq/DistributedCogKart.py new file mode 100644 index 0000000..0a80792 --- /dev/null +++ b/toontown/coghq/DistributedCogKart.py @@ -0,0 +1,375 @@ +import math +from pandac.PandaModules import CollisionSphere, CollisionNode, Vec3, Point3, deg2Rad +from direct.interval.IntervalGlobal import Sequence, Func, Parallel, ActorInterval, Wait, Parallel, LerpHprInterval, ProjectileInterval, LerpPosInterval +from direct.directnotify import DirectNotifyGlobal +from toontown.building import ElevatorConstants +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.safezone import DistributedGolfKart +from toontown.building import DistributedElevatorExt +from toontown.building import ElevatorConstants +from toontown.distributed import DelayDelete +from direct.showbase import PythonUtil +from toontown.building import BoardingGroupShow + +class DistributedCogKart(DistributedElevatorExt.DistributedElevatorExt): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogKart') + JumpOutOffsets = ((6.5, -2, -0.025), + (-6.5, -2, -0.025), + (3.75, 5, -0.025), + (-3.75, 5, -0.025)) + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB + self.kartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam' + self.leftDoor = None + self.rightDoor = None + self.fillSlotTrack = None + return + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + self.loader = self.cr.playGame.hood.loader + if self.loader: + self.notify.debug('Loader has been loaded') + self.notify.debug(str(self.loader)) + else: + self.notify.debug('Loader has not been loaded') + 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.golfKart.reparentTo(self.loader.geom) + self.wheels = self.kart.findAllMatches('**/wheelNode*') + self.numWheels = self.wheels.getNumPaths() + + def announceGenerate(self): + DistributedElevatorExt.DistributedElevatorExt.announceGenerate(self) + 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) + + def delete(self): + DistributedElevatorExt.DistributedElevatorExt.delete(self) + if hasattr(self, 'elevatorFSM'): + del self.elevatorFSM + + 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 setColor(self, r, g, b): + pass + + def getElevatorModel(self): + return self.golfKart + + def enterWaitEmpty(self, ts): + DistributedElevatorExt.DistributedElevatorExt.enterWaitEmpty(self, ts) + + def exitWaitEmpty(self): + DistributedElevatorExt.DistributedElevatorExt.exitWaitEmpty(self) + + def forceDoorsOpen(self): + pass + + def forceDoorsClosed(self): + pass + + 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 enterClosing(self, ts): + if self.localToonOnBoard: + elevator = self.getPlaceElevator() + if elevator: + elevator.fsm.request('elevatorClosing') + self.closeDoors.start(ts) + + def enterClosed(self, ts): + self.forceDoorsClosed() + self.kartDoorsClosed(self.getZoneId()) + + def kartDoorsClosed(self, zoneId): + if self.localToonOnBoard: + hoodId = ZoneUtil.getHoodId(zoneId) + doneStatus = {'loader': 'suitInterior', + 'where': 'suitInterior', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None} + elevator = self.elevatorFSM + del self.elevatorFSM + elevator.signalDone(doneStatus) + return + + def setCountryClubInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + countryClubId = self.countryClubId + if bboard.has('countryClubIdOverride'): + countryClubId = bboard.get('countryClubIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'countryClubInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'countryClubId': self.countryClubId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setCountryClubInteriorZoneForce(self, zoneId): + place = self.cr.playGame.getPlace() + if place: + place.fsm.request('elevator', [self, 1]) + hoodId = self.cr.playGame.hood.hoodId + countryClubId = self.countryClubId + if bboard.has('countryClubIdOverride'): + countryClubId = bboard.get('countryClubIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'countryClubInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'countryClubId': self.countryClubId, + '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("setCountryClubInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def setCountryClubId(self, countryClubId): + self.countryClubId = countryClubId + + def getZoneId(self): + return 0 + + def fillSlot(self, index, avId, wantBoardingShow = 0): + self.notify.debug('%s.fillSlot(%s, %s, ... %s)' % (self.doId, + index, + avId, + globalClock.getRealTime())) + 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 + elevator = self.getPlaceElevator() + if elevator == None: + 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() + 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) + if wantBoardingShow: + boardingTrack, boardingTrackType = self.getBoardingTrack(toon, index, True) + track = Sequence(boardingTrack, track) + if avId == base.localAvatar.getDoId(): + cameraWaitTime = 2.5 + if boardingTrackType == BoardingGroupShow.TRACK_TYPE_RUN: + cameraWaitTime = 0.5 + 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, 'CogKart.fillSlot') + self.storeToonTrack(avId, track) + track.start() + self.fillSlotTrack = track + 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))) + return jumpTrack + + 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 + 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.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, 'CogKart.emptySlot') + self.storeToonTrack(toon.doId, 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 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): + DistributedElevatorExt.DistributedElevatorExt.startCountdownClock(self, countdownTime, ts) + self.clock.setH(self.clock.getH() + 180) + + def rejectBoard(self, avId, reason = 0): + print 'rejectBoard %s' % reason + if hasattr(base.localAvatar, 'elevatorNotifier'): + if reason == ElevatorConstants.REJECT_SHUFFLE: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorHoppedOff) + elif reason == ElevatorConstants.REJECT_MINLAFF: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.KartMinLaff % self.minLaff) + elif reason == ElevatorConstants.REJECT_PROMOTION: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage) + elif reason == ElevatorConstants.REJECT_NOT_YET_AVAILABLE: + base.localAvatar.elevatorNotifier.showMe(TTLocalizer.NotYetAvailable) + doneStatus = {'where': 'reject'} + elevator = self.getPlaceElevator() + if elevator: + elevator.signalDone(doneStatus) + + def getDestName(self): + if self.countryClubId == ToontownGlobals.BossbotCountryClubIntA: + return TTLocalizer.ElevatorBossBotCourse0 + elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntB: + return TTLocalizer.ElevatorBossBotCourse1 + elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntC: + return TTLocalizer.ElevatorBossBotCourse2 diff --git a/toontown/coghq/DistributedCogKartAI.py b/toontown/coghq/DistributedCogKartAI.py new file mode 100644 index 0000000..154954d --- /dev/null +++ b/toontown/coghq/DistributedCogKartAI.py @@ -0,0 +1,64 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.safezone import DistributedGolfKartAI +from toontown.building import DistributedElevatorExtAI +from toontown.building import ElevatorConstants +from toontown.toonbase import ToontownGlobals + +class DistributedCogKartAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCogKartAI') + + def __init__(self, air, index, x, y, z, h, p, r, bldg, minLaff): + self.posHpr = ( + x, y, z, h, p, r) + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg, minLaff=minLaff) + self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB + self.courseIndex = index + if self.courseIndex == 0: + self.countryClubId = ToontownGlobals.BossbotCountryClubIntA + else: + if self.courseIndex == 1: + self.countryClubId = ToontownGlobals.BossbotCountryClubIntB + else: + if self.courseIndex == 2: + self.countryClubId = ToontownGlobals.BossbotCountryClubIntC + else: + self.countryClubId = 12500 + + def getPosHpr(self): + return self.posHpr + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + + countryClubZone = self.bldg.createCountryClub(self.countryClubId, players) + for seatIndex in range(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setCountryClubInteriorZone', [ + countryClubZone]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + return + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + countryClubZone = self.bldg.createCountryClub(self.countryClubId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setCountryClubInteriorZoneForce', [ + countryClubZone]) + + def getCountryClubId(self): + return self.countryClubId + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') diff --git a/toontown/coghq/DistributedCountryClub.py b/toontown/coghq/DistributedCountryClub.py new file mode 100644 index 0000000..fa54747 --- /dev/null +++ b/toontown/coghq/DistributedCountryClub.py @@ -0,0 +1,330 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import BulletinBoardWatcher +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.coghq import CountryClubLayout +from toontown.coghq import DistributedCountryClubRoom +from toontown.coghq import CountryClubRoom +from toontown.coghq import FactoryCameraViews +from direct.gui import OnscreenText +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * + +class DistributedCountryClub(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClub') + ReadyPost = 'CountryClubReady' + WinEvent = 'CountryClubWinEvent' + doBlockRooms = base.config.GetBool('block-country-club-rooms', 1) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.lastCamEnterRoom = 0 + self.titleColor = (1, 1, 1, 1) + self.titleText = OnscreenText.OnscreenText('', fg=self.titleColor, shadow=(0, 0, 0, 1), font=ToontownGlobals.getSignFont(), pos=(0, -0.5), scale=0.1, drawOrder=0, mayChange=1) + self.titleSequence = None + return + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('countryClub', self) + self.roomWatcher = None + self.geom = None + self.rooms = [] + self.hallways = [] + self.allRooms = [] + self.curToonRoomNum = None + self.allBlockedRooms = [] + base.localAvatar.setCameraCollisionsCanMove(1) + base.localAvatar.reparentTo(render) + base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0) + self.accept('SOSPanelEnter', self.handleSOSPanel) + self.factoryViews = FactoryCameraViews.FactoryCameraViews(self) + base.localAvatar.chatMgr.chatInputSpeedChat.addFactoryMenu() + self.__setupHighSky() + return + + def startSky(self): + self.sky = loader.loadModel('phase_12/models/bossbotHQ/BossTestSkyBox') + self.sky.reparentTo(camera) + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + self.sky.setBin('background', 0) + + def stopSky(self): + taskMgr.remove('skyTrack') + self.sky.remove() + + def __setupHighSky(self): + self.startSky() + sky = self.sky + sky.setH(150) + sky.setZ(-100) + + def __cleanupHighSky(self): + sky = self.sky + sky.setH(0) + sky.setZ(0) + self.stopSky() + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setCountryClubId(self, id): + DistributedCountryClub.notify.debug('setCountryClubId: %s' % id) + self.countryClubId = id + + def setFloorNum(self, num): + DistributedCountryClub.notify.debug('floorNum: %s' % num) + self.floorNum = num + self.layout = CountryClubLayout.CountryClubLayout(self.countryClubId, self.floorNum, self.layoutIndex) + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex + + def setRoomDoIds(self, roomDoIds): + self.roomDoIds = roomDoIds + self.roomWatcher = BulletinBoardWatcher.BulletinBoardWatcher('roomWatcher-%s' % self.doId, [ DistributedCountryClubRoom.getCountryClubRoomReadyPostName(doId) for doId in self.roomDoIds ], self.gotAllRooms) + + def gotAllRooms(self): + self.notify.debug('countryClub %s: got all rooms' % self.doId) + self.roomWatcher.destroy() + self.roomWatcher = None + self.geom = render.attachNewNode('countryClub%s' % self.doId) + for doId in self.roomDoIds: + self.rooms.append(base.cr.doId2do[doId]) + self.rooms[-1].setCountryClub(self) + + self.notify.info('countryClubId %s, floor %s, %s' % (self.countryClubId, self.floorNum, self.rooms[0].avIdList)) + rng = self.layout.getRng() + numRooms = self.layout.getNumRooms() + for i, room in enumerate(self.rooms): + if i == 0: + room.getGeom().reparentTo(self.geom) + else: + room.attachTo(self.hallways[i - 1], rng) + self.allRooms.append(room) + self.listenForFloorEvents(room) + if i < numRooms - 1: + hallway = CountryClubRoom.CountryClubRoom(self.layout.getHallwayModel(i)) + hallway.attachTo(room, rng) + hallway.setRoomNum(i * 2 + 1) + hallway.initFloorCollisions() + hallway.enter() + self.hallways.append(hallway) + self.allRooms.append(hallway) + self.listenForFloorEvents(hallway) + + def handleCameraRayFloorCollision(collEntry, self = self): + name = collEntry.getIntoNode().getName() + self.notify.debug('camera floor ray collided with: %s' % name) + prefix = CountryClubRoom.CountryClubRoom.FloorCollPrefix + prefixLen = len(prefix) + if name[:prefixLen] == prefix: + try: + roomNum = int(name[prefixLen:]) + except: + DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name) + else: + self.camEnterRoom(roomNum) + + self.accept('on-floor', handleCameraRayFloorCollision) + if bboard.has('countryClubRoom'): + self.warpToRoom(bboard.get('countryClubRoom')) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + self.notify.debug('countryClubHandleFirstSetZoneDone') + self.accept('takingScreenshot', self.handleScreenshot) + base.transitions.irisIn() + bboard.post(DistributedCountryClub.ReadyPost, self) + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + zoneList = [OTPGlobals.UberZone, self.zoneId] + for room in self.rooms: + zoneList.extend(room.zoneIds) + + base.cr.sendSetZoneMsg(self.zoneId, zoneList) + return + + def listenForFloorEvents(self, room): + roomNum = room.getRoomNum() + floorCollName = room.getFloorCollName() + + def handleZoneEnter(collisionEntry, self = self, roomNum = roomNum): + self.toonEnterRoom(roomNum) + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + room = self.allRooms[roomNum] + ouchLevel = room.getFloorOuchLevel() + room.startOuch(ouchLevel) + + self.accept('enter%s' % floorCollName, handleZoneEnter) + + def handleZoneExit(collisionEntry, self = self, roomNum = roomNum): + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + self.allRooms[roomNum].stopOuch() + + self.accept('exit%s' % floorCollName, handleZoneExit) + + def getAllRoomsTimeout(self): + self.notify.warning('countryClub %s: timed out waiting for room objs' % self.doId) + + def toonEnterRoom(self, roomNum): + self.notify.debug('toonEnterRoom: %s' % roomNum) + if roomNum != self.curToonRoomNum: + if self.curToonRoomNum is not None: + self.allRooms[self.curToonRoomNum].localToonFSM.request('notPresent') + self.allRooms[roomNum].localToonFSM.request('present') + self.curToonRoomNum = roomNum + return + + def camEnterRoom(self, roomNum): + self.notify.debug('camEnterRoom: %s' % roomNum) + blockRoomsAboveThisNumber = len(self.allRooms) + if self.allBlockedRooms and self.doBlockRooms: + blockRoomsAboveThisNumber = self.allBlockedRooms[0] + if roomNum % 2 == 1: + minVis = roomNum - 2 + maxVis = roomNum + 2 + else: + minVis = roomNum - 1 + maxVis = roomNum + 1 + for i, room in enumerate(self.allRooms): + if i < minVis or i > maxVis: + if not room.getGeom().isEmpty(): + room.getGeom().stash() + elif i <= blockRoomsAboveThisNumber: + if not room.getGeom().isEmpty(): + room.getGeom().unstash() + elif not room.getGeom().isEmpty(): + room.getGeom().stash() + + self.lastCamEnterRoom = roomNum + + def setBossConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.CountryClubBossConfrontedMsg % av.getName()) + return + + def warpToRoom(self, roomId): + for i in xrange(len(self.rooms)): + room = self.rooms[i] + if room.roomId == roomId: + break + else: + return False + + base.localAvatar.setPosHpr(room.getGeom(), 0, 0, 0, 0, 0, 0) + self.camEnterRoom(i * 2) + return True + + def disable(self): + self.notify.debug('disable') + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.__cleanupHighSky() + self.ignoreAll() + for hallway in self.hallways: + hallway.exit() + + self.rooms = [] + for hallway in self.hallways: + hallway.delete() + + self.hallways = [] + self.allRooms = [] + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignore('SOSPanelEnter') + bboard.remove('countryClub') + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.factoryViews.delete() + del self.factoryViews + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.rooms[0].avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleScreenshot(self): + base.addScreenshotString('countryClubId: %s, floor (from 1): %s' % (self.countryClubId, self.floorNum + 1)) + if hasattr(self, 'currentRoomName'): + base.addScreenshotString('%s' % self.currentRoomName) + + def setBlockedRooms(self, blockedRooms): + self.blockedRooms = blockedRooms + self.computeBlockedRoomsAndHallways() + self.camEnterRoom(self.lastCamEnterRoom) + + def computeBlockedRoomsAndHallways(self): + self.allBlockedRooms = [] + for roomIndex in self.blockedRooms: + self.allBlockedRooms.append(roomIndex * 2) + + self.allBlockedRooms.sort() + self.notify.debug('self.allBlockedRooms =%s' % self.allBlockedRooms) + + def setCountryClubZone(self, zoneId): + base.cr.sendSetZoneMsg(zoneId) + base.cr.playGame.getPlace().fsm.request('walk') + scale = base.localAvatar.getScale() + base.camera.setScale(scale) + + def elevatorAlert(self, avId): + if base.localAvatar.doId != avId: + name = base.cr.doId2do[avId].getName() + self.showInfoText(TTLocalizer.CountryClubToonEnterElevator % name) + + def showInfoText(self, text = 'hello world'): + description = text + if description and description != '': + self.titleText.setText(description) + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.setColorScale(1, 1, 1, 1) + self.titleText.setFg(self.titleColor) + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.titleSequence = Sequence(Func(self.showTitleText), Wait(3.1), LerpColorScaleInterval(self.titleText, duration=0.5, colorScale=Vec4(1, 1, 1, 0.0)), Func(self.hideTitleText)) + self.titleSequence.start() + return + + def showTitleText(self): + if self.titleText: + self.titleText.show() + + def hideTitleText(self): + if self.titleText or 1: + self.titleText.hide() diff --git a/toontown/coghq/DistributedCountryClubAI.py b/toontown/coghq/DistributedCountryClubAI.py new file mode 100644 index 0000000..951fc79 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubAI.py @@ -0,0 +1,128 @@ +from direct.distributed import DistributedObjectAI +from otp.level import DistributedLevelAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.coghq import CountryClubLayout, DistributedCountryClubRoomAI +from toontown.coghq import BattleExperienceAggregatorAI +from toontown.building import DistributedClubElevatorAI + +class DistributedCountryClubAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubAI') + + def __init__(self, air, countryClubId, zoneId, floorNum, avIds, layoutIndex, battleExpAggreg=None): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.countryClubId = countryClubId + self.zoneId = zoneId + self.floorNum = floorNum + self.avIds = avIds + self.layoutIndex = layoutIndex + self.blockedRooms = [] + self.elevatorList = [] + self.battleExpAggreg = battleExpAggreg + self.layout = CountryClubLayout.CountryClubLayout(self.countryClubId, self.floorNum, self.layoutIndex) + for i in xrange(self.layout.getNumRooms()): + if i: + self.blockedRooms.append(i) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.info('generate %s, id=%s, floor=%s' % (self.doId, self.countryClubId, self.floorNum)) + self.rooms = [] + if self.battleExpAggreg is None: + self.battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + for i in range(self.layout.getNumRooms()): + room = DistributedCountryClubRoomAI.DistributedCountryClubRoomAI(self.air, self.countryClubId, self.doId, self.zoneId, self.layout.getRoomId(i), i * 2, self.avIds, self.battleExpAggreg) + room.generateWithRequired(self.zoneId) + self.rooms.append(room) + + roomDoIds = [] + for room in self.rooms: + roomDoIds.append(room.doId) + + self.sendUpdate('setRoomDoIds', [roomDoIds]) + self.placeElevatorsOnMarkers() + if __dev__: + simbase.countryClub = self + description = '%s|%s|%s' % (self.countryClubId, self.floorNum, self.avIds) + for avId in self.avIds: + self.air.writeServerEvent('countryClubEntered', avId, description) + + return + + def requestDelete(self): + self.notify.info('requestDelete: %s' % self.doId) + for room in self.rooms: + room.requestDelete() + + if hasattr(self, 'elevatorList'): + for elevator in self.elevatorList: + elevator.requestDelete() + + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + if __dev__: + if hasattr(simbase, 'countryClub') and simbase.countryClub is self: + del simbase.countryClub + del self.rooms + del self.layout + del self.battleExpAggreg + if hasattr(self, 'elevatorList'): + del self.elevatorList + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getTaskZoneId(self): + return self.countryClubId + + def allToonsGone(self): + self.notify.info('allToonsGone') + self.requestDelete() + + def getZoneId(self): + return self.zoneId + + def getCountryClubId(self): + return self.countryClubId + + def getFloorNum(self): + return self.floorNum + + def getBlockedRooms(self): + self.notify.debug('getBlockedRooms returning %s' % self.blockedRooms) + return self.blockedRooms + + def roomDefeated(self, room): + if room in self.rooms: + roomIndex = self.rooms.index(room) + if roomIndex in self.blockedRooms: + self.blockedRooms.remove(roomIndex) + self.sendUpdate('setBlockedRooms', [self.blockedRooms]) + + def placeElevatorsOnMarkers(self): + for room in self.rooms: + if room.entType2ids['elevatorMarker']: + for markerId in room.entType2ids['elevatorMarker']: + marker = room.getEntity(markerId) + newElevator = DistributedClubElevatorAI.DistributedClubElevatorAI(self.air, self.doId, self, self.avIds, marker.doId) + newElevator.generateWithRequired(self.zoneId) + self.elevatorList.append(newElevator) + + def startNextFloor(self): + floor = self.floorNum + 1 + countryClubZone = self.air.allocateZone() + countryClub = DistributedCountryClubAI(self.air, self.countryClubId, countryClubZone, floor, self.avIds, self.layoutIndex, self.battleExpAggreg) + countryClub.generateWithRequired(countryClubZone) + for avId in self.avIds: + self.sendUpdateToAvatarId(avId, 'setCountryClubZone', [countryClubZone]) + + self.requestDelete() + + def elevatorAlert(self, avId): + self.sendUpdate('elevatorAlert', [avId]) + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex diff --git a/toontown/coghq/DistributedCountryClubBattle.py b/toontown/coghq/DistributedCountryClubBattle.py new file mode 100644 index 0000000..e291be1 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubBattle.py @@ -0,0 +1,47 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals + +class DistributedCountryClubBattle(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubBattle') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('CountryClubReward', self.enterCountryClubReward, self.exitCountryClubReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('CountryClubReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('CountryClubReward') + + def enterCountryClubReward(self, ts): + self.notify.debug('enterCountryClubReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedCountryClubBoss') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleCountryClubRewardDone) + + def __handleCountryClubRewardDone(self): + self.notify.debug('countryClub reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitCountryClubReward(self): + self.notify.debug('exitCountryClubReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedCountryClubBattleAI.py b/toontown/coghq/DistributedCountryClubBattleAI.py new file mode 100644 index 0000000..ac867a0 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubBattleAI.py @@ -0,0 +1,66 @@ +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedLevelBattleAI +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from toontown.toonbase.ToontownBattleGlobals import getCountryClubCreditMultiplier +from direct.showbase.PythonUtil import addListsByValue + +class DistributedCountryClubBattleAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback=None, finishCallback=None, maxSuits=4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'CountryClubReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setBossConfronted(toonId) + self.fsm.addState(State.State('CountryClubReward', self.enterCountryClubReward, self.exitCountryClubReward, [ + 'Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('CountryClubReward') + + def getTaskZoneId(self): + return self.level.countryClubId + + def handleToonsWon(self, toons): + extraMerits = [ + 0, 0, 0, 0] + amount = ToontownGlobals.CountryClubCogBuckRewards[self.level.countryClubId] + index = ToontownGlobals.cogHQZoneId2deptIndex(self.level.countryClubId) + extraMerits[index] = amount + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilled, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.getTaskZoneId(), getCountryClubCreditMultiplier(self.getTaskZoneId()), extraMerits=extraMerits) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful list, skipping merits' % toon.doId) + + def enterCountryClubReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitCountryClubReward(self): + return None + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() + + def enterReward(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterReward(self) + roomDoId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomDoId) + if room: + room.challengeDefeated() diff --git a/toontown/coghq/DistributedCountryClubRoom.py b/toontown/coghq/DistributedCountryClubRoom.py new file mode 100644 index 0000000..7469702 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubRoom.py @@ -0,0 +1,227 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import CountryClubRoomBase, CountryClubRoom +import FactoryEntityCreator +import CountryClubRoomSpecs +from otp.level import LevelSpec, LevelConstants +from toontown.toonbase import TTLocalizer +if __dev__: + from otp.level import EditorGlobals + +def getCountryClubRoomReadyPostName(doId): + return 'countryClubRoomReady-%s' % doId + + +class DistributedCountryClubRoom(DistributedLevel.DistributedLevel, CountryClubRoomBase.CountryClubRoomBase, CountryClubRoom.CountryClubRoom): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubRoom') + EmulateEntrancePoint = False + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + CountryClubRoomBase.CountryClubRoomBase.__init__(self) + CountryClubRoom.CountryClubRoom.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.countryClub = None + return + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + + def delete(self): + del self.countryClub + DistributedLevel.DistributedLevel.delete(self) + CountryClubRoom.CountryClubRoom.delete(self) + self.ignoreAll() + + def setCountryClubId(self, countryClubId): + self.notify.debug('countryClubId: %s' % countryClubId) + CountryClubRoomBase.CountryClubRoomBase.setCountryClubId(self, countryClubId) + + def setRoomId(self, roomId): + self.notify.debug('roomId: %s' % roomId) + CountryClubRoomBase.CountryClubRoomBase.setRoomId(self, roomId) + + def setRoomNum(self, num): + self.notify.debug('roomNum: %s' % num) + CountryClubRoom.CountryClubRoom.setRoomNum(self, num) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = CountryClubRoomSpecs.getCountryClubRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + if __dev__: + typeReg = self.getCountryClubEntityTypeReg() + roomSpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.initializeLevel(self, roomSpec) + + def getReadyPostName(self): + return getCountryClubRoomReadyPostName(self.doId) + + def privGotSpec(self, levelSpec): + if __dev__: + if not levelSpec.hasEntityTypeReg(): + typeReg = self.getCountryClubEntityTypeReg() + levelSpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + base.localAvatar.setH(-90) + CountryClubRoom.CountryClubRoom.enter(self) + self.acceptOnce('leavingCountryClub', self.announceLeaving) + bboard.post(self.getReadyPostName()) + + def fixupLevelModel(self): + CountryClubRoom.CountryClubRoom.setGeom(self, self.geom) + CountryClubRoom.CountryClubRoom.initFloorCollisions(self) + + def setCountryClub(self, countryClub): + self.countryClub = countryClub + + def setBossConfronted(self, avId): + self.countryClub.setBossConfronted(avId) + + def setDefeated(self): + self.notify.info('setDefeated') + from toontown.coghq import DistributedCountryClub + messenger.send(DistributedCountryClub.DistributedCountryClub.WinEvent) + + def initVisibility(self, *args, **kw): + pass + + def shutdownVisibility(self, *args, **kw): + pass + + def lockVisibility(self, *args, **kw): + pass + + def unlockVisibility(self, *args, **kw): + pass + + def enterZone(self, *args, **kw): + pass + + def updateVisibility(self, *args, **kw): + pass + + def setVisibility(self, *args, **kw): + pass + + def resetVisibility(self, *args, **kw): + pass + + def handleVisChange(self, *args, **kw): + pass + + def forceSetZoneThisFrame(self, *args, **kw): + pass + + def getParentTokenForEntity(self, entId): + if __dev__: + pass + return 1000000 * self.roomNum + entId + + def enterLtNotPresent(self): + CountryClubRoom.CountryClubRoom.enterLtNotPresent(self) + if __dev__: + bboard.removeIfEqual(EditorGlobals.EditTargetPostName, self) + self.ignore('f2') + + def enterLtPresent(self): + CountryClubRoom.CountryClubRoom.enterLtPresent(self) + if __dev__: + bboard.post(EditorGlobals.EditTargetPostName, self) + if self.countryClub is not None: + self.countryClub.currentRoomName = CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[self.roomId] + + def printPos(self = self): + thisZone = self.getZoneNode(LevelConstants.UberZoneEntId) + pos = base.localAvatar.getPos(thisZone) + h = base.localAvatar.getH(thisZone) + roomName = CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[self.roomId] + print 'countryClub pos: %s, h: %s, room: %s' % (repr(pos), h, roomName) + if self.countryClub is not None: + floorNum = self.countryClub.floorNum + else: + floorNum = '???' + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\ncountryClubId: %s' % self.countryClubId + '\nfloor: %s' % floorNum + '\nroomId: %s' % self.roomId + '\nroomName: %s' % roomName + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + return + + self.accept('f2', printPos) + return + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + CountryClubRoom.CountryClubRoom.exit(self) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + bboard.remove(self.getReadyPostName()) + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 8 + + def getTaskZoneId(self): + return self.countryClubId + + def getBossTaunt(self): + return TTLocalizer.CountryClubBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.CountryClubBossBattleTaunt + + def __str__(self): + if hasattr(self, 'roomId'): + return '%s %s: %s' % (self.__class__.__name__, self.roomId, CountryClubRoomSpecs.BossbotCountryClubRoomId2RoomName[self.roomId]) + else: + return 'DistributedCountryClubRoom' + + def __repr__(self): + return str(self) + + def forceOuch(self, penalty): + self.setOuch(penalty) diff --git a/toontown/coghq/DistributedCountryClubRoomAI.py b/toontown/coghq/DistributedCountryClubRoomAI.py new file mode 100644 index 0000000..50ca003 --- /dev/null +++ b/toontown/coghq/DistributedCountryClubRoomAI.py @@ -0,0 +1,144 @@ +from otp.level import DistributedLevelAI, LevelSpec +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from otp.level import LevelSpec +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import FactoryEntityCreatorAI, CountryClubRoomSpecs +from toontown.coghq import CountryClubRoomBase, LevelSuitPlannerAI +from toontown.coghq import DistributedCountryClubBattleAI +from toontown.suit import DistributedMintSuitAI + +class DistributedCountryClubRoomAI(DistributedLevelAI.DistributedLevelAI, CountryClubRoomBase.CountryClubRoomBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCountryClubRoomAI') + + def __init__(self, air, countryClubId, countryClubDoId, zoneId, roomId, roomNum, avIds, battleExpAggreg): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, 0, avIds) + CountryClubRoomBase.CountryClubRoomBase.__init__(self) + self.setCountryClubId(countryClubId) + self.countryClubId = countryClubId + self.setRoomId(roomId) + self.roomNum = roomNum + self.countryClubDoId = countryClubDoId + self.battleExpAggreg = battleExpAggreg + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getCountryClubCreditMultiplier(self.countryClubId) + + def generate(self): + self.notify.debug('generate %s: room=%s' % (self.doId, self.roomId)) + self.notify.debug('loading spec') + specModule = CountryClubRoomSpecs.getCountryClubRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + if __dev__: + self.notify.debug('creating entity type registry') + typeReg = self.getCountryClubEntityTypeReg() + roomSpec.setEntityTypeReg(typeReg) + self.notify.debug('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, roomSpec) + self.notify.debug('creating cogs') + cogSpecModule = CountryClubRoomSpecs.getCogSpecModule(self.roomId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedMintSuitAI.DistributedMintSuitAI, DistributedCountryClubBattleAI.DistributedCountryClubBattleAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells, battleExpAggreg=self.battleExpAggreg) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + self.notify.debug('finish mint room %s %s creation' % (self.roomId, self.doId)) + + def requestDelete(self): + self.notify.info('requestDelete: %s' % self.doId) + DistributedLevelAI.DistributedLevelAI.requestDelete(self) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + + del self.battleExpAggreg + DistributedLevelAI.DistributedLevelAI.delete(self, deAllocZone=False) + + def getCountryClubId(self): + return self.countryClubId + + def getRoomId(self): + return self.roomId + + def getRoomNum(self): + return self.roomNum + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds + + def d_setBossConfronted(self, toonId): + if toonId not in self.avIdList: + self.notify.warning('d_setBossConfronted: %s not in list of participants' % toonId) + return + self.sendUpdate('setBossConfronted', [toonId]) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + + description = '%s|%s' % (self.countryClubId, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('mintDefeated', avId, description) + + return + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def allToonsGone(self, toonsThatCleared): + DistributedLevelAI.DistributedLevelAI.allToonsGone(self, toonsThatCleared) + if self.roomNum == 0: + mint = simbase.air.doId2do.get(self.countryClubDoId) + if mint is not None: + mint.allToonsGone() + else: + self.notify.warning('no mint %s in allToonsGone' % self.countryClubDoId) + return + + def challengeDefeated(self): + countryClub = simbase.air.doId2do.get(self.countryClubDoId) + if countryClub: + countryClub.roomDefeated(self) diff --git a/toontown/coghq/DistributedCrate.py b/toontown/coghq/DistributedCrate.py new file mode 100644 index 0000000..c643f85 --- /dev/null +++ b/toontown/coghq/DistributedCrate.py @@ -0,0 +1,300 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from CrateGlobals import * +from direct.showbase.PythonUtil import fitSrcAngle2Dest +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +import MovingPlatform +from direct.task.Task import Task +import DistributedCrushableEntity + +class DistributedCrate(DistributedCrushableEntity.DistributedCrushableEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrate') + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + ModelPaths = ('phase_9/models/cogHQ/woodCrateB', 'phase_10/models/cashbotHQ/CBWoodCrate') + + def __init__(self, cr): + DistributedCrushableEntity.DistributedCrushableEntity.__init__(self, cr) + self.initNodePath() + self.modelType = 0 + self.crate = None + self.gridSize = 3.0 + self.tContact = 0 + self.tStick = 0.01 + self.moveTrack = None + self.avMoveTrack = None + self.avPushTrack = None + self.crate = None + self.crushTrack = None + self.isLocalToon = 0 + self.stuckToCrate = 0 + self.upPressed = 0 + self.isPushing = 0 + self.creakSound = loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_crate_effort.mp3') + self.pushSound = loader.loadSfx('phase_9/audio/sfx/CHQ_FACT_crate_sliding.mp3') + return + + def disable(self): + self.ignoreAll() + if self.moveTrack: + self.moveTrack.pause() + del self.moveTrack + if self.avMoveTrack: + self.avMoveTrack.pause() + del self.avMoveTrack + if self.avPushTrack: + self.avPushTrack.pause() + del self.avPushTrack + if self.crate: + self.crate.destroy() + del self.crate + if self.crushTrack: + self.crushTrack.pause() + del self.crushTrack + taskMgr.remove(self.taskName('crushTask')) + if self.pushable: + self.__listenForCollisions(0) + self.ignore('arrow_up') + self.ignore('arrow_up-up') + DistributedCrushableEntity.DistributedCrushableEntity.disable(self) + + def delete(self): + DistributedCrushableEntity.DistributedCrushableEntity.delete(self) + del self.creakSound + del self.pushSound + + def generateInit(self): + DistributedCrushableEntity.DistributedCrushableEntity.generateInit(self) + + def generate(self): + DistributedCrushableEntity.DistributedCrushableEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedCrushableEntity.DistributedCrushableEntity.announceGenerate(self) + self.loadModel() + self.modCrateCollisions() + if self.pushable: + self.__listenForCollisions(1) + self.accept('arrow_up', self.__upKeyPressed) + + def modCrateCollisions(self): + cNode = self.find('**/wall') + cNode.setName(self.uniqueName('crateCollision')) + cNode.setZ(-.8) + colNode = self.find('**/collision') + floor = colNode.find('**/MovingPlatform*') + floor2 = floor.copyTo(colNode) + floor2.setZ(-.8) + + def __upKeyPressed(self): + self.ignore('arrow_up') + self.accept('arrow_up-up', self.__upKeyReleased) + self.upPressed = 1 + + def __upKeyReleased(self): + self.ignore('arrow_up-up') + self.accept('arrow_up', self.__upKeyPressed) + self.upPressed = 0 + if self.stuckToCrate: + self.__resetStick() + + def loadModel(self): + crateModel = loader.loadModel(DistributedCrate.ModelPaths[self.modelType]) + self.crate = MovingPlatform.MovingPlatform() + self.crate.setupCopyModel(self.getParentToken(), crateModel, 'floor') + self.setScale(1.0) + self.crate.setScale(self.scale) + self.crate.reparentTo(self) + self.crate.flattenLight() + + def setScale(self, scale): + if self.crate: + self.crate.setScale(scale) + + def __listenForCollisions(self, on): + if on: + self.accept(self.uniqueName('entercrateCollision'), self.handleCollision) + else: + self.ignore(self.uniqueName('entercrateCollision')) + + def setPosition(self, x, y, z): + self.setPos(x, y, z) + + def handleCollision(self, collEntry = None): + if not self.upPressed: + return + crateNormal = Vec3(collEntry.getSurfaceNormal(self)) + relativeVec = base.localAvatar.getRelativeVector(self, crateNormal) + relativeVec.normalize() + worldVec = render.getRelativeVector(self, crateNormal) + worldVec.normalize() + offsetVec = Vec3(base.localAvatar.getPos(render) - self.getPos(render)) + offsetVec.normalize() + offsetDot = offsetVec[0] * worldVec[0] + offsetVec[1] * worldVec[1] + self.notify.debug('offsetDot = %s, world = %s, rel = %s' % (offsetDot, worldVec, offsetVec)) + if relativeVec.getY() < -0.7 and offsetDot > 0.9 and offsetVec.getZ() < 0.05: + self.getCrateSide(crateNormal) + self.tContact = globalClock.getFrameTime() + self.__listenForCollisions(0) + self.__listenForCancelEvents(1) + self.__startStickTask(crateNormal, base.localAvatar.getPos(render)) + + def setReject(self): + self.notify.debug('setReject') + self.sentRequest = 0 + if self.stuckToCrate: + self.__resetStick() + + def __startStickTask(self, crateNormal, toonPos): + self.__killStickTask() + self.stuckToCrate = 1 + sTask = Task(self.__stickTask) + sTask.crateNormal = crateNormal + sTask.toonPos = toonPos + taskMgr.add(sTask, self.taskName('stickTask')) + + def __killStickTask(self): + taskMgr.remove(self.taskName('stickTask')) + + def __stickTask(self, task): + tElapsed = globalClock.getFrameTime() - self.tContact + if tElapsed > self.tStick: + lToon = base.localAvatar + self.isLocalToon = 1 + crateNormal = task.crateNormal + crateWidth = 2.75 * self.scale + offset = crateWidth + 1.5 + TorsoToOffset[lToon.style.torso] + newPos = crateNormal * offset + if self.avPushTrack: + self.avPushTrack.pause() + place = base.cr.playGame.getPlace() + newHpr = CrateHprs[self.crateSide] + h = lToon.getH(self) + h = fitSrcAngle2Dest(h, newHpr[0]) + startHpr = Vec3(h, 0, 0) + self.avPushTrack = Sequence(LerpPosHprInterval(lToon, 0.25, newPos, newHpr, startHpr=startHpr, other=self, blendType='easeInOut'), Func(place.fsm.request, 'push'), Func(self.__sendPushRequest, task.crateNormal), SoundInterval(self.creakSound, node=self)) + self.avPushTrack.start() + return Task.done + else: + pos = task.toonPos + base.localAvatar.setPos(task.toonPos) + return Task.cont + + def getCrateSide(self, crateNormal): + for i in range(len(CrateNormals)): + dotP = CrateNormals[i].dot(crateNormal) + if dotP > 0.9: + self.crateSide = i + + def __sendPushRequest(self, crateNormal): + self.notify.debug('__sendPushRequest') + if self.crateSide != None: + self.sentRequest = 1 + self.sendUpdate('requestPush', [self.crateSide]) + else: + self.notify.debug("didn't send request") + return + + def __listenForCancelEvents(self, on): + self.notify.debug('%s, __listenForCancelEvents(%s)' % (self.doId, on)) + if on: + self.accept('arrow_down', self.__resetStick) + self.accept('arrow_left', self.__resetStick) + self.accept('arrow_right', self.__resetStick) + else: + self.ignore('arrow_down') + self.ignore('arrow_left') + self.ignore('arrow_right') + + def setMoveTo(self, avId, x0, y0, z0, x1, y1, z1): + self.notify.debug('setMoveTo') + self.__moveCrateTo(Vec3(x0, y0, z0), Vec3(x1, y1, z1)) + isLocal = base.localAvatar.doId == avId + if isLocal and self.stuckToCrate or not isLocal: + self.__moveAvTo(avId, Vec3(x0, y0, z0), Vec3(x1, y1, z1)) + + def __moveCrateTo(self, startPos, endPos): + if self.moveTrack: + self.moveTrack.finish() + self.moveTrack = None + self.moveTrack = Parallel(Sequence(LerpPosInterval(self, T_PUSH, endPos, startPos=startPos, fluid=1)), SoundInterval(self.creakSound, node=self), SoundInterval(self.pushSound, node=self, duration=T_PUSH, volume=0.2)) + self.moveTrack.start() + return + + def __moveAvTo(self, avId, startPos, endPos): + if self.avMoveTrack: + self.avMoveTrack.finish() + self.avMoveTrack = None + av = base.cr.doId2do.get(avId) + if av: + avMoveTrack = Sequence() + moveDir = endPos - startPos + crateNormal = startPos - endPos + crateNormal.normalize() + crateWidth = 2.75 * self.scale + offset = crateWidth + 1.5 + TorsoToOffset[av.style.torso] + toonOffset = crateNormal * offset + avMoveTrack.append(Sequence(LerpPosInterval(av, T_PUSH, toonOffset, startPos=toonOffset, other=self))) + self.avMoveTrack = avMoveTrack + self.avMoveTrack.start() + return + + def __resetStick(self): + self.notify.debug('__resetStick') + self.__killStickTask() + self.__listenForCancelEvents(0) + self.__listenForCollisions(1) + self.sendUpdate('setDone') + if self.avPushTrack: + self.avPushTrack.pause() + del self.avPushTrack + self.avPushTrack = None + if self.avMoveTrack: + self.avMoveTrack.pause() + del self.avMoveTrack + self.avMoveTrack = None + base.cr.playGame.getPlace().fsm.request('walk') + self.crateSide = None + self.crateNormal = None + self.isLocalToon = 0 + self.stuckToCrate = 0 + return + + def playCrushMovie(self, crusherId, axis): + self.notify.debug('playCrushMovie') + taskMgr.remove(self.taskName('crushTask')) + taskMgr.add(self.crushTask, self.taskName('crushTask'), extraArgs=(crusherId, axis), priority=25) + + def crushTask(self, crusherId, axis): + crusher = self.level.entities.get(crusherId, None) + if crusher: + crusherHeight = crusher.model.getPos(self)[2] + maxHeight = self.pos[2] + self.scale + minHeight = crusher.getPos(self)[2] + minScale = minHeight / maxHeight + self.notify.debug('cHeight= %s' % crusherHeight) + if crusherHeight < maxHeight and crusherHeight >= minHeight: + if crusherHeight == minHeight: + self.setScale(Vec3(1.2, 1.2, minScale)) + taskMgr.doMethodLater(2, self.setScale, 'resetScale', extraArgs=(1,)) + return Task.done + else: + k = crusherHeight / maxHeight + sx = min(1 / k, 0.2) + self.setScale(Vec3(1 + sx, 1 + sx, k)) + return Task.cont + + def originalTry(self, axis): + tSquash = 0.4 + if self.crushTrack: + self.crushTrack.finish() + del self.crushTrack + self.crushTrack = None + self.crushTrack = Sequence(LerpScaleInterval(self, tSquash, VBase3(1.2, 1.2, 0.25), blendType='easeInOut'), LerpColorScaleInterval(self, 2.0, VBase4(1, 1, 1, 0), blendType='easeInOut'), Wait(2.0), LerpScaleInterval(self, 0.1, VBase3(1, 1, 1), blendType='easeInOut'), LerpColorScaleInterval(self, 0.1, VBase4(1, 1, 1, 0), blendType='easeInOut')) + self.crushTrack.start() + return diff --git a/toontown/coghq/DistributedCrateAI.py b/toontown/coghq/DistributedCrateAI.py new file mode 100644 index 0000000..2487d78 --- /dev/null +++ b/toontown/coghq/DistributedCrateAI.py @@ -0,0 +1,82 @@ +from CrateGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedCrushableEntityAI +from direct.task import Task +import CrateGlobals + +class DistributedCrateAI(DistributedCrushableEntityAI.DistributedCrushableEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrateAI') + + def __init__(self, level, entId): + DistributedCrushableEntityAI.DistributedCrushableEntityAI.__init__(self, level, entId) + self.grid = None + self.avId = 0 + self.tPowerUp = 0 + self.width = 2 + return + + def generate(self): + DistributedCrushableEntityAI.DistributedCrushableEntityAI.generate(self) + + def delete(self): + taskMgr.remove(self.taskName('sendPush')) + DistributedCrushableEntityAI.DistributedCrushableEntityAI.delete(self) + + def requestPush(self, side): + self.notify.debug('requestPush') + avId = self.air.getAvatarIdFromSender() + if side not in [0, 1, 2, 3]: + self.air.writeServerEvent('suspicious', avId, 'DistributedCrateAI.requestPush given invalid side arg') + return + if not self.avId and self.grid.checkPush(self.entId, side): + self.avId = avId + self.side = side + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + taskMgr.remove(self.taskName('sendPush')) + taskMgr.doMethodLater(self.tPowerUp, self.sendPushTask, self.taskName('sendPush')) + else: + self.sendUpdateToAvatarId(avId, 'setReject', []) + + def setDone(self): + self.notify.debug('setDone') + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + taskMgr.remove(self.taskName('sendPush')) + self.avId = 0 + + def sendPushTask(self, task): + self.notify.debug('sendPushTask') + if not hasattr(self, 'entId'): + self.notify.warning("avoiding AI Crash AttributeError: DistributedCrateAI instance has no attribute 'entId'") + return + oldPos = self.grid.getObjPos(self.entId) + if self.grid.doPush(self.entId, self.side): + newPos = self.grid.getObjPos(self.entId) + self.sendUpdate('setMoveTo', [ + self.avId, oldPos[0], oldPos[1], oldPos[2], newPos[0], newPos[1], newPos[2]]) + taskMgr.doMethodLater(CrateGlobals.T_PUSH + CrateGlobals.T_PAUSE, self.sendPushTask, self.taskName('sendPush')) + else: + taskMgr.remove(self.taskName('sendPush')) + self.sendUpdateToAvatarId(self.avId, 'setReject', []) + self.avId = 0 + return Task.done + + def updateGrid(self): + pass + + def doCrush(self, crusherId, axis): + DistributedCrushableEntityAI.DistributedCrushableEntityAI.doCrush(self, crusherId, axis) + + def setGridId(self, gridId): + self.gridId = gridId + grid = self.level.entities.get(gridId, None) + if grid: + self.grid = grid + self.grid.addObjectByPos(self.entId, self.pos, width=2) + self.b_setPosition(self.getPosition()) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + if self.avId == avId: + self.avId = 0 diff --git a/toontown/coghq/DistributedCrushableEntity.py b/toontown/coghq/DistributedCrushableEntity.py new file mode 100644 index 0000000..cd13f12 --- /dev/null +++ b/toontown/coghq/DistributedCrushableEntity.py @@ -0,0 +1,37 @@ +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities + +class DistributedCrushableEntity(DistributedEntity.DistributedEntity, NodePath, BasicEntities.NodePathAttribs): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrushableEntity') + + def __init__(self, cr): + DistributedEntity.DistributedEntity.__init__(self, cr) + node = hidden.attachNewNode('DistributedNodePathEntity') + + def initNodePath(self): + node = hidden.attachNewNode('DistributedNodePathEntity') + NodePath.__init__(self, node) + + def announceGenerate(self): + DistributedEntity.DistributedEntity.announceGenerate(self) + BasicEntities.NodePathAttribs.initNodePathAttribs(self) + + def disable(self): + self.reparentTo(hidden) + BasicEntities.NodePathAttribs.destroy(self) + DistributedEntity.DistributedEntity.disable(self) + + def delete(self): + self.removeNode() + DistributedEntity.DistributedEntity.delete(self) + + def setPosition(self, x, y, z): + self.setPos(x, y, z) + + def setCrushed(self, crusherId, axis): + self.playCrushMovie(crusherId, axis) + + def playCrushMovie(self, crusherId, axis): + pass diff --git a/toontown/coghq/DistributedCrushableEntityAI.py b/toontown/coghq/DistributedCrushableEntityAI.py new file mode 100644 index 0000000..0d7880a --- /dev/null +++ b/toontown/coghq/DistributedCrushableEntityAI.py @@ -0,0 +1,88 @@ +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedCrushableEntityAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrushableEntityAI') + + def __init__(self, level, entId): + self.isCrushable = 0 + self.crushCellId = None + self.crushCell = None + self.grid = None + self.width = 1 + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + return + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + self.setActiveCrushCell() + if self.level: + self.attachToGrid() + + def delete(self): + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + if self.crushCell != None: + self.crushCell.unregisterCrushable(self.entId) + self.crushCell = None + DistributedEntityAI.DistributedEntityAI.destroy(self) + return + + def attachToGrid(self): + if self.gridId is not None: + + def setGrid(gridId=self.gridId, self=self): + grid = self.level.entities.get(gridId, None) + if grid: + self.grid = grid + self.grid.addObjectByPos(self.entId, self.pos, self.width) + self.b_setPosition(self.getPosition()) + self.initGridDependents() + return 1 + return 0 + + self.level.setEntityCreateCallback(self.gridId, setGrid) + return + + def initGridDependents(self): + pass + + def setActiveCrushCell(self): + if self.crushCellId != None: + self.notify.debug('setActiveCrushCell, entId: %d' % self.entId) + self.crushCell = self.level.entities.get(self.crushCellId, None) + if self.crushCell == None: + self.accept(self.level.getEntityCreateEvent(self.crushCellId), self.setActiveCrushCell) + else: + self.isCrushable = 1 + self.crushCell.registerCrushable(self.entId) + return + + def b_setPosition(self, pos): + self.d_setPosition(pos) + self.setPosition(pos) + + def d_setPosition(self, pos): + self.sendUpdate('setPosition', [pos[0], pos[1], pos[2]]) + + def setPosition(self, pos): + self.pos = pos + + def getPosition(self): + return self.grid.getObjPos(self.entId) + + def updateGrid(self): + pass + + def doCrush(self, crusherId, axis): + pass + + def setGridId(self, gridId): + self.gridId = gridId + self.attachToGrid() + + def setCrushCellId(self, crushCellId): + self.crushCellId = crushCellId + self.setActiveCrushCell() diff --git a/toontown/coghq/DistributedCrusherEntity.py b/toontown/coghq/DistributedCrusherEntity.py new file mode 100644 index 0000000..4aea0b2 --- /dev/null +++ b/toontown/coghq/DistributedCrusherEntity.py @@ -0,0 +1,12 @@ +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedCrusherEntity(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrusherEntity') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.crushMsg = self.getUniqueName('crushMsg') diff --git a/toontown/coghq/DistributedCrusherEntityAI.py b/toontown/coghq/DistributedCrusherEntityAI.py new file mode 100644 index 0000000..383b651 --- /dev/null +++ b/toontown/coghq/DistributedCrusherEntityAI.py @@ -0,0 +1,47 @@ +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedCrusherEntityAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCrusherEntityAI') + + def __init__(self, level, entId): + self.isCrusher = 0 + self.crushCell = None + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.crushMsg = self.getUniqueName('crusherDoCrush') + return + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + self.setActiveCrushCell() + + def delete(self): + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity %s' % self.entId) + if self.crushCell != None: + self.crushCell.unregisterCrusher(self.entId) + self.crushCell = None + DistributedEntityAI.DistributedEntityAI.destroy(self) + return + + def setActiveCrushCell(self): + self.notify.debug('setActiveCrushCell, entId: %d' % self.entId) + if self.crushCellId != None: + self.crushCell = self.level.entities.get(self.crushCellId, None) + if self.crushCell == None: + self.accept(self.level.getEntityCreateEvent(self.crushCellId), self.setActiveCrushCell) + else: + self.isCrusher = 1 + self.crushCell.registerCrusher(self.entId) + return + + def sendCrushMsg(self, axis=0): + if self.isCrusher: + messenger.send(self.crushMsg, [self.entId, axis]) + + def getPosition(self): + if hasattr(self, 'pos'): + return self.pos diff --git a/toontown/coghq/DistributedDoorEntity.py b/toontown/coghq/DistributedDoorEntity.py new file mode 100644 index 0000000..9bed797 --- /dev/null +++ b/toontown/coghq/DistributedDoorEntity.py @@ -0,0 +1,471 @@ +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 +import DistributedDoorEntityBase +from direct.fsm import FourState +from direct.fsm import ClassicFSM +from otp.level import DistributedEntity +from toontown.toonbase import TTLocalizer +from otp.level import BasicEntities +from direct.fsm import State +from otp.level import VisibilityBlocker + +class DistributedDoorEntityLock(DistributedDoorEntityBase.LockBase, FourState.FourState): + slideLeft = Vec3(-7.5, 0.0, 0.0) + slideRight = Vec3(7.5, 0.0, 0.0) + + def __init__(self, door, lockIndex, lockedNodePath, leftNodePath, rightNodePath, stateIndex): + self.door = door + self.lockIndex = lockIndex + self.lockedNodePath = lockedNodePath + self.leftNodePath = leftNodePath + self.rightNodePath = rightNodePath + self.initialStateIndex = stateIndex + FourState.FourState.__init__(self, self.stateNames, self.stateDurations) + + def delete(self): + self.takedown() + del self.door + + def setup(self): + self.setLockState(self.initialStateIndex) + del self.initialStateIndex + + def takedown(self): + if self.track is not None: + self.track.pause() + self.track = None + for i in self.states.keys(): + del self.states[i] + + self.states = [] + self.fsm = None + return + + def setLockState(self, stateIndex): + if self.stateIndex != stateIndex: + state = self.states.get(stateIndex) + if state is not None: + self.fsm.request(state) + return + + def isUnlocked(self): + return self.isOn() + + def enterState1(self): + FourState.FourState.enterState1(self) + beat = self.duration * 0.05 + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_arms_retracting.mp3') + self.setTrack(Sequence(Wait(beat * 2.0), Parallel(SoundInterval(slideSfx, node=self.door.node, volume=0.8), Sequence(ShowInterval(self.leftNodePath), ShowInterval(self.rightNodePath), Parallel(LerpPosInterval(nodePath=self.leftNodePath, other=self.lockedNodePath, duration=beat * 16.0, pos=Vec3(0.0), blendType='easeIn'), LerpPosInterval(nodePath=self.rightNodePath, other=self.lockedNodePath, duration=beat * 16.0, pos=Vec3(0.0), blendType='easeIn')), HideInterval(self.leftNodePath), HideInterval(self.rightNodePath), ShowInterval(self.lockedNodePath))))) + + def enterState2(self): + FourState.FourState.enterState2(self) + self.setTrack(None) + self.leftNodePath.setPos(self.lockedNodePath, Vec3(0.0)) + self.rightNodePath.setPos(self.lockedNodePath, Vec3(0.0)) + self.leftNodePath.hide() + self.rightNodePath.hide() + self.lockedNodePath.show() + return + + def enterState3(self): + FourState.FourState.enterState3(self) + unlockSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_unlock.mp3') + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_arms_retracting.mp3') + beat = self.duration * 0.05 + self.setTrack(Sequence(Wait(beat * 2), Parallel(SoundInterval(unlockSfx, node=self.door.node, volume=0.8), SoundInterval(slideSfx, node=self.door.node, volume=0.8), Sequence(HideInterval(self.lockedNodePath), ShowInterval(self.leftNodePath), ShowInterval(self.rightNodePath), Parallel(LerpPosInterval(nodePath=self.leftNodePath, other=self.lockedNodePath, duration=beat * 16, pos=self.slideLeft, blendType='easeOut'), LerpPosInterval(nodePath=self.rightNodePath, other=self.lockedNodePath, duration=beat * 16, pos=self.slideRight, blendType='easeOut')), HideInterval(self.leftNodePath), HideInterval(self.rightNodePath))))) + + def enterState4(self): + FourState.FourState.enterState4(self) + self.setTrack(None) + self.leftNodePath.setPos(self.lockedNodePath, self.slideLeft) + self.rightNodePath.setPos(self.lockedNodePath, self.slideRight) + self.leftNodePath.hide() + self.rightNodePath.hide() + self.lockedNodePath.hide() + return + + +class DistributedDoorEntity(DistributedDoorEntityBase.DistributedDoorEntityBase, DistributedEntity.DistributedEntity, BasicEntities.NodePathAttribsProxy, FourState.FourState, VisibilityBlocker.VisibilityBlocker): + + def __init__(self, cr): + self.innerDoorsTrack = None + self.isVisReady = 0 + self.isOuterDoorOpen = 0 + DistributedEntity.DistributedEntity.__init__(self, cr) + FourState.FourState.__init__(self, self.stateNames, self.stateDurations) + VisibilityBlocker.VisibilityBlocker.__init__(self) + self.locks = [] + return + + def generate(self): + DistributedEntity.DistributedEntity.generate(self) + + def announceGenerate(self): + self.doorNode = hidden.attachNewNode('door-%s' % self.entId) + DistributedEntity.DistributedEntity.announceGenerate(self) + BasicEntities.NodePathAttribsProxy.initNodePathAttribs(self) + self.setup() + + def disable(self): + self.takedown() + self.doorNode.removeNode() + del self.doorNode + DistributedEntity.DistributedEntity.disable(self) + + def delete(self): + DistributedEntity.DistributedEntity.delete(self) + + def setup(self): + self.setupDoor() + for i in self.locks: + i.setup() + + self.accept('exit%s' % (self.getName(),), self.exitTrigger) + self.acceptAvatar() + if __dev__: + self.initWantDoors() + + def takedown(self): + if __dev__: + self.shutdownWantDoors() + self.ignoreAll() + if self.track is not None: + self.track.finish() + self.track = None + if self.innerDoorsTrack is not None: + self.innerDoorsTrack.finish() + self.innerDoorsTrack = None + for i in self.locks: + i.takedown() + + self.locks = [] + self.fsm = None + for i in self.states.keys(): + del self.states[i] + + self.states = [] + return + + setUnlock0Event = DistributedDoorEntityBase.stubFunction + setUnlock1Event = DistributedDoorEntityBase.stubFunction + setUnlock2Event = DistributedDoorEntityBase.stubFunction + setUnlock3Event = DistributedDoorEntityBase.stubFunction + setIsOpenEvent = DistributedDoorEntityBase.stubFunction + setIsLock0Unlocked = DistributedDoorEntityBase.stubFunction + setIsLock1Unlocked = DistributedDoorEntityBase.stubFunction + setIsLock2Unlocked = DistributedDoorEntityBase.stubFunction + setIsLock3Unlocked = DistributedDoorEntityBase.stubFunction + setIsOpen = DistributedDoorEntityBase.stubFunction + setSecondsOpen = DistributedDoorEntityBase.stubFunction + + def acceptAvatar(self): + self.accept('enter%s' % (self.getName(),), self.enterTrigger) + + def rejectInteract(self): + DistributedEntity.DistributedEntity.rejectInteract(self) + self.acceptAvatar() + + def avatarExit(self, avatarId): + DistributedEntity.DistributedEntity.avatarExit(self, avatarId) + self.acceptAvatar() + + def enterTrigger(self, args = None): + messenger.send('DistributedInteractiveEntity_enterTrigger') + self.sendUpdate('requestOpen') + + def exitTrigger(self, args = None): + messenger.send('DistributedInteractiveEntity_exitTrigger') + + def okToUnblockVis(self): + VisibilityBlocker.VisibilityBlocker.okToUnblockVis(self) + self.isVisReady = 1 + self.openInnerDoors() + + def changedOnState(self, isOn): + messenger.send(self.getOutputEventName(), [not isOn]) + + def setLocksState(self, stateBits): + lock0 = stateBits & 15 + lock1 = (stateBits & 240) >> 4 + lock2 = (stateBits & 3840) >> 8 + if self.isGenerated(): + self.locks[0].setLockState(lock0) + self.locks[1].setLockState(lock1) + self.locks[2].setLockState(lock2) + else: + self.initialLock0StateIndex = lock0 + self.initialLock1StateIndex = lock1 + self.initialLock2StateIndex = lock2 + + def setDoorState(self, stateIndex, timeStamp): + self.stateTime = globalClockDelta.localElapsedTime(timeStamp) + if self.isGenerated(): + if self.stateIndex != stateIndex: + state = self.states.get(stateIndex) + if state is not None: + self.fsm.request(state) + else: + self.initialState = stateIndex + self.initialStateTimestamp = timeStamp + return + + def getName(self): + return 'switch-%s' % str(self.entId) + + def getNodePath(self): + if hasattr(self, 'doorNode'): + return self.doorNode + return None + + def setupDoor(self): + model = loader.loadModel('phase_9/models/cogHQ/CogDoorHandShake') + if model: + doorway = model.find('**/Doorway1') + rootNode = self.doorNode.attachNewNode(self.getName() + '-root') + rootNode.setPos(self.pos) + rootNode.setHpr(self.hpr) + rootNode.setScale(self.scale) + rootNode.setColor(self.color) + change = rootNode.attachNewNode('changePos') + doorway.reparentTo(change) + self.node = rootNode + self.node.show() + self.locks.append(DistributedDoorEntityLock(self, + 0, + doorway.find('**/Slide_One_Closed'), + doorway.find('**/Slide_One_Left_Open'), + doorway.find('**/Slide_One_Right_Open'), + self.initialLock0StateIndex)) + self.locks.append(DistributedDoorEntityLock(self, + 1, + doorway.find('**/Slide_Two_Closed'), + doorway.find('**/Slide_Two_Left_Open'), + doorway.find('**/Slide_Two_Right_Open'), + self.initialLock1StateIndex)) + self.locks.append(DistributedDoorEntityLock(self, + 2, + doorway.find('**/Slide_Three_Closed'), + doorway.find('**/Slide_Three_Left_Open'), + doorway.find('**/Slide_Three_Right_Open'), + self.initialLock2StateIndex)) + + del self.initialLock0StateIndex + del self.initialLock1StateIndex + del self.initialLock2StateIndex + + door = doorway.find('doortop') + if door.isEmpty(): + print 'doortop hack' + door = doorway.attachNewNode('doortop') + doorway.find('doortop1').reparentTo(door) + doorway.find('doortop2').reparentTo(door) + + rootNode = self.doorNode.attachNewNode(self.getName() + '-topDoor') + rootNode.setPos(self.pos) + rootNode.setHpr(self.hpr) + rootNode.setScale(self.scale) + rootNode.setColor(self.color) + change = rootNode.attachNewNode('changePos') + door.reparentTo(change) + self.doorTop = rootNode + self.doorTop.show() + + rootNode = self.doorTop.getParent().attachNewNode(self.getName() + '-leftDoor') + change = rootNode.attachNewNode('change') + door = doorway.find('**/doorLeft') + door = door.reparentTo(change) + self.doorLeft = rootNode + self.doorLeft.show() + change.setPos(self.pos) + change.setHpr(self.hpr) + change.setScale(self.scale) + change.setColor(self.color) + + door = doorway.find('doorbottom') + if door.isEmpty(): + print 'doorbottom hack' + door = doorway.attachNewNode('doorbottom') + doorway.find('doorbottom1').reparentTo(door) + doorway.find('doorbottom2').reparentTo(door) + + change = render.attachNewNode('changePos') + door.reparentTo(change) + rootNode = self.doorNode.attachNewNode(self.getName() + '-bottomDoor') + rootNode.setPos(self.pos) + rootNode.setHpr(self.hpr) + rootNode.setScale(self.scale) + rootNode.setColor(self.color) + change.reparentTo(rootNode) + self.doorBottom = rootNode + self.doorBottom.show() + + rootNode = self.doorTop.getParent().attachNewNode(self.getName() + '-rightDoor') + change = rootNode.attachNewNode('change') + door = doorway.find('**/doorRight') + door = door.reparentTo(change) + self.doorRight = rootNode + self.doorRight.show() + change.setPos(self.pos) + change.setHpr(self.hpr) + change.setScale(self.scale) + change.setColor(self.color) + + collision = self.doorLeft.find('**/doorLeft_collision1') + collision.setName(self.getName()) + collision = self.doorLeft.find('**/doorLeft_collision2') + collision.setName(self.getName()) + collision = self.doorRight.find('**/doorRight_collision1') + collision.setName(self.getName()) + collision = self.doorRight.find('**/doorRight_collision2') + collision.setName(self.getName()) + collision = self.doorLeft.find('**/doorLeft_innerCollision') + collision.setName(self.getName()) + self.leftInnerCollision = collision + collision = self.doorRight.find('**/doorRight_innerCollision') + collision.setName(self.getName()) + self.rightInnerCollision = collision + + if 1: + pass + else: + radius = 8.0 + cSphere = CollisionSphere(0.0, 0.0, 0.0, radius) + cSphere.setTangible(0) + cSphereNode = CollisionNode(self.getName()) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.cSphereNodePath = self.node.attachNewNode(cSphereNode) + + if 1: + self.node.flattenMedium() + self.doorTop.flattenMedium() + self.doorBottom.flattenMedium() + self.doorLeft.flattenMedium() + self.doorRight.flattenMedium() + + self.setDoorState(self.initialState, self.initialStateTimestamp) + del self.initialState + del self.initialStateTimestamp + + def setInnerDoorsTrack(self, track): + if self.innerDoorsTrack is not None: + self.innerDoorsTrack.pause() + self.innerDoorsTrack = None + if track is not None: + track.start(0.0) + self.innerDoorsTrack = track + return + + def openInnerDoors(self): + print 'openInnerDoors' + if not self.level.complexVis() or self.isOuterDoorOpen and (not self.isVisBlocker or self.isVisReady): + print 'openInnerDoors stage Two' + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.mp3') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.mp3') + moveDistance = 8.0 + self.setInnerDoorsTrack(Sequence(Func(self.leftInnerCollision.unstash), Func(self.rightInnerCollision.unstash), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorLeft, duration=duration * 0.4, pos=Vec3(-moveDistance, 0.0, 0.0), blendType='easeOut'), LerpPosInterval(nodePath=self.doorRight, duration=duration * 0.4, pos=Vec3(moveDistance, 0.0, 0.0), blendType='easeOut'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.doorLeft.stash), Func(self.doorRight.stash))) + + def closeInnerDoors(self): + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.mp3') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.mp3') + moveDistance = 8.0 + self.setInnerDoorsTrack(Sequence(Func(self.doorLeft.unstash), Func(self.doorRight.unstash), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorLeft, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), LerpPosInterval(nodePath=self.doorRight, duration=duration * 0.4, pos=Vec3(0.0), blendType='easeIn'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.leftInnerCollision.stash), Func(self.rightInnerCollision.stash))) + + def setisOuterDoorOpen(self, isOpen): + self.isOuterDoorOpen = isOpen + + def enterState1(self): + print 'doors enter state 1' + FourState.FourState.enterState1(self) + self.isOuterDoorOpen = 0 + if self.isVisBlocker: + if not self.isVisReady: + self.requestUnblockVis() + else: + self.okToUnblockVis() + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.mp3') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.mp3') + moveDistance = 8.0 + self.setTrack(Sequence(Wait(duration * 0.1), Parallel(SoundInterval(slideSfx, node=self.node, duration=duration * 0.4, volume=0.8), LerpPosInterval(nodePath=self.doorTop, duration=duration * 0.4, pos=Vec3(0.0, 0.0, moveDistance), blendType='easeOut'), LerpPosInterval(nodePath=self.doorBottom, duration=duration * 0.4, pos=Vec3(0.0, 0.0, -moveDistance), blendType='easeOut'), Sequence(Wait(duration * 0.375), SoundInterval(finalSfx, node=self.node, duration=1.0, volume=0.8))), Func(self.doorTop.stash), Func(self.doorBottom.stash), Func(self.setisOuterDoorOpen, 1), Func(self.openInnerDoors))) + + def enterState2(self): + FourState.FourState.enterState2(self) + self.isOuterDoorOpen = 1 + self.setTrack(None) + moveDistance = 7.5 + (self.doorTop.setPos(Vec3(0.0, 0.0, moveDistance)),) + (self.doorBottom.setPos(Vec3(0.0, 0.0, -moveDistance)),) + self.doorTop.stash() + self.doorBottom.stash() + if not self.isVisBlocker or not self.isWaitingForUnblockVis(): + self.setInnerDoorsTrack(None) + self.doorLeft.setPos(Vec3(-moveDistance, 0.0, 0.0)) + self.doorRight.setPos(Vec3(moveDistance, 0.0, 0.0)) + self.doorLeft.stash() + self.doorRight.stash() + return + + def exitState2(self): + FourState.FourState.exitState2(self) + self.cancelUnblockVis() + + def enterState3(self): + FourState.FourState.enterState3(self) + duration = self.duration + slideSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_sliding.mp3') + finalSfx = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_door_open_final.mp3') + self.setTrack(Sequence(Wait(duration * 0.1), + Func(self.closeInnerDoors), + Wait(duration * 0.4), + Func(self.doorTop.unstash), + Func(self.doorBottom.unstash), + Parallel(SoundInterval(slideSfx, node=self.node, duration=duration*0.4, volume=0.8), + LerpPosInterval(nodePath=self.doorTop, duration=duration*0.4, pos=Vec3(0.0), blendType='easeIn'), + LerpPosInterval(nodePath=self.doorBottom, duration=duration*0.4, pos=Vec3(0.0), blendType='easeIn'), + Sequence(Wait(duration*0.375), + SoundInterval(finalSfx, node=self.node, duration=duration*0.4, volume=0.8))), + Func(self.setisOuterDoorOpen, 0))) + + def enterState4(self): + FourState.FourState.enterState4(self) + self.setisOuterDoorOpen(0) + self.isVisReady = 0 + self.setTrack(None) + self.doorTop.unstash() + self.doorBottom.unstash() + self.doorTop.setPos(Vec3(0.0)) + self.doorBottom.setPos(Vec3(0.0)) + self.setInnerDoorsTrack(None) + self.leftInnerCollision.stash() + self.rightInnerCollision.stash() + self.doorLeft.unstash() + self.doorRight.unstash() + self.doorLeft.setPos(Vec3(0.0)) + self.doorRight.setPos(Vec3(0.0)) + + + if __dev__: + def initWantDoors(self): + self.accept('wantDoorsChanged', self.onWantDoorsChanged) + self.onWantDoorsChanged() + + def shutdownWantDoors(self): + self.ignore('wantDoorsChanged') + + def onWantDoorsChanged(self): + if self.level.levelMgrEntity.wantDoors: + self.getNodePath().unstash() + else: + self.getNodePath().stash() + + def attribChanged(self, attrib, value): + self.takedown() + self.setup() diff --git a/toontown/coghq/DistributedDoorEntityAI.py b/toontown/coghq/DistributedDoorEntityAI.py new file mode 100644 index 0000000..17d9f58 --- /dev/null +++ b/toontown/coghq/DistributedDoorEntityAI.py @@ -0,0 +1,213 @@ +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +import DistributedDoorEntityBase +from direct.distributed import DistributedObjectAI +from otp.level import DistributedEntityAI +from direct.fsm import FourStateAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task + +class Lock(DistributedDoorEntityBase.LockBase, DirectObject.DirectObject, FourStateAI.FourStateAI): + + def __init__(self, door, lockIndex, event, isUnlocked): + self.door = door + self.lockIndex = lockIndex + FourStateAI.FourStateAI.__init__(self, self.stateNames, durations=self.stateDurations) + self.unlockEvent = None + self.setUnlockEvent(event) + self.setIsUnlocked(isUnlocked) + return + + def getLockState(self): + return self.stateIndex + + def setup(self): + pass + + def takedown(self): + self.ignoreAll() + FourStateAI.FourStateAI.delete(self) + del self.door + + def setUnlockEvent(self, event): + if self.unlockEvent: + self.ignore(self.unlockEvent) + self.unlockEvent = self.door.getOutputEventName(event) + if self.unlockEvent: + self.accept(self.unlockEvent, self.setIsUnlocked) + + def distributeStateChange(self): + self.door.sendLocksState() + + def setIsUnlocked(self, isUnlocked): + self.setIsOn(isUnlocked) + if not isUnlocked: + self.door.locking() + + def setLockState(self, stateIndex): + if self.stateIndex != stateIndex: + self.fsm.request(self.states[stateIndex]) + + def isUnlocked(self): + return self.isOn() + + +class DistributedDoorEntityAI(DistributedDoorEntityBase.DistributedDoorEntityBase, DistributedEntityAI.DistributedEntityAI, FourStateAI.FourStateAI): + + def __init__(self, level, entId, zoneId=None): + self.entId = entId + self._isGenerated = 0 + self.isOpenInput = None + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.stateDurations[2] = self.secondsOpen + FourStateAI.FourStateAI.__init__(self, self.stateNames, durations=self.stateDurations) + self.setup() + if zoneId is not None: + self.generateWithRequired(zoneId) + return + + def generateWithRequired(self, zoneId): + DistributedEntityAI.DistributedEntityAI.generateWithRequired(self, zoneId) + self._isGenerated = 1 + + def delete(self): + self.takedown() + FourStateAI.FourStateAI.delete(self) + DistributedEntityAI.DistributedEntityAI.delete(self) + + def getLocksState(self): + stateBits = 0 + if hasattr(self, 'locks'): + stateBits = self.locks[0].getLockState() & 15 | self.locks[1].getLockState() << 4 & 240 | self.locks[2].getLockState() << 8 & 3840 + return stateBits + + def sendLocksState(self): + if self._isGenerated: + self.sendUpdate('setLocksState', [self.getLocksState()]) + + def getDoorState(self): + r = ( + self.stateIndex, globalClockDelta.getRealNetworkTime()) + return r + + def getName(self): + return 'door-%s' % (self.entId,) + + def setup(self): + if not hasattr(self, 'unlock0Event'): + self.unlock0Event = None + if not hasattr(self, 'unlock1Event'): + self.unlock1Event = None + if not hasattr(self, 'unlock2Event'): + self.unlock2Event = None + if not hasattr(self, 'unlock3Event'): + self.unlock3Event = None + if not hasattr(self, 'isLock0Unlocked'): + self.isLock0Unlocked = None + if not hasattr(self, 'isLock1Unlocked'): + self.isLock1Unlocked = None + if not hasattr(self, 'isLock2Unlocked'): + self.isLock2Unlocked = None + if not hasattr(self, 'isLock3Unlocked'): + self.isLock3Unlocked = None + self.locks = [Lock(self, 0, self.unlock0Event, self.isLock0Unlocked), Lock(self, 1, self.unlock1Event, self.isLock1Unlocked), Lock(self, 2, self.unlock2Event, self.isLock2Unlocked)] + del self.unlock0Event + del self.unlock1Event + del self.unlock2Event + del self.unlock3Event + del self.isLock0Unlocked + del self.isLock1Unlocked + del self.isLock2Unlocked + del self.isLock3Unlocked + if hasattr(self, 'isOpenEvent'): + self.setIsOpenEvent(self.isOpenEvent) + del self.isOpenEvent + if hasattr(self, 'isOpen'): + self.setIsOpen(self.isOpen) + del self.isOpen + return + + def takedown(self): + self.ignoreAll() + for i in self.locks: + i.takedown() + + del self.locks + + setScale = DistributedDoorEntityBase.stubFunction + setColor = DistributedDoorEntityBase.stubFunction + setModel = DistributedDoorEntityBase.stubFunction + + def setIsOpenEvent(self, event): + if self.isOpenEvent: + self.ignore(self.isOpenEvent) + self.isOpenEvent = self.getOutputEventName(event) + if self.isOpenEvent: + self.accept(self.isOpenEvent, self.setIsOpen) + + def changedOnState(self, isOn): + if hasattr(self, 'entId'): + messenger.send(self.getOutputEventName(), [not isOn]) + + def setIsOpen(self, isOpen): + self.setIsOn(not isOpen) + + def getIsOpen(self): + return not self.getIsOn() + + def setSecondsOpen(self, secondsOpen): + if self.secondsOpen != secondsOpen: + self.secondsOpen = secondsOpen + if secondsOpen < 0.0: + secondsOpen = None + self.stateDurations[2] = secondsOpen + return + + def locking(self): + if self.stateIndex == 1 or self.stateIndex == 2: + self.fsm.request(self.states[3]) + + def setUnlock0Event(self, event): + self.locks[0].setUnlockEvent(event) + + def setUnlock1Event(self, event): + self.locks[1].setUnlockEvent(event) + + def setUnlock2Event(self, event): + self.locks[2].setUnlockEvent(event) + + def setUnlock3Event(self, event): + pass + + def setIsLock0Unlocked(self, unlocked): + self.locks[0].setIsUnlocked(unlocked) + + def setIsLock1Unlocked(self, unlocked): + self.locks[1].setIsUnlocked(unlocked) + + def setIsLock2Unlocked(self, unlocked): + self.locks[2].setIsUnlocked(unlocked) + + def setIsLock3Unlocked(self, unlocked): + pass + + def isUnlocked(self): + isUnlocked = self.locks[0].isUnlocked() and self.locks[1].isUnlocked() and self.locks[2].isUnlocked() + return isUnlocked + + def distributeStateChange(self): + if self._isGenerated: + self.sendUpdate('setDoorState', self.getDoorState()) + + def requestOpen(self): + if self.isUnlocked(): + if self.fsm.getCurrentState() is not self.states[2]: + self.fsm.request(self.states[1]) + + if __dev__: + + def attribChanged(self, attrib, value): + self.takedown() + self.setup() diff --git a/toontown/coghq/DistributedDoorEntityBase.py b/toontown/coghq/DistributedDoorEntityBase.py new file mode 100644 index 0000000..3f66941 --- /dev/null +++ b/toontown/coghq/DistributedDoorEntityBase.py @@ -0,0 +1,30 @@ + + +def stubFunction(*args): + pass + + +class LockBase: + stateNames = ['off', + 'locking', + 'locked', + 'unlocking', + 'unlocked'] + stateDurations = [None, + 3.5, + None, + 4.0, + None] + + +class DistributedDoorEntityBase: + stateNames = ['off', + 'opening', + 'open', + 'closing', + 'closed'] + stateDurations = [None, + 5.0, + 1.0, + 6.0, + None] diff --git a/toontown/coghq/DistributedElevatorMarker.py b/toontown/coghq/DistributedElevatorMarker.py new file mode 100644 index 0000000..16b6799 --- /dev/null +++ b/toontown/coghq/DistributedElevatorMarker.py @@ -0,0 +1,57 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals + +class DistributedElevatorMarker(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedElevatorMarker') + elevatorMarkerModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + DistributedEntity.DistributedEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.rotateNode = self.attachNewNode('rotate') + self.model = None + if __dev__: + self.model = loader.loadModel(self.elevatorMarkerModels[self.modelPath]) + self.model.reparentTo(self.rotateNode) + return + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return diff --git a/toontown/coghq/DistributedElevatorMarkerAI.py b/toontown/coghq/DistributedElevatorMarkerAI.py new file mode 100644 index 0000000..bf06954 --- /dev/null +++ b/toontown/coghq/DistributedElevatorMarkerAI.py @@ -0,0 +1,28 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.task import Task +from otp.level import DistributedEntityAI +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedElevatorMarkerAI(DistributedEntityAI.DistributedEntityAI, NodePath, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedElevatorMarkerAI') + NodePath.__init__(self, node) + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + self.setPos(self.pos) + self.setHpr(self.hpr) + + def delete(self): + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(elevatorMaker) %s' % self.entId) + DistributedEntityAI.DistributedEntityAI.destroy(self) diff --git a/toontown/coghq/DistributedFactory.py b/toontown/coghq/DistributedFactory.py new file mode 100644 index 0000000..10aa96b --- /dev/null +++ b/toontown/coghq/DistributedFactory.py @@ -0,0 +1,173 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import FactoryBase +import FactoryEntityCreator +import FactorySpecs +from otp.level import LevelSpec +from otp.level import LevelConstants +from toontown.toonbase import TTLocalizer +from toontown.coghq import FactoryCameraViews +from direct.controls.ControlManager import CollisionHandlerRayStart +if __dev__: + from otp.level import EditorGlobals + +class DistributedFactory(DistributedLevel.DistributedLevel, FactoryBase.FactoryBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactory') + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + FactoryBase.FactoryBase.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + base.localAvatar.physControls.setCollisionRayHeight(10.0) + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + self.factoryViews = FactoryCameraViews.FactoryCameraViews(self) + base.localAvatar.chatMgr.chatInputSpeedChat.addFactoryMenu() + if __dev__: + bboard.post(EditorGlobals.EditTargetPostName, self) + self.accept('SOSPanelEnter', self.handleSOSPanel) + + def delete(self): + DistributedLevel.DistributedLevel.delete(self) + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.factoryViews.delete() + del self.factoryViews + self.ignore('SOSPanelEnter') + if __dev__: + bboard.removeIfEqual(EditorGlobals.EditTargetPostName, self) + base.localAvatar.physControls.setCollisionRayHeight(CollisionHandlerRayStart) + + def setFactoryId(self, id): + FactoryBase.FactoryBase.setFactoryId(self, id) + + def setForemanConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.ForemanConfrontedMsg % av.getName()) + return + + def setDefeated(self): + self.notify.info('setDefeated') + messenger.send('FactoryWinEvent') + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = FactorySpecs.getFactorySpecModule(self.factoryId) + factorySpec = LevelSpec.LevelSpec(specModule) + if __dev__: + typeReg = self.getEntityTypeReg() + factorySpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.initializeLevel(self, factorySpec) + + def privGotSpec(self, levelSpec): + if __dev__: + if not levelSpec.hasEntityTypeReg(): + typeReg = self.getEntityTypeReg() + levelSpec.setEntityTypeReg(typeReg) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + base.factoryReady = 1 + messenger.send('FactoryReady') + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + modelCount = len(levelSpec.getAllEntIds()) + loader.beginBulkLoad('factory', TTLocalizer.HeadingToFactoryTitle % TTLocalizer.FactoryNames[self.factoryId], modelCount, 1, TTLocalizer.TIP_COGHQ) + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + loader.endBulkLoad('factory') + + def printPos(self = self): + pos = base.localAvatar.getPos(self.getZoneNode(self.lastToonZone)) + h = base.localAvatar.getH(self.getZoneNode(self.lastToonZone)) + print 'factory pos: %s, h: %s, zone %s' % (repr(pos), h, self.lastToonZone) + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nZone: %s' % str(self.lastToonZone) + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + + self.accept('f2', printPos) + base.localAvatar.setCameraCollisionsCanMove(1) + self.acceptOnce('leavingFactory', self.announceLeaving) + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + newSuitIds = [] + for suitId in self.suitIds: + if suitId not in oldSuitIds: + newSuitIds.append(suitId) + + if len(newSuitIds): + + def bringOutOfReserve(suits): + for suit in suits: + if suit: + suit.comeOutOfReserve() + + self.relatedObjectMgrRequest = self.cr.relatedObjectMgr.requestObjects(newSuitIds, bringOutOfReserve) + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 2 + + def getGoonPathId(self): + return 'sellbotFactory' + + def getTaskZoneId(self): + return self.factoryId + + def getBossTaunt(self): + return TTLocalizer.FactoryBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.FactoryBossBattleTaunt diff --git a/toontown/coghq/DistributedFactoryAI.py b/toontown/coghq/DistributedFactoryAI.py new file mode 100644 index 0000000..0ef3e43 --- /dev/null +++ b/toontown/coghq/DistributedFactoryAI.py @@ -0,0 +1,132 @@ +from otp.level import DistributedLevelAI +from direct.directnotify import DirectNotifyGlobal +import cPickle, LevelSuitPlannerAI, FactoryBase +from direct.task import Task +import FactoryEntityCreatorAI, FactorySpecs +from otp.level import LevelSpec +import CogDisguiseGlobals +from toontown.suit import DistributedFactorySuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import DistributedBattleFactoryAI + +class DistributedFactoryAI(DistributedLevelAI.DistributedLevelAI, FactoryBase.FactoryBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactoryAI') + + def __init__(self, air, factoryId, zoneId, entranceId, avIds): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, entranceId, avIds) + FactoryBase.FactoryBase.__init__(self) + self.setFactoryId(factoryId) + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getFactoryCreditMultiplier(self.factoryId) + + def generate(self): + self.notify.info('generate') + self.notify.info('start factory %s %s creation, frame=%s' % (self.factoryId, self.doId, globalClock.getFrameCount())) + if __dev__: + simbase.factory = self + self.notify.info('loading spec') + specModule = FactorySpecs.getFactorySpecModule(self.factoryId) + factorySpec = LevelSpec.LevelSpec(specModule) + if __dev__: + self.notify.info('creating entity type registry') + typeReg = self.getEntityTypeReg() + factorySpec.setEntityTypeReg(typeReg) + self.notify.info('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, factorySpec) + self.notify.info('creating cogs') + cogSpecModule = FactorySpecs.getCogSpecModule(self.factoryId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedFactorySuitAI.DistributedFactorySuitAI, DistributedBattleFactoryAI.DistributedBattleFactoryAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + scenario = 0 + description = '%s|%s|%s|%s' % (self.factoryId, self.entranceId, scenario, self.avIdList) + for avId in self.avIdList: + self.air.writeServerEvent('factoryEntered', avId, description) + + self.notify.info('finish factory %s %s creation' % (self.factoryId, self.doId)) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + if __dev__: + if hasattr(simbase, 'factory') and simbase.factory is self: + del simbase.factory + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + + DistributedLevelAI.DistributedLevelAI.delete(self) + + def getTaskZoneId(self): + return self.factoryId + + def getFactoryId(self): + return self.factoryId + + def d_setForemanConfronted(self, avId): + if avId in self.avIdList: + self.sendUpdate('setForemanConfronted', [avId]) + else: + self.notify.warning('%s: d_setForemanConfronted: av %s not in av list %s' % (self.doId, avId, self.avIdList)) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + + scenario = 0 + description = '%s|%s|%s|%s' % (self.factoryId, self.entranceId, scenario, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('factoryDefeated', avId, description) + + for toon in activeVictors: + simbase.air.questManager.toonDefeatedFactory(toon, self.factoryId, activeVictors) + + return + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds diff --git a/toontown/coghq/DistributedFactoryElevatorExt.py b/toontown/coghq/DistributedFactoryElevatorExt.py new file mode 100644 index 0000000..a0264cf --- /dev/null +++ b/toontown/coghq/DistributedFactoryElevatorExt.py @@ -0,0 +1,91 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer + +class DistributedFactoryElevatorExt(DistributedElevatorExt.DistributedElevatorExt): + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorExt.DistributedElevatorExt.delete(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 setupElevator(self): + self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + self.elevatorModel.reparentTo(render) + 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() + DistributedElevator.DistributedElevator.setupElevator(self) + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setFactoryInteriorZone(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 setFactoryInteriorZoneForce(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': 'factoryInterior', + 'how': 'teleportIn', + '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("setFactoryInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def getDestName(self): + if self.entranceId == 0: + return TTLocalizer.ElevatorSellBotFactory0 + elif self.entranceId == 1: + return TTLocalizer.ElevatorSellBotFactory1 diff --git a/toontown/coghq/DistributedFactoryElevatorExtAI.py b/toontown/coghq/DistributedFactoryElevatorExtAI.py new file mode 100644 index 0000000..d235250 --- /dev/null +++ b/toontown/coghq/DistributedFactoryElevatorExtAI.py @@ -0,0 +1,50 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from toontown.building.ElevatorConstants import * +from toontown.building import DistributedElevatorExtAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task + +class DistributedFactoryElevatorExtAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + + def __init__(self, air, bldg, factoryId, entranceId, antiShuffle=0, minLaff=0): + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg, antiShuffle=antiShuffle, minLaff=minLaff) + self.factoryId = factoryId + self.entranceId = entranceId + + def getEntranceId(self): + return self.entranceId + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + + factoryZone = self.bldg.createFactory(self.factoryId, self.entranceId, players) + for seatIndex in range(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setFactoryInteriorZone', [factoryZone]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + return + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + factoryZone = self.bldg.createFactory(self.factoryId, self.entranceId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setFactoryInteriorZoneForce', [ + factoryZone]) diff --git a/toontown/coghq/DistributedFoodBelt.py b/toontown/coghq/DistributedFoodBelt.py new file mode 100644 index 0000000..b429d73 --- /dev/null +++ b/toontown/coghq/DistributedFoodBelt.py @@ -0,0 +1,352 @@ +from pandac.PandaModules import NodePath, Point3, CollisionTube, CollisionNode +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Wait, LerpPosInterval, ProjectileInterval, Func, SoundInterval +from direct.actor import Actor +from toontown.toonbase import ToontownGlobals +from toontown.coghq.FoodBeltBase import FoodBeltBase + +class DistributedFoodBelt(DistributedObject.DistributedObject, FSM.FSM, FoodBeltBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFoodBelt') + BeltSpeed = 5 + OnDuration = 300 + ToonupBeltSpeed = 1.0 + BeltActorPlayRate = 5.35 + ToonupBeltActorPlayRate = BeltActorPlayRate * ToonupBeltSpeed / BeltSpeed + ToonupModels = ['phase_6/models/golf/picnic_apple.bam', + 'phase_6/models/golf/picnic_cupcake.bam', + 'phase_6/models/golf/picnic_sandwich.bam', + 'phase_6/models/golf/picnic_chocolate_cake.bam'] + ToonupScales = [5, + 5, + 5, + 4] + ToonupZOffsets = [-0.25, + -0.25, + -0, + -0.25] + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedFoodBelt') + self.boss = None + self.bossCogId = 0 + self.index = -1 + self.foodNodes = [] + self.foodIvals = [] + self.foodWaitTimes = [] + self.foodModelDict = {} + self.foodNum = 0 + self.beltActor = None + self.toonupIvals = [] + self.toonupWaitTimes = [] + self.toonupModelDict = {} + self.toonupNum = 0 + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.cleanup() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if self.boss: + self.boss.setBelt(self, self.index) + self.loadAssets() + else: + self.notify.warning('announceGenerate self.boss is None, self.bossCogId = %d' % self.bossCogId) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do.get(bossCogId) + + def setIndex(self, index): + self.index = index + + def setState(self, state): + if state == 'F': + self.demand('Off') + elif state == 'N': + self.demand('On') + elif state == 'I': + self.demand('Inactive') + elif state == 'T': + self.demand('Toonup') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def enterOn(self): + self.beltSoundInterval.loop() + for i in xrange(len(self.foodNodes)): + self.doMethodLater(self.foodWaitTimes[i], self.startFoodMoving, 'start-%d-%d' % (self.index, i), extraArgs=[i]) + + def exitOn(self): + self.beltSoundInterval.finish() + for i in xrange(len(self.foodNodes)): + taskName = 'start-%d-%d' % (self.index, i) + self.removeTask(taskName) + + def enterToonup(self): + self.beltSound.setPlayRate(self.ToonupBeltSpeed / self.BeltSpeed) + self.beltSoundInterval.loop() + for i in xrange(len(self.foodNodes)): + self.removeFood(i) + self.beltActor.setPlayRate(self.ToonupBeltActorPlayRate, 'idle') + self.doMethodLater(self.toonupWaitTimes[i], self.startToonupMoving, 'startToonup-%d-%d' % (self.index, i), extraArgs=[i]) + + def exitToonup(self): + self.beltSoundInterval.finish() + for i in xrange(len(self.foodNodes)): + taskName = 'startToonup-%d-%d' % (self.index, i) + self.removeTask(taskName) + + def enterInactive(self): + for ival in self.foodIvals: + ival.finish() + + for ival in self.toonupIvals: + ival.finish() + + for i in xrange(len(self.foodNodes)): + self.removeFood(i) + self.removeToonup(i) + + if self.beltActor: + self.beltActor.stop() + + def exitInactive(self): + pass + + def startFoodMoving(self, foodIndex): + if foodIndex < len(self.foodIvals): + self.foodIvals[foodIndex].loop() + else: + self.notify.warning('startFoodMoving invalid index %d' % foodIndex) + if self.beltActor: + self.beltActor.loop('idle') + + def startToonupMoving(self, toonupIndex): + if toonupIndex < len(self.toonupIvals): + self.toonupIvals[toonupIndex].loop() + else: + self.notify.warning('startToonupMoving invalid index %d' % toonupIndex) + if self.beltActor: + self.beltActor.loop('idle') + + def loadAssets(self): + self.beltModel = NodePath('beltModel') + self.beltModel.reparentTo(self.boss.geom) + self.startLocator = self.boss.geom.find('**/conveyer_belt_start_%d' % (self.index + 1)) + self.endLocator = self.boss.geom.find('**/conveyer_belt_end_%d' % (self.index + 1)) + center = (self.startLocator.getPos() + self.endLocator.getPos()) / 2.0 + self.beltHeight = center.getZ() + self.beltHeight += 0.1 + center.setZ(0) + self.beltLength = (self.endLocator.getPos() - self.startLocator.getPos()).length() + self.distBetweenFoodNodes = self.beltLength / self.NumFoodNodes + self.notify.debug('setting beltModelPos to %s' % center) + self.beltModel.setPos(center) + self.setupFoodNodes() + self.setupFoodIvals() + self.setupToonupIvals() + if self.index == 0: + self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt1_model') + else: + self.beltActorModel = loader.loadModel('phase_12/models/bossbotHQ/food_belt2_model') + if self.beltActorModel: + self.beltActor = Actor.Actor(self.beltActorModel) + if self.index == 0: + self.beltActor.loadAnims({'idle': 'phase_12/models/bossbotHQ/food_belt1'}) + else: + self.beltActor.loadAnims({'idle': 'phase_12/models/bossbotHQ/food_belt2'}) + self.beltActor.reparentTo(render) + self.beltActor.setPlayRate(self.BeltActorPlayRate, 'idle') + mesh = self.beltActor.find('**/mesh_tide1') + joint = self.beltActor.find('**/uvj_WakeWhiteTide1') + mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.beltActor) + self.beltActor.setPos(self.startLocator.getPos()) + self.beltSound = base.loadSfx('phase_12/audio/sfx/CHQ_FACT_conveyor_belt.wav') + self.beltSound.setLoop(1) + self.beltSoundInterval = SoundInterval(self.beltSound, node=self.beltModel, listenerNode=base.localAvatar, seamlessLoop=True, volume=0.25, cutOff=100) + + def cleanup(self): + for i in xrange(len(self.foodNodes)): + taskName = 'start-%d-%d' % (self.index, i) + self.removeTask(taskName) + + for i in xrange(len(self.foodNodes)): + taskName = 'startToonup-%d-%d' % (self.index, i) + self.removeTask(taskName) + + for ival in self.foodIvals: + ival.finish() + + self.foodIvals = [] + for ival in self.toonupIvals: + ival.finish() + + self.toonupIvals = [] + self.beltSoundInterval.finish() + self.beltActor.delete() + self.beltModel = None + self.removeAllTasks() + self.ignoreAll() + return + + def setupFoodNodes(self): + for i in xrange(self.NumFoodNodes): + newPosIndex = self.NumFoodNodes - 1 - i + yPos = -(self.beltLength / 2.0) + newPosIndex * self.distBetweenFoodNodes + newFoodNode = NodePath('foodNode-%d-%d' % (self.index, i)) + newFoodNode.reparentTo(self.beltModel) + newFoodNode.setPos(0, yPos, self.beltHeight) + debugFood = None + if debugFood: + debugFood.setScale(0.1) + debugFood.reparentTo(newFoodNode) + newFoodNode.setH(180) + self.foodNodes.append(newFoodNode) + + return + + def setupFoodIvals(self): + for i in xrange(len(self.foodNodes)): + foodIval = self.createOneFoodIval(self.foodNodes[i]) + self.foodIvals.append(foodIval) + + def createOneFoodIval(self, foodNode): + foodIndex = self.foodNodes.index(foodNode) + waitTimeForOne = self.distBetweenFoodNodes / self.BeltSpeed + waitTime = waitTimeForOne * foodIndex + self.foodWaitTimes.append(waitTime) + totalTimeToTraverseBelt = self.beltLength / self.BeltSpeed + startPosY = -(self.beltLength / 2.0) + endPosY = self.beltLength / 2.0 + retval = Sequence(Func(self.loadFood, foodIndex), LerpPosInterval(foodNode, duration=totalTimeToTraverseBelt, startPos=Point3(0, startPosY, self.beltHeight), pos=Point3(0, endPosY, self.beltHeight)), ProjectileInterval(foodNode, startPos=Point3(0, endPosY, self.beltHeight), startVel=Point3(0, self.BeltSpeed, 0), endZ=0), Func(self.removeFood, foodIndex)) + return retval + + def loadFood(self, foodIndex): + self.foodNum += 1 + if foodIndex in self.foodModelDict: + foodModel = self.foodModelDict[foodIndex] + foodModel.reparentTo(self.foodNodes[foodIndex]) + colNp = foodModel.find('**/FoodCol*') + colNp.setTag('foodNum', str(self.foodNum)) + else: + foodModelScale = ToontownGlobals.BossbotFoodModelScale + foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood') + foodModel.setScale(foodModelScale) + foodModel.reparentTo(self.foodNodes[foodIndex]) + target = CollisionTube(4, 0, 0, -4, 0, 0, 2) + target.setTangible(0) + colName = 'FoodCol-%d-%d' % (self.index, foodIndex) + targetNode = CollisionNode(colName) + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.WallBitmask) + targetNodePath = foodModel.attachNewNode(targetNode) + targetNodePath.setScale(1.0 / foodModelScale) + targetNodePath.setTag('foodIndex', str(foodIndex)) + targetNodePath.setTag('beltIndex', str(self.index)) + targetNodePath.setTag('foodNum', str(self.foodNum)) + targetNodePath.setZ(targetNodePath.getZ() - 1.5) + self.accept('enter' + colName, self.touchedFood) + self.foodModelDict[foodIndex] = foodModel + + def removeFood(self, foodIndex): + if foodIndex in self.foodModelDict: + foodModel = self.foodModelDict[foodIndex] + foodModel.stash() + + def touchedFood(self, colEntry): + into = colEntry.getIntoNodePath() + try: + beltIndex = int(into.getTag('beltIndex')) + except: + beltIndex = 0 + + try: + foodIndex = int(into.getTag('foodIndex')) + except: + foodIndex = 0 + + try: + foodNum = int(into.getTag('foodNum')) + except: + foodNum = 0 + + if self.boss: + self.boss.localToonTouchedBeltFood(beltIndex, foodIndex, foodNum) + + def setupToonupIvals(self): + for i in xrange(len(self.foodNodes)): + toonupIval = self.createOneToonupIval(self.foodNodes[i]) + self.toonupIvals.append(toonupIval) + + def createOneToonupIval(self, foodNode): + toonupIndex = self.foodNodes.index(foodNode) + waitTimeForOne = self.distBetweenFoodNodes / self.ToonupBeltSpeed + waitTime = waitTimeForOne * toonupIndex + self.toonupWaitTimes.append(waitTime) + totalTimeToTraverseBelt = self.beltLength / self.ToonupBeltSpeed + startPosY = -(self.beltLength / 2.0) + endPosY = self.beltLength / 2.0 + retval = Sequence(Func(self.loadToonup, toonupIndex), LerpPosInterval(foodNode, duration=totalTimeToTraverseBelt, startPos=Point3(0, startPosY, self.beltHeight), pos=Point3(0, endPosY, self.beltHeight)), ProjectileInterval(foodNode, startPos=Point3(0, endPosY, self.beltHeight), startVel=Point3(0, self.BeltSpeed, 0), endZ=0), Func(self.removeToonup, toonupIndex)) + return retval + + def loadToonup(self, toonupIndex): + self.toonupNum += 1 + if toonupIndex in self.toonupModelDict: + toonupModel = self.toonupModelDict[toonupIndex] + toonupModel.reparentTo(self.foodNodes[toonupIndex]) + colNp = toonupModel.find('**/ToonupCol*') + colNp.setTag('toonupNum', str(self.toonupNum)) + else: + toonupModelScale = self.ToonupScales[toonupIndex] + modelName = self.ToonupModels[toonupIndex] + toonupModel = loader.loadModel(modelName) + self.foodNodes[toonupIndex].setZ(self.beltHeight - 0.1) + toonupModel.setZ(self.ToonupZOffsets[toonupIndex]) + toonupModel.setScale(toonupModelScale) + toonupModel.reparentTo(self.foodNodes[toonupIndex]) + target = CollisionTube(4, 0, 0, -4, 0, 0, 2) + target.setTangible(0) + colName = 'ToonupCol-%d-%d' % (self.index, toonupIndex) + targetNode = CollisionNode(colName) + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.WallBitmask) + targetNodePath = toonupModel.attachNewNode(targetNode) + targetNodePath.setScale(1.0 / toonupModelScale) + targetNodePath.setTag('toonupIndex', str(toonupIndex)) + targetNodePath.setTag('beltIndex', str(self.index)) + targetNodePath.setTag('toonupNum', str(self.toonupNum)) + targetNodePath.setZ(targetNodePath.getZ() - 1.5 / toonupModelScale) + self.accept('enter' + colName, self.touchedToonup) + self.toonupModelDict[toonupIndex] = toonupModel + + def removeToonup(self, toonupIndex): + if toonupIndex in self.toonupModelDict: + toonupModel = self.toonupModelDict[toonupIndex] + toonupModel.stash() + + def touchedToonup(self, colEntry): + if base.localAvatar.hp >= base.localAvatar.maxHp: + return + into = colEntry.getIntoNodePath() + try: + beltIndex = int(into.getTag('beltIndex')) + except: + beltIndex = 0 + + try: + toonupIndex = int(into.getTag('toonupIndex')) + except: + toonupIndex = 0 + + try: + toonupNum = int(into.getTag('toonupNum')) + except: + toonupNum = 0 + + if self.boss: + self.boss.localToonTouchedBeltToonup(beltIndex, toonupIndex, toonupNum) diff --git a/toontown/coghq/DistributedFoodBeltAI.py b/toontown/coghq/DistributedFoodBeltAI.py new file mode 100644 index 0000000..38e9e19 --- /dev/null +++ b/toontown/coghq/DistributedFoodBeltAI.py @@ -0,0 +1,71 @@ +from direct.distributed import DistributedObjectAI +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import FoodBeltBase + +class DistributedFoodBeltAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM, FoodBeltBase.FoodBeltBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFoodBeltAI') + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedFoodBeltAI') + self.boss = boss + self.index = index + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def setState(self, state): + self.request(state) + + def d_setState(self, state): + newState = state + if state == 'On': + newState = 'N' + else: + if state == 'Off': + newState = 'F' + else: + if state == 'Inactive': + newState = 'I' + else: + if state == 'Toonup': + newState = 'T' + self.sendUpdate('setState', [newState]) + + def b_setState(self, state): + self.request(state) + self.d_setState(state) + + def turnOn(self): + self.b_setState('On') + + def goInactive(self): + self.b_setState('Inactive') + + def goToonup(self): + self.b_setState('Toonup') + + def enterOn(self): + pass + + def exitOn(slef): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterInactive(self): + pass + + def exitInactive(slef): + pass diff --git a/toontown/coghq/DistributedGagBarrel.py b/toontown/coghq/DistributedGagBarrel.py new file mode 100644 index 0000000..fdbd4a8 --- /dev/null +++ b/toontown/coghq/DistributedGagBarrel.py @@ -0,0 +1,63 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedBarrelBase + +class DistributedGagBarrel(DistributedBarrelBase.DistributedBarrelBase): + + def __init__(self, cr): + self.gagLevelMax = 0 + DistributedBarrelBase.DistributedBarrelBase.__init__(self, cr) + self.numGags = 0 + self.gagScale = 13.0 + + def disable(self): + DistributedBarrelBase.DistributedBarrelBase.disable(self) + self.ignoreAll() + + def delete(self): + if hasattr(self, 'gagModel') and self.gagModel: + self.gagModel.removeNode() + del self.gagModel + DistributedBarrelBase.DistributedBarrelBase.delete(self) + + def applyLabel(self): + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + self.invModels = [] + from toontown.toonbase import ToontownBattleGlobals + for gagTrack in range(len(ToontownBattleGlobals.AvPropsNew)): + itemList = [] + for item in range(len(ToontownBattleGlobals.AvPropsNew[gagTrack])): + itemList.append(invModel.find('**/' + ToontownBattleGlobals.AvPropsNew[gagTrack][item])) + + self.invModels.append(itemList) + + invModel.removeNode() + del invModel + try: + gagTrack = self.getGagTrack() + gagLevel = self.getGagLevel() + self.notify.debug('gagTrack = %s, gagLevel = %s' % (gagTrack, gagLevel)) + self.gagModel = self.invModels[gagTrack][gagLevel] + self.gagModel.reparentTo(self.gagNode) + self.gagModel.setScale(self.gagScale) + self.gagModel.setPos(0, -0.1, 0) + except AttributeError: + self.notify.warning("Gag barrel is missing an attribute, can't apply label.") + + def setNumGags(self, num): + self.numGags = num + if hasattr(self, 'gagModel') and self.gagModel: + if self.numGags == 0: + self.gagModel.setColorScale(0.5, 0.5, 0.5, 1) + else: + self.gagModel.clearColorScale() + + def setGrab(self, avId): + DistributedBarrelBase.DistributedBarrelBase.setGrab(self, avId) + + def resetBarrel(self): + DistributedBarrelBase.DistributedBarrelBase.resetBarrel(self) + if hasattr(self, 'gagModel') and self.gagModel: + self.gagModel.setScale(self.gagScale) diff --git a/toontown/coghq/DistributedGagBarrelAI.py b/toontown/coghq/DistributedGagBarrelAI.py new file mode 100644 index 0000000..c780402 --- /dev/null +++ b/toontown/coghq/DistributedGagBarrelAI.py @@ -0,0 +1,33 @@ +from toontown.toonbase.ToontownBattleGlobals import * +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task + +class DistributedGagBarrelAI(DistributedBarrelBaseAI.DistributedBarrelBaseAI): + + def __init__(self, level, entId): + x = y = z = h = 0 + self.gagLevelMax = 0 + DistributedBarrelBaseAI.DistributedBarrelBaseAI.__init__(self, level, entId) + + def d_setGrab(self, avId): + self.notify.debug('d_setGrab %s' % avId) + self.sendUpdate('setGrab', [avId]) + av = self.air.doId2do.get(avId) + if av: + if not av.hasTrackAccess(self.getGagTrack()): + return + track = self.getGagTrack() + level = self.getGagLevel() + maxGags = av.getMaxCarry() + av.inventory.calcTotalProps() + numGags = av.inventory.totalProps + numReward = min(self.getRewardPerGrab(), maxGags - numGags) + while numReward > 0 and level >= 0: + result = av.inventory.addItem(track, level) + if result <= 0: + level -= 1 + else: + numReward -= 1 + + av.d_setInventory(av.inventory.makeNetString()) diff --git a/toontown/coghq/DistributedGolfGreenGame.py b/toontown/coghq/DistributedGolfGreenGame.py new file mode 100644 index 0000000..e404423 --- /dev/null +++ b/toontown/coghq/DistributedGolfGreenGame.py @@ -0,0 +1,1473 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BattleBlocker +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from direct.distributed.ClockDelta import * +from toontown.golf import BuildGeometry +from direct.gui.DirectGui import * +import random +from direct.showbase import RandomNumGen +import GameSprite3D +from math import pi +import math +import random +import cPickle +from toontown.distributed import DelayDelete +from toontown.toon import ToonHeadFrame +from toontown.battle import BattleParticles +from toontown.battle import MovieUtil +import time +from toontown.toonbase import ToontownTimer + +class DistributedGolfGreenGame(BattleBlocker.BattleBlocker): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfGreenGame') + + def __init__(self, cr): + BattleBlocker.BattleBlocker.__init__(self, cr) + self.blankColor = Vec4(1.0, 1.0, 1.0, 1.0) + self.fullColor = Vec4(0.6, 0.6, 0.6, 1.0) + self.neighborColor = Vec4(0.8, 0.8, 0.8, 1.0) + self.outColor = Vec4(0.0, 0.0, 0.0, 0.0) + self.blackColor = Vec4(0.0, 0.0, 0.0, 1.0) + self.acceptErrorDialog = None + self.doneEvent = 'game Done' + self.sprites = [] + self.controlSprite = None + self.standbySprite = None + self.setupFlag = 0 + self.colorGridFlag = 0 + self.boardIndex = None + self.board = None + self.attackPattern = None + self.tooLowFlag = 0 + self.toonPoints = (Point3(3.0, 13.0, 0.0), + Point3(6.0, 13.0, 0.0), + Point3(-3.0, 13.0, 0.0), + Point3(-6.0, 13.0, 0.0)) + self.joinedToons = [] + self.everJoinedToons = [] + self.flagNextLevel = 0 + self.wildIndex = 8 + self.bombIndex = 7 + self.sizeMult = 1.4 + self.cellSizeX = 1.0 * self.sizeMult + self.cellSizeZ = self.cellSizeX * 0.8 + self.radiusBall = 0.5 * self.cellSizeX + self.gridDimX = 9 + self.gridDimZ = 15 + self.minX = -1.0 * (self.gridDimX + 0.3751) * 0.5 * self.cellSizeX + self.minZ = -self.gridDimZ * 0.1 * self.cellSizeZ + self.newBallX = 0.0 + self.newBallZ = self.minZ + 0.1 * self.sizeMult + self.rangeX = (self.gridDimX + 0.5) * self.cellSizeX + self.rangeZ = self.gridDimZ * self.cellSizeZ + self.maxX = self.minX + self.rangeX + self.maxZ = self.minZ + self.rangeZ + self.sizeX = self.rangeX + self.sizeZ = self.rangeZ + self.isActive = 0 + self.boardsLeft = 0 + self.timeLeft = 0 + self.timeStart = None + self.timeTotal = None + self.timerTask = None + self.timerTaskName = 'golfgreengame timer task' + self.giftId = None + self.holdGiftId = None + self.rollTrack = None + self.zGap = 0.092 + self.screenSizeX = base.a2dRight - base.a2dLeft + self.screenSizeZ = base.a2dTop - base.a2dBottom + self.XtoZ = self.screenSizeX / (self.screenSizeZ * (1.0 - self.zGap * 1.0)) + self.countTimeOld = None + self.countDownRunning = 0 + self.timer = None + self.hasEntered = 0 + self.trackClosed = 0 + self.running = 0 + self.finished = 0 + self.__toonTracks = {} + return + + def disable(self): + self.unload() + self.clearToonTracks() + BattleBlocker.BattleBlocker.disable(self) + + def updateSpritePos(self): + if self.spriteNode.isEmpty(): + return + self.spriteNode.setZ(-self.spriteNotchPos * self.cellSizeZ) + if self.controlSprite: + if not self.controlSprite.isActive: + pass + self.colorGridFlag = 1 + + def lerpSpritePos(self): + if self.spriteNode.isEmpty(): + return + x = self.spriteNode.getX() + y = self.spriteNode.getY() + self.rollTrack = Sequence(LerpPosInterval(self.spriteNode, 0.5, Point3(x, y, -self.spriteNotchPos * self.cellSizeZ))) + if self.controlSprite: + if not self.controlSprite.isActive: + pass + self.colorGridFlag = 1 + self.rollTrack.start() + if self.soundMove: + self.soundMove.play() + messenger.send('wakeup') + + def findLowestSprite(self): + lowest = 100 + for sprite in self.sprites: + if sprite.gridPosZ: + if sprite.gridPosZ < lowest: + lowest = sprite.gridPosZ + + return lowest + + def setup(self): + if not self.setupFlag: + self.setupFlag = 1 + else: + return + self.updateSpritePos() + self.spriteNode.setY(self.radiusBall) + thing = self.model.find('**/item_board') + self.block = self.model1.find('**/minnieCircle') + self.colorRed = (1, 0, 0, 1) + self.colorBlue = (0, 0, 1, 1) + self.colorGreen = (0, 1, 0, 1) + self.colorGhostRed = (1, 0, 0, 0.5) + self.colorGhostBlue = (0, 0, 1, 0.5) + self.colorGhostGreen = (0, 1, 0, 0.5) + self.colorWhite = (1, 1, 1, 1) + self.colorBlack = (0, 0, 0, 1.0) + self.colorShadow = (0, 0, 0, 0.5) + self.lastTime = None + self.running = 0 + self.massCount = 0 + self.foundCount = 0 + self.controlOffsetX = 0.0 + self.controlOffsetZ = 0.0 + self.grid = [] + for countX in range(0, self.gridDimX): + newRow = [] + for countZ in range(self.gridDimZ): + offset = 0 + margin = self.cellSizeX * 0.4375 + if countZ % 2 == 0: + offset = self.cellSizeX * 0.5 + newCell = [None, + countX * self.cellSizeX + self.minX + offset + margin, + countZ * self.cellSizeZ + self.minZ, + countX, + countZ, + None] + groundCircle = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_hole') + groundCircle.reparentTo(self.spriteNode) + if groundCircle == None: + import pdb + pdb.set_trace() + groundCircle.setTransparency(TransparencyAttrib.MAlpha) + groundCircle.setPos(newCell[1], -self.radiusBall, newCell[2]) + groundCircle.setScale(1.2) + groundCircle.setR(90) + groundCircle.setH(-90) + newCell[5] = groundCircle + newCell[5].setColorScale(self.blankColor) + newRow.append(newCell) + + self.grid.append(newRow) + + self.cogSprite = self.addUnSprite(self.block, posX=0.25, posZ=0.5) + self.cogSprite.setColor(self.colorShadow) + self.cogSprite.nodeObj.hide() + self.standbySprite = self.addUnSprite(self.block, posX=0.0, posZ=-3.0) + self.standbySprite.setColor(self.colorShadow) + self.standbySprite.spriteBase.reparentTo(self.frame) + self.standbySprite.spriteBase.setY(self.radiusBall) + self.standbySprite.nodeObj.hide() + self.boardData = [((1, 0, 0), + (4, 0, 1), + (6, 0, 2), + (1, 1, 0)), ((1, 0, 1), + (4, 0, 1), + (6, 0, 1), + (1, 1, 1)), ((1, 0, 2), + (4, 0, 2), + (6, 0, 2), + (1, 1, 2))] + self.attackPatterns = [(0, 1, 2), (0, 0, 1, 1, 2, 2), (0, 1, 0, 2)] + self.winCounter = 0 + self.matchList = [] + self.newBallTime = 5.0 + self.newBallCountUp = 0.0 + self.cogX = 0 + self.cogZ = 0 + self.aimRadian = 0.0 + self.ballLoaded = 0.0 + self.countTime = 10 + self.countDown = self.countTime + return + + def printGrid(self): + printout = ' ' + for columnIndex in range(self.gridDimX - 1, -1, -1): + if columnIndex < 10: + printout += '%s ' % columnIndex + else: + printout += '%s ' % columnIndex + + print printout + for rowIndex in range(self.gridDimZ - 1, -1, -1): + if rowIndex < 10: + printout = 'row %s ' % rowIndex + else: + printout = 'row %s ' % rowIndex + for columnIndex in range(self.gridDimX - 1, -1, -1): + hasSprite = '_' + if self.grid[columnIndex][rowIndex][0]: + hasSprite = 'X' + if rowIndex < 10: + printout += '%s ' % hasSprite + else: + printout += '%s ' % hasSprite + + print printout + + count = 0 + for sprite in self.sprites: + print 'count %s X %s Z %s Color %s' % (count, + sprite.gridPosX, + sprite.gridPosZ, + sprite.colorType) + count += 1 + + def pickLevelPattern(self): + self.boardIndex = random.choice(range(0, len(self.boardData))) + self.board = self.boardData[self.boardIndex] + self.attackPattern = self.attackPatterns[self.boardIndex] + self.attackCounter = 0 + self.spriteNotchPos = 0 + for ball in self.board: + newSprite = self.addSprite(self.block, found=1, color=ball[2]) + self.placeIntoGrid(newSprite, ball[0], self.gridDimZ - 1 - ball[1]) + + self.colorGridFlag = 1 + self.updateSpritePos() + + def load(self): + BattleParticles.loadParticles() + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel') + model1 = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + self.invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + self.model = model + self.model1 = model1 + self.soundFire = base.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.mp3') + self.soundLand = base.loadSfx('phase_4/audio/sfx/MG_maze_pickup.mp3') + self.soundBurst = base.loadSfx('phase_5/audio/sfx/Toon_bodyfall_synergy.mp3') + self.soundBomb = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.soundLose = base.loadSfx('phase_11/audio/sfx/LB_capacitor_discharge_3.mp3') + self.soundWin = base.loadSfx('phase_4/audio/sfx/MG_pairing_match_bonus_both.mp3') + self.soundDone = base.loadSfx('phase_3/audio/sfx/GUI_create_toon_back.mp3') + self.soundMove = base.loadSfx('phase_3.5/audio/sfx/SA_shred.mp3') + background = model.find('**/bg') + itemBoard = model.find('**/item_board') + self.focusPoint = self.baseNode.attachNewNode('GolfGreenGameFrame') + self.frame2D = DirectFrame(scale=1.1, relief=DGG.FLAT, frameSize=(-0.1, + 0.1, + -0.1, + -0.1), frameColor=(0.737, 0.573, 0.345, 0.3)) + gui2 = loader.loadModel('phase_3/models/gui/quit_button') + self.quitButton = DirectButton(parent=self.frame2D, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.95, 1.3, -0.69), image_scale=(0.9, 1.0, 1.0), text=TTLocalizer.BustACogExit, text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0, 0, 0, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=TTLocalizer.DGGGquitButton, text_pos=(0, -0.01), command=self.__leaveGame) + self.quitButton.hide() + self.instructions = DirectFrame(parent=self.frame2D, relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.2, 1.0, 1.0), text=TTLocalizer.GolfGreenGameDirections, text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ALeft, text_wordwrap=16, text_scale=0.06, text_pos=(-0.5, 0.3), pos=(0.0, 0, -0.0)) + self.instructions.hide() + imageCogBall = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_ball_cog') + imageCogBall.setHpr(0, 90, 0) + self.instCogBall = DirectFrame(parent=self.instructions, relief=None, image=imageCogBall, image_color=ToontownGlobals.GlobalDialogColor, image_scale=(0.12, 0.12, 0.12), pos=(0.0, 0, -0.2)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.doneButton = DirectButton(parent=self.instructions, relief=None, image=cancelImageList, command=self.instructions.hide, pos=(0, 0, -0.4)) + self.howToButton = DirectButton(parent=self.frame2D, relief=None, image=(gui2.find('**/QuitBtn_UP'), gui2.find('**/QuitBtn_DN'), gui2.find('**/QuitBtn_RLVR')), pos=(0.95, 1.3, -0.82), image_scale=(0.9, 1.0, 1.0), text=TTLocalizer.BustACogHowto, text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0, 0, 0, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=TTLocalizer.DGGGhowToButton, text_pos=(0, -0.01), command=self.instructions.show) + self.howToButton.hide() + self.timerLabel = DirectLabel(parent=self.frame2D, relief=None, image=gui2.find('**/QuitBtn_UP'), pos=(0.9, 1.3, -0.42), image_scale=(0.5, 1.0, 1.0), text='Timer', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01)) + self.timerLabel.hide() + self.headPanel = loader.loadModel('phase_6/models/golf/headPanel') + self.scoreBoard = DirectFrame(scale=1.0, pos=(0.0, 0, 0.9), relief=DGG.FLAT, parent=aspect2d, frameSize=(-0.35, + 0.35, + -0.05, + 0.05), frameColor=(0.737, 0.573, 0.345, 0.3)) + self.scoreLabel = DirectLabel(parent=self.scoreBoard, relief=None, pos=(0, 0, 0), scale=1.0, text='', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0.0, 0.0, 0.0, 1), text_scale=TTLocalizer.DGGGscoreLabel, text_pos=(0, -0.02)) + self.scoreBoard.hide() + self.bonusBoard = DirectFrame(parent=self.frame2D, relief=None, image_pos=(0, 0, 0.0), image_scale=(0.4, 1, 0.4), image_color=(1, 1, 1, 1), pos=(0.0, 1.5, 0.67), scale=1.0, text='You gotsa bonus fool!', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text0_shadow=(0.0, 0.0, 0.0, 1), text_scale=0.055, text_pos=(0, -0.1), textMayChange=1) + self.bonusBoard.hide() + self.backBoard = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_background') + self.backBoard.setCollideMask(BitMask32.allOff()) + self.backBoard.reparentTo(self.frame) + self.backBoard.setScale(0.3, 0.2, 0.25) + self.backBoard.setHpr(0, -90, 0) + self.backBoard.setPos(0, -1.5, 8.0) + self.backBoard.hide() + base.bb = self.backBoard + self.aimbase = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_shooter') + self.aimbase.setHpr(90, 0, 90) + self.aimbase.setScale(0.3, 0.3, 0.15) + self.aimbase.reparentTo(self.frame) + self.aimbase.setPos(0.0, 0.0, self.minZ + 0.1) + self.aimer = self.aimbase.attachNewNode('GolfGreenGameBase') + aimer = self.aimbase.find('**/moving*') + aimer.reparentTo(self.aimer) + aimer.setPos(0.0, 0.0, 0.0) + base.gi = aimer + self.aimbase.hide() + self.toonPanels = {} + return + + def addToonHeadPanel(self, toon): + tPanels = ToonHeadFrame.ToonHeadFrame(toon, (0.4, 0.4, 0.4, 0.6), self.headPanel) + tPanels.extraData['text_fg'] = (1.0, 1.0, 1.0, 1.0) + tPanels.extraData['text_shadow'] = (0.0, 0.0, 0.0, 1.0) + tPanels.extraData.show() + tPanels.setScale(0.3, 1, 0.7) + tPanels.head.setPos(0, 10, 0.18) + tPanels.head.setScale(0.47, 0.2, 0.2) + tPanels.tag1.setPos(0.3, 10, 0.18) + tPanels.tag1.setScale(0.1283, 0.055, 0.055) + tPanels.tag2.setPos(0, 10, 0.43) + tPanels.tag2.setScale(0.117, 0.05, 0.05) + tPanels.hide() + self.toonPanels[toon.doId] = tPanels + self.arrangeToonHeadPanels() + + def removeToonHeadPanel(self, avId): + if self.toonPanels.has_key(avId): + self.toonPanels[avId].destroy() + del self.toonPanels[avId] + self.arrangeToonHeadPanels() + + def arrangeToonHeadPanels(self): + toonPanelsStart = 0.0 + whichToon = 0 + color = 0 + tpDiff = -0.45 + for panelKey in self.toonPanels: + panel = self.toonPanels[panelKey] + if self.isActive: + panel.show() + else: + panel.hide() + if whichToon <= 1: + panel.setPos(-1, 0, toonPanelsStart + whichToon * tpDiff) + else: + panel.setPos(1, 0, toonPanelsStart + (whichToon - 2) * tpDiff) + whichToon += 1 + + def unload(self): + self.cleanupTimer() + for panelKey in self.toonPanels: + self.toonPanels[panelKey].destroy() + + self.headPanel.remove() + self.toonPanels = None + self.soundFire = None + self.soundLand = None + self.soundBurst = None + self.soundBomb = None + self.soundLose = None + self.soundWin = None + self.soundDone = None + self.soundMove = None + self.scoreBoard.destroy() + self.instructions.destroy() + self.frame2D.destroy() + self.baseNode.removeNode() + del self.baseNode + if self.acceptErrorDialog: + self.acceptErrorDialog.cleanup() + self.acceptErrorDialog = None + self.stopCountDown() + self.__stop() + self.ignoreAll() + return + + def show(self): + self.frame.show() + + def hide(self): + self.frame.hide() + + def __handleExit(self): + self.__acceptExit() + + def __startGame(self): + if not self.setupFlag: + self.setup() + self.quitButton.show() + self.howToButton.show() + self.backBoard.show() + self.aimbase.show() + self.squareNode.show() + self.scoreBoard.show() + self.standbySprite.nodeObj.show() + self.groundFlag.hide() + self.isActive = 1 + self.__setCamera() + self.spriteNode.show() + base.setCellsAvailable([base.bottomCells[1], base.bottomCells[2], base.bottomCells[3]], 0) + self.setupFlag = 1 + + def startBoard(self, board, attackPattern): + if self.finished: + return + self.clearGrid() + self.board = board + self.attackPattern = attackPattern + self.attackCounter = 0 + self.spriteNotchPos = 0 + self.countDown = self.countTime + self.tooLowFlag = 0 + for ball in self.board: + newSprite = self.addSprite(self.block, found=1, color=ball[2]) + self.placeIntoGrid(newSprite, ball[0], self.gridDimZ - 1 - ball[1]) + + self.colorGridFlag = 1 + self.tooLowFlag = 0 + self.startCountDown() + self.updateSpritePos() + self.killSprite(self.controlSprite) + self.accept('mouse1', self.__handleMouseClick) + self.__run() + + def startCountDown(self): + if self.countDownRunning == 0: + taskMgr.add(self.doCountDown, 'GolfGreenGame countdown') + self.countDownRunning = 1 + + def stopCountDown(self): + taskMgr.remove('GolfGreenGame countdown') + self.countDownRunning = 0 + self.countTimeOld = None + return + + def doCountDown(self, task): + currentTime = globalClock.getFrameTime() + if self.countTimeOld == None: + self.countTimeOld = currentTime + if currentTime - self.countTimeOld < 1.0: + return task.cont + else: + self.countTimeOld = currentTime + self.countDown -= 1 + if self.countDown in [3, 2, 1]: + for sprite in self.sprites: + sprite.warningBump() + + elif self.countDown == 0: + self.countDown = self.countTime + self.spriteNotchPos += 1 + self.lerpSpritePos() + self.checkForTooLow() + self.timerLabel['text'] = '%s' % self.countDown + return task.cont + return + + def checkForTooLow(self): + low = self.findLowestSprite() + if low <= self.spriteNotchPos: + self.doFail() + + def doFail(self): + self.tooLowFlag = 1 + taskMgr.doMethodLater(1.0, self.failBoard, 'finishing Failure') + for sprite in self.sprites: + sprite.setColorType(4) + + self.__stop() + self.ignore('mouse1') + + def failBoard(self, task = None): + self.__finishBoard(0) + + def __handleWin(self): + self.__handleExit() + + def __finishBoard(self, success = 1): + if self.rollTrack: + self.rollTrack.finish() + self.countDown = self.countTime + if success: + if self.soundWin: + self.soundWin.play() + elif self.soundLose: + self.soundLose.play() + self.giftId = None + self.attackPattern = None + self.stopCountDown() + self.clearGrid() + self.spriteNotchPos = 0 + self.updateSpritePos() + self.__stop() + self.ignore('mouse1') + if not self.tooLowFlag or 1: + self.sendUpdate('requestBoard', [success]) + return + + def __acceptExit(self, buttonValue = None): + import pdb + pdb.set_trace() + if hasattr(self, 'frame'): + self.hide() + self.unload() + messenger.send(self.doneEvent) + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + + def __removeGame(self): + self.spriteNode.remove() + self.setupFlag = 0 + + def __leaveGame(self): + taskMgr.remove('GolfGreenGameTask') + self.stopCountDown() + taskMgr.remove(self.timerTaskName) + self.ignore('mouse1') + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + base.cr.playGame.getPlace().fsm.request('walk') + for sprite in self.sprites: + sprite.delete() + + self.sprites = [] + self.spriteNode.hide() + self.controlSprite = None + self.running = 0 + self.timerLabel.hide() + self.quitButton.hide() + self.howToButton.hide() + self.backBoard.hide() + self.aimbase.hide() + self.squareNode.hide() + self.groundFlag.show() + self.instructions.hide() + self.isActive = 0 + if self.standbySprite: + self.standbySprite.nodeObj.hide() + base.setCellsAvailable([base.bottomCells[1], base.bottomCells[2], base.bottomCells[3]], 1) + self.sendUpdate('leaveGame', []) + return + + def findGrid(self, x, z, force = 0): + currentClosest = None + currentDist = 10000000 + for countX in range(self.gridDimX): + for countZ in range(self.gridDimZ): + testDist = self.testPointDistanceSquare(x, z, self.grid[countX][countZ][1], self.grid[countX][countZ][2]) + if self.grid[countX][countZ][0] == None and testDist < currentDist and (force or self.hasNeighbor(countX, countZ) != None): + currentClosest = self.grid[countX][countZ] + self.closestX = countX + self.closestZ = countZ + currentDist = testDist + + return currentClosest + + def hasNeighbor(self, cellX, cellZ): + gotNeighbor = None + if cellZ % 2 == 0: + if self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = cellZ - 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ - 1)): + gotNeighbor = cellZ - 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = cellZ + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ + 1)): + gotNeighbor = cellZ + 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = cellZ - 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ - 1)): + gotNeighbor = cellZ - 1 + return gotNeighbor + + def clearFloaters(self): + self.grounded = [] + self.unknown = [] + groundZ = self.gridDimZ - 1 + for indexX in range(0, self.gridDimX): + gridCell = self.grid[indexX][groundZ] + if gridCell[0]: + self.grounded.append((indexX, groundZ)) + + for column in self.grid: + for cell in column: + if cell[0] != None: + cellData = (cell[3], cell[4]) + if cellData not in self.grounded: + self.unknown.append(cellData) + + lastUnknownCount = 0 + while len(self.unknown) != lastUnknownCount: + lastUnknownCount = len(self.unknown) + for cell in self.unknown: + if self.hasGroundedNeighbor(cell[0], cell[1]): + self.unknown.remove(cell) + self.grounded.append(cell) + + for entry in self.unknown: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + self.killSprite(sprite) + + return + + def explodeBombs(self): + didBomb = 0 + for column in self.grid: + for cell in column: + if cell[0] != None: + if cell[0].colorType == self.bombIndex: + self.killSprite(cell[0]) + didBomb += 1 + + if didBomb: + self.soundBomb.play() + return + + def hasGroundedNeighbor(self, cellX, cellZ): + gotNeighbor = None + if cellZ % 2 == 0: + if (cellX - 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX + 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX + 1, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + elif (cellX + 1, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + elif (cellX - 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX + 1, cellZ) in self.grounded: + gotNeighbor = cellZ + elif (cellX, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX - 1, cellZ + 1) in self.grounded: + gotNeighbor = cellZ + 1 + elif (cellX, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + elif (cellX - 1, cellZ - 1) in self.grounded: + gotNeighbor = cellZ - 1 + return gotNeighbor + + def clearMatchList(self, typeClear = 0): + self.soundBurst.play() + for entry in self.matchList: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + if typeClear == self.wildIndex: + self.questionSprite(sprite) + elif typeClear == 0: + pass + self.killSprite(sprite) + + def shakeList(self, neighbors): + for entry in neighbors: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + self.shakeSprite(sprite) + + def createMatchList(self, x, z): + self.matchList = [] + self.fillMatchList(x, z) + + def matchWild(self, x, z, color): + spriteType = self.getColorType(x, z) + if not self.getBreakable(x, z): + return 0 + elif spriteType != -1 and spriteType == self.wildIndex: + return 1 + elif spriteType != -1 and color == self.wildIndex: + return 1 + else: + return 0 + + def bombNeighbors(self, cellX, cellZ): + self.soundBomb.play() + self.matchList = [] + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) != -1: + self.addToMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) != -1: + self.addToMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) != -1: + self.addToMatchList(cellX, cellZ + 1) + if self.getColorType(cellX + 1, cellZ + 1) != -1: + self.addToMatchList(cellX + 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) != -1: + self.addToMatchList(cellX, cellZ - 1) + if self.getColorType(cellX + 1, cellZ - 1) != -1: + self.addToMatchList(cellX + 1, cellZ - 1) + else: + if self.getColorType(cellX - 1, cellZ) != -1: + self.addToMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) != -1: + self.addToMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) != -1: + self.addToMatchList(cellX, cellZ + 1) + if self.getColorType(cellX - 1, cellZ + 1) != -1: + self.addToMatchList(cellX - 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) != -1: + self.addToMatchList(cellX, cellZ - 1) + if self.getColorType(cellX - 1, cellZ - 1) != -1: + self.addToMatchList(cellX - 1, cellZ - 1) + + def addToMatchList(self, posX, posZ): + if self.getBreakable(posX, posZ) > 0: + self.matchList.append((posX, posZ)) + + def getNeighbors(self, cellX, cellZ): + neighborList = [] + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) != -1: + neighborList.append((cellX - 1, cellZ)) + if self.getColorType(cellX + 1, cellZ) != -1: + neighborList.append((cellX + 1, cellZ)) + if self.getColorType(cellX, cellZ + 1) != -1: + neighborList.append((cellX, cellZ + 1)) + if self.getColorType(cellX + 1, cellZ + 1) != -1: + neighborList.append((cellX + 1, cellZ + 1)) + if self.getColorType(cellX, cellZ - 1) != -1: + neighborList.append((cellX, cellZ - 1)) + if self.getColorType(cellX + 1, cellZ - 1) != -1: + neighborList.append((cellX + 1, cellZ - 1)) + else: + if self.getColorType(cellX - 1, cellZ) != -1: + neighborList.append((cellX - 1, cellZ)) + if self.getColorType(cellX + 1, cellZ) != -1: + neighborList.append((cellX + 1, cellZ)) + if self.getColorType(cellX, cellZ + 1) != -1: + neighborList.append((cellX, cellZ + 1)) + if self.getColorType(cellX - 1, cellZ + 1) != -1: + neighborList.append((cellX - 1, cellZ + 1)) + if self.getColorType(cellX, cellZ - 1) != -1: + neighborList.append((cellX, cellZ - 1)) + if self.getColorType(cellX - 1, cellZ - 1) != -1: + neighborList.append((cellX - 1, cellZ - 1)) + return neighborList + + def fillMatchList(self, cellX, cellZ): + if (cellX, cellZ) in self.matchList: + return + self.matchList.append((cellX, cellZ)) + colorType = self.grid[cellX][cellZ][0].colorType + if colorType == 4: + return + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) == colorType or self.matchWild(cellX - 1, cellZ, colorType): + self.fillMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) == colorType or self.matchWild(cellX + 1, cellZ, colorType): + self.fillMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) == colorType or self.matchWild(cellX, cellZ + 1, colorType): + self.fillMatchList(cellX, cellZ + 1) + if self.getColorType(cellX + 1, cellZ + 1) == colorType or self.matchWild(cellX + 1, cellZ + 1, colorType): + self.fillMatchList(cellX + 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) == colorType or self.matchWild(cellX, cellZ - 1, colorType): + self.fillMatchList(cellX, cellZ - 1) + if self.getColorType(cellX + 1, cellZ - 1) == colorType or self.matchWild(cellX + 1, cellZ - 1, colorType): + self.fillMatchList(cellX + 1, cellZ - 1) + else: + if self.getColorType(cellX - 1, cellZ) == colorType or self.matchWild(cellX - 1, cellZ, colorType): + self.fillMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) == colorType or self.matchWild(cellX + 1, cellZ, colorType): + self.fillMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) == colorType or self.matchWild(cellX, cellZ + 1, colorType): + self.fillMatchList(cellX, cellZ + 1) + if self.getColorType(cellX - 1, cellZ + 1) == colorType or self.matchWild(cellX - 1, cellZ + 1, colorType): + self.fillMatchList(cellX - 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) == colorType or self.matchWild(cellX, cellZ - 1, colorType): + self.fillMatchList(cellX, cellZ - 1) + if self.getColorType(cellX - 1, cellZ - 1) == colorType or self.matchWild(cellX - 1, cellZ - 1, colorType): + self.fillMatchList(cellX - 1, cellZ - 1) + + def testGridfull(self, cell): + if not cell: + return 0 + elif cell[0] != None: + return 1 + else: + return 0 + return + + def getValidGrid(self, x, z): + if x < 0 or x >= self.gridDimX: + return None + elif z < 0 or z >= self.gridDimZ: + return None + else: + return self.grid[x][z] + return None + + def getColorType(self, x, z): + if x < 0 or x >= self.gridDimX: + return -1 + elif z < 0 or z >= self.gridDimZ: + return -1 + elif self.grid[x][z][0] == None: + return -1 + else: + return self.grid[x][z][0].colorType + return + + def getBreakable(self, x, z): + if x < 0 or x >= self.gridDimX: + return -1 + elif z < 0 or z >= self.gridDimZ: + return -1 + elif self.grid[x][z][0] == None: + return -1 + else: + return self.grid[x][z][0].breakable + return + + def findGridCog(self): + self.cogX = 0 + self.cogZ = 0 + self.massCount = 0 + for row in self.grid: + for cell in row: + if cell[0] != None: + self.cogX += cell[1] + self.cogZ += cell[2] + self.massCount += 1 + + if self.massCount > 0: + self.cogX = self.cogX / self.massCount + self.cogZ = self.cogZ / self.massCount + self.cogSprite.setX(self.cogX) + self.cogSprite.setZ(self.cogZ) + return + + def doOnClearGrid(self): + self.winCounter += 1 + self.clearGrid() + self.flagNextLevel = 1 + if self.winCounter > 4: + self.__handleWin() + + def clearGrid(self): + for row in self.grid: + for cell in row: + if cell[0] != None: + self.killSprite(cell[0]) + cell[5].setColorScale(self.blankColor) + + self.killSprite(self.controlSprite) + return + + def killSprite(self, sprite): + if sprite == None: + return + if sprite.giftId != None: + self.giftId = sprite.giftId + if sprite.foundation: + self.foundCount -= 1 + if self.controlSprite == sprite: + self.controlSprite = None + if sprite in self.sprites: + self.sprites.remove(sprite) + if sprite.gridPosX != None: + self.grid[sprite.gridPosX][sprite.gridPosZ][0] = None + self.grid[sprite.gridPosX][sprite.gridPosZ][5].setColorScale(self.blankColor) + sprite.deathEffect() + sprite.delete() + self.hasChanged = 1 + return + + def shakeSprite(self, sprite): + if sprite == None: + return + sprite.shake() + return + + def questionSprite(self, sprite): + newSprite = self.addSprite(self.block, found=0, color=1) + newSprite.setX(sprite.getX()) + newSprite.setZ(sprite.getZ()) + newSprite.wildEffect() + + def colorGrid(self): + for row in self.grid: + for cell in row: + if cell[0] != None: + if cell[0].colorType == 3: + cell[5].setColorScale(self.blackColor) + else: + cell[5].setColorScale(self.fullColor) + elif cell[4] <= self.spriteNotchPos: + cell[5].setColorScale(self.outColor) + elif self.hasNeighbor(cell[3], cell[4]): + cell[5].setColorScale(self.neighborColor) + else: + cell[5].setColorScale(self.blankColor) + + return + + def findPos(self, x, z): + return (self.grid[x][z][1], self.grid[x][z][2]) + + def placeIntoGrid(self, sprite, x, z): + if self.grid[x][z][0] == None: + self.grid[x][z][0] = sprite + sprite.gridPosX = x + sprite.gridPosZ = z + sprite.setActive(0) + newX, newZ = self.findPos(x, z) + sprite.setX(newX) + sprite.setZ(newZ) + if sprite == self.controlSprite: + self.controlSprite = None + self.colorGridFlag = 1 + self.hasChanged = 1 + self.findGridCog() + self.checkForTooLow() + else: + self.placeIntoGrid(sprite, x + 1, z - 1) + return + + def stickInGrid(self, sprite, force = 0): + if sprite.isActive: + gridCell = self.findGrid(sprite.getX(), sprite.getZ(), force) + if gridCell: + colorType = sprite.colorType + sprite.setActive(0) + self.soundLand.play() + self.placeIntoGrid(sprite, gridCell[3], gridCell[4]) + if colorType == self.bombIndex: + kapow = MovieUtil.createKapowExplosionTrack(render, sprite.nodeObj.getPos(render)) + kapow.start() + self.bombNeighbors(self.closestX, self.closestZ) + allNeighbors = [] + for entry in self.matchList: + neighbors = self.getNeighbors(entry[0], entry[1]) + for neighbor in neighbors: + if neighbor not in allNeighbors and neighbor not in self.matchList: + allNeighbors.append(neighbor) + + self.shakeList(allNeighbors) + self.clearMatchList() + else: + self.createMatchList(self.closestX, self.closestZ) + if len(self.matchList) >= 3: + clearType = 0 + self.clearMatchList(colorType) + else: + neighbors = self.getNeighbors(self.closestX, self.closestZ) + self.shakeList(neighbors) + + def addSprite(self, image, size = 3.0, posX = 0, posZ = 0, found = 0, color = None): + spriteBase = self.spriteNode.attachNewNode('sprite base') + size = self.radiusBall * 2.0 + facing = 1 + if color == None: + colorChoice = random.choice(range(0, 3)) + else: + colorChoice = color + newSprite = GameSprite3D.GameSprite(spriteBase, size, colorChoice, found, facing) + newSprite.setX(posX) + newSprite.setZ(posZ) + self.sprites.append(newSprite) + if found: + self.foundCount += 1 + return newSprite + + def addControlSprite(self, x = 0.0, z = 0.0, color = None): + newSprite = self.addSprite(self.block, posX=x, posZ=z, color=color, found=1) + newSprite.spriteBase.reparentTo(self.frame) + newSprite.spriteBase.setPos(0.0, 0.7, -1.54) + self.controlSprite = newSprite + + def addUnSprite(self, image, size = 3.0, posX = 0, posZ = 0): + size = self.radiusBall * 2.0 + spriteBase = self.spriteNode.attachNewNode('sprite base') + newSprite = GameSprite3D.GameSprite(spriteBase, size) + newSprite.setX(posX) + newSprite.setZ(posZ) + return newSprite + + def __handleMouseClick(self): + if self.ballLoaded == 2: + pass + if self.ballLoaded and self.controlSprite: + self.controlSprite.spriteBase.wrtReparentTo(self.spriteNode) + self.controlSprite.setAccel(14.0, pi * 0.0 - self.aimRadian) + self.controlSprite.setActive(1) + self.soundFire.play() + self.ballLoaded = 0 + + def __run(self, cont = 1): + if cont and not self.running: + taskMgr.add(self.__run, 'GolfGreenGameTask') + self.running = 1 + if self.lastTime == None: + self.lastTime = globalClock.getRealTime() + timeDelta = globalClock.getRealTime() - self.lastTime + self.lastTime = globalClock.getRealTime() + self.newBallCountUp += timeDelta + if base.mouseWatcherNode.hasMouse(): + inputX = base.mouseWatcherNode.getMouseX() + inputZ = base.mouseWatcherNode.getMouseY() + outputZ = inputZ + self.screenSizeZ * (0.5 - self.zGap) + if outputZ <= 0.0: + outputZ = 0.0001 + if inputX > 0.0: + self.aimRadian = -1.0 * pi + math.atan(outputZ / (inputX * self.XtoZ)) + elif inputX < 0.0: + self.aimRadian = math.atan(outputZ / (inputX * self.XtoZ)) + else: + self.aimRadian = pi * -0.5 + margin = 0.2 + if self.aimRadian >= -margin: + self.aimRadian = -margin + elif self.aimRadian <= margin - pi: + self.aimRadian = margin - pi + degrees = self.__toDegrees(self.aimRadian) + self.aimer.setH(degrees) + self.wallMaxX = self.maxX - self.radiusBall + self.wallMinX = self.minX + self.radiusBall + self.wallMaxZ = self.maxZ - self.radiusBall + self.wallMinZ = self.minZ + self.radiusBall + if self.controlSprite and self.controlSprite.nodeObj.isEmpty(): + self.controlSprite = None + if self.giftId: + self.ballLoaded = 2 + self.updateSpritePos() + self.standbySprite.holdType = self.giftId + self.standbySprite.setBallType(self.giftId, 1) + self.standbySprite.face() + self.giftId = None + while self.controlSprite == None and self.attackPattern: + if self.attackCounter > len(self.attackPattern) - 1: + self.attackCounter = 0 + print 'Pattern %s Place %s Type %s' % (self.attackPattern, self.attackCounter, self.attackPattern[self.attackCounter]) + if self.standbySprite.holdType != None: + color = self.standbySprite.holdType + sprite = self.addControlSprite(self.newBallX, self.newBallZ + self.spriteNotchPos * self.cellSizeZ, color) + self.ballLoaded = 1 + self.updateSpritePos() + newColor = self.predictAttackPattern(0) + self.standbySprite.holdType = newColor + self.standbySprite.setBallType(newColor, 1) + self.standbySprite.face() + self.attackCounter += 1 + + self.standbySprite.runColor() + for sprite in self.sprites: + if sprite.deleteFlag: + self.sprites.remove(sprite) + else: + sprite.run(timeDelta) + if sprite.getX() > self.wallMaxX: + sprite.setX(self.wallMaxX) + sprite.reflectX() + if sprite.getX() < self.wallMinX: + sprite.setX(self.wallMinX) + sprite.reflectX() + if sprite.getZ() > self.wallMaxZ: + self.stickInGrid(sprite, 1) + if sprite.getZ() < self.wallMinZ: + pass + + self.__colTest() + if self.hasChanged and self.running: + self.clearFloaters() + self.explodeBombs() + self.findGridCog() + spriteCount = 0 + whiteCount = 0 + for row in self.grid: + for cell in row: + if cell[0] != None: + self.cogX += cell[1] + self.cogZ += cell[2] + spriteCount += 1 + if cell[0].colorType == 3: + whiteCount += 1 + + if whiteCount == 0: + self.__finishBoard() + self.flagNextLevel = 0 + self.killSprite(self.controlSprite) + self.standbySprite.holdType = None + self.colorGridFlag = 1 + self.hasChanged = 0 + if self.colorGridFlag: + self.colorGridFlag = 0 + self.colorGrid() + return Task.cont + + def predictAttackPattern(self, numSteps = 1): + predict = self.attackCounter + numSteps + predict = predict % len(self.attackPattern) + return self.attackPattern[predict] + + def __stop(self): + taskMgr.remove('GolfGreenGameTask') + self.running = 0 + + def __testWin(self): + gridCount = 0 + for column in self.grid: + for cell in column: + if cell[0]: + gridCount += 1 + + if gridCount == 0: + self.__handleWin() + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __colTest(self): + if not hasattr(self, 'tick'): + self.tick = 0 + self.tick += 1 + if self.tick > 5: + self.tick = 0 + sizeSprites = len(self.sprites) + for movingSpriteIndex in range(len(self.sprites)): + for testSpriteIndex in range(movingSpriteIndex, len(self.sprites)): + movingSprite = self.getSprite(movingSpriteIndex) + testSprite = self.getSprite(testSpriteIndex) + if testSprite and movingSprite: + if movingSpriteIndex != testSpriteIndex and (movingSprite.isActive or testSprite.isActive): + if self.testDistance(movingSprite.spriteBase, testSprite.spriteBase) < self.radiusBall * 1.65: + if not (movingSprite.isActive and testSprite.isActive): + if movingSprite.canCollide and testSprite.canCollide: + self.__collide(movingSprite, testSprite) + if self.tick == 5: + pass + + def getSprite(self, spriteIndex): + if spriteIndex >= len(self.sprites) or self.sprites[spriteIndex].markedForDeath: + return None + else: + return self.sprites[spriteIndex] + return None + + def testDistance(self, nodeA, nodeB): + if nodeA.isEmpty() or nodeB.isEmpty(): + return 10000 + distX = nodeA.getX() - nodeB.getX() + distZ = nodeA.getZ() - nodeB.getZ() + distC = distX * distX + distZ * distZ + dist = math.sqrt(distC) + return dist + + def testPointDistance(self, x1, z1, x2, z2): + distX = x1 - x2 + distZ = z1 - z2 + distC = distX * distX + distZ * distZ + dist = math.sqrt(distC) + if dist == 0: + dist = 1e-10 + return dist + + def testPointDistanceSquare(self, x1, z1, x2, z2): + distX = x1 - x2 + distZ = z1 - z2 + distC = distX * distX + distZ * distZ + if distC == 0: + distC = 1e-10 + return distC + + def angleTwoSprites(self, sprite1, sprite2): + x1 = sprite1.getX() + z1 = sprite1.getZ() + x2 = sprite2.getX() + z2 = sprite2.getZ() + x = x2 - x1 + z = z2 - z1 + angle = math.atan2(-x, z) + return angle + pi * 0.5 + + def angleTwoPoints(self, x1, z1, x2, z2): + x = x2 - x1 + z = z2 - z1 + angle = math.atan2(-x, z) + return angle + pi * 0.5 + + def __collide(self, move, test): + test.velX = 0 + test.velZ = 0 + move.velX = 0 + move.velZ = 0 + test.collide() + move.collide() + self.stickInGrid(move) + self.stickInGrid(test) + + def generateInit(self): + self.notify.debug('generateInit') + BattleBlocker.BattleBlocker.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BattleBlocker.BattleBlocker.announceGenerate(self) + self.baseNode = self.attachNewNode('GolfGreenGameBase') + self.frame = self.baseNode.attachNewNode('GolfGreenGameFrame') + self.spriteNode = self.frame.attachNewNode('GolfGreenGameSpriteNode') + self.frame.setScale(1.0) + self.frame.setP(90) + self.spriteNotchPos = 0 + self.frame.setY(10.0) + self.frame.setZ(2.0) + self.spriteNode.setY(0.5) + self.hasChanged = 0 + self.squareNode = self.frame.attachNewNode('GolfGreenGameBase') + groundCircle = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_golf_green') + groundCircle.reparentTo(self.baseNode) + groundCircle.setScale(0.24) + self.groundFlag = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_golf_flag') + self.groundFlag.reparentTo(self.baseNode) + self.groundFlag.setScale(0.5) + self.groundFlag.setH(-45) + self.groundFlag.setPos(3.0, 4.0, 0.0) + groundSquare = BuildGeometry.addSquareGeom(self.squareNode, self.sizeX, self.sizeZ, color=Vec4(0.4, 0.4, 0.4, 0.5)) + self.centerZ = (self.minZ + self.maxZ) * 0.5 + self.squareNode.setZ((self.minZ + self.maxZ) * 0.5) + self.squareNode.setP(-90) + groundCircle.setDepthWrite(False) + groundCircle.setDepthTest(True) + groundCircle.setBin('ground', 1) + groundSquare[0].setDepthWrite(False) + groundSquare[0].setDepthTest(False) + groundSquare[0].setBin('ground', 2) + self.squareNode.hide() + self.load() + + def initCollisionGeom(self): + self.actSphere = CollisionSphere(0, 0, 0, 11.5) + self.actSphereNode = CollisionNode('gridgame-%s-%s' % (self.level.getLevelId(), self.entId)) + self.actSphereNode.addSolid(self.actSphere) + self.actSphereNodePath = self.attachNewNode(self.actSphereNode) + self.actSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.actSphere.setTangible(0) + self.enterEvent = 'enter' + self.actSphereNode.getName() + self.accept(self.enterEvent, self.__handleToonEnter) + + def __handleToonEnter(self, collEntry): + self.sendUpdate('requestJoin', []) + + def __setCamera(self): + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -16.0, 16.0) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.stopUpdateSmartCamera() + basePos = self.frame.getPos(render) + modPos = Point3(basePos[0] + 0.0, basePos[1] + 12.0, basePos[2] + 12.0) + camera.setPos(0, 0, 0) + camera.setH(0) + camera.setP(-70) + camera.reparentTo(self.focusPoint) + base.camLens.setFov(60, 46.8265) + self.focusPoint.setPos(0, 12, 27) + self.focusPoint.setH(180) + + def acceptJoin(self, time, timeStamp, avIds): + self.timeStart = timeStamp + timePassed = globalClockDelta.localElapsedTime(self.timeStart) + timeleft = time - timePassed + self.timeTotal = time + if localAvatar.doId in avIds and localAvatar.doId not in self.joinedToons: + self.__startGame() + base.cr.playGame.getPlace().fsm.request('stopped') + self.sendUpdate('requestBoard', [0]) + if not self.hasEntered: + self.level.countryClub.showInfoText(TTLocalizer.BustACogInstruction) + self.hasEntered = 1 + for avId in self.joinedToons: + if avId not in avIds: + self.joinedToons.remove(avId) + self.removeToonHeadPanel(avId) + toon = base.cr.doId2do.get(avId) + if toon: + toon.startSmooth() + + for avId in avIds: + if avId and avId not in self.joinedToons: + if avId not in self.everJoinedToons: + self.everJoinedToons.append(avId) + self.joinedToons.append(avId) + index = self.everJoinedToons.index(avId) + if index > 3: + print 'ERROR! green game has had more than 4 players, we are about to crash\n %s' % self.everJoinedToons + print 'Joining Toon is %s index is %s' % (avId, index) + toon = base.cr.doId2do.get(avId) + selfPos = self.getPos(render) + offset = self.toonPoints[index] + if index > 3: + print 'odd... we should have crashed by now' + standPoint = render.getRelativePoint(self, offset) + if toon: + toon.stopSmooth() + self.addToonHeadPanel(toon) + toon.setAnimState('run', 1.0) + animFunc = Func(toon.setAnimState, 'neutral', 1.0) + track = Sequence(LerpPosInterval(toon, 0.75, standPoint), LerpHprInterval(toon, 0.25, Point3(180, 0, 0)), animFunc, Func(self.clearToonTrack, avId), name=toon.uniqueName('gggEnter'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'GolfGreenGame.acceptJoin') + self.storeToonTrack(avId, track) + track.start() + + def signalDone(self, success): + self.finished = 1 + self.soundDone.play() + self.__leaveGame() + self.__removeGame() + self.scoreBoard.hide() + self.cleanupTimer() + if success: + self.level.countryClub.showInfoText(TTLocalizer.BustACogSuccess) + else: + self.level.countryClub.showInfoText(TTLocalizer.BustACogFailure) + + def boardCleared(self, avId): + self.doFail() + + def setTimerStart(self, time, timeStamp): + if self.timer == None: + self.timeStart = timeStamp + timePassed = globalClockDelta.localElapsedTime(self.timeStart) + timeleft = time - timePassed + self.timeTotal = time + self.cleanupTimer() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posBelowTopRightCorner() + self.timer.setTime(timeleft) + self.timer.countdown(timeleft, self.timerExpired) + return + + def cleanupTimer(self): + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + return + + def timerExpired(self): + self.cleanupTimer() + + def useTime(self, time = None): + if time != None: + self.timeLeft = time + if self.timerTask != None: + taskMgr.remove(self.timerTaskName) + if time != None and time > 0.0 and self.isActive: + self.timerTask = taskMgr.doMethodLater(1.0, self.gameCountDown, self.timerTaskName) + self.scoreLabel['text'] = TTLocalizer.GolfGreenGameScoreString % (self.boardsLeft, int(self.timeLeft)) + return + + def gameCountDown(self, task): + self.timeLeft = self.timeTotal - globalClockDelta.localElapsedTime(self.timeStart) + return task.done + + def scoreData(self, total = 2, closed = 1, scoreList = 'hello world'): + self.boardsLeft = total - closed + for panelIndex in self.toonPanels: + panel = self.toonPanels[panelIndex] + panel.extraData['text'] = TTLocalizer.GolfGreenGamePlayerScore % 0 + + for entryIndex in range(len(scoreList)): + entry = scoreList[entryIndex] + if self.toonPanels.has_key(entry[0]): + panel = self.toonPanels[entry[0]] + panel.extraData['text'] = TTLocalizer.GolfGreenGamePlayerScore % entry[1] + + self.scoreLabel['text'] = TTLocalizer.GolfGreenGameScoreString % self.boardsLeft + + def informGag(self, track, level): + self.bonusBoard.show() + self.bonusBoard['text'] = TTLocalizer.GolfGreenGameBonusGag % TTLocalizer.BattleGlobalAvPropStringsSingular[track][level] + iconName = ToontownBattleGlobals.AvPropsNew[track][level] + icon = self.invModel.find('**/%s' % iconName) + self.bonusBoard['image'] = icon + self.bonusBoard['image_scale'] = (1.0, 1, 1.0) + taskMgr.doMethodLater(4.0, self.hideBonusBoard, 'hide bonus') + + def helpOthers(self, avId): + if not avId == localAvatar.doId and self.running: + self.giftId = 7 + toonName = '' + toon = base.cr.doId2do[avId] + if toon: + toonName = toon.getName() + self.bonusBoard['text'] = TTLocalizer.GolfGreenGameGotHelp % toonName + imageBall = loader.loadModel('phase_12/models/bossbotHQ/bust_a_cog_ball_fire') + imageBall.setHpr(0, 90, 0) + self.bonusBoard['image'] = imageBall + self.bonusBoard['image_scale'] = 0.13 + self.bonusBoard.show() + taskMgr.doMethodLater(4.0, self.hideBonusBoard, 'hide bonus') + + def hideBonusBoard(self, task): + if self.bonusBoard: + if not self.bonusBoard.isEmpty(): + self.bonusBoard.hide() + + 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/coghq/DistributedGolfGreenGameAI.py b/toontown/coghq/DistributedGolfGreenGameAI.py new file mode 100644 index 0000000..0cf3ff7 --- /dev/null +++ b/toontown/coghq/DistributedGolfGreenGameAI.py @@ -0,0 +1,338 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.task import Task +from otp.level import DistributedEntityAI +from otp.level import BasicEntities +from toontown.coghq import BattleBlockerAI +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownBattleGlobals +from GolfGreenGameGlobals import * +import random, time + +class DistributedGolfGreenGameAI(BattleBlockerAI.BattleBlockerAI, NodePath, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + BattleBlockerAI.BattleBlockerAI.__init__(self, level, entId) + random.seed(time.time() * entId) + node = hidden.attachNewNode('DistributedLaserFieldAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + self.gridScale = 1 + self.enabled = 1 + self.hasShownSuits = 0 + self.healReady = 1 + self.playedSound = 0 + self.canButton = 1 + self.allBoardsClear = 0 + self.challengeDefeated = False + self.title = 'MemTag: This is a golfGreenGame %s' % random.random() + self.translateData = {} + self.translateData['r'] = 0 + self.translateData['b'] = 1 + self.translateData['g'] = 2 + self.translateData['w'] = 3 + self.translateData['k'] = 4 + self.translateData['l'] = 5 + self.translateData['y'] = 6 + self.translateData['o'] = 7 + self.translateData['a'] = 8 + self.translateData['s'] = 9 + self.translateData['R'] = 10 + self.translateData['B'] = 11 + self.preData = [] + self.boardList = [] + self.joinedToons = [] + self.everJoinedToons = [] + self.startTime = None + self.totalTime = 1180.0 + self.DamageOnFailure = 20.0 + return + + def announceGenerate(self): + BattleBlockerAI.BattleBlockerAI.announceGenerate(self) + self.totalTime = self.timeToPlay + numToons = 0 + if hasattr(self, 'level'): + numToons = len(self.level.presentAvIds) + numBoards = self.puzzleBase + numToons * self.puzzlePerPlayer + boardSelect = range(0, len(gameBoards)) + didGetLast = 1 + for index in range(numBoards): + choice = random.choice(boardSelect) + if not didGetLast: + didGetLast = 1 + choice = len(gameBoards) - 1 + self.preData.append(gameBoards[choice]) + boardSelect.remove(choice) + self.boardList.append([[], index, index, None]) + + self.boardData = [] + self.attackPatterns = [] + self.processPreData() + return + + def processPreData(self): + for board in self.preData: + x = [] + for rowIndex in range(1, len(board)): + for columnIndex in range(len(board[rowIndex])): + color = self.translateData.get(board[rowIndex][columnIndex]) + if color != None: + x.append((len(board[rowIndex]) - (columnIndex + 1), rowIndex - 1, color)) + + self.boardData.append(x) + + for board in self.preData: + attackString = board[0] + attackPattern = [] + for ball in attackString: + color = self.translateData.get(ball) + if color or color == 0: + place = random.choice(range(0, len(attackPattern) + 1)) + attackPattern.insert(place, color) + place = random.choice(range(0, len(attackPattern) + 1)) + attackPattern.insert(place, color) + + self.attackPatterns.append(attackPattern) + + return + + def startTimer(self): + self.startTime = globalClockDelta.getFrameNetworkTime() + taskMgr.doMethodLater(self.totalTime, self.__handleTimeOut, self.taskName('GolfGreenGameTimeout')) + self.sendUpdate('setTimerStart', [self.totalTime, self.startTime]) + + def __printTime(self, task): + print 'Time Left %s' % self.getTimeLeft() + taskMgr.doMethodLater(1.0, self.__printTime, self.taskName('GolfGreenGameTimeout Print')) + return task.done + + def __handleTimeOut(self, task=None): + taskMgr.remove(self.taskName('GolfGreenGameTimeout')) + self.__handleFinsihed(0) + return task.done + + def getTimeLeft(self): + if self.startTime == None: + return self.totalTime + else: + timePassed = globalClockDelta.localElapsedTime(self.startTime) + timeLeft = self.totalTime - timePassed + return timeLeft + return + + def choosePattern(self): + dataSize = len(self.boardData) + indexChoice = int(random.random() * dataSize) + boardToAssign = None + for boardIndex in range(len(self.boardList)): + board = self.boardList[boardIndex] + if self.boardList[boardIndex][0] == 'closed': + pass + elif boardToAssign == None or len(self.boardList[boardIndex][0]) < len(self.boardList[boardToAssign][0]): + boardToAssign = boardIndex + elif len(self.boardList[boardIndex][0]) == len(self.boardList[boardToAssign][0]): + choice = random.choice(range(2)) + if choice: + boardToAssign = boardIndex + + if boardToAssign == None: + pass + return boardToAssign + + def checkForAssigned(self, avId): + for index in range(len(self.boardList)): + board = self.boardList[index] + if board[0] == 'closed': + pass + elif avId in board[0]: + return index + + return None + + def leaveGame(self): + senderId = self.air.getAvatarIdFromSender() + if senderId in self.joinedToons: + self.joinedToons.remove(senderId) + self.sendUpdate('acceptJoin', [self.totalTime, self.startTime, self.joinedToons]) + for boardDatum in self.boardList: + if boardDatum[0] == 'closed': + pass + elif senderId in boardDatum[0]: + boardDatum[0].remove(senderId) + + def requestJoin(self): + if self.allBoardsClear: + self.sendUpdate('acceptJoin', [0.0, 0.0, [0]]) + return + senderId = self.air.getAvatarIdFromSender() + if senderId not in self.joinedToons: + if self.startTime == None: + self.startTimer() + self.joinedToons.append(senderId) + if senderId not in self.everJoinedToons: + self.everJoinedToons.append(senderId) + self.sendUpdate('acceptJoin', [self.totalTime, self.startTime, self.joinedToons]) + self.sendScoreData() + return + + def requestBoard(self, boardVerify): + senderId = self.air.getAvatarIdFromSender() + assigned = self.checkForAssigned(senderId) + if assigned != None: + if self.boardList[assigned][0] == 'closed': + return + if boardVerify: + toon = simbase.air.doId2do.get(senderId) + if toon: + self.addGag(senderId) + self.sendUpdate('helpOthers', [senderId]) + for avId in self.boardList[assigned][0]: + if avId != senderId: + self.sendUpdateToAvatarId(avId, 'boardCleared', [senderId]) + + self.boardList[assigned][0] = 'closed' + self.boardList[assigned][3] = senderId + self.sendScoreData() + else: + self.boardList[assigned][0].remove(senderId) + boardIndex = self.choosePattern() + if boardIndex == None: + self.__handleFinsihed(1) + else: + self.boardList[boardIndex][0].append(senderId) + self.sendUpdateToAvatarId(senderId, 'startBoard', [self.boardData[self.boardList[boardIndex][1]], self.attackPatterns[self.boardList[boardIndex][2]]]) + return + + def addGag(self, avId): + av = simbase.air.doId2do.get(avId) + if av: + level = ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL + track = int(random.random() * ToontownBattleGlobals.NUM_GAG_TRACKS) + while not av.hasTrackAccess(track): + track = int(random.random() * ToontownBattleGlobals.NUM_GAG_TRACKS) + + maxGags = av.getMaxCarry() + av.inventory.calcTotalProps() + numGags = av.inventory.totalProps + numReward = min(1, maxGags - numGags) + while numReward > 0 and level >= 0: + result = av.inventory.addItem(track, level) + if result <= 0: + level -= 1 + else: + numReward -= 1 + self.sendUpdateToAvatarId(avId, 'informGag', [track, level]) + + av.d_setInventory(av.inventory.makeNetString()) + + def __handleFinsihed(self, success): + self.allBoardsClear = 1 + self.sendUpdate('signalDone', [success]) + self.switchFire() + taskMgr.remove(self.taskName('GolfGreenGameTimeout')) + if success: + for avId in self.joinedToons: + self.addGag(avId) + toon = simbase.air.doId2do.get(avId) + timeleft = int(self.getTimeLeft()) + if toon and timeleft > 0: + toon.toonUp(timeleft) + + else: + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + playerIds = self.everJoinedToons + for avId in playerIds: + av = simbase.air.doId2do.get(avId) + if av: + av.takeDamage(self.DamageOnFailure, quietly=0) + room.sendUpdate('forceOuch', [self.DamageOnFailure]) + + if not self.challengeDefeated: + self.challengeDefeated = True + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + self.challengeDefeated = True + room.challengeDefeated() + eventName = self.getOutputEventName() + messenger.send(eventName, [1]) + + def switchFire(self): + if self.switchId != 0: + switch = self.level.getEntity(self.switchId) + if switch: + switch.setIsOn(1) + + def generate(self): + BattleBlockerAI.BattleBlockerAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.detectName = 'golfGreenGame %s' % self.doId + taskMgr.doMethodLater(1.0, self.__detect, self.detectName) + self.setPos(self.pos) + self.setHpr(self.hpr) + + def registerBlocker(self): + BattleBlockerAI.BattleBlockerAI.registerBlocker(self) + if hasattr(self, 'hideSuits'): + self.hideSuits() + + def delete(self): + taskMgr.remove(self.detectName) + self.ignoreAll() + BattleBlockerAI.BattleBlockerAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + BattleBlockerAI.BattleBlockerAI.destroy(self) + + def __detect(self, task): + isThereAnyToons = False + if hasattr(self, 'level'): + toonInRange = 0 + for avId in self.level.presentAvIds: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + isThereAnyToons = True + distance = self.getDistance(av) + + if isThereAnyToons: + taskMgr.doMethodLater(1.0, self.__detect, self.detectName) + self.__run() + return Task.done + + def __run(self): + pass + + def reactToSwitch(self, on): + pass + + def setBattleFinished(self): + BattleBlockerAI.BattleBlockerAI.setBattleFinished(self) + messenger.send(self.getOutputEventName(), [1]) + self.switchFire() + + def sendScoreData(self): + total = len(self.boardList) + closed = 0 + scoreDict = {} + for board in self.boardList: + if board[0] == 'closed': + closed += 1 + if board[3] not in scoreDict: + scoreDict[board[3]] = 1 + else: + scoreDict[board[3]] += 1 + + outList = [] + for key in scoreDict: + score = scoreDict[key] + outList.append([key, score]) + + self.sendUpdate('scoreData', [total, closed, outList]) diff --git a/toontown/coghq/DistributedGolfSpot.py b/toontown/coghq/DistributedGolfSpot.py new file mode 100644 index 0000000..4c754e6 --- /dev/null +++ b/toontown/coghq/DistributedGolfSpot.py @@ -0,0 +1,757 @@ +import math +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, CollisionHandlerEvent, TextNode, VBase4, SmoothMover, NodePath, BitMask32 +from direct.fsm import FSM +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import globalClockDelta +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import DGG, DirectButton, DirectLabel, DirectWaitBar +from direct.interval.IntervalGlobal import Sequence, Wait, ActorInterval, Parallel, Func, LerpPosInterval, LerpHprInterval, ProjectileInterval, LerpScaleInterval, SoundInterval +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.golf import GolfGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DistributedGolfSpot(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfSpot') + positions = ((-45, 100, GolfGlobals.GOLF_BALL_RADIUS), + (-15, 100, GolfGlobals.GOLF_BALL_RADIUS), + (15, 100, GolfGlobals.GOLF_BALL_RADIUS), + (45, 100, GolfGlobals.GOLF_BALL_RADIUS)) + toonGolfOffsetPos = Point3(-2, 0, -GolfGlobals.GOLF_BALL_RADIUS) + toonGolfOffsetHpr = Point3(-90, 0, 0) + rotateSpeed = 20 + golfPowerSpeed = base.config.GetDouble('golf-power-speed', 3) + golfPowerExponent = base.config.GetDouble('golf-power-exponent', 0.75) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedGolfSpot') + self.boss = None + self.index = None + self.avId = 0 + self.toon = None + self.golfSpotSmoother = SmoothMover() + self.golfSpotSmoother.setSmoothMode(SmoothMover.SMOn) + self.smoothStarted = 0 + self.__broadcastPeriod = 0.2 + if self.index > len(self.positions): + self.notify.error('Invalid index %d' % index) + self.fadeTrack = None + self.setupPowerBar() + self.aimStart = None + self.golfSpotAdviceLabel = None + self.changeSeq = 0 + self.lastChangeSeq = 0 + self.controlKeyAllowed = False + self.flyBallTracks = {} + self.splatTracks = {} + self.__flyBallBubble = None + self.flyBallHandler = None + self.__flyBallSequenceNum = 0 + self.swingInterval = None + self.lastHitSequenceNum = -1 + self.goingToReward = False + self.gotHitByBoss = False + self.releaseTrack = None + self.grabTrack = None + self.restoreScaleTrack = None + return + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + self.boss.setGolfSpot(self, self.index) + + def setIndex(self, index): + self.index = index + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.ignoreAll() + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignoreAll() + self.boss = None + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.triggerName = self.uniqueName('trigger') + self.triggerEvent = 'enter%s' % self.triggerName + self.smoothName = self.uniqueName('golfSpotSmooth') + self.golfSpotAdviceName = self.uniqueName('golfSpotAdvice') + self.posHprBroadcastName = self.uniqueName('golfSpotBroadcast') + self.ballPowerTaskName = self.uniqueName('updateGolfPower') + self.adjustClubTaskName = self.uniqueName('adjustClub') + self.loadAssets() + self.accept('flyBallHit-%d' % self.index, self.__flyBallHit) + + def loadAssets(self): + self.root = render.attachNewNode('golfSpot-%d' % self.index) + self.root.setPos(*self.positions[self.index]) + self.ballModel = loader.loadModel('phase_6/models/golf/golf_ball') + self.ballColor = VBase4(1, 1, 1, 1) + if self.index < len(GolfGlobals.PlayerColors): + self.ballColor = VBase4(*GolfGlobals.PlayerColors[self.index]) + self.ballModel.setColorScale(self.ballColor) + self.ballModel.reparentTo(self.root) + self.club = loader.loadModel('phase_6/models/golf/putter') + self.clubLookatSpot = self.root.attachNewNode('clubLookat') + self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1)) + cs = CollisionSphere(0, 0, 0, 1) + cs.setTangible(0) + cn = CollisionNode(self.triggerName) + cn.addSolid(cs) + cn.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.trigger = self.root.attachNewNode(cn) + self.trigger.stash() + self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.mp3') + + def cleanup(self): + if self.swingInterval: + self.swingInterval.finish() + self.swingInterval = None + if self.releaseTrack: + self.releaseTrack.finish() + self.releaseTrack = None + flyTracks = self.flyBallTracks.values() + for track in flyTracks: + track.finish() + + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + if self.restoreScaleTrack: + self.restoreScaleTrack.finish() + self.restoreScaleTrack = None + self.root.removeNode() + self.ballModel.removeNode() + self.club.removeNode() + if self.powerBar: + self.powerBar.destroy() + self.powerBar = None + taskMgr.remove(self.triggerName) + self.boss = None + return + + def setState(self, state, avId, extraInfo): + if not self.isDisabled(): + self.gotHitByBoss = extraInfo + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + elif state == 'O': + self.demand('Off') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack = Sequence(Wait(6), self.getRestoreScaleInterval(), name='restoreScaleTrack') + self.restoreScaleTrack.start() + if self.avId == localAvatar.doId: + if not self.isDisabled(): + self.ballModel.setAlphaScale(0.3) + self.ballModel.setTransparency(1) + taskMgr.doMethodLater(5, self.__allowDetect, self.triggerName) + self.fadeTrack = Sequence(Func(self.ballModel.setTransparency, 1), self.ballModel.colorScaleInterval(0.2, VBase4(1, 1, 1, 0.3)), name='fadeTrack-enterFree') + self.fadeTrack.start() + else: + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + self.avId = 0 + return + + def exitFree(self): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + self.restoreScaleTrack.finish() + self.restoreScaleTrack = None + taskMgr.remove(self.triggerName) + self.ballModel.clearTransparency() + self.trigger.stash() + self.ignore(self.triggerEvent) + return + + def enterControlled(self, avId): + self.avId = avId + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.enableControlKey() + self.toon = toon + self.grabTrack = self.makeToonGrabInterval(toon) + if avId == localAvatar.doId: + self.boss.toCraneMode() + camera.reparentTo(self.root) + camera.setPosHpr(0, -10, 3, 0, 0, 0) + localAvatar.setPos(self.root, self.toonGolfOffsetPos) + localAvatar.setHpr(self.root, self.toonGolfOffsetHpr) + localAvatar.sendCurrentPosition() + self.__enableControlInterface() + self.startPosHprBroadcast() + self.accept('exitCrane', self.gotBossZapped) + self.grabTrack.start() + + def exitControlled(self): + self.grabTrack.finish() + del self.grabTrack + if self.swingInterval: + self.swingInterval.finish() + self.swingInterval = None + if not self.ballModel.isEmpty(): + if self.ballModel.isHidden(): + self.notify.debug('ball is hidden scale =%s' % self.ballModel.getScale()) + else: + self.notify.debug('ball is showing scale=%s' % self.ballModel.getScale()) + if self.toon and not self.toon.isDisabled(): + self.toon.startSmooth() + self.releaseTrack = self.makeToonReleaseInterval(self.toon) + self.stopPosHprBroadcast() + self.stopSmooth() + if self.avId == localAvatar.doId: + self.__disableControlInterface() + if not self.goingToReward: + camera.reparentTo(base.localAvatar) + camera.setPos(base.localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.stopAdjustClubTask() + self.releaseTrack.start() + self.enableControlKey() + return + + def __allowDetect(self, task): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = Sequence(self.ballModel.colorScaleInterval(0.2, self.ballColor), Func(self.ballModel.clearTransparency), name='fadeTrack-allowDetect') + self.fadeTrack.start() + self.trigger.unstash() + self.accept(self.triggerEvent, self.__hitTrigger) + + def __hitTrigger(self, event): + self.d_requestControl() + + def getRestoreScaleInterval(self): + return Sequence() + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestFree(self, gotHitByBoss): + self.sendUpdate('requestFree', [gotHitByBoss]) + + def makeToonGrabInterval(self, toon): + origPos = toon.getPos(self.root) + origHpr = toon.getHpr(self.root) + a = self.accomodateToon(toon) + newPos = toon.getPos() + newHpr = toon.getHpr() + origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) + self.notify.debug('toon.setPosHpr %s %s' % (origPos, origHpr)) + toon.setPosHpr(origPos, origHpr) + walkTime = 0.2 + reach = Sequence() + 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(toon.stopLookAround)) + if toon == base.localAvatar: + i.append(Func(self.switchToAnimState, 'GolfPuttLoop')) + i.append(Func(self.startAdjustClubTask)) + i = Parallel(i, a) + return i + + def accomodateToon(self, toon): + toon.wrtReparentTo(self.root) + toon.setPos(self.toonGolfOffsetPos) + toon.setHpr(self.toonGolfOffsetHpr) + return Sequence() + + def switchToAnimState(self, animStateName, forced = False): + curAnimState = base.localAvatar.animFSM.getCurrentState() + curAnimStateName = '' + if curAnimState: + curAnimStateName = curAnimState.getName() + if curAnimStateName != animStateName or forced: + base.localAvatar.b_setAnimState(animStateName) + + def __enableControlInterface(self): + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=2, text=TTLocalizer.BossbotGolfSpotLeave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(1.05, 0, -0.82), command=self.__exitGolfSpot) + self.accept('escape', self.__exitGolfSpot) + 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, 'watchGolfSpotControls') + taskMgr.doMethodLater(5, self.__displayGolfSpotAdvice, self.golfSpotAdviceName) + self.arrowVert = 0 + self.arrowHorz = 0 + if self.powerBar: + self.powerBar.show() + return + + def __disableControlInterface(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = None + self.__cleanupGolfSpotAdvice() + 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 + taskMgr.remove('watchGolfSpotControls') + if self.powerBar: + self.powerBar.hide() + else: + self.notify.debug('self.powerBar is none') + return + + def setupPowerBar(self): + self.powerBar = DirectWaitBar(pos=(0.0, 0, -0.94), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(1.0, 0.0, 0.0, 1.0), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar.hide() + + def resetPowerBar(self): + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar['text'] = '' + + def __displayGolfSpotAdvice(self, task): + if self.golfSpotAdviceLabel == None: + self.golfSpotAdviceLabel = DirectLabel(text=TTLocalizer.BossbotGolfSpotAdvice, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.69), scale=0.1) + return + + def __cleanupGolfSpotAdvice(self): + if self.golfSpotAdviceLabel: + self.golfSpotAdviceLabel.destroy() + self.golfSpotAdviceLabel = None + taskMgr.remove(self.golfSpotAdviceName) + return + + def showExiting(self): + if self.closeButton: + self.closeButton.destroy() + self.closeButton = DirectLabel(relief=None, text=TTLocalizer.BossbotGolfSpotLeaving, pos=(1.05, 0, -0.88), text_pos=(0, 0), text_scale=0.06, text_fg=VBase4(1, 1, 1, 1)) + self.__cleanupGolfSpotAdvice() + return + + def __exitGolfSpot(self): + self.d_requestFree(False) + + def __controlPressed(self): + if self.controlKeyAllowed: + self.__beginFireBall() + + def __controlReleased(self): + if self.controlKeyAllowed: + self.__endFireBall() + + def __upArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowHorz = 1 + self.switchToAnimState('GolfRotateLeft') + elif self.arrowHorz > 0: + self.arrowHorz = 0 + self.switchToAnimState('GolfPuttLoop') + + def __leftArrow(self, pressed): + self.__incrementChangeSeq() + self.__cleanupGolfSpotAdvice() + if pressed: + self.arrowHorz = -1 + self.switchToAnimState('GolfRotateRight') + elif self.arrowHorz < 0: + self.arrowHorz = 0 + self.switchToAnimState('GolfPuttLoop') + + def __watchControls(self, task): + if self.arrowHorz: + self.__moveGolfSpot(self.arrowHorz) + return Task.cont + + def __moveGolfSpot(self, xd): + dt = globalClock.getDt() + h = self.root.getH() - xd * self.rotateSpeed * dt + h %= 360 + limitH = h + self.root.setH(limitH) + + def __incrementChangeSeq(self): + self.changeSeq = self.changeSeq + 1 & 255 + + def __beginFireBall(self): + if self.aimStart != None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + time = globalClock.getFrameTime() + self.aimStart = time + messenger.send('wakeup') + taskMgr.add(self.__updateBallPower, self.ballPowerTaskName) + return + + def __endFireBall(self): + if self.aimStart == None: + return + if not self.state == 'Controlled': + return + if not self.avId == localAvatar.doId: + return + taskMgr.remove(self.ballPowerTaskName) + self.disableControlKey() + messenger.send('wakeup') + self.aimStart = None + power = self.power + angle = self.root.getH() + self.notify.debug('incrementing self.__flyBallSequenceNum') + self.__flyBallSequenceNum = (self.__flyBallSequenceNum + 1) % 255 + self.sendSwingInfo(power, angle, self.__flyBallSequenceNum) + self.setSwingInfo(power, angle, self.__flyBallSequenceNum) + self.resetPowerBar() + return + + def __updateBallPower(self, task): + if not self.powerBar: + print '### no power bar!!!' + return task.done + newPower = self.__getBallPower(globalClock.getFrameTime()) + self.power = newPower + self.powerBar['value'] = newPower + return task.cont + + def __getBallPower(self, time): + elapsed = max(time - self.aimStart, 0.0) + t = elapsed / self.golfPowerSpeed + t = math.pow(t, self.golfPowerExponent) + power = int(t * 100) % 200 + if power > 100: + power = 200 - power + return power + + def stopPosHprBroadcast(self): + taskName = self.posHprBroadcastName + taskMgr.remove(taskName) + + def startPosHprBroadcast(self): + taskName = self.posHprBroadcastName + self.b_clearSmoothing() + self.d_sendGolfSpotPos() + taskMgr.remove(taskName) + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + + def __posHprBroadcast(self, task): + self.d_sendGolfSpotPos() + taskName = self.posHprBroadcastName + taskMgr.doMethodLater(self.__broadcastPeriod, self.__posHprBroadcast, taskName) + return Task.done + + def d_sendGolfSpotPos(self): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setGolfSpotPos', [self.changeSeq, self.root.getH(), timestamp]) + + def setGolfSpotPos(self, changeSeq, h, timestamp): + self.changeSeq = changeSeq + if self.smoothStarted: + now = globalClock.getFrameTime() + local = globalClockDelta.networkToLocalTime(timestamp, now) + self.golfSpotSmoother.setH(h) + self.golfSpotSmoother.setTimestamp(local) + self.golfSpotSmoother.markPosition() + else: + self.root.setH(h) + + def b_clearSmoothing(self): + self.d_clearSmoothing() + self.clearSmoothing() + + def d_clearSmoothing(self): + self.sendUpdate('clearSmoothing', [0]) + + def clearSmoothing(self, bogus = None): + self.golfSpotSmoother.clearPositions(1) + + def doSmoothTask(self, task): + self.golfSpotSmoother.computeAndApplySmoothHpr(self.root) + 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 makeToonReleaseInterval(self, toon): + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -5, 0)) + + if self.gotHitByBoss: + grabIval = Sequence(Func(self.detachClub), name='makeToonReleaseInterval-gotHitByBoss') + if not toon.isEmpty(): + toonIval = Sequence(Func(toon.wrtReparentTo, render), Parallel(ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1)), name='makeToonReleaseInterval-toonIval') + grabIval.append(toonIval) + else: + grabIval = Sequence(Func(self.detachClub)) + if not toon.isEmpty(): + toonIval = Sequence(Parallel(ActorInterval(toon, 'walk', duration=1.0, playRate=-1.0), LerpPosInterval(toon, duration=1.0, pos=Point3(-10, 0, 0))), Func(toon.wrtReparentTo, render)) + grabIval.append(toonIval) + if localAvatar.doId == toon.doId: + if not self.goingToReward and toon.hp > 0: + grabIval.append(Func(self.goToFinalBattle)) + grabIval.append(Func(self.notify.debug, 'goingToFinalBattlemode')) + grabIval.append(Func(self.safeBossToFinalBattleMode)) + return grabIval + + def safeBossToFinalBattleMode(self): + if self.boss: + self.boss.toFinalBattleMode() + + def goToFinalBattle(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + curState = place.fsm.getCurrentState().getName() + if place.fsm.getCurrentState().getName() == 'crane': + place.setState('finalBattle') + else: + self.notify.debug('NOT going to final battle, state=%s' % curState) + + def attachClub(self, avId, pointToBall = False): + club = self.club + if club: + av = base.cr.doId2do.get(avId) + if av: + av.useLOD(1000) + lHand = av.getLeftHands()[0] + club.setPos(0, 0, 0) + club.reparentTo(lHand) + netScale = club.getNetTransform().getScale()[1] + counterActToonScale = lHand.find('**/counteractToonScale') + if counterActToonScale.isEmpty(): + counterActToonScale = lHand.attachNewNode('counteractToonScale') + counterActToonScale.setScale(1 / netScale) + self.notify.debug('creating counterActToonScale for %s' % av.getName()) + club.reparentTo(counterActToonScale) + club.setX(-0.25 * netScale) + if pointToBall: + club.lookAt(self.clubLookatSpot) + + def detachClub(self): + if not self.club.isEmpty(): + self.club.reparentTo(self.root) + self.club.setZ(-20) + self.club.setScale(1) + + def adjustClub(self): + club = self.club + if club: + distance = club.getDistance(self.clubLookatSpot) + scaleFactor = distance / 2.058 + club.setScale(1, scaleFactor, 1) + + def startAdjustClubTask(self): + taskMgr.add(self.adjustClubTask, self.adjustClubTaskName) + + def stopAdjustClubTask(self): + taskMgr.remove(self.adjustClubTaskName) + + def adjustClubTask(self, task): + self.attachClub(self.avId, True) + self.adjustClub() + return task.cont + + def enableControlKey(self): + self.controlKeyAllowed = True + + def disableControlKey(self): + self.controlKeyAllowed = False + + def sendSwingInfo(self, power, angle, sequenceNum): + self.sendUpdate('setSwingInfo', [power, angle, sequenceNum]) + + def startBallPlayback(self, power, angle, sequenceNum): + flyBall = self.ballModel.copyTo(NodePath()) + flyBall.setScale(1.0) + flyBallBubble = self.getFlyBallBubble().instanceTo(NodePath()) + flyBallBubble.reparentTo(flyBall) + flyBall.setTag('pieSequence', str(sequenceNum)) + flyBall.setTag('throwerId', str(self.avId)) + t = power / 100.0 + t = 1.0 - t + dist = 300 - 200 * t + time = 1.5 + 0.5 * t + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + + def getVelocity(root = self.root, relVel = relVel): + return render.getRelativeVector(root, relVel) + + fly = Sequence(Func(flyBall.reparentTo, render), Func(flyBall.setPosHpr, self.root, 0, 0, 0, 0, 0, 0), Func(base.cTrav.addCollider, flyBallBubble, self.flyBallHandler), ProjectileInterval(flyBall, startVel=getVelocity, duration=3), Func(flyBall.detachNode), Func(base.cTrav.removeCollider, flyBallBubble), Func(self.notify.debug, 'removed collider'), Func(self.flyBallFinishedFlying, sequenceNum)) + flyWithSound = Parallel(fly, SoundInterval(self.hitBallSfx, node=self.root), name='flyWithSound') + self.notify.debug('starting flyball track') + flyWithSound.start() + self.flyBallTracks[sequenceNum] = flyWithSound + return + + def setSwingInfo(self, power, angle, sequenceNum): + av = base.cr.doId2do.get(self.avId) + self.swingInterval = Sequence() + if av: + self.stopAdjustClubTask() + self.swingInterval = Sequence(ActorInterval(av, 'swing-putt', startFrame=0, endFrame=GolfGlobals.BALL_CONTACT_FRAME), Func(self.startBallPlayback, power, angle, sequenceNum), Func(self.ballModel.hide), ActorInterval(av, 'swing-putt', startFrame=GolfGlobals.BALL_CONTACT_FRAME, endFrame=24), Func(self.ballModel.setScale, 0.1), Func(self.ballModel.show), LerpScaleInterval(self.ballModel, 1.0, Point3(1, 1, 1)), Func(self.enableControlKey)) + if av == localAvatar: + self.swingInterval.append(Func(self.switchToAnimState, 'GolfPuttLoop', True)) + self.swingInterval.start() + + def getFlyBallBubble(self): + if self.__flyBallBubble == None: + bubble = CollisionSphere(0, 0, 0, GolfGlobals.GOLF_BALL_RADIUS) + node = CollisionNode('flyBallBubble') + node.addSolid(bubble) + node.setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) + node.setIntoCollideMask(BitMask32.allOff()) + self.__flyBallBubble = NodePath(node) + self.flyBallHandler = CollisionHandlerEvent() + self.flyBallHandler.addInPattern('flyBallHit-%d' % self.index) + return self.__flyBallBubble + + def __flyBallHit(self, entry): + print entry + + def flyBallFinishedFlying(self, sequence): + if self.flyBallTracks.has_key(sequence): + del self.flyBallTracks[sequence] + + def __finishFlyBallTrack(self, sequence): + if self.flyBallTracks.has_key(sequence): + flyBallTrack = self.flyBallTracks[sequence] + del self.flyBallTracks[sequence] + flyBallTrack.finish() + + def flyBallFinishedSplatting(self, sequence): + if self.splatTracks.has_key(sequence): + del self.splatTracks[sequence] + + def __flyBallHit(self, entry): + if not entry.hasSurfacePoint() or not entry.hasInto(): + return + if not entry.getInto().isTangible(): + return + sequence = int(entry.getFromNodePath().getNetTag('pieSequence')) + self.__finishFlyBallTrack(sequence) + if self.splatTracks.has_key(sequence): + splatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + splatTrack.finish() + flyBallCode = 0 + flyBallCodeStr = entry.getIntoNodePath().getNetTag('pieCode') + if flyBallCodeStr: + flyBallCode = int(flyBallCodeStr) + pos = entry.getSurfacePoint(render) + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + throwerId = int(entry.getFromNodePath().getNetTag('throwerId')) + splat = self.getFlyBallSplatInterval(pos[0], pos[1], pos[2], flyBallCode, throwerId) + splat = Sequence(splat, Func(self.flyBallFinishedSplatting, sequence)) + self.splatTracks[sequence] = splat + splat.start() + self.notify.debug('doId=%d into=%s flyBallCode=%d, throwerId=%d' % (self.doId, + entry.getIntoNodePath(), + flyBallCode, + throwerId)) + if flyBallCode == ToontownGlobals.PieCodeBossCog and self.avId == localAvatar.doId and self.lastHitSequenceNum != self.__flyBallSequenceNum: + self.lastHitSequenceNum = self.__flyBallSequenceNum + self.boss.d_ballHitBoss(1) + elif flyBallCode == ToontownGlobals.PieCodeToon and self.avId == localAvatar.doId and self.lastHitSequenceNum != self.__flyBallSequenceNum: + self.lastHitSequenceNum = self.__flyBallSequenceNum + avatarDoId = entry.getIntoNodePath().getNetTag('avatarDoId') + if avatarDoId == '': + self.notify.warning('Toon %s has no avatarDoId tag.' % repr(entry.getIntoNodePath())) + return + doId = int(avatarDoId) + if doId != localAvatar.doId: + pass + + def getFlyBallSplatInterval(self, x, y, z, flyBallCode, throwerId): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + splatName = 'dust' + splat = BattleProps.globalPropPool.getProp(splatName) + splat.setBillboardPointWorld(2) + color = ToontownGlobals.PieCodeColors.get(flyBallCode) + if color: + splat.setColor(*color) + if flyBallCode == ToontownGlobals.PieCodeBossCog: + self.notify.debug('changing color to %s' % self.ballColor) + splat.setColor(self.ballColor) + sound = loader.loadSfx('phase_11/audio/sfx/LB_evidence_miss.mp3') + vol = 1.0 + if flyBallCode == ToontownGlobals.PieCodeBossCog: + sound = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.mp3') + soundIval = SoundInterval(sound, node=splat, volume=vol) + if flyBallCode == ToontownGlobals.PieCodeBossCog and localAvatar.doId == throwerId: + vol = 1.0 + soundIval = SoundInterval(sound, node=localAvatar, volume=vol) + ival = Parallel(Func(splat.reparentTo, render), Func(splat.setPos, x, y, z), soundIval, Sequence(ActorInterval(splat, splatName), Func(splat.detachNode))) + return ival + + def setGoingToReward(self): + self.goingToReward = True + + def gotBossZapped(self): + self.showExiting() + self.d_requestFree(True) diff --git a/toontown/coghq/DistributedGolfSpotAI.py b/toontown/coghq/DistributedGolfSpotAI.py new file mode 100644 index 0000000..83f1bf6 --- /dev/null +++ b/toontown/coghq/DistributedGolfSpotAI.py @@ -0,0 +1,82 @@ +from direct.distributed import DistributedObjectAI +from direct.fsm import FSM + +class DistributedGolfSpotAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedGolfSpotAI') + self.boss = boss + self.index = index + self.avId = 0 + self.allowControl = True + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def d_setState(self, state, avId, extraInfo=0): + self.sendUpdate('setState', [state, avId, extraInfo]) + + def requestControl(self): + if not self.allowControl: + return + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0 and self.state != 'Off': + golfSpotId = self.__getGolfSpotId(avId) + if golfSpotId == 0: + grantRequest = True + if self.boss and not self.boss.isToonRoaming(avId): + grantRequest = False + if grantRequest: + self.request('Controlled', avId) + + def requestFree(self, gotHitByBoss): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId and self.state == 'Controlled': + self.request('Free', gotHitByBoss) + + def forceFree(self): + self.request('Free', 0) + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getGolfSpotId(self, avId): + if self.boss and self.boss.golfSpots != None: + for golfSpot in self.boss.golfSpots: + if golfSpot.avId == avId: + return golfSpot.doId + + return 0 + + def turnOff(self): + self.request('Off') + self.allowControl = False + + def enterOff(self): + self.sendUpdate('setGoingToReward', []) + self.d_setState('O', 0) + + def exitOff(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C', avId) + + def exitControlled(self): + pass + + def enterFree(self, gotHitByBoss): + self.avId = 0 + self.d_setState('F', 0, gotHitByBoss) + + def exitFree(self): + pass diff --git a/toontown/coghq/DistributedGrid.py b/toontown/coghq/DistributedGrid.py new file mode 100644 index 0000000..02b722c --- /dev/null +++ b/toontown/coghq/DistributedGrid.py @@ -0,0 +1,69 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from CrateGlobals import * +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal + +class DistributedGrid(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGrid') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.model = None + return + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + BasicEntities.DistributedNodePathEntity.disable(self) + self.unloadModel() + self.ignoreAll() + + def delete(self): + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.notify.debug('loadModel') + texSize = 6.0 + scale = self.cellSize / texSize + self.model = loader.loadModel('phase_9/models/cogHQ/FloorWear.bam') + self.model.reparentTo(self) + long = self.numCol + short = self.numRow + h = 0 + if self.numCol < self.numRow: + long = self.numRow + short = self.numCol + h = 90 + self.model.setScale(scale * long, scale * short, 1) + self.model.setHpr(h, 180, 0) + self.model.setPos(self.cellSize * self.numCol / 2.0, self.cellSize * self.numRow / 2.0, 0.025) + self.model.setColor(0.588, 0.588, 0.459, 0.4) + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + + def setNumRow(self, rows): + self.numRow = rows + self.unloadModel() + self.loadModel() + + def setNumCol(self, cols): + self.numCol = cols + self.unloadModel() + self.loadModel() diff --git a/toontown/coghq/DistributedGridAI.py b/toontown/coghq/DistributedGridAI.py new file mode 100644 index 0000000..edb5012 --- /dev/null +++ b/toontown/coghq/DistributedGridAI.py @@ -0,0 +1,250 @@ +from CrateGlobals import * +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedGridAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGridAI') + + def __init__(self, level, entId): + self.initialized = 0 + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.activeCellList = [] + + def delete(self): + DistributedEntityAI.DistributedEntityAI.delete(self) + del self.activeCellList + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + + def initializeGrid(self): + if not self.initialized: + self.objPos = {} + self.gridCells = [ + None] * self.numRow + for i in range(len(self.gridCells)): + self.gridCells[i] = [ + None] * self.numCol + for j in range(len(self.gridCells[i])): + self.gridCells[i][j] = [] + + self.initialized = 1 + return + + def addActiveCell(self, cell): + self.activeCellList.append(cell) + + def getObjPos(self, objId): + objPos = self.objPos.get(objId, None) + if objPos: + row, col = objPos + if row >= 0 and row < self.numRow and col >= 0 and col < self.numCol: + return [(col + 1) * self.cellSize, (row + 1) * self.cellSize, 0] + else: + self.notify.debug('row/col out of range %s/%s' % (row, col)) + else: + self.notify.debug("didn't have record of obj") + return + + def addObjectByPos(self, objId, pos, width=1): + if not self.initialized: + self.initializeGrid() + if self.objPos.get(objId, None): + return 1 + x, y = pos[0], pos[1] + col = min(int(x / self.cellSize), self.numCol - width) + row = min(int(y / self.cellSize), self.numRow - width) + self.notify.debug('attempt add %d at %s, row,col = %d,%d' % (objId, pos, row, col)) + while col >= 0 and col < self.numCol: + while row >= 0 and row < self.numRow: + if self.addObjectByRowCol(objId, row, col): + return 1 + else: + row += 2 + else: + row = 0 + col += 2 + + else: + self.notify.debug('requestObjPos: row/col out of range %s/%s' % (row, col)) + row = min(row, self.numRow) + row = max(0, row) + col = min(col, self.numRow) + col = max(0, col) + return self.addObjectByRowCol(objId, row, col) + + return + + def addObjectByRowCol(self, objId, row, col): + if row >= 0 and row < self.numRow - 1 and col >= 0 and col < self.numCol - 1: + self.notify.debug('adding obj %s to grid cell %s,%s' % (objId, row, col)) + self.gridCells[row][col].append(objId) + self.gridCells[row + 1][col].append(objId) + self.gridCells[row][col + 1].append(objId) + self.gridCells[row + 1][col + 1].append(objId) + self.objPos[objId] = [row, col] + self.__setChangedActiveCells(onList=[[row, col], [row + 1, col], [row, col + 1], [row + 1, col + 1]], objId=objId) + return 1 + self.notify.debug("couldn't obj to grid cell %s,%s" % (row, col)) + return 0 + + def removeObject(self, objId): + objPos = self.objPos.get(objId) + if not objPos: + return + row, col = objPos + self.notify.debug('removing obj %s from %s, %s' % (objId, row, col)) + self.gridCells[row][col].remove(objId) + self.gridCells[row + 1][col].remove(objId) + self.gridCells[row][col + 1].remove(objId) + self.gridCells[row + 1][col + 1].remove(objId) + del self.objPos[objId] + self.__setChangedActiveCells(offList=[[row, col], [row + 1, col], [row, col + 1], [row + 1, col + 1]], objId=objId) + + def checkMoveDir(self, objId, h): + if h > 225 and h < 315: + return self.checkMove(objId, 0, 1) + elif h > 45 and h < 135: + return self.checkMove(objId, 0, -1) + elif h < 45 or h > 315: + return self.checkMove(objId, 1, 0) + elif h > 135 and h < 225: + return self.checkMove(objId, -1, 0) + + def doMoveDir(self, objId, h): + if h > 225 and h < 315: + return self.doMove(objId, 0, 1) + elif h > 45 and h < 135: + return self.doMove(objId, 0, -1) + elif h < 45 or h > 315: + return self.doMove(objId, 1, 0) + elif h > 135 and h < 225: + return self.doMove(objId, -1, 0) + + def checkPush(self, objId, side): + if side == 0: + return self.checkMove(objId, 0, -1) + else: + if side == 1: + return self.checkMove(objId, 0, 1) + else: + if side == 2: + return self.checkMove(objId, -1, 0) + else: + if side == 3: + return self.checkMove(objId, 1, 0) + + def doPush(self, objId, side): + if side == 0: + return self.doMove(objId, 0, -1) + else: + if side == 1: + return self.doMove(objId, 0, 1) + else: + if side == 2: + return self.doMove(objId, -1, 0) + else: + if side == 3: + return self.doMove(objId, 1, 0) + + def checkMove(self, objId, dRow, dCol): + objPos = self.objPos.get(objId) + if not objPos: + return + row, col = objPos + validMove = 1 + if dRow < 0: + validMove = validMove & self.__isEmpty(row - 1, col) & self.__isEmpty(row - 1, col + 1) + else: + if dRow > 0: + validMove = validMove & self.__isEmpty(row + 2, col) & self.__isEmpty(row + 2, col + 1) + if dCol < 0: + validMove = validMove & self.__isEmpty(row, col - 1) & self.__isEmpty(row + 1, col - 1) + else: + if dCol > 0: + validMove = validMove & self.__isEmpty(row, col + 2) & self.__isEmpty(row + 1, col + 2) + return validMove + + def doMove(self, objId, dRow, dCol): + objPos = self.objPos.get(objId) + if not objPos: + return 0 + row, col = objPos + validMove = self.checkMove(objId, dRow, dCol) + if validMove: + self.gridCells[row][col].remove(objId) + self.gridCells[row + 1][col].remove(objId) + self.gridCells[row][col + 1].remove(objId) + self.gridCells[row + 1][col + 1].remove(objId) + newRow = row + dRow + newCol = col + dCol + self.gridCells[newRow][newCol].append(objId) + self.gridCells[newRow + 1][newCol].append(objId) + self.gridCells[newRow][newCol + 1].append(objId) + self.gridCells[newRow + 1][newCol + 1].append(objId) + self.objPos[objId] = [ + newRow, newCol] + self.updateActiveCells(objId, row, col, dRow, dCol) + return validMove + + def updateActiveCells(self, objId, row, col, dRow, dCol): + newRow = row + dRow + newCol = col + dCol + newCells = [ + [ + newRow, newCol], [newRow + 1, newCol], [newRow, newCol + 1], [newRow + 1, newCol + 1]] + oldCells = [ + [ + row, col], [row + 1, col], [row, col + 1], [row + 1, col + 1]] + onList = [] + offList = [] + for cell in newCells: + if cell not in oldCells: + onList.append(cell) + + for cell in oldCells: + if cell not in newCells: + offList.append(cell) + + self.__setChangedActiveCells(onList, offList, objId) + + def __setChangedActiveCells(self, onList=[], offList=[], objId=None): + for cell in self.activeCellList: + self.notify.debug('onList = %s, offList = %s, cell = %s' % (onList, offList, cell.getRowCol())) + if cell.getRowCol() in onList: + cell.b_setState(1, objId) + elif cell.getRowCol() in offList: + cell.b_setState(0, objId) + + def __isEmpty(self, row, col): + if row < 0 or row >= self.numRow or col < 0 or col >= self.numCol: + return 0 + if len(self.gridCells[row][col]) > 0: + return 0 + return 1 + + def printGrid(self): + if not __debug__: + return + for i in range(len(self.gridCells)): + str = '' + for j in range(len(self.gridCells[i])): + col = self.gridCells[i][j] + active = 0 + for cell in self.activeCellList: + if cell.getRowCol() == [i, j]: + active = 1 + + if len(col) > 0: + if active: + str += '[X]' + else: + str += ' X ' + elif active: + str += '[.]' + else: + str += ' . ' + + print str + ' : %d' % i + + print '' diff --git a/toontown/coghq/DistributedHealBarrel.py b/toontown/coghq/DistributedHealBarrel.py new file mode 100644 index 0000000..fc48e65 --- /dev/null +++ b/toontown/coghq/DistributedHealBarrel.py @@ -0,0 +1,31 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import DistributedBarrelBase + +class DistributedHealBarrel(DistributedBarrelBase.DistributedBarrelBase): + + def __init__(self, cr): + DistributedBarrelBase.DistributedBarrelBase.__init__(self, cr) + self.numGags = 0 + self.gagScale = 0.6 + + def disable(self): + DistributedBarrelBase.DistributedBarrelBase.disable(self) + self.ignoreAll() + + def delete(self): + self.gagModel.removeNode() + del self.gagModel + DistributedBarrelBase.DistributedBarrelBase.delete(self) + + def applyLabel(self): + self.gagModel = loader.loadModel('phase_4/models/props/icecream') + self.gagModel.reparentTo(self.gagNode) + self.gagModel.find('**/p1_2').clearBillboard() + self.gagModel.setScale(self.gagScale) + self.gagModel.setPos(0, -0.1, -.1 - self.gagScale) + + def setGrab(self, avId): + DistributedBarrelBase.DistributedBarrelBase.setGrab(self, avId) diff --git a/toontown/coghq/DistributedHealBarrelAI.py b/toontown/coghq/DistributedHealBarrelAI.py new file mode 100644 index 0000000..2b1c01a --- /dev/null +++ b/toontown/coghq/DistributedHealBarrelAI.py @@ -0,0 +1,16 @@ +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task + +class DistributedHealBarrelAI(DistributedBarrelBaseAI.DistributedBarrelBaseAI): + + def __init__(self, level, entId): + x = y = z = h = 0 + DistributedBarrelBaseAI.DistributedBarrelBaseAI.__init__(self, level, entId) + + def d_setGrab(self, avId): + self.notify.debug('d_setGrab %s' % avId) + self.sendUpdate('setGrab', [avId]) + av = self.air.doId2do.get(avId) + if av: + av.toonUp(self.getRewardPerGrab()) diff --git a/toontown/coghq/DistributedInGameEditor.py b/toontown/coghq/DistributedInGameEditor.py new file mode 100644 index 0000000..42e39ba --- /dev/null +++ b/toontown/coghq/DistributedInGameEditor.py @@ -0,0 +1,775 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import lineInfo, Functor +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from otp.level import Level +from otp.level import LevelConstants +from otp.level import Entity +from otp.level import EditMgr +from SpecImports import * +from InGameEditorElements import * +from toontown.cogdominium import CogdoEntityCreator +import string + +class InGameEditorEntityBase(InGameEditorElement): + + def __init__(self): + InGameEditorElement.__init__(self) + + def attribChanged(self, attrib, value): + Entity.Entity.attribChanged(self, attrib, value) + print 'attribChange: %s %s, %s = %s' % (self.level.getEntityType(self.entId), + self.entId, + attrib, + repr(value)) + + def getTypeName(self): + return self.level.getEntityType(self.entId) + + def privGetNamePrefix(self): + return '[%s-%s] ' % (self.getTypeName(), self.entId) + + def privGetEntityName(self): + return self.level.levelSpec.getEntitySpec(self.entId)['name'] + + def getName(self): + return '%s%s' % (self.privGetNamePrefix(), self.privGetEntityName()) + + def setNewName(self, newName): + prefix = self.privGetNamePrefix() + if newName[:len(prefix)] == prefix: + newName = newName[len(prefix):] + oldName = self.privGetEntityName() + if oldName != newName: + self.level.setAttribEdit(self.entId, 'name', newName) + + def setParentEntId(self, parentEntId): + self.parentEntId = parentEntId + self.level.buildEntityTree() + + def setName(self, name): + self.name = name + self.level.buildEntityTree() + + +class InGameEditorEntity(Entity.Entity, InGameEditorEntityBase): + + def __init__(self, level, entId): + Entity.Entity.__init__(self, level, entId) + InGameEditorEntityBase.__init__(self) + + def id(self): + return self.entId + + def destroy(self): + Entity.Entity.destroy(self) + + +class InGameEditorEditMgr(EditMgr.EditMgr, InGameEditorEntityBase): + + def __init__(self, level, entId): + EditMgr.EditMgr.__init__(self, level, entId) + InGameEditorEntityBase.__init__(self) + + def destroy(self): + EditMgr.EditMgr.destroy(self) + + +class AttribModifier(Entity.Entity, InGameEditorEntityBase): + notify = DirectNotifyGlobal.directNotify.newCategory('AttribModifier') + + def __init__(self, level, entId): + Entity.Entity.__init__(self, level, entId) + InGameEditorEntityBase.__init__(self) + + def destroy(self): + Entity.Entity.destroy(self) + + def setValue(self, value): + if len(self.typeName) == 0: + AttribModifier.notify.warning('no typeName set') + return + entTypeReg = self.level.entTypeReg + if self.typeName not in entTypeReg.getAllTypeNames(): + AttribModifier.notify.warning('invalid typeName: %s' % self.typeName) + return + typeDesc = entTypeReg.getTypeDesc(self.typeName) + if len(self.attribName) == 0: + AttribModifier.notify.warning('no attribName set') + return + if self.attribName not in typeDesc.getAttribNames(): + AttribModifier.notify.warning('invalid attribName: %s' % self.attribName) + return + if len(value) == 0: + AttribModifier.notify.warning('no value set') + + def setAttrib(entId, typeName = self.typeName, attribName = self.attribName, value = eval(value), recursive = self.recursive): + if typeName == self.level.getEntityType(entId): + self.level.setAttribEdit(entId, attribName, value) + if recursive: + entity = self.level.getEntity(entId) + for child in entity.getChildren(): + setAttrib(child.entId) + + setAttrib(self.parentEntId) + + +def getInGameEditorEntityCreatorClass(level): + entCreator = level.createEntityCreator() + EntCreatorClass = entCreator.__class__ + + class InGameEditorEntityCreator(EntCreatorClass): + + def __init__(self, editor): + EntCreatorClass.__init__(self, editor) + entTypes = self.entType2Ctor.keys() + for type in entTypes: + self.entType2Ctor[type] = InGameEditorEntity + + self.entType2Ctor['editMgr'] = InGameEditorEditMgr + self.entType2Ctor['attribModifier'] = AttribModifier + + return InGameEditorEntityCreator + + +class DistributedInGameEditor(DistributedObject.DistributedObject, Level.Level, InGameEditorElement): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedInGameEditor') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + Level.Level.__init__(self) + InGameEditorElement.__init__(self) + self.editorInitialized = 0 + self.specModified = 0 + self.undoStack = [] + self.redoStack = [] + self.entCreateHandlerQ = [] + self.entitiesWeCreated = [] + self.nodePathId2EntId = {} + + def generate(self): + self.notify.debug('generate') + DistributedObject.DistributedObject.generate(self) + base.inGameEditor = self + + def setEditorAvId(self, editorAvId): + self.editorAvId = editorAvId + + def setEditUsername(self, editUsername): + self.editUsername = editUsername + + def getEditUsername(self): + return self.editUsername + + def setLevelDoId(self, levelDoId): + self.levelDoId = levelDoId + self.level = base.cr.doId2do[self.levelDoId] + + def getLevelDoId(self): + return self.levelDoId + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedObject.DistributedObject.announceGenerate(self) + if self.editorIsLocalToon(): + from otp.level import EditorGlobals + EditorGlobals.assertReadyToEdit() + self.notify.debug('requesting an up-to-date copy of the level spec') + self.sendUpdate('requestCurrentLevelSpec') + + def setSpecSenderDoId(self, doId): + DistributedInGameEditor.notify.debug('setSpecSenderDoId: %s' % doId) + blobSender = base.cr.doId2do[doId] + + def setSpecBlob(specBlob, blobSender = blobSender, self = self): + blobSender.sendAck() + from otp.level.LevelSpec import LevelSpec + curSpec = eval(specBlob) + self.gotCurrentSpec(curSpec) + + if blobSender.isComplete(): + setSpecBlob(blobSender.getBlob()) + else: + evtName = self.uniqueName('specDone') + blobSender.setDoneEvent(evtName) + self.acceptOnce(evtName, setSpecBlob) + + def gotCurrentSpec(self, curSpec): + self.entTypeReg = self.level.getEntityTypeReg() + curSpec.setEntityTypeReg(self.entTypeReg) + self.axis = loader.loadModel('models/misc/xyzAxis.bam') + self.axis.setColorOff() + self.axis.setColorScale(1, 1, 1, 1, 1) + self.initializeLevel(self.doId, curSpec, curSpec.getScenario()) + entCreator = self.level.createEntityCreator() + self.entTypes = entCreator.getEntityTypes() + self.selectedEntity = None + base.startTk() + import InGameEditor + doneEvent = self.uniqueName('editorDone') + saveAsEvent = self.uniqueName('saveSpec') + requestSaveEvent = self.uniqueName('requestSpecSave') + undoEvent = self.uniqueName('undoEvent') + redoEvent = self.uniqueName('redoEvent') + wireframeEvent = self.uniqueName('wireframeEvent') + oobeEvent = self.uniqueName('oobeEvent') + csEvent = self.uniqueName('csEvent') + runEvent = self.uniqueName('runEvent') + texEvent = self.uniqueName('texEvent') + self.editor = InGameEditor.InGameEditor(level=self, doneEvent=doneEvent, requestSaveEvent=requestSaveEvent, saveAsEvent=saveAsEvent, undoEvent=undoEvent, redoEvent=redoEvent, wireframeEvent=wireframeEvent, oobeEvent=oobeEvent, csEvent=csEvent, runEvent=runEvent, texEvent=texEvent) + self.acceptOnce(doneEvent, self.doneEditing) + self.accept(saveAsEvent, self.saveSpec) + self.accept(requestSaveEvent, self.requestSpecSave) + self.accept(undoEvent, self.doUndo) + self.accept(redoEvent, self.doRedo) + self.accept(wireframeEvent, self.doWireframe) + self.accept(oobeEvent, self.doOobe) + self.accept(csEvent, self.doCs) + self.accept(runEvent, self.doRun) + self.accept(texEvent, self.doTex) + self.accept(self.editor.getEventMsgName('Select'), self.handleEntitySelect) + self.accept(self.editor.getEventMsgName('Flash'), self.handleEntitySelect) + self.editorInitialized = 1 + self.buildEntityTree() + return + + def editorIsLocalToon(self): + return self.editorAvId == base.localAvatar.doId + + def createEntityCreator(self): + return getInGameEditorEntityCreatorClass(self.level)(self) + + def doneEditing(self): + self.notify.debug('doneEditing') + if self.specModified: + if self.editor.askYesNo('Save the spec on the AI?'): + self.requestSpecSave() + self.sendUpdate('setFinished') + + def disable(self): + self.notify.debug('disable') + if self.editorInitialized and self.editorIsLocalToon(): + self.axis.removeNode() + del self.axis + if hasattr(self, 'entTypeReg'): + del self.entTypeReg + self.editorInitialized = 0 + Level.Level.destroyLevel(self) + if hasattr(self, 'editor'): + self.editor.quit() + del self.editor + DistributedObject.DistributedObject.disable(self) + self.ignoreAll() + + def getEntInstance(self, entId): + return self.level.getEntity(entId) + + def getEntInstanceNP(self, entId): + entity = self.getEntInstance(entId) + if entity is None: + return + if isinstance(entity, NodePath): + return entity + if hasattr(entity, 'getNodePath'): + return entity.getNodePath() + return + + def getEntInstanceNPCopy(self, entId): + np = self.getEntInstanceNP(entId) + if np is None: + return np + stashNodeGroups = [] + searches = ('**/+ActorNode', '**/+Character') + for search in searches: + stashNodeGroups.append(np.findAllMatches(search)) + + for group in stashNodeGroups: + if not group.isEmpty(): + group.stash() + + par = np.getParent() + copy = np.copyTo(par) + for group in stashNodeGroups: + if not group.isEmpty(): + group.unstash() + + return copy + + def saveSpec(self, filename): + return self.levelSpec.saveToDisk(filename) + + def setEntityParent(self, entity, parent): + parent.addChild(entity) + entity._parentEntity = parent + + def insertEntityIntoTree(self, entId): + ent = self.getEntity(entId) + if entId == LevelConstants.UberZoneEntId: + self.setEntityParent(ent, self) + return + parentEnt = self.getEntity(ent.parentEntId) + if parentEnt is not None: + self.setEntityParent(ent, parentEnt) + return + self.setEntityParent(ent, self.uberZoneEntity) + return + + def buildEntityTree(self): + self.setChildren([]) + entIds = self.entities.keys() + entIds.sort() + for entId in entIds: + ent = self.getEntity(entId) + ent.setChildren([]) + + for entId in entIds: + self.insertEntityIntoTree(entId) + + self.editor.refreshExplorer() + + def onEntityCreate(self, entId): + DistributedInGameEditor.notify.debug('onEntityCreate %s' % entId) + Level.Level.onEntityCreate(self, entId) + entityNP = self.getEntInstanceNP(entId) + if entityNP: + self.nodePathId2EntId[entityNP.id()] = entId + if not self.editorInitialized: + return + self.insertEntityIntoTree(entId) + self.editor.refreshExplorer() + if entId == self.entitiesWeCreated[0]: + self.entitiesWeCreated = self.entitiesWeCreated[1:] + self.editor.selectEntity(entId) + + def onEntityDestroy(self, entId): + DistributedInGameEditor.notify.debug('onEntityDestroy %s' % entId) + ent = self.getEntity(entId) + if self.editorInitialized: + entityNP = self.getEntInstanceNP(entId) + if entityNP in self.nodePathId2EntId: + del self.nodePathId2EntId[entityNP.id()] + if ent is self.selectedEntity: + self.editor.clearAttribEditPane() + self.selectedEntity = None + ent._parentEntity.removeChild(ent) + del ent._parentEntity + self.editor.refreshExplorer() + Level.Level.onEntityDestroy(self, entId) + return + + def handleEntitySelect(self, entity): + self.selectedEntity = entity + if hasattr(self, 'identifyIval'): + self.identifyIval.finish() + if entity is self: + self.editor.clearAttribEditPane() + else: + entityNP = self.getEntInstanceNP(entity.entId) + if entityNP is not None: + dur = float(0.5) + oColor = entityNP.getColorScale() + flashIval = Sequence(Func(Functor(entityNP.setColorScale, 1, 0, 0, 1)), WaitInterval(dur / 3), Func(Functor(entityNP.setColorScale, 0, 1, 0, 1)), WaitInterval(dur / 3), Func(Functor(entityNP.setColorScale, 0, 0, 1, 1)), WaitInterval(dur / 3), Func(Functor(entityNP.setColorScale, oColor[0], oColor[1], oColor[2], oColor[3]))) + boundIval = Sequence(Func(entityNP.showBounds), WaitInterval(dur * 0.5), Func(entityNP.hideBounds)) + entCp = self.getEntInstanceNPCopy(entity.entId) + entCp.setRenderModeWireframe() + entCp.setTextureOff(1) + wireIval = Sequence(Func(Functor(entCp.setColor, 1, 0, 0, 1, 1)), WaitInterval(dur / 3), Func(Functor(entCp.setColor, 0, 1, 0, 1, 1)), WaitInterval(dur / 3), Func(Functor(entCp.setColor, 0, 0, 1, 1, 1)), WaitInterval(dur / 3), Func(entCp.removeNode)) + self.identifyIval = Parallel(flashIval, boundIval, wireIval) + + def putAxis(self = self, entityNP = entityNP): + self.axis.reparentTo(entityNP) + self.axis.setPos(0, 0, 0) + self.axis.setHpr(0, 0, 0) + + def takeAxis(self = self): + self.axis.reparentTo(hidden) + + self.identifyIval = Sequence(Func(putAxis), Parallel(self.identifyIval, WaitInterval(1000.5)), Func(takeAxis)) + self.identifyIval.start() + self.editor.updateAttribEditPane(entity.entId, self.levelSpec, self.entTypeReg) + entType = self.getEntityType(entity.entId) + menu = self.editor.menuBar.component('Entity-menu') + index = menu.index('Remove Selected Entity') + if entType in self.entTypeReg.getPermanentTypeNames(): + menu.entryconfigure(index, state='disabled') + else: + menu.entryconfigure(index, state='normal') + return + + def privSendAttribEdit(self, entId, attrib, value): + self.specModified = 1 + valueStr = repr(value) + self.notify.debug("sending edit: %s: '%s' = %s" % (entId, attrib, valueStr)) + self.sendUpdate('setEdit', [entId, + attrib, + valueStr, + self.editUsername]) + + def privExecActionList(self, actions): + for action in actions: + if callable(action): + action() + else: + entId, attrib, value = action + self.privSendAttribEdit(entId, attrib, value) + + def setUndoableAttribEdit(self, old2new, new2old): + self.redoStack = [] + self.undoStack.append((new2old, old2new)) + self.privExecActionList(old2new) + + def setAttribEdit(self, entId, attrib, value, canUndo = 1): + oldValue = eval(repr(self.levelSpec.getEntitySpec(entId)[attrib])) + new2old = [(entId, attrib, oldValue)] + old2new = [(entId, attrib, value)] + self.setUndoableAttribEdit(old2new, new2old) + if not canUndo: + self.undoStack = [] + + def doUndo(self): + if len(self.undoStack) == 0: + self.editor.showWarning('Nothing left to undo') + return + undo = self.undoStack.pop() + self.redoStack.append(undo) + new2old, old2new = undo + self.privExecActionList(new2old) + + def doRedo(self): + if len(self.redoStack) == 0: + self.editor.showWarning('Nothing to redo') + return + redo = self.redoStack.pop() + self.undoStack.append(redo) + new2old, old2new = redo + self.privExecActionList(old2new) + + def doWireframe(self): + messenger.send('magicWord', ['~wire']) + + def doOobe(self): + messenger.send('magicWord', ['~oobe']) + + def doCs(self): + messenger.send('magicWord', ['~cs']) + + def doRun(self): + messenger.send('magicWord', ['~run']) + + def doTex(self): + messenger.send('magicWord', ['~tex']) + + def insertEntity(self, entType, parentEntId = None, callback = None): + if parentEntId is None: + try: + parentEntId = self.selectedEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid parent entity first.', 'error') + return + + removeAction = (self.editMgrEntity.entId, 'removeEntity', {'entId': 'REPLACEME'}) + new2old = [removeAction] + + def setNewEntityId(entId, self = self, action = removeAction, callback = callback): + action[2]['entId'] = entId + if callback: + callback(entId) + + def setEntCreateHandler(self = self, handler = setNewEntityId): + self.entCreateHandlerQ.append(handler) + + old2new = [setEntCreateHandler, (self.editMgrEntity.entId, 'requestNewEntity', {'entType': entType, + 'parentEntId': parentEntId, + 'username': self.editUsername})] + self.setUndoableAttribEdit(old2new, new2old) + return + + def setEntityCreatorUsername(self, entId, editUsername): + Level.Level.setEntityCreatorUsername(self, entId, editUsername) + if editUsername == self.getEditUsername(): + print 'entity %s about to be created; we requested it' % entId + callback = self.entCreateHandlerQ[0] + del self.entCreateHandlerQ[:1] + callback(entId) + self.entitiesWeCreated.append(entId) + + def removeSelectedEntity(self): + try: + selectedEntId = self.selectedEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid entity to be removed first.', 'error') + return -1 + + if self.getEntity(selectedEntId).getNumChildren() > 0: + self.editor.showWarning('Remove children first.') + return -1 + self.doRemoveEntity(selectedEntId) + + def removeSelectedEntityTree(self): + try: + selectedEntId = self.selectedEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid entity to be removed first.', 'error') + return -1 + + def removeEntity(entId): + entity = self.getEntity(entId) + for child in entity.getChildren(): + removeEntity(child.entId) + + self.doRemoveEntity(entId) + + removeEntity(selectedEntId) + + def doRemoveEntity(self, entId): + parentEntId = self.getEntity(entId)._parentEntity.entId + entType = self.getEntityType(entId) + if entType in self.entTypeReg.getPermanentTypeNames(): + self.editor.showWarning("Cannot remove entities of type '%s'" % entType) + return + removeAction = (self.editMgrEntity.entId, 'removeEntity', {'entId': entId}) + old2new = [removeAction] + oldAttribs = [] + spec = self.levelSpec.getEntitySpecCopy(entId) + del spec['type'] + for attrib, value in spec.items(): + oldAttribs.append((attrib, value)) + + def setNewEntityId(entId, self = self, removeAction = removeAction, oldAttribs = oldAttribs): + removeAction[2]['entId'] = entId + for attrib, value in spec.items(): + self.privSendAttribEdit(entId, attrib, value) + + def setEntCreateHandler(self = self, handler = setNewEntityId): + self.entCreateHandlerQ.append(handler) + + new2old = [setEntCreateHandler, (self.editMgrEntity.entId, 'requestNewEntity', {'entType': self.getEntityType(entId), + 'parentEntId': parentEntId, + 'username': self.editUsername})] + self.setUndoableAttribEdit(old2new, new2old) + + def makeCopyOfEntName(self, name): + prefix = 'copy of ' + suffix = ' (%s)' + oldName = name + if len(oldName) <= len(prefix): + newName = prefix + oldName + elif oldName[:len(prefix)] != prefix: + newName = prefix + oldName + else: + hasSuffix = True + copyNum = 2 + if oldName[-1] != ')': + hasSuffix = False + if hasSuffix and oldName[-2] in string.digits: + i = len(oldName) - 2 + numString = '' + while oldName[i] in string.digits: + numString = oldName[i] + numString + i -= 1 + + if oldName[i] != '(': + hasSuffix = False + else: + i -= 1 + if oldName[i] != ' ': + hasSuffix = False + else: + print 'numString: %s' % numString + copyNum = int(numString) + 1 + if hasSuffix: + newName = oldName[:i] + suffix % copyNum + else: + newName = oldName + suffix % copyNum + return newName + + def duplicateSelectedEntity(self): + try: + selectedEntId = self.selectedEntity.entId + parentEntId = self.selectedEntity._parentEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid entity to be removed first.', 'error') + return + + if self.selectedEntity.getNumChildren() > 0: + self.editor.showTodo('Cannot duplicate entity with children.') + return + removeAction = (self.editMgrEntity.entId, 'removeEntity', {'entId': selectedEntId}) + new2old = [removeAction] + copyAttribs = self.levelSpec.getEntitySpecCopy(selectedEntId) + copyAttribs['comment'] = '' + copyAttribs['name'] = self.makeCopyOfEntName(copyAttribs['name']) + typeDesc = self.entTypeReg.getTypeDesc(copyAttribs['type']) + attribDescs = typeDesc.getAttribDescDict() + for attribName, attribDesc in attribDescs.items(): + if attribDesc.getDatatype() == 'const': + del copyAttribs[attribName] + + def setNewEntityId(entId, self = self, removeAction = removeAction, copyAttribs = copyAttribs): + removeAction[2]['entId'] = entId + for attribName, value in copyAttribs.items(): + self.privSendAttribEdit(entId, attribName, value) + + def setEntCreateHandler(self = self, handler = setNewEntityId): + self.entCreateHandlerQ.append(handler) + + old2new = [setEntCreateHandler, (self.editMgrEntity.entId, 'requestNewEntity', {'entType': self.getEntityType(selectedEntId), + 'parentEntId': parentEntId, + 'username': self.editUsername})] + self.setUndoableAttribEdit(old2new, new2old) + + def specPrePickle(self, spec): + for attribName, value in spec.items(): + spec[attribName] = repr(value) + + return spec + + def specPostUnpickle(self, spec): + for attribName, value in spec.items(): + spec[attribName] = eval(value) + + return spec + + def handleImportEntities(self): + try: + selectedEntId = self.selectedEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid entity first.', 'error') + return + + import tkFileDialog + filename = tkFileDialog.askopenfilename(parent=self.editor.parent, defaultextension='.egroup', filetypes=[('Entity Group', '.egroup'), ('All Files', '*')]) + if len(filename) == 0: + return + try: + import pickle + f = open(filename, 'r') + eTree = pickle.load(f) + eGroup = pickle.load(f) + for entId, spec in eGroup.items(): + eGroup[entId] = self.specPostUnpickle(spec) + + except: + self.editor.showWarning("Error importing entity group from '%s'." % filename, 'error') + return + + oldEntId2new = {} + + def addEntities(treeEntry, parentEntId, eGroup = eGroup): + for entId, children in treeEntry.items(): + spec = eGroup[entId] + entType = spec['type'] + del spec['type'] + del spec['parentEntId'] + typeDesc = self.entTypeReg.getTypeDesc(entType) + for attribName, attribDesc in typeDesc.getAttribDescDict().items(): + if attribDesc.getDatatype() == 'const': + if attribName in spec: + del spec[attribName] + + def handleEntityInsertComplete(newEntId, oldEntId = entId, oldEntId2new = oldEntId2new, spec = spec, treeEntry = treeEntry, addEntities = addEntities): + oldEntId2new[oldEntId] = newEntId + + def assignAttribs(entId = newEntId, oldEntId = oldEntId, spec = spec, treeEntry = treeEntry): + for attribName in spec: + self.setAttribEdit(entId, attribName, spec[attribName]) + + addEntities(treeEntry[oldEntId], newEntId) + + self.acceptOnce(self.getEntityCreateEvent(newEntId), assignAttribs) + + self.insertEntity(entType, parentEntId=parentEntId, callback=handleEntityInsertComplete) + + addEntities(eTree, selectedEntId) + + def handleExportEntity(self): + try: + selectedEntId = self.selectedEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid entity first.', 'error') + return + + import tkFileDialog + filename = tkFileDialog.asksaveasfilename(parent=self.editor.parent, defaultextension='.egroup', filetypes=[('Entity Group', '.egroup'), ('All Files', '*')]) + if len(filename) == 0: + return + eTree = {selectedEntId: {}} + eGroup = {} + eGroup[selectedEntId] = self.levelSpec.getEntitySpecCopy(selectedEntId) + for entId, spec in eGroup.items(): + eGroup[entId] = self.specPrePickle(spec) + + try: + import pickle + f = open(filename, 'w') + pickle.dump(eTree, f) + pickle.dump(eGroup, f) + except: + self.editor.showWarning("Error exporting entity group to '%s'." % filename, 'error') + return + + def handleExportEntityTree(self): + try: + selectedEntId = self.selectedEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid entity first.', 'error') + return + + import tkFileDialog + filename = tkFileDialog.asksaveasfilename(parent=self.editor.parent, defaultextension='.egroup', filetypes=[('Entity Group', '.egroup'), ('All Files', '*')]) + if len(filename) == 0: + return + eTree = {} + eGroup = {} + + def addEntity(entId, treeEntry): + treeEntry[entId] = {} + eGroup[entId] = self.levelSpec.getEntitySpecCopy(entId) + entity = self.getEntity(entId) + for child in entity.getChildren(): + addEntity(child.entId, treeEntry[entId]) + + addEntity(selectedEntId, eTree) + for entId, spec in eGroup.items(): + eGroup[entId] = self.specPrePickle(spec) + + try: + import pickle + f = open(filename, 'w') + pickle.dump(eTree, f) + pickle.dump(eGroup, f) + except: + self.editor.showWarning("Error exporting entity group to '%s'." % filename, 'error') + return + + def moveAvToSelected(self): + try: + selectedEntId = self.selectedEntity.entId + except AttributeError: + self.editor.showWarning('Please select a valid entity first.', 'error') + return + + entNp = self.getEntInstanceNP(selectedEntId) + if entNp is None: + zoneEntId = self.levelSpec.getEntityZoneEntId(selectedEntId) + entNp = self.getEntInstanceNP(zoneEntId) + base.localAvatar.setPos(entNp, 0, 0, 0) + base.localAvatar.setHpr(entNp, 0, 0, 0) + zoneNum = self.getEntityZoneEntId(selectedEntId) + self.level.enterZone(zoneNum) + return + + def requestSpecSave(self): + self.privSendAttribEdit(LevelConstants.EditMgrEntId, 'requestSave', None) + self.specModified = 0 + return + + def setAttribChange(self, entId, attrib, valueStr, username): + if username == self.editUsername: + print 'we got our own edit back!' + value = eval(valueStr) + self.levelSpec.setAttribChange(entId, attrib, value, username) + + def getTypeName(self): + return 'Level' diff --git a/toontown/coghq/DistributedInGameEditorAI.py b/toontown/coghq/DistributedInGameEditorAI.py new file mode 100644 index 0000000..762b742 --- /dev/null +++ b/toontown/coghq/DistributedInGameEditorAI.py @@ -0,0 +1,57 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObjectAI +from direct.directutil import DistributedLargeBlobSenderAI +from SpecImports import * + +class DistributedInGameEditorAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedInGameEditorAI') + + def __init__(self, air, level, editorAvId, editUsername): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.editorAvId = editorAvId + self.editUsername = editUsername + self.level = level + self.levelDoId = self.level.getDoId() + self.generateWithRequired(level.zoneId) + + def generate(self): + self.notify.debug('generate') + DistributedObjectAI.DistributedObjectAI.generate(self) + simbase.levelEditor = self + self.acceptOnce(self.air.getAvatarExitEvent(self.editorAvId), self.setFinished) + self.accept(self.level.getAttribChangeEventName(), self.handleAttribChange) + + def delete(self): + self.notify.debug('delete') + messenger.send(self.getDoneEvent()) + DistributedObjectAI.DistributedObjectAI.delete(self) + self.ignoreAll() + + def getDoneEvent(self): + return self.uniqueName('levelEditorDone') + + def getEditorAvId(self): + return self.editorAvId + + def getEditUsername(self): + return self.editUsername + + def getLevelDoId(self): + return self.levelDoId + + def requestCurrentLevelSpec(self): + print 'requestCurrentLevelSpec' + spec = self.level.levelSpec + specStr = repr(spec) + largeBlob = DistributedLargeBlobSenderAI.DistributedLargeBlobSenderAI(self.air, self.zoneId, self.editorAvId, specStr, useDisk=simbase.air._specByDisk) + self.sendUpdateToAvatarId(self.editorAvId, 'setSpecSenderDoId', [largeBlob.doId]) + + def setEdit(self, entId, attribName, valueStr, username): + self.level.setAttribChange(entId, attribName, eval(valueStr), username) + + def handleAttribChange(self, entId, attrib, value, username): + self.sendUpdateToAvatarId(self.editorAvId, 'setAttribChange', [ + entId, attrib, repr(value), username]) + + def setFinished(self): + self.requestDelete() diff --git a/toontown/coghq/DistributedLaserField.py b/toontown/coghq/DistributedLaserField.py new file mode 100644 index 0000000..4d331e4 --- /dev/null +++ b/toontown/coghq/DistributedLaserField.py @@ -0,0 +1,715 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BattleBlocker +from toontown.toonbase import TTLocalizer +import random + +class DistributedLaserField(BattleBlocker.BattleBlocker): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLaserField') + laserFieldModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BattleBlocker.BattleBlocker.__init__(self, cr) + node = hidden.attachNewNode('DistributedNodePathEntity') + if not hasattr(self, 'radius'): + self.radius = 5 + if not hasattr(self, 'blockerX'): + self.blockerX = 0.0 + if not hasattr(self, 'blockerY'): + self.blockerY = 0.0 + self.blockerZ = -10000 + self.gridWireGN = None + self.gridBeamGN = None + self.traceWireGN = None + self.traceBeamGN = None + self.gridSeed = 0 + self.gridScaleX = 2.0 + self.gridScaleY = 2.0 + self.zFloat = 0.05 + self.projector = Point3(0, 0, 25) + self.tracePath = [] + self.gridData = [[0, 0]] * 2 + self.gridNumX = 2 + self.gridNumY = 2 + self.tracePath.append((0.51, 0.51, 1)) + self.tracePath.append((0.59, 0.51, 1)) + self.tracePath.append((0.55, 0.59, 1)) + self.tracePath.append((0.51, 0.51, 1)) + self.gridSymbols = [] + self.makeGridSymbols() + self.isToonIn = 0 + self.toonX = -1 + self.toonY = -1 + self.isToonInRange = 0 + self.detectCount = 0 + self.cameraHold = None + self.gridGame = 'some game' + self.gridGameText = ' ' + self.activeLF = 1 + self.successSound = loader.loadSfx('phase_11/audio/sfx/LB_capacitor_discharge_3.mp3') + self.successTrack = Parallel(SoundInterval(self.successSound, node=self, volume=0.8)) + self.failSound = loader.loadSfx('phase_11/audio/sfx/LB_sparks_1.mp3') + self.failTrack = Parallel(SoundInterval(self.failSound, node=self, volume=0.8)) + return + + def generateInit(self): + self.notify.debug('generateInit') + BattleBlocker.BattleBlocker.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BattleBlocker.BattleBlocker.announceGenerate(self) + self.gridWireNode = self.attachNewNode('grid Wire Node') + self.gridWireGN = GeomNode('grid wire') + self.gridWireNode.attachNewNode(self.gridWireGN) + self.gridWireNode.setRenderModeWireframe() + self.gridWireNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.gridWireNode.setTwoSided(True) + self.gridWireNode.setBin('fixed', 1) + self.gridWireNode.setDepthWrite(False) + self.gridBeamNode = self.attachNewNode('grid Beam Node') + self.gridBeamGN = GeomNode('grid beam') + self.gridBeamNode.attachNewNode(self.gridBeamGN) + self.gridBeamNode.setTransparency(TransparencyAttrib.MAlpha) + self.gridBeamNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.gridBeamNode.setTwoSided(True) + self.gridBeamNode.setBin('fixed', 1) + self.gridBeamNode.setDepthWrite(False) + self.traceWireNode = self.attachNewNode('trace Wire Node') + self.traceWireGN = GeomNode('trace Wire') + self.traceWireNode.attachNewNode(self.traceWireGN) + self.traceWireNode.setRenderModeWireframe() + self.traceWireNode.setTransparency(TransparencyAttrib.MAlpha) + self.traceWireNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.traceWireNode.setTwoSided(True) + self.traceWireNode.setBin('fixed', 1) + self.traceWireNode.setDepthWrite(False) + self.traceBeamNode = self.attachNewNode('trace Beam Node') + self.traceBeamGN = GeomNode('trace Beam') + self.traceBeamNode.attachNewNode(self.traceBeamGN) + self.traceBeamNode.setTransparency(TransparencyAttrib.MAlpha) + self.traceBeamNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.traceBeamNode.setTwoSided(True) + self.traceBeamNode.setBin('fixed', 1) + self.traceBeamNode.setDepthWrite(False) + self.loadModel() + self.detectName = 'laserField %s' % self.doId + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + + def initCollisionGeom(self): + pass + + def setGridGame(self, gameName): + self.gridGame = gameName + self.gridGameText = gameName + if gameName == 'MineSweeper': + self.gridGameText = TTLocalizer.LaserGameMine + elif gameName == 'Roll': + self.gridGameText = TTLocalizer.LaserGameRoll + elif gameName == 'Avoid': + self.gridGameText = TTLocalizer.LaserGameAvoid + elif gameName == 'Drag': + self.gridGameText = TTLocalizer.LaserGameDrag + else: + self.gridGameText = TTLocalizer.LaserGameDefault + + def setActiveLF(self, active = 1): + if active: + self.activeLF = 1 + else: + self.activeLF = 0 + + def setSuccess(self, success): + if success: + self.successTrack.start() + else: + self.startBattle() + self.failTrack.start() + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, 0) + + def makeGridSymbols(self): + symbolBlank = [0, (1.0, 0.0, 0.0), ()] + symbolOne = [None, (1.0, 0.0, 0.0), ((0.45, 0.8), + (0.55, 0.8), + (0.55, 0.2), + (0.45, 0.2), + (0.45, 0.8))] + symbolTwo = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.7, 0.8), + (0.7, 0.45), + (0.4, 0.45), + (0.4, 0.3), + (0.7, 0.3), + (0.7, 0.2), + (0.3, 0.2), + (0.3, 0.55), + (0.6, 0.55), + (0.6, 0.7), + (0.3, 0.7), + (0.3, 0.8))] + symbolThree = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.3, 0.2), + (0.3, 0.3), + (0.6, 0.3), + (0.6, 0.45), + (0.4, 0.45), + (0.4, 0.55), + (0.6, 0.55), + (0.6, 0.7), + (0.3, 0.7), + (0.3, 0.8))] + symbolFour = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolFive = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolSix = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolSeven = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolEight = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolNine = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.4, 0.8), + (0.4, 0.6), + (0.6, 0.6), + (0.6, 0.8), + (0.7, 0.8), + (0.7, 0.2), + (0.6, 0.2), + (0.6, 0.5), + (0.3, 0.5), + (0.3, 0.8))] + symbolSquare = [None, (1.0, 0.0, 0.0), ((0.1, 0.9), + (0.9, 0.9), + (0.9, 0.1), + (0.1, 0.1), + (0.1, 0.9))] + symbolTriangle = [None, (0.0, 1.0, 0.0), ((0.1, 0.1), + (0.5, 0.9), + (0.9, 0.1), + (0.1, 0.1))] + symbolBlueSquare = [None, (0.3, 0.3, 1.0), ((0.1, 0.9), + (0.9, 0.9), + (0.9, 0.1), + (0.1, 0.1), + (0.1, 0.9))] + symbolHiddenBomb = [self.sendFail, (1.0, 0.0, 0.0), ((0.1, 0.9), + (0.9, 0.9), + (0.9, 0.1), + (0.1, 0.1), + (0.1, 0.9))] + symbolBomb = [self.sendFail, (1.0, 0.0, 0.0), ((0.8, 1.0), + (1.0, 0.8), + (0.8, 0.6), + (0.8, 0.4), + (0.6, 0.2), + (0.4, 0.2), + (0.2, 0.4), + (0.2, 0.6), + (0.4, 0.8), + (0.6, 0.8), + (0.8, 1.0))] + symbolSkull = [self.sendFail, (1.0, 0.0, 0.0), ((0.5, 0.9), + (0.7, 0.8), + (0.7, 0.6), + (0.6, 0.5), + (0.6, 0.4), + (0.5, 0.4), + (0.5, 0.3), + (0.7, 0.4), + (0.8, 0.4), + (0.8, 0.3), + (0.7, 0.3), + (0.6, 0.25), + (0.7, 0.2), + (0.8, 0.2), + (0.8, 0.1), + (0.7, 0.1), + (0.5, 0.2), + (0.3, 0.1), + (0.2, 0.1), + (0.2, 0.2), + (0.3, 0.2), + (0.4, 0.25), + (0.3, 0.3), + (0.2, 0.3), + (0.2, 0.4), + (0.3, 0.4), + (0.5, 0.3), + (0.5, 0.4), + (0.4, 0.4), + (0.4, 0.5), + (0.3, 0.6), + (0.3, 0.8), + (0.5, 0.9))] + symbolDot = [None, (1.0, 0.0, 0.0), ((0.4, 0.6), + (0.6, 0.6), + (0.6, 0.4), + (0.4, 0.4), + (0.4, 0.6))] + symbolRedX = [None, (1.0, 0.0, 0.0), ((0.3, 0.8), + (0.5, 0.6), + (0.7, 0.8), + (0.8, 0.7), + (0.6, 0.5), + (0.8, 0.3), + (0.7, 0.2), + (0.5, 0.4), + (0.3, 0.2), + (0.2, 0.3), + (0.4, 0.5), + (0.2, 0.7), + (0.3, 0.8))] + self.symbolSelect = [(1.0, 0.0, 0.0), ((0.05, 0.95), + (0.95, 0.95), + (0.95, 0.05), + (0.05, 0.05), + (0.05, 0.95))] + symbolBlueDot = [None, (0.5, 0.5, 1.0), ((0.5, 0.7), + (0.7, 0.5), + (0.5, 0.3), + (0.3, 0.5), + (0.5, 0.7))] + self.gridSymbols = [] + self.gridSymbols.append(symbolBlank) + self.gridSymbols.append(symbolOne) + self.gridSymbols.append(symbolTwo) + self.gridSymbols.append(symbolThree) + self.gridSymbols.append(symbolFour) + self.gridSymbols.append(symbolFive) + self.gridSymbols.append(symbolSix) + self.gridSymbols.append(symbolSeven) + self.gridSymbols.append(symbolEight) + self.gridSymbols.append(symbolNine) + self.gridSymbols.append(symbolSquare) + self.gridSymbols.append(symbolHiddenBomb) + self.gridSymbols.append(symbolSkull) + self.gridSymbols.append(symbolTriangle) + self.gridSymbols.append(symbolDot) + self.gridSymbols.append(symbolBlueSquare) + self.gridSymbols.append(symbolRedX) + self.gridSymbols.append(symbolBlueDot) + return None + + def sendFail(self): + self.sendUpdate('trapFire', []) + + def __detect(self, task): + distance = self.centerNode.getDistance(localAvatar) + greaterDim = self.gridScaleX + if self.gridScaleY > self.gridScaleX: + greaterDim = self.gridScaleY + self.detectCount += 1 + if self.detectCount > 5: + self.detectCount = 0 + if distance < greaterDim * 0.75: + if not self.isToonInRange: + self.doToonInRange() + if localAvatar.getPos(self)[0] > 0 and localAvatar.getPos(self)[0] < self.gridScaleX and localAvatar.getPos(self)[1] > 0 and localAvatar.getPos(self)[1] < self.gridScaleY: + self.__toonHit() + else: + if self.isToonIn: + self.toonX = -1 + self.toonY = -1 + self.isToonIn = 0 + self.genGrid() + self.isToonIn = 0 + elif self.isToonInRange: + self.doToonOutOfRange() + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + return Task.done + + def doToonInRange(self): + self.isToonInRange = 1 + if self.activeLF: + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -20.0 * heightScaleFactor, camHeight + 8.0) + base.localAvatar.setIdealCameraPos(cameraPoint) + self.level.stage.showInfoText(self.gridGameText) + + def doToonOutOfRange(self): + self.isToonInRange = 0 + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + self.cameraHold = None + return + + def __toonHit(self): + posX = localAvatar.getPos(self)[0] + posY = localAvatar.getPos(self)[1] + tileX = int(posX / (self.gridScaleX / self.gridNumX)) + tileY = int(posY / (self.gridScaleY / self.gridNumY)) + oldX = self.toonX + oldY = self.toonY + if self.toonX != tileX or self.toonY != tileY or not self.isToonIn: + self.toonX = tileX + self.toonY = tileY + self.isToonIn = 1 + if self.gridData[tileX][tileY] < len(self.gridSymbols): + tileFunction = self.gridSymbols[self.gridData[tileX][tileY]][0] + if tileFunction: + tileFunction() + self.sendHit(self.toonX, self.toonY, oldX, oldY) + self.genGrid() + self.isToonIn = 1 + + def __testTile(self): + if self.toonX >= 0 and self.toonY >= 0 and self.toonX < self.gridNumX and self.toonY < self.gridNumY: + if self.gridData[self.toonX][self.toonY] < len(self.gridSymbols): + tileFunction = self.gridSymbols[self.gridData[self.toonX][self.toonY]][0] + if tileFunction: + tileFunction() + + def sendHit(self, newX, newY, oldX, oldY): + if self.toonX >= 0: + self.sendUpdate('hit', [newX, + newY, + oldX, + oldY]) + + def disable(self): + self.notify.debug('disable') + if self.failTrack.isPlaying(): + self.failTrack.finish() + if self.successTrack.isPlaying(): + self.successTrack.finish() + self.ignoreAll() + taskMgr.remove(self.detectName) + BattleBlocker.BattleBlocker.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + BattleBlocker.BattleBlocker.delete(self) + + def loadModel(self): + self.rotateNode = self.attachNewNode('rotate') + self.model = None + self.centerNode = self.attachNewNode('center') + self.centerNode.setPos(self.gridScaleX * 0.5, self.gridScaleY * 0.5, 0.0) + self.genGrid() + return + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def genTrace(self): + wireRed = 1.0 + wireGreen = 0.0 + wireBlue = 0.0 + wireAlpha = 0.5 + beamRed = 0.04 + beamGreen = 0.0 + beamBlue = 0.0 + beamAlpha = 0.1 + self.gFormat = GeomVertexFormat.getV3cp() + self.traceWireVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.traceWireVertexWriter = GeomVertexWriter(self.traceWireVertexData, 'vertex') + self.traceWireColorWriter = GeomVertexWriter(self.traceWireVertexData, 'color') + self.traceBeamVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.traceBeamVertexWriter = GeomVertexWriter(self.traceBeamVertexData, 'vertex') + self.traceBeamColorWriter = GeomVertexWriter(self.traceBeamVertexData, 'color') + self.traceWireVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.traceWireColorWriter.addData4f(wireRed, wireGreen, wireBlue, wireAlpha) + self.traceBeamVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.traceBeamColorWriter.addData4f(0.0, 0.0, 0.0, 0.0) + for vertex in self.tracePath: + self.traceWireVertexWriter.addData3f(vertex[0] * self.gridScaleX, vertex[1] * self.gridScaleY, self.zFloat) + self.traceWireColorWriter.addData4f(wireRed, wireGreen, wireBlue, wireAlpha) + self.traceBeamVertexWriter.addData3f(vertex[0] * self.gridScaleX, vertex[1] * self.gridScaleY, self.zFloat) + self.traceBeamColorWriter.addData4f(beamRed, beamGreen, beamBlue, beamAlpha) + + self.traceBeamTris = GeomTriangles(Geom.UHStatic) + self.traceWireTris = GeomLinestrips(Geom.UHStatic) + vertexCounter = 1 + sizeTrace = len(self.tracePath) + chainTrace = sizeTrace - 1 + previousTrace = 1 + for countVertex in range(0, sizeTrace): + self.traceWireTris.addVertex(countVertex + 1) + + self.traceWireTris.closePrimitive() + for countVertex in range(0, chainTrace): + self.traceBeamTris.addVertex(0) + self.traceBeamTris.addVertex(countVertex + 1) + self.traceBeamTris.addVertex(countVertex + 2) + self.traceBeamTris.closePrimitive() + + self.traceWireGeom = Geom(self.traceWireVertexData) + self.traceWireGeom.addPrimitive(self.traceWireTris) + self.traceWireGN.addGeom(self.traceWireGeom) + self.traceBeamGeom = Geom(self.traceBeamVertexData) + self.traceBeamGeom.addPrimitive(self.traceBeamTris) + self.traceBeamGN.addGeom(self.traceBeamGeom) + + def createGrid(self): + gridScaleX = self.gridScaleX / self.gridNumX + gridScaleY = self.gridScaleY / self.gridNumY + red = 0.25 + green = 0.25 + blue = 0.25 + alpha = 0.5 + beamRed = 0.04 + beamGreen = 0.04 + beamBlue = 0.04 + beamAlpha = 0.1 + self.gFormat = GeomVertexFormat.getV3cp() + self.gridVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.gridVertexWriter = GeomVertexWriter(self.gridVertexData, 'vertex') + self.gridColorWriter = GeomVertexWriter(self.gridVertexData, 'color') + self.beamVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.beamVertexWriter = GeomVertexWriter(self.beamVertexData, 'vertex') + self.beamColorWriter = GeomVertexWriter(self.beamVertexData, 'color') + self.gridVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.gridColorWriter.addData4f(red, green, blue, 0.0) + self.beamVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.beamColorWriter.addData4f(0.0, 0.0, 0.0, 0.0) + border = 0.4 + for column in range(0, self.gridNumX): + columnLeft = 0.0 + gridScaleX * column + columnRight = columnLeft + gridScaleX + rowBottom = 0 + for row in range(0, self.gridNumY): + rowTop = rowBottom + gridScaleY + if self.gridData[column][row] and self.gridData[column][row] < len(self.gridSymbols): + gridColor = self.gridSymbols[self.gridData[column][row]][1] + gridSymbol = self.gridSymbols[self.gridData[column][row]][2] + sizeSymbol = len(gridSymbol) + for iVertex in range(sizeSymbol): + vertex = gridSymbol[iVertex] + self.gridVertexWriter.addData3f(columnLeft + vertex[0] * gridScaleX, rowBottom + vertex[1] * gridScaleY, self.zFloat) + self.gridColorWriter.addData4f(gridColor[0] * red, gridColor[1] * green, gridColor[2] * blue, alpha) + self.beamVertexWriter.addData3f(columnLeft + vertex[0] * gridScaleX, rowBottom + vertex[1] * gridScaleY, self.zFloat) + self.beamColorWriter.addData4f(gridColor[0] * beamRed, gridColor[1] * beamGreen, gridColor[2] * beamBlue, beamAlpha) + + rowBottom = rowTop + + if self.isToonIn: + gridSymbol = self.symbolSelect[1] + gridColor = self.symbolSelect[0] + sizeSymbol = len(gridSymbol) + for iVertex in range(sizeSymbol): + vertex = gridSymbol[iVertex] + self.gridVertexWriter.addData3f(self.toonX * gridScaleX + vertex[0] * gridScaleX, self.toonY * gridScaleY + vertex[1] * gridScaleY, self.zFloat) + self.gridColorWriter.addData4f(gridColor[0] * red, gridColor[1] * green, gridColor[2] * blue, alpha) + self.beamVertexWriter.addData3f(self.toonX * gridScaleX + vertex[0] * gridScaleX, self.toonY * gridScaleY + vertex[1] * gridScaleY, self.zFloat) + self.beamColorWriter.addData4f(gridColor[0] * beamRed, gridColor[1] * beamGreen, gridColor[2] * beamBlue, beamAlpha) + + self.gridTris = GeomLinestrips(Geom.UHDynamic) + self.beamTris = GeomTriangles(Geom.UHDynamic) + vertexCounter = 1 + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + if self.gridData[column][row] and self.gridData[column][row] < len(self.gridSymbols): + gridSymbol = self.gridSymbols[self.gridData[column][row]][2] + sizeSymbol = len(gridSymbol) + for iVertex in range(sizeSymbol): + self.gridTris.addVertex(vertexCounter + iVertex) + + self.gridTris.closePrimitive() + for iVertex in range(sizeSymbol - 1): + self.beamTris.addVertex(0) + self.beamTris.addVertex(vertexCounter + iVertex + 0) + self.beamTris.addVertex(vertexCounter + iVertex + 1) + self.beamTris.closePrimitive() + + vertexCounter += sizeSymbol + + if self.isToonIn: + gridSymbol = self.symbolSelect[1] + sizeSymbol = len(gridSymbol) + for iVertex in range(sizeSymbol): + self.gridTris.addVertex(vertexCounter + iVertex) + + self.gridTris.closePrimitive() + for iVertex in range(sizeSymbol - 1): + self.beamTris.addVertex(0) + self.beamTris.addVertex(vertexCounter + iVertex + 0) + self.beamTris.addVertex(vertexCounter + iVertex + 1) + self.beamTris.closePrimitive() + + self.wireGeom = Geom(self.gridVertexData) + self.wireGeom.addPrimitive(self.gridTris) + self.gridWireGN.addGeom(self.wireGeom) + self.beamGeom = Geom(self.beamVertexData) + self.beamGeom.addPrimitive(self.beamTris) + self.gridBeamGN.addGeom(self.beamGeom) + + def setGrid(self, gridNumX, gridNumY): + self.gridNumX = gridNumX + self.gridNumY = gridNumY + self.gridData = [] + for i in range(0, gridNumX): + self.gridData.append([0] * gridNumY) + + self.genGrid() + + def getGrid(self): + return (self.gridNumX, self.gridNumY) + + def setGridScaleX(self, gridScale): + self.gridScaleX = gridScale + self.centerNode.setPos(self.gridScaleX * 0.5, self.gridScaleY * 0.5, 0.0) + self.blockerX = self.gridScaleX * 0.5 + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, self.blockerZ) + self.genGrid() + + def setGridScaleY(self, gridScale): + self.gridScaleY = gridScale + self.centerNode.setPos(self.gridScaleX * 0.5, self.gridScaleY * 0.5, 0.0) + self.blockerY = self.gridScaleY + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, self.blockerZ) + self.genGrid() + + def setField(self, fieldData): + if fieldData[0] != self.gridNumX or fieldData[1] != self.gridNumY: + self.setGrid(fieldData[0], fieldData[1]) + fieldCounter = 2 + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + if len(fieldData) > fieldCounter: + self.gridData[column][row] = fieldData[fieldCounter] + fieldCounter += 1 + + self.genGrid() + self.__testTile() + + def getField(self): + fieldData.append(self.game.gridNumX) + fieldData.append(self.game.gridNumY) + fieldData = [] + for column in range(0, self.game.gridNumX): + for row in range(0, self.game.gridNumY): + fieldData.append(self.game.gridData[column][row]) + + return fieldData + + def setSeed(self, seed): + self.gridSeed = seed + random.seed(seed) + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + rint = random.randint(0, 2) + self.gridData[column][row] = rint + + self.genGrid() + + def getSeed(self): + return self.gridSeed + + def setMode(self, mode): + self.mode = mode + + def getMode(self): + return self.mode + + def setProjector(self, projPoint): + self.projector = projPoint + if self.gridWireGN and self.gridBeamGN: + self.genGrid() + + def genGrid(self): + if self.activeLF: + if self.gridWireGN: + self.gridWireGN.removeAllGeoms() + if self.gridBeamGN: + self.gridBeamGN.removeAllGeoms() + if self.gridWireGN and self.gridBeamGN: + self.createGrid() + + def hideSuit(self, suitIdarray): + for suitId in suitIdarray: + suit = base.cr.doId2do.get(suitId) + if suit: + suit.stash() + + def showSuit(self, suitIdarray): + for suitId in suitIdarray: + suit = base.cr.doId2do.get(suitId) + if suit: + suit.unstash() + suit.setVirtual() + + def initCollisionGeom(self): + print 'Laser Field initCollisionGeom' + self.blockerX = self.gridScaleX * 0.5 + self.blockerY = self.gridScaleY + self.cSphere = CollisionSphere(0, 0, 0, self.blockerX) + self.cSphereNode = CollisionNode('battleBlocker-%s-%s' % (self.level.getLevelId(), self.entId)) + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.attachNewNode(self.cSphereNode) + self.cSphereNodePath.setPos(self.blockerX, self.blockerY, self.blockerZ) + self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.cSphere.setTangible(0) + self.enterEvent = 'enter' + self.cSphereNode.getName() + self.accept(self.enterEvent, self.__handleToonEnter) + + def __handleToonEnter(self, collEntry): + self.notify.debug('__handleToonEnter, %s' % self.entId) + self.sendFail() diff --git a/toontown/coghq/DistributedLaserFieldAI.py b/toontown/coghq/DistributedLaserFieldAI.py new file mode 100644 index 0000000..95f7b8c --- /dev/null +++ b/toontown/coghq/DistributedLaserFieldAI.py @@ -0,0 +1,217 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.task import Task +from otp.level import DistributedEntityAI +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import BattleBlockerAI +from toontown.coghq import LaserGameMineSweeper +from toontown.coghq import LaserGameRoll +from toontown.coghq import LaserGameAvoid +from toontown.coghq import LaserGameDrag +import random + +class DistributedLaserFieldAI(BattleBlockerAI.BattleBlockerAI, NodePath, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + BattleBlockerAI.BattleBlockerAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedLaserFieldAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + self.gridScale = 1 + self.game = LaserGameRoll.LaserGameRoll(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + if not hasattr(self, 'gridGame'): + self.gridGame = 'Roll' + self.enabled = 1 + self.hasShownSuits = 0 + self.healReady = 1 + self.playedSound = 0 + self.canButton = 1 + self.title = 'MemTag: This is a laserField %s' % random.random() + + def setGridGame(self, gameName): + if gameName == 'Random': + gameName = random.choice(['MineSweeper', 'Roll', 'Avoid', 'Drag']) + self.gridGame = gameName + if hasattr(self, 'game'): + self.game.delete() + self.game = None + if gameName == 'Drag': + self.game = LaserGameDrag.LaserGameDrag(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + else: + if gameName == 'MineSweeper': + self.game = LaserGameMineSweeper.LaserGameMineSweeper(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + else: + if gameName == 'Roll': + self.game = LaserGameRoll.LaserGameRoll(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + else: + if gameName == 'Avoid': + self.game = LaserGameAvoid.LaserGameAvoid(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + else: + self.game = LaserGameMineSweeper.LaserGameMineSweeper(self.trapDisable, self.trapFire, self.sendField, self.setGrid) + self.game.startGrid() + self.sendField() + self.sendUpdate('setGridGame', [gameName]) + return + + def generate(self): + BattleBlockerAI.BattleBlockerAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.detectName = 'laserField %s' % self.doId + taskMgr.doMethodLater(1.0, self.__detect, self.detectName) + self.setPos(self.pos) + self.setHpr(self.hpr) + self.setGridGame(self.gridGame) + + def registerBlocker(self): + BattleBlockerAI.BattleBlockerAI.registerBlocker(self) + self.hideSuits() + + def delete(self): + taskMgr.remove(self.detectName) + self.ignoreAll() + self.game.delete() + self.game = None + BattleBlockerAI.BattleBlockerAI.delete(self) + return + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + BattleBlockerAI.BattleBlockerAI.destroy(self) + + def setGrid(self, gridNumX, gridNumY): + self.game.setGridSize(gridNumX, gridNumY) + + def getGrid(self): + return ( + self.game.gridNumX, self.game.gridNumY) + + def getField(self): + fieldData = [] + fieldData.append(self.game.gridNumX) + fieldData.append(self.game.gridNumY) + for column in range(0, self.game.gridNumX): + for row in range(0, self.game.gridNumY): + fieldData.append(self.game.gridData[column][row]) + + return fieldData + + def sendField(self): + self.sendUpdate('setField', [self.getField()]) + + def __detect(self, task): + isThereAnyToons = False + if hasattr(self, 'level'): + toonInRange = 0 + for avId in self.level.presentAvIds: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + isThereAnyToons = True + distance = self.getDistance(av) + + if isThereAnyToons: + taskMgr.doMethodLater(1.0, self.__detect, self.detectName) + self.__run() + return Task.done + + def hit(self, hitX, hitY, oldX, oldY): + if self.enabled: + self.game.hit(hitX, hitY, oldX, oldY) + + def __run(self): + pass + + def __toonHit(self): + self.gridNumX = random.randint(1, 4) + self.gridNumY = random.randint(1, 4) + self.gridScale = random.randint(1, 4) + self.sendUpdate('setGrid', [self.gridNumX, self.gridNumY, self.gridScale]) + + def reactToSwitch(self, on): + if on and self.canButton: + self.trapDisable() + self.game.win() + + def trapFire(self): + if not self.enabled: + return + self.enabled = 0 + self.game.lose() + self.showSuits() + stage = self.air.getDo(self.level.stageDoId) + stage.resetPuzzelReward() + self.healReady = 0 + if not __dev__ and 1: + self.canButton = 0 + self.sendUpdate('setActiveLF', [0]) + if not self.playedSound: + self.sendUpdate('setSuccess', [0]) + self.playedSound = 1 + + def setBattleFinished(self): + print 'battle Finished' + BattleBlockerAI.BattleBlockerAI.setBattleFinished(self) + messenger.send(self.getOutputEventName(), [1]) + self.switchFire() + + def switchFire(self): + print 'switchFire' + if self.switchId != 0: + switch = self.level.getEntity(self.switchId) + if switch: + switch.setIsOn(1) + + def trapDisable(self): + if not self.enabled: + return + self.enabled = 0 + suits = self.level.planner.battleCellId2suits.get(self.cellId) + messenger.send(self.getOutputEventName(), [1]) + if self.hasShownSuits == 0: + for suit in suits: + suit.requestRemoval() + + self.sendUpdate('setActiveLF', [0]) + stage = self.air.getDo(self.level.stageDoId) + reward = stage.getPuzzelReward() + if self.healReady: + for avId in self.level.presentAvIds: + av = self.air.doId2do.get(avId) + if av: + av.toonUp(reward) + + stage.increasePuzzelReward() + self.healReady = 0 + if not self.playedSound: + self.sendUpdate('setSuccess', [1]) + self.playedSound = 1 + self.switchFire() + + def hideSuits(self): + suits = self.level.planner.battleCellId2suits.get(self.cellId) + suitArray = [] + for suit in suits: + suitArray.append(suit.doId) + + if suitArray: + self.sendUpdate('hideSuit', [suitArray]) + + def showSuits(self): + if self.hasShownSuits == 0: + suits = self.level.planner.battleCellId2suits.get(self.cellId) + suitArray = [] + for suit in suits: + suit.setVirtual() + suitArray.append(suit.doId) + + if suitArray: + self.sendUpdate('showSuit', [suitArray]) + self.hasShownSuits = 1 + + def addSuit(self, suit): + print 'Adding Suit %s' % suit.doId + BattleBlockerAI.BattleBlockerAI.addSuit(self, suit) diff --git a/toontown/coghq/DistributedLawOffice.py b/toontown/coghq/DistributedLawOffice.py new file mode 100644 index 0000000..810e603 --- /dev/null +++ b/toontown/coghq/DistributedLawOffice.py @@ -0,0 +1,69 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import LawOfficeBase +import FactoryEntityCreator +import FactorySpecs +from otp.level import LevelSpec +from otp.level import LevelConstants +from toontown.toonbase import TTLocalizer +from toontown.coghq import FactoryCameraViews +from direct.distributed.DistributedObject import DistributedObject +if __dev__: + from otp.level import EditorGlobals + +class DistributedLawOffice(DistributedObject, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOffice') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + LawOfficeBase.LawOfficeBase.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.level = None + return + + def generate(self): + self.notify.debug('generate') + self.accept('lawOfficeFloorDone', self.handleFloorDone) + + def delete(self): + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.ignore('lawOfficeFloorDone') + + def setLawOfficeId(self, id): + LawOfficeBase.LawOfficeBase.setLawOfficeId(self, id) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleFloorDone(self): + self.sendUpdate('readyForNextFloor') + + def disable(self): + self.notify.debug('disable') + base.localAvatar.setCameraCollisionsCanMove(0) + + def getTaskZoneId(self): + return self.lawOfficeId + + def startSignal(self): + base.camera.setScale(base.localAvatar.getScale()) + localAvatar.setCameraFov(DefaultCameraFov) + base.camera.clearMat() diff --git a/toontown/coghq/DistributedLawOfficeAI.py b/toontown/coghq/DistributedLawOfficeAI.py new file mode 100644 index 0000000..c7d376f --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeAI.py @@ -0,0 +1,142 @@ +from otp.level import DistributedLevelAI +from direct.directnotify import DirectNotifyGlobal +import cPickle, LevelSuitPlannerAI, LawOfficeBase +from direct.task import Task +import FactoryEntityCreatorAI, FactorySpecs +from otp.level import LevelSpec +import CogDisguiseGlobals +from toontown.suit import DistributedFactorySuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import DistributedBattleFactoryAI +from toontown.coghq import LawOfficeLayout +from toontown.coghq import DistributedLawOfficeFloorAI +from toontown.coghq import LawOfficeLayout +from toontown.coghq import DistributedLawOfficeElevatorIntAI +from toontown.building import DistributedElevatorFloorAI +from toontown.ai.ToonBarrier import * +from direct.distributed.DistributedObjectAI import * +from direct.showbase import PythonUtil + +class DistributedLawOfficeAI(DistributedObjectAI, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOfficeAI') + + def __init__(self, air, lawOfficeId, zoneId, entranceId, avIds): + DistributedObjectAI.__init__(self, air) + LawOfficeBase.LawOfficeBase.__init__(self) + self.setLawOfficeId(lawOfficeId) + self.layout = None + self.currentFloor = 0 + self.elevator = None + self.elevatorB = None + self.activeElevator = 0 + self.level = None + self.avIds = avIds + self.entranceId = entranceId + return + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getFactoryCreditMultiplier(self.lawOfficeId) + + def generate(self): + self.notify.info('generate') + self.notify.info('start factory %s %s creation, frame=%s' % (self.lawOfficeId, self.doId, globalClock.getFrameCount())) + DistributedObjectAI.generate(self) + self.layout = LawOfficeLayout.LawOfficeLayout(self.lawOfficeId) + self.exitEvents = {} + for avId in self.avIds: + self.exitEvents[avId] = simbase.air.getAvatarExitEvent(avId) + self.accept(self.exitEvents[avId], Functor(self.handleAvatarExit, avId)) + + self.startOffice() + + def handleAvatarExit(self, toonId): + pass + + def readyForNextFloor(self): + toonId = self.air.getAvatarIdFromSender() + + def generateWithRequired(self, zone): + DistributedObjectAI.generateWithRequired(self, zone) + + def startOffice(self): + self.notify.info('loading spec') + specModule = self.layout.getFloorSpec(self.currentFloor) + self.level = DistributedLawOfficeFloorAI.DistributedLawOfficeFloorAI(self.air, self.lawOfficeId, self.zoneId, self.entranceId, self.avIds, specModule) + self.level.setLevelSpec(LevelSpec.LevelSpec(specModule)) + self.notify.info('creating entities') + self.level.generateWithRequired(self.zoneId) + self.elevator = DistributedElevatorFloorAI.DistributedElevatorFloorAI(self.air, self.doId, self, self.avIds) + self.elevator.setEntering(0) + self.elevator.generateWithRequired(self.zoneId) + self.elevatorB = DistributedElevatorFloorAI.DistributedElevatorFloorAI(self.air, self.doId, self, self.avIds) + self.elevatorB.setEntering(1) + self.elevatorB.generateWithRequired(self.zoneId) + self.exchangeElevators() + + def delete(self): + self.notify.info('delete: %s' % self.doId) + if self.elevator: + del self.elevator + if self.level: + self.level.requestDelete() + self.level = None + self.ignoreAll() + return + + def exchangeElevators(self): + if self.activeElevator == 0: + self.elevator.lock() + self.elevatorB.unlock() + self.activeElevator = 1 + else: + self.elevator.unlock() + self.elevatorB.lock() + self.activeElevator = 0 + + def startNextFloor(self): + if self.avIds: + print self.avIds + self.currentFloor += 1 + specModule = self.layout.getFloorSpec(self.currentFloor) + self.level.requestDelete() + self.level = DistributedLawOfficeFloorAI.DistributedLawOfficeFloorAI(self.air, self.lawOfficeId, self.zoneId, self.entranceId, self.avIds, specModule) + self.level.setLevelSpec(LevelSpec.LevelSpec(specModule)) + self.level.generateWithRequired(self.zoneId) + print 'exchanging elevators' + self.exchangeElevators() + self.startSignal() + + def startSignal(self): + self.sendUpdate('startSignal') + + def dumpEveryone(self, optArg=None): + pass + + def getTaskZoneId(self): + return self.lawOfficeId + + def getLawOfficeId(self): + return self.lawOfficeId + + def getCogLevel(self): + return self.level.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds diff --git a/toontown/coghq/DistributedLawOfficeElevatorExt.py b/toontown/coghq/DistributedLawOfficeElevatorExt.py new file mode 100644 index 0000000..8cdac78 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorExt.py @@ -0,0 +1,107 @@ +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.gui import DirectGui +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer + +class DistributedLawOfficeElevatorExt(DistributedElevatorExt.DistributedElevatorExt): + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + self.type = ELEVATOR_OFFICE + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorExt.DistributedElevatorExt.delete(self) + + def setEntranceId(self, entranceId): + self.entranceId = entranceId + geom = self.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_origin_%s' % entranceId) + if locator: + self.elevatorModel.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + else: + self.notify.error('No origin found for originId: %s' % entranceId) + entranceId2zoneId = {0: ToontownGlobals.LawbotStageIntA, + 1: ToontownGlobals.LawbotStageIntB, + 2: ToontownGlobals.LawbotStageIntC, + 3: ToontownGlobals.LawbotStageIntD} + self.intZoneId = entranceId2zoneId[entranceId] + locator = geom.find('**/elevator_signorigin_%s' % entranceId) + backgroundGeom = geom.find('**/ElevatorFrameFront_%d' % entranceId) + backgroundGeom.node().setEffect(DecalEffect.make()) + signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.GlobalStreetNames[self.intZoneId][-1]), font=ToontownGlobals.getSuitFont(), scale=2, fg=(0.87, 0.87, 0.87, 1), mayChange=False, parent=backgroundGeom) + signText.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + signText.setDepthWrite(0) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_10/models/cogHQ/mintElevator') + self.elevatorModel.reparentTo(render) + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right_door') + DistributedElevator.DistributedElevator.setupElevator(self) + self.elevatorSphereNodePath.setY(-1.42) + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setLawOfficeInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + doneStatus = {'loader': 'cogHQLoader', + 'where': 'stageInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId, + 'stageId': self.intZoneId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setLawOfficeInteriorZoneForce(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': 'stageInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'hoodId': hoodId, + 'stageId': self.intZoneId} + 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("setLawOfficeInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def getDestName(self): + if self.intZoneId == ToontownGlobals.LawbotStageIntA: + return TTLocalizer.ElevatorLawBotCourse0 + elif self.intZoneId == ToontownGlobals.LawbotStageIntB: + return TTLocalizer.ElevatorLawBotCourse1 + elif self.intZoneId == ToontownGlobals.LawbotStageIntC: + return TTLocalizer.ElevatorLawBotCourse2 + elif self.intZoneId == ToontownGlobals.LawbotStageIntD: + return TTLocalizer.ElevatorLawBotCourse3 diff --git a/toontown/coghq/DistributedLawOfficeElevatorExtAI.py b/toontown/coghq/DistributedLawOfficeElevatorExtAI.py new file mode 100644 index 0000000..53d7e50 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorExtAI.py @@ -0,0 +1,50 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from toontown.building.ElevatorConstants import * +from toontown.building import DistributedElevatorExtAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task + +class DistributedLawOfficeElevatorExtAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + + def __init__(self, air, bldg, lawOfficeId, entranceId, antiShuffle=0, minLaff=0): + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg, antiShuffle=antiShuffle, minLaff=minLaff) + self.lawOfficeId = lawOfficeId + self.entranceId = entranceId + + def getEntranceId(self): + return self.entranceId + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + + lawOfficeZone = self.bldg.createLawOffice(self.lawOfficeId, self.entranceId, players) + for seatIndex in range(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setLawOfficeInteriorZone', [lawOfficeZone]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + return + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + officeZone = self.bldg.createLawOffice(self.lawOfficeId, self.entranceId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setLawOfficeInteriorZoneForce', [ + officeZone]) diff --git a/toontown/coghq/DistributedLawOfficeElevatorInt.py b/toontown/coghq/DistributedLawOfficeElevatorInt.py new file mode 100644 index 0000000..e0e9f5b --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorInt.py @@ -0,0 +1,87 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorFloor +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer + +class DistributedLawOfficeElevatorInt(DistributedElevatorFloor.DistributedElevatorFloor): + + def __init__(self, cr): + DistributedElevatorFloor.DistributedElevatorFloor.__init__(self, cr) + + def generate(self): + DistributedElevator.DistributedElevator.generate(self) + self.accept('LawOffice_Spec_Loaded', self.__placeElevator) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorFloor.DistributedElevatorFloor.delete(self) + self.ignore('LawOffice_Spec_Loaded') + + 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 setupElevator(self): + self.elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + self.elevatorModel.reparentTo(render) + 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() + DistributedElevatorFloor.DistributedElevatorFloor.setupElevator(self) + + 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, 0, -15) + self.elevatorModel.reparentTo(elevatorNode) + else: + self.notify.debug('NO NODE SlidingDoor!!') + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + 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/coghq/DistributedLawOfficeElevatorIntAI.py b/toontown/coghq/DistributedLawOfficeElevatorIntAI.py new file mode 100644 index 0000000..2ebe47d --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeElevatorIntAI.py @@ -0,0 +1,50 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from toontown.building.ElevatorConstants import * +from toontown.building import DistributedElevatorFloorAI +from toontown.building import DistributedElevatorAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task + +class DistributedLawOfficeElevatorIntAI(DistributedElevatorFloorAI.DistributedElevatorFloorAI): + + def __init__(self, air, lawOfficeId, bldg, avIds): + DistributedElevatorFloorAI.DistributedElevatorFloorAI.__init__(self, air, bldg, avIds) + self.lawOfficeId = lawOfficeId + + def getEntranceId(self): + return self.entranceId + + def elevatorClosed(self): + 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: + print 'THIS AV ID %s IS NOT ON BOARD' % avId + + self.bldg.startNextFloor() + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + return + + def enterClosed(self): + print 'DistributedLawOfficeElevatorIntAI.elevatorClosed %s' % self.doId + DistributedElevatorFloorAI.DistributedElevatorFloorAI.enterClosed(self) + if not self.hasOpenedLocked or not self.isLocked: + self.fsm.request('opening') + if self.isLocked: + self.hasOpenedLocked = 1 diff --git a/toontown/coghq/DistributedLawOfficeFloor.py b/toontown/coghq/DistributedLawOfficeFloor.py new file mode 100644 index 0000000..cf9bb04 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeFloor.py @@ -0,0 +1,193 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import LawOfficeBase +import FactoryEntityCreator +import FactorySpecs +from otp.level import LevelSpec +from otp.level import LevelConstants +from toontown.toonbase import TTLocalizer +from toontown.coghq import FactoryCameraViews +if __dev__: + from otp.level import EditorGlobals + +class DistributedLawOfficeFloor(DistributedLevel.DistributedLevel, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOffice') + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + LawOfficeBase.LawOfficeBase.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + self.factoryViews = FactoryCameraViews.FactoryCameraViews(self) + base.localAvatar.chatMgr.chatInputSpeedChat.addFactoryMenu() + if __dev__: + bboard.post(EditorGlobals.EditTargetPostName, self) + self.accept('SOSPanelEnter', self.handleSOSPanel) + + def delete(self): + DistributedLevel.DistributedLevel.delete(self) + base.localAvatar.chatMgr.chatInputSpeedChat.removeFactoryMenu() + self.factoryViews.delete() + del self.factoryViews + self.ignore('SOSPanelEnter') + if __dev__: + bboard.removeIfEqual(EditorGlobals.EditTargetPostName, self) + + def setLawOfficeId(self, id): + LawOfficeBase.LawOfficeBase.setLawOfficeId(self, id) + + def setForemanConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.ForemanConfrontedMsg % av.getName()) + return + + def setDefeated(self): + self.notify.info('setDefeated') + messenger.send('FactoryWinEvent') + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = FactorySpecs.getFactorySpecModule(self.lawOfficeId) + factorySpec = LevelSpec.LevelSpec(specModule) + if __dev__: + typeReg = self.getEntityTypeReg() + factorySpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.initializeLevel(self, factorySpec) + + def privGotSpec(self, levelSpec): + if __dev__: + if not levelSpec.hasEntityTypeReg(): + typeReg = self.getEntityTypeReg() + levelSpec.setEntityTypeReg(typeReg) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + base.factoryReady = 1 + messenger.send('FactoryReady') + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + modelCount = len(levelSpec.getAllEntIds()) + loader.beginBulkLoad('factory', TTLocalizer.HeadingToFactoryTitle % TTLocalizer.FactoryNames[self.lawOfficeId], modelCount, 1, TTLocalizer.TIP_COGHQ) + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + loader.endBulkLoad('factory') + messenger.send('LawOffice_Spec_Loaded') + + def printPos(self = self): + pos = base.localAvatar.getPos(self.getZoneNode(self.lastToonZone)) + h = base.localAvatar.getH(self.getZoneNode(self.lastToonZone)) + print 'factory pos: %s, h: %s, zone %s' % (repr(pos), h, self.lastToonZone) + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nZone: %s' % str(self.lastToonZone) + base.localAvatar.setChat(posStr, CFThought, 0) + + self.accept('f2', printPos) + base.localAvatar.setCameraCollisionsCanMove(1) + self.acceptOnce('leavingFactory', self.announceLeaving) + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleFloorDone(self): + self.sendUpdate('readyForNextFloor') + + def disable(self): + self.notify.debug('disable') + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + newSuitIds = [] + for suitId in self.suitIds: + if suitId not in oldSuitIds: + newSuitIds.append(suitId) + + if len(newSuitIds): + + def bringOutOfReserve(suits): + for suit in suits: + suit.comeOutOfReserve() + + self.relatedObjectMgrRequest = self.cr.relatedObjectMgr.requestObjects(newSuitIds, bringOutOfReserve) + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 2 + + def getGoonPathId(self): + return 'sellbotFactory' + + def getTaskZoneId(self): + return self.lawOfficeId + + def getBossTaunt(self): + return TTLocalizer.FactoryBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.FactoryBossBattleTaunt + + def placeLocalToon(self): + initialZoneEnt = None + if self.entranceId in self.entranceId2entity: + epEnt = self.entranceId2entity[self.entranceId] + initialZoneEnt = self.getEntity(epEnt.getZoneEntId()) + elif self.EmulateEntrancePoint: + self.notify.debug('unknown entranceId %s' % self.entranceId) + 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) + else: + initialZoneEnt = self.getEntity(LevelConstants.UberZoneEntId) + if initialZoneEnt is not None: + self.enterZone(initialZoneEnt.entId) + return diff --git a/toontown/coghq/DistributedLawOfficeFloorAI.py b/toontown/coghq/DistributedLawOfficeFloorAI.py new file mode 100644 index 0000000..758ebf1 --- /dev/null +++ b/toontown/coghq/DistributedLawOfficeFloorAI.py @@ -0,0 +1,146 @@ +from otp.level import DistributedLevelAI +from direct.directnotify import DirectNotifyGlobal +import cPickle, LevelSuitPlannerAI, LawOfficeBase +from direct.task import Task +import FactoryEntityCreatorAI, FactorySpecs +from otp.level import LevelSpec +import CogDisguiseGlobals +from toontown.suit import DistributedFactorySuitAI +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import DistributedBattleFactoryAI +from toontown.coghq import LawOfficeLayout +from toontown.coghq import DistributedLawOfficeElevatorIntAI +from direct.distributed import DistributedObjectAI +from toontown.ai.ToonBarrier import * + +class DistributedLawOfficeFloorAI(DistributedLevelAI.DistributedLevelAI, LawOfficeBase.LawOfficeBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawOfficeAI') + + def __init__(self, air, lawOfficeId, zoneId, entranceId, avIds, spec): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, entranceId, avIds) + LawOfficeBase.LawOfficeBase.__init__(self) + self.setLawOfficeId(lawOfficeId) + self.layout = None + self.elevator = None + self.level = None + self.spec = spec + return + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getFactoryCreditMultiplier(self.lawOfficeId) + + def generate(self): + self.notify.info('generate') + self.notify.info('start factory %s %s creation, frame=%s' % (self.lawOfficeId, self.doId, globalClock.getFrameCount())) + self.layout = LawOfficeLayout.LawOfficeLayout(self.lawOfficeId) + self.startFloor() + + def startFloor(self): + self.notify.info('loading spec') + self.factorySpec = LevelSpec.LevelSpec(self.spec) + if __dev__: + self.notify.info('creating entity type registry') + typeReg = self.getEntityTypeReg() + self.factorySpec.setEntityTypeReg(typeReg) + self.notify.info('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, self.factorySpec) + self.notify.info('creating cogs') + cogSpecModule = FactorySpecs.getCogSpecModule(self.lawOfficeId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedFactorySuitAI.DistributedFactorySuitAI, DistributedBattleFactoryAI.DistributedBattleFactoryAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + scenario = 0 + description = '%s|%s|%s|%s' % (self.lawOfficeId, self.entranceId, scenario, self.avIdList) + for avId in self.avIdList: + self.air.writeServerEvent('DAOffice Entered', avId, description) + + self.notify.info('finish factory %s %s creation' % (self.lawOfficeId, self.doId)) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + + DistributedLevelAI.DistributedLevelAI.delete(self, False) + + def readyForNextFloor(self): + toonId = self.air.getAvatarIdFromSender() + self.__barrier.clear(toonId) + + def dumpEveryone(self): + pass + + def getTaskZoneId(self): + return self.lawOfficeId + + def getLawOfficeId(self): + return self.lawOfficeId + + def d_setForemanConfronted(self, avId): + if avId in self.avIdList: + self.sendUpdate('setForemanConfronted', [avId]) + else: + self.notify.warning('%s: d_setForemanConfronted: av %s not in av list %s' % (self.doId, avId, self.avIdList)) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + + scenario = 0 + description = '%s|%s|%s|%s' % (self.lawOfficeId, self.entranceId, scenario, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('DAOffice Defeated', avId, description) + + for toon in activeVictors: + simbase.air.questManager.toonDefeatedFactory(toon, self.lawOfficeId, activeVictors) + + return + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds diff --git a/toontown/coghq/DistributedLawbotBossGavel.py b/toontown/coghq/DistributedLawbotBossGavel.py new file mode 100644 index 0000000..8c5f50d --- /dev/null +++ b/toontown/coghq/DistributedLawbotBossGavel.py @@ -0,0 +1,166 @@ +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 otp.otpbase import OTPGlobals +from direct.actor import Actor + +class DistributedLawbotBossGavel(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossGavel') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedLawbotBossGavel') + self.boss = None + self.index = None + self.avId = 0 + self.modelPath = 'phase_11/models/lawbotHQ/LB_gavel' + self.modelFindString = None + self.nodePath = None + self.ival = None + self.origHpr = Point3(0, 0, 0) + self.downTime = 0.5 + self.upTime = 5 + self.gavelSfx = None + return + + def announceGenerate(self): + self.notify.debug('announceGenerate: %s' % self.doId) + DistributedObject.DistributedObject.announceGenerate(self) + self.name = 'gavel-%s' % self.doId + self.loadModel(self.modelPath, self.modelFindString) + self.nodePath.wrtReparentTo(render) + self.gavelSfx = loader.loadSfx('phase_11/audio/sfx/LB_gavel.mp3') + tempTuple = ToontownGlobals.LawbotBossGavelPosHprs[self.index] + self.nodePath.setPosHpr(*tempTuple) + self.origHpr = Point3(tempTuple[3], tempTuple[4], tempTuple[5]) + self.downTime = ToontownGlobals.LawbotBossGavelTimes[self.index][0] + self.upTime = ToontownGlobals.LawbotBossGavelTimes[self.index][1] + self.stayDownTime = ToontownGlobals.LawbotBossGavelTimes[self.index][2] + self.boss.gavels[self.index] = self + + def delete(self): + DistributedObject.DistributedObject.delete(self) + loader.unloadModel(self.modelPath) + self.nodePath.removeNode() + + def loadModel(self, modelPath, modelFindString = None): + if self.nodePath == None: + self.makeNodePath() + else: + self.gavel.getChildren().detach() + model = loader.loadModel(modelPath) + if modelFindString != None: + modTel = model.find('**/' + modelFindString) + parts = model.findAllMatches('**/gavel*') + gavelTop = model.find('**/top*') + gavelHandle = model.find('**/handle*') + model.instanceTo(self.gavel) + self.attachColTube() + self.scale = 3.0 + self.nodePath.setScale(self.scale) + return + + def attachColTube(self): + gavelTop = self.nodePath.find('**/top*') + self.gavelTop = gavelTop + gavelHandle = self.nodePath.find('**/handle*') + collNode = CollisionNode(self.uniqueName('headSphere')) + topBounds = gavelTop.getBounds() + center = topBounds.getCenter() + radius = topBounds.getRadius() + tube1 = CollisionTube(0, -1, center.getZ(), 0, 1, center.getZ(), 1) + tube1.setTangible(0) + collNode.addSolid(tube1) + collNode.setTag('attackCode', str(ToontownGlobals.BossCogGavelStomp)) + collNode.setName('GavelZap') + self.collNodePath = self.nodePath.attachNewNode(collNode) + handleBounds = gavelHandle.getBounds() + handleCenter = handleBounds.getCenter() + handleRadius = handleBounds.getRadius() + tube2 = CollisionTube(0, 0, handleCenter.getZ() + handleRadius, 0, 0, handleCenter.getZ() - handleRadius, 0.25) + tube2.setTangible(0) + handleCollNode = CollisionNode(self.uniqueName('gavelHandle')) + handleCollNode.addSolid(tube2) + handleCollNode.setTag('attackCode', str(ToontownGlobals.BossCogGavelHandle)) + handleCollNode.setName('GavelHandleZap') + self.handleCollNodePath = self.nodePath.attachNewNode(handleCollNode) + + def makeNodePath(self): + self.nodePath = Actor.Actor() + self.gavel = self.nodePath.attachNewNode('myGavel') + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.nodePath.detachNode() + if self.ival: + self.ival.finish() + self.ival = None + self.ignoreAll() + del self.boss.gavels[self.index] + self.cleanup() + return + + def cleanup(self): + self.boss = None + return + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def setIndex(self, index): + self.index = index + + def setState(self, state): + avId = 0 + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + elif state == 'N': + self.demand('On') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def enterOn(self): + self.notify.debug('enterOn for gavel %d' % self.index) + myHeadings = ToontownGlobals.LawbotBossGavelHeadings[self.index] + seqName = 'LawbotBossGavel-%s' % self.doId + self.ival = Sequence(name=seqName) + downAngle = -80 + for index in range(len(myHeadings)): + nextIndex = index + 1 + if nextIndex == len(myHeadings): + nextIndex = 0 + goingDown = self.nodePath.hprInterval(self.downTime, Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], 0, self.origHpr[2])) + self.ival.append(goingDown) + self.ival.append(SoundInterval(self.gavelSfx, node=self.gavelTop)) + self.ival.append(Wait(self.stayDownTime)) + goingUp = self.nodePath.hprInterval(self.upTime, Point3(myHeadings[nextIndex] + self.origHpr[0], 0, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2])) + self.ival.append(goingUp) + + self.ival.loop() + self.accept('enterGavelZap', self.__touchedGavel) + self.accept('enterGavelHandleZap', self.__touchedGavelHandle) + + def enterOff(self): + if self.ival: + self.ival.finish() + tempTuple = ToontownGlobals.LawbotBossGavelPosHprs[self.index] + self.nodePath.setPosHpr(*tempTuple) + + def __touchedGavel(self, entry): + self.notify.debug('__touchedGavel') + self.notify.debug('self=%s entry=%s' % (self, entry)) + self.boss.touchedGavel(self, entry) + + def __touchedGavelHandle(self, entry): + self.notify.debug('__touchedGavelHandle') + self.boss.touchedGavelHandle(self, entry) diff --git a/toontown/coghq/DistributedLawbotBossGavelAI.py b/toontown/coghq/DistributedLawbotBossGavelAI.py new file mode 100644 index 0000000..e48634f --- /dev/null +++ b/toontown/coghq/DistributedLawbotBossGavelAI.py @@ -0,0 +1,99 @@ +from direct.directnotify import DirectNotifyGlobal +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 DistributedLawbotBossGavelAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossGavelAI') + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedLawbotBossGavelAI') + self.boss = boss + self.index = index + cn = CollisionNode('controls') + cs = CollisionSphere(0, -6, 0, 6) + cn.addSolid(cs) + self.goonShield = NodePath(cn) + self.goonShield.setPosHpr(*ToontownGlobals.LawbotBossGavelPosHprs[self.index]) + self.avId = 0 + self.objectId = 0 + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def setState(self, state): + self.request(state) + + def d_setState(self, state): + newState = state + if state == 'On': + newState = 'N' + else: + if state == 'Off': + newState = 'F' + self.sendUpdate('setState', [newState]) + + def b_setState(self, state): + self.request(state) + self.d_setState(state) + + def turnOn(self): + self.b_setState('On') + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0: + craneId = self.__getCraneId(avId) + if craneId == 0: + self.request('Controlled', avId) + + def requestFree(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.request('Free') + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getCraneId(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.cranes: + if crane.avId == avId: + return crane.doId + + return 0 + + def enterOn(self): + pass + + def exitOn(slef): + pass + + def enterOff(self): + self.goonShield.detachNode() + + def exitOff(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C') + + 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') + + def exitFree(self): + pass diff --git a/toontown/coghq/DistributedLawbotCannon.py b/toontown/coghq/DistributedLawbotCannon.py new file mode 100644 index 0000000..0adfa23 --- /dev/null +++ b/toontown/coghq/DistributedLawbotCannon.py @@ -0,0 +1,885 @@ +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import CollisionSphere, CollisionNode +from toontown.toonbase import ToontownGlobals +from toontown.estate import DistributedCannon +from toontown.estate import CannonGlobals +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toon import NPCToons +from toontown.toon import ToonHead +from toontown.toonbase import TTLocalizer +from toontown.minigame import Trajectory +from toontown.effects import DustCloud +GROUND_PLANE_MIN = -15 +CANNON_ROTATION_MIN = -55 +CANNON_ROTATION_MAX = 50 +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_MIN = 10 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 +INITIAL_VELOCITY = 80 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 + +class DistributedLawbotCannon(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotCannon') + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + HIT_GROUND = 0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.index = None + self.avId = 0 + self.av = None + self.localToonShooting = 0 + self.cannonsActive = 0 + self.cannonLocation = None + self.cannonPostion = None + self.cannon = None + self.madeGui = 0 + self.jurorToon = None + self.toonModel = None + self.toonHead = None + self.toonScale = None + self.dustCloud = None + self.hitBumper = 0 + self.hitTarget = 0 + self.lastPos = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.vel = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.hitTrack = None + self.flyColNode = None + self.flyColNodePath = None + self.localAvId = base.localAvatar.doId + self.model_Created = 0 + return + + def disable(self): + taskMgr.remove(self.uniqueName('fireCannon')) + taskMgr.remove(self.uniqueName('shootTask')) + self.__stopFlyTask(self.avId) + taskMgr.remove(self.uniqueName('flyTask')) + self.ignoreAll() + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0) + self.nodePath.detachNode() + self.__unmakeGui() + if self.hitTrack: + self.hitTrack.finish() + del self.hitTrack + self.hitTrack = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.offstage() + self.unload() + DistributedObject.DistributedObject.delete(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.boss.cannons[self.index] = self + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('Cannon')) + self.load() + self.activateCannons() + + def setPosHpr(self, x, y, z, h, p, r): + self.nodePath.setPosHpr(x, y, z, h, p, r) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def getSphereRadius(self): + return 1.5 + + def getParentNodePath(self): + return render + + def setIndex(self, index): + self.index = index + + def load(self): + self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.dustCloud = DustCloud.DustCloud(render) + self.dustCloud.setBillboardPointEye() + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.uniqueName('CannonSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.cannon.reparentTo(self.nodePath) + self.kartColNode = CollisionNode(self.uniqueName('KartColNode')) + self.kartNode = self.nodePath.attachNewNode(self.kartColNode) + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.sndHitChair = base.loadSfx('phase_11/audio/sfx/LB_toon_jury.mp3') + self.cannon.hide() + self.flashingLabel = None + return + + def unload(self): + if self.cannon: + self.cannon.removeNode() + del self.cannon + if self.dustCloud != None: + self.dustCloud.destroy() + del self.dustCloud + del self.sndCannonMove + del self.sndCannonFire + del self.sndHitGround + del self.sndHitChair + if self.av: + self.__resetToon(self.av) + self.av.loop('neutral') + self.av.setPlayRate(1.0, 'run') + if self.toonHead != None: + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + del self.toonHead + if self.toonModel != None: + self.toonModel.removeNode() + del self.toonModel + if self.jurorToon != None: + self.jurorToon.delete() + del self.jurorToon + del self.toonScale + return + + def activateCannons(self): + if not self.cannonsActive: + self.cannonsActive = 1 + self.onstage() + self.nodePath.reparentTo(self.getParentNodePath()) + self.accept(self.uniqueName('enterCannonSphere'), self.__handleEnterSphere) + + def onstage(self): + self.__createCannon() + self.cannon.reparentTo(self.nodePath) + self.dustCloud.reparentTo(render) + + def offstage(self): + if self.cannon: + self.cannon.reparentTo(hidden) + if self.dustCloud: + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + + def __createCannon(self): + self.barrel = self.cannon.find('**/cannon') + self.cannonLocation = Point3(0, 0, 0.025) + self.cannonPosition = [0, CANNON_ANGLE_MIN] + self.cannon.setPos(self.cannonLocation) + self.__updateCannonPosition(self.avId) + + def updateCannonPosition(self, avId, zRot, angle): + if avId != self.localAvId: + self.cannonPosition = [zRot, angle] + self.__updateCannonPosition(avId) + + def __updateCannonPosition(self, avId): + self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0) + self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0) + maxP = 90 + newP = self.barrel.getP() + yScale = 1 - 0.5 * float(newP) / maxP + shadow = self.cannon.find('**/square_drop_shadow') + shadow.setScale(1, yScale, 1) + + def __handleEnterSphere(self, collEntry): + self.d_requestEnter() + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def setMovie(self, mode, avId, extraInfo): + wasLocalToon = self.localToonShooting + self.avId = avId + if mode == CannonGlobals.CANNON_MOVIE_CLEAR: + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LANDED: + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_FORCE_EXIT: + self.exitCannon(self.avId) + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LOAD: + if self.avId == base.localAvatar.doId: + self.cannonBallsLeft = extraInfo + base.cr.playGame.getPlace().setState('crane') + base.localAvatar.setTeleportAvailable(0) + self.localToonShooting = 1 + self.__makeGui() + camera.reparentTo(self.barrel) + camera.setPos(0.5, -2, 2.5) + camera.setHpr(0, 0, 0) + self.boss.toonEnteredCannon(self.avId, self.index) + if self.cr.doId2do.has_key(self.avId): + self.av = self.cr.doId2do[self.avId] + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.av.loop('neutral') + self.av.stopSmooth() + self.__destroyToonModels() + self.__createToonModels() + self.av.setPosHpr(3, 0, 0, 90, 0, 0) + self.av.reparentTo(self.cannon) + else: + self.notify.warning('Unknown avatar %d in cannon %d' % (self.avId, self.doId)) + else: + self.notify.warning('unhandled case, mode = %d' % mode) + + def __avatarGone(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0, 0) + + def __makeGui(self): + if self.madeGui: + return + NametagGlobals.setMasterArrowsOn(0) + guiModel = 'phase_4/models/gui/cannon_game_gui' + cannonGui = loader.loadModel(guiModel) + self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + cannonGui.removeNode() + self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + guiClose = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + cannonBallText = '%d/%d' % (self.cannonBallsLeft, ToontownGlobals.LawbotBossCannonBallMax) + self.cannonBallLabel = DirectLabel(parent=self.aimPad, text=cannonBallText, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0.475, 0.0, -0.35), scale=0.25) + if self.cannonBallsLeft < 5: + if self.flashingLabel: + self.flashingLabel.stop() + flashingTrack = Sequence() + for i in range(10): + flashingTrack.append(LerpColorScaleInterval(self.cannonBallLabel, 0.5, VBase4(1, 0, 0, 1))) + flashingTrack.append(LerpColorScaleInterval(self.cannonBallLabel, 0.5, VBase4(1, 1, 1, 1))) + + self.flashingLabel = flashingTrack + self.flashingLabel.start() + self.aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.__enableAimInterface() + self.madeGui = 1 + return + + def __unmakeGui(self): + self.notify.debug('__unmakeGui') + if not self.madeGui: + return + if self.flashingLabel: + self.flashingLabel.finish() + self.flashingLabel = None + NametagGlobals.setMasterArrowsOn(1) + self.__disableAimInterface() + self.upButton.unbind(DGG.B1PRESS) + self.upButton.unbind(DGG.B1RELEASE) + self.downButton.unbind(DGG.B1PRESS) + self.downButton.unbind(DGG.B1RELEASE) + self.leftButton.unbind(DGG.B1PRESS) + self.leftButton.unbind(DGG.B1RELEASE) + self.rightButton.unbind(DGG.B1PRESS) + self.rightButton.unbind(DGG.B1RELEASE) + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.madeGui = 0 + return + + def __enableAimInterface(self): + self.aimPad.show() + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__spawnLocalCannonMoveTask() + + def __disableAimInterface(self): + self.aimPad.hide() + self.ignore(self.FIRE_KEY) + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.FIRE_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.__killLocalCannonMoveTask() + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downReleased() + + def __leaveCannon(self): + self.notify.debug('__leaveCannon') + self.sendUpdate('requestLeave') + + def __firePressed(self): + self.notify.debug('fire pressed') + if not self.boss.state == 'BattleTwo': + self.notify.debug('boss is in state=%s, not firing' % self.boss.state) + return + self.__broadcastLocalCannonPosition() + self.__unmakeGui() + self.sendUpdate('setCannonLit', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) + + def __spawnLocalCannonMoveTask(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __killLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.cannonMoving: + self.sndCannonMove.stop() + + def __localCannonMoveTask(self, task): + pos = self.cannonPosition + oldRot = pos[0] + oldAng = pos[1] + rotVel = 0 + if self.leftPressed: + rotVel += CANNON_ROTATION_VEL + if self.rightPressed: + rotVel -= CANNON_ROTATION_VEL + pos[0] += rotVel * globalClock.getDt() + if pos[0] < CANNON_ROTATION_MIN: + pos[0] = CANNON_ROTATION_MIN + elif pos[0] > CANNON_ROTATION_MAX: + pos[0] = CANNON_ROTATION_MAX + angVel = 0 + if self.upPressed: + angVel += CANNON_ANGLE_VEL + if self.downPressed: + angVel -= CANNON_ANGLE_VEL + pos[1] += angVel * globalClock.getDt() + if pos[1] < CANNON_ANGLE_MIN: + pos[1] = CANNON_ANGLE_MIN + elif pos[1] > CANNON_ANGLE_MAX: + pos[1] = CANNON_ANGLE_MAX + if oldRot != pos[0] or oldAng != pos[1]: + if self.cannonMoving == 0: + self.cannonMoving = 1 + base.playSfx(self.sndCannonMove, looping=1) + self.__updateCannonPosition(self.localAvId) + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.cannonMoving: + self.cannonMoving = 0 + self.sndCannonMove.stop() + self.__broadcastLocalCannonPosition() + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.sendUpdate('setCannonPosition', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __updateCannonPosition(self, avId): + self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0) + self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0) + maxP = 90 + newP = self.barrel.getP() + yScale = 1 - 0.5 * float(newP) / maxP + shadow = self.cannon.find('**/square_drop_shadow') + shadow.setScale(1, yScale, 1) + + def __createToonModels(self): + self.model_Created = 1 + self.jurorToon = NPCToons.createLocalNPC(ToontownGlobals.LawbotBossBaseJurorNpcId + self.index) + self.toonScale = self.jurorToon.getScale() + jurorToonParent = render.attachNewNode('toonOriginChange') + self.jurorToon.wrtReparentTo(jurorToonParent) + self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0, -90, 0) + self.toonModel = jurorToonParent + self.toonHead = ToonHead.ToonHead() + self.toonHead.setupHead(self.jurorToon.style) + self.toonHead.reparentTo(hidden) + self.__loadToonInCannon() + + def __destroyToonModels(self): + if (0): + self.av.dropShadow.show() + if self.dropShadow != None: + self.dropShadow.removeNode() + self.dropShadow = None + self.hitBumper = 0 + self.hitTarget = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.av = None + self.lastWakeTime = 0 + self.localToonShooting = 0 + if self.toonHead != None: + self.toonHead.reparentTo(hidden) + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead = None + if self.toonModel != None: + self.toonModel.removeNode() + self.toonModel = None + if self.jurorToon != None: + self.jurorToon.delete() + self.jurorToon = None + self.model_Created = 0 + return + + def __loadToonInCannon(self): + self.toonModel.reparentTo(hidden) + self.toonHead.startBlink() + self.toonHead.startLookAround() + self.toonHead.reparentTo(self.barrel) + self.toonHead.setPosHpr(0, 6, 0, 0, -45, 0) + sc = self.toonScale + self.toonHead.setScale(render, sc[0], sc[1], sc[2]) + + def exitCannon(self, avId): + self.__unmakeGui() + if self.avId == avId: + self.av.reparentTo(render) + self.__resetToonToCannon(self.av) + + def __resetToonToCannon(self, avatar): + pos = None + if not avatar: + if self.avId: + avatar = base.cr.doId2do.get(self.avId, None) + if avatar: + if hasattr(self, 'cannon') and self.cannon: + avatar.reparentTo(self.cannon) + avatar.setPosHpr(3, 0, 0, 90, 0, 0) + avatar.wrtReparentTo(render) + self.__resetToon(avatar) + return + + def __resetToon(self, avatar, pos = None): + if avatar: + self.__stopCollisionHandler(avatar) + self.__setToonUpright(avatar, pos) + if self.localToonShooting: + self.notify.debug('toon setting position to %s' % pos) + if pos: + base.localAvatar.setPos(pos) + camera.reparentTo(avatar) + camera.setPos(self.av.cameraPositions[0][0]) + place = base.cr.playGame.getPlace() + if place: + place.setState('finalBattle') + self.b_setLanded() + + def __stopCollisionHandler(self, avatar): + if avatar: + avatar.loop('neutral') + if self.flyColNode: + self.flyColNode = None + if avatar == base.localAvatar: + avatar.collisionsOn() + self.flyColSphere = None + if self.flyColNodePath: + base.cTrav.removeCollider(self.flyColNodePath) + self.flyColNodePath.removeNode() + self.flyColNodePath = None + self.handler = None + return + + def __setToonUpright(self, avatar, pos = None): + if avatar: + if not pos: + pos = avatar.getPos(render) + avatar.setPos(render, pos) + avatar.loop('neutral') + + def b_setLanded(self): + self.d_setLanded() + + def d_setLanded(self): + if self.localToonShooting: + self.sendUpdate('setLanded', []) + + def setLanded(self): + self.removeAvFromCannon() + + def removeAvFromCannon(self): + if self.av != None: + self.__stopCollisionHandler(self.av) + self.av.resetLOD() + place = base.cr.playGame.getPlace() + if self.av == base.localAvatar: + if place: + place.setState('finalBattle') + self.av.loop('neutral') + self.av.setPlayRate(1.0, 'run') + if self.av.getParent().getName() == 'toonOriginChange': + self.av.wrtReparentTo(render) + self.__setToonUpright(self.av) + if self.av == base.localAvatar: + self.av.startPosHprBroadcast() + self.av.startSmooth() + self.av.setScale(1, 1, 1) + self.ignore(self.av.uniqueName('disable')) + self.__destroyToonModels() + return + + def setCannonWillFire(self, avId, fireTime, zRot, angle, timestamp): + self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime)) + if not self.model_Created: + self.notify.warning("We walked into the zone mid-flight, so we won't see it") + return + self.cannonPosition[0] = zRot + self.cannonPosition[1] = angle + self.__updateCannonPosition(avId) + task = Task(self.__fireCannonTask) + task.avId = avId + ts = globalClockDelta.localElapsedTime(timestamp) + task.fireTime = fireTime - ts + if task.fireTime < 0.0: + task.fireTime = 0.0 + taskMgr.add(task, self.taskName('fireCannon')) + + def __fireCannonTask(self, task): + launchTime = task.fireTime + avId = task.avId + if self.toonHead == None or not self.boss.state == 'BattleTwo': + return Task.done + flightResults = self.__calcFlightResults(avId, launchTime) + if not isClient(): + print 'EXECWARNING DistributedLawbotCannon: %s' % flightResults + printStack() + for key in flightResults: + exec "%s = flightResults['%s']" % (key, key) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + self.notify.debug('time of impact: ' + str(timeOfImpact)) + self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact))) + head = self.toonHead + head.stopBlink() + head.stopLookAroundNow() + head.reparentTo(hidden) + juror = self.toonModel + juror.reparentTo(render) + juror.setPos(startPos) + barrelHpr = self.barrel.getHpr(render) + juror.setHpr(startHpr) + self.jurorToon.loop('swim') + self.jurorToon.setPosHpr(0, 0, -(self.jurorToon.getHeight() / 2.0), 0, 0, 0) + info = {} + info['avId'] = avId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['timeOfImpact'] = timeOfImpact + info['hitWhat'] = hitWhat + info['toon'] = self.toonModel + info['hRot'] = self.cannonPosition[0] + info['haveWhistled'] = 0 + info['maxCamPullback'] = CAMERA_PULLBACK_MIN + if self.localToonShooting: + camera.reparentTo(juror) + camera.setP(45.0) + camera.setZ(-10.0) + self.flyColSphere = CollisionSphere(0, 0, self.av.getHeight() / 2.0, 1.0) + self.flyColNode = CollisionNode(self.uniqueName('flySphere')) + self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask | ToontownGlobals.PieBitmask) + self.flyColNode.addSolid(self.flyColSphere) + self.flyColNodePath = self.jurorToon.attachNewNode(self.flyColNode) + self.flyColNodePath.setColor(1, 0, 0, 1) + self.handler = CollisionHandlerEvent() + self.handler.setInPattern(self.uniqueName('cannonHit')) + base.cTrav.addCollider(self.flyColNodePath, self.handler) + self.accept(self.uniqueName('cannonHit'), self.__handleCannonHit) + shootTask = Task(self.__shootTask, self.taskName('shootTask')) + flyTask = Task(self.__flyTask, self.taskName('flyTask')) + shootTask.info = info + flyTask.info = info + seqTask = Task.sequence(shootTask, flyTask) + taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(avId)) + self.acceptOnce(self.uniqueName('stopFlyTask'), self.__stopFlyTask) + return Task.done + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __calcFlightResults(self, avId, launchTime): + head = self.toonHead + startPos = head.getPos(render) + startHpr = head.getHpr(render) + hpr = self.barrel.getHpr(render) + rotation = self.__toRadians(hpr[0]) + angle = self.__toRadians(hpr[1]) + horizVel = INITIAL_VELOCITY * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = INITIAL_VELOCITY * math.sin(angle) + startVel = Vec3(xVel, yVel, zVel) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + self.trajectory = trajectory + timeOfImpact, hitWhat = self.__calcToonImpact(trajectory) + return {'startPos': startPos, + 'startHpr': startHpr, + 'startVel': startVel, + 'trajectory': trajectory, + 'timeOfImpact': 3 * timeOfImpact, + 'hitWhat': hitWhat} + + def __calcToonImpact(self, trajectory): + t_groundImpact = trajectory.checkCollisionWithGround(GROUND_PLANE_MIN) + if t_groundImpact >= trajectory.getStartTime(): + return (t_groundImpact, self.HIT_GROUND) + else: + self.notify.error('__calcToonImpact: toon never impacts ground?') + return (0.0, self.HIT_GROUND) + + def __handleCannonHit(self, collisionEntry): + if self.av == None or self.flyColNode == None: + return + interPt = collisionEntry.getSurfacePoint(render) + hitNode = collisionEntry.getIntoNode().getName() + fromNodePath = collisionEntry.getFromNodePath() + intoNodePath = collisionEntry.getIntoNodePath() + ignoredHits = ['NearBoss'] + for nodeName in ignoredHits: + if hitNode == nodeName: + return + + self.__stopFlyTask(self.avId) + self.__stopCollisionHandler(self.jurorToon) + if self.localToonShooting: + camera.wrtReparentTo(render) + pos = interPt + hpr = self.jurorToon.getHpr() + track = Sequence() + if self.localToonShooting: + pass + chairlist = ['trigger-chair'] + for index in range(len(ToontownGlobals.LawbotBossChairPosHprs)): + chairlist.append('Chair-%s' % index) + + if hitNode in chairlist: + track.append(Func(self.__hitChair, self.jurorToon, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.av)) + if self.av == base.localAvatar: + strs = hitNode.split('-') + chairNum = int(strs[1]) + self.boss.sendUpdate('hitChair', [chairNum, self.index]) + else: + track.append(Func(self.__hitGround, self.jurorToon, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.av)) + track.append(Func(self.b_setLanded)) + if self.localToonShooting: + pass + if self.hitTrack: + self.hitTrack.finish() + self.hitTrack = track + self.hitTrack.start() + return + + def enterCannonHit(self, collisionEntry): + pass + + def __shootTask(self, task): + base.playSfx(self.sndCannonFire) + return Task.done + + def __flyTask(self, task): + toon = task.info['toon'] + if toon.isEmpty(): + self.__resetToonToCannon(self.av) + return Task.done + curTime = task.time + task.info['launchTime'] + t = min(curTime, task.info['timeOfImpact']) + self.lastT = self.t + self.t = t + deltaT = self.t - self.lastT + self.deltaT = deltaT + if t >= task.info['timeOfImpact']: + self.__resetToonToCannon(self.av) + return Task.done + pos = task.info['trajectory'].getPos(t) + toon.setFluidPos(pos) + vel = task.info['trajectory'].getVel(t) + run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]) + rise = vel[2] + theta = self.__toDegrees(math.atan(rise / run)) + toon.setHpr(self.cannon.getH(render), -90 + theta, 0) + view = 2 + lookAt = task.info['toon'].getPos(render) + hpr = task.info['toon'].getHpr(render) + if self.localToonShooting: + if view == 0: + camera.wrtReparentTo(render) + camera.lookAt(lookAt) + elif view == 1: + camera.reparentTo(render) + camera.setPos(render, 100, 100, 35.25) + camera.lookAt(render, lookAt) + elif view == 2: + if hpr[1] > -90: + camera.setPos(0, 0, -30) + if camera.getZ() < lookAt[2]: + camera.setZ(render, lookAt[2] + 10) + camera.lookAt(Point3(0, 0, 0)) + return Task.cont + + def __stopFlyTask(self, avId): + taskMgr.remove(self.taskName('flyingToon') + '-' + str(avId)) + + def __hitGround(self, avatar, pos, extraArgs = []): + hitP = avatar.getPos(render) + h = self.barrel.getH(render) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(h, -135, 0) + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + avatar.hide() + + def __hitChair(self, avatar, pos, extraArgs = []): + hitP = avatar.getPos(render) + h = self.barrel.getH(render) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(h, -135, 0) + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + base.playSfx(self.sndHitChair) + avatar.hide() + + def generateCannonAppearTrack(self, avatar): + self.cannon.setScale(0.1) + self.cannon.show() + kartTrack = Parallel(Sequence(ActorInterval(avatar, 'feedPet'), Func(avatar.loop, 'neutral')), Sequence(Func(self.cannon.reparentTo, avatar.rightHand), Wait(2.1), Func(self.cannon.wrtReparentTo, render), Func(self.cannon.setShear, 0, 0, 0), Parallel(LerpHprInterval(self.cannon, hpr=self.nodePath.getHpr(render), duration=1.2), ProjectileInterval(self.cannon, endPos=self.nodePath.getPos(render), duration=1.2, gravityMult=0.45)), Wait(0.2), Sequence(LerpScaleInterval(self.cannon, scale=Point3(1.1, 1.1, 0.1), duration=0.2), LerpScaleInterval(self.cannon, scale=Point3(0.9, 0.9, 0.1), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 0.1), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 1.1), duration=0.2), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 0.9), duration=0.1), LerpScaleInterval(self.cannon, scale=Point3(1.0, 1.0, 1.0), duration=0.1), Func(self.cannon.wrtReparentTo, self.nodePath)))) + return kartTrack diff --git a/toontown/coghq/DistributedLawbotCannonAI.py b/toontown/coghq/DistributedLawbotCannonAI.py new file mode 100644 index 0000000..f841f6b --- /dev/null +++ b/toontown/coghq/DistributedLawbotCannonAI.py @@ -0,0 +1,99 @@ +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from toontown.minigame import Trajectory +from toontown.estate import DistributedCannonAI +from toontown.estate import CannonGlobals +from toontown.minigame import CannonGameGlobals + +class DistributedLawbotCannonAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedLawbotCannonAI') + + def __init__(self, air, lawbotBoss, index, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.index = index + self.posHpr = [x, y, z, h, p, r] + self.boss = lawbotBoss + self.bossId = lawbotBoss.doId + self.avId = 0 + + def delete(self): + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def getPosHpr(self): + return self.posHpr + + def canEnterCannon(self): + avId = self.air.getAvatarIdFromSender() + if self.boss.getCannonBallsLeft(avId) == 0: + return False + if not self.boss.state == 'BattleTwo': + return False + if not (self.avId == 0 or self.avId == avId): + return False + return True + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.canEnterCannon(): + return + if self.avId == 0 or self.avId == avId: + self.avId = avId + self.boss.toonEnteredCannon(self.avId, self.index) + cannonBallsLeft = self.boss.getCannonBallsLeft(avId) + self.setMovie(CannonGlobals.CANNON_MOVIE_LOAD, self.avId, cannonBallsLeft) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedLawbotCannonAI.requestEnter cannon already occupied') + self.notify.warning('requestEnter() - cannon already occupied') + + def setMovie(self, mode, avId, extraInfo): + self.avId = avId + self.sendUpdate('setMovie', [mode, avId, extraInfo]) + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.__doExit() + + def __doExit(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_FORCE_EXIT, self.avId, 0) + self.avId = 0 + + def requestLeave(self): + avId = self.air.getAvatarIdFromSender() + if self.avId != 0: + self.__doExit() + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedCannonAI.requestLeave cannon not occupied') + self.notify.warning('requestLeave() - cannon not occupied') + + def setCannonPosition(self, zRot, angle): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonPosition: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + self.sendUpdate('updateCannonPosition', [avId, zRot, angle]) + + def setLanded(self): + self.ignore(self.air.getAvatarExitEvent(self.avId)) + if self.canEnterCannon(): + self.requestEnter() + else: + self.setMovie(CannonGlobals.CANNON_MOVIE_LANDED, 0, 0) + + def setCannonLit(self, zRot, angle): + if not self.boss.state == 'BattleTwo': + self.notify.debug('ignoring setCannonList since boss in state %s' % self.boss.state) + return + avId = self.air.getAvatarIdFromSender() + if self.boss.getCannonBallsLeft(avId) == 0: + self.notify.debug('ignoring setCannonList since no balls left for %s' % avId) + return + self.notify.debug('setCannonLit: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + fireTime = CannonGameGlobals.FUSE_TIME + self.sendUpdate('setCannonWillFire', [avId, fireTime, zRot, angle, globalClockDelta.getRealNetworkTime()]) + self.boss.decrementCannonBallsLeft(avId) diff --git a/toontown/coghq/DistributedLawbotChair.py b/toontown/coghq/DistributedLawbotChair.py new file mode 100644 index 0000000..f183295 --- /dev/null +++ b/toontown/coghq/DistributedLawbotChair.py @@ -0,0 +1,353 @@ +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 otp.otpbase import OTPGlobals +from direct.actor import Actor +from toontown.suit import Suit +from toontown.suit import SuitDNA +import random +from toontown.battle import BattleProps +from toontown.toon import NPCToons + +class DistributedLawbotChair(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotChair') + chairCushionSurface = Point3(0, -0.75, 2.25) + landingPt = Point3(0, -1.5, 0) + courtroomCeiling = 30 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedLawbotBossChair') + self.boss = None + self.index = None + self.avId = 0 + self.modelPath = 'phase_11/models/lawbotHQ/JuryBoxChair' + self.modelFindString = None + self.nodePath = None + self.ival = None + self.origHpr = Point3(0, 0, 0) + self.downTime = 0.5 + self.upTime = 5 + self.cogJuror = None + self.propInSound = None + self.propOutSound = None + self.propTrack = None + self.cogJurorTrack = None + self.cogJurorSound = None + self.toonJurorIndex = -1 + self.toonJuror = None + return + + def announceGenerate(self): + self.notify.debug('announceGenerate: %s' % self.doId) + DistributedObject.DistributedObject.announceGenerate(self) + self.name = 'Chair-%s' % self.doId + self.loadModel(self.modelPath, self.modelFindString) + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.doId) + self.loadSounds() + self.loadCogJuror() + self.cogJuror.stash() + origPos = self.computePos() + self.nodePath.setPos(origPos) + self.nodePath.setHpr(-90, 0, 0) + chairParent = self.boss.getChairParent() + self.nodePath.wrtReparentTo(chairParent) + self.boss.chairs[self.index] = self + + def delete(self): + DistributedObject.DistributedObject.delete(self) + loader.unloadModel(self.modelPath) + self.unloadSounds() + self.nodePath.removeNode() + + def loadModel(self, modelPath, modelFindString = None): + if self.nodePath == None: + self.makeNodePath() + else: + self.chair.getChildren().detach() + model = loader.loadModel(modelPath) + if modelFindString != None: + model = model.find('**/' + modelFindString) + model.instanceTo(self.chair) + trigger_chair = self.chair.find('**/trigger_chair') + if not trigger_chair.isEmpty(): + trigger_chair.stash() + collision_chair = self.chair.find('**/collision_chair') + if not collision_chair.isEmpty(): + collision_chair.stash() + shadow = self.chair.find('**/shadow') + if not shadow.isEmpty(): + pass + self.scale = 0.5 + self.chair.setScale(self.scale) + self.attachColSphere() + return + + def loadSounds(self): + if self.propInSound == None: + self.propInSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_in.mp3') + if self.propOutSound == None: + self.propOutSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_out.mp3') + if self.cogJurorSound == None: + self.cogJurorSound = base.loadSfx('phase_11/audio/sfx/LB_cog_jury.mp3') + return + + def unloadSounds(self): + if self.propInSound: + del self.propInSound + self.propInSound = None + if self.propOutSound: + del self.propOutSound + self.propOutSound = None + if self.cogJurorSound: + del self.cogJurorSound + self.cogJurorSound = None + return + + def loadCogJuror(self): + self.cleanupCogJuror() + self.cogJuror = Suit.Suit() + level = self.randomGenerator.randrange(len(SuitDNA.suitsPerLevel)) + self.cogJuror.dna = SuitDNA.SuitDNA() + self.cogJuror.dna.newSuitRandom(level=level, dept='l') + self.cogJuror.setDNA(self.cogJuror.dna) + self.cogJuror.pose('landing', 0) + self.cogJuror.reparentTo(self.nodePath) + self.cogJuror.prop = None + if self.cogJuror.prop == None: + self.cogJuror.prop = BattleProps.globalPropPool.getProp('propeller') + head = self.cogJuror.find('**/joint_head') + self.cogJuror.prop.reparentTo(head) + self.propTrack = Sequence(ActorInterval(self.cogJuror.prop, 'propeller', startFrame=8, endFrame=25)) + return + + def attachColSphere(self): + chairTop = self.nodePath.find('**/top*') + chairHandle = self.nodePath.find('**/handle*') + collNode = CollisionNode(self.uniqueName('headSphere')) + topBounds = self.chair.getBounds() + center = topBounds.getCenter() + radius = topBounds.getRadius() + radius *= 0.65 + adjustedZ = center[2] + adjustedZ += 0.6 + sphere1 = CollisionSphere(center[0], center[1], adjustedZ, radius) + sphere1.setTangible(1) + collNode.addSolid(sphere1) + collNode.setName('Chair-%s' % self.index) + self.collNodePath = self.nodePath.attachNewNode(collNode) + + def makeNodePath(self): + self.nodePath = Actor.Actor() + self.chair = self.nodePath.attachNewNode('myChair') + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.nodePath.detachNode() + if self.ival: + self.ival.finish() + self.ival = None + self.ignoreAll() + del self.boss.chairs[self.index] + self.cleanup() + if self.propTrack: + self.propTrack.finish() + self.propTrack = None + if self.cogJurorTrack: + self.cogJurorTrack.finish() + self.cogJurorTrack = None + self.cleanupCogJuror() + self.cleanupToonJuror() + return + + def stopCogsFlying(self): + if self.ival: + self.ival.finish() + self.ival = None + if self.propTrack: + self.propTrack.finish() + self.propTrack = None + if self.cogJurorTrack: + self.cogJurorTrack.finish() + self.cogJurorTrack = None + return + + def cleanupCogJuror(self): + if self.cogJuror: + self.cogJuror.detachNode() + self.cogJuror.delete() + del self.cogJuror + self.cogJuror = None + return + + def cleanupToonJuror(self): + if self.toonJuror: + self.toonJuror.detachNode() + self.toonJuror.delete() + del self.toonJuror + self.toonJuror = None + return + + def cleanup(self): + self.boss = None + return + + def startCogJuror(self, duration, y): + if self.cogJurorTrack: + self.cogJurorTrack.finish() + self.loadCogJuror() + self.cogJuror.stash() + x = 0 + curPos = self.nodePath.getPos(render) + z = self.courtroomCeiling - curPos[2] + self.notify.debug('curPos =%s\nz=%f' % (curPos, z)) + cogTrack = Sequence(Func(self.cogJuror.setPos, x, y, z), Func(self.cogJuror.unstash), Func(self.propTrack.loop), self.cogJuror.posInterval(duration, self.landingPt, Point3(x, y, z)), Func(self.propTrack.finish), Func(self.stashCogJuror)) + audioTrack = SoundInterval(self.propInSound, duration=duration, node=self.cogJuror, loop=1) + self.cogJurorTrack = Parallel(audioTrack, cogTrack) + self.cogJurorTrack.start() + + def stashCogJuror(self): + if self.cogJuror and not self.cogJuror.isEmpty(): + self.cogJuror.stash() + + def putCogJurorOnSeat(self): + self.stopCogsFlying() + if self.cogJuror and not self.cogJuror.isEmpty(): + base.playSfx(self.cogJurorSound, node=self.chair) + self.cogJuror.unstash() + self.cogJuror.prop.stash() + self.cogJuror.pose('landing', 47) + self.cogJuror.setH(180) + self.cogJuror.setPos(0, -1.25, 0.95) + if self.toonJuror: + self.toonJuror.hide() + else: + self.notify.warning('putCogJurorOnSeat invalid cogJuror') + + def putToonJurorOnSeat(self): + if self.toonJuror and not self.toonJuror.isEmpty(): + self.toonJuror.show() + self.toonJuror.reparentTo(self.nodePath) + self.toonJuror.setH(180) + self.toonJuror.setPos(0, -2.5, 0.95) + self.toonJuror.animFSM.request('Sit') + else: + self.notify.warning('putToonJurorOnSeat invalid toonJuror') + + def showCogJurorFlying(self): + self.notify.debug('showCogJurorFlying') + self.startCogJuror(ToontownGlobals.LawbotBossCogJurorFlightTime, -ToontownGlobals.LawbotBossCogJurorDistance) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def setIndex(self, index): + self.index = index + + def setState(self, state): + avId = 0 + if state == 'C': + self.demand('Controlled', avId) + elif state == 'F': + self.demand('Free') + elif state == 'N': + self.demand('On') + elif state == 'T': + self.demand('ToonJuror') + elif state == 'S': + self.demand('SuitJuror') + elif state == 'E': + self.demand('EmptyJuror') + elif state == 'E': + self.demand('StopCogs') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def __touchedChair(self, entry): + self.notify.debug('__touchedChair') + self.notify.debug('self=%s entry=%s' % (self, entry)) + self.boss.touchedChair(self, entry) + + def __touchedChairHandle(self, entry): + self.notify.debug('__touchedChairHandle') + self.boss.touchedChairHandle(self, entry) + + def enterToonJuror(self): + self.chair.setColorScale(0.2, 0.2, 1.0, 1.0) + self.boss.countToonJurors() + if not self.cogJurorTrack: + self.cogJuror.stash() + self.putToonJurorOnSeat() + + def enterSuitJuror(self): + self.chair.setColorScale(0.5, 0.5, 0.5, 1.0) + self.boss.countToonJurors() + if self.toonJuror: + self.toonJuror.hide() + self.putCogJurorOnSeat() + + def enterEmptyJuror(self): + self.chair.setColorScale(1.0, 1.0, 1.0, 1.0) + + def enterStopCogs(self): + self.stopCogs() + + def exitStopCogs(self): + pass + + def enterOn(self): + self.notify.debug('enterOn for chair %d' % self.index) + myHeadings = ToontownGlobals.LawbotBossChairHeadings[self.index] + seqName = 'LawbotBossChair-%s' % self.doId + self.ival = Sequence(name=seqName) + downAngle = -80 + for index in range(len(myHeadings)): + nextIndex = index + 1 + if nextIndex == len(myHeadings): + nextIndex = 0 + goingDown = self.nodePath.hprInterval(self.downTime, Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], 0, self.origHpr[2])) + self.ival.append(goingDown) + self.ival.append(Wait(self.stayDownTime)) + goingUp = self.nodePath.hprInterval(self.upTime, Point3(myHeadings[nextIndex] + self.origHpr[0], 0, self.origHpr[2]), startHpr=Point3(myHeadings[index] + self.origHpr[0], downAngle, self.origHpr[2])) + self.ival.append(goingUp) + + self.ival.loop() + self.accept('enterChairZap', self.__touchedChair) + self.accept('enterChairHandleZap', self.__touchedChairHandle) + + def computePos(self): + rowIndex = self.index % 6 + if self.index < 6: + startPt = Point3(*ToontownGlobals.LawbotBossChairRow1PosA) + endPt = Point3(*ToontownGlobals.LawbotBossChairRow1PosB) + else: + startPt = Point3(*ToontownGlobals.LawbotBossChairRow2PosA) + endPt = Point3(*ToontownGlobals.LawbotBossChairRow2PosB) + totalDisplacement = endPt - startPt + stepDisplacement = totalDisplacement / (6 - 1) + newPos = stepDisplacement * rowIndex + self.notify.debug('curDisplacement = %s' % newPos) + newPos += startPt + self.notify.debug('newPos before offset = %s' % newPos) + newPos -= Point3(*ToontownGlobals.LawbotBossJuryBoxRelativeEndPos) + self.notify.debug('newPos = %s' % newPos) + return newPos + + def loadToonJuror(self): + self.cleanupToonJuror() + self.toonJuror = NPCToons.createLocalNPC(ToontownGlobals.LawbotBossBaseJurorNpcId + self.toonJurorIndex) + self.toonJuror.hide() + + def setToonJurorIndex(self, newVal): + if not self.toonJurorIndex == newVal: + self.toonJurorIndex = newVal + self.loadToonJuror() diff --git a/toontown/coghq/DistributedLawbotChairAI.py b/toontown/coghq/DistributedLawbotChairAI.py new file mode 100644 index 0000000..b9fc048 --- /dev/null +++ b/toontown/coghq/DistributedLawbotChairAI.py @@ -0,0 +1,188 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from direct.distributed import DistributedObjectAI +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from direct.fsm import FSM +import random + +class DistributedLawbotChairAI(DistributedObjectAI.DistributedObjectAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotChairAI') + + def __init__(self, air, boss, index): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedLawbotBossChairAI') + self.boss = boss + self.index = index + cn = CollisionNode('controls') + cs = CollisionSphere(0, -6, 0, 6) + cn.addSolid(cs) + self.goonShield = NodePath(cn) + self.goonShield.setPosHpr(*ToontownGlobals.LawbotBossChairPosHprs[self.index]) + self.avId = 0 + self.objectId = 0 + self.changeToCogTask = None + self.startCogFlyTask = None + self.toonJurorIndex = -1 + return + + def delete(self): + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + taskName = self.uniqueName('startCogFlyTask') + taskMgr.remove(taskName) + changeTaskName = self.uniqueName('changeToCogJuror') + taskMgr.remove(changeTaskName) + + def stopCogs(self): + taskName = self.uniqueName('startCogFlyTask') + taskMgr.remove(taskName) + changeTaskName = self.uniqueName('changeToCogJuror') + taskMgr.remove(changeTaskName) + + def getBossCogId(self): + return self.boss.doId + + def getIndex(self): + return self.index + + def getToonJurorIndex(self): + return self.toonJurorIndex + + def setToonJurorIndex(self, newVal): + self.toonJurorIndex = newVal + + def b_setToonJurorIndex(self, newVal): + self.setToonJurorIndex(newVal) + self.d_setToonJurorIndex(newVal) + + def d_setToonJurorIndex(self, newVal): + self.sendUpdate('setToonJurorIndex', [newVal]) + + def setState(self, state): + self.request(state) + + def d_setState(self, state): + newState = state + if state == 'On': + newState = 'N' + else: + if state == 'Off': + newState = 'F' + else: + if state == 'ToonJuror': + newState = 'T' + else: + if state == 'SuitJuror': + newState = 'S' + else: + if state == 'EmptyJuror': + newState = 'E' + else: + if state == 'StopCogs': + newState = 'C' + self.sendUpdate('setState', [newState]) + + def b_setState(self, state): + self.request(state) + self.d_setState(state) + + def turnOn(self): + self.b_setState('On') + + def requestStopCogs(self): + self.b_setState('StopCogs') + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.boss.involvedToons and self.avId == 0: + craneId = self.__getCraneId(avId) + if craneId == 0: + self.request('Controlled', avId) + + def requestFree(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.avId: + self.request('Free') + + def removeToon(self, avId): + if avId == self.avId: + self.request('Free') + + def __getCraneId(self, avId): + if self.boss and self.boss.cranes != None: + for crane in self.boss.cranes: + if crane.avId == avId: + return crane.doId + + return 0 + + def requestToonJuror(self): + self.b_setState('ToonJuror') + if self.changeToCogTask == None: + if self.startCogFlyTask == None: + delayTime = random.randrange(9, 19) + self.startCogFlyTask = taskMgr.doMethodLater(delayTime, self.cogFlyAndSit, self.uniqueName('startCogFlyTask')) + return + + def requestSuitJuror(self): + self.b_setState('SuitJuror') + + def requestEmptyJuror(self): + self.b_setState('EmptyJuror') + delayTime = random.randrange(1, 20) + self.startCogFlyTask = taskMgr.doMethodLater(delayTime, self.cogFlyAndSit, self.uniqueName('startCogFlyTask')) + + def cogFlyAndSit(self, taskName=None): + self.notify.debug('cogFlyAndSit') + self.sendUpdate('showCogJurorFlying', []) + self.changeToCogTask = taskMgr.doMethodLater(ToontownGlobals.LawbotBossCogJurorFlightTime, self.changeToCogJuror, self.uniqueName('changeToCogJuror')) + if self.startCogFlyTask: + self.startCogFlyTask = None + return + + def changeToCogJuror(self, task): + self.notify.debug('changeToCogJuror') + self.requestSuitJuror() + self.changeToCogTask = None + return + + def enterOn(self): + pass + + def exitOn(slef): + pass + + def enterOff(self): + self.goonShield.detachNode() + + def exitOff(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.d_setState('C') + + 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') + + def exitFree(self): + pass + + def enterToonJuror(self): + pass + + def exitToonJuror(self): + pass + + def enterStopCogs(self): + self.__stopCogs() + + def exitStopCogs(self): + pass diff --git a/toontown/coghq/DistributedLevelBattle.py b/toontown/coghq/DistributedLevelBattle.py new file mode 100644 index 0000000..cfe135b --- /dev/null +++ b/toontown/coghq/DistributedLevelBattle.py @@ -0,0 +1,229 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.battle import DistributedBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM +from toontown.toonbase import ToontownGlobals + +class DistributedLevelBattle(DistributedBattle.DistributedBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelBattle') + + def __init__(self, cr): + DistributedBattle.DistributedBattle.__init__(self, cr) + self.levelRequest = None + self.levelBattle = 1 + return + + def setLevelDoId(self, levelDoId): + self.levelDoId = levelDoId + + def setBattleCellId(self, battleCellId): + self.battleCellId = battleCellId + + def doPlacement(levelList, self = self): + self.levelRequest = None + self.level = levelList[0] + spec = self.level.getBattleCellSpec(self.battleCellId) + self.level.requestReparent(self, spec['parentEntId']) + self.setPos(spec['pos']) + print 'spec = %s' % spec + print 'h = %s' % spec.get('h') + self.wrtReparentTo(render) + return + + level = base.cr.doId2do.get(self.levelDoId) + if level is None: + self.notify.warning('level %s not in doId2do yet, battle %s will be mispositioned.' % self.levelDoId, self.doId) + self.levelRequest = self.cr.relatedObjectMgr.requestObjects([self.levelDoId], doPlacement) + else: + doPlacement([level]) + return + + def setPosition(self, *args): + pass + + def setInitialSuitPos(self, x, y, z): + self.initialSuitPos = Point3(x, y, z) + + def disable(self): + if self.hasLocalToon(): + self.unlockLevelViz() + if self.levelRequest is not None: + self.cr.relatedObjectMgr.abortRequest(self.levelRequest) + self.levelRequest = None + DistributedBattle.DistributedBattle.disable(self) + return + + def delete(self): + self.ignoreAll() + DistributedBattle.DistributedBattle.delete(self) + + def handleBattleBlockerCollision(self): + messenger.send(self.getCollisionName(), [None]) + return + + def lockLevelViz(self): + level = base.cr.doId2do.get(self.levelDoId) + if level: + level.lockVisibility(zoneId=self.zoneId) + else: + self.notify.warning("lockLevelViz: couldn't find level %s" % self.levelDoId) + + def unlockLevelViz(self): + level = base.cr.doId2do.get(self.levelDoId) + if level: + level.unlockVisibility() + else: + self.notify.warning("unlockLevelViz: couldn't find level %s" % self.levelDoId) + + def onWaitingForJoin(self): + self.lockLevelViz() + + 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 + 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) + if len(self.suits) == 1: + leaderIndex = 0 + elif self.bossBattle == 1: + for suit in self.suits: + if suit.boss: + leaderIndex = self.suits.index(suit) + break + + 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, toonPos)) + if self.suits.index(suit) == leaderIndex: + suitLeader = suit + suitIsLeader = 1 + if self.bossBattle == 1 and self.levelDoId in base.cr.doId2do: + level = base.cr.doId2do[self.levelDoId] + if suit.boss: + taunt = level.getBossTaunt() + else: + taunt = level.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) + + suitHeight = suitLeader.getHeight() + suitOffsetPnt = Point3(0, 0, suitHeight) + 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) + + if self.hasLocalToon(): + MidTauntCamHeight = suitHeight * 0.66 + MidTauntCamHeightLim = suitHeight - 1.8 + if MidTauntCamHeight < MidTauntCamHeightLim: + MidTauntCamHeight = MidTauntCamHeightLim + TauntCamY = 18 + TauntCamX = 0 + TauntCamHeight = random.choice((MidTauntCamHeight, 1, 11)) + camTrack = Sequence() + camTrack.append(Func(camera.reparentTo, suitLeader)) + camTrack.append(Func(base.camLens.setFov, self.camFOFov)) + camTrack.append(Func(camera.setPos, TauntCamX, TauntCamY, TauntCamHeight)) + camTrack.append(Func(camera.lookAt, suitLeader, 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)) + mtrack = Parallel(suitTrack, toonTrack) + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + mtrack = Parallel(mtrack, 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) + + 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() + + 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.info('enterReward()') + self.disableCollision() + self.delayDeleteMembers() + self.__playReward(ts, self.__handleFloorRewardDone) + + def __handleFloorRewardDone(self): + pass + + def exitReward(self): + self.notify.info('exitReward()') + self.clearInterval(self.uniqueName('floorReward')) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) + for toon in self.toons: + toon.startSmooth() diff --git a/toontown/coghq/DistributedLevelBattleAI.py b/toontown/coghq/DistributedLevelBattleAI.py new file mode 100644 index 0000000..554d4ce --- /dev/null +++ b/toontown/coghq/DistributedLevelBattleAI.py @@ -0,0 +1,152 @@ +from toontown.battle import DistributedBattleAI +from toontown.battle import DistributedBattleBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from direct.showbase.PythonUtil import addListsByValue + +class DistributedLevelBattleAI(DistributedBattleAI.DistributedBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevelBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, winState, roundCallback=None, finishCallback=None, maxSuits=4): + self.blocker = None + self.level = level + self.battleCellId = battleCellId + self.winState = winState + self.roundCallback = roundCallback + self.suitTrack = suit.dna.dept + DistributedBattleAI.DistributedBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, finishCallback, maxSuits, tutorialFlag=0, levelFlag=1) + isBossBattle = 0 + for suit in self.battleMgr.level.planner.battleCellId2suits[battleCellId]: + if suit.boss: + isBossBattle = 1 + break + + self.setBossBattle(isBossBattle) + self.bossDefeated = 0 + return + + def generate(self): + DistributedBattleAI.DistributedBattleAI.generate(self) + battleBlocker = self.battleMgr.battleBlockers.get(self.battleCellId) + if battleBlocker: + self.blocker = battleBlocker + battleBlocker.b_setBattle(self.doId) + + def getLevelDoId(self): + return self.level.doId + + def getBattleCellId(self): + return self.battleCellId + + def getTaskZoneId(self): + pass + + 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.battleCellId, self.activeToons, totalHp, deadSuits) + + def storeSuitsKilledThisBattle(self): + self.suitsKilledPerFloor.append(self.suitsKilledThisBattle) + + def resume(self, topFloor=0): + if len(self.suits) == 0: + avList = [] + for toonId in self.activeToons: + toon = self.getToon(toonId) + if toon: + avList.append(toon) + + self.d_setMembers() + self.storeSuitsKilledThisBattle() + if self.bossBattle == 0: + self.b_setState('Reward') + else: + self.handleToonsWon(avList) + self.d_setBattleExperience() + self.b_setState(self.winState) + if self.blocker: + if len(self.activeToons): + self.blocker.b_setBattleFinished() + 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 handleToonsWon(self, toons): + pass + + def enterFaceOff(self): + self.notify.debug('DistributedLevelBattleAI.enterFaceOff()') + self.joinableFsm.request('Joinable') + self.runableFsm.request('Unrunable') + self.suits[0].releaseControl() + faceOffTime = self.calcToonMoveTime(self.pos, self.initialSuitPos) + FACEOFF_TAUNT_T + SERVER_BUFFER_TIME + self.notify.debug('faceOffTime = %s' % faceOffTime) + self.timer.startCallback(faceOffTime, self.__serverFaceOffDone) + return None + + def __serverFaceOffDone(self): + self.notify.debug('faceoff timed out on server') + self.ignoreFaceOffDone = 1 + self.handleFaceOffDone() + + def exitFaceOff(self): + self.notify.debug('DistributedLevelBattleAI.exitFaceOff()') + self.timer.stop() + return None + + 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) + if not self.ignoreFaceOffDone: + self.handleFaceOffDone() + + def suitRequestJoin(self, suit): + self.notify.debug('DistributedLevelBattleAI.suitRequestJoin(%d)' % suit.getDoId()) + if suit in self.suits: + self.notify.warning('suit %s already in this battle' % suit.getDoId()) + return 0 + DistributedBattleBaseAI.DistributedBattleBaseAI.suitRequestJoin(self, suit) + + def enterReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.timer.startCallback(FLOOR_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitReward(self): + self.timer.stop() + return None diff --git a/toontown/coghq/DistributedLift.py b/toontown/coghq/DistributedLift.py new file mode 100644 index 0000000..d79d272 --- /dev/null +++ b/toontown/coghq/DistributedLift.py @@ -0,0 +1,209 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import LiftConstants +import MovingPlatform + +class DistributedLift(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLift') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + self.moveSnd = base.loadSfx('phase_9/audio/sfx/CHQ_FACT_elevator_up_down.mp3') + self.fsm = ClassicFSM.ClassicFSM('DistributedLift', [State.State('off', self.enterOff, self.exitOff, ['moving']), State.State('moving', self.enterMoving, self.exitMoving, ['waiting']), State.State('waiting', self.enterWaiting, self.exitWaiting, ['moving'])], 'off', 'off') + self.fsm.enterInitialState() + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + self.platform = self.attachNewNode('platParent') + + def setStateTransition(self, toState, fromState, arrivalTimestamp): + self.notify.debug('setStateTransition: %s->%s' % (fromState, toState)) + if not self.isGenerated(): + self.initialState = toState + self.initialFromState = fromState + self.initialStateTimestamp = arrivalTimestamp + else: + self.fsm.request('moving', [toState, fromState, arrivalTimestamp]) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.initPlatform() + self.state = None + self.fsm.request('moving', [self.initialState, self.initialFromState, self.initialStateTimestamp]) + del self.initialState + del self.initialStateTimestamp + return + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + self.fsm.requestFinalState() + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + del self.moveSnd + del self.fsm + self.destroyPlatform() + self.platform.removeNode() + del self.platform + BasicEntities.DistributedNodePathEntity.delete(self) + + def initPlatform(self): + model = loader.loadModel(self.modelPath) + if model is None: + return + model.setScale(self.modelScale) + if self.floorName is None: + return + self.platformModel = MovingPlatform.MovingPlatform() + self.platformModel.setupCopyModel(self.getParentToken(), model, self.floorName) + self.accept(self.platformModel.getEnterEvent(), self.localToonEntered) + self.accept(self.platformModel.getExitEvent(), self.localToonLeft) + self.startGuard = None + self.endGuard = None + zoneNp = self.getZoneNode() + if len(self.startGuardName): + self.startGuard = zoneNp.find('**/%s' % self.startGuardName) + if len(self.endGuardName): + self.endGuard = zoneNp.find('**/%s' % self.endGuardName) + side2srch = {'front': '**/wall_front', + 'back': '**/wall_back', + 'left': '**/wall_left', + 'right': '**/wall_right'} + for side in side2srch.values(): + np = self.platformModel.find(side) + if not np.isEmpty(): + np.setScale(1.0, 1.0, 2.0) + np.setZ(-10) + np.flattenLight() + + self.startBoardColl = NodePathCollection() + self.endBoardColl = NodePathCollection() + for side in self.startBoardSides: + np = self.platformModel.find(side2srch[side]) + if np.isEmpty(): + DistributedLift.warning("couldn't find %s board collision" % side) + else: + self.startBoardColl.addPath(np) + + for side in self.endBoardSides: + np = self.platformModel.find(side2srch[side]) + if np.isEmpty(): + DistributedLift.warning("couldn't find %s board collision" % side) + else: + self.endBoardColl.addPath(np) + + self.platformModel.reparentTo(self.platform) + return + + def destroyPlatform(self): + if hasattr(self, 'platformModel'): + self.ignore(self.platformModel.getEnterEvent()) + self.ignore(self.platformModel.getExitEvent()) + self.platformModel.destroy() + del self.platformModel + if self.startGuard is not None: + self.startGuard.unstash() + if self.endGuard is not None: + self.endGuard.unstash() + del self.startGuard + del self.endGuard + del self.startBoardColl + del self.endBoardColl + return + + def localToonEntered(self): + self.sendUpdate('setAvatarEnter') + + def localToonLeft(self): + self.sendUpdate('setAvatarLeave') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def getPosition(self, state): + if state is LiftConstants.Down: + return self.startPos + else: + return self.endPos + + def getGuard(self, state): + if state is LiftConstants.Down: + return self.startGuard + else: + return self.endGuard + + def getBoardColl(self, state): + if state is LiftConstants.Down: + return self.startBoardColl + else: + return self.endBoardColl + + def enterMoving(self, toState, fromState, arrivalTimestamp): + self.notify.debug('enterMoving, %s->%s' % (fromState, toState)) + if self.state == toState: + self.notify.warning('already in state %s' % toState) + startPos = self.getPosition(fromState) + endPos = self.getPosition(toState) + startGuard = self.getGuard(fromState) + endGuard = self.getGuard(toState) + startBoardColl = self.getBoardColl(fromState) + endBoardColl = self.getBoardColl(toState) + + def startMoving(self = self, guard = startGuard, boardColl = startBoardColl): + if guard is not None and not guard.isEmpty(): + guard.unstash() + boardColl.unstash() + self.soundIval = SoundInterval(self.moveSnd, node=self.platform) + self.soundIval.loop() + return + + def doneMoving(self = self, guard = endGuard, boardColl = endBoardColl, newState = toState): + self.state = newState + if hasattr(self, 'soundIval'): + self.soundIval.pause() + del self.soundIval + if guard is not None and not guard.isEmpty(): + guard.stash() + boardColl.stash() + self.fsm.request('waiting') + return + + self.moveIval = Sequence(Func(startMoving), LerpPosInterval(self.platform, self.duration, endPos, startPos=startPos, blendType='easeInOut', name='lift-%s-move' % self.entId, fluid=1), Func(doneMoving)) + ivalStartT = globalClockDelta.networkToLocalTime(arrivalTimestamp, bits=32) - self.moveIval.getDuration() + self.moveIval.start(globalClock.getFrameTime() - ivalStartT) + + def exitMoving(self): + if hasattr(self, 'soundIval'): + self.soundIval.pause() + del self.soundIval + self.moveIval.pause() + del self.moveIval + + def enterWaiting(self): + self.notify.debug('enterWaiting') + + def exitWaiting(self): + pass + + if __dev__: + + def attribChanged(self, *args): + BasicEntities.DistributedNodePathEntity.attribChanged(self, *args) + self.destroyPlatform() + self.initPlatform() diff --git a/toontown/coghq/DistributedLiftAI.py b/toontown/coghq/DistributedLiftAI.py new file mode 100644 index 0000000..a69ed99 --- /dev/null +++ b/toontown/coghq/DistributedLiftAI.py @@ -0,0 +1,135 @@ +from direct.distributed.ClockDelta import * +from otp.level import DistributedEntityAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import LiftConstants + +class DistributedLiftAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLiftAI') + + def __init__(self, level, entId, initialState=LiftConstants.Down): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.name = 'Lift%s:%s' % (self.levelDoId, self.entId) + self.startMoveTaskName = '%s-StartMove' % self.name + self.moveDoneTaskName = '%s-MoveDone' % self.name + self.state = initialState + self.fromState = initialState + self.stateTimestamp = globalClock.getFrameTime() + self.boardedAvs = [] + + def generate(self): + self.notify.debug('generate') + DistributedEntityAI.DistributedEntityAI.generate(self) + self.fsm = ClassicFSM.ClassicFSM('DistributedLiftAI', [ + State.State('off', self.enterOff, self.exitOff, [ + 'waiting']), + State.State('waiting', self.enterWaiting, self.exitWaiting, [ + 'moving', 'waiting']), + State.State('moving', self.enterMoving, self.exitMoving, [ + 'waiting'])], 'off', 'off') + self.fsm.enterInitialState() + self.fsm.request('waiting') + + def delete(self): + self.notify.debug('delete') + DistributedEntityAI.DistributedEntityAI.delete(self) + self.ignoreAll() + taskMgr.remove(self.startMoveTaskName) + taskMgr.remove(self.moveDoneTaskName) + del self.fsm + + def b_setStateTransition(self, toState, fromState, arrivalTimestamp): + self.d_setStateTransition(toState, fromState, arrivalTimestamp) + self.setStateTransition(toState, fromState, arrivalTimestamp) + + def d_setStateTransition(self, toState, fromState, arrivalTimestamp): + self.sendUpdate('setStateTransition', [ + toState, fromState, arrivalTimestamp]) + + def setStateTransition(self, toState, fromState, arrivalTimestamp): + self.state = toState + self.fromState = fromState + self.stateTimestamp = arrivalTimestamp + + def getStateTransition(self): + return ( + self.state, self.fromState, self.stateTimestamp) + + def setAvatarEnter(self): + avId = self.air.getAvatarIdFromSender() + avatar = self.air.doId2do.get(avId) + if not avatar: + self.air.writeServerEvent('suspicious', avId, 'LiftAI.setAvatarEnter avId not valid') + return + self.notify.debug('setAvatarEnter: %s' % avId) + if avId in self.boardedAvs: + self.notify.warning('avatar %s already in list' % avId) + else: + self.boardedAvs.append(avId) + + def handleExitedAvatar(self=self, avId=avId): + self.notify.debug('avatar %s exited' % avId) + self.avatarLeft(avId) + + self.acceptOnce(self.air.getAvatarExitEvent(avId), handleExitedAvatar) + self.setMoveLater(self.moveDelay) + + def setAvatarLeave(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setAvatarLeave: %s' % avId) + self.avatarLeft(avId) + + def avatarLeft(self, avId): + if avId in self.boardedAvs: + self.boardedAvs.remove(avId) + self.ignore(self.air.getAvatarExitEvent(avId)) + if len(self.boardedAvs) == 0: + if self.fsm.getCurrentState().getName() == 'waiting': + self.fsm.request('waiting') + else: + self.notify.warning('avatar %s tried to leave, but is not in list' % avId) + + def setMoveLater(self, delay): + + def startMoving(task, self=self): + targetState = LiftConstants.oppositeState(self.state) + self.fsm.request('moving', [targetState]) + return Task.done + + self.cancelMoveLater() + taskMgr.doMethodLater(delay, startMoving, self.startMoveTaskName) + + def cancelMoveLater(self): + taskMgr.remove(self.startMoveTaskName) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterWaiting(self): + self.notify.debug('enterWaiting') + self.setMoveLater(self.autoMoveDelay) + + def exitWaiting(self): + self.cancelMoveLater() + + def enterMoving(self, targetState): + self.notify.debug('enterMoving, target=%s' % targetState) + if self.state == targetState: + self.notify.warning('already in state %s' % targetState) + return + arriveDelay = 1.0 + self.duration + self.b_setStateTransition(targetState, self.state, globalClockDelta.localToNetworkTime(globalClock.getFrameTime() + arriveDelay, bits=32)) + + def doneMoving(task, self=self): + self.fsm.request('waiting') + return Task.done + + taskMgr.doMethodLater(arriveDelay, doneMoving, self.moveDoneTaskName) + + def exitMoving(self): + pass diff --git a/toontown/coghq/DistributedMaze.py b/toontown/coghq/DistributedMaze.py new file mode 100644 index 0000000..1d570c9 --- /dev/null +++ b/toontown/coghq/DistributedMaze.py @@ -0,0 +1,235 @@ +from otp.level.BasicEntities import DistributedNodePathEntity +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +import random +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta +import DistributedBarrelBase +from otp.level.BasicEntities import DistributedNodePathEntity +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from direct.task import Task +from direct.gui.DirectGui import DGG, DirectFrame, DirectLabel + +class DistributedMaze(DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMaze') + ScheduleTaskName = 'mazeScheduler' + RemoveBlocksDict = {2: ('HedgeBlock_0_1',), + 4: (('HedgeBlock_0_1', 'HedgeBlock_1_3', 'HedgeBlock_2_3'), ('HedgeBlock_0_2', 'HedgeBlock_2_3', 'HedgeBlock_1_3'), ('HedgeBlock_0_1', 'HedgeBlock_0_2', 'HedgeBlock_1_3', 'HedgeBlock_2_3'))} + + def __init__(self, cr): + DistributedNodePathEntity.__init__(self, cr) + self.numSections = 0 + self.GameDuration = 35.0 + self.numSections * 15.0 + self.timer = None + self.frame2D = None + self.gameLabel = None + self.gameStarted = 0 + self.finished = 0 + self.timedOut = 0 + self.toonFinishedText = TTLocalizer.toonFinishedHedgeMaze + self.toonEnteredText = TTLocalizer.enterHedgeMaze + return + + def announceGenerate(self): + DistributedNodePathEntity.announceGenerate(self) + self.addHints(self.roomHold) + self.loadGui() + + def disable(self): + DistributedNodePathEntity.disable(self) + self.unloadGui() + self.cleanupTimer() + self.ignoreAll() + + def delete(self): + self.cleanupTimer() + DistributedNodePathEntity.delete(self) + + def setRoomDoId(self, roomDoId): + self.roomDoId = roomDoId + room = self.cr.doId2do.get(roomDoId) + if room: + self.gotRoom([room]) + else: + self.roomRequest = self.cr.relatedObjectMgr.requestObjects([roomDoId], allCallback=self.gotRoom, timeout=5) + + def gotRoom(self, rooms): + self.roomRequest = None + room = rooms[0] + self.roomHold = room + rotations = [0, + 0, + 90, + 90, + 180, + 180, + 270, + 270] + self.getRng().shuffle(rotations) + self.numSections = 0 + for i in xrange(0, 4): + maze = room.getGeom().find('**/Maze_Inside_%d' % i) + if not maze.isEmpty(): + self.numSections += 1 + if rotations: + maze.setH(rotations.pop()) + + self.GameDuration = 35.0 + self.numSections * 15.0 + self.removeHedgeBlocks(room) + return + + def addHints(self, room): + self.focusPoint = self.attachNewNode('GolfGreenGameFrame') + hintList = room.getGeom().findAllMatches('**/dead*') + for hint in hintList: + self.actSphere = CollisionSphere(0, 0, 0, 7.0) + self.actSphereNode = CollisionNode('mazegame_hint-%s-%s' % (self.level.getLevelId(), self.entId)) + self.actSphereNode.addSolid(self.actSphere) + self.actSphereNodePath = hint.attachNewNode(self.actSphereNode) + self.actSphereNode.setCollideMask(WallBitmask) + self.actSphere.setTangible(0) + self.enterEvent = 'enter' + self.actSphereNode.getName() + self.accept(self.enterEvent, self.__handleToonEnterHint) + self.exitEvent = 'exit' + self.actSphereNode.getName() + self.accept(self.exitEvent, self.__handleToonExitHint) + + enterance = room.getGeom().find('**/ENTRANCE') + self.enterSphere = CollisionSphere(0, 0, 0, 8.0) + self.enterSphereNode = CollisionNode('mazegame_enter-%s-%s' % (self.level.getLevelId(), self.entId)) + self.enterSphereNode.addSolid(self.enterSphere) + self.enterSphereNodePath = enterance.attachNewNode(self.enterSphereNode) + self.enterSphereNode.setCollideMask(WallBitmask) + self.enterSphere.setTangible(0) + self.enteranceEvent = 'enter' + self.enterSphereNode.getName() + self.accept(self.enteranceEvent, self.__handleToonEnterance) + finish = room.getGeom().find('**/finish') + self.finishSphere = CollisionSphere(0, 0, 0, 15.0) + self.finishSphereNode = CollisionNode('mazegame_finish-%s-%s' % (self.level.getLevelId(), self.entId)) + self.finishSphereNode.addSolid(self.finishSphere) + self.finishSphereNodePath = finish.attachNewNode(self.finishSphereNode) + self.finishSphereNode.setCollideMask(WallBitmask) + self.finishSphere.setTangible(0) + self.finishEvent = 'enter' + self.finishSphereNode.getName() + self.accept(self.finishEvent, self.__handleToonFinish) + + def __handleToonEnterance(self, collEntry): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + self.level.countryClub.showInfoText(self.toonEnteredText) + + def __handleToonFinish(self, collEntry): + self.sendUpdate('setFinishedMaze', []) + self.finished = 1 + + def __handleToonEnterHint(self, collEntry): + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -22.0 * heightScaleFactor, camHeight + 54.0) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.startUpdateSmartCamera(push=0) + base.localAvatar.setIdealCameraPos(cameraPoint) + + def __handleToonExitHint(self, collEntry): + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + self.cameraHold = None + return + + def getRng(self): + return random.Random(self.entId * self.doId) + + def removeHedgeBlocks(self, room): + if self.numSections in self.RemoveBlocksDict: + blocksToRemove = self.getRng().choice(self.RemoveBlocksDict[self.numSections]) + for blockName in blocksToRemove: + block = room.getGeom().find('**/%s' % blockName) + if not block.isEmpty(): + block.removeNode() + + def setGameStart(self, timestamp): + self.notify.debug('%d setGameStart: Starting game' % self.doId) + self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.gameStarted = True + curGameTime = self.getCurrentGameTime() + timeLeft = self.GameDuration - curGameTime + self.cleanupTimer() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posBelowTopRightCorner() + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.startScheduleTask() + self.frame2D.show() + + def setGameOver(self): + self.timedOut = 1 + if not self.finished: + self.sendUpdate('damageMe', []) + roomNum = self.level.roomNum + club = self.level.countryClub + self.gameOverTrack = Sequence() + self.gameOverTrack.append(localAvatar.getTeleportOutTrack()) + self.gameOverTrack.append(Func(localAvatar.setPos, self.finishSphereNodePath.getPos(render))) + self.gameOverTrack.append(Func(localAvatar.play, 'jump')) + self.gameOverTrack.append(Func(self.level.countryClub.camEnterRoom, roomNum)) + self.gameOverTrack.start() + self.timerExpired() + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def startScheduleTask(self): + taskMgr.add(self.scheduleTask, self.ScheduleTaskName) + + def stopScheduleTask(self): + taskMgr.remove(self.ScheduleTaskName) + + def scheduleTask(self, task): + curTime = self.getCurrentGameTime() + + def cleanupTimer(self): + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + return + + def timerExpired(self): + self.cleanupTimer() + self.unloadGui() + + def loadGui(self): + self.frame2D = DirectFrame(scale=1.0, pos=(0.0, 0, 0.9), relief=DGG.FLAT, parent=aspect2d, frameSize=(-0.3, + 0.3, + -0.05, + 0.05), frameColor=(0.737, 0.573, 0.345, 0.3)) + self.frame2D.hide() + self.gameLabel = DirectLabel(parent=self.frame2D, relief=None, pos=(0, 0, 0), scale=1.0, text=TTLocalizer.mazeLabel, text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text_scale=0.075, text_pos=(0, -0.02)) + return + + def unloadGui(self): + if self.frame2D: + self.frame2D.destroy() + self.frame2D = None + if self.gameLabel: + self.gameLabel.destroy() + self.gameLabel = None + return + + def toonFinished(self, avId, place, lastToon): + toon = base.cr.doId2do.get(avId) + if toon and not self.timedOut: + self.level.countryClub.showInfoText(self.toonFinishedText % (toon.getName(), TTLocalizer.hedgeMazePlaces[place])) + if lastToon: + self.setGameOver() diff --git a/toontown/coghq/DistributedMazeAI.py b/toontown/coghq/DistributedMazeAI.py new file mode 100644 index 0000000..390acd7 --- /dev/null +++ b/toontown/coghq/DistributedMazeAI.py @@ -0,0 +1,87 @@ +from otp.level import DistributedEntityAI +import DistributedBarrelBaseAI +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta +from direct.task import Task + +class DistributedMazeAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMazeAI') + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.roomDoId = level.doId + self.GameDuration = 60.0 + self.DamageOnFailure = 20 + self.finishedList = [] + + def delete(self): + self.removeAllTasks() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def announceGenerate(self): + DistributedEntityAI.DistributedEntityAI.announceGenerate(self) + self.mazeEndTimeTaskName = self.uniqueName('mazeEndTime') + + def getRoomDoId(self): + return self.roomDoId + + def setClientTriggered(self): + if not hasattr(self, 'gameStartTime'): + self.gameStartTime = globalClock.getRealTime() + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime)) + + def b_setGameStart(self, timestamp): + self.d_setGameStart(timestamp) + self.setGameStart(timestamp) + + def d_setGameStart(self, timestamp): + self.notify.debug('BASE: Sending setGameStart') + self.sendUpdate('setGameStart', [timestamp]) + + def setGameStart(self, timestamp): + self.notify.debug('BASE: setGameStart') + self.GameDuration = 35.0 + self.numSections * 15.0 + self.prepareForGameStartOrRestart() + + def prepareForGameStartOrRestart(self): + self.doMethodLater(self.GameDuration, self.gameEndingTimeHit, self.mazeEndTimeTaskName) + + def setFinishedMaze(self): + senderId = self.air.getAvatarIdFromSender() + if senderId not in self.finishedList: + toon = simbase.air.doId2do.get(senderId) + if toon: + if len(self.finishedList) < 1: + toon.toonUp(200.0) + else: + toon.toonUp(20.0) + lastToon = 0 + if hasattr(self, 'level'): + numToons = len(self.level.presentAvIds) + if numToons == len(self.finishedList) + 1: + lastToon = 1 + self.sendUpdate('toonFinished', [senderId, len(self.finishedList), lastToon]) + self.finishedList.append(senderId) + + def gameEndingTimeHit(self, task): + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + playerIds = room.presentAvIds + for avId in playerIds: + av = simbase.air.doId2do.get(avId) + if av and avId not in self.finishedList: + self.finishedList.append(avId) + + self.sendUpdate('setGameOver', []) + + def damageMe(self): + senderId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(senderId) + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + playerIds = room.presentAvIds + if av and senderId in playerIds: + av.takeDamage(self.DamageOnFailure, quietly=0) + room.sendUpdate('forceOuch', [self.DamageOnFailure]) diff --git a/toontown/coghq/DistributedMint.py b/toontown/coghq/DistributedMint.py new file mode 100644 index 0000000..e605c5c --- /dev/null +++ b/toontown/coghq/DistributedMint.py @@ -0,0 +1,220 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import BulletinBoardWatcher +from otp.otpbase import OTPGlobals +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from toontown.coghq import DistributedMintRoom, MintLayout, MintRoom + +class DistributedMint(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMint') + ReadyPost = 'MintReady' + WinEvent = 'MintWinEvent' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('mint', self) + self.roomWatcher = None + self.geom = None + self.rooms = [] + self.hallways = [] + self.allRooms = [] + self.curToonRoomNum = None + base.localAvatar.setCameraCollisionsCanMove(1) + base.localAvatar.reparentTo(render) + base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0) + self.accept('SOSPanelEnter', self.handleSOSPanel) + return + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setMintId(self, id): + DistributedMint.notify.debug('setMintId: %s' % id) + self.mintId = id + + def setFloorNum(self, num): + DistributedMint.notify.debug('floorNum: %s' % num) + self.floorNum = num + self.layout = MintLayout.MintLayout(self.mintId, self.floorNum) + + def setRoomDoIds(self, roomDoIds): + self.roomDoIds = roomDoIds + self.roomWatcher = BulletinBoardWatcher.BulletinBoardWatcher('roomWatcher-%s' % self.doId, [ DistributedMintRoom.getMintRoomReadyPostName(doId) for doId in self.roomDoIds ], self.gotAllRooms) + + def gotAllRooms(self): + self.notify.debug('mint %s: got all rooms' % self.doId) + self.roomWatcher.destroy() + self.roomWatcher = None + self.geom = render.attachNewNode('mint%s' % self.doId) + for doId in self.roomDoIds: + self.rooms.append(base.cr.doId2do[doId]) + self.rooms[-1].setMint(self) + + self.notify.info('mintId %s, floor %s, %s' % (self.mintId, self.floorNum, self.rooms[0].avIdList)) + rng = self.layout.getRng() + numRooms = self.layout.getNumRooms() + for i, room in enumerate(self.rooms): + if i == 0: + room.getGeom().reparentTo(self.geom) + else: + room.attachTo(self.hallways[i - 1], rng) + self.allRooms.append(room) + self.listenForFloorEvents(room) + if i < numRooms - 1: + hallway = MintRoom.MintRoom(self.layout.getHallwayModel(i)) + hallway.attachTo(room, rng) + hallway.setRoomNum(i * 2 + 1) + hallway.initFloorCollisions() + hallway.enter() + self.hallways.append(hallway) + self.allRooms.append(hallway) + self.listenForFloorEvents(hallway) + + def handleCameraRayFloorCollision(collEntry, self = self): + name = collEntry.getIntoNode().getName() + self.notify.debug('camera floor ray collided with: %s' % name) + prefix = MintRoom.MintRoom.FloorCollPrefix + prefixLen = len(prefix) + if name[:prefixLen] == prefix: + try: + roomNum = int(name[prefixLen:]) + except: + DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name) + else: + self.camEnterRoom(roomNum) + + self.accept('on-floor', handleCameraRayFloorCollision) + if bboard.has('mintRoom'): + self.warpToRoom(bboard.get('mintRoom')) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + self.notify.debug('mintHandleFirstSetZoneDone') + bboard.post(DistributedMint.ReadyPost, self) + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + zoneList = [OTPGlobals.UberZone, self.zoneId] + for room in self.rooms: + zoneList.extend(room.zoneIds) + + base.cr.sendSetZoneMsg(self.zoneId, zoneList) + self.accept('takingScreenshot', self.handleScreenshot) + return + + def listenForFloorEvents(self, room): + roomNum = room.getRoomNum() + floorCollName = room.getFloorCollName() + + def handleZoneEnter(collisionEntry, self = self, roomNum = roomNum): + self.toonEnterRoom(roomNum) + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + room = self.allRooms[roomNum] + ouchLevel = room.getFloorOuchLevel() + room.startOuch(ouchLevel) + + self.accept('enter%s' % floorCollName, handleZoneEnter) + + def handleZoneExit(collisionEntry, self = self, roomNum = roomNum): + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + self.allRooms[roomNum].stopOuch() + + self.accept('exit%s' % floorCollName, handleZoneExit) + + def getAllRoomsTimeout(self): + self.notify.warning('mint %s: timed out waiting for room objs' % self.doId) + + def toonEnterRoom(self, roomNum): + self.notify.debug('toonEnterRoom: %s' % roomNum) + if roomNum != self.curToonRoomNum: + if self.curToonRoomNum is not None: + self.allRooms[self.curToonRoomNum].localToonFSM.request('notPresent') + self.allRooms[roomNum].localToonFSM.request('present') + self.curToonRoomNum = roomNum + return + + def camEnterRoom(self, roomNum): + self.notify.debug('camEnterRoom: %s' % roomNum) + if roomNum % 2 == 1: + minVis = roomNum - 2 + maxVis = roomNum + 2 + else: + minVis = roomNum - 1 + maxVis = roomNum + 1 + for i, room in enumerate(self.allRooms): + if i < minVis or i > maxVis: + room.getGeom().stash() + else: + room.getGeom().unstash() + + def setBossConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.MintBossConfrontedMsg % av.getName()) + return + + def warpToRoom(self, roomId): + for i in xrange(len(self.rooms)): + room = self.rooms[i] + if room.roomId == roomId: + break + else: + return False + + base.localAvatar.setPosHpr(room.getGeom(), 0, 0, 0, 0, 0, 0) + self.camEnterRoom(i * 2) + return True + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + for hallway in self.hallways: + hallway.exit() + + self.rooms = [] + for hallway in self.hallways: + hallway.delete() + + self.hallways = [] + self.allRooms = [] + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignore('SOSPanelEnter') + bboard.remove('mint') + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.rooms[0].avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleScreenshot(self): + base.addScreenshotString('mintId: %s, floor (from 1): %s' % (self.mintId, self.floorNum + 1)) + if hasattr(self, 'currentRoomName'): + base.addScreenshotString('%s' % self.currentRoomName) diff --git a/toontown/coghq/DistributedMintAI.py b/toontown/coghq/DistributedMintAI.py new file mode 100644 index 0000000..5aa6f3f --- /dev/null +++ b/toontown/coghq/DistributedMintAI.py @@ -0,0 +1,71 @@ +from direct.distributed import DistributedObjectAI +from otp.level import DistributedLevelAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MintLayout, DistributedMintRoomAI +from toontown.coghq import BattleExperienceAggregatorAI + +class DistributedMintAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintAI') + + def __init__(self, air, mintId, zoneId, floorNum, avIds): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.mintId = mintId + self.zoneId = zoneId + self.floorNum = floorNum + self.avIds = avIds + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.info('generate %s, id=%s, floor=%s' % (self.doId, self.mintId, self.floorNum)) + self.layout = MintLayout.MintLayout(self.mintId, self.floorNum) + self.rooms = [] + self.battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + for i in range(self.layout.getNumRooms()): + room = DistributedMintRoomAI.DistributedMintRoomAI(self.air, self.mintId, self.doId, self.zoneId, self.layout.getRoomId(i), i * 2, self.avIds, self.battleExpAggreg) + room.generateWithRequired(self.zoneId) + self.rooms.append(room) + + roomDoIds = [] + for room in self.rooms: + roomDoIds.append(room.doId) + + self.sendUpdate('setRoomDoIds', [roomDoIds]) + if __dev__: + simbase.mint = self + description = '%s|%s|%s' % (self.mintId, self.floorNum, self.avIds) + for avId in self.avIds: + self.air.writeServerEvent('mintEntered', avId, description) + + def requestDelete(self): + self.notify.info('requestDelete: %s' % self.doId) + for room in self.rooms: + room.requestDelete() + + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + if __dev__: + if hasattr(simbase, 'mint') and simbase.mint is self: + del simbase.mint + del self.rooms + del self.layout + del self.battleExpAggreg + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getTaskZoneId(self): + return self.mintId + + def allToonsGone(self): + self.notify.info('allToonsGone') + self.requestDelete() + + def getZoneId(self): + return self.zoneId + + def getMintId(self): + return self.mintId + + def getFloorNum(self): + return self.floorNum diff --git a/toontown/coghq/DistributedMintBattle.py b/toontown/coghq/DistributedMintBattle.py new file mode 100644 index 0000000..96ec7b9 --- /dev/null +++ b/toontown/coghq/DistributedMintBattle.py @@ -0,0 +1,47 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals + +class DistributedMintBattle(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintBattle') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('MintReward', self.enterMintReward, self.exitMintReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('MintReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('MintReward') + + def enterMintReward(self, ts): + self.notify.debug('enterMintReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedMintBoss') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleMintRewardDone) + + def __handleMintRewardDone(self): + self.notify.debug('mint reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitMintReward(self): + self.notify.debug('exitMintReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedMintBattleAI.py b/toontown/coghq/DistributedMintBattleAI.py new file mode 100644 index 0000000..af88990 --- /dev/null +++ b/toontown/coghq/DistributedMintBattleAI.py @@ -0,0 +1,59 @@ +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedLevelBattleAI +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from toontown.toonbase.ToontownBattleGlobals import getMintCreditMultiplier +from direct.showbase.PythonUtil import addListsByValue + +class DistributedMintBattleAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback=None, finishCallback=None, maxSuits=4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'MintReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setBossConfronted(toonId) + self.fsm.addState(State.State('MintReward', self.enterMintReward, self.exitMintReward, [ + 'Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('MintReward') + + def getTaskZoneId(self): + return self.level.mintId + + def handleToonsWon(self, toons): + extraMerits = [ + 0, 0, 0, 0] + amount = ToontownGlobals.MintCogBuckRewards[self.level.mintId] + index = ToontownGlobals.cogHQZoneId2deptIndex(self.level.mintId) + extraMerits[index] = amount + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, self.suitsKilled, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, self.suitsKilled, self.getTaskZoneId(), getMintCreditMultiplier(self.getTaskZoneId()), extraMerits=extraMerits) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful list, skipping merits' % toon.doId) + + def enterMintReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitMintReward(self): + return None + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() diff --git a/toontown/coghq/DistributedMintElevatorExt.py b/toontown/coghq/DistributedMintElevatorExt.py new file mode 100644 index 0000000..5e35bab --- /dev/null +++ b/toontown/coghq/DistributedMintElevatorExt.py @@ -0,0 +1,125 @@ +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.gui import DirectGui +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +import CogDisguiseGlobals + +class DistributedMintElevatorExt(DistributedElevatorExt.DistributedElevatorExt): + + def __init__(self, cr): + DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) + self.type = ELEVATOR_MINT + self.countdownTime = ElevatorData[self.type]['countdown'] + + def generate(self): + DistributedElevatorExt.DistributedElevatorExt.generate(self) + + def delete(self): + self.elevatorModel.removeNode() + del self.elevatorModel + DistributedElevatorExt.DistributedElevatorExt.delete(self) + + def setMintId(self, mintId): + self.mintId = mintId + mintId2originId = {ToontownGlobals.CashbotMintIntA: 1, + ToontownGlobals.CashbotMintIntB: 2, + ToontownGlobals.CashbotMintIntC: 0} + originId = mintId2originId[self.mintId] + geom = self.cr.playGame.hood.loader.geom + locator = geom.find('**/elevator_origin_%s' % originId) + if locator: + self.elevatorModel.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + else: + self.notify.error('No origin found for originId: %s' % originId) + locator = geom.find('**/elevator_signorigin_%s' % originId) + backgroundGeom = geom.find('**/ElevatorFrameFront_%d' % originId) + backgroundGeom.node().setEffect(DecalEffect.make()) + signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.GlobalStreetNames[mintId][-1]), font=ToontownGlobals.getSuitFont(), scale=TTLocalizer.DMEEsignText, fg=(0.87, 0.87, 0.87, 1), mayChange=False, parent=backgroundGeom) + signText.setPosHpr(locator, 0, 0, 0, 0, 0, 0) + signText.setDepthWrite(0) + + def setupElevator(self): + self.elevatorModel = loader.loadModel('phase_10/models/cogHQ/mintElevator') + self.elevatorModel.reparentTo(render) + self.leftDoor = self.elevatorModel.find('**/left_door') + self.rightDoor = self.elevatorModel.find('**/right_door') + DistributedElevator.DistributedElevator.setupElevator(self) + self.elevatorSphereNodePath.setY(-1.42) + + def getElevatorModel(self): + return self.elevatorModel + + def setBldgDoId(self, bldgDoId): + self.bldg = None + self.setupElevator() + return + + def getZoneId(self): + return 0 + + def __doorsClosed(self, zoneId): + pass + + def setMintInteriorZone(self, zoneId): + if self.localToonOnBoard: + hoodId = self.cr.playGame.hood.hoodId + mintId = self.mintId + if bboard.has('mintIdOverride'): + mintId = bboard.get('mintIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'mintInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'mintId': self.mintId, + 'hoodId': hoodId} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + + def setMintInteriorZoneForce(self, zoneId): + place = self.cr.playGame.getPlace() + if place: + place.fsm.request('elevator', [self, 1]) + hoodId = self.cr.playGame.hood.hoodId + mintId = self.mintId + if bboard.has('mintIdOverride'): + mintId = bboard.get('mintIdOverride') + doneStatus = {'loader': 'cogHQLoader', + 'where': 'mintInterior', + 'how': 'teleportIn', + 'zoneId': zoneId, + 'mintId': self.mintId, + '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("setMintInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" % zoneId) + + def rejectBoard(self, avId, reason = 0): + DistributedElevatorExt.DistributedElevatorExt.rejectBoard(self, avId, reason) + + def __handleRejectAck(self): + doneStatus = self.rejectDialog.doneStatus + if doneStatus != 'ok': + self.notify.error('Unrecognized doneStatus: ' + str(doneStatus)) + doneStatus = {'where': 'reject'} + self.cr.playGame.getPlace().elevator.signalDone(doneStatus) + self.rejectDialog.cleanup() + del self.rejectDialog + + def getDestName(self): + if self.mintId == ToontownGlobals.CashbotMintIntA: + return TTLocalizer.ElevatorCashBotMint0 + elif self.mintId == ToontownGlobals.CashbotMintIntB: + return TTLocalizer.ElevatorCashBotMint1 + elif self.mintId == ToontownGlobals.CashbotMintIntC: + return TTLocalizer.ElevatorCashBotMint2 diff --git a/toontown/coghq/DistributedMintElevatorExtAI.py b/toontown/coghq/DistributedMintElevatorExtAI.py new file mode 100644 index 0000000..39ed95e --- /dev/null +++ b/toontown/coghq/DistributedMintElevatorExtAI.py @@ -0,0 +1,59 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from toontown.building.ElevatorConstants import * +from toontown.building import DistributedElevatorExtAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +import CogDisguiseGlobals + +class DistributedMintElevatorExtAI(DistributedElevatorExtAI.DistributedElevatorExtAI): + + def __init__(self, air, bldg, mintId, antiShuffle=0, minLaff=0): + DistributedElevatorExtAI.DistributedElevatorExtAI.__init__(self, air, bldg, antiShuffle=antiShuffle, minLaff=minLaff) + self.mintId = mintId + self.cogDept = ToontownGlobals.cogHQZoneId2deptIndex(self.mintId) + self.type = ELEVATOR_MINT + self.countdownTime = ElevatorData[self.type]['countdown'] + + def getMintId(self): + return self.mintId + + def avIsOKToBoard(self, av): + if not DistributedElevatorExtAI.DistributedElevatorExtAI.avIsOKToBoard(self, av): + return False + return True + + def elevatorClosed(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + players = [] + for i in self.seats: + if i not in [None, 0]: + players.append(i) + + mintZone = self.bldg.createMint(self.mintId, players) + for seatIndex in range(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setMintInteriorZone', [ + mintZone]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The elevator left, but was empty.') + self.fsm.request('closed') + return + + def enterClosed(self): + DistributedElevatorExtAI.DistributedElevatorExtAI.enterClosed(self) + self.fsm.request('opening') + + def sendAvatarsToDestination(self, avIdList): + if len(avIdList) > 0: + mintZone = self.bldg.createMint(self.mintId, avIdList) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setMintInteriorZoneForce', [ + mintZone]) diff --git a/toontown/coghq/DistributedMintRoom.py b/toontown/coghq/DistributedMintRoom.py new file mode 100644 index 0000000..e85cc05 --- /dev/null +++ b/toontown/coghq/DistributedMintRoom.py @@ -0,0 +1,223 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import MintRoomBase, MintRoom +import FactoryEntityCreator +import MintRoomSpecs +from otp.level import LevelSpec, LevelConstants +from toontown.toonbase import TTLocalizer +if __dev__: + from otp.level import EditorGlobals + +def getMintRoomReadyPostName(doId): + return 'mintRoomReady-%s' % doId + + +class DistributedMintRoom(DistributedLevel.DistributedLevel, MintRoomBase.MintRoomBase, MintRoom.MintRoom): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintRoom') + EmulateEntrancePoint = False + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + MintRoomBase.MintRoomBase.__init__(self) + MintRoom.MintRoom.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.mint = None + return + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + + def delete(self): + del self.mint + DistributedLevel.DistributedLevel.delete(self) + MintRoom.MintRoom.delete(self) + self.ignoreAll() + + def setMintId(self, mintId): + self.notify.debug('mintId: %s' % mintId) + MintRoomBase.MintRoomBase.setMintId(self, mintId) + + def setRoomId(self, roomId): + self.notify.debug('roomId: %s' % roomId) + MintRoomBase.MintRoomBase.setRoomId(self, roomId) + + def setRoomNum(self, num): + self.notify.debug('roomNum: %s' % num) + MintRoom.MintRoom.setRoomNum(self, num) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = MintRoomSpecs.getMintRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + if __dev__: + typeReg = self.getEntityTypeReg() + roomSpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.initializeLevel(self, roomSpec) + + def getReadyPostName(self): + return getMintRoomReadyPostName(self.doId) + + def privGotSpec(self, levelSpec): + if __dev__: + if not levelSpec.hasEntityTypeReg(): + typeReg = self.getEntityTypeReg() + levelSpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + MintRoom.MintRoom.enter(self) + self.acceptOnce('leavingMint', self.announceLeaving) + bboard.post(self.getReadyPostName()) + + def fixupLevelModel(self): + MintRoom.MintRoom.setGeom(self, self.geom) + MintRoom.MintRoom.initFloorCollisions(self) + + def setMint(self, mint): + self.mint = mint + + def setBossConfronted(self, avId): + self.mint.setBossConfronted(avId) + + def setDefeated(self): + self.notify.info('setDefeated') + from toontown.coghq import DistributedMint + messenger.send(DistributedMint.DistributedMint.WinEvent) + + def initVisibility(self, *args, **kw): + pass + + def shutdownVisibility(self, *args, **kw): + pass + + def lockVisibility(self, *args, **kw): + pass + + def unlockVisibility(self, *args, **kw): + pass + + def enterZone(self, *args, **kw): + pass + + def updateVisibility(self, *args, **kw): + pass + + def setVisibility(self, *args, **kw): + pass + + def resetVisibility(self, *args, **kw): + pass + + def handleVisChange(self, *args, **kw): + pass + + def forceSetZoneThisFrame(self, *args, **kw): + pass + + def getParentTokenForEntity(self, entId): + if __dev__: + pass + return 1000000 * self.roomNum + entId + + def enterLtNotPresent(self): + MintRoom.MintRoom.enterLtNotPresent(self) + if __dev__: + bboard.removeIfEqual(EditorGlobals.EditTargetPostName, self) + self.ignore('f2') + + def enterLtPresent(self): + MintRoom.MintRoom.enterLtPresent(self) + if __dev__: + bboard.post(EditorGlobals.EditTargetPostName, self) + if self.mint is not None: + self.mint.currentRoomName = MintRoomSpecs.CashbotMintRoomId2RoomName[self.roomId] + + def printPos(self = self): + thisZone = self.getZoneNode(LevelConstants.UberZoneEntId) + pos = base.localAvatar.getPos(thisZone) + h = base.localAvatar.getH(thisZone) + roomName = MintRoomSpecs.CashbotMintRoomId2RoomName[self.roomId] + print 'mint pos: %s, h: %s, room: %s' % (repr(pos), h, roomName) + if self.mint is not None: + floorNum = self.mint.floorNum + else: + floorNum = '???' + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nmintId: %s' % self.mintId + '\nfloor: %s' % floorNum + '\nroomId: %s' % self.roomId + '\nroomName: %s' % roomName + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + return + + self.accept('f2', printPos) + return + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + MintRoom.MintRoom.exit(self) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + bboard.remove(self.getReadyPostName()) + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 8 + + def getTaskZoneId(self): + return self.mintId + + def getBossTaunt(self): + return TTLocalizer.MintBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.MintBossBattleTaunt + + def __str__(self): + if hasattr(self, 'roomId'): + return '%s %s: %s' % (self.__class__.__name__, self.roomId, MintRoomSpecs.CashbotMintRoomId2RoomName[self.roomId]) + else: + return 'DistributedMintRoom' + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/DistributedMintRoomAI.py b/toontown/coghq/DistributedMintRoomAI.py new file mode 100644 index 0000000..d0f3130 --- /dev/null +++ b/toontown/coghq/DistributedMintRoomAI.py @@ -0,0 +1,137 @@ +from otp.level import DistributedLevelAI, LevelSpec +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from otp.level import LevelSpec +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import FactoryEntityCreatorAI, MintRoomSpecs +from toontown.coghq import MintRoomBase, LevelSuitPlannerAI +from toontown.coghq import DistributedMintBattleAI +from toontown.suit import DistributedMintSuitAI + +class DistributedMintRoomAI(DistributedLevelAI.DistributedLevelAI, MintRoomBase.MintRoomBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintRoomAI') + + def __init__(self, air, mintId, mintDoId, zoneId, roomId, roomNum, avIds, battleExpAggreg): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, 0, avIds) + MintRoomBase.MintRoomBase.__init__(self) + self.setMintId(mintId) + self.setRoomId(roomId) + self.roomNum = roomNum + self.mintDoId = mintDoId + self.battleExpAggreg = battleExpAggreg + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getMintCreditMultiplier(self.mintId) + + def generate(self): + self.notify.debug('generate %s: room=%s' % (self.doId, self.roomId)) + self.notify.debug('loading spec') + specModule = MintRoomSpecs.getMintRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + if __dev__: + self.notify.debug('creating entity type registry') + typeReg = self.getMintEntityTypeReg() + roomSpec.setEntityTypeReg(typeReg) + self.notify.debug('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, roomSpec) + self.notify.debug('creating cogs') + cogSpecModule = MintRoomSpecs.getCogSpecModule(self.roomId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedMintSuitAI.DistributedMintSuitAI, DistributedMintBattleAI.DistributedMintBattleAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells, battleExpAggreg=self.battleExpAggreg) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + self.notify.debug('finish mint room %s %s creation' % (self.roomId, self.doId)) + + def delete(self): + self.notify.debug('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + + del self.battleExpAggreg + DistributedLevelAI.DistributedLevelAI.delete(self, deAllocZone=False) + + def getMintId(self): + return self.mintId + + def getRoomId(self): + return self.roomId + + def getRoomNum(self): + return self.roomNum + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds + + def d_setBossConfronted(self, toonId): + if toonId not in self.avIdList: + self.notify.warning('d_setBossConfronted: %s not in list of participants' % toonId) + return + self.sendUpdate('setBossConfronted', [toonId]) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + + description = '%s|%s' % (self.mintId, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('mintDefeated', avId, description) + + for toon in activeVictors: + simbase.air.questManager.toonDefeatedMint(toon, self.mintId, activeVictors) + + return + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def allToonsGone(self, toonsThatCleared): + DistributedLevelAI.DistributedLevelAI.allToonsGone(self, toonsThatCleared) + if self.roomNum == 0: + mint = simbase.air.doId2do.get(self.mintDoId) + if mint is not None: + mint.allToonsGone() + else: + self.notify.warning('no mint %s in allToonsGone' % self.mintDoId) + return diff --git a/toontown/coghq/DistributedMoleField.py b/toontown/coghq/DistributedMoleField.py new file mode 100644 index 0000000..e500f7f --- /dev/null +++ b/toontown/coghq/DistributedMoleField.py @@ -0,0 +1,472 @@ +from pandac.PandaModules import * +from otp.level.BasicEntities import DistributedNodePathEntity +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import MoleHill +from toontown.coghq import MoleFieldBase +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import ToontownTimer +from direct.gui.DirectGui import DGG, DirectFrame, DirectLabel +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +import random +from toontown.minigame import Trajectory +from direct.interval.IntervalGlobal import * +from toontown.battle import MovieUtil + +class DistributedMoleField(DistributedNodePathEntity, MoleFieldBase.MoleFieldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMoleField') + ScheduleTaskName = 'moleFieldScheduler' + + def __init__(self, cr): + DistributedNodePathEntity.__init__(self, cr) + self.gameStarted = False + self.moleHills = [] + self.numMolesWhacked = 0 + self.timer = None + self.frame2D = None + self.isToonInRange = 0 + self.detectCount = 0 + self.cameraHold = None + self.activeField = 1 + self.dimensionX = 0.0 + self.dimensionY = 0.0 + self.gameText = TTLocalizer.MolesInstruction + self.winText = TTLocalizer.MolesFinished + self.pityWinText = TTLocalizer.MolesPityWin + self.restartedText = TTLocalizer.MolesRestarted + self.toonHitTracks = {} + self.hasRestarted = 0 + self.hasEntered = 0 + self.MolesWhackedTarget = 1000 + self.GameDuration = 1000 + return + + def disable(self): + self.cleanupTimer() + for ival in self.toonHitTracks.values(): + ival.finish() + + self.toonHitTracks = {} + DistributedNodePathEntity.disable(self) + taskMgr.remove(self.detectName) + self.ignoreAll() + + def delete(self): + self.soundBomb = None + self.soundBomb2 = None + self.soundCog = None + DistributedNodePathEntity.delete(self) + self.stopScheduleTask() + for mole in self.moleHills: + mole.destroy() + + self.moleHills = [] + self.cleanupTimer() + self.unloadGui() + return + + def announceGenerate(self): + DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + self.loadGui() + self.detectName = 'moleField %s' % self.doId + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + self.calcDimensions() + self.notify.debug('announceGenerate doId=%d entId=%d' % (self.doId, self.entId)) + + def setNumSquaresX(self, num): + self.numSquaresX = num + self.calcDimensions() + + def setNumSquaresY(self, num): + self.numSquaresY = num + self.calcDimensions() + + def setSpacingX(self, num): + self.spacingX = num + self.calcDimensions() + + def setSpacingY(self, num): + self.spacingY = num + self.calcDimensions() + + def calcDimensions(self): + self.dimensionX = self.numSquaresX * self.spacingX + self.dimensionY = self.numSquaresY * self.spacingY + self.centerCenterNode() + + def loadModel(self): + moleIndex = 0 + self.moleHills = [] + for indexY in xrange(self.numSquaresY): + for indexX in xrange(self.numSquaresX): + xPos = indexX * self.spacingX + yPos = indexY * self.spacingY + newMoleHill = MoleHill.MoleHill(xPos, yPos, 0, self, moleIndex) + newMoleHill.reparentTo(self) + self.moleHills.append(newMoleHill) + moleIndex += 1 + + self.numMoles = len(self.moleHills) + self.centerNode = self.attachNewNode('center') + self.centerCenterNode() + self.soundBomb = base.loadSfx('phase_12/audio/sfx/Mole_Surprise.mp3') + self.soundBomb2 = base.loadSfx('phase_3.5/audio/dial/AV_pig_howl.mp3') + self.soundCog = base.loadSfx('phase_12/audio/sfx/Mole_Stomp.mp3') + self.soundUp = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.mp3') + self.soundDown = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.mp3') + upInterval = SoundInterval(self.soundUp, loop=0) + downInterval = SoundInterval(self.soundDown, loop=0) + self.soundIUpDown = Sequence(upInterval, downInterval) + + def centerCenterNode(self): + self.centerNode.setPos(self.dimensionX * 0.5, self.dimensionY * 0.5, 0.0) + + def loadGui(self): + self.frame2D = DirectFrame(scale=1.0, pos=(0.0, 0, 0.9), relief=DGG.FLAT, parent=aspect2d, frameSize=(-0.3, + 0.3, + -0.05, + 0.05), frameColor=(0.737, 0.573, 0.345, 0.3)) + self.scoreLabel = DirectLabel(parent=self.frame2D, relief=None, pos=(0, 0, 0), scale=1.0, text='', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text_scale=0.075, text_pos=(0, -0.02)) + self.updateGuiScore() + self.frame2D.hide() + return + + def unloadGui(self): + self.frame2D.destroy() + self.frame2D = None + return + + def setGameStart(self, timestamp, molesWhackTarget, totalTime): + self.GameDuration = totalTime + self.MolesWhackedTarget = molesWhackTarget + self.activeField = 1 + self.isToonInRange = 0 + self.scheduleMoles() + self.notify.debug('%d setGameStart: Starting game' % self.doId) + self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.gameStarted = True + for hill in self.moleHills: + hill.setGameStartTime(self.gameStartTime) + + curGameTime = self.getCurrentGameTime() + timeLeft = self.GameDuration - curGameTime + self.cleanupTimer() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posBelowTopRightCorner() + self.timer.setTime(timeLeft) + self.timer.countdown(timeLeft, self.timerExpired) + self.startScheduleTask() + self.frame2D.show() + if self.hasRestarted: + self.level.countryClub.showInfoText(self.restartedText) + self.sendUpdate('damageMe', []) + else: + self.hasRestarted = 1 + self.updateGuiScore() + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def startScheduleTask(self): + taskMgr.add(self.scheduleTask, self.ScheduleTaskName) + + def stopScheduleTask(self): + taskMgr.remove(self.ScheduleTaskName) + + def scheduleTask(self, task): + curTime = self.getCurrentGameTime() + while self.schedule and self.schedule[0][0] <= curTime and self.activeField: + popupInfo = self.schedule[0] + self.schedule = self.schedule[1:] + startTime, moleIndex, curMoveUpTime, curStayUpTime, curMoveDownTime, moleType = popupInfo + hill = self.moleHills[moleIndex] + hill.doMolePop(startTime, curMoveUpTime, curStayUpTime, curMoveDownTime, moleType) + + if self.schedule: + return task.cont + else: + return task.done + + def handleEnterHill(self, colEntry): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + + def handleEnterMole(self, colEntry): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + surfaceNormal = colEntry.getSurfaceNormal(render) + self.notify.debug('surfaceNormal=%s' % surfaceNormal) + into = colEntry.getIntoNodePath() + moleIndex = int(into.getName().split('-')[-1]) + self.notify.debug('hit mole %d' % moleIndex) + moleHill = self.moleHills[moleIndex] + moleHill.stashMoleCollision() + popupNum = moleHill.getPopupNum() + if moleHill.hillType == MoleFieldBase.HILL_MOLE: + timestamp = globalClockDelta.getFrameNetworkTime() + moleHill.setHillType(MoleFieldBase.HILL_WHACKED) + self.sendUpdate('whackedBomb', [moleIndex, popupNum, timestamp]) + self.__showToonHitByBomb(localAvatar.doId, moleIndex, timestamp) + elif moleHill.hillType == MoleFieldBase.HILL_BOMB: + moleHill.setHillType(MoleFieldBase.HILL_COGWHACKED) + self.soundCog.play() + self.sendUpdate('whackedMole', [moleIndex, popupNum]) + + def updateMole(self, moleIndex, status): + if status == self.WHACKED: + moleHill = self.moleHills[moleIndex] + if not moleHill.hillType == MoleFieldBase.HILL_COGWHACKED: + moleHill.setHillType(MoleFieldBase.HILL_COGWHACKED) + self.soundCog.play() + moleHill.doMoleDown() + + def updateGuiScore(self): + molesLeft = self.MolesWhackedTarget - self.numMolesWhacked + if self.frame2D and hasattr(self, 'scoreLabel') and molesLeft >= 0: + newText = TTLocalizer.MolesLeft % molesLeft + self.scoreLabel['text'] = newText + + def setScore(self, score): + self.notify.debug('score=%d' % score) + self.numMolesWhacked = score + self.updateGuiScore() + molesLeft = self.MolesWhackedTarget - self.numMolesWhacked + if molesLeft == 0: + self.gameWon() + + def cleanupTimer(self): + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + return + + def timerExpired(self): + self.cleanupTimer() + self.cleanDetect() + + def gameWon(self): + for hill in self.moleHills: + hill.forceMoleDown() + + self.cleanupTimer() + self.frame2D.hide() + self.level.countryClub.showInfoText(self.winText) + self.cleanDetect() + + def setPityWin(self): + for hill in self.moleHills: + hill.forceMoleDown() + + self.cleanupTimer() + self.frame2D.hide() + self.level.countryClub.showInfoText(self.pityWinText) + self.cleanDetect() + + def cleanDetect(self): + self.activeField = 0 + self.doToonOutOfRange() + + def __detect(self, task): + distance = self.centerNode.getDistance(localAvatar) + greaterDim = self.dimensionX + if self.dimensionY > self.dimensionX: + greaterDim = self.gridScaleY + self.detectCount += 1 + if self.detectCount > 5: + self.detectCount = 0 + if distance < greaterDim * 0.75: + if not self.isToonInRange: + self.doToonInRange() + elif self.isToonInRange: + self.doToonOutOfRange() + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + return Task.done + + def doToonInRange(self): + if not self.gameStarted: + self.notify.debug('sending clientTriggered for %d' % self.doId) + self.sendUpdate('setClientTriggered', []) + self.isToonInRange = 1 + if self.activeField: + self.setUpCamera() + if not self.hasEntered: + self.level.countryClub.showInfoText(self.gameText) + self.hasEntered = 1 + + def setUpCamera(self): + camHeight = base.localAvatar.getClampedAvatarHeight() + heightScaleFactor = camHeight * 0.3333333333 + defLookAt = Point3(0.0, 1.5, camHeight) + cameraPoint = Point3(0.0, -22.0 * heightScaleFactor, camHeight + 12.0) + base.localAvatar.setIdealCameraPos(cameraPoint) + + def doToonOutOfRange(self): + self.isToonInRange = 0 + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + self.cameraHold = None + return + + def reportToonHitByBomb(self, avId, moleIndex, timestamp): + if avId != localAvatar.doId: + self.__showToonHitByBomb(avId, moleIndex, timestamp) + moleHill = self.moleHills[moleIndex] + if not moleHill.hillType == MoleFieldBase.HILL_WHACKED: + moleHill.setHillType(MoleFieldBase.HILL_WHACKED) + self.soundCog.play() + moleHill.doMoleDown() + + def __showToonHitByBomb(self, avId, moleIndex, timestamp = 0): + toon = base.cr.doId2do.get(avId) + moleHill = self.moleHills[moleIndex] + if toon == None: + return + rng = random.Random(timestamp) + curPos = toon.getPos(render) + oldTrack = self.toonHitTracks.get(avId) + if oldTrack: + if oldTrack.isPlaying(): + oldTrack.finish() + toon.setPos(curPos) + toon.setZ(self.getZ()) + parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) + parentNode.setPos(toon.getPos(render)) + toon.reparentTo(parentNode) + toon.setPos(0, 0, 0) + startPos = parentNode.getPos() + dropShadow = toon.dropShadow.copyTo(parentNode) + dropShadow.setScale(toon.dropShadow.getScale(render)) + trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=1.0) + flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + endTile = [rng.randint(0, self.numSquaresX - 1), rng.randint(0, self.numSquaresY - 1)] + endWorldCoords = (self.getX(render) + endTile[0] * self.spacingX, self.getY(render) + endTile[1] * self.spacingY) + endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2]) + + def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): + u = t / dur + moveNode.setX(startPos[0] + u * (endPos[0] - startPos[0])) + moveNode.setY(startPos[1] + u * (endPos[1] - startPos[1])) + if flyNode and not flyNode.isEmpty(): + flyNode.setPos(trajectory.getPos(t)) + + def safeSetHpr(node, hpr): + if node and not node.isEmpty(): + node.setHpr(hpr) + + flyTrack = Sequence(LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly')) + if avId != localAvatar.doId: + cameraTrack = Sequence() + else: + base.localAvatar.stopUpdateSmartCamera() + self.camParentHold = camera.getParent() + self.camParent = base.localAvatar.attachNewNode('iCamParent') + self.camParent.setPos(self.camParentHold.getPos()) + self.camParent.setHpr(self.camParentHold.getHpr()) + camera.reparentTo(self.camParent) + self.camParent.reparentTo(parentNode) + startCamPos = camera.getPos() + destCamPos = camera.getPos() + zenith = trajectory.getPos(flyDur / 2.0)[2] + destCamPos.setZ(zenith * 1.3) + destCamPos.setY(destCamPos[1] * 0.3) + + def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos): + u = flyNode.getZ() / zenith + camera.lookAt(toon) + return Task.cont + + camTaskName = 'mazeToonFlyCam-' + `avId` + taskMgr.add(camTask, camTaskName, priority=20) + + def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos): + taskMgr.remove(camTaskName) + self.camParent.reparentTo(toon) + camera.setPos(startCamPos) + camera.lookAt(toon) + camera.reparentTo(self.camParentHold) + base.localAvatar.startUpdateSmartCamera() + self.setUpCamera() + + cameraTrack = Sequence(Wait(flyDur), Func(cleanupCamTask), name='hitBySuit-cameraLerp') + geomNode = toon.getGeomNode() + startHpr = geomNode.getHpr() + destHpr = Point3(startHpr) + hRot = rng.randrange(1, 8) + if rng.choice([0, 1]): + hRot = -hRot + destHpr.setX(destHpr[0] + hRot * 360) + spinHTrack = Sequence(LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(safeSetHpr, geomNode, startHpr), name=toon.uniqueName('hitBySuit-spinH')) + parent = geomNode.getParent() + rotNode = parent.attachNewNode('rotNode') + geomNode.reparentTo(rotNode) + rotNode.setZ(toon.getHeight() / 2.0) + oldGeomNodeZ = geomNode.getZ() + geomNode.setZ(-toon.getHeight() / 2.0) + startHpr = rotNode.getHpr() + destHpr = Point3(startHpr) + pRot = rng.randrange(1, 3) + if rng.choice([0, 1]): + pRot = -pRot + destHpr.setY(destHpr[1] + pRot * 360) + spinPTrack = Sequence(LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(safeSetHpr, rotNode, startHpr), name=toon.uniqueName('hitBySuit-spinP')) + soundTrack = Sequence() + + def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): + forwardSpeed = toon.forwardSpeed + rotateSpeed = toon.rotateSpeed + if avId == localAvatar.doId: + toon.stopSmooth() + base.cr.playGame.getPlace().fsm.request('stopped') + else: + toon.stopSmooth() + if forwardSpeed or rotateSpeed: + toon.setSpeed(forwardSpeed, rotateSpeed) + toon.dropShadow.hide() + + def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): + if avId == localAvatar.doId: + base.localAvatar.setPos(endPos) + if hasattr(self, 'orthoWalk'): + self.orthoWalk.start() + dropShadow.removeNode() + del dropShadow + if toon and toon.dropShadow: + toon.dropShadow.show() + geomNode = toon.getGeomNode() + rotNode = geomNode.getParent() + baseNode = rotNode.getParent() + geomNode.reparentTo(baseNode) + rotNode.removeNode() + del rotNode + geomNode.setZ(oldGeomNodeZ) + toon.reparentTo(render) + toon.setPos(endPos) + parentNode.removeNode() + del parentNode + if avId == localAvatar.doId: + toon.startSmooth() + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('walk') + else: + toon.startSmooth() + + preFunc() + hitTrack = Sequence(Func(toon.setPos, Point3(0.0, 0.0, 0.0)), Wait(0.25), Parallel(flyTrack, cameraTrack, self.soundIUpDown, spinHTrack, spinPTrack, soundTrack), Func(postFunc), name=toon.uniqueName('hitBySuit')) + self.toonHitTracks[avId] = hitTrack + hitTrack.start() + posM = moleHill.getPos(render) + posN = Point3(posM[0], posM[1], posM[2] + 4.0) + self.soundBomb.play() + self.soundBomb2.play() + return diff --git a/toontown/coghq/DistributedMoleFieldAI.py b/toontown/coghq/DistributedMoleFieldAI.py new file mode 100644 index 0000000..bc0f980 --- /dev/null +++ b/toontown/coghq/DistributedMoleFieldAI.py @@ -0,0 +1,124 @@ +from otp.level import DistributedEntityAI +from toontown.coghq import MoleFieldBase +from direct.distributed.ClockDelta import globalClockDelta +from direct.directnotify import DirectNotifyGlobal + +class DistributedMoleFieldAI(DistributedEntityAI.DistributedEntityAI, MoleFieldBase.MoleFieldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMoleFieldAI') + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.whackedMoles = {} + self.numMolesWhacked = 0 + self.roundsFailed = 0 + self.started = 0 + self.challengeDefeated = False + + def announceGenerate(self): + DistributedEntityAI.DistributedEntityAI.announceGenerate(self) + self.numMoles = self.numSquaresX * self.numSquaresY + self.moleFieldEndTimeTaskName = self.uniqueName('moleFieldEndTime') + self.GameDuration = self.timeToPlay + numToons = 0 + if hasattr(self, 'level'): + numToons = len(self.level.presentAvIds) + self.moleTarget = self.molesBase + self.molesPerPlayer * numToons + + def delete(self): + DistributedEntityAI.DistributedEntityAI.delete(self) + self.removeAllTasks() + + def setClientTriggered(self): + if not hasattr(self, 'gameStartTime'): + self.gameStartTime = globalClock.getRealTime() + if not self.started: + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime), self.moleTarget, self.timeToPlay) + self.started = 1 + + def b_setGameStart(self, timestamp, moleTarget, timeToPlay): + self.d_setGameStart(timestamp, moleTarget, timeToPlay) + self.setGameStart(timestamp) + + def d_setGameStart(self, timestamp, moleTarget, timeToPlay): + self.notify.debug('BASE: Sending setGameStart') + self.sendUpdate('setGameStart', [timestamp, moleTarget, timeToPlay]) + + def setGameStart(self, timestamp): + self.GameDuration = self.timeToPlay + self.notify.debug('BASE: setGameStart') + self.prepareForGameStartOrRestart() + + def prepareForGameStartOrRestart(self): + self.GameDuration = self.timeToPlay + self.scheduleMoles() + self.whackedMoles = {} + self.doMethodLater(self.timeToPlay, self.gameEndingTimeHit, self.moleFieldEndTimeTaskName) + + def whackedMole(self, moleIndex, popupNum): + validMoleWhack = False + if self.whackedMoles.has_key(moleIndex): + if self.whackedMoles[moleIndex] < popupNum: + validMoleWhack = True + else: + self.whackedMoles[moleIndex] = popupNum + validMoleWhack = True + if validMoleWhack: + self.numMolesWhacked += 1 + self.sendUpdate('updateMole', [moleIndex, self.WHACKED]) + self.sendUpdate('setScore', [self.numMolesWhacked]) + self.checkForTargetReached() + + def whackedBomb(self, moleIndex, popupNum, timestamp): + senderId = self.air.getAvatarIdFromSender() + self.sendUpdate('reportToonHitByBomb', [senderId, moleIndex, timestamp]) + + def checkForTargetReached(self): + if self.numMolesWhacked >= self.moleTarget: + if not self.challengeDefeated: + self.forceChallengeDefeated() + + def forceChallengeDefeated(self, pityWin=False): + self.challengeDefeated = True + self.removeTask(self.moleFieldEndTimeTaskName) + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if room: + self.challengeDefeated = True + room.challengeDefeated() + eventName = self.getOutputEventName() + messenger.send(eventName, [1]) + if pityWin: + self.sendUpdate('setPityWin') + + def gameEndingTimeHit(self, task): + if self.numMolesWhacked < self.moleTarget and self.roundsFailed < 4: + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + self.roundsFailed += 1 + self.restartGame() + else: + if self.roundsFailed >= 4: + if not self.challengeDefeated: + self.forceChallengeDefeated(pityWin=True) + + def damageMe(self): + roomId = self.getLevelDoId() + room = simbase.air.doId2do.get(roomId) + if not room: + return + senderId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(senderId) + playerIds = room.presentAvIds + if av and senderId in playerIds: + av.takeDamage(self.DamageOnFailure, quietly=0) + room.sendUpdate('forceOuch', [self.DamageOnFailure]) + + def restartGame(self): + if not hasattr(self, 'entId'): + return + self.gameStartTime = globalClock.getRealTime() + self.started = 0 + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime), self.moleTarget, self.timeToPlay) + + def getScore(self): + return self.numMolesWhacked diff --git a/toontown/coghq/DistributedMover.py b/toontown/coghq/DistributedMover.py new file mode 100644 index 0000000..9f01daf --- /dev/null +++ b/toontown/coghq/DistributedMover.py @@ -0,0 +1,126 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BattleBlocker +import random +from math import * + +class DistributedMover(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMover') + laserFieldModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.attachedEnt = None + self.oldParent = None + self.entity2Move = None + self.moveTarget = None + self.pos0Wait = 1.0 + self.pos0Move = 1.0 + self.pos1Wait = 1.0 + self.pos1Move = 1.0 + self.moverIval = None + return + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + taskMgr.remove(self.taskName) + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + if self.moverIval: + self.moverIval.finish() + self.unloadModel() + if self.taskName: + taskMgr.remove(self.taskName) + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.moverNode = self.attachNewNode('mover') + self.rotateNode = self.attachNewNode('rotate') + self.model = None + if self.entity2Move: + self.setEntity2Move(self.entity2Move) + self.taskName = 'moverUpdate %s' % self.doId + return + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def setEntity2Move(self, entId): + self.entity2Move = entId + if entId: + ent = self.level.getEntity(entId) + if self.attachedEnt and not self.attachedEnt.isEmpty(): + self.attachedEnt.reparentTo(self.oldParent) + self.oldParent = ent.getParent() + ent.reparentTo(self.moverNode) + self.attachedEnt = ent + + def startMove(self, timeStamp): + currentTime = ClockDelta.globalClockDelta.getRealNetworkTime() + timeDiff = (currentTime - timeStamp) / 1000.0 + target = self.level.getEntity(self.moveTarget) + if not target: + return + okay2Play = 1 + if self.moverIval: + self.moverIval.finish() + if self.moverIval.isPlaying(): + okay2Play = 0 + if okay2Play and self.moveTarget: + childList = self.getChildren() + for child in childList: + if child != self.moverNode: + child.reparentTo(self.moverNode) + + timeLag = 0.0 + timeJump = self.pos0Move - timeDiff + if timeJump < 0 or self.cycleType in 'linear': + timeJump = self.pos0Move + timeLag = timeDiff + myBlend = 'easeInOut' + if self.cycleType in 'linear': + myBlend = 'noBlend' + self.moverIval = Sequence() + firstIVal = LerpPosHprInterval(self.moverNode, timeJump, Vec3(target.getPos(self)[0], target.getPos(self)[1], target.getPos(self)[2]), Vec3(target.getHpr(self)[0], target.getHpr(self)[1], target.getHpr(self)[2]), blendType=myBlend, fluid=1) + self.moverIval.append(firstIVal) + if self.cycleType in 'linear': + for linearCycle in range(10): + self.moverIval.append(firstIVal) + + if self.cycleType != 'oneWay': + self.moverIval.append(Wait(self.pos1Wait)) + self.moverIval.append(LerpPosHprInterval(self.moverNode, self.pos1Move, Vec3(0, 0, 0), Vec3(0, 0, 0), blendType=myBlend, fluid=1)) + if self.cycleType == 'loop': + self.moverIval.append(Wait(self.pos0Wait)) + self.moverIval.start() + self.moverIval.setT(timeLag) diff --git a/toontown/coghq/DistributedMoverAI.py b/toontown/coghq/DistributedMoverAI.py new file mode 100644 index 0000000..27aa5b7 --- /dev/null +++ b/toontown/coghq/DistributedMoverAI.py @@ -0,0 +1,105 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.task import Task +from otp.level import DistributedEntityAI +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import BattleBlockerAI +from toontown.coghq import LaserGameMineSweeper +from toontown.coghq import LaserGameRoll +import random + +class DistributedMoverAI(DistributedEntityAI.DistributedEntityAI, NodePath, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedMoverAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + if not hasattr(self, 'pos0Wait'): + self.pos0Wait = 1.0 + if not hasattr(self, 'pos0Move'): + self.pos0Move = 1.0 + if not hasattr(self, 'pos1Wait'): + self.pos1Wait = 1.0 + if not hasattr(self, 'pos1Move'): + self.pos1Move = 1.0 + if not hasattr(self, 'startOn'): + self.startOn = 0 + if not hasattr(self, 'cycleType'): + self.cycleType = 'return' + self.moveTime = {} + self.setTimes() + self.oK2Play = 1 + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.timerName = 'mover %s' % self.doId + self.setPos(self.pos) + self.setHpr(self.hpr) + self.setTimes() + if self.startOn: + self.sendMove() + + def delete(self): + taskMgr.remove(self.timerName) + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + DistributedEntityAI.DistributedEntityAI.destroy(self) + + def reactToSwitch(self, on): + if on: + self.sendMove() + + def setPos0Move(self, time): + self.pos0Move = time + self.setTimes() + + def setPos1Move(self, time): + self.pos1Move = time + self.setTimes() + + def setPos0Wait(self, time): + self.pos0Wait = time + self.setTimes() + + def setPos1Wait(self, time): + self.pos1Wait = time + self.setTimes() + + def setTimes(self): + self.moveTime = {} + self.moveTime['return'] = self.pos0Move + self.pos1Wait + self.pos1Move + self.moveTime['loop'] = self.pos0Wait + self.pos0Move + self.pos1Wait + self.pos1Move + self.moveTime['oneWay'] = self.pos0Move + self.moveTime['linear'] = self.pos0Move * 8 + + def setCycleType(self, type): + self.cycleType = type + self.oK2Play = 1 + + def setStartOn(self, on): + self.startOn = on + self.sendMove() + + def sendMove(self): + timeStamp = ClockDelta.globalClockDelta.getRealNetworkTime() + if self.oK2Play: + self.sendUpdate('startMove', [timeStamp]) + taskMgr.doMethodLater(self.moveTime[self.cycleType], self.__resetTimer, self.timerName) + self.oK2Play = 0 + + def __resetTimer(self, taskMgrFooler=1): + if not self.cycleType == 'oneWay': + self.oK2Play = 1 + if self.cycleType in ('loop', 'linear') or self.startOn: + self.sendMove() + return Task.done diff --git a/toontown/coghq/DistributedSecurityCamera.py b/toontown/coghq/DistributedSecurityCamera.py new file mode 100644 index 0000000..565ddff --- /dev/null +++ b/toontown/coghq/DistributedSecurityCamera.py @@ -0,0 +1,388 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from otp.level import BasicEntities +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BattleBlocker +import random +from math import * + +def circleX(angle, radius, centerX, centerY): + x = radius * cos(angle) + centerX + return x + + +def circleY(angle, radius, centerX, centerY): + y = radius * sin(angle) + centerY + return y + + +def getCirclePoints(segCount, centerX, centerY, radius, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in range(0, segCount): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + coordX = wideX * circleX(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + return returnShape + + +class DistributedSecurityCamera(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSecurityCamera') + laserFieldModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + node = hidden.attachNewNode('DistributedNodePathEntity') + self.trackBeamGN = None + self.trackFloorGN = None + self.trackX = 10.0 + self.trackY = -5.0 + self.radius = 5.0 + self.trackShape = [] + self.trackShape = getCirclePoints(7, 0.0, 0.0, self.radius) + self.trackShapeFloor = [] + self.trackShapeFloor = getCirclePoints(16, 0.0, 0.0, self.radius) + self.zFloat = 0.05 + self.projector = Point3(0, 0, 25) + self.isToonIn = 0 + self.toonX = 0 + self.toonY = 0 + self.canDamage = 1 + self.accel = 0.5 + self.maxVel = 1.0 + self.vX = 0.0 + self.vY = 0.0 + self.targetX = self.trackX + self.targetY = self.trackY + self.target = 0 + self.trackTargetList = [None, + None, + None, + None] + self.lastTime = 0.0 + self.currentTime = 0.0 + self.delta = 0.0 + self.Norm = {} + self.Norm['Red'] = 0.2 + self.Norm['Green'] = 0.2 + self.Norm['Blue'] = 0.2 + self.Norm['Alpha'] = 1.0 + self.Alert = {} + self.Alert['Red'] = 1.0 + self.Alert['Green'] = 0.0 + self.Alert['Blue'] = 0.0 + self.Alert['Alpha'] = 1.0 + self.attackSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_tractor_beam_alarmed.mp3') + self.onSound = loader.loadSfx('phase_11/audio/sfx/LB_camera_shutter_2.mp3') + self.attackTrack = Parallel(SoundInterval(self.attackSound, node=self, volume=0.8), SoundInterval(self.onSound, node=self, volume=0.8)) + self.moveStartSound = loader.loadSfx('phase_11/audio/sfx/LB_laser_beam_on_2.mp3') + self.moveStartTrack = Parallel(SoundInterval(self.moveStartSound, node=self, volume=0.4)) + self.moveLoopSound = loader.loadSfx('phase_11/audio/sfx/LB_laser_beam_hum_2.mp3') + self.moveLoopSound.setLoop() + self.moveLoopTrack = Parallel(SoundInterval(self.moveLoopSound, node=self, volume=0.4)) + self.moveStopSound = loader.loadSfx('phase_11/audio/sfx/LB_laser_beam_off_2.mp3') + self.moveStopTrack = Parallel(SoundInterval(self.moveStopSound, node=self, volume=0.4)) + self.taskName = None + return + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.trackBeamNode = self.attachNewNode('tracking Beam Node') + self.trackBeamGN = GeomNode('tracking Beam') + self.trackBeamNode.attachNewNode(self.trackBeamGN) + self.trackBeamNode.setTransparency(TransparencyAttrib.MAlpha) + self.trackBeamNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.trackBeamNode.setTwoSided(False) + self.trackBeamNode.setDepthWrite(False) + self.trackFloorNode = self.attachNewNode('tracking floor Node') + self.trackFloorGN = GeomNode('tracking Floor') + self.trackFloorNode.attachNewNode(self.trackFloorGN) + self.trackFloorNode.setTransparency(TransparencyAttrib.MAlpha) + self.trackFloorNode.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.trackFloorNode.setTwoSided(False) + self.trackFloorNode.setDepthWrite(False) + if not hasattr(self, 'trackTarget1'): + self.trackTarget1 = None + else: + self.trackTargetList[1] = self.trackTarget1 + if not hasattr(self, 'trackTarget2'): + self.trackTarget2 = None + else: + self.trackTargetList[2] = self.trackTarget2 + if not hasattr(self, 'trackTarget3'): + self.trackTarget3 = None + else: + self.trackTargetList[3] = self.trackTarget3 + self.loadModel() + self.detectName = 'securityCamera %s' % self.doId + return + + def initCollisionGeom(self): + pass + + def sendFail(self): + self.battleStart() + self.sendUpdate('trapFire', []) + + def battleStart(self): + pass + + def setTarget(self, targetHash): + targetCount = targetHash % 3 + sanity = 10 + self.target = None + while targetCount >= 0 and sanity > 0: + sanity -= 1 + for index in range(1, 4): + if self.trackTargetList[index]: + if targetCount == 0: + self.target = self.trackTargetList[index] + targetCount -= 1 + + return + + def setTrackTarget1(self, targetId): + self.trackTarget1 = targetId + self.trackTargetList[1] = targetId + + def setTrackTarget2(self, targetId): + self.trackTarget2 = targetId + self.trackTargetList[2] = targetId + + def setTrackTarget3(self, targetId): + self.trackTarget3 = targetId + self.trackTargetList[3] = targetId + + def __detect(self, task): + distance = self.getDistance(localAvatar) + greaterDim = self.gridScaleX + if self.gridScaleY > self.gridScaleX: + greaterDim = self.gridScaleY + if distance < greaterDim * 1.6: + if localAvatar.getPos(self)[0] > 0 and localAvatar.getPos(self)[0] < self.gridScaleX and localAvatar.getPos(self)[1] > 0 and localAvatar.getPos(self)[1] < self.gridScaleY: + self.__toonHit() + else: + if self.isToonIn: + self.isToonIn = 0 + self.isToonIn = 0 + taskMgr.doMethodLater(0.1, self.__detect, self.detectName) + return Task.done + + def __toonHit(self): + posX = localAvatar.getPos(self)[0] + posY = localAvatar.getPos(self)[1] + tileX = int(posX / (self.gridScaleX / self.gridNumX)) + tileY = int(posY / (self.gridScaleY / self.gridNumY)) + if self.toonX != tileX or self.toonY != tileY or not self.isToonIn: + self.toonX = tileX + self.toonY = tileY + self.isToonIn = 1 + if self.gridData[tileX][tileY] < len(self.gridSymbols): + tileFunction = self.gridSymbols[self.gridData[tileX][tileY]][0] + if tileFunction: + tileFunction() + self.sendHit() + self.isToonIn = 1 + + def sendHit(self): + self.sendUpdate('hit', [self.toonX, self.toonY]) + + def disable(self): + self.notify.debug('disable') + if self.moveLoopTrack.isPlaying(): + self.moveLoopTrack.finish() + if self.attackTrack.isPlaying(): + self.attackTrack.finish() + if self.moveStartTrack.isPlaying(): + self.moveStartTrack.finish() + if self.moveStopTrack.isPlaying(): + self.moveStopTrack.finish() + self.ignoreAll() + taskMgr.remove(self.detectName) + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + if self.taskName: + taskMgr.remove(self.taskName) + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.rotateNode = self.attachNewNode('rotate') + self.model = loader.loadModel(self.laserFieldModels[self.modelPath]) + self.model.reparentTo(self.rotateNode) + self.model.setPos(0, 1, 0) + self.taskName = 'securityCameraupdate %s' % self.doId + taskMgr.add(self.__updateTrack, self.taskName, priority=25) + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def __updateTrack(self, task): + if self.target and self.level and hasattr(self.level, 'entities'): + thing = self.level.entities.get(self.target, None) + self.targetX = thing.getPos(self)[0] + self.targetY = thing.getPos(self)[1] + else: + return Task.cont + self.rotateNode.setPos(self.projector) + self.rotateNode.lookAt(Point3(self.trackX, self.trackY, 0.0)) + dt = globalClock.getDt() + deccel = 1.0 - 1.0 * (dt * 7.0) + if deccel < 0: + deccel = 0.0 + dirX = 0.0 + dirY = 0.0 + distX = self.targetX - self.trackX + distY = self.targetY - self.trackY + trigDist = math.sqrt(distX * distX + distY * distY) + totalDist = abs(distX) + abs(distY) + propX = abs(distX) / (totalDist + 0.01) + propY = abs(distY) / (totalDist + 0.01) + if self.targetX != self.trackX: + dirX = distX / abs(distX) + if self.targetY != self.trackY: + dirY = distY / abs(distY) + if trigDist < self.radius * 0.5 + 1.0: + self.vX = self.vX * deccel + self.vY = self.vY * deccel + self.moveStopTrack.start() + self.moveLoopTrack.finish() + else: + if not self.moveLoopTrack.isPlaying(): + self.moveLoopTrack.start() + self.moveStartTrack.start() + self.vX += dirX * self.accel * propX + self.vY += dirY * self.accel * propY + if self.vX > self.maxVel: + self.vX = self.maxVel + if self.vX < -self.maxVel: + self.vX = -self.maxVel + if self.vY > self.maxVel: + self.vY = self.maxVel + if self.vY < -self.maxVel: + self.vY = -self.maxVel + self.trackX += self.vX * dt + self.trackY += self.vY * dt + self.genTrack() + dist = self.getDist(base.localAvatar) + if dist < self.radius and self.canDamage: + self.canDamage = 0 + self.sendUpdate('trapFire', []) + taskMgr.doMethodLater(2.0, self._resetDam, 'reset Damage') + self.attackTrack.start() + return Task.cont + + def _resetDam(self, task = None): + self.canDamage = 1 + + def getDist(self, thing): + posTX = thing.getX() + posTY = thing.getY() + dx = thing.getPos(self)[0] - self.trackX + dy = thing.getPos(self)[1] - self.trackY + return sqrt(dx * dx + dy * dy) + + def genTrack(self): + dist = self.getDist(base.localAvatar) + draw = 1.0 / (0.01 + float(pow(dist, 0.4))) + self.trackShape = [] + wideX = 1 + wideY = 1 + self.trackShape = getCirclePoints(5 + draw * 12.0, 0.0, 0.0, self.radius, wideX, wideY) + self.trackShapeFloor = [] + self.trackShapeFloor = getCirclePoints(5 + draw * 50.0, 0.0, 0.0, self.radius, wideX, wideY) + if self.trackBeamGN: + self.trackBeamGN.removeAllGeoms() + if self.trackFloorGN: + self.trackFloorGN.removeAllGeoms() + beamRed = 0.0 + beamGreen = 0.0 + beamBlue = 0.0 + beamAlpha = 1.0 + origin = {} + origin['Red'] = 0.2 + origin['Green'] = 0.2 + origin['Blue'] = 0.2 + origin['Alpha'] = 1.0 + if self.canDamage: + origin = self.Norm + else: + origin = self.Alert + self.gFormat = GeomVertexFormat.getV3cp() + self.trackBeamVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.trackBeamVertexWriter = GeomVertexWriter(self.trackBeamVertexData, 'vertex') + self.trackBeamColorWriter = GeomVertexWriter(self.trackBeamVertexData, 'color') + self.trackFloorVertexData = GeomVertexData('holds my vertices', self.gFormat, Geom.UHDynamic) + self.trackFloorVertexWriter = GeomVertexWriter(self.trackFloorVertexData, 'vertex') + self.trackFloorColorWriter = GeomVertexWriter(self.trackFloorVertexData, 'color') + self.trackBeamVertexWriter.addData3f(self.projector[0], self.projector[1], self.projector[2]) + self.trackBeamColorWriter.addData4f(origin['Red'], origin['Green'], origin['Blue'], origin['Alpha']) + self.trackFloorVertexWriter.addData3f(self.trackX, self.trackY, self.zFloat) + self.trackFloorColorWriter.addData4f(origin['Red'], origin['Green'], origin['Blue'], origin['Alpha']) + for vertex in self.trackShape: + self.trackBeamVertexWriter.addData3f(self.trackX + vertex[0], self.trackY + vertex[1], self.zFloat) + self.trackBeamColorWriter.addData4f(beamRed, beamGreen, beamBlue, beamAlpha) + + for vertex in self.trackShapeFloor: + self.trackFloorVertexWriter.addData3f(self.trackX + vertex[0], self.trackY + vertex[1], self.zFloat) + self.trackFloorColorWriter.addData4f(origin['Red'], origin['Green'], origin['Blue'], origin['Alpha']) + + self.trackBeamTris = GeomTrifans(Geom.UHStatic) + self.trackFloorTris = GeomTrifans(Geom.UHStatic) + sizeTrack = len(self.trackShape) + self.trackBeamTris.addVertex(0) + for countVertex in range(1, sizeTrack + 1): + self.trackBeamTris.addVertex(countVertex) + + self.trackBeamTris.addVertex(1) + self.trackBeamTris.closePrimitive() + self.trackBeamGeom = Geom(self.trackBeamVertexData) + self.trackBeamGeom.addPrimitive(self.trackBeamTris) + self.trackBeamGN.addGeom(self.trackBeamGeom) + sizeTrack = len(self.trackShapeFloor) + self.trackFloorTris.addVertex(0) + for countVertex in range(1, sizeTrack + 1): + self.trackFloorTris.addVertex(countVertex) + + self.trackFloorTris.addVertex(1) + self.trackFloorTris.closePrimitive() + self.trackFloorGeom = Geom(self.trackFloorVertexData) + self.trackFloorGeom.addPrimitive(self.trackFloorTris) + self.trackFloorGN.addGeom(self.trackFloorGeom) + + def setProjector(self, projPoint): + self.projector = projPoint + if self.trackBeamGN and self.trackFloorGN: + self.genTrack() + + def setHideModel(self, flag): + if flag: + self.model.stash() + else: + self.model.unstash() diff --git a/toontown/coghq/DistributedSecurityCameraAI.py b/toontown/coghq/DistributedSecurityCameraAI.py new file mode 100644 index 0000000..b616b28 --- /dev/null +++ b/toontown/coghq/DistributedSecurityCameraAI.py @@ -0,0 +1,80 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from direct.task import Task +from otp.level import DistributedEntityAI +from otp.level import BasicEntities +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import BattleBlockerAI +from toontown.coghq import LaserGameMineSweeper +from toontown.coghq import LaserGameRoll +import random + +class DistributedSecurityCameraAI(DistributedEntityAI.DistributedEntityAI, NodePath, BasicEntities.NodePathAttribs): + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + node = hidden.attachNewNode('DistributedSecurityCameraAI') + NodePath.__init__(self, node) + if not hasattr(self, 'switchId'): + self.switchId = 0 + if not hasattr(self, 'damPow'): + self.damPow = 1 + self.enabled = 1 + self.detectName = None + return + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.detectName = 'laserField %s' % self.doId + taskMgr.doMethodLater(3.0, self.__detect, self.detectName) + self.setPos(self.pos) + self.setHpr(self.hpr) + + def delete(self): + if self.detectName: + taskMgr.remove(self.detectName) + self.ignoreAll() + DistributedEntityAI.DistributedEntityAI.delete(self) + + def destroy(self): + self.notify.info('destroy entity(laserField) %s' % self.entId) + DistributedEntityAI.DistributedEntityAI.destroy(self) + + def __detect(self, task): + isThereAnyToons = False + if hasattr(self, 'level'): + toonInRange = 0 + for avId in self.level.presentAvIds: + if avId in self.air.doId2do: + av = self.air.doId2do[avId] + isThereAnyToons = True + distance = self.getDistance(av) + + if isThereAnyToons: + randTime = float(random.randint(1, 6)) * 0.5 + taskMgr.doMethodLater(randTime, self.__detect, self.detectName) + randTarget = random.randint(0, 100) + self.sendUpdate('setTarget', [randTarget]) + return Task.done + + def hit(self, hitX, hitY): + if self.enabled: + self.game.hit(hitX, hitY) + + def reactToSwitch(self, on): + if on: + self.trapDisable() + self.game.win() + + def trapFire(self): + avId = self.air.getAvatarIdFromSender() + toon = self.air.doId2do[avId] + if toon: + toon.takeDamage(self.damPow) + + def trapDisable(self): + self.enabled = 0 diff --git a/toontown/coghq/DistributedSellbotHQDoor.py b/toontown/coghq/DistributedSellbotHQDoor.py new file mode 100644 index 0000000..fc17ca1 --- /dev/null +++ b/toontown/coghq/DistributedSellbotHQDoor.py @@ -0,0 +1,24 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import DistributedCogHQDoor +from toontown.toonbase import TTLocalizer +import CogDisguiseGlobals + +class DistributedSellbotHQDoor(DistributedCogHQDoor.DistributedCogHQDoor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSellbotHQDoor') + + def __init__(self, cr): + DistributedCogHQDoor.DistributedCogHQDoor.__init__(self, cr) + + def informPlayer(self, suitType): + self.notify.debugStateCall(self) + if suitType == CogDisguiseGlobals.suitTypes.NoSuit: + popupMsg = TTLocalizer.SellbotRentalSuitMessage + elif suitType == CogDisguiseGlobals.suitTypes.NoMerits: + popupMsg = TTLocalizer.SellbotCogSuitNoMeritsMessage + elif suitType == CogDisguiseGlobals.suitTypes.FullSuit: + popupMsg = TTLocalizer.SellbotCogSuitHasMeritsMessage + else: + popupMsg = TTLocalizer.FADoorCodes_SB_DISGUISE_INCOMPLETE + localAvatar.elevatorNotifier.showMeWithoutStopping(popupMsg, pos=(0, 0, 0.26), ttDialog=True) + localAvatar.elevatorNotifier.setOkButton() + localAvatar.elevatorNotifier.doneButton.setZ(-0.3) diff --git a/toontown/coghq/DistributedSellbotHQDoorAI.py b/toontown/coghq/DistributedSellbotHQDoorAI.py new file mode 100644 index 0000000..86ab66a --- /dev/null +++ b/toontown/coghq/DistributedSellbotHQDoorAI.py @@ -0,0 +1,55 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import DistributedCogHQDoorAI +from toontown.building import FADoorCodes, DoorTypes +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownAccessAI +import CogDisguiseGlobals +from otp.otpbase import OTPGlobals + +class DistributedSellbotHQDoorAI(DistributedCogHQDoorAI.DistributedCogHQDoorAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSellbotHQDoorAI') + + def __init__(self, air, blockNumber, doorType, destinationZone, doorIndex = 0, lockValue = FADoorCodes.SB_DISGUISE_INCOMPLETE, swing = 3): + self.notify.debugStateCall(self) + DistributedCogHQDoorAI.DistributedCogHQDoorAI.__init__(self, air, blockNumber, doorType, destinationZone, doorIndex, lockValue, swing) + + def requestEnter(self): + avatarID = self.air.getAvatarIdFromSender() + (allowed, suitType) = self.__getAccessLevel(avatarID) + if not allowed: + self.sendReject(avatarID, self.isLockedDoor()) + else: + self.enqueueAvatarIdEnter(avatarID) + self.sendUpdateToAvatarId(avatarID, 'setOtherZoneIdAndDoId', [ + self.destinationZone, + self.otherDoor.getDoId()]) + if ToontownGlobals.SELLBOT_NERF_HOLIDAY in self.air.holidayManager.currentHolidays: + self.sendUpdateToAvatarId(avatarID, 'informPlayer', [ + suitType]) + + def __getAccessLevel(self, avatarID): + av = self.air.doId2do.get(avatarID) + allowed = 0 + suitType = -1 + if av: + if self.doorType == DoorTypes.EXT_COGHQ and self.isLockedDoor(): + parts = av.getCogParts() + dept = ToontownGlobals.cogHQZoneId2deptIndex(self.destinationZone) + if CogDisguiseGlobals.isPaidSuitComplete(av, parts, dept): + if av.getCogMerits()[dept] >= CogDisguiseGlobals.getTotalMerits(av, dept): + suitType = CogDisguiseGlobals.suitTypes.FullSuit + else: + suitType = CogDisguiseGlobals.suitTypes.NoMerits + allowed = 1 + else: + suitType = CogDisguiseGlobals.suitTypes.NoSuit + if ToontownGlobals.SELLBOT_NERF_HOLIDAY in self.air.holidayManager.currentHolidays: + allowed = 1 + + else: + allowed = 1 + + if not ToontownAccessAI.canAccess(avatarID, self.zoneId, 'DistributedSellbotHQDoorAI.__getAccessLevel'): + allowed = 0 + + return (allowed, suitType) diff --git a/toontown/coghq/DistributedSinkingPlatform.py b/toontown/coghq/DistributedSinkingPlatform.py new file mode 100644 index 0000000..1691d20 --- /dev/null +++ b/toontown/coghq/DistributedSinkingPlatform.py @@ -0,0 +1,132 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.level import BasicEntities +import MovingPlatform +from direct.distributed import DistributedObject +import SinkingPlatformGlobals +from direct.directnotify import DirectNotifyGlobal + +class DistributedSinkingPlatform(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSinkingPlatform') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.moveIval = None + return + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + self.fsm = ClassicFSM.ClassicFSM('DistributedSinkingPlatform', [State.State('off', self.enterOff, self.exitOff, ['sinking']), State.State('sinking', self.enterSinking, self.exitSinking, ['rising']), State.State('rising', self.enterRising, self.exitRising, ['sinking', 'off'])], 'off', 'off') + self.fsm.enterInitialState() + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.loadModel() + self.accept(self.platform.getEnterEvent(), self.localToonEntered) + self.accept(self.platform.getExitEvent(), self.localToonLeft) + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + self.fsm.requestFinalState() + BasicEntities.DistributedNodePathEntity.disable(self) + + def delete(self): + self.notify.debug('delete') + self.ignoreAll() + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.platform.destroy() + del self.platform + BasicEntities.DistributedNodePathEntity.delete(self) + + def loadModel(self): + self.notify.debug('loadModel') + model = loader.loadModel('phase_9/models/cogHQ/platform1') + self.platform = MovingPlatform.MovingPlatform() + self.platform.setupCopyModel(self.getParentToken(), model, 'platformcollision') + self.platform.reparentTo(self) + self.platform.setPos(0, 0, 0) + + def localToonEntered(self): + ts = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('setOnOff', [1, ts]) + + def localToonLeft(self): + ts = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('setOnOff', [0, ts]) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + self.notify.debug('exitOff') + + def enterSinking(self, ts = 0): + self.notify.debug('enterSinking') + self.startMoving(SinkingPlatformGlobals.SINKING, ts) + + def exitSinking(self): + self.notify.debug('exitSinking') + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def enterRising(self, ts = 0): + self.notify.debug('enterRising') + self.startMoving(SinkingPlatformGlobals.RISING, ts) + + def exitRising(self): + self.notify.debug('exitRising') + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def setSinkMode(self, avId, mode, ts): + self.notify.debug('setSinkMode %s' % mode) + if mode == SinkingPlatformGlobals.OFF: + self.fsm.requestInitialState() + elif mode == SinkingPlatformGlobals.RISING: + self.fsm.request('rising', [ts]) + elif mode == SinkingPlatformGlobals.SINKING: + self.fsm.request('sinking', [ts]) + + def startMoving(self, direction, ts): + if direction == SinkingPlatformGlobals.RISING: + endPos = Vec3(0, 0, 0) + pause = self.pauseBeforeRise + duration = self.riseDuration + else: + endPos = Vec3(0, 0, -self.verticalRange) + pause = None + duration = self.sinkDuration + startT = globalClockDelta.networkToLocalTime(ts, bits=32) + curT = globalClock.getFrameTime() + ivalTime = curT - startT + if ivalTime < 0: + ivalTime = 0 + elif ivalTime > duration: + ivalTime = duration + duration = duration - ivalTime + duration = max(0.0, duration) + moveNode = self.platform + self.moveIval = Sequence() + if pause is not None: + self.moveIval.append(WaitInterval(pause)) + self.moveIval.append(LerpPosInterval(moveNode, duration, endPos, startPos=moveNode.getPos(), blendType='easeInOut', name='%s-move' % self.platform.name, fluid=1)) + self.moveIval.start() + return diff --git a/toontown/coghq/DistributedSinkingPlatformAI.py b/toontown/coghq/DistributedSinkingPlatformAI.py new file mode 100644 index 0000000..cb33c45 --- /dev/null +++ b/toontown/coghq/DistributedSinkingPlatformAI.py @@ -0,0 +1,24 @@ +from otp.ai.AIBase import * +from direct.directnotify import DirectNotifyGlobal +from otp.level import DistributedEntityAI +import SinkingPlatformGlobals + +class DistributedSinkingPlatformAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSinkingPlatformAI') + + def __init__(self, levelDoId, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, levelDoId, entId) + self.numStanding = 0 + + def setOnOff(self, on, timestamp): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setOnOff %s' % on) + if on: + self.numStanding += 1 + else: + self.numStanding -= 1 + self.notify.debug('numStanding = %s' % self.numStanding) + if self.numStanding > 0: + self.sendUpdate('setSinkMode', [avId, SinkingPlatformGlobals.SINKING, timestamp]) + else: + self.sendUpdate('setSinkMode', [avId, SinkingPlatformGlobals.RISING, timestamp]) diff --git a/toontown/coghq/DistributedStage.py b/toontown/coghq/DistributedStage.py new file mode 100644 index 0000000..f28a216 --- /dev/null +++ b/toontown/coghq/DistributedStage.py @@ -0,0 +1,305 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import BulletinBoardWatcher +from otp.otpbase import OTPGlobals +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from direct.gui import OnscreenText +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedStageRoom, StageLayout, StageRoom +import random +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * + +class DistributedStage(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStage') + ReadyPost = 'StageReady' + WinEvent = 'StageWinEvent' + FloorNum = 'StageFloorNum' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + 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.1, drawOrder=0, mayChange=1) + self.titleSequence = None + self.pendingZoneChange = 0 + return + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('stage', self) + self.roomWatcher = None + self.geom = None + self.rooms = [] + self.hallways = [] + self.allRooms = [] + self.curToonRoomNum = None + base.localAvatar.setCameraCollisionsCanMove(1) + self.accept('SOSPanelEnter', self.handleSOSPanel) + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + + def placeToon(self): + pX = random.randint(-2, 2) + pY = random.randint(-2, 2) + base.localAvatar.reparentTo(render) + base.localAvatar.setPosHpr(pX, pY, 0, 0, 0, 0) + self.camEnterRoom(0) + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setStageId(self, id): + DistributedStage.notify.debug('setStageId: %s' % id) + self.stageId = id + + def setFloorNum(self, num): + DistributedStage.notify.debug('floorNum: %s' % num) + self.floorNum = num + bboard.post(DistributedStage.FloorNum, num) + self.layout = StageLayout.StageLayout(self.stageId, self.floorNum, self.layoutIndex) + + def setRoomDoIds(self, roomDoIds): + self.roomDoIds = roomDoIds + self.roomWatcher = BulletinBoardWatcher.BulletinBoardWatcher('roomWatcher-%s' % self.doId, [ DistributedStageRoom.getStageRoomReadyPostName(doId) for doId in self.roomDoIds ], self.gotAllRooms) + + def gotAllRooms(self): + self.notify.debug('stage %s: got all rooms' % self.doId) + self.roomWatcher.destroy() + self.roomWatcher = None + self.geom = render.attachNewNode('stage%s' % self.doId) + for doId in self.roomDoIds: + self.rooms.append(base.cr.doId2do[doId]) + self.rooms[-1].setStage(self) + + self.notify.info('stageId %s, floor %s, %s' % (self.stageId, self.floorNum, self.rooms[0].avIdList)) + rng = self.layout.getRng() + numRooms = self.layout.getNumRooms() + for i, room in enumerate(self.rooms): + if i == 0: + room.getGeom().reparentTo(self.geom) + else: + room.attachTo(self.hallways[i - 1], rng) + self.allRooms.append(room) + self.listenForFloorEvents(room) + if i < numRooms - 1: + hallway = StageRoom.StageRoom(self.layout.getHallwayModel(i)) + hallway.attachTo(room, rng) + hallway.setRoomNum(i * 2 + 1) + hallway.initFloorCollisions() + hallway.enter() + self.hallways.append(hallway) + self.allRooms.append(hallway) + self.listenForFloorEvents(hallway) + + self.placeToon() + + def handleCameraRayFloorCollision(collEntry, self = self): + name = collEntry.getIntoNode().getName() + self.notify.debug('camera floor ray collided with: %s' % name) + prefix = StageRoom.StageRoom.FloorCollPrefix + prefixLen = len(prefix) + if name[:prefixLen] == prefix: + try: + roomNum = int(name[prefixLen:]) + except: + DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name) + else: + self.camEnterRoom(roomNum) + print collEntry + print + + self.accept('on-floor', handleCameraRayFloorCollision) + if bboard.has('stageRoom'): + self.warpToRoom(bboard.get('stageRoom')) + firstSetZoneDoneEvent = self.cr.getNextSetZoneDoneEvent() + + def handleFirstSetZoneDone(): + self.notify.debug('stageHandleFirstSetZoneDone') + bboard.post(DistributedStage.ReadyPost, self) + + self.acceptOnce(firstSetZoneDoneEvent, handleFirstSetZoneDone) + zoneList = [OTPGlobals.UberZone, self.zoneId] + for room in self.rooms: + zoneList.extend(room.zoneIds) + + base.cr.sendSetZoneMsg(self.zoneId, zoneList) + self.accept('takingScreenshot', self.handleScreenshot) + base.transitions.irisIn() + taskMgr.doMethodLater(0.25, self._delayedInit, 'delayedInit') + return + + def _delayedInit(self, taskFooler = 0): + self.camEnterRoom(0) + base.localAvatar.unpauseGlitchKiller() + try: + fsm = base.cr.playGame.getPlace().fsm + fsm.forceTransition('walk') + except: + pass + + return Task.done + + def listenForFloorEvents(self, room): + roomNum = room.getRoomNum() + floorCollName = room.getFloorCollName() + + def handleZoneEnter(collisionEntry, self = self, roomNum = roomNum): + self.toonEnterRoom(roomNum) + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + room = self.allRooms[roomNum] + ouchLevel = room.getFloorOuchLevel() + room.startOuch(ouchLevel) + + self.accept('enter%s' % floorCollName, handleZoneEnter) + + def handleZoneExit(collisionEntry, self = self, roomNum = roomNum): + floorNode = collisionEntry.getIntoNode() + if floorNode.hasTag('ouch'): + self.allRooms[roomNum].stopOuch() + + self.accept('exit%s' % floorCollName, handleZoneExit) + + def getAllRoomsTimeout(self): + self.notify.warning('stage %s: timed out waiting for room objs' % self.doId) + + def toonEnterRoom(self, roomNum): + self.notify.debug('toonEnterRoom: %s' % roomNum) + if roomNum != self.curToonRoomNum: + if self.curToonRoomNum is not None: + self.allRooms[self.curToonRoomNum].localToonFSM.request('notPresent') + self.allRooms[roomNum].localToonFSM.request('present') + self.curToonRoomNum = roomNum + return + + def camEnterRoom(self, roomNum): + self.notify.debug('camEnterRoom: %s' % roomNum) + self.notify.info('CAMENTERROOM doID%s num%s' % (self.doId, roomNum)) + self.notify.info('av: %s, cam: %s' % (localAvatar.getPos(), camera.getPos())) + if roomNum % 2 == 1: + minVis = roomNum - 2 + maxVis = roomNum + 2 + else: + minVis = roomNum - 1 + maxVis = roomNum + 1 + for i, room in enumerate(self.allRooms): + if i < minVis or i > maxVis: + room.getGeom().stash() + else: + room.getGeom().unstash() + + def setBossConfronted(self, avId): + if avId == base.localAvatar.doId: + return + av = base.cr.identifyFriend(avId) + if av is None: + return + base.localAvatar.setSystemMessage(avId, TTLocalizer.StageBossConfrontedMsg % av.getName()) + return + + def warpToRoom(self, roomId): + for i in xrange(len(self.rooms)): + room = self.rooms[i] + if room.roomId == roomId: + break + else: + return False + + base.localAvatar.setPosHpr(room.getGeom(), 0, 0, 0, 0, 0, 0) + self.camEnterRoom(i * 2) + return True + + def disable(self): + self.notify.debug('disable') + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.ignoreAll() + if self.titleText: + self.titleText.cleanup() + self.titleText = None + for hallway in self.hallways: + hallway.exit() + + self.rooms = [] + for hallway in self.hallways: + hallway.delete() + + self.hallways = [] + self.allRooms = [] + if self.roomWatcher: + self.roomWatcher.destroy() + self.roomWatcher = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + base.localAvatar.setCameraCollisionsCanMove(0) + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.ignore('SOSPanelEnter') + bboard.remove('stage') + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.rooms[0].avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def handleScreenshot(self): + base.addScreenshotString('stageId: %s, floor (from 1): %s' % (self.stageId, self.floorNum + 1)) + if hasattr(self, 'currentRoomName'): + base.addScreenshotString('%s' % self.currentRoomName) + + def setStageZone(self, zoneId): + base.cr.sendSetZoneMsg(zoneId) + base.cr.playGame.getPlace().fsm.request('walk') + scale = base.localAvatar.getScale() + base.camera.setScale(scale) + + def showInfoText(self, text = 'hello world'): + description = text + if description and description != '': + self.titleText.setText(description) + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.setColorScale(1, 1, 1, 1) + self.titleText.setFg(self.titleColor) + if self.titleSequence: + self.titleSequence.finish() + self.titleSequence = None + self.titleSequence = Sequence(Func(self.showTitleText), Wait(3.1), LerpColorScaleInterval(self.titleText, duration=0.5, colorScale=Vec4(1, 1, 1, 0.0)), Func(self.hideTitleText)) + self.titleSequence.start() + return + + def showTitleText(self): + if self.titleText: + self.titleText.show() + + def hideTitleText(self): + if self.titleText or 1: + self.titleText.hide() + self.titleText.setText('') + + def elevatorAlert(self, avId): + if base.localAvatar.doId != avId: + name = base.cr.doId2do[avId].getName() + self.showInfoText(TTLocalizer.stageToonEnterElevator % name) diff --git a/toontown/coghq/DistributedStageAI.py b/toontown/coghq/DistributedStageAI.py new file mode 100644 index 0000000..3dd571a --- /dev/null +++ b/toontown/coghq/DistributedStageAI.py @@ -0,0 +1,131 @@ +from direct.distributed import DistributedObjectAI +from otp.level import DistributedLevelAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.coghq import StageLayout, DistributedStageRoomAI +from toontown.coghq import BattleExperienceAggregatorAI +from toontown.building import DistributedElevatorFloorAI +from pandac.PandaModules import * + +class DistributedStageAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageAI') + + def __init__(self, air, stageId, zoneId, floorNum, avIds, layoutIndex, battleExpAggreg=None): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.stageId = stageId + self.zoneId = zoneId + self.floorNum = floorNum + self.avIds = avIds + self.elevatorList = [] + self.setLayoutIndex(layoutIndex) + self.battleExpAggreg = battleExpAggreg + self.puzzelReward = 5 + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.info('generate %s, id=%s, floor=%s' % (self.doId, self.stageId, self.floorNum)) + self.layout = StageLayout.StageLayout(self.stageId, self.floorNum, self.layoutIndex) + self.rooms = [] + if self.battleExpAggreg is None: + self.battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + for i in range(self.layout.getNumRooms()): + room = DistributedStageRoomAI.DistributedStageRoomAI(self.air, self.stageId, self.doId, self.zoneId, self.layout.getRoomId(i), i * 2, self.avIds, self.battleExpAggreg) + room.generateWithRequired(self.zoneId) + self.rooms.append(room) + + roomDoIds = [] + for room in self.rooms: + roomDoIds.append(room.doId) + + self.sendUpdate('setRoomDoIds', [roomDoIds]) + self.placeElevatorsOnMarkers() + if __dev__: + simbase.stage = self + description = '%s|%s|%s' % (self.stageId, self.floorNum, self.avIds) + for avId in self.avIds: + self.air.writeServerEvent('stageEntered', avId, description) + + return + + def requestDelete(self): + self.notify.info('requestDelete: %s' % self.doId) + if hasattr(self, 'rooms'): + for room in self.rooms: + room.requestDelete() + + if hasattr(self, 'elevatorList'): + for elevator in self.elevatorList: + elevator.requestDelete() + + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + + def delete(self): + self.notify.info('delete: %s' % self.doId) + if __dev__: + if hasattr(simbase, 'stage') and simbase.stage is self: + del simbase.stage + self.air.deallocateZone(self.zoneId) + if hasattr(self, 'elevatorList'): + del self.elevatorList + if hasattr(self, 'rooms'): + del self.rooms + if hasattr(self, 'layout'): + del self.layout + if hasattr(self, 'battleExpAggreg'): + del self.battleExpAggreg + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getTaskZoneId(self): + return self.stageId + + def placeElevatorsOnMarkers(self): + for room in self.rooms: + if room.entType2ids['elevatorMarker']: + for markerId in room.entType2ids['elevatorMarker']: + marker = room.getEntity(markerId) + newElevator = DistributedElevatorFloorAI.DistributedElevatorFloorAI(self.air, self.doId, self, self.avIds, marker.doId) + newElevator.generateWithRequired(self.zoneId) + self.elevatorList.append(newElevator) + + def allToonsGone(self): + self.notify.info('allToonsGone') + self.requestDelete() + + def getZoneId(self): + return self.zoneId + + def getStageId(self): + return self.stageId + + def getFloorNum(self): + return self.floorNum + + def setLayoutIndex(self, layoutIndex): + self.layoutIndex = layoutIndex + + def getLayoutIndex(self): + return self.layoutIndex + + def startNextFloor(self): + floor = self.floorNum + 1 + StageZone = self.air.allocateZone() + Stage = DistributedStageAI(self.air, self.stageId, StageZone, floor, self.avIds, self.layoutIndex, self.battleExpAggreg) + Stage.generateWithRequired(StageZone) + for avId in self.avIds: + self.sendUpdateToAvatarId(avId, 'setStageZone', [StageZone]) + + self.requestDelete() + + def elevatorAlert(self, avId): + self.sendUpdate('elevatorAlert', [avId]) + + def increasePuzzelReward(self): + self.puzzelReward += 5 + if self.puzzelReward > 10: + self.puzzelReward = 10 + + def resetPuzzelReward(self): + self.puzzelReward = 5 + + def getPuzzelReward(self): + return self.puzzelReward diff --git a/toontown/coghq/DistributedStageBattle.py b/toontown/coghq/DistributedStageBattle.py new file mode 100644 index 0000000..e63c64f --- /dev/null +++ b/toontown/coghq/DistributedStageBattle.py @@ -0,0 +1,47 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleBase import * +from toontown.coghq import DistributedLevelBattle +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import TTEmote +from otp.avatar import Emote +from toontown.battle import SuitBattleGlobals +import random +from toontown.suit import SuitDNA +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals + +class DistributedStageBattle(DistributedLevelBattle.DistributedLevelBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageBattle') + + def __init__(self, cr): + DistributedLevelBattle.DistributedLevelBattle.__init__(self, cr) + self.fsm.addState(State.State('StageReward', self.enterStageReward, self.exitStageReward, ['Resume'])) + offState = self.fsm.getStateNamed('Off') + offState.addTransition('StageReward') + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('StageReward') + + def enterStageReward(self, ts): + self.notify.debug('enterStageReward()') + self.disableCollision() + self.delayDeleteMembers() + if self.hasLocalToon(): + NametagGlobals.setMasterArrowsOn(0) + if self.bossBattle: + messenger.send('localToonConfrontedStageBoss') + self.movie.playReward(ts, self.uniqueName('building-reward'), self.__handleStageRewardDone) + + def __handleStageRewardDone(self): + self.notify.debug('stage reward done') + if self.hasLocalToon(): + self.d_rewardDone(base.localAvatar.doId) + self.movie.resetReward() + self.fsm.request('Resume') + + def exitStageReward(self): + self.notify.debug('exitStageReward()') + self.movie.resetReward(finish=1) + self._removeMembersKeep() + NametagGlobals.setMasterArrowsOn(1) diff --git a/toontown/coghq/DistributedStageBattleAI.py b/toontown/coghq/DistributedStageBattleAI.py new file mode 100644 index 0000000..656114a --- /dev/null +++ b/toontown/coghq/DistributedStageBattleAI.py @@ -0,0 +1,77 @@ +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedLevelBattleAI +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.battle.BattleBase import * +import CogDisguiseGlobals +from toontown.toonbase.ToontownBattleGlobals import getStageCreditMultiplier +from direct.showbase.PythonUtil import addListsByValue, enumerate + +class DistributedStageBattleAI(DistributedLevelBattleAI.DistributedLevelBattleAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageBattleAI') + + def __init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, roundCallback=None, finishCallback=None, maxSuits=4): + DistributedLevelBattleAI.DistributedLevelBattleAI.__init__(self, air, battleMgr, pos, suit, toonId, zoneId, level, battleCellId, 'StageReward', roundCallback, finishCallback, maxSuits) + self.battleCalc.setSkillCreditMultiplier(1) + if self.bossBattle: + self.level.d_setBossConfronted(toonId) + self.fsm.addState(State.State('StageReward', self.enterStageReward, self.exitStageReward, [ + 'Resume'])) + playMovieState = self.fsm.getStateNamed('PlayMovie') + playMovieState.addTransition('StageReward') + + def getTaskZoneId(self): + return self.level.stageId + + def storeSuitsKilledThisBattle(self): + floor = self.level.getFloorNum() + if len(self.suitsKilledPerFloor) < floor + 1: + self.suitsKilledPerFloor.append(self.suitsKilledThisBattle) + else: + self.suitsKilledPerFloor[floor].extend(self.suitsKilledThisBattle) + + def handleToonsWon(self, toons): + extraMerits = [ + 0, 0, 0, 0] + amount = ToontownGlobals.StageNoticeRewards[self.level.stageId] + index = ToontownGlobals.cogHQZoneId2deptIndex(self.level.stageId) + extraMerits[index] = amount + for toon in toons: + mult = 1.0 + meritArray = self.air.promotionMgr.recoverMerits(toon, [], self.getTaskZoneId(), mult, extraMerits=extraMerits) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful list, skipping merits' % toon.doId) + + for floorNum, cogsThisFloor in enumerate(self.suitsKilledPerFloor): + self.notify.info('merits for floor %s' % floorNum) + for toon in toons: + recovered, notRecovered = self.air.questManager.recoverItems(toon, cogsThisFloor, self.getTaskZoneId()) + self.toonItems[toon.doId][0].extend(recovered) + self.toonItems[toon.doId][1].extend(notRecovered) + meritArray = self.air.promotionMgr.recoverMerits(toon, cogsThisFloor, self.getTaskZoneId(), getStageCreditMultiplier(floorNum)) + self.notify.info('toon %s: %s' % (toon.doId, meritArray)) + if toon.doId in self.helpfulToons: + self.toonMerits[toon.doId] = addListsByValue(self.toonMerits[toon.doId], meritArray) + else: + self.notify.debug('toon %d not helpful list, skipping merits' % toon.doId) + + def enterStageReward(self): + self.joinableFsm.request('Unjoinable') + self.runableFsm.request('Unrunable') + self.resetResponses() + self.assignRewards() + self.bossDefeated = 1 + self.level.setVictors(self.activeToons[:]) + self.timer.startCallback(BUILDING_REWARD_TIMEOUT, self.serverRewardDone) + return None + + def exitStageReward(self): + return None + + def enterResume(self): + DistributedLevelBattleAI.DistributedLevelBattleAI.enterResume(self) + if self.bossBattle and self.bossDefeated: + self.battleMgr.level.b_setDefeated() diff --git a/toontown/coghq/DistributedStageRoom.py b/toontown/coghq/DistributedStageRoom.py new file mode 100644 index 0000000..3aefd71 --- /dev/null +++ b/toontown/coghq/DistributedStageRoom.py @@ -0,0 +1,238 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import random +from otp.level import DistributedLevel +from direct.directnotify import DirectNotifyGlobal +import StageRoomBase, StageRoom +import FactoryEntityCreator +import StageRoomSpecs +from otp.level import LevelSpec, LevelConstants +from toontown.toonbase import TTLocalizer +if __dev__: + from otp.level import EditorGlobals + +def getStageRoomReadyPostName(doId): + return 'stageRoomReady-%s' % doId + + +class DistributedStageRoom(DistributedLevel.DistributedLevel, StageRoomBase.StageRoomBase, StageRoom.StageRoom): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageRoom') + EmulateEntrancePoint = False + + def __init__(self, cr): + DistributedLevel.DistributedLevel.__init__(self, cr) + StageRoomBase.StageRoomBase.__init__(self) + StageRoom.StageRoom.__init__(self) + self.suitIds = [] + self.suits = [] + self.reserveSuits = [] + self.joiningReserves = [] + self.suitsInitialized = 0 + self.goonClipPlanes = {} + self.stage = None + return + + def createEntityCreator(self): + return FactoryEntityCreator.FactoryEntityCreator(level=self) + + def generate(self): + self.notify.debug('generate') + DistributedLevel.DistributedLevel.generate(self) + + def delete(self): + del self.stage + DistributedLevel.DistributedLevel.delete(self) + StageRoom.StageRoom.delete(self) + self.ignoreAll() + + def setStageId(self, stageId): + self.notify.debug('stageId: %s' % stageId) + StageRoomBase.StageRoomBase.setStageId(self, stageId) + + def setRoomId(self, roomId): + self.notify.debug('roomId: %s' % roomId) + StageRoomBase.StageRoomBase.setRoomId(self, roomId) + + def setRoomNum(self, num): + self.notify.debug('roomNum: %s' % num) + StageRoom.StageRoom.setRoomNum(self, num) + + def levelAnnounceGenerate(self): + self.notify.debug('levelAnnounceGenerate') + DistributedLevel.DistributedLevel.levelAnnounceGenerate(self) + specModule = StageRoomSpecs.getStageRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + if __dev__: + typeReg = self.getEntityTypeReg() + roomSpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.initializeLevel(self, roomSpec) + + def getReadyPostName(self): + return getStageRoomReadyPostName(self.doId) + + def privGotSpec(self, levelSpec): + if __dev__: + if not levelSpec.hasEntityTypeReg(): + typeReg = self.getEntityTypeReg() + levelSpec.setEntityTypeReg(typeReg) + DistributedLevel.DistributedLevel.privGotSpec(self, levelSpec) + StageRoom.StageRoom.enter(self) + self.acceptOnce('leavingStage', self.announceLeaving) + bboard.post(self.getReadyPostName()) + + def fixupLevelModel(self): + StageRoom.StageRoom.setGeom(self, self.geom) + StageRoom.StageRoom.initFloorCollisions(self) + + def setStage(self, stage): + self.stage = stage + + def setBossConfronted(self, avId): + self.stage.setBossConfronted(avId) + + def setDefeated(self): + self.notify.info('setDefeated') + from toontown.coghq import DistributedStage + messenger.send(DistributedStage.DistributedStage.WinEvent) + + def initVisibility(self, *args, **kw): + pass + + def shutdownVisibility(self, *args, **kw): + pass + + def lockVisibility(self, *args, **kw): + pass + + def unlockVisibility(self, *args, **kw): + pass + + def enterZone(self, *args, **kw): + pass + + def updateVisibility(self, *args, **kw): + pass + + def setVisibility(self, *args, **kw): + pass + + def resetVisibility(self, *args, **kw): + pass + + def handleVisChange(self, *args, **kw): + pass + + def forceSetZoneThisFrame(self, *args, **kw): + pass + + def getParentTokenForEntity(self, entId): + if __dev__: + pass + return 1000000 * self.roomNum + entId + + def enterLtNotPresent(self): + StageRoom.StageRoom.enterLtNotPresent(self) + if __dev__: + bboard.removeIfEqual(EditorGlobals.EditTargetPostName, self) + self.ignore('f2') + + def enterLtPresent(self): + StageRoom.StageRoom.enterLtPresent(self) + if __dev__: + bboard.post(EditorGlobals.EditTargetPostName, self) + if self.stage is not None: + self.stage.currentRoomName = StageRoomSpecs.CashbotStageRoomId2RoomName[self.roomId] + + def printPos(self = self): + thisZone = self.getZoneNode(LevelConstants.UberZoneEntId) + pos = base.localAvatar.getPos(thisZone) + h = base.localAvatar.getH(thisZone) + roomName = StageRoomSpecs.CashbotStageRoomId2RoomName[self.roomId] + print 'stage pos: %s, h: %s, room: %s' % (repr(pos), h, roomName) + if self.stage is not None: + floorNum = self.stage.floorNum + else: + floorNum = '???' + posStr = 'X: %.3f' % pos[0] + '\nY: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + '\nH: %.3f' % h + '\nstageId: %s' % self.stageId + '\nfloor: %s' % floorNum + '\nroomId: %s' % self.roomId + '\nroomName: %s' % roomName + base.localAvatar.setChatAbsolute(posStr, CFThought | CFTimeout) + return + + self.accept('f2', printPos) + return + + def handleSOSPanel(self, panel): + avIds = [] + for avId in self.avIdList: + if base.cr.doId2do.get(avId): + avIds.append(avId) + + panel.setFactoryToonIdList(avIds) + + def disable(self): + self.notify.debug('disable') + StageRoom.StageRoom.exit(self) + if hasattr(self, 'suits'): + del self.suits + if hasattr(self, 'relatedObjectMgrRequest') and self.relatedObjectMgrRequest: + self.cr.relatedObjectMgr.abortRequest(self.relatedObjectMgrRequest) + del self.relatedObjectMgrRequest + bboard.remove(self.getReadyPostName()) + DistributedLevel.DistributedLevel.disable(self) + + def setSuits(self, suitIds, reserveSuitIds): + oldSuitIds = list(self.suitIds) + self.suitIds = suitIds + self.reserveSuitIds = reserveSuitIds + newSuitIds = [] + for suitId in self.suitIds: + if suitId not in oldSuitIds: + newSuitIds.append(suitId) + + if len(newSuitIds): + + def bringOutOfReserve(suits): + for suit in suits: + suit.comeOutOfReserve() + + self.relatedObjectMgrRequest = self.cr.relatedObjectMgr.requestObjects(newSuitIds, bringOutOfReserve) + + def reservesJoining(self): + pass + + def getCogSpec(self, cogId): + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.CogData[cogId] + + def getReserveCogSpec(self, cogId): + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.ReserveCogData[cogId] + + def getBattleCellSpec(self, battleCellId): + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + return cogSpecModule.BattleCells[battleCellId] + + def getFloorOuchLevel(self): + return 8 + + def getTaskZoneId(self): + return self.stageId + + def getBossTaunt(self): + return TTLocalizer.StageBossTaunt + + def getBossBattleTaunt(self): + return TTLocalizer.StageBossBattleTaunt + + def __str__(self): + if hasattr(self, 'roomId'): + return '%s %s: %s' % (self.__class__.__name__, self.roomId, StageRoomSpecs.CashbotStageRoomId2RoomName[self.roomId]) + else: + return 'DistributedStageRoom' + + def __repr__(self): + return str(self) + + def complexVis(self): + return 0 diff --git a/toontown/coghq/DistributedStageRoomAI.py b/toontown/coghq/DistributedStageRoomAI.py new file mode 100644 index 0000000..05dd64b --- /dev/null +++ b/toontown/coghq/DistributedStageRoomAI.py @@ -0,0 +1,144 @@ +from otp.level import DistributedLevelAI, LevelSpec +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from otp.level import LevelSpec +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals +from toontown.coghq import FactoryEntityCreatorAI, StageRoomSpecs +from toontown.coghq import StageRoomBase, LevelSuitPlannerAI +from toontown.coghq import DistributedStageBattleAI +from toontown.suit import DistributedStageSuitAI + +class DistributedStageRoomAI(DistributedLevelAI.DistributedLevelAI, StageRoomBase.StageRoomBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageRoomAI') + + def __init__(self, air, stageId, stageDoId, zoneId, roomId, roomNum, avIds, battleExpAggreg): + DistributedLevelAI.DistributedLevelAI.__init__(self, air, zoneId, 0, avIds) + StageRoomBase.StageRoomBase.__init__(self) + self.setStageId(stageId) + self.setRoomId(roomId) + self.roomNum = roomNum + self.stageDoId = stageDoId + self.battleExpAggreg = battleExpAggreg + + def createEntityCreator(self): + return FactoryEntityCreatorAI.FactoryEntityCreatorAI(level=self) + + def getBattleCreditMultiplier(self): + return ToontownBattleGlobals.getStageCreditMultiplier(self.getFloorNum()) + + def getFloorNum(self): + stage = self.air.getDo(self.stageDoId) + if stage is None: + self.notify.warning('getFloorNum: could not find stage %s' % self.stageDoId) + return 0 + return stage.floorNum + + def generate(self): + self.notify.debug('generate %s: room=%s' % (self.doId, self.roomId)) + self.notify.debug('loading spec') + specModule = StageRoomSpecs.getStageRoomSpecModule(self.roomId) + roomSpec = LevelSpec.LevelSpec(specModule) + if __dev__: + self.notify.debug('creating entity type registry') + typeReg = self.getEntityTypeReg() + roomSpec.setEntityTypeReg(typeReg) + self.notify.debug('creating entities') + DistributedLevelAI.DistributedLevelAI.generate(self, roomSpec) + self.notify.debug('creating cogs') + cogSpecModule = StageRoomSpecs.getCogSpecModule(self.roomId) + self.planner = LevelSuitPlannerAI.LevelSuitPlannerAI(self.air, self, DistributedStageSuitAI.DistributedStageSuitAI, DistributedStageBattleAI.DistributedStageBattleAI, cogSpecModule.CogData, cogSpecModule.ReserveCogData, cogSpecModule.BattleCells, battleExpAggreg=self.battleExpAggreg) + suitHandles = self.planner.genSuits() + messenger.send('plannerCreated-' + str(self.doId)) + self.suits = suitHandles['activeSuits'] + self.reserveSuits = suitHandles['reserveSuits'] + self.d_setSuits() + self.notify.debug('finish stage room %s %s creation' % (self.roomId, self.doId)) + + def delete(self): + self.notify.debug('delete: %s' % self.doId) + suits = self.suits + for reserve in self.reserveSuits: + suits.append(reserve[0]) + + self.planner.destroy() + del self.planner + for suit in suits: + if not suit.isDeleted(): + suit.factoryIsGoingDown() + suit.requestDelete() + + del self.battleExpAggreg + DistributedLevelAI.DistributedLevelAI.delete(self, deAllocZone=False) + + def getStageId(self): + return self.stageId + + def getRoomId(self): + return self.roomId + + def getRoomNum(self): + return self.roomNum + + def getCogLevel(self): + return self.cogLevel + + def d_setSuits(self): + self.sendUpdate('setSuits', [self.getSuits(), self.getReserveSuits()]) + + def getSuits(self): + suitIds = [] + for suit in self.suits: + suitIds.append(suit.doId) + + return suitIds + + def getReserveSuits(self): + suitIds = [] + for suit in self.reserveSuits: + suitIds.append(suit[0].doId) + + return suitIds + + def d_setBossConfronted(self, toonId): + if toonId not in self.avIdList: + self.notify.warning('d_setBossConfronted: %s not in list of participants' % toonId) + return + self.sendUpdate('setBossConfronted', [toonId]) + + def setVictors(self, victorIds): + activeVictors = [] + activeVictorIds = [] + for victorId in victorIds: + toon = self.air.doId2do.get(victorId) + if toon is not None: + activeVictors.append(toon) + activeVictorIds.append(victorId) + + description = '%s|%s' % (self.stageId, activeVictorIds) + for avId in activeVictorIds: + self.air.writeServerEvent('stageDefeated', avId, description) + + for toon in activeVictors: + simbase.air.questManager.toonDefeatedStage(toon, self.stageId, activeVictors) + + return + + def b_setDefeated(self): + self.d_setDefeated() + self.setDefeated() + + def d_setDefeated(self): + self.sendUpdate('setDefeated') + + def setDefeated(self): + pass + + def allToonsGone(self, toonsThatCleared): + DistributedLevelAI.DistributedLevelAI.allToonsGone(self, toonsThatCleared) + if self.roomNum == 0: + stage = simbase.air.doId2do.get(self.stageDoId) + if stage is not None: + stage.allToonsGone() + else: + self.notify.warning('no stage %s in allToonsGone' % self.stageDoId) + return diff --git a/toontown/coghq/DistributedStomper.py b/toontown/coghq/DistributedStomper.py new file mode 100644 index 0000000..127127d --- /dev/null +++ b/toontown/coghq/DistributedStomper.py @@ -0,0 +1,355 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from StomperGlobals import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +import DistributedCrusherEntity +import MovingPlatform +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from toontown.toonbase import ToontownGlobals + +class DistributedStomper(DistributedCrusherEntity.DistributedCrusherEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomper') + stomperSounds = ['phase_4/audio/sfx/CHQ_FACT_stomper_small.mp3', 'phase_9/audio/sfx/CHQ_FACT_stomper_med.mp3', 'phase_9/audio/sfx/CHQ_FACT_stomper_large.mp3'] + stomperModels = ['phase_9/models/cogHQ/square_stomper'] + + def __init__(self, cr): + self.stomperModels = ['phase_9/models/cogHQ/square_stomper'] + self.lastPos = Point3(0, 0, 0) + self.model = None + self.smokeTrack = None + self.ival = None + self.smoke = None + self.shadow = None + self.sound = None + self.crushSurface = None + self.cogStyle = 0 + self.loaded = 0 + self.crushedList = [] + self.sounds = [] + self.wantSmoke = 1 + self.wantShadow = 1 + self.animateShadow = 1 + self.removeHeadFloor = 0 + self.removeCamBarrierCollisions = 0 + for s in self.stomperSounds: + self.sounds.append(loader.loadSfx(s)) + + DistributedCrusherEntity.DistributedCrusherEntity.__init__(self, cr) + return + + def generateInit(self): + self.notify.debug('generateInit') + DistributedCrusherEntity.DistributedCrusherEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + DistributedCrusherEntity.DistributedCrusherEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedCrusherEntity.DistributedCrusherEntity.announceGenerate(self) + self.loadModel() + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + if self.smokeTrack: + self.smokeTrack.pause() + del self.smokeTrack + self.smokeTrack = None + DistributedCrusherEntity.DistributedCrusherEntity.disable(self) + return + + def delete(self): + self.notify.debug('delete') + self.unloadModel() + taskMgr.remove(self.taskName('smokeTask')) + DistributedCrusherEntity.DistributedCrusherEntity.delete(self) + + def loadModel(self): + self.loaded = 1 + self.stomperModels = ['phase_9/models/cogHQ/square_stomper'] + if self.cogStyle == 1: + self.stomperModels = ['phase_11/models/lawbotHQ/LB_square_stomper'] + self.notify.debug('loadModel') + shadow = None + self.sound = self.sounds[self.soundPath] + self.rotateNode = self.attachNewNode('rotate') + stomperModel = loader.loadModel(self.stomperModels[self.modelPath]) + if self.style == 'vertical': + model = stomperModel + self.rotateNode.setP(-90) + sideList = model.findAllMatches('**/collSide') + for side in sideList: + side.stash() + + upList = model.findAllMatches('**/collUp') + for up in upList: + up.stash() + + head = model.find('**/head') + shaft = model.find('**/shaft') + self.crushSurface = head.find('**/collDownWalls') + self.shadow = None + if self.wantShadow: + shadow = loader.loadModel('phase_3/models/props/square_drop_shadow').getChild(0) + shadow.setScale(0.3 * self.headScale[0], 0.3 * self.headScale[2], 1) + shadow.setAlphaScale(0.8) + shadow.flattenMedium() + shadow.reparentTo(self) + shadow.setPos(0, 0, 0.025) + shadow.setTransparency(1) + self.shadow = shadow + floorHeadNp = model.find('**/head_collisions/**/collDownFloor') + floorHead = floorHeadNp.node() + if self.removeHeadFloor: + floorHeadNp.stash() + else: + for i in range(floorHead.getNumSolids()): + floorHead.modifySolid(i).setEffectiveNormal(Vec3(0.0, -1.0, 0.0)) + + floorShaft = model.find('**/shaft_collisions/**/collDownFloor').node() + for i in range(floorShaft.getNumSolids()): + floorShaft.modifySolid(i).setEffectiveNormal(Vec3(0.0, -1.0, 0.0)) + + self.accept(self.crushMsg, self.checkSquashedToon) + elif self.style == 'horizontal': + model = MovingPlatform.MovingPlatform() + model.setupCopyModel(self.getParentToken(), stomperModel, 'collSideFloor') + head = model.find('**/head') + head.node().setPreserveTransform(0) + head.setZ(1.0) + for child in head.findAllMatches('+ModelNode'): + child.node().setPreserveTransform(ModelNode.PTNet) + + model.flattenLight() + upList = model.findAllMatches('**/collUp') + for up in upList: + up.stash() + + downList = model.findAllMatches('**/collDown') + for down in downList: + down.stash() + + self.crushSurface = model.find('**/head_collisions/**/collSideWalls') + if self.removeCamBarrierCollisions: + walls = model.findAllMatches('**/collDownWalls') + for wall in walls: + node = wall.node() + bitmask = node.getIntoCollideMask() + invBitmask = BitMask32(ToontownGlobals.CameraBitmask) + invBitmask.invertInPlace() + bitmask &= invBitmask + node.setIntoCollideMask(bitmask) + + shaft = model.find('**/shaft') + shaft.setScale(self.shaftScale) + head.setScale(self.headScale) + model.find('**/shaft').node().setPreserveTransform(0) + model.flattenLight() + self.model = model + if self.motion == MotionSwitched: + self.model.setPos(0, -self.range, 0) + self.model.reparentTo(self.rotateNode) + if self.wantSmoke: + self.smoke = loader.loadModel('phase_4/models/props/test_clouds') + self.smoke.setColor(0.8, 0.7, 0.5, 1) + self.smoke.setBillboardPointEye() + return + + def stashCrushSurface(self, isStunned): + self.notify.debug('stashCrushSurface(%s)' % isStunned) + if self.crushSurface and not self.crushSurface.isEmpty(): + if isStunned: + self.crushSurface.stash() + else: + self.crushSurface.unstash() + + def unloadModel(self): + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + if self.smoke: + self.smoke.removeNode() + del self.smoke + self.smoke = None + if self.shadow: + self.shadow.removeNode() + del self.shadow + self.shadow = None + if self.model: + if isinstance(self.model, MovingPlatform.MovingPlatform): + self.model.destroy() + else: + self.model.removeNode() + del self.model + self.model = None + return + + def sendStompToon(self): + messenger.send(self.crushMsg) + + def doCrush(self): + self.notify.debug('doCrush, crushedList = %s' % self.crushedList) + for crushableId in self.crushedList: + crushable = self.level.entities.get(crushableId) + if crushable: + if self.style == 'vertical': + axis = 2 + else: + axis = 0 + crushable.playCrushMovie(self.entId, axis) + + self.crushedList = [] + + def getMotionIval(self, mode = STOMPER_START): + if self.range == 0.0: + return (None, 0) + wantSound = self.soundOn + if self.motion is MotionLinear: + motionIval = Sequence(LerpPosInterval(self.model, self.period / 2.0, Point3(0, -self.range, 0), startPos=Point3(0, 0, 0), fluid=1), WaitInterval(self.period / 4.0), LerpPosInterval(self.model, self.period / 4.0, Point3(0, 0, 0), startPos=Point3(0, -self.range, 0), fluid=1)) + elif self.motion is MotionSinus: + + def sinusFunc(t, self = self): + theta = math.pi + t * 2.0 * math.pi + c = math.cos(theta) + self.model.setFluidY((0.5 + c * 0.5) * -self.range) + + motionIval = Sequence(LerpFunctionInterval(sinusFunc, duration=self.period)) + elif self.motion is MotionSlowFast: + + def motionFunc(t, self = self): + stickTime = 0.2 + turnaround = 0.95 + t = t % 1 + if t < stickTime: + self.model.setFluidY(0) + elif t < turnaround: + self.model.setFluidY((t - stickTime) * -self.range / (turnaround - stickTime)) + elif t > turnaround: + self.model.setFluidY(-self.range + (t - turnaround) * self.range / (1 - turnaround)) + + motionIval = Sequence(LerpFunctionInterval(motionFunc, duration=self.period)) + elif self.motion is MotionCrush: + + def motionFunc(t, self = self): + stickTime = 0.2 + pauseAtTopTime = 0.5 + turnaround = 0.85 + t = t % 1 + if t < stickTime: + self.model.setFluidY(0) + elif t <= turnaround - pauseAtTopTime: + self.model.setFluidY((t - stickTime) * -self.range / (turnaround - pauseAtTopTime - stickTime)) + elif t > turnaround - pauseAtTopTime and t <= turnaround: + self.model.setFluidY(-self.range) + elif t > turnaround: + self.model.setFluidY(-self.range + (t - turnaround) * self.range / (1 - turnaround)) + + tStick = 0.2 * self.period + tUp = 0.45 * self.period + tPause = 0.2 * self.period + tDown = 0.15 * self.period + motionIval = Sequence(Wait(tStick), LerpPosInterval(self.model, tUp, Vec3(0, -self.range, 0), blendType='easeInOut', fluid=1), Wait(tPause), Func(self.doCrush), LerpPosInterval(self.model, tDown, Vec3(0, 0, 0), blendType='easeInOut', fluid=1)) + elif self.motion is MotionSwitched: + if mode == STOMPER_STOMP: + motionIval = Sequence(Func(self.doCrush), LerpPosInterval(self.model, 0.35, Vec3(0, 0, 0), blendType='easeInOut', fluid=1)) + elif mode == STOMPER_RISE: + motionIval = Sequence(LerpPosInterval(self.model, 0.5, Vec3(0, -self.range, 0), blendType='easeInOut', fluid=1)) + wantSound = 0 + else: + motionIval = None + else: + + def halfSinusFunc(t, self = self): + self.model.setFluidY(math.sin(t * math.pi) * -self.range) + + motionIval = Sequence(LerpFunctionInterval(halfSinusFunc, duration=self.period)) + return (motionIval, wantSound) + + def startStomper(self, startTime, mode = STOMPER_START): + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + motionIval, wantSound = self.getMotionIval(mode) + if motionIval == None: + return + self.ival = Parallel(Sequence(motionIval, Func(self.__startSmokeTask), Func(self.sendStompToon)), name=self.uniqueName('Stomper')) + if wantSound: + sndDur = motionIval.getDuration() + self.ival.append(Sequence(Wait(sndDur), Func(base.playSfx, self.sound, node=self.model, volume=0.45))) + if self.shadow is not None and self.animateShadow: + + def adjustShadowScale(t, self = self): + modelY = self.model.getY() + maxHeight = 10 + a = min(-modelY / maxHeight, 1.0) + self.shadow.setScale(lerp(1, 0.2, a)) + self.shadow.setAlphaScale(lerp(1, 0.2, a)) + + self.ival.append(LerpFunctionInterval(adjustShadowScale, duration=self.period)) + if mode == STOMPER_START: + self.ival.loop() + self.ival.setT(globalClock.getFrameTime() - self.level.startTime + self.period * self.phaseShift) + else: + self.ival.start(startTime) + return + + def stopStomper(self): + if self.ival: + self.ival.pause() + if self.smokeTrack: + self.smokeTrack.finish() + del self.smokeTrack + self.smokeTrack = None + return + + def setMovie(self, mode, timestamp, crushedList): + self.notify.debug('setMovie %d' % mode) + timestamp = ClockDelta.globalClockDelta.networkToLocalTime(timestamp) + now = globalClock.getFrameTime() + if mode == STOMPER_START or mode == STOMPER_RISE or mode == STOMPER_STOMP: + self.crushedList = crushedList + self.startStomper(timestamp, mode) + + def __startSmokeTask(self): + taskMgr.remove(self.taskName('smokeTask')) + if self.wantSmoke: + taskMgr.add(self.__smokeTask, self.taskName('smokeTask')) + + def __smokeTask(self, task): + self.smoke.reparentTo(self) + self.smoke.setScale(1) + if self.smokeTrack: + self.smokeTrack.finish() + del self.smokeTrack + self.smokeTrack = Sequence(Parallel(LerpScaleInterval(self.smoke, 0.2, Point3(4, 1, 4)), LerpColorScaleInterval(self.smoke, 1, Vec4(1, 1, 1, 0))), Func(self.smoke.reparentTo, hidden), Func(self.smoke.clearColorScale)) + self.smokeTrack.start() + return Task.done + + def checkSquashedToon(self): + if self.style == 'vertical': + tPos = base.localAvatar.getPos(self.rotateNode) + zRange = self.headScale[2] + xRange = self.headScale[0] + yRange = 5 + if tPos[2] < zRange and tPos[2] > -zRange and tPos[0] < xRange and tPos[0] > -xRange and tPos[1] < yRange / 10.0 and tPos[1] > -yRange: + self.level.b_setOuch(self.damage, 'Squish') + base.localAvatar.setZ(self.getZ(render) + 0.025) + + if __dev__: + + def attribChanged(self, *args): + self.stopStomper() + self.unloadModel() + self.loadModel() + self.startStomper(0) diff --git a/toontown/coghq/DistributedStomperAI.py b/toontown/coghq/DistributedStomperAI.py new file mode 100644 index 0000000..47f6f93 --- /dev/null +++ b/toontown/coghq/DistributedStomperAI.py @@ -0,0 +1,40 @@ +from otp.ai.AIBase import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +import DistributedCrusherEntityAI, StomperGlobals +from direct.distributed import ClockDelta + +class DistributedStomperAI(DistributedCrusherEntityAI.DistributedCrusherEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomperAI') + + def __init__(self, level, entId, pairId=-1): + DistributedCrusherEntityAI.DistributedCrusherEntityAI.__init__(self, level, entId) + self.pairId = pairId + + def generate(self): + DistributedCrusherEntityAI.DistributedCrusherEntityAI.generate(self) + if self.switchId != 0: + self.accept(self.getOutputEventName(self.switchId), self.reactToSwitch) + self.d_startStomper() + + def delete(self): + del self.pos + self.ignoreAll() + DistributedCrusherEntityAI.DistributedCrusherEntityAI.delete(self) + + def d_startStomper(self): + self.sendUpdate('setMovie', [StomperGlobals.STOMPER_START, ClockDelta.globalClockDelta.getRealNetworkTime(), []]) + + def reactToSwitch(self, on): + if on: + crushedList = [] + if self.crushCell: + self.crushCell.updateCrushables() + for id in self.crushCell.occupantIds: + if id in self.crushCell.crushables: + crushedList.append(id) + + self.sendCrushMsg() + self.sendUpdate('setMovie', [StomperGlobals.STOMPER_STOMP, ClockDelta.globalClockDelta.getRealNetworkTime(), crushedList]) + else: + self.sendUpdate('setMovie', [StomperGlobals.STOMPER_RISE, ClockDelta.globalClockDelta.getRealNetworkTime(), []]) diff --git a/toontown/coghq/DistributedStomperPair.py b/toontown/coghq/DistributedStomperPair.py new file mode 100644 index 0000000..84e0acc --- /dev/null +++ b/toontown/coghq/DistributedStomperPair.py @@ -0,0 +1,44 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import math +import StomperGlobals +from direct.directnotify import DirectNotifyGlobal +from otp.level import BasicEntities + +class DistributedStomperPair(BasicEntities.DistributedNodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomperPair') + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.children = None + return + + def delete(self): + BasicEntities.DistributedNodePathEntity.delete(self) + self.ignoreAll() + + def generateInit(self): + self.notify.debug('generateInit') + BasicEntities.DistributedNodePathEntity.generateInit(self) + + def generate(self): + self.notify.debug('generate') + BasicEntities.DistributedNodePathEntity.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.listenForChildren() + + def listenForChildren(self): + if self.stomperIds: + for entId in self.stomperIds: + self.accept(self.getUniqueName('crushMsg', entId), self.checkSquashedToon) + + def checkSquashedToon(self): + tPos = base.localAvatar.getPos(self) + print 'tpos = %s' % tPos + yRange = 3.0 + xRange = 3.0 + if tPos[1] < yRange and tPos[1] > -yRange and tPos[0] < xRange and tPos[0] > -xRange: + self.level.b_setOuch(3) diff --git a/toontown/coghq/DistributedStomperPairAI.py b/toontown/coghq/DistributedStomperPairAI.py new file mode 100644 index 0000000..5ea9204 --- /dev/null +++ b/toontown/coghq/DistributedStomperPairAI.py @@ -0,0 +1,32 @@ +from otp.ai.AIBase import * +from direct.directnotify import DirectNotifyGlobal +from otp.level import DistributedEntityAI +import StomperGlobals +from direct.distributed import ClockDelta + +class DistributedStomperPairAI(DistributedEntityAI.DistributedEntityAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStomperAI') + + def __init__(self, level, entId): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.stompers = [None, None] + self.hitPtsTaken = 3 + return + + def generate(self): + DistributedEntityAI.DistributedEntityAI.generate(self) + + def delete(self): + DistributedEntityAI.DistributedEntityAI.delete(self) + + def setChildren(self, doIds): + for id in doIds: + self.children = simbase.air.doId2do[id] + + self.sendUpdate('setChildren', [doIds]) + + def setSquash(self): + avId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(avId) + if av: + av.takeDamage(self.hitPtsTaken) diff --git a/toontown/coghq/DistributedSwitch.py b/toontown/coghq/DistributedSwitch.py new file mode 100644 index 0000000..5fd98bf --- /dev/null +++ b/toontown/coghq/DistributedSwitch.py @@ -0,0 +1,130 @@ +from pandac.PandaModules import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from otp.level import BasicEntities +import DistributedSwitchBase +import MovingPlatform +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from otp.level import DistributedEntity + +class DistributedSwitch(DistributedSwitchBase.DistributedSwitchBase, BasicEntities.DistributedNodePathEntity): + + def __init__(self, cr): + BasicEntities.DistributedNodePathEntity.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedSwitch', [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() + self.node = None + self.triggerName = '' + return + + def setup(self): + self.setupSwitch() + self.setState(self.initialState, self.initialStateTimestamp) + del self.initialState + del self.initialStateTimestamp + self.accept('exit%s' % (self.getName(),), self.exitTrigger) + self.acceptAvatar() + + def takedown(self): + pass + + setIsOnEvent = DistributedSwitchBase.stubFunction + setIsOn = DistributedSwitchBase.stubFunction + setSecondsOn = DistributedSwitchBase.stubFunction + + def generate(self): + BasicEntities.DistributedNodePathEntity.generate(self) + self.track = None + return + + def announceGenerate(self): + BasicEntities.DistributedNodePathEntity.announceGenerate(self) + self.setup() + + def disable(self): + self.ignoreAll() + self.fsm.request('off') + BasicEntities.DistributedNodePathEntity.disable(self) + self.takedown() + + def delete(self): + del self.fsm + BasicEntities.DistributedNodePathEntity.delete(self) + + def acceptAvatar(self): + self.acceptOnce('enter%s' % (self.getName(),), self.enterTrigger) + + def rejectInteract(self): + self.acceptAvatar() + + def avatarExit(self, avatarId): + self.acceptAvatar() + + def getName(self): + return 'switch-%s' % (self.entId,) + + def setupSwitch(self): + pass + + def switchOnTrack(self): + pass + + def switchOffTrack(self): + pass + + def setAvatarInteract(self, avatarId): + self.avatarId = avatarId + + 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): + self.sendUpdate('requestInteract') + + def exitTrigger(self, args = None): + self.sendUpdate('requestExit') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterAttract(self, ts): + track = self.switchOffTrack() + if track is not None: + track.start(ts) + self.track = track + return + + def exitAttract(self): + if self.track: + self.track.finish() + self.track = None + return + + def enterPlaying(self, ts): + track = self.switchOnTrack() + if track is not None: + track.start(ts) + self.track = track + return + + def exitPlaying(self): + if self.track: + self.track.finish() + self.track = None + return + + if __dev__: + + def attribChanged(self, attrib, value): + self.takedown() + self.setup() diff --git a/toontown/coghq/DistributedSwitchAI.py b/toontown/coghq/DistributedSwitchAI.py new file mode 100644 index 0000000..9b810e5 --- /dev/null +++ b/toontown/coghq/DistributedSwitchAI.py @@ -0,0 +1,124 @@ +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +import DistributedSwitchBase +from direct.task import Task +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.level import DistributedEntityAI + +class DistributedSwitchAI(DistributedSwitchBase.DistributedSwitchBase, DistributedEntityAI.DistributedEntityAI): + + def __init__(self, level, entId, zoneId=None): + DistributedEntityAI.DistributedEntityAI.__init__(self, level, entId) + self.fsm = ClassicFSM.ClassicFSM('DistributedSwitch', [ + 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.avatarId = 0 + self.doLaterTask = None + if zoneId is not None: + self.generateWithRequired(zoneId) + return + + def setup(self): + pass + + def takedown(self): + pass + + setScale = DistributedSwitchBase.stubFunction + + def delete(self): + if self.doLaterTask: + self.doLaterTask.remove() + self.doLaterTask = None + del self.fsm + DistributedEntityAI.DistributedEntityAI.delete(self) + return + + def getAvatarInteract(self): + return self.avatarId + + def getState(self): + r = [ + self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + return r + + def sendState(self): + self.sendUpdate('setState', self.getState()) + + def setIsOn(self, isOn): + if self.isOn != isOn: + self.isOn = isOn + stateName = self.fsm.getCurrentState().getName() + if isOn: + if stateName != 'playing': + self.fsm.request('playing') + else: + if stateName != 'attract': + self.fsm.request('attract') + messenger.send(self.getOutputEventName(), [isOn]) + + def getIsOn(self): + return self.isOn + + def getName(self): + return 'switch-%s' % (self.entId,) + + def switchOffTask(self, task): + self.setIsOn(0) + self.fsm.request('attract') + return Task.done + + 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 self.avatarId and avatarId == self.avatarId: + stateName = self.fsm.getCurrentState().getName() + if stateName == 'playing': + self.sendUpdate('avatarExit', [avatarId]) + self.avatarId = None + if self.isOn and self.secondsOn != -1.0 and self.secondsOn >= 0.0: + self.doLaterTask = taskMgr.doMethodLater(self.secondsOn, self.switchOffTask, self.uniqueName('switch-timer')) + return + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterAttract(self): + self.sendState() + + def exitAttract(self): + pass + + def enterPlaying(self): + self.sendState() + self.setIsOn(1) + + def exitPlaying(self): + if self.doLaterTask: + self.doLaterTask.remove() + self.doLaterTask = None + return + + if __dev__: + + def attribChanged(self, attrib, value): + self.takedown() + self.setup() diff --git a/toontown/coghq/DistributedSwitchBase.py b/toontown/coghq/DistributedSwitchBase.py new file mode 100644 index 0000000..bfee36e --- /dev/null +++ b/toontown/coghq/DistributedSwitchBase.py @@ -0,0 +1,8 @@ + + +def stubFunction(*args): + pass + + +class DistributedSwitchBase: + pass diff --git a/toontown/coghq/DistributedTrigger.py b/toontown/coghq/DistributedTrigger.py new file mode 100644 index 0000000..9bc0ae9 --- /dev/null +++ b/toontown/coghq/DistributedTrigger.py @@ -0,0 +1,41 @@ +from pandac.PandaModules import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +import MovingPlatform +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +import DistributedSwitch +from toontown.toonbase import TTLocalizer + +class DistributedTrigger(DistributedSwitch.DistributedSwitch): + + def setupSwitch(self): + radius = 1.0 + cSphere = CollisionSphere(0.0, 0.0, 0.0, radius) + cSphere.setTangible(0) + cSphereNode = CollisionNode(self.getName()) + cSphereNode.addSolid(cSphere) + self.cSphereNodePath = self.attachNewNode(cSphereNode) + cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.flattenMedium() + + def delete(self): + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + DistributedSwitch.DistributedSwitch.delete(self) + + def enterTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.enterTrigger(self, args) + self.setIsOn(1) + + def exitTrigger(self, args = None): + DistributedSwitch.DistributedSwitch.exitTrigger(self, args) + self.setIsOn(0) + + def getName(self): + if self.triggerName != '': + return self.triggerName + else: + return DistributedSwitch.DistributedSwitch.getName(self) diff --git a/toontown/coghq/DistributedTriggerAI.py b/toontown/coghq/DistributedTriggerAI.py new file mode 100644 index 0000000..a5d80cc --- /dev/null +++ b/toontown/coghq/DistributedTriggerAI.py @@ -0,0 +1,6 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +import DistributedSwitchAI + +class DistributedTriggerAI(DistributedSwitchAI.DistributedSwitchAI): + pass diff --git a/toontown/coghq/FactoryBase.py b/toontown/coghq/FactoryBase.py new file mode 100644 index 0000000..ae84085 --- /dev/null +++ b/toontown/coghq/FactoryBase.py @@ -0,0 +1,27 @@ +import FactorySpecs +from otp.level import LevelSpec +from toontown.toonbase import ToontownGlobals + +class FactoryBase: + + def __init__(self): + pass + + def setFactoryId(self, factoryId): + self.factoryId = factoryId + self.factoryType = ToontownGlobals.factoryId2factoryType[factoryId] + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(factoryId) + + def getCogTrack(self): + return self.cogTrack + + def getFactoryType(self): + return self.factoryType + + if __dev__: + + def getEntityTypeReg(self): + import FactoryEntityTypes + from otp.level import EntityTypeRegistry + typeReg = EntityTypeRegistry.EntityTypeRegistry(FactoryEntityTypes) + return typeReg diff --git a/toontown/coghq/FactoryCameraViews.py b/toontown/coghq/FactoryCameraViews.py new file mode 100644 index 0000000..ebcf09e --- /dev/null +++ b/toontown/coghq/FactoryCameraViews.py @@ -0,0 +1,68 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import Functor +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class FactoryCameraViews: + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryCameraViews') + + def __init__(self, factory): + self.factory = factory + av = base.localAvatar + self.currentCamPos = None + self.views = [['signatureRoomView', (Point3(0.0, -14.8419799805, 13.212685585), + Point3(0.0, -13.9563484192, 12.749215126), + Point3(0.0, 1.5, 15.75), + Point3(0.0, 1.5, -3.9375), + 1), ['localToonLeftBattle']], ['lookoutTrigger', (Point3(0, -17.7, 28.8), + Point3(0, 10, 0), + Point3(0.0, 1.5, 15.75), + Point3(0.0, 1.5, -3.9375), + 1), []], ['moleFieldView', (Point3(0, -17.7, 28.8), + Point3(0, 10, 0), + Point3(0.0, 1.5, 15.75), + Point3(0.0, 1.5, -3.9375), + 1), []]] + camHeight = av.getClampedAvatarHeight() + for i in range(len(self.views)): + camPos = self.views[i][1] + av.auxCameraPositions.append(camPos) + factory.accept('enter' + self.views[i][0], Functor(self.switchCamPos, i)) + for msg in self.views[i][2]: + factory.accept(msg, self.checkCamPos) + + return + + def delete(self): + for i in range(len(self.views)): + base.localAvatar.auxCameraPositions.remove(self.views[i][1]) + self.factory.ignore('enter' + self.views[i][0]) + self.factory.ignore('exit' + self.views[i][0]) + for msg in self.views[i][2]: + self.factory.ignore(msg) + + base.localAvatar.resetCameraPosition() + del self.views + + def switchCamPos(self, viewIndex, colEntry = None): + av = base.localAvatar + prevView = av.cameraIndex + self.currentCamPos = viewIndex + av.accept('exit' + self.views[viewIndex][0], Functor(self.prevCamPos, prevView)) + self.notify.info('auto-switching to camera position %s' % viewIndex) + av.setCameraSettings(self.views[viewIndex][1]) + + def prevCamPos(self, index, colEntry = None): + av = base.localAvatar + if len(av.cameraPositions) > index: + av.setCameraPositionByIndex(index) + self.currentCamPos = None + return + + def checkCamPos(self): + if self.currentCamPos != None: + av = base.localAvatar + viewIndex = self.currentCamPos + self.notify.info('returning to camera position %s' % viewIndex) + av.setCameraSettings(self.views[viewIndex][1]) + return diff --git a/toontown/coghq/FactoryEntityCreator.py b/toontown/coghq/FactoryEntityCreator.py new file mode 100644 index 0000000..6a0f10e --- /dev/null +++ b/toontown/coghq/FactoryEntityCreator.py @@ -0,0 +1,55 @@ +from otp.level import EntityCreator +import FactoryLevelMgr +import PlatformEntity +import ConveyorBelt +import GearEntity +import PaintMixer +import GoonClipPlane +import MintProduct +import MintProductPallet +import MintShelf +import PathMasterEntity +import RenderingEntity + +class FactoryEntityCreator(EntityCreator.EntityCreator): + + def __init__(self, level): + EntityCreator.EntityCreator.__init__(self, level) + nothing = EntityCreator.nothing + nonlocal = EntityCreator.nonlocal + self.privRegisterTypes({'activeCell': nonlocal, + 'crusherCell': nonlocal, + 'battleBlocker': nonlocal, + 'beanBarrel': nonlocal, + 'button': nonlocal, + 'conveyorBelt': ConveyorBelt.ConveyorBelt, + 'crate': nonlocal, + 'door': nonlocal, + 'directionalCell': nonlocal, + 'gagBarrel': nonlocal, + 'gear': GearEntity.GearEntity, + 'goon': nonlocal, + 'gridGoon': nonlocal, + 'golfGreenGame': nonlocal, + 'goonClipPlane': GoonClipPlane.GoonClipPlane, + 'grid': nonlocal, + 'healBarrel': nonlocal, + 'levelMgr': FactoryLevelMgr.FactoryLevelMgr, + 'lift': nonlocal, + 'mintProduct': MintProduct.MintProduct, + 'mintProductPallet': MintProductPallet.MintProductPallet, + 'mintShelf': MintShelf.MintShelf, + 'mover': nonlocal, + 'paintMixer': PaintMixer.PaintMixer, + 'pathMaster': PathMasterEntity.PathMasterEntity, + 'rendering': RenderingEntity.RenderingEntity, + 'platform': PlatformEntity.PlatformEntity, + 'sinkingPlatform': nonlocal, + 'stomper': nonlocal, + 'stomperPair': nonlocal, + 'laserField': nonlocal, + 'securityCamera': nonlocal, + 'elevatorMarker': nonlocal, + 'trigger': nonlocal, + 'moleField': nonlocal, + 'maze': nonlocal}) diff --git a/toontown/coghq/FactoryEntityCreatorAI.py b/toontown/coghq/FactoryEntityCreatorAI.py new file mode 100644 index 0000000..e8640ed --- /dev/null +++ b/toontown/coghq/FactoryEntityCreatorAI.py @@ -0,0 +1,17 @@ +from otp.level import EntityCreatorAI +from direct.showbase.PythonUtil import Functor +import DistributedBeanBarrelAI, DistributedButtonAI, DistributedCrateAI, DistributedLiftAI, DistributedDoorEntityAI, DistributedGagBarrelAI, DistributedGridAI +from toontown.suit import DistributedGridGoonAI +from toontown.suit import DistributedGoonAI +import DistributedHealBarrelAI, DistributedStomperPairAI, DistributedTriggerAI, DistributedStomperAI, DistributedLaserFieldAI, DistributedSecurityCameraAI, DistributedMoverAI, DistributedElevatorMarkerAI, DistributedSinkingPlatformAI, ActiveCellAI, CrusherCellAI, DirectionalCellAI, FactoryLevelMgrAI, BattleBlockerAI, DistributedGolfGreenGameAI +from toontown.coghq import DistributedMoleFieldAI +from toontown.coghq import DistributedMazeAI + +class FactoryEntityCreatorAI(EntityCreatorAI.EntityCreatorAI): + + def __init__(self, level): + EntityCreatorAI.EntityCreatorAI.__init__(self, level) + cDE = EntityCreatorAI.createDistributedEntity + cLE = EntityCreatorAI.createLocalEntity + nothing = EntityCreatorAI.nothing + self.privRegisterTypes({'activeCell': Functor(cDE, ActiveCellAI.ActiveCellAI), 'crusherCell': Functor(cDE, CrusherCellAI.CrusherCellAI), 'battleBlocker': Functor(cDE, BattleBlockerAI.BattleBlockerAI), 'beanBarrel': Functor(cDE, DistributedBeanBarrelAI.DistributedBeanBarrelAI), 'button': DistributedButtonAI.DistributedButtonAI, 'conveyorBelt': nothing, 'crate': Functor(cDE, DistributedCrateAI.DistributedCrateAI), 'directionalCell': Functor(cDE, DirectionalCellAI.DirectionalCellAI), 'door': DistributedDoorEntityAI.DistributedDoorEntityAI, 'gagBarrel': Functor(cDE, DistributedGagBarrelAI.DistributedGagBarrelAI), 'gear': nothing, 'goon': Functor(cDE, DistributedGoonAI.DistributedGoonAI), 'gridGoon': Functor(cDE, DistributedGridGoonAI.DistributedGridGoonAI), 'golfGreenGame': Functor(cDE, DistributedGolfGreenGameAI.DistributedGolfGreenGameAI), 'goonClipPlane': nothing, 'grid': Functor(cDE, DistributedGridAI.DistributedGridAI), 'healBarrel': Functor(cDE, DistributedHealBarrelAI.DistributedHealBarrelAI), 'levelMgr': Functor(cLE, FactoryLevelMgrAI.FactoryLevelMgrAI), 'lift': Functor(cDE, DistributedLiftAI.DistributedLiftAI), 'mintProduct': nothing, 'mintProductPallet': nothing, 'mintShelf': nothing, 'mover': Functor(cDE, DistributedMoverAI.DistributedMoverAI), 'paintMixer': nothing, 'pathMaster': nothing, 'rendering': nothing, 'platform': nothing, 'sinkingPlatform': Functor(cDE, DistributedSinkingPlatformAI.DistributedSinkingPlatformAI), 'stomper': Functor(cDE, DistributedStomperAI.DistributedStomperAI), 'stomperPair': Functor(cDE, DistributedStomperPairAI.DistributedStomperPairAI), 'laserField': Functor(cDE, DistributedLaserFieldAI.DistributedLaserFieldAI), 'securityCamera': Functor(cDE, DistributedSecurityCameraAI.DistributedSecurityCameraAI), 'elevatorMarker': Functor(cDE, DistributedElevatorMarkerAI.DistributedElevatorMarkerAI), 'trigger': DistributedTriggerAI.DistributedTriggerAI, 'moleField': Functor(cDE, DistributedMoleFieldAI.DistributedMoleFieldAI), 'maze': Functor(cDE, DistributedMazeAI.DistributedMazeAI)}) diff --git a/toontown/coghq/FactoryEntityTypes.py b/toontown/coghq/FactoryEntityTypes.py new file mode 100644 index 0000000..87494f9 --- /dev/null +++ b/toontown/coghq/FactoryEntityTypes.py @@ -0,0 +1,635 @@ +from otp.level.EntityTypes import * + +class FactoryLevelMgr(LevelMgr): + type = 'levelMgr' + attribs = (('cogLevel', + 0, + 'int', + {'min': 0, + 'max': 11}), ('wantDoors', 1, 'bool'), ('farPlaneDistance', 1500, 'float')) + + +class BarrelBase(Nodepath): + abstract = 1 + attribs = (('rewardPerGrab', 5, 'int'), ('rewardPerGrabMax', 0, 'int')) + + +class BeanBarrel(BarrelBase): + type = 'beanBarrel' + + +class GagBarrel(BarrelBase): + type = 'gagBarrel' + attribs = (('gagLevel', + 0, + 'int', + {'min': 0, + 'max': 5}), ('gagLevelMax', + 0, + 'int', + {'min': 0, + 'max': 5}), ('gagTrack', + 0, + 'choice', + {'choiceSet': ('heal', 'trap', 'lure', 'sound', 'throw', 'squirt', 'drop', 'random'), + 'valueDict': {'heal': 0, + 'trap': 1, + 'lure': 2, + 'sound': 3, + 'throw': 4, + 'squirt': 5, + 'drop': 6, + 'random': 'random'}})) + + +class HealBarrel(BarrelBase): + type = 'healBarrel' + + +class Switch(Nodepath): + abstract = 1 + output = 'bool' + attribs = (('isOnEvent', + 0, + 'entId', + {'output': 'bool'}), ('isOn', 0, 'bool'), ('secondsOn', 1, 'float')) + + +class Button(Switch): + type = 'button' + attribs = (('scale', Vec3(3), 'scale'), ('color', Vec4(1, 1, 1, 1), 'color')) + + +class Trigger(Switch): + type = 'trigger' + attribs = (('scale', Vec3(10), 'scale'), ('triggerName', '')) + + +class ConveyorBelt(Nodepath): + type = 'conveyorBelt' + attribs = (('speed', 1.0, 'float'), + ('length', 1.0, 'float'), + ('widthScale', 1.0, 'float'), + ('treadLength', 10.0, 'float'), + ('treadModelPath', 'phase_9/models/cogHQ/platform1', 'bamfilename'), + ('floorName', 'platformcollision')) + + +class Door(Nodepath): + type = 'door' + output = 'bool' + attribs = (('color', Vec4(1, 1, 1, 1), 'color'), + ('isVisBlocker', 1, 'bool'), + ('unlock0Event', + 0, + 'entId', + {'output': 'bool'}), + ('unlock1Event', + 0, + 'entId', + {'output': 'bool'}), + ('unlock2Event', + 0, + 'entId', + {'output': 'bool'}), + ('unlock3Event', + 0, + 'entId', + {'output': 'bool'}), + ('isOpenEvent', + 0, + 'entId', + {'output': 'bool'}), + ('isLock0Unlocked', 1, 'bool'), + ('isLock1Unlocked', 1, 'bool'), + ('isLock2Unlocked', 1, 'bool'), + ('isLock3Unlocked', 1, 'bool'), + ('isOpen', 0, 'bool'), + ('secondsOpen', 1, 'float')) + + +class Grid(Nodepath): + type = 'grid' + blockAttribs = ('hpr',) + attribs = (('cellSize', 3, 'float'), ('numCol', 3, 'int'), ('numRow', 3, 'int')) + + +class Crushable(Entity): + abstract = 1 + attribs = (('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('crushCellId', + None, + 'entId', + {'type': 'crusherCell'}), + ('gridId', + None, + 'entId', + {'type': 'grid'})) + + +class Crusher(Nodepath): + abstract = 1 + attribs = (('crushCellId', + None, + 'entId', + {'type': 'crusherCell'}),) + + +class Crate(Crushable): + type = 'crate' + blockAttribs = ('hpr',) + attribs = (('modelType', + 0, + 'int', + {'min': 0, + 'max': 1}), ('scale', 0.92, 'float'), ('pushable', 1, 'bool')) + + +class Goon(Crushable): + type = 'goon' + attribs = (('goonType', + 'pg', + 'choice', + {'choiceSet': ['pg', 'sg']}), + ('strength', + 5, + 'int', + {'min': 0, + 'max': 105}), + ('velocity', + 4, + 'float', + {'min': 0, + 'max': 10}), + ('attackRadius', + 15, + 'float', + {'min': 1, + 'max': 20}), + ('scale', 1.5, 'float'), + ('hFov', + 70, + 'float', + {'min': 0, + 'max': 179})) + + +class GridGoon(Goon): + type = 'gridGoon' + attribs = () + + +class GoonClipPlane(Nodepath): + type = 'goonClipPlane' + attribs = (('goonId', + None, + 'entId', + {'type': 'goon'}),) + + +class ActiveCell(Nodepath): + type = 'activeCell' + attribs = (('row', 0, 'int'), ('col', 0, 'int'), ('gridId', + None, + 'entId', + {'type': 'grid'})) + + +class CrusherCell(ActiveCell): + type = 'crusherCell' + attribs = () + + +class DirectionalCell(ActiveCell): + type = 'directionalCell' + attribs = (('dir', + [0, 0], + 'choice', + {'choiceSet': ['l', + 'r', + 'up', + 'dn'], + 'valueDict': {'l': [-1, 0], + 'r': [1, 0], + 'up': [0, 1], + 'dn': [0, -1]}}),) + + +class GolfGreenGame(Nodepath): + type = 'golfGreenGame' + output = 'bool' + attribs = (('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('cellId', 0, 'int'), + ('switchId', + 0, + 'entId', + {'type': 'button'}), + ('timeToPlay', 120, 'int'), + ('puzzleBase', 4, 'int'), + ('puzzlePerPlayer', 1, 'int')) + + +class LaserField(Nodepath): + type = 'laserField' + output = 'bool' + attribs = (('laserFactor', 3, 'float'), + ('gridScaleX', 32.0, 'float'), + ('gridScaleY', 32.0, 'float'), + ('projector', Point3(6, 6, 25), 'pos'), + ('modelPath', + 0, + 'choice', + {'choiceSet': ['square'], + 'valueDict': {'square': 0}}), + ('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('cellId', 0, 'int'), + ('switchId', + 0, + 'entId', + {'type': 'button'}), + ('gridGame', + 'Random', + 'choice', + {'choiceSet': ['MineSweeper', + 'Roll', + 'Avoid', + 'Random']})) + + +class SecurityCamera(Nodepath): + type = 'securityCamera' + attribs = (('damPow', 3, 'int'), + ('radius', 5, 'float'), + ('accel', 1, 'float'), + ('maxVel', 5, 'float'), + ('projector', Point3(6, 6, 25), 'pos'), + ('modelPath', + 0, + 'choice', + {'choiceSet': ['square'], + 'valueDict': {'square': 0}}), + ('hideModel', 0, 'bool'), + ('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('switchId', + 0, + 'entId', + {'type': 'button'}), + ('trackTarget1', + 0, + 'entId', + {'type': 'button'}), + ('trackTarget2', + 0, + 'entId', + {'type': 'button'}), + ('trackTarget3', + 0, + 'entId', + {'type': 'button'})) + + +class ElevatorMarker(Nodepath): + type = 'elevatorMarker' + attribs = (('modelPath', + 0, + 'choice', + {'choiceSet': ['square'], + 'valueDict': {'square': 0}}), ('pos', Point3(0, 0, 0), 'pos'), ('hpr', Vec3(0, 0, 0), 'hpr')) + + +class Lift(Nodepath): + type = 'lift' + attribs = (('duration', 1, 'float'), + ('startPos', Point3(0, 0, 0), 'pos'), + ('endPos', Point3(0, 0, 0), 'pos'), + ('modelPath', 'phase_9/models/cogHQ/Elevator.bam', 'bamfilename'), + ('floorName', 'elevator_floor', 'string'), + ('modelScale', Vec3(1), 'scale'), + ('startGuardName', '', 'string'), + ('endGuardName', '', 'string'), + ('startBoardSides', ['front', + 'back', + 'left', + 'right']), + ('endBoardSides', ['front', + 'back', + 'left', + 'right']), + ('moveDelay', + 1, + 'float', + {'min': 0}), + ('autoMoveDelay', + 5, + 'float', + {'min': 0})) + + +class Mover(Nodepath): + type = 'mover' + attribs = (('modelPath', + 0, + 'choice', + {'choiceSet': ['square'], + 'valueDict': {'square': 0}}), + ('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('switchId', + 0, + 'entId', + {'type': 'button'}), + ('entity2Move', + 0, + 'entId', + {'type': 'button'}), + ('moveTarget', + 0, + 'entId', + {'type': 'button'}), + ('pos0Move', 2, 'float'), + ('pos0Wait', 2, 'float'), + ('pos1Move', 2, 'float'), + ('pos1Wait', 2, 'float'), + ('startOn', 0, 'bool'), + ('cycleType', + 'return', + 'choice', + {'choiceSet': ['return', + 'linear', + 'loop', + 'oneWay']})) + + +class Platform(Nodepath): + type = 'platform' + attribs = (('modelPath', 'phase_9/models/cogHQ/platform1', 'bamfilename'), + ('modelScale', Vec3(1, 1, 1), 'scale'), + ('floorName', 'platformcollision', 'string'), + ('offset', Point3(0, 0, 0), 'pos'), + ('period', 2, 'float'), + ('waitPercent', + 0.1, + 'float', + {'min': 0, + 'max': 1}), + ('phaseShift', + 0.0, + 'float', + {'min': 0, + 'max': 1}), + ('motion', + 'noBlend', + 'choice', + {'choiceSet': ['noBlend', + 'easeInOut', + 'easeIn', + 'easeOut']})) + + +class SinkingPlatform(Nodepath): + type = 'sinkingPlatform' + attribs = (('verticalRange', 1, 'float'), + ('sinkDuration', 1, 'float'), + ('pauseBeforeRise', 1, 'float'), + ('riseDuration', 1, 'float')) + + +class Stomper(Crusher): + type = 'stomper' + attribs = (('damage', 3, 'int'), + ('style', + 'vertical', + 'choice', + {'choiceSet': ['horizontal', 'vertical']}), + ('period', 2.0, 'float'), + ('phaseShift', + 0.0, + 'float', + {'min': 0, + 'max': 1}), + ('range', 6, 'float'), + ('motion', + 3, + 'choice', + {'choiceSet': ['linear', + 'sinus', + 'half sinus', + 'slow fast', + 'crush', + 'switched'], + 'valueDict': {'linear': 0, + 'sinus': 1, + 'half sinus': 2, + 'slow fast': 3, + 'crush': 4, + 'switched': 5}}), + ('headScale', Vec3(1, 1, 1), 'scale'), + ('shaftScale', Vec3(1, 1, 1), 'scale'), + ('wantSmoke', 1, 'bool'), + ('wantShadow', 1, 'bool'), + ('animateShadow', 1, 'bool'), + ('soundOn', 0, 'bool'), + ('soundPath', + 0, + 'choice', + {'choiceSet': ['small', 'medium', 'large'], + 'valueDict': {'small': 0, + 'medium': 1, + 'large': 2}}), + ('soundLen', 0, 'float'), + ('zOffset', 0, 'float'), + ('switchId', + 0, + 'entId', + {'type': 'button'}), + ('modelPath', + 0, + 'choice', + {'choiceSet': ['square'], + 'valueDict': {'square': 0}}), + ('cogStyle', + 0, + 'choice', + {'choiceSet': ['default', 'lawbot'], + 'valueDict': {'default': 0, + 'lawbot': 1}}), + ('removeHeadFloor', 0, 'bool'), + ('removeCamBarrierCollisions', 0, 'bool')) + + +class StomperPair(Nodepath): + type = 'stomperPair' + attribs = (('headScale', Vec3(1, 1, 1), 'scale'), + ('motion', + 3, + 'choice', + {'choiceSet': ['linear', + 'sinus', + 'half sinus', + 'slow fast', + 'crush', + 'switched'], + 'valueDict': {'linear': 0, + 'sinus': 1, + 'half sinus': 2, + 'slow fast': 3, + 'crush': 4, + 'switched': 5}}), + ('period', 2.0, 'float'), + ('phaseShift', + 0.0, + 'float', + {'min': 0, + 'max': 1}), + ('range', 6, 'float'), + ('shaftScale', Vec3(1, 1, 1), 'scale'), + ('soundLen', 0, 'float'), + ('soundOn', 0, 'bool'), + ('stomperIds', + [], + 'entId', + {'type': 'stomper', + 'num': 2}), + ('style', + 'horizontal', + 'choice', + {'choiceSet': ['horizontal', 'vertical']})) + + +class Gear(Nodepath): + type = 'gear' + attribs = (('modelType', + 'factory', + 'choice', + {'choiceSet': ['factory', 'mint']}), + ('gearScale', 1, 'float'), + ('orientation', + 'horizontal', + 'choice', + {'choiceSet': ['horizontal', 'vertical']}), + ('degreesPerSec', 0, 'float'), + ('phaseShift', + 0, + 'float', + {'min': 0, + 'max': 1})) + + +class BattleBlocker(Nodepath): + type = 'battleBlocker' + attribs = (('radius', 10, 'float'), ('cellId', 0, 'int')) + + +class PaintMixer(Platform): + type = 'paintMixer' + attribs = (('modelPath', 'phase_9/models/cogHQ/PaintMixer', 'const'), ('floorName', 'PaintMixerFloorCollision', 'const'), ('shaftScale', 1, 'float')) + + +class MintProduct(Nodepath): + type = 'mintProduct' + attribs = (('mintId', + 12500, + 'choice', + {'choiceSet': ('coin', 'dollar', 'bullion'), + 'valueDict': {'coin': 12500, + 'dollar': 12600, + 'bullion': 12700}}),) + + +class MintProductPallet(Nodepath): + type = 'mintProductPallet' + attribs = (('mintId', + 12500, + 'choice', + {'choiceSet': ('coin', 'dollar', 'bullion'), + 'valueDict': {'coin': 12500, + 'dollar': 12600, + 'bullion': 12700}}),) + + +class MintShelf(Nodepath): + type = 'mintShelf' + attribs = (('mintId', + 12500, + 'choice', + {'choiceSet': ('coin', 'dollar', 'bullion'), + 'valueDict': {'coin': 12500, + 'dollar': 12600, + 'bullion': 12700}}),) + + +class PathMaster(Nodepath): + type = 'pathMaster' + attribs = (('pathIndex', 0, 'int'), + ('pathScale', 1.0, 'float'), + ('pathTarget0', + 0, + 'entId', + {'type': 'button'}), + ('pathTarget1', + 0, + 'entId', + {'type': 'button'}), + ('pathTarget2', + 0, + 'entId', + {'type': 'button'}), + ('pathTarget3', + 0, + 'entId', + {'type': 'button'}), + ('pathTarget4', + 0, + 'entId', + {'type': 'button'}), + ('pathTarget5', + 0, + 'entId', + {'type': 'button'}), + ('pathTarget6', + 0, + 'entId', + {'type': 'button'}), + ('pathTarget7', + 0, + 'entId', + {'type': 'button'})) + + +class Rendering(Nodepath): + type = 'rendering' + attribs = (('pos', Point3(0, 0, 0), 'pos'), + ('hpr', Vec3(0, 0, 0), 'hpr'), + ('colorR', 1.0, 'float'), + ('colorG', 1.0, 'float'), + ('colorB', 1.0, 'float'), + ('colorA', 1.0, 'float'), + ('blending', + 'Normal', + 'choice', + {'choiceSet': ['Normal', 'Additive', 'Alpha']}), + ('fogOn', 0, 'bool'), + ('renderBin', + 'default', + 'choice', + {'choiceSet': ['default', 'fixed', 'transparent']})) + + +class MoleField(Nodepath): + type = 'moleField' + attribs = (('numSquaresX', 5, 'int'), + ('numSquaresY', 5, 'int'), + ('spacingX', 5.0, 'float'), + ('spacingY', 5.0, 'float'), + ('timeToPlay', 60, 'int'), + ('molesBase', 4, 'int'), + ('molesPerPlayer', 1, 'int')) + + +class Maze(Nodepath): + type = 'maze' + attribs = (('numSections', 4, 'int'),) diff --git a/toontown/coghq/FactoryExterior.py b/toontown/coghq/FactoryExterior.py new file mode 100644 index 0000000..6fd9886 --- /dev/null +++ b/toontown/coghq/FactoryExterior.py @@ -0,0 +1,154 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.building import Elevator +from pandac.PandaModules import * + +class FactoryExterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryExterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.elevatorDoneEvent = 'elevatorDone' + + def load(self): + self.fsm = ClassicFSM.ClassicFSM('FactoryExterior', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'teleportIn', + 'doorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['stickerBook', + 'teleportOut', + 'tunnelOut', + 'DFA', + 'doorOut', + 'elevator', + 'stopped', + 'WaitForBattle', + 'battle']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'elevator']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'DFA', + 'WaitForBattle', + 'battle', + 'elevator']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut', 'tunnelOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'final', 'WaitForBattle']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk']), + State.State('died', self.enterDied, self.exitDied, ['quietZone']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk', 'stopped']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.parentFSM.getStateNamed('factoryExterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + + def unload(self): + self.parentFSM.getStateNamed('factoryExterior').removeChild(self.fsm) + del self.fsm + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.zoneId = requestStatus['zoneId'] + BattlePlace.BattlePlace.enter(self) + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + self.nodeList = [self.loader.geom] + self.loader.hood.startSky() + self._telemLimiter = TLGatherAllAvs('FactoryExterior', RotationLimitToH) + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + NametagGlobals.setMasterArrowsOn(1) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.nodeList, self.zoneId) + how = requestStatus['how'] + self.fsm.request(how, [requestStatus]) + + def exit(self): + self._telemLimiter.destroy() + del self._telemLimiter + self.loader.hood.stopSky() + self.fsm.requestFinalState() + self.loader.music.stop() + for node in self.tunnelOriginList: + node.removeNode() + + del self.tunnelOriginList + del self.nodeList + self.ignoreAll() + BattlePlace.BattlePlace.exit(self) + + def enterTunnelOut(self, requestStatus): + fromZoneId = self.zoneId - self.zoneId % 100 + tunnelName = base.cr.hoodMgr.makeLinkTunnelName(self.loader.hood.id, fromZoneId) + requestStatus['tunnelName'] = tunnelName + BattlePlace.BattlePlace.enterTunnelOut(self, requestStatus) + + def enterTeleportIn(self, requestStatus): + base.localAvatar.setPosHpr(-34, -350, 0, -28, 0, 0) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def enterTeleportOut(self, requestStatus): + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.hoodId 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 exitTeleportOut(self): + BattlePlace.BattlePlace.exitTeleportOut(self) + + 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 + distElevator.elevatorFSM = self.elevator + 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 == 'factoryInterior': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + elif where == 'stageInterior': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') diff --git a/toontown/coghq/FactoryInterior.py b/toontown/coghq/FactoryInterior.py new file mode 100644 index 0000000..6b5a7f4 --- /dev/null +++ b/toontown/coghq/FactoryInterior.py @@ -0,0 +1,276 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.building import Elevator + +class FactoryInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = ToontownGlobals.SellbotFactoryInt + self.elevatorDoneEvent = 'elevatorDone' + + def load(self): + self.fsm = ClassicFSM.ClassicFSM('FactoryInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'DFA', + 'fallDown', + 'elevator']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'DFA', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walkteleportOut']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.parentFSM.getStateNamed('factoryInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + self.music = base.loadMusic('phase_9/audio/bgm/CHQ_FACT_bg.mid') + + def unload(self): + self.parentFSM.getStateNamed('factoryInterior').removeChild(self.fsm) + del self.fsm + del self.music + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + base.localAvatar.inventory.setRespectInvasions(0) + self._telemLimiter = TLGatherAllAvs('FactoryInterior', RotationLimitToH) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + + if hasattr(base, 'factoryReady'): + commence() + else: + self.acceptOnce('FactoryReady', commence) + self.factoryDefeated = 0 + self.acceptOnce('FactoryWinEvent', self.handleFactoryWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send('FactoryWinEvent')) + self.confrontedForeman = 0 + + def handleConfrontedForeman(self = self): + self.confrontedForeman = 1 + + self.acceptOnce('localToonConfrontedForeman', handleConfrontedForeman) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + self._telemLimiter.destroy() + del self._telemLimiter + if hasattr(base, 'factoryReady'): + del base.factoryReady + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + FactoryInterior.notify.info('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + FactoryInterior.notify.info('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + FactoryInterior.notify.info('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getFactoryCreditMultiplier(self.zoneId) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + FactoryInterior.notify.info('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + FactoryInterior.notify.info('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + FactoryInterior.notify.info('__teleportOutDone()') + messenger.send('leavingFactory') + messenger.send('localToonLeft') + if self.factoryDefeated and not self.confrontedForeman: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + FactoryInterior.notify.info('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + + 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 == 'factoryInterior' or where == 'suitInterior': + self.doneStatus = doneStatus + self.doneEvent = 'lawOfficeFloorDone' + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def handleFactoryWinEvent(self): + FactoryInterior.notify.info('handleFactoryWinEvent') + + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + + self.factoryDefeated = 1 + + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + + self.fsm.request('teleportOut', [{ + 'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + }]) + + def enterDied(self, requestStatus, callback = None): + FactoryInterior.notify.info('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingFactory') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + FactoryInterior.notify.info('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveFactoryAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + FactoryInterior.notify.info('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog diff --git a/toontown/coghq/FactoryLevelMgr.py b/toontown/coghq/FactoryLevelMgr.py new file mode 100644 index 0000000..39c37ee --- /dev/null +++ b/toontown/coghq/FactoryLevelMgr.py @@ -0,0 +1,42 @@ +from otp.level import LevelMgr +import FactoryUtil +from direct.showbase.PythonUtil import Functor +from toontown.toonbase import ToontownGlobals + +class FactoryLevelMgr(LevelMgr.LevelMgr): + InterestingLocations = [(((-866, -272, -40), -101), + ((-662, -242, 7.5), 0), + ((-20, -180, 20), 0), + ((-249, 258, 111), 0), + ((318, 241, 115), -16), + ((-251, 241, 109), -180), + ((296, 292, 703), 56), + ((-740, 122, 28), 90), + ((210, -270, 38), -90)), (((20, 21, 0), 0), ((3, 404, 39), -16), ((-496, 358, 5), 0))] + + def __init__(self, level, entId): + LevelMgr.LevelMgr.__init__(self, level, entId) + if base.config.GetBool('want-factory-lifter', 0): + self.toonLifter = FactoryUtil.ToonLifter('f3') + self.callSetters('farPlaneDistance') + self.geom.reparentTo(render) + oilRoomOil = self.geom.find('**/oilroom/room/geometry_oilroom/*oil') + oilRoomFloor = self.geom.find('**/oilroom/room/geometry_oilroom/*platform') + if oilRoomOil and not oilRoomOil.isEmpty() and oilRoomFloor and not oilRoomFloor.isEmpty(): + oilRoomOil.setBin('background', 10) + oilRoomFloor.setBin('background', 11) + + def destroy(self): + if hasattr(self, 'toonLifter'): + self.toonLifter.destroy() + del self.toonLifter + LevelMgr.LevelMgr.destroy(self) + + def setFarPlaneDistance(self, farPlaneDistance): + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, farPlaneDistance) + + if __dev__: + + def setWantDoors(self, wantDoors): + self.wantDoors = wantDoors + messenger.send('wantDoorsChanged') diff --git a/toontown/coghq/FactoryLevelMgrAI.py b/toontown/coghq/FactoryLevelMgrAI.py new file mode 100644 index 0000000..04eebfa --- /dev/null +++ b/toontown/coghq/FactoryLevelMgrAI.py @@ -0,0 +1,10 @@ +from otp.level import LevelMgrAI + +class FactoryLevelMgrAI(LevelMgrAI.LevelMgrAI): + + def __init__(self, level, entId): + LevelMgrAI.LevelMgrAI.__init__(self, level, entId) + self.callSettersAndDelete('cogLevel') + + def setCogLevel(self, cogLevel): + self.level.cogLevel = cogLevel diff --git a/toontown/coghq/FactoryManagerAI.py b/toontown/coghq/FactoryManagerAI.py new file mode 100644 index 0000000..1b5cc81 --- /dev/null +++ b/toontown/coghq/FactoryManagerAI.py @@ -0,0 +1,23 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedFactoryAI +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject + +class FactoryManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('FactoryManagerAI') + factoryId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createFactory(self, factoryId, entranceId, players): + factoryZone = self.air.allocateZone() + if FactoryManagerAI.factoryId is not None: + factoryId = FactoryManagerAI.factoryId + factory = DistributedFactoryAI.DistributedFactoryAI(self.air, factoryId, factoryZone, entranceId, players) + factory.generateWithRequired(factoryZone) + return factoryZone diff --git a/toontown/coghq/FactoryMockupCogs.py b/toontown/coghq/FactoryMockupCogs.py new file mode 100644 index 0000000..78bd47e --- /dev/null +++ b/toontown/coghq/FactoryMockupCogs.py @@ -0,0 +1,4 @@ +from SpecImports import * +BattleCells = {} +CogData = [] +ReserveCogData = [] diff --git a/toontown/coghq/FactoryMockupSpec.py b/toontown/coghq/FactoryMockupSpec.py new file mode 100644 index 0000000..7312425 --- /dev/null +++ b/toontown/coghq/FactoryMockupSpec.py @@ -0,0 +1,9135 @@ +from SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'cogTrack': 'c', + 'factoryType': 'fullSuit', + 'modelFilename': 'phase_9/models/cogHQ/SelbotLegFactory', + 'ouchMultiplier': 1, + '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': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 3: {'type': 'zone', + 'name': 'zone3', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 4: {'type': 'zone', + 'name': 'zone4', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 5: {'type': 'zone', + 'name': 'zone5', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 6: {'type': 'zone', + 'name': 'zone6', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 7: {'type': 'zone', + 'name': 'zone7', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 8: {'type': 'zone', + 'name': 'zone8', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 9: {'type': 'zone', + 'name': 'zone9', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 10: {'type': 'zone', + 'name': 'zone10', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 11: {'type': 'zone', + 'name': 'zone11', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 12: {'type': 'zone', + 'name': 'zone12', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 13: {'type': 'zone', + 'name': 'zone13', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 14: {'type': 'zone', + 'name': 'zone14', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 15: {'type': 'zone', + 'name': 'zone15', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 16: {'type': 'zone', + 'name': 'zone16', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 17: {'type': 'zone', + 'name': 'zone17', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 18: {'type': 'zone', + 'name': 'zone18', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 19: {'type': 'zone', + 'name': 'zone19', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 20: {'type': 'zone', + 'name': 'zone20', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 21: {'type': 'zone', + 'name': 'zone21', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 22: {'type': 'zone', + 'name': 'zone22', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 23: {'type': 'zone', + 'name': 'zone23', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 24: {'type': 'zone', + 'name': 'zone24', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 25: {'type': 'zone', + 'name': 'zone25', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 26: {'type': 'zone', + 'name': 'zone26', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 27: {'type': 'zone', + 'name': 'zone27', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 30: {'type': 'zone', + 'name': 'zone30', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 31: {'type': 'zone', + 'name': 'zone31', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 32: {'type': 'zone', + 'name': 'zone32', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 33: {'type': 'zone', + 'name': 'zone33', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 34: {'type': 'zone', + 'name': 'zone34', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 35: {'type': 'zone', + 'name': 'zone35', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 36: {'type': 'zone', + 'name': 'zone36', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 37: {'type': 'zone', + 'name': 'zone37', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 38: {'type': 'zone', + 'name': 'zone38', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 40: {'type': 'zone', + 'name': 'zone40', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 41: {'type': 'zone', + 'name': 'zone41', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 53: {'type': 'zone', + 'name': 'zone53', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 60: {'type': 'zone', + 'name': 'zone60', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 61: {'type': 'zone', + 'name': 'zone61', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 101: {'type': 'zone', + 'name': 'zone101', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 102: {'type': 'zone', + 'name': 'zone102', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 103: {'type': 'zone', + 'name': 'zone103', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 105: {'type': 'zone', + 'name': 'zone105', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 106: {'type': 'zone', + 'name': 'zone106', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 107: {'type': 'zone', + 'name': 'zone107', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 108: {'type': 'zone', + 'name': 'zone108', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 109: {'type': 'zone', + 'name': 'zone109', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 110: {'type': 'zone', + 'name': 'zone110', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 111: {'type': 'zone', + 'name': 'zone111', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 112: {'type': 'zone', + 'name': 'zone112', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 113: {'type': 'zone', + 'name': 'zone113', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 114: {'type': 'zone', + 'name': 'zone114', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 115: {'type': 'zone', + 'name': 'zone115', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 116: {'type': 'zone', + 'name': 'zone116', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 117: {'type': 'zone', + 'name': 'zone117', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 118: {'type': 'zone', + 'name': 'zone118', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 119: {'type': 'zone', + 'name': 'zone119', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 120: {'type': 'zone', + 'name': 'zone120', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 121: {'type': 'zone', + 'name': 'zone121', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 122: {'type': 'zone', + 'name': 'zone122', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 123: {'type': 'zone', + 'name': 'zone123', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 124: {'type': 'zone', + 'name': 'zone124', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 125: {'type': 'zone', + 'name': 'zone125', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 126: {'type': 'zone', + 'name': 'zone126', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 127: {'type': 'zone', + 'name': 'zone127', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 128: {'type': 'zone', + 'name': 'zone128', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 129: {'type': 'zone', + 'name': 'zone129', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 130: {'type': 'zone', + 'name': 'zone130', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 131: {'type': 'zone', + 'name': 'zone131', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 200: {'type': 'zone', + 'name': 'zone200', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 201: {'type': 'zone', + 'name': 'zone201', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 202: {'type': 'zone', + 'name': 'zone202', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 203: {'type': 'zone', + 'name': 'zone203', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 204: {'type': 'zone', + 'name': 'zone204', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 205: {'type': 'zone', + 'name': 'zone205', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 206: {'type': 'zone', + 'name': 'zone206', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 207: {'type': 'zone', + 'name': 'zone207', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 208: {'type': 'zone', + 'name': 'zone208', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 209: {'type': 'zone', + 'name': 'zone209', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 210: {'type': 'zone', + 'name': 'zone210', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 211: {'type': 'zone', + 'name': 'zone211', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 212: {'type': 'zone', + 'name': 'zone212', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 213: {'type': 'zone', + 'name': 'zone213', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 214: {'type': 'zone', + 'name': 'zone214', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 215: {'type': 'zone', + 'name': 'zone215', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 216: {'type': 'zone', + 'name': 'zone216', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 217: {'type': 'zone', + 'name': 'zone217', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 218: {'type': 'zone', + 'name': 'zone218', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 219: {'type': 'zone', + 'name': 'zone219', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 220: {'type': 'zone', + 'name': 'zone220', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 221: {'type': 'zone', + 'name': 'zone221', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}, + 222: {'type': 'zone', + 'name': 'zone222', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 101, + 102, + 103, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222]}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/FactorySpecs.py b/toontown/coghq/FactorySpecs.py new file mode 100644 index 0000000..f61a504 --- /dev/null +++ b/toontown/coghq/FactorySpecs.py @@ -0,0 +1,23 @@ +from toontown.toonbase import ToontownGlobals +import SellbotLegFactorySpec +import SellbotLegFactoryCogs +import LawbotLegFactorySpec +import LawbotLegFactoryCogs + +def getFactorySpecModule(factoryId): + return FactorySpecModules[factoryId] + + +def getCogSpecModule(factoryId): + return CogSpecModules[factoryId] + + +FactorySpecModules = {ToontownGlobals.SellbotFactoryInt: SellbotLegFactorySpec, + ToontownGlobals.LawbotOfficeInt: LawbotLegFactorySpec} +CogSpecModules = {ToontownGlobals.SellbotFactoryInt: SellbotLegFactoryCogs, + ToontownGlobals.LawbotOfficeInt: LawbotLegFactoryCogs} +if __dev__: + import FactoryMockupSpec + FactorySpecModules[ToontownGlobals.MockupFactoryId] = FactoryMockupSpec + import FactoryMockupCogs + CogSpecModules[ToontownGlobals.MockupFactoryId] = FactoryMockupCogs diff --git a/toontown/coghq/FactoryUtil.py b/toontown/coghq/FactoryUtil.py new file mode 100644 index 0000000..39e305f --- /dev/null +++ b/toontown/coghq/FactoryUtil.py @@ -0,0 +1,77 @@ +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +import MovingPlatform +from direct.task.Task import Task +from toontown.suit import Suit +from toontown.suit import SuitDNA + +class Ouch(DirectObject.DirectObject): + + def __init__(self, keyEvent, callback): + DirectObject.DirectObject.__init__(self) + self.accept(keyEvent, callback) + + def destroy(self): + self.ignoreAll() + + +class CyclePlacer(DirectObject.DirectObject): + + def __init__(self, locations, keyEvent, startIndex = 0): + DirectObject.DirectObject.__init__(self) + self.locations = locations + self.index = startIndex + self.accept(keyEvent, self.gotoNextLocation) + + def destroy(self): + self.locations = None + self.ignoreAll() + return + + def gotoNextLocation(self): + self.index = (self.index + 1) % len(self.locations) + self.gotoLocation() + + def gotoLocation(self, index = None): + if index is None: + index = self.index + pos, h = self.locations[index] + base.localAvatar.reparentTo(render) + base.localAvatar.setPos(*pos) + base.localAvatar.setH(h) + return + + +class ToonLifter(DirectObject.DirectObject): + SerialNum = 0 + + def __init__(self, keyDownEvent, speed = 2): + DirectObject.DirectObject.__init__(self) + self.serialNum = ToonLifter.SerialNum + ToonLifter.SerialNum += 1 + self.taskName = 'ToonLifter%s' % self.serialNum + self.keyDownEvent = keyDownEvent + self.keyUpEvent = self.keyDownEvent + '-up' + self.speed = speed + self.accept(self.keyDownEvent, self.startLifting) + + def destroy(self): + self.ignoreAll() + taskMgr.remove(self.taskName) + + def startLifting(self): + + def liftTask(task, self = self): + base.localAvatar.setZ(base.localAvatar.getZ() + self.speed) + return Task.cont + + def stopLifting(self = self): + taskMgr.remove(self.taskName) + self.ignore(self.keyUpEvent) + self.accept(self.keyDownEvent, self.startLifting) + + self.ignore(self.keyDownEvent) + self.accept(self.keyUpEvent, stopLifting) + taskMgr.add(liftTask, self.taskName) diff --git a/toontown/coghq/FoodBeltBase.py b/toontown/coghq/FoodBeltBase.py new file mode 100644 index 0000000..ffeeddc --- /dev/null +++ b/toontown/coghq/FoodBeltBase.py @@ -0,0 +1,4 @@ + + +class FoodBeltBase: + NumFoodNodes = 4 diff --git a/toontown/coghq/GameSprite3D.py b/toontown/coghq/GameSprite3D.py new file mode 100644 index 0000000..ac2eb0b --- /dev/null +++ b/toontown/coghq/GameSprite3D.py @@ -0,0 +1,367 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import math +import random + +class GameSprite: + colorRed = Vec4(1, 0.2, 0.2, 1) + colorBlue = Vec4(0.7, 0.8, 1, 1) + colorGreen = Vec4(0, 1, 0, 1) + colorGhostRed = Vec4(1, 0.2, 0.2, 0.5) + colorGhostBlue = Vec4(0.7, 0.8, 1, 0.5) + colorGhostGreen = Vec4(0, 1, 0, 0.5) + colorDisolveRed = Vec4(1, 0.2, 0.2, 0.0) + colorDisolveBlue = Vec4(0.7, 0.8, 1, 0.0) + colorDisolveGreen = Vec4(0, 1, 0, 0.0) + colorWhite = Vec4(1, 1, 1, 1) + colorBlack = Vec4(0, 0, 0, 1.0) + colorDisolveWhite = Vec4(1, 1, 1, 0.0) + colorDisolveBlack = Vec4(0, 0, 0, 0.0) + colorShadow = Vec4(0, 0, 0, 0.5) + colorPurple = Vec4(1.0, 0, 1.0, 1.0) + colorDisolvePurple = Vec4(1.0, 0, 1.0, 0.0) + colorYellow = Vec4(1.0, 1.0, 0.0, 1.0) + colorDisolveYellow = Vec4(1.0, 1.0, 0.0, 0.0) + colorOrange = Vec4(1.0, 0.5, 0.0, 1.0) + colorDisolveOrange = Vec4(1.0, 0.5, 0.0, 0.0) + colorAqua = Vec4(0.0, 1.0, 1.0, 1.0) + colorDisolveAqua = Vec4(0.0, 1.0, 1.0, 0.0) + colorSteel = Vec4(0.5, 0.5, 0.5, 1.0) + colorSteelDissolve = Vec4(0.5, 0.5, 0.5, 0.0) + colorList = (colorRed, + colorBlue, + colorGreen, + colorWhite, + colorBlack, + colorPurple, + colorYellow, + colorOrange, + colorAqua, + colorSteel) + disolveList = (colorDisolveRed, + colorDisolveBlue, + colorDisolveGreen, + colorDisolveWhite, + colorDisolveBlack, + colorDisolvePurple, + colorDisolveYellow, + colorDisolveOrange, + colorDisolveAqua, + colorSteelDissolve) + + def __init__(self, spriteBase, size, colorType = 0, foundation = 0, facing = 0): + self.colorType = colorType + self.spriteBase = spriteBase + self.frame = self.spriteBase.getParent() + self.foundation = foundation + self.sizeMult = 1.4 + self.velX = 0 + self.velZ = 0 + self.prevX = 0 + self.prevZ = 0 + self.isActive = 0 + self.canCollide = 1 + self.accX = None + self.accZ = None + self.delayRemove = 0 + self.giftId = None + self.holdType = None + self.multiColor = 0 + self.multiColorList = [0, + 1, + 2, + 6] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.025 + self.facing = facing + self.breakable = 1 + self.deleteFlag = 0 + self.nodeObj = None + self.inputSize = size + myColor = GameSprite.colorWhite + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_white' + self.setBallType(colorType) + self.size = 0.4 * self.sizeMult + self.isQue = 0 + self.nodeObj.setTransparency(TransparencyAttrib.MAlpha) + self.markedForDeath = 0 + self.gridPosX = None + self.gridPosZ = None + return + + def setBallType(self, type, solidOverride = 0): + if not self.nodeObj or self.nodeObj.isEmpty(): + self.nodeObj = None + else: + self.nodeObj.remove() + colorType = type + self.multiColor = 0 + self.breakable = 1 + solid = self.foundation + if solidOverride: + solid = 1 + myColor = GameSprite.colorWhite + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_white' + if not solid or colorType > 9: + if colorType == 0: + myColor = GameSprite.colorGhostRed + elif colorType == 1: + myColor = GameSprite.colorGhostBlue + elif colorType == 2: + myColor = GameSprite.colorGhostGreen + elif colorType == 3: + myColor = GameSprite.colorWhite + elif colorType == 4: + myColor = GameSprite.colorBlack + elif colorType == 5: + myColor = GameSprite.colorPurple + elif colorType == 6: + myColor = GameSprite.colorYellow + elif colorType == 7: + myColor = GameSprite.colorOrange + self.multiColor = 1 + self.multiColorList = [7, 4] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.1 + elif colorType == 8: + myColor = GameSprite.colorAqua + self.multiColor = 1 + self.multiColorList = [0, + 1, + 2, + 6] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.025 + elif colorType == 9: + myColor = GameSprite.colorSteel + self.breakable = 0 + elif colorType == 10: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_fire' + self.giftId = 7 + self.colorType = 0 + elif colorType == 11: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_unknown' + self.giftId = 8 + self.colorType = 1 + elif colorType == 0: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_red' + elif colorType == 1: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_blue' + elif colorType == 2: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_green' + elif colorType == 3: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_cog' + elif colorType == 4: + myColor = GameSprite.colorBlack + elif colorType == 5: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_purple' + elif colorType == 6: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_yello' + elif colorType == 7: + myColor = GameSprite.colorOrange + self.multiColor = 1 + self.multiColorList = [7, 4] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.15 + elif colorType == 8: + myColor = GameSprite.colorAqua + self.multiColor = 1 + self.multiColorList = [0, + 1, + 2, + 6] + self.multiColorIndex = 0 + self.multiColorNext = 1 + self.multiColorLevel = 0.0 + self.multiColorStep = 0.1 + elif colorType == 9: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_steel' + if not myModel: + import pdb + pdb.set_trace() + self.breakable = 0 + elif colorType == 10: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_fire' + self.giftId = 7 + self.colorType = 0 + elif colorType == 11: + myModel = 'phase_12/models/bossbotHQ/bust_a_cog_ball_unknown' + self.giftId = 8 + self.colorType = 1 + self.nodeObj = loader.loadModel(myModel) + self.nodeObj.setScale(self.inputSize) + self.nodeObj.reparentTo(self.spriteBase) + self.setColor(myColor) + return + + def removeDelay(self): + self.delayRemove = 0 + + def delete(self): + if not self.delayRemove: + self.spriteBase.removeNode() + self.deleteFlag = 1 + + def face(self): + frameZ = self.frame.getZ() + tilt = -95.0 + (self.getZ() + frameZ) * 2.0 + self.nodeObj.setP(-tilt) + + def runColor(self): + if self.multiColor: + c1 = GameSprite.colorList[self.multiColorList[self.multiColorIndex]] + c2 = GameSprite.colorList[self.multiColorList[self.multiColorNext]] + iLevel = 1.0 - self.multiColorLevel + mixColor = c1 * iLevel + c2 * self.multiColorLevel + self.nodeObj.setColorScale(mixColor) + self.multiColorLevel += self.multiColorStep + if self.multiColorLevel > 1.0: + self.multiColorLevel = 0.0 + self.multiColorIndex += 1 + if self.multiColorIndex >= len(self.multiColorList): + self.multiColorIndex = 0 + self.multiColorNext = self.multiColorIndex + 1 + if self.multiColorNext >= len(self.multiColorList): + self.multiColorNext = 0 + + def run(self, timeDelta): + if self.facing: + self.face() + self.runColor() + if self.isActive and not self.isQue: + self.prevX = self.spriteBase.getX() + self.prevZ = self.spriteBase.getZ() + self.setX(self.getX() + self.velX * timeDelta) + self.setZ(self.getZ() + self.velZ * timeDelta) + self.velX = self.velX * (1 - timeDelta * 4) + self.velZ = self.velZ * (1 - timeDelta * 4) + if self.accX != None: + self.velX = self.accX + self.velZ = self.accZ + if self.nodeObj.isEmpty(): + self.markedForDeath = 1 + return + + def reflectX(self): + self.velX = -self.velX + if self.accX != None: + self.accX = -self.accX + return + + def reflectZ(self): + self.velZ = -self.velZ + if self.accZ != None: + self.accZ = -self.accZ + return + + def warningBump(self): + num1 = random.random() * 2.0 + num2 = random.random() * 2.0 + num3 = random.random() * 2.0 + curr = self.nodeObj.getPos() + dest = Point3(0 + curr[0], 0 + curr[1], 1.0 + curr[2]) + track = Sequence(Wait(num1 * 0.1), LerpPosInterval(self.nodeObj, num2 * 0.1, Point3(0.0, 0.0, 0.5)), LerpPosInterval(self.nodeObj, num3 * 0.1, Point3(0.0, 0.0, 0.0)), LerpPosInterval(self.nodeObj, num2 * 0.1, Point3(0.0, 0.0, 0.5)), LerpPosInterval(self.nodeObj, num1 * 0.1, Point3(0.0, 0.0, 0.0))) + track.start() + + def shake(self): + num1 = random.random() * 1.0 + num2 = random.random() * 1.0 + curr = self.nodeObj.getPos() + dest = Point3(0 + curr[0], 0 + curr[1], 1.0 + curr[2]) + track = Sequence(LerpPosInterval(self.nodeObj, num2 * 0.1, Point3(0.0, 0.0, 0.25)), LerpPosInterval(self.nodeObj, num1 * 0.1, Point3(0.0, 0.0, 0.0))) + track.start() + + def deathEffect(self): + if self.spriteBase.isEmpty(): + return + self.spriteBase.wrtReparentTo(render) + num1 = (random.random() - 0.5) * 1.0 + num2 = random.random() * 1.0 + num3 = random.random() * 1.0 + notNum3 = 1.0 - num3 + curr = self.spriteBase.getPos() + self.delayRemove = 1 + self.canCollide = 0 + track = Sequence(Parallel(ProjectileInterval(self.spriteBase, startVel=Vec3(-20.0 + notNum3 * 40.0, -20.0 + num3 * 40.0, 30), duration=0.5 + num2 * 1.0, gravityMult=2.0), LerpColorScaleInterval(self.spriteBase, duration=0.5 + num2 * 1.0, startColorScale=GameSprite.colorList[self.colorType], colorScale=GameSprite.disolveList[self.colorType])), Func(self.removeDelay), Func(self.delete)) + track.start() + + def wildEffect(self): + if self.spriteBase.isEmpty(): + return + num1 = (random.random() - 0.5) * 1.0 + num2 = random.random() * 1.0 + num3 = random.random() * 1.0 + notNum3 = 1.0 - num3 + curr = self.spriteBase.getPos() + self.delayRemove = 1 + self.canCollide = 0 + track = Sequence(Parallel(LerpScaleInterval(self.spriteBase, 1.0, 1.5, startScale=1.0), LerpColorScaleInterval(self.spriteBase, duration=1.0, startColorScale=GameSprite.colorList[self.colorType], colorScale=Vec4(0, 0, 0, 0.0))), Func(self.removeDelay), Func(self.delete)) + track.start() + + def setActive(self, active): + if active: + self.isActive = 1 + else: + self.isActive = 0 + self.velX = 0 + self.velZ = 0 + self.accX = None + self.accZ = None + return + + def getX(self): + if self.nodeObj.isEmpty(): + return None + return self.spriteBase.getX() + + def getZ(self): + if self.nodeObj.isEmpty(): + return None + return self.spriteBase.getZ() + + def setX(self, x): + if self.nodeObj.isEmpty(): + return None + self.prevX = self.spriteBase.getX() + self.spriteBase.setX(x) + return None + + def setZ(self, z): + if self.nodeObj.isEmpty(): + return None + self.prevZ = self.spriteBase.getZ() + self.spriteBase.setZ(z) + return None + + def addForce(self, force, direction): + if self.isActive: + forceX = math.cos(direction) * force + forceZ = math.sin(direction) * force + self.velX += forceX + self.velZ += forceZ + + def setAccel(self, accel, direction): + accelX = math.cos(direction) * accel + accelZ = math.sin(direction) * accel + self.accX = accelX + self.accZ = accelZ + + def setColorType(self, typeIndex): + self.colorType = typeIndex + self.setColor(GameSprite.colorList[typeIndex]) + + def setColor(self, trip): + self.nodeObj.setColorScale(trip[0], trip[1], trip[2], trip[3]) + + def collide(self): + if self.isActive: + self.setX(self.prevX) + self.setZ(self.prevZ) diff --git a/toontown/coghq/GearEntity.py b/toontown/coghq/GearEntity.py new file mode 100644 index 0000000..126f435 --- /dev/null +++ b/toontown/coghq/GearEntity.py @@ -0,0 +1,121 @@ +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities +import MovingPlatform +from pandac.PandaModules import Vec3 + +class GearEntity(BasicEntities.NodePathEntity): + ModelPaths = {'factory': 'phase_9/models/cogHQ/FactoryGearB', + 'mint': 'phase_10/models/cashbotHQ/MintGear'} + + def __init__(self, level, entId): + self.modelType = 'factory' + self.entInitialized = False + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.entInitialized = True + self.initGear() + + def destroy(self): + self.destroyGear() + BasicEntities.NodePathEntity.destroy(self) + + def initGear(self): + if hasattr(self, 'in_initGear'): + return + self.in_initGear = True + self.destroyGear() + model = loader.loadModel(GearEntity.ModelPaths[self.modelType]) + self.gearParent = self.attachNewNode('gearParent-%s' % self.entId) + if self.orientation == 'horizontal': + vertNodes = model.findAllMatches('**/VerticalCollisions') + for node in vertNodes: + node.stash() + + mPlat = MovingPlatform.MovingPlatform() + mPlat.setupCopyModel(self.getParentToken(), model, 'HorizontalFloor') + model = mPlat + else: + horizNodes = model.findAllMatches('**/HorizontalCollisions') + for node in horizNodes: + node.stash() + + model.setZ(0.15) + model.flattenLight() + model.setScale(self.gearScale) + model.flattenLight() + model.setScale(self.getScale()) + self.setScale(1) + model.flattenLight() + if self.orientation == 'vertical': + self.gearParent.setP(-90) + self.model = model + self.model.reparentTo(self.gearParent) + self.startRotate() + del self.in_initGear + + def destroyGear(self): + self.stopRotate() + if hasattr(self, 'model'): + if isinstance(self.model, MovingPlatform.MovingPlatform): + self.model.destroy() + else: + self.model.removeNode() + del self.model + if hasattr(self, 'gearParent'): + self.gearParent.removeNode() + del self.gearParent + + def startRotate(self): + self.stopRotate() + try: + ivalDur = 360.0 / self.degreesPerSec + except ZeroDivisionError: + pass + else: + hOffset = 360.0 + if ivalDur < 0.0: + ivalDur = -ivalDur + hOffset = -hOffset + self.rotateIval = LerpHprInterval(self.model, ivalDur, Vec3(hOffset, 0, 0), startHpr=Vec3(0, 0, 0), name='gearRot-%s' % self.entId) + self.rotateIval.loop() + self.rotateIval.setT(globalClock.getFrameTime() - self.level.startTime + ivalDur * self.phaseShift) + + def stopRotate(self): + if hasattr(self, 'rotateIval'): + self.rotateIval.pause() + del self.rotateIval + + if __dev__: + + def setDegreesPerSec(self, degreesPerSec): + if self.entInitialized: + self.degreesPerSec = degreesPerSec + self.startRotate() + + def setPhaseShift(self, phaseShift): + if self.entInitialized: + self.phaseShift = phaseShift + self.startRotate() + + def attribChanged(self, attrib, value): + self.destroyGear() + self.initGear() + + def setScale(self, *args): + BasicEntities.NodePathEntity.setScale(self, *args) + if self.entInitialized: + self.initGear() + + def setSx(self, *args): + BasicEntities.NodePathEntity.setSx(self, *args) + if self.entInitialized: + self.initGear() + + def setSy(self, *args): + BasicEntities.NodePathEntity.setSy(self, *args) + if self.entInitialized: + self.initGear() + + def setSz(self, *args): + BasicEntities.NodePathEntity.setSz(self, *args) + if self.entInitialized: + self.initGear() diff --git a/toontown/coghq/GolfGreenGameGlobals.py b/toontown/coghq/GolfGreenGameGlobals.py new file mode 100644 index 0000000..09255e1 --- /dev/null +++ b/toontown/coghq/GolfGreenGameGlobals.py @@ -0,0 +1,18 @@ +gameBoards = [ + ('rbygl', 'r_______b', 'byyywgggr', 'r_______b', 'bggyyyggr', 'r_______b', 'byylllyyr', 'r_______b', 'Blllllllr'), + ('rgby', 'rrrgggbbb', 'rwrgggbwb', 'rrryyybbb', 'bByyyyyRr'), + ('bygr', 'b_y_b_y__', 'b_y_b_yw_', '_b_y_b_y_', '_b_y_b_g_', '__b_g_r__', '__b_g_r__', '___b_g_r_', '___b_g_R_'), + ('rgb', '_b__g____', '_r__g____', '_r__r____', '_w__R____'), + ('lbryg', '___bbb___', '___bwb___', '__lgggr__', 'rrgggggll', '_ybbbbby_', '__ryyyr__', '_________', '_________'), + ('lbryg', 'l_rr__b__', 'l__w__b__', 'b__b__b__', 'b__b__r__', 'l__g__r__', 'l__g__b__', 'y__y__B__', '_________'), + ('byr', 'R_______y', 'ygggwgggR', 'B_______B', 'Rgggggggy', 'y_______R', 'BgggggggB', 'R_______y', 'BgggggggR'), + ('bygr', '____bb___', '___bwb___', '_y__y____', '_y__y____', '_yggg_bbb', '____y____', '____y____', '__b_y__b_', '__b_y__b_', '__rrRrrr_'), + ('bryg', 'b_yr_by_r', 'by_rb_yr_', 'r_gbwrg_y', 'rg_br_gy_', 'b_yg_yr_b', 'by_gy_Rb_', '_________', '_________'), + ('lyg', '__lyyyyl_', '__lywyl__', '___lyyl__', '___lyl___', '____ll___', '__ygyg___', 'lyl___lyl', '_________'), + ('rgbyl', 'l_______r', 'brbrbw__r', 'r_______r', 'r__ylblbl', 'r_______b', 'lglgly__b', '________b', '___yrgrgr'), + ('rgbyl', 'b_______r', 'bbbw_wrrr', '___b__r__', '__b__r___', '___b__r__', '__g__y___', '___y__g__', 'rrrr_bbbb', 'lbyy_ggrl'), + ('yrbg', 'ry_____yb', '_yrwwby__', '___yyy___', '_rl__gb__', 'lr_____bg', 'ylyg_gyly', '_________', '_________'), + ('bylr', 'rrr_r_r_r', '_w__r_r_r', '____r_r_r', 'ggggR_r_r', '______r_r', 'ggggggR_r', '________r', 'ggggggggR', '_________'), + ('rgbl', '__y_bb_y_', '_y__w_y__', 'yyy____yy', 'y_gyyyr_y', 'y_______y', 'byyyyyyl_', '_________', '_________', '_________', '_________'), + ('o', 'b_bb_bb_b', 'wb_bb_bw_', 'b_bbbbb_b', 'bb_bb_bb_', 'b_bb_bb_b', 'bb_bb_bb_', '_________', '_________'), + ('oa', 's________', 'sw_______', 's________', 's________', 's________', 's________', 'ssssss___', '_________')] diff --git a/toontown/coghq/GoonClipPlane.py b/toontown/coghq/GoonClipPlane.py new file mode 100644 index 0000000..9a39737 --- /dev/null +++ b/toontown/coghq/GoonClipPlane.py @@ -0,0 +1,38 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities + +class GoonClipPlane(BasicEntities.NodePathEntity): + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.zoneNum = self.getZoneEntity().getZoneNum() + self.initPlane() + self.registerWithFactory() + + def destroy(self): + self.unregisterWithFactory() + BasicEntities.NodePathEntity.destroy(self) + self.removeNode() + + def registerWithFactory(self): + clipList = self.level.goonClipPlanes.get(self.zoneNum) + if clipList: + if self.entId not in clipList: + clipList.append(self.entId) + else: + self.level.goonClipPlanes[self.zoneNum] = [self.entId] + + def unregisterWithFactory(self): + clipList = self.level.goonClipPlanes.get(self.zoneNum) + if clipList: + if self.entId in clipList: + clipList.remove(self.entId) + + def initPlane(self): + self.coneClip = PlaneNode('coneClip') + self.coneClip.setPlane(Plane(Vec3(1, 0, 0), Point3(0, 0, 0))) + self.coneClipPath = self.attachNewNode(self.coneClip) + + def getPlane(self): + return self.coneClipPath diff --git a/toontown/coghq/InGameEditor.py b/toontown/coghq/InGameEditor.py new file mode 100644 index 0000000..dd96eb0 --- /dev/null +++ b/toontown/coghq/InGameEditor.py @@ -0,0 +1,1054 @@ +from pandac.PandaModules import Point3, Vec3, VBase3 +from direct.tkwidgets.AppShell import * +from direct.showbase.TkGlobal import * +from direct.tkwidgets.Tree import * +from direct.tkwidgets import Slider, Floater +from tkSimpleDialog import askstring +from tkMessageBox import showwarning, askyesno +from Tkinter import * +from direct.showbase.PythonUtil import Functor, list2dict +from direct.gui.DirectGui import DGG +import tkFileDialog +from direct.showbase import DirectObject +import math +import operator +from direct.tkwidgets import Valuator +from direct.tkwidgets import VectorWidgets +from otp.level import LevelConstants +from direct.directtools import DirectSession +import types +import Pmw + +class InGameEditor(AppShell): + appname = 'In-Game Editor' + frameWidth = 900 + frameHeight = 475 + usecommandarea = 1 + usestatusarea = 1 + contactname = 'Darren Ranalli' + contactphone = '(818) 623-3904' + contactemail = 'darren.ranalli@disney.com' + WantUndo = False + + def __init__(self, level, doneEvent, requestSaveEvent, saveAsEvent, undoEvent, redoEvent, wireframeEvent, oobeEvent, csEvent, runEvent, texEvent, **kw): + DGG.INITOPT = Pmw.INITOPT + optiondefs = (('title', 'In-Game ' + level.getName() + ' Editor', None),) + self.defineoptions(kw, optiondefs) + self.level = level + self.doneEvent = doneEvent + self.requestSaveEvent = requestSaveEvent + self.saveAsEvent = saveAsEvent + self.undoEvent = undoEvent + self.redoEvent = redoEvent + self.wireframeEvent = wireframeEvent + self.oobeEvent = oobeEvent + self.csEvent = csEvent + self.runEvent = runEvent + self.texEvent = texEvent + self.entityCopy = None + self.curEntId = None + self.curEntWidgets = {} + self.visZonesEditor = None + AppShell.__init__(self) + self.initialiseoptions(self.__class__) + self.accept(self.level.getAttribChangeEventName(), self.handleAttribChange) + self.accept('DIRECT_selectedNodePath', self.selectedNodePathHook) + self.accept('DIRECT_manipulateObjectCleanup', self.manipCleanupHook) + self.accept('DIRECT_undo', self.manipCleanupHook) + self.accept('DIRECT_redo', self.manipCleanupHook) + return + + def getEventMsgName(self, event): + return 'InGameEditor%s_%s' % (self.level.getLevelId(), event) + + def getEntityName(self, entId): + return self.level.levelSpec.getEntitySpec(entId)['name'] + + def appInit(self): + from direct.directtools import DirectSession + direct.enableLight() + direct.disable() + bboard.post(DirectSession.DirectSession.DIRECTdisablePost) + + def createMenuBar(self): + menuBar = self.menuBar + menuBar.addmenuitem('File', 'command', 'Save the level spec on the AI', label='Save on AI', command=self.handleRequestSave) + menuBar.addmenuitem('File', 'command', 'Save the level spec locally', label='Save Locally As...', command=self.handleSaveAs) + menuBar.addmenuitem('File', 'separator') + menuBar.addmenuitem('File', 'command', 'Export a single entity', label='Export Entity...', command=self.level.handleExportEntity) + menuBar.addmenuitem('File', 'command', 'Export a tree of entities', label='Export Entity Tree...', command=self.level.handleExportEntityTree) + menuBar.addmenuitem('File', 'separator') + menuBar.addmenuitem('File', 'command', 'Import a tree of entities', label='Import Entities...', command=self.level.handleImportEntities) + menuBar.addmenuitem('File', 'separator') + if InGameEditor.WantUndo: + menuBar.addmenu('Edit', 'Edit Operations') + menuBar.addmenuitem('Edit', 'command', 'Undo the last edit', label='Undo', command=self.doUndo) + menuBar.addmenuitem('Edit', 'command', 'Redo the last undo', label='Redo', command=self.doRedo) + menuBar.addmenu('Entity', 'Entity Operations') + menuBar.addmenuitem('Entity', 'command', 'Duplicate Selected Entity', label='Duplicate Selected Entity', command=self.level.duplicateSelectedEntity) + menuBar.addmenuitem('Entity', 'separator') + permanentTypes = self.level.entTypeReg.getPermanentTypeNames() + entTypes = list(self.level.entTypes) + map(entTypes.remove, permanentTypes) + entTypes.sort() + numEntities = len(entTypes) + cascadeMenu = '' + for index in range(numEntities): + type = entTypes[index] + if index % 10 == 0: + lastIndex = min(index + 9, numEntities - 1) + cascadeMenu = '%s->%s' % (entTypes[index], entTypes[lastIndex]) + menuBar.addcascademenu('Entity', cascadeMenu) + + def doInsert(self = self, type = type): + self.level.insertEntity(type) + self.explorer.update(fUseCachedChildren=0) + + menuBar.addmenuitem(cascadeMenu, 'command', 'Insert %s' % type, label='Insert %s' % type, command=doInsert) + + menuBar.addmenuitem('Entity', 'separator') + menuBar.addmenuitem('Entity', 'command', 'Remove Selected Entity', label='Remove Selected Entity', command=self.removeSelectedEntity) + menuBar.addmenuitem('Entity', 'command', 'Remove Selected EntityTree', label='Remove Selected Entity Tree', command=self.removeSelectedEntityTree) + menuBar.addmenuitem('Entity', 'separator') + menuBar.addmenuitem('Entity', 'command', 'Go To Selected Entity', label='Go To Selected Entity', command=self.level.moveAvToSelected) + menuBar.addmenuitem('Entity', 'command', 'Refresh Entity Explorer', label='Refresh Entity Explorer', command=self.refreshExplorer) + self.menuBar.addmenu('DIRECT', 'Direct Session Panel Operations') + self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Enabled', label='Enable', variable=direct.panel.directEnabled, command=direct.panel.toggleDirect) + self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Grid Enabled', label='Enable Grid', variable=direct.panel.directGridEnabled, command=direct.panel.toggleDirectGrid) + self.menuBar.addmenuitem('DIRECT', 'command', 'Toggle Object Handles Visability', label='Toggle Widget Viz', command=direct.toggleWidgetVis) + self.menuBar.addmenuitem('DIRECT', 'command', 'Toggle Widget Move/COA Mode', label='Toggle Widget Mode', command=direct.manipulationControl.toggleObjectHandlesMode) + self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Widget On Top', label='Widget On Top', variable=direct.panel.directWidgetOnTop, command=direct.panel.toggleWidgetOnTop) + self.menuBar.addmenuitem('DIRECT', 'command', 'Deselect All', label='Deselect All', command=direct.deselectAll) + if InGameEditor.WantUndo: + undoFrame = Frame(self.menuFrame) + self.redoButton = Button(undoFrame, text='Redo', command=self.doRedo) + self.redoButton.pack(side=RIGHT, expand=0) + self.undoButton = Button(undoFrame, text='Undo', command=self.doUndo) + self.undoButton.pack(side=RIGHT, expand=0) + undoFrame.pack(side=RIGHT, expand=0) + toggleFrame = Frame(self.menuFrame) + self.wireframeButton = Button(toggleFrame, text='Wire', command=self.doWireframe) + self.wireframeButton.pack(side=RIGHT, expand=0) + self.texButton = Button(toggleFrame, text='Tex', command=self.doTex) + self.texButton.pack(side=RIGHT, expand=0) + self.csButton = Button(toggleFrame, text='Cs', command=self.doCs) + self.csButton.pack(side=RIGHT, expand=0) + self.runButton = Button(toggleFrame, text='Run', command=self.doRun) + self.runButton.pack(side=RIGHT, expand=0) + self.oobeButton = Button(toggleFrame, text='Oobe', command=self.doOobe) + self.oobeButton.pack(side=RIGHT, expand=0) + toggleFrame.pack(side=RIGHT, expand=0, padx=5) + AppShell.createMenuBar(self) + + def createInterface(self): + interior = self.interior() + mainFrame = Frame(interior) + self.framePane = Pmw.PanedWidget(mainFrame, orient=DGG.HORIZONTAL) + self.explorerFrame = self.framePane.add('left', min=250) + self.widgetFrame = self.framePane.add('right', min=300) + self.explorer = LevelExplorer(parent=self.explorerFrame, editor=self) + self.explorer.pack(fill=BOTH, expand=1) + self.explorerFrame.pack(fill=BOTH, expand=1) + self.pageOneFrame = Pmw.ScrolledFrame(self.widgetFrame, borderframe_relief=GROOVE, horizflex='elastic') + self.pageOneFrame.pack(expand=1, fill=BOTH) + self.widgetFrame.pack(fill=BOTH, expand=1) + self.framePane.pack(fill=BOTH, expand=1) + mainFrame.pack(fill=BOTH, expand=1) + self.createButtons() + self.initialiseoptions(self.__class__) + self.attribWidgets = [] + + def selectedNodePathHook(self, nodePath): + np = nodePath.findNetTag('entity') + if not np.isEmpty(): + if np.id() != nodePath.id(): + np.select() + else: + self.findEntityFromNP(np) + + def findEntityFromNP(self, nodePath): + entId = self.level.nodePathId2EntId.get(nodePath.id()) + if entId: + self.selectEntity(entId) + else: + for entId in self.level.levelSpec.getAllEntIds(): + np = self.level.getEntInstanceNP(entId) + if np: + if np.id() == nodePath.id(): + self.selectEntity(entId) + return + + def manipCleanupHook(self, nodePathList): + if not nodePathList: + return + entId = self.level.nodePathId2EntId.get(nodePathList[0].id()) + if entId: + t = nodePathList[0].getTransform() + entSpec = self.level.levelSpec.getEntitySpec(entId) + entPos = entSpec.get('pos') + if entPos and t.getPos() != entPos: + self.level.setAttribEdit(entId, 'pos', Point3(t.getPos())) + entHpr = entSpec.get('hpr') + if entHpr and t.getHpr() != entHpr: + self.level.setAttribEdit(entId, 'hpr', Vec3(t.getHpr())) + entScale = entSpec.get('scale') + if entScale and t.getScale() != entScale: + self.level.setAttribEdit(entId, 'scale', Vec3(t.getScale())) + + def refreshExplorer(self): + self.explorer.update() + + def selectEntity(self, entId): + node = self.explorer._node.find(entId) + if node: + node.reveal() + self.explorer._node.deselecttree() + node.select() + + def removeSelectedEntity(self): + if self.level.selectedEntity: + parentEntId = self.level.selectedEntity.parentEntId + else: + parentEntId = None + if self.level.removeSelectedEntity() != -1: + self.explorer.update(fUseCachedChildren=0) + if parentEntId: + self.selectEntity(parentEntId) + return + + def removeSelectedEntityTree(self): + if self.level.selectedEntity: + parentEntId = self.level.selectedEntity.parentEntId + else: + parentEntId = None + if self.level.removeSelectedEntityTree() != -1: + self.explorer.update(fUseCachedChildren=0) + if parentEntId: + self.selectEntity(parentEntId) + return + + def clearAttribEditPane(self): + for widg in self.attribWidgets: + widg.destroy() + + if self.visZonesEditor: + self.visZonesEditor.destroy() + self.attribWidgets = [] + self.curEntId = None + self.curEntWidgets = {} + self.cbDict = {} + return + + def updateEntityCopy(self, entId): + if self.entityCopy == None: + self.entityCopy = self.level.getEntInstanceNPCopy(entId) + if self.entityCopy is not None: + self.entityCopy.setRenderModeWireframe() + self.entityCopy.setTextureOff(1) + self.entityCopy.setColor(1, 0, 0) + return + + def updateAttribEditPane(self, entId, levelSpec, entTypeReg): + self.clearAttribEditPane() + self.curEntId = entId + widgetSetter = None + entSpec = levelSpec.getEntitySpec(entId) + typeDesc = entTypeReg.getTypeDesc(entSpec['type']) + attribNames = typeDesc.getAttribNames() + attribDescs = typeDesc.getAttribDescDict() + for attribName in attribNames: + desc = attribDescs[attribName] + params = desc.getParams() + datatype = desc.getDatatype() + if datatype == 'int': + self.addIntWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'float': + self.addFloatWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'bool': + self.addBoolWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'choice': + self.addChoiceWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'multiChoice': + self.addMultiChoiceWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype in ['pos', 'hpr', 'scale']: + self.addVec3Widget(levelSpec, entSpec, entId, attribName, params, datatype) + elif datatype == 'color': + self.addColorWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'bamfilename': + self.addFileWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'visZoneList': + self.addVisZoneWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'entId': + self.addEntIdWidget(levelSpec, entSpec, entId, attribName, params, entTypeReg) + elif datatype == 'string': + self.addStringWidget(levelSpec, entSpec, entId, attribName, params) + elif datatype == 'const': + self.addConstWidget(levelSpec, entSpec, entId, attribName, params) + else: + self.addPythonWidget(levelSpec, entSpec, entId, attribName, params) + + return + + def addIntWidget(self, levelSpec, entSpec, entId, attribName, params): + minVal = params.get('min', None) + maxVal = params.get('max', None) + if minVal is not None and maxVal is not None: + widgClass = Slider.Slider + else: + widgClass = Floater.Floater + widg = widgClass(self.pageOneFrame.interior(), text=attribName, value=entSpec[attribName], numDigits=0, label_width=15, label_anchor=W, label_justify=LEFT, label_font=None, min=minVal, max=maxVal, resolution=1.0) + + def clientIntCommand(val): + entity = self.level.getEntInstance(entId) + if entity: + entity.handleAttribChange(attribName, int(val)) + + def finalIntCommand(): + self.level.setAttribEdit(entId, attribName, int(widg.get())) + + widg['command'] = clientIntCommand + widg['postCallback'] = finalIntCommand + widg.pack(fill=X, expand=1) + self.attribWidgets.append(widg) + self.curEntWidgets[attribName] = lambda x: widg.set(x, 0) + return + + def addFloatWidget(self, levelSpec, entSpec, entId, attribName, params): + minVal = params.get('min', None) + maxVal = params.get('max', None) + widg = Floater.Floater(self.pageOneFrame.interior(), text=attribName, value=entSpec[attribName], label_width=15, label_anchor=W, label_justify=LEFT, label_font=None, min=minVal, max=maxVal) + + def clientFloatCommand(val): + entity = self.level.getEntInstance(entId) + if entity: + entity.handleAttribChange(attribName, val) + + def finalFloatCommand(): + self.level.setAttribEdit(entId, attribName, widg.get()) + + widg['command'] = clientFloatCommand + widg['postCallback'] = finalFloatCommand + widg.pack(fill=X, expand=1) + self.attribWidgets.append(widg) + self.curEntWidgets[attribName] = lambda x: widg.set(x, 0) + return + + def addBoolWidget(self, levelSpec, entSpec, entId, attribName, params): + flag = BooleanVar() + flag.set(entSpec[attribName]) + + def booleanCommand(booleanVar = flag): + self.level.setAttribEdit(entId, attribName, flag.get()) + + frame = Frame(self.pageOneFrame.interior()) + label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) + label.pack(side=LEFT, expand=0) + trueButton = Radiobutton(frame, text='True', value=1, variable=flag, command=booleanCommand) + trueButton.pack(side=LEFT, expand=0) + falseButton = Radiobutton(frame, text='False', value=0, variable=flag, command=booleanCommand) + falseButton.pack(side=LEFT, expand=0) + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + self.curEntWidgets[attribName] = flag.set + + def addChoiceWidget(self, levelSpec, entSpec, entId, attribName, params): + if attribName in entSpec: + attributeValue = entSpec.get(attribName) + else: + typeDesc = self.level.entTypeReg.getTypeDesc(entSpec['type']) + attribDesc = typeDesc.getAttribDescDict()[attribName] + attributeValue = attribDesc.getDefaultValue() + valueDict = params.get('valueDict', {}) + for key, value in valueDict.items(): + if value == attributeValue: + attributeValue = key + break + + radioVar = StringVar() + radioVar.set(attributeValue) + + def radioCommand(radioVar = radioVar): + value = valueDict.get(radioVar.get(), radioVar.get()) + self.level.setAttribEdit(entId, attribName, value) + + frame = Frame(self.pageOneFrame.interior(), relief=GROOVE, borderwidth=2) + label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) + label.pack(side=LEFT, expand=0) + for choice in params.get('choiceSet', []): + if type(choice) is types.StringType: + choiceStr = choice + else: + choiceStr = `choice` + if choiceStr not in valueDict: + valueDict[choiceStr] = choice + choiceButton = Radiobutton(frame, text=choiceStr, value=choiceStr, variable=radioVar, command=radioCommand) + choiceButton.pack(side=LEFT, expand=0) + + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + + def setRadioVar(attributeValue): + for key, value in valueDict.items(): + if value == attributeValue: + attributeValue = key + break + + radioVar.set(attributeValue) + + self.curEntWidgets[attribName] = setRadioVar + + def addMultiChoiceWidget(self, levelSpec, entSpec, entId, attribName, params): + frame = Frame(self.pageOneFrame.interior(), relief=GROOVE, borderwidth=2) + label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) + label.pack(side=LEFT, expand=0) + valueDict = params.get('valueDict', {}) + self.cbDict[attribName] = list2dict(entSpec[attribName], value=1) + checkbuttonDict = {} + base.cbButtons = [] + base.checkbuttonDict = checkbuttonDict + for choice in params.get('choiceSet', []): + trueValue = valueDict.get(choice, choice) + cbVar = IntVar() + cbVar.set(trueValue in self.cbDict[attribName]) + checkbuttonDict[trueValue] = cbVar + + def cbCommand(var, trueValue = trueValue): + vd = self.cbDict[attribName] + print vd + if var.get(): + print 'got it', trueValue, vd + vd[trueValue] = 1 + else: + print 'not it', trueValue, vd + if trueValue in vd: + del vd[trueValue] + value = vd.keys() + print 'SENDING', value + self.level.setAttribEdit(entId, attribName, value) + + if type(choice) is types.StringType: + labelStr = choice + else: + labelStr = `choice` + func = Functor(cbCommand, cbVar) + choiceButton = Checkbutton(frame, text=labelStr, variable=cbVar, command=lambda : func()) + choiceButton.pack(side=LEFT, expand=0) + base.cbButtons.append(choiceButton) + + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + + def setCheckbuttonVar(attributeValueList): + print 'COMING BACK', attributeValueList + for attributeValue, cb in checkbuttonDict.items(): + if attributeValue in attributeValueList: + cb.set(1) + else: + cb.set(0) + + self.curEntWidgets[attribName] = setCheckbuttonVar + + def addVec3Widget(self, levelSpec, entSpec, entId, attribName, params, datatype): + + def veCommand(poslist): + self.level.setAttribEdit(entId, attribName, Point3(*poslist)) + + vec = entSpec[attribName] + if not isinstance(vec, VBase3): + vec = Vec3(vec) + value = [vec[0], vec[1], vec[2]] + minVal = maxVal = None + if datatype == 'pos': + floaterLabels = ['x', 'y', 'z'] + floaterType = 'floater' + elif datatype == 'hpr': + floaterLabels = ['h', 'p', 'r'] + floaterType = 'angledial' + else: + floaterLabels = ['sx', 'sy', 'sz'] + floaterType = 'slider' + minVal = 0 + maxVal = 1000 + widg = VectorWidgets.VectorEntry(self.pageOneFrame.interior(), text=attribName, value=value, type=floaterType, bd=0, relief=None, min=minVal, max=maxVal, label_justify=LEFT, label_anchor=W, label_width=14, label_bd=0, labelIpadx=0, floaterGroup_labels=floaterLabels) + widg['command'] = veCommand + widg.pack(fill=X, expand=1) + if attribName in ('pos', 'hpr', 'scale'): + + def placeEntity(): + selectedEntityNP = self.level.getEntInstanceNP(entId) + if selectedEntityNP is not None: + selectedEntityNP.place() + return + + widg.menu.add_command(label='Place...', command=placeEntity) + + def adjustCopy(vec): + self.updateEntityCopy(entId) + if self.entityCopy is not None: + if datatype == 'pos': + self.entityCopy.setPos(vec[0], vec[1], vec[2]) + elif datatype == 'hpr': + self.entityCopy.setHpr(vec[0], vec[1], vec[2]) + elif datatype == 'scale': + self.entityCopy.setScale(vec[0], vec[1], vec[2]) + widg.set(vec, 0) + return + + widg._floaters['command'] = adjustCopy + widg._floaters.component('valuatorGroup')['postCallback'] = lambda x, y, z: veCommand([x, y, z]) + self.attribWidgets.append(widg) + self.curEntWidgets[attribName] = lambda x: widg.set(x, 0) + return + + def addColorWidget(self, levelSpec, entSpec, entId, attribName, params): + + def veCommand(colorlist): + self.level.setAttribEdit(entId, attribName, Vec4(*colorlist) / 255.0) + + vec = entSpec[attribName] + value = [vec[0] * 255.0, + vec[1] * 255.0, + vec[2] * 255.0, + vec[3] * 255.0] + floaterLabels = ['r', + 'g', + 'b', + 'a'] + widg = VectorWidgets.ColorEntry(self.pageOneFrame.interior(), text=attribName, type='slider', relief=None, bd=0, label_justify=LEFT, label_anchor=W, label_width=14, label_bd=0, labelIpadx=0, floaterGroup_labels=floaterLabels, value=value, floaterGroup_value=value) + widg['command'] = veCommand + widg.pack(fill=X, expand=1) + + def adjustEntity(vec): + entity = self.level.getEntInstance(entId) + if entity is not None: + entity.setColor(vec[0] / 255.0, vec[1] / 255.0, vec[2] / 255.0, vec[3] / 255.0) + widg.set(vec, 0) + return + + widg._floaters['command'] = adjustEntity + widg._floaters.component('valuatorGroup')['postCallback'] = lambda x, y, z, a: veCommand([x, + y, + z, + a]) + self.attribWidgets.append(widg) + self.curEntWidgets[attribName] = lambda x: widg.set(x * 255.0, 0) + return + + def addFileWidget(self, levelSpec, entSpec, entId, attribName, params): + text = StringVar() + text.set(repr(entSpec[attribName])) + + def handleReturn(event): + self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) + + def askFilename(callback = handleReturn): + if text.get() == 'None': + initialDir = Filename.expandFrom('$TTMODELS/built/').toOsSpecific() + else: + initialDir = Filename.expandFrom('$TTMODELS/built/%s' % text.get()[1:-1]).toOsSpecific() + print text, text.get()[1:-1], initialDir + rawFilename = askopenfilename(defaultextension='*', initialdir=initialDir, filetypes=(('Bam Files', '*.bam'), + ('Egg Files', '*.egg'), + ('Maya Binaries', '*.mb'), + ('All files', '*')), title='Load Model File', parent=self.interior()) + if rawFilename != '': + filename = Filename.fromOsSpecific(rawFilename) + filename.findOnSearchpath(getModelPath().getValue()) + text.set("'%s'" % `filename`) + handleReturn(None) + return + + frame = Frame(self.pageOneFrame.interior()) + label = Button(frame, text=attribName, width=14, borderwidth=0, anchor=W, justify=LEFT) + label['command'] = askFilename + label.pack(side=LEFT, expand=0) + + def enterWidget(event, widg = label): + widg['background'] = '#909090' + + def leaveWidget(event, widg = label): + widg['background'] = 'SystemButtonFace' + + label.bind('', enterWidget) + label.bind('', leaveWidget) + widg = Entry(frame, textvariable=text) + widg.bind('', handleReturn) + widg.pack(side=LEFT, fill=X, expand=1) + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + self.curEntWidgets[attribName] = lambda x: text.set(repr(x)) + + def addVisZoneWidget(self, levelSpec, entSpec, entId, attribName, params): + text = StringVar() + text.set(repr(entSpec[attribName])) + + def handleReturn(event): + self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) + + def handleUpdate(visZoneList): + self.level.setAttribEdit(entId, attribName, visZoneList) + + def getZoneList(callback = handleReturn): + zoneEntIds = list(self.level.entType2ids['zone']) + zoneEntIds.remove(LevelConstants.UberZoneEntId) + zoneEntIds.sort() + self.visZonesEditor = LevelVisZonesEditor(self, entId, entSpec[attribName], modelZones=zoneEntIds, updateCommand=handleUpdate) + + frame = Frame(self.pageOneFrame.interior()) + label = Button(frame, text=attribName, width=14, borderwidth=0, anchor=W, justify=LEFT) + label['command'] = getZoneList + label.pack(side=LEFT, expand=0) + + def enterWidget(event, widg = label): + widg['background'] = '#909090' + + def leaveWidget(event, widg = label): + widg['background'] = 'SystemButtonFace' + + label.bind('', enterWidget) + label.bind('', leaveWidget) + widg = Entry(frame, textvariable=text) + widg.bind('', handleReturn) + widg.pack(side=LEFT, fill=X, expand=1) + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + + def refreshWidget(visZoneList): + text.set(repr(visZoneList)) + if self.visZonesEditor: + self.visZonesEditor.setVisible(visZoneList) + + self.curEntWidgets[attribName] = refreshWidget + + def addEntIdWidget(self, levelSpec, entSpec, entId, attribName, params, entTypeReg): + text = StringVar() + text.set(repr(entSpec[attribName])) + + def handleReturn(event): + self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) + + def handleMenu(id): + text.set(id) + self.level.setAttribEdit(entId, attribName, id) + + frame = Frame(self.pageOneFrame.interior()) + label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) + label.pack(side=LEFT, expand=0) + widg = Entry(frame, textvariable=text) + widg.bind('', handleReturn) + widg.pack(side=LEFT, fill=X, expand=1) + if attribName is 'parentEntId': + buttonText = 'Reparent To' + else: + buttonText = 'Select Entity' + entButton = Menubutton(frame, width=8, text=buttonText, relief=RAISED, borderwidth=2) + entMenu = Menu(entButton, tearoff=0) + entButton['menu'] = entMenu + entButton.pack(side=LEFT, fill='x', expand=1) + entType = params.get('type') + entOutput = params.get('output') + idDict = {} + if entType == 'grid': + for eType in entTypeReg.getDerivedTypeNames('grid'): + idDict[eType] = self.level.entType2ids.get(eType, []) + + elif entType == 'nodepath': + for eType in entTypeReg.getDerivedTypeNames('nodepath'): + idDict[eType] = self.level.entType2ids.get(eType, []) + + elif entOutput == 'bool': + for eType in entTypeReg.getTypeNamesFromOutputType('bool'): + idDict[eType] = self.level.entType2ids.get(eType, []) + + else: + for eType in self.level.entType2ids.keys(): + idDict[eType] = self.level.entType2ids.get(eType, []) + + typeKeys = idDict.keys() + typeKeys.sort() + + def getChildEntIds(entity): + entIds = [] + for child in entity.getChildren(): + entIds.append(child.entId) + entIds.extend(getChildEntIds(child)) + + return entIds + + thisEntity = self.level.getEntity(entId) + forbiddenEntIds = [entId, thisEntity.parentEntId] + forbiddenEntIds.extend(getChildEntIds(thisEntity)) + for eType in typeKeys: + idList = list(idDict[eType]) + for forbiddenId in forbiddenEntIds: + if forbiddenId in idList: + idList.remove(forbiddenId) + + if len(idList) == 0: + continue + subMenu = Menu(entMenu, tearoff=0) + entMenu.add_cascade(label=eType, menu=subMenu) + idList.sort() + numIds = len(idList) + idIndex = 0 + for id in idList: + if idIndex % 10 == 0: + if numIds > 10: + m = Menu(subMenu, tearoff=0) + firstId = idList[idIndex] + lastIndex = min(idIndex + 9, numIds - 1) + lastId = idList[lastIndex] + subMenu.add_cascade(label='Ids %d-%d' % (firstId, lastId), menu=m) + else: + m = subMenu + m.add_command(label='%d: %s' % (id, self.getEntityName(id)), command=lambda id = id, h = handleMenu: h(id)) + idIndex += 1 + + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + self.curEntWidgets[attribName] = text.set + + def addStringWidget(self, levelSpec, entSpec, entId, attribName, params): + text = StringVar() + text.set(str(entSpec[attribName])) + + def handleReturn(event): + self.level.setAttribEdit(entId, attribName, text.get()) + + frame = Frame(self.pageOneFrame.interior()) + label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) + label.pack(side=LEFT, expand=0) + widg = Entry(frame, textvariable=text) + widg.bind('', handleReturn) + widg.pack(side=LEFT, fill=X, expand=1) + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + self.curEntWidgets[attribName] = text.set + + def addConstWidget(self, levelSpec, entSpec, entId, attribName, params): + frame = Frame(self.pageOneFrame.interior()) + label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) + label.pack(side=LEFT, expand=0) + text = str(entSpec[attribName]) + valueLabel = Label(frame, text=text, anchor=W, justify=LEFT, relief=GROOVE) + valueLabel.pack(side=LEFT, fill=X, expand=1) + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + + def addPythonWidget(self, levelSpec, entSpec, entId, attribName, params): + text = StringVar() + text.set(repr(entSpec[attribName])) + + def handleReturn(event): + self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) + + frame = Frame(self.pageOneFrame.interior()) + label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) + label.pack(side=LEFT, expand=0) + widg = Entry(frame, textvariable=text) + widg.bind('', handleReturn) + widg.pack(side=LEFT, fill=X, expand=1) + frame.pack(fill=X, expand=1) + self.attribWidgets.append(frame) + self.curEntWidgets[attribName] = lambda x: text.set(repr(x)) + + def handleAttributeChangeSubmit(self, attribName, textObj, entId, levelSpec): + newText = textObj.get() + try: + value = eval(newText) + except: + showwarning('ERROR', 'that is not a valid Python object', parent=self.parent) + return + + self.level.setAttribEdit(entId, attribName, value) + + def handleAttribChange(self, entId, attribName, value, username): + if username == self.level.editUsername: + if self.entityCopy: + self.entityCopy.removeNode() + self.entityCopy = None + if entId == self.curEntId: + widgetSetter = self.curEntWidgets[attribName] + if widgetSetter is not None: + widgetSetter(value) + return + + def createButtons(self): + pass + + def toggleBalloon(self): + if self.toggleBalloonVar.get(): + self.balloon().configure(state='both') + else: + self.balloon().configure(state='none') + + def onDestroy(self, event): + from direct.directtools import DirectSession + direct.disable() + bboard.remove(DirectSession.DirectSession.DIRECTdisablePost) + self.ignore(self.level.getAttribChangeEventName()) + self.ignore('DIRECT_selectedNodePath') + self.ignore('DIRECT_manipulateObjectCleanup') + self.ignore('DIRECT_undo') + self.ignore('DIRECT_redo') + print 'InGameEditor.onDestroy()' + if self.visZonesEditor: + self.visZonesEditor.destroy() + self.explorer._node.destroy() + del self.level + messenger.send(self.doneEvent) + + def handleRequestSave(self): + messenger.send(self.requestSaveEvent) + + def handleSaveAs(self): + filename = tkFileDialog.asksaveasfilename(parent=self.parent, defaultextension='.py', filetypes=[('Python Source Files', '.py'), ('All Files', '*')]) + if len(filename) > 0: + messenger.send(self.saveAsEvent, [filename]) + + def doUndo(self): + messenger.send(self.undoEvent) + + def doRedo(self): + messenger.send(self.redoEvent) + + def doWireframe(self): + messenger.send(self.wireframeEvent) + + def doOobe(self): + messenger.send(self.oobeEvent) + + def doCs(self): + messenger.send(self.csEvent) + + def doRun(self): + messenger.send(self.runEvent) + + def doTex(self): + messenger.send(self.texEvent) + + def resetLevel(self): + self.showTodo(what='resetLevel') + + def showTodo(self, what = ''): + self.showWarning('%s\nThis is not yet implemented.' % what, 'TODO') + + def showWarning(self, msg, title = 'Warning'): + showwarning(title, msg, parent=self.parent) + + def askYesNo(self, msg, title = 'Query'): + return askyesno(title, msg, parent=self.parent) + + def popupLevelDialog(self): + data = askstring('Input Level Data', 'Level Data:', parent=self) + if data: + self.messageBar().helpmessage(data) + + +class LevelVisZonesEditor(Pmw.MegaToplevel): + + def __init__(self, editor, entId, visible, modelZones = [], updateCommand = None, parent = None, **kw): + DGG.INITOPT = Pmw_INITOPT + optiondefs = (('title', 'Level Vis-Zone Editor', None),) + self.defineoptions(kw, optiondefs) + Pmw.MegaToplevel.__init__(self, parent, title=self['title']) + self.editor = editor + self.entId = entId + self.modelZones = modelZones + self.modelZones.sort() + self.updateCommand = updateCommand + hull = self.component('hull') + balloon = self.balloon = Pmw.Balloon(hull) + self.balloon.configure(state='none') + menuFrame = Frame(hull, relief=GROOVE, bd=2) + menuFrame.pack(fill=X, expand=1) + menuBar = Pmw.MenuBar(menuFrame, hotkeys=1, balloon=balloon) + menuBar.pack(side=LEFT, expand=1, fill=X) + menuBar.addmenu('Vis Zones Editor', 'Visability Zones Editor Operations') + menuBar.addmenuitem('Vis Zones Editor', 'command', 'Exit Visability Zones Editor', label='Exit', command=self.destroy) + menuBar.addmenu('Help', 'Visibility Zones Editor Help Operations') + self.toggleBalloonVar = IntVar() + self.toggleBalloonVar.set(0) + menuBar.addmenuitem('Help', 'checkbutton', 'Toggle balloon help', label='Balloon Help', variable=self.toggleBalloonVar, command=self.toggleBalloon) + sf = Pmw.ScrolledFrame(hull, horizflex='elastic', usehullsize=1, hull_width=200, hull_height=400) + frame = sf.interior() + sf.pack(padx=5, pady=3, fill=BOTH, expand=1) + self.radioSelect = Pmw.RadioSelect(frame, selectmode=MULTIPLE, orient=DGG.VERTICAL, pady=0, command=self.toggleVisZone) + self.buttons = [] + for modelZoneNum in self.modelZones: + buttonStr = '%d - %s' % (modelZoneNum, self.editor.getEntityName(modelZoneNum)) + button = self.radioSelect.add(buttonStr, width=12) + button.configure(anchor=W, justify=LEFT) + self.buttons.append(button) + + self.radioSelect.pack(expand=1, fill=X) + sf.reposition() + buttonFrame = Frame(hull) + self.showMode = IntVar() + self.showMode.set(self.editor.level.level.getColorZones()) + self.showActiveButton = Radiobutton(buttonFrame, text='Stash Invisible', value=0, indicatoron=1, variable=self.showMode, command=self.setVisRenderMode) + self.showActiveButton.pack(side=LEFT, fill=X, expand=1) + self.showAllButton = Radiobutton(buttonFrame, text='Color Invisible', value=1, indicatoron=1, variable=self.showMode, command=self.setVisRenderMode) + self.showAllButton.pack(side=LEFT, fill=X, expand=1) + buttonFrame.pack(fill=X, expand=1) + self.initialiseoptions(LevelVisZonesEditor) + self.setVisible(visible) + return None + + def toggleVisZone(self, zoneStr, state): + zoneNum = int(zoneStr.split('-')[0]) + if state == 0: + if zoneNum in self.visible: + self.visible.remove(zoneNum) + elif zoneNum not in self.visible: + self.visible.append(zoneNum) + self.visible.sort() + if self.updateCommand: + self.updateCommand(self.visible) + + def setVisible(self, visible): + self.visible = visible + self.refreshVisibility() + + def setVisRenderMode(self): + self.editor.level.level.setColorZones(self.showMode.get()) + + def refreshVisibility(self): + self.radioSelect.selection = [] + for button in self.buttons: + componentName = button['text'] + modelZone = int(componentName.split('-')[0]) + if modelZone in self.visible: + button.configure(relief='sunken') + self.radioSelect.selection.append(componentName) + else: + button.configure(relief='raised') + + def destroy(self): + self.editor.visZonesEditor = None + Pmw.MegaToplevel.destroy(self) + return + + def toggleBalloon(self): + if self.toggleBalloonVar.get(): + self.balloon.configure(state='balloon') + else: + self.balloon.configure(state='none') + + +DEFAULT_MENU_ITEMS = ['Update Explorer', + 'Separator', + 'Select', + 'Deselect', + 'Separator', + 'Delete', + 'Separator', + 'Fit', + 'Flash', + 'Isolate', + 'Toggle Vis', + 'Show All', + 'Separator', + 'Set Reparent Target', + 'Reparent', + 'WRT Reparent', + 'Separator', + 'Place', + 'Set Name', + 'Set Color', + 'Explore', + 'Separator'] + +class LevelExplorer(Pmw.MegaWidget, DirectObject.DirectObject): + + def __init__(self, parent = None, editor = None, **kw): + optiondefs = (('menuItems', [], Pmw.INITOPT),) + self.defineoptions(kw, optiondefs) + Pmw.MegaWidget.__init__(self, parent) + self.editor = editor + interior = self.interior() + interior.configure(relief=GROOVE, borderwidth=2) + self._scrolledCanvas = self.createcomponent('scrolledCanvas', (), None, Pmw.ScrolledCanvas, (interior,), hull_width=300, hull_height=80, usehullsize=1) + self._canvas = self._scrolledCanvas.component('canvas') + self._canvas['scrollregion'] = ('0i', '0i', '2i', '4i') + self._scrolledCanvas.resizescrollregion() + self._scrolledCanvas.pack(padx=3, pady=3, expand=1, fill=BOTH) + self._canvas.bind('', self.mouse2Down) + self._canvas.bind('', self.mouse2Motion) + self._canvas.bind('', lambda e, sc = self._scrolledCanvas: sc.resizescrollregion()) + self.interior().bind('', self.onDestroy) + self._treeItem = LevelExplorerItem(self.editor.level, self.editor) + self._node = TreeNode(self._canvas, None, self._treeItem, DEFAULT_MENU_ITEMS + self['menuItems']) + self._node.expand() + self._parentFrame = Frame(interior) + self.accept('Level_Update Explorer', lambda f, s = self: s.update()) + self.initialiseoptions(LevelExplorer) + return + + def update(self, fUseCachedChildren = 1): + self._node.update(fUseCachedChildren) + self._node.expand() + + def mouse2Down(self, event): + self._width = 1.0 * self._canvas.winfo_width() + self._height = 1.0 * self._canvas.winfo_height() + xview = self._canvas.xview() + yview = self._canvas.yview() + self._left = xview[0] + self._top = yview[0] + self._dxview = xview[1] - xview[0] + self._dyview = yview[1] - yview[0] + self._2lx = event.x + self._2ly = event.y + + def mouse2Motion(self, event): + newx = self._left - (event.x - self._2lx) / self._width * self._dxview + self._canvas.xview_moveto(newx) + newy = self._top - (event.y - self._2ly) / self._height * self._dyview + self._canvas.yview_moveto(newy) + self._2lx = event.x + self._2ly = event.y + self._left = self._canvas.xview()[0] + self._top = self._canvas.yview()[0] + + def onDestroy(self, event): + self.ignore('Level_Update Explorer') + + +class LevelExplorerItem(TreeItem): + + def __init__(self, levelElement, editor): + self.levelElement = levelElement + self.editor = editor + + def GetText(self): + return self.levelElement.getName() + + def GetKey(self): + return self.levelElement.id() + + def IsEditable(self): + return 1 + + def SetText(self, text): + self.levelElement.setNewName(text) + + def GetIconName(self): + return 'sphere2' + + def IsExpandable(self): + return self.levelElement.getNumChildren() != 0 + + def GetSubList(self): + sublist = [] + for element in self.levelElement.getChildren(): + item = LevelExplorerItem(element, self.editor) + sublist.append(item) + + return sublist + + def OnSelect(self): + messenger.send(self.editor.getEventMsgName('Select'), [self.levelElement]) + + def MenuCommand(self, command): + messenger.send(self.editor.getEventMsgName(command), [self.levelElement]) diff --git a/toontown/coghq/InGameEditorDCImports.py b/toontown/coghq/InGameEditorDCImports.py new file mode 100644 index 0000000..fd5f750 --- /dev/null +++ b/toontown/coghq/InGameEditorDCImports.py @@ -0,0 +1,3 @@ +if __dev__: + from direct.directutil import DistributedLargeBlobSender + import DistributedInGameEditor diff --git a/toontown/coghq/InGameEditorDCImportsAI.py b/toontown/coghq/InGameEditorDCImportsAI.py new file mode 100644 index 0000000..9ce0941 --- /dev/null +++ b/toontown/coghq/InGameEditorDCImportsAI.py @@ -0,0 +1,3 @@ +if __dev__: + from direct.directutil import DistributedLargeBlobSenderAI + import DistributedInGameEditorAI diff --git a/toontown/coghq/InGameEditorElements.py b/toontown/coghq/InGameEditorElements.py new file mode 100644 index 0000000..73cba9f --- /dev/null +++ b/toontown/coghq/InGameEditorElements.py @@ -0,0 +1,37 @@ +from direct.showbase import DirectObject + +class InGameEditorElement(DirectObject.DirectObject): + elementId = 0 + + def __init__(self, children = []): + self.elementId = InGameEditorElement.elementId + InGameEditorElement.elementId += 1 + self.setChildren(children) + self.feName = self.getTypeName() + + def getName(self): + return self.feName + + def setNewName(self, newName): + self.feName = newName + + def getTypeName(self): + return 'Level Element' + + def id(self): + return self.elementId + + def getChildren(self): + return self.children + + def setChildren(self, children): + self.children = list(children) + + def addChild(self, child): + self.children.append(child) + + def removeChild(self, child): + self.children.remove(child) + + def getNumChildren(self): + return len(self.children) diff --git a/toontown/coghq/LaserGameAvoid.py b/toontown/coghq/LaserGameAvoid.py new file mode 100644 index 0000000..311f284 --- /dev/null +++ b/toontown/coghq/LaserGameAvoid.py @@ -0,0 +1,71 @@ +from toontown.coghq import LaserGameBase +from direct.distributed import ClockDelta +from direct.task import Task +import random + +class LaserGameAvoid(LaserGameBase.LaserGameBase): + + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(8, 8) + self.blankGrid() + self.cycleName = simbase.air.trueUniqueName('AvoidGame') + + def delete(self): + LaserGameBase.LaserGameBase.delete(self) + self.endTask() + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + self.endTask() + + LaserGameBase.LaserGameBase.win(self) + + def lose(self): + self.endTask() + self.blankGrid() + self.funcSendGrid() + LaserGameBase.LaserGameBase.lose(self) + + def endTask(self): + taskMgr.remove(self.cycleName) + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + tile = random.choice([ + 0, + 14, + 12]) + self.gridData[column][row] = tile + + taskMgr.doMethodLater(2.5, self.__cycle, self.cycleName) + + def __cycle(self, taskMgrFooler = 0): + if not hasattr(self, 'gridNumX'): + return Task.done + + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + if self.gridData[column][row] == 0: + tile = random.choice([ + 0, + 14]) + self.gridData[column][row] = tile + else: + if self.gridData[column][row] == 14: + tile = 12 + self.gridData[column][row] = tile + else: + if self.gridData[column][row] == 12: + tile = 0 + self.gridData[column][row] = tile + + if not self.finshed: + taskMgr.doMethodLater(2.5, self.__cycle, self.cycleName) + self.funcSendGrid() + + return Task.done diff --git a/toontown/coghq/LaserGameBase.py b/toontown/coghq/LaserGameBase.py new file mode 100644 index 0000000..add716d --- /dev/null +++ b/toontown/coghq/LaserGameBase.py @@ -0,0 +1,55 @@ +from direct.distributed import ClockDelta +from direct.task import Task +import random + +class LaserGameBase: + + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + self.funcSuccess = funcSuccess + self.funcFail = funcFail + self.funcSendGrid = funcSendGrid + self.funcSetGrid = funcSetGrid + self.setGridSize(2, 2) + self.blankGrid() + self.finshed = 0 + + def delete(self): + funcSuccess = None + funcFail = None + funcSendGrid = None + funcSetGrid = None + + def setGridSize(self, x, y): + self.gridNumX = x + self.gridNumY = y + + def blankGrid(self): + self.gridData = [] + for i in range(0, self.gridNumX): + self.gridData.append([ + 0] * self.gridNumY) + + def win(self): + if not self.finshed: + self.finshed = 1 + self.funcSuccess() + + def lose(self): + if not self.finshed: + self.finshed = 1 + self.funcFail() + + def startGrid(self): + self.blankGrid() + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return + + if self.checkForWin(): + self.win() + else: + self.funcSendGrid() + + def checkForWin(self): + return 0 diff --git a/toontown/coghq/LaserGameDrag.py b/toontown/coghq/LaserGameDrag.py new file mode 100644 index 0000000..35fffe4 --- /dev/null +++ b/toontown/coghq/LaserGameDrag.py @@ -0,0 +1,125 @@ +from toontown.coghq import LaserGameBase +from direct.distributed import ClockDelta +from direct.task import Task +import random + +class LaserGameDrag(LaserGameBase.LaserGameBase): + + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(6, 6) + self.blankGrid() + self.symbolList = [ + 16, + 13, + 17] + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.win(self) + + def lose(self): + self.blankGrid() + self.funcSendGrid() + LaserGameBase.LaserGameBase.lose(self) + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + tile = 0 + self.gridData[column][row] = tile + + for column in range(0, self.gridNumX): + self.gridData[column][self.gridNumY - 1] = 12 + + for symbol in self.symbolList: + finished = 0 + while finished == 0: + numTris = 4 + tris = 0 + sanity = 1000 + if numTris >= 1: + while tris < numTris and sanity: + sanity -= 1 + column = random.randint(0, self.gridNumX - 1) + row = random.randint(1, self.gridNumY - 1) + if self.gridData[column][row] == 0: + self.gridData[column][row] = symbol + tris += 1 + + if self.checkFor3(symbol): + self.clearIndex(symbol) + finished = 0 + else: + finished = 1 + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return + + if oldx >= 0 and oldy >= 0: + if self.gridData[hitX][hitY] == 0: + if self.gridData[oldx][oldy] in self.symbolList: + self.gridData[hitX][hitY] = self.gridData[oldx][oldy] + self.gridData[oldx][oldy] = 0 + + for index in self.symbolList: + if self.checkFor3(index): + self.clearIndex(index) + + if self.checkForWin(): + self.win() + else: + self.funcSendGrid() + + def checkFor3(self, index): + numInARow = 0 + for posX in range(0, self.gridNumX): + for posY in range(0, self.gridNumY): + if self.gridData[posX][posY] == index: + numInARow += 1 + if numInARow >= 3: + return 1 + else: + numInARow = 0 + + numInARow = 0 + + numInARow = 0 + for posY in range(0, self.gridNumY): + for posX in range(0, self.gridNumX): + if self.gridData[posX][posY] == index: + numInARow += 1 + if numInARow >= 3: + return 1 + else: + numInARow = 0 + + numInARow = 0 + + return 0 + + def clearIndex(self, index): + for posX in range(0, self.gridNumX): + for posY in range(0, self.gridNumY): + if self.gridData[posX][posY] == index: + self.gridData[posX][posY] = 0 + + def checkForClear(self, index): + for posX in range(0, self.gridNumX): + for posY in range(0, self.gridNumY): + if self.gridData[posX][posY] == index: + return 0 + + return 1 + + def checkForWin(self): + for symbol in self.symbolList: + if not self.checkForClear(symbol): + return 0 + + return 1 diff --git a/toontown/coghq/LaserGameMineSweeper.py b/toontown/coghq/LaserGameMineSweeper.py new file mode 100644 index 0000000..2f688b0 --- /dev/null +++ b/toontown/coghq/LaserGameMineSweeper.py @@ -0,0 +1,144 @@ +from toontown.coghq import LaserGameBase +from direct.distributed import ClockDelta +from direct.task import Task +import random + +class LaserGameMineSweeper(LaserGameBase.LaserGameBase): + + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(7, 7) + self.blankGrid() + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.win(self) + + def lose(self): + if not self.finshed: + self.revealAll() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.lose(self) + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + self.hiddenData = [] + for i in range(0, self.gridNumX): + self.hiddenData.append([ + 0] * self.gridNumY) + + numBombs = int(self.gridNumX * self.gridNumY / 8) + numBombs += 1 + bomb = 0 + sanity = 1000 + if numBombs > 1: + while bomb < numBombs and sanity: + sanity -= 1 + column = random.randint(0, self.gridNumX - 1) + row = random.randint(1, self.gridNumY - 1) + if self.hiddenData[column][row] != 12 and self.neighborSum(column, row) < 2 and self.rowSum(row) < numBombs / 3: + self.hiddenData[column][row] = 12 + bomb += 1 + + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + if self.hiddenData[column][row] == 12: + self.gridData[column][row] = 11 + else: + self.gridData[column][row] = 10 + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return + + if self.hiddenData[hitX][hitY] == 12: + self.gridData[hitX][hitY] = 12 + else: + self.neighborReveal(hitX, hitY) + self.funcSendGrid() + + def revealAll(self): + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + self.neighborReveal(column, row, 1) + + def neighborReveal(self, hitX, hitY, showBomb = 0): + if showBomb and self.gridData[hitX][hitY] == 11: + self.gridData[hitX][hitY] = 12 + + if self.gridData[hitX][hitY] != 10: + return + + self.gridData[hitX][hitY] = self.neighborSum(hitX, hitY) + if self.neighborSum(hitX, hitY) == 0: + if hitX > 0 and hitY > 0: + self.neighborReveal(hitX - 1, hitY - 1) + + if hitY > 0: + self.neighborReveal(hitX, hitY - 1) + + if hitX < self.gridNumX - 1 and hitY > 0: + self.neighborReveal(hitX + 1, hitY - 1) + + if hitX > 0: + self.neighborReveal(hitX - 1, hitY) + + if hitX < self.gridNumX - 1: + self.neighborReveal(hitX + 1, hitY) + + if hitX > 0 and hitY < self.gridNumY - 1: + self.neighborReveal(hitX - 1, hitY + 1) + + if hitY < self.gridNumY - 1: + self.neighborReveal(hitX, hitY + 1) + + if hitX < self.gridNumX - 1 and hitY < self.gridNumY - 1: + self.neighborReveal(hitX + 1, hitY + 1) + + def rowSum(self, y): + sum = 0 + for i in range(0, self.gridNumX - 1): + if self.hiddenData[i][y] == 12: + sum += 1 + + return sum + + def neighborSum(self, hitX, hitY): + sum = 0 + if hitX > 0 and hitY > 0: + if self.hiddenData[hitX - 1][hitY - 1] == 12: + sum += 1 + + if hitY > 0: + if self.hiddenData[hitX][hitY - 1] == 12: + sum += 1 + + if hitX < self.gridNumX - 1 and hitY > 0: + if self.hiddenData[hitX + 1][hitY - 1] == 12: + sum += 1 + + if hitX > 0: + if self.hiddenData[hitX - 1][hitY] == 12: + sum += 1 + + if hitX < self.gridNumX - 1: + if self.hiddenData[hitX + 1][hitY] == 12: + sum += 1 + + if hitX > 0 and hitY < self.gridNumY - 1: + if self.hiddenData[hitX - 1][hitY + 1] == 12: + sum += 1 + + if hitY < self.gridNumY - 1: + if self.hiddenData[hitX][hitY + 1] == 12: + sum += 1 + + if hitX < self.gridNumX - 1 and hitY < self.gridNumY - 1: + if self.hiddenData[hitX + 1][hitY + 1] == 12: + sum += 1 + + return sum diff --git a/toontown/coghq/LaserGameRoll.py b/toontown/coghq/LaserGameRoll.py new file mode 100644 index 0000000..6592d06 --- /dev/null +++ b/toontown/coghq/LaserGameRoll.py @@ -0,0 +1,65 @@ +from toontown.coghq import LaserGameBase +from direct.distributed import ClockDelta +from direct.task import Task +import random + +class LaserGameRoll(LaserGameBase.LaserGameBase): + + def __init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid): + LaserGameBase.LaserGameBase.__init__(self, funcSuccess, funcFail, funcSendGrid, funcSetGrid) + self.setGridSize(5, 5) + self.blankGrid() + + def win(self): + if not self.finshed: + self.blankGrid() + self.funcSendGrid() + + LaserGameBase.LaserGameBase.win(self) + + def lose(self): + self.blankGrid() + self.funcSendGrid() + LaserGameBase.LaserGameBase.lose(self) + + def startGrid(self): + LaserGameBase.LaserGameBase.startGrid(self) + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + tile = random.choice([ + 10, + 13]) + self.gridData[column][row] = tile + + for column in range(0, self.gridNumX): + self.gridData[column][self.gridNumY - 1] = 12 + + def hit(self, hitX, hitY, oldx = -1, oldy = -1): + if self.finshed: + return + + if self.gridData[hitX][hitY] == 10: + self.gridData[hitX][hitY] = 13 + elif self.gridData[hitX][hitY] == 13: + self.gridData[hitX][hitY] = 10 + + if self.checkForWin(): + self.win() + else: + self.funcSendGrid() + + def checkForWin(self): + count1 = 0 + count2 = 0 + for column in range(0, self.gridNumX): + for row in range(0, self.gridNumY): + if self.gridData[column][row] == 10: + count1 += 1 + else: + if self.gridData[column][row] == 13: + count2 += 1 + + if count1 and count2: + return 0 + else: + return 1 diff --git a/toontown/coghq/LawOfficeBase.py b/toontown/coghq/LawOfficeBase.py new file mode 100644 index 0000000..9252a00 --- /dev/null +++ b/toontown/coghq/LawOfficeBase.py @@ -0,0 +1,27 @@ +import FactorySpecs +from otp.level import LevelSpec +from toontown.toonbase import ToontownGlobals + +class LawOfficeBase: + + def __init__(self): + pass + + def setLawOfficeId(self, factoryId): + self.lawOfficeId = factoryId + self.factoryType = ToontownGlobals.factoryId2factoryType[factoryId] + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(factoryId) + + def getCogTrack(self): + return self.cogTrack + + def getFactoryType(self): + return self.factoryType + + if __dev__: + + def getEntityTypeReg(self): + import FactoryEntityTypes + from otp.level import EntityTypeRegistry + typeReg = EntityTypeRegistry.EntityTypeRegistry(FactoryEntityTypes) + return typeReg diff --git a/toontown/coghq/LawOfficeLayout.py b/toontown/coghq/LawOfficeLayout.py new file mode 100644 index 0000000..46a10da --- /dev/null +++ b/toontown/coghq/LawOfficeLayout.py @@ -0,0 +1,38 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from toontown.coghq import MintRoomSpecs +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import normalDistrib, lerp +import random +OfficeBuildingFloorSequences = { + 13300: [ + (0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0)]} +Index2Spec = { + 0: 'LawOffice_Spec_Tier0_a', + 1: 'LawOffice_Spec_Tier0_b'} +LawbotFloorSpecs = {} +for (floorIndex, floorSpec) in Index2Spec.items(): + exec 'from toontown.coghq import %s' % floorSpec + LawbotFloorSpecs[floorIndex] = eval(floorSpec) + + +class LawOfficeLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('MintLayout') + + def __init__(self, lawOfficeId): + self.lawOfficeId = lawOfficeId + self.floorIds = [] + if self.lawOfficeId in OfficeBuildingFloorSequences: + self.floorIds = OfficeBuildingFloorSequences[self.lawOfficeId][random.randint(0, len(OfficeBuildingFloorSequences[self.lawOfficeId])) - 1] + else: + self.notify.warning('no layout for Law Office ID: using defaults') + self.floorIds = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + + def getFloorId(self, index): + return self.floorIds[index] + + def getFloorSpec(self, index): + return LawbotFloorSpecs[self.floorIds[index]] + + def getFloorIds(self): + return self.floorIds diff --git a/toontown/coghq/LawOfficeManagerAI.py b/toontown/coghq/LawOfficeManagerAI.py new file mode 100644 index 0000000..eff2e82 --- /dev/null +++ b/toontown/coghq/LawOfficeManagerAI.py @@ -0,0 +1,48 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedLawOfficeAI, DistributedStageAI +from toontown.coghq import StageLayout +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject +import random +StageId2Layouts = {ToontownGlobals.LawbotStageIntA: (0, 1, 2), ToontownGlobals.LawbotStageIntB: (3, 4, 5), ToontownGlobals.LawbotStageIntC: (6, 7, 8), ToontownGlobals.LawbotStageIntD: (9, 10, 11)} + +class LawOfficeManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('LawOfficeManagerAI') + lawOfficeId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createLawOffice(self, StageId, entranceId, players): + for avId in players: + if bboard.has('StageId-%s' % avId): + StageId = bboard.get('StageId-%s' % avId) + break + + floor = 0 + layoutIndex = None + for avId in players: + if bboard.has('stageRoom-%s' % avId): + roomId = bboard.get('stageRoom-%s' % avId) + for lt in StageId2Layouts[StageId]: + for i in xrange(StageLayout.getNumFloors(lt)): + layout = StageLayout.StageLayout(StageId, i, stageLayout=lt) + if roomId in layout.getRoomIds(): + layoutIndex = lt + floor = i + + else: + from toontown.coghq import StageRoomSpecs + roomName = StageRoomSpecs.CashbotStageRoomId2RoomName[roomId] + LawOfficeManagerAI.notify.warning('room %s (%s) not found in any floor of Stage %s' % (roomId, roomName, StageId)) + + StageZone = self.air.allocateZone() + if layoutIndex is None: + layoutIndex = random.choice(StageId2Layouts[StageId]) + Stage = DistributedStageAI.DistributedStageAI(self.air, StageId, StageZone, floor, players, layoutIndex) + Stage.generateWithRequired(StageZone) + return StageZone diff --git a/toontown/coghq/LawOffice_Spec_Tier0_a.py b/toontown/coghq/LawOffice_Spec_Tier0_a.py new file mode 100644 index 0000000..838186b --- /dev/null +++ b/toontown/coghq/LawOffice_Spec_Tier0_a.py @@ -0,0 +1,42 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = { + 1000: { + 'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/lawbotHQ/LawbotCourtroom3', + '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': []}, + 100000: { + 'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(68.7225, -27.4154, 71), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'laserFactor': 3, + 'modelPath': 0}} +Scenario0 = {} +levelSpec = { + 'globalEntities': GlobalEntities, + 'scenarios': [ + Scenario0]} diff --git a/toontown/coghq/LawOffice_Spec_Tier0_b.py b/toontown/coghq/LawOffice_Spec_Tier0_b.py new file mode 100644 index 0000000..3da523f --- /dev/null +++ b/toontown/coghq/LawOffice_Spec_Tier0_b.py @@ -0,0 +1,60 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = { + 1000: { + 'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/lawbotHQ/LawbotCourtroom3', + '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': []}, + 100000: { + 'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.80022, -32.6574, 70), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Vec3(1, 1, 1), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}} +Scenario0 = {} +levelSpec = { + 'globalEntities': GlobalEntities, + 'scenarios': [ + Scenario0]} diff --git a/toontown/coghq/LawbotCogHQLoader.py b/toontown/coghq/LawbotCogHQLoader.py new file mode 100644 index 0000000..b5f01b9 --- /dev/null +++ b/toontown/coghq/LawbotCogHQLoader.py @@ -0,0 +1,115 @@ +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 +import StageInterior +import LawbotHQExterior +import LawbotHQBossBattle +import LawbotOfficeExterior +aspectSF = 0.7227 + +class LawbotCogHQLoader(CogHQLoader.CogHQLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotCogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent) + self.fsm.addState(State.State('stageInterior', self.enterStageInterior, self.exitStageInterior, ['quietZone', 'cogHQExterior'])) + self.fsm.addState(State.State('factoryExterior', self.enterFactoryExterior, self.exitFactoryExterior, ['quietZone', 'cogHQExterior'])) + for stateName in ['start', 'cogHQExterior', 'quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('stageInterior') + + for stateName in ['quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('factoryExterior') + + self.musicFile = 'phase_11/audio/bgm/LB_courtyard.mid' + self.cogHQExteriorModelPath = 'phase_11/models/lawbotHQ/LawbotPlaza' + self.factoryExteriorModelPath = 'phase_11/models/lawbotHQ/LB_DA_Lobby' + self.cogHQLobbyModelPath = 'phase_11/models/lawbotHQ/LB_CH_Lobby' + self.geom = None + return + + def load(self, zoneId): + CogHQLoader.CogHQLoader.load(self, zoneId) + Toon.loadSellbotHQAnims() + + 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.LawbotHQ=%d' % (zoneId, ToontownGlobals.LawbotHQ)) + if zoneId == ToontownGlobals.LawbotHQ: + self.geom = loader.loadModel(self.cogHQExteriorModelPath) + ug = self.geom.find('**/underground') + ug.setBin('ground', -10) + brLinkTunnel = self.geom.find('**/TunnelEntrance1') + brLinkTunnel.setName('linktunnel_br_3326_DNARoot') + elif zoneId == ToontownGlobals.LawbotOfficeExt: + self.geom = loader.loadModel(self.factoryExteriorModelPath) + ug = self.geom.find('**/underground') + ug.setBin('ground', -10) + elif zoneId == ToontownGlobals.LawbotLobby: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGHQ: Visit LawbotLobby') + self.notify.debug('cogHQLobbyModelPath = %s' % self.cogHQLobbyModelPath) + self.geom = loader.loadModel(self.cogHQLobbyModelPath) + ug = self.geom.find('**/underground') + ug.setBin('ground', -10) + else: + self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId) + CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId) + + 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 LawbotHQExterior.LawbotHQExterior + + def getBossPlaceClass(self): + self.notify.debug('getBossPlaceClass') + return LawbotHQBossBattle.LawbotHQBossBattle + + def enterFactoryExterior(self, requestStatus): + self.placeClass = LawbotOfficeExterior.LawbotOfficeExterior + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitFactoryExterior(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterCogHQBossBattle(self, requestStatus): + self.notify.debug('LawbotCogHQLoader.enterCogHQBossBattle') + CogHQLoader.CogHQLoader.enterCogHQBossBattle(self, requestStatus) + base.cr.forbidCheesyEffects(1) + + def exitCogHQBossBattle(self): + self.notify.debug('LawbotCogHQLoader.exitCogHQBossBattle') + CogHQLoader.CogHQLoader.exitCogHQBossBattle(self) + base.cr.forbidCheesyEffects(0) diff --git a/toontown/coghq/LawbotHQBossBattle.py b/toontown/coghq/LawbotHQBossBattle.py new file mode 100644 index 0000000..14fcc35 --- /dev/null +++ b/toontown/coghq/LawbotHQBossBattle.py @@ -0,0 +1,24 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.suit import DistributedLawbotBoss +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQBossBattle + +class LawbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotHQBossBattle') + + 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, DistributedLawbotBoss.OneBossCog) + + def exit(self): + CogHQBossBattle.CogHQBossBattle.exit(self) diff --git a/toontown/coghq/LawbotHQExterior.py b/toontown/coghq/LawbotHQExterior.py new file mode 100644 index 0000000..2cdb286 --- /dev/null +++ b/toontown/coghq/LawbotHQExterior.py @@ -0,0 +1,11 @@ +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 LawbotHQExterior(CogHQExterior.CogHQExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotHQExterior') diff --git a/toontown/coghq/LawbotLegFactoryCogs.py b/toontown/coghq/LawbotLegFactoryCogs.py new file mode 100644 index 0000000..eadb991 --- /dev/null +++ b/toontown/coghq/LawbotLegFactoryCogs.py @@ -0,0 +1,6 @@ +from SpecImports import * +LobbyParent = 10014 +LobbyCell = 0 +BattleCells = {} +ReserveCogData = [] +CogData = [] diff --git a/toontown/coghq/LawbotLegFactorySpec.py b/toontown/coghq/LawbotLegFactorySpec.py new file mode 100644 index 0000000..6cbc91f --- /dev/null +++ b/toontown/coghq/LawbotLegFactorySpec.py @@ -0,0 +1,26 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_10/models/lawbotHQ/LawbotCourtroom3', + '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': []}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Action01.py b/toontown/coghq/LawbotOfficeBoilerRoom_Action01.py new file mode 100644 index 0000000..541e05f --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Action01.py @@ -0,0 +1,763 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 10010: {'type': 'attribModifier', + 'name': 'goonStrength', + 'comment': '', + 'parentEntId': 100021, + 'attribName': 'strength', + 'recursive': 1, + 'typeName': 'goon', + 'value': '15'}, + 10015: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0, 2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 3.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10017: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 3.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10026: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10024, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 3.0, + 'radius': 1.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10027: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(51.033, -2.36694, -0.821283), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 7.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10028: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(14.9923, 28.704, -0.821283), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 7.518188505180132, + 'showSolid': 0, + 'solidType': 'tube'}, + 10038: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-2.32046, 0.439226, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 6.0, + 'radius': 0.8, + 'showSolid': 0, + 'solidType': 'tube'}, + 10041: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10040, + 'pos': Point3(2.4512, 1.72783, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 4.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10001: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10008: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10012: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10020: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10047: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10052: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10057: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 10007: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(164.055, 0, 0), + 'scale': Vec3(7, 7, 7), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'hardhat', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/char/Cog_Goonie-zero'}, + 10018: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(215, 0, 0), + 'scale': Vec3(7, 7, 7), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'hardhat', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/char/Cog_Goonie-zero'}, + 10024: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10025, + 'pos': Point3(39.8396, -3.38811, 2), + 'hpr': Vec3(45, 280, 0), + 'scale': Vec3(10, 10, 10), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'hardhat', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/char/Cog_Goonie-zero'}, + 10031: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10032: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(1.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10033: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(3.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10034: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-2, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA.bam'}, + 10036: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10035, + 'pos': Point3(-2.6, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA.bam'}, + 10040: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(14.6642, -1.01767, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.32128, 1.32128, 1.32128), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks.bam'}, + 10044: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(1.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10045: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(3.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10060: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10043, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10062: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(1.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10063: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(3.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10064: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10061, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10066: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(8.93303, 26.855, 0), + 'hpr': Vec3(315, 0, 0), + 'scale': Vec3(1.28721, 1.28721, 1.28721), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks.bam'}, + 10067: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(40.4297, -10.7197, 0), + 'hpr': Vec3(41.1859, 0, 0), + 'scale': Vec3(1.38665, 1.38665, 1.38665), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'goonHatType': 'none', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks.bam'}, + 10002: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10005: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10006: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 25, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10013: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10011, + 'pos': Point3(-40, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10022: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10019, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10023: {'type': 'nodepath', + 'name': 'upperFloor', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 60, 10.6166), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10029: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10030: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0, -43.0808, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5)}, + 10035: {'type': 'nodepath', + 'name': 'plants', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0, -0.9, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(5, 5, 5)}, + 10037: {'type': 'nodepath', + 'name': 'filingCabinets', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10039: {'type': 'nodepath', + 'name': 'paperStacks', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10042: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10043: {'type': 'nodepath', + 'name': 'filingCabinets', + 'comment': '', + 'parentEntId': 10042, + 'pos': Point3(-40.2501, 9.25953, 0), + 'hpr': Vec3(41.5, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10048: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10049: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10050: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10046, + 'pos': Point3(10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10053: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10054: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10055: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(10, 15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10058: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10059: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(20, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10061: {'type': 'nodepath', + 'name': 'filingCabinets', + 'comment': '', + 'parentEntId': 10042, + 'pos': Point3(-29.6959, 18.2379, 0), + 'hpr': Point3(41.5, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10065: {'type': 'nodepath', + 'name': 'paperStacks', + 'comment': '', + 'parentEntId': 10042, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100021: {'type': 'nodepath', + 'name': 'gauntlet', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10000: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(10, 35, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10002, + 'pathTarget1': 10004, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10003: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(0, -7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10005, + 'pathTarget1': 10006, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10011: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(0, -35, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10013, + 'pathTarget1': 10014, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10019: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-90, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10021, + 'pathTarget1': 10022, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10046: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10048, + 'pathTarget1': 10049, + 'pathTarget2': 10050, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10051: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-60, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10053, + 'pathTarget1': 10054, + 'pathTarget2': 10055, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10056: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-80, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10058, + 'pathTarget1': 10059, + 'pathTarget2': 0, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10009: {'type': 'rendering', + 'name': 'farCornerGoon', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(-60.62, -37.7557, 0), + 'hpr': Vec3(330, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.2, + 'colorG': 0.2, + 'colorR': 0.5, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 10016: {'type': 'rendering', + 'name': 'nearCornerGoon', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(60, -37.76, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.2, + 'colorG': 0.2, + 'colorR': 0.5, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 10025: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.2, + 'colorG': 0.2, + 'colorR': 0.5, + 'fogOn': 0, + 'renderBin': 'transparent'}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Battle00.py b/toontown/coghq/LawbotOfficeBoilerRoom_Battle00.py new file mode 100644 index 0000000..f64f44f --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Battle00.py @@ -0,0 +1,336 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone8a', + '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}, + 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/LawbotOfficeBoilerRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeBoilerRoom_Battle00_Cogs.py new file mode 100644 index 0000000..991eeca --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_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/LawbotOfficeBoilerRoom_Security00.py b/toontown/coghq/LawbotOfficeBoilerRoom_Security00.py new file mode 100644 index 0000000..892ac7b --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Security00.py @@ -0,0 +1,510 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 100014: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-25.1777, 5.83836, 0.03), + 'hpr': Vec3(92.8624, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100035: {'type': 'gagBarrel', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(16.7354, -42.9601, 0.03), + 'hpr': Vec3(151.049, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100013: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 100012, + 'pos': Point3(0, 0, 0.591849), + 'hpr': Vec3(147.995, 0, 0), + 'scale': Vec3(1, 1, 1), + 'rewardPerGrab': 15, + 'rewardPerGrabMax': 0}, + 100016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(1.77609, -41.5342, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100018: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(27.5451, -41.4709, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100019: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(36.4846, -38.3301, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100020: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(10.7887, -37.8558, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100022: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(32.4792, -42.2737, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100023: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(5.52344, -42.2737, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100024: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-39.2286, -39.5741, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100025: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-62.8751, -40.0794, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100026: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-39.2286, -33.027, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100027: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-31.2652, -39.7321, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100028: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-22.0578, -39.3922, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100029: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(1.77609, -21.8266, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100030: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(10.7887, -18.0704, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100031: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(36.6874, -16.1212, 0), + 'hpr': Vec3(270.526, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100032: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(27.5451, -20.5329, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100033: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(5.52344, -22.4424, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100034: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(32.445, -22.0763, 4.71821), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.6, 1.6, 1.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100036: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(15.0906, 40.376, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100037: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(23.8805, 40.376, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100038: {'type': 'model', + 'name': 'copy of (7)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(31.7416, 40.376, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100039: {'type': 'model', + 'name': 'copy of (8)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(31.7416, 33.2113, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100003: {'type': 'nodepath', + 'name': 'light target 2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-46.465, -27.1019, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100004: {'type': 'nodepath', + 'name': 'light target1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(45.4612, -33.6397, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100006: {'type': 'nodepath', + 'name': 'copy of light target 2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-46.465, 31.2292, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100007: {'type': 'nodepath', + 'name': 'copy of light target1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(22.3708, 14.195, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100008: {'type': 'nodepath', + 'name': 'stompergroup', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-45.2964, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100011: {'type': 'platform', + 'name': '', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(-8.92462, 5.26364, 19.9994), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'floorName': 'platformcollision', + 'modelPath': 'phase_9/models/cogHQ/platform1', + 'modelScale': Vec3(1, 1, 1), + 'motion': 'noBlend', + 'offset': Point3(-10, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'waitPercent': 0.1}, + 100012: {'type': 'platform', + 'name': '', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(-18.1468, -5.11, 20), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'floorName': 'platformcollision', + 'modelPath': 'phase_9/models/cogHQ/platform1', + 'modelScale': Vec3(1, 1, 1), + 'motion': 'noBlend', + 'offset': Point3(0, 0, 0), + 'period': 2, + 'phaseShift': 0.0, + 'waitPercent': 0.1}, + 100002: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-5.84843, -50.8043, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 10.0, + 'switchId': 0, + 'trackTarget1': 100004, + 'trackTarget2': 100003, + 'trackTarget3': 0}, + 100005: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-5.38565, 34.1311, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 10.0, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100007, + 'trackTarget3': 0}, + 100000: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(5, 5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 18.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100001: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(34.0608, 5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.75, + 'range': 12.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100009: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(19.6858, 21.6045, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.5, + 'range': 12.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100010: {'type': 'stomper', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100008, + 'pos': Point3(19.6858, -11.5601, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(8, 4, 8), + 'modelPath': 0, + 'motion': 4, + 'period': 2.0, + 'phaseShift': 0.25, + 'range': 12.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 10, 0.5), + '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/LawbotOfficeBoilerRoom_Trap00.py b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00.py new file mode 100644 index 0000000..a47bc4b --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00.py @@ -0,0 +1,402 @@ +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': ''}, + 100023: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 100022, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 6.0, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 90.0, + 'strength': 15, + 'velocity': 4}, + 100000: {'type': 'model', + 'name': 'wall', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(51.4205, -2.29817, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100002: {'type': 'model', + 'name': 'copy of wall', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(36.415, -2.3, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100005: {'type': 'model', + 'name': 'copy of wall (2)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(23.2853, -0.371783, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100006: {'type': 'model', + 'name': 'copy of wall (3)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(23.2853, -15.2326, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100007: {'type': 'model', + 'name': 'copy of wall (4)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(23.2853, -30.3458, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100008: {'type': 'model', + 'name': 'copy of wall (5)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(8.38448, 29.6127, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100009: {'type': 'model', + 'name': 'copy of wall (6)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(8.38448, 14.8894, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100010: {'type': 'model', + 'name': 'copy of wall (7)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(8.38448, -0.135276, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100011: {'type': 'model', + 'name': 'copy of wall (8)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-8.54444, 0.0828297, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100012: {'type': 'model', + 'name': 'copy of wall (9)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-23.6854, 0.0828297, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100013: {'type': 'model', + 'name': 'copy of wall (10)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -45.2411, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100014: {'type': 'model', + 'name': 'copy of wall (11)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -30.557, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100015: {'type': 'model', + 'name': 'copy of wall (12)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -15.612, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100016: {'type': 'model', + 'name': 'copy of wall (13)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, -0.453893, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100017: {'type': 'model', + 'name': 'copy of wall (14)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, 14.6685, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100018: {'type': 'model', + 'name': 'copy of wall (15)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-36.5, 14.6685, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 100019: {'type': 'model', + 'name': 'copy of wall (16)', + 'comment': '', + 'parentEntId': 100004, + 'pos': Point3(-21.5858, 14.6685, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(0.95, 2, 1.25), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/lawbotHQ/LB_wall_panel.bam'}, + 10001: {'type': 'nodepath', + 'name': 'crates', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.3, 1.3, 1.64892)}, + 10002: {'type': 'nodepath', + 'name': 'rewardBarrels', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.719734, 56.9691, 10.0021), + 'hpr': Vec3(61.6992, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10003: {'type': 'nodepath', + 'name': 'upperWall', + 'comment': 'TODO: replace with lines of shelves', + 'parentEntId': 0, + 'pos': Point3(-20.3203, 52.6549, 9.90873), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1.1143, 1.1143, 1.1143)}, + 10009: {'type': 'nodepath', + 'name': 'toGear0', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-26.5593, 31.856, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10011: {'type': 'nodepath', + 'name': 'toGear1', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-25.884, 13.6749, 0), + 'hpr': Vec3(41.6335, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10023: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10024: {'type': 'nodepath', + 'name': 'rightWall', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(-26.7112, 6.85982, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10028: {'type': 'nodepath', + 'name': 'lowerPuzzle', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10029: {'type': 'nodepath', + 'name': 'entranceWall', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'archStompers', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10040: {'type': 'nodepath', + 'name': 'backWall', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10044: {'type': 'nodepath', + 'name': 'gear', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(11.85, -11.38, 12.528), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10046: {'type': 'nodepath', + 'name': 'supportedCrateBackWall', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(34.9045, -34.0589, -1.51687), + 'hpr': Vec3(63.4349, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10051: {'type': 'nodepath', + 'name': 'supportedCrateEntrance', + 'comment': '', + 'parentEntId': 10028, + 'pos': Point3(48.5077, 7.75915, 0.357897), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10059: {'type': 'nodepath', + 'name': 'largeStack', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(47.98, -16.98, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10061: {'type': 'nodepath', + 'name': 'lower', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100001: {'type': 'nodepath', + 'name': 'trap1 cog node', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100004: {'type': 'nodepath', + 'name': 'maze', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100021: {'type': 'nodepath', + 'name': 'Goon Parent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100003: {'type': 'path', + 'name': 'test goon path', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-50.4808, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0}, + 100020: {'type': 'path', + 'name': 'GoonPath1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 100022: {'type': 'path', + 'name': 'GoonPath1', + 'comment': '', + 'parentEntId': 100021, + 'pos': Point3(-10, -30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 2.0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeBoilerRoom_Trap00_Cogs.py b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00_Cogs.py new file mode 100644 index 0000000..384552a --- /dev/null +++ b/toontown/coghq/LawbotOfficeBoilerRoom_Trap00_Cogs.py @@ -0,0 +1,8 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 100001 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': CogParent, + 'pos': Point3(0, 0, 0)}} +CogData = [] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Action00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Action00.py new file mode 100644 index 0000000..488caa0 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Action00.py @@ -0,0 +1,936 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 10005: {'type': 'attribModifier', + 'name': 'stomperDamage', + 'comment': '', + 'parentEntId': 0, + 'attribName': 'damage', + 'recursive': 1, + 'typeName': 'stomper', + 'value': '10'}, + 10009: {'type': 'attribModifier', + 'name': 'spotlightDamage', + 'comment': '', + 'parentEntId': 0, + 'attribName': 'damPow', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '10'}, + 10032: {'type': 'attribModifier', + 'name': 'radius', + 'comment': '', + 'parentEntId': 10059, + 'attribName': 'radius', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '7'}, + 10036: {'type': 'attribModifier', + 'name': 'accel', + 'comment': '', + 'parentEntId': 10059, + 'attribName': 'accel', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '2'}, + 10066: {'type': 'attribModifier', + 'name': 'maxVel', + 'comment': '', + 'parentEntId': 10059, + 'attribName': 'maxVel', + 'recursive': 1, + 'typeName': 'securityCamera', + 'value': '10'}, + 10000: {'type': 'nodepath', + 'name': 'front', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-80, -1.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10001: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 40, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10002: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -40, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10003: {'type': 'nodepath', + 'name': 'back', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(80, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10007: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10011: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10012: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10010, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10017: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, -6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10020: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': 'leftFront', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-29.5779, 0, 0), + 'hpr': Point3(27, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10026: {'type': 'nodepath', + 'name': 'projectors', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10027: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10028: {'type': 'nodepath', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10029: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, -28, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10031: {'type': 'nodepath', + 'name': 'nodepaths', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10033: {'type': 'nodepath', + 'name': 'backLeft', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10034: {'type': 'nodepath', + 'name': 'backCenter', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10035: {'type': 'nodepath', + 'name': 'backRight', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(0, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10037: {'type': 'nodepath', + 'name': 'rightFront', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-20.7505, 15.0698, 0), + 'hpr': Point3(333, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10038: {'type': 'nodepath', + 'name': 'projectors', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(-15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10043: {'type': 'nodepath', + 'name': 'left', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10044: {'type': 'nodepath', + 'name': 'middle', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10045: {'type': 'nodepath', + 'name': 'frontLeft', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(-15, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10046: {'type': 'nodepath', + 'name': 'frontCenter', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(-15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10047: {'type': 'nodepath', + 'name': 'frontRight', + 'comment': '', + 'parentEntId': 10031, + 'pos': Point3(-15, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10048: {'type': 'nodepath', + 'name': 'right', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, -28, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10049: {'type': 'nodepath', + 'name': 'rightBack', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(20, 4, 0), + 'hpr': Point3(21, 0, 0), + 'scale': 1}, + 10050: {'type': 'nodepath', + 'name': 'leftBack', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(20, -7, 0), + 'hpr': Point3(334, 0, 0), + 'scale': 1}, + 10057: {'type': 'nodepath', + 'name': 'discoFloor', + 'comment': '', + 'parentEntId': 10003, + 'pos': Point3(36.354, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10059: {'type': 'nodepath', + 'name': 'cameras', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(-7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10006: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(15, 15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10007, + 'trackTarget2': 10008, + 'trackTarget3': 0}, + 10010: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(15, -15, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10011, + 'trackTarget2': 10012, + 'trackTarget3': 0}, + 10018: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10019, + 'trackTarget2': 10020, + 'trackTarget3': 0}, + 10024: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10027, + 'trackTarget2': 10028, + 'trackTarget3': 0}, + 10025: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10026, + 'pos': Point3(0, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10028, + 'trackTarget2': 10029, + 'trackTarget3': 0}, + 10041: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10043, + 'trackTarget2': 10044, + 'trackTarget3': 0}, + 10042: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, -20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 1, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10044, + 'trackTarget2': 10048, + 'trackTarget3': 0}, + 10060: {'type': 'securityCamera', + 'name': 'backRow', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 0, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 10033, + 'trackTarget2': 10034, + 'trackTarget3': 0}, + 10062: {'type': 'securityCamera', + 'name': 'frontRow', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 1, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 10045, + 'trackTarget2': 10046, + 'trackTarget3': 0}, + 10063: {'type': 'securityCamera', + 'name': 'backRow2', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 1, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 0, + 'trackTarget2': 10034, + 'trackTarget3': 10035}, + 10065: {'type': 'securityCamera', + 'name': 'frontRow2', + 'comment': '', + 'parentEntId': 10059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 2, + 'damPow': 10, + 'hideModel': 1, + 'maxVel': 10, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7, + 'switchId': 0, + 'trackTarget1': 0, + 'trackTarget2': 10046, + 'trackTarget3': 10047}, + 10004: {'type': 'stomper', + 'name': '3', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.25, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10013: {'type': 'stomper', + 'name': '2', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, 12.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.5, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10014: {'type': 'stomper', + 'name': '1', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, 25, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10015: {'type': 'stomper', + 'name': 'copy of 4 (2)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10016: {'type': 'stomper', + 'name': '4', + 'comment': '', + 'parentEntId': 10017, + 'pos': Point3(0, -12.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 6.25), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.75, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10022: {'type': 'stomper', + 'name': 'copy of 3', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10023: {'type': 'stomper', + 'name': 'copy of 3 (2)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(0, -20, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.4, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10030: {'type': 'stomper', + 'name': 'copy of 4 (3)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(10, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10039: {'type': 'stomper', + 'name': 'copy of 3', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10040: {'type': 'stomper', + 'name': 'copy of 3 (2)', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, -20, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(10, 6, 10), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.4, + 'range': 6, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10051: {'type': 'stomper', + 'name': 'copy of 4 (4)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(20, -5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10052: {'type': 'stomper', + 'name': 'copy of 4 (5)', + 'comment': '', + 'parentEntId': 10049, + 'pos': Point3(35, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10053: {'type': 'stomper', + 'name': 'copy of 4', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10054: {'type': 'stomper', + 'name': 'copy of 4 (2)', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(10, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10055: {'type': 'stomper', + 'name': 'copy of 4 (3)', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(20, -5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10056: {'type': 'stomper', + 'name': 'copy of 4 (4)', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(35, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(5, 6, 5), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.75, + 'range': 30.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 10, 1), + '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/LawbotOfficeDiamondRoom_Action01.py b/toontown/coghq/LawbotOfficeDiamondRoom_Action01.py new file mode 100644 index 0000000..dc182f3 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Action01.py @@ -0,0 +1,644 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 10074: {'type': 'attribModifier', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'attribName': 'velocity', + 'recursive': 1, + 'typeName': 'goon', + 'value': '10.0'}, + 10005: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10011: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10017: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10022: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 10.0}, + 10037: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10039: {'type': 'model', + 'name': 'jumpCabinet', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, 1.75, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10040: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10037, + 'pos': Point3(0, 0, 4), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10041: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10057, + 'pos': Point3(0, 3.5, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10042: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10041, + 'pos': Point3(0, 0, 4), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10043: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10052, + 'pos': Point3(1.95, -4, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10044: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10051, + 'pos': Point3(1.95, 7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10045: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -7.8, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10046: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -9.6, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10047: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -11.4, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10048: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -7.8, 4), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabB.bam'}, + 10049: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -9.6, 4), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 10050: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10054, + 'pos': Point3(0, -11.4, 4), + 'hpr': Point3(270, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabB.bam'}, + 10056: {'type': 'model', + 'name': 'stepCrate', + 'comment': '', + 'parentEntId': 10053, + 'pos': Point3(-2.08696, 1.52947, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(0.4, 0.4, 0.4), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_metal_crate.bam'}, + 10059: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-65.7043, 26.7723, 0), + 'hpr': Vec3(30.2564, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10060: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-22.3997, 48.3291, 0), + 'hpr': Vec3(30.2564, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10061: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-30.0586, 11.8711, 0), + 'hpr': Vec3(30.2564, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10066: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-35.5539, -11.1795, 0), + 'hpr': Vec3(326.31, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10067: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-68.8196, -28.185, 0), + 'hpr': Vec3(329.74, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10068: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-12.0298, -56.6682, 0), + 'hpr': Vec3(326.31, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10069: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(37.402, -43.7359, 0), + 'hpr': Vec3(33.69, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10070: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(23.2824, -14.8496, 0), + 'hpr': Vec3(28.6105, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3.bam'}, + 10071: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10064, + 'pos': Point3(23.28, 14.85, 0), + 'hpr': Point3(-28.61, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3.bam'}, + 10072: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10064, + 'pos': Point3(19.9879, 48.3291, 0), + 'hpr': Point3(-30.26, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10073: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10064, + 'pos': Point3(66.6957, 25.7694, 0), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10075: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10063, + 'pos': Point3(-7.27334, -24.2132, 0), + 'hpr': Vec3(326.31, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2.bam'}, + 10076: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(66.6957, -29.1363, 0), + 'hpr': Vec3(26.57, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox.bam'}, + 10000: {'type': 'nodepath', + 'name': 'goons', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10002: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-74.9317, -1.26934, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10003: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(0, -40.588, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10004: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10001, + 'pos': Point3(-44.8133, -38.3892, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10007: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-10, 40, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10009: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-50, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10010: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(-40, 30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10013: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(15, -30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(20, -45, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10015: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(40, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10016: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10012, + 'pos': Point3(55, -30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10019: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(20, 35, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10020: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(65, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(55, 25, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10023: {'type': 'nodepath', + 'name': 'front', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-110, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'filingCabinetWall', + 'comment': '', + 'parentEntId': 10023, + 'pos': Point3(0, 1, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2, 2, 2)}, + 10051: {'type': 'nodepath', + 'name': 'leftCrate', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10052: {'type': 'nodepath', + 'name': 'rightCrate', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10053: {'type': 'nodepath', + 'name': 'leftCabinets', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10054: {'type': 'nodepath', + 'name': 'rightCabinets', + 'comment': '', + 'parentEntId': 10038, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10057: {'type': 'nodepath', + 'name': 'cabinets', + 'comment': '', + 'parentEntId': 10053, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1)}, + 10058: {'type': 'nodepath', + 'name': 'obstacles', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10062: {'type': 'nodepath', + 'name': 'leftFront', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10063: {'type': 'nodepath', + 'name': 'rightFront', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10064: {'type': 'nodepath', + 'name': 'backLeft', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10065: {'type': 'nodepath', + 'name': 'backRight', + 'comment': '', + 'parentEntId': 10058, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10001: {'type': 'pathMaster', + 'name': 'rightFrontGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10002, + 'pathTarget1': 10003, + 'pathTarget2': 10004, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10006: {'type': 'pathMaster', + 'name': 'leftFrontGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10007, + 'pathTarget1': 10008, + 'pathTarget2': 10009, + 'pathTarget3': 10010, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10012: {'type': 'pathMaster', + 'name': 'rightRearGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10013, + 'pathTarget1': 10014, + 'pathTarget2': 10015, + 'pathTarget3': 10016, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}, + 10018: {'type': 'pathMaster', + 'name': 'leftRearGoon', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 10019, + 'pathTarget1': 10020, + 'pathTarget2': 10021, + 'pathTarget3': 0, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Battle00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00.py new file mode 100644 index 0000000..1e14a19 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00.py @@ -0,0 +1,317 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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(140, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cellId': 0, + 'radius': 15.0}, + 10002: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-96.7274, 20.5323, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 6.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10003: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-96.7274, -23.0117, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 10.0, + 'radius': 6.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-4.75302, 55.148, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-4.75302, -55.15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100003: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-24.8072, -17.6762, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100004: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-24.8072, 17.68, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100005: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-93.6953, 17.68, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100006: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-99.5488, 17.68, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100007: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-99.5488, -20.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100008: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-93.6953, -20.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100009: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(13.2635, -58.812, 0), + 'hpr': Vec3(211.701, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100010: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(32.4825, -48.4216, 0), + 'hpr': Vec3(211.701, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100011: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(53.5274, -37.2339, 0), + 'hpr': Vec3(204.775, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100012: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(14.898, 55.5914, 0), + 'hpr': Vec3(336.371, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100013: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(37.3381, 45.4534, 0), + 'hpr': Vec3(335.225, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100014: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(60.4494, 34.2323, 0), + 'hpr': Vec3(334.011, 0, 0), + 'scale': Vec3(1.8, 1.8, 1.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(100, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100015: {'type': 'nodepath', + 'name': 'lights', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100017: {'type': 'nodepath', + 'name': 'cameratarget1', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-67.0138, 21.058, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100018: {'type': 'nodepath', + 'name': 'copy of cameratarget1', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-3.89516, 39.263, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100019: {'type': 'nodepath', + 'name': 'copy of cameratarget1 (2)', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-23.9228, 20.3744, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100020: {'type': 'nodepath', + 'name': 'lights2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 4.9793, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100022: {'type': 'nodepath', + 'name': 'camtar1', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(16.2876, -35.7071, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100023: {'type': 'nodepath', + 'name': 'copy of camtar1', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(-52.2915, -8.50529, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100024: {'type': 'nodepath', + 'name': 'copy of camtar1 (2)', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(-52.2915, -37.5322, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100016: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(-45.8708, 39.2215, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 12.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100017, + 'trackTarget2': 100018, + 'trackTarget3': 100019}, + 100021: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100020, + 'pos': Point3(-45.2862, -55.4163, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 12.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100022, + 'trackTarget2': 100023, + 'trackTarget3': 100024}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00_Cogs.py new file mode 100644 index 0000000..c10ac73 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Battle00_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattlePlace1 = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Security00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Security00.py new file mode 100644 index 0000000..34e98aa --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Security00.py @@ -0,0 +1,85 @@ +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': []}, + 100001: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 3.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + 'velocity': 4}, + 100002: {'type': 'nodepath', + 'name': 'pathmasta1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(32.0525, 0.204516, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100003: {'type': 'nodepath', + 'name': 'copy of pathmasta1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-28.2492, 5.56589, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100004: {'type': 'nodepath', + 'name': 'copy of pathmasta1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-10.8179, 14.6481, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100005: {'type': 'nodepath', + 'name': 'pathmastachild', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-59.4987, 29.1448, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'pathMaster', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-17.2957, -37.5269, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 0, + 'pathScale': 1.0, + 'pathTarget0': 100002, + 'pathTarget1': 100003, + 'pathTarget2': 100004, + 'pathTarget3': 100005, + 'pathTarget4': 0, + 'pathTarget5': 0, + 'pathTarget6': 0, + 'pathTarget7': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Trap00.py b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00.py new file mode 100644 index 0000000..5485322 --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00.py @@ -0,0 +1,424 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 100002: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10.796, 37.9181, 0), + 'hpr': Vec3(23.9625, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100003: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-11.023, -40.7364, 0), + 'hpr': Vec3(338.199, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100006: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 100010, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(1.02303, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 0, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 0, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 100000, + 'unlock1Event': 0, + 'unlock2Event': 100001, + 'unlock3Event': 0}, + 100000: {'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-57.9165, 36.4902, 0.05), + 'hpr': Vec3(296.565, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'gridGame': 'Random', + 'gridScaleX': 40.0, + 'gridScaleY': 40.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(21, 21, 25), + 'switchId': 100002}, + 100001: {'type': 'laserField', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-40.0571, -3.38424, 0.07), + 'hpr': Vec3(243.435, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 1, + 'gridGame': 'Random', + 'gridScaleX': 37.0, + 'gridScaleY': 38.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(21, 21, 25), + 'switchId': 100003}, + 100008: {'type': 'model', + 'name': 'testMoverModel', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100011: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-66.4031, -28.3337, 0), + 'hpr': Vec3(330.945, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_stacks'}, + 100012: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-61.3414, 28.3839, 0), + 'hpr': Vec3(330.945, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_stacks'}, + 100013: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-73.0019, 23.1112, 0), + 'hpr': Vec3(311.424, 0, 0), + 'scale': Point3(0.6, 0.6, 0.6), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_stacks'}, + 100014: {'type': 'model', + 'name': 'critbox4', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10.5395, 55.5366, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100015: {'type': 'model', + 'name': 'critbox3', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-2.51556, -64.2189, 0), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100016: {'type': 'model', + 'name': 'critbox2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.559084, -27.465, 0.05), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100019: {'type': 'model', + 'name': 'copy of crit box', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.82422, 21.9664, 0), + 'hpr': Vec3(60.9454, 0, 0), + 'scale': Point3(0.55, 0.55, 0.9), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100021: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100017, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.8, 0.8, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100022: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100017, + 'pos': Point3(-1.72022, 8.94383, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.8, 0.8, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100023: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100017, + 'pos': Point3(-1.27251, -8.84256, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.8, 0.8, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks2'}, + 100026: {'type': 'model', + 'name': 'crit box', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(1.3729, 25.0434, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100028: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(-0.758049, -10.1966, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100029: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(1.82438, -2.94686, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100030: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(0.164113, 4.44244, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100031: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100027, + 'pos': Point3(5.13217, 10.0594, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.6, 0.6, 0.8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100035: {'type': 'model', + 'name': 'copy of crit box (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(3.01721, -23.3741, 0), + 'hpr': Vec3(13.2405, 0, 0), + 'scale': Point3(0.55, 0.55, 0.9), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100036: {'type': 'model', + 'name': 'copy of crit box (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-8.58923, 61.9548, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Point3(0.55, 0.55, 0.9), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_big_stacks3'}, + 100018: {'type': 'mover', + 'name': 'testMover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-64.3817, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'return', + 'entity2Move': 100008, + 'modelPath': 0, + 'moveTarget': 100034, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 0, + 'switchId': 0}, + 100024: {'type': 'mover', + 'name': 'paperwall2mover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -46.3957, 0), + 'hpr': Vec3(333.435, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'oneWay', + 'entity2Move': 100017, + 'modelPath': 0, + 'moveTarget': 100025, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 0, + 'switchId': 100001}, + 100032: {'type': 'mover', + 'name': 'paperwall1mover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-1.54332, 42.0157, 0), + 'hpr': Vec3(26.5651, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'oneWay', + 'entity2Move': 100027, + 'modelPath': 0, + 'moveTarget': 100033, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 0, + 'switchId': 100000}, + 100004: {'type': 'nodepath', + 'name': 'BattlePos1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-31.9689, 28.7456, 0), + 'hpr': Vec3(22.6199, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100005: {'type': 'nodepath', + 'name': 'BattlePos2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-31.0858, -34.9835, 0), + 'hpr': Vec3(331.39, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100007: {'type': 'nodepath', + 'name': 'copy of Cog Parent1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-11.0871, 37.4001, 0), + 'hpr': Vec3(299.249, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100009: {'type': 'nodepath', + 'name': 'Cog Parent1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10.2246, -41.5943, 0), + 'hpr': Vec3(246.214, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100010: {'type': 'nodepath', + 'name': 'doorparent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(133.9, -1.81291, 0), + 'hpr': Vec3(265.74, 0, 0), + 'scale': Point3(1, 1, 1)}, + 100017: {'type': 'nodepath', + 'name': 'PaperDoor2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100020: {'type': 'nodepath', + 'name': 'movertarget', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-84.0273, -14.7227, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100025: {'type': 'nodepath', + 'name': 'papperwall2target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(14.2998, -46.0911, 0), + 'hpr': Vec3(336.038, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100027: {'type': 'nodepath', + 'name': 'PaperWall1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100033: {'type': 'nodepath', + 'name': 'paperwall1movertarget', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(13.6684, 42.7864, 0), + 'hpr': Vec3(41.1859, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100034: {'type': 'nodepath', + 'name': 'test mover target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-51.1549, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeDiamondRoom_Trap00_Cogs.py b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00_Cogs.py new file mode 100644 index 0000000..177b36a --- /dev/null +++ b/toontown/coghq/LawbotOfficeDiamondRoom_Trap00_Cogs.py @@ -0,0 +1,85 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 100007 +CogParent1 = 100009 +BattlePlace1 = 100004 +BattlePlace2 = 100005 +BattleCellId = 0 +BattleCellId1 = 1 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}, + BattleCellId1: {'parentEntId': BattlePlace2, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeEntrance_Action00.py b/toontown/coghq/LawbotOfficeEntrance_Action00.py new file mode 100644 index 0000000..c942f95 --- /dev/null +++ b/toontown/coghq/LawbotOfficeEntrance_Action00.py @@ -0,0 +1,260 @@ +from toontown.coghq.SpecImports import * +import random +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'entranceId': 0, + 'radius': 15, + 'theta': 20}, + 100000: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100001: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100002, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100004: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100006: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100005, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100008: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100007, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100010: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100009, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 180), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100012: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100013: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100014, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 100017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 180), + 'scale': Point3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 10002: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'lamp', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-10.1845, 16.3439, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100003: {'type': 'nodepath', + 'name': 'lamp2', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(10.3093, 14.7794, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100007: {'type': 'nodepath', + 'name': 'paper', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(14.3907, 11.4389, 0), + 'hpr': Vec3(300.379, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100011: {'type': 'nodepath', + 'name': 'box', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(-18.5313, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'couch', + 'comment': '', + 'parentEntId': 10002, + 'pos': Point3(14.3585, -9.43671, 0), + 'hpr': Vec3(270.699, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100002: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100005: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100003, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100009: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100007, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100014: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}, + 100018: {'type': 'rendering', + 'name': '', + 'comment': '', + 'parentEntId': 100015, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'default'}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0], + 'titleString': 'MemTag: LawbotOfficeEntrance_Action00 %s' % random.random()} diff --git a/toontown/coghq/LawbotOfficeExterior.py b/toontown/coghq/LawbotOfficeExterior.py new file mode 100644 index 0000000..75ebc31 --- /dev/null +++ b/toontown/coghq/LawbotOfficeExterior.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 LawbotOfficeExterior(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/LawbotOfficeGearRoom_Action00.py b/toontown/coghq/LawbotOfficeGearRoom_Action00.py new file mode 100644 index 0000000..0812b97 --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_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_11/models/lawbotHQ/LB_Zone7av2', + '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': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + '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': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + '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': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + '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': 'sg', + 'gridId': None, + 'hFov': 70, + 'strength': 15, + '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/LawbotOfficeGearRoom_Battle00.py b/toontown/coghq/LawbotOfficeGearRoom_Battle00.py new file mode 100644 index 0000000..39293b1 --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Battle00.py @@ -0,0 +1,291 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone7a', + '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(8.03298, 23.7306, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 15.0}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(15.6666, -22.49, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.21365, 18.3459, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100003: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-17.6094, 17.5472, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100004: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(51.1181, -21.6279, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100005: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(39.917, -25.2425, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabB'}, + 100006: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(36.8762, -25.0662, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA'}, + 100007: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(42.9283, -25.1704, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA'}, + 100008: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(55.8945, -14.0806, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100009: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(55.8945, 5.46793, 0), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.5, 1.5, 1.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_deskA'}, + 100010: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(51.1181, -2.86719, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB'}, + 100011: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(49.0929, 21.7054, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100012: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.21365, -63.6967, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100013: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.21365, -57.4073, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100014: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-15.1195, -63.5076, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100015: {'type': 'model', + 'name': 'copy of (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-0.987966, -63.5446, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100016: {'type': 'model', + 'name': 'copy of (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(6.21987, -63.5446, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100017: {'type': 'model', + 'name': 'copy of (7)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(6.21987, -63.5446, 5.45351), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100018: {'type': 'model', + 'name': 'copy of (7)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-38.1114, -20.025, 0), + 'hpr': Vec3(175.135, 0, 0), + 'scale': Vec3(1.81307, 1.81307, 1.81307), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100019: {'type': 'model', + 'name': 'copy of (8)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-38.1114, 16.2551, 0), + 'hpr': Vec3(3.01279, 0, 0), + 'scale': Vec3(1.44025, 1.44025, 1.44025), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA'}, + 100020: {'type': 'model', + 'name': 'copy of (9)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-55.9616, 16.2551, 0), + 'hpr': Vec3(3.01279, 0, 0), + 'scale': Vec3(1.44025, 1.44025, 1.44025), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA'}, + 100021: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-45.4842, 16.387, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(8, 8, 8), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pottedplantA'}, + 100022: {'type': 'model', + 'name': 'copy of (8)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(12.856, -33.9782, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1.81307, 1.81307, 1.81307), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10000: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(270, 0, 0), + 'scale': 1}, + 100000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0.05), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeGearRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeGearRoom_Battle00_Cogs.py new file mode 100644 index 0000000..c10ac73 --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Battle00_Cogs.py @@ -0,0 +1,44 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +BattlePlace1 = 10000 +BattleCellId = 0 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}} +CogData = [{'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(-8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(3, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(8, 4, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeGearRoom_Platform00.py b/toontown/coghq/LawbotOfficeGearRoom_Platform00.py new file mode 100644 index 0000000..965f5ec --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Platform00.py @@ -0,0 +1,281 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone7a', + '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': []}, + 100008: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(9.21172, -65.1636, 0), + 'hpr': Vec3(218.48, 0, 0), + 'scale': Vec3(1, 1, 1), + 'rewardPerGrab': 20, + 'rewardPerGrabMax': 0}, + 100000: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-7.80516, 13.4827, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-22.7622, 13.4827, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX3'}, + 100003: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-22.5819, -16.4761, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100004: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(7.45035, -18.4214, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.75), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100005: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-7.74191, -16.7512, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2.75), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBoxX2'}, + 100009: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(7.38913, -1.56591, 31.7727), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.3, 2.9, 2.62459), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_metal_crate'}, + 100017: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100001, + 'pos': Point3(-7.94704, -1.56591, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2.3, 2.92, 2.92), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_CardBoardBox'}, + 100012: {'type': 'mover', + 'name': 'mover', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cycleType': 'loop', + 'entity2Move': 100014, + 'modelPath': 0, + 'moveTarget': 100013, + 'pos0Move': 2, + 'pos0Wait': 2, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 1, + 'switchId': 0}, + 100001: {'type': 'nodepath', + 'name': 'crateMaster', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100011: {'type': 'nodepath', + 'name': 'spotlightMover', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(29.8179, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100013: {'type': 'nodepath', + 'name': 'mover target', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 10, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'lightTarget1', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100016: {'type': 'nodepath', + 'name': 'copy of lightTarget1', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100010: {'type': 'platform', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-22.5404, -1.15634, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.18055, 1.18055, 1.18055), + 'floorName': 'platformcollision', + 'modelPath': 'phase_9/models/cogHQ/platform1', + 'modelScale': Point3(1, 1, 2), + 'motion': 'noBlend', + 'offset': Point3(0, 0, 15), + 'period': 4.0, + 'phaseShift': 0.0, + 'waitPercent': 0.1}, + 100014: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 10.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 30.0, + 'modelPath': 0, + 'projector': Point3(0, 0, 25), + 'radius': 7.0, + 'switchId': 0, + 'trackTarget1': 100015, + 'trackTarget2': 100016, + 'trackTarget3': 0}, + 100006: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-22.9004, -31.939, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(7, 3, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 15.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 20, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100007: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(6.83388, -1.26706, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(7, 3, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.3, + 'range': 15.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 20, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 100018: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-37.5869, -1.26706, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Point3(7, 3, 7), + 'modelPath': 0, + 'motion': 4, + 'period': 2.5, + 'phaseShift': 0.6, + 'range': 15.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(0.5, 20, 0.5), + '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/LawbotOfficeGearRoom_Security00.py b/toontown/coghq/LawbotOfficeGearRoom_Security00.py new file mode 100644 index 0000000..ad9e9e7 --- /dev/null +++ b/toontown/coghq/LawbotOfficeGearRoom_Security00.py @@ -0,0 +1,566 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_Zone7a', + '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': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(57.0219, 5.15024, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(0.660517, 0.660517, 0.660517), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 10015: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-25.74, 58.3575, 9.73551), + 'hpr': Vec3(95.4403, 0, 0), + 'scale': Vec3(1.5379, 1.5379, 1.5379), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10016: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(33.3395, -18.3643, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(0.66, 0.66, 0.66), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D1.bam'}, + 10017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(169.7, 0, 0), + 'scale': Vec3(0.90247, 0.90247, 0.90247), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10020: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10018, + 'pos': Point3(-12.0714, 0, 0), + 'hpr': Vec3(288.435, 0, 0), + 'scale': Vec3(0.90247, 0.90247, 0.90247), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_D4.bam'}, + 10022: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-5.97179, -60.3134, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(0.869391, 0.869391, 0.869391), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_10/models/cashbotHQ/pipes_C.bam'}, + 100015: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-1.31696, 0, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.6, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100016: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-12.7478, -11.9991, 0.05), + 'hpr': Vec3(180.47, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 100017: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-17.0503, 0, 0.1), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100019: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(0.897832, -12.2053, 0.05), + 'hpr': Vec3(180.47, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA'}, + 100020: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(6.3491, -6.57612, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Point3(1, 1, 0.5), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA'}, + 100021: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-20.9336, -5.07158, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.00449, 1.00449, 1.00449), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100022: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(4.96172, -5.07158, 0.05), + 'hpr': Vec3(272.49, 0, 0), + 'scale': Vec3(1.00449, 1.00449, 1.00449), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_paper_twist_stacks'}, + 100023: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-20.5363, -8.42755, 0.05), + 'hpr': Vec3(90, 0, 0), + 'scale': Vec3(1.00449, 1.00449, 1.00449), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA'}, + 100024: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100018, + 'pos': Point3(-4.9392, -12.3495, 0.05), + 'hpr': Vec3(180.47, 0, 0), + 'scale': Vec3(3.79099, 3.79099, 3.79099), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampA'}, + 100026: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(16.7866, 12.9562, 0.1), + 'hpr': Vec3(185.194, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100027: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-21.2469, 12.8535, 0.0929851), + 'hpr': Vec3(187.125, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100028: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(5.20127, 12.8535, 0.0929851), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100029: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-8.51009, 13.1118, 0.0929851), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100030: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-15.7803, 1.79844, 0.0929851), + 'hpr': Vec3(188.13, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100031: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(14.449, 2.90238, 0.0929851), + 'hpr': Vec3(184.764, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100032: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(12.2409, -22.0432, 0.0929851), + 'hpr': Vec3(184.764, 0, 0), + 'scale': Point3(1.4, 1.4, 1.4), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 100033: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 100025, + 'pos': Point3(-1.1837, 1.79844, 0.0929851), + 'hpr': Vec3(170.538, 0, 0), + 'scale': Vec3(1.2, 1.2, 1.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA'}, + 10000: {'type': 'nodepath', + 'name': 'props', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, -2), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10018: {'type': 'nodepath', + 'name': 'rightVertPipes', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-16.4537, -45.3982, -8.4), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.65, 0.65, 1.56)}, + 10021: {'type': 'nodepath', + 'name': 'rightPipes', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100001: {'type': 'nodepath', + 'name': 'cameraTarget1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(10, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100002: {'type': 'nodepath', + 'name': 'copy of cameraTarget1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(30, -10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100003: {'type': 'nodepath', + 'name': 'copy of cameraTarget1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(40, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100005: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-30, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100006: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-60, 15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100007: {'type': 'nodepath', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-60, -15, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100009: {'type': 'nodepath', + 'name': 'camera3 target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(25, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100010: {'type': 'nodepath', + 'name': 'copy of camera3 target', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100011: {'type': 'nodepath', + 'name': 'copy of camera3 target (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-50, -2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100013: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10, 60, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100014: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-10, 40, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100018: {'type': 'nodepath', + 'name': 'wall1parent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-7.53236, 20.7488, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100025: {'type': 'nodepath', + 'name': 'wall2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-7.36698, -23.6933, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100035: {'type': 'nodepath', + 'name': 'targ1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-27.7132, -17.0199, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100036: {'type': 'nodepath', + 'name': 'copy of targ1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(9.37401, -17.0199, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100038: {'type': 'nodepath', + 'name': 'tegrat', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(8.31643, -40.4532, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100039: {'type': 'nodepath', + 'name': 'copy of tegrat', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-27.6613, -37.0841, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100040: {'type': 'nodepath', + 'name': 'copy of tegrat (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-6.48412, -29.8115, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100042: {'type': 'nodepath', + 'name': 'gettar', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-7.92397, 14.3026, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100043: {'type': 'nodepath', + 'name': 'copy of gettar', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-23.1978, 15.1905, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(33.4453, -2.27555, 0), + 'hpr': Point3(-3, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 6.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 10.0, + 'modelPath': 0, + 'projector': Point3(-3, -3, 25), + 'radius': 6.0, + 'switchId': 0, + 'trackTarget1': 100001, + 'trackTarget2': 100002, + 'trackTarget3': 100003}, + 100004: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-58.4773, 4.03197, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 4.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100005, + 'trackTarget2': 100006, + 'trackTarget3': 100007}, + 100008: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-22.5923, -33.41, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 17.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 20.0, + 'modelPath': 0, + 'projector': Point3(12, 16, 32), + 'radius': 7.0, + 'switchId': 0, + 'trackTarget1': 100009, + 'trackTarget2': 100010, + 'trackTarget3': 100011}, + 100012: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-9.20073, 65.6563, 8.45), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 7.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(0, 0, 17), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100014, + 'trackTarget2': 100013, + 'trackTarget3': 0}, + 100034: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -10.5537, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 40.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 20.0, + 'modelPath': 0, + 'projector': Point3(10, 0, 25), + 'radius': 4.0, + 'switchId': 0, + 'trackTarget1': 100035, + 'trackTarget2': 100036, + 'trackTarget3': 0}, + 100037: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-28.9964, -30.2849, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 12.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100039, + 'trackTarget2': 100038, + 'trackTarget3': 100040}, + 100041: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-32.9569, 19.6137, 0.0470875), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'accel': 12.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(12, 0, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100042, + 'trackTarget2': 100043, + 'trackTarget3': 0}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Action00.py b/toontown/coghq/LawbotOfficeLobby_Action00.py new file mode 100644 index 0000000..83310df --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Action00.py @@ -0,0 +1,1636 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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}, + 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/LawbotOfficeLobby_Action01.py b/toontown/coghq/LawbotOfficeLobby_Action01.py new file mode 100644 index 0000000..49f27f4 --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Action01.py @@ -0,0 +1,846 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 10055: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'length': 20.0, + 'radius': 15.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10058: {'type': 'collisionSolid', + 'name': '', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(29.3404, 25.876, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 6.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10059: {'type': 'collisionSolid', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-13.34, 25.88, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'length': 6.0, + 'radius': 3.0, + 'showSolid': 0, + 'solidType': 'tube'}, + 10001: {'type': 'model', + 'name': 'middlePillar', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(4.40185, 4.40185, 4.40185), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_pillar.bam'}, + 10002: {'type': 'model', + 'name': 'leftCouch', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-12.4308, 0, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 10003: {'type': 'model', + 'name': 'rightCouch', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(12.43, 0, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 10004: {'type': 'model', + 'name': 'frontChair', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, -13, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA.bam'}, + 10005: {'type': 'model', + 'name': 'backChair', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 13, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_chairA.bam'}, + 10006: {'type': 'model', + 'name': 'frontlamp', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(-10, -14, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB.bam'}, + 10007: {'type': 'model', + 'name': 'backLamp', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(10, 14, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2, 2, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_torch_lampB.bam'}, + 10009: {'type': 'model', + 'name': 'movingShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0.1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 0.7, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10012: {'type': 'model', + 'name': 'leftShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10013: {'type': 'model', + 'name': 'rightShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(15.8, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10015: {'type': 'model', + 'name': 'copy of leftShelf', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10017: {'type': 'model', + 'name': 'copy of leftShelf (2)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(8.7, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10018: {'type': 'model', + 'name': 'copy of leftShelf (3)', + 'comment': '', + 'parentEntId': 10016, + 'pos': Point3(17.4, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10019: {'type': 'model', + 'name': 'copy of leftShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(-8.7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10020: {'type': 'model', + 'name': 'copy of rightShelf', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(24.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10023: {'type': 'model', + 'name': 'copy of rightShelf (2)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-10.66, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10024: {'type': 'model', + 'name': 'copy of rightShelf (3)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(-1.97, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10025: {'type': 'model', + 'name': 'copy of rightShelf (4)', + 'comment': '', + 'parentEntId': 10021, + 'pos': Point3(6.65, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10026: {'type': 'model', + 'name': 'copy of rightShelf (3)', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10027: {'type': 'model', + 'name': 'copy of rightShelf (4)', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-8.7173, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10028: {'type': 'model', + 'name': 'copy of rightShelf (5)', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-17.416, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 10030: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10031: {'type': 'model', + 'name': 'moveWall', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(-8.7, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 0.7, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10033: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10034: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(8.7, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1, 0.7, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 10046: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10047: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10048: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(-14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10049: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10068, + 'pos': Point3(-29.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10051: {'type': 'model', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(-14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10052: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10053: {'type': 'model', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(14.75, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10054: {'type': 'model', + 'name': 'copy of (4)', + 'comment': '', + 'parentEntId': 10069, + 'pos': Point3(29.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10010: {'type': 'mover', + 'name': 'shelfMover', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 10009, + 'modelPath': 0, + 'moveTarget': 10011, + 'pos0Move': 2.0, + 'pos0Wait': 0.0, + 'pos1Move': 2, + 'pos1Wait': 3.0, + 'startOn': 1, + 'switchId': 0}, + 10035: {'type': 'mover', + 'name': 'wallMover', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 10031, + 'modelPath': 0, + 'moveTarget': 10036, + 'pos0Move': 2, + 'pos0Wait': 6.0, + 'pos1Move': 2, + 'pos1Wait': 2, + 'startOn': 1, + 'switchId': 0}, + 10037: {'type': 'mover', + 'name': 'copy of wallMover', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 10034, + 'modelPath': 0, + 'moveTarget': 10038, + 'pos0Move': 2, + 'pos0Wait': 6.0, + 'pos1Move': 2, + 'pos1Wait': 2.0, + 'startOn': 1, + 'switchId': 0}, + 10000: {'type': 'nodepath', + 'name': 'center', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10008: {'type': 'nodepath', + 'name': 'maze', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-11.32, 35.7767, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1.4, 1.4, 1.4)}, + 10011: {'type': 'nodepath', + 'name': 'movetarget', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': 'frontDoor', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0, 0.75, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10016: {'type': 'nodepath', + 'name': 'firstInnerWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-1.4, 9.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10021: {'type': 'nodepath', + 'name': 'rightWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(28.038, 16.5374, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10022: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-12.3934, 5.78821, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10029: {'type': 'nodepath', + 'name': 'rearRightWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(19.8603, 23.5345, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10032: {'type': 'nodepath', + 'name': 'rearLeftWall', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(-5.06744, 23.3692, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 10036: {'type': 'nodepath', + 'name': 'moveTarget', + 'comment': '', + 'parentEntId': 10029, + 'pos': Point3(8.7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10038: {'type': 'nodepath', + 'name': 'copy of moveTarget', + 'comment': '', + 'parentEntId': 10032, + 'pos': Point3(-8.7, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10039: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10044: {'type': 'nodepath', + 'name': 'walls', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10045: {'type': 'nodepath', + 'name': 'leftWall', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(-40, -2, 0), + 'hpr': Point3(270, 0, 0), + 'scale': Point3(1, 1, 1)}, + 10050: {'type': 'nodepath', + 'name': 'rightWall', + 'comment': '', + 'parentEntId': 10044, + 'pos': Point3(40, -2, 0), + 'hpr': Point3(90, 0, 0), + 'scale': Point3(1, 1, 1)}, + 10062: {'type': 'nodepath', + 'name': 'cameras', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10063: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(5, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10065: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-10, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10066: {'type': 'nodepath', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(5, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10067: {'type': 'nodepath', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-10, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10068: {'type': 'nodepath', + 'name': 'scaleNode', + 'comment': '', + 'parentEntId': 10045, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.7, 1, 1)}, + 10069: {'type': 'nodepath', + 'name': 'scaleNode', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.7, 1, 1)}, + 10070: {'type': 'nodepath', + 'name': 'cameras', + 'comment': '', + 'parentEntId': 10050, + 'pos': Point3(4, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10073: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(5, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10074: {'type': 'nodepath', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-10, 6, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10075: {'type': 'nodepath', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(5, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10076: {'type': 'nodepath', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-10, 17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10056: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10063, + 'trackTarget2': 10065, + 'trackTarget3': 10066}, + 10057: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'pos': Point3(-10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10065, + 'trackTarget2': 10066, + 'trackTarget3': 10067}, + 10071: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10073, + 'trackTarget2': 10074, + 'trackTarget3': 10075}, + 10072: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 10070, + 'pos': Point3(-10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 1, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 5, + 'modelPath': 0, + 'projector': Point3(0, 0, 28), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 10074, + 'trackTarget2': 10075, + 'trackTarget3': 10076}, + 10040: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(0, 5.05514, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 12, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10041: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(20, 5.06, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 12, 1), + 'soundLen': 0, + 'soundOn': 0, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 10042: {'type': 'stomper', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(23.7219, 13.3088, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 8, 1), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 0, + 'zOffset': 0}, + 10043: {'type': 'stomper', + 'name': 'copy of (3)', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(-8.59782, 13.3088, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(2.5, 2.5, 2.5), + 'animateShadow': 1, + 'cogStyle': 1, + 'crushCellId': None, + 'damage': 10, + 'headScale': Vec3(1, 1, 1), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 3.0, + 'removeCamBarrierCollisions': 0, + 'removeHeadFloor': 0, + 'shaftScale': Point3(1, 8, 1), + '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/LawbotOfficeLobby_Lights00.py b/toontown/coghq/LawbotOfficeLobby_Lights00.py new file mode 100644 index 0000000..969bce3 --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Lights00.py @@ -0,0 +1,236 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 100016: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-1.17415, -2.82071, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'rewardPerGrab': 42, + 'rewardPerGrabMax': 0}, + 100017: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(1.70419, 4.05857, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100018: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-6.14262, 5.87553, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'gagLevel': 5, + 'gagLevelMax': 5, + 'gagTrack': 'random', + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 100011: {'type': 'mover', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'cycleType': 'loop', + 'entity2Move': 100000, + 'modelPath': 0, + 'moveTarget': 100012, + 'pos0Move': 60.0, + 'pos0Wait': 10.0, + 'pos1Move': 60.0, + 'pos1Wait': 10.0, + 'startOn': 1, + 'switchId': 0}, + 100000: {'type': 'nodepath', + 'name': 'cameraRing', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100006: {'type': 'nodepath', + 'name': 'centerTarget', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 100007: {'type': 'nodepath', + 'name': 'sidetarget1', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(8.39848, 43.7828, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100008: {'type': 'nodepath', + 'name': 'sidetarget2', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(7.54716, -38.4515, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100009: {'type': 'nodepath', + 'name': 'sidetarget3', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-39.4668, 1.83033, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100010: {'type': 'nodepath', + 'name': 'sidetarget4', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(39.47, 1.83033, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100012: {'type': 'nodepath', + 'name': 'moverTarget', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(360, 0, 0), + 'scale': 1}, + 100013: {'type': 'nodepath', + 'name': 'target exit', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.535633, 68.0682, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100014: {'type': 'nodepath', + 'name': 'targetoff1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(47.1742, 5.53207, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100015: {'type': 'nodepath', + 'name': 'targetoff2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-47.17, 5.53207, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100001: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(24.3, -17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100013, + 'trackTarget3': 100008}, + 100002: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(28.5, 9.27, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100008, + 'trackTarget3': 100013}, + 100003: {'type': 'securityCamera', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, 30, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100009, + 'trackTarget3': 100014}, + 100004: {'type': 'securityCamera', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-24.3, -17, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(6, 6, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100010, + 'trackTarget3': 100007}, + 100005: {'type': 'securityCamera', + 'name': 'copy of (2)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-28.5, 9.27, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'accel': 5.0, + 'damPow': 8, + 'hideModel': 0, + 'maxVel': 15.0, + 'modelPath': 0, + 'projector': Point3(5, 5, 25), + 'radius': 5, + 'switchId': 0, + 'trackTarget1': 100006, + 'trackTarget2': 100007, + 'trackTarget3': 100015}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Trap00.py b/toontown/coghq/LawbotOfficeLobby_Trap00.py new file mode 100644 index 0000000..3866cbd --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Trap00.py @@ -0,0 +1,572 @@ +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 100018: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(54.7666, 7.03896, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100019: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-58.0835, 7.37219, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 100015: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 100005, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 0, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 0, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 100016, + 'unlock1Event': 0, + 'unlock2Event': 100017, + 'unlock3Event': 0}, + 100016: {'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-15.1345, -13.2285, 0.25), + 'hpr': Point3(90, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'gridGame': 'Random', + 'gridScaleX': 42.0, + 'gridScaleY': 40.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(20, 40, 45), + 'switchId': 100019}, + 100017: {'type': 'laserField', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(11.2941, 28.7739, 0.28), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 1, + 'gridGame': 'Random', + 'gridScaleX': 42.0, + 'gridScaleY': 40.0, + 'laserFactor': 3, + 'modelPath': 0, + 'projector': Point3(20, 40, 45), + 'switchId': 100018}, + 100001: {'type': 'model', + 'name': 'copy of partition (3)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-8.98508, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100002: {'type': 'model', + 'name': 'copy of partition (4)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(5.36486, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100003: {'type': 'model', + 'name': 'copy of partition (5)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(20.1513, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100004: {'type': 'model', + 'name': 'copy of partition (6)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(34.9439, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100007: {'type': 'model', + 'name': 'copy of partition', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100008: {'type': 'model', + 'name': 'copy of partition (2)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-14.9029, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100009: {'type': 'model', + 'name': 'copy of partition (3)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-29.7119, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100010: {'type': 'model', + 'name': 'copy of partition (4)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-44.4821, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100012: {'type': 'model', + 'name': 'copy of partition (3)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100013: {'type': 'model', + 'name': 'copy of partition (4)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(-14.9149, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100014: {'type': 'model', + 'name': 'copy of partition (5)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(-29.7289, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100023: {'type': 'model', + 'name': 'copy of partition (6)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(-44.4361, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100025: {'type': 'model', + 'name': 'copy of partition (7)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(42.3323, -38.4749, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100026: {'type': 'model', + 'name': 'copy of partition (8)', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(42.3323, -25.7861, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100027: {'type': 'model', + 'name': 'copy of partition (5)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-52.4944, -39.8953, 0), + 'hpr': Vec3(270.818, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100028: {'type': 'model', + 'name': 'copy of partition (6)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(-52.4944, -25.343, 0), + 'hpr': Vec3(270.818, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100029: {'type': 'model', + 'name': 'scenary', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-38.0023, -20.533, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 100030: {'type': 'model', + 'name': 'copy of scenary', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-28.9763, -28.2505, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_couchA.bam'}, + 100031: {'type': 'model', + 'name': 'copy of scenary (2)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-51.8392, -28.2505, 0), + 'hpr': Vec3(131.894, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 100032: {'type': 'model', + 'name': 'copy of scenary (3)', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-44.1843, -37.0309, 0), + 'hpr': Vec3(131.894, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB.bam'}, + 100033: {'type': 'model', + 'name': 'copy of scenary (4)', + 'comment': '', + 'parentEntId': 100034, + 'pos': Point3(16.5447, -16.1896, 0), + 'hpr': Vec3(358.668, 0, 0), + 'scale': Point3(2, 2, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 100037: {'type': 'model', + 'name': 'partition', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(-14.6846, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100038: {'type': 'model', + 'name': 'copy of partition', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100039: {'type': 'model', + 'name': 'copy of partition', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(15.087, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100040: {'type': 'model', + 'name': 'copy of partition (2)', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(-29.9877, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100041: {'type': 'model', + 'name': 'copy of scenary (5)', + 'comment': '', + 'parentEntId': 100034, + 'pos': Point3(20.0815, -16.1896, 0), + 'hpr': Vec3(358.668, 0, 0), + 'scale': Point3(2, 2, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_filing_cabA.bam'}, + 100042: {'type': 'model', + 'name': 'copy of scenary (6)', + 'comment': '', + 'parentEntId': 100034, + 'pos': Point3(31.1973, -15.8974, 0), + 'hpr': Vec3(358.668, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfA.bam'}, + 100043: {'type': 'model', + 'name': 'box', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-10.7296, -3.52948, 0), + 'hpr': Vec3(270.924, 0, 0), + 'scale': Point3(2, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100044: {'type': 'model', + 'name': 'copy of box', + 'comment': '', + 'parentEntId': 100036, + 'pos': Point3(-32.0389, 7.09736, 0), + 'hpr': Vec3(90, 0, 0), + 'scale': Point3(1, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100045: {'type': 'model', + 'name': 'copy of box (2)', + 'comment': '', + 'parentEntId': 100006, + 'pos': Point3(2.05698, -7.92436, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Point3(2, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 100046: {'type': 'model', + 'name': 'copy of box (3)', + 'comment': '', + 'parentEntId': 100011, + 'pos': Point3(1.66589, 6.97288, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Point3(2, 8, 2), + 'collisionsOnly': 1, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/PartitionA.bam'}, + 10000: {'type': 'nodepath', + 'name': '0', + 'comment': '', + 'parentEntId': 100035, + 'pos': Point3(-64.0541, 7.6285, 0), + 'hpr': Vec3(90, 0, 0), + 'scale': Point3(1, 1, 1)}, + 100020: {'type': 'nodepath', + 'name': 'battle place 1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-35.0928, 6.21518, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100021: {'type': 'nodepath', + 'name': 'cogparent2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(64.2291, 5.46719, 0), + 'hpr': Vec3(270, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100022: {'type': 'nodepath', + 'name': 'BattlePlace2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(32.4809, 7.68949, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100035: {'type': 'nodepath', + 'name': 'cogparent1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1)}, + 100000: {'type': 'rendering', + 'name': 'PartitionWall2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-53.71, -14.4718, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100005: {'type': 'rendering', + 'name': 'doorparent', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.845594, 73.9927, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 0.75, + 'colorG': 0.75, + 'colorR': 0.8, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100006: {'type': 'rendering', + 'name': 'wallparent3', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(61.7299, -14.6471, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100011: {'type': 'rendering', + 'name': 'wallprent4', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(62.3555, 30.4101, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100024: {'type': 'rendering', + 'name': 'cogrenderingparent2', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(64.2291, 5.46719, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Additive', + 'colorA': 1.0, + 'colorB': 0.0, + 'colorG': 0.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'fixed'}, + 100034: {'type': 'rendering', + 'name': 'scenaryLight', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'blending': 'Alpha', + 'colorA': 1.0, + 'colorB': 0.5, + 'colorG': 0.5, + 'colorR': 0.6, + 'fogOn': 0, + 'renderBin': 'transparent'}, + 100036: {'type': 'rendering', + 'name': 'partitionwall1', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-33.8745, 29.3783, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'blending': 'Normal', + 'colorA': 1.0, + 'colorB': 1.0, + 'colorG': 1.0, + 'colorR': 1.0, + 'fogOn': 0, + 'renderBin': 'transparent'}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/LawbotOfficeLobby_Trap00_Cogs.py b/toontown/coghq/LawbotOfficeLobby_Trap00_Cogs.py new file mode 100644 index 0000000..157e76f --- /dev/null +++ b/toontown/coghq/LawbotOfficeLobby_Trap00_Cogs.py @@ -0,0 +1,85 @@ +from SpecImports import * +from toontown.toonbase import ToontownGlobals +CogParent = 10000 +CogParent1 = 100021 +BattlePlace1 = 100020 +BattlePlace2 = 100022 +BattleCellId = 0 +BattleCellId1 = 1 +BattleCells = {BattleCellId: {'parentEntId': BattlePlace1, + 'pos': Point3(0, 0, 0)}, + BattleCellId1: {'parentEntId': BattlePlace2, + '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': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(-6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(-2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel, + 'battleCell': BattleCellId1, + 'pos': Point3(2, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'parentEntId': CogParent1, + 'boss': 0, + 'level': ToontownGlobals.CashbotMintCogLevel + 1, + 'battleCell': BattleCellId1, + 'pos': Point3(6, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}] +ReserveCogData = [] diff --git a/toontown/coghq/LawbotOfficeOilRoom_Battle00.py b/toontown/coghq/LawbotOfficeOilRoom_Battle00.py new file mode 100644 index 0000000..7d9519a --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_Battle00.py @@ -0,0 +1,63 @@ +from toontown.coghq.SpecImports import * +import random +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 100030: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.124318, -27.1644, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 25.0}, + 100000: {'type': 'elevatorMarker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0.199988, -31.3479, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': Point3(1, 1, 1), + 'modelPath': 0}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, -30.3883, 13.5561), + 'hpr': Vec3(180, 0, 0), + 'scale': Vec3(2.6262, 2.6262, 2.6262), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0], + 'titleString': 'MemTag: LawbotOfficeOilRoom_Battle00 %s' % random.random()} diff --git a/toontown/coghq/LawbotOfficeOilRoom_Battle00_Cogs.py b/toontown/coghq/LawbotOfficeOilRoom_Battle00_Cogs.py new file mode 100644 index 0000000..ceb3cfc --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_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.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/LawbotOfficeOilRoom_Battle01.py b/toontown/coghq/LawbotOfficeOilRoom_Battle01.py new file mode 100644 index 0000000..dce5bc0 --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_Battle01.py @@ -0,0 +1,73 @@ +from toontown.coghq.SpecImports import * +import random +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500, + 'modelFilename': 'phase_11/models/lawbotHQ/LB_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': []}, + 100030: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(-0.942754, -16.0554, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 0, + 'radius': 20.0}, + 100001: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(-0.345429, -31.6315, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(2.65, 2.55, 3.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB'}, + 100002: {'type': 'model', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 100000, + 'pos': Point3(0, -31.6418, 20.1296), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(2.65, 2.52, 3.2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_11/models/lawbotHQ/LB_bookshelfB'}, + 10000: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(180, 0, 0), + 'scale': 1}, + 100000: {'type': 'nodepath', + 'name': 'extra cases', + 'comment': '', + 'parentEntId': 0, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0], + 'titleString': 'MemTag: LawbotOfficeOilRoom_Battle01 %s' % random.random()} diff --git a/toontown/coghq/LawbotOfficeOilRoom_Battle01_Cogs.py b/toontown/coghq/LawbotOfficeOilRoom_Battle01_Cogs.py new file mode 100644 index 0000000..61883ec --- /dev/null +++ b/toontown/coghq/LawbotOfficeOilRoom_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/LevelBattleManagerAI.py b/toontown/coghq/LevelBattleManagerAI.py new file mode 100644 index 0000000..b3a2b69 --- /dev/null +++ b/toontown/coghq/LevelBattleManagerAI.py @@ -0,0 +1,49 @@ +from toontown.battle import BattleManagerAI +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import BattleExperienceAggregatorAI + +class LevelBattleManagerAI(BattleManagerAI.BattleManagerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('LevelBattleManagerAI') + + def __init__(self, air, level, battleCtor, battleExpAggreg=None): + BattleManagerAI.BattleManagerAI.__init__(self, air) + self.battleCtor = battleCtor + self.level = level + self.battleBlockers = {} + if battleExpAggreg is None: + battleExpAggreg = BattleExperienceAggregatorAI.BattleExperienceAggregatorAI() + self.battleExpAggreg = battleExpAggreg + return + + def destroyBattleMgr(self): + battles = self.cellId2battle.values() + for battle in battles: + self.destroy(battle) + + for cellId, battleBlocker in self.battleBlockers.items(): + if battleBlocker is not None: + battleBlocker.deactivate() + + del self.battleBlockers + del self.cellId2battle + del self.battleExpAggreg + return + + def newBattle(self, cellId, zoneId, pos, suit, toonId, roundCallback=None, finishCallback=None, maxSuits=4): + battle = self.cellId2battle.get(cellId, None) + if battle != None: + self.notify.debug('battle already created by battle blocker, add toon %d' % toonId) + battle.signupToon(toonId, pos[0], pos[1], pos[2]) + return battle + else: + battle = self.battleCtor(self.air, self, pos, suit, toonId, zoneId, self.level, cellId, roundCallback, finishCallback, maxSuits) + self.battleExpAggreg.attachToBattle(battle) + battle.battleCalc.setSkillCreditMultiplier(self.level.getBattleCreditMultiplier()) + battle.addToon(toonId) + battle.generateWithRequired(zoneId) + self.cellId2battle[cellId] = battle + return battle + + def addBattleBlocker(self, blocker, cellId): + self.battleBlockers[cellId] = blocker + messenger.send(self.level.planner.getBattleBlockerEvent(cellId)) diff --git a/toontown/coghq/LevelSuitPlannerAI.py b/toontown/coghq/LevelSuitPlannerAI.py new file mode 100644 index 0000000..651ad6b --- /dev/null +++ b/toontown/coghq/LevelSuitPlannerAI.py @@ -0,0 +1,217 @@ +from pandac.PandaModules import * +from direct.showbase import DirectObject +from toontown.suit import SuitDNA +from direct.directnotify import DirectNotifyGlobal +import LevelBattleManagerAI, types, random + +class LevelSuitPlannerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('LevelSuitPlannerAI') + + def __init__(self, air, level, cogCtor, battleCtor, cogSpecs, reserveCogSpecs, battleCellSpecs, battleExpAggreg=None): + self.air = air + self.level = level + self.cogCtor = cogCtor + self.cogSpecs = cogSpecs + if simbase.config.GetBool('level-reserve-suits', 0): + self.reserveCogSpecs = reserveCogSpecs + else: + self.reserveCogSpecs = [] + self.battleCellSpecs = battleCellSpecs + self.__genSuitInfos(self.level.getCogLevel(), self.level.getCogTrack()) + self.battleMgr = LevelBattleManagerAI.LevelBattleManagerAI(self.air, self.level, battleCtor, battleExpAggreg) + self.battleCellId2suits = {} + for id in self.battleCellSpecs.keys(): + self.battleCellId2suits[id] = [] + + def destroy(self): + self.battleMgr.destroyBattleMgr() + del self.battleMgr + self.battleCellId2suits = {} + self.ignoreAll() + del self.cogSpecs + del self.cogCtor + del self.level + del self.air + + def __genJoinChances(self, num): + joinChances = [] + for currChance in range(num): + joinChances.append(random.randint(1, 100)) + + joinChances.sort(cmp) + return joinChances + + def __genSuitInfos(self, level, track): + if __dev__: + pass + + def getSuitDict(spec, cogId, level=level, track=track): + suitDict = {} + suitDict['track'] = track + suitDict.update(spec) + suitDict['zoneId'] = self.level.getEntityZoneId(spec['parentEntId']) + suitDict['level'] += level + suitDict['cogId'] = cogId + return suitDict + + self.suitInfos = {} + self.suitInfos['activeSuits'] = [] + for i in range(len(self.cogSpecs)): + spec = self.cogSpecs[i] + self.suitInfos['activeSuits'].append(getSuitDict(spec, i)) + + numReserve = len(self.reserveCogSpecs) + joinChances = self.__genJoinChances(numReserve) + self.suitInfos['reserveSuits'] = [] + for i in range(len(self.reserveCogSpecs)): + spec = self.reserveCogSpecs[i] + suitDict = getSuitDict(spec, i) + suitDict['joinChance'] = joinChances[i] + self.suitInfos['reserveSuits'].append(suitDict) + + def __genSuitObject(self, suitDict, reserve): + suit = self.cogCtor(simbase.air, self) + dna = SuitDNA.SuitDNA() + dna.newSuitRandom(level=SuitDNA.getRandomSuitType(suitDict['level']), dept=suitDict['track']) + suit.dna = dna + suit.setLevel(suitDict['level']) + suit.setSkeleRevives(suitDict.get('revives')) + suit.setLevelDoId(self.level.doId) + suit.setCogId(suitDict['cogId']) + suit.setReserve(reserve) + if suitDict['skeleton']: + suit.setSkelecog(1) + suit.generateWithRequired(suitDict['zoneId']) + suit.boss = suitDict['boss'] + return suit + + def genSuits(self): + suitHandles = {} + activeSuits = [] + for activeSuitInfo in self.suitInfos['activeSuits']: + suit = self.__genSuitObject(activeSuitInfo, 0) + suit.setBattleCellIndex(activeSuitInfo['battleCell']) + activeSuits.append(suit) + + suitHandles['activeSuits'] = activeSuits + reserveSuits = [] + for reserveSuitInfo in self.suitInfos['reserveSuits']: + suit = self.__genSuitObject(reserveSuitInfo, 1) + reserveSuits.append([suit, reserveSuitInfo['joinChance'], reserveSuitInfo['battleCell']]) + + suitHandles['reserveSuits'] = reserveSuits + return suitHandles + + def __suitCanJoinBattle(self, cellId): + battle = self.battleMgr.getBattle(cellId) + if not battle.suitCanJoin(): + return 0 + return 1 + + def requestBattle(self, suit, toonId): + cellIndex = suit.getBattleCellIndex() + cellSpec = self.battleCellSpecs[cellIndex] + pos = cellSpec['pos'] + zone = self.level.getZoneId(self.level.getEntityZoneEntId(cellSpec['parentEntId'])) + maxSuits = 4 + self.battleMgr.newBattle(cellIndex, zone, pos, suit, toonId, self.__handleRoundFinished, self.__handleBattleFinished, maxSuits) + for otherSuit in self.battleCellId2suits[cellIndex]: + if otherSuit is not suit: + if self.__suitCanJoinBattle(cellIndex): + self.battleMgr.requestBattleAddSuit(cellIndex, otherSuit) + else: + battle = self.battleMgr.getBattle(cellIndex) + if battle: + self.notify.warning('battle not joinable: numSuits=%s, joinable=%s, fsm=%s, toonId=%s' % (len(battle.suits), battle.isJoinable(), battle.fsm.getCurrentState().getName(), toonId)) + else: + self.notify.warning('battle not joinable: no battle for cell %s, toonId=%s' % (cellIndex, toonId)) + return 0 + + return 1 + + def __handleRoundFinished(self, cellId, toonIds, totalHp, deadSuits): + totalMaxHp = 0 + level = self.level + battle = self.battleMgr.cellId2battle[cellId] + for suit in battle.suits: + totalMaxHp += suit.maxHP + + for suit in deadSuits: + level.suits.remove(suit) + + cellReserves = [] + for info in level.reserveSuits: + if info[2] == cellId: + cellReserves.append(info) + + numSpotsAvailable = 4 - len(battle.suits) + if len(cellReserves) > 0 and numSpotsAvailable > 0: + self.joinedReserves = [] + if __dev__: + pass + if len(battle.suits) == 0: + hpPercent = 100 + else: + hpPercent = 100 - totalHp / totalMaxHp * 100.0 + for info in cellReserves: + if info[1] <= hpPercent and len(self.joinedReserves) < numSpotsAvailable: + level.suits.append(info[0]) + self.joinedReserves.append(info) + info[0].setBattleCellIndex(cellId) + + for info in self.joinedReserves: + level.reserveSuits.remove(info) + + if len(self.joinedReserves) > 0: + self.reservesJoining(battle) + level.d_setSuits() + return + if len(battle.suits) == 0: + if battle: + battle.resume() + else: + battle = self.battleMgr.cellId2battle.get(cellId) + if battle: + battle.resume() + + def __handleBattleFinished(self, zoneId): + pass + + def reservesJoining(self, battle): + for info in self.joinedReserves: + battle.suitRequestJoin(info[0]) + + battle.resume() + self.joinedReserves = [] + + def getDoId(self): + return 0 + + def removeSuit(self, suit): + suit.requestDelete() + + def suitBattleCellChange(self, suit, oldCell, newCell): + if oldCell is not None: + if oldCell in self.battleCellId2suits: + self.battleCellId2suits[oldCell].remove(suit) + else: + self.notify.warning('FIXME crash bandaid suitBattleCellChange suit.doId =%s, oldCell=%s not in battleCellId2Suits.keys %s' % (suit.doId, oldCell, self.battleCellId2suits.keys())) + blocker = self.battleMgr.battleBlockers.get(oldCell) + if blocker: + blocker.removeSuit(suit) + if newCell is not None: + self.battleCellId2suits[newCell].append(suit) + + def addSuitToBlocker(self=self): + blocker = self.battleMgr.battleBlockers.get(newCell) + if blocker: + blocker.addSuit(suit) + return 1 + return 0 + + if not addSuitToBlocker(): + self.accept(self.getBattleBlockerEvent(newCell), addSuitToBlocker) + return + + def getBattleBlockerEvent(self, cellId): + return 'battleBlockerAdded-' + str(self.level.doId) + '-' + str(cellId) diff --git a/toontown/coghq/LiftConstants.py b/toontown/coghq/LiftConstants.py new file mode 100644 index 0000000..ed21fdd --- /dev/null +++ b/toontown/coghq/LiftConstants.py @@ -0,0 +1,8 @@ +Down = 0 +Up = 1 + +def oppositeState(state): + if state is Down: + return Up + else: + return Down diff --git a/toontown/coghq/LobbyManager.py b/toontown/coghq/LobbyManager.py new file mode 100644 index 0000000..222991b --- /dev/null +++ b/toontown/coghq/LobbyManager.py @@ -0,0 +1,27 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class LobbyManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('LobbyManager') + SetFactoryZoneMsg = 'setFactoryZone' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + self.notify.debug('generate') + DistributedObject.DistributedObject.generate(self) + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + DistributedObject.DistributedObject.disable(self) + + def getSuitDoorOrigin(self): + return 1 + + def getBossLevel(self): + return 0 diff --git a/toontown/coghq/LobbyManagerAI.py b/toontown/coghq/LobbyManagerAI.py new file mode 100644 index 0000000..9fd2f0a --- /dev/null +++ b/toontown/coghq/LobbyManagerAI.py @@ -0,0 +1,39 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals + +class LobbyManagerAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('LobbyManagerAI') + + def __init__(self, air, bossConstructor): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.air = air + self.bossConstructor = bossConstructor + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.debug('generate') + + def delete(self): + self.notify.debug('delete') + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def createBossOffice(self, avIdList): + bossZone = self.air.allocateZone() + self.notify.info('createBossOffice: %s' % bossZone) + bossCog = self.bossConstructor(self.air) + for avId in avIdList: + if avId: + bossCog.addToon(avId) + + bossCog.generateWithRequired(bossZone) + self.acceptOnce(bossCog.uniqueName('BossDone'), self.destroyBossOffice, extraArgs=[bossCog]) + bossCog.b_setState('WaitForToons') + return bossZone + + def destroyBossOffice(self, bossCog): + bossZone = bossCog.zoneId + self.notify.info('destroyBossOffice: %s' % bossZone) + bossCog.requestDelete() + self.air.deallocateZone(bossZone) diff --git a/toontown/coghq/MintInterior.py b/toontown/coghq/MintInterior.py new file mode 100644 index 0000000..5ddd15b --- /dev/null +++ b/toontown/coghq/MintInterior.py @@ -0,0 +1,239 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import BulletinBoardWatcher +from pandac.PandaModules import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.coghq import DistributedMint + +class MintInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('MintInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = loader.mintId + self.fsm = ClassicFSM.ClassicFSM('MintInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'DFA', + 'fallDown', + 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut', 'stickerBook']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'DFA', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walkteleportOut']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('mintInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + self.music = base.loadMusic('phase_9/audio/bgm/CHQ_FACT_bg.mid') + + def unload(self): + self.parentFSM.getStateNamed('mintInterior').removeChild(self.fsm) + del self.music + del self.fsm + del self.parentFSM + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + base.localAvatar.inventory.setRespectInvasions(0) + base.cr.forbidCheesyEffects(1) + self._telemLimiter = TLGatherAllAvs('MintInterior', RotationLimitToH) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + mint = bboard.get(DistributedMint.DistributedMint.ReadyPost) + self.loader.hood.spawnTitleText(mint.mintId, mint.floorNum) + + self.mintReadyWatcher = BulletinBoardWatcher.BulletinBoardWatcher('MintReady', DistributedMint.DistributedMint.ReadyPost, commence) + self.mintDefeated = 0 + self.acceptOnce(DistributedMint.DistributedMint.WinEvent, self.handleMintWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send(DistributedMint.DistributedMint.WinEvent)) + self.confrontedBoss = 0 + + def handleConfrontedBoss(self = self): + self.confrontedBoss = 1 + + self.acceptOnce('localToonConfrontedMintBoss', handleConfrontedBoss) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + bboard.remove(DistributedMint.DistributedMint.ReadyPost) + self._telemLimiter.destroy() + del self._telemLimiter + base.cr.forbidCheesyEffects(0) + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + del self.mintReadyWatcher + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + MintInterior.notify.debug('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + MintInterior.notify.debug('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + MintInterior.notify.debug('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getMintCreditMultiplier(self.zoneId) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + MintInterior.notify.debug('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + MintInterior.notify.debug('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + MintInterior.notify.debug('__teleportOutDone()') + messenger.send('leavingMint') + messenger.send('localToonLeft') + if self.mintDefeated and not self.confrontedBoss: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + MintInterior.notify.debug('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def handleMintWinEvent(self): + MintInterior.notify.debug('handleMintWinEvent') + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + self.mintDefeated = 1 + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + self.fsm.request('teleportOut', [{'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1}]) + + def enterDied(self, requestStatus, callback = None): + MintInterior.notify.debug('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingMint') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + MintInterior.notify.debug('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveMintAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + MintInterior.notify.debug('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog diff --git a/toontown/coghq/MintLayout.py b/toontown/coghq/MintLayout.py new file mode 100644 index 0000000..32a3f67 --- /dev/null +++ b/toontown/coghq/MintLayout.py @@ -0,0 +1,848 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from toontown.coghq import MintRoomSpecs +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import normalDistrib, lerp +import random + +def printAllCashbotInfo(): + print 'roomId: roomName' + for roomId, roomName in MintRoomSpecs.CashbotMintRoomId2RoomName.items(): + print '%s: %s' % (roomId, roomName) + + print '\nroomId: numBattles' + for roomId, numBattles in MintRoomSpecs.roomId2numBattles.items(): + print '%s: %s' % (roomId, numBattles) + + print '\nmintId floor roomIds' + printMintRoomIds() + print '\nmintId floor numRooms' + printNumRooms() + print '\nmintId floor numForcedBattles' + printNumBattles() + + +def iterateCashbotMints(func): + from toontown.toonbase import ToontownGlobals + for mintId in [ToontownGlobals.CashbotMintIntA, ToontownGlobals.CashbotMintIntB, ToontownGlobals.CashbotMintIntC]: + for floorNum in xrange(ToontownGlobals.MintNumFloors[mintId]): + func(MintLayout(mintId, floorNum)) + + +def printMintInfo(): + + def func(ml): + print ml + + iterateCashbotMints(func) + + +def printMintRoomIds(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getRoomIds() + + iterateCashbotMints(func) + + +def printMintRoomNames(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getRoomNames() + + iterateCashbotMints(func) + + +def printNumRooms(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getNumRooms() + + iterateCashbotMints(func) + + +def printNumBattles(): + + def func(ml): + print ml.getMintId(), ml.getFloorNum(), ml.getNumBattles() + + iterateCashbotMints(func) + + +BakedFloorLayouts = {12500: {0: (0, + 4, + 9, + 6, + 5, + 8, + 17), + 1: (0, + 15, + 13, + 16, + 7, + 6, + 22), + 2: (0, + 4, + 11, + 3, + 9, + 6, + 14, + 19), + 3: (0, + 1, + 3, + 4, + 16, + 14, + 15, + 24), + 4: (0, + 15, + 5, + 8, + 9, + 11, + 10, + 21), + 5: (0, + 13, + 12, + 8, + 7, + 16, + 10, + 18), + 6: (0, + 16, + 13, + 5, + 12, + 7, + 1, + 23), + 7: (0, + 10, + 12, + 7, + 3, + 13, + 16, + 8, + 20), + 8: (0, + 3, + 5, + 7, + 6, + 1, + 4, + 9, + 25), + 9: (0, + 6, + 9, + 10, + 13, + 16, + 8, + 4, + 22), + 10: (0, + 13, + 1, + 7, + 2, + 16, + 11, + 3, + 19), + 11: (0, + 3, + 1, + 6, + 4, + 14, + 8, + 9, + 24), + 12: (0, + 7, + 14, + 2, + 1, + 8, + 5, + 10, + 11, + 21), + 13: (0, + 13, + 6, + 4, + 11, + 3, + 9, + 10, + 8, + 17), + 14: (0, + 15, + 5, + 1, + 14, + 10, + 4, + 7, + 16, + 23), + 15: (0, + 16, + 10, + 11, + 2, + 1, + 3, + 14, + 5, + 20), + 16: (0, + 5, + 8, + 10, + 6, + 3, + 15, + 14, + 7, + 25), + 17: (0, + 12, + 13, + 5, + 8, + 14, + 11, + 7, + 16, + 10, + 22), + 18: (0, + 11, + 3, + 15, + 7, + 16, + 14, + 6, + 1, + 5, + 18), + 19: (0, + 10, + 16, + 11, + 3, + 5, + 12, + 13, + 7, + 14, + 24)}, + 12600: {0: (0, + 8, + 1, + 6, + 14, + 2, + 5, + 9, + 17), + 1: (0, + 4, + 14, + 7, + 2, + 13, + 8, + 9, + 18), + 2: (0, + 7, + 9, + 6, + 5, + 14, + 12, + 3, + 20), + 3: (0, + 6, + 2, + 13, + 16, + 7, + 5, + 3, + 9, + 22), + 4: (0, + 15, + 4, + 9, + 8, + 6, + 13, + 5, + 11, + 23), + 5: (0, + 13, + 7, + 14, + 15, + 11, + 3, + 2, + 8, + 25), + 6: (0, + 5, + 14, + 2, + 11, + 7, + 16, + 10, + 15, + 18), + 7: (0, + 10, + 9, + 5, + 4, + 2, + 7, + 13, + 11, + 19), + 8: (0, + 11, + 4, + 12, + 6, + 1, + 13, + 7, + 3, + 21), + 9: (0, + 15, + 16, + 5, + 13, + 9, + 14, + 4, + 6, + 3, + 23), + 10: (0, + 16, + 15, + 7, + 6, + 8, + 3, + 4, + 9, + 10, + 24), + 11: (0, + 5, + 8, + 4, + 12, + 13, + 9, + 11, + 16, + 3, + 17), + 12: (0, + 13, + 16, + 7, + 4, + 12, + 3, + 6, + 5, + 1, + 19), + 13: (0, + 14, + 6, + 12, + 13, + 7, + 10, + 3, + 16, + 9, + 20), + 14: (0, + 9, + 15, + 13, + 5, + 6, + 3, + 14, + 11, + 4, + 22), + 15: (0, + 13, + 14, + 3, + 12, + 16, + 11, + 9, + 4, + 5, + 7, + 24), + 16: (0, + 3, + 6, + 1, + 7, + 5, + 10, + 9, + 4, + 13, + 15, + 25), + 17: (0, + 3, + 6, + 14, + 4, + 13, + 16, + 12, + 8, + 5, + 7, + 18), + 18: (0, + 11, + 13, + 4, + 1, + 15, + 6, + 3, + 8, + 9, + 16, + 20), + 19: (0, + 11, + 5, + 8, + 7, + 2, + 6, + 13, + 3, + 14, + 9, + 21)}, + 12700: {0: (0, + 16, + 14, + 6, + 1, + 5, + 9, + 2, + 15, + 8, + 17), + 1: (0, + 3, + 2, + 12, + 14, + 8, + 13, + 6, + 10, + 7, + 23), + 2: (0, + 15, + 9, + 5, + 12, + 7, + 4, + 11, + 14, + 16, + 21), + 3: (0, + 2, + 13, + 7, + 6, + 8, + 15, + 4, + 1, + 11, + 19), + 4: (0, + 12, + 7, + 4, + 6, + 10, + 14, + 13, + 16, + 15, + 11, + 17), + 5: (0, + 10, + 2, + 9, + 13, + 4, + 8, + 1, + 15, + 14, + 11, + 23), + 6: (0, + 2, + 14, + 4, + 10, + 16, + 15, + 1, + 3, + 8, + 6, + 21), + 7: (0, + 14, + 11, + 1, + 7, + 9, + 10, + 12, + 8, + 5, + 2, + 19), + 8: (0, + 9, + 11, + 8, + 5, + 1, + 4, + 3, + 7, + 15, + 2, + 17), + 9: (0, + 2, + 9, + 7, + 11, + 16, + 10, + 15, + 3, + 8, + 6, + 23), + 10: (0, + 4, + 10, + 6, + 8, + 7, + 15, + 2, + 1, + 3, + 13, + 21), + 11: (0, + 10, + 14, + 8, + 6, + 9, + 15, + 5, + 1, + 2, + 13, + 19), + 12: (0, + 16, + 5, + 12, + 10, + 6, + 9, + 11, + 3, + 15, + 13, + 17), + 13: (0, + 1, + 3, + 6, + 14, + 4, + 10, + 12, + 15, + 13, + 16, + 24), + 14: (0, + 8, + 7, + 14, + 9, + 1, + 2, + 6, + 16, + 10, + 15, + 13, + 21), + 15: (0, + 4, + 1, + 8, + 11, + 12, + 3, + 10, + 16, + 13, + 6, + 15, + 19), + 16: (0, + 6, + 3, + 10, + 4, + 1, + 2, + 13, + 11, + 5, + 15, + 16, + 17), + 17: (0, + 6, + 16, + 5, + 12, + 11, + 1, + 8, + 14, + 15, + 9, + 10, + 24), + 18: (0, + 15, + 8, + 12, + 10, + 1, + 7, + 11, + 9, + 16, + 4, + 5, + 21), + 19: (0, + 10, + 2, + 16, + 5, + 6, + 11, + 13, + 7, + 12, + 1, + 3, + 19)}} + +class MintLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('MintLayout') + + def __init__(self, mintId, floorNum): + self.mintId = mintId + self.floorNum = floorNum + self.roomIds = [] + self.hallways = [] + self.numRooms = 1 + ToontownGlobals.MintNumRooms[self.mintId][self.floorNum] + self.numHallways = self.numRooms - 1 + if self.mintId in BakedFloorLayouts and self.floorNum in BakedFloorLayouts[self.mintId]: + self.roomIds = list(BakedFloorLayouts[self.mintId][self.floorNum]) + else: + self.roomIds = self._genFloorLayout() + hallwayRng = self.getRng() + connectorRoomNames = MintRoomSpecs.CashbotMintConnectorRooms + for i in xrange(self.numHallways): + self.hallways.append(hallwayRng.choice(connectorRoomNames)) + + def _genFloorLayout(self): + rng = self.getRng() + startingRoomIDs = MintRoomSpecs.CashbotMintEntranceIDs + middleRoomIDs = MintRoomSpecs.CashbotMintMiddleRoomIDs + finalRoomIDs = MintRoomSpecs.CashbotMintFinalRoomIDs + + numBattlesLeft = ToontownGlobals.MintNumBattles[self.mintId] + + finalRoomId = rng.choice(finalRoomIDs) + numBattlesLeft -= MintRoomSpecs.getNumBattles(finalRoomId) + + middleRoomIds = [] + middleRoomsLeft = self.numRooms - 2 + + numBattles2middleRoomIds = invertDictLossless(MintRoomSpecs.middleRoomId2numBattles) + + allBattleRooms = [] + for num, roomIds in numBattles2middleRoomIds.items(): + if num > 0: + allBattleRooms.extend(roomIds) + while 1: + allBattleRoomIds = list(allBattleRooms) + rng.shuffle(allBattleRoomIds) + battleRoomIds = self._chooseBattleRooms(numBattlesLeft, + allBattleRoomIds) + if battleRoomIds is not None: + break + + MintLayout.notify.info('could not find a valid set of battle rooms, trying again') + + middleRoomIds.extend(battleRoomIds) + middleRoomsLeft -= len(battleRoomIds) + + if middleRoomsLeft > 0: + actionRoomIds = numBattles2middleRoomIds[0] + for i in xrange(middleRoomsLeft): + roomId = rng.choice(actionRoomIds) + actionRoomIds.remove(roomId) + middleRoomIds.append(roomId) + + roomIds = [] + + roomIds.append(rng.choice(startingRoomIDs)) + + rng.shuffle(middleRoomIds) + roomIds.extend(middleRoomIds) + + roomIds.append(finalRoomId) + + return roomIds + + def getNumRooms(self): + return len(self.roomIds) + + def getRoomId(self, n): + return self.roomIds[n] + + def getRoomIds(self): + return self.roomIds[:] + + def getRoomNames(self): + names = [] + for roomId in self.roomIds: + names.append(MintRoomSpecs.CashbotMintRoomId2RoomName[roomId]) + + return names + + def getNumHallways(self): + return len(self.hallways) + + def getHallwayModel(self, n): + return self.hallways[n] + + def getNumBattles(self): + numBattles = 0 + for roomId in self.getRoomIds(): + numBattles += MintRoomSpecs.roomId2numBattles[roomId] + + return numBattles + + def getMintId(self): + return self.mintId + + def getFloorNum(self): + return self.floorNum + + def getRng(self): + return random.Random(self.mintId * self.floorNum) + + def _chooseBattleRooms(self, numBattlesLeft, allBattleRoomIds, baseIndex = 0, chosenBattleRooms = None): + if chosenBattleRooms is None: + chosenBattleRooms = [] + while baseIndex < len(allBattleRoomIds): + nextRoomId = allBattleRoomIds[baseIndex] + baseIndex += 1 + newNumBattlesLeft = numBattlesLeft - MintRoomSpecs.middleRoomId2numBattles[nextRoomId] + if newNumBattlesLeft < 0: + continue + elif newNumBattlesLeft == 0: + chosenBattleRooms.append(nextRoomId) + return chosenBattleRooms + chosenBattleRooms.append(nextRoomId) + result = self._chooseBattleRooms(newNumBattlesLeft, allBattleRoomIds, baseIndex, chosenBattleRooms) + if result is not None: + return result + else: + del chosenBattleRooms[-1:] + else: + return + + return + + def __str__(self): + return 'MintLayout: id=%s, floor=%s, numRooms=%s, numBattles=%s' % (self.mintId, + self.floorNum, + self.getNumRooms(), + self.getNumBattles()) + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/MintManagerAI.py b/toontown/coghq/MintManagerAI.py new file mode 100644 index 0000000..3b5fcbd --- /dev/null +++ b/toontown/coghq/MintManagerAI.py @@ -0,0 +1,49 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedMintAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MintLayout +from direct.showbase import DirectObject +import random + +class MintManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('MintManagerAI') + mintId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createMint(self, mintId, players): + for avId in players: + if bboard.has('mintId-%s' % avId): + mintId = bboard.get('mintId-%s' % avId) + break + + numFloors = ToontownGlobals.MintNumFloors[mintId] + floor = random.randrange(numFloors) + for avId in players: + if bboard.has('mintFloor-%s' % avId): + floor = bboard.get('mintFloor-%s' % avId) + floor = max(0, floor) + floor = min(floor, numFloors - 1) + break + + for avId in players: + if bboard.has('mintRoom-%s' % avId): + roomId = bboard.get('mintRoom-%s' % avId) + for i in xrange(numFloors): + layout = MintLayout.MintLayout(mintId, i) + if roomId in layout.getRoomIds(): + floor = i + else: + from toontown.coghq import MintRoomSpecs + roomName = MintRoomSpecs.CashbotMintRoomId2RoomName[roomId] + MintManagerAI.notify.warning('room %s (%s) not found in any floor of mint %s' % (roomId, roomName, mintId)) + + mintZone = self.air.allocateZone() + mint = DistributedMintAI.DistributedMintAI(self.air, mintId, mintZone, floor, players) + mint.generateWithRequired(mintZone) + return mintZone diff --git a/toontown/coghq/MintProduct.py b/toontown/coghq/MintProduct.py new file mode 100644 index 0000000..f21903b --- /dev/null +++ b/toontown/coghq/MintProduct.py @@ -0,0 +1,40 @@ +from toontown.toonbase.ToontownGlobals import * +from otp.level import BasicEntities + +class MintProduct(BasicEntities.NodePathEntity): + Models = {CashbotMintIntA: 'phase_10/models/cashbotHQ/MoneyBag', + CashbotMintIntB: 'phase_10/models/cashbotHQ/MoneyStackPallet', + CashbotMintIntC: 'phase_10/models/cashbotHQ/GoldBarStack'} + Scales = {CashbotMintIntA: 0.98, + CashbotMintIntB: 0.38, + CashbotMintIntC: 0.6} + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.model = None + self.mintId = self.level.mintId + 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 + self.model = loader.loadModel(self.Models[self.mintId]) + self.model.setScale(self.Scales[self.mintId]) + self.model.flattenStrong() + if self.model: + self.model.reparentTo(self) + return + + if __dev__: + + def setMintId(self, mintId): + self.mintId = mintId + self.loadModel() diff --git a/toontown/coghq/MintProductPallet.py b/toontown/coghq/MintProductPallet.py new file mode 100644 index 0000000..9cc7471 --- /dev/null +++ b/toontown/coghq/MintProductPallet.py @@ -0,0 +1,10 @@ +from toontown.toonbase.ToontownGlobals import * +from toontown.coghq import MintProduct + +class MintProductPallet(MintProduct.MintProduct): + Models = {CashbotMintIntA: 'phase_10/models/cashbotHQ/DoubleCoinStack.bam', + CashbotMintIntB: 'phase_10/models/cogHQ/DoubleMoneyStack.bam', + CashbotMintIntC: 'phase_10/models/cashbotHQ/DoubleGoldStack.bam'} + Scales = {CashbotMintIntA: 1.0, + CashbotMintIntB: 1.0, + CashbotMintIntC: 1.0} diff --git a/toontown/coghq/MintRoom.py b/toontown/coghq/MintRoom.py new file mode 100644 index 0000000..6659de5 --- /dev/null +++ b/toontown/coghq/MintRoom.py @@ -0,0 +1,103 @@ +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MintRoomSpecs +import random + +class MintRoom(DirectObject.DirectObject): + FloorCollPrefix = 'mintFloorColl' + CashbotMintDoorFrame = 'phase_10/models/cashbotHQ/DoorFrame' + + def __init__(self, path = None): + if path is not None: + if path in MintRoomSpecs.CashbotMintConnectorRooms: + loadFunc = loader.loadModelCopy + else: + loadFunc = loader.loadModel + self.setGeom(loadFunc(path)) + self.localToonFSM = ClassicFSM.ClassicFSM('MintRoomLocalToonPresent', [State.State('off', self.enterLtOff, self.exitLtOff, ['notPresent']), State.State('notPresent', self.enterLtNotPresent, self.exitLtNotPresent, ['present']), State.State('present', self.enterLtPresent, self.exitLtPresent, ['notPresent'])], 'notPresent', 'notPresent') + self.localToonFSM.enterInitialState() + return + + def delete(self): + del self.localToonFSM + + def enter(self): + self.localToonFSM.request('notPresent') + + def exit(self): + self.localToonFSM.requestFinalState() + + def setRoomNum(self, num): + self.roomNum = num + + def getRoomNum(self): + return self.roomNum + + def setGeom(self, geom): + self.__geom = geom + + def getGeom(self): + return self.__geom + + def _getEntrances(self): + return self.__geom.findAllMatches('**/ENTRANCE*') + + def _getExits(self): + return self.__geom.findAllMatches('**/EXIT*') + + def attachTo(self, other, rng): + otherExits = other._getExits() + entrances = self._getEntrances() + otherDoor = otherExits[0] + thisDoor = rng.choice(entrances) + geom = self.getGeom() + otherGeom = other.getGeom() + tempNode = otherDoor.attachNewNode('tempRotNode') + geom.reparentTo(tempNode) + geom.clearMat() + geom.setPos(Vec3(0) - thisDoor.getPos(geom)) + tempNode.setH(-thisDoor.getH(otherDoor)) + geom.wrtReparentTo(otherGeom.getParent()) + tempNode.removeNode() + doorFrame = loader.loadModel(MintRoom.CashbotMintDoorFrame) + doorFrame.reparentTo(thisDoor) + + def getFloorCollName(self): + return '%s%s' % (MintRoom.FloorCollPrefix, self.roomNum) + + def initFloorCollisions(self): + allColls = self.getGeom().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 = self.getFloorCollName() + others = self.getGeom().findAllMatches('**/%s' % floorCollName) + for other in others: + other.setName('%s_renamed' % floorCollName) + + for floorColl in floorColls: + floorColl.setName(floorCollName) + + def enterLtOff(self): + pass + + def exitLtOff(self): + pass + + def enterLtNotPresent(self): + pass + + def exitLtNotPresent(self): + pass + + def enterLtPresent(self): + pass + + def exitLtPresent(self): + pass diff --git a/toontown/coghq/MintRoomBase.py b/toontown/coghq/MintRoomBase.py new file mode 100644 index 0000000..9cc523a --- /dev/null +++ b/toontown/coghq/MintRoomBase.py @@ -0,0 +1,24 @@ +from toontown.toonbase import ToontownGlobals + +class MintRoomBase: + + def __init__(self): + pass + + def setMintId(self, mintId): + self.mintId = mintId + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(mintId) + + def setRoomId(self, roomId): + self.roomId = roomId + + def getCogTrack(self): + return self.cogTrack + + if __dev__: + + def getEntityTypeReg(self): + import FactoryEntityTypes + from otp.level import EntityTypeRegistry + typeReg = EntityTypeRegistry.EntityTypeRegistry(FactoryEntityTypes) + return typeReg diff --git a/toontown/coghq/MintRoomSpecs.py b/toontown/coghq/MintRoomSpecs.py new file mode 100644 index 0000000..431ee8d --- /dev/null +++ b/toontown/coghq/MintRoomSpecs.py @@ -0,0 +1,101 @@ +from direct.showbase.PythonUtil import invertDict +from toontown.toonbase import ToontownGlobals +from toontown.coghq import NullCogs +from toontown.coghq import CashbotMintBoilerRoom_Battle00_Cogs +from toontown.coghq import CashbotMintBoilerRoom_Battle01_Cogs +from toontown.coghq import CashbotMintControlRoom_Battle00_Cogs +from toontown.coghq import CashbotMintDuctRoom_Battle00_Cogs +from toontown.coghq import CashbotMintDuctRoom_Battle01_Cogs +from toontown.coghq import CashbotMintGearRoom_Battle00_Cogs +from toontown.coghq import CashbotMintGearRoom_Battle01_Cogs +from toontown.coghq import CashbotMintLavaRoomFoyer_Battle00_Cogs +from toontown.coghq import CashbotMintLavaRoomFoyer_Battle01_Cogs +from toontown.coghq import CashbotMintLobby_Battle00_Cogs +from toontown.coghq import CashbotMintLobby_Battle01_Cogs +from toontown.coghq import CashbotMintOilRoom_Battle00_Cogs +from toontown.coghq import CashbotMintPaintMixerReward_Battle00_Cogs +from toontown.coghq import CashbotMintPipeRoom_Battle00_Cogs +from toontown.coghq import CashbotMintPipeRoom_Battle01_Cogs + +def getMintRoomSpecModule(roomId): + return CashbotMintSpecModules[roomId] + + +def getCogSpecModule(roomId): + roomName = CashbotMintRoomId2RoomName[roomId] + return CogSpecModules.get(roomName, NullCogs) + + +def getNumBattles(roomId): + return roomId2numBattles[roomId] + + +CashbotMintRoomId2RoomName = {0: 'CashbotMintEntrance_Action00', + 1: 'CashbotMintBoilerRoom_Action00', + 2: 'CashbotMintBoilerRoom_Battle00', + 3: 'CashbotMintDuctRoom_Action00', + 4: 'CashbotMintDuctRoom_Battle00', + 5: 'CashbotMintGearRoom_Action00', + 6: 'CashbotMintGearRoom_Battle00', + 7: 'CashbotMintLavaRoomFoyer_Action00', + 8: 'CashbotMintLavaRoomFoyer_Action01', + 9: 'CashbotMintLavaRoomFoyer_Battle00', + 10: 'CashbotMintLavaRoom_Action00', + 11: 'CashbotMintLobby_Action00', + 12: 'CashbotMintLobby_Battle00', + 13: 'CashbotMintPaintMixer_Action00', + 14: 'CashbotMintPipeRoom_Action00', + 15: 'CashbotMintPipeRoom_Battle00', + 16: 'CashbotMintStomperAlley_Action00', + 17: 'CashbotMintBoilerRoom_Battle01', + 18: 'CashbotMintControlRoom_Battle00', + 19: 'CashbotMintDuctRoom_Battle01', + 20: 'CashbotMintGearRoom_Battle01', + 21: 'CashbotMintLavaRoomFoyer_Battle01', + 22: 'CashbotMintOilRoom_Battle00', + 23: 'CashbotMintLobby_Battle01', + 24: 'CashbotMintPaintMixerReward_Battle00', + 25: 'CashbotMintPipeRoom_Battle01'} +CashbotMintRoomName2RoomId = invertDict(CashbotMintRoomId2RoomName) +CashbotMintEntranceIDs = (0,) +CashbotMintMiddleRoomIDs = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) +CashbotMintFinalRoomIDs = (17, 18, 19, 20, 21, 22, 23, 24, 25) +CashbotMintConnectorRooms = ('phase_10/models/cashbotHQ/connector_7cubeL2', 'phase_10/models/cashbotHQ/connector_7cubeR2') +CashbotMintSpecModules = {} +if not isClient(): + print 'EXECWARNING MintRoomSpecs: %s' % CashbotMintRoomName2RoomId + printStack() +for roomName, roomId in CashbotMintRoomName2RoomId.items(): + exec 'from toontown.coghq import %s' % roomName + CashbotMintSpecModules[roomId] = eval(roomName) + +CogSpecModules = {'CashbotMintBoilerRoom_Battle00': CashbotMintBoilerRoom_Battle00_Cogs, + 'CashbotMintBoilerRoom_Battle01': CashbotMintBoilerRoom_Battle01_Cogs, + 'CashbotMintControlRoom_Battle00': CashbotMintControlRoom_Battle00_Cogs, + 'CashbotMintDuctRoom_Battle00': CashbotMintDuctRoom_Battle00_Cogs, + 'CashbotMintDuctRoom_Battle01': CashbotMintDuctRoom_Battle01_Cogs, + 'CashbotMintGearRoom_Battle00': CashbotMintGearRoom_Battle00_Cogs, + 'CashbotMintGearRoom_Battle01': CashbotMintGearRoom_Battle01_Cogs, + 'CashbotMintLavaRoomFoyer_Battle00': CashbotMintLavaRoomFoyer_Battle00_Cogs, + 'CashbotMintLavaRoomFoyer_Battle01': CashbotMintLavaRoomFoyer_Battle01_Cogs, + 'CashbotMintLobby_Battle00': CashbotMintLobby_Battle00_Cogs, + 'CashbotMintLobby_Battle01': CashbotMintLobby_Battle01_Cogs, + 'CashbotMintOilRoom_Battle00': CashbotMintOilRoom_Battle00_Cogs, + 'CashbotMintPaintMixerReward_Battle00': CashbotMintPaintMixerReward_Battle00_Cogs, + 'CashbotMintPipeRoom_Battle00': CashbotMintPipeRoom_Battle00_Cogs, + 'CashbotMintPipeRoom_Battle01': CashbotMintPipeRoom_Battle01_Cogs} +roomId2numBattles = {} +for roomName, roomId in CashbotMintRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numBattles[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numBattles[roomId] = len(cogSpecModule.BattleCells) + +name2id = CashbotMintRoomName2RoomId +roomId2numBattles[name2id['CashbotMintBoilerRoom_Battle00']] = 3 +roomId2numBattles[name2id['CashbotMintPipeRoom_Battle00']] = 2 +del name2id +middleRoomId2numBattles = {} +for roomId in CashbotMintMiddleRoomIDs: + middleRoomId2numBattles[roomId] = roomId2numBattles[roomId] diff --git a/toontown/coghq/MintShelf.py b/toontown/coghq/MintShelf.py new file mode 100644 index 0000000..99908be --- /dev/null +++ b/toontown/coghq/MintShelf.py @@ -0,0 +1,10 @@ +from toontown.toonbase.ToontownGlobals import * +from toontown.coghq import MintProduct + +class MintShelf(MintProduct.MintProduct): + Models = {CashbotMintIntA: 'phase_10/models/cashbotHQ/shelf_A1MoneyBags', + CashbotMintIntB: 'phase_10/models/cashbotHQ/shelf_A1Money', + CashbotMintIntC: 'phase_10/models/cashbotHQ/shelf_A1Gold'} + Scales = {CashbotMintIntA: 1.0, + CashbotMintIntB: 1.0, + CashbotMintIntC: 1.0} diff --git a/toontown/coghq/MoleFieldBase.py b/toontown/coghq/MoleFieldBase.py new file mode 100644 index 0000000..275e09b --- /dev/null +++ b/toontown/coghq/MoleFieldBase.py @@ -0,0 +1,92 @@ +import random +HILL_MOLE = 0 +HILL_BOMB = 1 +HILL_WHACKED = 2 +HILL_COGWHACKED = 3 + +class MoleFieldBase: + WHACKED = 1 + MoveUpTimeMax = 1 + MoveUpTimeMultiplier = 0.95 + MoveUpTimeMin = 0.5 + StayUpTimeMax = 7 + StayUpTimeMultiplier = 0.95 + StayUpTimeMin = 3 + MoveDownTimeMax = 1 + MoveDownTimeMultiplier = 0.95 + MoveDownTimeMin = 0.5 + TimeBetweenPopupMax = 1.5 + TimeBetweenPopupMultiplier = 0.95 + TimeBetweenPopupMin = 0.25 + DamageOnFailure = 20 + + def getRng(self): + return random.Random(self.entId * self.level.doId) + + def scheduleMoles(self): + self.schedule = [] + totalTime = 0 + curMoveUpTime = self.MoveUpTimeMax + curMoveDownTime = self.MoveDownTimeMax + curTimeBetweenPopup = self.TimeBetweenPopupMax + curStayUpTime = self.StayUpTimeMax + curTime = 3 + eligibleMoles = range(self.numMoles) + self.getRng().shuffle(eligibleMoles) + usedMoles = [] + self.notify.debug('eligibleMoles=%s' % eligibleMoles) + self.endingTime = 0 + randOb = random.Random(self.entId * self.level.doId) + while self.endingTime < self.GameDuration: + if len(eligibleMoles) == 0: + eligibleMoles = usedMoles + self.getRng().shuffle(usedMoles) + usedMoles = [] + self.notify.debug('eligibleMoles=%s' % eligibleMoles) + moleIndex = eligibleMoles[0] + eligibleMoles.remove(moleIndex) + usedMoles.append(moleIndex) + moleType = randOb.choice([HILL_MOLE, + HILL_MOLE, + HILL_MOLE, + HILL_BOMB]) + self.schedule.append((curTime, + moleIndex, + curMoveUpTime, + curStayUpTime, + curMoveDownTime, + moleType)) + curTime += curTimeBetweenPopup + curMoveUpTime = self.calcNextMoveUpTime(curTime, curMoveUpTime) + curStayUpTime = self.calcNextStayUpTime(curTime, curStayUpTime) + curMoveDownTime = self.calcNextMoveDownTime(curTime, curMoveDownTime) + curTimeBetweenPopup = self.calcNextTimeBetweenPopup(curTime, curTimeBetweenPopup) + self.endingTime = curTime + curMoveUpTime + curStayUpTime + curMoveDownTime + + self.schedule.pop() + self.endingTime = self.schedule[-1][0] + self.schedule[-1][2] + self.schedule[-1][3] + self.schedule[-1][4] + self.notify.debug('schedule length = %d, endingTime=%f' % (len(self.schedule), self.endingTime)) + + def calcNextMoveUpTime(self, curTime, curMoveUpTime): + newMoveUpTime = curMoveUpTime * self.MoveUpTimeMultiplier + if newMoveUpTime < self.MoveDownTimeMin: + newMoveUpTime = self.MoveDownTimeMin + return newMoveUpTime + + def calcNextStayUpTime(self, curTime, curStayUpTime): + newStayUpTime = curStayUpTime * self.StayUpTimeMultiplier + if newStayUpTime < self.StayUpTimeMin: + newStayUpTime = self.StayUpTimeMin + return newStayUpTime + + def calcNextMoveDownTime(self, curTime, curMoveDownTime): + newMoveDownTime = curMoveDownTime * self.MoveDownTimeMultiplier + if newMoveDownTime < self.MoveDownTimeMin: + newMoveDownTime = self.MoveDownTimeMin + return newMoveDownTime + + def calcNextTimeBetweenPopup(self, curTime, curTimeBetweenPopup): + newTimeBetweenPopup = curTimeBetweenPopup * self.TimeBetweenPopupMultiplier + if newTimeBetweenPopup < self.TimeBetweenPopupMin: + newTimeBetweenPopup = self.TimeBetweenPopupMin + return newTimeBetweenPopup diff --git a/toontown/coghq/MoleHill.py b/toontown/coghq/MoleHill.py new file mode 100644 index 0000000..5e72230 --- /dev/null +++ b/toontown/coghq/MoleHill.py @@ -0,0 +1,152 @@ +from pandac.PandaModules import NodePath, Point3, CollisionSphere, CollisionNode, Vec4 +from direct.interval.IntervalGlobal import Sequence, LerpPosInterval, Parallel, LerpScaleInterval, Track, ParticleInterval, Wait, Func +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MoleFieldBase +from direct.particles import ParticleEffect +from toontown.battle import BattleParticles +from toontown.battle import BattleProps + +class MoleHill(NodePath): + + def __init__(self, x, y, z, moleField, index): + NodePath.__init__(self, 'MoleHill-%d' % index) + self.moleField = moleField + self.index = index + self.loadModel() + self.setPos(x, y, z) + self.schedule = [] + self.popIval = None + self.downIval = None + self.popupNum = 0 + self.hillType = None + self.isUp = 0 + return + + def loadModel(self): + self.hill = loader.loadModel('phase_12/models/bossbotHQ/mole_hole') + self.hill.setZ(0.0) + self.hill.reparentTo(self) + self.hillColName = 'moleHillCol-%d-%d' % (self.moleField.doId, self.index) + self.moleField.accept('enter' + self.hillColName, self.moleField.handleEnterHill) + self.mole = self.attachNewNode('mole') + self.mole.reparentTo(self) + self.mole.setScale(0.75) + self.mole.setZ(-2.5) + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_norm') + self.moleHead.reparentTo(self.mole) + moleColName = 'moleCol-%d-%s' % (self.moleField.doId, self.index) + moleSphere = CollisionSphere(0, 0, 0, 1.0) + collNode = CollisionNode(moleColName) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(moleSphere) + self.moleColNodePath = self.mole.attachNewNode(collNode) + self.moleColNodePath.stash() + self.moleColNodePath.setScale(1.0) + self.moleField.accept('enter' + moleColName, self.moleField.handleEnterMole) + + def destroy(self): + if self.popIval: + self.popIval.pause() + self.popIval = None + if self.downIval: + self.downIval.pause() + self.downIval = None + self.removeNode() + return + + def switchUp(self): + self.isUp = 1 + + def switchDown(self): + self.isUp = 0 + + def setHillType(self, type): + if self.isUp and (self.hillType == MoleFieldBase.HILL_MOLE and type == MoleFieldBase.HILL_BOMB or self.hillType == MoleFieldBase.HILL_BOMB and type == MoleFieldBase.HILL_MOLE): + return + self.hillType = type + self.moleHead.remove() + if type == MoleFieldBase.HILL_MOLE: + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_norm') + self.moleColNodePath.setScale(3.0) + self.moleHead.setH(0) + self.mole.setBillboardAxis(localAvatar, 0) + if type == MoleFieldBase.HILL_BOMB or type == MoleFieldBase.HILL_COGWHACKED: + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_cog') + self.moleColNodePath.setScale(1.0) + self.mole.setBillboardAxis(localAvatar, 0) + if type == MoleFieldBase.HILL_COGWHACKED: + self.doMoleDown() + BattleParticles.loadParticles() + singleGear = BattleParticles.createParticleEffect('GearExplosion', numParticles=1) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('BigGearExplosion', numParticles=30) + gears2MTrack = Track((0.0, ParticleInterval(singleGear, self.hill, worldRelative=1, duration=5.7, cleanup=True)), (0.0, ParticleInterval(smallGearExplosion, self.hill, worldRelative=0, duration=1.2, cleanup=True)), (0.3, ParticleInterval(bigGearExplosion, self.hill, worldRelative=0, duration=1.0, cleanup=True)), name='gears2MTrack') + gears2MTrack.start() + self.popIval = Sequence(Parallel(Sequence(LerpPosInterval(self.moleHead, 0.05, Point3(0.28, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.23, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.28)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.35, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.28, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.31, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.32, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.48)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.28, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.29, 0.0)))), LerpPosInterval(self.mole, 0.5, Point3(0, 0, -2.5)), Func(self.setHillType, MoleFieldBase.HILL_BOMB)) + self.popIval.start() + else: + self.moleHead.setH(0) + if type == MoleFieldBase.HILL_WHACKED: + self.moleHead = loader.loadModel('phase_12/models/bossbotHQ/mole_hit') + self.mole.setBillboardAxis(0) + self.moleColNodePath.setScale(0.0) + if self.popIval: + self.popIval.finish() + if self.downIval: + self.downIval.finish() + self.mole.setPos(0.0, 0.0, 0.0) + self.popIval = Sequence(Parallel(Sequence(LerpPosInterval(self.moleHead, 0.05, Point3(0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.13, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.18)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.15, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.18, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.11, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.12, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.18)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.13, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.15, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.18)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.16, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.18, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.11, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, -0.18, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.17)), LerpPosInterval(self.moleHead, 0.05, Point3(-0.18, 0.0, 0.0)), LerpPosInterval(self.moleHead, 0.05, Point3(0.0, 0.0, 0.0))), Sequence(LerpScaleInterval(self.moleHead, 0.5, 3.5), LerpScaleInterval(self.moleHead, 0.5, 1.0))), LerpPosInterval(self.mole, 0.5, Point3(0, 0, -2.5)), Func(self.setHillType, MoleFieldBase.HILL_MOLE)) + self.popIval.start() + self.moleHead.reparentTo(self.mole) + + def doMolePop(self, startTime, timeToMoveUp, timeToStayUp, timeToMoveDown, moleType): + if self.hillType == MoleFieldBase.HILL_WHACKED or self.hillType == MoleFieldBase.HILL_COGWHACKED: + return + if self.popIval: + self.popIval.pause() + if self.downIval: + self.downIval.pause() + self.downIval = None + moleColor = None + self.switchUp() + self.popupNum += 1 + if self.hillType == MoleFieldBase.HILL_BOMB: + self.popIval = Sequence(Func(self.setHillType, moleType), Func(self.moleColNodePath.unstash), LerpPosInterval(self.mole, timeToMoveUp, Point3(0, 0, 0.0)), Wait(timeToStayUp), LerpPosInterval(self.mole, timeToMoveDown, Point3(0, 0, -2.5)), Func(self.stashMoleCollision), Func(self.switchDown)) + else: + self.popIval = Sequence(Func(self.setHillType, moleType), LerpPosInterval(self.mole, timeToMoveUp, Point3(0, 0, 0.0)), Func(self.moleColNodePath.unstash), Wait(timeToStayUp), Func(self.stashMoleCollision), LerpPosInterval(self.mole, timeToMoveDown, Point3(0, 0, -2.5)), Func(self.switchDown)) + self.popIval.start() + return + + def setGameStartTime(self, gameStartTime): + self.gameStartTime = gameStartTime + self.popupNum = 0 + + def stashMoleCollision(self): + self.moleColNodePath.stash() + + def getPopupNum(self): + return self.popupNum + + def doMoleDown(self): + if self.hillType == MoleFieldBase.HILL_WHACKED or self.hillType == MoleFieldBase.HILL_COGWHACKED: + return + if self.popIval: + self.popIval.pause() + self.popIval = None + if self.downIval: + self.downIval.pause() + self.downIval = Sequence(Func(self.stashMoleCollision), LerpPosInterval(self.mole, 1, Point3(0, 0, -2.5)), Func(self.switchDown)) + self.downIval.start() + return + + def forceMoleDown(self): + if self.popIval: + self.popIval.pause() + self.popIval = None + if self.downIval: + self.downIval.pause() + self.downIval = None + self.stashMoleCollision() + self.switchDown() + self.mole.setPos(0, 0, -2.5) + return diff --git a/toontown/coghq/MovingPlatform.py b/toontown/coghq/MovingPlatform.py new file mode 100644 index 0000000..936181f --- /dev/null +++ b/toontown/coghq/MovingPlatform.py @@ -0,0 +1,88 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.showbase import DirectObject +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +import types + +class MovingPlatform(DirectObject.DirectObject, NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('MovingPlatform') + + def __init__(self): + self.hasLt = 0 + DirectObject.DirectObject.__init__(self) + NodePath.__init__(self) + + def setupCopyModel(self, parentToken, model, floorNodeName = None, parentingNode = None): + if floorNodeName is None: + floorNodeName = 'floor' + if type(parentToken) == types.IntType: + parentToken = ToontownGlobals.SPDynamic + parentToken + self.parentToken = parentToken + self.name = 'MovingPlatform-%s' % parentToken + self.assign(hidden.attachNewNode(self.name)) + self.model = model.copyTo(self) + self.ownsModel = 1 + floorList = self.model.findAllMatches('**/%s' % floorNodeName) + if len(floorList) == 0: + MovingPlatform.notify.warning('no floors in model') + return + for floor in floorList: + floor.setName(self.name) + + if parentingNode == None: + parentingNode = self + base.cr.parentMgr.registerParent(self.parentToken, parentingNode) + self.parentingNode = parentingNode + self.accept('enter%s' % self.name, self.__handleEnter) + self.accept('exit%s' % self.name, self.__handleExit) + return + + def destroy(self): + base.cr.parentMgr.unregisterParent(self.parentToken) + self.ignoreAll() + if self.hasLt: + self.__releaseLt() + if self.ownsModel: + self.model.removeNode() + del self.model + if hasattr(self, 'parentingNode') and self.parentingNode is self: + del self.parentingNode + + def getEnterEvent(self): + return '%s-enter' % self.name + + def getExitEvent(self): + return '%s-exit' % self.name + + def releaseLocalToon(self): + if self.hasLt: + self.__releaseLt() + + def __handleEnter(self, collEntry): + self.notify.debug('on movingPlatform %s' % self.name) + self.__grabLt() + messenger.send(self.getEnterEvent()) + + def __handleExit(self, collEntry): + self.notify.debug('off movingPlatform %s' % self.name) + self.__releaseLt() + messenger.send(self.getExitEvent()) + + def __handleOnFloor(self, collEntry): + if collEntry.getIntoNode().getName() == self.name: + self.__handleEnter(collEntry) + + def __handleOffFloor(self, collEntry): + if collEntry.getIntoNode().getName() == self.name: + self.__handleExit(collEntry) + + def __grabLt(self): + base.localAvatar.b_setParent(self.parentToken) + self.hasLt = 1 + + def __releaseLt(self): + if base.localAvatar.getParent().compareTo(self.parentingNode) == 0: + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + base.localAvatar.controlManager.currentControls.doDeltaPos() + self.hasLt = 0 diff --git a/toontown/coghq/NullCogs.py b/toontown/coghq/NullCogs.py new file mode 100644 index 0000000..78bd47e --- /dev/null +++ b/toontown/coghq/NullCogs.py @@ -0,0 +1,4 @@ +from SpecImports import * +BattleCells = {} +CogData = [] +ReserveCogData = [] diff --git a/toontown/coghq/PaintMixer.py b/toontown/coghq/PaintMixer.py new file mode 100644 index 0000000..1bd9357 --- /dev/null +++ b/toontown/coghq/PaintMixer.py @@ -0,0 +1,13 @@ +import PlatformEntity + +class PaintMixer(PlatformEntity.PlatformEntity): + + def start(self): + PlatformEntity.PlatformEntity.start(self) + model = self.platform.model + shaft = model.find('**/PaintMixerBase1') + shaft.setSz(self.shaftScale) + shaft.node().setPreserveTransform(0) + shaftChild = shaft.find('**/PaintMixerBase') + shaftChild.node().setPreserveTransform(0) + model.flattenMedium() diff --git a/toontown/coghq/PathMasterEntity.py b/toontown/coghq/PathMasterEntity.py new file mode 100644 index 0000000..db22ba3 --- /dev/null +++ b/toontown/coghq/PathMasterEntity.py @@ -0,0 +1,179 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.suit import GoonPathData +from otp.level import PathEntity + +class PathMasterEntity(PathEntity.PathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('PathMasterEntity') + + def __init__(self, level, entId): + self.pathScale = 1.0 + PathEntity.PathEntity.__init__(self, level, entId) + self.setPathIndex(self.pathIndex) + self.initPath() + + def initPath(self): + self.pathTargetList = [None, + None, + None, + None, + None, + None, + None, + None] + if not hasattr(self, 'pathTarget0'): + self.pathTarget0 = None + else: + self.pathTargetList[0] = self.pathTarget0 + if not hasattr(self, 'pathTarget1'): + self.pathTarget1 = None + else: + self.pathTargetList[1] = self.pathTarget1 + if not hasattr(self, 'pathTarget2'): + self.pathTarget2 = None + else: + self.pathTargetList[2] = self.pathTarget2 + if not hasattr(self, 'pathTarget3'): + self.pathTarget3 = None + else: + self.pathTargetList[3] = self.pathTarget3 + if not hasattr(self, 'pathTarget4'): + self.pathTarget4 = None + else: + self.pathTargetList[4] = self.pathTarget4 + if not hasattr(self, 'pathTarget5'): + self.pathTarget5 = None + else: + self.pathTargetList[5] = self.pathTarget5 + if not hasattr(self, 'pathTarget6'): + self.pathTarget6 = None + else: + self.pathTargetList[6] = self.pathTarget6 + if not hasattr(self, 'pathTarget7'): + self.pathTarget7 = None + else: + self.pathTargetList[7] = self.pathTarget7 + return + + def destroy(self): + PathEntity.PathEntity.destroy(self) + + def setPathTarget0(self, targetId): + self.pathTarget0 = targetId + self.pathTargetList[0] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def setPathTarget1(self, targetId): + self.pathTarget1 = targetId + self.pathTargetList[1] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def setPathTarget2(self, targetId): + self.pathTarget2 = targetId + self.pathTargetList[2] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def setPathTarget3(self, targetId): + self.pathTarget3 = targetId + self.pathTargetList[3] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def setPathTarget4(self, targetId): + self.pathTarget4 = targetId + self.pathTargetList[4] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def setPathTarget5(self, targetId): + self.pathTarget5 = targetId + self.pathTargetList[5] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def setPathTarget6(self, targetId): + self.pathTarget6 = targetId + self.pathTargetList[6] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def setPathTarget7(self, targetId): + self.pathTarget7 = targetId + self.pathTargetList[7] = targetId + if __dev__: + messenger.send(self.getChangeEvent()) + + def getReducedPath(self): + returnPath = [] + for entityId in self.pathTargetList: + if self.level and entityId != 0: + thing = self.level.entities.get(entityId, None) + returnPath.append(thing.getPos(self)) + + return returnPath + + 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) + self.path = self.getReducedPath() + if self.path is None or len(self.path) < 1: + 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 + + def makePathTrackBak(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/toontown/coghq/PlatformEntity.py b/toontown/coghq/PlatformEntity.py new file mode 100644 index 0000000..87a1ef8 --- /dev/null +++ b/toontown/coghq/PlatformEntity.py @@ -0,0 +1,49 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from otp.level import BasicEntities +import MovingPlatform + +class PlatformEntity(BasicEntities.NodePathEntity): + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + self.start() + + def destroy(self): + self.stop() + BasicEntities.NodePathEntity.destroy(self) + + def start(self): + model = loader.loadModel(self.modelPath) + if model is None: + return + if len(self.floorName) == 0: + return + model.setScale(self.modelScale) + model.flattenMedium() + self.platform = MovingPlatform.MovingPlatform() + self.platform.setupCopyModel(self.getParentToken(), model, self.floorName) + self.platform.reparentTo(self) + startPos = Point3(0, 0, 0) + endPos = self.offset + distance = Vec3(self.offset).length() + waitDur = self.period * self.waitPercent + moveDur = self.period - waitDur + self.moveIval = Sequence(WaitInterval(waitDur * 0.5), LerpPosInterval(self.platform, moveDur * 0.5, endPos, startPos=startPos, name='platformOut%s' % self.entId, blendType=self.motion, fluid=1), WaitInterval(waitDur * 0.5), LerpPosInterval(self.platform, moveDur * 0.5, startPos, startPos=endPos, name='platformBack%s' % self.entId, blendType=self.motion, fluid=1), name=self.getUniqueName('platformIval')) + self.moveIval.loop() + self.moveIval.setT(globalClock.getFrameTime() - self.level.startTime + self.period * self.phaseShift) + return + + def stop(self): + if hasattr(self, 'moveIval'): + self.moveIval.pause() + del self.moveIval + if hasattr(self, 'platform'): + self.platform.destroy() + del self.platform + + if __dev__: + + def attribChanged(self, *args): + self.stop() + self.start() diff --git a/toontown/coghq/PromotionManagerAI.py b/toontown/coghq/PromotionManagerAI.py new file mode 100644 index 0000000..4bc4c67 --- /dev/null +++ b/toontown/coghq/PromotionManagerAI.py @@ -0,0 +1,78 @@ +from otp.ai.AIBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +import random +from toontown.suit import SuitDNA +import CogDisguiseGlobals +from toontown.toonbase.ToontownBattleGlobals import getInvasionMultiplier +MeritMultiplier = 0.5 + +class PromotionManagerAI: + notify = DirectNotifyGlobal.directNotify.newCategory('PromotionManagerAI') + + def __init__(self, air): + self.air = air + + def getPercentChance(self): + return 100.0 + + def recoverMerits(self, av, cogList, zoneId, multiplier = 1, extraMerits = None): + avId = av.getDoId() + meritsRecovered = [0, + 0, + 0, + 0] + if extraMerits is None: + extraMerits = [0, + 0, + 0, + 0] + if self.air.suitInvasionManager.getInvading(): + multiplier *= getInvasionMultiplier() + for i in range(len(extraMerits)): + if CogDisguiseGlobals.isSuitComplete(av.getCogParts(), i): + meritsRecovered[i] += extraMerits[i] + self.notify.debug('recoverMerits: extra merits = %s' % extraMerits[i]) + + self.notify.debug('recoverMerits: multiplier = %s' % multiplier) + for cogDict in cogList: + dept = SuitDNA.suitDepts.index(cogDict['track']) + if avId in cogDict['activeToons']: + if CogDisguiseGlobals.isSuitComplete(av.getCogParts(), SuitDNA.suitDepts.index(cogDict['track'])): + self.notify.debug('recoverMerits: checking against cogDict: %s' % cogDict) + rand = random.random() * 100 + if rand <= self.getPercentChance() and not cogDict['isVirtual']: + merits = cogDict['level'] * MeritMultiplier + merits = int(round(merits)) + if cogDict['hasRevives']: + merits *= 2 + merits = merits * multiplier + merits = int(round(merits)) + meritsRecovered[dept] += merits + self.notify.debug('recoverMerits: merits = %s' % merits) + else: + self.notify.debug('recoverMerits: virtual cog!') + + if meritsRecovered != [0, + 0, + 0, + 0]: + actualCounted = [0, + 0, + 0, + 0] + merits = av.getCogMerits() + for i in range(len(meritsRecovered)): + max = CogDisguiseGlobals.getTotalMerits(av, i) + if max: + if merits[i] + meritsRecovered[i] <= max: + actualCounted[i] = meritsRecovered[i] + merits[i] += meritsRecovered[i] + else: + actualCounted[i] = max - merits[i] + merits[i] = max + av.b_setCogMerits(merits) + + if reduce(lambda x, y: x + y, actualCounted): + self.air.writeServerEvent('merits', avId, '%s|%s|%s|%s' % tuple(actualCounted)) + self.notify.debug('recoverMerits: av %s recovered merits %s' % (avId, actualCounted)) + return meritsRecovered diff --git a/toontown/coghq/RenderingEntity.py b/toontown/coghq/RenderingEntity.py new file mode 100644 index 0000000..367c506 --- /dev/null +++ b/toontown/coghq/RenderingEntity.py @@ -0,0 +1,71 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.suit import GoonPathData +from otp.level import BasicEntities + +class RenderingEntity(BasicEntities.NodePathEntity): + notify = DirectNotifyGlobal.directNotify.newCategory('PathMasterEntity') + + def __init__(self, level, entId): + BasicEntities.NodePathEntity.__init__(self, level, entId) + if hasattr(self, 'colorR'): + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + if hasattr(self, 'blending'): + self.setBlending(self.blending) + if hasattr(self, 'fogOn'): + self.setFogOn(self.fogOn) + + def destroy(self): + BasicEntities.NodePathEntity.destroy(self) + + def setColorR(self, newColor): + self.colorR = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorG(self, newColor): + self.colorG = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorB(self, newColor): + self.colorB = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorA(self, newColor): + self.colorA = newColor + self.setColorScale(self.colorR, self.colorG, self.colorB, self.colorA) + + def setColorScale(self, R, G, B, A): + BasicEntities.NodePathEntity.setColorScale(self, R, G, B, A) + self.chooseBin() + self.setBlending(self.blending) + + def chooseBin(self): + if not hasattr(self, 'renderBin'): + self.renderBin = 'default' + self.setBin(self.renderBin, 0) + + def setBlending(self, blending): + self.blending = blending + if blending == 'Additive': + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + self.setDepthWrite(False) + elif blending == 'Alpha': + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) + self.setDepthWrite(True) + self.setTransparency(1) + else: + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) + self.setDepthWrite(True) + self.setTransparency(0) + self.chooseBin() + + def setFogOn(self, fog): + self.fogOn = fog + if self.fogOn: + myFog = Fog('Fog Rendering Fog of Rendering') + myFog.setColor(1.0, 0.0, 0.0) + myFog.setExpDensity(0.5) + self.setFog(myFog) + else: + self.clearFog() diff --git a/toontown/coghq/SellbotCogHQLoader.py b/toontown/coghq/SellbotCogHQLoader.py new file mode 100644 index 0000000..8b43c90 --- /dev/null +++ b/toontown/coghq/SellbotCogHQLoader.py @@ -0,0 +1,169 @@ +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 +import FactoryExterior +import FactoryInterior +import SellbotHQExterior +import SellbotHQBossBattle +from pandac.PandaModules import DecalEffect +aspectSF = 0.7227 + +class SellbotCogHQLoader(CogHQLoader.CogHQLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('SellbotCogHQLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + CogHQLoader.CogHQLoader.__init__(self, hood, parentFSMState, doneEvent) + self.fsm.addState(State.State('factoryExterior', self.enterFactoryExterior, self.exitFactoryExterior, ['quietZone', 'factoryInterior', 'cogHQExterior'])) + for stateName in ['start', 'cogHQExterior', 'quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('factoryExterior') + + self.fsm.addState(State.State('factoryInterior', self.enterFactoryInterior, self.exitFactoryInterior, ['quietZone', 'factoryExterior'])) + for stateName in ['quietZone']: + state = self.fsm.getStateNamed(stateName) + state.addTransition('factoryInterior') + + self.musicFile = 'phase_9/audio/bgm/encntr_suit_HQ_nbrhood.mid' + self.cogHQExteriorModelPath = 'phase_9/models/cogHQ/SellbotHQExterior' + self.cogHQLobbyModelPath = 'phase_9/models/cogHQ/SellbotHQLobby' + self.factoryExteriorModelPath = 'phase_9/models/cogHQ/SellbotFactoryExterior' + self.geom = None + return + + def load(self, zoneId): + CogHQLoader.CogHQLoader.load(self, zoneId) + Toon.loadSellbotHQAnims() + + 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.SellbotHQ: + self.geom = loader.loadModel(self.cogHQExteriorModelPath) + dgLinkTunnel = self.geom.find('**/Tunnel1') + dgLinkTunnel.setName('linktunnel_dg_5316_DNARoot') + factoryLinkTunnel = self.geom.find('**/Tunnel2') + factoryLinkTunnel.setName('linktunnel_sellhq_11200_DNARoot') + cogSignModel = loader.loadModel('phase_4/models/props/sign_sellBotHeadHQ') + cogSign = cogSignModel.find('**/sign_sellBotHeadHQ') + cogSignSF = 23 + dgSign = cogSign.copyTo(dgLinkTunnel) + dgSign.setPosHprScale(0.0, -291.5, 29, 180.0, 0.0, 0.0, cogSignSF, cogSignSF, cogSignSF * aspectSF) + dgSign.node().setEffect(DecalEffect.make()) + dgText = DirectGui.OnscreenText(text=TTLocalizer.DaisyGardens[-1], font=ToontownGlobals.getSuitFont(), pos=(0, -0.3), scale=TTLocalizer.SCHQLdgText, mayChange=False, parent=dgSign) + dgText.setDepthWrite(0) + factorySign = cogSign.copyTo(factoryLinkTunnel) + factorySign.setPosHprScale(148.625, -155, 27, -90.0, 0.0, 0.0, cogSignSF, cogSignSF, cogSignSF * aspectSF) + factorySign.node().setEffect(DecalEffect.make()) + factoryTypeText = DirectGui.OnscreenText(text=TTLocalizer.Sellbot, font=ToontownGlobals.getSuitFont(), pos=(0, -0.25), scale=0.075, mayChange=False, parent=factorySign) + factoryTypeText.setDepthWrite(0) + factoryText = DirectGui.OnscreenText(text=TTLocalizer.Factory, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=0.12, mayChange=False, parent=factorySign) + factoryText.setDepthWrite(0) + doors = self.geom.find('**/doors') + door0 = doors.find('**/door_0') + door1 = doors.find('**/door_1') + door2 = doors.find('**/door_2') + door3 = doors.find('**/door_3') + index = 0 + for door in [door0, + door1, + door2, + door3]: + doorFrame = door.find('**/doorDoubleFlat/+GeomNode') + door.find('**/doorFrameHoleLeft').wrtReparentTo(doorFrame) + door.find('**/doorFrameHoleRight').wrtReparentTo(doorFrame) + doorFrame.node().setEffect(DecalEffect.make()) + index += 1 + + elif zoneId == ToontownGlobals.SellbotFactoryExt: + self.geom = loader.loadModel(self.factoryExteriorModelPath) + factoryLinkTunnel = self.geom.find('**/tunnel_group2') + factoryLinkTunnel.setName('linktunnel_sellhq_11000_DNARoot') + factoryLinkTunnel.find('**/tunnel_sphere').setName('tunnel_trigger') + cogSignModel = loader.loadModel('phase_4/models/props/sign_sellBotHeadHQ') + cogSign = cogSignModel.find('**/sign_sellBotHeadHQ') + cogSignSF = 23 + elevatorSignSF = 15 + hqSign = cogSign.copyTo(factoryLinkTunnel) + hqSign.setPosHprScale(0.0, -353, 27.5, -180.0, 0.0, 0.0, cogSignSF, cogSignSF, cogSignSF * aspectSF) + hqSign.node().setEffect(DecalEffect.make()) + hqTypeText = DirectGui.OnscreenText(text=TTLocalizer.Sellbot, font=ToontownGlobals.getSuitFont(), pos=(0, -0.25), scale=0.075, mayChange=False, parent=hqSign) + hqTypeText.setDepthWrite(0) + hqText = DirectGui.OnscreenText(text=TTLocalizer.Headquarters, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=0.1, mayChange=False, parent=hqSign) + hqText.setDepthWrite(0) + frontDoor = self.geom.find('**/doorway1') + fdSign = cogSign.copyTo(frontDoor) + fdSign.setPosHprScale(62.74, -87.99, 17.26, 2.72, 0.0, 0.0, elevatorSignSF, elevatorSignSF, elevatorSignSF * aspectSF) + fdSign.node().setEffect(DecalEffect.make()) + fdTypeText = DirectGui.OnscreenText(text=TTLocalizer.Factory, font=ToontownGlobals.getSuitFont(), pos=(0, -0.25), scale=TTLocalizer.SCHQLfdTypeText, mayChange=False, parent=fdSign) + fdTypeText.setDepthWrite(0) + fdText = DirectGui.OnscreenText(text=TTLocalizer.SellbotFrontEntrance, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=TTLocalizer.SCHQLdgText, mayChange=False, parent=fdSign) + fdText.setDepthWrite(0) + sideDoor = self.geom.find('**/doorway2') + sdSign = cogSign.copyTo(sideDoor) + sdSign.setPosHprScale(-164.78, 26.28, 17.25, -89.89, 0.0, 0.0, elevatorSignSF, elevatorSignSF, elevatorSignSF * aspectSF) + sdSign.node().setEffect(DecalEffect.make()) + sdTypeText = DirectGui.OnscreenText(text=TTLocalizer.Factory, font=ToontownGlobals.getSuitFont(), pos=(0, -0.25), scale=0.075, mayChange=False, parent=sdSign) + sdTypeText.setDepthWrite(0) + sdText = DirectGui.OnscreenText(text=TTLocalizer.SellbotSideEntrance, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=0.1, mayChange=False, parent=sdSign) + sdText.setDepthWrite(0) + elif zoneId == ToontownGlobals.SellbotLobby: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: COGHQ: Visit SellbotLobby') + self.geom = loader.loadModel(self.cogHQLobbyModelPath) + front = self.geom.find('**/frontWall') + front.node().setEffect(DecalEffect.make()) + door = self.geom.find('**/door_0') + parent = door.getParent() + door.wrtReparentTo(front) + doorFrame = door.find('**/doorDoubleFlat/+GeomNode') + door.find('**/doorFrameHoleLeft').wrtReparentTo(doorFrame) + door.find('**/doorFrameHoleRight').wrtReparentTo(doorFrame) + doorFrame.node().setEffect(DecalEffect.make()) + door.find('**/leftDoor').wrtReparentTo(parent) + door.find('**/rightDoor').wrtReparentTo(parent) + else: + self.notify.warning('loadPlaceGeom: unclassified zone %s' % zoneId) + CogHQLoader.CogHQLoader.loadPlaceGeom(self, zoneId) + + def unload(self): + CogHQLoader.CogHQLoader.unload(self) + Toon.unloadSellbotHQAnims() + + def enterFactoryExterior(self, requestStatus): + self.placeClass = FactoryExterior.FactoryExterior + self.enterPlace(requestStatus) + self.hood.spawnTitleText(requestStatus['zoneId']) + + def exitFactoryExterior(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + self.exitPlace() + self.placeClass = None + return + + def enterFactoryInterior(self, requestStatus): + self.placeClass = FactoryInterior.FactoryInterior + self.enterPlace(requestStatus) + + def exitFactoryInterior(self): + self.exitPlace() + self.placeClass = None + return + + def getExteriorPlaceClass(self): + return SellbotHQExterior.SellbotHQExterior + + def getBossPlaceClass(self): + return SellbotHQBossBattle.SellbotHQBossBattle diff --git a/toontown/coghq/SellbotHQBossBattle.py b/toontown/coghq/SellbotHQBossBattle.py new file mode 100644 index 0000000..5cda14e --- /dev/null +++ b/toontown/coghq/SellbotHQBossBattle.py @@ -0,0 +1,38 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.suit import DistributedSellbotBoss +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQBossBattle + +class SellbotHQBossBattle(CogHQBossBattle.CogHQBossBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('SellbotHQBossBattle') + + def __init__(self, loader, parentFSM, doneEvent): + CogHQBossBattle.CogHQBossBattle.__init__(self, loader, parentFSM, doneEvent) + self.teleportInPosHpr = (0, 95, 18, 180, 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, DistributedSellbotBoss.OneBossCog) + self.__setupHighSky() + + def exit(self): + CogHQBossBattle.CogHQBossBattle.exit(self) + self.__cleanupHighSky() + + def __setupHighSky(self): + self.loader.hood.startSky() + sky = self.loader.hood.sky + sky.setH(150) + sky.setZ(-100) + + def __cleanupHighSky(self): + self.loader.hood.stopSky() + sky = self.loader.hood.sky + sky.setH(0) + sky.setZ(0) diff --git a/toontown/coghq/SellbotHQExterior.py b/toontown/coghq/SellbotHQExterior.py new file mode 100644 index 0000000..776303d --- /dev/null +++ b/toontown/coghq/SellbotHQExterior.py @@ -0,0 +1,13 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import CogHQExterior + +class SellbotHQExterior(CogHQExterior.CogHQExterior): + notify = DirectNotifyGlobal.directNotify.newCategory('SellbotHQExterior') + + def enter(self, requestStatus): + CogHQExterior.CogHQExterior.enter(self, requestStatus) + self.loader.hood.startSky() + + def exit(self): + self.loader.hood.stopSky() + CogHQExterior.CogHQExterior.exit(self) diff --git a/toontown/coghq/SellbotLegFactoryCogs.py b/toontown/coghq/SellbotLegFactoryCogs.py new file mode 100644 index 0000000..4089e00 --- /dev/null +++ b/toontown/coghq/SellbotLegFactoryCogs.py @@ -0,0 +1,601 @@ +from SpecImports import * +LobbyParent = 10014 +BoilerParent = 10030 +PipeLeftParent = 10023 +PipeRightParent = 10032 +OilParent = 10034 +ControlParent = 10037 +DuctParent = 10036 +CenterSiloBattleCellParent = 10064 +CenterSiloParent = 20095 +SigRoomParent = 20058 +WestSiloParent = 20094 +WestSiloBattleCellParent = 10047 +EastSiloParent = 20096 +EastSiloBattleCellParent = 10068 +LobbyCell = 0 +BoilerCell = 1 +PipeLeftCell = 2 +PipeRightCell = 3 +OilCell = 4 +ControlCell = 5 +DuctCell = 6 +CenterSiloCell = 7 +SigRoomCell = 8 +WestSiloCell = 9 +EastSiloCell = 10 +BattleCells = {LobbyCell: {'parentEntId': LobbyParent, + 'pos': Point3(0, 0, 0)}, + BoilerCell: {'parentEntId': BoilerParent, + 'pos': Point3(0, 0, 0)}, + OilCell: {'parentEntId': OilParent, + 'pos': Point3(0, 0, 0)}, + ControlCell: {'parentEntId': ControlParent, + 'pos': Point3(0, 0, 0)}, + CenterSiloCell: {'parentEntId': CenterSiloBattleCellParent, + 'pos': Point3(0, 0, 0)}, + PipeLeftCell: {'parentEntId': PipeLeftParent, + 'pos': Point3(0, 0, 0)}, + PipeRightCell: {'parentEntId': PipeRightParent, + 'pos': Point3(0, 0, 0)}, + DuctCell: {'parentEntId': DuctParent, + 'pos': Point3(0, 0, 0)}, + SigRoomCell: {'parentEntId': SigRoomParent, + 'pos': Point3(0, 0, 0)}, + WestSiloCell: {'parentEntId': WestSiloBattleCellParent, + 'pos': Point3(0, 0, 0)}, + EastSiloCell: {'parentEntId': EastSiloBattleCellParent, + 'pos': Point3(-20, -10, 0)}} +CogData = [{'type': 'cc', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 3, + 'battleCell': LobbyCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20078, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 5, + 'battleCell': LobbyCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20009, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 3, + 'battleCell': LobbyCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20079, + 'skeleton': 0}, + {'type': 'cc', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 3, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20076, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 5, + 'battleCell': BoilerCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20077, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 6, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 4, + 'battleCell': OilCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60133, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 5, + 'battleCell': OilCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60134, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 5, + 'battleCell': OilCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60135, + 'skeleton': 0}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 5, + 'battleCell': ControlCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20039, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 6, + 'battleCell': ControlCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20049, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 5, + 'battleCell': ControlCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20075, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 6, + 'battleCell': CenterSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20103, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': CenterSiloParent, + 'boss': 1, + 'level': 9, + 'battleCell': CenterSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 180, + 'behavior': 'stand', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 6, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20104, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 6, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20105, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 6, + 'battleCell': WestSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20097, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 6, + 'battleCell': WestSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20098, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': WestSiloParent, + 'boss': 0, + 'level': 7, + 'battleCell': WestSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20099, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 6, + 'battleCell': EastSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20100, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 6, + 'battleCell': EastSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20101, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': EastSiloParent, + 'boss': 0, + 'level': 7, + 'battleCell': EastSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20102, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 5, + 'battleCell': PipeLeftCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20109, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeLeftCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20110, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeLeftCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20111, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeRightCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20106, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 5, + 'battleCell': PipeRightCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20107, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeRightCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20108, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 3, + 'battleCell': DuctCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20038, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 5, + 'battleCell': DuctCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20067, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 4, + 'battleCell': DuctCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20068, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 4, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 5, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 4, + 'battleCell': SigRoomCell, + 'pos': Point3(5, 3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 6, + 'battleCell': SigRoomCell, + 'pos': Point3(5, 10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] +ReserveCogData = [{'type': 'cc', + 'parentEntId': LobbyParent, + 'boss': 0, + 'level': 2, + 'battleCell': LobbyCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20009, + 'skeleton': 0, + 'joinParent': 20018}, + {'type': 'cc', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 2, + 'battleCell': BoilerCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20076, + 'skeleton': 0, + 'joinParent': 20019}, + {'type': 'tm', + 'parentEntId': BoilerParent, + 'boss': 0, + 'level': 2, + 'battleCell': BoilerCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20077, + 'skeleton': 0, + 'joinParent': 20019}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 3, + 'battleCell': OilCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60133, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': OilParent, + 'boss': 0, + 'level': 4, + 'battleCell': OilCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 60135, + 'skeleton': 0}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 2, + 'battleCell': ControlCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20039, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 2, + 'battleCell': ControlCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20049, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': ControlParent, + 'boss': 0, + 'level': 2, + 'battleCell': ControlCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'walk', + 'path': 20075, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 3, + 'battleCell': CenterSiloCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20103, + 'skeleton': 0}, + {'type': 'gh', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 5, + 'battleCell': CenterSiloCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 3, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20104, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': CenterSiloParent, + 'boss': 0, + 'level': 3, + 'battleCell': CenterSiloCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20105, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 3, + 'battleCell': PipeLeftCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeLeftParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeLeftCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 3, + 'battleCell': PipeRightCell, + 'pos': Point3(-10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'tm', + 'parentEntId': PipeRightParent, + 'boss': 0, + 'level': 4, + 'battleCell': PipeRightCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': None, + 'skeleton': 1}, + {'type': 'cc', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 2, + 'battleCell': DuctCell, + 'pos': Point3(0, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20038, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': DuctParent, + 'boss': 0, + 'level': 2, + 'battleCell': DuctCell, + 'pos': Point3(10, 0, 0), + 'h': 0, + 'behavior': 'chase', + 'path': 20067, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 3, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -10.75, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}, + {'type': 'tm', + 'parentEntId': SigRoomParent, + 'boss': 0, + 'level': 4, + 'battleCell': SigRoomCell, + 'pos': Point3(5, -3.25, 0), + 'h': -90, + 'behavior': 'stand', + 'path': None, + 'skeleton': 0}] diff --git a/toontown/coghq/SellbotLegFactorySpec.py b/toontown/coghq/SellbotLegFactorySpec.py new file mode 100644 index 0000000..d80eb1f --- /dev/null +++ b/toontown/coghq/SellbotLegFactorySpec.py @@ -0,0 +1,4772 @@ +from toontown.toonbase import TTLocalizer +from toontown.coghq.SpecImports import * +GlobalEntities = {1000: {'type': 'levelMgr', + 'name': 'LevelMgr', + 'comment': '', + 'parentEntId': 0, + 'cogLevel': 0, + 'farPlaneDistance': 1500.0, + 'modelFilename': 'phase_9/models/cogHQ/SelbotLegFactory', + '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': []}, + 3: {'type': 'zone', + 'name': 'Main Entrance', + 'comment': '', + 'parentEntId': 0, + 'scale': Vec3(1, 1, 1), + 'description': TTLocalizer.SellbotLegFactorySpecMainEntrance, + 'visibility': [114]}, + 4: {'type': 'zone', + 'name': 'Lobby', + 'comment': '', + 'parentEntId': 0, + 'scale': Vec3(1, 1, 1), + 'description': TTLocalizer.SellbotLegFactorySpecLobby, + 'visibility': [113, 114]}, + 5: {'type': 'zone', + 'name': 'hallwayFromLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [113, 116]}, + 6: {'type': 'zone', + 'name': 'hallwayToBoiler/Control/Lookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLobbyHallway, + 'visibility': [109, + 116, + 117, + 118]}, + 7: {'type': 'zone', + 'name': 'GearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecGearRoom, + 'visibility': [109, 110]}, + 8: {'type': 'zone', + 'name': 'BoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecBoilerRoom, + 'visibility': [108, 117]}, + 9: {'type': 'zone', + 'name': 'EastCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastCatwalk, + 'visibility': [23, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 110, + 112, + 115, + 124, + 200, + 222]}, + 10: {'type': 'zone', + 'name': 'PaintMixer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPaintMixer, + 'visibility': [11, 111, 112]}, + 11: {'type': 'zone', + 'name': 'PaintMixerRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPaintMixerStorageRoom, + 'visibility': [10, 111, 112]}, + 12: {'type': 'zone', + 'name': 'WestSiloCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSiloCatwalk, + 'visibility': [21, + 26, + 33, + 34, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 119, + 120, + 125, + 127, + 128, + 129, + 130, + 200]}, + 13: {'type': 'zone', + 'name': 'PipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecPipeRoom, + 'visibility': [119, 121]}, + 14: {'type': 'zone', + 'name': 'StairsToPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [17, + 18, + 121, + 126, + 131]}, + 15: {'type': 'zone', + 'name': 'DuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecDuctRoom, + 'visibility': [106, 126]}, + 16: {'type': 'zone', + 'name': 'Side Entrance', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecSideEntrance, + 'visibility': [106]}, + 17: {'type': 'zone', + 'name': 'StomperAlley', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecStomperAlley, + 'visibility': [14, + 121, + 126, + 131]}, + 18: {'type': 'zone', + 'name': 'LavaRoomFoyer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaRoomFoyer, + 'visibility': [19, + 20, + 102, + 103, + 105, + 131]}, + 19: {'type': 'zone', + 'name': 'LavaRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaRoom, + 'visibility': [17, + 18, + 20, + 105, + 131]}, + 20: {'type': 'zone', + 'name': 'LavaRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLavaStorageRoom, + 'visibility': [18, 19, 105]}, + 21: {'type': 'zone', + 'name': 'WestCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestCatwalk, + 'visibility': [12, + 23, + 26, + 33, + 34, + 35, + 40, + 41, + 53, + 60, + 108, + 119, + 120, + 125, + 127, + 200]}, + 22: {'type': 'zone', + 'name': 'OilRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecOilRoom, + 'visibility': [107]}, + 23: {'type': 'zone', + 'name': 'Lookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecLookout, + 'visibility': [24, + 39, + 115, + 118, + 120, + 123, + 124, + 125]}, + 24: {'type': 'zone', + 'name': 'Warehouse', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWarehouse, + 'visibility': [23, + 39, + 115, + 120, + 123, + 124, + 125]}, + 25: {'type': 'zone', + 'name': 'PaintMixerExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 26: {'type': 'zone', + 'name': 'WarehouseExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 27: {'type': 'zone', + 'name': 'OilRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecOilRoomHallway, + 'visibility': [105, 107, 127]}, + 30: {'type': 'zone', + 'name': 'EastSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSiloControlRoom, + 'visibility': [130]}, + 31: {'type': 'zone', + 'name': 'WestSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSiloControlRoom, + 'visibility': [128]}, + 32: {'type': 'zone', + 'name': 'CenterSiloControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecCenterSiloControlRoom, + 'visibility': [129]}, + 33: {'type': 'zone', + 'name': 'EastSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSilo, + 'visibility': [9, + 12, + 21, + 25, + 26, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 124, + 128, + 129, + 130, + 200, + 222]}, + 34: {'type': 'zone', + 'name': 'WestSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestSilo, + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 120, + 125, + 127, + 128, + 129, + 130, + 200]}, + 35: {'type': 'zone', + 'name': 'CenterSilo', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecCenterSilo, + 'visibility': [9, + 21, + 25, + 26, + 33, + 34, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 128, + 129, + 130, + 200]}, + 36: {'type': 'zone', + 'name': 'WestSiloBridge', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 127, + 128, + 129, + 130, + 200]}, + 37: {'type': 'zone', + 'name': 'EastSiloBridge', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': [9, + 12, + 21, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 38, + 40, + 41, + 53, + 60, + 61, + 108, + 110, + 112, + 119, + 128, + 129, + 130, + 200, + 222]}, + 38: {'type': 'zone', + 'name': 'EastSiloCatwalk', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastSiloCatwalk, + 'visibility': [9, + 25, + 26, + 33, + 34, + 35, + 36, + 37, + 41, + 53, + 60, + 110, + 112, + 115, + 124, + 200, + 222]}, + 39: {'type': 'zone', + 'name': 'WarehouseCeiling', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 40: {'type': 'zone', + 'name': 'WestExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 41: {'type': 'zone', + 'name': 'EastExterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 53: {'type': 'zone', + 'name': 'ExteriorFloor', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 60: {'type': 'zone', + 'name': 'WestElevatorShaft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecWestElevatorShaft, + 'visibility': [12, 34]}, + 61: {'type': 'zone', + 'name': 'EastElevatorShaft', + 'comment': 'no geom or DCS', + 'parentEntId': 0, + 'scale': 1, + 'description': TTLocalizer.SellbotLegFactorySpecEastElevatorShaft, + 'visibility': [33, 38]}, + 101: {'type': 'zone', + 'name': 'dwToLavaRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 102: {'type': 'zone', + 'name': 'dwToLavaRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 103: {'type': 'zone', + 'name': 'dwToLavaRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 105: {'type': 'zone', + 'name': 'dwToOilRoomCatwalks', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 106: {'type': 'zone', + 'name': 'dwToDuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 107: {'type': 'zone', + 'name': 'dwToOilRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 108: {'type': 'zone', + 'name': 'dwFromBoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 109: {'type': 'zone', + 'name': 'dwToGearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 110: {'type': 'zone', + 'name': 'dwFromGearRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 111: {'type': 'zone', + 'name': 'dwToPaintMixerRewardRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 112: {'type': 'zone', + 'name': 'dwToPaintMixer', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 113: {'type': 'zone', + 'name': 'dwFromLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 114: {'type': 'zone', + 'name': 'dwToLobby', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 115: {'type': 'zone', + 'name': 'dwToWarehouseFromRight', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 116: {'type': 'zone', + 'name': 'dwFromLobbyFar', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 117: {'type': 'zone', + 'name': 'dwToBoilerRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 118: {'type': 'zone', + 'name': 'dwToLookout', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 119: {'type': 'zone', + 'name': 'dwFromPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 120: {'type': 'zone', + 'name': 'dwToWarehouseFromLeft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 121: {'type': 'zone', + 'name': 'dwToPipeRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 122: {'type': 'zone', + 'name': 'dwToWarehouseControlRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 123: {'type': 'zone', + 'name': 'dwFromWarehouseFloor', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 124: {'type': 'zone', + 'name': 'dwFromWarehouseRight', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 125: {'type': 'zone', + 'name': 'dwFromWarehouseLeft', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 126: {'type': 'zone', + 'name': 'dwFromDuctRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 127: {'type': 'zone', + 'name': 'dwFromOilRoomHallway', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 128: {'type': 'zone', + 'name': 'dwToWestSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 129: {'type': 'zone', + 'name': 'dwToCenterSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 130: {'type': 'zone', + 'name': 'dwToEastSiloRoom', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 131: {'type': 'zone', + 'name': 'dwFromStomperAlley', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 200: {'type': 'zone', + 'name': 'sky', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 201: {'type': 'zone', + 'name': 'extraZone201', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 202: {'type': 'zone', + 'name': 'extraZone202', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 203: {'type': 'zone', + 'name': 'extraZone203', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 204: {'type': 'zone', + 'name': 'extraZone204', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 205: {'type': 'zone', + 'name': 'extraZone205', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 206: {'type': 'zone', + 'name': 'extraZone206', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 207: {'type': 'zone', + 'name': 'extraZone207', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 208: {'type': 'zone', + 'name': 'extraZone208', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 209: {'type': 'zone', + 'name': 'extraZone209', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 210: {'type': 'zone', + 'name': 'extraZone210', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 211: {'type': 'zone', + 'name': 'extraZone211', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 212: {'type': 'zone', + 'name': 'extraZone212', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 213: {'type': 'zone', + 'name': 'extraZone213', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 214: {'type': 'zone', + 'name': 'extraZone214', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 215: {'type': 'zone', + 'name': 'extraZone215', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 216: {'type': 'zone', + 'name': 'extraZone216', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 217: {'type': 'zone', + 'name': 'extraZone217', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 218: {'type': 'zone', + 'name': 'extraZone218', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 219: {'type': 'zone', + 'name': 'extraZone219', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 220: {'type': 'zone', + 'name': 'extraZone220', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 221: {'type': 'zone', + 'name': 'extraZone221', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 222: {'type': 'zone', + 'name': 'dwToEastSiloInterior', + 'comment': '', + 'parentEntId': 0, + 'scale': 1, + 'description': '', + 'visibility': []}, + 10010: {'type': 'ambientSound', + 'name': 'westWind', + 'comment': '', + 'parentEntId': 35, + 'pos': Point3(-52.7549, -38.8374, 53.3758), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_whistling_wind.mp3', + 'volume': 1}, + 10016: {'type': 'ambientSound', + 'name': 'sndConveyorBelt', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_conveyor_belt.mp3', + 'volume': 0.5}, + 10053: {'type': 'ambientSound', + 'name': 'eastWind', + 'comment': '', + 'parentEntId': 35, + 'pos': Point3(52.75, -38.84, 53.38), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_whistling_wind.mp3', + 'volume': 1}, + 10055: {'type': 'ambientSound', + 'name': 'sndGears', + 'comment': '', + 'parentEntId': 10056, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'enabled': 1, + 'soundPath': 'phase_9/audio/sfx/CHQ_FACT_gears_turning.mp3', + 'volume': 1}, + 10031: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(-1, 79, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.75, 1, 1), + 'cellId': 1, + 'radius': 10.0}, + 10035: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 10039, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 4, + 'radius': 10.0}, + 10038: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, -28.04, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 5, + 'radius': 10.0}, + 20048: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0.973602, 71.7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 0.2, 1), + 'cellId': 0, + 'radius': 15.0}, + 20063: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20033, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20064: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20034, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20065: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20035, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20066: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 20036, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'cellId': 8, + 'radius': 1}, + 20086: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 33, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1, 1), + 'cellId': 6, + 'radius': 12.0}, + 20112: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(-10.0936, -9.55975, 4), + 'hpr': Point3(45, 0, 0), + 'scale': Point3(10, 1, 5), + 'cellId': 10, + 'radius': 5.0}, + 20113: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(9.08399, 4.42157, 0), + 'hpr': Point3(-50, 0, 0), + 'scale': Point3(10, 2, 6), + 'cellId': 9, + 'radius': 5.0}, + 20114: {'type': 'battleBlocker', + 'name': '', + 'comment': '', + 'parentEntId': 60103, + 'pos': Point3(0, 0, 1), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 0.5), + 'cellId': 8, + 'radius': 3.0}, + 10003: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(1.25458, 19.2471, 0.0249529), + 'hpr': Vec3(-8.28434, 0, 0), + 'scale': 1, + 'rewardPerGrab': 25, + 'rewardPerGrabMax': 0}, + 10011: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(16.344, -9.73, 0.025), + 'hpr': Vec3(-79.8888, 0, 0), + 'scale': 1, + 'rewardPerGrab': 25, + 'rewardPerGrabMax': 0}, + 20017: {'type': 'beanBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(20.0035, 2.94232, 0), + 'hpr': Vec3(-31.6033, 0, 0), + 'scale': 1, + 'rewardPerGrab': 35, + 'rewardPerGrabMax': 0}, + 10039: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(-7, 29, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(4, 4, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 20033: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20034: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(7.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20035: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(15, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 20036: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(22.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(0.862745, 0.517647, 0.0941177, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1}, + 30040: {'type': 'button', + 'name': 'door button', + 'comment': 'Entrance door unlock', + 'parentEntId': 3, + 'pos': Point3(0, 6.75, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 30076: {'type': 'button', + 'name': 'open door 113', + 'comment': 'Lobby door unlock', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1}, + 60102: {'type': 'button', + 'name': 'door button', + 'comment': 'Entrance Door Unlock', + 'parentEntId': 16, + 'pos': Point3(4, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60103: {'type': 'button', + 'name': 'door button', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(25, -7, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(4, 4, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60104: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 31, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(5, 5, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60105: {'type': 'button', + 'name': 'door button', + 'comment': '', + 'parentEntId': 30, + 'pos': Point3(-4, 7, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(5, 5, 4), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 60118: {'type': 'button', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 20, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(3, 3, 3), + 'color': Vec4(1, 0, 0, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': -1.0}, + 10005: {'type': 'conveyorBelt', + 'name': 'belt', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 45.2024, 7.24937), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'floorName': 'platformcollision', + 'length': 78.81881352704218, + 'speed': 2.0, + 'treadLength': 10.0, + 'treadModelPath': 'phase_9/models/cogHQ/platform1', + 'widthScale': 0.85}, + 20081: {'type': 'crate', + 'name': '', + 'comment': '', + 'parentEntId': 20080, + 'pos': Point3(0, 0, 0), + 'scale': 0.920000016689, + 'crushCellId': None, + 'gridId': 20080, + 'modelType': 0, + 'pushable': 1}, + 20091: {'type': 'crate', + 'name': '', + 'comment': '', + 'parentEntId': 20090, + 'pos': Point3(0, 23, 0), + 'scale': 0.920000016689, + 'crushCellId': None, + 'gridId': 20090, + 'modelType': 0, + 'pushable': 1}, + 20024: {'type': 'crusherCell', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 1, + 'gridId': 20025, + 'row': 14}, + 20026: {'type': 'crusherCell', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 10, + 'gridId': 20025, + 'row': 14}, + 20027: {'type': 'crusherCell', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 21, + 'gridId': 20025, + 'row': 14}, + 20028: {'type': 'crusherCell', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(2, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'col': 28, + 'gridId': 20025, + 'row': 14}, + 30078: {'type': 'cutScene', + 'name': 'button door', + 'comment': '', + 'parentEntId': 114, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'duration': 4.0, + 'effect': 'irisInOut', + 'motion': 'doorUnlock', + 'startStopEvent': 30077}, + 10002: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 128, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 10052: {'type': 'door', + 'name': 'door 127', + 'comment': '', + 'parentEntId': 127, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(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': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 10039, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30000: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 114, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(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': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60132, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30001: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 105, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30002: {'type': 'door', + 'name': 'door 106', + 'comment': '', + 'parentEntId': 106, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(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': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60132, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30003: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 107, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30004: {'type': 'door', + 'name': 'doorFromBoilerRoom', + 'comment': '', + 'parentEntId': 108, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30005: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 109, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30006: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 110, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30008: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 112, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30009: {'type': 'door', + 'name': 'door 113', + 'comment': '', + 'parentEntId': 113, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(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': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60119, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30010: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 115, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30011: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 116, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30012: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 117, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30013: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 118, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30014: {'type': 'door', + 'name': 'doorFromPipeRoom 119', + 'comment': '', + 'parentEntId': 119, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30015: {'type': 'door', + 'name': 'door 120', + 'comment': '', + 'parentEntId': 120, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30016: {'type': 'door', + 'name': 'door 121', + 'comment': '', + 'parentEntId': 121, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30017: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 122, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30018: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 123, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(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': 60103, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30019: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 124, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30020: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 125, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 30021: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 126, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(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': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 60119, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60088: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 131, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60094: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 129, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 0, + 'isLock2Unlocked': 0, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1.0, + 'unlock0Event': 0, + 'unlock1Event': 60104, + 'unlock2Event': 60105, + 'unlock3Event': 0}, + 60095: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 130, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 60101: {'type': 'door', + 'name': '', + 'comment': '', + 'parentEntId': 222, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'color': Vec4(1, 1, 1, 1), + 'isLock0Unlocked': 1, + 'isLock1Unlocked': 1, + 'isLock2Unlocked': 1, + 'isLock3Unlocked': 1, + 'isOpen': 0, + 'isOpenEvent': 0, + 'isVisBlocker': 1, + 'secondsOpen': 1, + 'unlock0Event': 0, + 'unlock1Event': 0, + 'unlock2Event': 0, + 'unlock3Event': 0}, + 10049: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 3}, + 10051: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 4}, + 60000: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 5}, + 60001: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 6}, + 60002: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 7}, + 60003: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 9}, + 60004: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 10}, + 60005: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 8}, + 60006: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 21}, + 60007: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 24}, + 60009: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 38}, + 60011: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 12}, + 60013: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 13}, + 60014: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 14}, + 60015: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 17}, + 60016: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 15}, + 60017: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 16}, + 60018: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 19}, + 60019: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 18}, + 60024: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 22}, + 60031: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 23}, + 60044: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 33}, + 60066: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 11}, + 60067: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 27}, + 60096: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 30}, + 60108: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 34}, + 60111: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 36}, + 60114: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 37}, + 60121: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 31}, + 60126: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 35}, + 60130: {'type': 'entityGroup', + 'name': 'viz', + 'comment': '', + 'parentEntId': 32}, + 10028: {'type': 'entrancePoint', + 'name': 'entrance1', + 'comment': '', + 'parentEntId': 3, + 'pos': Point3(0, 10, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'entranceId': 0, + 'radius': 15, + 'theta': 20}, + 10029: {'type': 'entrancePoint', + 'name': 'entrance2', + 'comment': '', + 'parentEntId': 16, + 'pos': Point3(0, 10, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'entranceId': 1, + 'radius': 15, + 'theta': 20}, + 10021: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-2.02081, 0, 0), + 'hpr': Vec3(337.477, 0, 0), + 'scale': 1, + 'gagLevel': 2, + 'gagLevelMax': 0, + 'gagTrack': 0, + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 10024: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(20.3012, -26.3219, 0), + 'hpr': Vec3(233.187, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 0}, + 10025: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-47.312, 7.22571, 0), + 'hpr': Vec3(19.1524, 0, 0), + 'scale': 1, + 'gagLevel': 0, + 'gagLevelMax': 0, + 'gagTrack': 0, + 'rewardPerGrab': 3, + 'rewardPerGrabMax': 0}, + 10026: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 10022, + 'pos': Point3(-11.2037, 5.43514, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 5, + 'rewardPerGrab': 4, + 'rewardPerGrabMax': 0}, + 20020: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-23.0209, 0, 0), + 'hpr': Vec3(126.676, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 3, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20021: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-31.3225, 14.1021, 0), + 'hpr': Vec3(-136.57, 0, 0), + 'scale': 1, + 'gagLevel': 4, + 'gagLevelMax': 0, + 'gagTrack': 5, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20085: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(3.14, 12.6703, 10.12), + 'hpr': Vec3(-24.8105, 0, 0), + 'scale': 1, + 'gagLevel': 5, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 20093: {'type': 'gagBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(2.4, -1, 7), + 'hpr': Vec3(-151.532, 0, 0), + 'scale': 1, + 'gagLevel': 0, + 'gagLevelMax': 0, + 'gagTrack': 0, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10006: {'type': 'gear', + 'name': 'first', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 0, 26.0634), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'degreesPerSec': 20.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10007: {'type': 'gear', + 'name': 'second', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 15, 26.06), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'degreesPerSec': 30.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10008: {'type': 'gear', + 'name': 'third', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 30, 26.06), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'degreesPerSec': 40.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 10009: {'type': 'gear', + 'name': 'fourth', + 'comment': '', + 'parentEntId': 10004, + 'pos': Point3(0, 45, 26.06), + 'hpr': Point3(180, 0, 0), + 'scale': 1, + 'degreesPerSec': 47.0, + 'gearScale': 25.0, + 'modelType': 'factory', + 'orientation': 'vertical', + 'phaseShift': 0}, + 20013: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20012, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20014: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20010, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20016: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20015, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20041: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20040, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20026, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 6.0}, + 20043: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20042, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20024, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 5.0}, + 20046: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20044, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20024, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 10, + 'velocity': 6.0}, + 20047: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20045, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20026, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 6.0}, + 20052: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20051, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 12.0, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 3, + 'velocity': 4.0}, + 20054: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20053, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20027, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 80.0, + 'strength': 10, + 'velocity': 5.5}, + 20056: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20055, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20028, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 10, + 'velocity': 6.0}, + 20060: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20059, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20028, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 90.0, + 'strength': 10, + 'velocity': 6.5}, + 20062: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20061, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': 20027, + 'goonType': 'pg', + 'gridId': 20025, + 'hFov': 70, + 'strength': 10, + 'velocity': 7.5}, + 20071: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20070, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 6.0}, + 20072: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20069, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 80.0, + 'strength': 7, + 'velocity': 6.0}, + 20074: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20073, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.25, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20089: {'type': 'goon', + 'name': '', + 'comment': '', + 'parentEntId': 20084, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1.5, + 'attackRadius': 15, + 'crushCellId': None, + 'goonType': 'pg', + 'gridId': None, + 'hFov': 70, + 'strength': 7, + 'velocity': 4}, + 20115: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, -7.4, 0), + 'hpr': Point3(-90, 0, 0), + 'scale': Point3(5, 5, 5), + 'goonId': 20052}, + 20116: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, -58, 0), + 'hpr': Point3(90, 0, 0), + 'scale': 1, + 'goonId': None}, + 20117: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, -29, 0), + 'hpr': Point3(90, 0, 0), + 'scale': 1, + 'goonId': None}, + 20118: {'type': 'goonClipPlane', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-52, 0, 5), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'goonId': None}, + 20025: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 20023, + 'pos': Point3(-48.4442, -24.9385, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 30, + 'numRow': 16}, + 20080: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(1.5, -10.7, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 2, + 'numRow': 5}, + 20090: {'type': 'grid', + 'name': '', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(-6.5, -111, 0), + 'scale': 1, + 'cellSize': 3, + 'numCol': 2, + 'numRow': 9}, + 20011: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20, + 'pos': Point3(-2.06235, 20.2198, 0.025), + 'hpr': Vec3(-19.2153, 0, 0), + 'scale': 1, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 20092: {'type': 'healBarrel', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(-1, -1.5, 7), + 'hpr': Vec3(-191.79, 0, 0), + 'scale': 1, + 'rewardPerGrab': 5, + 'rewardPerGrabMax': 0}, + 10041: {'type': 'lift', + 'name': 'westLift', + 'comment': '', + 'parentEntId': 60, + 'pos': Point3(0, 0, 0.0641994), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'autoMoveDelay': 5, + 'duration': 7.0, + 'endBoardSides': ['back'], + 'endGuardName': 'topGuard', + 'endPos': Point3(0, 0, 165), + 'floorName': 'elevator_floor', + 'modelPath': 'phase_9/models/cogHQ/Elevator.bam', + 'modelScale': Vec3(1, 1, 1), + 'moveDelay': 1, + 'startBoardSides': ['front'], + 'startGuardName': 'bottomGuard', + 'startPos': Point3(0, 0, 0)}, + 10048: {'type': 'lift', + 'name': 'eastLift', + 'comment': '', + 'parentEntId': 61, + 'pos': Point3(0, -0.684064, 0.589322), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'autoMoveDelay': 5.0, + 'duration': 7.0, + 'endBoardSides': ['front', + 'back', + 'left', + 'right'], + 'endGuardName': 'topGuard', + 'endPos': Point3(0, 0, 165), + 'floorName': 'elevator_floor', + 'modelPath': 'phase_9/models/cogHQ/Elevator.bam', + 'modelScale': Vec3(1, 1, 1), + 'moveDelay': 1, + 'startBoardSides': ['front'], + 'startGuardName': 'bottomGuard', + 'startPos': Point3(0, 0, 0)}, + 10057: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10043, + 'input1Event': 30009, + 'input2Event': 30000, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10059: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10058, + 'input1Event': 10057, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10061: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10060, + 'input1Event': 10059, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10063: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 10062, + 'input1Event': 60033, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 30068: {'type': 'logicGate', + 'name': 'door 116 and door 118', + 'comment': '', + 'parentEntId': 30069, + 'input1Event': 30013, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60023: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60021, + 'input1Event': 30011, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60025: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60022, + 'input1Event': 60023, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60028: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60026, + 'input1Event': 30011, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60029: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60027, + 'input1Event': 30011, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60030: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30071, + 'input1Event': 30011, + 'input2Event': 30009, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60033: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30073, + 'input1Event': 30013, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60034: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 30075, + 'input1Event': 30013, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60035: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60032, + 'input1Event': 30013, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60037: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60036, + 'input1Event': 30005, + 'input2Event': 30012, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60039: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60038, + 'input1Event': 30012, + 'input2Event': 30005, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60041: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60040, + 'input1Event': 30020, + 'input2Event': 30019, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60043: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60042, + 'input1Event': 30019, + 'input2Event': 30020, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60047: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60045, + 'input1Event': 10002, + 'input2Event': 30019, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60049: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60048, + 'input1Event': 30003, + 'input2Event': 10052, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60051: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60050, + 'input1Event': 30001, + 'input2Event': 10052, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60053: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60052, + 'input1Event': 30021, + 'input2Event': 30016, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60055: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60054, + 'input1Event': 30002, + 'input2Event': 30021, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60057: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60056, + 'input1Event': 30016, + 'input2Event': 30021, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60059: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60058, + 'input1Event': 30012, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60061: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60060, + 'input1Event': 30012, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60064: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60062, + 'input1Event': 30005, + 'input2Event': 30011, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60065: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60063, + 'input1Event': 30005, + 'input2Event': 30013, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60074: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60072, + 'input1Event': 10052, + 'input2Event': 30003, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60075: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60073, + 'input1Event': 10052, + 'input2Event': 30001, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60076: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60020, + 'input1Event': 30021, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60078: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60077, + 'input1Event': 30021, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60080: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60079, + 'input1Event': 60057, + 'input2Event': 30002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60082: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60081, + 'input1Event': 60055, + 'input2Event': 30016, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60084: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60083, + 'input1Event': 30004, + 'input2Event': 30014, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60086: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60085, + 'input1Event': 30006, + 'input2Event': 30008, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60091: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60087, + 'input1Event': 60088, + 'input2Event': 30001, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60093: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60092, + 'input1Event': 30001, + 'input2Event': 60088, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60100: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60099, + 'input1Event': 60095, + 'input2Event': 10002, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60119: {'type': 'logicGate', + 'name': 'open sesame Duct & Lobby', + 'comment': 'links together the Duct Room and Lobby buttons', + 'parentEntId': 0, + 'input1Event': 30076, + 'input2Event': 60118, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'or'}, + 60132: {'type': 'logicGate', + 'name': 'open sesame Entrances', + 'comment': 'links together the buttons in the two entrances', + 'parentEntId': 0, + 'input1Event': 30040, + 'input2Event': 60102, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'or'}, + 60138: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60137, + 'input1Event': 60095, + 'input2Event': 60094, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60141: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60139, + 'input1Event': 10002, + 'input2Event': 60094, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 60142: {'type': 'logicGate', + 'name': '', + 'comment': '', + 'parentEntId': 60140, + 'input1Event': 10002, + 'input2Event': 60095, + 'isInput1': 0, + 'isInput2': 0, + 'logicType': 'and'}, + 10001: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10006, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10012: {'type': 'model', + 'name': 'backCrate', + 'comment': '', + 'parentEntId': 10067, + 'pos': Point3(0, -5.81496, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10033: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10007, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10045: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10008, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10046: {'type': 'model', + 'name': 'dropshadow', + 'comment': '', + 'parentEntId': 10009, + 'pos': Point3(0, 0, -25), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(2, 1.5, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_3/models/props/drop_shadow.bam'}, + 10050: {'type': 'model', + 'name': 'sky', + 'comment': '', + 'parentEntId': 200, + 'pos': Point3(-142.02, 437.227, 0.922491), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(2.5, 2.5, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/cog_sky.bam'}, + 10066: {'type': 'model', + 'name': 'frontCrate', + 'comment': '', + 'parentEntId': 10067, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10069: {'type': 'model', + 'name': 'backCrate', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(0, -5.81496, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 2), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10070: {'type': 'model', + 'name': 'frontCrate', + 'comment': '', + 'parentEntId': 10065, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20082: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 5, + 'pos': Point3(4.50815, 11.6508, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(0.92, 0.92, 0.92), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20083: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 20082, + 'pos': Point3(0, 0, 5.5), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 20088: {'type': 'model', + 'name': '', + 'comment': '', + 'parentEntId': 20087, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1.3, 1, 1.3), + 'collisionsOnly': 0, + 'flattenType': 'light', + 'loadType': 'loadModelCopy', + 'modelPath': 'phase_9/models/cogHQ/metal_crateB.bam'}, + 10000: {'type': 'nodepath', + 'name': 'gearGauntletObstacle', + 'comment': '', + 'parentEntId': 10027, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10004: {'type': 'nodepath', + 'name': 'gearGauntlet', + 'comment': 'gears are staggered 15 ft in Y', + 'parentEntId': 10000, + 'pos': Point3(0, -23.25, 6.85), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10014: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 34.07, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10015: {'type': 'nodepath', + 'name': 'paint mixer platforms', + 'comment': '', + 'parentEntId': 10, + 'pos': Point3(0, 5.15136, -2), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10022: {'type': 'nodepath', + 'name': 'gagBarrels', + 'comment': '', + 'parentEntId': 11, + 'pos': Point3(11.2328, 14.7959, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10023: {'type': 'nodepath', + 'name': 'leftCogs', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(-42.0363, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10027: {'type': 'nodepath', + 'name': 'zoneNodeCompensate', + 'comment': 'I think the ZoneNode was moved.', + 'parentEntId': 19, + 'pos': Point3(-0.426482, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10030: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(2.5, 62.5, 10), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10032: {'type': 'nodepath', + 'name': 'rightCogs', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(46.88, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10034: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(180, 0, 0), + 'scale': 1}, + 10036: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(5.5, 0, 0), + 'hpr': Point3(161, 0, 0), + 'scale': 1}, + 10037: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(3.1, -48.27, 0.05), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 10040: {'type': 'nodepath', + 'name': 'FactoryBoss', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, 68.4457, 9.5669), + 'hpr': Point3(180, 0, 0), + 'scale': 1}, + 10047: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10056: {'type': 'nodepath', + 'name': 'sounds', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 0, 15), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10064: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, -5.20447, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10065: {'type': 'nodepath', + 'name': 'backSteps', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, 56.2652, 0), + 'hpr': Point3(0, 0, 0), + 'scale': Point3(1.5, 1.3, 0.73)}, + 10067: {'type': 'nodepath', + 'name': 'frontSteps', + 'comment': '', + 'parentEntId': 10000, + 'pos': Point3(0, -44.7196, 0), + 'hpr': Point3(180, 0, 0), + 'scale': Point3(1.5, 1.3, 0.729057)}, + 10068: {'type': 'nodepath', + 'name': 'battleCell', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20000: {'type': 'nodepath', + 'name': 'stompers', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(0.75, 0, 0.5), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20018: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 10014, + 'pos': Point3(0, -24, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20019: {'type': 'nodepath', + 'name': 'cogsJoin', + 'comment': '', + 'parentEntId': 10030, + 'pos': Point3(16, 2, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20022: {'type': 'nodepath', + 'name': 'StomperButtonsNodepath', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-11.75, -35.8, 14.9), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20023: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20037: {'type': 'nodepath', + 'name': 'SignatureGoonNP', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-48.4442, -24.9385, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20058: {'type': 'nodepath', + 'name': 'SigRoomCogs', + 'comment': '', + 'parentEntId': 24, + 'pos': Point3(-1.0928, -45, 14.99), + 'hpr': Point3(90, 0, 0), + 'scale': 1}, + 20087: {'type': 'nodepath', + 'name': '', + 'comment': '', + 'parentEntId': 17, + 'pos': Point3(-4, -117, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20094: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(-0.720506, 27.5461, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 20095: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1}, + 20096: {'type': 'nodepath', + 'name': 'cogs', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(4.84921, 8.74482, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1}, + 10017: {'type': 'paintMixer', + 'name': 'fifth', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(5.24, 23.52, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(-12, -6, 0), + 'period': 8.0, + 'phaseShift': 0.5, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 10018: {'type': 'paintMixer', + 'name': 'fourth', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-12.1, 3, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(0, -6, 15), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10019: {'type': 'paintMixer', + 'name': 'third', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-3.85419, -7.75751, 22.5836), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(7, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10020: {'type': 'paintMixer', + 'name': 'second', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(16.01, -6.47, 23), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(-4, -8, -15), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 2.5, + 'waitPercent': 0.1}, + 10054: {'type': 'paintMixer', + 'name': 'first', + 'comment': '', + 'parentEntId': 10015, + 'pos': Point3(-10, -26.1, 8), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'floorName': 'PaintMixerFloorCollision', + 'modelPath': 'phase_9/models/cogHQ/PaintMixer', + 'modelScale': Point3(0.8, 0.8, 0.8), + 'motion': 'easeInOut', + 'offset': Point3(15, 0, 0), + 'period': 8.0, + 'phaseShift': 0.0, + 'shaftScale': 1, + 'waitPercent': 0.1}, + 20008: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20009: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 17, + 'pathScale': 1.0}, + 20010: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 36, + 'pathScale': 1.0}, + 20012: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 34, + 'pathScale': 1.0}, + 20015: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 37, + 'pathScale': 1.0}, + 20038: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 38, + 'pathScale': 1.0}, + 20039: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 12, + 'pathScale': 1.0}, + 20040: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(41.5, 33.5, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20042: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(15, 34, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20044: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(1.5, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Vec3(1, 1, 1), + 'pathIndex': 6, + 'pathScale': 1.0}, + 20045: {'type': 'path', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'pathIndex': 7, + 'pathScale': 1.0}, + 20049: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 13, + 'pathScale': 1.0}, + 20051: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(1, -24, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 42, + 'pathScale': 1.0}, + 20053: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 8, + 'pathScale': 1.0}, + 20055: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 9, + 'pathScale': 1.0}, + 20059: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 10, + 'pathScale': 1.0}, + 20061: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 20037, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 11, + 'pathScale': 1.0}, + 20067: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 39, + 'pathScale': 1.0}, + 20068: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 15, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 40, + 'pathScale': 1.0}, + 20069: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 5, + 'pathScale': 1.0}, + 20070: {'type': 'path', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(1, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 5, + 'pathScale': 1.0}, + 20073: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 21, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 35, + 'pathScale': 1.0}, + 20075: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 7, + 'pos': Point3(4, 4, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 14, + 'pathScale': 1.0}, + 20076: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 15, + 'pathScale': 1.0}, + 20077: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 8, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 16, + 'pathScale': 1.0}, + 20078: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 18, + 'pathScale': 1.0}, + 20079: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 4, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 20084: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 9, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 41, + 'pathScale': 1.0}, + 20097: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 19, + 'pathScale': 1.0}, + 20098: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 20, + 'pathScale': 1.0}, + 20099: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 34, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 21, + 'pathScale': 1.0}, + 20100: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 22, + 'pathScale': 1.0}, + 20101: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 23, + 'pathScale': 1.0}, + 20102: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 33, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 24, + 'pathScale': 1.0}, + 20103: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 25, + 'pathScale': 1.0}, + 20104: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 26, + 'pathScale': 1.0}, + 20105: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 32, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 27, + 'pathScale': 1.0}, + 20106: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 28, + 'pathScale': 1.0}, + 20107: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 29, + 'pathScale': 1.0}, + 20108: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 30, + 'pathScale': 1.0}, + 20109: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 31, + 'pathScale': 1.0}, + 20110: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 32, + 'pathScale': 1.0}, + 20111: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 13, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 33, + 'pathScale': 1.0}, + 60133: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(-10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 60134: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(0, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 60135: {'type': 'path', + 'name': '', + 'comment': '', + 'parentEntId': 22, + 'pos': Point3(10, 0, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'pathIndex': 0, + 'pathScale': 1.0}, + 10042: {'type': 'propSpinner', + 'name': '', + 'comment': '', + 'parentEntId': 7}, + 20001: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, 0, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 4.0, + 'phaseShift': 0.0, + 'range': 30.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20002: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, -14.3294, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 10.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20003: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(0, -28.3252, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(7, 5, 7), + 'modelPath': 0, + 'motion': 3, + 'period': 2.0, + 'phaseShift': 0.5, + 'range': 10.0, + 'shaftScale': Point3(0.5, 12, 0.5), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20004: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(-3.5, 16.2588, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0001373423482587, + 'phaseShift': 0.0, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20005: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(3.5, 16.2588, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 1.5, + 'phaseShift': 0.0, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 1, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20006: {'type': 'stomper', + 'name': 'copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(-3.5, 23.4392, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 1.5, + 'phaseShift': 0.5, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20007: {'type': 'stomper', + 'name': 'copy of copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20000, + 'pos': Point3(3.5, 23.4392, 0), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'crushCellId': None, + 'damage': 3, + 'headScale': Point3(3.5, 5, 3.5), + 'modelPath': 0, + 'motion': 3, + 'period': 3.0, + 'phaseShift': 0.5, + 'range': 15.0, + 'shaftScale': Point3(0.71, 12, 0.71), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 0, + 'style': 'vertical', + 'switchId': 0, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20029: {'type': 'stomper', + 'name': '', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(4.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20024, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20033, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20030: {'type': 'stomper', + 'name': 'copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(31.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20026, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20034, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20031: {'type': 'stomper', + 'name': 'copy of copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(64.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20027, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20035, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20032: {'type': 'stomper', + 'name': 'copy of copy of copy of ', + 'comment': '', + 'parentEntId': 20025, + 'pos': Point3(85.5, 43.5, 0.25), + 'hpr': Point3(0, 0, 0), + 'scale': 1, + 'animateShadow': 0, + 'crushCellId': 20028, + 'damage': 3, + 'headScale': Point3(3, 2, 3), + 'modelPath': 0, + 'motion': 5, + 'period': 2.0, + 'phaseShift': 0.0, + 'range': 12.0, + 'shaftScale': Point3(0.66, 37.5, 0.66), + 'soundLen': 0, + 'soundOn': 1, + 'soundPath': 2, + 'style': 'vertical', + 'switchId': 20036, + 'wantShadow': 1, + 'wantSmoke': 1, + 'zOffset': 0}, + 20050: {'type': 'trigger', + 'name': '', + 'comment': '', + 'parentEntId': 20022, + 'pos': Point3(10, 0, 10), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(20, 20, 20), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': 'signatureRoomView'}, + 20057: {'type': 'trigger', + 'name': '', + 'comment': '', + 'parentEntId': 23, + 'pos': Point3(3, -8.8, 15.5091), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(25, 25, 25), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': 'lookoutTrigger'}, + 30077: {'type': 'trigger', + 'name': 'button cutscene', + 'comment': '', + 'parentEntId': 3, + 'pos': Point3(-4, 8, 0), + 'hpr': Vec3(0, 0, 0), + 'scale': Point3(1, 1, 1), + 'isOn': 0, + 'isOnEvent': 0, + 'secondsOn': 1, + 'triggerName': ''}, + 10013: {'type': 'visibilityExtender', + 'name': 'intoEastSilo', + 'comment': '', + 'parentEntId': 60009, + 'event': 60101, + 'newZones': [61]}, + 10043: {'type': 'visibilityExtender', + 'name': 'beyondLobby', + 'comment': '', + 'parentEntId': 10049, + 'event': 10057, + 'newZones': [5, 116]}, + 10044: {'type': 'visibilityExtender', + 'name': 'intoEntrance1', + 'comment': '', + 'parentEntId': 10051, + 'event': 30000, + 'newZones': [3]}, + 10058: {'type': 'visibilityExtender', + 'name': 'intoFarHallway', + 'comment': '', + 'parentEntId': 10049, + 'event': 10059, + 'newZones': [6, 118]}, + 10060: {'type': 'visibilityExtender', + 'name': 'intoLookout', + 'comment': '', + 'parentEntId': 10049, + 'event': 10061, + 'newZones': [23]}, + 10062: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 60031, + 'event': 10063, + 'newZones': [4, 114]}, + 30022: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 10049, + 'event': 30000, + 'newZones': [4, 113]}, + 30023: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60017, + 'event': 30002, + 'newZones': [15, 126]}, + 30024: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60016, + 'event': 30002, + 'newZones': [16]}, + 30025: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60016, + 'event': 30021, + 'newZones': [14, 17, 121]}, + 30026: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60015, + 'event': 30016, + 'newZones': [13, 119]}, + 30027: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60015, + 'event': 30021, + 'newZones': [15, 106]}, + 30029: {'type': 'visibilityExtender', + 'name': 'beyondLobby', + 'comment': '', + 'parentEntId': 10051, + 'event': 30009, + 'newZones': [5, 116]}, + 30030: {'type': 'visibilityExtender', + 'name': 'beyond door 113', + 'comment': '', + 'parentEntId': 60000, + 'event': 30009, + 'newZones': [4, 114]}, + 30031: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60000, + 'event': 30011, + 'newZones': [6, + 109, + 117, + 118]}, + 30032: {'type': 'visibilityExtender', + 'name': 'intoHallwayFromLobby', + 'comment': '', + 'parentEntId': 60001, + 'event': 30011, + 'newZones': [5, 113]}, + 30033: {'type': 'visibilityExtender', + 'name': 'intoBoilerRoom', + 'comment': '', + 'parentEntId': 60001, + 'event': 30012, + 'newZones': [8]}, + 30034: {'type': 'visibilityExtender', + 'name': 'intoLookout', + 'comment': '', + 'parentEntId': 60001, + 'event': 30013, + 'newZones': [23, 39]}, + 30035: {'type': 'visibilityExtender', + 'name': 'intoGearRoom', + 'comment': '', + 'parentEntId': 60001, + 'event': 30005, + 'newZones': [7]}, + 30036: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60002, + 'event': 30005, + 'newZones': [6, + 116, + 117, + 118]}, + 30037: {'type': 'visibilityExtender', + 'name': 'beyond door 110', + 'comment': '', + 'parentEntId': 60002, + 'event': 30006, + 'newZones': [9, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 112, + 115, + 200]}, + 30038: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60005, + 'event': 30012, + 'newZones': [6, + 109, + 116, + 118]}, + 30039: {'type': 'visibilityExtender', + 'name': 'beyond door 108', + 'comment': '', + 'parentEntId': 60005, + 'event': 30004, + 'newZones': [12, + 21, + 26, + 34, + 35, + 40, + 41, + 53, + 60, + 119, + 120, + 200]}, + 30041: {'type': 'visibilityExtender', + 'name': 'beyond door 110', + 'comment': '', + 'parentEntId': 60003, + 'event': 30006, + 'newZones': [7]}, + 30042: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60003, + 'event': 30008, + 'newZones': [10, 11]}, + 30043: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60003, + 'event': 30010, + 'newZones': [24, 39]}, + 30044: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60004, + 'event': 30008, + 'newZones': [9, + 25, + 26, + 33, + 34, + 35, + 38, + 41, + 53, + 110, + 115, + 200]}, + 30046: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60066, + 'event': 30008, + 'newZones': [9, + 25, + 26, + 41, + 200]}, + 30049: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60013, + 'event': 30014, + 'newZones': [12, + 21, + 23, + 26, + 33, + 34, + 35, + 41, + 53, + 60, + 108, + 112, + 120, + 200]}, + 30050: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60013, + 'event': 30016, + 'newZones': [14, 17, 126]}, + 30051: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60014, + 'event': 30016, + 'newZones': [13, 119]}, + 30052: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60014, + 'event': 30021, + 'newZones': [15, 106]}, + 30055: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60019, + 'event': 30001, + 'newZones': [27, 127]}, + 30056: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60018, + 'event': 30001, + 'newZones': [27, 127]}, + 30057: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60018, + 'event': 60088, + 'newZones': [17]}, + 30059: {'type': 'visibilityExtender', + 'name': 'beyond door 108', + 'comment': '', + 'parentEntId': 60006, + 'event': 30004, + 'newZones': [8, 117]}, + 30060: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60006, + 'event': 30014, + 'newZones': [13, 121]}, + 30061: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60006, + 'event': 30015, + 'newZones': [24, 39]}, + 30062: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60024, + 'event': 30003, + 'newZones': [27, 127]}, + 30063: {'type': 'visibilityExtender', + 'name': 'intoHallway', + 'comment': '', + 'parentEntId': 60031, + 'event': 30013, + 'newZones': [6, + 109, + 116, + 117]}, + 30064: {'type': 'visibilityExtender', + 'name': 'beyondLowerWestDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30015, + 'newZones': [12, + 21, + 26, + 34, + 40, + 41, + 53, + 200]}, + 30066: {'type': 'visibilityExtender', + 'name': 'beyondLowerEastDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30010, + 'newZones': [9, + 25, + 26, + 33, + 38, + 41, + 200]}, + 30067: {'type': 'visibilityExtender', + 'name': 'beyondUpperEastDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30019, + 'newZones': [9, + 33, + 38, + 41, + 200, + 222]}, + 30069: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60000, + 'event': 30068, + 'newZones': [23]}, + 30071: {'type': 'visibilityExtender', + 'name': 'intoLobby', + 'comment': '', + 'parentEntId': 60001, + 'event': 60030, + 'newZones': [4, 114]}, + 30073: {'type': 'visibilityExtender', + 'name': 'intoLobbyHallway', + 'comment': '', + 'parentEntId': 60031, + 'event': 60033, + 'newZones': [5, 113]}, + 30075: {'type': 'visibilityExtender', + 'name': 'intoGearRoom', + 'comment': '', + 'parentEntId': 60031, + 'event': 60034, + 'newZones': [7]}, + 60008: {'type': 'visibilityExtender', + 'name': 'beyondUpperWestDoor', + 'comment': '', + 'parentEntId': 60007, + 'event': 30020, + 'newZones': [12, + 21, + 34, + 40, + 41, + 60, + 127, + 200]}, + 60010: {'type': 'visibilityExtender', + 'name': 'intoWarehouse', + 'comment': '', + 'parentEntId': 60009, + 'event': 30019, + 'newZones': [24, 39, 125]}, + 60012: {'type': 'visibilityExtender', + 'name': 'beyond door 125', + 'comment': '', + 'parentEntId': 60011, + 'event': 30020, + 'newZones': [24, 39, 124]}, + 60020: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60015, + 'event': 60076, + 'newZones': [16]}, + 60021: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 10051, + 'event': 60023, + 'newZones': [6, 118]}, + 60022: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 10051, + 'event': 60025, + 'newZones': [23]}, + 60026: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60000, + 'event': 60028, + 'newZones': [7]}, + 60027: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60000, + 'event': 60029, + 'newZones': [8]}, + 60032: {'type': 'visibilityExtender', + 'name': 'intoBoilerRoom', + 'comment': '', + 'parentEntId': 60031, + 'event': 60035, + 'newZones': [8]}, + 60036: {'type': 'visibilityExtender', + 'name': 'beyond door 117', + 'comment': '', + 'parentEntId': 60002, + 'event': 60037, + 'newZones': [8]}, + 60038: {'type': 'visibilityExtender', + 'name': 'beyond door 109', + 'comment': '', + 'parentEntId': 60005, + 'event': 60039, + 'newZones': [7]}, + 60040: {'type': 'visibilityExtender', + 'name': 'beyond door 124', + 'comment': '', + 'parentEntId': 60011, + 'event': 60041, + 'newZones': [38]}, + 60042: {'type': 'visibilityExtender', + 'name': 'beyondWarehouse', + 'comment': '', + 'parentEntId': 60009, + 'event': 60043, + 'newZones': [12, 200]}, + 60045: {'type': 'visibilityExtender', + 'name': 'beyond door 124', + 'comment': '', + 'parentEntId': 60044, + 'event': 60047, + 'newZones': [24]}, + 60046: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60044, + 'event': 10002, + 'newZones': [31]}, + 60048: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60024, + 'event': 60049, + 'newZones': [21, 200]}, + 60050: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60019, + 'event': 60051, + 'newZones': [21, 34, 200]}, + 60052: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60016, + 'event': 60053, + 'newZones': [13, 119]}, + 60054: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60017, + 'event': 60055, + 'newZones': [14, 17, 121]}, + 60056: {'type': 'visibilityExtender', + 'name': 'beyond door 126', + 'comment': '', + 'parentEntId': 60013, + 'event': 60057, + 'newZones': [15, 106]}, + 60058: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60005, + 'event': 60059, + 'newZones': [5]}, + 60060: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60005, + 'event': 60061, + 'newZones': [23]}, + 60062: {'type': 'visibilityExtender', + 'name': 'beyond door 116', + 'comment': '', + 'parentEntId': 60002, + 'event': 60064, + 'newZones': [5]}, + 60063: {'type': 'visibilityExtender', + 'name': 'beyond door 118', + 'comment': '', + 'parentEntId': 60002, + 'event': 60065, + 'newZones': [23]}, + 60068: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60067, + 'event': 30001, + 'newZones': [18, + 19, + 20, + 131]}, + 60069: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60067, + 'event': 30003, + 'newZones': [22]}, + 60070: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60067, + 'event': 10052, + 'newZones': [12, + 21, + 26, + 34, + 35, + 40, + 41, + 53, + 60, + 200]}, + 60071: {'type': 'visibilityExtender', + 'name': 'beyond door 127', + 'comment': '', + 'parentEntId': 60006, + 'event': 10052, + 'newZones': [27, 105, 107]}, + 60072: {'type': 'visibilityExtender', + 'name': 'beyond door 107', + 'comment': '', + 'parentEntId': 60006, + 'event': 60074, + 'newZones': [22]}, + 60073: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60006, + 'event': 60075, + 'newZones': [18]}, + 60077: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60014, + 'event': 60078, + 'newZones': [16]}, + 60079: {'type': 'visibilityExtender', + 'name': 'beyond door 106', + 'comment': '', + 'parentEntId': 60013, + 'event': 60080, + 'newZones': [16]}, + 60081: {'type': 'visibilityExtender', + 'name': 'beyond door 121', + 'comment': '', + 'parentEntId': 60017, + 'event': 60082, + 'newZones': [13]}, + 60083: {'type': 'visibilityExtender', + 'name': 'beyond door 119', + 'comment': '', + 'parentEntId': 60005, + 'event': 60084, + 'newZones': [13]}, + 60085: {'type': 'visibilityExtender', + 'name': 'beyond door 112', + 'comment': '', + 'parentEntId': 60002, + 'event': 60086, + 'newZones': [10]}, + 60087: {'type': 'visibilityExtender', + 'name': 'beyond door 105', + 'comment': '', + 'parentEntId': 60015, + 'event': 60091, + 'newZones': [27]}, + 60089: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60019, + 'event': 60088, + 'newZones': [17]}, + 60090: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60015, + 'event': 60088, + 'newZones': [18, 19, 105]}, + 60092: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60067, + 'event': 60093, + 'newZones': [17]}, + 60097: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60096, + 'event': 60095, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 129, + 200]}, + 60098: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60044, + 'event': 60095, + 'newZones': [30]}, + 60099: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60096, + 'event': 60100, + 'newZones': [31]}, + 60106: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60011, + 'event': 60094, + 'newZones': [32]}, + 60107: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60011, + 'event': 60095, + 'newZones': [30]}, + 60109: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60108, + 'event': 60094, + 'newZones': [32]}, + 60110: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60108, + 'event': 60095, + 'newZones': [30]}, + 60112: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60111, + 'event': 60094, + 'newZones': [32]}, + 60113: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60111, + 'event': 60095, + 'newZones': [30]}, + 60115: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60114, + 'event': 60094, + 'newZones': [32]}, + 60116: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60114, + 'event': 60095, + 'newZones': [30]}, + 60117: {'type': 'visibilityExtender', + 'name': 'beyond door 103', + 'comment': '', + 'parentEntId': 60014, + 'event': 60088, + 'newZones': [18]}, + 60120: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60108, + 'event': 10002, + 'newZones': [31]}, + 60122: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60121, + 'event': 10002, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 129, + 130, + 200]}, + 60123: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60111, + 'event': 10002, + 'newZones': []}, + 60124: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60114, + 'event': 10002, + 'newZones': [31]}, + 60125: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60011, + 'event': 10002, + 'newZones': [31]}, + 60127: {'type': 'visibilityExtender', + 'name': 'beyond door 128', + 'comment': '', + 'parentEntId': 60126, + 'event': 10002, + 'newZones': [31]}, + 60128: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60126, + 'event': 60094, + 'newZones': [32]}, + 60129: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60126, + 'event': 60095, + 'newZones': [30]}, + 60131: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60130, + 'event': 60094, + 'newZones': [33, + 34, + 35, + 36, + 37, + 60, + 61, + 128, + 130, + 200]}, + 60136: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60044, + 'event': 60094, + 'newZones': [32]}, + 60137: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60096, + 'event': 60138, + 'newZones': [32]}, + 60139: {'type': 'visibilityExtender', + 'name': 'beyond door 129', + 'comment': '', + 'parentEntId': 60121, + 'event': 60141, + 'newZones': [32]}, + 60140: {'type': 'visibilityExtender', + 'name': 'beyond door 130', + 'comment': '', + 'parentEntId': 60121, + 'event': 60142, + 'newZones': [30]}} +Scenario0 = {} +levelSpec = {'globalEntities': GlobalEntities, + 'scenarios': [Scenario0]} diff --git a/toontown/coghq/SinkingPlatformGlobals.py b/toontown/coghq/SinkingPlatformGlobals.py new file mode 100644 index 0000000..ce9ca8c --- /dev/null +++ b/toontown/coghq/SinkingPlatformGlobals.py @@ -0,0 +1,3 @@ +OFF = 0 +SINKING = 1 +RISING = 2 diff --git a/toontown/coghq/SpecImports.py b/toontown/coghq/SpecImports.py new file mode 100644 index 0000000..7148752 --- /dev/null +++ b/toontown/coghq/SpecImports.py @@ -0,0 +1 @@ +from pandac.PandaModules import * diff --git a/toontown/coghq/StageInterior.py b/toontown/coghq/StageInterior.py new file mode 100644 index 0000000..d936b1a --- /dev/null +++ b/toontown/coghq/StageInterior.py @@ -0,0 +1,279 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattlePlace +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.showbase import BulletinBoardWatcher +from pandac.PandaModules import * +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownBattleGlobals +from toontown.coghq import DistributedStage +from toontown.building import Elevator + +class StageInterior(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('StageInterior') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.parentFSM = parentFSM + self.zoneId = loader.stageId + self.elevatorDoneEvent = 'elevatorDone' + self.fsm = ClassicFSM.ClassicFSM('StageInterior', [State.State('start', self.enterStart, self.exitStart, ['walk', 'teleportIn', 'fallDown']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'died', + 'teleportOut', + 'squished', + 'DFA', + 'fallDown', + 'elevator']), + State.State('sit', self.enterSit, self.exitSit, ['walk', 'died', 'teleportOut']), + State.State('push', self.enterPush, self.exitPush, ['walk', 'died', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'battle', + 'DFA', + 'WaitForBattle', + 'died', + 'teleportOut']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', + 'walk', + 'died', + 'teleportOut']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['walk', 'died', 'teleportOut']), + State.State('squished', self.enterSquished, self.exitSquished, ['walk', 'died', 'teleportOut']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'died']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', + 'FLA', + 'quietZone', + 'WaitForBattle']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walkteleportOut']), + State.State('died', self.enterDied, self.exitDied, ['teleportOut']), + State.State('FLA', self.enterFLA, self.exitFLA, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.parentFSM.getStateNamed('stageInterior').addChild(self.fsm) + BattlePlace.BattlePlace.load(self) + self.music = base.loadMusic('phase_9/audio/bgm/CHQ_FACT_bg.mid') + + def unload(self): + self.parentFSM.getStateNamed('stageInterior').removeChild(self.fsm) + del self.music + del self.fsm + del self.parentFSM + BattlePlace.BattlePlace.unload(self) + + def enter(self, requestStatus): + self.fsm.enterInitialState() + base.transitions.fadeOut(t=0) + self._telemLimiter = TLGatherAllAvs('StageInterior', RotationLimitToH) + base.localAvatar.inventory.setRespectInvasions(0) + base.cr.forbidCheesyEffects(1) + + def commence(self = self): + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + base.playMusic(self.music, looping=1, volume=0.8) + base.transitions.irisIn() + stage = bboard.get(DistributedStage.DistributedStage.ReadyPost) + self.loader.hood.spawnTitleText(stage.stageId) + + self.stageReadyWatcher = BulletinBoardWatcher.BulletinBoardWatcher('StageReady', DistributedStage.DistributedStage.ReadyPost, commence) + self.stageDefeated = 0 + self.acceptOnce(DistributedStage.DistributedStage.WinEvent, self.handleStageWinEvent) + if __debug__ and 0: + self.accept('f10', lambda : messenger.send(DistributedStage.DistributedStage.WinEvent)) + self.confrontedBoss = 0 + + def handleConfrontedBoss(self = self): + self.confrontedBoss = 1 + + self.acceptOnce('localToonConfrontedStageBoss', handleConfrontedBoss) + + def exit(self): + NametagGlobals.setMasterArrowsOn(0) + self._telemLimiter.destroy() + del self._telemLimiter + bboard.remove(DistributedStage.DistributedStage.ReadyPost) + base.cr.forbidCheesyEffects(0) + base.localAvatar.inventory.setRespectInvasions(1) + self.fsm.requestFinalState() + self.loader.music.stop() + self.music.stop() + self.ignoreAll() + del self.stageReadyWatcher + + def enterWalk(self, teleportIn = 0): + BattlePlace.BattlePlace.enterWalk(self, teleportIn) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterPush(self): + BattlePlace.BattlePlace.enterPush(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterWaitForBattle(self): + StageInterior.notify.debug('enterWaitForBattle') + BattlePlace.BattlePlace.enterWaitForBattle(self) + if base.localAvatar.getParent() != render: + base.localAvatar.wrtReparentTo(render) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def exitWaitForBattle(self): + StageInterior.notify.debug('exitWaitForBattle') + BattlePlace.BattlePlace.exitWaitForBattle(self) + + def enterBattle(self, event): + StageInterior.notify.debug('enterBattle') + self.music.stop() + BattlePlace.BattlePlace.enterBattle(self, event) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterTownBattle(self, event): + mult = ToontownBattleGlobals.getStageCreditMultiplier(bboard.get(DistributedStage.DistributedStage.FloorNum)) + base.localAvatar.inventory.setBattleCreditMultiplier(mult) + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), bldg=1, creditMultiplier=mult) + + def exitBattle(self): + StageInterior.notify.debug('exitBattle') + BattlePlace.BattlePlace.exitBattle(self) + self.loader.music.stop() + base.playMusic(self.music, looping=1, volume=0.8) + + def enterStickerBook(self, page = None): + BattlePlace.BattlePlace.enterStickerBook(self, page) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterSit(self): + BattlePlace.BattlePlace.enterSit(self) + self.ignore('teleportQuery') + base.localAvatar.setTeleportAvailable(0) + + def enterZone(self, zoneId): + pass + + def enterTeleportOut(self, requestStatus): + StageInterior.notify.debug('enterTeleportOut()') + BattlePlace.BattlePlace.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __processLeaveRequest(self, requestStatus): + hoodId = requestStatus['hoodId'] + if hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def __teleportOutDone(self, requestStatus): + StageInterior.notify.debug('__teleportOutDone()') + messenger.send('leavingStage') + messenger.send('localToonLeft') + if self.stageDefeated and not self.confrontedBoss: + self.fsm.request('FLA', [requestStatus]) + else: + self.__processLeaveRequest(requestStatus) + + def exitTeleportOut(self): + StageInterior.notify.debug('exitTeleportOut()') + BattlePlace.BattlePlace.exitTeleportOut(self) + + def handleStageWinEvent(self): + StageInterior.notify.debug('handleStageWinEvent') + + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'died': + return + + self.stageDefeated = 1 + + if 1: + zoneId = ZoneUtil.getHoodId(self.zoneId) + else: + zoneId = ZoneUtil.getSafeZoneId(base.localAvatar.defaultZone) + + self.fsm.request('teleportOut', [{ + 'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + }]) + + def enterDied(self, requestStatus, callback = None): + StageInterior.notify.debug('enterDied') + + def diedDone(requestStatus, self = self, callback = callback): + if callback is not None: + callback() + messenger.send('leavingStage') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + BattlePlace.BattlePlace.enterDied(self, requestStatus, diedDone) + + def enterFLA(self, requestStatus): + StageInterior.notify.debug('enterFLA') + self.flaDialog = TTDialog.TTGlobalDialog(message=TTLocalizer.ForcedLeaveStageAckMsg, doneEvent='FLADone', style=TTDialog.Acknowledge, fadeScreen=1) + + def continueExit(self = self, requestStatus = requestStatus): + self.__processLeaveRequest(requestStatus) + + self.accept('FLADone', continueExit) + self.flaDialog.show() + + def exitFLA(self): + StageInterior.notify.debug('exitFLA') + if hasattr(self, 'flaDialog'): + self.flaDialog.cleanup() + del self.flaDialog + + def detectedElevatorCollision(self, distElevator): + self.fsm.request('elevator', [distElevator]) + + def enterElevator(self, distElevator): + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + distElevator.elevatorFSM = self.elevator + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + + 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 == 'factoryInterior' or where == 'suitInterior': + self.doneStatus = doneStatus + self.doneEvent = 'lawOfficeFloorDone' + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') diff --git a/toontown/coghq/StageLayout.py b/toontown/coghq/StageLayout.py new file mode 100644 index 0000000..23c0e79 --- /dev/null +++ b/toontown/coghq/StageLayout.py @@ -0,0 +1,702 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import invertDictLossless +from toontown.coghq import StageRoomSpecs +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import normalDistrib, lerp +import random + +def printAllCashbotInfo(): + print 'roomId: roomName' + for roomId, roomName in StageRoomSpecs.CashbotStageRoomId2RoomName.items(): + print '%s: %s' % (roomId, roomName) + + print '\nroomId: numBattles' + for roomId, numBattles in StageRoomSpecs.roomId2numBattles.items(): + print '%s: %s' % (roomId, numBattles) + + print '\nstageId floor roomIds' + printStageRoomIds() + print '\nstageId floor numRooms' + printNumRooms() + print '\nstageId floor numForcedBattles' + printNumBattles() + + +def iterateLawbotStages(func): + from toontown.toonbase import ToontownGlobals + for layoutId in xrange(len(stageLayouts)): + for floorNum in xrange(getNumFloors(layoutId)): + func(StageLayout(0, floorNum, layoutId)) + + +def printStageInfo(): + + def func(sl): + print sl + + iterateLawbotStages(func) + + +def printRoomUsage(): + usage = {} + + def func(sl): + for roomId in sl.getRoomIds(): + usage.setdefault(roomId, 0) + usage[roomId] += 1 + + iterateLawbotStages(func) + roomIds = usage.keys() + roomIds.sort() + for roomId in roomIds: + print '%s: %s' % (roomId, usage[roomId]) + + +def printRoomInfo(): + roomIds = StageRoomSpecs.roomId2numCogs.keys() + roomIds.sort() + for roomId in roomIds: + print 'room %s: %s cogs, %s cogLevels, %s merit cogLevels' % (roomId, + StageRoomSpecs.roomId2numCogs[roomId], + StageRoomSpecs.roomId2numCogLevels[roomId], + StageRoomSpecs.roomId2numMeritCogLevels[roomId]) + + +def printStageRoomIds(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getRoomIds() + + iterateCashbotStages(func) + + +def printStageRoomNames(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getRoomNames() + + iterateCashbotStages(func) + + +def printNumRooms(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getNumRooms() + + iterateCashbotStages(func) + + +def printNumBattles(): + + def func(ml): + print ml.getStageId(), ml.getFloorNum(), ml.getNumBattles() + + iterateCashbotStages(func) + + +DefaultLayout1 = ({0: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 1: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 2: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 3: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 4: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 5: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 6: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 7: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 8: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 9: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 10: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 11: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 12: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 13: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 14: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 15: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 16: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 17: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 18: (0, + 1, + 2, + 3, + 1, + 2, + 4), + 19: (0, + 1, + 2, + 3, + 1, + 2, + 4)},) +DefaultLayout = [(0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1), + (0, + 5, + 2, + 3, + 5, + 2, + 1)] +testLayout = [(0, + 3, + 8, + 105, + 1), (0, + 7, + 8, + 105, + 2)] +LawOfficeLayout2_0 = [(0, + 7, + 8, + 105, + 1), (0, + 10, + 104, + 103, + 1), (0, + 105, + 101, + 12, + 2)] +LawOfficeLayout2_1 = [(0, + 10, + 11, + 104, + 1), (0, + 100, + 105, + 8, + 1), (0, + 103, + 3, + 104, + 2)] +LawOfficeLayout2_2 = [(0, + 8, + 105, + 102, + 1), (0, + 100, + 104, + 10, + 1), (0, + 101, + 105, + 3, + 2)] +LawOfficeLayout3_0 = [(0, + 8, + 101, + 104, + 1), + (0, + 7, + 105, + 103, + 1), + (0, + 100, + 8, + 104, + 1), + (0, + 105, + 10, + 12, + 2)] +LawOfficeLayout3_1 = [(0, + 100, + 8, + 105, + 1), + (0, + 103, + 10, + 104, + 1), + (0, + 8, + 7, + 105, + 1), + (0, + 104, + 12, + 101, + 2)] +LawOfficeLayout3_2 = [(0, + 103, + 104, + 100, + 1), + (0, + 102, + 8, + 105, + 1), + (0, + 10, + 104, + 3, + 1), + (0, + 105, + 10, + 11, + 2)] +LawOfficeLayout4_0 = [(0, + 3, + 7, + 105, + 1), + (0, + 103, + 104, + 8, + 1), + (0, + 102, + 105, + 11, + 1), + (0, + 8, + 104, + 100, + 1), + (0, + 10, + 105, + 12, + 2)] +LawOfficeLayout4_1 = [(0, + 7, + 105, + 102, + 1), + (0, + 103, + 12, + 104, + 1), + (0, + 101, + 104, + 8, + 1), + (0, + 10, + 3, + 105, + 1), + (0, + 8, + 104, + 102, + 2)] +LawOfficeLayout4_2 = [(0, + 11, + 105, + 102, + 1), + (0, + 3, + 104, + 8, + 1), + (0, + 100, + 10, + 104, + 1), + (0, + 8, + 12, + 105, + 1), + (0, + 104, + 102, + 11, + 2)] +LawOfficeLayout5_0 = [(0, + 104, + 10, + 7, + 1), + (0, + 105, + 103, + 3, + 1), + (0, + 104, + 11, + 12, + 1), + (0, + 101, + 8, + 105, + 1), + (0, + 10, + 104, + 12, + 1), + (0, + 105, + 100, + 7, + 2)] +LawOfficeLayout5_1 = [(0, + 11, + 8, + 104, + 1), + (0, + 102, + 10, + 105, + 1), + (0, + 104, + 7, + 101, + 1), + (0, + 105, + 10, + 12, + 1), + (0, + 8, + 11, + 105, + 1), + (0, + 104, + 12, + 3, + 2)] +LawOfficeLayout5_2 = [(0, + 105, + 103, + 8, + 1), + (0, + 10, + 3, + 104, + 1), + (0, + 105, + 103, + 101, + 1), + (0, + 12, + 8, + 104, + 1), + (0, + 7, + 11, + 104, + 1), + (0, + 105, + 12, + 10, + 2)] +stageLayouts = [LawOfficeLayout2_0, + LawOfficeLayout2_1, + LawOfficeLayout2_2, + LawOfficeLayout3_0, + LawOfficeLayout3_1, + LawOfficeLayout3_2, + LawOfficeLayout4_0, + LawOfficeLayout4_1, + LawOfficeLayout4_2, + LawOfficeLayout5_0, + LawOfficeLayout5_1, + LawOfficeLayout5_2] +stageLayouts1 = [testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout, + testLayout] + +def getNumFloors(layoutIndex): + return len(stageLayouts[layoutIndex]) + + +class StageLayout: + notify = DirectNotifyGlobal.directNotify.newCategory('StageLayout') + + def __init__(self, stageId, floorNum, stageLayout = 0): + self.stageId = stageId + self.floorNum = floorNum + self.roomIds = [] + self.hallways = [] + self.layoutId = stageLayout + self.roomIds = stageLayouts[stageLayout][floorNum] + self.numRooms = 1 + len(self.roomIds) + self.numHallways = self.numRooms - 1 + hallwayRng = self.getRng() + connectorRoomNames = StageRoomSpecs.CashbotStageConnectorRooms + for i in xrange(self.numHallways): + self.hallways.append(hallwayRng.choice(connectorRoomNames)) + + def getNumRooms(self): + return len(self.roomIds) + + def getRoomId(self, n): + return self.roomIds[n] + + def getRoomIds(self): + return self.roomIds[:] + + def getRoomNames(self): + names = [] + for roomId in self.roomIds: + names.append(StageRoomSpecs.CashbotStageRoomId2RoomName[roomId]) + + return names + + def getNumHallways(self): + return len(self.hallways) + + def getHallwayModel(self, n): + return self.hallways[n] + + def getNumBattles(self): + numBattles = 0 + for roomId in self.getRoomIds(): + numBattles += StageRoomSpecs.roomId2numBattles[roomId] + + return numBattles + + def getNumCogs(self): + numCogs = 0 + for roomId in self.getRoomIds(): + numCogs += StageRoomSpecs.roomId2numCogs[roomId] + + return numCogs + + def getNumCogLevels(self): + numLevels = 0 + for roomId in self.getRoomIds(): + numLevels += StageRoomSpecs.roomId2numCogLevels[roomId] + + return numLevels + + def getNumMeritCogLevels(self): + numLevels = 0 + for roomId in self.getRoomIds(): + numLevels += StageRoomSpecs.roomId2numMeritCogLevels[roomId] + + return numLevels + + def getStageId(self): + return self.stageId + + def getFloorNum(self): + return self.floorNum + + def getRng(self): + return random.Random(self.stageId * self.floorNum) + + def __str__(self): + return 'StageLayout: id=%s, layout=%s, floor=%s, meritCogLevels=%s, numRooms=%s, numBattles=%s, numCogs=%s' % (self.stageId, + self.layoutId, + self.floorNum, + self.getNumMeritCogLevels(), + self.getNumRooms(), + self.getNumBattles(), + self.getNumCogs()) + + def __repr__(self): + return str(self) diff --git a/toontown/coghq/StageManagerAI.py b/toontown/coghq/StageManagerAI.py new file mode 100644 index 0000000..666e201 --- /dev/null +++ b/toontown/coghq/StageManagerAI.py @@ -0,0 +1,49 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedStageAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import StageLayout +from direct.showbase import DirectObject +import random + +class StageManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('StageManagerAI') + stageId = None + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + + def getDoId(self): + return 0 + + def createStage(self, stageId, players): + for avId in players: + if bboard.has('stageId-%s' % avId): + stageId = bboard.get('stageId-%s' % avId) + break + + numFloors = StageLayout.getNumFloors(stageId) + floor = random.randrange(numFloors) + for avId in players: + if bboard.has('stageFloor-%s' % avId): + floor = bboard.get('stageFloor-%s' % avId) + floor = max(0, floor) + floor = min(floor, numFloors - 1) + break + + for avId in players: + if bboard.has('stageRoom-%s' % avId): + roomId = bboard.get('stageRoom-%s' % avId) + for i in xrange(numFloors): + layout = StageLayout.StageLayout(stageId, i) + if roomId in layout.getRoomIds(): + floor = i + else: + from toontown.coghq import StageRoomSpecs + roomName = StageRoomSpecs.CashbotStageRoomId2RoomName[roomId] + StageManagerAI.notify.warning('room %s (%s) not found in any floor of stage %s' % (roomId, roomName, stageId)) + + stageZone = self.air.allocateZone() + stage = DistributedStageAI.DistributedStageAI(self.air, stageId, stageZone, floor, players) + stage.generateWithRequired(stageZone) + return stageZone diff --git a/toontown/coghq/StageRoom.py b/toontown/coghq/StageRoom.py new file mode 100644 index 0000000..04da9f4 --- /dev/null +++ b/toontown/coghq/StageRoom.py @@ -0,0 +1,104 @@ +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from toontown.toonbase import ToontownGlobals +from toontown.coghq import StageRoomSpecs +import random + +class StageRoom(DirectObject.DirectObject): + FloorCollPrefix = 'stageFloorColl' + CashbotStageDoorFrame = 'phase_10/models/cashbotHQ/DoorFrame' + + def __init__(self, path = None): + if path is not None: + if path in StageRoomSpecs.CashbotStageConnectorRooms: + loadFunc = loader.loadModelCopy + else: + loadFunc = loader.loadModel + self.setGeom(loadFunc(path)) + self.localToonFSM = ClassicFSM.ClassicFSM('StageRoomLocalToonPresent', [State.State('off', self.enterLtOff, self.exitLtOff, ['notPresent']), State.State('notPresent', self.enterLtNotPresent, self.exitLtNotPresent, ['present']), State.State('present', self.enterLtPresent, self.exitLtPresent, ['notPresent'])], 'notPresent', 'notPresent') + self.localToonFSM.enterInitialState() + return + + def delete(self): + del self.localToonFSM + + def enter(self): + self.localToonFSM.request('notPresent') + + def exit(self): + self.localToonFSM.requestFinalState() + + def setRoomNum(self, num): + self.roomNum = num + + def getRoomNum(self): + return self.roomNum + + def setGeom(self, geom): + self.__geom = geom + ug = self.__geom.find('**/underground') + if not ug.isEmpty(): + ug.setBin('ground', -10) + + def getGeom(self): + return self.__geom + + def _getEntrances(self): + return self.__geom.findAllMatches('**/ENTRANCE*') + + def _getExits(self): + return self.__geom.findAllMatches('**/EXIT*') + + def attachTo(self, other, rng): + otherExits = other._getExits() + entrances = self._getEntrances() + otherDoor = otherExits[0] + thisDoor = rng.choice(entrances) + geom = self.getGeom() + otherGeom = other.getGeom() + tempNode = otherDoor.attachNewNode('tempRotNode') + geom.reparentTo(tempNode) + geom.clearMat() + geom.setPos(Vec3(0) - thisDoor.getPos(geom)) + tempNode.setH(-thisDoor.getH(otherDoor)) + geom.wrtReparentTo(otherGeom.getParent()) + tempNode.removeNode() + + def getFloorCollName(self): + return '%s%s' % (StageRoom.FloorCollPrefix, self.roomNum) + + def initFloorCollisions(self): + allColls = self.getGeom().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 = self.getFloorCollName() + others = self.getGeom().findAllMatches('**/%s' % floorCollName) + for other in others: + other.setName('%s_renamed' % floorCollName) + + for floorColl in floorColls: + floorColl.setName(floorCollName) + + def enterLtOff(self): + pass + + def exitLtOff(self): + pass + + def enterLtNotPresent(self): + pass + + def exitLtNotPresent(self): + pass + + def enterLtPresent(self): + pass + + def exitLtPresent(self): + pass diff --git a/toontown/coghq/StageRoomBase.py b/toontown/coghq/StageRoomBase.py new file mode 100644 index 0000000..e51923f --- /dev/null +++ b/toontown/coghq/StageRoomBase.py @@ -0,0 +1,24 @@ +from toontown.toonbase import ToontownGlobals + +class StageRoomBase: + + def __init__(self): + pass + + def setStageId(self, stageId): + self.stageId = stageId + self.cogTrack = ToontownGlobals.cogHQZoneId2dept(stageId) + + def setRoomId(self, roomId): + self.roomId = roomId + + def getCogTrack(self): + return self.cogTrack + + if __dev__: + + def getEntityTypeReg(self): + import FactoryEntityTypes + from otp.level import EntityTypeRegistry + typeReg = EntityTypeRegistry.EntityTypeRegistry(FactoryEntityTypes) + return typeReg diff --git a/toontown/coghq/StageRoomSpecs.py b/toontown/coghq/StageRoomSpecs.py new file mode 100644 index 0000000..290f705 --- /dev/null +++ b/toontown/coghq/StageRoomSpecs.py @@ -0,0 +1,108 @@ +from direct.showbase.PythonUtil import invertDict +from toontown.toonbase import ToontownGlobals +from toontown.coghq import NullCogs +from toontown.coghq import LawbotOfficeOilRoom_Battle00_Cogs +from toontown.coghq import LawbotOfficeOilRoom_Battle01_Cogs +from toontown.coghq import LawbotOfficeBoilerRoom_Battle00_Cogs +from toontown.coghq import LawbotOfficeBoilerRoom_Trap00_Cogs +from toontown.coghq import LawbotOfficeLobby_Trap00_Cogs +from toontown.coghq import LawbotOfficeDiamondRoom_Trap00_Cogs +from toontown.coghq import LawbotOfficeDiamondRoom_Battle00_Cogs +from toontown.coghq import LawbotOfficeGearRoom_Battle00_Cogs + +def getStageRoomSpecModule(roomId): + return CashbotStageSpecModules[roomId] + + +def getCogSpecModule(roomId): + roomName = CashbotStageRoomId2RoomName[roomId] + return CogSpecModules.get(roomName, NullCogs) + + +def getNumBattles(roomId): + return roomId2numBattles[roomId] + + +CashbotStageRoomId2RoomName = {0: 'LawbotOfficeEntrance_Action00', + 1: 'LawbotOfficeOilRoom_Battle00', + 2: 'LawbotOfficeOilRoom_Battle01', + 3: 'LawbotOfficeBoilerRoom_Security00', + 4: 'LawbotOfficeBoilerRoom_Battle00', + 5: 'LawbotOfficeGearRoom_Action00', + 6: 'LawbotOfficeLobby_Action00', + 7: 'LawbotOfficeGearRoom_Security00', + 8: 'LawbotOfficeLobby_Trap00', + 9: 'LawbotOfficeDiamondRoom_Security00', + 10: 'LawbotOfficeDiamondRoom_Trap00', + 11: 'LawbotOfficeGearRoom_Platform00', + 12: 'LawbotOfficeLobby_Lights00', + 100: 'LawbotOfficeBoilerRoom_Action01', + 101: 'LawbotOfficeDiamondRoom_Action00', + 102: 'LawbotOfficeDiamondRoom_Action01', + 103: 'LawbotOfficeLobby_Action01', + 104: 'LawbotOfficeDiamondRoom_Battle00', + 105: 'LawbotOfficeGearRoom_Battle00'} +CashbotStageRoomName2RoomId = invertDict(CashbotStageRoomId2RoomName) +CashbotStageEntranceIDs = (0,) +CashbotStageMiddleRoomIDs = (1,) +CashbotStageFinalRoomIDs = (2,) +CashbotStageConnectorRooms = ('phase_11/models/lawbotHQ/LB_connector_7cubeL2', 'phase_11/models/lawbotHQ/LB_connector_7cubeLR') +CashbotStageSpecModules = {} +if not isClient(): + print 'EXECWARNING StageRoomSpecs: %s' % CashbotStageRoomName2RoomId + printStack() +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + exec 'from toontown.coghq import %s' % roomName + CashbotStageSpecModules[roomId] = eval(roomName) + +CogSpecModules = {'LawbotOfficeOilRoom_Battle00': LawbotOfficeOilRoom_Battle00_Cogs, + 'LawbotOfficeOilRoom_Battle01': LawbotOfficeOilRoom_Battle01_Cogs, + 'LawbotOfficeBoilerRoom_Battle00': LawbotOfficeBoilerRoom_Battle00_Cogs, + 'LawbotOfficeBoilerRoom_Trap00': LawbotOfficeBoilerRoom_Trap00_Cogs, + 'LawbotOfficeLobby_Trap00': LawbotOfficeLobby_Trap00_Cogs, + 'LawbotOfficeDiamondRoom_Trap00': LawbotOfficeDiamondRoom_Trap00_Cogs, + 'LawbotOfficeDiamondRoom_Battle00': LawbotOfficeDiamondRoom_Battle00_Cogs, + 'LawbotOfficeGearRoom_Battle00': LawbotOfficeGearRoom_Battle00_Cogs} +roomId2numBattles = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numBattles[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numBattles[roomId] = len(cogSpecModule.BattleCells) + +roomId2numCogs = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numCogs[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + roomId2numCogs[roomId] = len(cogSpecModule.CogData) + +roomId2numCogLevels = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules: + roomId2numCogLevels[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + levels = 0 + for cogData in cogSpecModule.CogData: + levels += cogData['level'] + + roomId2numCogLevels[roomId] = levels + +roomId2numMeritCogLevels = {} +for roomName, roomId in CashbotStageRoomName2RoomId.items(): + if roomName not in CogSpecModules or roomId in (8, 10): + roomId2numMeritCogLevels[roomId] = 0 + else: + cogSpecModule = CogSpecModules[roomName] + levels = 0 + for cogData in cogSpecModule.CogData: + levels += cogData['level'] + + roomId2numMeritCogLevels[roomId] = levels + +middleRoomId2numBattles = {} +for roomId in CashbotStageMiddleRoomIDs: + middleRoomId2numBattles[roomId] = roomId2numBattles[roomId] diff --git a/toontown/coghq/StomperGlobals.py b/toontown/coghq/StomperGlobals.py new file mode 100644 index 0000000..9359f90 --- /dev/null +++ b/toontown/coghq/StomperGlobals.py @@ -0,0 +1,12 @@ +STOMPER_START = 0 +STOMPER_PAUSE = 1 +STOMPER_RESUME = 2 +STOMPER_RESET = 3 +STOMPER_STOMP = 4 +STOMPER_RISE = 5 +MotionLinear = 0 +MotionSinus = 1 +MotionHalfSinus = 2 +MotionSlowFast = 3 +MotionCrush = 4 +MotionSwitched = 5 diff --git a/toontown/coghq/__init__.py b/toontown/coghq/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/distributed/DelayDeletable.py b/toontown/distributed/DelayDeletable.py new file mode 100644 index 0000000..5b3782f --- /dev/null +++ b/toontown/distributed/DelayDeletable.py @@ -0,0 +1,31 @@ +from direct.distributed.DistributedObject import ESGenerating, ESGenerated, ESNum2Str + +class DelayDeletable: + DelayDeleteSerialGen = SerialNumGen() + + def delayDelete(self): + pass + + def acquireDelayDelete(self, name): + global ESGenerating + global ESGenerated + if not self._delayDeleteForceAllow and self.activeState not in (ESGenerating, ESGenerated): + self.notify.error('cannot acquire DelayDelete "%s" on %s because it is in state %s' % (name, self.__class__.__name__, ESNum2Str[self.activeState])) + if self.getDelayDeleteCount() == 0: + self.cr._addDelayDeletedDO(self) + token = DelayDeletable.DelayDeleteSerialGen.next() + self._token2delayDeleteName[token] = name + return token + + def releaseDelayDelete(self, token): + name = self._token2delayDeleteName.pop(token) + if len(self._token2delayDeleteName) == 0: + self.cr._removeDelayDeletedDO(self) + if self._delayDeleted: + self.disableAnnounceAndDelete() + + def getDelayDeleteNames(self): + return self._token2delayDeleteName.values() + + def forceAllowDelayDelete(self): + self._delayDeleteForceAllow = True diff --git a/toontown/distributed/DelayDelete.py b/toontown/distributed/DelayDelete.py new file mode 100644 index 0000000..59f4924 --- /dev/null +++ b/toontown/distributed/DelayDelete.py @@ -0,0 +1,42 @@ + + +class DelayDelete: + + def __init__(self, distObj, name): + self._distObj = distObj + self._name = name + self._token = self._distObj.acquireDelayDelete(name) + + def getObject(self): + return self._distObj + + def getName(self): + return self._name + + def destroy(self): + token = self._token + del self._token + self._distObj.releaseDelayDelete(token) + del self._distObj + del self._name + + +def cleanupDelayDeletes(interval): + if hasattr(interval, 'delayDelete'): + delayDelete = interval.delayDelete + del interval.delayDelete + if type(delayDelete) == type([]): + for i in delayDelete: + i.destroy() + + else: + delayDelete.destroy() + if hasattr(interval, 'delayDeletes'): + delayDeletes = interval.delayDeletes + del interval.delayDeletes + if type(delayDeletes) == type([]): + for i in delayDeletes: + i.destroy() + + else: + delayDeletes.destroy() diff --git a/toontown/distributed/DistributedTimer.py b/toontown/distributed/DistributedTimer.py new file mode 100644 index 0000000..bc39268 --- /dev/null +++ b/toontown/distributed/DistributedTimer.py @@ -0,0 +1,32 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +import time + +class DistributedTimer(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTimer') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + base.cr.DTimer = self + + def delete(self): + DistributedObject.DistributedObject.delete(self) + base.cr.DTimer = None + return + + def setStartTime(self, time): + self.startTime = time + print 'TIMER startTime %s' % time + + def getStartTime(self): + return self.startTime + + def getTime(self): + elapsedTime = globalClockDelta.localElapsedTime(self.startTime, bits=32) + return elapsedTime diff --git a/toontown/distributed/DistributedTimerAI.py b/toontown/distributed/DistributedTimerAI.py new file mode 100644 index 0000000..1450554 --- /dev/null +++ b/toontown/distributed/DistributedTimerAI.py @@ -0,0 +1,22 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +import time + +class DistributedTimerAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTimerAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.setStartTime(globalClockDelta.getRealNetworkTime(bits=32)) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + + def setStartTime(self, time): + self.startTime = time + + def getStartTime(self): + return self.startTime diff --git a/toontown/distributed/HoodMgr.py b/toontown/distributed/HoodMgr.py new file mode 100644 index 0000000..6eeadec --- /dev/null +++ b/toontown/distributed/HoodMgr.py @@ -0,0 +1,932 @@ +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.launcher import DownloadForceAcknowledge +import string +import random +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil + +class HoodMgr(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('HoodMgr') + ToontownCentralInitialDropPoints = ([-90.7, + -60.0, + 0.025, + 102.575, + 0, + 0], + [-91.4, + -40.5, + -3.948, + 125.763, + 0, + 0], + [-107.8, + -17.8, + -1.937, + 149.456, + 0, + 0], + [-108.7, + 12.8, + -1.767, + 158.756, + 0, + 0], + [-42.1, + -22.8, + -1.328, + -248.1, + 0, + 0], + [-35.2, + -60.2, + 0.025, + -265.639, + 0, + 0]) + ToontownCentralHQDropPoints = ([-43.5, + 42.6, + -0.55, + -100.454, + 0, + 0], + [-53.0, + 12.5, + -2.948, + 281.502, + 0, + 0], + [-40.3, + -18.5, + -0.913, + -56.674, + 0, + 0], + [-1.9, + -37.0, + 0.025, + -23.43, + 0, + 0], + [1.9, + -5.9, + 4.0, + -37.941, + 0, + 0]) + ToontownCentralTunnelDropPoints = ([-28.3, + 40.1, + 0.25, + 17.25, + 0, + 0], + [-63.75, + 58.96, + -0.5, + -23.75, + 0, + 0], + [-106.93, + 17.66, + -2.2, + 99.0, + 0, + 0], + [-116.0, + -21.5, + -0.038, + 50.0, + 0, + 0], + [74.88, + -115.0, + 2.53, + -224.41, + 0, + 0], + [30.488, + -101.5, + 2.53, + -179.23, + 0, + 0]) + dropPoints = {ToontownGlobals.DonaldsDock: ([-28.0, + -2.5, + 5.8, + 120.0, + 0.0, + 0.0], + [-22, + 13, + 5.8, + 155.6, + 0.0, + 0.0], + [67, + 47, + 5.7, + 134.7, + 0.0, + 0.0], + [62, + 19, + 5.7, + 97.0, + 0.0, + 0.0], + [66, + -27, + 5.7, + 80.5, + 0.0, + 0.0], + [-114, + -7, + 5.7, + -97.0, + -0.0, + 0.0], + [-108, + 36, + 5.7, + -153.8, + -0.0, + 0.0], + [-116, + -46, + 5.7, + -70.1, + -0.0, + 0.0], + [-63, + -79, + 5.7, + -41.2, + -0.0, + 0.0], + [-2, + -79, + 5.7, + 57.4, + -0.0, + 0.0], + [-38, + -78, + 5.7, + 9.1, + -0.0, + 0.0]), + ToontownGlobals.ToontownCentral: ([-60, + -8, + 1.3, + -90, + 0, + 0], + [-66, + -9, + 1.3, + -274, + 0, + 0], + [17, + -28, + 4.1, + -44, + 0, + 0], + [87.7, + -22, + 4, + 66, + 0, + 0], + [-9.6, + 61.1, + 0, + 132, + 0, + 0], + [-109.0, + -2.5, + -1.656, + -90, + 0, + 0], + [-35.4, + -81.3, + 0.5, + -4, + 0, + 0], + [-103, + 72, + 0, + -141, + 0, + 0], + [93.5, + -148.4, + 2.5, + 43, + 0, + 0], + [25, + 123.4, + 2.55, + 272, + 0, + 0], + [48, + 39, + 4, + 201, + 0, + 0], + [-80, + -61, + 0.1, + -265, + 0, + 0], + [-46.875, + 43.68, + -1.05, + 124, + 0, + 0], + [34, + -105, + 2.55, + 45, + 0, + 0], + [16, + -75, + 2.55, + 56, + 0, + 0], + [-27, + -56, + 0.1, + 45, + 0, + 0], + [100, + 27, + 4.1, + 150, + 0, + 0], + [-70, + 4.6, + -1.9, + 90, + 0, + 0], + [-130.7, + 50, + 0.55, + -111, + 0, + 0]), + ToontownGlobals.TheBrrrgh: ([35, + -32, + 6.2, + 138, + 0.0, + 0.0], + [26, + -105, + 6.2, + -339, + 0.0, + 0.0], + [-29, + -139, + 6.2, + -385, + 0.0, + 0.0], + [-79, + -123, + 6.2, + -369, + 0.0, + 0.0], + [-114, + -86, + 3, + -54, + 0.0, + 0.0], + [-136, + 9, + 6.2, + -125, + 0.0, + 0.0], + [-75, + 92, + 6.2, + -187, + 0.0, + 0.0], + [-7, + 75, + 6.2, + -187, + 0.0, + 0.0], + [-106, + -42, + 8.6, + -111, + 0.0, + 0.0], + [-116, + -44, + 8.3, + -20, + 0.0, + 0.0]), + ToontownGlobals.MinniesMelodyland: ([86, + 44, + -13.5, + 121.1, + 0.0, + 0.0], + [88, + -8, + -13.5, + 91, + 0, + 0], + [92, + -76, + -13.5, + 62.5, + 0.0, + 0.0], + [53, + -112, + 6.5, + 65.8, + 0.0, + 0.0], + [-69, + -71, + 6.5, + -67.2, + 0.0, + 0.0], + [-75, + 21, + 6.5, + -100.9, + 0.0, + 0.0], + [-21, + 72, + 6.5, + -129.5, + 0.0, + 0.0], + [56, + 72, + 6.5, + 138.2, + 0.0, + 0.0], + [-41, + 47, + 6.5, + -98.9, + 0.0, + 0.0]), + ToontownGlobals.DaisyGardens: ([0, + 0, + 0, + -10.5, + 0, + 0], + [76, + 35, + 1.1, + -30.2, + 0.0, + 0.0], + [97, + 106, + 0.0, + 51.4, + 0.0, + 0.0], + [51, + 180, + 10.0, + 22.6, + 0.0, + 0.0], + [-14, + 203, + 10.0, + 85.6, + 0.0, + 0.0], + [-58, + 158, + 10.0, + -146.9, + 0.0, + 0.0], + [-86, + 128, + 0.0, + -178.9, + 0.0, + 0.0], + [-64, + 65, + 0.0, + 17.7, + 0.0, + 0.0], + [-13, + 39, + 0.0, + -15.7, + 0.0, + 0.0], + [-12, + 193, + 0.0, + -112.4, + 0.0, + 0.0], + [87, + 128, + 0.0, + 45.4, + 0.0, + 0.0]), + ToontownGlobals.DonaldsDreamland: ([77, + 91, + 0.0, + 124.4, + 0.0, + 0.0], + [29, + 92, + 0.0, + -154.5, + 0.0, + 0.0], + [-28, + 49, + -16.4, + -142.0, + 0.0, + 0.0], + [21, + 40, + -16.0, + -65.1, + 0.0, + 0.0], + [48, + 27, + -15.4, + -161.0, + 0.0, + 0.0], + [-2, + -22, + -15.2, + -132.1, + 0.0, + 0.0], + [-92, + -88, + 0.0, + -116.3, + 0.0, + 0.0], + [-56, + -93, + 0.0, + -21.5, + 0.0, + 0.0], + [20, + -88, + 0.0, + -123.4, + 0.0, + 0.0], + [76, + -90, + 0.0, + 11.0, + 0.0, + 0.0]), + ToontownGlobals.GoofySpeedway: ([-0.7, + 62, + 0.08, + 182, + 0, + 0], + [-1, + -30, + 0.06, + 183, + 0, + 0], + [-13, + -120, + 0, + 307, + 0, + 0], + [16.4, + -120, + 0, + 65, + 0, + 0], + [-0.5, + -90, + 0, + 182, + 0, + 0], + [-30, + -25, + -0.373, + 326, + 0, + 0], + [29, + -17, + -0.373, + 32, + 0, + 0]), + ToontownGlobals.GolfZone: ([-49.6, + 102, + 0, + 162, + 0, + 0], + [-22.8, + 36.6, + 0, + 157.5, + 0, + 0], + [40, + 51, + 0, + 185, + 0, + 0], + [48.3, + 122.2, + 0, + 192, + 0, + 0], + [106.3, + 69.2, + 0, + 133, + 0, + 0], + [-81.5, + 47.2, + 0, + 183, + 0, + 0], + [-80.5, + -84.2, + 0, + 284, + 0, + 0], + [73, + -111, + 0, + 354, + 0, + 0]), + ToontownGlobals.OutdoorZone: ([-165.8, + 108, + 0.025, + 252, + 0, + 0], + [21, + 130, + 0.16, + 170, + 0, + 0], + [93, + 78.5, + 0.23, + 112, + 0, + 0], + [79, + -1.6, + 0.75, + 163, + 0, + 0], + [10, + 33, + 5.32, + 130.379, + 0, + 0], + [-200, + -42, + 0.025, + 317.543, + 0, + 0], + [-21, + -65, + 0.335, + -18, + 0, + 0], + [23, + 68.5, + 4.51, + -22.808, + 0, + 0]), + ToontownGlobals.Tutorial: ([130.9, + -8.6, + -1.3, + 105.5, + 0, + 0],), + ToontownGlobals.SellbotHQ: ([64, + -128, + 0.26, + 36, + 0, + 0], + [9, + -140, + 0.26, + 0, + 0, + 0], + [-82, + -112, + 0.26, + -127, + 0, + 0], + [-73, + -213, + 0.26, + -23, + 0, + 0], + [-20, + -243, + 0.26, + -9, + 0, + 0], + [79, + -208, + 0.26, + 43, + 0, + 0]), + ToontownGlobals.CashbotHQ: ([102, + -437, + -23.439, + 360, + 0, + 0], + [124, + -437, + -23.439, + 360, + 0, + 0], + [110, + -446, + -23.439, + 360, + 0, + 0], + [132, + -446, + -23.439, + 360, + 0, + 0]), + ToontownGlobals.LawbotHQ: ([77.5, + 129.13, + -68.4, + -166.6, + 0, + 0], + [-57.7, + 80.75, + -68.4, + -139.2, + 0, + 0], + [203.3, + 46.36, + -68.4, + -213.37, + 0, + 0], + [88.2, + -336.52, + -68.4, + -720.4, + 0, + 0], + [232.77, + -305.33, + -68.4, + -651, + 0, + 0], + [-20.16, + -345.76, + -68.4, + -777.98, + 0, + 0])} + hoodName2Id = {'dd': ToontownGlobals.DonaldsDock, + 'tt': ToontownGlobals.ToontownCentral, + 'br': ToontownGlobals.TheBrrrgh, + 'mm': ToontownGlobals.MinniesMelodyland, + 'dg': ToontownGlobals.DaisyGardens, + 'oz': ToontownGlobals.OutdoorZone, + 'ff': ToontownGlobals.FunnyFarm, + 'gs': ToontownGlobals.GoofySpeedway, + 'dl': ToontownGlobals.DonaldsDreamland, + 'bosshq': ToontownGlobals.BossbotHQ, + 'sellhq': ToontownGlobals.SellbotHQ, + 'cashhq': ToontownGlobals.CashbotHQ, + 'lawhq': ToontownGlobals.LawbotHQ, + 'gz': ToontownGlobals.GolfZone} + hoodId2Name = {ToontownGlobals.DonaldsDock: 'dd', + ToontownGlobals.ToontownCentral: 'tt', + ToontownGlobals.Tutorial: 'tt', + ToontownGlobals.TheBrrrgh: 'br', + ToontownGlobals.MinniesMelodyland: 'mm', + ToontownGlobals.DaisyGardens: 'dg', + ToontownGlobals.OutdoorZone: 'oz', + ToontownGlobals.FunnyFarm: 'ff', + ToontownGlobals.GoofySpeedway: 'gs', + ToontownGlobals.DonaldsDreamland: 'dl', + ToontownGlobals.BossbotHQ: 'bosshq', + ToontownGlobals.SellbotHQ: 'sellhq', + ToontownGlobals.CashbotHQ: 'cashhq', + ToontownGlobals.LawbotHQ: 'lawhq', + ToontownGlobals.GolfZone: 'gz'} + DefaultDropPoint = [0, + 0, + 0, + 0, + 0, + 0] + dbgDropMode = 0 + currentDropPoint = 0 + + def __init__(self, cr): + self.cr = cr + + def getDropPoint(self, dropPointList): + if self.dbgDropMode == 0: + return random.choice(dropPointList) + else: + droppnt = self.currentDropPoint % len(dropPointList) + self.currentDropPoint = (self.currentDropPoint + 1) % len(dropPointList) + return dropPointList[droppnt] + + def getAvailableZones(self): + if base.launcher == None: + return self.getZonesInPhase(4) + self.getZonesInPhase(6) + self.getZonesInPhase(8) + self.getZonesInPhase(9) + self.getZonesInPhase(10) + self.getZonesInPhase(11) + self.getZonesInPhase(12) + self.getZonesInPhase(13) + else: + zones = [] + for phase in set(ToontownGlobals.phaseMap.values()): + if base.launcher.getPhaseComplete(phase): + zones = zones + self.getZonesInPhase(phase) + + return zones + return + + def getZonesInPhase(self, phase): + p = [] + for i in ToontownGlobals.phaseMap.items(): + if i[1] == phase: + p.append(i[0]) + + return p + + def getPhaseFromHood(self, hoodId): + hoodId = ZoneUtil.getCanonicalHoodId(hoodId) + return ToontownGlobals.phaseMap[hoodId] + + def getPlaygroundCenterFromId(self, hoodId): + dropPointList = self.dropPoints.get(hoodId, None) + if dropPointList: + return self.getDropPoint(dropPointList) + else: + self.notify.warning('getPlaygroundCenterFromId: No such hood name as: ' + str(hoodId)) + return self.DefaultDropPoint + return + + def getIdFromName(self, hoodName): + id = self.hoodName2Id.get(hoodName) + if id: + return id + else: + self.notify.error('No such hood name as: %s' % hoodName) + return None + return None + + def getNameFromId(self, hoodId): + name = self.hoodId2Name.get(hoodId) + if name: + return name + else: + self.notify.error('No such hood id as: %s' % hoodId) + return None + return None + + def getFullnameFromId(self, hoodId): + hoodId = ZoneUtil.getCanonicalZoneId(hoodId) + return ToontownGlobals.hoodNameMap[hoodId][-1] + + def addLinkTunnelHooks(self, hoodPart, nodeList, currentZoneId): + tunnelOriginList = [] + for i in nodeList: + linkTunnelNPC = i.findAllMatches('**/linktunnel*') + for p in range(linkTunnelNPC.getNumPaths()): + linkTunnel = linkTunnelNPC.getPath(p) + name = linkTunnel.getName() + nameParts = name.split('_') + hoodStr = nameParts[1] + zoneStr = nameParts[2] + hoodId = self.getIdFromName(hoodStr) + zoneId = int(zoneStr) + hoodId = ZoneUtil.getTrueZoneId(hoodId, currentZoneId) + zoneId = ZoneUtil.getTrueZoneId(zoneId, currentZoneId) + linkSphere = linkTunnel.find('**/tunnel_trigger') + if linkSphere.isEmpty(): + linkSphere = linkTunnel.find('**/tunnel_sphere') + if not linkSphere.isEmpty(): + cnode = linkSphere.node() + cnode.setName('tunnel_trigger_' + hoodStr + '_' + zoneStr) + cnode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.GhostBitmask) + else: + linkSphere = linkTunnel.find('**/tunnel_trigger_' + hoodStr + '_' + zoneStr) + if linkSphere.isEmpty(): + self.notify.error('tunnel_trigger not found') + tunnelOrigin = linkTunnel.find('**/tunnel_origin') + if tunnelOrigin.isEmpty(): + self.notify.error('tunnel_origin not found') + tunnelOriginPlaceHolder = render.attachNewNode('toph_' + hoodStr + '_' + zoneStr) + tunnelOriginList.append(tunnelOriginPlaceHolder) + tunnelOriginPlaceHolder.setPos(tunnelOrigin.getPos(render)) + tunnelOriginPlaceHolder.setHpr(tunnelOrigin.getHpr(render)) + hood = base.localAvatar.cr.playGame.hood + if ZoneUtil.tutorialDict: + how = 'teleportIn' + tutorialFlag = 1 + else: + how = 'tunnelIn' + tutorialFlag = 0 + hoodPart.accept('enter' + linkSphere.getName(), hoodPart.handleEnterTunnel, [{'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': how, + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'tunnelOrigin': tunnelOriginPlaceHolder, + 'tutorial': tutorialFlag}]) + + return tunnelOriginList + + def extractGroupName(self, groupFullName): + return string.split(groupFullName, ':', 1)[0] + + def makeLinkTunnelName(self, hoodId, currentZone): + return '**/toph_' + self.getNameFromId(hoodId) + '_' + str(currentZone) diff --git a/toontown/distributed/NonRepeatableRandomSourceAI.py b/toontown/distributed/NonRepeatableRandomSourceAI.py new file mode 100644 index 0000000..a3de26b --- /dev/null +++ b/toontown/distributed/NonRepeatableRandomSourceAI.py @@ -0,0 +1,49 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.distributed.DistributedObjectAI import DistributedObjectAI +from direct.task import Task +from otp.distributed import OtpDoGlobals +import random + +class NonRepeatableRandomSourceAI(DistributedObjectAI): + notify = directNotify.newCategory('NonRepeatableRandomSourceAI') + + def __init__(self, air): + DistributedObjectAI.__init__(self, air) + + def announceGenerate(self): + DistributedObjectAI.announceGenerate(self) + self._contextGen = SerialMaskedGen((1L << 32) - 1) + self._requests = {} + self._sampleTask = self.doMethodLater(3 * 60, self._sampleRandomTask, self.uniqueName('sampleRandom')) + self._sampleRandom() + + def delete(self): + self.removeTask(self._sampleTask) + self._sampleTask = None + DistributedObjectAI.delete(self) + return + + def _sampleRandomTask(self, task = None): + self._sampleRandom() + return Task.again + + def _sampleRandom(self): + self.air.sendUpdateToDoId('NonRepeatableRandomSource', 'randomSample', OtpDoGlobals.OTP_DO_ID_TOONTOWN_NON_REPEATABLE_RANDOM_SOURCE, [self.doId, int(random.randrange(1L << 32))]) + + def randomSampleAck(self): + self._sampleRandom() + + def getRandomSamples(self, callback, num = None): + if num is None: + num = 1 + context = self._contextGen.next() + self._requests[context] = (callback,) + self.air.sendUpdateToDoId('NonRepeatableRandomSource', 'getRandomSamples', OtpDoGlobals.OTP_DO_ID_TOONTOWN_NON_REPEATABLE_RANDOM_SOURCE, [self.doId, + 'NonRepeatableRandomSource', + context, + num]) + return + + def getRandomSamplesReply(self, context, samples): + callback, = self._requests.pop(context) + callback(samples) diff --git a/toontown/distributed/NonRepeatableRandomSourceUD.py b/toontown/distributed/NonRepeatableRandomSourceUD.py new file mode 100644 index 0000000..1dc148d --- /dev/null +++ b/toontown/distributed/NonRepeatableRandomSourceUD.py @@ -0,0 +1,55 @@ +from direct.distributed.DistributedObjectGlobalUD import DistributedObjectGlobalUD +from direct.directnotify.DirectNotifyGlobal import directNotify +import random + +class NonRepeatableRandomSourceUD(DistributedObjectGlobalUD): + notify = directNotify.newCategory('NonRepeatableRandomSourceUD') + RandomNumberCacheSize = 2000000 + + class Request(ScratchPad): + + def __init__(self, replyTo, replyToClass, context, num): + ScratchPad.__init__(self, replyTo=replyTo, replyToClass=replyToClass, context=context, num=num, randoms=[]) + + def __init__(self, air): + DistributedObjectGlobalUD.__init__(self, air) + + def announceGenerate(self): + DistributedObjectGlobalUD.announceGenerate(self) + self._randoms = [] + self._requests = [] + self._fakeIt = 0 + if __dev__: + NonRepeatableRandomSourceUD.RandomNumberCacheSize = config.GetInt('random-source-cache-size', 5000) + self._fakeIt = config.GetBool('fake-non-repeatable-random-source', self._fakeIt) + + def randomSample(self, nrrsDoId, random): + self._randoms = [random] + self._randoms + if len(self._randoms) > self.RandomNumberCacheSize: + self._randoms.pop() + self._processRequests() + self.air.sendUpdateToDoId('NonRepeatableRandomSource', 'randomSampleAck', nrrsDoId, []) + + def getRandomSamples(self, replyTo, replyToClass, context, num): + self._requests.append(self.Request(replyTo, replyToClass, context, num)) + self._processRequests() + + def _processRequests(self): + while len(self._requests): + request = self._requests[0] + if not self._fakeIt and len(request.randoms) < request.num: + if len(self._randoms) == 0: + break + needed = request.num - len(request.randoms) + if needed > 0: + numRandoms = min(needed, len(self._randoms)) + if self._fakeIt: + for i in xrange(numRandoms): + request.randoms.append(random.random() * 4294967295L) + + else: + request.randoms += self._randoms[:numRandoms] + self._randoms = self._randoms[numRandoms:] + if request.num == len(request.randoms): + self._requests.pop(0) + self.air.dispatchUpdateToDoId(request.replyToClass, 'getRandomSamplesReply', request.replyTo, [request.context, request.randoms]) diff --git a/toontown/distributed/PlayGame.py b/toontown/distributed/PlayGame.py new file mode 100644 index 0000000..9d3a6bf --- /dev/null +++ b/toontown/distributed/PlayGame.py @@ -0,0 +1,567 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task.Task import Task +from ToontownMsgTypes import * +from toontown.toonbase import ToontownGlobals +from toontown.hood import TTHood +from toontown.hood import DDHood +from toontown.hood import MMHood +from toontown.hood import BRHood +from toontown.hood import DGHood +from toontown.hood import DLHood +from toontown.hood import GSHood +from toontown.hood import OZHood +from toontown.hood import GZHood +from toontown.hood import SellbotHQ, CashbotHQ, LawbotHQ, BossbotHQ +from toontown.hood import TutorialHood +from direct.task import TaskManagerGlobal +from toontown.hood import QuietZoneState +from toontown.hood import ZoneUtil +from toontown.hood import EstateHood +from toontown.hood import PartyHood +from toontown.toonbase import TTLocalizer +from toontown.parties.PartyGlobals import GoToPartyStatus + +class PlayGame(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('PlayGame') + Hood2ClassDict = {ToontownGlobals.ToontownCentral: TTHood.TTHood, + ToontownGlobals.DonaldsDock: DDHood.DDHood, + ToontownGlobals.TheBrrrgh: BRHood.BRHood, + ToontownGlobals.MinniesMelodyland: MMHood.MMHood, + ToontownGlobals.DaisyGardens: DGHood.DGHood, + ToontownGlobals.DonaldsDreamland: DLHood.DLHood, + ToontownGlobals.GoofySpeedway: GSHood.GSHood, + ToontownGlobals.OutdoorZone: OZHood.OZHood, + ToontownGlobals.Tutorial: TutorialHood.TutorialHood, + ToontownGlobals.MyEstate: EstateHood.EstateHood, + ToontownGlobals.BossbotHQ: BossbotHQ.BossbotHQ, + ToontownGlobals.SellbotHQ: SellbotHQ.SellbotHQ, + ToontownGlobals.CashbotHQ: CashbotHQ.CashbotHQ, + ToontownGlobals.LawbotHQ: LawbotHQ.LawbotHQ, + ToontownGlobals.GolfZone: GZHood.GZHood, + ToontownGlobals.PartyHood: PartyHood.PartyHood} + Hood2StateDict = {ToontownGlobals.ToontownCentral: 'TTHood', + ToontownGlobals.DonaldsDock: 'DDHood', + ToontownGlobals.TheBrrrgh: 'BRHood', + ToontownGlobals.MinniesMelodyland: 'MMHood', + ToontownGlobals.DaisyGardens: 'DGHood', + ToontownGlobals.DonaldsDreamland: 'DLHood', + ToontownGlobals.GoofySpeedway: 'GSHood', + ToontownGlobals.OutdoorZone: 'OZHood', + ToontownGlobals.Tutorial: 'TutorialHood', + ToontownGlobals.MyEstate: 'EstateHood', + ToontownGlobals.BossbotHQ: 'BossbotHQ', + ToontownGlobals.SellbotHQ: 'SellbotHQ', + ToontownGlobals.CashbotHQ: 'CashbotHQ', + ToontownGlobals.LawbotHQ: 'LawbotHQ', + ToontownGlobals.GolfZone: 'GZHood', + ToontownGlobals.PartyHood: 'PartyHood'} + + def __init__(self, parentFSM, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.place = None + self.fsm = ClassicFSM.ClassicFSM('PlayGame', [State.State('start', self.enterStart, self.exitStart, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['TTHood', + 'DDHood', + 'BRHood', + 'MMHood', + 'DGHood', + 'DLHood', + 'GSHood', + 'OZHood', + 'GZHood', + 'SellbotHQ', + 'CashbotHQ', + 'LawbotHQ', + 'BossbotHQ', + 'TutorialHood', + 'EstateHood', + 'PartyHood']), + State.State('TTHood', self.enterTTHood, self.exitTTHood, ['quietZone']), + State.State('DDHood', self.enterDDHood, self.exitDDHood, ['quietZone']), + State.State('BRHood', self.enterBRHood, self.exitBRHood, ['quietZone']), + State.State('MMHood', self.enterMMHood, self.exitMMHood, ['quietZone']), + State.State('DGHood', self.enterDGHood, self.exitDGHood, ['quietZone']), + State.State('DLHood', self.enterDLHood, self.exitDLHood, ['quietZone']), + State.State('GSHood', self.enterGSHood, self.exitGSHood, ['quietZone']), + State.State('OZHood', self.enterOZHood, self.exitOZHood, ['quietZone']), + State.State('GZHood', self.enterGZHood, self.exitGZHood, ['quietZone']), + State.State('BossbotHQ', self.enterBossbotHQ, self.exitBossbotHQ, ['quietZone']), + State.State('SellbotHQ', self.enterSellbotHQ, self.exitSellbotHQ, ['quietZone']), + State.State('CashbotHQ', self.enterCashbotHQ, self.exitCashbotHQ, ['quietZone']), + State.State('LawbotHQ', self.enterLawbotHQ, self.exitLawbotHQ, ['quietZone']), + State.State('TutorialHood', self.enterTutorialHood, self.exitTutorialHood, ['quietZone']), + State.State('EstateHood', self.enterEstateHood, self.exitEstateHood, ['quietZone']), + State.State('PartyHood', self.enterPartyHood, self.exitPartyHood, ['quietZone'])], 'start', 'start') + self.fsm.enterInitialState() + self.parentFSM = parentFSM + self.parentFSM.getStateNamed('playGame').addChild(self.fsm) + self.hoodDoneEvent = 'hoodDone' + self.hood = None + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.quietZoneStateData = None + return + + def enter(self, hoodId, zoneId, avId): + if hoodId == ToontownGlobals.Tutorial: + loaderName = 'townLoader' + whereName = 'toonInterior' + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(avId, zoneId) + return + elif hoodId == ToontownGlobals.PartyHood: + self.getPartyZoneAndGoToParty(avId, zoneId) + return + else: + loaderName = ZoneUtil.getLoaderName(zoneId) + whereName = ZoneUtil.getToonWhereName(zoneId) + self.fsm.request('quietZone', [{'loader': loaderName, + 'where': whereName, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': avId}]) + return + + def exit(self): + if base.placeBeforeObjects and self.quietZoneStateData: + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + self.ignore(self.quietZoneDoneEvent) + return + + def load(self): + pass + + def loadDnaStoreTutorial(self): + self.dnaStore = DNAStorage() + loader.loadDNAFile(self.dnaStore, 'phase_3.5/dna/storage_tutorial.dna') + loader.loadDNAFile(self.dnaStore, 'phase_3.5/dna/storage_interior.dna') + + def loadDnaStore(self): + if not hasattr(self, 'dnaStore'): + self.dnaStore = DNAStorage() + loader.loadDNAFile(self.dnaStore, 'phase_4/dna/storage.dna') + self.dnaStore.storeFont('humanist', ToontownGlobals.getInterfaceFont()) + self.dnaStore.storeFont('mickey', ToontownGlobals.getSignFont()) + self.dnaStore.storeFont('suit', ToontownGlobals.getSuitFont()) + loader.loadDNAFile(self.dnaStore, 'phase_3.5/dna/storage_interior.dna') + + def unloadDnaStore(self): + if hasattr(self, 'dnaStore'): + self.dnaStore.resetNodes() + self.dnaStore.resetTextures() + del self.dnaStore + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def unload(self): + self.unloadDnaStore() + if self.hood: + self.notify.info('Aggressively cleaning up hood: %s' % self.hood) + self.hood.exit() + self.hood.unload() + self.hood = None + return + + def enterStart(self): + pass + + def exitStart(self): + pass + + def handleHoodDone(self): + doneStatus = self.hood.getDoneStatus() + shardId = doneStatus['shardId'] + if shardId != None: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + base.transitions.fadeOut(0) + return + if doneStatus['where'] == 'party': + self.getPartyZoneAndGoToParty(doneStatus['avId'], doneStatus['zoneId']) + return + how = doneStatus['how'] + if how in ['tunnelIn', + 'teleportIn', + 'doorIn', + 'elevatorIn']: + self.fsm.request('quietZone', [doneStatus]) + else: + self.notify.error('Exited hood with unexpected mode %s' % how) + return + + def _destroyHood(self): + self.ignore(self.hoodDoneEvent) + self.hood.exit() + self.hood.unload() + self.hood = None + base.cr.cache.flush() + return + + def enterQuietZone(self, requestStatus): + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self._quietZoneLeftEvent = self.quietZoneStateData.getQuietZoneLeftEvent() + if base.placeBeforeObjects: + self.acceptOnce(self._quietZoneLeftEvent, self.handleLeftQuietZone) + self._enterWaitForSetZoneResponseMsg = self.quietZoneStateData.getEnterWaitForSetZoneResponseMsg() + self.acceptOnce(self._enterWaitForSetZoneResponseMsg, self.handleWaitForSetZoneResponse) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self._quietZoneLeftEvent) + self.ignore(self._enterWaitForSetZoneResponseMsg) + if not base.placeBeforeObjects: + self.ignore(self.quietZoneDoneEvent) + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleWaitForSetZoneResponse(self, requestStatus): + hoodId = requestStatus['hoodId'] + canonicalHoodId = ZoneUtil.getCanonicalZoneId(hoodId) + toHoodPhrase = ToontownGlobals.hoodNameMap[canonicalHoodId][0] + hoodName = ToontownGlobals.hoodNameMap[canonicalHoodId][-1] + zoneId = requestStatus['zoneId'] + loaderName = requestStatus['loader'] + avId = requestStatus.get('avId', -1) + ownerId = requestStatus.get('ownerId', avId) + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: NEIGHBORHOODS: Visit %s' % hoodName) + count = ToontownGlobals.hoodCountMap[canonicalHoodId] + if loaderName == 'safeZoneLoader': + count += ToontownGlobals.safeZoneCountMap[canonicalHoodId] + elif loaderName == 'townLoader': + count += ToontownGlobals.townCountMap[canonicalHoodId] + if not loader.inBulkBlock: + if hoodId == ToontownGlobals.MyEstate: + if avId == -1: + loader.beginBulkLoad('hood', TTLocalizer.HeadingToYourEstate, count, 1, TTLocalizer.TIP_ESTATE) + else: + owner = base.cr.identifyAvatar(ownerId) + if owner == None: + friend = base.cr.identifyAvatar(avId) + if friend != None: + avName = friend.getName() + loader.beginBulkLoad('hood', TTLocalizer.HeadingToFriend % avName, count, 1, TTLocalizer.TIP_ESTATE) + else: + self.notify.warning("we can't perform this teleport") + return + else: + avName = owner.getName() + loader.beginBulkLoad('hood', TTLocalizer.HeadingToEstate % avName, count, 1, TTLocalizer.TIP_ESTATE) + elif ZoneUtil.isCogHQZone(zoneId): + loader.beginBulkLoad('hood', TTLocalizer.HeadingToHood % {'to': toHoodPhrase, + 'hood': hoodName}, count, 1, TTLocalizer.TIP_COGHQ) + elif ZoneUtil.isGoofySpeedwayZone(zoneId): + loader.beginBulkLoad('hood', TTLocalizer.HeadingToHood % {'to': toHoodPhrase, + 'hood': hoodName}, count, 1, TTLocalizer.TIP_KARTING) + else: + loader.beginBulkLoad('hood', TTLocalizer.HeadingToHood % {'to': toHoodPhrase, + 'hood': hoodName}, count, 1, TTLocalizer.TIP_GENERAL) + if hoodId == ToontownGlobals.Tutorial: + self.loadDnaStoreTutorial() + else: + self.loadDnaStore() + hoodClass = self.getHoodClassByNumber(canonicalHoodId) + self.hood = hoodClass(self.fsm, self.hoodDoneEvent, self.dnaStore, hoodId) + self.hood.load() + self.hood.loadLoader(requestStatus) + if not base.placeBeforeObjects: + loader.endBulkLoad('hood') + return + + def handleLeftQuietZone(self): + status = self.quietZoneStateData.getRequestStatus() + hoodId = ZoneUtil.getCanonicalZoneId(status['hoodId']) + hoodState = self.getHoodStateByNumber(hoodId) + self.fsm.request(hoodState, [status]) + + def handleQuietZoneDone(self): + if base.placeBeforeObjects: + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + loader.endBulkLoad('hood') + else: + self.handleLeftQuietZone() + return + + def enterTTHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitTTHood(self): + self._destroyHood() + + def enterDDHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitDDHood(self): + self._destroyHood() + + def enterMMHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitMMHood(self): + self._destroyHood() + + def enterBRHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitBRHood(self): + self._destroyHood() + + def enterDGHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitDGHood(self): + self._destroyHood() + + def enterDLHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitDLHood(self): + self._destroyHood() + + def enterGSHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitGSHood(self): + self._destroyHood() + + def enterOZHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitOZHood(self): + self._destroyHood() + + def enterGZHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitGZHood(self): + self._destroyHood() + + def enterSellbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitSellbotHQ(self): + self._destroyHood() + + def enterCashbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitCashbotHQ(self): + self._destroyHood() + + def enterLawbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitLawbotHQ(self): + self._destroyHood() + + def enterBossbotHQ(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitBossbotHQ(self): + self._destroyHood() + + def enterTutorialHood(self, requestStatus): + messenger.send('toonArrivedTutorial') + self.accept(self.hoodDoneEvent, self.handleHoodDone) + base.localAvatar.book.obscureButton(1) + base.localAvatar.book.setSafeMode(1) + base.localAvatar.laffMeter.obscure(1) + base.localAvatar.chatMgr.obscure(1, 1) + base.localAvatar.obscureFriendsListButton(1) + requestStatus['how'] = 'tutorial' + if base.config.GetString('language', 'english') == 'japanese': + musicVolume = base.config.GetFloat('tutorial-music-volume', 0.5) + requestStatus['musicVolume'] = musicVolume + self.hood.enter(requestStatus) + + def exitTutorialHood(self): + self.unloadDnaStore() + self._destroyHood() + base.localAvatar.book.obscureButton(0) + base.localAvatar.book.setSafeMode(0) + base.localAvatar.laffMeter.obscure(0) + base.localAvatar.chatMgr.obscure(0, 0) + base.localAvatar.obscureFriendsListButton(-1) + + def enterEstateHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + self.hood.enter(requestStatus) + + def exitEstateHood(self): + self._destroyHood() + + def getEstateZoneAndGoHome(self, avId, zoneId): + self.doneStatus = {'avId': avId, + 'zoneId': zoneId, + 'hoodId': ToontownGlobals.MyEstate, + 'loader': 'safeZoneLoader', + 'how': 'teleportIn', + 'shardId': None} + self.acceptOnce('setLocalEstateZone', self.goHome) + if avId > 0: + base.cr.estateMgr.getLocalEstateZone(avId) + else: + base.cr.estateMgr.getLocalEstateZone(base.localAvatar.getDoId()) + return + + def goHome(self, ownerId, zoneId): + self.notify.debug('goHome ownerId = %s' % ownerId) + if ownerId > 0 and ownerId != base.localAvatar.doId and not base.cr.isFriend(ownerId): + self.doneStatus['failed'] = 1 + taskMgr.remove('goHomeFailed') + taskMgr.add(self.goHomeFailed, 'goHomeFailed') + return + if ownerId == 0 and zoneId == 0: + self.doneStatus['failed'] = 1 + self.goHomeFailed(None) + return + if self.doneStatus['zoneId'] != zoneId: + self.doneStatus['where'] = 'house' + else: + self.doneStatus['where'] = 'estate' + self.doneStatus['ownerId'] = ownerId + self.fsm.request('quietZone', [self.doneStatus]) + return + + def goHomeFailed(self, task): + self.notify.debug('goHomeFailed') + failedToVisitAvId = self.doneStatus.get('avId') + if failedToVisitAvId > 0: + message = TTLocalizer.EstateTeleportFailedNotFriends % base.cr.identifyAvatar(failedToVisitAvId).getName() + else: + message = TTLocalizer.EstateTeleportFailed + self.notify.debug('goHomeFailed, why =: %s' % message) + self.ignore('setLocalEstateZone') + zoneId = base.localAvatar.lastHood + loaderName = ZoneUtil.getLoaderName(zoneId) + whereName = ZoneUtil.getToonWhereName(zoneId) + base.localAvatar.setSystemMessage(0, message) + self.fsm.request('quietZone', [{'loader': loaderName, + 'where': whereName, + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None}]) + return Task.done + + def enterPartyHood(self, requestStatus): + self.accept(self.hoodDoneEvent, self.handleHoodDone) + requestStatus['where'] = 'party' + self.hood.enter(requestStatus) + + def exitPartyHood(self): + self._destroyHood() + + def getPartyZoneAndGoToParty(self, avId, zoneId): + self.doneStatus = {'avId': avId, + 'zoneId': zoneId, + 'hoodId': ToontownGlobals.PartyHood, + 'loader': 'safeZoneLoader', + 'how': 'teleportIn', + 'shardId': None} + if avId < 0: + avId = base.localAvatar.getDoId() + base.cr.partyManager.requestPartyZone(avId, zoneId, callback=self.goToParty) + return + + def goToParty(self, ownerId, partyId, zoneId): + if ownerId == 0 and partyId == 0 and zoneId == 0: + self.doneStatus['where'] = 'playground' + else: + self.doneStatus['where'] = 'party' + self.doneStatus['ownerId'] = ownerId + self.doneStatus['partyId'] = partyId + self.doneStatus['zoneId'] = zoneId + self.fsm.request('quietZone', [self.doneStatus]) + + def goToPartyFailed(self, reason): + self.notify.debug('goToPartyFailed') + failedToVisitAvId = self.doneStatus.get('avId') + message = base.cr.partyManager.getGoToPartyFailedMessage(reason) + self.notify.debug('goToPartyFailed, why =: %s' % message) + self.ignore('gotLocalPartyZone') + zoneId = base.localAvatar.lastHood + loaderName = ZoneUtil.getLoaderName(zoneId) + whereName = ZoneUtil.getToonWhereName(zoneId) + base.localAvatar.setSystemMessage(0, message) + self.fsm.request('quietZone', [{'loader': loaderName, + 'where': whereName, + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None}]) + return Task.done + + def getCatalogCodes(self, category): + numCodes = self.dnaStore.getNumCatalogCodes(category) + codes = [] + for i in range(numCodes): + codes.append(self.dnaStore.getCatalogCode(category, i)) + + return codes + + def getNodePathList(self, catalogGroup): + result = [] + codes = self.getCatalogCodes(catalogGroup) + for code in codes: + np = self.dnaStore.findNode(code) + result.append(np) + + return result + + def getNodePathDict(self, catalogGroup): + result = {} + codes = self.getCatalogCodes(catalogGroup) + for code in codes: + np = self.dnaStore.findNode(code) + result[code] = np + + return result + + def getHoodClassByNumber(self, hoodNumber): + return self.Hood2ClassDict[hoodNumber] + + def getHoodStateByNumber(self, hoodNumber): + return self.Hood2StateDict[hoodNumber] + + def setPlace(self, place): + self.place = place + if self.place: + messenger.send('playGameSetPlace') + + def getPlace(self): + return self.place + + def getPlaceId(self): + if self.hood: + return self.hood.hoodId + else: + return None + return None diff --git a/toontown/distributed/ToontownClientRepository.py b/toontown/distributed/ToontownClientRepository.py new file mode 100644 index 0000000..cc6b011 --- /dev/null +++ b/toontown/distributed/ToontownClientRepository.py @@ -0,0 +1,1142 @@ +import types +import time +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import ivalMgr +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNode +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +from direct.task import Task +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.showbase.PythonUtil import Functor, ScratchPad +from direct.showbase.InputStateGlobal import inputState +from otp.avatar import Avatar +from otp.avatar import DistributedAvatar +from otp.friends import FriendManager +from otp.login import TTAccount +from otp.login import AccountServerConstants +from otp.login import LoginScreen +from otp.login import LoginGSAccount +from otp.login import LoginGoAccount +from otp.login import LoginWebPlayTokenAccount +from otp.login import LoginTTAccount +from otp.login import HTTPUtil +from otp.distributed import OTPClientRepository +from otp.distributed import PotentialAvatar +from otp.distributed import PotentialShard +from otp.distributed import DistributedDistrict +from otp.distributed.OtpDoGlobals import * +from otp.distributed import OtpDoGlobals +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from otp.otpbase import OTPLauncherGlobals +from otp.avatar.Avatar import teleportNotify +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.launcher.DownloadForceAcknowledge import * +from toontown.distributed import DelayDelete +from toontown.friends import FriendHandle +from toontown.friends import FriendsListPanel +from toontown.friends import ToontownFriendSecret +from toontown.uberdog import TTSpeedchatRelay +from toontown.login import DateObject +from toontown.login import AccountServerDate +from toontown.login import AvatarChooser +from toontown.makeatoon import MakeAToon +from toontown.pets import DistributedPet, PetDetail, PetHandle +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toon import LocalToon +from toontown.toon import ToonDNA +from toontown.distributed import ToontownDistrictStats +from toontown.makeatoon import TTPickANamePattern +from toontown.parties import ToontownTimeManager +from toontown.toon import Toon, DistributedToon +from ToontownMsgTypes import * +import HoodMgr +import PlayGame +from toontown.toontowngui import ToontownLoadingBlocker +from toontown.hood import StreetSign + +class ToontownClientRepository(OTPClientRepository.OTPClientRepository): + SupportTutorial = 1 + GameGlobalsId = OTP_DO_ID_TOONTOWN + SetZoneDoneEvent = 'TCRSetZoneDone' + EmuSetZoneDoneEvent = 'TCREmuSetZoneDone' + SetInterest = 'Set' + ClearInterest = 'Clear' + ClearInterestDoneEvent = 'TCRClearInterestDone' + KeepSubShardObjects = False + + def __init__(self, serverVersion, launcher = None): + OTPClientRepository.OTPClientRepository.__init__(self, serverVersion, launcher, playGame=PlayGame.PlayGame) + self._playerAvDclass = self.dclassesByName['DistributedToon'] + setInterfaceFont(TTLocalizer.InterfaceFont) + setSignFont(TTLocalizer.SignFont) + setFancyFont(TTLocalizer.FancyFont) + nameTagFontIndex = 0 + for font in TTLocalizer.NametagFonts: + setNametagFont(nameTagFontIndex, TTLocalizer.NametagFonts[nameTagFontIndex]) + nameTagFontIndex += 1 + + self.toons = {} + if self.http.getVerifySsl() != HTTPClient.VSNoVerify: + self.http.setVerifySsl(HTTPClient.VSNoDateCheck) + prepareAvatar(self.http) + self.__forbidCheesyEffects = 0 + self.friendManager = None + self.speedchatRelay = None + self.trophyManager = None + self.bankManager = None + self.catalogManager = None + self.welcomeValleyManager = None + self.newsManager = None + self.streetSign = None + self.distributedDistrict = None + self.partyManager = None + self.inGameNewsMgr = None + self.whitelistMgr = None + self.toontownTimeManager = ToontownTimeManager.ToontownTimeManager() + self.avatarFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_AVATAR_FRIENDS_MANAGER, 'AvatarFriendsManager') + self.playerFriendsManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_PLAYER_FRIENDS_MANAGER, 'TTPlayerFriendsManager') + self.speedchatRelay = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_SPEEDCHAT_RELAY, 'TTSpeedchatRelay') + self.deliveryManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_DELIVERY_MANAGER, 'DistributedDeliveryManager') + if config.GetBool('want-code-redemption', 1): + self.codeRedemptionManager = self.generateGlobalObject(OtpDoGlobals.OTP_DO_ID_TOONTOWN_CODE_REDEMPTION_MANAGER, 'TTCodeRedemptionMgr') + self.streetSign = None + self.furnitureManager = None + self.objectManager = None + self.friendsMap = {} + self.friendsOnline = {} + self.friendsMapPending = 0 + self.friendsListError = 0 + self.friendPendingChatSettings = {} + self.elderFriendsMap = {} + self.__queryAvatarMap = {} + self.dateObject = DateObject.DateObject() + self.accountServerDate = AccountServerDate.AccountServerDate() + self.hoodMgr = HoodMgr.HoodMgr(self) + self.setZonesEmulated = 0 + self.old_setzone_interest_handle = None + self.setZoneQueue = Queue() + self.accept(ToontownClientRepository.SetZoneDoneEvent, self._handleEmuSetZoneDone) + self._deletedSubShardDoIds = set() + self.toonNameDict = {} + self.gameFSM.addState(State.State('skipTutorialRequest', self.enterSkipTutorialRequest, self.exitSkipTutorialRequest, ['playGame', 'gameOff', 'tutorialQuestion'])) + state = self.gameFSM.getStateNamed('waitOnEnterResponses') + state.addTransition('skipTutorialRequest') + state = self.gameFSM.getStateNamed('playGame') + state.addTransition('skipTutorialRequest') + self.wantCogdominiums = base.config.GetBool('want-cogdominiums', 1) + self.wantEmblems = base.config.GetBool('want-emblems', 0) + if base.config.GetBool('tt-node-check', 0): + for species in ToonDNA.toonSpeciesTypes: + for head in ToonDNA.getHeadList(species): + for torso in ToonDNA.toonTorsoTypes: + for legs in ToonDNA.toonLegTypes: + for gender in ('m', 'f'): + print 'species: %s, head: %s, torso: %s, legs: %s, gender: %s' % (species, + head, + torso, + legs, + gender) + dna = ToonDNA.ToonDNA() + dna.newToon((head, + torso, + legs, + gender)) + toon = Toon.Toon() + try: + toon.setDNA(dna) + except Exception, e: + print e + + return + + def congratulations(self, avatarChoice): + self.acceptedScreen = loader.loadModel('phase_3/models/gui/toon_council') + self.acceptedScreen.setScale(0.667) + self.acceptedScreen.reparentTo(aspect2d) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.acceptedBanner = DirectLabel(parent=self.acceptedScreen, relief=None, text=OTPLocalizer.CRNameCongratulations, text_scale=0.18, text_fg=Vec4(0.6, 0.1, 0.1, 1), text_pos=(0, 0.05), text_font=getMinnieFont()) + newName = avatarChoice.approvedName + self.acceptedText = DirectLabel(parent=self.acceptedScreen, relief=None, text=OTPLocalizer.CRNameAccepted % newName, text_scale=0.125, text_fg=Vec4(0, 0, 0, 1), text_pos=(0, -0.15)) + self.okButton = DirectButton(parent=self.acceptedScreen, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text='Ok', scale=1.5, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0, 0, -1), command=self.__handleCongrats, extraArgs=[avatarChoice]) + buttons.removeNode() + base.transitions.noFade() + return + + def __handleCongrats(self, avatarChoice): + self.acceptedBanner.destroy() + self.acceptedText.destroy() + self.okButton.destroy() + self.acceptedScreen.removeNode() + del self.acceptedScreen + del self.okButton + del self.acceptedText + del self.acceptedBanner + datagram = PyDatagram() + datagram.addUint16(CLIENT_SET_WISHNAME_CLEAR) + datagram.addUint32(avatarChoice.id) + datagram.addUint8(1) + self.send(datagram) + self.loginFSM.request('waitForSetAvatarResponse', [avatarChoice]) + + def betterlucknexttime(self, avList, index): + self.rejectDoneEvent = 'rejectDone' + self.rejectDialog = TTDialog.TTGlobalDialog(doneEvent=self.rejectDoneEvent, message=TTLocalizer.NameShopNameRejected, style=TTDialog.Acknowledge) + self.rejectDialog.show() + self.acceptOnce(self.rejectDoneEvent, self.__handleReject, [avList, index]) + base.transitions.noFade() + + def __handleReject(self, avList, index): + self.rejectDialog.cleanup() + datagram = PyDatagram() + datagram.addUint16(CLIENT_SET_WISHNAME_CLEAR) + avid = 0 + for k in avList: + if k.position == index: + avid = k.id + + if avid == 0: + self.notify.error('Avatar rejected not found in avList. Index is: ' + str(index)) + datagram.addUint32(avid) + datagram.addUint8(0) + self.send(datagram) + self.loginFSM.request('waitForAvatarList') + + def enterChooseAvatar(self, avList): + ModelPool.garbageCollect() + TexturePool.garbageCollect() + self.sendSetAvatarIdMsg(0) + self.clearFriendState() + if self.music == None and base.musicManagerIsValid: + self.music = base.musicManager.getSound('phase_3/audio/bgm/tt_theme.mid') + if self.music: + self.music.setLoop(1) + self.music.setVolume(0.9) + self.music.play() + base.playMusic(self.music, looping=1, volume=0.9, interrupt=None) + self.handler = self.handleMessageType + self.avChoiceDoneEvent = 'avatarChooserDone' + self.avChoice = AvatarChooser.AvatarChooser(avList, self.loginFSM, self.avChoiceDoneEvent) + self.avChoice.load(self.isPaid()) + self.avChoice.enter() + self.accept(self.avChoiceDoneEvent, self.__handleAvatarChooserDone, [avList]) + if config.GetBool('want-gib-loader', 1): + self.loadingBlocker = ToontownLoadingBlocker.ToontownLoadingBlocker(avList) + return + + def __handleAvatarChooserDone(self, avList, doneStatus): + done = doneStatus['mode'] + if done == 'exit': + if not launcher.isDummy() and launcher.VISTA: + if not self.isPaid(): + self.loginFSM.request('shutdown', [OTPLauncherGlobals.ExitUpsell]) + else: + self.loginFSM.request('shutdown') + else: + self.loginFSM.request('shutdown') + return + index = self.avChoice.getChoice() + for av in avList: + if av.position == index: + avatarChoice = av + self.notify.info('================') + self.notify.info('Chose avatar id: %s' % av.id) + self.notify.info('Chose avatar name: %s' % av.name) + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(av.dna) + if base.logPrivateInfo: + self.notify.info('Chose avatar dna: %s' % (dna.asTuple(),)) + self.notify.info('Chose avatar position: %s' % av.position) + self.notify.info('isPaid: %s' % self.isPaid()) + self.notify.info('freeTimeLeft: %s' % self.freeTimeLeft()) + self.notify.info('allowSecretChat: %s' % self.allowSecretChat()) + self.notify.info('================') + + if done == 'chose': + self.avChoice.exit() + if avatarChoice.approvedName != '': + self.congratulations(avatarChoice) + avatarChoice.approvedName = '' + elif avatarChoice.rejectedName != '': + avatarChoice.rejectedName = '' + self.betterlucknexttime(avList, index) + else: + self.loginFSM.request('waitForSetAvatarResponse', [avatarChoice]) + elif done == 'nameIt': + self.accept('downloadAck-response', self.__handleDownloadAck, [avList, index]) + self.downloadAck = DownloadForceAcknowledge('downloadAck-response') + self.downloadAck.enter(4) + elif done == 'create': + self.loginFSM.request('createAvatar', [avList, index]) + elif done == 'delete': + self.loginFSM.request('waitForDeleteAvatarResponse', [avatarChoice]) + + def __handleDownloadAck(self, avList, index, doneStatus): + if doneStatus['mode'] == 'complete': + self.goToPickAName(avList, index) + else: + self.loginFSM.request('chooseAvatar', [avList]) + self.downloadAck.exit() + self.downloadAck = None + self.ignore('downloadAck-response') + return + + def exitChooseAvatar(self): + self.handler = None + self.avChoice.exit() + self.avChoice.unload() + self.avChoice = None + self.ignore(self.avChoiceDoneEvent) + return + + def goToPickAName(self, avList, index): + self.avChoice.exit() + self.loginFSM.request('createAvatar', [avList, index]) + + def enterCreateAvatar(self, avList, index, newDNA = None): + if self.music: + self.music.stop() + self.music = None + if newDNA != None: + self.newPotAv = PotentialAvatar.PotentialAvatar('deleteMe', ['', + '', + '', + ''], newDNA.makeNetString(), index, 1) + avList.append(self.newPotAv) + base.transitions.noFade() + self.avCreate = MakeAToon.MakeAToon(self.loginFSM, avList, 'makeAToonComplete', index, self.isPaid()) + self.avCreate.load() + self.avCreate.enter() + self.handler = self.handleCreateAvatar + self.accept('makeAToonComplete', self.__handleMakeAToon, [avList, index]) + self.accept('nameShopCreateAvatar', self.sendCreateAvatarMsg) + self.accept('nameShopPost', self.relayMessage) + return + + def relayMessage(self, dg): + self.send(dg) + + def handleCreateAvatar(self, msgType, di): + if msgType == CLIENT_CREATE_AVATAR_RESP or msgType == CLIENT_SET_NAME_PATTERN_ANSWER or msgType == CLIENT_SET_WISHNAME_RESP: + self.avCreate.ns.nameShopHandler(msgType, di) + else: + self.handleMessageType(msgType, di) + + def __handleMakeAToon(self, avList, avPosition): + done = self.avCreate.getDoneStatus() + if done == 'cancel': + if hasattr(self, 'newPotAv'): + if self.newPotAv in avList: + avList.remove(self.newPotAv) + self.avCreate.exit() + self.loginFSM.request('chooseAvatar', [avList]) + elif done == 'created': + self.avCreate.exit() + if not base.launcher or base.launcher.getPhaseComplete(3.5): + for i in avList: + if i.position == avPosition: + newPotAv = i + + self.loginFSM.request('waitForSetAvatarResponse', [newPotAv]) + else: + self.loginFSM.request('chooseAvatar', [avList]) + else: + self.notify.error('Invalid doneStatus from MakeAToon: ' + str(done)) + + def exitCreateAvatar(self): + self.ignore('makeAToonComplete') + self.ignore('nameShopPost') + self.ignore('nameShopCreateAvatar') + self.avCreate.unload() + self.avCreate = None + self.handler = None + if hasattr(self, 'newPotAv'): + del self.newPotAv + return + + def handleAvatarResponseMsg(self, di): + self.cleanupWaitingForDatabase() + avatarId = di.getUint32() + returnCode = di.getUint8() + if returnCode == 0: + dclass = self.dclassesByName['DistributedToon'] + NametagGlobals.setMasterArrowsOn(0) + loader.beginBulkLoad('localAvatarPlayGame', OTPLocalizer.CREnteringToontown, 400, 1, TTLocalizer.TIP_GENERAL) + localAvatar = LocalToon.LocalToon(self) + localAvatar.dclass = dclass + base.localAvatar = localAvatar + __builtins__['localAvatar'] = base.localAvatar + NametagGlobals.setToon(base.localAvatar) + localAvatar.doId = avatarId + self.localAvatarDoId = avatarId + parentId = None + zoneId = None + localAvatar.setLocation(parentId, zoneId) + localAvatar.generateInit() + localAvatar.generate() + localAvatar.updateAllRequiredFields(dclass, di) + self.doId2do[avatarId] = localAvatar + localAvatar.initInterface() + self.sendGetFriendsListRequest() + self.loginFSM.request('playingGame') + else: + self.notify.error('Bad avatar: return code %d' % returnCode) + return + + def getAvatarDetails(self, avatar, func, *args): + pad = ScratchPad() + pad.func = func + pad.args = args + pad.avatar = avatar + pad.delayDelete = DelayDelete.DelayDelete(avatar, 'getAvatarDetails') + avId = avatar.doId + self.__queryAvatarMap[avId] = pad + self.__sendGetAvatarDetails(avId) + + def cancelAvatarDetailsRequest(self, avatar): + avId = avatar.doId + if self.__queryAvatarMap.has_key(avId): + pad = self.__queryAvatarMap.pop(avId) + pad.delayDelete.destroy() + + def __sendGetAvatarDetails(self, avId): + datagram = PyDatagram() + avatar = self.__queryAvatarMap[avId].avatar + datagram.addUint16(avatar.getRequestID()) + datagram.addUint32(avId) + self.send(datagram) + + def handleGetAvatarDetailsResp(self, di): + avId = di.getUint32() + returnCode = di.getUint8() + self.notify.info('Got query response for avatar %d, code = %d.' % (avId, returnCode)) + try: + pad = self.__queryAvatarMap[avId] + except: + self.notify.warning('Received unexpected or outdated details for avatar %d.' % avId) + return + + del self.__queryAvatarMap[avId] + gotData = 0 + if returnCode != 0: + self.notify.warning('No information available for avatar %d.' % avId) + else: + dclassName = pad.args[0] + dclass = self.dclassesByName[dclassName] + pad.avatar.updateAllRequiredFields(dclass, di) + gotData = 1 + if isinstance(pad.func, types.StringType): + messenger.send(pad.func, list((gotData, pad.avatar) + pad.args)) + else: + apply(pad.func, (gotData, pad.avatar) + pad.args) + pad.delayDelete.destroy() + + def enterPlayingGame(self, *args, **kArgs): + OTPClientRepository.OTPClientRepository.enterPlayingGame(self, *args, **kArgs) + self.gameFSM.request('waitOnEnterResponses', [None, + base.localAvatar.defaultZone, + base.localAvatar.defaultZone, + -1]) + self._userLoggingOut = False + if not self.streetSign: + self.streetSign = StreetSign.StreetSign() + return + + def exitPlayingGame(self): + ivalMgr.interrupt() + if self.objectManager != None: + self.objectManager.destroy() + self.objectManager = None + ToontownFriendSecret.unloadFriendSecret() + FriendsListPanel.unloadFriendsList() + messenger.send('cancelFriendInvitation') + base.removeGlitchMessage() + taskMgr.remove('avatarRequestQueueTask') + OTPClientRepository.OTPClientRepository.exitPlayingGame(self) + if hasattr(base, 'localAvatar'): + camera.reparentTo(render) + camera.setPos(0, 0, 0) + camera.setHpr(0, 0, 0) + del self.doId2do[base.localAvatar.getDoId()] + if base.localAvatar.getDelayDeleteCount() != 0: + self.notify.error('could not delete localAvatar, delayDeletes=%s' % (base.localAvatar.getDelayDeleteNames(),)) + base.localAvatar.deleteOrDelay() + base.localAvatar.detectLeaks() + NametagGlobals.setToon(base.cam) + del base.localAvatar + del __builtins__['localAvatar'] + loader.abortBulkLoad() + base.transitions.noTransitions() + if self._userLoggingOut: + self.detectLeaks(okTasks=[], okEvents=['destroy-ToontownLoadingScreenTitle', 'destroy-ToontownLoadingScreenTip', 'destroy-ToontownLoadingScreenWaitBar']) + return + + def enterGameOff(self): + OTPClientRepository.OTPClientRepository.enterGameOff(self) + + def enterWaitOnEnterResponses(self, shardId, hoodId, zoneId, avId): + self.resetDeletedSubShardDoIds() + OTPClientRepository.OTPClientRepository.enterWaitOnEnterResponses(self, shardId, hoodId, zoneId, avId) + + def enterSkipTutorialRequest(self, hoodId, zoneId, avId): + self.handlerArgs = {'hoodId': hoodId, + 'zoneId': zoneId, + 'avId': avId} + self.handler = self.handleTutorialQuestion + self.__requestSkipTutorial(hoodId, zoneId, avId) + + def __requestSkipTutorial(self, hoodId, zoneId, avId): + self.notify.debug('requesting skip tutorial') + self.acceptOnce('skipTutorialAnswered', self.__handleSkipTutorialAnswered, [hoodId, zoneId, avId]) + messenger.send('requestSkipTutorial') + self.waitForDatabaseTimeout(requestName='RequestSkipTutorial') + + def __handleSkipTutorialAnswered(self, hoodId, zoneId, avId, allOk): + if allOk: + hoodId = self.handlerArgs['hoodId'] + zoneId = self.handlerArgs['zoneId'] + avId = self.handlerArgs['avId'] + self.gameFSM.request('playGame', [hoodId, zoneId, avId]) + else: + self.notify.warning('allOk is false on skip tutorial, forcing the tutorial.') + self.gameFSM.request('tutorialQuestion', [hoodId, zoneId, avId]) + + def exitSkipTutorialRequest(self): + self.cleanupWaitingForDatabase() + self.handler = None + self.handlerArgs = None + self.ignore('skipTutorialAnswered') + return + + def enterTutorialQuestion(self, hoodId, zoneId, avId): + self.handler = self.handleTutorialQuestion + self.__requestTutorial(hoodId, zoneId, avId) + + def handleTutorialQuestion(self, msgType, di): + 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) + else: + self.handleMessageType(msgType, di) + + def __requestTutorial(self, hoodId, zoneId, avId): + self.notify.debug('requesting tutorial') + self.acceptOnce('startTutorial', self.__handleStartTutorial, [avId]) + messenger.send('requestTutorial') + self.waitForDatabaseTimeout(requestName='RequestTutorial') + + def __handleStartTutorial(self, avId, zoneId): + self.gameFSM.request('playGame', [Tutorial, zoneId, avId]) + + def exitTutorialQuestion(self): + self.cleanupWaitingForDatabase() + self.handler = None + self.handlerArgs = None + self.ignore('startTutorial') + taskMgr.remove('waitingForTutorial') + return + + def enterSwitchShards(self, shardId, hoodId, zoneId, avId): + OTPClientRepository.OTPClientRepository.enterSwitchShards(self, shardId, hoodId, zoneId, avId) + self.handler = self.handleCloseShard + + def exitSwitchShards(self): + OTPClientRepository.OTPClientRepository.exitSwitchShards(self) + self.ignore(ToontownClientRepository.ClearInterestDoneEvent) + self.handler = None + return + + def enterCloseShard(self, loginState = None): + OTPClientRepository.OTPClientRepository.enterCloseShard(self, loginState) + self.handler = self.handleCloseShard + self._removeLocalAvFromStateServer() + + def handleCloseShard(self, msgType, di): + if msgType == CLIENT_CREATE_OBJECT_REQUIRED: + di2 = PyDatagramIterator(di) + parentId = di2.getUint32() + if self._doIdIsOnCurrentShard(parentId): + return + elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER: + di2 = PyDatagramIterator(di) + parentId = di2.getUint32() + if self._doIdIsOnCurrentShard(parentId): + return + elif msgType == CLIENT_OBJECT_UPDATE_FIELD: + di2 = PyDatagramIterator(di) + doId = di2.getUint32() + if self._doIdIsOnCurrentShard(doId): + return + self.handleMessageType(msgType, di) + + def _logFailedDisable(self, doId, ownerView): + if doId not in self.doId2do and doId in self._deletedSubShardDoIds: + return + OTPClientRepository.OTPClientRepository._logFailedDisable(self, doId, ownerView) + + def exitCloseShard(self): + OTPClientRepository.OTPClientRepository.exitCloseShard(self) + self.ignore(ToontownClientRepository.ClearInterestDoneEvent) + self.handler = None + return + + def isShardInterestOpen(self): + return self.old_setzone_interest_handle is not None or self.uberZoneInterest is not None + + def resetDeletedSubShardDoIds(self): + self._deletedSubShardDoIds.clear() + + def dumpAllSubShardObjects(self): + if self.KeepSubShardObjects: + return + isNotLive = not base.cr.isLive() + if isNotLive: + try: + localAvatar + except: + self.notify.info('dumpAllSubShardObjects') + else: + self.notify.info('dumpAllSubShardObjects: defaultShard is %s' % localAvatar.defaultShard) + + ignoredClasses = ('MagicWordManager', 'TimeManager', 'DistributedDistrict', 'FriendManager', 'NewsManager', 'ToontownMagicWordManager', 'WelcomeValleyManager', 'DistributedTrophyMgr', 'CatalogManager', 'DistributedBankMgr', 'EstateManager', 'RaceManager', 'SafeZoneManager', 'DeleteManager', 'TutorialManager', 'ToontownDistrict', 'DistributedDeliveryManager', 'DistributedPartyManager', 'AvatarFriendsManager', 'InGameNewsMgr', 'WhitelistMgr', 'TTCodeRedemptionMgr') + messenger.send('clientCleanup') + for avId, pad in self.__queryAvatarMap.items(): + pad.delayDelete.destroy() + + self.__queryAvatarMap = {} + delayDeleted = [] + doIds = self.doId2do.keys() + for doId in doIds: + obj = self.doId2do[doId] + if isNotLive: + ignoredClass = obj.__class__.__name__ in ignoredClasses + if not ignoredClass and obj.parentId != localAvatar.defaultShard: + self.notify.info('dumpAllSubShardObjects: %s %s parent %s is not defaultShard' % (obj.__class__.__name__, obj.doId, obj.parentId)) + if obj.parentId == localAvatar.defaultShard and obj is not localAvatar: + if obj.neverDisable: + if isNotLive: + if not ignoredClass: + self.notify.warning('dumpAllSubShardObjects: neverDisable set for %s %s' % (obj.__class__.__name__, obj.doId)) + else: + self.deleteObject(doId) + self._deletedSubShardDoIds.add(doId) + if obj.getDelayDeleteCount() != 0: + delayDeleted.append(obj) + + delayDeleteLeaks = [] + for obj in delayDeleted: + if obj.getDelayDeleteCount() != 0: + delayDeleteLeaks.append(obj) + + if len(delayDeleteLeaks): + s = 'dumpAllSubShardObjects:' + for obj in delayDeleteLeaks: + s += '\n could not delete %s (%s), delayDeletes=%s' % (safeRepr(obj), itype(obj), obj.getDelayDeleteNames()) + + self.notify.error(s) + if isNotLive: + self.notify.info('dumpAllSubShardObjects: doIds left: %s' % self.doId2do.keys()) + + def _removeCurrentShardInterest(self, callback): + if self.old_setzone_interest_handle is None: + self.notify.warning('removeToontownShardInterest: no shard interest open') + callback() + return + self.acceptOnce(ToontownClientRepository.ClearInterestDoneEvent, Functor(self._tcrRemoveUberZoneInterest, callback)) + self._removeEmulatedSetZone(ToontownClientRepository.ClearInterestDoneEvent) + return + + def _tcrRemoveUberZoneInterest(self, callback): + self.acceptOnce(ToontownClientRepository.ClearInterestDoneEvent, Functor(self._tcrRemoveShardInterestDone, callback)) + self.removeInterest(self.uberZoneInterest, ToontownClientRepository.ClearInterestDoneEvent) + + def _tcrRemoveShardInterestDone(self, callback): + self.uberZoneInterest = None + callback() + return + + def _doIdIsOnCurrentShard(self, doId): + if doId == base.localAvatar.defaultShard: + return True + do = self.getDo(doId) + if do: + if do.parentId == base.localAvatar.defaultShard: + return True + return False + + def _wantShardListComplete(self): + print self.activeDistrictMap + if self._shardsAreReady(): + self.acceptOnce(ToontownDistrictStats.EventName(), self.shardDetailStatsComplete) + ToontownDistrictStats.refresh() + else: + self.loginFSM.request('noShards') + + def shardDetailStatsComplete(self): + self.loginFSM.request('waitForAvatarList') + + def exitWaitForShardList(self): + self.ignore(ToontownDistrictStats.EventName()) + OTPClientRepository.OTPClientRepository.exitWaitForShardList(self) + + def fillUpFriendsMap(self): + if self.isFriendsMapComplete(): + return 1 + if not self.friendsMapPending and not self.friendsListError: + self.notify.warning('Friends list stale; fetching new list.') + self.sendGetFriendsListRequest() + return 0 + + def isFriend(self, doId): + for friendId, flags in base.localAvatar.friendsList: + if friendId == doId: + self.identifyFriend(doId) + return 1 + + return 0 + + def isAvatarFriend(self, doId): + for friendId, flags in base.localAvatar.friendsList: + if friendId == doId: + self.identifyFriend(doId) + return 1 + + return 0 + + def getFriendFlags(self, doId): + for friendId, flags in base.localAvatar.friendsList: + if friendId == doId: + return flags + + return 0 + + def isFriendOnline(self, doId): + return self.friendsOnline.has_key(doId) + + def addAvatarToFriendsList(self, avatar): + self.friendsMap[avatar.doId] = avatar + + def identifyFriend(self, doId, source = None): + if self.friendsMap.has_key(doId): + teleportNotify.debug('friend %s in friendsMap' % doId) + return self.friendsMap[doId] + avatar = None + if self.doId2do.has_key(doId): + teleportNotify.debug('found friend %s in doId2do' % doId) + avatar = self.doId2do[doId] + elif self.cache.contains(doId): + teleportNotify.debug('found friend %s in cache' % doId) + avatar = self.cache.dict[doId] + elif self.playerFriendsManager.getAvHandleFromId(doId): + teleportNotify.debug('found friend %s in playerFriendsManager' % doId) + avatar = base.cr.playerFriendsManager.getAvHandleFromId(doId) + else: + self.notify.warning("Don't know who friend %s is." % doId) + return + if not ((isinstance(avatar, DistributedToon.DistributedToon) and avatar.__class__ is DistributedToon.DistributedToon) or isinstance(avatar, DistributedPet.DistributedPet)): + self.notify.warning('friendsNotify%s: invalid friend object %s' % (choice(source, '(%s)' % source, ''), doId)) + return + if base.wantPets: + if avatar.isPet(): + if avatar.bFake: + handle = PetHandle.PetHandle(avatar) + else: + handle = avatar + else: + handle = FriendHandle.FriendHandle(doId, avatar.getName(), avatar.style, avatar.getPetId()) + else: + handle = FriendHandle.FriendHandle(doId, avatar.getName(), avatar.style, '') + teleportNotify.debug('adding %s to friendsMap' % doId) + self.friendsMap[doId] = handle + return handle + + def identifyPlayer(self, pId): + return base.cr.playerFriendsManager.getFriendInfo(pId) + + def identifyAvatar(self, doId): + if self.doId2do.has_key(doId): + return self.doId2do[doId] + else: + return self.identifyFriend(doId) + + def isFriendsMapComplete(self): + for friendId, flags in base.localAvatar.friendsList: + if self.identifyFriend(friendId) == None: + return 0 + + if base.wantPets and base.localAvatar.hasPet(): + print str(self.friendsMap) + print str(self.friendsMap.has_key(base.localAvatar.getPetId())) + if self.friendsMap.has_key(base.localAvatar.getPetId()) == None: + return 0 + return 1 + + def removeFriend(self, avatarId): + base.localAvatar.sendUpdate('friendsNotify', [base.localAvatar.doId, 1], sendToId=avatarId) + datagram = PyDatagram() + datagram.addUint16(CLIENT_REMOVE_FRIEND) + datagram.addUint32(avatarId) + self.send(datagram) + self.estateMgr.removeFriend(base.localAvatar.doId, avatarId) + for pair in base.localAvatar.friendsList: + friendId = pair[0] + if friendId == avatarId: + base.localAvatar.friendsList.remove(pair) + return + + def clearFriendState(self): + self.friendsMap = {} + self.friendsOnline = {} + self.friendsMapPending = 0 + self.friendsListError = 0 + + def sendGetFriendsListRequest(self): + self.friendsMapPending = 1 + self.friendsListError = 0 + datagram = PyDatagram() + datagram.addUint16(CLIENT_GET_FRIEND_LIST) + self.send(datagram) + + def cleanPetsFromFriendsMap(self): + for objId, obj in self.friendsMap.items(): + from toontown.pets import DistributedPet + if isinstance(obj, DistributedPet.DistributedPet): + print 'Removing %s reference from the friendsMap' % obj.getName() + del self.friendsMap[objId] + + def removePetFromFriendsMap(self): + doId = base.localAvatar.getPetId() + if doId and self.friendsMap.has_key(doId): + del self.friendsMap[doId] + + def addPetToFriendsMap(self, callback = None): + doId = base.localAvatar.getPetId() + if not doId or self.friendsMap.has_key(doId): + if callback: + callback() + return + + def petDetailsCallback(petAvatar): + handle = PetHandle.PetHandle(petAvatar) + self.friendsMap[doId] = handle + petAvatar.disable() + petAvatar.delete() + if callback: + callback() + if self._proactiveLeakChecks: + petAvatar.detectLeaks() + + PetDetail.PetDetail(doId, petDetailsCallback) + + def handleGetFriendsList(self, di): + error = di.getUint8() + if error: + self.notify.warning('Got error return from friends list.') + self.friendsListError = 1 + else: + count = di.getUint16() + for i in range(0, count): + doId = di.getUint32() + name = di.getString() + dnaString = di.getString() + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(dnaString) + petId = di.getUint32() + handle = FriendHandle.FriendHandle(doId, name, dna, petId) + self.friendsMap[doId] = handle + if self.friendsOnline.has_key(doId): + self.friendsOnline[doId] = handle + if self.friendPendingChatSettings.has_key(doId): + self.notify.debug('calling setCommonAndWL %s' % str(self.friendPendingChatSettings[doId])) + handle.setCommonAndWhitelistChatFlags(*self.friendPendingChatSettings[doId]) + + if base.wantPets and base.localAvatar.hasPet(): + + def handleAddedPet(): + self.friendsMapPending = 0 + messenger.send('friendsMapComplete') + + self.addPetToFriendsMap(handleAddedPet) + return + self.friendsMapPending = 0 + messenger.send('friendsMapComplete') + + def handleGetFriendsListExtended(self, di): + avatarHandleList = [] + error = di.getUint8() + if error: + self.notify.warning('Got error return from friends list extended.') + else: + count = di.getUint16() + for i in range(0, count): + abort = 0 + doId = di.getUint32() + name = di.getString() + if name == '': + abort = 1 + dnaString = di.getString() + if dnaString == '': + abort = 1 + else: + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(dnaString) + petId = di.getUint32() + if not abort: + handle = FriendHandle.FriendHandle(doId, name, dna, petId) + avatarHandleList.append(handle) + + if avatarHandleList: + messenger.send('gotExtraFriendHandles', [avatarHandleList]) + + def handleFriendOnline(self, di): + doId = di.getUint32() + commonChatFlags = 0 + whitelistChatFlags = 0 + if di.getRemainingSize() > 0: + commonChatFlags = di.getUint8() + if di.getRemainingSize() > 0: + whitelistChatFlags = di.getUint8() + self.notify.debug('Friend %d now online. common=%d whitelist=%d' % (doId, commonChatFlags, whitelistChatFlags)) + if not self.friendsOnline.has_key(doId): + self.friendsOnline[doId] = self.identifyFriend(doId) + messenger.send('friendOnline', [doId, commonChatFlags, whitelistChatFlags]) + if not self.friendsOnline[doId]: + self.friendPendingChatSettings[doId] = (commonChatFlags, whitelistChatFlags) + + def handleFriendOffline(self, di): + doId = di.getUint32() + self.notify.debug('Friend %d now offline.' % doId) + try: + del self.friendsOnline[doId] + messenger.send('friendOffline', [doId]) + except: + pass + + def getFirstBattle(self): + from toontown.battle import DistributedBattleBase + for dobj in self.doId2do.values(): + if isinstance(dobj, DistributedBattleBase.DistributedBattleBase): + return dobj + + def forbidCheesyEffects(self, forbid): + wasAllowed = self.__forbidCheesyEffects != 0 + if forbid: + self.__forbidCheesyEffects += 1 + else: + self.__forbidCheesyEffects -= 1 + isAllowed = self.__forbidCheesyEffects != 0 + if wasAllowed != isAllowed: + for av in Avatar.Avatar.ActiveAvatars: + if hasattr(av, 'reconsiderCheesyEffect'): + av.reconsiderCheesyEffect() + + base.localAvatar.reconsiderCheesyEffect() + + def areCheesyEffectsAllowed(self): + return self.__forbidCheesyEffects == 0 + + def getNextSetZoneDoneEvent(self): + return '%s-%s' % (ToontownClientRepository.EmuSetZoneDoneEvent, self.setZonesEmulated + 1) + + def getLastSetZoneDoneEvent(self): + return '%s-%s' % (ToontownClientRepository.EmuSetZoneDoneEvent, self.setZonesEmulated) + + def getQuietZoneLeftEvent(self): + return 'leftQuietZone-%s' % (id(self),) + + def sendSetZoneMsg(self, zoneId, visibleZoneList = None): + event = self.getNextSetZoneDoneEvent() + self.setZonesEmulated += 1 + parentId = base.localAvatar.defaultShard + self.sendSetLocation(base.localAvatar.doId, parentId, zoneId) + localAvatar.setLocation(parentId, zoneId) + interestZones = zoneId + if visibleZoneList is not None: + interestZones = visibleZoneList + self._addInterestOpToQueue(ToontownClientRepository.SetInterest, [parentId, interestZones, 'OldSetZoneEmulator'], event) + return + + def resetInterestStateForConnectionLoss(self): + OTPClientRepository.OTPClientRepository.resetInterestStateForConnectionLoss(self) + self.old_setzone_interest_handle = None + self.setZoneQueue.clear() + return + + def _removeEmulatedSetZone(self, doneEvent): + self._addInterestOpToQueue(ToontownClientRepository.ClearInterest, None, doneEvent) + return + + def _addInterestOpToQueue(self, op, args, event): + self.setZoneQueue.push([op, args, event]) + if len(self.setZoneQueue) == 1: + self._sendNextSetZone() + + def _sendNextSetZone(self): + op, args, event = self.setZoneQueue.top() + if op == ToontownClientRepository.SetInterest: + parentId, interestZones, name = args + if self.old_setzone_interest_handle == None: + self.old_setzone_interest_handle = self.addInterest(parentId, interestZones, name, ToontownClientRepository.SetZoneDoneEvent) + else: + self.alterInterest(self.old_setzone_interest_handle, parentId, interestZones, name, ToontownClientRepository.SetZoneDoneEvent) + elif op == ToontownClientRepository.ClearInterest: + self.removeInterest(self.old_setzone_interest_handle, ToontownClientRepository.SetZoneDoneEvent) + self.old_setzone_interest_handle = None + else: + self.notify.error('unknown setZone op: %s' % op) + return + + def _handleEmuSetZoneDone(self): + op, args, event = self.setZoneQueue.pop() + queueIsEmpty = self.setZoneQueue.isEmpty() + if event is not None: + if not base.killInterestResponse: + messenger.send(event) + elif not hasattr(self, '_dontSendSetZoneDone'): + import random + if random.random() < 0.05: + self._dontSendSetZoneDone = True + else: + messenger.send(event) + if not queueIsEmpty: + self._sendNextSetZone() + return + + def _isPlayerDclass(self, dclass): + return dclass == self._playerAvDclass + + def _isValidPlayerLocation(self, parentId, zoneId): + if not self.distributedDistrict: + return False + if parentId != self.distributedDistrict.doId: + return False + if parentId == self.distributedDistrict.doId and zoneId == OTPGlobals.UberZone: + return False + return True + + def sendQuietZoneRequest(self): + self.sendSetZoneMsg(OTPGlobals.QuietZone) + + def handleQuietZoneGenerateWithRequired(self, di): + parentId = di.getUint32() + zoneId = di.getUint32() + classId = di.getUint16() + doId = di.getUint32() + dclass = self.dclassesByNumber[classId] + if dclass.getClassDef().neverDisable: + 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] + if dclass.getClassDef().neverDisable: + dclass.startGenerate() + distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId) + dclass.stopGenerate() + + def handleQuietZoneUpdateField(self, di): + di2 = DatagramIterator(di) + doId = di2.getUint32() + if doId in self.deferredDoIds: + args, deferrable, dg0, updates = self.deferredDoIds[doId] + dclass = args[2] + if not dclass.getClassDef().neverDisable: + return + else: + do = self.getDo(doId) + if do: + if not do.neverDisable: + return + OTPClientRepository.OTPClientRepository.handleUpdateField(self, di) + + def handleDelete(self, di): + doId = di.getUint32() + self.deleteObject(doId) + + def deleteObject(self, doId, ownerView = False): + if self.doId2do.has_key(doId): + obj = self.doId2do[doId] + del self.doId2do[doId] + obj.deleteOrDelay() + if obj.getDelayDeleteCount() <= 0: + obj.detectLeaks() + elif self.cache.contains(doId): + self.cache.delete(doId) + else: + ClientRepository.notify.warning('Asked to delete non-existent DistObj ' + str(doId)) + + def _abandonShard(self): + for doId, obj in self.doId2do.items(): + if obj.parentId == localAvatar.defaultShard and obj is not localAvatar: + self.deleteObject(doId) + + def askAvatarKnown(self, avId): + if not hasattr(base, 'localAvatar'): + return 0 + for friendPair in base.localAvatar.friendsList: + if friendPair[0] == avId: + return 1 + + return 0 + + def requestAvatarInfo(self, avId): + if avId == 0: + return + datagram = PyDatagram() + datagram.addUint16(CLIENT_GET_FRIEND_LIST_EXTENDED) + datagram.addUint16(1) + datagram.addUint32(avId) + base.cr.send(datagram) + + def queueRequestAvatarInfo(self, avId): + removeTask = 0 + if not hasattr(self, 'avatarInfoRequests'): + self.avatarInfoRequests = [] + if self.avatarInfoRequests: + taskMgr.remove('avatarRequestQueueTask') + if avId not in self.avatarInfoRequests: + self.avatarInfoRequests.append(avId) + taskMgr.doMethodLater(0.1, self.sendAvatarInfoRequests, 'avatarRequestQueueTask') + + def sendAvatarInfoRequests(self, task = None): + print 'Sending request Queue for AV Handles' + if not hasattr(self, 'avatarInfoRequests'): + return + if len(self.avatarInfoRequests) == 0: + return + datagram = PyDatagram() + datagram.addUint16(CLIENT_GET_FRIEND_LIST_EXTENDED) + datagram.addUint16(len(self.avatarInfoRequests)) + for avId in self.avatarInfoRequests: + datagram.addUint32(avId) + + base.cr.send(datagram) diff --git a/toontown/distributed/ToontownDistrict.py b/toontown/distributed/ToontownDistrict.py new file mode 100644 index 0000000..830e473 --- /dev/null +++ b/toontown/distributed/ToontownDistrict.py @@ -0,0 +1,17 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from otp.distributed import DistributedDistrict + +class ToontownDistrict(DistributedDistrict.DistributedDistrict): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownDistrict') + + def __init__(self, cr): + DistributedDistrict.DistributedDistrict.__init__(self, cr) + self.avatarCount = 0 + self.newAvatarCount = 0 + + def allowAHNNLog(self, allow): + self.allowAHNN = allow + + def getAllowAHNNLog(self): + return self.allowAHNN diff --git a/toontown/distributed/ToontownDistrictStats.py b/toontown/distributed/ToontownDistrictStats.py new file mode 100644 index 0000000..7cda422 --- /dev/null +++ b/toontown/distributed/ToontownDistrictStats.py @@ -0,0 +1,94 @@ +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.task import Task +from direct.distributed import DoInterestManager +from otp.distributed.OtpDoGlobals import * +_ToonTownDistrictStatInterest = None +_ToonTownDistrictStatInterestComplete = 0 +_trashObject = DirectObject.DirectObject() + +def EventName(): + return 'ShardPopulationSet' + + +def isOpen(): + global _ToonTownDistrictStatInterest + return _ToonTownDistrictStatInterest is not None + + +def isComplete(): + global _ToonTownDistrictStatInterestComplete + return _ToonTownDistrictStatInterestComplete + + +def open(event = None): + global _trashObject + global _ToonTownDistrictStatInterest + if not isOpen(): + + def _CompleteProc(event): + global _ToonTownDistrictStatInterestComplete + _ToonTownDistrictStatInterestComplete = 1 + if event is not None: + messenger.send(event) + return + + _trashObject.acceptOnce(EventName(), _CompleteProc) + _ToonTownDistrictStatInterest = base.cr.addInterest(OTP_DO_ID_TOONTOWN, OTP_ZONE_ID_DISTRICTS_STATS, EventName(), EventName()) + elif isComplete(): + messenger.send(EventName()) + + +def refresh(event = None): + global _ToonTownDistrictStatInterest + if isOpen(): + if isComplete(): + messenger.send(EventName()) + if event is not none: + messenger.send(event) + else: + + def _CompleteProc(event): + global _ToonTownDistrictStatInterestComplete + _ToonTownDistrictStatInterestComplete = 1 + if event is not None: + messenger.send(event) + close() + return + + _trashObject.acceptOnce(EventName(), _CompleteProc, [event]) + _ToonTownDistrictStatInterest = base.cr.addInterest(OTP_DO_ID_TOONTOWN, OTP_ZONE_ID_DISTRICTS_STATS, EventName(), EventName()) + + +def close(): + global _ToonTownDistrictStatInterest + global _ToonTownDistrictStatInterestComplete + if isOpen(): + _ToonTownDistrictStatInterestComplete = 0 + base.cr.removeInterest(_ToonTownDistrictStatInterest, None) + _ToonTownDistrictStatInterest = None + return + + +class ToontownDistrictStats(DistributedObject.DistributedObject): + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.toontownDistrictId = 0 + + def settoontownDistrictId(self, value): + self.toontownDistrictId = value + + def setAvatarCount(self, avatarCount): + if self.cr.activeDistrictMap.has_key(self.toontownDistrictId): + self.cr.activeDistrictMap[self.toontownDistrictId].avatarCount = avatarCount + + def setNewAvatarCount(self, newAvatarCount): + if self.cr.activeDistrictMap.has_key(self.toontownDistrictId): + self.cr.activeDistrictMap[self.toontownDistrictId].newAvatarCount = newAvatarCount + + def setStats(self, avatarCount, newAvatarCount): + self.setAvatarCount(avatarCount) + self.setNewAvatarCount(newAvatarCount) diff --git a/toontown/distributed/ToontownMsgTypes.py b/toontown/distributed/ToontownMsgTypes.py new file mode 100644 index 0000000..8cdcb2b --- /dev/null +++ b/toontown/distributed/ToontownMsgTypes.py @@ -0,0 +1 @@ +from direct.distributed.MsgTypes import * diff --git a/toontown/distributed/__init__.py b/toontown/distributed/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/effects/BlastEffect.py b/toontown/effects/BlastEffect.py new file mode 100644 index 0000000..c1407f5 --- /dev/null +++ b/toontown/effects/BlastEffect.py @@ -0,0 +1,29 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class BlastEffect(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'BlastEffect') + EffectController.__init__(self) + self.fadeTime = 0.15 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.effectModel = model.find('**/tt_t_efx_ext_particleBlast') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeOut') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 4, startScale=1.0, blendType='easeIn') + self.track = Sequence(Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color diff --git a/toontown/effects/Bubbles.py b/toontown/effects/Bubbles.py new file mode 100644 index 0000000..3d5dad1 --- /dev/null +++ b/toontown/effects/Bubbles.py @@ -0,0 +1,62 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect +from direct.particles import Particles +from direct.particles import ForceGroup +import random + +class Bubbles(NodePath): + + def __init__(self, parent, renderParent): + NodePath.__init__(self) + self.renderParent = renderParent.attachNewNode('bubbleRenderParent') + self.renderParent.setBin('fixed', 0) + self.assign(parent.attachNewNode('bubbles')) + self.effect = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SpriteParticleRenderer') + p0.setEmitter('DiscEmitter') + p0.setPoolSize(8) + p0.setBirthRate(0.75) + p0.setLitterSize(2) + p0.setLitterSpread(1) + p0.factory.setLifespanBase(2.0) + p0.factory.setLifespanSpread(0.5) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(40.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setTextureFromNode('phase_4/models/char/bubble', '**/*') + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setInitialXScale(0.07) + p0.renderer.setFinalXScale(0.2) + p0.renderer.setInitialYScale(0.07) + p0.renderer.setFinalYScale(0.2) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitudeSpread(0.025) + p0.emitter.setAmplitude(0.1) + p0.emitter.setRadius(0.5) + gravityForceGroup = ForceGroup.ForceGroup('air') + force0 = LinearVectorForce(Vec3(0.0, 0.0, 1.0), 1.0, 0) + force0.setActive(1) + force1 = LinearJitterForce(2.5, 0) + force1.setActive(1) + gravityForceGroup.addForce(force0) + gravityForceGroup.addForce(force1) + self.effect.addForceGroup(gravityForceGroup) + self.effect.addParticles(p0) + self.effect.setPos(0, 0, 0) + + def start(self): + self.effect.start(self, self.renderParent) + + def stop(self): + self.effect.disable() + + def destroy(self): + self.effect.cleanup() + self.renderParent.removeNode() + del self.effect + del self.renderParent diff --git a/toontown/effects/CarSmoke.py b/toontown/effects/CarSmoke.py new file mode 100644 index 0000000..e0ca403 --- /dev/null +++ b/toontown/effects/CarSmoke.py @@ -0,0 +1,54 @@ +from pandac.PandaModules import * +from direct.particles import ParticleEffect +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import AppRunnerGlobal +import os + +class CarSmoke(NodePath): + + def __init__(self, parent): + NodePath.__init__(self) + notify = DirectNotifyGlobal.directNotify.newCategory('CarSmokeParticles') + self.effectNode = parent.attachNewNode('carSmoke') + self.effectNode.setBin('fixed', 1) + self.effectNode.setDepthWrite(0) + self.effect = ParticleEffect.ParticleEffect() + 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/effects')) + particleSearchPath.appendDirectory(Filename('phase_3.5/etc')) + particleSearchPath.appendDirectory(Filename('phase_4/etc')) + particleSearchPath.appendDirectory(Filename('phase_5/etc')) + particleSearchPath.appendDirectory(Filename('phase_6/etc')) + particleSearchPath.appendDirectory(Filename('phase_7/etc')) + particleSearchPath.appendDirectory(Filename('phase_8/etc')) + particleSearchPath.appendDirectory(Filename('phase_9/etc')) + particleSearchPath.appendDirectory(Filename('.')) + pfile = Filename('smokeTest4.ptf') + found = vfs.resolveFilename(pfile, particleSearchPath) + if not found: + notify.warning('loadParticleFile() - no path: %s' % pfile) + return + notify.debug('Loading particle file: %s' % pfile) + self.effect.loadConfig(pfile) + ren = self.effect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_4/models/props/tt_m_efx_ext_smoke', '**/*') + + def start(self): + self.effect.start(parent=self.effectNode) + + def stop(self): + try: + self.effect.disable() + except AttributeError: + pass + + def destroy(self): + self.stop() + self.effect.cleanup() + self.effectNode.removeNode() + del self.effect + del self.effectNode diff --git a/toontown/effects/ChrysanthemumEffect.py b/toontown/effects/ChrysanthemumEffect.py new file mode 100644 index 0000000..72a8a57 --- /dev/null +++ b/toontown/effects/ChrysanthemumEffect.py @@ -0,0 +1,48 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class ChrysanthemumEffect(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'ChrysanthemumEffect') + EffectController.__init__(self) + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectModel = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkBurst_tflip') + self.effectModel.setColorScale(Point4(0, 0, 0, 0)) + self.effectModel.reparentTo(self) + self.seqNode = self.effectModel.getChild(0).getChild(0).node() + self.effectModel2 = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkBurst_tflip') + self.effectModel2.setColorScale(Point4(0, 0, 0, 0)) + self.effectModel2.reparentTo(self) + self.seqNode2 = self.effectModel2.getChild(0).getChild(0).node() + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.stars = model.find('**/tt_t_efx_ext_fireworkStars_02') + self.stars.setColorScale(Point4(0, 0, 0, 0)) + self.stars.reparentTo(self) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + self.effectModel2.setColorScale(0, 0, 0, 0) + self.stars.setColorScale(0, 0, 0, 0) + fadeColor = self.effectColor - Vec4(0, 0, 0, 1) + fadeBlast = self.effectModel.colorScaleInterval(1.25, fadeColor, startColorScale=Vec4(1, 1, 0.8, 1), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(0.5, 700 * self.effectScale, startScale=200 * self.effectScale, blendType='easeOut') + fadeBlast2 = self.effectModel2.colorScaleInterval(1.0, fadeColor, startColorScale=Vec4(1, 1, 0.8, 1), blendType='easeIn') + scaleBlast2 = self.effectModel2.scaleInterval(1.0, 720 * self.effectScale, startScale=250 * self.effectScale, blendType='easeOut') + starsFadeIn = self.stars.colorScaleInterval(0.25, self.effectColor, startColorScale=Vec4(1, 1, 1, 0)) + starsFadeOut = self.stars.colorScaleInterval(1.0, Vec4(0, 0, 0, 0), startColorScale=self.effectColor, blendType='easeIn') + starsScaleUp = self.stars.scaleInterval(1.5, 720 * self.effectScale, startScale=660 * self.effectScale, blendType='easeOut') + self.track = Parallel(Func(self.effectModel.setColorScale, self.effectColor), Func(self.effectModel2.setColorScale, self.effectColor), scaleBlast, fadeBlast, scaleBlast2, fadeBlast2, starsScaleUp, Sequence(Wait(0.4), starsFadeIn, starsFadeOut, Wait(0.5), Func(self.cleanUpEffect))) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale diff --git a/toontown/effects/DistributedFireworkShow.py b/toontown/effects/DistributedFireworkShow.py new file mode 100644 index 0000000..334fb73 --- /dev/null +++ b/toontown/effects/DistributedFireworkShow.py @@ -0,0 +1,27 @@ +from direct.distributed import DistributedObject +from toontown.effects.FireworkShowMixin import FireworkShowMixin + +class DistributedFireworkShow(DistributedObject.DistributedObject, FireworkShowMixin): + notify = directNotify.newCategory('DistributedFireworkShow') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FireworkShowMixin.__init__(self) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + FireworkShowMixin.disable(self) + + def delete(self): + DistributedObject.DistributedObject.delete(self) + + def d_requestFirework(self, x, y, z, style, color1, color2): + self.sendUpdate('requestFirework', (x, + y, + z, + style, + color1, + color2)) diff --git a/toontown/effects/DistributedFireworkShowAI.py b/toontown/effects/DistributedFireworkShowAI.py new file mode 100644 index 0000000..da2f54b --- /dev/null +++ b/toontown/effects/DistributedFireworkShowAI.py @@ -0,0 +1,62 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import ClockDelta +from FireworkShow import FireworkShow +from FireworkShows import getShowDuration +import random +from direct.task import Task + +class DistributedFireworkShowAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFireworkShowAI') + + def __init__(self, air, fireworkMgr = None): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.fireworkMgr = fireworkMgr + self.eventId = None + self.style = None + self.timestamp = None + self.throwAwayShow = FireworkShow() + return + + def delete(self): + del self.throwAwayShow + taskMgr.remove(self.taskName('waitForShowDone')) + DistributedObjectAI.DistributedObjectAI.delete(self) + + def d_startShow(self, eventId, style): + timestamp = ClockDelta.globalClockDelta.getRealNetworkTime() + self.eventId = eventId + self.style = style + self.timestamp = timestamp + self.sendUpdate('startShow', (self.eventId, self.style, self.timestamp)) + if simbase.air.config.GetBool('want-old-fireworks', 0): + duration = getShowDuration(self.eventId, self.style) + taskMgr.doMethodLater(duration, self.fireworkShowDone, self.taskName('waitForShowDone')) + else: + duration = self.throwAwayShow.getShowDuration(self.eventId) + duration += 20.0 + taskMgr.doMethodLater(duration, self.fireworkShowDone, self.taskName('waitForShowDone')) + + def fireworkShowDone(self, task): + self.notify.debug('fireworkShowDone') + if self.fireworkMgr: + self.fireworkMgr.stopShow(self.zoneId) + return Task.done + + def requestFirework(self, x, y, z, style, color1, color2): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestFirework: avId: %s, style: %s' % (avId, style)) + if self.fireworkMgr: + if self.fireworkMgr.isShowRunning(self.zoneId): + self.d_shootFirework(x, y, z, style, color1, color2) + else: + self.d_shootFirework(x, y, z, style, color1, color2) + + def d_shootFirework(self, x, y, z, style, color1, color2): + self.sendUpdate('shootFirework', (x, + y, + z, + style, + color1, + color2)) diff --git a/toontown/effects/Drift.py b/toontown/effects/Drift.py new file mode 100644 index 0000000..bd6a08d --- /dev/null +++ b/toontown/effects/Drift.py @@ -0,0 +1,52 @@ +from pandac.PandaModules import * +from direct.particles import ParticleEffect +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import AppRunnerGlobal +import os + +class Drift(NodePath): + + def __init__(self, parent, renderParent): + NodePath.__init__(self) + notify = DirectNotifyGlobal.directNotify.newCategory('DriftParticles') + self.renderParent = renderParent.attachNewNode('driftRenderParent') + self.renderParent.setBin('fixed', 0) + self.renderParent.setDepthWrite(0) + self.assign(parent.attachNewNode('drift')) + self.effect = ParticleEffect.ParticleEffect() + 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/effects')) + particleSearchPath.appendDirectory(Filename('phase_3.5/etc')) + particleSearchPath.appendDirectory(Filename('phase_4/etc')) + particleSearchPath.appendDirectory(Filename('phase_5/etc')) + particleSearchPath.appendDirectory(Filename('phase_6/etc')) + particleSearchPath.appendDirectory(Filename('phase_7/etc')) + particleSearchPath.appendDirectory(Filename('phase_8/etc')) + particleSearchPath.appendDirectory(Filename('phase_9/etc')) + particleSearchPath.appendDirectory(Filename('.')) + pfile = Filename('drift.ptf') + found = vfs.resolveFilename(pfile, particleSearchPath) + if not found: + notify.warning('loadParticleFile() - no path: %s' % pfile) + return + notify.debug('Loading particle file: %s' % pfile) + self.effect.loadConfig(pfile) + ren = self.effect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_6/models/karting/driftSmoke', '**/*') + + def start(self): + self.effect.start(self, self.renderParent) + + def stop(self): + self.effect.disable() + + def destroy(self): + self.stop() + self.effect.cleanup() + self.renderParent.removeNode() + del self.effect + del self.renderParent diff --git a/toontown/effects/DustCloud.py b/toontown/effects/DustCloud.py new file mode 100644 index 0000000..08b4a6d --- /dev/null +++ b/toontown/effects/DustCloud.py @@ -0,0 +1,87 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.showbase import PythonUtil +from toontown.battle.BattleProps import globalPropPool +from direct.directnotify import DirectNotifyGlobal +SFX = PythonUtil.Enum('poof, magic') +SFXPATHS = {SFX.poof: 'phase_4/audio/sfx/firework_distance_02.mp3', + SFX.magic: 'phase_4/audio/sfx/SZ_DD_treasure.mp3'} + +class DustCloud(NodePath): + dustCloudCount = 0 + sounds = {} + notify = DirectNotifyGlobal.directNotify.newCategory('DustCloud') + + def __init__(self, parent = hidden, fBillboard = 1, wantSound = 0): + NodePath.__init__(self) + self.assign(globalPropPool.getProp('suit_explosion_dust')) + if fBillboard: + self.setBillboardAxis() + self.reparentTo(parent) + self.seqNode = self.find('**/+SequenceNode').node() + self.seqNode.setFrameRate(0) + self.wantSound = wantSound + if self.wantSound and not DustCloud.sounds: + DustCloud.sounds[SFX.poof] = loader.loadSfx(SFXPATHS[SFX.poof]) + self.track = None + self.trackId = DustCloud.dustCloudCount + DustCloud.dustCloudCount += 1 + self.setBin('fixed', 100, 1) + self.hide() + return + + def createTrack(self, rate = 24): + + def getSoundFuncIfAble(soundId): + sound = DustCloud.sounds.get(soundId) + if self.wantSound and sound: + return sound.play + else: + + def dummy(): + pass + + return dummy + + tflipDuration = self.seqNode.getNumChildren() / float(rate) + self.track = Sequence(Func(self.show), Func(self.messaging), Func(self.seqNode.play, 0, self.seqNode.getNumFrames() - 1), Func(self.seqNode.setFrameRate, rate), Func(getSoundFuncIfAble(SFX.poof)), Wait(tflipDuration), Func(self._resetTrack), name='dustCloud-track-%d' % self.trackId) + + def _resetTrack(self): + self.seqNode.setFrameRate(0) + self.hide() + + def messaging(self): + self.notify.debug('CREATING TRACK ID: %s' % self.trackId) + + def isPlaying(self): + if self.track == None: + return False + if self.track.isPlaying(): + return True + else: + return False + return + + def play(self, rate = 24): + self.stop() + self.createTrack(rate) + self.track.start() + + def loop(self, rate = 24): + self.stop() + self.createTrack(rate) + self.track.loop() + + def stop(self): + if self.track: + self.track.finish() + self.track.clearToInitial() + + def destroy(self): + self.notify.debug('DESTROYING TRACK ID: %s' % self.trackId) + if self.track: + self._resetTrack() + self.track.clearToInitial() + del self.track + del self.seqNode + self.removeNode() diff --git a/toontown/effects/EffectController.py b/toontown/effects/EffectController.py new file mode 100644 index 0000000..e348e1f --- /dev/null +++ b/toontown/effects/EffectController.py @@ -0,0 +1,107 @@ +from pandac.PandaModules import * + +class EffectController: + particleDummy = None + + def __init__(self): + self.track = None + self.startEffect = None + self.endEffect = None + self.f = None + self.p0 = None + return + + def createTrack(self): + pass + + def destroy(self): + self.finish() + if self.f: + self.f.cleanup() + self.f = None + self.p0 = None + self.removeNode() + return + + def cleanUpEffect(self): + if self.f: + self.setPosHpr(0, 0, 0, 0, 0, 0) + self.f.disable() + self.detachNode() + + def reallyCleanUpEffect(self): + self.cleanUpEffect() + self.finish() + + def play(self, lod = None): + if lod != None: + try: + self.createTrack(lod) + except TypeError, e: + raise TypeError('Error loading %s effect.' % self.__class__.__name__) + + else: + self.createTrack() + self.track.start() + return + + def stop(self): + if self.track: + self.track.pause() + self.track = None + if self.startEffect: + self.startEffect.pause() + self.startEffect = None + if self.endEffect: + self.endEffect.pause() + self.endEffect = None + self.cleanUpEffect() + return + + def finish(self): + if self.track: + self.track.pause() + self.track = None + if self.startEffect: + self.startEffect.pause() + self.startEffect = None + if self.endEffect: + self.endEffect.pause() + self.endEffect = None + return + + def startLoop(self, lod = None): + if lod != None: + try: + self.createTrack(lod) + except TypeError, e: + raise TypeError('Error loading %s effect.' % self.__class__.__name__) + + else: + self.createTrack() + if self.startEffect: + self.startEffect.start() + return + + def stopLoop(self): + if self.startEffect: + self.startEffect.pause() + self.startEffect = None + if self.endEffect and not self.endEffect.isPlaying(): + self.endEffect.start() + return + + def getTrack(self): + if not self.track: + self.createTrack() + return self.track + + def enableEffect(self): + if self.f and self.particleDummy: + self.f.start(self, self.particleDummy) + elif self.f: + self.f.start(self, self) + + def disableEffect(self): + if self.f: + self.f.disable() diff --git a/toontown/effects/Firework.py b/toontown/effects/Firework.py new file mode 100644 index 0000000..3535fc3 --- /dev/null +++ b/toontown/effects/Firework.py @@ -0,0 +1,149 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.effects.FireworkGlobals import * +from toontown.effects.FireworkEffect import FireworkEffect +import random + +class Firework(NodePath): + + def __init__(self, typeId, velocity = Vec3(0, 0, 500), scale = 1.0, color1 = Vec4(1, 1, 1, 1), color2 = None, burstDelay = 1.25): + NodePath.__init__(self, 'Firework') + self.typeId = typeId + self.velocity = velocity + self.scale = scale + self.primaryColor = color1 + self.secondaryColor = color2 + if not self.secondaryColor: + self.secondaryColor = self.primaryColor + self.burstDelay = burstDelay + self.fireworkIval = None + self.fireworkEffects = [] + return + + def play(self): + if not self.fireworkIval: + self.generateFireworkIval() + self.fireworkIval.start() + + def generateFireworkIval(self): + if not self.fireworkIval: + self.fireworkIval = Sequence() + if self.typeId == FireworkType.BasicPeony: + firework = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.AdvancedPeony: + firework = FireworkEffect(FireworkBurstType.PeonyParticleShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.DiademPeony: + firework = FireworkEffect(FireworkBurstType.PeonyDiademShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Chrysanthemum: + firework = FireworkEffect(FireworkBurstType.ChrysanthemumShell, FireworkTrailType.Glow, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.DiademChrysanthemum: + firework = FireworkEffect(FireworkBurstType.ChrysanthemumDiademShell, FireworkTrailType.Glow, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Ring: + firework = FireworkEffect(FireworkBurstType.RingShell, FireworkTrailType.Default, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Saturn: + firework = FireworkEffect(FireworkBurstType.SaturnShell, FireworkTrailType.GlowSparkle, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Bees: + firework = FireworkEffect(FireworkBurstType.BeeShell, FireworkTrailType.Polygonal, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.TrailBurst: + firework = FireworkEffect(FireworkBurstType.TrailExplosion, FireworkTrailType.Glow, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.GlowFlare: + firework = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + firework.gravityMult = 1.0 + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.PalmTree: + firework = FireworkEffect(FireworkBurstType.TrailExplosion, FireworkTrailType.LongGlowSparkle, self.velocity, self.scale, self.primaryColor, self.secondaryColor, self.burstDelay) + firework.reparentTo(self) + firework.gravityMult = 1.0 + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + elif self.typeId == FireworkType.Mickey: + head = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Glow, velocity=Vec3(0, 0, 80 * self.scale), scale=self.scale / 1.2, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.5) + leftEar = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Glow, velocity=Vec3(-25 * self.scale, 0, 115 * self.scale), scale=self.scale / 1.6, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.7) + rightEar = FireworkEffect(FireworkBurstType.PeonyShell, FireworkTrailType.Glow, velocity=Vec3(25 * self.scale, 0, 115 * self.scale), scale=self.scale / 1.6, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.7) + head.reparentTo(self) + leftEar.reparentTo(self) + rightEar.reparentTo(self) + self.fireworkEffects = self.fireworkEffects + [head, leftEar, rightEar] + fireworkParallel = Parallel() + fireworkParallel.append(head.getFireworkMainIval()) + fireworkParallel.append(leftEar.getFireworkMainIval()) + fireworkParallel.append(rightEar.getFireworkMainIval()) + self.fireworkIval.append(fireworkParallel) + elif self.typeId == FireworkType.PirateSkull: + skull = FireworkEffect(FireworkBurstType.SkullBlast, FireworkTrailType.GlowSparkle, velocity=Vec3(0, 0, 400 * self.scale), scale=self.scale, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=1.75) + leftBone = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, velocity=Vec3(220 * self.scale, 0, 250 * self.scale), scale=self.scale * 1.25, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=2.25) + rightBone = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, velocity=Vec3(-220 * self.scale, 0, 250 * self.scale), scale=self.scale * 1.25, primaryColor=self.primaryColor, secondaryColor=self.secondaryColor, burstDelay=2.25) + skull.reparentTo(self) + leftBone.reparentTo(self) + leftBone.setPos(-225 * self.scale, 0, 0) + leftBone.gravityMult = 3.5 + rightBone.reparentTo(self) + rightBone.setPos(225 * self.scale, 0, 0) + rightBone.gravityMult = 3.5 + self.fireworkEffects = self.fireworkEffects + [skull, leftBone, rightBone] + fireworkParallel = Parallel() + fireworkParallel.append(skull.getFireworkMainIval()) + fireworkParallel.append(leftBone.getFireworkMainIval()) + fireworkParallel.append(rightBone.getFireworkMainIval()) + self.fireworkIval.append(fireworkParallel) + elif self.typeId == FireworkType.AmericanFlag: + fireworkParallel = Parallel() + colors = [Vec4(1, 0, 0, 1), Vec4(1, 1, 1, 1)] + for i in range(4): + firework = FireworkEffect(None, FireworkTrailType.LongGlowSparkle, velocity=Vec3(-30 * self.scale, 0, 150 * self.scale - 20 * i), scale=self.scale * 3.0, primaryColor=colors[i % 2], burstDelay=2.5) + firework.reparentTo(self) + firework.setX(-20.0 * self.scale + 10.0 * i * self.scale) + self.fireworkEffects.append(firework) + fireworkParallel.append(Sequence(Wait(0.25 * i), firework.getFireworkMainIval())) + + firework = FireworkEffect(FireworkBurstType.Sparkles, FireworkTrailType.Default, velocity=Vec3(20, 0, 90), scale=self.scale * 1.5) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + fireworkParallel.append(Sequence(Wait(1.5), firework.getFireworkMainIval())) + self.fireworkIval.append(fireworkParallel) + elif self.typeId == FireworkType.IceCream: + firework = FireworkEffect(FireworkBurstType.IceCream, FireworkTrailType.Default, self.velocity, self.scale, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 1), self.burstDelay) + firework.reparentTo(self) + self.fireworkEffects.append(firework) + self.fireworkIval.append(firework.getFireworkMainIval()) + self.fireworkIval.append(Func(self.cleanup)) + return self.fireworkIval + + def cleanup(self): + if self.fireworkIval: + self.fireworkIval.pause() + self.fireworkIval = None + for effect in self.fireworkEffects: + effect.cleanupEffect() + + self.fireworkEffects = [] + return diff --git a/toontown/effects/FireworkEffect.py b/toontown/effects/FireworkEffect.py new file mode 100644 index 0000000..564a61a --- /dev/null +++ b/toontown/effects/FireworkEffect.py @@ -0,0 +1,447 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import random +from toontown.effects.FireworkGlobals import * +from toontown.effects.Glow import Glow +from toontown.effects.GlowTrail import GlowTrail +from toontown.effects.SparksTrail import SparksTrail +from toontown.effects.SparksTrailLong import SparksTrailLong +from toontown.effects.PolyTrail import PolyTrail +from toontown.effects.FlashEffect import FlashEffect +from toontown.effects.BlastEffect import BlastEffect +from toontown.effects.FireworkSparkles import FireworkSparkles +from toontown.effects.SimpleSparkles import SimpleSparkles +from toontown.effects.PeonyEffect import PeonyEffect +from toontown.effects.RayBurst import RayBurst +from toontown.effects.StarBurst import StarBurst +from toontown.effects.ChrysanthemumEffect import ChrysanthemumEffect +from toontown.effects.RingEffect import RingEffect +from toontown.effects.NoiseSparkles import NoiseSparkles +from toontown.effects.SkullBurst import SkullBurst +from toontown.effects.SkullFlash import SkullFlash +from toontown.effects.TrailExplosion import TrailExplosion +from toontown.effects.IceCream import IceCream +trailSfxNames = ['phase_4/audio/sfx/firework_whistle_01.mp3', 'phase_4/audio/sfx/firework_whistle_02.mp3'] +burstSfxNames = ['phase_4/audio/sfx/firework_explosion_01.mp3', + 'phase_4/audio/sfx/firework_explosion_02.mp3', + 'phase_4/audio/sfx/firework_explosion_03.mp3', + 'phase_4/audio/sfx/firework_distance_01.mp3', + 'phase_4/audio/sfx/firework_distance_02.mp3', + 'phase_4/audio/sfx/firework_distance_03.mp3'] + +class FireworkEffect(NodePath): + + def __init__(self, burstEffectId, trailEffectId = FireworkTrailType.Default, velocity = Vec3(0, 0, 500), scale = 1.0, primaryColor = Vec4(1, 1, 1, 1), secondaryColor = None, burstDelay = 1.25): + NodePath.__init__(self, 'FireworkEffect') + self.burstTypeId = burstEffectId + self.trailTypeId = trailEffectId + self.velocity = velocity + self.scale = scale / 7 + self.primaryColor = primaryColor + self.secondaryColor = secondaryColor + if not self.secondaryColor: + self.secondaryColor = self.primaryColor + self.burstDelay = burstDelay + self.gravityMult = 1.0 + self.fireworkMainIval = None + self.trailEffectsIval = None + self.burstEffectsIval = None + self.effectsNode = self.attachNewNode('fireworkEffectsNode') + self.trailEffects = [] + self.burstEffects = [] + self.trailSfx = [] + for audio in trailSfxNames: + audio = loader.loadSfx(audio) + audio.setVolume(0.075) + self.trailSfx.append(audio) + + self.burstSfx = [] + for audio in burstSfxNames: + audio = loader.loadSfx(audio) + audio.setVolume(0.8) + self.burstSfx.append(audio) + + return + + def play(self): + self.getFireworkMainIval().start() + + def getFireworkMainIval(self): + self.effectsNode.setPos(0, 0, 0) + if not self.fireworkMainIval: + self.fireworkMainIval = Parallel() + self.fireworkMainIval.append(self.getTrailEffectsIval()) + self.fireworkMainIval.append(Sequence(Wait(self.burstDelay), Func(self.cleanupTrailEffects), self.getBurstEffectsIval(), Func(self.cleanupBurstEffects), Func(self.cleanupEffect))) + return self.fireworkMainIval + + def getTrailEffectsIval(self): + if not self.trailEffectsIval: + if self.trailTypeId is None: + self.effectNode.setPos(self.velocity) + self.trailEffectsIval = Wait(self.burstDelay) + return self.trailEffectsIval + self.trailEffectsIval = Parallel() + self.trailEffectsIval.append(ProjectileInterval(self.effectsNode, startVel=self.velocity, duration=self.burstDelay, gravityMult=self.gravityMult)) + if self.trailTypeId is None: + return self.trailEffectsIval + self.trailEffectsIval.append(Func(random.choice(self.trailSfx).play)) + if base.config.GetInt('toontown-sfx-setting', 1) == 0: + if self.trailTypeId != FireworkTrailType.LongGlowSparkle: + self.trailTypeId = FireworkTrailType.Default + if self.trailTypeId == FireworkTrailType.Default: + glowEffect = Glow.getEffect() + if glowEffect: + glowEffect.reparentTo(self.effectsNode) + glowEffect.setColorScale(Vec4(1, 1, 1, 1)) + glowEffect.setScale(10.0) + self.trailEffects.append(glowEffect) + self.trailEffectsIval.append(Func(glowEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.Polygonal: + r = 0.75 + mColor = Vec4(1, 1, 1, 1) + vertex_list = [Vec4(r, 0.0, r, 1.0), + Vec4(r, 0.0, -r, 1.0), + Vec4(-r, 0.0, -r, 1.0), + Vec4(-r, 0.0, r, 1.0), + Vec4(r, 0.0, r, 1.0)] + motion_color = [mColor, + mColor, + mColor, + mColor, + mColor] + trailEffect = PolyTrail(None, vertex_list, motion_color, 0.5) + trailEffect.setUnmodifiedVertexColors(motion_color) + trailEffect.reparentTo(self.effectsNode) + trailEffect.motion_trail.geom_node_path.setTwoSided(False) + trailEffect.setBlendModeOn() + trailEffect.setLightOff() + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.beginTrail)) + elif self.trailTypeId == FireworkTrailType.Glow: + trailEffect = GlowTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale * 0.75) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + trailEffect.setLifespan(0.25) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.Sparkle: + trailEffect = SparksTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.GlowSparkle: + glowEffect = Glow.getEffect() + if glowEffect: + glowEffect.reparentTo(self.effectsNode) + glowEffect.setColorScale(Vec4(1, 1, 1, 1)) + glowEffect.setScale(15.0) + self.trailEffects.append(glowEffect) + self.trailEffectsIval.append(Func(glowEffect.startLoop)) + trailEffect = SparksTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.LongSparkle: + trailEffect = SparksTrailLong.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(Vec4(1, 1, 1, 1)) + trailEffect.setLifespan(4.0) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + elif self.trailTypeId == FireworkTrailType.LongGlowSparkle: + trailEffect = SparksTrailLong.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(self.secondaryColor) + trailEffect.setLifespan(3.5) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + trailEffect = GlowTrail.getEffect() + if trailEffect: + trailEffect.reparentTo(self.effectsNode) + trailEffect.setEffectScale(self.scale) + trailEffect.setEffectColor(self.primaryColor) + trailEffect.setLifespan(1.0) + self.trailEffects.append(trailEffect) + self.trailEffectsIval.append(Func(trailEffect.startLoop)) + return self.trailEffectsIval + + def getBurstEffectsIval(self): + if not self.burstEffectsIval: + self.burstEffectsIval = Parallel() + if self.burstTypeId is None: + return self.burstEffectsIval + self.burstEffectsIval.append(Wait(0.5)) + self.burstEffectsIval.append(Func(random.choice(self.burstSfx).play)) + flash = FlashEffect() + flash.reparentTo(self.effectsNode) + flash.setEffectColor(self.primaryColor) + flash.setScale(1200 * self.scale) + flash.fadeTime = 0.5 + self.burstEffectsIval.append(flash.getTrack()) + self.burstEffects.append(flash) + primaryBlast = BlastEffect() + primaryBlast.reparentTo(self.effectsNode) + primaryBlast.setScale(100 * self.scale) + primaryBlast.setEffectColor(Vec4(1, 1, 1, 1)) + primaryBlast.fadeTime = 0.75 + self.burstEffectsIval.append(primaryBlast.getTrack()) + self.burstEffects.append(primaryBlast) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + secondaryBlast = BlastEffect() + secondaryBlast.reparentTo(self.effectsNode) + secondaryBlast.setScale(250 * self.scale) + secondaryBlast.setEffectColor(self.primaryColor) + secondaryBlast.fadeTime = 0.3 + self.burstEffectsIval.append(secondaryBlast.getTrack()) + self.burstEffects.append(secondaryBlast) + if self.burstTypeId == FireworkBurstType.Sparkles: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(100 * self.scale) + sparkles.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.PeonyShell: + explosion = PeonyEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + explosion.startDelay = 0.0 + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + if base.config.GetInt('toontown-sfx-setting', 1) >= 2: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setEffectColor(self.primaryColor) + sparkles.startDelay = 0.0 + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + explosion = PeonyEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale * 0.8) + explosion.setEffectColor(self.primaryColor) + explosion.startDelay = 0.15 + explosion.setR(220) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + elif self.burstTypeId == FireworkBurstType.PeonyParticleShell: + explosion = StarBurst.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale * 0.75) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + elif self.burstTypeId == FireworkBurstType.PeonyDiademShell: + explosion = StarBurst.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + sparkles = SimpleSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(100 * self.scale) + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.ChrysanthemumShell: + explosion = ChrysanthemumEffect() + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 2: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale * 0.8) + sparkles.setEffectColor(self.primaryColor) + sparkles.startDelay = 0.2 + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.ChrysanthemumDiademShell: + explosion = ChrysanthemumEffect() + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + sparkles = SimpleSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(100 * self.scale) + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.RingShell: + explosion = RingEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + elif self.burstTypeId == FireworkBurstType.SaturnShell: + explosion = RingEffect.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + sparkles = SimpleSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(75 * self.scale) + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.BeeShell: + explosion = NoiseSparkles.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(Sequence(Wait(0.1), explosion.getTrack())) + self.burstEffects.append(explosion) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + elif self.burstTypeId == FireworkBurstType.SkullBlast: + explosion = SkullBurst.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + explosion.startDelay = 0.1 + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + skullFlash = SkullFlash.getEffect() + if skullFlash: + skullFlash.reparentTo(self.effectsNode) + skullFlash.setScale(650 * self.scale) + skullFlash.fadeTime = 0.75 + skullFlash.startDelay = 0.08 + self.burstEffectsIval.append(skullFlash.getTrack()) + self.burstEffects.append(skullFlash) + if base.config.GetInt('toontown-sfx-setting', 1) >= 1: + rays = RayBurst() + rays.reparentTo(self.effectsNode) + rays.setEffectScale(self.scale) + rays.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(rays.getTrack()) + self.burstEffects.append(rays) + if base.config.GetInt('toontown-sfx-setting', 1) >= 2: + sparkles = FireworkSparkles.getEffect() + if sparkles: + sparkles.reparentTo(self.effectsNode) + sparkles.setEffectScale(self.scale) + sparkles.setRadius(400 * self.scale) + sparkles.startDelay = 0.1 + sparkles.setEffectColor(self.secondaryColor) + self.burstEffectsIval.append(sparkles.getTrack()) + self.burstEffects.append(sparkles) + elif self.burstTypeId == FireworkBurstType.TrailExplosion: + explosion = TrailExplosion.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + explosion.numTrails = 3 + base.config.GetInt('toontown-sfx-setting', 1) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + elif self.burstTypeId == FireworkBurstType.IceCream: + explosion = IceCream.getEffect() + if explosion: + explosion.reparentTo(self.effectsNode) + explosion.setEffectScale(self.scale) + explosion.setEffectColor(self.primaryColor) + self.burstEffectsIval.append(explosion.getTrack()) + self.burstEffects.append(explosion) + return self.burstEffectsIval + + def cleanupTrailEffects(self): + if self.trailEffectsIval: + self.trailEffectsIval.pause() + self.trailEffectsIval = None + for effect in self.trailEffects: + if isinstance(effect, PolyTrail): + effect.destroy() + effect = None + else: + effect.stopLoop() + effect = None + + self.trailEffects = [] + return + + def cleanupBurstEffects(self): + if self.burstEffectsIval: + self.burstEffectsIval.pause() + self.burstEffectsIval = None + for effect in self.burstEffects: + effect.stop() + effect = None + + self.burstEffects = [] + return + + def cleanupEffect(self): + if self.fireworkMainIval: + self.fireworkMainIval.pause() + self.fireworkMainIval = None + self.cleanupTrailEffects() + self.cleanupBurstEffects() + return diff --git a/toontown/effects/FireworkGlobals.py b/toontown/effects/FireworkGlobals.py new file mode 100644 index 0000000..c1c3b8e --- /dev/null +++ b/toontown/effects/FireworkGlobals.py @@ -0,0 +1,106 @@ +FW_T = 0 +FW_STYLE = 1 +FW_COLOR1 = 2 +FW_COLOR2 = 3 +FW_AMP = 4 +FW_POS_X = 5 +FW_POS_Y = 6 +FW_POS_Z = 7 +CIRCLE = 0 +ROCKET = 1 +RING = 2 +CIRCLELARGE = 3 +CIRCLESMALL = 4 +POP = 5 +CIRCLESPRITE = 6 +styleNames = ['CIRCLE', + 'ROCKET', + 'RING', + 'CIRCLELARGE', + 'CIRCLESMALL', + 'POP', + 'CIRCLESPRITE'] +styleNamesShort = ['CIR', + 'RKT', + 'RNG', + 'CLG', + 'CSM', + 'POP', + 'SPR'] +Names = ['Pow', + 'Rocket', + 'Ring', + 'Large\nPow', + 'Small\nPow', + 'Pop', + 'Widow\nMaker'] +WHITE = 0 +RED = 1 +BLUE = 2 +YELLOW = 3 +GREEN = 4 +PINK = 5 +PURPLE = 6 +CYAN = 7 +PEACH = 8 +ColorNames = ['White', + 'Red', + 'Blue', + 'Yellow', + 'Green', + 'Pink', + 'Purple', + 'Cyan', + 'Peach'] +SNOWFLAKE = 0 +MUSICNOTE = 1 +FLOWER = 2 +ICECREAM = 3 +STARFISH = 4 +ZZZ = 5 +skyTransitionDuration = 2.0 +preShowPauseDuration = 2.0 +postShowPauseDuration = 4.0 +preNormalMusicPauseDuration = 0.5 + +class FireworkTrailType: + Default = 0 + Polygonal = 1 + Glow = 2 + Sparkle = 3 + GlowSparkle = 4 + LongSparkle = 5 + LongGlowSparkle = 6 + + +class FireworkBurstType: + Sparkles = 0 + PeonyShell = 1 + PeonyParticleShell = 2 + PeonyDiademShell = 3 + ChrysanthemumShell = 4 + ChrysanthemumDiademShell = 5 + RingShell = 6 + SaturnShell = 7 + BeeShell = 8 + SkullBlast = 9 + TrailExplosion = 10 + IceCream = 11 + + +class FireworkType: + BasicPeony = 0 + AdvancedPeony = 1 + DiademPeony = 2 + Chrysanthemum = 3 + DiademChrysanthemum = 4 + Ring = 5 + Saturn = 6 + Bees = 7 + TrailBurst = 8 + GlowFlare = 9 + PalmTree = 10 + Mickey = 11 + PirateSkull = 12 + AmericanFlag = 13 + IceCream = 14 diff --git a/toontown/effects/FireworkShow.py b/toontown/effects/FireworkShow.py new file mode 100644 index 0000000..f884bdb --- /dev/null +++ b/toontown/effects/FireworkShow.py @@ -0,0 +1,1204 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.effects.FireworkGlobals import * +from toontown.effects.Firework import Firework +from toontown.toonbase import ToontownGlobals +from toontown.parties import PartyGlobals +import random +colors = [Vec4(1, 1, 1, 1), + Vec4(1, 0.1, 0.1, 1), + Vec4(0.1, 1, 0.1, 1), + Vec4(0.3, 1, 0.3, 1), + Vec4(0.2, 0.2, 1, 1), + Vec4(1, 1, 0.1, 1), + Vec4(1, 0.5, 0.1, 1), + Vec4(1, 0.1, 1, 1), + Vec4(0.1, 1, 1, 1), + Vec4(0.1, 0.5, 1, 1)] +fireworkShowTypes = [ToontownGlobals.JULY4_FIREWORKS, + PartyGlobals.FireworkShows.Summer, + ToontownGlobals.NEWYEARS_FIREWORKS, + ToontownGlobals.COMBO_FIREWORKS] + +class FireworkShow(NodePath): + + def r(): + return random.randint(8, 12) / 10.0 + + def rV(): + return Vec3(random.randint(-60, 60), random.randint(10, 30), random.randint(125, 150)) + + def rP(): + return Point3(0, 0, 0) + + def rS(): + return 1.0 + random.random() / 2.0 + + def rC(): + return random.choice(colors) + + def rT(): + return random.randint(12, 20) / 10.0 + + def rD(): + return random.randint(1, 20) / 10.0 + + showData = {ToontownGlobals.JULY4_FIREWORKS: [[FireworkType.GlowFlare, + Vec3(-90, 0, 80), + Vec3(120, 0, 0), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 0.0], + [FireworkType.GlowFlare, + Vec3(90, 0, 80), + Vec3(-120, 0, 0), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.BasicPeony, + Vec3(50, 0, 140), + rP(), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + rT(), + 0.0], + [FireworkType.BasicPeony, + Vec3(-50, 0, 140), + rP(), + rS(), + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + rT(), + 3.0], + [FireworkType.AdvancedPeony, + Vec3(-90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(0, 0, 90), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.GlowFlare, + Vec3(-90, 0, 80), + Vec3(120, 0, 0), + 1.5, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 3.0, + 3.0], + [FireworkType.Ring, + Vec3(-90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.2], + [FireworkType.Ring, + Vec3(-30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.2], + [FireworkType.Ring, + Vec3(30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.2], + [FireworkType.Ring, + Vec3(90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Bees, + Vec3(0, 50, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.0], + [FireworkType.TrailBurst, + Vec3(-70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.TrailBurst, + Vec3(70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.PalmTree, + Vec3(0, 40, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.Chrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Saturn, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.Saturn, + Vec3(-90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 90), + Vec3(-120, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(-60, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 110), + Vec3(0, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(60, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 130), + Vec3(120, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 2.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.AmericanFlag, + Vec3(0, 0, 230), + Vec3(-50, 0, 0), + rS(), + rC(), + rC(), + rT(), + 6], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + 2.5, + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 140), + rP(), + rS(), + rC(), + rC(), + 2.5, + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + 2.5, + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 140), + rP(), + rS(), + rC(), + rC(), + 2.5, + 3.0], + [FireworkType.Mickey, + Vec3(0, 0, 100), + rP(), + 1.4, + rC(), + rC(), + 2.0, + 10.0]], + PartyGlobals.FireworkShows.Summer: [[FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademPeony, + Vec3(0, 0, 70), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 130), + Vec3(0, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 3.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 90), + Vec3(-50, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 2.5, + 0.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 90), + Vec3(50, 0, 0), + rS(), + Vec4(0.1, 0.5, 1, 1), + Vec4(1, 1, 1, 1), + 2.5, + 2.0], + [FireworkType.DiademChrysanthemum, + Vec3(40, 50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + Vec3(-40, -50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.GlowFlare, + Vec3(-90, 0, 80), + Vec3(120, 0, 0), + 1.5, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 3.0, + 5.5], + [FireworkType.DiademChrysanthemum, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.DiademChrysanthemum, + Vec3(0, 0, 100), + Vec3(0, 20, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(0, 20, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.DiademChrysanthemum, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 5.0], + [FireworkType.AdvancedPeony, + Vec3(-90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(0, 0, 90), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.AdvancedPeony, + Vec3(90, 0, 110), + rP(), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.Mickey, + Vec3(70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Bees, + Vec3(0, 0, 100), + rP(), + 1.4, + rC(), + rC(), + 2.0, + 4.0], + [FireworkType.Chrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.0], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.GlowFlare, + Vec3(200, 0, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.GlowFlare, + Vec3(150, 10, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(100, 20, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(50, 30, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 40, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 1.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.GlowFlare, + Vec3(0, 0, 100), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 5.0], + [FireworkType.GlowFlare, + Vec3(-15, 0, 60), + Vec3(0, 0, 0), + rS(), + Vec4(1, 1, 0.4, 1), + Vec4(1, 1, 1, 1), + 2.5, + 0.0], + [FireworkType.GlowFlare, + Vec3(15, 0, 60), + Vec3(0, 0, 0), + rS(), + Vec4(1, 1, 0.4, 1), + Vec4(1, 1, 1, 1), + 2.5, + 0.0], + [FireworkType.IceCream, + Vec3(0, 0, 80), + Vec3(0, 0, 0), + 1.0, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 0.0], + [FireworkType.IceCream, + Vec3(0, 0, 110), + Vec3(0, 0, 0), + 0.6, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 0.0], + [FireworkType.IceCream, + Vec3(0, 0, 130), + Vec3(0, 0, 0), + 0.3, + Vec4(1, 1, 1, 1), + Vec4(1, 1, 1, 1), + 1.5, + 10.0]], + ToontownGlobals.NEWYEARS_FIREWORKS: [[FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(-120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(-10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 180), + Vec3(-10, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 120), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 1.0], + [FireworkType.GlowFlare, + Vec3(0, 0, 80), + Vec3(-120, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 1.5, + 2.0], + [FireworkType.GlowFlare, + Vec3(-180, 0, 180), + Vec3(-60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 2.5, + 0.15], + [FireworkType.GlowFlare, + Vec3(180, 0, 180), + Vec3(60, 0, 0), + rS(), + rC(), + Vec4(1, 1, 1, 1), + 2.5, + 0.15], + [FireworkType.DiademChrysanthemum, + Vec3(40, 50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + Vec3(-40, -50, 140), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.DiademChrysanthemum, + Vec3(-140, 50, 120), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(70, -40, 90), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(-100, 30, 60), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(0, 20, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(-70, 0, 130), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + Vec3(120, 50, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 3.5], + [FireworkType.Mickey, + Vec3(70, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.5], + [FireworkType.DiademPeony, + Vec3(90, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(30, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademPeony, + Vec3(-90, 0, 100), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Chrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.15], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + rV(), + rP(), + rS(), + rC(), + rC(), + rT(), + 3.0], + [FireworkType.Saturn, + Vec3(0, 0, 100), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.Saturn, + Vec3(20, 0, 70), + Vec3(-120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademPeony, + Vec3(-30, 0, 120), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.5], + [FireworkType.DiademPeony, + Vec3(0, 0, 90), + Vec3(120, 0, 0), + rS(), + rC(), + rC(), + rT(), + 4.0], + [FireworkType.DiademPeony, + Vec3(-140, 50, 120), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(70, -40, 90), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 0.25], + [FireworkType.DiademPeony, + Vec3(-100, 30, 60), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 2.25, + 0.25], + [FireworkType.DiademChrysanthemum, + Vec3(0, 20, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 2.0], + [FireworkType.DiademPeony, + Vec3(-70, 0, 130), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + rT(), + 1.5], + [FireworkType.DiademChrysanthemum, + Vec3(120, 50, 100), + Vec3(0, 0, 0), + rS(), + rC(), + rC(), + 1.5, + 5.0], + [FireworkType.Bees, + Vec3(0, 0, 100), + rP(), + 1.4, + rC(), + rC(), + 2.0, + 10.0]]} + showData[ToontownGlobals.COMBO_FIREWORKS] = showData[ToontownGlobals.NEWYEARS_FIREWORKS] + sectionData = {ToontownGlobals.JULY4_FIREWORKS: [(0, 24), (24, len(showData[ToontownGlobals.JULY4_FIREWORKS]))], + PartyGlobals.FireworkShows.Summer: [(0, 24), (24, len(showData[PartyGlobals.FireworkShows.Summer]))], + ToontownGlobals.NEWYEARS_FIREWORKS: [(0, len(showData[PartyGlobals.FireworkShows.Summer]))], + ToontownGlobals.COMBO_FIREWORKS: [(0, len(showData[PartyGlobals.FireworkShows.Summer]))]} + showMusic = {} + + @classmethod + def isValidShowType(cls, showType = -1): + if showType in cls.showData.keys(): + return True + else: + return False + + def __init__(self, showType = ToontownGlobals.NEWYEARS_FIREWORKS): + NodePath.__init__(self, 'FireworkShow') + self.showType = showType + self.sectionIvals = [] + self.fireworks = [] + self.delaySectionStart = None + self.curSection = None + self.curOffset = 0.0 + return + + def beginSection(self, startIndex, endIndex, offset): + taskMgr.remove('beginSection' + str(startIndex) + str(endIndex)) + sectionIval = Parallel() + time = 2.0 + showMusic = self.showMusic.get(self.showType) + if showMusic: + base.musicMgr.load(showMusic, looping=False) + musicOffset = self.getDuration(0, startIndex) - self.getDuration(startIndex, startIndex) + offset + sectionIval.append(Func(base.musicMgr.request, showMusic, priority=2, looping=False)) + sectionIval.append(Func(base.musicMgr.offsetMusic, musicOffset)) + sectionData = self.showData.get(self.showType)[startIndex:endIndex] + for fireworkInfo in sectionData: + typeId = fireworkInfo[0] + velocity = fireworkInfo[1] + pos = fireworkInfo[2] + scale = fireworkInfo[3] + color1 = fireworkInfo[4] + color2 = fireworkInfo[5] + if color2 == -1: + color2 = color1 + trailDur = fireworkInfo[6] + delay = fireworkInfo[7] + firework = Firework(typeId, velocity, scale, color1, color2, trailDur) + firework.reparentTo(self) + firework.setPos(pos) + self.fireworks.append(firework) + sectionIval.append(Sequence(Wait(time), firework.generateFireworkIval())) + time += delay + + self.sectionIvals.append(sectionIval) + self.curSection = sectionIval + self.curOffset = offset + self.delaySectionStart = FrameDelayedCall('delaySectionStart', self.startCurSection, frames=24) + + def startCurSection(self): + self.curSection.start(self.curOffset) + + def begin(self, timestamp): + time = 0.0 + for section in self.sectionData.get(self.showType): + startIndex = section[0] + endIndex = section[1] + sectionDur = self.getDuration(startIndex, endIndex) + if timestamp < sectionDur: + timestamp = max(0.0, timestamp) + taskMgr.doMethodLater(time, self.beginSection, 'beginSection' + str(startIndex) + str(endIndex), extraArgs=[startIndex, endIndex, timestamp]) + time = time + sectionDur - timestamp + timestamp -= sectionDur + + def getDuration(self, startIndex = 0, endIndex = None): + duration = 0.0 + if endIndex == None: + endIndex = len(self.showData.get(self.showType)) + for firework in self.showData.get(self.showType)[startIndex:endIndex]: + duration += firework[7] + + return duration + + def getShowDuration(self, eventId = None): + duration = 0.0 + if eventId: + for firework in self.showData[eventId]: + duration += firework[7] + + else: + for firework in self.showData[self.showType]: + duration += firework[7] + + return duration + + def isPlaying(self): + for ival in self.sectionIvals: + if ival.isPlaying(): + return True + + return False + + def cleanupShow(self): + if self.delaySectionStart: + self.delaySectionStart.destroy() + del self.delaySectionStart + self.delaySectionStart = None + showMusic = self.showMusic.get(self.showType) + if showMusic: + base.musicMgr.requestFadeOut(showMusic) + for section in self.sectionData.get(self.showType): + startIndex = section[0] + endIndex = section[1] + taskMgr.remove('beginSection' + str(startIndex) + str(endIndex)) + + for ival in self.sectionIvals: + ival.pause() + del ival + ival = None + + self.sectionIvals = [] + for firework in self.fireworks: + firework.cleanup() + del firework + firework = None + + self.fireworks = [] + return diff --git a/toontown/effects/FireworkShowMixin.py b/toontown/effects/FireworkShowMixin.py new file mode 100644 index 0000000..d39dab9 --- /dev/null +++ b/toontown/effects/FireworkShowMixin.py @@ -0,0 +1,283 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.hood import * +import Fireworks +import FireworkShows +from FireworkGlobals import skyTransitionDuration, preShowPauseDuration, postShowPauseDuration, preNormalMusicPauseDuration +from toontown.effects.FireworkShow import FireworkShow + +class FireworkShowMixin: + notify = DirectNotifyGlobal.directNotify.newCategory('FireworkShowMixin') + + def __init__(self, restorePlaygroundMusic = True, startDelay = 0.0): + self.currentShow = None + self.restorePlaygroundMusic = restorePlaygroundMusic + self.startDelay = startDelay + self.timestamp = None + self.fireworkShow = None + self.eventId = JULY4_FIREWORKS + self.accept('MusicEnabled', self.startMusic) + return + + def disable(self): + if self.currentShow: + self.currentShow.pause() + self.currentShow = None + if base.cr.config.GetBool('want-old-fireworks', 0): + ivalMgr.finishIntervalsMatching('shootFirework*') + else: + self.destroyFireworkShow() + from toontown.hood import DDHood + if isinstance(self.getHood(), DDHood.DDHood): + self.getHood().whiteFogColor = Vec4(0.8, 0.8, 0.8, 1) + self.restoreCameraLens() + if hasattr(self.getHood(), 'loader'): + self.getGeom().clearColorScale() + if hasattr(self.getHood(), 'sky'): + self.getSky().show() + self.getSky().clearColorScale() + if hasattr(base, 'localAvatar') and base.localAvatar: + base.localAvatar.clearColorScale() + base.setBackgroundColor(DefaultBackgroundColor) + self.ignoreAll() + return + + def startMusic(self): + if self.timestamp: + self.getLoader().music.stop() + t = globalClockDelta.localElapsedTime(self.timestamp) - self.startDelay + base.playMusic(self.showMusic, 0, 1, 0.8, max(0, t)) + + def shootFirework(self, x, y, z, style, color1, color2): + amp = 5 + Fireworks.shootFirework(style, x, y, z, color1, color2, amp) + + def startShow(self, eventId, style, timestamp, root = render): + t = globalClockDelta.localElapsedTime(timestamp) - self.startDelay + self.timestamp = timestamp + self.showMusic = None + self.eventId = eventId + if base.config.GetBool('want-old-fireworks', 0): + self.currentShow = self.getFireworkShowIval(eventId, style, t) + if self.currentShow: + self.currentShow.start(t) + else: + self.createFireworkShow() + if t > self.fireworkShow.getShowDuration(): + return + preShow = self.preShow(eventId, t) + postShow = self.postShow(eventId) + beginFireworkShow = Func(self.beginFireworkShow, max(0, t), root) + self.currentShow = Sequence(preShow, beginFireworkShow, Wait(max(0, self.fireworkShow.getShowDuration() - max(0, t))), postShow) + self.currentShow.start() + return + + def preShow(self, eventId, startT): + if eventId == JULY4_FIREWORKS: + instructionMessage = TTLocalizer.FireworksInstructions + startMessage = TTLocalizer.FireworksJuly4Beginning + endMessage = TTLocalizer.FireworksJuly4Ending + musicFile = 'phase_4/audio/bgm/tt_party2.mid' + elif eventId == NEWYEARS_FIREWORKS: + instructionMessage = TTLocalizer.FireworksInstructions + startMessage = TTLocalizer.FireworksNewYearsEveBeginning + endMessage = TTLocalizer.FireworksNewYearsEveEnding + musicFile = 'phase_4/audio/bgm/tt_s_ara_gen_fireworks_auldLangSyne.mid' + elif eventId == PartyGlobals.FireworkShows.Summer: + instructionMessage = TTLocalizer.FireworksActivityInstructions + startMessage = TTLocalizer.FireworksActivityBeginning + endMessage = TTLocalizer.FireworksActivityEnding + musicFile = 'phase_4/audio/bgm/tt_summer.mid' + elif eventId == COMBO_FIREWORKS: + instructionMessage = TTLocalizer.FireworksInstructions + startMessage = TTLocalizer.FireworksComboBeginning + endMessage = TTLocalizer.FireworksComboEnding + musicFile = 'phase_4/audio/bgm/tt_party2.mid' + else: + FireworkShowMixin.notify.warning('Invalid fireworks event ID: %d' % eventId) + return None + self.showMusic = loader.loadMusic(musicFile) + self.showMusic.setVolume(1) + + def __lightDecorationOn__(): + place = base.cr.playGame.getPlace() + if place is None: + return + if hasattr(place, 'halloweenLights'): + if not self.__checkStreetValidity(): + return + else: + place.halloweenLights = base.cr.playGame.getPlace().loader.geom.findAllMatches('**/*light*').asList() + place.halloweenLights.extend(base.cr.playGame.getPlace().loader.geom.findAllMatches('**/*lamp*').asList()) + for light in place.halloweenLights: + light.setColorScaleOff(0) + + elif not self.__checkHoodValidity(): + return + else: + place.loader.hood.halloweenLights = base.cr.playGame.hood.loader.geom.findAllMatches('**/*light*').asList() + place.loader.hood.halloweenLights.extend(base.cr.playGame.hood.loader.geom.findAllMatches('**/*lamp*').asList()) + for light in base.cr.playGame.hood.halloweenLights: + light.setColorScaleOff(0) + + if self.fireworkShow and not self.fireworkShow.isEmpty(): + self.fireworkShow.setColorScaleOff(0) + return + + if self.__checkHoodValidity() 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(Func(base.localAvatar.setSystemMessage, 0, startMessage), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(0.0, 0.0, 0.0, 1.0)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(0.25, 0.25, 0.35, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(0.85, 0.85, 0.85, 1)), Func(__lightDecorationOn__)), Func(base.setBackgroundColor, Vec4(0, 0, 0, 1)), Func(self.__checkDDFog), Func(base.camLens.setFar, 1000.0), Func(base.cr.playGame.hood.sky.hide), Func(base.localAvatar.setSystemMessage, 0, instructionMessage), Func(self.getLoader().music.stop), Wait(2.0), Func(base.playMusic, self.showMusic, 0, 1, 0.8, max(0, startT))) + return preShow + return None + + def restoreCameraLens(self): + hood = self.getHood() + from toontown.hood import * + if isinstance(hood, OZHood.OZHood): + base.camLens.setFar(SpeedwayCameraFar) + elif isinstance(hood, GSHood.GSHood): + base.camLens.setFar(SpeedwayCameraFar) + else: + base.camLens.setFar(DefaultCameraFar) + + def postShow(self, eventId): + if eventId == JULY4_FIREWORKS: + endMessage = TTLocalizer.FireworksJuly4Ending + elif eventId == NEWYEARS_FIREWORKS: + endMessage = TTLocalizer.FireworksNewYearsEveEnding + elif eventId == PartyGlobals.FireworkShows.Summer: + endMessage = TTLocalizer.FireworksActivityEnding + elif eventId == COMBO_FIREWORKS: + endMessage = TTLocalizer.FireworksComboEnding + else: + FireworkShowMixin.notify.warning('Invalid fireworks event ID: %d' % eventId) + return None + if self.__checkHoodValidity() and hasattr(base.cr.playGame.hood, 'sky') and base.cr.playGame.hood.sky: + postShow = Sequence(Func(base.cr.playGame.hood.sky.show), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.sky, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 2.5, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.localAvatar, 2.5, Vec4(1, 1, 1, 1))), Func(self.__restoreDDFog), Func(self.restoreCameraLens), Func(base.setBackgroundColor, DefaultBackgroundColor), Func(self.showMusic.stop), Func(base.localAvatar.setSystemMessage, 0, endMessage)) + if self.restorePlaygroundMusic: + postShow.append(Wait(2.0)) + postShow.append(Func(base.playMusic, self.getLoader().music, 1, 1, 0.8)) + return postShow + + def createFireworkShow(self): + if not self.fireworkShow: + self.fireworkShow = FireworkShow(self.eventId) + + def destroyFireworkShow(self): + if self.fireworkShow: + self.fireworkShow.cleanupShow() + self.fireworkShow = None + return + + def beginFireworkShow(self, timeStamp, root): + if self.fireworkShow and not self.fireworkShow.isPlaying(): + self.fireworkShow.begin(timeStamp) + self.fireworkShow.reparentTo(root) + hood = self.getHood() + from toontown.hood import * + if isinstance(hood, TTHood.TTHood): + self.fireworkShow.setPos(150, 0, 80) + self.fireworkShow.setHpr(90, 0, 0) + elif isinstance(hood, BRHood.BRHood): + self.fireworkShow.setPos(-200, -60, 50) + self.fireworkShow.setHpr(270, 0, 0) + elif isinstance(hood, MMHood.MMHood): + self.fireworkShow.setPos(150, -25, 40) + self.fireworkShow.setHpr(90, 0, 0) + elif isinstance(hood, DGHood.DGHood): + self.fireworkShow.setPos(-80, -50, 60) + self.fireworkShow.setHpr(0, 0, 0) + elif isinstance(hood, DLHood.DLHood): + self.fireworkShow.setPos(-160, 0, 80) + self.fireworkShow.setHpr(270, 0, 0) + elif isinstance(hood, GSHood.GSHood): + self.fireworkShow.setPos(60, -350, 80) + self.fireworkShow.setHpr(20, 0, 0) + elif isinstance(hood, DDHood.DDHood): + self.fireworkShow.setPos(150, 0, 50) + self.fireworkShow.setHpr(90, 0, 0) + elif isinstance(hood, OZHood.OZHood): + self.fireworkShow.setPos(-450, -80, 140) + self.fireworkShow.setHpr(300, 0, 0) + elif isinstance(hood, PartyHood.PartyHood): + self.fireworkShow.setPos(0, -400, 120) + self.fireworkShow.lookAt(0, 0, 0) + self.fireworkShow.setScale(1.8) + + def getFireworkShowIval(self, eventId, index, startT): + show = FireworkShows.getShow(eventId, index) + if show is None: + FireworkShowMixin.notify.warning('could not find firework show: index: %s' % index) + return + preShow = self.preShow(eventId, startT) + mainShow = Sequence() + currentT = skyTransitionDuration + preShowPauseDuration + for effect in show: + waitTime, style, colorIndex1, colorIndex2, amp, x, y, z = effect + if waitTime > 0: + currentT += waitTime + mainShow.append(Wait(waitTime)) + if currentT >= startT: + mainShow.append(Func(Fireworks.shootFirework, style, x, y, z, colorIndex1, colorIndex2, amp)) + + postShow = self.postShow(eventId) + return Sequence(preShow, mainShow, postShow) + + def clearMyColorScales(self): + if self.getGeom() and not self.getGeom().isEmpty(): + self.getGeom().clearColorScale() + if self.getSky() and not self.getSky().isEmpty(): + self.getSky().clearColorScale() + + def getLoader(self): + if base.cr.playGame.hood != None: + return base.cr.playGame.hood.loader + return + + def getHood(self): + if base.cr.playGame.hood != None: + return base.cr.playGame.hood + return + + def getGeom(self): + loader = self.getLoader() + if loader: + return loader.geom + return None + + def getSky(self): + hood = self.getHood() + if hood: + return hood.sky + return None + + def __checkDDFog(self): + from toontown.hood import DDHood + if isinstance(self.getHood(), DDHood.DDHood): + self.getHood().whiteFogColor = Vec4(0.2, 0.2, 0.2, 1) + if hasattr(base.cr.playGame.getPlace(), 'cameraSubmerged'): + if not base.cr.playGame.getPlace().cameraSubmerged: + self.getHood().setWhiteFog() + + def __restoreDDFog(self): + from toontown.hood import DDHood + if isinstance(self.getHood(), DDHood.DDHood): + self.getHood().whiteFogColor = Vec4(0.8, 0.8, 0.8, 1) + if hasattr(base.cr.playGame.getPlace(), 'cameraSubmerged'): + if not base.cr.playGame.getPlace().cameraSubmerged: + self.getHood().setWhiteFog() + + def __checkStreetValidity(self): + if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() 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: + return False + + def __checkHoodValidity(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return True + else: + return False diff --git a/toontown/effects/FireworkShows.py b/toontown/effects/FireworkShows.py new file mode 100644 index 0000000..2c4c1c5 --- /dev/null +++ b/toontown/effects/FireworkShows.py @@ -0,0 +1,13881 @@ +from FireworkGlobals import * +from toontown.toonbase import ToontownGlobals +from toontown.parties import PartyGlobals +shows = {ToontownGlobals.JULY4_FIREWORKS: [((2, + ROCKET, + RED, + RED, + 5, + 80, + 88, + 30), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 80, + -88, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + -66, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + 66, + 30), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 80, + 44, + 30), + (0.1, + ROCKET, + RED, + RED, + 5, + 80, + -44, + 30), + (0.1, + ROCKET, + RED, + RED, + 5, + 80, + 22, + 30), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 80, + -22, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + 0, + 30), + (2, + CIRCLELARGE, + RED, + RED, + 20, + 120, + -2, + 120), + (0.1, + CIRCLE, + RED, + RED, + 12, + 120, + -30, + 150), + (0.1, + CIRCLE, + RED, + RED, + 12, + 120, + 26, + 150), + (1.5, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + -40, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + -20, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + 20, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 80, + 40, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 6, + 80, + -40, + 70), + (0.2, + CIRCLE, + RED, + RED, + 7, + 80, + -20, + 70), + (0.2, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 80, + 0, + 70), + (0.2, + CIRCLE, + RED, + RED, + 7, + 80, + 20, + 70), + (0.2, + CIRCLE, + BLUE, + BLUE, + 6, + 80, + 40, + 70), + (1, + CIRCLE, + WHITE, + WHITE, + 14, + 80, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 7, + 80, + -10, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 7, + 80, + 10, + 90), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 40, + 0, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 6, + -40, + 0, + 60), + (0.2, + CIRCLE, + RED, + RED, + 7, + -20, + 0, + 60), + (0.2, + CIRCLE, + WHITE, + WHITE, + 8, + 0, + 0, + 60), + (0.2, + CIRCLE, + RED, + RED, + 7, + 20, + 0, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 6, + 40, + 0, + 60), + (1.5, + CIRCLE, + WHITE, + WHITE, + 10, + 0, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + -10, + 0, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 40, + 0, + 0), + (1.5, + CIRCLE, + BLUE, + RED, + 10, + -30, + -30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -30, + -30, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 30, + -30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 30, + -30, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + -30, + 30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -30, + 30, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 30, + 30, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 30, + 30, + 80), + (2, + CIRCLELARGE, + RED, + WHITE, + 16, + 0, + 0, + 100), + (2, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (2, + CIRCLE, + RED, + RED, + 8, + -10, + 0, + 90), + (0.3, + CIRCLE, + BLUE, + BLUE, + 10, + 0, + 20, + 60), + (0.3, + CIRCLE, + RED, + RED, + 5, + 10, + 10, + 80), + (0.5, + CIRCLE, + RED, + BLUE, + 8, + -10, + 0, + 90), + (0.4, + CIRCLE, + RED, + BLUE, + 10, + 0, + 10, + 60), + (0.4, + CIRCLE, + RED, + WHITE, + 8, + -10, + 0, + 90), + (0.5, + CIRCLE, + BLUE, + WHITE, + 10, + 0, + -20, + 80), + (0.4, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (1, + CIRCLE, + WHITE, + WHITE, + 15, + 0, + 0, + 100), + (1.5, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + 0), + (2, + RING, + RED, + RED, + 8, + 0, + 0, + 100), + (0.1, + RING, + BLUE, + BLUE, + 8, + 0, + 0, + 100), + (1.5, + ROCKET, + RED, + RED, + 5, + 82, + -47, + 20), + (0.1, + ROCKET, + RED, + RED, + 5, + 93, + 45, + 26), + (0.1, + ROCKET, + RED, + RED, + 5, + 37, + -47, + 20), + (0.1, + ROCKET, + RED, + RED, + 5, + 60, + 45, + 26), + (1.5, + ROCKET, + BLUE, + BLUE, + 5, + 82, + -47, + 20), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 93, + 45, + 26), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 37, + -47, + 20), + (0.1, + ROCKET, + BLUE, + BLUE, + 5, + 60, + 45, + 26), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 40, + 0, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 6, + -40, + 0, + 60), + (0.1, + CIRCLE, + BLUE, + BLUE, + 6, + 40, + 0, + 60), + (0.1, + CIRCLE, + RED, + RED, + 7, + -20, + 0, + 60), + (0.1, + CIRCLE, + RED, + RED, + 7, + 20, + 0, + 60), + (0.1, + CIRCLE, + WHITE, + WHITE, + 8, + 0, + 0, + 60), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (1, + CIRCLE, + WHITE, + WHITE, + 12, + 0, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 6, + -10, + 0, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 6, + 10, + 0, + 90), + (1, + RING, + RED, + WHITE, + 10, + 0, + 0, + 80), + (0.1, + RING, + BLUE, + WHITE, + 5, + -10, + 0, + 90), + (0.1, + RING, + BLUE, + WHITE, + 5, + 10, + 0, + 90), + (1, + CIRCLE, + BLUE, + RED, + 10, + -20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + -20, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + -20, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + -20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + 20, + 80), + (0.5, + CIRCLE, + BLUE, + RED, + 10, + 20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + 20, + 80), + (2, + CIRCLE, + WHITE, + RED, + 8, + -10, + 0, + 90), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + -21, + 30), + (0.3, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + RED, + 5, + 10, + 10, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + RED, + 8, + -10, + 0, + 90), + (0.3, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + -20, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 5, + 10, + 0, + 90), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + -21, + 30), + (1, + CIRCLELARGE, + RED, + RED, + 12, + 0, + 0, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 14, + 0, + 0, + 90), + (0.2, + CIRCLELARGE, + BLUE, + BLUE, + 16, + 0, + 0, + 100)), + ((1, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.8, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.6, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.4, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (2, + CIRCLE, + WHITE, + YELLOW, + 5, + 0, + 80, + 80), + (1, + CIRCLE, + PURPLE, + PURPLE, + 4, + -15, + 70, + 90), + (0.5, + CIRCLE, + GREEN, + GREEN, + 6, + 15, + 100, + 85), + (1, + CIRCLE, + CYAN, + CYAN, + 8, + 20, + 90, + 90), + (0.6, + CIRCLE, + YELLOW, + WHITE, + 4, + 30, + 110, + 90), + (1, + CIRCLE, + PINK, + PINK, + 5, + 10, + 80, + 80), + (0.3, + CIRCLE, + YELLOW, + YELLOW, + 5, + 0, + 80, + 90), + (1, + CIRCLE, + WHITE, + WHITE, + 8, + -20, + 120, + 85), + (0.4, + CIRCLE, + PINK, + YELLOW, + 8, + 30, + 85, + 90), + (0.7, + CIRCLE, + YELLOW, + PINK, + 8, + 10, + 90, + 100), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + 10, + 90, + 0), + (2, + CIRCLE, + YELLOW, + YELLOW, + 8, + 10, + 90, + 70), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 10, + 90, + 70), + (0.8, + CIRCLE, + YELLOW, + YELLOW, + 8, + 10, + 90, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 10, + 90, + 80), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + 10, + 90, + 0), + (2, + CIRCLE, + GREEN, + GREEN, + 8, + 10, + 90, + 70), + (0.2, + RING, + GREEN, + GREEN, + 8, + 10, + 90, + 70), + (0.8, + CIRCLE, + GREEN, + GREEN, + 8, + 10, + 90, + 80), + (0.2, + RING, + GREEN, + GREEN, + 8, + 10, + 90, + 80), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + 10, + 90, + 0), + (2, + CIRCLE, + PINK, + PINK, + 8, + 10, + 90, + 75), + (0.2, + RING, + PINK, + PINK, + 8, + 10, + 90, + 75), + (0.8, + CIRCLE, + PINK, + PINK, + 8, + 10, + 90, + 85), + (0.2, + RING, + PINK, + PINK, + 8, + 10, + 90, + 85), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 70, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + 20, + 70, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 110, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + 20, + 110, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 70, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + -20, + 70, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 110, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 10, + -20, + 110, + 60), + (1.5, + CIRCLE, + PURPLE, + PINK, + 8, + -30, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 9, + -15, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 10, + 0, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 9, + 15, + 110, + 60), + (0.5, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 110, + 60), + (1.5, + CIRCLE, + GREEN, + CYAN, + 8, + 30, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 9, + 15, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 10, + 0, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 9, + -15, + 110, + 70), + (0.5, + CIRCLE, + GREEN, + CYAN, + 8, + -30, + 110, + 70), + (2, + CIRCLE, + WHITE, + YELLOW, + 6, + 0, + 80, + 80), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 8, + -15, + 70, + 90), + (0.5, + CIRCLE, + GREEN, + GREEN, + 6, + 15, + 100, + 85), + (1, + CIRCLE, + CYAN, + CYAN, + 8, + 20, + 90, + 90), + (0.5, + CIRCLE, + YELLOW, + WHITE, + 7, + 30, + 110, + 90), + (1, + CIRCLE, + PINK, + PINK, + 12, + 10, + 80, + 80), + (0.6, + CIRCLELARGE, + YELLOW, + YELLOW, + 5, + 0, + 80, + 90), + (1, + CIRCLE, + PINK, + YELLOW, + 8, + 30, + 85, + 90), + (0.5, + CIRCLE, + YELLOW, + PINK, + 12, + 10, + 90, + 100), + (0.4, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 90, + 60), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.8, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 90, + 70), + (0.8, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 0, + 90, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 0, + 90, + 80), + (0.8, + CIRCLELARGE, + WHITE, + WHITE, + 12, + 0, + 90, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 0, + 90, + 80), + (1.5, + RING, + YELLOW, + YELLOW, + 6, + 20, + 110, + 70), + (0.4, + RING, + YELLOW, + YELLOW, + 6, + 0, + 90, + 65), + (0.4, + RING, + YELLOW, + YELLOW, + 8, + -10, + 100, + 80), + (0.4, + RING, + YELLOW, + YELLOW, + 8, + 10, + 110, + 60), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.4, + RING, + YELLOW, + YELLOW, + 12, + -20, + 90, + 70), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 1, + 92, + 0), + (0.4, + CIRCLELARGE, + WHITE, + WHITE, + 12, + 0, + 90, + 80), + (0.7, + RING, + YELLOW, + YELLOW, + 6, + 0, + 90, + 65), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + -10, + 100, + 80), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 10, + 110, + 60), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + -20, + 90, + 70), + (1, + CIRCLESPRITE, + WHITE, + FLOWER, + 24, + 1, + 92, + 80)), + ((1, + ROCKET, + WHITE, + YELLOW, + 3, + 63, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + 63, + -88, + 10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + -88, + 10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -22, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + -22, + -88, + 10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -83, + 88, + 10), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + -83, + -88, + 10), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 30, + 0, + -20), + (2, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 0, + 80), + (0.2, + CIRCLE, + PURPLE, + PINK, + 8, + 10, + 0, + 80), + (0.4, + CIRCLE, + PURPLE, + PINK, + 8, + 0.5, + 0, + 80), + (0.5, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 20, + 80), + (0.2, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + -20, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 30, + 0, + 100), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (2, + CIRCLE, + YELLOW, + PINK, + 8, + 0, + 0, + 80), + (0.2, + CIRCLE, + YELLOW, + PINK, + 8, + -20, + 0, + 80), + (0.4, + CIRCLE, + YELLOW, + PINK, + 8, + 20, + 0, + 80), + (0.5, + CIRCLE, + YELLOW, + PINK, + 8, + 0, + 20, + 80), + (0.2, + CIRCLE, + YELLOW, + PINK, + 8, + 0, + -20, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 0, + 0, + 100), + (1.5, + ROCKET, + WHITE, + YELLOW, + 5, + -30, + 0, + -20), + (2, + CIRCLE, + PEACH, + PINK, + 8, + -30, + 0, + 80), + (0.2, + CIRCLE, + PEACH, + PINK, + 8, + 0.5, + 0, + 80), + (0.4, + CIRCLE, + PEACH, + PINK, + 8, + 10, + 0, + 80), + (0.5, + CIRCLE, + PEACH, + PINK, + 8, + -30, + 20, + 80), + (0.2, + CIRCLE, + PEACH, + PINK, + 8, + -30, + -20, + 80), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 10, + -30, + 0, + 100), + (2, + CIRCLE, + PEACH, + PEACH, + 3, + 0, + 0, + 80), + (0.5, + RING, + YELLOW, + WHITE, + 3, + 0, + 0, + 80), + (0.1, + RING, + YELLOW, + WHITE, + 3, + 0, + 0, + 80), + (1, + CIRCLE, + PEACH, + PEACH, + 9, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + WHITE, + 9, + 0, + 0, + 100), + (0.1, + RING, + YELLOW, + WHITE, + 9, + 0, + 0, + 100), + (1, + ROCKET, + YELLOW, + YELLOW, + 5, + 80, + 88, + 30), + (0.1, + ROCKET, + YELLOW, + YELLOW, + 5, + 80, + -88, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + 80, + -66, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + 80, + 66, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + 80, + 44, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + 80, + -44, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + 80, + 22, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + 80, + -22, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + 80, + 0, + 30), + (2, + ROCKET, + YELLOW, + YELLOW, + 5, + -80, + 88, + 30), + (0.1, + ROCKET, + YELLOW, + YELLOW, + 5, + -80, + -88, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + -80, + -66, + 30), + (0.1, + ROCKET, + PURPLE, + PURPLE, + 5, + -80, + 66, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + -80, + 44, + 30), + (0.1, + ROCKET, + PINK, + PINK, + 5, + -80, + -44, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + -80, + 22, + 30), + (0.1, + ROCKET, + PEACH, + PEACH, + 5, + -80, + -22, + 30), + (0.1, + ROCKET, + WHITE, + WHITE, + 5, + -80, + 0, + 30), + (2, + CIRCLE, + PURPLE, + PURPLE, + 6, + -40, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 8, + -40, + 0, + 100), + (1, + CIRCLE, + BLUE, + BLUE, + 6, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 8, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 10, + 0, + 0, + 100), + (1, + CIRCLE, + PURPLE, + PURPLE, + 8, + 40, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 10, + 40, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 40, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 14, + 40, + 0, + 100), + (1, + CIRCLELARGE, + PURPLE, + PURPLE, + 8, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 8, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 8, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 10, + 0, + 0, + 100), + (0.2, + RING, + YELLOW, + YELLOW, + 12, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 30, + 30, + -20), + (0.2, + ROCKET, + WHITE, + YELLOW, + 4, + 30, + -30, + -20), + (0.2, + ROCKET, + WHITE, + YELLOW, + 6, + -30, + 30, + -20), + (0.2, + ROCKET, + WHITE, + YELLOW, + 8, + -30, + -30, + -20), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 16, + 20, + -2, + 120), + (0.2, + CIRCLE, + WHITE, + WHITE, + 12, + 20, + -30, + 150), + (0.2, + CIRCLE, + WHITE, + WHITE, + 12, + 20, + 26, + 150), + (1, + CIRCLE, + WHITE, + WHITE, + 6, + 0, + 0, + 100), + (0.5, + RING, + YELLOW, + YELLOW, + 6, + 0, + 0, + 100), + (0.2, + RING, + PURPLE, + PURPLE, + 8, + 0, + 0, + 100), + (0.2, + RING, + PEACH, + PEACH, + 10, + 0, + 0, + 100), + (0.2, + RING, + PINK, + PINK, + 12, + 0, + 0, + 100), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (0.2, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 10, + 30, + 80), + (0.3, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 20, + -20, + 90), + (0.5, + CIRCLESMALL, + WHITE, + WHITE, + 4, + -30, + 0, + 100), + (0.4, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 15, + -10, + 95), + (0.3, + CIRCLESMALL, + WHITE, + WHITE, + 4, + 0, + 0, + 100), + (1, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 105), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 0, + -20), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 8, + 0, + 0, + 100), + (0.1, + RING, + RED, + WHITE, + 8, + 0, + 0, + 100), + (0.2, + RING, + BLUE, + WHITE, + 4, + 10, + 30, + 80), + (0.3, + RING, + RED, + WHITE, + 6, + 20, + -20, + 90), + (0.1, + RING, + BLUE, + WHITE, + 8, + -30, + 0, + 100), + (0.4, + RING, + RED, + WHITE, + 6, + 15, + -10, + 95), + (0.1, + RING, + BLUE, + WHITE, + 9, + 0, + 0, + 100), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 0, + 0, + 100), + (0.5, + CIRCLE, + RED, + WHITE, + 20, + 20, + 20, + 105), + (0.2, + CIRCLE, + BLUE, + WHITE, + 20, + -30, + 40, + 110), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 30, + 0, + -20), + (2, + CIRCLE, + PURPLE, + PINK, + 8, + 30, + 0, + 80), + (0.2, + CIRCLE, + YELLOW, + PINK, + 8, + 10, + 0, + 80), + (0.4, + CIRCLE, + PINK, + WHITE, + 8, + 0.5, + 0, + 80), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 8, + 30, + 20, + 80), + (0.2, + CIRCLE, + BLUE, + PURPLE, + 8, + 30, + -20, + 80), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 10, + 30, + 0, + 100), + (0.1, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.2, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.1, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.2, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90)), + ((3, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (2, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -35, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -30, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -10, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -5, + 60), + (1.5, + CIRCLE, + PINK, + PINK, + 14, + 145, + -100, + 100), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 14, + 145, + -60, + 120), + (0.5, + CIRCLE, + PINK, + PINK, + 14, + 145, + -20, + 140), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 14, + 145, + 20, + 120), + (0.5, + CIRCLE, + PINK, + PINK, + 14, + 145, + 60, + 100), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (2, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -5, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -10, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -30, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -35, + 60), + (2, + CIRCLELARGE, + PINK, + PINK, + 14, + 145, + -20, + 120), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -38, + 140), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -2, + 140), + (1, + CIRCLE, + PURPLE, + PINK, + 10, + 120, + -60, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 120, + -60, + 80), + (0.5, + CIRCLE, + PINK, + PURPLE, + 10, + 0.5, + -60, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 0.5, + -60, + 80), + (0.5, + CIRCLE, + PURPLE, + YELLOW, + 10, + 120, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 120, + 20, + 80), + (0.5, + CIRCLE, + YELLOW, + PURPLE, + 10, + 0.5, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 0.5, + 20, + 80), + (0.5, + CIRCLE, + WHITE, + WHITE, + 14, + 145, + -20, + 140), + (0.1, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.2, + POP, + WHITE, + WHITE, + 20, + 0, + 0, + 90), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 6, + 140, + -80, + 80), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 6, + 140, + 40, + 80), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 10, + 140, + -60, + 100), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 10, + 140, + 20, + 100), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 14, + 140, + -40, + 120), + (0.5, + CIRCLE, + YELLOW, + YELLOW, + 14, + 140, + 0, + 120), + (0.5, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 29, + -10), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -70, + -10), + (2, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (1.5, + RING, + PURPLE, + PURPLE, + 8, + 140, + -20, + 120), + (0.8, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.1, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.8, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (0.1, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (2, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -5, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -10, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -30, + 60), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -35, + 60), + (2, + CIRCLELARGE, + PINK, + PINK, + 14, + 145, + -20, + 120), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -38, + 140), + (0.1, + CIRCLE, + PINK, + PINK, + 14, + 145, + -2, + 140), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 6, + 140, + -80, + 80), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.1, + CIRCLE, + YELLOW, + YELLOW, + 6, + 140, + 40, + 80), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + CIRCLE, + PURPLE, + PURPLE, + 10, + 140, + -60, + 100), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.1, + CIRCLE, + YELLOW, + YELLOW, + 10, + 140, + 20, + 100), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (0, + CIRCLE, + PURPLE, + PURPLE, + 14, + 140, + -40, + 120), + (0.1, + CIRCLE, + YELLOW, + YELLOW, + 14, + 140, + 0, + 120), + (0.5, + ROCKET, + WHITE, + YELLOW, + 4, + 145, + -20, + 60), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (0.4, + RING, + PURPLE, + PURPLE, + 8, + 140, + -20, + 120), + (0.1, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -70, + -10), + (0.4, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.1, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + -39, + -10), + (0.1, + RING, + PINK, + PINK, + 10, + 140, + -20, + 120), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + -20, + -10), + (0.5, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 118, + 0, + -10), + (0.1, + RING, + YELLOW, + YELLOW, + 12, + 140, + -20, + 120), + (0.4, + ROCKET, + WHITE, + YELLOW, + 5, + 114, + 29, + -10), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 25, + 140, + -20, + 140), + (0.3, + CIRCLE, + WHITE, + WHITE, + 20, + 140, + -40, + 120), + (0.2, + CIRCLE, + PINK, + WHITE, + 22, + 130, + 40, + 140), + (0.4, + CIRCLE, + PURPLE, + WHITE, + 12, + 0.5, + -30, + 110), + (0.8, + CIRCLE, + WHITE, + PINK, + 20, + 140, + -60, + 100), + (0.5, + CIRCLE, + PURPLE, + YELLOW, + 20, + 0.5, + 0.5, + 120), + (0.2, + CIRCLE, + PURPLE, + PURPLE, + 16, + 140, + -20, + 90), + (0.4, + CIRCLE, + PURPLE, + PEACH, + 20, + 0.5, + -30, + 110), + (0.8, + CIRCLE, + WHITE, + PINK, + 16, + 140, + -20, + 140), + (0.5, + CIRCLE, + PINK, + YELLOW, + 22, + 0.5, + 10, + 120), + (0.2, + CIRCLE, + PEACH, + PURPLE, + 20, + 140, + -20, + 90), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 140, + -20, + 140), + (0.6, + CIRCLESPRITE, + WHITE, + MUSICNOTE, + 30, + 140, + -20, + 140)), + ((1, + ROCKET, + WHITE, + BLUE, + 3, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 5, + -117, + -39, + 0), + (2, + CIRCLE, + WHITE, + WHITE, + 16, + -117, + -39, + 100), + (0.3, + CIRCLE, + WHITE, + BLUE, + 20, + -140, + -40, + 70), + (0.2, + CIRCLE, + BLUE, + WHITE, + 22, + -130, + 40, + 70), + (0.8, + CIRCLE, + WHITE, + CYAN, + 20, + -140, + -60, + 80), + (0.5, + CIRCLE, + CYAN, + BLUE, + 20, + -.5, + 0.5, + 90), + (0.2, + CIRCLE, + BLUE, + CYAN, + 16, + -140, + -20, + 80), + (0.8, + CIRCLE, + WHITE, + WHITE, + 16, + -140, + -20, + 80), + (0.5, + CIRCLE, + BLUE, + WHITE, + 22, + -.5, + 10, + 90), + (0.2, + CIRCLE, + CYAN, + BLUE, + 20, + -140, + -20, + 80), + (0, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (0.5, + ROCKET, + WHITE, + BLUE, + 4, + -117, + -39, + 0), + (1.7, + CIRCLE, + WHITE, + WHITE, + 16, + -117, + -39, + 100), + (1, + CIRCLE, + WHITE, + WHITE, + 12, + -147, + -39, + 100), + (0.8, + CIRCLE, + WHITE, + WHITE, + 12, + -87, + -39, + 100), + (0.2, + CIRCLE, + WHITE, + WHITE, + 12, + -107, + -59, + 100), + (0.4, + CIRCLE, + WHITE, + WHITE, + 12, + -127, + -59, + 100), + (0.3, + CIRCLE, + WHITE, + WHITE, + 12, + -107, + -19, + 100), + (0.5, + CIRCLE, + WHITE, + WHITE, + 12, + -127, + -19, + 100), + (1.8, + ROCKET, + BLUE, + WHITE, + 4, + -117, + -39, + 0), + (2, + CIRCLE, + BLUE, + WHITE, + 10, + -117, + -39, + 80), + (0.8, + CIRCLE, + BLUE, + WHITE, + 12, + -117, + -39, + 100), + (0.5, + CIRCLE, + BLUE, + WHITE, + 10, + -117, + -39, + 90), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -39, + 90), + (0.6, + CIRCLE, + BLUE, + WHITE, + 10, + -97, + -59, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -97, + -59, + 80), + (0.5, + CIRCLE, + BLUE, + WHITE, + 10, + -147, + -9, + 100), + (0.1, + RING, + WHITE, + WHITE, + 14, + -147, + -9, + 100), + (0.7, + CIRCLE, + BLUE, + WHITE, + 10, + -87, + -39, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -87, + -39, + 80), + (2.5, + ROCKET, + BLUE, + WHITE, + 3, + -137, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -127, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -107, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 3, + -97, + -39, + 0), + (1.5, + RING, + WHITE, + BLUE, + 14, + -117, + -39, + 90), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -39, + 90), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -39, + 90), + (1.5, + ROCKET, + BLUE, + WHITE, + 3, + -117, + -19, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -117, + -29, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -39, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -117, + -49, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 3, + -117, + -59, + 0), + (1.5, + RING, + WHITE, + WHITE, + 14, + -117, + -39, + 90), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -39, + 90), + (0.1, + RING, + WHITE, + BLUE, + 14, + -117, + -39, + 90), + (1.5, + CIRCLE, + WHITE, + WHITE, + 20, + -100, + -40, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 20, + -140, + -40, + 80), + (0.1, + CIRCLE, + WHITE, + BLUE, + 10, + -110, + -40, + 100), + (0.1, + CIRCLE, + WHITE, + BLUE, + 10, + -130, + -40, + 100), + (0.1, + CIRCLE, + WHITE, + WHITE, + 8, + -120, + -40, + 120), + (1.5, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 120), + (0.2, + CIRCLE, + BLUE, + WHITE, + 22, + -130, + 0, + 140), + (0.4, + CIRCLE, + BLUE, + WHITE, + 12, + -110, + -30, + 110), + (0.8, + CIRCLE, + WHITE, + BLUE, + 20, + -120, + -60, + 100), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.2, + CIRCLE, + BLUE, + CYAN, + 16, + -140, + -20, + 90), + (0.5, + CIRCLE, + CYAN, + BLUE, + 22, + -130, + 10, + 120), + (0.2, + CIRCLE, + BLUE, + WHITE, + 20, + -110, + -20, + 90), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + -120, + -40, + 140), + (1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -59, + 0), + (0.1, + ROCKET, + BLUE, + WHITE, + 5, + -117, + -19, + 0), + (2, + RING, + WHITE, + BLUE, + 14, + -117, + -59, + 80), + (0.1, + RING, + WHITE, + BLUE, + 14, + -117, + -19, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -59, + 80), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -19, + 80), + (0.1, + RING, + WHITE, + WHITE, + 14, + -117, + -59, + 80), + (0.1, + RING, + BLUE, + WHITE, + 14, + -117, + -19, + 80), + (2, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -20, + 0), + (0.2, + CIRCLE, + BLUE, + WHITE, + 22, + -130, + 0, + 140), + (0.8, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -20, + 0), + (0.5, + CIRCLE, + WHITE, + BLUE, + 20, + -120, + -60, + 100), + (0.4, + CIRCLE, + BLUE, + BLUE, + 20, + -110, + -30, + 110), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -60, + 0), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.5, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -60, + 0), + (1, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -20, + 0), + (0.8, + CIRCLE, + WHITE, + BLUE, + 16, + -140, + -20, + 140), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -20, + 0), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.4, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (0.1, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -60, + 0), + (1, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -60, + 0), + (0.8, + CIRCLE, + WHITE, + BLUE, + 16, + -140, + -20, + 140), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -20, + 0), + (0.7, + CIRCLE, + WHITE, + BLUE, + 16, + -140, + -20, + 140), + (0.3, + ROCKET, + BLUE, + WHITE, + 4, + -135, + -20, + 0), + (1, + CIRCLELARGE, + WHITE, + WHITE, + 20, + -120, + -40, + 140), + (0, + ROCKET, + BLUE, + WHITE, + 4, + -105, + -60, + 0), + (0.5, + CIRCLE, + BLUE, + BLUE, + 20, + -100, + 10, + 120), + (1, + ROCKET, + BLUE, + WHITE, + 4, + -115, + -35, + 0), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -120, + -35, + 0), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -115, + -45, + 0), + (0.2, + ROCKET, + BLUE, + WHITE, + 4, + -120, + -45, + 0), + (1.2, + CIRCLELARGE, + WHITE, + WHITE, + 20, + -120, + -40, + 120), + (1, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 100), + (0.8, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 110), + (0.8, + CIRCLE, + WHITE, + WHITE, + 20, + -120, + -40, + 100), + (1, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 30, + -117, + -39, + 110)), + ((1, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + -60, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (2, + CIRCLE, + BLUE, + GREEN, + 6, + -60, + 0, + 60), + (0.2, + CIRCLE, + GREEN, + BLUE, + 7, + -40, + 0, + 60), + (0.2, + CIRCLE, + WHITE, + WHITE, + 8, + -20, + 0, + 60), + (0.2, + CIRCLE, + GREEN, + GREEN, + 7, + 0, + 0, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 6, + 20, + 0, + 60), + (1.5, + CIRCLE, + WHITE, + WHITE, + 10, + 0, + 0, + 80), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + -10, + 0, + 90), + (0.1, + CIRCLE, + WHITE, + WHITE, + 5, + 10, + 0, + 90), + (1, + CIRCLE, + GREEN, + BLUE, + 10, + -20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + -20, + 80), + (0.5, + CIRCLE, + GREEN, + BLUE, + 10, + 20, + -20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + -20, + 80), + (0.5, + CIRCLE, + GREEN, + BLUE, + 10, + -20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + -20, + 20, + 80), + (0.5, + CIRCLE, + GREEN, + BLUE, + 10, + 20, + 20, + 80), + (0.1, + RING, + WHITE, + WHITE, + 10, + 20, + 20, + 80), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -60, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -40, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -20, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 0, + 0, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + 20, + 0, + 0), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (1, + CIRCLELARGE, + BLUE, + WHITE, + 16, + 0, + 0, + 100), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + 120, + -21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 3, + -23, + 3, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 10, + -23, + 3, + 80), + (0.1, + ROCKET, + WHITE, + YELLOW, + 4, + -23, + 3, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 14, + -23, + 3, + 90), + (0.1, + ROCKET, + WHITE, + YELLOW, + 5, + -23, + 3, + 0), + (2, + CIRCLE, + BLUE, + BLUE, + 18, + -23, + 3, + 100), + (1.5, + ROCKET, + WHITE, + RED, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + RED, + 6, + 120, + -21, + 30), + (0.3, + CIRCLE, + WHITE, + RED, + 10, + 0, + 20, + 60), + (0.4, + CIRCLE, + WHITE, + RED, + 5, + 10, + 10, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + RED, + 5, + 10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + RED, + 8, + -10, + 0, + 90), + (0.3, + CIRCLE, + WHITE, + RED, + 10, + 0, + -20, + 80), + (0.2, + CIRCLE, + WHITE, + RED, + 5, + 10, + 0, + 90), + (2, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + YELLOW, + 6, + 120, + -21, + 30), + (0.2, + CIRCLE, + WHITE, + YELLOW, + 5, + 10, + 10, + 80), + (0.4, + CIRCLE, + WHITE, + YELLOW, + 8, + -10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + YELLOW, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + YELLOW, + 5, + 10, + 0, + 90), + (0.4, + CIRCLE, + WHITE, + YELLOW, + 10, + 0, + -20, + 80), + (0.3, + CIRCLE, + WHITE, + YELLOW, + 10, + 0, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + YELLOW, + 5, + 10, + 0, + 110), + (2, + ROCKET, + WHITE, + BLUE, + 6, + 120, + 21, + 30), + (0.1, + ROCKET, + WHITE, + BLUE, + 6, + 120, + -21, + 30), + (0.3, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + BLUE, + 5, + 10, + 10, + 80), + (0.4, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + 10, + 60), + (0.3, + CIRCLE, + WHITE, + BLUE, + 5, + 10, + 0, + 90), + (0.2, + CIRCLE, + WHITE, + BLUE, + 8, + -10, + 0, + 90), + (0.4, + CIRCLE, + WHITE, + BLUE, + 10, + 0, + -20, + 80), + (0.2, + CIRCLE, + WHITE, + BLUE, + 5, + 10, + 0, + 90), + (2.5, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (2, + CIRCLE, + BLUE, + GREEN, + 8, + -60, + 20, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 8, + 20, + -20, + 60), + (0.6, + CIRCLE, + GREEN, + BLUE, + 9, + -40, + 20, + 60), + (0.2, + CIRCLE, + GREEN, + GREEN, + 9, + 0, + -20, + 60), + (0.6, + CIRCLE, + WHITE, + WHITE, + 11, + -20, + 20, + 60), + (0.2, + CIRCLE, + WHITE, + WHITE, + 11, + -20, + -20, + 60), + (0.6, + CIRCLE, + GREEN, + GREEN, + 9, + 0, + 20, + 60), + (0.2, + CIRCLE, + GREEN, + BLUE, + 9, + -40, + -20, + 60), + (0.6, + CIRCLE, + BLUE, + BLUE, + 8, + 20, + 20, + 60), + (0.2, + CIRCLE, + BLUE, + GREEN, + 8, + -60, + -20, + 60), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (2, + RING, + BLUE, + GREEN, + 8, + -60, + 20, + 60), + (0.1, + RING, + BLUE, + BLUE, + 8, + 20, + -20, + 60), + (0.5, + RING, + GREEN, + BLUE, + 9, + -40, + 20, + 60), + (0.1, + RING, + GREEN, + GREEN, + 9, + 0, + -20, + 60), + (0.5, + RING, + WHITE, + WHITE, + 11, + -20, + 20, + 60), + (0.1, + RING, + WHITE, + WHITE, + 11, + -20, + -20, + 60), + (0.5, + RING, + GREEN, + GREEN, + 9, + 0, + 20, + 60), + (0.1, + RING, + GREEN, + BLUE, + 9, + -40, + -20, + 60), + (0.5, + RING, + BLUE, + BLUE, + 8, + 20, + 20, + 60), + (0.1, + RING, + BLUE, + GREEN, + 8, + -60, + -20, + 60), + (2, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (1, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.4, + CIRCLE, + RED, + RED, + 15, + -30, + -10, + 80), + (0.6, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.2, + CIRCLE, + RED, + RED, + 12, + -10, + -30, + 85), + (0.2, + CIRCLE, + BLUE, + BLUE, + 10, + 20, + -30, + 90), + (0.6, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (0.2, + CIRCLE, + BLUE, + BLUE, + 8, + -20, + 10, + 100), + (0.3, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + 8, + 0), + (0.5, + RING, + WHITE, + WHITE, + 11, + -20, + 20, + 60), + (0, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + 8, + 0), + (0.1, + RING, + WHITE, + WHITE, + 11, + -20, + -20, + 60), + (0.2, + CIRCLE, + BLUE, + BLUE, + 12, + -20, + 10, + 90), + (0.2, + ROCKET, + WHITE, + YELLOW, + 3, + -28, + -2, + 0), + (0.2, + CIRCLE, + BLUE, + BLUE, + 14, + -23, + 3, + 80), + (0.3, + ROCKET, + WHITE, + YELLOW, + 3, + -18, + -2, + 0), + (0.3, + CIRCLE, + RED, + RED, + 14, + -23, + 3, + 90), + (0.3, + CIRCLELARGE, + WHITE, + WHITE, + 16, + -23, + 3, + 100))], + ToontownGlobals.NEWYEARS_FIREWORKS: [((0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 113.22, + 1.05, + 73.24), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 115.54, + 13.8, + 59.11), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 118.36, + -13.83, + 57.71), + (0.22, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05, + 25.37, + 57.01), + (0.03, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05, + -25.37, + 57.01), + (3.22, + CIRCLELARGE, + WHITE, + WHITE, + 10.0, + 121.75, + 1.07, + 106.69), + (1.94, + CIRCLELARGE, + RED, + RED, + 10.0, + 121.75, + 1.07, + 106.69), + (1.31, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.75, + 28.63, + 38.55), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.75, + 1.3, + 38.76), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81, + -25.6, + 37.54), + (0.88, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.81, + -25.6, + 37.54), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.74, + 1.3, + 37.75), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81, + 27.34, + 37.12), + (0.82, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0, + 40.0, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0, + 40.0, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0, + 40.0, + 40.0), + (0.72, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0, + -40.0, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0, + -40.0, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0, + -40.0, + 40.0), + (0.84, + CIRCLE, + BLUE, + BLUE, + 10.0, + 121.81, + 29.29, + 78.62), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 121.81, + 29.29, + 78.62), + (0.75, + CIRCLE, + RED, + RED, + 10.0, + 120.0, + -30.0, + 100.0), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 120.0, + -30.0, + 100.0), + (0.7, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81, + 29.29, + 92.0), + (0.19, + RING, + BLUE, + BLUE, + 5.0, + 121.81, + 29.29, + 92.0), + (0.8, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81, + 0.0, + 100.0), + (0.08, + RING, + RED, + RED, + 5.0, + 121.81, + 0.0, + 100.0), + (1.0, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.43, + 22.69, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.24, + 17.57, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 10.82, + 12.13, + 4.0), + (0.22, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.17, + 6.76, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.1, + 1.91, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.71, + -3.56, + 4.0), + (0.19, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.26, + -8.51, + 4.0), + (0.28, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.25, + -14.18, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.2, + -19.42, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.53, + -24.46, + 4.0), + (1.24, + CIRCLESMALL, + RED, + WHITE, + 15.0, + 68.68, + -29.58, + 57.72), + (1.63, + CIRCLE, + BLUE, + WHITE, + 15.0, + 70.33, + 29.76, + 56.3), + (0.87, + CIRCLELARGE, + WHITE, + WHITE, + 15.0, + 121.64, + 1.2, + 92.64), + (1.26, + RING, + WHITE, + WHITE, + 15.0, + 83.12, + 12.21, + 92.04), + (0.44, + RING, + RED, + RED, + 15.0, + 84.41, + -23.72, + 78.01), + (0.44, + RING, + WHITE, + WHITE, + 15.0, + 83.5, + 1.77, + 68.65), + (0.38, + RING, + BLUE, + BLUE, + 15.0, + 82.6, + 26.64, + 68.65), + (0.37, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 74.5, + 15.0, + 71.8), + (0.12, + CIRCLELARGE, + RED, + RED, + 11.0, + 74.5, + 15.0, + 71.8), + (0.75, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 94.03, + -15.23, + 62.12), + (0.13, + CIRCLELARGE, + BLUE, + BLUE, + 11.0, + 94.03, + -15.23, + 62.12), + (0.56, + CIRCLESMALL, + RED, + RED, + 11.0, + 54.56, + 7.87, + 53.74), + (0.19, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 54.56, + 7.87, + 53.74), + (0.63, + CIRCLESMALL, + BLUE, + BLUE, + 11.0, + 82.51, + 0.23, + 82.99), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 82.51, + 0.23, + 82.99), + (0.37, + RING, + RED, + RED, + 5.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 6.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + BLUE, + BLUE, + 7.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 8.0, + 82.51, + 0.23, + 82.99), + (0.47, + RING, + RED, + RED, + 10.0, + 82.51, + 0.23, + 82.99), + (0.37, + ROCKET, + WHITE, + WHITE, + 8.0, + 53.85, + 14.92, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 54.66, + -16.71, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.27, + 15.16, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.38, + -15.98, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.33, + 15.17, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.71, + -15.73, + 4.0), + (0.89, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 151.88, + 1.92, + 144.56), + (0.93, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.25, + 16.72, + 82.5), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 79.31, + 1.0, + 63.69), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.02, + -13.25, + 79.16), + (0.88, + ROCKET, + WHITE, + RED, + 4.0, + 6.47, + -15.0, + 8.6), + (0.06, + ROCKET, + WHITE, + RED, + 4.0, + 18.41, + 15.0, + 4.0), + (0.31, + ROCKET, + WHITE, + BLUE, + 4.0, + 53.79, + 15.0, + 4.0), + (0.06, + ROCKET, + WHITE, + BLUE, + 4.0, + 54.85, + -15.0, + 4.0), + (0.5, + ROCKET, + RED, + RED, + 4.0, + 90.45, + -15.0, + 4.0), + (0.19, + ROCKET, + RED, + RED, + 4.0, + 90.21, + 15.0, + 4.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 4.0, + 90.21, + 0.0, + 4.0), + (0.44, + CIRCLE, + WHITE, + WHITE, + 4.0, + 18.41, + 15.0, + 90.0), + (0.06, + RING, + RED, + RED, + 1.0, + 18.41, + 15.0, + 90.0), + (0.75, + CIRCLE, + WHITE, + WHITE, + 4.0, + 93.56, + -16.02, + 72.89), + (0.06, + RING, + RED, + RED, + 1.0, + 93.56, + -16.02, + 72.89), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0, + 53.19, + -10.03, + 98.97), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 53.19, + -10.03, + 98.97), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0, + 71.46, + 13.03, + 56.82), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 71.46, + 13.03, + 56.82), + (0.25, + CIRCLE, + RED, + RED, + 4.0, + 89.38, + -3.18, + 95.18), + (0.06, + RING, + WHITE, + WHITE, + 1.0, + 89.38, + -3.18, + 95.18), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 19.0, + 94.74, + 0.89, + 65.55), + (0.44, + ROCKET, + WHITE, + PEACH, + 3.0, + 96.5, + 2.5, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 95.93, + -2.61, + 4.0), + (0.38, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.08, + 7.72, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.54, + -8.28, + 4.0), + (0.37, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.41, + 12.05, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.76, + -13.51, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 3.0, + 36.29, + 16.92, + 4.0), + (0.1, + ROCKET, + WHITE, + PEACH, + 3.0, + 37.03, + -19.08, + 4.0), + (0.96, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31, + 2.5, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31, + -2.5, + 66.59), + (0.44, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25, + 8.0, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25, + -8.0, + 66.59), + (0.44, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77, + 12.0, + 66.59), + (0.06, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77, + -12.0, + 66.59), + (0.44, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0, + 16.0, + 66.59), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0, + -16.0, + 66.59), + (0.94, + CIRCLESPRITE, + WHITE, + ICECREAM, + 6.0, + 10.0, + 0.0, + 66.59), + (2.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.13, + ROCKET, + RED, + RED, + 5.0, + 90.0, + 23.91, + 19.44), + (0.44, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.06, + ROCKET, + RED, + RED, + 5.0, + 90.0, + -23.91, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + 23.91, + 19.44), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + -23.91, + 19.44), + (0.63, + ROCKET, + RED, + RED, + 5.0, + 50.0, + 23.91, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + 23.91, + 19.44), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 90.0, + -23.91, + 19.44), + (0.94, + ROCKET, + WHITE, + WHITE, + 5.0, + 74.23, + -0.06, + 4.0), + (0.28, + ROCKET, + BLUE, + BLUE, + 5.0, + 53.66, + 0.14, + 13.48), + (0.28, + ROCKET, + RED, + RED, + 5.0, + 53.96, + -0.38, + 4.0), + (0.28, + ROCKET, + WHITE, + WHITE, + 5.0, + 63.67, + 10.54, + 4.0), + (0.34, + ROCKET, + WHITE, + WHITE, + 4.0, + 63.88, + -10.98, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.24, + -4.44, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.16, + 2.92, + 4.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.12, + ROCKET, + RED, + RED, + 5.0, + 90.0, + 23.91, + 19.44), + (0.34, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 90.0, + -23.91, + 19.44), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + 23.91, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + 23.91, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0, + -23.91, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0, + -23.91, + 19.44), + (0.34, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58, + 11.29, + 45.0), + (0.22, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22, + 3.33, + 60.0), + (0.37, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34, + -0.98, + 60.0), + (0.22, + ROCKET, + RED, + RED, + 5.0, + 108.45, + -9.33, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.53, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58, + 11.29, + 45.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22, + 3.33, + 60.0), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34, + -0.98, + 60.0), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 108.45, + -9.33, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.5, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.19, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22, + 3.33, + 60.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34, + -0.98, + 60.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79, + 19.8, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69, + -17.77, + 24.4), + (0.94, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.13, + CIRCLELARGE, + RED, + RED, + 5.0, + 40.83, + 10.33, + 65.41), + (0.25, + CIRCLE, + WHITE, + WHITE, + 5.0, + 103.3, + 12.66, + 83.93), + (0.25, + RING, + RED, + RED, + 5.0, + 95.27, + -9.92, + 72.55), + (0.37, + POP, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.13, + CIRCLELARGE, + BLUE, + RED, + 5.0, + 107.72, + -11.81, + 50.51), + (0.5, + CIRCLESMALL, + BLUE, + WHITE, + 5.0, + 102.29, + -8.59, + 80.37), + (0.31, + RING, + BLUE, + BLUE, + 5.0, + 49.85, + 12.65, + 59.45), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 22.91, + -0.7, + 68.75), + (0.13, + CIRCLELARGE, + WHITE, + RED, + 5.0, + 91.87, + 0.99, + 72.32), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82, + -2.09, + 49.31), + (0.25, + CIRCLE, + RED, + RED, + 5.0, + 2.49, + 6.37, + 43.93), + (0.37, + RING, + BLUE, + BLUE, + 5.0, + 84.93, + 8.5, + 77.25), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 85.37, + -5.29, + 75.3), + (0.44, + CIRCLELARGE, + RED, + RED, + 5.0, + 86.11, + -11.82, + 71.43), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 5.0, + 79.35, + 8.71, + 77.0), + (0.19, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 66.08, + 0.98, + 68.8)), + ((3, + ROCKET, + RED, + WHITE, + 1, + 1.44, + 92.5, + -5), + (1, + ROCKET, + WHITE, + WHITE, + 2, + 1.44, + 92.5, + -5), + (1, + ROCKET, + BLUE, + WHITE, + 3, + 1.44, + 92.5, + -5), + (2.5, + CIRCLE, + YELLOW, + WHITE, + 17, + 1.44, + 92.5, + 70), + (3.5, + ROCKET, + BLUE, + WHITE, + 2, + 1.44, + 92.5, + -5), + (1, + ROCKET, + WHITE, + WHITE, + 3, + 1.44, + 92.5, + -5), + (1, + ROCKET, + RED, + WHITE, + 4, + 1.44, + 92.5, + -5), + (2.25, + CIRCLE, + YELLOW, + WHITE, + 20, + 1.44, + 92.5, + 75), + (2.75, + ROCKET, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + -5), + (2.97, + RING, + RED, + RED, + 5, + 1.44, + 92.5, + 80), + (0.03, + RING, + RED, + RED, + 5, + 1.44, + 92.5, + 80), + (0.03, + RING, + RED, + RED, + 5, + 1.44, + 92.5, + 80), + (0.44, + RING, + BLUE, + BLUE, + 7.5, + 1.44, + 92.5, + 85), + (0.03, + RING, + BLUE, + BLUE, + 7.5, + 1.44, + 92.5, + 85), + (0.05, + RING, + BLUE, + BLUE, + 7.5, + 1.44, + 92.5, + 85), + (0.39, + RING, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + 90), + (0.03, + RING, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + 90), + (0.03, + RING, + WHITE, + WHITE, + 5, + 1.44, + 92.5, + 90), + (4, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (1.5, + CIRCLESPRITE, + WHITE, + FLOWER, + 5, + 1.44, + 92.5, + 30), + (0.5, + CIRCLESPRITE, + RED, + FLOWER, + 7.5, + 1.44, + 92.5, + 0.5), + (0.63, + CIRCLESPRITE, + BLUE, + FLOWER, + 10, + 1.44, + 92.5, + 70), + (3.38, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (1, + CIRCLESPRITE, + YELLOW, + FLOWER, + 20, + 1.44, + 92.5, + 40), + (0.5, + CIRCLESPRITE, + GREEN, + FLOWER, + 25, + 1.44, + 92.5, + 60), + (0.5, + CIRCLESPRITE, + PINK, + FLOWER, + 30, + 1.44, + 92.5, + 80), + (3.5, + CIRCLELARGE, + BLUE, + WHITE, + 20, + -4.44, + 88.5, + 80), + (0.5, + CIRCLELARGE, + WHITE, + WHITE, + 20, + 6.44, + 92.5, + 80), + (0.5, + CIRCLELARGE, + RED, + WHITE, + 20, + 6.44, + 88.5, + 80), + (1.5, + CIRCLESMALL, + WHITE, + WHITE, + 30, + 1.44, + 92.5, + 80), + (0.5, + CIRCLE, + RED, + RED, + 40, + 1.44, + 92.5, + 90), + (0.5, + CIRCLELARGE, + BLUE, + BLUE, + 0.5, + 1.44, + 92.5, + 100), + (1, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (0.22, + POP, + BLUE, + WHITE, + 25, + 1.44, + 92.5, + 70), + (0.91, + POP, + BLUE, + WHITE, + 25, + 1.44, + 92.5, + 70), + (0.12, + POP, + BLUE, + BLUE, + 15, + 1.44, + 92.5, + 90), + (0.13, + POP, + BLUE, + WHITE, + 25, + 1.44, + 92.5, + 70), + (1.62, + CIRCLESMALL, + BLUE, + BLUE, + 20, + -4.44, + 88.5, + 90), + (0.5, + CIRCLESMALL, + RED, + RED, + 20, + 6.44, + 92.5, + 90), + (0.5, + CIRCLESMALL, + WHITE, + WHITE, + 20, + 6.44, + 88.5, + 90), + (2, + CIRCLE, + WHITE, + WHITE, + 40, + -4.44, + 88.5, + 100), + (0.5, + CIRCLE, + BLUE, + BLUE, + 40, + 6.44, + 92.5, + 100), + (0.38, + CIRCLE, + RED, + RED, + 40, + 6.44, + 88.5, + 100), + (3.88, + POP, + WHITE, + WHITE, + 20, + 1.44, + 92.5, + 40), + (0.25, + POP, + WHITE, + WHITE, + 30, + 1.44, + 92.5, + 40), + (0.25, + POP, + WHITE, + WHITE, + 40, + 1.44, + 92.5, + 40), + (1.69, + ROCKET, + BLUE, + BLUE, + 2, + 6.44, + 92.5, + -1), + (0.07, + ROCKET, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.05, + ROCKET, + WHITE, + WHITE, + 2, + -4.44, + 88.5, + -1), + (0.87, + ROCKET, + RED, + RED, + 4, + 6.44, + 88.5, + -1), + (0.07, + ROCKET, + WHITE, + WHITE, + 4, + -4.44, + 88.5, + -1), + (0.05, + ROCKET, + BLUE, + BLUE, + 4, + 6.44, + 92.5, + -1), + (0.87, + ROCKET, + BLUE, + BLUE, + 8, + 6.44, + 92.5, + -1), + (0.07, + ROCKET, + WHITE, + WHITE, + 8, + -4.44, + 88.5, + -1), + (0.05, + ROCKET, + RED, + RED, + 8, + 6.44, + 88.5, + -1), + (0.87, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.07, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.05, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.87, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.07, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.05, + POP, + RED, + RED, + 2, + 6.44, + 88.5, + -1), + (0.87, + CIRCLELARGE, + BLUE, + BLUE, + 2, + 6.44, + 88.5, + 80), + (0.06, + CIRCLELARGE, + RED, + RED, + 2, + -4.44, + 88.5, + 80), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 2, + 6.44, + 92.5, + 80), + (0.44, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.06, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.06, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.49, + CIRCLE, + BLUE, + WHITE, + 40, + 6.44, + 88.5, + 100), + (0.06, + CIRCLE, + WHITE, + WHITE, + 40, + -4.44, + 88.5, + 100), + (0.06, + CIRCLE, + RED, + WHITE, + 40, + 6.01, + 91.87, + 96.32), + (0.55, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.04, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.05, + POP, + RED, + WHITE, + 30, + 6.44, + 92.5, + 100), + (0.41, + CIRCLELARGE, + BLUE, + WHITE, + 30, + -4.44, + 88.5, + 120), + (0.06, + CIRCLELARGE, + RED, + WHITE, + 30, + 6.44, + 92.5, + 120), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 30, + 6.44, + 88.5, + 120)), + ((1.0, + ROCKET, + PURPLE, + PURPLE, + 4.0, + -120.0, + -71.66, + 52.06), + (0.06, + ROCKET, + PURPLE, + PURPLE, + 4.0, + -120.0, + 71.17, + 52.45), + (0.81, + RING, + RED, + RED, + 4.0, + -76.73, + -12.67, + 38.84), + (0.75, + RING, + BLUE, + BLUE, + 4.0, + -76.18, + 13.97, + 53.23), + (0.5, + RING, + PURPLE, + PURPLE, + 4.0, + -66.77, + -1.55, + 46.73), + (0.13, + RING, + PINK, + PINK, + 4.0, + -66.77, + -1.55, + 46.73), + (0.12, + RING, + BLUE, + BLUE, + 4.0, + -66.77, + -1.55, + 46.73), + (1.13, + ROCKET, + RED, + RED, + 4.0, + -62.87, + -56.53, + -5.17), + (0.06, + ROCKET, + RED, + RED, + 4.0, + -62.67, + 56.28, + -5.13), + (0.06, + ROCKET, + RED, + RED, + 4.0, + -116.1, + -53.82, + -1.46), + (0.06, + ROCKET, + RED, + RED, + 4.0, + -116.34, + 52.84, + -1.33), + (1.44, + CIRCLELARGE, + PURPLE, + PURPLE, + 12.0, + -92.38, + -0.33, + 55.76), + (0.62, + RING, + PINK, + PINK, + 12.0, + -92.38, + -0.33, + 55.76), + (0.13, + RING, + RED, + RED, + 12.0, + -92.38, + -0.33, + 55.76), + (0.13, + RING, + BLUE, + BLUE, + 12.0, + -92.38, + -0.33, + 55.76), + (0.87, + ROCKET, + RED, + RED, + 12.0, + -63.5, + -56.18, + -4.62), + (0.03, + ROCKET, + RED, + RED, + 12.0, + -63.5, + 55.42, + -3.42), + (0.09, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + -56.5, + -3.09), + (0.03, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + 54.87, + -1.89), + (0.09, + ROCKET, + BLUE, + BLUE, + 12.0, + -118.26, + -55.07, + -1.63), + (0.03, + ROCKET, + BLUE, + BLUE, + 12.0, + -118.26, + 54.95, + -0.44), + (0.59, + ROCKET, + RED, + RED, + 12.0, + -117.97, + -56.18, + -4.62), + (0.03, + ROCKET, + RED, + RED, + 12.0, + -115.83, + 56.32, + -3.41), + (0.09, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + -55.17, + -3.08), + (0.03, + ROCKET, + PURPLE, + PURPLE, + 12.0, + -91.51, + 55.55, + -1.88), + (0.09, + ROCKET, + BLUE, + BLUE, + 12.0, + -64.8, + -55.07, + -1.63), + (0.03, + ROCKET, + BLUE, + BLUE, + 12.0, + -64.8, + 54.24, + -0.45), + (0.84, + CIRCLELARGE, + PURPLE, + PURPLE, + 12.0, + -92.38, + -0.33, + 55.76), + (0.13, + RING, + RED, + RED, + 5.0, + -92.38, + -0.33, + 55.76), + (1.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.54, + 75.3), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.34, + 75.34), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.38, + 70.28), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.49, + 70.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.85, + 65.71), + (0.25, + CIRCLESMALL, + RED, + RED, + 2.0, + -134.53, + -45.37, + 65.13), + (0.25, + CIRCLESMALL, + RED, + RED, + 2.0, + -134.53, + -40.51, + 65.14), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.65, + 65.14), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.38, + 60.39), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.49, + 60.42), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -50.07, + 55.81), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.34, + 54.95), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.11, + 75.23), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -22.78, + 70.35), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -17.6, + 70.36), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -24.2, + 65.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -16.5, + 65.04), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -26.71, + 60.43), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 2.0, + -134.53, + -22.94, + 60.14), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 2.0, + -134.53, + -18.54, + 60.15), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -13.04, + 60.6), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -30.32, + 55.1), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.37, + 55.13), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -5.65, + 74.82), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -0.46, + 75.43), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.26, + 75.74), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -5.18, + 70.39), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 7.41, + 70.12), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -5.49, + 65.35), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + -0.46, + 65.36), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.42, + 65.23), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + -5.42, + 58.9), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + -5.13, + 53.96), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.5, + 74.87), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 19.71, + 75.33), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 24.28, + 75.34), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 70.58), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 27.12, + 70.46), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 65.25), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 19.39, + 65.25), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 24.28, + 64.82), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 60.21), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 14.66, + 55.47), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.33, + 75.21), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 44.65, + 74.8), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 33.28, + 70.03), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 40.54, + 70.19), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.54, + 65.14), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.54, + 59.95), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.22, + 55.35), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 74.94), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.34, + 74.83), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 70.35), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -25.71, + 70.07), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.49, + 70.09), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 65.03), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -23.2, + 65.04), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.65, + 65.2), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.43, + 60.44), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -17.85, + 60.02), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.49, + 60.03), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.27, + 55.27), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -15.65, + 55.58), + (0.53, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.3, + 75.13), + (0.22, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.58, + 75.0), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -0.38, + 75.01), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 4.66, + 74.88), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.3, + 70.4), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.14, + 65.22), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.89, + 65.22), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -0.38, + 65.09), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.5, + -10.26, + 59.42), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.5, + -10.26, + 54.56), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.89, + 55.01), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -0.54, + 55.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + 4.35, + 55.17), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 9.71, + 75.04), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 29.59, + 75.38), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 11.44, + 70.15), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 28.01, + 70.19), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 12.23, + 65.26), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 19.96, + 65.13), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 27.07, + 65.29), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 12.86, + 60.22), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 16.78, + 60.16), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 21.54, + 60.39), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 26.12, + 60.25), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.75, + 55.63), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 24.38, + 55.35), + (0.53, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.16, + 75.06), + (0.22, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -25.4, + 75.39), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.5, + -35.69, + 68.32), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -29.33, + 70.21), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -32.63, + 65.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -32.78, + 60.44), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -32.78, + 55.41), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.37, + 74.96), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.49, + 74.83), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.3, + 74.99), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.74, + 74.85), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.53, + 70.08), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.21, + 65.2), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.65, + 64.91), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.14, + 65.07), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.05, + 60.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.21, + 55.43), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.02, + 55.14), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.62, + 55.15), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.74, + 55.3), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + 8.72, + 73.77), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 6.24, + 70.14), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 13.02, + 70.3), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.35, + 65.39), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.28, + 65.12), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + 1.92, + 60.18), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 7.9, + 60.22), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 2.0, + -134.53, + 11.91, + 60.22), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 16.17, + 60.23), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.5, + -0.85, + 54.23), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 19.49, + 55.34), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.7, + 75.22), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.28, + 75.23), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 34.97, + 74.65), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.86, + 69.88), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.34, + 70.06), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.7, + 65.29), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.75, + 64.85), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 34.49, + 65.16), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.54, + 59.8), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 37.66, + 60.12), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 24.54, + 55.2), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 40.82, + 54.63), + (0.5, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.47, + 75.06), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.45, + 75.07), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -45.49, + 70.47), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.58, + 70.5), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -34.67, + 65.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.47, + 62.05), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -45.65, + 59.53), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -45.65, + 55.39), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -40.47, + 55.55), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -35.92, + 55.26), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + -30.58, + 55.27), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.49, + 75.12), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.62, + 74.99), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.68, + 70.37), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -4.95, + 70.26), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.37, + 65.2), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.74, + 65.08), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -20.21, + 60.02), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -5.42, + 60.19), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -15.49, + 55.14), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 4.0, + -134.53, + -10.46, + 55.15), + (0.5, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 9.39, + 75.18), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.6, + 75.2), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.66, + 69.99), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 18.7, + 70.31), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 4.35, + 65.24), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 19.64, + 65.28), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 5.45, + 60.21), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 18.54, + 60.08), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 9.39, + 55.03), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 4.0, + -134.53, + 14.6, + 55.04), + (0.53, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.75, + 75.23), + (0.22, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.56, + 74.97), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.44, + 70.04), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.4, + 70.22), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 29.59, + 65.15), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 33.07, + 65.15), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 38.29, + 65.02), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.08, + 65.17), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.24, + 60.57), + (0.25, + CIRCLESMALL, + RED, + RED, + 4.0, + -134.53, + 42.08, + 55.23), + (0.28, + ROCKET, + RED, + RED, + 1.0, + -134.53, + 51.93, + 55.34), + (0.75, + CIRCLELARGE, + RED, + RED, + 5.0, + -90.69, + -19.74, + 38.96), + (0.22, + CIRCLESMALL, + PURPLE, + PURPLE, + 5.0, + -89.41, + 12.93, + 57.47), + (0.25, + RING, + BLUE, + BLUE, + 5.0, + -89.41, + -14.28, + 51.82), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 5.0, + -89.41, + 17.67, + 27.95), + (0.25, + CIRCLESMALL, + WHITE, + WHITE, + 5.0, + -89.41, + -15.05, + 31.01), + (0.25, + CIRCLELARGE, + BLUE, + RED, + 5.0, + -89.41, + -16.86, + 59.2), + (0.25, + RING, + RED, + PURPLE, + 5.0, + -89.41, + 9.76, + 46.8), + (0.25, + CIRCLESMALL, + PURPLE, + PURPLE, + 5.0, + -89.41, + 12.64, + 30.14), + (0.25, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + -89.41, + -16.2, + 36.45), + (0.38, + CIRCLESMALL, + PURPLE, + PURPLE, + 5.0, + -89.41, + -3.85, + 61.98), + (0.37, + RING, + RED, + RED, + 5.0, + -89.41, + 12.96, + 34.61), + (0.37, + CIRCLELARGE, + PURPLE, + PURPLE, + 5.0, + -89.41, + 13.46, + 66.24), + (0.25, + CIRCLESMALL, + BLUE, + RED, + 5.0, + -89.41, + -5.28, + 37.68), + (0.25, + RING, + RED, + RED, + 5.0, + -89.41, + -0.94, + 44.71), + (0.06, + RING, + PURPLE, + PURPLE, + 5.0, + -89.41, + -0.94, + 44.71), + (0.06, + RING, + BLUE, + BLUE, + 5.0, + -89.41, + -0.94, + 44.71)), + ((0.56, + CIRCLELARGE, + WHITE, + PEACH, + 10.0, + 137.92, + -20.14, + 107.4), + (0.94, + CIRCLE, + PEACH, + PEACH, + 10.0, + 137.92, + 63.27, + 50.69), + (0.06, + CIRCLE, + PEACH, + PEACH, + 10.0, + 137.92, + -101.42, + 50.69), + (0.69, + CIRCLE, + YELLOW, + YELLOW, + 10.0, + 137.92, + 30.99, + 73.3), + (0.06, + CIRCLE, + YELLOW, + YELLOW, + 10.0, + 137.92, + -72.63, + 73.3), + (0.69, + ROCKET, + PINK, + PINK, + 3.0, + 137.92, + 5.88, + 75.31), + (0.12, + ROCKET, + PINK, + PINK, + 3.0, + 137.92, + -45.83, + 75.31), + (0.13, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 137.92, + -19.45, + 75.31), + (1.75, + CIRCLELARGE, + WHITE, + PURPLE, + 15.0, + 137.92, + -19.45, + 170.91), + (1.0, + CIRCLE, + PINK, + PINK, + 15.0, + 137.92, + 27.97, + 118.05), + (0.12, + RING, + WHITE, + WHITE, + 15.0, + 137.92, + 27.97, + 118.05), + (0.62, + CIRCLE, + PURPLE, + PURPLE, + 15.0, + 137.92, + -72.79, + 118.05), + (0.13, + RING, + WHITE, + WHITE, + 15.0, + 137.92, + -72.79, + 118.05), + (0.75, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + 36.51, + -10.35), + (0.09, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + 20.55, + -3.8), + (0.09, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + 5.97, + 1.14), + (0.09, + ROCKET, + YELLOW, + YELLOW, + 5.0, + 132.97, + -9.7, + 5.16), + (0.59, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -77.37, + -10.5), + (0.13, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -60.43, + -3.31), + (0.13, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -47.12, + 1.14), + (0.12, + ROCKET, + PEACH, + PEACH, + 5.0, + 132.97, + -30.65, + 5.16), + (0.62, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + 36.51, + -10.35), + (0.13, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + 20.55, + -3.8), + (0.13, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + 5.97, + 1.14), + (0.12, + ROCKET, + PINK, + PINK, + 5.0, + 132.97, + -9.7, + 5.16), + (0.5, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -77.37, + -10.5), + (0.13, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -60.43, + -3.31), + (0.12, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -47.12, + 1.14), + (0.13, + ROCKET, + PURPLE, + PURPLE, + 5.0, + 132.97, + -30.65, + 5.16), + (0.75, + CIRCLELARGE, + YELLOW, + YELLOW, + 15.0, + 110.03, + 29.4, + 21.16), + (0.75, + CIRCLELARGE, + PEACH, + PEACH, + 15.0, + 110.03, + -68.21, + 21.16), + (0.75, + CIRCLELARGE, + PINK, + PINK, + 15.0, + 110.03, + 0.88, + 47.21), + (0.75, + CIRCLELARGE, + PURPLE, + PURPLE, + 15.0, + 110.03, + -58.67, + 68.78), + (0.75, + CIRCLESMALL, + WHITE, + YELLOW, + 15.0, + 110.03, + -50.66, + 48.46), + (0.31, + CIRCLESMALL, + WHITE, + PINK, + 15.0, + 110.03, + 13.95, + 48.97), + (0.25, + CIRCLELARGE, + PURPLE, + PURPLE, + 15.0, + 110.03, + -17.82, + 72.34), + (0.06, + RING, + WHITE, + WHITE, + 15.0, + 110.03, + -17.82, + 72.34), + (0.31, + CIRCLELARGE, + PEACH, + PEACH, + 15.0, + 110.03, + -17.82, + 72.34), + (0.06, + RING, + WHITE, + WHITE, + 15.0, + 110.03, + -17.82, + 72.34), + (0.38, + POP, + WHITE, + WHITE, + 15.0, + 110.03, + -17.82, + 72.34), + (2.62, + ROCKET, + PEACH, + WHITE, + 3.0, + 112.26, + 15.89, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 112.26, + -54.6, + -4.52), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 101.59, + 20.65, + -4.71), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 103.6, + -57.91, + -4.52), + (0.31, + ROCKET, + PEACH, + WHITE, + 3.0, + 99.07, + 31.56, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 98.42, + -68.05, + -4.52), + (0.31, + ROCKET, + PEACH, + WHITE, + 3.0, + 102.69, + -78.04, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 103.51, + 42.25, + -4.52), + (0.34, + ROCKET, + PEACH, + WHITE, + 3.0, + 114.48, + 45.43, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 112.58, + -82.93, + -4.52), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 123.92, + 40.62, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 122.92, + -79.72, + -4.52), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + 30.05, + -4.52), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.24, + -69.29, + -4.52), + (0.34, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + 14.58, + -0.18), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -55.6, + -0.18), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + 1.12, + 4.79), + (0.03, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -41.73, + 4.79), + (0.28, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -10.37, + 7.53), + (0.06, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -29.31, + 7.53), + (0.19, + ROCKET, + PEACH, + WHITE, + 3.0, + 128.0, + -20.23, + 7.53), + (2.12, + RING, + PINK, + PINK, + 5.0, + 128.0, + -19.26, + 65.26), + (0.13, + CIRCLELARGE, + PURPLE, + PURPLE, + 5.0, + 128.0, + -19.26, + 65.26), + (2.0, + ROCKET, + WHITE, + WHITE, + 3.0, + 128.74, + -76.99, + -10.35), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 3.0, + 128.74, + -48.58, + -0.3), + (0.25, + ROCKET, + PEACH, + PEACH, + 3.0, + 128.74, + -19.91, + 5.62), + (0.25, + ROCKET, + PINK, + PINK, + 3.0, + 128.74, + 7.82, + 0.08), + (0.25, + ROCKET, + PURPLE, + PURPLE, + 3.0, + 128.74, + 36.06, + -9.94), + (0.25, + ROCKET, + PINK, + PINK, + 3.0, + 128.74, + 7.97, + 0.13), + (0.25, + ROCKET, + PEACH, + PEACH, + 3.0, + 128.74, + -20.22, + 5.66), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 3.0, + 128.74, + -49.59, + -0.26), + (0.25, + ROCKET, + WHITE, + WHITE, + 3.0, + 128.74, + -77.29, + -10.44), + (0.75, + CIRCLE, + WHITE, + PINK, + 8.0, + 128.74, + 49.33, + 27.06), + (0.25, + CIRCLE, + WHITE, + YELLOW, + 10.0, + 128.74, + 24.3, + 59.18), + (0.25, + CIRCLE, + WHITE, + PEACH, + 12.0, + 128.74, + -20.59, + 86.54), + (0.25, + CIRCLE, + WHITE, + PURPLE, + 10.0, + 128.74, + -63.3, + 62.82), + (0.25, + CIRCLE, + WHITE, + RED, + 8.0, + 128.74, + -85.9, + 28.25), + (1.19, + RING, + PINK, + PINK, + 4.0, + 78.66, + -50.27, + 65.42), + (0.5, + CIRCLESMALL, + PURPLE, + PURPLE, + 12.0, + 119.63, + -34.17, + 43.49), + (0.25, + CIRCLESMALL, + YELLOW, + YELLOW, + 12.0, + 78.8, + 0.32, + 44.5), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 12.0, + 81.0, + -13.05, + 72.06), + (0.06, + RING, + PEACH, + PEACH, + 12.0, + 81.0, + -13.05, + 72.06), + (2.44, + ROCKET, + WHITE, + WHITE, + 4.0, + 139.06, + 4.93, + 66.62), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 139.06, + -45.46, + 66.08), + (0.44, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.06, + -78.11, + 35.09), + (0.06, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.06, + -97.43, + 22.07), + (0.44, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.06, + 57.01, + 22.06), + (0.06, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.06, + 37.16, + 34.56), + (0.44, + ROCKET, + PINK, + PINK, + 4.0, + 139.06, + -83.11, + 43.43), + (0.06, + ROCKET, + PINK, + PINK, + 4.0, + 139.06, + -46.63, + 55.47), + (0.44, + ROCKET, + PURPLE, + PURPLE, + 4.0, + 139.06, + 41.59, + 44.45), + (0.06, + ROCKET, + PURPLE, + PURPLE, + 4.0, + 139.06, + 5.84, + 55.9), + (1.44, + RING, + PINK, + PINK, + 2.0, + 139.06, + 58.76, + 48.85), + (0.06, + RING, + PINK, + PINK, + 2.0, + 139.06, + -100.96, + 47.13), + (0.38, + RING, + PURPLE, + PURPLE, + 2.0, + 139.06, + 30.48, + 72.08), + (0.06, + RING, + PURPLE, + PURPLE, + 2.0, + 139.06, + -68.17, + 72.65), + (0.44, + CIRCLELARGE, + RED, + WHITE, + 2.0, + 136.43, + -9.32, + 91.88), + (0.37, + CIRCLELARGE, + WHITE, + WHITE, + 2.0, + 139.06, + -24.09, + 110.54), + (0.06, + RING, + PURPLE, + PURPLE, + 2.0, + 139.06, + -24.09, + 110.54), + (0.5, + ROCKET, + WHITE, + PINK, + 3.0, + 140.86, + -3.39, + 66.1), + (0.06, + ROCKET, + WHITE, + PINK, + 3.0, + 140.86, + -34.91, + 65.76), + (0.31, + ROCKET, + WHITE, + PEACH, + 3.0, + 140.86, + 8.9, + 54.37), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 140.86, + -49.88, + 53.74), + (0.31, + ROCKET, + WHITE, + PURPLE, + 3.0, + 140.86, + 31.63, + 47.78), + (0.06, + ROCKET, + WHITE, + PURPLE, + 3.0, + 140.86, + -74.65, + 46.63), + (0.41, + ROCKET, + WHITE, + RED, + 3.0, + 140.86, + 57.27, + 21.24), + (0.09, + ROCKET, + WHITE, + RED, + 3.0, + 140.86, + -98.14, + 19.57), + (0.91, + ROCKET, + PINK, + PINK, + 3.0, + 140.86, + 41.17, + 44.63), + (0.03, + ROCKET, + PINK, + PINK, + 3.0, + 140.86, + -80.26, + 43.33), + (0.22, + ROCKET, + PURPLE, + PURPLE, + 3.0, + 140.86, + 12.12, + 62.48), + (0.03, + ROCKET, + PURPLE, + PURPLE, + 3.0, + 140.86, + -54.27, + 61.76), + (0.16, + ROCKET, + RED, + RED, + 3.0, + 140.86, + -13.79, + 75.93), + (0.03, + ROCKET, + RED, + RED, + 3.0, + 140.86, + -23.17, + 75.83), + (0.56, + RING, + YELLOW, + YELLOW, + 3.0, + 120.63, + -19.19, + 77.13), + (0.5, + RING, + PEACH, + PEACH, + 3.0, + 120.63, + -19.19, + 77.13), + (0.38, + CIRCLESMALL, + PURPLE, + PURPLE, + 8.0, + 85.06, + -0.67, + 38.82), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 9.0, + 85.06, + -23.72, + 49.29), + (0.13, + CIRCLESMALL, + PEACH, + PEACH, + 10.0, + 85.06, + 8.51, + 62.3), + (0.25, + CIRCLELARGE, + WHITE, + PINK, + 15.0, + 86.04, + -16.85, + 58.76), + (0.06, + RING, + PURPLE, + WHITE, + 5.0, + 86.04, + -16.85, + 58.76), + (0.69, + CIRCLELARGE, + WHITE, + WHITE, + 5.0, + 82.52, + -32.63, + 42.45), + (0.25, + CIRCLELARGE, + PURPLE, + PURPLE, + 10.0, + 98.02, + -4.11, + 58.76), + (0.5, + CIRCLELARGE, + PINK, + PINK, + 8.0, + 107.66, + -45.36, + 67.65), + (0.5, + CIRCLELARGE, + PEACH, + PEACH, + 12.0, + 101.02, + -11.32, + 35.33), + (0.38, + CIRCLELARGE, + YELLOW, + YELLOW, + 14.0, + 84.21, + -9.1, + 68.81), + (0.31, + CIRCLELARGE, + RED, + RED, + 8.0, + 86.04, + -37.69, + 42.21), + (0.5, + RING, + PURPLE, + PINK, + 4.0, + 97.11, + -17.22, + 58.76), + (0.02, + RING, + PINK, + PEACH, + 4.0, + 97.11, + -17.22, + 58.76), + (0.02, + RING, + PEACH, + YELLOW, + 4.0, + 97.11, + -17.22, + 58.76), + (1.03, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + 53.94, + 24.67), + (0.06, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + -94.94, + 23.07), + (0.19, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + 27.59, + 49.73), + (0.06, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + -70.33, + 48.68), + (0.19, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + 4.74, + 66.45), + (0.09, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + -45.2, + 65.92), + (0.59, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + 53.94, + 24.67), + (0.03, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 139.7, + -94.94, + 23.07), + (0.09, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + 27.59, + 49.73), + (0.03, + ROCKET, + PEACH, + PEACH, + 4.0, + 139.7, + -70.33, + 48.68), + (0.09, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + 4.74, + 66.45), + (0.03, + ROCKET, + PINK, + PINK, + 4.0, + 139.7, + -45.2, + 65.92), + (0.47, + CIRCLELARGE, + PURPLE, + PINK, + 5.0, + 82.52, + -32.63, + 42.45), + (0.31, + CIRCLELARGE, + PINK, + PEACH, + 10.0, + 98.02, + -4.11, + 58.76), + (0.31, + CIRCLELARGE, + PEACH, + YELLOW, + 8.0, + 107.66, + -45.36, + 67.65), + (0.44, + CIRCLELARGE, + YELLOW, + WHITE, + 12.0, + 101.02, + -11.32, + 35.33), + (0.69, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 106.9, + 41.71, + -4.52), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 127.41, + 29.95, + -4.52), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 112.57, + 16.35, + -4.52), + (0.31, + ROCKET, + PEACH, + PEACH, + 4.0, + 112.01, + -82.53, + -4.52), + (0.31, + ROCKET, + PEACH, + PEACH, + 4.0, + 127.64, + -69.67, + -4.52), + (0.25, + ROCKET, + PEACH, + PEACH, + 4.0, + 113.58, + -54.24, + -4.52), + (0.37, + ROCKET, + PINK, + PINK, + 4.0, + 113.77, + 44.25, + -4.52), + (0.25, + ROCKET, + PINK, + PINK, + 4.0, + 127.64, + 30.1, + -4.52), + (0.25, + ROCKET, + PINK, + PINK, + 4.0, + 112.21, + 16.69, + -4.52), + (0.31, + ROCKET, + WHITE, + WHITE, + 4.0, + 112.99, + -81.98, + -4.52), + (0.25, + ROCKET, + WHITE, + WHITE, + 4.0, + 127.44, + -69.67, + -4.52), + (0.25, + ROCKET, + WHITE, + WHITE, + 4.0, + 113.97, + -54.42, + -4.52), + (0.25, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 113.97, + 44.25, + -4.52), + (0.06, + ROCKET, + YELLOW, + YELLOW, + 4.0, + 112.4, + 16.14, + -4.52), + (0.19, + ROCKET, + PEACH, + PEACH, + 4.0, + 113.77, + -54.24, + -4.52), + (0.06, + ROCKET, + PEACH, + PEACH, + 4.0, + 112.4, + -81.98, + -4.52), + (0.19, + ROCKET, + PINK, + PINK, + 4.0, + 115.09, + -10.1, + 0.79), + (0.06, + ROCKET, + PINK, + PINK, + 4.0, + 115.66, + -29.69, + 0.79), + (0.69, + CIRCLELARGE, + PURPLE, + PURPLE, + 10.0, + 112.99, + -21.41, + 73.4), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 82.59, + -7.83, + 49.23), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 106.28, + -39.22, + 60.32), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 10.0, + 106.28, + -18.8, + 69.23), + (0.25, + CIRCLELARGE, + RED, + RED, + 10.0, + 106.28, + -5.25, + 43.69), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 107.33, + -21.69, + 78.98), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 111.92, + -52.95, + 64.92), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 10.0, + 109.82, + 14.35, + 79.55), + (0.25, + CIRCLELARGE, + YELLOW, + YELLOW, + 10.0, + 75.62, + -42.59, + 39.25), + (0.25, + CIRCLESMALL, + PEACH, + PEACH, + 10.0, + 92.41, + -1.7, + 30.9), + (0.12, + CIRCLESMALL, + PINK, + PINK, + 10.0, + 71.9, + -0.06, + 50.32), + (0.13, + CIRCLESMALL, + RED, + RED, + 10.0, + 100.33, + -38.07, + 49.92), + (0.25, + CIRCLELARGE, + RED, + WHITE, + 10.0, + 125.29, + -21.7, + 85.61), + (0.25, + RING, + YELLOW, + YELLOW, + 4.0, + 125.29, + -21.7, + 85.61), + (0.13, + RING, + PEACH, + PEACH, + 4.0, + 125.29, + -21.7, + 85.61), + (0.12, + RING, + PINK, + PINK, + 4.0, + 125.29, + -21.7, + 85.61), + (0.25, + CIRCLELARGE, + PEACH, + PEACH, + 4.0, + 86.78, + -32.61, + 78.74), + (0.25, + CIRCLESMALL, + PINK, + PINK, + 4.0, + 85.81, + 6.87, + 65.98), + (0.13, + RING, + YELLOW, + YELLOW, + 4.0, + 97.06, + -16.05, + 94.59), + (0.12, + CIRCLESMALL, + RED, + RED, + 4.0, + 76.01, + -46.69, + 61.22), + (0.31, + CIRCLELARGE, + WHITE, + PINK, + 4.0, + 56.79, + 4.47, + 27.11), + (0.31, + RING, + RED, + RED, + 4.0, + 113.67, + -19.24, + 78.88), + (0.06, + CIRCLESMALL, + PINK, + PINK, + 4.0, + 86.78, + -11.48, + 84.63), + (0.06, + RING, + PURPLE, + PURPLE, + 4.0, + 76.66, + -54.89, + 50.2), + (0.5, + CIRCLELARGE, + PURPLE, + WHITE, + 4.0, + 94.84, + -38.08, + 63.24), + (0.25, + CIRCLESMALL, + PEACH, + WHITE, + 4.0, + 109.52, + -1.16, + 63.64), + (0.13, + CIRCLESMALL, + PINK, + WHITE, + 4.0, + 76.28, + -37.94, + 50.25), + (0.12, + CIRCLESMALL, + YELLOW, + WHITE, + 4.0, + 71.61, + 1.57, + 41.52), + (0.69, + RING, + YELLOW, + YELLOW, + 4.0, + 141.96, + -20.09, + 92.36), + (0.03, + RING, + PEACH, + PEACH, + 4.0, + 141.96, + -20.09, + 92.36), + (0.03, + RING, + RED, + RED, + 4.0, + 141.96, + -20.09, + 92.36)), + ((4, + POP, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 9), + (1.5, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 30), + (2.25, + POP, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 9), + (1.5, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 60), + (2.75, + POP, + CYAN, + WHITE, + 20, + -107.67, + -39.76, + 60), + (1.5, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 60), + (2.5, + POP, + BLUE, + WHITE, + 20, + -107.67, + -39.76, + 60), + (1.5, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 20, + -107.67, + -39.76, + 80), + (4.75, + POP, + WHITE, + WHITE, + 20, + -107.67, + -39.76, + 80), + (0.5, + POP, + WHITE, + WHITE, + 20, + -107.67, + -39.76, + 80), + (0.5, + POP, + WHITE, + WHITE, + 20, + -107.67, + -39.76, + 80), + (1.25, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 30, + -107.67, + -39.76, + 90), + (0.5, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 35, + -107.67, + -39.76, + 90), + (0.5, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 40, + -107.67, + -39.76, + 90), + (3.75, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (0.25, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (0.25, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (0.25, + POP, + CYAN, + WHITE, + 60, + -107.67, + -39.76, + 90), + (1.25, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 0.5, + -107.67, + -59.76, + 90), + (0.25, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 0.5, + -87.67, + -39.76, + 90), + (0.25, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 0.5, + -107.67, + -19.76, + 90), + (0.25, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 0.5, + -127.67, + -39.76, + 90), + (6, + POP, + CYAN, + WHITE, + 40, + -107.67, + -39.76, + 90), + (1, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 10), + (0.5, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (3, + POP, + WHITE, + WHITE, + 49, + -107.67, + -39.76, + 110), + (0.25, + POP, + WHITE, + WHITE, + 49, + -107.67, + -39.76, + 110), + (1, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -39.76, + 10), + (0.25, + CIRCLE, + CYAN, + WHITE, + 20, + -107.67, + -39.76, + 10), + (0.25, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (2.5, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + ROCKET, + BLUE, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + CIRCLESPRITE, + BLUE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + CYAN, + SNOWFLAKE, + 49, + -107.67, + -39.76, + 110), + (6.5, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.25, + POP, + CYAN, + WHITE, + 5, + -107.67, + -39.76, + 10), + (0.5, + POP, + CYAN, + WHITE, + 40, + -107.67, + -59.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -107.67, + -59.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -87.67, + -39.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -87.67, + -39.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -107.67, + -19.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -107.67, + -19.76, + 90), + (0.05, + POP, + CYAN, + WHITE, + 40, + -127.67, + -39.76, + 90), + (0, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -59.76, + 10), + (0.05, + POP, + CYAN, + WHITE, + 40, + -127.67, + -39.76, + 90), + (0, + ROCKET, + BLUE, + WHITE, + 5, + -107.67, + -59.76, + 10), + (0.05, + ROCKET, + CYAN, + WHITE, + 5, + -87.67, + -39.76, + 10), + (0.05, + ROCKET, + BLUE, + WHITE, + 5, + -87.67, + -39.76, + 10), + (0.05, + ROCKET, + CYAN, + WHITE, + 5, + -107.67, + -19.76, + 10), + (0.05, + ROCKET, + BLUE, + WHITE, + 5, + -107.67, + -19.76, + 10), + (0.05, + ROCKET, + CYAN, + WHITE, + 5, + -127.67, + -39.76, + 10), + (0, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -59.76, + 10), + (0.05, + ROCKET, + BLUE, + WHITE, + 5, + -127.67, + -39.76, + 10), + (0, + CIRCLE, + CYAN, + WHITE, + 20, + -107.67, + -59.76, + 10), + (0.05, + CIRCLE, + PURPLE, + WHITE, + 20, + -87.67, + -39.76, + 10), + (0.05, + CIRCLE, + CYAN, + WHITE, + 20, + -87.67, + -39.76, + 10), + (0.05, + CIRCLE, + PURPLE, + WHITE, + 20, + -107.67, + -19.76, + 10), + (0.05, + CIRCLE, + CYAN, + WHITE, + 20, + -107.67, + -19.76, + 10), + (0.05, + CIRCLE, + PURPLE, + WHITE, + 20, + -127.67, + -39.76, + 10), + (0.05, + CIRCLE, + CYAN, + WHITE, + 20, + -127.67, + -39.76, + 10), + (0.35, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -59.76, + 110), + (0.06, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -107.67, + -59.76, + 110), + (0.05, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -87.67, + -39.76, + 110), + (0.05, + CIRCLESPRITE, + PURPLE, + WHITE, + 49, + -87.67, + -39.76, + 110), + (0.05, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -107.67, + -19.76, + 110), + (0.05, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -107.67, + -19.76, + 110), + (0.05, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 49, + -127.67, + -39.76, + 110), + (0.06, + CIRCLESPRITE, + PURPLE, + SNOWFLAKE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.06, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.25, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -107.67, + -59.76, + 110), + (0.16, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -87.67, + -39.76, + 110), + (0.22, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -107.67, + -19.76, + 110), + (0.12, + CIRCLESPRITE, + PINK, + SNOWFLAKE, + 65, + -127.67, + -39.76, + 110), + (0.22, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.09, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.12, + POP, + PURPLE, + WHITE, + 49, + -127.67, + -39.76, + 110), + (0.28, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -107.67, + -59.76, + 110), + (0.12, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -87.67, + -39.76, + 110), + (0.12, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -107.67, + -19.76, + 110), + (0.09, + CIRCLESPRITE, + WHITE, + SNOWFLAKE, + 55, + -127.67, + -39.76, + 110)), + ((4.5, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.5, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (3.5, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (4, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (1, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0.31, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.31, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.38, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (4, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.38, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.37, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (4, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (2, + ROCKET, + WHITE, + YELLOW, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 25.65, + 158.38, + 0), + (0.5, + ROCKET, + WHITE, + YELLOW, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 37.27, + 139.59, + 0), + (0.25, + ROCKET, + WHITE, + YELLOW, + 5, + 35, + 0.5, + 0), + (3.13, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 0, + 190, + 0), + (0, + ROCKET, + PINK, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0, + ROCKET, + PINK, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + PINK, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 20, + 170, + 0), + (2.88, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 0, + 190, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 0, + 190, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0, + ROCKET, + PURPLE, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 20, + 170, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 25.65, + 158.38, + 0), + (0.25, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 35, + 0.5, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 35, + 0.5, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 40, + 130, + 0), + (0, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + PINK, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.25, + ROCKET, + PURPLE, + WHITE, + 5, + 54.79, + 127.03, + 0), + (3.99, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.12, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.14, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.75, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.12, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.13, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.75, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.13, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.12, + POP, + PINK, + WHITE, + 5, + 40, + 130, + 0), + (0.71, + CIRCLE, + YELLOW, + WHITE, + 10, + 0, + 190, + 5), + (0.03, + ROCKET, + YELLOW, + WHITE, + 5, + 0, + 190, + 0), + (0.09, + CIRCLE, + YELLOW, + WHITE, + 10, + 14.69, + 179.71, + 0), + (0.03, + ROCKET, + YELLOW, + WHITE, + 5, + 14.69, + 179.71, + 0), + (0.09, + CIRCLE, + YELLOW, + WHITE, + 10, + 20, + 170, + 0), + (0.04, + ROCKET, + YELLOW, + WHITE, + 5, + 20, + 170, + 0), + (0.08, + CIRCLE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 0), + (0.04, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.08, + CIRCLE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 0), + (0.05, + ROCKET, + YELLOW, + WHITE, + 5, + 35, + 0.5, + 0), + (0.07, + CIRCLE, + YELLOW, + WHITE, + 10, + 37.27, + 139.59, + 0), + (0.05, + ROCKET, + YELLOW, + WHITE, + 5, + 37.27, + 139.59, + 0), + (0.07, + CIRCLE, + YELLOW, + WHITE, + 10, + 40, + 130, + 0), + (0.06, + ROCKET, + YELLOW, + WHITE, + 5, + 40, + 130, + 0), + (0.06, + CIRCLE, + YELLOW, + WHITE, + 10, + 54.79, + 127.03, + 0), + (0.06, + ROCKET, + YELLOW, + WHITE, + 5, + 54.79, + 127.03, + 0), + (0.56, + CIRCLE, + PINK, + WHITE, + 12, + 0, + 190, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 0, + 190, + 0), + (0.08, + CIRCLE, + PINK, + WHITE, + 12, + 14.69, + 179.71, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 14.69, + 179.71, + 0), + (0.08, + CIRCLE, + PINK, + WHITE, + 7, + 20, + 170, + 5), + (0.05, + ROCKET, + PINK, + WHITE, + 7, + 20, + 170, + 0), + (0.06, + CIRCLE, + PINK, + WHITE, + 12, + 35, + 0.5, + 5), + (0.03, + ROCKET, + PINK, + WHITE, + 7, + 35, + 0.5, + 0), + (0.1, + CIRCLE, + PINK, + WHITE, + 12, + 35, + 0.5, + 5), + (0.03, + ROCKET, + PINK, + WHITE, + 7, + 35, + 0.5, + 0), + (0.09, + CIRCLE, + PINK, + WHITE, + 12, + 37.27, + 139.59, + 5), + (0.03, + ROCKET, + PINK, + WHITE, + 7, + 37.27, + 139.59, + 0), + (0.09, + CIRCLE, + PINK, + WHITE, + 12, + 40, + 130, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 40, + 130, + 0), + (0.08, + CIRCLE, + PINK, + WHITE, + 12, + 54.79, + 127.03, + 5), + (0.04, + ROCKET, + PINK, + WHITE, + 7, + 54.79, + 127.03, + 0), + (0.58, + CIRCLE, + PURPLE, + WHITE, + 14, + 0, + 190, + 5), + (0.05, + ROCKET, + PURPLE, + WHITE, + 9, + 0, + 190, + 0), + (0.12, + CIRCLE, + PURPLE, + WHITE, + 14, + 14.69, + 179.71, + 5), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 14.69, + 179.71, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 20, + 170, + 5), + (0.06, + ROCKET, + PURPLE, + WHITE, + 9, + 20, + 170, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 35, + 0.5, + 5), + (0.06, + ROCKET, + PURPLE, + WHITE, + 9, + 35, + 0.5, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 35, + 0.5, + 5), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 35, + 0.5, + 0), + (0.12, + CIRCLE, + PURPLE, + WHITE, + 14, + 37.27, + 139.59, + 5), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 37.27, + 139.59, + 0), + (0.12, + CIRCLE, + PURPLE, + WHITE, + 14, + 40, + 130, + 0), + (0.03, + ROCKET, + PURPLE, + WHITE, + 9, + 40, + 130, + 0), + (0.09, + CIRCLE, + PURPLE, + WHITE, + 14, + 54.79, + 127.03, + 5), + (0.06, + ROCKET, + PURPLE, + WHITE, + 9, + 54.79, + 127.03, + 0), + (0.5, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 0, + 190, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 14.69, + 179.71, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 20, + 170, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 35, + 0.5, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 37.27, + 139.59, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 40, + 130, + 80), + (0.06, + CIRCLELARGE, + YELLOW, + WHITE, + 10, + 54.79, + 127.03, + 80), + (0.56, + CIRCLELARGE, + PINK, + WHITE, + 15, + 0, + 190, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 14.69, + 179.71, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 20, + 170, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 35, + 0.5, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 35, + 0.5, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 37.27, + 139.59, + 0), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 15, + 40, + 130, + 90), + (0.06, + CIRCLELARGE, + PINK, + WHITE, + 7, + 54.79, + 127.03, + 90), + (0.56, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 0, + 190, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 14.69, + 179.71, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 20, + 170, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 35, + 0.5, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 35, + 0.5, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 37.27, + 139.59, + 100), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 9, + 40, + 130, + 0), + (0.06, + CIRCLELARGE, + PURPLE, + WHITE, + 20, + 54.79, + 127.03, + 100))], + PartyGlobals.FireworkShows.Summer: [((0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 113.22 + PartyGlobals.FireworksGlobalXOffset, + 1.05 + PartyGlobals.FireworksGlobalYOffset, + 73.24), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 115.54 + PartyGlobals.FireworksGlobalXOffset, + 13.8 + PartyGlobals.FireworksGlobalYOffset, + 59.11), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 118.36 + PartyGlobals.FireworksGlobalXOffset, + -13.83 + PartyGlobals.FireworksGlobalYOffset, + 57.71), + (0.22, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05 + PartyGlobals.FireworksGlobalXOffset, + 25.37 + PartyGlobals.FireworksGlobalYOffset, + 57.01), + (0.03, + ROCKET, + BLUE, + BLUE, + 5.0, + 110.05 + PartyGlobals.FireworksGlobalXOffset, + -25.37 + PartyGlobals.FireworksGlobalYOffset, + 57.01), + (3.22, + CIRCLELARGE, + WHITE, + WHITE, + 10.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 1.07 + PartyGlobals.FireworksGlobalYOffset, + 106.69), + (1.94, + CIRCLELARGE, + RED, + RED, + 10.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 1.07 + PartyGlobals.FireworksGlobalYOffset, + 106.69), + (1.31, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 28.63 + PartyGlobals.FireworksGlobalYOffset, + 38.55), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.75 + PartyGlobals.FireworksGlobalXOffset, + 1.3 + PartyGlobals.FireworksGlobalYOffset, + 38.76), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + -25.6 + PartyGlobals.FireworksGlobalYOffset, + 37.54), + (0.88, + ROCKET, + BLUE, + BLUE, + 4.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + -25.6 + PartyGlobals.FireworksGlobalYOffset, + 37.54), + (0.06, + ROCKET, + RED, + RED, + 4.0, + 121.74 + PartyGlobals.FireworksGlobalXOffset, + 1.3 + PartyGlobals.FireworksGlobalYOffset, + 37.75), + (0.06, + ROCKET, + WHITE, + WHITE, + 4.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 27.34 + PartyGlobals.FireworksGlobalYOffset, + 37.12), + (0.82, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0 + PartyGlobals.FireworksGlobalXOffset, + 40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0 + PartyGlobals.FireworksGlobalXOffset, + 40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0 + PartyGlobals.FireworksGlobalXOffset, + 40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.72, + ROCKET, + BLUE, + BLUE, + 4.0, + 55.0 + PartyGlobals.FireworksGlobalXOffset, + -40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + RED, + RED, + 4.0, + 70.0 + PartyGlobals.FireworksGlobalXOffset, + -40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.09, + ROCKET, + WHITE, + WHITE, + 4.0, + 85.0 + PartyGlobals.FireworksGlobalXOffset, + -40.0 + PartyGlobals.FireworksGlobalYOffset, + 40.0), + (0.84, + CIRCLE, + BLUE, + BLUE, + 10.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 78.62), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 78.62), + (0.75, + CIRCLE, + RED, + RED, + 10.0, + 120.0 + PartyGlobals.FireworksGlobalXOffset, + -30.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (0.06, + RING, + WHITE, + WHITE, + 5.0, + 120.0 + PartyGlobals.FireworksGlobalXOffset, + -30.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (0.7, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 92.0), + (0.19, + RING, + BLUE, + BLUE, + 5.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 29.29 + PartyGlobals.FireworksGlobalYOffset, + 92.0), + (0.8, + CIRCLE, + WHITE, + WHITE, + 10.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (0.08, + RING, + RED, + RED, + 5.0, + 121.81 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 100.0), + (1.0, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.43 + PartyGlobals.FireworksGlobalXOffset, + 22.69 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.24 + PartyGlobals.FireworksGlobalXOffset, + 17.57 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 10.82 + PartyGlobals.FireworksGlobalXOffset, + 12.13 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.22, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.17 + PartyGlobals.FireworksGlobalXOffset, + 6.76 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.1 + PartyGlobals.FireworksGlobalXOffset, + 1.91 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.71 + PartyGlobals.FireworksGlobalXOffset, + -3.56 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.19, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.26 + PartyGlobals.FireworksGlobalXOffset, + -8.51 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.28, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.25 + PartyGlobals.FireworksGlobalXOffset, + -14.18 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.2 + PartyGlobals.FireworksGlobalXOffset, + -19.42 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + PEACH, + 4.0, + 11.53 + PartyGlobals.FireworksGlobalXOffset, + -24.46 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (1.24, + CIRCLESMALL, + RED, + WHITE, + 15.0, + 68.68 + PartyGlobals.FireworksGlobalXOffset, + -29.58 + PartyGlobals.FireworksGlobalYOffset, + 57.72), + (1.63, + CIRCLE, + BLUE, + WHITE, + 15.0, + 70.33 + PartyGlobals.FireworksGlobalXOffset, + 29.76 + PartyGlobals.FireworksGlobalYOffset, + 56.3), + (0.87, + CIRCLELARGE, + WHITE, + WHITE, + 15.0, + 121.64 + PartyGlobals.FireworksGlobalXOffset, + 1.2 + PartyGlobals.FireworksGlobalYOffset, + 92.64), + (1.26, + RING, + WHITE, + WHITE, + 15.0, + 83.12 + PartyGlobals.FireworksGlobalXOffset, + 12.21 + PartyGlobals.FireworksGlobalYOffset, + 92.04), + (0.44, + RING, + RED, + RED, + 15.0, + 84.41 + PartyGlobals.FireworksGlobalXOffset, + -23.72 + PartyGlobals.FireworksGlobalYOffset, + 78.01), + (0.44, + RING, + WHITE, + WHITE, + 15.0, + 83.5 + PartyGlobals.FireworksGlobalXOffset, + 1.77 + PartyGlobals.FireworksGlobalYOffset, + 68.65), + (0.38, + RING, + BLUE, + BLUE, + 15.0, + 82.6 + PartyGlobals.FireworksGlobalXOffset, + 26.64 + PartyGlobals.FireworksGlobalYOffset, + 68.65), + (0.37, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 74.5 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 71.8), + (0.12, + CIRCLELARGE, + RED, + RED, + 11.0, + 74.5 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 71.8), + (0.75, + CIRCLESMALL, + WHITE, + WHITE, + 11.0, + 94.03 + PartyGlobals.FireworksGlobalXOffset, + -15.23 + PartyGlobals.FireworksGlobalYOffset, + 62.12), + (0.13, + CIRCLELARGE, + BLUE, + BLUE, + 11.0, + 94.03 + PartyGlobals.FireworksGlobalXOffset, + -15.23 + PartyGlobals.FireworksGlobalYOffset, + 62.12), + (0.56, + CIRCLESMALL, + RED, + RED, + 11.0, + 54.56 + PartyGlobals.FireworksGlobalXOffset, + 7.87 + PartyGlobals.FireworksGlobalYOffset, + 53.74), + (0.19, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 54.56 + PartyGlobals.FireworksGlobalXOffset, + 7.87 + PartyGlobals.FireworksGlobalYOffset, + 53.74), + (0.63, + CIRCLESMALL, + BLUE, + BLUE, + 11.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 11.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.37, + RING, + RED, + RED, + 5.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 6.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + BLUE, + BLUE, + 7.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + WHITE, + WHITE, + 8.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.47, + RING, + RED, + RED, + 10.0, + 82.51 + PartyGlobals.FireworksGlobalXOffset, + 0.23 + PartyGlobals.FireworksGlobalYOffset, + 82.99), + (0.37, + ROCKET, + WHITE, + WHITE, + 8.0, + 53.85 + PartyGlobals.FireworksGlobalXOffset, + 14.92 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 54.66 + PartyGlobals.FireworksGlobalXOffset, + -16.71 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.27 + PartyGlobals.FireworksGlobalXOffset, + 15.16 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 73.38 + PartyGlobals.FireworksGlobalXOffset, + -15.98 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.44, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.33 + PartyGlobals.FireworksGlobalXOffset, + 15.17 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 8.0, + 93.71 + PartyGlobals.FireworksGlobalXOffset, + -15.73 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.89, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 151.88 + PartyGlobals.FireworksGlobalXOffset, + 1.92 + PartyGlobals.FireworksGlobalYOffset, + 144.56), + (0.93, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.25 + PartyGlobals.FireworksGlobalXOffset, + 16.72 + PartyGlobals.FireworksGlobalYOffset, + 82.5), + (0.12, + CIRCLELARGE, + WHITE, + WHITE, + 8.0, + 79.31 + PartyGlobals.FireworksGlobalXOffset, + 1.0 + PartyGlobals.FireworksGlobalYOffset, + 63.69), + (0.13, + CIRCLESMALL, + WHITE, + WHITE, + 8.0, + 82.02 + PartyGlobals.FireworksGlobalXOffset, + -13.25 + PartyGlobals.FireworksGlobalYOffset, + 79.16), + (0.88, + ROCKET, + WHITE, + RED, + 4.0, + 6.47 + PartyGlobals.FireworksGlobalXOffset, + -15.0 + PartyGlobals.FireworksGlobalYOffset, + 8.6), + (0.06, + ROCKET, + WHITE, + RED, + 4.0, + 18.41 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.31, + ROCKET, + WHITE, + BLUE, + 4.0, + 53.79 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + BLUE, + 4.0, + 54.85 + PartyGlobals.FireworksGlobalXOffset, + -15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.5, + ROCKET, + RED, + RED, + 4.0, + 90.45 + PartyGlobals.FireworksGlobalXOffset, + -15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.19, + ROCKET, + RED, + RED, + 4.0, + 90.21 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 4.0, + 90.21 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.44, + CIRCLE, + WHITE, + WHITE, + 4.0, + 18.41 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 90.0), + (0.06, + RING, + RED, + RED, + 1.0, + 18.41 + PartyGlobals.FireworksGlobalXOffset, + 15.0 + PartyGlobals.FireworksGlobalYOffset, + 90.0), + (0.75, + CIRCLE, + WHITE, + WHITE, + 4.0, + 93.56 + PartyGlobals.FireworksGlobalXOffset, + -16.02 + PartyGlobals.FireworksGlobalYOffset, + 72.89), + (0.06, + RING, + RED, + RED, + 1.0, + 93.56 + PartyGlobals.FireworksGlobalXOffset, + -16.02 + PartyGlobals.FireworksGlobalYOffset, + 72.89), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0 + PartyGlobals.FireworksGlobalXOffset, + 53.19 + PartyGlobals.FireworksGlobalYOffset, + -10.03, + 98.97), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 53.19 + PartyGlobals.FireworksGlobalXOffset, + -10.03 + PartyGlobals.FireworksGlobalYOffset, + 98.97), + (0.69, + CIRCLE, + WHITE, + WHITE, + 4.0, + 71.46 + PartyGlobals.FireworksGlobalXOffset, + 13.03 + PartyGlobals.FireworksGlobalYOffset, + 56.82), + (0.06, + RING, + BLUE, + BLUE, + 1.0, + 71.46 + PartyGlobals.FireworksGlobalXOffset, + 13.03 + PartyGlobals.FireworksGlobalYOffset, + 56.82), + (0.25, + CIRCLE, + RED, + RED, + 4.0, + 89.38 + PartyGlobals.FireworksGlobalXOffset, + -3.18 + PartyGlobals.FireworksGlobalYOffset, + 95.18), + (0.06, + RING, + WHITE, + WHITE, + 1.0, + 89.38 + PartyGlobals.FireworksGlobalXOffset, + -3.18 + PartyGlobals.FireworksGlobalYOffset, + 95.18), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 19.0, + 94.74 + PartyGlobals.FireworksGlobalXOffset, + 0.89 + PartyGlobals.FireworksGlobalYOffset, + 65.55), + (0.44, + ROCKET, + WHITE, + PEACH, + 3.0, + 96.5 + PartyGlobals.FireworksGlobalXOffset, + 2.5 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 95.93 + PartyGlobals.FireworksGlobalXOffset, + -2.61 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.38, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.08 + PartyGlobals.FireworksGlobalXOffset, + 7.72 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 76.54 + PartyGlobals.FireworksGlobalXOffset, + -8.28 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.37, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.41 + PartyGlobals.FireworksGlobalXOffset, + 12.05 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.06, + ROCKET, + WHITE, + PEACH, + 3.0, + 56.76 + PartyGlobals.FireworksGlobalXOffset, + -13.51 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.31, + ROCKET, + WHITE, + PEACH, + 3.0, + 36.29 + PartyGlobals.FireworksGlobalXOffset, + 16.92 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.1, + ROCKET, + WHITE, + PEACH, + 3.0, + 37.03 + PartyGlobals.FireworksGlobalXOffset, + -19.08 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.96, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31 + PartyGlobals.FireworksGlobalXOffset, + 2.5 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + WHITE, + 3.0, + 100.31 + PartyGlobals.FireworksGlobalXOffset, + -2.5 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.44, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25 + PartyGlobals.FireworksGlobalXOffset, + 8.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLESMALL, + WHITE, + RED, + 6.0, + 76.25 + PartyGlobals.FireworksGlobalXOffset, + -8.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.44, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77 + PartyGlobals.FireworksGlobalXOffset, + 12.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLE, + WHITE, + BLUE, + 3.0, + 34.77 + PartyGlobals.FireworksGlobalXOffset, + -12.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.44, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0 + PartyGlobals.FireworksGlobalXOffset, + 16.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.06, + CIRCLELARGE, + WHITE, + WHITE, + 3.0, + 10.0 + PartyGlobals.FireworksGlobalXOffset, + -16.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (0.94, + CIRCLESPRITE, + WHITE, + ICECREAM, + 6.0, + 10.0 + PartyGlobals.FireworksGlobalXOffset, + 0.0 + PartyGlobals.FireworksGlobalYOffset, + 66.59), + (2.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.13, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.44, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.06, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.63, + ROCKET, + RED, + RED, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.13, + ROCKET, + BLUE, + BLUE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.25, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.94, + ROCKET, + WHITE, + WHITE, + 5.0, + 74.23 + PartyGlobals.FireworksGlobalXOffset, + -0.06 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.28, + ROCKET, + BLUE, + BLUE, + 5.0, + 53.66 + PartyGlobals.FireworksGlobalXOffset, + 0.14 + PartyGlobals.FireworksGlobalYOffset, + 13.48), + (0.28, + ROCKET, + RED, + RED, + 5.0, + 53.96 + PartyGlobals.FireworksGlobalXOffset, + -0.38 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.28, + ROCKET, + WHITE, + WHITE, + 5.0, + 63.67 + PartyGlobals.FireworksGlobalXOffset, + 10.54 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.34, + ROCKET, + WHITE, + WHITE, + 4.0, + 63.88 + PartyGlobals.FireworksGlobalXOffset, + -10.98 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.24 + PartyGlobals.FireworksGlobalXOffset, + -4.44 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.16, + ROCKET, + RED, + RED, + 5.0, + 64.16 + PartyGlobals.FireworksGlobalXOffset, + 2.92 + PartyGlobals.FireworksGlobalYOffset, + 4.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.12, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.34, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.03, + ROCKET, + RED, + RED, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + 23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.38, + ROCKET, + WHITE, + WHITE, + 5.0, + 50.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 90.0 + PartyGlobals.FireworksGlobalXOffset, + -23.91 + PartyGlobals.FireworksGlobalYOffset, + 19.44), + (0.34, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58 + PartyGlobals.FireworksGlobalXOffset, + 11.29 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.22, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22 + PartyGlobals.FireworksGlobalXOffset, + 3.33 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.37, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34 + PartyGlobals.FireworksGlobalXOffset, + -0.98 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.22, + ROCKET, + RED, + RED, + 5.0, + 108.45 + PartyGlobals.FireworksGlobalXOffset, + -9.33 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.53, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.25, + ROCKET, + RED, + RED, + 5.0, + 107.58 + PartyGlobals.FireworksGlobalXOffset, + 11.29 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.25, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22 + PartyGlobals.FireworksGlobalXOffset, + 3.33 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.5, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34 + PartyGlobals.FireworksGlobalXOffset, + -0.98 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.19, + ROCKET, + RED, + RED, + 5.0, + 108.45 + PartyGlobals.FireworksGlobalXOffset, + -9.33 + PartyGlobals.FireworksGlobalYOffset, + 45.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.5, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.19, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.22 + PartyGlobals.FireworksGlobalXOffset, + 3.33 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.06, + ROCKET, + WHITE, + WHITE, + 5.0, + 108.34 + PartyGlobals.FireworksGlobalXOffset, + -0.98 + PartyGlobals.FireworksGlobalYOffset, + 60.0), + (0.19, + ROCKET, + BLUE, + BLUE, + 5.0, + 107.79 + PartyGlobals.FireworksGlobalXOffset, + 19.8 + PartyGlobals.FireworksGlobalYOffset, + 24.32), + (0.06, + ROCKET, + BLUE, + BLUE, + 5.0, + 108.69 + PartyGlobals.FireworksGlobalXOffset, + -17.77 + PartyGlobals.FireworksGlobalYOffset, + 24.4), + (0.94, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.25, + CIRCLESMALL, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.13, + CIRCLELARGE, + RED, + RED, + 5.0, + 40.83 + PartyGlobals.FireworksGlobalXOffset, + 10.33 + PartyGlobals.FireworksGlobalYOffset, + 65.41), + (0.25, + CIRCLE, + WHITE, + WHITE, + 5.0, + 103.3 + PartyGlobals.FireworksGlobalXOffset, + 12.66 + PartyGlobals.FireworksGlobalYOffset, + 83.93), + (0.25, + RING, + RED, + RED, + 5.0, + 95.27 + PartyGlobals.FireworksGlobalXOffset, + -9.92 + PartyGlobals.FireworksGlobalYOffset, + 72.55), + (0.37, + POP, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.13, + CIRCLELARGE, + BLUE, + RED, + 5.0, + 107.72 + PartyGlobals.FireworksGlobalXOffset, + -11.81 + PartyGlobals.FireworksGlobalYOffset, + 50.51), + (0.5, + CIRCLESMALL, + BLUE, + WHITE, + 5.0, + 102.29 + PartyGlobals.FireworksGlobalXOffset, + -8.59 + PartyGlobals.FireworksGlobalYOffset, + 80.37), + (0.31, + RING, + BLUE, + BLUE, + 5.0, + 49.85 + PartyGlobals.FireworksGlobalXOffset, + 12.65 + PartyGlobals.FireworksGlobalYOffset, + 59.45), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 22.91 + PartyGlobals.FireworksGlobalXOffset, + -0.7 + PartyGlobals.FireworksGlobalYOffset, + 68.75), + (0.13, + CIRCLELARGE, + WHITE, + RED, + 5.0, + 91.87 + PartyGlobals.FireworksGlobalXOffset, + 0.99 + PartyGlobals.FireworksGlobalYOffset, + 72.32), + (0.25, + POP, + BLUE, + BLUE, + 5.0, + 43.82 + PartyGlobals.FireworksGlobalXOffset, + -2.09 + PartyGlobals.FireworksGlobalYOffset, + 49.31), + (0.25, + CIRCLE, + RED, + RED, + 5.0, + 2.49 + PartyGlobals.FireworksGlobalXOffset, + 6.37 + PartyGlobals.FireworksGlobalYOffset, + 43.93), + (0.37, + RING, + BLUE, + BLUE, + 5.0, + 84.93 + PartyGlobals.FireworksGlobalXOffset, + 8.5 + PartyGlobals.FireworksGlobalYOffset, + 77.25), + (0.44, + RING, + WHITE, + WHITE, + 5.0, + 85.37 + PartyGlobals.FireworksGlobalXOffset, + -5.29 + PartyGlobals.FireworksGlobalYOffset, + 75.3), + (0.44, + CIRCLELARGE, + RED, + RED, + 5.0, + 86.11 + PartyGlobals.FireworksGlobalXOffset, + -11.82 + PartyGlobals.FireworksGlobalYOffset, + 71.43), + (0.31, + CIRCLELARGE, + WHITE, + WHITE, + 5.0, + 79.35 + PartyGlobals.FireworksGlobalXOffset, + 8.71 + PartyGlobals.FireworksGlobalYOffset, + 77.0), + (0.19, + CIRCLELARGE, + BLUE, + BLUE, + 5.0, + 66.08 + PartyGlobals.FireworksGlobalXOffset, + 0.98 + PartyGlobals.FireworksGlobalYOffset, + 68.8))]} + +def getShow(holidayId, index): + showList = shows.get(holidayId, []) + if index < len(showList): + return showList[index] + else: + return None + return None + + +def getShowDuration(eventId, index): + show = getShow(eventId, index) + duration = 0.0 + duration += skyTransitionDuration + duration += preShowPauseDuration + for effect in show: + waitTime, style, colorIndex1, colorIndex2, amp, x, y, z = effect + duration += waitTime + + duration += postShowPauseDuration + duration += preNormalMusicPauseDuration + return duration diff --git a/toontown/effects/FireworkSparkles.py b/toontown/effects/FireworkSparkles.py new file mode 100644 index 0000000..afbe043 --- /dev/null +++ b/toontown/effects/FireworkSparkles.py @@ -0,0 +1,94 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class FireworkSparkles(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleSpark_sharp') + self.cardScale = 16.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.startDelay = 0.0 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('Sparkles') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereVolumeEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -15.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(64) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(10) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(1.5) + self.p0.factory.setLifespanSpread(1.0) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.getColorInterpolationManager().addLinear(0.0, 0.1, Vec4(0, 0, 0, 0), self.effectColor, 1) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Wait(self.startDelay), Func(self.p0.setBirthRate, 0.03), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(0.3), Func(self.p0.setBirthRate, 100.0), Wait(2.5), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(1.2 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(1.5 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(1.5 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.2 * self.cardScale * scale) + self.p0.emitter.setAmplitude(25.0 * scale) + self.p0.emitter.setRadius(400.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Fireworks.py b/toontown/effects/Fireworks.py new file mode 100644 index 0000000..adf3b78 --- /dev/null +++ b/toontown/effects/Fireworks.py @@ -0,0 +1,235 @@ +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect +from direct.particles import Particles +from direct.particles import ForceGroup +from pandac.PandaModules import * +import random +from FireworkGlobals import * +colors = {WHITE: Vec4(1, 1, 1, 1), + RED: Vec4(1, 0.2, 0.2, 1), + BLUE: Vec4(0.2, 0.2, 1, 1), + YELLOW: Vec4(1, 1, 0.2, 1), + GREEN: Vec4(0.2, 1, 0.2, 1), + PINK: Vec4(1, 0.5, 0.5, 1), + PEACH: Vec4(0.9, 0.6, 0.4, 1), + PURPLE: Vec4(1, 0.1, 1, 1), + CYAN: Vec4(0.2, 1, 1, 1)} +textures = {SNOWFLAKE: 'phase_8/models/props/snowflake_treasure', + MUSICNOTE: 'phase_6/models/props/music_treasure', + FLOWER: 'phase_8/models/props/flower_treasure', + ICECREAM: 'phase_4/models/props/icecream', + STARFISH: 'phase_6/models/props/starfish_treasure', + ZZZ: 'phase_8/models/props/zzz_treasure'} +fireworkId = 0 + +def getNextSequenceName(name): + global fireworkId + fireworkId += 1 + return '%s-%s' % (name, fireworkId) + + +def getColor(colorIndex): + return colors.get(colorIndex) + + +def getTexture(textureIndex): + return loader.loadModel(textures.get(textureIndex)) + + +def shootFirework(style, x = 0, y = 0, z = 0, colorIndex1 = 0, colorIndex2 = 0, amp = 10): + func = style2shootFunc.get(style) + color1 = getColor(colorIndex1) + if style is CIRCLESPRITE: + color2 = getTexture(colorIndex2) + else: + color2 = getColor(colorIndex2) + if func and color1 and color2: + return func(x, y, z, color1, color2, amp) + + +def shootFireworkRing(x, y, z, color1, color2, amp): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('RingEmitter') + p0.setPoolSize(100) + p0.setBirthRate(0.01) + p0.setLitterSize(100) + p0.setLitterSpread(0) + p0.factory.setLifespanBase(1.5) + p0.factory.setLifespanSpread(0.5) + p0.factory.setMassBase(1.0) + p0.factory.setMassSpread(0.0) + p0.factory.setTerminalVelocityBase(20.0) + p0.factory.setTerminalVelocitySpread(2.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 = LinearSourceForce(Point3(x, y, z), LinearDistanceForce.FTONEOVERR, 0.1, 1.1 * amp, 1) + force0.setActive(1) + f0.addForce(force0) + force1 = LinearSinkForce(Point3(x, y, z), LinearDistanceForce.FTONEOVERR, 0.5, 2.0 * amp, 1) + force1.setActive(1) + f0.addForce(force1) + f.addForceGroup(f0) + p0.emitter.setRadius(4.0) + f.addParticles(p0) + f.setPos(x, y, z) + f.setHpr(0, random.random() * 180, random.random() * 180) + sfx = loader.loadSfx('phase_4/audio/sfx/firework_distance_03.mp3') + t = Sequence(Func(f.start, render, render), Func(sfx.play), Wait(0.5), Func(p0.setBirthRate, 3), Wait(1.5), Func(f.cleanup), name=getNextSequenceName('shootFireworkRing')) + t.start() + + +def shootFireworkRocket(x, y, z, color1, color2, amp): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('SphereVolumeEmitter') + p0.setPoolSize(110) + p0.setBirthRate(0.01) + p0.setLitterSize(2) + p0.setLitterSpread(0) + p0.factory.setLifespanBase(0.4) + p0.factory.setLifespanSpread(0.1) + p0.factory.setMassBase(1.0) + p0.factory.setMassSpread(0.0) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(0.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setCenterColor(color1) + p0.renderer.setEdgeColor(color2) + p0.renderer.setBirthRadius(0.6) + p0.renderer.setDeathRadius(0.6) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitude(amp) + p0.emitter.setAmplitudeSpread(0.0) + p0.emitter.setRadius(0.3) + f.addParticles(p0) + gravityForceGroup = ForceGroup.ForceGroup('gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -10.0), 1.0, 0) + force0.setActive(1) + gravityForceGroup.addForce(force0) + f.addForceGroup(gravityForceGroup) + f.setPos(x, y, z) + sfxName = random.choice(('phase_4/audio/sfx/firework_whistle_01.mp3', 'phase_4/audio/sfx/firework_whistle_02.mp3')) + sfx = loader.loadSfx(sfxName) + t = Sequence(Func(f.start, render, render), Func(sfx.play), LerpPosInterval(f, 2.0, Vec3(x, y, z + 20 * amp), blendType='easeInOut'), Func(p0.setBirthRate, 3), Wait(0.5), Func(f.cleanup), name=getNextSequenceName('shootFirework')) + t.start() + + +def shootPop(x, y, z, color1, color2, amp): + sfxName = random.choice(('phase_4/audio/sfx/firework_distance_01.mp3', 'phase_4/audio/sfx/firework_distance_02.mp3', 'phase_4/audio/sfx/firework_distance_03.mp3')) + sfx = loader.loadSfx(sfxName) + t = Sequence(Func(sfx.play), Wait(3), name=getNextSequenceName('shootFireworkRocket')) + t.start() + + +def shootFireworkCircle(x, y, z, color1, color2, amp): + return shootFireworkCircleGeneric(x, y, z, color1, color2, amp, 100) + + +def shootFireworkCircleLarge(x, y, z, color1, color2, amp): + return shootFireworkCircleGeneric(x, y, z, color1, color2, amp * 1.5, 200) + + +def shootFireworkCircleSmall(x, y, z, color1, color2, amp): + return shootFireworkCircleGeneric(x, y, z, color1, color2, amp * 0.5, 50) + + +def shootFireworkCircleGeneric(x, y, z, color1, color2, amp, poolSize): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SparkleParticleRenderer') + p0.setEmitter('SphereVolumeEmitter') + p0.setPoolSize(poolSize) + p0.setBirthRate(0.01) + p0.setLitterSize(poolSize) + p0.factory.setLifespanBase(2.0) + p0.factory.setLifespanSpread(0.5) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(40.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setCenterColor(color1) + p0.renderer.setEdgeColor(color1) + p0.renderer.setBirthRadius(0.4) + p0.renderer.setDeathRadius(0.6) + p0.renderer.setLifeScale(SparkleParticleRenderer.SPSCALE) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitudeSpread(0.1) + p0.emitter.setAmplitude(amp) + p0.emitter.setRadius(0.1) + f.addParticles(p0) + circleForceGroup = ForceGroup.ForceGroup('gravity') + force1 = LinearSinkForce(Point3(x, y, z - 100), LinearDistanceForce.FTONEOVERRSQUARED, 2.0, 0.3 * amp * 0.1, 1) + force1.setActive(1) + circleForceGroup.addForce(force1) + f.addForceGroup(circleForceGroup) + f.setPos(x, y, z) + sfxName = random.choice(('phase_4/audio/sfx/firework_explosion_01.mp3', 'phase_4/audio/sfx/firework_explosion_02.mp3', 'phase_4/audio/sfx/firework_explosion_03.mp3')) + sfx = loader.loadSfx(sfxName) + t = Sequence(Func(f.start, render, render), Func(sfx.play), Wait(0.5), Func(p0.setBirthRate, 3), Wait(0.5), Func(p0.renderer.setCenterColor, color2), Func(p0.renderer.setEdgeColor, color2), Wait(1.5), Func(f.cleanup), name=getNextSequenceName('shootFireworkCircle')) + t.start() + + +def shootFireworkCircleSprite(x, y, z, color, texture, amp): + f = ParticleEffect.ParticleEffect() + p0 = Particles.Particles('particles-1') + p0.setFactory('PointParticleFactory') + p0.setRenderer('SpriteParticleRenderer') + p0.setEmitter('SphereVolumeEmitter') + p0.setPoolSize(100) + p0.setBirthRate(0.01) + p0.setLitterSize(100) + p0.factory.setLifespanBase(2.0) + p0.factory.setLifespanSpread(0.5) + p0.factory.setTerminalVelocityBase(400.0) + p0.factory.setTerminalVelocitySpread(40.0) + p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + p0.renderer.setUserAlpha(1.0) + p0.renderer.setFromNode(texture) + p0.renderer.setColor(color) + p0.renderer.setXScaleFlag(1) + p0.renderer.setYScaleFlag(1) + p0.renderer.setInitialXScale(0.12) + p0.renderer.setFinalXScale(0.48) + p0.renderer.setInitialYScale(0.12) + p0.renderer.setFinalYScale(0.48) + p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + p0.emitter.setAmplitudeSpread(0.1) + p0.emitter.setAmplitude(amp) + p0.emitter.setRadius(0.1) + f.addParticles(p0) + circleForceGroup = ForceGroup.ForceGroup('gravity') + force1 = LinearSinkForce(Point3(x, y, z - 100), LinearDistanceForce.FTONEOVERRSQUARED, 2.0, 0.3 * amp * 0.1, 1) + force1.setActive(1) + circleForceGroup.addForce(force1) + f.addForceGroup(circleForceGroup) + f.setPos(x, y, z) + sfxName = random.choice(('phase_4/audio/sfx/firework_explosion_01.mp3', 'phase_4/audio/sfx/firework_explosion_02.mp3', 'phase_4/audio/sfx/firework_explosion_03.mp3')) + sfx = loader.loadSfx(sfxName) + t = Sequence(Func(f.start, render, render), Func(sfx.play), Wait(0.5), Func(p0.setBirthRate, 3), Wait(2.0), Func(f.cleanup), name=getNextSequenceName('shootFireworkSprite')) + t.start() + + +style2shootFunc = {CIRCLE: shootFireworkCircle, + CIRCLELARGE: shootFireworkCircleLarge, + CIRCLESMALL: shootFireworkCircleSmall, + CIRCLESPRITE: shootFireworkCircleSprite, + ROCKET: shootFireworkRocket, + RING: shootFireworkRing, + POP: shootPop} diff --git a/toontown/effects/FlashEffect.py b/toontown/effects/FlashEffect.py new file mode 100644 index 0000000..87664d7 --- /dev/null +++ b/toontown/effects/FlashEffect.py @@ -0,0 +1,29 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class FlashEffect(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'FlashEffect') + EffectController.__init__(self) + self.fadeTime = 0.15 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.effectModel = model.find('**/tt_t_efx_ext_particleWhiteGlow') + self.effectModel.setBillboardAxis(0) + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeOut') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 5, startScale=1.0, blendType='easeIn') + self.track = Sequence(Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color diff --git a/toontown/effects/Glow.py b/toontown/effects/Glow.py new file mode 100644 index 0000000..8ee5831 --- /dev/null +++ b/toontown/effects/Glow.py @@ -0,0 +1,39 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController +from PooledEffect import PooledEffect + +class Glow(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + + def createTrack(self): + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.spark = model.find('**/tt_t_efx_ext_particleSparkle') + self.effectModel = self.attachNewNode('effectModelNode') + self.spark.reparentTo(self.effectModel) + self.effectColor = Vec4(1, 1, 1, 1) + self.effectModel.hide() + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + self.effectModel.hide() + self.effectModel.setColorScale(self.effectColor) + pulseIval = Sequence(Wait(0.1), Func(self.effectModel.setColorScale, self.effectColor), Wait(0.1), Func(self.effectModel.setColorScale, Vec4(1, 1, 1, 0.7))) + self.startEffect = Sequence(Func(self.effectModel.show), Func(pulseIval.loop)) + self.endEffect = Sequence(Func(pulseIval.finish), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def cleanUpEffect(self): + self.effectModel.hide() + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/GlowTrail.py b/toontown/effects/GlowTrail.py new file mode 100644 index 0000000..475e11e --- /dev/null +++ b/toontown/effects/GlowTrail.py @@ -0,0 +1,90 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from PooledEffect import PooledEffect +from EffectController import EffectController + +class GlowTrail(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.card = model.find('**/tt_t_efx_ext_particleWhiteGlow') + self.cardScale = 64.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectScale = 1.0 + self.lifespan = 0.5 + if not GlowTrail.particleDummy: + GlowTrail.particleDummy = render.attachNewNode(ModelNode('GlowTrailParticleDummy')) + GlowTrail.particleDummy.setDepthWrite(0) + GlowTrail.particleDummy.setLightOff() + GlowTrail.particleDummy.setFogOff() + self.f = ParticleEffect.ParticleEffect('GlowTrail') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-1') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('PointEmitter') + self.f.addParticles(self.p0) + self.p0.setPoolSize(64) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(1) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(0) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(self.lifespan) + self.p0.factory.setLifespanSpread(0.1) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, -2.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + + def createTrack(self): + self.startEffect = Sequence(Func(self.p0.setBirthRate, 0.015), Func(self.p0.clearToInitial), Func(self.f.start, self, self.particleDummy)) + self.endEffect = Sequence(Func(self.p0.setBirthRate, 100.0), Wait(self.lifespan + 0.1), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def setLifespan(self, duration): + self.lifespan = duration + self.p0.setPoolSize(int(128 * self.lifespan)) + self.p0.factory.setLifespanBase(self.lifespan) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.15 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.1 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.15 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.4 * self.cardScale * scale) + self.p0.emitter.setAmplitude(10.0 * scale) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/IceCream.py b/toontown/effects/IceCream.py new file mode 100644 index 0000000..9e8cce3 --- /dev/null +++ b/toontown/effects/IceCream.py @@ -0,0 +1,91 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect + +class IceCream(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.card = loader.loadModel('phase_4/models/props/icecream') + self.cardScale = 18.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('IceCream') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-0') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -0.25), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(128) + self.p0.setBirthRate(0.04) + self.p0.setLitterSize(64) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(3.25) + self.p0.factory.setLifespanSpread(1.0) + self.p0.factory.setMassBase(0.5) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(360.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(100.0) + self.p0.factory.setAngularVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(2) + self.p0.renderer.setYScaleFlag(2) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.04), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(0.2), Func(self.p0.setBirthRate, 100.0), Wait(4.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(1.0 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.0 * self.cardScale * scale) + self.p0.emitter.setAmplitude(20.0 * scale) + self.p0.emitter.setAmplitudeSpread(2.0 * scale) + self.p0.emitter.setRadius(150.0 * scale) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/NoiseSparkles.py b/toontown/effects/NoiseSparkles.py new file mode 100644 index 0000000..6f97d55 --- /dev/null +++ b/toontown/effects/NoiseSparkles.py @@ -0,0 +1,98 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class NoiseSparkles(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleSpark_soft') + self.cardScale = 16.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('NoiseSparkles') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + 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) + f1 = ForceGroup.ForceGroup('Noise') + self.noiseForce = LinearNoiseForce(10.0, 0.0) + self.noiseForce.setVectorMasks(1, 1, 1) + self.noiseForce.setActive(0) + f1.addForce(self.noiseForce) + self.f.addForceGroup(f1) + self.p0.setPoolSize(150) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(50) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(2.0) + self.p0.factory.setLifespanSpread(0.5) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAUSER) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.02), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Func(self.noiseForce.setActive, 0), Wait(0.25), Func(self.noiseForce.setActive, 1), Wait(0.5), Func(self.p0.setBirthRate, 100.0), Wait(3.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(1.0 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(1.0 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.5 * self.cardScale * scale) + self.p0.emitter.setAmplitude(25.0 * scale) + self.p0.emitter.setRadius(250.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/PeonyEffect.py b/toontown/effects/PeonyEffect.py new file mode 100644 index 0000000..3c54ab7 --- /dev/null +++ b/toontown/effects/PeonyEffect.py @@ -0,0 +1,47 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import * +from direct.interval.IntervalGlobal import * +from PooledEffect import PooledEffect +from EffectController import EffectController + +class PeonyEffect(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.fadeTime = 1.25 + self.startDelay = 0.0 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_fireworkStars_01') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + self.effectModel.setScale(700 * self.effectScale) + fadeIn = self.effectModel.colorScaleInterval(0.2, Vec4(self.effectColor), startColorScale=Vec4(0, 0, 0, 0), blendType='easeIn') + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 750 * self.effectScale, startScale=700 * self.effectScale, blendType='easeOut') + self.track = Sequence(Wait(self.startDelay), fadeIn, Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/PolyTrail.py b/toontown/effects/PolyTrail.py new file mode 100644 index 0000000..63558e8 --- /dev/null +++ b/toontown/effects/PolyTrail.py @@ -0,0 +1,165 @@ +from pandac.PandaModules import * +from direct.motiontrail.MotionTrail import * +import random + +class PolyTrail(NodePath): + + def __init__(self, root_node_path = None, vertex_list = None, color_list = None, time_window = 0.25): + NodePath.__init__(self, 'PolyTrail') + self.time_window = time_window + self.root_node_path = root_node_path + if not self.root_node_path: + self.root_node_path = render + self.vertex_list = vertex_list + if not self.vertex_list: + self.vertex_list = [Vec4(0.0, 0.4, 0.0, 1.0), Vec4(0.0, 2.0, 0.0, 1.0)] + self.color_list = color_list + if not self.color_list: + self.color_list = [] + for i in self.vertex_list: + self.color_list.append(Vec4(0.1, 0.2, 0.4, 1.0)) + + self.motion_trail = None + self.motion_trail_vertex = None + self.addMotionTrail() + self.setVertexColors(self.color_list) + self.setTimeWindow(self.time_window) + self.motion_trail.attach_motion_trail() + return + + def destroy(self): + self.removeMotionTrail() + self.removeNode() + self.root_node_path = None + self.motion_trail = None + self.vertex_list = None + self.motion_trail_vertex = None + return + + def beginTrail(self): + if self.motion_trail: + self.motion_trail.begin_motion_trail() + + def endTrail(self): + if self.motion_trail: + self.motion_trail.end_motion_trail() + self.motion_trail.time_window = self.time_window + + def removeMotionTrail(self): + self.endTrail() + if self.motion_trail: + self.motion_trail.unregister_motion_trail() + self.motion_trail.delete() + self.motion_trail = None + if self.motion_trail_vertex: + self.motion_trail_vertex = None + return + + def addMotionTrail(self): + if not self.motion_trail: + self.motion_trail = MotionTrail('motion_trail', self) + self.motion_trail.root_node_path = self.root_node_path + if False: + axis = loader.loadModel('models/misc/xyzAxis') + axis.reparentTo(self) + + def test_vertex_function(motion_trail_vertex, vertex_id, context): + return self.vertex_list[vertex_id] + + index = 0 + total_test_vertices = len(self.vertex_list) + while index < total_test_vertices: + self.motion_trail_vertex = self.motion_trail.add_vertex(index, test_vertex_function, None) + if True: + if index == 0: + self.motion_trail_vertex.start_color = Vec4(0.0, 0.25, 0.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 1: + self.motion_trail_vertex.start_color = Vec4(0.25, 0.0, 0.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 2: + self.motion_trail_vertex.start_color = Vec4(0.0, 0.0, 1.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 3: + self.motion_trail_vertex.start_color = Vec4(0.0, 1.0, 1.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + if index == 4: + self.motion_trail_vertex.start_color = Vec4(1.0, 1.0, 0.0, 1.0) + self.motion_trail_vertex.end_color = Vec4(0.0, 0.0, 0.0, 1.0) + index += 1 + + self.motion_trail.update_vertices() + self.motion_trail.calculate_relative_matrix = True + self.motion_trail.time_window = self.time_window + self.motion_trail.continuous_motion_trail = False + self.motion_trail.end_motion_trail() + self.motion_trail.register_motion_trail() + if False: + axis = Vec3(0.0, 0.0, 1.0) + time = 0.0 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.2 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.4 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.6 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 0.8 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + time = 1.0 + angle = (1.0 - time) * 90.0 + matrix = Mat4.rotateMat(angle, axis) + self.motion_trail.update_motion_trail(time, matrix) + return + + def setVertexColors(self, color_list): + if self.motion_trail: + black = Vec4(0.0, 0.0, 0.0, 1.0) + scale_array = [0.25, + 0.4, + 0.7, + 1.0] + total_scales = len(scale_array) + for index in range(len(color_list)): + color = color_list[index] + if index < total_scales: + scale = scale_array[index] * 0.75 + else: + scale = 1.0 + scaled_color = Vec4(color[0] * scale, color[1] * scale, color[2] * scale, 1.0) + self.motion_trail.set_vertex_color(index, scaled_color, black) + + def setUnmodifiedVertexColors(self, color_list): + if self.motion_trail: + for index in range(len(color_list)): + color = color_list[index] + self.motion_trail.set_vertex_color(index, color, color) + + def setTimeWindow(self, time_window): + if self.motion_trail: + self.motion_trail.time_window = time_window + + def setUseNurbs(self, val): + self.motion_trail.use_nurbs = val + + def setTexture(self, texture): + if self.motion_trail: + self.motion_trail.set_texture(texture) + + def setBlendModeOn(self): + if self.motion_trail: + self.motion_trail.geom_node_path.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + + def setBlendModeOff(self): + if self.motion_trail: + self.motion_trail.geom_node_path.node().setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) diff --git a/toontown/effects/PooledEffect.py b/toontown/effects/PooledEffect.py new file mode 100644 index 0000000..7bab21b --- /dev/null +++ b/toontown/effects/PooledEffect.py @@ -0,0 +1,40 @@ +from pandac.PandaModules import * +from direct.showbase import Pool +from direct.showbase.DirectObject import DirectObject +import re + +class PooledEffect(DirectObject, NodePath): + pool = None + poolLimit = 124 + + @classmethod + def getEffect(cls, context = ''): + if cls.pool is None: + cls.pool = Pool.Pool() + if cls.pool.hasFree(): + return cls.pool.checkout() + else: + free, used = cls.pool.getNumItems() + if free + used < cls.poolLimit: + cls.pool.add(cls()) + return cls.pool.checkout() + else: + return + return + + @classmethod + def cleanup(cls): + if cls.pool: + cls.pool.cleanup(cls.destroy) + cls.pool = None + return + + def __init__(self): + NodePath.__init__(self, self.__class__.__name__) + self.accept('clientLogout', self.__class__.cleanup) + + def destroy(self, item = None): + if item: + self.pool.remove(item) + self.ignore('clientLogout') + self.removeNode() diff --git a/toontown/effects/RayBurst.py b/toontown/effects/RayBurst.py new file mode 100644 index 0000000..ce17750 --- /dev/null +++ b/toontown/effects/RayBurst.py @@ -0,0 +1,34 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController + +class RayBurst(NodePath, EffectController): + + def __init__(self): + NodePath.__init__(self, 'RayBurst') + EffectController.__init__(self) + self.fadeTime = 0.25 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_fireworkRays') + self.effectModel.setBillboardPointWorld() + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(1, 1, 1, 0) + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(1, 1, 1, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 700 * self.effectScale, startScale=100 * self.effectScale, blendType='easeOut') + self.track = Sequence(Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale diff --git a/toontown/effects/RingEffect.py b/toontown/effects/RingEffect.py new file mode 100644 index 0000000..a182c93 --- /dev/null +++ b/toontown/effects/RingEffect.py @@ -0,0 +1,109 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class RingEffect(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleSpark_soft') + self.cardScale = 16.0 + self.effectModel = model.find('**/tt_t_efx_ext_particleStars') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('RingEffect') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('PointParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('RingEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -15.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + f1 = ForceGroup.ForceGroup('Noise') + force1 = LinearNoiseForce(2.5, 0.0) + force1.setVectorMasks(1, 1, 1) + force1.setActive(1) + f1.addForce(force1) + self.f.addForceGroup(f1) + self.p0.setPoolSize(16) + self.p0.setBirthRate(0.1) + self.p0.setLitterSize(16) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(1.0) + self.p0.factory.setLifespanSpread(0.2) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAINOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(0) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.p0.emitter.setUniformEmission(16) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.f.setP(random.randint(50, 100)) + self.effectModel.setR(random.randint(0, 90)) + self.effectModel.setPos(random.randint(-20, 20), random.randint(-20, 20), random.randint(-20, 20)) + fadeBlast = self.effectModel.colorScaleInterval(1.0, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(1.0, 75 * self.effectScale, startScale=50 * self.effectScale, blendType='easeOut') + self.track = Sequence(Func(self.p0.setBirthRate, 0.15), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Parallel(fadeBlast, scaleBlast), Func(self.p0.setBirthRate, 100.0), Wait(3.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(1.4 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(1.2 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(1.4 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.2 * self.cardScale * scale) + self.p0.emitter.setAmplitude(75.0 * scale) + self.p0.emitter.setRadius(200.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Ripples.py b/toontown/effects/Ripples.py new file mode 100644 index 0000000..b0aa70e --- /dev/null +++ b/toontown/effects/Ripples.py @@ -0,0 +1,45 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import globalPropPool + +class Ripples(NodePath): + rippleCount = 0 + + def __init__(self, parent = hidden): + NodePath.__init__(self) + self.assign(globalPropPool.getProp('ripples')) + self.reparentTo(parent) + self.getChild(0).setZ(0.1) + self.seqNode = self.find('**/+SequenceNode').node() + self.seqNode.setPlayRate(0) + self.track = None + self.trackId = Ripples.rippleCount + Ripples.rippleCount += 1 + self.setBin('fixed', 100, 1) + self.hide() + return + + def createTrack(self, rate = 1): + tflipDuration = self.seqNode.getNumChildren() / (float(rate) * 24) + self.track = Sequence(Func(self.show), Func(self.seqNode.play, 0, self.seqNode.getNumFrames() - 1), Func(self.seqNode.setPlayRate, rate), Wait(tflipDuration), Func(self.seqNode.setPlayRate, 0), Func(self.hide), name='ripples-track-%d' % self.trackId) + + def play(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.start() + + def loop(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.loop() + + def stop(self): + if self.track: + self.track.finish() + + def destroy(self): + self.stop() + self.track = None + del self.seqNode + self.removeNode() + return diff --git a/toontown/effects/RocketExplosion.py b/toontown/effects/RocketExplosion.py new file mode 100644 index 0000000..dbbf01b --- /dev/null +++ b/toontown/effects/RocketExplosion.py @@ -0,0 +1,87 @@ +from pandac.PandaModules import * +from direct.particles import ParticleEffect +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.showbase import AppRunnerGlobal +import os + +class RocketExplosion(NodePath): + + def __init__(self, parent, smokeParent): + NodePath.__init__(self) + notify = DirectNotifyGlobal.directNotify.newCategory('RocketExplosionParticles') + self.effectNode = parent.attachNewNode('RocketExplosion') + self.effectNode.setBin('fixed', 1) + self.effectNode.setDepthWrite(1) + self.smokeEffectNode = smokeParent.attachNewNode('RocketSmoke') + self.smokeEffectNode.setBin('fixed', 1) + self.smokeEffectNode.setDepthWrite(0) + self.effect = ParticleEffect.ParticleEffect('RocketFire') + self.smokeEffect = ParticleEffect.ParticleEffect('RocketSmoke') + 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/effects')) + particleSearchPath.appendDirectory(Filename('phase_3.5/etc')) + particleSearchPath.appendDirectory(Filename('phase_4/etc')) + particleSearchPath.appendDirectory(Filename('phase_5/etc')) + particleSearchPath.appendDirectory(Filename('phase_6/etc')) + particleSearchPath.appendDirectory(Filename('phase_7/etc')) + particleSearchPath.appendDirectory(Filename('phase_8/etc')) + particleSearchPath.appendDirectory(Filename('phase_9/etc')) + particleSearchPath.appendDirectory(Filename('.')) + pfile = Filename('tt_p_efx_rocketLaunchFire.ptf') + found = vfs.resolveFilename(pfile, particleSearchPath) + if not found: + notify.warning('loadParticleFile() - no path: %s' % pfile) + return + notify.debug('Loading particle file: %s' % pfile) + self.effect.loadConfig(pfile) + ren = self.effect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_4/models/props/tt_m_efx_fireball', '**/*') + pfile = Filename('tt_p_efx_rocketLaunchSmoke.ptf') + found = vfs.resolveFilename(pfile, particleSearchPath) + if not found: + notify.warning('loadParticleFile() - no path: %s' % pfile) + return + notify.debug('Loading particle file: %s' % pfile) + self.smokeEffect.loadConfig(pfile) + ren = self.smokeEffect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_4/models/props/tt_m_efx_smoke', '**/*') + self.endSeq = None + self.cleanupCompleted = 0 + return + + def start(self): + self.effect.start(parent=self.effectNode) + self.smokeEffect.start(parent=self.smokeEffectNode) + + def stop(self): + try: + self.effect.disable() + self.smokeEffect.disable() + except AttributeError: + pass + + def end(self): + self.endSeq = Sequence(LerpColorScaleInterval(self.smokeEffectNode, 2.0, Vec4(1, 1, 1, 0)), Func(self.destroy)) + self.endSeq.start() + + def destroy(self): + if self.endSeq: + self.endSeq.pause() + self.endSeq = None + self.stop() + if not self.cleanupCompleted: + self.effect.cleanup() + self.smokeEffect.cleanup() + self.effectNode.removeNode() + self.smokeEffectNode.removeNode() + del self.effect + del self.smokeEffect + del self.effectNode + del self.smokeEffectNode + self.cleanupCompleted = 1 + return diff --git a/toontown/effects/ScavengerHuntEffects.py b/toontown/effects/ScavengerHuntEffects.py new file mode 100644 index 0000000..1b690c4 --- /dev/null +++ b/toontown/effects/ScavengerHuntEffects.py @@ -0,0 +1,119 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.gui.DirectFrame import DirectFrame +from direct.gui.DirectLabel import DirectLabel + +class ScavengerHuntEffect: + images = None + + def __init__(self, beanAmount): + if not ScavengerHuntEffect.images: + ScavengerHuntEffect.images = loader.loadModel('phase_4/models/props/tot_jar') + self.npRoot = DirectFrame(parent=aspect2d, relief=None, scale=0.75, pos=(0, 0, 0.6)) + if beanAmount > 0: + self.npRoot.setColorScale(VBase4(1, 1, 1, 0)) + self.jar = DirectFrame(parent=self.npRoot, relief=None, image=ScavengerHuntEffect.images.find('**/tot_jar')) + self.jar.hide() + self.eventImage = NodePath('EventImage') + self.eventImage.reparentTo(self.npRoot) + self.countLabel = DirectLabel(parent=self.jar, relief=None, text='+0', text_pos=(0.02, -0.2), text_scale=0.25, text_fg=(0.95, 0.0, 0, 1), text_font=ToontownGlobals.getSignFont()) + + def countUp(t, startVal, endVal): + beanCountStr = startVal + t * (endVal - startVal) + self.countLabel['text'] = '+' + `(int(beanCountStr))` + + def setCountColor(color): + self.countLabel['text_fg'] = color + + self.track = Sequence(LerpColorScaleInterval(self.npRoot, 1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Wait(1), Func(self.jar.show), LerpColorScaleInterval(self.eventImage, 1, colorScale=VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Parallel(LerpScaleInterval(self.npRoot, 1, scale=0.5, startScale=0.75), LerpPosInterval(self.npRoot, 1, pos=VBase3(-0.9, 0, -0.83))), LerpFunc(countUp, duration=2, extraArgs=[0, beanAmount]), Func(setCountColor, VBase4(0.95, 0.95, 0, 1)), Wait(3), Func(self.destroy)) + else: + self.npRoot.setColorScale(VBase4(1, 1, 1, 0)) + self.attemptFailedMsg() + self.track = Sequence(LerpColorScaleInterval(self.npRoot, 1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Wait(5), LerpColorScaleInterval(self.npRoot, 1, colorScale=VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Func(self.destroy)) + return + + def play(self): + if self.npRoot: + self.track.start() + + def stop(self): + if self.track != None: + if self.track.isPlaying(): + self.track.finish() + return + + def cleanupIntervals(self, interval): + while len(interval) > 0: + if isinstance(interval[0], Sequence) or isinstance(interval[0], Parallel): + self.cleanupIntervals(interval[0]) + interval.pop(0) + else: + interval.pop(0) + + def destroy(self): + self.stop() + self.track = None + if hasattr(self, 'eventImage') and self.eventImage: + self.eventImage.detachNode() + del self.eventImage + if hasattr(self, 'countLabel') and self.countLabel: + self.countLabel.destroy() + del self.countLabel + if hasattr(self, 'jar') and self.jar: + self.jar.destroy() + del self.jar + if hasattr(self, 'npRoot') and self.npRoot: + self.npRoot.destroy() + del self.npRoot + return + + +class TrickOrTreatTargetEffect(ScavengerHuntEffect): + + def __init__(self, beanAmount): + ScavengerHuntEffect.__init__(self, beanAmount) + if beanAmount > 0: + self.pumpkin = DirectFrame(parent=self.eventImage, relief=None, image=ScavengerHuntEffect.images.find('**/tot_pumpkin_tall')) + return + + def attemptFailedMsg(self): + pLabel = DirectLabel(parent=self.npRoot, relief=None, pos=(0.0, 0.0, -0.15), text=TTLocalizer.TrickOrTreatMsg, text_fg=(0.95, 0.5, 0.0, 1.0), text_scale=0.12, text_font=ToontownGlobals.getSignFont()) + return + + def destroy(self): + if hasattr(self, 'pumpkin') and self.pumpkin: + self.pumpkin.destroy() + ScavengerHuntEffect.destroy(self) + + +class WinterCarolingEffect(ScavengerHuntEffect): + + def __init__(self, beanAmount): + ScavengerHuntEffect.__init__(self, beanAmount) + if beanAmount > 0: + sm = loader.loadModel('phase_5.5/models/estate/tt_m_prp_ext_snowman_icon') + self.snowman = DirectFrame(parent=self.eventImage, relief=None, image=sm, scale=20.0) + return + + def attemptFailedMsg(self): + pLabel = DirectLabel(parent=self.npRoot, relief=None, pos=(0.0, 0.0, -0.15), text=TTLocalizer.WinterCarolingMsg, text_fg=(0.9, 0.9, 1.0, 1.0), text_scale=0.12, text_font=ToontownGlobals.getSignFont()) + return + + def destroy(self): + if hasattr(self, 'snowman') and self.snowman: + self.snowman.destroy() + ScavengerHuntEffect.destroy(self) + + +class TrickOrTreatMilestoneEffect: + + def __init__(self): + pass + + def play(self): + pass + + def stop(self): + pass diff --git a/toontown/effects/SimpleSparkles.py b/toontown/effects/SimpleSparkles.py new file mode 100644 index 0000000..3910c7d --- /dev/null +++ b/toontown/effects/SimpleSparkles.py @@ -0,0 +1,98 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect +import random + +class SimpleSparkles(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('SimpleSparkles') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-2') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + 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) + self.p0.setPoolSize(32) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(4) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(2.0) + self.p0.factory.setLifespanSpread(0.5) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(180.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(10.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitude(3.0) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.03), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(1.0), Func(self.p0.setBirthRate, 100.0), Wait(2.5), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.8 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.4 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.8 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.4 * self.cardScale * scale) + self.p0.emitter.setAmplitude(30.0 * scale) + self.p0.emitter.setRadius(75.0 * scale) + + def setRadius(self, radius): + self.p0.emitter.setRadius(radius) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/SkullBurst.py b/toontown/effects/SkullBurst.py new file mode 100644 index 0000000..f82030f --- /dev/null +++ b/toontown/effects/SkullBurst.py @@ -0,0 +1,47 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import * +from direct.interval.IntervalGlobal import * +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SkullBurst(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.fadeTime = 1.5 + self.startDelay = 0.0 + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_skull') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + self.effectModel.setScale(700 * self.effectScale) + fadeIn = self.effectModel.colorScaleInterval(0.2, Vec4(self.effectColor), startColorScale=Vec4(0, 0, 0, 0), blendType='easeIn') + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeIn') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 850 * self.effectScale, startScale=750 * self.effectScale, blendType='easeOut') + self.track = Sequence(Wait(self.startDelay), fadeIn, Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def setEffectColor(self, color): + self.effectColor = color + + def setEffectScale(self, scale): + self.effectScale = scale + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/SkullFlash.py b/toontown/effects/SkullFlash.py new file mode 100644 index 0000000..746cafc --- /dev/null +++ b/toontown/effects/SkullFlash.py @@ -0,0 +1,38 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import * +from direct.interval.IntervalGlobal import * +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SkullFlash(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.fadeTime = 0.15 + self.startDelay = 0.0 + self.effectColor = Vec4(1, 1, 1, 1) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.effectModel = model.find('**/tt_t_efx_ext_skullGlow') + self.effectModel.reparentTo(self) + self.effectModel.setColorScale(0, 0, 0, 0) + self.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.setBillboardPointWorld() + self.setDepthWrite(0) + self.setLightOff() + self.setFogOff() + + def createTrack(self): + self.effectModel.setColorScale(0, 0, 0, 0) + fadeBlast = self.effectModel.colorScaleInterval(self.fadeTime, Vec4(0, 0, 0, 0), startColorScale=Vec4(self.effectColor), blendType='easeOut') + scaleBlast = self.effectModel.scaleInterval(self.fadeTime, 2.0, startScale=1.0, blendType='easeOut') + self.track = Sequence(Wait(self.startDelay), Parallel(fadeBlast, scaleBlast), Func(self.cleanUpEffect)) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Sparks.py b/toontown/effects/Sparks.py new file mode 100644 index 0000000..a7391ca --- /dev/null +++ b/toontown/effects/Sparks.py @@ -0,0 +1,55 @@ +from pandac.PandaModules import * +from direct.particles import ParticleEffect +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import AppRunnerGlobal +import os + +class Sparks(NodePath): + + def __init__(self, parent, renderParent): + NodePath.__init__(self) + notify = DirectNotifyGlobal.directNotify.newCategory('SparkParticles') + self.renderParent = renderParent.attachNewNode('sparkRenderParent') + self.renderParent.setBin('fixed', 0) + self.renderParent.setDepthWrite(0) + self.assign(parent.attachNewNode('sparks')) + self.effect = ParticleEffect.ParticleEffect('Sparks') + 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/effects')) + particleSearchPath.appendDirectory(Filename('phase_3.5/etc')) + particleSearchPath.appendDirectory(Filename('phase_4/etc')) + particleSearchPath.appendDirectory(Filename('phase_5/etc')) + particleSearchPath.appendDirectory(Filename('phase_6/etc')) + particleSearchPath.appendDirectory(Filename('phase_7/etc')) + particleSearchPath.appendDirectory(Filename('phase_8/etc')) + particleSearchPath.appendDirectory(Filename('phase_9/etc')) + particleSearchPath.appendDirectory(Filename('.')) + pfile = Filename('sparks.ptf') + found = vfs.resolveFilename(pfile, particleSearchPath) + if not found: + notify.warning('loadParticleFile() - no path: %s' % pfile) + return + notify.debug('Loading particle file: %s' % pfile) + self.effect.loadConfig(pfile) + ren = self.effect.getParticlesNamed('particles-1').getRenderer() + ren.setTextureFromNode('phase_6/models/karting/particleSpark', '**/*') + + def start(self): + self.effect.start(self, self.renderParent) + + def stop(self): + try: + self.effect.disable() + except AttributeError: + pass + + def destroy(self): + self.stop() + self.effect.cleanup() + self.renderParent.removeNode() + del self.effect + del self.renderParent diff --git a/toontown/effects/SparksTrail.py b/toontown/effects/SparksTrail.py new file mode 100644 index 0000000..f1bf0e8 --- /dev/null +++ b/toontown/effects/SparksTrail.py @@ -0,0 +1,90 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SparksTrail(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectScale = 1.0 + self.lifespan = 1.0 + if not SparksTrail.particleDummy: + SparksTrail.particleDummy = render.attachNewNode(ModelNode('SparksTrailParticleDummy')) + SparksTrail.particleDummy.setDepthWrite(0) + SparksTrail.particleDummy.setLightOff() + SparksTrail.particleDummy.setFogOff() + self.f = ParticleEffect.ParticleEffect('SparksTrail') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-1') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('PointEmitter') + self.f.addParticles(self.p0) + self.p0.setPoolSize(64) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(1) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(0) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(0.5) + self.p0.factory.setLifespanSpread(0.1) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(90.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(25.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, -2.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + + def createTrack(self): + self.startEffect = Sequence(Func(self.p0.setBirthRate, 0.01), Func(self.p0.clearToInitial), Func(self.f.start, self, self.particleDummy)) + self.endEffect = Sequence(Func(self.p0.setBirthRate, 100.0), Wait(1.0), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.1 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.2 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.1 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.2 * self.cardScale * scale) + self.p0.emitter.setAmplitude(20.0 * scale) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/SparksTrailLong.py b/toontown/effects/SparksTrailLong.py new file mode 100644 index 0000000..4760274 --- /dev/null +++ b/toontown/effects/SparksTrailLong.py @@ -0,0 +1,98 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from PooledEffect import PooledEffect +from EffectController import EffectController + +class SparksTrailLong(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_particleCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.effectScale = 1.0 + self.lifespan = 2.0 + if not SparksTrailLong.particleDummy: + SparksTrailLong.particleDummy = render.attachNewNode(ModelNode('SparksTrailLongParticleDummy')) + SparksTrailLong.particleDummy.setDepthWrite(0) + self.f = ParticleEffect.ParticleEffect('SparksTrailLong') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-1') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('PointEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -2.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(128) + self.p0.setBirthRate(0.02) + self.p0.setLitterSize(1) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(0) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(self.lifespan) + self.p0.factory.setLifespanSpread(0.1) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(90.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(25.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setAmplitudeSpread(0.0) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, -2.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + + def createTrack(self): + self.startEffect = Sequence(Func(self.p0.setBirthRate, 0.015), Func(self.p0.clearToInitial), Func(self.f.start, self, self.particleDummy)) + self.endEffect = Sequence(Func(self.p0.setBirthRate, 100.0), Wait(self.lifespan + 0.1), Func(self.cleanUpEffect)) + self.track = Sequence(self.startEffect, Wait(1.0), self.endEffect) + + def setLifespan(self, duration): + self.lifespan = duration + self.p0.factory.setLifespanBase(duration) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.05 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.05 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(0.5 * self.cardScale * scale) + self.p0.emitter.setAmplitude(30.0 * scale) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Splash.py b/toontown/effects/Splash.py new file mode 100644 index 0000000..1f89af6 --- /dev/null +++ b/toontown/effects/Splash.py @@ -0,0 +1,69 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from Ripples import * +from toontown.battle.BattleProps import globalPropPool +from toontown.battle import BattleParticles + +class Splash(NodePath): + splashCount = 0 + + def __init__(self, parent = hidden, wantParticles = 1): + NodePath.__init__(self, parent) + self.assign(parent.attachNewNode('splash')) + self.splashdown = globalPropPool.getProp('splashdown') + self.splashdown.reparentTo(self) + self.splashdown.setZ(-0.01) + self.splashdown.setScale(0.4) + ta = TransparencyAttrib.make(TransparencyAttrib.MBinary) + self.splashdown.node().setAttrib(ta, 1) + self.splashdown.setBin('fixed', 130, 1) + self.ripples = Ripples(self) + self.ripples.setBin('fixed', 120, 1) + self.wantParticles = wantParticles + if self.wantParticles: + self.pSystem = BattleParticles.createParticleEffect('SplashLines') + self.pSystem.setScale(0.4) + self.pSystem.setBin('fixed', 150, 1) + self.particles = self.pSystem.particlesDict.get('particles-1') + self.track = None + self.trackId = Splash.splashCount + Splash.splashCount += 1 + self.setBin('fixed', 100, 1) + self.hide() + return + + def createTrack(self, rate = 1): + self.ripples.createTrack(rate) + self.splashdown.setPlayRate(rate, 'splashdown') + animDuration = self.splashdown.getDuration('splashdown') * 0.65 + rippleSequence = Sequence(Func(self.splashdown.show), Func(self.splashdown.play, 'splashdown'), Wait(animDuration), Func(self.splashdown.hide)) + if self.wantParticles: + particleSequence = Sequence(Func(self.pSystem.show), Func(self.particles.induceLabor), Func(self.pSystem.start, self), Wait(2.2), Func(self.pSystem.hide), Func(self.pSystem.disable)) + else: + particleSequence = Sequence() + self.track = Sequence(Func(self.show), Parallel(self.ripples.track, rippleSequence, particleSequence), Func(self.hide), name='splashdown-%d-track' % self.trackId) + + def play(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.start() + + def loop(self, rate = 1): + self.stop() + self.createTrack(rate) + self.track.loop() + + def stop(self): + if self.track: + self.track.finish() + + def destroy(self): + self.stop() + del self.track + self.ripples.destroy() + del self.ripples + if self.wantParticles: + self.pSystem.cleanup() + del self.pSystem + del self.particles + self.removeNode() diff --git a/toontown/effects/StarBurst.py b/toontown/effects/StarBurst.py new file mode 100644 index 0000000..6d500b6 --- /dev/null +++ b/toontown/effects/StarBurst.py @@ -0,0 +1,94 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.particles import ParticleEffect, Particles, ForceGroup +from EffectController import EffectController +from PooledEffect import PooledEffect + +class StarBurst(PooledEffect, EffectController): + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + model = loader.loadModel('phase_4/models/props/tt_m_efx_ext_fireworkCards') + self.card = model.find('**/tt_t_efx_ext_particleStars') + self.cardScale = 64.0 + self.setDepthWrite(0) + self.setColorScaleOff() + self.setLightOff() + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.f = ParticleEffect.ParticleEffect('StarBurst') + self.f.reparentTo(self) + self.p0 = Particles.Particles('particles-0') + self.p0.setFactory('ZSpinParticleFactory') + self.p0.setRenderer('SpriteParticleRenderer') + self.p0.setEmitter('SphereSurfaceEmitter') + self.f.addParticles(self.p0) + f0 = ForceGroup.ForceGroup('Gravity') + force0 = LinearVectorForce(Vec3(0.0, 0.0, -25.0), 1.0, 0) + force0.setVectorMasks(1, 1, 1) + force0.setActive(1) + f0.addForce(force0) + self.f.addForceGroup(f0) + self.p0.setPoolSize(128) + self.p0.setBirthRate(0.04) + self.p0.setLitterSize(64) + self.p0.setLitterSpread(0) + self.p0.setSystemLifespan(0.0) + self.p0.setLocalVelocityFlag(1) + self.p0.setSystemGrowsOlderFlag(0) + self.p0.factory.setLifespanBase(1.25) + self.p0.factory.setLifespanSpread(0.5) + self.p0.factory.setMassBase(1.0) + self.p0.factory.setMassSpread(0.0) + self.p0.factory.setTerminalVelocityBase(400.0) + self.p0.factory.setTerminalVelocitySpread(0.0) + self.p0.factory.setInitialAngle(0.0) + self.p0.factory.setInitialAngleSpread(360.0) + self.p0.factory.enableAngularVelocity(1) + self.p0.factory.setAngularVelocity(0.0) + self.p0.factory.setAngularVelocitySpread(0.0) + self.p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) + self.p0.renderer.setUserAlpha(1.0) + self.p0.renderer.setColorBlendMode(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne) + self.p0.renderer.setFromNode(self.card) + self.p0.renderer.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.p0.renderer.setXScaleFlag(1) + self.p0.renderer.setYScaleFlag(1) + self.p0.renderer.setAnimAngleFlag(1) + self.p0.renderer.setNonanimatedTheta(0.0) + self.p0.renderer.setAlphaBlendMethod(BaseParticleRenderer.PPBLENDLINEAR) + self.p0.renderer.setAlphaDisable(0) + self.p0.renderer.getColorInterpolationManager().addLinear(0.0, 0.1, Vec4(0, 0, 0, 0), self.effectColor, 1) + self.p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) + self.p0.emitter.setOffsetForce(Vec3(0.0, 0.0, 0.0)) + self.p0.emitter.setExplicitLaunchVector(Vec3(1.0, 0.0, 0.0)) + self.p0.emitter.setRadiateOrigin(Point3(0.0, 0.0, 0.0)) + self.setEffectScale(self.effectScale) + self.setEffectColor(self.effectColor) + + def createTrack(self): + self.track = Sequence(Func(self.p0.setBirthRate, 0.04), Func(self.p0.clearToInitial), Func(self.f.start, self, self), Wait(0.2), Func(self.p0.setBirthRate, 100.0), Wait(2.0), Func(self.cleanUpEffect)) + + def setEffectScale(self, scale): + self.effectScale = scale + self.p0.renderer.setInitialXScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalXScale(0.8 * self.cardScale * scale) + self.p0.renderer.setInitialYScale(0.5 * self.cardScale * scale) + self.p0.renderer.setFinalYScale(1.0 * self.cardScale * scale) + self.p0.emitter.setAmplitude(100.0 * scale) + self.p0.emitter.setAmplitudeSpread(50.0 * scale) + self.p0.emitter.setRadius(220.0 * scale) + + def setEffectColor(self, color): + self.effectColor = color + self.p0.renderer.setColor(self.effectColor) + + def cleanUpEffect(self): + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/TrailExplosion.py b/toontown/effects/TrailExplosion.py new file mode 100644 index 0000000..552872b --- /dev/null +++ b/toontown/effects/TrailExplosion.py @@ -0,0 +1,75 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from EffectController import EffectController +from PooledEffect import PooledEffect +from toontown.effects.SparksTrailLong import SparksTrailLong +import random + +class TrailExplosion(PooledEffect, EffectController): + trailsVel = [[Vec3(150, -50, 100), Vec3(-150, -50, 100), Vec3(0, 150, 100)], [Vec3(120, 120, 100), + Vec3(120, -120, 100), + Vec3(-120, 120, 100), + Vec3(-120, -120, 100)], [Vec3(0, 150, 100), + Vec3(140, 30, 100), + Vec3(-140, 30, 100), + Vec3(30, -60, 100), + Vec3(-30, -60, 100)]] + + def __init__(self): + PooledEffect.__init__(self) + EffectController.__init__(self) + self.effectScale = 1.0 + self.effectColor = Vec4(1, 1, 1, 1) + self.numTrails = 5 + self.trails = [] + self.trailEffects = [] + self.trailIval = Parallel() + + def createTrack(self): + self.trails = [] + self.trailEffects = [] + self.trailIval = Parallel() + vels = None + if self.numTrails >= 3 and self.numTrails <= 5: + vels = self.trailsVel[self.numTrails - 3] + for i in range(self.numTrails): + self.trails.append(self.attachNewNode('trail')) + vel = Vec3(0, 0, 0) + if vels: + vel = Vec3(vels[i][0] + random.randint(-20, 20), vels[i][1] + random.randint(-20, 20), vels[i][2] + random.randint(0, 50)) + else: + vel = Vec3(random.randint(-200, 200), random.randint(-200, 200), random.randint(80, 150)) + vel *= self.effectScale + dur = 2.0 + random.random() / 4.0 + self.trailIval.append(ProjectileInterval(self.trails[i], startVel=vel, duration=dur, gravityMult=0.6)) + self.trailEffects.append(SparksTrailLong.getEffect()) + if self.trailEffects[i]: + self.trailEffects[i].reparentTo(self.trails[i]) + self.trailEffects[i].setLifespan(2.0) + self.trailEffects[i].setEffectColor(self.effectColor) + self.trailEffects[i].setEffectScale(self.effectScale) + self.trailIval.append(Sequence(Func(self.trailEffects[i].startLoop), Wait(dur), Func(self.trailEffects[i].stopLoop))) + + self.track = Sequence(self.trailIval, Func(self.cleanUpEffect)) + return + + def setEffectScale(self, scale): + self.effectScale = scale + + def setEffectColor(self, color): + self.effectColor = color + + def cleanUpEffect(self): + for effect in self.trailEffects: + if effect: + effect.stopLoop() + effect = None + + EffectController.cleanUpEffect(self) + if self.pool and self.pool.isUsed(self): + self.pool.checkin(self) + return + + def destroy(self): + EffectController.destroy(self) + PooledEffect.destroy(self) diff --git a/toontown/effects/Wake.py b/toontown/effects/Wake.py new file mode 100644 index 0000000..ebaf20d --- /dev/null +++ b/toontown/effects/Wake.py @@ -0,0 +1,137 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import globalPropPool + +class Wake(NodePath): + wakeCount = 0 + + def __init__(self, parent = hidden, target = hidden): + NodePath.__init__(self) + self.assign(parent.attachNewNode('wake')) + self.target = target + self.ripples = globalPropPool.getProp('ripples') + tformNode = self.ripples.getChild(0) + tformNode.setZ(0.01) + self.seqNodePath = self.ripples.find('**/+SequenceNode') + self.seqNode = self.seqNodePath.node() + self.sortBase = 10 + self.rippleCount = 0 + self.doLaters = [None] * 20 + self.trackId = Wake.wakeCount + Wake.wakeCount += 1 + return + + def createRipple(self, zPos, rate = 1.0, startFrame = 0): + ripple = self.ripples.copyTo(self) + ripple.iPos(self.target) + ripple.setZ(render, zPos + self.rippleCount * 0.001) + ripple.setBin('fixed', self.sortBase + self.rippleCount, 1) + seqNode = ripple.find('**/+SequenceNode').node() + seqNode.setPlayRate(rate) + seqNode.play(startFrame, seqNode.getNumFrames() - 1) + duration = (24 - startFrame) / 24.0 + + def clearDoLaterList(rippleCount): + self.doLaters[rippleCount] = None + return + + def destroyRipple(task): + ripple.removeNode() + + t = taskMgr.doMethodLater(duration, clearDoLaterList, 'wake-%d-destroy-%d' % (self.trackId, self.rippleCount), extraArgs=(self.rippleCount,), uponDeath=destroyRipple) + self.doLaters[self.rippleCount] = t + self.rippleCount = (self.rippleCount + 1) % 20 + + def stop(self): + for i in range(len(self.doLaters)): + if self.doLaters[i]: + taskMgr.remove(self.doLaters[i]) + self.doLaters[i] = None + + return + + def destroy(self): + self.stop() + self.removeNode() + self.ripples.removeNode() + del self.target + + +class WakeSequence(NodePath): + wakeCount = 0 + + def __init__(self, parent = hidden): + NodePath.__init__(self) + self.assign(globalPropPool.getProp('wake')) + self.reparentTo(parent) + tformNode = self.getChild(0) + tformNode.setZ(0.1) + self.startNodePath = self.find('**/+SequenceNode') + self.startSeqNode = self.startNodePath.node() + self.startSeqNode.setName('start') + self.startSeqNode.setPlayRate(0) + self.cycleNodePath = NodePath(SequenceNode(0, 'cycle')) + self.cycleNodePath.reparentTo(tformNode) + self.cycleSeqNode = self.cycleNodePath.node() + self.endNodePath = NodePath(SequenceNode(0, 'end')) + self.endNodePath.reparentTo(tformNode) + self.endSeqNode = self.endNodePath.node() + children = self.startNodePath.getChildren() + for child in children[12:16]: + child.reparentTo(self.cycleNodePath) + + for child in children[16:]: + child.reparentTo(self.endNodePath) + + self.tracks = [] + self.rate = None + self.trackId = Wake.wakeCount + Wake.wakeCount += 1 + self.setBin('fixed', 10, 1) + self.hide() + return + + def createTracks(self, rate = 1): + self.stop() + self.tracks = [] + tflipDuration = self.startSeqNode.getNumChildren() / (float(rate) * 24) + startTrack = Sequence(Func(self.show), Func(self.showTrack, 0), Func(self.startSeqNode.play, 0, self.startSeqNode.getNumFrames() - 1), Func(self.startSeqNode.setPlayRate, rate), Wait(tflipDuration), Func(self.showTrack, 1), Func(self.startSeqNode.play, 0, self.startSeqNode.getNumFrames() - 1), Func(self.cycleSeqNode.setPlayRate, rate), name='start-wake-track-%d' % self.trackId) + self.tracks.append(startTrack) + tflipDuration = self.endSeqNode.getNumChildren() / (float(rate) * 24) + endTrack = Sequence(Func(self.showTrack, 2), Func(self.endSeqNode.play, 0, self.endSeqNode.getNumFrames() - 1), Func(self.endSeqNode.setPlayRate, rate), Wait(tflipDuration), Func(self.endSeqNode.setPlayRate, 0), Func(self.hide), name='end-wake-track-%d' % self.trackId) + self.tracks.append(endTrack) + self.rate = rate + + def showTrack(self, trackId): + if trackId == 0: + self.startNodePath.show() + else: + self.startNodePath.hide() + if trackId == 1: + self.cycleNodePath.show() + else: + self.cycleNodePath.hide() + if trackId == 2: + self.endNodePath.show() + else: + self.endNodePath.hide() + + def play(self, trackId, rate = 1): + if self.rate != rate: + self.createTracks(rate) + self.tracks[trackId].start() + + def loop(self, trackId, rate = 1): + if self.rate != rate: + self.createTracks(rate) + self.tracks[trackId].loop() + + def stop(self): + for track in self.tracks: + track.finish() + + def destroy(self): + self.stop() + self.tracks = None + self.removeNode() + return diff --git a/toontown/effects/__init__.py b/toontown/effects/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/estate/BankGUI.py b/toontown/estate/BankGUI.py new file mode 100644 index 0000000..6aa88c0 --- /dev/null +++ b/toontown/estate/BankGUI.py @@ -0,0 +1,141 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task.Task import Task + +class BankGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('BankGui') + + def __init__(self, doneEvent, allowWithdraw = 1): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.33, 1, 1.1), pos=(0, 0, 0)) + self.initialiseoptions(BankGui) + self.doneEvent = doneEvent + self.__transactionAmount = 0 + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + jarGui = loader.loadModel('phase_3.5/models/gui/jar_gui') + arrowGui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + bankModel = loader.loadModel('phase_5.5/models/estate/jellybeanBank.bam') + bankModel.setDepthWrite(1) + bankModel.setDepthTest(1) + bankModel.find('**/jellybeans').setDepthWrite(0) + bankModel.find('**/jellybeans').setDepthTest(0) + 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')) + arrowImageList = (arrowGui.find('**/CrtATn_R_Arrow_UP'), + arrowGui.find('**/CrtATn_R_Arrow_DN'), + arrowGui.find('**/CrtATn_R_Arrow_RLVR'), + arrowGui.find('**/CrtATn_R_Arrow_UP')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(-0.2, 0, -0.4), text=TTLocalizer.BankGuiCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.2, 0, -0.4), text=TTLocalizer.BankGuiOk, text_scale=0.06, text_pos=(0, -0.1), command=self.__requestTransaction) + self.jarDisplay = DirectLabel(parent=self, relief=None, pos=(-0.4, 0, 0), scale=0.7, text=str(base.localAvatar.getMoney()), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), image=jarGui.find('**/Jar'), text_font=ToontownGlobals.getSignFont()) + self.bankDisplay = DirectLabel(parent=self, relief=None, pos=(0.4, 0, 0), scale=0.9, text=str(base.localAvatar.getBankMoney()), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), geom=bankModel, geom_scale=0.08, geom_pos=(0, 10, -0.26), geom_hpr=(0, 0, 0), text_font=ToontownGlobals.getSignFont()) + self.depositArrow = DirectButton(parent=self, relief=None, image=arrowImageList, image_scale=(1, 1, 1), image3_color=Vec4(0.6, 0.6, 0.6, 0.25), pos=(0.01, 0, 0.15)) + self.withdrawArrow = DirectButton(parent=self, relief=None, image=arrowImageList, image_scale=(-1, 1, 1), image3_color=Vec4(0.6, 0.6, 0.6, 0.25), pos=(-0.01, 0, -0.15)) + self.depositArrow.bind(DGG.B1PRESS, self.__depositButtonDown) + self.depositArrow.bind(DGG.B1RELEASE, self.__depositButtonUp) + self.withdrawArrow.bind(DGG.B1PRESS, self.__withdrawButtonDown) + self.withdrawArrow.bind(DGG.B1RELEASE, self.__withdrawButtonUp) + self.accept('bankAsleep', self.__cancel) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__bankMoneyChange) + if allowWithdraw: + self.depositArrow.setPos(0.01, 0, 0.15) + self.withdrawArrow.setPos(-0.01, 0, -0.15) + else: + self.depositArrow.setPos(0, 0, 0) + self.withdrawArrow.hide() + buttons.removeNode() + jarGui.removeNode() + arrowGui.removeNode() + self.__updateTransaction(0) + return + + def destroy(self): + taskMgr.remove(self.taskName('runCounter')) + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + DirectFrame.destroy(self) + + def __cancel(self): + messenger.send(self.doneEvent, [0]) + + def __requestTransaction(self): + messenger.send(self.doneEvent, [self.__transactionAmount]) + + def __updateTransaction(self, amount): + hitLimit = 0 + self.__transactionAmount += amount + jarMoney = base.localAvatar.getMoney() + maxJarMoney = base.localAvatar.getMaxMoney() + bankMoney = base.localAvatar.getBankMoney() + maxBankMoney = base.localAvatar.getMaxBankMoney() + self.__transactionAmount = min(self.__transactionAmount, jarMoney) + self.__transactionAmount = min(self.__transactionAmount, maxBankMoney - bankMoney) + self.__transactionAmount = -min(-self.__transactionAmount, maxJarMoney - jarMoney) + self.__transactionAmount = -min(-self.__transactionAmount, bankMoney) + newJarMoney = jarMoney - self.__transactionAmount + newBankMoney = bankMoney + self.__transactionAmount + if newJarMoney <= 0 or newBankMoney >= maxBankMoney: + self.depositArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.depositArrow['state'] = DGG.NORMAL + if newBankMoney <= 0 or newJarMoney >= maxJarMoney: + self.withdrawArrow['state'] = DGG.DISABLED + hitLimit = 1 + else: + self.withdrawArrow['state'] = DGG.NORMAL + self.jarDisplay['text'] = str(newJarMoney) + self.bankDisplay['text'] = str(newBankMoney) + return (hitLimit, + newJarMoney, + newBankMoney, + self.__transactionAmount) + + def __runCounter(self, task): + if task.time - task.prevTime < task.delayTime: + return Task.cont + else: + task.delayTime = max(0.05, task.delayTime * 0.75) + task.prevTime = task.time + hitLimit, jar, bank, trans = self.__updateTransaction(task.delta) + if hitLimit: + return Task.done + else: + return Task.cont + + def __depositButtonUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('runCounter')) + + def __depositButtonDown(self, event): + messenger.send('wakeup') + task = Task(self.__runCounter) + task.delayTime = 0.4 + task.prevTime = 0.0 + task.delta = 1 + hitLimit, jar, bank, trans = self.__updateTransaction(task.delta) + if not hitLimit: + taskMgr.add(task, self.taskName('runCounter')) + + def __withdrawButtonUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('runCounter')) + + def __withdrawButtonDown(self, event): + messenger.send('wakeup') + task = Task(self.__runCounter) + task.delayTime = 0.4 + task.prevTime = 0.0 + task.delta = -1 + hitLimit, jar, bank, trans = self.__updateTransaction(task.delta) + if not hitLimit: + taskMgr.add(task, self.taskName('runCounter')) + + def __moneyChange(self, money): + self.__updateTransaction(0) + + def __bankMoneyChange(self, bankMoney): + self.__updateTransaction(0) diff --git a/toontown/estate/BankGlobals.py b/toontown/estate/BankGlobals.py new file mode 100644 index 0000000..7aef483 --- /dev/null +++ b/toontown/estate/BankGlobals.py @@ -0,0 +1,7 @@ +BANK_MOVIE_CLEAR = 1 +BANK_MOVIE_GUI = 2 +BANK_MOVIE_DEPOSIT = 3 +BANK_MOVIE_WITHDRAW = 4 +BANK_MOVIE_NO_OP = 5 +BANK_MOVIE_NOT_OWNER = 6 +BANK_MOVIE_NO_OWNER = 7 diff --git a/toontown/estate/BeanRecipeGui.py b/toontown/estate/BeanRecipeGui.py new file mode 100644 index 0000000..6f5406e --- /dev/null +++ b/toontown/estate/BeanRecipeGui.py @@ -0,0 +1,60 @@ +from direct.gui.DirectGui import * +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from toontown.estate import GardenGlobals +from toontown.estate import PlantingGUI +from toontown.toonbase import TTLocalizer + +class BeanRecipeGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantingGUI') + + def __init__(self, parent, recipe, **kw): + left = 0 + right = 0.445 + bottom = 0 + top = 0.08 + borderWidth = 0.01 + optiondefs = [('relief', DGG.RIDGE, None), + ('state', 'normal', None), + ('pos', (0, 0, 0), None), + ('frameSize', (left, + right, + bottom, + top), None), + ('borderWidth', (borderWidth, borderWidth), self.setBorderWidth)] + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(BeanRecipeGui) + self.jellyBeanBoxList = [] + xIncrement = 0.052 + for i in range(len(recipe)): + beanIndex = GardenGlobals.BeanColorLetters.index(recipe[i]) + self.createJellyBeanBox(beanIndex, borderWidth + xIncrement * i, borderWidth) + + for j in range(len(recipe), GardenGlobals.getNumberOfShovelBoxes()): + self.createEmptyBeanBox(borderWidth + xIncrement * j, borderWidth) + + return + + def createJellyBeanBox(self, beanIndex, xPos, zPos): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = None + xAdj = 0.03 + zAdj = 0.03 + newBox = DirectButton(parent=self, pos=(xPos + xAdj, 0, zPos + zAdj), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, scale=(0.5, 1, 0.5), relief=None, state=state, command=command, extraArgs=[beanIndex], text=TTLocalizer.BeanColorWords[beanIndex], text_pos=(0.0, 0.1), text_scale=0.07, text_fg=Vec4(0, 0, 0, 0), text1_fg=Vec4(0, 0, 0, 1), text2_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 0), clickSound=None, pressEffect=0) + beanParent = newBox.attachNewNode('bean_%d' % beanIndex) + PlantingGUI.loadJellyBean(beanParent, beanIndex) + self.jellyBeanBoxList.append(newBox) + return + + def createEmptyBeanBox(self, xPos, zPos): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = None + xAdj = 0.03 + zAdj = 0.03 + newBox = DirectButton(parent=self, pos=(xPos + xAdj, 0, zPos + zAdj), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, scale=(0.5, 1, 0.5), relief=None, state=state, command=command, text='', text_pos=(0.0, 0.1), text_scale=0.07, text_fg=Vec4(0, 0, 0, 0), text1_fg=Vec4(0, 0, 0, 1), text2_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 0), clickSound=None, pressEffect=0) + newBox.setColorScale(0.5, 0.5, 0.5, 1) + self.jellyBeanBoxList.append(newBox) + return diff --git a/toontown/estate/CannonGlobals.py b/toontown/estate/CannonGlobals.py new file mode 100644 index 0000000..f187cf0 --- /dev/null +++ b/toontown/estate/CannonGlobals.py @@ -0,0 +1,11 @@ +CANNON_TIMEOUT = 20 +CANNON_MOVIE_LOAD = 1 +CANNON_MOVIE_CLEAR = 2 +CANNON_MOVIE_FORCE_EXIT = 3 +CANNON_MOVIE_LANDED = 4 +cannonDrops = [(-110, -66, 0.025, -64, 0, 0), + (65.68, -71.1, 0.025, 24, 0, 0), + (-53.7788, -104.5, 0.72, -24, 0, 0), + (39.58, 1.53, 8.2, 101.2, 0, 0), + (61.3, 62.78, 0.031, 171.2, 0, 0), + (-43.8, 75.2, 0.025, -158.15, 0, 0)] diff --git a/toontown/estate/ClosetGUI.py b/toontown/estate/ClosetGUI.py new file mode 100644 index 0000000..65117d1 --- /dev/null +++ b/toontown/estate/ClosetGUI.py @@ -0,0 +1,222 @@ +from direct.showbase.PythonUtil import Functor +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.makeatoon import ClothesGUI +import ClosetGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer + +class ClosetGUI(ClothesGUI.ClothesGUI): + notify = directNotify.newCategory('ClosetGUI') + + def __init__(self, isOwner, doneEvent, cancelEvent, swapEvent, deleteEvent, topList = None, botList = None): + ClothesGUI.ClothesGUI.__init__(self, ClothesGUI.CLOTHES_CLOSET, doneEvent, swapEvent) + self.toon = None + self.topsList = topList + self.bottomsList = botList + self.isOwner = isOwner + self.deleteEvent = deleteEvent + self.cancelEvent = cancelEvent + self.genderChange = 0 + self.verify = None + return + + def load(self): + ClothesGUI.ClothesGUI.load(self) + self.gui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + self.cancelButton = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn2_UP'), self.gui.find('**/CrtAtoon_Btn2_DOWN'), self.gui.find('**/CrtAtoon_Btn2_RLLVR')), pos=(0.15, 0, -0.85), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.cancelButton.hide() + if self.isOwner: + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui.bam') + trashImage = (trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')) + self.trashPanel = DirectFrame(parent=aspect2d, image=DGG.getDefaultDialogGeom(), image_color=(1, 1, 0.75, 0.8), image_scale=(0.36, 0, 0.75), pos=(-.86, 0, -.05), relief=None) + self.topTrashButton = DirectButton(parent=self.trashPanel, image=trashImage, relief=None, pos=(-0.09, 0, 0.2), command=self.__handleDelete, extraArgs=[ClosetGlobals.SHIRT], scale=(0.5, 0.5, 0.5), text=TTLocalizer.ClosetDeleteShirt, text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.12, text_pos=(0.3, 0), text_fg=(0.8, 0.2, 0.2, 1), text_shadow=(0, 0, 0, 1), textMayChange=0) + self.bottomTrashButton = DirectButton(parent=self.trashPanel, image=trashImage, relief=None, textMayChange=1, pos=(-0.09, 0, -0.2), command=self.__handleDelete, extraArgs=[ClosetGlobals.SHORTS], scale=(0.5, 0.5, 0.5), text=TTLocalizer.ClosetDeleteShorts, text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.12, text_pos=(0.3, 0), text_fg=(0.8, 0.2, 0.2, 1), text_shadow=(0, 0, 0, 1)) + self.button = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn1_UP'), self.gui.find('**/CrtAtoon_Btn1_DOWN'), self.gui.find('**/CrtAtoon_Btn1_RLLVR')), pos=(-0.15, 0, -0.85), command=self.__handleButton, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + trashcanGui.removeNode() + return + + def unload(self): + self.ignore('verifyDone') + ClothesGUI.ClothesGUI.unload(self) + self.cancelButton.destroy() + del self.cancelButton + if self.isOwner: + self.topTrashButton.destroy() + self.bottomTrashButton.destroy() + self.button.destroy() + del self.topTrashButton + del self.bottomTrashButton + del self.button + self.trashPanel.destroy() + del self.trashPanel + if self.verify: + self.verify.cleanup() + del self.verify + + def showButtons(self): + ClothesGUI.ClothesGUI.showButtons(self) + self.cancelButton.show() + if self.isOwner: + self.topTrashButton.show() + self.bottomTrashButton.show() + self.button.show() + + def hideButtons(self): + ClothesGUI.ClothesGUI.hideButtons(self) + self.cancelButton.hide() + if self.isOwner: + self.topTrashButton.hide() + self.bottomTrashButton.hide() + self.button.hide() + + def setupScrollInterface(self): + self.notify.debug('setupScrollInterface') + self.dna = self.toon.getStyle() + self.gender = self.dna.getGender() + self.swappedTorso = 0 + if self.topsList == None: + self.topsList = self.toon.getClothesTopsList() + if self.bottomsList == None: + self.bottomsList = self.toon.getClothesBottomsList() + self.tops = [] + self.bottoms = [] + self.tops.append((self.dna.topTex, + self.dna.topTexColor, + self.dna.sleeveTex, + self.dna.sleeveTexColor)) + self.bottoms.append((self.dna.botTex, self.dna.botTexColor)) + i = 0 + while i < len(self.topsList): + self.tops.append((self.topsList[i], + self.topsList[i + 1], + self.topsList[i + 2], + self.topsList[i + 3])) + i = i + 4 + + i = 0 + while i < len(self.bottomsList): + self.bottoms.append((self.bottomsList[i], self.bottomsList[i + 1])) + i = i + 2 + + self.topChoice = 0 + self.bottomChoice = 0 + self.swapTop(0) + self.swapBottom(0) + if self.isOwner: + self.updateTrashButtons() + self.setupButtons() + return + + def updateTrashButtons(self): + if len(self.tops) < 2: + self.topTrashButton['state'] = DGG.DISABLED + else: + self.topTrashButton['state'] = DGG.NORMAL + if len(self.bottoms) < 2: + self.bottomTrashButton['state'] = DGG.DISABLED + else: + self.bottomTrashButton['state'] = DGG.NORMAL + if self.toon: + if self.toon.style.torso[1] == 'd': + self.bottomTrashButton['text'] = TTLocalizer.ClosetDeleteSkirt + else: + self.bottomTrashButton['text'] = TTLocalizer.ClosetDeleteShorts + + def setGender(self, gender): + self.ownerGender = gender + self.genderChange = 1 + + def swapBottom(self, offset): + length = len(self.bottoms) + self.bottomChoice += offset + if self.bottomChoice <= 0: + self.bottomChoice = 0 + self.updateScrollButtons(self.bottomChoice, length, 0, self.bottomLButton, self.bottomRButton) + if self.bottomChoice < 0 or self.bottomChoice >= len(self.bottoms) or len(self.bottoms[self.bottomChoice]) != 2: + self.notify.warning('bottomChoice index is out of range!') + return None + self.toon.style.botTex = self.bottoms[self.bottomChoice][0] + self.toon.style.botTexColor = self.bottoms[self.bottomChoice][1] + if self.genderChange == 1: + if self.bottomChoice > 0: + self.__handleGenderBender(1) + else: + self.__handleGenderBender(0) + if self.toon.generateToonClothes() == 1: + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + if self.isOwner: + self.updateTrashButtons() + if self.swapEvent != None: + messenger.send(self.swapEvent) + + def __handleGenderBender(self, type): + if type == 1: + if self.toon.style.gender != self.ownerGender and self.toon.style.gender == 'f': + self.toon.swapToonTorso(self.toon.style.torso[0] + 's', genClothes=0) + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + self.toon.style.gender = self.ownerGender + else: + self.toon.style.gender = self.gender + if self.toon.style.gender != self.ownerGender and self.toon.style.gender == 'm': + self.toon.swapToonTorso(self.toon.style.torso[0] + 's', genClothes=0) + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + + def removeTop(self, index): + listLen = len(self.tops) + if index < listLen: + del self.tops[index] + if self.topChoice > index: + self.topChoice -= 1 + elif self.topChoice == index: + self.topChoice = 0 + return 1 + return 0 + + def removeBottom(self, index): + listLen = len(self.bottoms) + if index < listLen: + del self.bottoms[index] + if self.bottomChoice > index: + self.bottomChoice -= 1 + elif self.bottomChoice == index: + self.bottomChoice = 0 + return 1 + return 0 + + def __handleButton(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + messenger.send('wakeup') + + def __handleCancel(self): + messenger.send(self.cancelEvent) + messenger.send('wakeup') + + def __handleDelete(self, t_or_b): + if t_or_b == ClosetGlobals.SHIRT: + item = TTLocalizer.ClosetShirt + elif self.toon.style.torso[1] == 'd': + item = TTLocalizer.ClosetSkirt + else: + item = TTLocalizer.ClosetShorts + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=TTLocalizer.ClosetVerifyDelete % item, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyDone', Functor(self.__handleVerifyDelete, t_or_b)) + messenger.send('wakeup') + + def __handleVerifyDelete(self, t_or_b): + status = self.verify.doneStatus + self.ignore('verifyDone') + self.verify.cleanup() + del self.verify + self.verify = None + if status == 'ok': + messenger.send(self.deleteEvent, [t_or_b]) + messenger.send('wakeup') + return diff --git a/toontown/estate/ClosetGlobals.py b/toontown/estate/ClosetGlobals.py new file mode 100644 index 0000000..afad39e --- /dev/null +++ b/toontown/estate/ClosetGlobals.py @@ -0,0 +1,8 @@ +SHIRT = 1 +SHORTS = 2 +CLOSET_MOVIE_COMPLETE = 1 +CLOSET_MOVIE_CLEAR = 2 +CLOSET_MOVIE_TIMEOUT = 3 +CLOSED = 0 +OPEN = 1 +TIMEOUT_TIME = 200 diff --git a/toontown/estate/DistributedAnimatedStatuary.py b/toontown/estate/DistributedAnimatedStatuary.py new file mode 100644 index 0000000..8e4d749 --- /dev/null +++ b/toontown/estate/DistributedAnimatedStatuary.py @@ -0,0 +1,45 @@ +from pandac.PandaModules import NodePath +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.estate import DistributedStatuary +from toontown.estate import GardenGlobals +from direct.actor import Actor + +class DistributedAnimatedStatuary(DistributedStatuary.DistributedStatuary): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedAnimatedStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedAnimatedStatuary') + DistributedStatuary.DistributedStatuary.__init__(self, cr) + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = Actor.Actor() + animPath = self.modelPath + self.anims[1] + self.model.loadModel(self.modelPath + self.anims[0]) + self.model.loadAnims(dict([[self.anims[1], animPath]])) + colNode = self.model.find('**/+CollisionNode') + if self.typeIndex == 234: + colNode.setScale(0.5) + if not colNode.isEmpty(): + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + if self.pinballScore: + score = self.pinballScore[0] + multiplier = self.pinballScore[1] + scoreNodePath = NodePath('statuary-%d-%d' % (score, multiplier)) + colNode.setName('statuaryCol') + scoreNodePath.reparentTo(colNode.getParent()) + colNode.reparentTo(scoreNodePath) + self.model.setScale(self.worldScale) + self.model.reparentTo(self.rotateNode) + self.model.loop(self.anims[1]) + + def setTypeIndex(self, typeIndex): + DistributedStatuary.DistributedStatuary.setTypeIndex(self, typeIndex) + self.anims = GardenGlobals.PlantAttributes[typeIndex]['anims'] + + def setupShadow(self): + if self.typeIndex == 234: + pass + else: + DistributedStatuary.DistributedStatuary.setupShadow() diff --git a/toontown/estate/DistributedBank.py b/toontown/estate/DistributedBank.py new file mode 100644 index 0000000..23626fe --- /dev/null +++ b/toontown/estate/DistributedBank.py @@ -0,0 +1,221 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +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 DistributedFurnitureItem +from toontown.toonbase import TTLocalizer +import BankGUI +from BankGlobals import * +from toontown.toontowngui import TTDialog +from toontown.catalog.CatalogFurnitureItem import FurnitureTypes +from toontown.catalog.CatalogFurnitureItem import FTScale + +class DistributedBank(DistributedFurnitureItem.DistributedFurnitureItem): + notify = directNotify.newCategory('DistributedBank') + + def __init__(self, cr): + DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr) + self.bankGui = None + self.bankTrack = None + self.bankDialog = None + self.hasLocalAvatar = 0 + self.hasJarOut = 0 + self.jarLods = [] + return + + def generate(self): + DistributedFurnitureItem.DistributedFurnitureItem.generate(self) + self.bankSphereEvent = 'bankSphere' + self.bankSphereEnterEvent = 'enter' + self.bankSphereEvent + self.bankSphereExitEvent = 'exit' + self.bankSphereEvent + self.bankGuiDoneEvent = 'bankGuiDone' + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self) + self.accept(self.bankSphereEnterEvent, self.__handleEnterSphere) + + def loadModel(self): + model = DistributedFurnitureItem.DistributedFurnitureItem.loadModel(self) + bowl = model.find('**/bowl') + if bowl: + bowl.setBin('fixed', 40) + return model + + def disable(self): + self.notify.debug('disable') + self.ignore(self.bankSphereEnterEvent) + self.ignore(self.bankSphereExitEvent) + self.ignore(self.bankGuiDoneEvent) + if self.bankTrack: + self.bankTrack.pause() + self.bankTrack = None + if self.bankGui: + self.bankGui.destroy() + self.bankGui = None + if self.bankDialog: + self.bankDialog.cleanup() + self.bankDialog = None + if self.hasLocalAvatar: + self.freeAvatar() + self.__removeToonJar() + self.ignoreAll() + DistributedFurnitureItem.DistributedFurnitureItem.disable(self) + return + + def delete(self): + self.notify.debug('delete') + DistributedFurnitureItem.DistributedFurnitureItem.delete(self) + + def __handleEnterSphere(self, collEntry): + if self.smoothStarted: + return + if self.hasLocalAvatar: + self.freeAvatar() + self.notify.debug('Entering Bank Sphere....') + self.acceptOnce(self.bankSphereExitEvent, self.__handleExitSphere) + self.ignore(self.bankSphereEnterEvent) + self.cr.playGame.getPlace().fsm.request('banking') + self.hasLocalAvatar = 1 + self.sendUpdate('avatarEnter', []) + + def __handleExitSphere(self, collEntry): + self.notify.debug('Exiting Bank Sphere....') + if self.bankTrack is not None: + self.bankTrack.pause() + self.bankTrack = None + if self.bankDialog is not None: + self.bankDialog.cleanup() + self.bankDialog = None + self.__handleBankDone(0) + return + + def __handleBankDone(self, transactionAmount): + self.notify.debug('__handleBankDone(transactionAmount=%s' % (transactionAmount,)) + self.sendUpdate('transferMoney', [transactionAmount]) + self.ignore(self.bankGuiDoneEvent) + self.ignore(self.bankSphereExitEvent) + if self.bankGui is not None: + self.bankGui.destroy() + self.bankGui = None + return + + def freeAvatar(self): + self.notify.debug('freeAvatar()') + if self.hasLocalAvatar: + base.localAvatar.posCamera(0, 0) + if base.cr.playGame.place != None: + base.cr.playGame.getPlace().setState('walk') + self.hasLocalAvatar = 0 + self.accept(self.bankSphereEnterEvent, self.__handleEnterSphere) + return + + def showBankGui(self): + if self.bankGui: + self.bankGui.destroy() + self.bankGui = BankGUI.BankGui(self.bankGuiDoneEvent) + self.accept(self.bankGuiDoneEvent, self.__handleBankDone) + + def setMovie(self, mode, avId, timestamp): + self.notify.debug('setMovie(mode=%s, avId=%s, timestamp=%s)' % (mode, avId, timestamp)) + timeStamp = globalClockDelta.localElapsedTime(timestamp) + isLocalToon = avId == base.localAvatar.doId + self.notify.info('setMovie: mode=%s, avId=%s, timeStamp=%s, isLocalToon=%s' % (mode, + avId, + timeStamp, + isLocalToon)) + if mode == BANK_MOVIE_CLEAR: + self.notify.debug('setMovie: clear') + elif mode == BANK_MOVIE_GUI: + self.notify.debug('setMovie: gui') + track = Sequence() + track.append(Func(self.__takeOutToonJar, avId)) + if isLocalToon: + track.append(Wait(3.0)) + track.append(Func(self.showBankGui)) + track.start() + self.bankTrack = track + elif mode == BANK_MOVIE_DEPOSIT: + self.notify.debug('setMovie: deposit') + self.__putAwayToonJar(avId) + elif mode == BANK_MOVIE_WITHDRAW: + self.notify.debug('setMovie: withdraw') + self.__putAwayToonJar(avId) + elif mode == BANK_MOVIE_NO_OP: + self.notify.debug('setMovie: no op') + self.__putAwayToonJar(avId) + elif mode == BANK_MOVIE_NOT_OWNER: + self.notify.debug('setMovie: not owner') + if isLocalToon: + self.bankDialog = TTDialog.TTDialog(dialogName='BankNotOwner', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedBankNotOwner, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + elif mode == BANK_MOVIE_NO_OWNER: + self.notify.debug('setMovie: no owner') + if isLocalToon: + self.bankDialog = TTDialog.TTDialog(dialogName='BankNoOwner', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedBankNoOwner, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + else: + self.notify.warning('unknown mode in setMovie: %s' % mode) + + def __clearDialog(self, event): + self.notify.debug('__clearDialog(event=%s)' % (event,)) + if self.bankDialog is not None: + self.bankDialog.cleanup() + self.bankDialog = None + self.freeAvatar() + return + + def __attachToonJar(self, toon): + self.__removeToonJar() + for hand in toon.getRightHands(): + self.jarLods.append(toon.jar.instanceTo(hand)) + + def __removeToonJar(self): + for jar in self.jarLods: + jar.removeNode() + + self.jarLods = [] + + def __takeOutToonJar(self, avId): + self.notify.debug('__takeOutToonJar(avId=%s)' % (avId,)) + toon = base.cr.doId2do.get(avId) + if toon == None: + return + track = Sequence() + index = self.item.furnitureType + scale = FurnitureTypes[index][FTScale] + walkToBank = Sequence(Func(toon.stopSmooth), Func(toon.loop, 'walk'), toon.posHprInterval(0.5, Point3(0, -3.125 * (scale + 0.2), 0), Point3(0, 0, 0), other=self, blendType='easeInOut'), Func(toon.loop, 'neutral'), Func(toon.startSmooth)) + track.append(walkToBank) + if not toon.jar: + toon.getJar() + self.__attachToonJar(toon) + jarAndBank = Parallel(LerpScaleInterval(toon.jar, 1.5, 1.0, blendType='easeOut'), ActorInterval(base.cr.doId2do[avId], 'bank', endTime=3.8)) + track.append(jarAndBank) + track.append(Func(base.cr.doId2do[avId].pingpong, 'bank', fromFrame=48, toFrame=92)) + track.start() + self.hasJarOut = 1 + return + + def __putAwayToonJar(self, avId): + self.notify.debug('__putAwayToonJar(avId=%s)' % (avId,)) + toon = base.cr.doId2do.get(avId) + if toon is None: + return + if not self.hasJarOut: + return + self.hasJarOut = 0 + if not toon.jar: + toon.getJar() + track = Sequence() + jarAndBank = Parallel(ActorInterval(base.cr.doId2do[avId], 'bank', startTime=2.0, endTime=0.0), LerpScaleInterval(toon.jar, 2.0, 0.0, blendType='easeIn')) + track.append(jarAndBank) + track.append(Func(self.__removeToonJar)) + track.append(Func(toon.removeJar)) + track.append(Func(toon.loop, 'neutral')) + if avId == base.localAvatar.doId: + track.append(Func(self.freeAvatar)) + track.start() + self.bankTrack = track + return diff --git a/toontown/estate/DistributedBankMgr.py b/toontown/estate/DistributedBankMgr.py new file mode 100644 index 0000000..7a086eb --- /dev/null +++ b/toontown/estate/DistributedBankMgr.py @@ -0,0 +1,30 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedBankMgr(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBankMgr') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + if base.cr.bankManager != None: + base.cr.bankManager.delete() + base.cr.bankManager = self + DistributedObject.DistributedObject.generate(self) + return + + def disable(self): + base.cr.bankManager = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + base.cr.bankManager = None + DistributedObject.DistributedObject.delete(self) + return + + def d_transferMoney(self, amount): + self.sendUpdate('transferMoney', [amount]) diff --git a/toontown/estate/DistributedCannon.py b/toontown/estate/DistributedCannon.py new file mode 100644 index 0000000..282566f --- /dev/null +++ b/toontown/estate/DistributedCannon.py @@ -0,0 +1,1589 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from direct.task.Task import Task +from toontown.minigame import Trajectory +import math +from toontown.toon import ToonHead +from toontown.effects import Splash +from toontown.effects import DustCloud +from toontown.minigame import CannonGameGlobals +import CannonGlobals +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedObject +from toontown.effects import Wake +from direct.controls.ControlManager import CollisionHandlerRayStart +LAND_TIME = 2 +WORLD_SCALE = 2.0 +GROUND_SCALE = 1.4 * WORLD_SCALE +CANNON_SCALE = 1.0 +FAR_PLANE_DIST = 600 * WORLD_SCALE +GROUND_PLANE_MIN = -15 +CANNON_Y = -int(CannonGameGlobals.TowerYRange / 2 * 1.3) +CANNON_X_SPACING = 12 +CANNON_Z = 20 +CANNON_ROTATION_MIN = -55 +CANNON_ROTATION_MAX = 50 +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_MIN = 15 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 +MAX_LOOKAT_OFFSET = 80 +TOON_TOWER_THRESHOLD = 150 +SHADOW_Z_OFFSET = 0.5 +TOWER_HEIGHT = 43.85 +TOWER_RADIUS = 10.5 +BUCKET_HEIGHT = 36 +TOWER_Y_RANGE = CannonGameGlobals.TowerYRange +TOWER_X_RANGE = int(TOWER_Y_RANGE / 2.0) +INITIAL_VELOCITY = 80.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.35 + +class DistributedCannon(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCannon') + font = ToontownGlobals.getToonFont() + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + REWARD_COUNTDOWN_TASK = 'cannonGameRewardCountdown' + HIT_GROUND = 0 + HIT_TOWER = 1 + HIT_WATER = 2 + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + BUMPER_KEY = 'delete' + BUMPER_KEY2 = 'insert' + INTRO_TASK_NAME = 'CannonGameIntro' + INTRO_TASK_NAME_CAMERA_LERP = 'CannonGameIntroCamera' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.avId = 0 + self.av = None + self.localToonShooting = 0 + self.nodePath = None + self.collSphere = None + self.collNode = None + self.collNodePath = None + self.madeGui = 0 + self.gui = None + self.cannonLocation = None + self.cannonPosition = None + self.cannon = None + self.toonModel = None + self.shadowNode = None + self.toonHead = None + self.toonScale = None + self.estateId = None + self.targetId = None + self.splash = None + self.dustCloud = None + self.model_Created = 0 + self.lastWakeTime = 0 + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.hitBumper = 0 + self.hitTarget = 0 + self.lastPos = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.vel = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.hitTrack = None + self.cTrav = None + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.lifter = None + self.flyColNode = None + self.flyColNodePath = None + self.bumperCol = None + self.cannonMoving = 0 + self.inWater = 0 + self.localAvId = base.localAvatar.doId + self.nextState = None + self.nextKey = None + self.cannonsActive = 0 + self.codeFSM = ClassicFSM.ClassicFSM('CannonCode', [State.State('init', self.enterInit, self.exitInit, ['u1', 'init']), + State.State('u1', self.enteru1, self.exitu1, ['u2', 'init']), + State.State('u2', self.enteru2, self.exitu2, ['d3', 'init']), + State.State('d3', self.enterd3, self.exitd3, ['d4', 'init']), + State.State('d4', self.enterd4, self.exitd4, ['l5', 'init']), + State.State('l5', self.enterl5, self.exitl5, ['r6', 'init']), + State.State('r6', self.enterr6, self.exitr6, ['l7', 'init']), + State.State('l7', self.enterl7, self.exitl7, ['r8', 'init']), + State.State('r8', self.enterr8, self.exitr8, ['acceptCode', 'init']), + State.State('acceptCode', self.enterAcceptCode, self.exitAcceptCode, ['init', 'final']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'init', 'final') + self.codeFSM.enterInitialState() + self.curPinballScore = 0 + self.curPinballMultiplier = 1 + return + + def disable(self): + self.__unmakeGui() + taskMgr.remove(self.taskNameFireCannon) + taskMgr.remove(self.taskNameShoot) + taskMgr.remove(self.taskNameFly) + taskMgr.remove(self.taskNameSmoke) + self.ignoreAll() + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0) + self.nodePath.detachNode() + if self.hitTrack: + self.hitTrack.finish() + del self.hitTrack + self.hitTrack = None + DistributedObject.DistributedObject.disable(self) + return + + def __unmakeGui(self): + if not self.madeGui: + return + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.madeGui = 0 + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.taskNameFireCannon = self.taskName('fireCannon') + self.taskNameShoot = self.taskName('shootTask') + self.taskNameSmoke = self.taskName('smokeTask') + self.taskNameFly = self.taskName('flyTask') + self.nodePath = NodePath(self.uniqueName('Cannon')) + self.load() + self.activateCannons() + self.listenForCode() + + def listenForCode(self): + self.accept(self.UP_KEY + '-up', self.__upKeyCode) + self.accept(self.DOWN_KEY + '-up', self.__downKeyCode) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyCode) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyCode) + + def ignoreCode(self): + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + + def activateCannons(self): + if not self.cannonsActive: + self.cannonsActive = 1 + self.onstage() + self.nodePath.reparentTo(self.getParentNodePath()) + self.accept(self.uniqueName('enterCannonSphere'), self.__handleEnterSphere) + + def deActivateCannons(self): + if self.cannonsActive: + self.cannonsActive = 0 + self.offstage() + self.nodePath.reparentTo(hidden) + self.ignore(self.uniqueName('enterCannonSphere')) + + def delete(self): + self.offstage() + self.unload() + DistributedObject.DistributedObject.delete(self) + + def __handleEnterSphere(self, collEntry): + self.notify.debug('collEntry: %s' % collEntry) + base.cr.playGame.getPlace().setState('fishing') + self.d_requestEnter() + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def requestExit(self): + self.notify.debug('requestExit') + base.localAvatar.reparentTo(render) + base.cr.playGame.getPlace().setState('walk') + + def getSphereRadius(self): + return 1.5 + + def getParentNodePath(self): + return base.cr.playGame.hood.loader.geom + + def setEstateId(self, estateId): + self.estateId = estateId + + def setTargetId(self, targetId): + self.notify.debug('setTargetId %d' % targetId) + self.targetId = targetId + + def setPosHpr(self, x, y, z, h, p, r): + self.nodePath.setPosHpr(x, y, z, h, p, r) + + def setMovie(self, mode, avId): + wasLocalToon = self.localToonShooting + self.avId = avId + if mode == CannonGlobals.CANNON_MOVIE_CLEAR: + self.listenForCode() + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LANDED: + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_FORCE_EXIT: + self.exitCannon(self.avId) + self.setLanded() + elif mode == CannonGlobals.CANNON_MOVIE_LOAD: + self.ignoreCode() + if self.avId == base.localAvatar.doId: + base.localAvatar.pose('lose', 110) + base.localAvatar.pose('slip-forward', 25) + base.cr.playGame.getPlace().setState('fishing') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.collisionsOff() + base.setCellsAvailable([base.bottomCells[3], base.bottomCells[4]], 0) + base.setCellsAvailable([base.rightCells[1]], 0) + self.localToonShooting = 1 + self.__makeGui() + camera.reparentTo(self.barrel) + camera.setPos(0.5, -2, 2.5) + self.curPinballScore = 0 + self.curPinballMultiplier = 1 + self.incrementPinballInfo(0, 0) + if self.cr.doId2do.has_key(self.avId): + self.av = self.cr.doId2do[self.avId] + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.av.stopSmooth() + self.__createToonModels() + else: + self.notify.warning('Unknown avatar %d in cannon %d' % (self.avId, self.doId)) + if wasLocalToon and not self.localToonShooting: + base.setCellsAvailable([base.bottomCells[3], base.bottomCells[4]], 1) + base.setCellsAvailable([base.rightCells[1]], 1) + + def __avatarGone(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_CLEAR, 0) + + def load(self): + self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadowNode = hidden.attachNewNode('dropShadow') + self.shadow.copyTo(self.shadowNode) + self.smoke = loader.loadModel('phase_4/models/props/test_clouds') + self.smoke.setBillboardPointEye() + self.cannon.setScale(CANNON_SCALE) + self.shadowNode.setColor(0, 0, 0, 0.5) + self.shadowNode.setBin('fixed', 0, 1) + self.splash = Splash.Splash(render) + self.dustCloud = DustCloud.DustCloud(render) + self.dustCloud.setBillboardPointEye() + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.sndHitTower = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_tower.mp3') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.mp3') + self.sndWhizz = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.mp3') + self.sndWin = base.loadSfx('phase_4/audio/sfx/MG_win.mp3') + self.sndHitHouse = base.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.mp3') + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.uniqueName('CannonSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.loadCannonBumper() + + def setupMovingShadow(self): + self.cTrav = base.cTrav + self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0) + self.cRayNode = CollisionNode('cRayNode') + self.cRayNode.addSolid(self.cRay) + self.cRayNodePath = self.shadowNode.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(20.0) + self.enableRaycast(1) + + def enableRaycast(self, enable = 1): + if not self.cTrav or not hasattr(self, 'cRayNode') or not self.cRayNode: + return + self.notify.debug('-------enabling raycast--------') + self.cTrav.removeCollider(self.cRayNodePath) + if enable: + self.cTrav.addCollider(self.cRayNodePath, self.lifter) + + def __makeGui(self): + if self.madeGui: + return + guiModel = 'phase_4/models/gui/cannon_game_gui' + cannonGui = loader.loadModel(guiModel) + self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + cannonGui.removeNode() + self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + self.aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.__enableAimInterface() + self.madeGui = 1 + return + + def __unmakeGui(self): + self.notify.debug('__unmakeGui') + if not self.madeGui: + return + self.__disableAimInterface() + self.upButton.unbind(DGG.B1PRESS) + self.upButton.unbind(DGG.B1RELEASE) + self.downButton.unbind(DGG.B1PRESS) + self.downButton.unbind(DGG.B1RELEASE) + self.leftButton.unbind(DGG.B1PRESS) + self.leftButton.unbind(DGG.B1RELEASE) + self.rightButton.unbind(DGG.B1PRESS) + self.rightButton.unbind(DGG.B1RELEASE) + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.madeGui = 0 + + def unload(self): + self.ignoreCode() + del self.codeFSM + if self.cannon: + self.cannon.removeNode() + self.cannon = None + if self.shadowNode != None: + self.shadowNode.removeNode() + del self.shadowNode + if self.splash != None: + self.splash.destroy() + del self.splash + if self.dustCloud != None: + self.dustCloud.destroy() + del self.dustCloud + del self.sndCannonMove + del self.sndCannonFire + del self.sndHitHouse + del self.sndHitGround + del self.sndHitTower + del self.sndHitWater + del self.sndWhizz + del self.sndWin + self.bumperCol = None + taskMgr.remove(self.uniqueName('BumperON')) + if self.av: + self.__resetToon(self.av) + self.av.loop('neutral') + self.av.setPlayRate(1.0, 'run') + if hasattr(self.av, 'nametag'): + self.av.nametag.removeNametag(self.toonHead.tag) + if self.toonHead != None: + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + self.toonHead = None + if self.toonModel != None: + self.toonModel.removeNode() + self.toonModel = None + del self.toonScale + del self.cannonLocation + del self.cRay + del self.cRayNode + if self.cRayNodePath: + self.cRayNodePath.removeNode() + del self.cRayNodePath + del self.lifter + self.enableRaycast(0) + return + + def onstage(self): + self.__createCannon() + self.cannon.reparentTo(self.nodePath) + self.splash.reparentTo(render) + self.dustCloud.reparentTo(render) + + def offstage(self): + if self.cannon: + self.cannon.reparentTo(hidden) + if self.splash: + self.splash.reparentTo(hidden) + self.splash.stop() + if self.dustCloud: + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + + def __createCannon(self): + self.barrel = self.cannon.find('**/cannon') + self.cannonLocation = Point3(0, 0, 0.025) + self.cannonPosition = [0, CANNON_ANGLE_MIN] + self.cannon.setPos(self.cannonLocation) + self.__updateCannonPosition(self.avId) + + def __createToonModels(self): + self.model_Created = 1 + toon = self.av + self.toonScale = toon.getScale() + toon.useLOD(1000) + toonParent = render.attachNewNode('toonOriginChange') + toon.wrtReparentTo(toonParent) + toon.setPosHpr(0, 0, -(toon.getHeight() / 2.0), 0, -90, 0) + self.toonModel = toonParent + self.toonHead = ToonHead.ToonHead() + self.toonHead.setupHead(self.av.style) + self.toonHead.reparentTo(hidden) + tag = NametagFloat3d() + tag.setContents(Nametag.CSpeech | Nametag.CThought) + tag.setBillboardOffset(0) + tag.setAvatar(self.toonHead) + toon.nametag.addNametag(tag) + tagPath = self.toonHead.attachNewNode(tag.upcastToPandaNode()) + tagPath.setPos(0, 0, 1) + self.toonHead.tag = tag + self.__loadToonInCannon() + self.av.dropShadow.hide() + self.dropShadow = self.shadowNode.copyTo(hidden) + + def __destroyToonModels(self): + if self.av != None: + self.av.dropShadow.show() + if self.dropShadow != None: + self.dropShadow.removeNode() + self.dropShadow = None + self.hitBumper = 0 + self.hitTarget = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.av = None + self.lastWakeTime = 0 + self.localToonShooting = 0 + if self.toonHead != None: + self.toonHead.reparentTo(hidden) + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + self.toonHead = None + if self.toonModel != None: + self.toonModel.removeNode() + self.toonModel = None + self.model_Created = 0 + return + + def updateCannonPosition(self, avId, zRot, angle): + if avId != self.localAvId: + self.cannonPosition = [zRot, angle] + self.__updateCannonPosition(avId) + + def setCannonWillFire(self, avId, fireTime, zRot, angle, timestamp): + self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime)) + if not self.model_Created: + self.notify.warning("We walked into the zone mid-flight, so we won't see it") + return + self.cannonPosition[0] = zRot + self.cannonPosition[1] = angle + self.__updateCannonPosition(avId) + task = Task(self.__fireCannonTask) + task.avId = avId + ts = globalClockDelta.localElapsedTime(timestamp) + task.fireTime = fireTime - ts + if task.fireTime < 0.0: + task.fireTime = 0.0 + taskMgr.add(task, self.taskNameFireCannon) + + def exitCannon(self, avId): + self.__unmakeGui() + if self.avId == avId: + if self.av: + self.__resetToonToCannon(self.av) + + def __enableAimInterface(self): + self.aimPad.show() + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.accept(self.BUMPER_KEY, self.__bumperKeyPressed) + self.accept(self.BUMPER_KEY2, self.__bumperKeyPressed) + self.__spawnLocalCannonMoveTask() + + def __disableAimInterface(self): + self.aimPad.hide() + self.ignore(self.FIRE_KEY) + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.FIRE_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.__killLocalCannonMoveTask() + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.__fireReleased() + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.handleCodeKey('left') + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.handleCodeKey('right') + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.handleCodeKey('down') + self.__downReleased() + + def __upKeyCode(self): + self.handleCodeKey('up') + + def __downKeyCode(self): + self.handleCodeKey('down') + + def __rightKeyCode(self): + self.handleCodeKey('right') + + def __leftKeyCode(self): + self.handleCodeKey('left') + + def __firePressed(self): + self.notify.debug('fire pressed') + self.__broadcastLocalCannonPosition() + self.__unmakeGui() + self.sendUpdate('setCannonLit', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) + + def __spawnLocalCannonMoveTask(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __killLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.cannonMoving: + self.sndCannonMove.stop() + + def __localCannonMoveTask(self, task): + pos = self.cannonPosition + oldRot = pos[0] + oldAng = pos[1] + rotVel = 0 + if self.leftPressed: + rotVel += CANNON_ROTATION_VEL + if self.rightPressed: + rotVel -= CANNON_ROTATION_VEL + pos[0] += rotVel * globalClock.getDt() + if pos[0] < CANNON_ROTATION_MIN: + pos[0] = CANNON_ROTATION_MIN + elif pos[0] > CANNON_ROTATION_MAX: + pos[0] = CANNON_ROTATION_MAX + angVel = 0 + if self.upPressed: + angVel += CANNON_ANGLE_VEL + if self.downPressed: + angVel -= CANNON_ANGLE_VEL + pos[1] += angVel * globalClock.getDt() + if pos[1] < CANNON_ANGLE_MIN: + pos[1] = CANNON_ANGLE_MIN + elif pos[1] > CANNON_ANGLE_MAX: + pos[1] = CANNON_ANGLE_MAX + if oldRot != pos[0] or oldAng != pos[1]: + if self.cannonMoving == 0: + self.cannonMoving = 1 + base.playSfx(self.sndCannonMove, looping=1) + self.__updateCannonPosition(self.localAvId) + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.cannonMoving: + self.cannonMoving = 0 + self.sndCannonMove.stop() + self.__broadcastLocalCannonPosition() + print 'Cannon Rot:%s Angle:%s' % (pos[0], pos[1]) + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.sendUpdate('setCannonPosition', [self.cannonPosition[0], self.cannonPosition[1]]) + + def __updateCannonPosition(self, avId): + self.cannon.setHpr(self.cannonPosition[0], 0.0, 0.0) + self.barrel.setHpr(0.0, self.cannonPosition[1], 0.0) + maxP = 90 + newP = self.barrel.getP() + yScale = 1 - 0.5 * float(newP) / maxP + shadow = self.cannon.find('**/square_drop_shadow') + shadow.setScale(1, yScale, 1) + + def __getCameraPositionBehindCannon(self): + return Point3(self.cannonLocationDict[self.localAvId][0], CANNON_Y - 5.0, CANNON_Z + 7) + + def __putCameraBehindCannon(self): + camera.setPos(self.__getCameraPositionBehindCannon()) + camera.setHpr(0, 0, 0) + + def __loadToonInCannon(self): + self.toonModel.reparentTo(hidden) + self.toonHead.startBlink() + self.toonHead.startLookAround() + self.toonHead.reparentTo(self.barrel) + self.toonHead.setPosHpr(0, 6, 0, 0, -45, 0) + sc = self.toonScale + self.toonHead.setScale(render, sc[0], sc[1], sc[2]) + self.toonModel.setPos(self.toonHead.getPos(render)) + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __calcFlightResults(self, avId, launchTime): + head = self.toonHead + startPos = head.getPos(render) + startHpr = head.getHpr(render) + hpr = self.barrel.getHpr(render) + rotation = self.__toRadians(hpr[0]) + angle = self.__toRadians(hpr[1]) + horizVel = INITIAL_VELOCITY * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = INITIAL_VELOCITY * math.sin(angle) + startVel = Vec3(xVel, yVel, zVel) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + self.trajectory = trajectory + hitTreasures = self.__calcHitTreasures(trajectory) + timeOfImpact, hitWhat = self.__calcToonImpact(trajectory) + return {'startPos': startPos, + 'startHpr': startHpr, + 'startVel': startVel, + 'trajectory': trajectory, + 'timeOfImpact': 3 * timeOfImpact, + 'hitWhat': hitWhat} + + def __fireCannonTask(self, task): + launchTime = task.fireTime + avId = task.avId + self.inWater = 0 + if not self.toonHead: + return Task.done + flightResults = self.__calcFlightResults(avId, launchTime) + if not isClient(): + print 'EXECWARNING DistributedCannon: %s' % flightResults + printStack() + for key in flightResults: + exec "%s = flightResults['%s']" % (key, key) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + self.notify.debug('time of impact: ' + str(timeOfImpact)) + self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact))) + if hitWhat == self.HIT_WATER: + self.notify.debug('toon will land in the water') + elif hitWhat == self.HIT_TOWER: + self.notify.debug('toon will hit the tower') + else: + self.notify.debug('toon will hit the ground') + head = self.toonHead + head.stopBlink() + head.stopLookAroundNow() + head.reparentTo(hidden) + av = self.toonModel + av.reparentTo(render) + print 'start Pos%s Hpr%s' % (startPos, startHpr) + av.setPos(startPos) + barrelHpr = self.barrel.getHpr(render) + place = base.cr.playGame.getPlace() + if self.av == base.localAvatar: + place.fsm.request('stopped') + av.setHpr(startHpr) + avatar = self.av + avatar.loop('swim') + avatar.setPosHpr(0, 0, -(avatar.getHeight() / 2.0), 0, 0, 0) + info = {} + info['avId'] = avId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['timeOfImpact'] = timeOfImpact + info['hitWhat'] = hitWhat + info['toon'] = self.toonModel + info['hRot'] = self.cannonPosition[0] + info['haveWhistled'] = 0 + info['maxCamPullback'] = CAMERA_PULLBACK_MIN + if self.localToonShooting: + camera.reparentTo(self.av) + camera.setP(45.0) + camera.setZ(-10.0) + self.flyColSphere = CollisionSphere(0, 0, self.av.getHeight() / 2.0, 1.0) + self.flyColNode = CollisionNode(self.uniqueName('flySphere')) + self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) + self.flyColNode.addSolid(self.flyColSphere) + self.flyColNodePath = self.av.attachNewNode(self.flyColNode) + self.flyColNodePath.setColor(1, 0, 0, 1) + self.handler = CollisionHandlerEvent() + self.handler.setInPattern(self.uniqueName('cannonHit')) + base.cTrav.addCollider(self.flyColNodePath, self.handler) + self.accept(self.uniqueName('cannonHit'), self.__handleCannonHit) + shootTask = Task(self.__shootTask, self.taskNameShoot) + smokeTask = Task(self.__smokeTask, self.taskNameSmoke) + flyTask = Task(self.__flyTask, self.taskNameFly) + shootTask.info = info + flyTask.info = info + seqTask = Task.sequence(shootTask, smokeTask, flyTask) + if self.av == base.localAvatar: + print 'disable controls' + base.localAvatar.disableAvatarControls() + taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(avId)) + self.acceptOnce(self.uniqueName('stopFlyTask'), self.__stopFlyTask) + return Task.done + + def __stopFlyTask(self, avId): + taskMgr.remove(self.taskName('flyingToon') + '-' + str(avId)) + + def b_setLanded(self): + self.d_setLanded() + + def d_setLanded(self): + self.notify.debug('localTOonshooting = %s' % self.localToonShooting) + if self.localToonShooting: + self.sendUpdate('setLanded', []) + + def setLanded(self): + self.removeAvFromCannon() + + def removeAvFromCannon(self): + place = base.cr.playGame.getPlace() + print 'removeAvFromCannon' + self.notify.debug('self.inWater = %s' % self.inWater) + if place: + if not hasattr(place, 'fsm'): + return + placeState = place.fsm.getCurrentState().getName() + print placeState + if (self.inWater or place.toonSubmerged) and placeState != 'fishing': + if self.av != None: + self.av.startSmooth() + self.__destroyToonModels() + return + self.inWater = 0 + if self.av != None: + self.__stopCollisionHandler(self.av) + self.av.resetLOD() + if self.av == base.localAvatar: + if place and not self.inWater: + place.fsm.request('walk') + self.av.setPlayRate(1.0, 'run') + self.av.nametag.removeNametag(self.toonHead.tag) + if self.av.getParent().getName() == 'toonOriginChange': + self.av.wrtReparentTo(render) + self.__setToonUpright(self.av) + if self.av == base.localAvatar: + self.av.startPosHprBroadcast() + self.av.startSmooth() + self.av.setScale(1, 1, 1) + if self.av == base.localAvatar: + print 'enable controls' + base.localAvatar.enableAvatarControls() + self.ignore(self.av.uniqueName('disable')) + self.__destroyToonModels() + return + + def __stopCollisionHandler(self, avatar): + if avatar: + avatar.loop('neutral') + if self.flyColNode: + self.flyColNode = None + if avatar == base.localAvatar: + avatar.collisionsOn() + self.flyColSphere = None + if self.flyColNodePath: + base.cTrav.removeCollider(self.flyColNodePath) + self.flyColNodePath.removeNode() + self.flyColNodePath = None + self.handler = None + return + + def __handleCannonHit(self, collisionEntry): + if self.av == None or self.flyColNode == None: + return + + hitNode = collisionEntry.getIntoNode().getName() + self.notify.debug('hitNode = %s' % hitNode) + self.notify.debug('hitNodePath.getParent = %s' % collisionEntry.getIntoNodePath().getParent()) + + self.vel = self.trajectory.getVel(self.t) + vel = self.trajectory.getVel(self.t) + vel.normalize() + + if self.hitBumper: + vel = self.lastVel * 1 + vel.normalize() + self.notify.debug('normalized vel=%s' % vel) + + solid = collisionEntry.getInto() + intoNormal = collisionEntry.getSurfaceNormal(collisionEntry.getIntoNodePath()) + self.notify.debug('old intoNormal = %s' % intoNormal) + intoNormal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('new intoNormal = %s' % intoNormal) + + hitPylonAboveWater = False + hitPylonBelowWater = False + if hitNode in ['pier_pylon_collisions_1', 'pier_pylon_collisions_3']: + if collisionEntry.getSurfacePoint(render)[2] > 0: + hitPylonAboveWater = True + self.notify.debug('hitPylonAboveWater = True') + else: + hitPylonBelowWater = True + self.notify.debug('hitPylonBelowWater = True') + + hitNormal = intoNormal + if ( hitNode.find('cSphere') == 0 or + hitNode.find('treasureSphere') == 0 or + hitNode.find('prop') == 0 or + hitNode.find('distAvatarCollNode') == 0 or + hitNode.find('CannonSphere') == 0 or + hitNode.find('plotSphere') == 0 or + hitNode.find('flySphere') == 0 or + hitNode.find('mailboxSphere') == 0 or + hitNode.find('FishingSpotSphere') == 0 or + hitNode == 'gagtree_collision' or + hitNode == 'sign_collision' or + hitNode == 'FlowerSellBox' or + hitPylonBelowWater): + self.notify.debug('--------------hit and ignoring %s' % hitNode) + return + + if vel.dot(hitNormal) > 0 and not hitNode == 'collision_roof' and not hitNode == 'collision_fence': + self.notify.debug('--------------hit and ignoring backfacing %s, dot=%s' % (hitNode, vel.dot(hitNormal))) + return + + intoNode = collisionEntry.getIntoNodePath() + bumperNodes = ['collision_house', + 'collision_fence', + 'targetSphere', + 'collision_roof', + 'collision_cannon_bumper', + 'statuaryCol'] + cloudBumpers = ['cloudSphere-0'] + bumperNodes += cloudBumpers + + if hitNode not in bumperNodes: + self.__stopCollisionHandler(self.av) + self.__stopFlyTask(self.avId) + self.notify.debug('stopping flying since we hit %s' % hitNode) + if self.hitTarget == 0: + messenger.send('missedTarget') + else: + if hitNode == 'collision_house': + self.__hitHouse(self.av, collisionEntry) + elif hitNode == 'collision_fence': + self.__hitFence(self.av, collisionEntry) + elif hitNode == 'collision_roof': + self.__hitRoof(self.av, collisionEntry) + elif hitNode == 'targetSphere': + self.__hitTarget(self.av, collisionEntry, [vel]) + elif hitNode in cloudBumpers: + self.__hitCloudPlatform(self.av, collisionEntry) + elif hitNode == 'collision_cannon_bumper': + self.__hitCannonBumper(self.av, collisionEntry) + elif hitNode == 'statuaryCol': + self.__hitStatuary(self.av, collisionEntry) + else: + self.notify.debug('*************** hit something else ************') + return + + if self.localToonShooting: + camera.wrtReparentTo(render) + + if self.dropShadow: + self.dropShadow.reparentTo(hidden) + + pos = collisionEntry.getSurfacePoint(render) + hpr = self.av.getHpr() + hitPos = collisionEntry.getSurfacePoint(render) + pos = hitPos + self.landingPos = pos + + self.notify.debug('hitNode,Normal = %s,%s' % (hitNode, intoNormal)) + + track = Sequence() + track.append(Func(self.av.wrtReparentTo, render)) + if self.localToonShooting: + track.append(Func(self.av.collisionsOff)) + if hitPylonAboveWater or hitNode in ['matCollisions', + 'collision1', + 'floor', + 'sand_collision', + 'dirt_collision', + 'soil1', + 'collision2', + 'floor_collision']: + track.append(Func(self.__hitGround, self.av, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.av, self.landingPos)) + elif hitNode == 'collision_house': + track.append(Func(self.__hitHouse, self.av, collisionEntry)) + elif hitNode == 'collision_fence' or hitNode == 'collision4': + track.append(Func(self.__hitFence, self.av, collisionEntry)) + elif hitNode == 'targetSphere': + track.append(Func(self.__hitHouse, self.av, collisionEntry)) + elif hitNode == 'collision3': + track.append(Func(self.__hitWater, self.av, pos, collisionEntry)) + track.append(Wait(2.0)) + track.append(Func(self.__setToonUpright, self.av, self.landingPos)) + elif hitNode == 'roofOutside' or hitNode == 'collision_roof' or hitNode == 'roofclision': + track.append(Func(self.__hitRoof, self.av, collisionEntry)) + track.append(Wait(2.0)) + track.append(Func(self.__setToonUpright, self.av, self.landingPos)) + elif hitNode.find('MovingPlatform') == 0 or hitNode.find('cloudSphere') == 0: + track.append(Func(self.__hitCloudPlatform, self.av, collisionEntry)) + else: + self.notify.warning('************* unhandled hitNode=%s parent =%s' % (hitNode, collisionEntry.getIntoNodePath().getParent())) + + track.append(Func(self.b_setLanded)) + + if self.localToonShooting: + track.append(Func(self.av.collisionsOn)) + + if 1: + if self.hitTrack: + self.hitTrack.finish() + self.hitTrack = track + self.hitTrack.start() + + def __hitGround(self, avatar, pos, extraArgs = []): + hitP = avatar.getPos(render) + self.notify.debug('hitGround pos = %s, hitP = %s' % (pos, hitP)) + self.notify.debug('avatar hpr = %s' % avatar.getHpr()) + h = self.barrel.getH(render) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(h, -135, 0) + self.notify.debug('parent = %s' % avatar.getParent()) + self.notify.debug('pos = %s, hpr = %s' % (avatar.getPos(render), avatar.getHpr(render))) + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + avatar.setPlayRate(2.0, 'run') + avatar.loop('run') + + def __hitHouse(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballHouse] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + + def __hitFence(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballFence] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + + def __hitTarget(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.1, angVel=2) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballTarget] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + if self.localToonShooting: + self.hitTarget = 1 + messenger.send('hitTarget', [self.avId, self.lastVel]) + + def __hitBumper(self, avatar, collisionEntry, sound, kr = 0.6, angVel = 1): + self.hitBumper = 1 + base.playSfx(self.sndHitHouse) + hitP = avatar.getPos(render) + self.lastPos = hitP + house = collisionEntry.getIntoNodePath() + normal = collisionEntry.getSurfaceNormal(house) + normal.normalize() + normal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('normal = %s' % normal) + vel = self.vel * 1 + speed = vel.length() + vel.normalize() + self.notify.debug('old vel = %s' % vel) + newVel = (normal * 2.0 + vel) * (kr * speed) + self.lastVel = newVel + self.notify.debug('new vel = %s' % newVel) + self.angularVel = angVel * 360 + t = Sequence(Func(avatar.pose, 'lose', 110)) + t.start() + + def __hitRoof(self, avatar, collisionEntry, extraArgs = []): + if True: + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.3, angVel=3) + pinballScore = ToontownGlobals.PinballScoring[ToontownGlobals.PinballRoof] + self.incrementPinballInfo(pinballScore[0], pinballScore[1]) + return + np = collisionEntry.getIntoNodePath() + roof = np.getParent() + normal = collisionEntry.getSurfaceNormal(np) + normal.normalize() + vel = self.trajectory.getVel(self.t) + vel.normalize() + dot = normal.dot(vel) + self.notify.debug('--------------dot product = %s---------------' % dot) + temp = render.attachNewNode('temp') + temp.iPosHpr() + temp.lookAt(Point3(normal)) + temp.reparentTo(roof) + self.notify.debug('avatar pos = %s, landingPos = %s' % (avatar.getPos(), self.landingPos)) + temp.setPos(render, self.landingPos) + avatar.reparentTo(temp) + avatar.setPosHpr(0, 0.25, 0.5, 0, 270, 180) + avatar.pose('slip-forward', 25) + base.playSfx(self.sndHitHouse) + avatar.setPlayRate(1.0, 'jump') + h = self.barrel.getH(render) + t = Sequence(LerpPosInterval(avatar, 0.5, Point3(0, 0, -.5), blendType='easeInOut'), Func(avatar.clearColorScale), Func(avatar.wrtReparentTo, render), Wait(0.3), Parallel(Func(avatar.setP, 0), Func(avatar.play, 'jump', None, 19, 39), LerpHprInterval(avatar, 0.3, Vec3(h, 0, 0), blendType='easeOut')), Func(avatar.play, 'neutral')) + t.start() + hitP = avatar.getPos(render) + return + + def __hitBridge(self, avatar, collisionEntry, extraArgs = []): + self.notify.debug('hit bridge') + hitP = avatar.getPos(render) + self.dustCloud.setPos(render, hitP[0], hitP[1], hitP[2] - 0.5) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + + def __hitWater(self, avatar, pos, collisionEntry, extraArgs = []): + hitP = avatar.getPos(render) + if hitP[2] > ToontownGlobals.EstateWakeWaterHeight: + self.notify.debug('we hit the ground before we hit water') + self.__hitGround(avatar, pos, extraArgs) + print 'but not really' + return + self.inWater = 1 + self.notify.debug('hit water') + hitP = avatar.getPos(render) + avatar.loop('neutral') + self.splash.setPos(hitP) + self.splash.setZ(ToontownGlobals.EstateWakeWaterHeight) + self.splash.setScale(2) + self.splash.play() + base.playSfx(self.sndHitWater) + place = base.cr.playGame.getPlace() + self.notify.debug('hitWater: submerged = %s' % place.toonSubmerged) + + def __hitCannonBumper(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballCannonBumper] + self.incrementPinballInfo(score, multiplier) + + def __hitStatuary(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + intoNodePath = collisionEntry.getIntoNodePath() + name = intoNodePath.getParent().getName() + splitParts = name.split('-') + if len(splitParts) >= 3: + score = int(splitParts[1]) + multiplier = int(splitParts[2]) + self.incrementPinballInfo(score, multiplier) + + def __hitCloudPlatform(self, avatar, collisionEntry, extraArgs = []): + if True: + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballCloudBumperLow] + intoNodePath = collisionEntry.getIntoNodePath() + name = intoNodePath.getParent().getName() + splitParts = name.split('-') + if len(splitParts) >= 3: + score = int(splitParts[1]) + multiplier = int(splitParts[2]) + self.incrementPinballInfo(score, multiplier) + return + avatar.reparentTo(collisionEntry.getIntoNodePath()) + h = self.barrel.getH(render) + avatar.setPosHpr(0, 0, 0, h, 0, 0) + messenger.send('hitCloud') + + def __setToonUpright(self, avatar, pos = None): + if avatar: + if self.inWater: + avatar.setP(0) + avatar.setR(0) + return + if not pos: + pos = avatar.getPos(render) + avatar.setPos(render, pos) + avatar.loop('neutral') + place = base.cr.playGame.getPlace() + h = self.barrel.getH(render) + p = Point3(self.vel[0], self.vel[1], self.vel[2]) + self.notify.debug('lookat = %s' % p) + if hasattr(self, 'cannon') and self.cannon: + avatar.lookAt(self.cannon) + avatar.setP(0) + avatar.setR(0) + avatar.setScale(1, 1, 1) + + def __calcToonImpact(self, trajectory): + t_groundImpact = trajectory.checkCollisionWithGround(GROUND_PLANE_MIN) + if t_groundImpact >= trajectory.getStartTime(): + return (t_groundImpact, self.HIT_GROUND) + else: + self.notify.error('__calcToonImpact: toon never impacts ground?') + return (0.0, self.HIT_GROUND) + + def __calcHitTreasures(self, trajectory): + estate = self.cr.doId2do.get(self.estateId) + self.hitTreasures = [] + if estate: + doIds = estate.flyingTreasureId + for id in doIds: + t = self.cr.doId2do.get(id) + if t: + pos = t.pos + rad = 10.5 + height = 10.0 + t_impact = trajectory.checkCollisionWithCylinderSides(pos, rad, height) + if t_impact > 0: + self.hitTreasures.append([t_impact, t]) + + del estate + return None + + def __shootTask(self, task): + base.playSfx(self.sndCannonFire) + self.dropShadow.reparentTo(render) + return Task.done + + def __smokeTask(self, task): + self.smoke.reparentTo(self.barrel) + self.smoke.setPos(0, 6, -3) + self.smoke.setScale(0.5) + self.smoke.wrtReparentTo(render) + track = Sequence(Parallel(LerpScaleInterval(self.smoke, 0.5, 3), LerpColorScaleInterval(self.smoke, 0.5, Vec4(2, 2, 2, 0))), Func(self.smoke.reparentTo, hidden), Func(self.smoke.clearColorScale)) + track.start() + return Task.done + + def __flyTask(self, task): + toon = task.info['toon'] + if toon.isEmpty(): + self.__resetToonToCannon(self.av) + return Task.done + curTime = task.time + task.info['launchTime'] + t = curTime + self.lastT = self.t + self.t = t + deltaT = self.t - self.lastT + self.deltaT = deltaT + if self.hitBumper: + pos = self.lastPos + self.lastVel * deltaT + vel = self.lastVel + self.lastVel += Vec3(0, 0, -32.0) * deltaT + self.lastPos = pos + toon.setFluidPos(pos) + lastH = toon.getH() + toon.setH(lastH + deltaT * self.angularVel) + view = 0 + else: + pos = task.info['trajectory'].getPos(t) + toon.setFluidPos(pos) + shadowPos = Point3(pos) + shadowPos.setZ(SHADOW_Z_OFFSET) + self.dropShadow.setPos(shadowPos) + vel = task.info['trajectory'].getVel(t) + run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]) + rise = vel[2] + theta = self.__toDegrees(math.atan(rise / run)) + toon.setHpr(self.cannon.getH(render), -90 + theta, 0) + view = 2 + if pos.getZ() < -20 or pos.getZ() > 1000: + self.notify.debug('stopping fly task toon.getZ()=%.2f' % pos.getZ()) + self.__resetToonToCannon(self.av) + return Task.done + lookAt = task.info['toon'].getPos(render) + hpr = task.info['toon'].getHpr(render) + if self.localToonShooting: + if view == 0: + camera.wrtReparentTo(render) + camera.lookAt(lookAt) + elif view == 1: + camera.reparentTo(render) + camera.setPos(render, 100, 100, 35.25) + camera.lookAt(render, lookAt) + elif view == 2: + if hpr[1] > -90: + camera.setPos(0, 0, -30) + if camera.getZ() < lookAt[2]: + camera.setZ(render, lookAt[2] + 10) + camera.lookAt(Point3(0, 0, 0)) + self.__pickupTreasures(t) + return Task.cont + + def __pickupTreasures(self, t): + updatedList = [] + for tList in self.hitTreasures: + if t > tList[0]: + messenger.send(tList[1].uniqueName('entertreasureSphere')) + self.notify.debug('hit something!') + else: + updatedList.append(tList) + + self.hitTreasures = updatedList + + def __resetToonToCannon(self, avatar): + pos = None + if not avatar: + if self.avId: + avatar = base.cr.doId2do.get(self.avId, None) + if avatar: + if self.cannon: + avatar.reparentTo(self.cannon) + avatar.setPos(2, -4, 0) + avatar.wrtReparentTo(render) + self.__resetToon(avatar) + return + + def __resetToon(self, avatar, pos = None): + self.notify.debug('__resetToon') + if avatar: + self.__stopCollisionHandler(avatar) + self.__setToonUpright(avatar, pos) + if self.localToonShooting: + self.notify.debug('toon setting position to %s' % pos) + if pos: + base.localAvatar.setPos(pos) + camera.reparentTo(avatar) + self.b_setLanded() + + def setActiveState(self, active): + self.notify.debug('got setActiveState(%s)' % active) + if active and not self.cannonsActive: + self.activateCannons() + elif not active and self.cannonsActive: + self.deActivateCannons() + + def enterInit(self): + self.nextKey = 'up' + self.nextState = 'u1' + + def exitInit(self): + pass + + def enteru1(self): + self.nextKey = 'up' + self.nextState = 'u2' + + def exitu1(self): + pass + + def enteru2(self): + self.nextKey = 'down' + self.nextState = 'd3' + + def exitu2(self): + pass + + def enterd3(self): + self.nextKey = 'down' + self.nextState = 'd4' + + def exitd3(self): + pass + + def enterd4(self): + self.nextKey = 'left' + self.nextState = 'l5' + + def exitd4(self): + pass + + def enterl5(self): + self.nextKey = 'right' + self.nextState = 'r6' + + def exitl5(self): + pass + + def enterr6(self): + self.nextKey = 'left' + self.nextState = 'l7' + + def exitr6(self): + pass + + def enterl7(self): + self.nextKey = 'right' + self.nextState = 'r8' + + def exitl7(self): + pass + + def enterr8(self): + self.nextKey = None + self.nextState = '' + self.codeFSM.request('acceptCode') + return + + def exitr8(self): + pass + + def enterAcceptCode(self): + if not self.cannonsActive: + self.activateCannons() + self.sendUpdate('setActive', [1]) + else: + self.deActivateCannons() + self.sendUpdate('setActive', [0]) + self.codeFSM.request('init') + + def exitAcceptCode(self): + pass + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def handleCodeKey(self, key): + if self.nextKey and self.nextState: + if key == self.nextKey: + self.codeFSM.request(self.nextState) + else: + self.codeFSM.request('init') + + def incrementPinballInfo(self, score, multiplier): + if base.localAvatar.doId == self.avId: + self.curPinballScore += score + self.curPinballMultiplier += multiplier + self.notify.debug('score =%d multiplier=%d curscore=%d curMult=%d' % (score, + multiplier, + self.curPinballScore, + self.curPinballMultiplier)) + self.d_setPinballInfo() + + def d_setPinballInfo(self): + self.notify.debug('d_setPinballInfo %d %d' % (self.curPinballScore, self.curPinballMultiplier)) + target = base.cr.doId2do[self.targetId] + target.b_setCurPinballScore(self.avId, self.curPinballScore, self.curPinballMultiplier) + + def createBlock(self, collisionName = None): + gFormat = GeomVertexFormat.getV3c4() + myVertexData = GeomVertexData('Cannon bumper vertices', gFormat, Geom.UHDynamic) + vertexWriter = GeomVertexWriter(myVertexData, 'vertex') + colorWriter = GeomVertexWriter(myVertexData, 'color') + vertices = [(-1, 1, 1), + (1, 1, 1), + (1, -1, 1), + (-1, -1, 1), + (-1, 1, -1), + (1, 1, -1), + (1, -1, -1), + (-1, -1, -1)] + colors = [(0, 0, 0, 1), + (0, 0, 1, 1), + (0, 1, 0, 1), + (0, 1, 1, 1), + (1, 0, 0, 1), + (1, 0, 1, 1), + (1, 1, 0, 1), + (1, 1, 1, 1)] + faces = [(0, 2, 1), + (0, 3, 2), + (7, 4, 5), + (6, 7, 5), + (2, 3, 7), + (2, 7, 6), + (4, 0, 1), + (5, 4, 1), + (0, 4, 3), + (3, 4, 7), + (1, 2, 6), + (1, 6, 5)] + quads = [(3, 2, 1, 0), + (4, 5, 6, 7), + (3, 7, 6, 2), + (0, 1, 5, 4), + (0, 4, 7, 3), + (1, 2, 6, 5)] + for i in range(len(vertices)): + vertex = vertices[i] + vertexWriter.addData3f(vertex[0], vertex[1], vertex[2]) + colorWriter.addData4f(*colors[i]) + + cubeGeom = Geom(myVertexData) + tris = GeomTriangles(Geom.UHDynamic) + tris.makeIndexed() + for face in faces: + for vertex in face: + tris.addVertex(vertex) + + tris.closePrimitive() + cubeGeom.addPrimitive(tris) + cubeGN = GeomNode('cubeGeom') + cubeGN.addGeom(cubeGeom) + if collisionName: + colNode = CollisionNode(collisionName) + else: + colNode = CollisionNode('cubeCollision') + for quad in quads: + colQuad = CollisionPolygon(Point3(*vertices[quad[0]]), Point3(*vertices[quad[1]]), Point3(*vertices[quad[2]]), Point3(*vertices[quad[3]])) + colQuad.setTangible(0) + colNode.addSolid(colQuad) + + block = NodePath('cubeNodePath') + block.attachNewNode(cubeGN) + block.attachNewNode(colNode) + return block + + def loadCannonBumper(self): + self.cannonBumper = loader.loadModel('phase_5.5/models/estate/bumper_cloud') + self.cannonBumper.reparentTo(self.nodePath) + self.cannonBumper.setScale(4.0) + self.cannonBumper.setColor(0.52, 0.8, 0.98, 1) + colCube = self.cannonBumper.find('**/collision') + colCube.setName('cloudSphere-0') + self.bumperCol = colCube + self.notify.debug('------------self.cannonBumper.setPos %.2f %.2f %.2f' % (ToontownGlobals.PinballCannonBumperInitialPos[0], ToontownGlobals.PinballCannonBumperInitialPos[1], ToontownGlobals.PinballCannonBumperInitialPos[2])) + self.cannonBumper.setPos(*ToontownGlobals.PinballCannonBumperInitialPos) + + def __bumperKeyPressed(self): + self.notify.debug('__bumperKeyPressed') + self.__bumperPressed() + + def __bumperPressed(self): + renderPos = base.localAvatar.getPos(render) + if renderPos[2] > 15.0: + if not self.localToonShooting: + return + self.ignore(self.BUMPER_KEY) + self.ignore(self.BUMPER_KEY2) + self.notify.debug('renderPos %s' % renderPos) + cannonPos = base.localAvatar.getPos(self.nodePath) + self.notify.debug('cannonPos %s' % cannonPos) + self.setCannonBumperPos(cannonPos[0], cannonPos[1], cannonPos[2]) + self.requestBumperMove(cannonPos[0], cannonPos[1], cannonPos[2]) + + def requestBumperMove(self, x, y, z): + self.sendUpdate('requestBumperMove', [x, y, z]) + + def setCannonBumperPos(self, x, y, z): + self.notify.debug('------------setCannonBumperPos %f %f %f' % (x, y, z)) + self.cannonBumper.setPos(x, y, z) + self.bumperCol.setCollideMask(BitMask32.allOff()) + taskMgr.doMethodLater(0.25, self.turnOnBumperCollision, self.uniqueName('BumperON')) + + def turnOnBumperCollision(self, whatever = 0): + if self.bumperCol: + self.bumperCol.setCollideMask(ToontownGlobals.WallBitmask) diff --git a/toontown/estate/DistributedCannonAI.py b/toontown/estate/DistributedCannonAI.py new file mode 100644 index 0000000..d11cf3e --- /dev/null +++ b/toontown/estate/DistributedCannonAI.py @@ -0,0 +1,121 @@ +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from toontown.minigame import CannonGameGlobals +from direct.distributed import DistributedObjectAI +from toontown.minigame import Trajectory +import CannonGlobals + +class DistributedCannonAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedCannonAI') + + def __init__(self, air, estateId, targetId, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.posHpr = [x, + y, + z, + h, + p, + r] + self.avId = 0 + self.estateId = estateId + self.timeoutTask = None + self.targetId = targetId + self.cannonBumperPos = list(ToontownGlobals.PinballCannonBumperInitialPos) + return + + def delete(self): + self.ignoreAll() + self.__stopTimeout() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def requestEnter(self): + avId = self.air.getAvatarIdFromSender() + if self.avId == 0: + self.avId = avId + self.__stopTimeout() + self.setMovie(CannonGlobals.CANNON_MOVIE_LOAD, self.avId) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.acceptOnce('bootAvFromEstate-' + str(avId), self.__handleBootMessage, extraArgs=[avId]) + self.__startTimeout(CannonGlobals.CANNON_TIMEOUT) + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedCannonAI.requestEnter cannon already occupied') + self.notify.warning('requestEnter() - cannon already occupied') + self.sendUpdateToAvatarId(avId, 'requestExit', []) + + def setMovie(self, mode, avId): + self.avId = avId + self.sendUpdate('setMovie', [mode, avId]) + + def getCannonBumperPos(self): + self.notify.debug('---------getCannonBumperPos %s' % self.cannonBumperPos) + return self.cannonBumperPos + + def requestBumperMove(self, x, y, z): + self.cannonBumperPos = [x, y, z] + self.sendUpdate('setCannonBumperPos', [x, y, z]) + + def getPosHpr(self): + return self.posHpr + + def getEstateId(self): + return self.estateId + + def getTargetId(self): + return self.targetId + + def setCannonPosition(self, zRot, angle): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonPosition: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + self.sendUpdate('updateCannonPosition', [avId, zRot, angle]) + + def setCannonLit(self, zRot, angle): + avId = self.air.getAvatarIdFromSender() + self.__stopTimeout() + self.notify.debug('setCannonLit: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + fireTime = CannonGameGlobals.FUSE_TIME + self.sendUpdate('setCannonWillFire', [avId, + fireTime, + zRot, + angle, + globalClockDelta.getRealNetworkTime()]) + + def setLanded(self): + self.ignore(self.air.getAvatarExitEvent(self.avId)) + self.setMovie(CannonGlobals.CANNON_MOVIE_LANDED, 0) + self.avId = 0 + + def setActive(self, active): + if active < 0 or active > 1: + self.air.writeServerEvent('suspicious', active, 'DistributedCannon.setActive value should be 0-1 range') + return + self.active = active + self.sendUpdate('setActiveState', [active]) + + def __startTimeout(self, timeLimit): + self.__stopTimeout() + self.timeoutTask = taskMgr.doMethodLater(timeLimit, self.__handleTimeout, self.taskName('timeout')) + + def __stopTimeout(self): + if self.timeoutTask != None: + taskMgr.remove(self.timeoutTask) + self.timeoutTask = None + return + + def __handleTimeout(self, task): + self.notify.debug('Timeout expired!') + self.__doExit() + return Task.done + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.__doExit() + + def __handleBootMessage(self, avId): + self.notify.warning('avatar:' + str(avId) + ' got booted ') + self.__doExit() + + def __doExit(self): + self.setMovie(CannonGlobals.CANNON_MOVIE_FORCE_EXIT, self.avId) diff --git a/toontown/estate/DistributedChangingStatuary.py b/toontown/estate/DistributedChangingStatuary.py new file mode 100644 index 0000000..e963322 --- /dev/null +++ b/toontown/estate/DistributedChangingStatuary.py @@ -0,0 +1,60 @@ +from pandac.PandaModules import NodePath +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.estate import DistributedStatuary +from toontown.estate import GardenGlobals + +class DistributedChangingStatuary(DistributedStatuary.DistributedStatuary): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedChangingStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedChangingStatuary') + DistributedStatuary.DistributedStatuary.__init__(self, cr) + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = loader.loadModel(self.modelPath) + colNode = self.model.find('**/+CollisionNode') + if not colNode.isEmpty(): + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + if self.pinballScore: + score = self.pinballScore[0] + multiplier = self.pinballScore[1] + scoreNodePath = NodePath('statuary-%d-%d' % (score, multiplier)) + colNode.setName('statuaryCol') + scoreNodePath.reparentTo(colNode.getParent()) + colNode.reparentTo(scoreNodePath) + self.model.setScale(self.worldScale) + self.model.reparentTo(self.rotateNode) + self.hideParts() + self.stick2Ground() + + def hideParts(self): + stage = -1 + attrib = GardenGlobals.PlantAttributes[self.typeIndex] + growthThresholds = attrib['growthThresholds'] + for index, threshold in enumerate(growthThresholds): + if self.growthLevel < threshold: + stage = index + break + + if stage == -1: + stage = len(growthThresholds) + self.notify.debug('growth Stage=%d' % stage) + for index in xrange(len(growthThresholds) + 1): + if index != stage: + partName = '**/growthStage_%d' % index + self.notify.debug('trying to remove %s' % partName) + hideThis = self.model.find(partName) + if not hideThis.isEmpty(): + hideThis.removeNode() + + def setupShadow(self): + DistributedStatuary.DistributedStatuary.setupShadow(self) + self.hideShadow() + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + if self.model: + self.model.removeNode() + self.loadModel() diff --git a/toontown/estate/DistributedCloset.py b/toontown/estate/DistributedCloset.py new file mode 100644 index 0000000..5fe965f --- /dev/null +++ b/toontown/estate/DistributedCloset.py @@ -0,0 +1,506 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +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.showbase import DirectObject +from toontown.toon import ToonDNA +from direct.fsm import ClassicFSM, State, StateData +import ClosetGUI +from direct.task.Task import Task +import ClosetGlobals +import DistributedFurnitureItem +from toontown.toonbase import TTLocalizer + +class DistributedCloset(DistributedFurnitureItem.DistributedFurnitureItem): + notify = directNotify.newCategory('DistributedCloset') + + def __init__(self, cr): + DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr) + self.notify.debug('__init__') + self.lastAvId = 0 + self.hasLocalAvatar = 0 + self.lastTime = 0 + self.av = None + self.closetGUI = None + self.closetModel = None + self.closetSphere = None + self.closetSphereNode = None + self.closetSphereNodePath = None + self.topList = [] + self.botList = [] + self.oldTopList = [] + self.oldBotList = [] + self.oldStyle = None + self.button = None + self.topTrashButton = None + self.bottomTrashButton = None + self.isLocalToon = None + self.popupInfo = None + self.isOwner = 0 + self.ownerId = 0 + self.customerId = 0 + self.purchaseDoneEvent = '' + self.swapEvent = '' + self.locked = 0 + self.gender = None + self.topDeleted = 0 + self.bottomDeleted = 0 + self.closetTrack = None + self.avMoveTrack = None + self.scale = 1.0 + self.fsm = ClassicFSM.ClassicFSM('Closet', [State.State('off', self.enterOff, self.exitOff, ['ready', 'open', 'closed']), + State.State('ready', self.enterReady, self.exitReady, ['open', 'closed']), + State.State('closed', self.enterClosed, self.exitClosed, ['open', 'off']), + State.State('open', self.enterOpen, self.exitOpen, ['closed', 'off'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def generate(self): + DistributedFurnitureItem.DistributedFurnitureItem.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self) + self.load() + self.setupCollisionSphere() + self.fsm.request('ready') + + def load(self): + self.setTwoSided(1) + lNode = self.find('**/door_rotate_L') + lDoor = self.find('**/closetdoor_L') + if lNode.isEmpty() or lDoor.isEmpty(): + self.leftDoor = None + else: + lDoor.wrtReparentTo(lNode) + self.leftDoor = lNode + rNode = self.find('**/door_rotate_R') + rDoor = self.find('**/closetdoor_R') + if rNode.isEmpty() or rDoor.isEmpty(): + self.rightDoor = None + else: + rDoor.wrtReparentTo(rNode) + self.rightDoor = rNode + if not lNode.isEmpty(): + self.scale = lNode.getScale()[0] + return + + def setupCollisionSphere(self): + if self.ownerId: + self.closetSphereEvent = self.uniqueName('closetSphere') + self.closetSphereEnterEvent = 'enter' + self.closetSphereEvent + self.closetSphere = CollisionSphere(0, 0, 0, self.scale * 2.125) + self.closetSphere.setTangible(0) + self.closetSphereNode = CollisionNode(self.closetSphereEvent) + self.closetSphereNode.setIntoCollideMask(WallBitmask) + self.closetSphereNode.addSolid(self.closetSphere) + self.closetSphereNodePath = self.attachNewNode(self.closetSphereNode) + + def disable(self): + self.notify.debug('disable') + self.ignore(self.closetSphereEnterEvent) + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupChangeClothesGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.closetTrack: + self.closetTrack.finish() + self.closetTrack = None + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + self.resetCloset() + if self.hasLocalAvatar: + self.freeAvatar() + self.ignoreAll() + DistributedFurnitureItem.DistributedFurnitureItem.disable(self) + return + + def delete(self): + self.notify.debug('delete') + DistributedFurnitureItem.DistributedFurnitureItem.delete(self) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.av: + del self.av + del self.gender + del self.closetSphere + del self.closetSphereNode + del self.closetSphereNodePath + del self.closetGUI + del self.fsm + return + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterReady(self): + if self.ownerId: + self.accept(self.closetSphereEnterEvent, self.handleEnterSphere) + + def exitReady(self): + pass + + def enterOpen(self): + if self.ownerId: + self.ignore(self.closetSphereEnterEvent) + self._openDoors() + if self.customerId == base.localAvatar.doId: + camera.wrtReparentTo(self) + camera.lerpPosHpr(-7.58, -6.02, 6.9, 286.3, 336.8, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + camera.setPosHpr(self, -7.58, -6.02, 6.9, 286.3, 336.8, 0) + if self.av: + if self.avMoveTrack: + self.avMoveTrack.finish() + self.av.stopSmooth() + self.avMoveTrack = Sequence(Parallel(Func(self.av.play, 'walk'), LerpPosHprInterval(nodePath=self.av, other=self, duration=1.0, pos=Vec3(1.67, -3.29, 0.025), hpr=Vec3(112, 0, 0), blendType='easeOut')), Func(self.av.loop, 'neutral'), Func(self.av.startSmooth)) + self.avMoveTrack.start() + + def exitOpen(self): + if self.ownerId: + self._closeDoors() + + def enterClosed(self): + if self.ownerId: + self.accept(self.closetSphereEnterEvent, self.handleEnterSphere) + + def exitClosed(self): + pass + + def handleEnterSphere(self, collEntry): + if self.smoothStarted: + return + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameTime() <= self.lastTime + 0.5: + self.notify.info('Ignoring duplicate entry for avatar.') + return + if self.hasLocalAvatar: + self.freeAvatar() + self.notify.debug('Entering Closet Sphere....%s' % self.closetSphereEnterEvent) + if self.cr.playGame.getPlace() == None: + self.notify.info('Not opening closet before place is defined.') + return + self.ignore(self.closetSphereEnterEvent) + if not self.locked: + self.cr.playGame.getPlace().fsm.request('closet') + self.accept('closetAsleep', self._handleCancel) + self.sendUpdate('enterAvatar', []) + self.hasLocalAvatar = 1 + return + + def setState(self, mode, avId, ownerId, gender, topList, botList): + self.notify.debug('setState, mode=%s, avId=%s, ownerId=%d' % (mode, avId, ownerId)) + self.isOwner = avId == ownerId + self.ownerGender = gender + if mode == ClosetGlobals.CLOSED: + self.fsm.request('closed') + return + elif mode == ClosetGlobals.OPEN: + self.customerId = avId + self.av = self.cr.doId2do.get(self.customerId, None) + if self.av: + if base.localAvatar.getDoId() == self.customerId: + self.gender = self.av.style.gender + self.topList = topList + self.botList = botList + self.oldTopList = self.topList[0:] + self.oldBotList = self.botList[0:] + print '-----------Starting closet interaction-----------' + self.printInfo() + print '-------------------------------------------------' + if not self.isOwner: + self.__popupNotOwnerPanel() + else: + taskMgr.doMethodLater(0.5, self.popupChangeClothesGUI, self.uniqueName('popupChangeClothesGUI')) + self.fsm.request('open') + return + + def _revertGender(self): + if self.gender: + self.av.style.gender = self.gender + self.av.loop('neutral') + + def popupChangeClothesGUI(self, task): + self.notify.debug('popupChangeClothesGUI') + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapEvent = self.uniqueName('swap') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapEvent, self.__handleSwap) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + if not self.closetGUI: + self.closetGUI = ClosetGUI.ClosetGUI(self.isOwner, self.purchaseDoneEvent, self.cancelEvent, self.swapEvent, self.deleteEvent, self.topList, self.botList) + self.closetGUI.load() + if self.gender != self.ownerGender: + self.closetGUI.setGender(self.ownerGender) + self.closetGUI.enter(base.localAvatar) + self.closetGUI.showButtons() + style = self.av.getStyle() + self.oldStyle = ToonDNA.ToonDNA() + self.oldStyle.makeFromNetString(style.makeNetString()) + return Task.done + + def resetCloset(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupChangeClothesGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.closetGUI: + self.closetGUI.hideButtons() + self.closetGUI.exit() + self.closetGUI.unload() + self.closetGUI = None + del self.av + self.av = base.localAvatar + style = self.av.getStyle() + self.oldStyle = ToonDNA.ToonDNA() + self.oldStyle.makeFromNetString(style.makeNetString()) + self.topDeleted = 0 + self.bottomDeleted = 0 + return Task.done + + def __handleButton(self): + messenger.send('next') + + def _handleCancel(self): + if self.oldStyle: + self.d_setDNA(self.oldStyle.makeNetString(), 1) + else: + self.notify.info('avoided crash in handleCancel') + self._handlePurchaseDone() + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + return + + def __handleSwap(self): + self.d_setDNA(self.av.getStyle().makeNetString(), 0) + + def __handleDelete(self, t_or_b): + if t_or_b == ClosetGlobals.SHIRT: + itemList = self.closetGUI.tops + trashIndex = self.closetGUI.topChoice + swapFunc = self.closetGUI.swapTop + removeFunc = self.closetGUI.removeTop + self.topDeleted = self.topDeleted | 1 + + def setItemChoice(i): + self.closetGUI.topChoice = i + + else: + itemList = self.closetGUI.bottoms + trashIndex = self.closetGUI.bottomChoice + swapFunc = self.closetGUI.swapBottom + removeFunc = self.closetGUI.removeBottom + self.bottomDeleted = self.bottomDeleted | 1 + + def setItemChoice(i): + self.closetGUI.bottomChoice = i + + if len(itemList) > 1: + trashDNA = ToonDNA.ToonDNA() + trashItem = self.av.getStyle().makeNetString() + trashDNA.makeFromNetString(trashItem) + if trashIndex == 0: + swapFunc(1) + else: + swapFunc(-1) + removeFunc(trashIndex) + self.sendUpdate('removeItem', [trashItem, t_or_b]) + swapFunc(0) + self.closetGUI.updateTrashButtons() + else: + self.notify.warning("cant delete this item(type = %s), since we don't have a replacement" % t_or_b) + + def resetItemLists(self): + self.topList = self.oldTopList[0:] + self.botList = self.oldBotList[0:] + self.closetGUI.tops = self.topList + self.closetGUI.bottoms = self.botList + self.topDeleted = 0 + self.bottomDeleted = 0 + + def __proceedToCheckout(self): + if self.topDeleted or self.bottomDeleted: + self.__popupAreYouSurePanel() + else: + self._handlePurchaseDone() + + def _handlePurchaseDone(self, timeout = 0): + if timeout == 1: + self.d_setDNA(self.oldStyle.makeNetString(), 1) + else: + which = 0 + if hasattr(self.closetGUI, 'topChoice') and hasattr(self.closetGUI, 'bottomChoice'): + if self.closetGUI.topChoice != 0 or self.topDeleted: + which = which | 1 + if self.closetGUI.bottomChoice != 0 or self.bottomDeleted: + which = which | 2 + self.d_setDNA(self.av.getStyle().makeNetString(), 2, which) + + def d_setDNA(self, dnaString, finished, whichItems = 3): + self.sendUpdate('setDNA', [dnaString, finished, whichItems]) + + def setCustomerDNA(self, avId, dnaString): + if avId and avId != base.localAvatar.doId: + av = base.cr.doId2do.get(avId, None) + if av: + if self.av == base.cr.doId2do[avId]: + oldTorso = self.av.style.torso + self.av.style.makeFromNetString(dnaString) + if len(oldTorso) == 2 and len(self.av.style.torso) == 2 and self.av.style.torso[1] != oldTorso[1]: + self.av.swapToonTorso(self.av.style.torso, genClothes=0) + self.av.loop('neutral', 0) + self.av.generateToonClothes() + return + + def printInfo(self): + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex, + self.av.style.topTexColor, + self.av.style.sleeveTex, + self.av.style.sleeveTexColor, + self.av.style.botTex, + self.av.style.botTexColor) + print 'topsList = %s' % self.av.getClothesTopsList() + print 'bottomsList = %s' % self.av.getClothesBottomsList() + + def setMovie(self, mode, avId, timestamp): + self.isLocalToon = avId == base.localAvatar.doId + if avId != 0: + self.lastAvId = avId + self.lastTime = globalClock.getFrameTime() + if mode == ClosetGlobals.CLOSET_MOVIE_CLEAR: + return + elif mode == ClosetGlobals.CLOSET_MOVIE_COMPLETE: + if self.isLocalToon: + self._revertGender() + print '-----------ending trunk interaction-----------' + self.printInfo() + print '-------------------------------------------------' + self.resetCloset() + self.freeAvatar() + return + elif mode == ClosetGlobals.CLOSET_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.isLocalToon: + self.ignore(self.purchaseDoneEvent) + self.ignore(self.swapEvent) + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + self._handlePurchaseDone(timeout=1) + self.resetCloset() + self._popupTimeoutPanel() + self.freeAvatar() + + def freeAvatar(self): + self.notify.debug('freeAvatar()') + if self.hasLocalAvatar: + base.localAvatar.posCamera(0, 0) + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + self.ignore('closetAsleep') + base.localAvatar.startLookAround() + self.hasLocalAvatar = 0 + self.lastTime = globalClock.getFrameTime() + + def setOwnerId(self, avId): + self.ownerId = avId + + def _popupTimeoutPanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = 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.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.ClosetTimeoutMessage, frameSize=(-1, 1, -1, 1), geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.45), geom_pos=(0, 0, -.08), text_scale=0.08) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.16), command=self.__handleTimeoutMessageOK) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def __handleTimeoutMessageOK(self): + self.popupInfo.reparentTo(hidden) + + def __popupNotOwnerPanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapEvent = self.uniqueName('swap') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapEvent, self.__handleSwap) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + 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.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.ClosetNotOwnerMessage, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.06)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.21), command=self._handleNotOwnerMessageOK) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def _handleNotOwnerMessageOK(self): + self.popupInfo.reparentTo(hidden) + taskMgr.doMethodLater(0.1, self.popupChangeClothesGUI, self.uniqueName('popupChangeClothesGUI')) + + def __popupAreYouSurePanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = 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.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.ClosetAreYouSureMessage, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.08)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.1, 0.0, -0.21), command=self._handleYesImSure) + DirectButton(self.popupInfo, image=cancelButtonImage, relief=None, text=TTLocalizer.ClosetPopupCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.1, 0.0, -0.21), command=self._handleNotSure) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def _handleYesImSure(self): + self.popupInfo.reparentTo(hidden) + self._handlePurchaseDone() + + def _handleNotSure(self): + self.popupInfo.reparentTo(hidden) + + def _openDoors(self): + if self.closetTrack: + self.closetTrack.finish() + leftHpr = Vec3(-110, 0, 0) + rightHpr = Vec3(110, 0, 0) + self.closetTrack = Parallel() + if self.rightDoor: + self.closetTrack.append(self.rightDoor.hprInterval(0.5, rightHpr)) + if self.leftDoor: + self.closetTrack.append(self.leftDoor.hprInterval(0.5, leftHpr)) + self.closetTrack.start() + + def _closeDoors(self): + if self.closetTrack: + self.closetTrack.finish() + leftHpr = Vec3(0, 0, 0) + rightHpr = Vec3(0, 0, 0) + self.closetTrack = Parallel() + if self.rightDoor: + self.closetTrack.append(self.rightDoor.hprInterval(0.5, rightHpr)) + if self.leftDoor: + self.closetTrack.append(self.leftDoor.hprInterval(0.5, leftHpr)) + self.closetTrack.start() diff --git a/toontown/estate/DistributedEstate.py b/toontown/estate/DistributedEstate.py new file mode 100644 index 0000000..fe96309 --- /dev/null +++ b/toontown/estate/DistributedEstate.py @@ -0,0 +1,410 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import math +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +import random +import cPickle +import time +from direct.showbase import PythonUtil +from toontown.hood import Place +import Estate +import HouseGlobals +from toontown.estate import GardenGlobals +from toontown.estate import DistributedFlower +from toontown.estate import DistributedGagTree +from toontown.estate import DistributedStatuary +import GardenDropGame +import GardenProgressMeter +from toontown.estate import FlowerSellGUI +from toontown.toontowngui import TTDialog + +class DistributedEstate(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedEstate') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.closestHouse = 0 + self.ground = None + self.dayTrack = None + self.sunTrack = None + self.airplane = None + self.flowerSellBox = None + self.estateDoneEvent = 'estateDone' + self.load() + self.initCamera() + self.plotTable = [] + self.idList = [] + base.estate = self + self.flowerGuiDoneEvent = 'flowerGuiDone' + return + + def disable(self): + self.notify.debug('disable') + self.__stopBirds() + self.__stopCrickets() + DistributedObject.DistributedObject.disable(self) + self.ignore('enterFlowerSellBox') + + def delete(self): + self.notify.debug('delete') + self.unload() + DistributedObject.DistributedObject.delete(self) + + def load(self): + self.lt = base.localAvatar + newsManager = base.cr.newsManager + if newsManager: + holidayIds = base.cr.newsManager.getDecorationHolidayId() + if ToontownGlobals.HALLOWEEN_COSTUMES in holidayIds or ToontownGlobals.SPOOKY_COSTUMES in holidayIds: + self.loadWitch() + else: + self.loadAirplane() + self.loadFlowerSellBox() + self.oldClear = base.win.getClearColor() + base.win.setClearColor(Vec4(0.09, 0.55, 0.21, 1.0)) + + def unload(self): + self.ignoreAll() + base.win.setClearColor(self.oldClear) + self.__killAirplaneTask() + self.__killDaytimeTask() + self.__stopBirds() + self.__stopCrickets() + if self.dayTrack: + self.dayTrack.pause() + self.dayTrack = None + self.__killSunTask() + if self.sunTrack: + self.sunTrack.pause() + self.sunTrack = None + if self.ground: + self.ground.removeNode() + del self.ground + if self.airplane: + self.airplane.removeNode() + del self.airplane + self.airplane = None + if self.flowerSellBox: + self.flowerSellBox.removeNode() + del self.flowerSellBox + self.flowerSellBox = None + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.accept('gardenGame', self.startGame) + + def startGame(self): + self.game = GardenDropGame.GardenDropGame() + + def loadAirplane(self): + self.airplane = loader.loadModel('phase_4/models/props/airplane.bam') + self.airplane.setScale(4) + self.airplane.setPos(0, 0, 1) + self.banner = self.airplane.find('**/*banner') + bannerText = TextNode('bannerText') + bannerText.setTextColor(1, 0, 0, 1) + bannerText.setAlign(bannerText.ACenter) + bannerText.setFont(ToontownGlobals.getSignFont()) + bannerText.setText('Cog invasion!!!') + self.bn = self.banner.attachNewNode(bannerText.generate()) + self.bn.setHpr(180, 0, 0) + self.bn.setPos(-1.8, 0.1, 0) + self.bn.setScale(0.35) + self.banner.hide() + + def loadWitch(self): + if not self.airplane: + self.airplane = loader.loadModel('phase_4/models/props/tt_m_prp_ext_flyingWitch.bam') + + def __replaceAirplane__(): + self.airplane.reparentTo(hidden) + del self.airplane + self.airplane = loader.loadModel('phase_4/models/props/tt_m_prp_ext_flyingWitch.bam') + self.airplane.setScale(2) + self.airplane.setPos(0, 0, 1) + bannerText = TextNode('bannerText') + bannerText.setTextColor(1, 0, 0, 1) + bannerText.setAlign(bannerText.ACenter) + bannerText.setFont(ToontownGlobals.getSignFont()) + bannerText.setText('Happy halloween!!!') + self.bn = self.airplane.attachNewNode(bannerText.generate()) + self.bn.setHpr(0, 0, 0) + self.bn.setPos(20.0, -.1, 0) + self.bn.setScale(2.35) + + replacement = Sequence(LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 0)), Func(__replaceAirplane__), LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 1))) + replacement.start() + + def unloadWitch(self): + + def __replaceWitch__(): + self.airplane.reparentTo(hidden) + del self.airplane + del self.bn + self.airplane = loader.loadModel('phase_4/models/props/airplane.bam') + self.airplane.setScale(4) + self.airplane.setPos(0, 0, 1) + self.banner = self.airplane.find('**/*banner') + bannerText = TextNode('bannerText') + bannerText.setTextColor(1, 0, 0, 1) + bannerText.setAlign(bannerText.ACenter) + bannerText.setFont(ToontownGlobals.getSignFont()) + bannerText.setText('Happy halloween!!!') + self.bn = self.banner.attachNewNode(bannerText.generate()) + self.bn.setHpr(180, 0, 0) + self.bn.setPos(-1.8, 0.1, 0) + self.bn.setScale(0.35) + self.banner.hide() + + replacement = Sequence(LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 0)), Func(__replaceWitch__), LerpColorScaleInterval(self.airplane, 0.1, Vec4(1, 1, 1, 1))) + replacement.start() + + def initCamera(self): + initCamPos = VBase3(0, -10, 5) + initCamHpr = VBase3(0, -10, 0) + + def setEstateType(self, index): + self.estateType = index + + def setHouseInfo(self, houseInfo): + self.notify.debug('setHouseInfo') + houseType, housePos = cPickle.loads(houseInfo) + self.loadEstate(houseType, housePos) + + def loadEstate(self, indexList, posList): + self.notify.debug('loadEstate') + self.houseType = indexList + self.housePos = posList + self.numHouses = len(self.houseType) + self.house = [None] * self.numHouses + return + + def __startAirplaneTask(self): + self.theta = 0 + self.phi = 0 + taskMgr.remove(self.taskName('estate-airplane')) + taskMgr.add(self.airplaneFlyTask, self.taskName('estate-airplane')) + + def __pauseAirplaneTask(self): + pause = 45 + self.phi = 0 + self.airplane.hide() + self.theta = (self.theta + 10) % 360 + taskMgr.remove(self.taskName('estate-airplane')) + taskMgr.doMethodLater(pause, self.airplaneFlyTask, self.taskName('estate-airplane')) + + def __killAirplaneTask(self): + taskMgr.remove(self.taskName('estate-airplane')) + + def airplaneFlyTask(self, task): + rad = 300.0 + amp = 80.0 + self.theta += 0.25 + self.phi += 0.005 + sinPhi = math.sin(self.phi) + if sinPhi <= 0: + self.__pauseAirplaneTask() + angle = math.pi * self.theta / 180.0 + x = rad * math.cos(angle) + y = rad * math.sin(angle) + z = amp * sinPhi + self.airplane.reparentTo(render) + self.airplane.setH(90 + self.theta + 180) + self.airplane.setPos(x, y, z) + return Task.cont + + def sendHouseColor(self, index, r, g, b, a): + self.house[index].setColor(r, g, b, a) + + def setTreasureIds(self, doIds): + self.flyingTreasureId = [] + for id in doIds: + self.flyingTreasureId.append(id) + + def setDawnTime(self, ts): + self.notify.debug('setDawnTime') + self.dawnTime = ts + self.sendUpdate('requestServerTime', []) + + def setServerTime(self, ts): + self.notify.debug('setServerTime') + self.serverTime = ts + self.clientTime = time.time() % HouseGlobals.DAY_NIGHT_PERIOD + self.deltaTime = self.clientTime - self.serverTime + if base.dayNightEnabled: + self.__initDaytimeTask() + self.__initSunTask() + self.__startAirplaneTask() + + def getDeltaTime(self): + curTime = time.time() % HouseGlobals.DAY_NIGHT_PERIOD + dawnTime = self.dawnTime + dT = (curTime - dawnTime - self.deltaTime) % HouseGlobals.DAY_NIGHT_PERIOD + print 'getDeltaTime = %s. curTime=%s. dawnTime=%s. serverTime=%s. deltaTime=%s' % (dT, + curTime, + dawnTime, + self.serverTime, + self.deltaTime) + return dT + + def __initDaytimeTask(self): + self.__killDaytimeTask() + task = Task(self.__dayTimeTask) + dT = self.getDeltaTime() + task.ts = dT + taskMgr.add(task, self.taskName('daytime')) + + def __killDaytimeTask(self): + taskMgr.remove(self.taskName('daytime')) + + def __dayTimeTask(self, task): + taskName = self.taskName('daytime') + track = Sequence(Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 0.6, 0.6, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 0.8, 0.8, 1))), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.2, 0.2, 0.5, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.4, 0.4, 0.6, 1))), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.6, 0.6, 0.8, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_NIGHT_PERIOD, Vec4(0.7, 0.7, 0.8, 1))), Parallel(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 1, 1, 1)), LerpColorScaleInterval(base.cr.playGame.hood.sky, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 1, 1, 1))), Func(base.cr.playGame.hood.loader.geom.clearColorScale), Func(base.cr.playGame.hood.sky.clearColorScale)) + if self.dayTrack: + self.dayTrack.finish() + self.dayTrack = track + ts = 0 + if hasattr(task, 'ts'): + ts = task.ts + print 'ts=%s' % ts + self.dayTrack.start(ts) + taskMgr.doMethodLater(HouseGlobals.DAY_NIGHT_PERIOD - ts, self.__dayTimeTask, self.taskName('daytime')) + return Task.done + + def __initSunTask(self): + self.__killSunTask() + task = Task(self.__sunTask) + dT = self.getDeltaTime() + task.ts = dT + taskMgr.add(task, self.taskName('sunTask')) + + def __killSunTask(self): + taskMgr.remove(self.taskName('sunTask')) + + def __sunTask(self, task): + sunMoonNode = base.cr.playGame.hood.loader.sunMoonNode + sun = base.cr.playGame.hood.loader.sun + h = 30 + halfPeriod = HouseGlobals.DAY_NIGHT_PERIOD / 2.0 + track = Sequence(Parallel(LerpHprInterval(sunMoonNode, HouseGlobals.HALF_DAY_PERIOD, Vec3(0, 0, 0)), LerpColorScaleInterval(sun, HouseGlobals.HALF_DAY_PERIOD, Vec4(1, 1, 0.5, 1))), Func(sun.clearColorScale), Func(self.__stopBirds), LerpHprInterval(sunMoonNode, 0.2, Vec3(0, -h - 3, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, -h + 2, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, -h - 1.5, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, -h, 0)), Func(self.notify.debug, 'night'), Wait(HouseGlobals.HALF_NIGHT_PERIOD - 0.5), LerpHprInterval(sunMoonNode, HouseGlobals.HALF_NIGHT_PERIOD, Vec3(0, 0, 0)), Func(self.__startBirds), LerpHprInterval(sunMoonNode, 0.2, Vec3(0, h + 3, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, h - 2, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, h + 1.5, 0)), LerpHprInterval(sunMoonNode, 0.1, Vec3(0, h, 0)), Func(self.notify.debug, 'day'), Func(sunMoonNode.setHpr, 0, h, 0), Wait(HouseGlobals.HALF_DAY_PERIOD - 0.5)) + if self.sunTrack: + self.sunTrack.finish() + self.sunTrack = track + ts = 0 + if hasattr(task, 'ts'): + ts = task.ts + if ts > HouseGlobals.HALF_DAY_PERIOD and ts < HouseGlobals.DAY_NIGHT_PERIOD - HouseGlobals.HALF_DAY_PERIOD: + self.__stopBirds() + self.__startCrickets() + else: + self.__stopCrickets() + self.__startBirds() + print 'ts(sun)=%s' % ts + self.sunTrack.start(ts) + taskMgr.doMethodLater(HouseGlobals.DAY_NIGHT_PERIOD - ts, self.__sunTask, self.taskName('sunTask')) + return Task.done + + def __stopBirds(self): + taskMgr.remove('estate-birds') + + def __startBirds(self): + self.__stopBirds() + taskMgr.doMethodLater(1, self.__birds, 'estate-birds') + + def __birds(self, task): + base.playSfx(random.choice(base.cr.playGame.hood.loader.birdSound)) + t = random.random() * 20.0 + 1 + taskMgr.doMethodLater(t, self.__birds, 'estate-birds') + return Task.done + + def __stopCrickets(self): + taskMgr.remove('estate-crickets') + + def __startCrickets(self): + self.__stopCrickets() + taskMgr.doMethodLater(1, self.__crickets, 'estate-crickets') + + def __crickets(self, task): + sfx = random.choice(base.cr.playGame.hood.loader.cricketSound) + track = Sequence(Func(base.playSfx, sfx), Wait(1)) + track.play() + t = random.random() * 20.0 + 1 + taskMgr.doMethodLater(t, self.__crickets, 'estate-crickets') + return Task.done + + def getLastEpochTimeStamp(self): + return self.lastEpochTimeStamp + + def setLastEpochTimeStamp(self, ts): + self.lastEpochTimeStamp = ts + + def getIdList(self): + return self.idList + + def setIdList(self, idList): + self.idList = idList + + def loadFlowerSellBox(self): + self.flowerSellBox = loader.loadModel('phase_5.5/models/estate/wheelbarrel.bam') + self.flowerSellBox.setPos(-142.586, 4.353, 0.025) + self.flowerSellBox.reparentTo(render) + colNode = self.flowerSellBox.find('**/collision') + colNode.setName('FlowerSellBox') + self.accept('enterFlowerSellBox', self.__touchedFlowerSellBox) + + def __touchedFlowerSellBox(self, entry): + if base.localAvatar.doId in self.idList: + if len(base.localAvatar.flowerBasket.flowerList): + self.popupFlowerGUI() + + def __handleSaleDone(self, sell = 0): + self.ignore(self.flowerGuiDoneEvent) + self.sendUpdate('completeFlowerSale', [sell]) + self.ignore('stoppedAsleep') + self.flowerGui.destroy() + self.flowerGui = None + return + + def popupFlowerGUI(self): + self.acceptOnce(self.flowerGuiDoneEvent, self.__handleSaleDone) + self.flowerGui = FlowerSellGUI.FlowerSellGUI(self.flowerGuiDoneEvent) + self.accept('stoppedAsleep', self.__handleSaleDone) + + def closedAwardDialog(self, value): + self.awardDialog.destroy() + base.cr.playGame.getPlace().detectedGardenPlotDone() + + def awardedTrophy(self, avId): + if base.localAvatar.doId == avId: + base.cr.playGame.getPlace().detectedGardenPlotUse() + msg = TTLocalizer.GardenTrophyAwarded % (len(base.localAvatar.getFlowerCollection()), GardenGlobals.getNumberOfFlowerVarieties()) + self.awardDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=msg, command=self.closedAwardDialog) + + def setClouds(self, clouds): + self.clouds = clouds + base.cr.playGame.hood.loader.setCloudSwitch(clouds) + + def getClouds(self): + if hasattr(self, 'clouds'): + return self.clouds + else: + return 0 + + def cannonsOver(self, arg = None): + base.localAvatar.setSystemMessage(0, TTLocalizer.EstateCannonGameEnd) + + def gameTableOver(self, arg = None): + base.localAvatar.setSystemMessage(0, TTLocalizer.GameTableRentalEnd) diff --git a/toontown/estate/DistributedFireworksCannon.py b/toontown/estate/DistributedFireworksCannon.py new file mode 100644 index 0000000..5572794 --- /dev/null +++ b/toontown/estate/DistributedFireworksCannon.py @@ -0,0 +1,109 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from HouseGlobals import * +from toontown.effects import DistributedFireworkShow +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import CollisionSphere +from pandac.PandaModules import CollisionNode +import FireworksGui + +class DistributedFireworksCannon(DistributedFireworkShow.DistributedFireworkShow): + notify = directNotify.newCategory('DistributedFireworksCannon') + + def __init__(self, cr): + DistributedFireworkShow.DistributedFireworkShow.__init__(self, cr) + self.fireworksGui = None + self.load() + return + + def generateInit(self): + DistributedFireworkShow.DistributedFireworkShow.generateInit(self) + self.fireworksSphereEvent = self.uniqueName('fireworksSphere') + self.fireworksSphereEnterEvent = 'enter' + self.fireworksSphereEvent + self.fireworksGuiDoneEvent = 'fireworksGuiDone' + self.shootEvent = 'fireworkShootEvent' + self.collSphere = CollisionSphere(0, 0, 0, 2.5) + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.fireworksSphereEvent) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.geom.attachNewNode(self.collNode) + + def generate(self): + DistributedFireworkShow.DistributedFireworkShow.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + self.accept(self.fireworksSphereEnterEvent, self.__handleEnterSphere) + + def disable(self): + self.notify.debug('disable') + self.ignore(self.fireworksSphereEnterEvent) + self.ignore(self.shootEvent) + self.ignore(self.fireworksGuiDoneEvent) + if self.fireworksGui: + self.fireworksGui.destroy() + self.fireworksGui = None + DistributedFireworkShow.DistributedFireworkShow.disable(self) + return + + def delete(self): + self.notify.debug('delete') + self.geom.removeNode() + DistributedFireworkShow.DistributedFireworkShow.delete(self) + + def load(self): + self.geom = loader.loadModel('phase_5/models/props/trashcan_TT.bam') + self.geom.reparentTo(base.cr.playGame.hood.loader.geom) + self.geom.setScale(0.5) + + def __handleEnterSphere(self, collEntry): + self.notify.debug('handleEnterSphere()') + self.ignore(self.fireworksSphereEnterEvent) + self.sendUpdate('avatarEnter', []) + + def __handleFireworksDone(self): + self.ignore(self.fireworksGuiDoneEvent) + self.ignore(self.shootEvent) + self.sendUpdate('avatarExit') + self.fireworksGui.destroy() + self.fireworksGui = None + return + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + self.accept(self.fireworksSphereEnterEvent, self.__handleEnterSphere) + + def setMovie(self, mode, avId, timestamp): + timeStamp = globalClockDelta.localElapsedTime(timestamp) + isLocalToon = avId == base.localAvatar.doId + if mode == FIREWORKS_MOVIE_CLEAR: + self.notify.debug('setMovie: clear') + return + elif mode == FIREWORKS_MOVIE_GUI: + self.notify.debug('setMovie: gui') + if isLocalToon: + self.fireworksGui = FireworksGui.FireworksGui(self.fireworksGuiDoneEvent, self.shootEvent) + self.accept(self.fireworksGuiDoneEvent, self.__handleFireworksDone) + self.accept(self.shootEvent, self.localShootFirework) + return + else: + self.notify.warning('unknown mode in setMovie: %s' % mode) + + def setPosition(self, x, y, z): + self.pos = [x, y, z] + self.geom.setPos(x, y, z) + + def localShootFirework(self, index): + style = index + col1, col2 = self.fireworksGui.getCurColor() + amp = 30 + dummy = base.localAvatar.attachNewNode('dummy') + dummy.setPos(0, 100, 60) + pos = dummy.getPos(render) + dummy.removeNode() + print 'lauFirework: %s, col=%s' % (index, col1) + self.d_requestFirework(pos[0], pos[1], pos[2], style, col1, col2) diff --git a/toontown/estate/DistributedFlower.py b/toontown/estate/DistributedFlower.py new file mode 100644 index 0000000..8692847 --- /dev/null +++ b/toontown/estate/DistributedFlower.py @@ -0,0 +1,194 @@ +from toontown.estate import DistributedPlantBase +from direct.directnotify import DirectNotifyGlobal +from toontown.estate import FlowerBase +from toontown.estate import GardenGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +DIRT_AS_WATER_INDICATOR = True +DIRT_MOUND_HEIGHT = 0.3 + +class DistributedFlower(DistributedPlantBase.DistributedPlantBase, FlowerBase.FlowerBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFlower') + + def __init__(self, cr): + DistributedPlantBase.DistributedPlantBase.__init__(self, cr) + FlowerBase.FlowerBase.__init__(self, 49, 0) + self.confirmDialog = None + self.stickUp = 1.07 + if DIRT_AS_WATER_INDICATOR: + self.stickUp += DIRT_MOUND_HEIGHT + self.collSphereRadius = 2.2 + self.shadowScale = 0.5 + self.collSphereOffset = 0.0 + self.dirtMound = None + self.sandMound = None + self.resultDialog = None + return + + def delete(self): + DistributedPlantBase.DistributedPlantBase.delete(self) + del self.dirtMound + del self.sandMound + + def setTypeIndex(self, typeIndex): + DistributedPlantBase.DistributedPlantBase.setTypeIndex(self, typeIndex) + self.setSpecies(typeIndex) + + def showWiltOrBloom(self): + if not self.model: + return + nodePath = self.model + desat = None + flowerColorIndex = GardenGlobals.PlantAttributes[self.getSpecies()]['varieties'][self.getVariety()][1] + colorTuple = GardenGlobals.FlowerColors[flowerColorIndex] + useWilted = self.waterLevel < 0 + wilt = nodePath.find('**/*wilt*') + bloom = nodePath.find('**/*bloom*') + if useWilted: + wilt.show() + desat = wilt.find('**/*desat*') + bloom.hide() + leaves = wilt.findAllMatches('**/*leaf*') + for leafIndex in range(leaves.getNumPaths()): + leaf = leaves.getPath(leafIndex) + leaf.setColorScale(1.0, 0.3, 0.1, 1.0) + + else: + bloom.show() + desat = bloom.find('**/*desat*') + wilt.hide() + if desat and not desat.isEmpty(): + desat.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + elif not self.isSeedling(): + nodePath.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + return + + def loadModel(self): + DistributedPlantBase.DistributedPlantBase.loadModel(self) + self.showWiltOrBloom() + self.model.setH(180) + flowerScale = 2.0 + invFlowerScale = 1.0 / flowerScale + self.model.setScale(flowerScale) + if DIRT_AS_WATER_INDICATOR: + dirtMoundScale = invFlowerScale * 0.73 + self.dirtMound = loader.loadModel('phase_5.5/models/estate/dirt_mound') + self.dirtMound.reparentTo(self.model) + self.dirtMound.setScale(dirtMoundScale) + self.dirtMound.setZ(self.dirtMound.getZ() - DIRT_MOUND_HEIGHT / 2.0) + self.sandMound = loader.loadModel('phase_5.5/models/estate/sand_mound') + self.sandMound.reparentTo(self.model) + self.sandMound.setScale(dirtMoundScale) + self.sandMound.setZ(self.sandMound.getZ() - DIRT_MOUND_HEIGHT / 2.0) + self.adjustWaterIndicator() + + def handlePicking(self): + messenger.send('wakeup') + fullName = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + if self.isWilted(): + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmWiltedFlower % {'plant': fullName}, command=self.confirmCallback) + elif not self.isFruiting(): + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmUnbloomingFlower % {'plant': fullName}, command=self.confirmCallback) + elif base.localAvatar.isFlowerBasketFull(): + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.CancelOnly, text=TTLocalizer.ConfirmBasketFull, command=self.confirmCallback) + else: + shovel = base.localAvatar.shovel + skill = base.localAvatar.shovelSkill + shovelPower = GardenGlobals.getShovelPower(shovel, skill) + giveSkillUp = True + beansRequired = GardenGlobals.getNumBeansRequired(self.species, self.variety) + if not shovelPower == beansRequired: + giveSkillUp = False + if giveSkillUp: + if skill == GardenGlobals.getMaxShovelSkill(): + text = (TTLocalizer.ConfirmMaxedSkillFlower % {'plant': fullName},) + else: + text = TTLocalizer.ConfirmSkillupFlower % {'plant': fullName} + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=text, command=self.confirmCallback) + else: + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmNoSkillupFlower % {'plant': fullName}, command=self.confirmCallback) + self.confirmDialog.show() + base.localAvatar.setInGardenAction(self) + base.cr.playGame.getPlace().detectedGardenPlotUse() + + def confirmCallback(self, value): + self.notify.debug('value=%d' % value) + self.confirmDialog.destroy() + self.confirmDialog = None + if value > 0: + self.doPicking() + else: + self.finishInteraction() + return + + def doPicking(self): + if not self.canBePicked(): + self.notify.debug("I don't own this flower, just returning") + return + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + base.localAvatar.setInGardenAction(self) + base.cr.playGame.getPlace().detectedGardenPlotUse() + self.sendUpdate('removeItem', []) + + def setWaterLevel(self, waterLevel): + DistributedPlantBase.DistributedPlantBase.setWaterLevel(self, waterLevel) + self.showWiltOrBloom() + if self.model: + self.adjustWaterIndicator() + + def setGrowthLevel(self, growthLevel): + origGrowthLevel = self.growthLevel + self.growthLevel = growthLevel + if origGrowthLevel > -1: + self.loadModel() + self.makeMovieNode() + + def makeMovieNode(self): + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, 3, 0) + self.movieNode.setH(180) + self.stick2Ground() + + def setupShadow(self): + if DIRT_AS_WATER_INDICATOR: + pass + else: + DistributedPlantBase.DistributedPlantBase.setupShadow(self) + self.setShadowHeight(-(self.stickUp + 1)) + + def adjustWaterIndicator(self): + if DIRT_AS_WATER_INDICATOR: + if self.dirtMound: + curWaterLevel = self.waterLevel + if curWaterLevel > self.maxWaterLevel: + curWaterLevel = self.maxWaterLevel + if curWaterLevel > 0: + darkestColorScale = 0.4 + lightestColorScale = 1.0 + scaleRange = lightestColorScale - darkestColorScale + scaleIncrement = scaleRange / self.maxWaterLevel + darker = lightestColorScale - scaleIncrement * curWaterLevel + self.dirtMound.setColorScale(darker, darker, darker, 1.0) + self.sandMound.hide() + self.dirtMound.show() + else: + self.sandMound.show() + self.dirtMound.hide() + elif self.model: + color = float(self.waterLevel) / self.maxWaterLevel + self.dropShadow.setColor(0.0, 0.0, 0.0, color) + + def doResultDialog(self): + self.startInteraction() + flowerName = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(flowerName) + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=stringToShow, command=self.resultsCallback) + + def resultsCallback(self, value): + self.notify.debug('value=%d' % value) + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + return diff --git a/toontown/estate/DistributedFurnitureItem.py b/toontown/estate/DistributedFurnitureItem.py new file mode 100644 index 0000000..73b7df8 --- /dev/null +++ b/toontown/estate/DistributedFurnitureItem.py @@ -0,0 +1,138 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.catalog import CatalogItem +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from toontown.toonbase import TTLocalizer +import DistributedHouseItem +from direct.distributed import DistributedSmoothNode +from direct.task import Task +import HouseGlobals + +class DistributedFurnitureItem(DistributedHouseItem.DistributedHouseItem, DistributedSmoothNode.DistributedSmoothNode): + notify = directNotify.newCategory('DistributedFurnitureItem') + + def __init__(self, cr): + DistributedHouseItem.DistributedHouseItem.__init__(self, cr) + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + NodePath.__init__(self) + self.localControl = True + self.__broadcastFrequency = 0.25 + self.__adjustStarted = 0 + self.furnitureMgr = None + self.transmitRelativeTo = None + return + + def generate(self): + DistributedHouseItem.DistributedHouseItem.generate(self) + DistributedSmoothNode.DistributedSmoothNode.generate(self) + self.__taskName = self.taskName('sendRequestPosHpr') + + def announceGenerate(self): + DistributedHouseItem.DistributedHouseItem.announceGenerate(self) + DistributedSmoothNode.DistributedSmoothNode.announceGenerate(self) + self.load() + + def load(self): + pass + + def disable(self): + taskMgr.remove(self.__taskName) + self.stopSmooth() + self.furnitureMgr.dfitems.remove(self) + self.furnitureMgr = None + DistributedHouseItem.DistributedHouseItem.disable(self) + DistributedSmoothNode.DistributedSmoothNode.disable(self) + return + + def delete(self): + self.removeNode() + del self.item + DistributedHouseItem.DistributedHouseItem.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + + def setItem(self, furnitureMgrId, blob): + self.furnitureMgr = self.cr.doId2do[furnitureMgrId] + self.furnitureMgr.dfitems.append(self) + self.item = CatalogItem.getItem(blob, store=CatalogItem.Customization) + self.assign(self.loadModel()) + interior = self.furnitureMgr.getInteriorObject() + self.reparentTo(interior.interior) + + def loadModel(self): + return self.item.loadModel() + + def startAdjustPosHpr(self): + if self.__adjustStarted: + return + self.__adjustStarted = 1 + self.clearSmoothing() + taskMgr.remove(self.__taskName) + posHpr = self.__getPosHpr() + self.__oldPosHpr = posHpr + self.sendRequestPosHpr(0, *posHpr) + taskMgr.doMethodLater(self.__broadcastFrequency, self.__posHprBroadcast, self.__taskName) + + def __posHprBroadcast(self, task): + posHpr = self.__getPosHpr() + if not self.__comparePosHpr(posHpr, self.__oldPosHpr, 0.1): + pass + else: + self.__oldPosHpr = posHpr + self.sendRequestPosHpr(0, *posHpr) + taskMgr.doMethodLater(self.__broadcastFrequency, self.__posHprBroadcast, self.__taskName) + return Task.done + + def stopAdjustPosHpr(self): + if not self.__adjustStarted: + return + self.__adjustStarted = 0 + taskMgr.remove(self.__taskName) + posHpr = self.__getPosHpr() + self.sendRequestPosHpr(1, *posHpr) + del self.__oldPosHpr + + def sendRequestPosHpr(self, final, x, y, z, h, p, r): + t = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('requestPosHpr', (final, + x, + y, + z, + h, + p, + r, + t)) + + def setMode(self, mode, avId): + if mode == HouseGlobals.FURNITURE_MODE_START: + if avId != base.localAvatar.getDoId(): + self.startSmooth() + elif mode == HouseGlobals.FURNITURE_MODE_STOP: + if avId != base.localAvatar.getDoId(): + self.stopSmooth() + elif mode == HouseGlobals.FURNITURE_MODE_OFF: + pass + else: + self.notify.warning('setMode: unknown mode: %s avId: %s' % (mode, avId)) + + def __getPosHpr(self): + if self.transmitRelativeTo == None: + pos = self.getPos() + hpr = self.getHpr() + else: + pos = self.getPos(self.transmitRelativeTo) + hpr = self.getHpr(self.transmitRelativeTo) + return (pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]) + + def __comparePosHpr(self, a, b, threshold): + for i in range(len(a)): + if abs(a[i] - b[i]) >= threshold: + return 1 + + return 0 diff --git a/toontown/estate/DistributedFurnitureManager.py b/toontown/estate/DistributedFurnitureManager.py new file mode 100644 index 0000000..b947e7c --- /dev/null +++ b/toontown/estate/DistributedFurnitureManager.py @@ -0,0 +1,173 @@ +from direct.distributed import DistributedObject +from toontown.catalog import CatalogItem +from toontown.catalog import CatalogItemList +from direct.directnotify.DirectNotifyGlobal import * + +class DistributedFurnitureManager(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedFurnitureManager') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.director = 0 + self.dfitems = [] + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.accept('releaseDirector', self.releaseDirector) + + def disable(self): + self.ignoreAll() + if self.cr.furnitureManager == self: + self.cr.furnitureManager = None + base.localAvatar.setFurnitureDirector(0, self) + self.director = 0 + self.notify.debug('disable') + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.notify.debug('delete') + DistributedObject.DistributedObject.delete(self) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + if self.ownerId == base.localAvatar.doId: + self.cr.furnitureManager = self + if self.cr.objectManager == None: + import houseDesign + self.cr.objectManager = houseDesign.ObjectManager() + return + + def setOwnerName(self, name): + self.ownerName = name + + def setInteriorId(self, interiorId): + self.interiorId = interiorId + + def getInteriorObject(self): + return self.cr.doId2do.get(self.interiorId) + + def setAtticItems(self, items): + self.atticItems = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def setAtticWallpaper(self, items): + self.atticWallpaper = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def setAtticWindows(self, items): + self.atticWindows = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def setDeletedItems(self, items): + self.deletedItems = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + + def releaseDirector(self): + if self.director == base.localAvatar.doId: + self.d_suggestDirector(0) + self.setDirector(0) + + def d_suggestDirector(self, avId): + self.sendUpdate('suggestDirector', [avId]) + + def setDirector(self, avId): + self.notify.info('Furniture director is now %s' % avId) + base.localAvatar.setFurnitureDirector(avId, self) + self.director = avId + + def d_avatarEnter(self): + self.sendUpdate('avatarEnter', []) + + def d_avatarExit(self): + self.sendUpdate('avatarExit', []) + + def moveItemToAttic(self, dfitem, callback): + context = self.getCallbackContext(callback, [dfitem.item]) + self.sendUpdate('moveItemToAtticMessage', [dfitem.doId, context]) + + def moveItemFromAttic(self, index, posHpr, callback): + context = self.getCallbackContext(callback, [index]) + self.sendUpdate('moveItemFromAtticMessage', [index, + posHpr[0], + posHpr[1], + posHpr[2], + posHpr[3], + posHpr[4], + posHpr[5], + context]) + + def deleteItemFromAttic(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteItemFromAtticMessage', [blob, index, context]) + + def deleteItemFromRoom(self, dfitem, callback): + context = self.getCallbackContext(callback, [dfitem.item]) + blob = dfitem.item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteItemFromRoomMessage', [blob, dfitem.doId, context]) + + def moveWallpaperFromAttic(self, index, room, callback): + context = self.getCallbackContext(callback, [index, room]) + self.sendUpdate('moveWallpaperFromAtticMessage', [index, room, context]) + + def deleteWallpaperFromAttic(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteWallpaperFromAtticMessage', [blob, index, context]) + + def moveWindowToAttic(self, slot, callback): + context = self.getCallbackContext(callback, [slot]) + self.sendUpdate('moveWindowToAtticMessage', [slot, context]) + + def moveWindowFromAttic(self, index, slot, callback): + context = self.getCallbackContext(callback, [index, slot]) + self.sendUpdate('moveWindowFromAtticMessage', [index, slot, context]) + + def moveWindow(self, fromSlot, toSlot, callback): + context = self.getCallbackContext(callback, [fromSlot, toSlot]) + self.sendUpdate('moveWindowMessage', [fromSlot, toSlot, context]) + + def deleteWindowFromAttic(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('deleteWindowFromAtticMessage', [blob, index, context]) + + def recoverDeletedItem(self, item, index, callback): + context = self.getCallbackContext(callback, [item, index]) + blob = item.getBlob(store=CatalogItem.Customization) + self.sendUpdate('recoverDeletedItemMessage', [blob, index, context]) + + def moveItemToAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveItemFromAtticResponse(self, retcode, objectId, context): + if retcode >= 0: + dfitem = base.cr.doId2do[objectId] + else: + dfitem = None + self.doCallbackContext(context, [retcode, dfitem]) + return + + def deleteItemFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def deleteItemFromRoomResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWallpaperFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def deleteWallpaperFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWindowToAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWindowFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def moveWindowResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def deleteWindowFromAtticResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) + + def recoverDeletedItemResponse(self, retcode, context): + self.doCallbackContext(context, [retcode]) diff --git a/toontown/estate/DistributedGagTree.py b/toontown/estate/DistributedGagTree.py new file mode 100644 index 0000000..e53973e --- /dev/null +++ b/toontown/estate/DistributedGagTree.py @@ -0,0 +1,428 @@ +from toontown.estate import DistributedPlantBase +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownBattleGlobals +from toontown.toontowngui import TTDialog +from toontown.toontowngui.TeaserPanel import TeaserPanel +from toontown.toonbase import TTLocalizer +import GardenGlobals +import HouseGlobals +from direct.task import Task +from pandac.PandaModules import * +from otp.otpbase import OTPGlobals +from toontown.estate import DistributedLawnDecor +DIRT_AS_WATER_INDICATOR = True + +class DistributedGagTree(DistributedPlantBase.DistributedPlantBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGagTree') + + def __init__(self, cr): + DistributedPlantBase.DistributedPlantBase.__init__(self, cr) + base.tree = self + self.collSphereRadius = 4.2 + self.confirmDialog = None + self.resultDialog = None + self.dirtMound = None + self.sandMound = None + self.needToPlant = 0 + self.needToLoad = 0 + self.backupFruits = [] + self.signHasBeenStuck2Ground = False + self._teaserPanel = None + self.setName('DistributedGagTree') + return + + def delete(self): + DistributedPlantBase.DistributedPlantBase.delete(self) + if self._teaserPanel: + self._teaserPanel.destroy() + self._teaserPanel = None + del self.prop + del self.prop2 + del self.dirtMound + del self.sandMound + self.signModel.removeNode() + self.signModel = None + return + + def setTypeIndex(self, typeIndex): + DistributedPlantBase.DistributedPlantBase.setTypeIndex(self, typeIndex) + track, level = GardenGlobals.getTreeTrackAndLevel(typeIndex) + self.gagTrack = track + self.gagLevel = level + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + propName = ToontownBattleGlobals.AvPropsNew[track][level] + self.prop = invModel.find('**/' + propName) + self.prop.setScale(7) + invModel.removeNode() + invModel2 = loader.loadModel('phase_3.5/models/gui/inventory_icons') + propName = ToontownBattleGlobals.AvPropsNew[track][level] + self.prop2 = invModel2.find('**/' + propName) + self.prop2.setScale(7) + self.filename = self.attributes['filename'] + self.maxFruit = self.attributes['maxFruit'] + if hasattr(self, 'needToLoad'): + if self.needToLoad: + self.loadModel() + + def loadModel(self): + if not hasattr(self, 'filename'): + self.needToLoad = 1 + return + if not self.rotateNode: + self.rotateNode = self.plantPath.attachNewNode('rotate') + all = loader.loadModel(self.filename) + self.modelName = self.getModelName() + if self.isWilted(): + self.modelName += '_wilt' + self.model = all.find('**/' + self.modelName) + all.detachNode() + shadow = self.model.find('**/shadow1') + if shadow: + shadow.hide() + self.model.reparentTo(self.rotateNode) + if self.isFruiting() and not self.isWilted(): + self.fruits = [] + for i in range(1, self.maxFruit + 1): + pos = self.model.find('**/locator' + str(i)) + if pos and not pos.isEmpty(): + fruit = self.prop.copyTo(self.model) + fruit.setPos(pos, 0, 0, 0) + fruit.setScale(13) + self.fruits.append(fruit) + + self.createBackupFruits() + if DIRT_AS_WATER_INDICATOR: + self.dirtMound = loader.loadModel('phase_5.5/models/estate/dirt_mound') + self.dirtMound.reparentTo(self.model) + self.sandMound = loader.loadModel('phase_5.5/models/estate/sand_mound') + self.sandMound.reparentTo(self.model) + self.adjustGrowth() + self.signModel = loader.loadModel('phase_5.5/models/estate/garden_sign.bam') + self.signModel.setPos(3.5, 0, 0.025) + self.signModel.reparentTo(self.rotateNode) + owner = self.getOwnerIndex() + color = HouseGlobals.houseColors[owner] + for geomName in ('sign', 'sign1'): + sign = self.signModel.find('**/' + geomName) + if sign: + sign.setColor(*color) + + self.prop.setPos(0.1, -0.17, 1.63) + self.prop.reparentTo(self.signModel) + self.prop2.setPos(0.15, 0.17, 1.63) + self.prop2.setH(self.prop.getH() + 180) + self.prop2.reparentTo(self.signModel) + self.needToLoad = 0 + if self.needToPlant: + self.stickParts() + + def setupShadow(self): + DistributedPlantBase.DistributedPlantBase.setupShadow(self) + self.adjustGrowth() + + def makeMovieNode(self): + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, -5, 0) + self.createBackupFruits() + + def handlePicking(self): + messenger.send('wakeup') + if self.isFruiting() and self.canBeHarvested(): + if self.velvetRoped(): + self._teaserPanel = TeaserPanel(pageName='pickGags') + localAvatar._gagTreeVelvetRoped = None + else: + self.startInteraction() + self.doHarvesting() + return + fullName = self.name + text = TTLocalizer.ConfirmRemoveTree % {'tree': fullName} + if self.hasDependentTrees(): + text += TTLocalizer.ConfirmWontBeAbleToHarvest + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=text, command=self.confirmCallback) + self.confirmDialog.show() + self.startInteraction() + return + + def confirmCallback(self, value): + self.confirmDialog.destroy() + self.confirmDialog = None + if value > 0: + self.doPicking() + else: + self.finishInteraction() + return + + def doPicking(self): + if not self.canBePicked(): + return + self.sendUpdate('removeItem', []) + + def createBackupFruits(self): + if not hasattr(self, 'fruits'): + return + if not self.fruits: + return + if not hasattr(self, 'movieNode'): + return + if not self.movieNode: + return + if self.movieNode.isEmpty(): + return + if not self.signHasBeenStuck2Ground: + return + if not self.backupFruits: + for fruit in self.fruits: + newFruit = fruit.copyTo(render) + newFruit.setPos(fruit.getPos(render)) + newFruit.setH(self.movieNode.getH(render)) + newFruit.hide() + self.backupFruits.append(newFruit) + + def clearBackupFruits(self): + self.backupFruits = [] + + def doHarvesting(self): + if not self.canBePicked(): + return + if hasattr(self, 'backupFruits'): + for fruit in self.backupFruits: + fruit.show() + + self.sendUpdate('requestHarvest', []) + + def getTrack(self): + return self.gagTrack + + def getGagLevel(self): + return self.gagLevel + + def setWaterLevel(self, waterLevel): + self.waterLevel = waterLevel + self.adjustWaterIndicator() + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + if self.model: + newModelName = self.getModelName() + if True: + self.model.removeNode() + self.loadModel() + self.adjustWaterIndicator() + self.stick2Ground() + else: + self.adjustGrowth() + + def adjustGrowth(self): + newScale = self.growthLevel + 1 + if newScale > 1: + newScale = 1 + shadowScale = 2.5 + collScale = 1.5 + if self.isSeedling(): + shadowScale = 1 + collScale = 1 + if self.shadowJoint: + self.shadowJoint.setScale(shadowScale) + if DIRT_AS_WATER_INDICATOR: + dirtMoundScale = shadowScale * 1.5 + dirtMoundDepth = 2.0 + if self.isEstablished(): + dirtMoundScale = shadowScale * 1.2 + self.dirtMound.setScale(dirtMoundScale, dirtMoundScale, dirtMoundDepth) + self.sandMound.setScale(dirtMoundScale, dirtMoundScale, dirtMoundDepth) + self.adjustWaterIndicator() + + def setWilted(self, wilted): + self.wilted = wilted + + def isWilted(self): + return self.wilted + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_HARVEST: + self.doHarvestTrack(avId) + elif mode == GardenGlobals.MOVIE_WATER: + self.doWaterTrack(avId) + elif mode == GardenGlobals.MOVIE_FINISHPLANTING: + self.doFinishPlantingTrack(avId) + elif mode == GardenGlobals.MOVIE_REMOVE: + self.doDigupTrack(avId) + + def doFinishPlantingTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + if self.model: + self.model.setTransparency(1) + self.model.setAlphaScale(0) + self.movie.append(LerpFunc(self.model.setAlphaScale, fromData=0, toData=1, duration=3)) + if self.signModel: + self.signModel.hide() + self.movie.append(Func(self.signModel.show)) + self.movie.append(LerpScaleInterval(self.signModel, 1, 1, 0)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.append(Func(self.doResultDialog)) + self.movie.start() + + def doHarvestTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + moveTrack = self.generateToonMoveTrack(toon) + harvestTrack = self.generateHarvestTrack(toon) + self.movie = Sequence(self.startCamIval(avId), moveTrack, harvestTrack, self.stopCamIval(avId)) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def setupShadow(self): + if DIRT_AS_WATER_INDICATOR: + pass + else: + DistributedPlantBase.DistributedPlantBase.setupShadow(self) + + def generateHarvestTrack(self, toon): + pos = toon.getPos(render) + pos.setZ(pos.getZ() + 2) + fruitTrack = Parallel() + for fruit in self.backupFruits: + fruitTrack.append(Sequence(Func(fruit.show), LerpPosInterval(fruit, 1.5, pos, startPos=Point3(fruit.getX(), fruit.getY(), fruit.getZ() + self.model.getZ())), Func(fruit.removeNode))) + + self.fruits = None + harvestTrack = Sequence(fruitTrack, Func(self.clearBackupFruits)) + return harvestTrack + + def adjustWaterIndicator(self): + DistributedPlantBase.DistributedPlantBase.adjustWaterIndicator(self) + if self.dirtMound: + curWaterLevel = self.waterLevel + if curWaterLevel > self.maxWaterLevel: + curWaterLevel = self.maxWaterLevel + if curWaterLevel > 0: + darkestColorScale = 0.4 + lightestColorScale = 1.0 + scaleRange = lightestColorScale - darkestColorScale + scaleIncrement = scaleRange / self.maxWaterLevel + darker = lightestColorScale - scaleIncrement * curWaterLevel + self.dirtMound.setColorScale(darker, darker, darker, 1.0) + self.sandMound.hide() + self.dirtMound.show() + else: + self.sandMound.show() + self.dirtMound.hide() + + def stickParts(self): + if not hasattr(self, 'signModel'): + self.needToPlant = 1 + return Task.done + if self.signModel.isEmpty(): + return Task.done + testPath = NodePath('testPath') + testPath.reparentTo(render) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode(self.uniqueName('estate-FloorRay')) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + cRayNodePath = testPath.attachNewNode(cRayNode) + queue = CollisionHandlerQueue() + picker = CollisionTraverser() + picker.addCollider(cRayNodePath, queue) + testPath.setPos(self.signModel.getX(render), self.signModel.getY(render), 0) + picker.traverse(render) + if queue.getNumEntries() > 0: + queue.sortEntries() + for index in range(queue.getNumEntries()): + entry = queue.getEntry(index) + if DistributedLawnDecor.recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): + self.signModel.wrtReparentTo(render) + self.signModel.setZ(entry.getSurfacePoint(render)[2] + self.stickUp + 0.1) + self.signModel.wrtReparentTo(self.rotateNode) + self.signHasBeenStuck2Ground = True + self.createBackupFruits() + return Task.done + + return Task.done + + def canBeHarvested(self): + if not base.cr.isPaid(): + if self.velvetRoped(): + if hasattr(localAvatar, '_gagTreeVelvetRoped'): + return False + myTrack, myLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex) + levelsInTrack = [] + levelTreeDict = {} + allGagTrees = base.cr.doFindAll('DistributedGagTree') + for gagTree in allGagTrees: + if gagTree.getOwnerId() == localAvatar.doId: + curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(gagTree.typeIndex) + if curTrack == myTrack: + levelsInTrack.append(curLevel) + levelTreeDict[curLevel] = gagTree + + for levelToTest in range(myLevel): + if levelToTest not in levelsInTrack: + return False + curTree = levelTreeDict[levelToTest] + if not curTree.isGTEFullGrown(): + return False + + return True + + def hasDependentTrees(self): + myTrack, myLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex) + allGagTrees = base.cr.doFindAll('DistributedGagTree') + for gagTree in allGagTrees: + if gagTree.getOwnerId() == localAvatar.doId: + curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(gagTree.typeIndex) + if curTrack == myTrack: + if myLevel < curLevel: + return True + + return False + + def doResultDialog(self): + self.startInteraction() + curTrack, curLevel = GardenGlobals.getTreeTrackAndLevel(self.typeIndex) + species = GardenGlobals.getTreeTypeIndex(curTrack, curLevel) + treeName = GardenGlobals.PlantAttributes[species]['name'] + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(treeName) + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=stringToShow, command=self.resultsCallback) + + def resultsCallback(self, value): + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + return + + def velvetRoped(self): + return not base.cr.isPaid() and ToontownBattleGlobals.gagIsPaidOnly(self.gagTrack, self.gagLevel) + + def allowedToPick(self): + retval = True + if self.velvetRoped(): + retval = False + return retval + + def unlockPick(self): + retval = True + toon = base.localAvatar + inventory = toon.inventory + load = inventory.totalProps + maxCarry = toon.getMaxCarry() + if load >= maxCarry and not self.gagLevel > ToontownBattleGlobals.LAST_REGULAR_GAG_LEVEL: + retval = False + if inventory.numItem(self.gagTrack, self.gagLevel) >= inventory.getMax(self.gagTrack, self.gagLevel): + retval = False + return retval diff --git a/toontown/estate/DistributedGarden.py b/toontown/estate/DistributedGarden.py new file mode 100644 index 0000000..e52a5a7 --- /dev/null +++ b/toontown/estate/DistributedGarden.py @@ -0,0 +1,130 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from toontown.toonbase import TTLocalizer +import random +import random +import cPickle +from direct.showbase import PythonUtil +from toontown.hood import Place +import Estate +import HouseGlobals + +class DistributedGarden(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedGarden') + + def __init__(self, cr): + self.notify.debug('init') + DistributedObject.DistributedObject.__init__(self, cr) + self.lt = base.localAvatar + self.props = [] + self.pos = None + self.radius = 0 + self.gridCells = 20 + self.propTable = [None] * self.gridCells + for i in range(len(self.propTable)): + self.propTable[i] = [None] * self.gridCells + + self.dx = 1.0 / self.gridCells + self.occupied = [] + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + + def unload(self): + pass + + def delete(self): + for prop in self.props: + prop[0].removeNode() + del prop[0] + del prop + + del self.props + self.props = None + self.unload() + return + + def sendNewProp(self, prop, x, y, z): + self.notify.debug('sendNewProp') + print 'new prop (%d) = %s,%s,%s' % (prop, + x, + y, + z) + if prop == HouseGlobals.PROP_ICECUBE: + model = loader.loadModel('phase_8/models/props/icecube.bam') + elif prop == HouseGlobals.PROP_FLOWER: + model = loader.loadModel('phase_8/models/props/flower_treasure.bam') + elif prop == HouseGlobals.PROP_SNOWFLAKE: + model = loader.loadModel('phase_8/models/props/snowflake_treasure.bam') + model.reparentTo(hidden) + model.setPos(x, y, z) + model.setScale(0.2) + model.setBillboardPointEye() + model.reparentTo(render) + self.props.append([model, + x, + y, + z]) + + def getPropPos(self, i, j): + pos = [self.pos[0] - self.radius + 2 * self.radius * i, self.pos[1] - self.radius + 2 * self.radius * j, self.pos[2]] + return pos + + def loadProp(self, prop, i, j): + pos = self.getPropPos(i, j) + if prop == HouseGlobals.PROP_ICECUBE: + model = loader.loadModel('phase_8/models/props/icecube.bam') + elif prop == HouseGlobals.PROP_FLOWER: + model = loader.loadModel('phase_8/models/props/flower_treasure.bam') + elif prop == HouseGlobals.PROP_SNOWFLAKE: + model = loader.loadModel('phase_8/models/props/snowflake_treasure.bam') + else: + self.notify.error('cant find prop: %s' % prop) + model.reparentTo(hidden) + model.setPos(pos[0], pos[1], pos[2]) + model.setScale(0.2) + model.setBillboardPointEye() + model.reparentTo(render) + + def setAddProp(self, prop, i, j): + self.notify.debug('addProp') + self.props.append([prop, i, j]) + self.loadProp(prop, i, j) + self.b_setProps(self, props) + + def b_setProps(self, props): + self.notify.debug('b_setProps') + self.setProps(props) + self.d_setProps(props) + + def d_setProps(self, props): + self.notify.debug('d_setProps') + aProps = [] + for prop in props: + aProps = aProps + prop + + self.sendUpdate('setProps', [aProps]) + + def setProps(self, props): + self.notify.debug('setProps') + self.props = props + for prop in self.props: + pInd, i, j = prop + self.propTable[i, j] = pInd diff --git a/toontown/estate/DistributedGardenBox.py b/toontown/estate/DistributedGardenBox.py new file mode 100644 index 0000000..ad9cb9b --- /dev/null +++ b/toontown/estate/DistributedGardenBox.py @@ -0,0 +1,57 @@ +import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.estate import PlantingGUI +from toontown.estate import PlantTreeGUI +from direct.distributed import DistributedNode +from pandac.PandaModules import NodePath +from pandac.PandaModules import Vec3 + +class DistributedGardenBox(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGardenPlot') + + def __init__(self, cr): + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.plantPath = NodePath('plantPath') + self.plantPath.reparentTo(self) + self.plotScale = 1.0 + self.plantingGuiDoneEvent = 'plantingGuiDone' + self.defaultModel = 'phase_5.5/models/estate/planterC' + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedLawnDecor.DistributedLawnDecor.announceGenerate(self) + + def doModelSetup(self): + if self.typeIndex == GardenGlobals.BOX_THREE: + self.defaultModel = 'phase_5.5/models/estate/planterA' + elif self.typeIndex == GardenGlobals.BOX_TWO: + self.defaultModel = 'phase_5.5/models/estate/planterC' + else: + self.defaultModel = 'phase_5.5/models/estate/planterD' + self.collSphereOffset = 0.0 + self.collSphereRadius = self.collSphereRadius * 1.41 + self.plotScale = Vec3(1.0, 1.0, 1.0) + + def setupShadow(self): + pass + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + self.model = loader.loadModel(self.defaultModel) + self.model.setScale(self.plotScale) + self.model.reparentTo(self.rotateNode) + self.stick2Ground() + return + + def handleEnterPlot(self, entry = None): + pass + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + + def setTypeIndex(self, typeIndex): + self.typeIndex = typeIndex diff --git a/toontown/estate/DistributedGardenPlot.py b/toontown/estate/DistributedGardenPlot.py new file mode 100644 index 0000000..043f170 --- /dev/null +++ b/toontown/estate/DistributedGardenPlot.py @@ -0,0 +1,356 @@ +import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +from direct.interval.IntervalGlobal import * +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.estate import PlantingGUI +from toontown.estate import PlantTreeGUI +from toontown.estate import ToonStatueSelectionGUI +from toontown.toontowngui import TTDialog +from pandac.PandaModules import Vec4 +from pandac.PandaModules import NodePath +import types + +class DistributedGardenPlot(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGardenPlot') + + def __init__(self, cr): + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.plantPath = NodePath('plantPath') + self.plantPath.reparentTo(self) + self.plotScale = 1.0 + self.plantingGuiDoneEvent = 'plantingGuiDone' + self.toonStatueSelectionDoneEvent = 'toonStatueSelectionDone' + self.defaultModel = 'phase_5.5/models/estate/dirt_mound' + self.colorScaler = Vec4(1, 1, 1, 1) + self.plantingGui = None + return + + def delete(self): + if self.plantingGui: + self.plantingGui.destroy() + self.plantingGui = None + DistributedLawnDecor.DistributedLawnDecor.delete(self) + return + + def announceGenerate(self): + self.plotType = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + self.stickUp = 0.0 + if self.getOwnerId() != localAvatar.doId: + self.defaultModel = None + elif self.plotType == GardenGlobals.FLOWER_TYPE: + self.collSphereRadius = 2.0 + self.collSphereOffset = 0.0 + self.plotScale = 0.7 + self.stickUp = 1.1 + elif self.plotType == GardenGlobals.GAG_TREE_TYPE: + self.collSphereRadius = 3.0 + self.plotScale = 1.5 + self.colorScaler = Vec4(1.0, 1.0, 1.0, 1) + elif self.plotType == GardenGlobals.STATUARY_TYPE: + self.collSphereRadius = 3.0 + self.plotScale = 0.075 + self.stickUp = -0.0 + self.defaultModel = 'phase_5.5/models/estate/garden_slab' + else: + self.collSphereOffset = 0.0 + self.notify.debug('announceGenerate') + DistributedLawnDecor.DistributedLawnDecor.announceGenerate(self) + return + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + if self.defaultModel: + self.model = loader.loadModel(self.defaultModel) + if type(self.plotScale) == types.TupleType: + self.model.setScale(*self.plotScale) + else: + self.model.setScale(self.plotScale) + self.model.reparentTo(self.rotateNode) + self.model.setColorScale(self.colorScaler) + self.stick2Ground() + return + + def setupShadow(self): + pass + + def getShovelCommand(self): + return self.plantSomething + + def getShovelAction(self): + return self.getPlantingText() + + def handleEnterPlot(self, entry = None): + dist = self.getDistance(localAvatar) + if self.canBePlanted(): + base.localAvatar.addShovelRelatedDoId(self.doId) + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + base.localAvatar.removeShovelRelatedDoId(self.doId) + + def getPlantingText(self): + plantText = 'hardcoding' + if self.canBePlanted(): + whatCanBePlanted = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + plantText = TTLocalizer.GardeningPlant + if whatCanBePlanted == GardenGlobals.INVALID_TYPE: + self.notify.warning('whatCanBePlanted returned INVALID_TYPE for %d %d' % (self.ownerIndex, self.plot)) + elif whatCanBePlanted == GardenGlobals.FLOWER_TYPE: + plantText = TTLocalizer.GardeningPlantFlower + elif whatCanBePlanted == GardenGlobals.GAG_TREE_TYPE: + plantText = TTLocalizer.GardeningPlantTree + elif whatCanBePlanted == GardenGlobals.STATUARY_TYPE: + plantText = TTLocalizer.GardeningPlantItem + return plantText + + def canBePlanted(self): + retval = True + if not base.localAvatar.doId == self.getOwnerId(): + retval = False + return retval + + def plantSomething(self): + whatCanBePlanted = GardenGlobals.whatCanBePlanted(self.ownerIndex, self.plot) + if whatCanBePlanted == GardenGlobals.INVALID_TYPE: + self.notify.warning('whatCanBePlanted returned INVALID_TYPE for %d %d' % (self.ownerIndex, self.plot)) + elif whatCanBePlanted == GardenGlobals.FLOWER_TYPE: + self.popupFlowerPlantingGui() + self.startInteraction() + elif whatCanBePlanted == GardenGlobals.GAG_TREE_TYPE: + self.popupTreePlantingGui() + self.startInteraction() + elif whatCanBePlanted == GardenGlobals.STATUARY_TYPE: + self.popupItemPlantingGui() + self.startInteraction() + + def __handleFlowerPlantingDone(self, willPlant = 0, recipeStr = '', special = -1): + self.ignore(self.plantingGuiDoneEvent) + self.ignore('stoppedAsleep') + self.plantingGui.destroy() + self.plantingGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + successPlanting = False + if willPlant: + recipeKey = GardenGlobals.getRecipeKey(recipeStr, special) + if recipeKey >= 0: + species, variety = GardenGlobals.getSpeciesVarietyGivenRecipe(recipeKey) + if species >= 0 and variety >= 0: + self.sendUpdate('plantFlower', [species, variety]) + successPlanting = True + else: + self.notify.debug('%s %d is not a valid recipe' % (recipeStr, special)) + burntBeans = len(recipeStr) + self.sendUpdate('plantNothing', [burntBeans]) + if successPlanting: + flowerName = GardenGlobals.getFlowerVarietyName(species, variety) + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(flowerName) + elif willPlant: + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.ResultPlantedNothing, command=self.popupFlowerPlantingGuiAgain) + else: + self.finishInteraction() + return + + def popupFlowerPlantingGui(self): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.plantingGuiDoneEvent, self.__handleFlowerPlantingDone) + self.plantingGui = PlantingGUI.PlantingGUI(self.plantingGuiDoneEvent) + self.accept('stoppedAsleep', self.__handleFlowerPlantingDone) + + def resultsCallback(self, value): + self.notify.debug('value=%d' % value) + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + return + + def popupFlowerPlantingGuiAgain(self, value): + self.notify.debug('value=%d' % value) + self.resultDialog.destroy() + self.resultDialog = None + self.popupFlowerPlantingGui() + return + + def popupItemPlantingGuiAgain(self, value): + self.notify.debug('value=%d' % value) + self.resultDialog.destroy() + self.resultDialog = None + self.popupItemPlantingGui() + return + + def __handleItemPlantingDone(self, willPlant = 0, recipeStr = '', selectedSpecial = -1): + self.ignore(self.plantingGuiDoneEvent) + self.ignore('stoppedAsleep') + self.plantingGui.destroy() + self.plantingGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + gardenSpecials = base.localAvatar.getGardenSpecials() + special = -1 + if selectedSpecial >= 0: + special = gardenSpecials[selectedSpecial][0] + successPlanting = False + successToonStatue = False + if willPlant: + recipeKey = GardenGlobals.getRecipeKey(recipeStr, special) + if recipeKey >= 0: + species, variety = GardenGlobals.getSpeciesVarietyGivenRecipe(recipeKey) + if species >= 0 and variety >= 0: + if GardenGlobals.PlantAttributes[species]['plantType'] == GardenGlobals.STATUARY_TYPE: + successPlanting = True + if species >= 205 and species <= 208: + successToonStatue = True + else: + self.sendUpdate('plantStatuary', [species]) + else: + self.notify.debug('%s %d is not a valid recipe' % (recipeStr, special)) + burntBeans = len(recipeStr) + self.sendUpdate('plantNothing', [burntBeans]) + if successPlanting: + itemName = GardenGlobals.PlantAttributes[species]['name'] + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(itemName) + elif willPlant: + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.ResultPlantedNothing, command=self.popupItemPlantingGuiAgain) + else: + self.finishInteraction() + if successToonStatue: + self.popupToonStatueSelectionGui(species) + return + + def popupItemPlantingGui(self): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.plantingGuiDoneEvent, self.__handleItemPlantingDone) + self.plantingGui = PlantingGUI.PlantingGUI(self.plantingGuiDoneEvent, True) + self.plantingGui.showFirstSpecial() + self.accept('stoppedAsleep', self.__handleItemPlantingDone) + + def popupToonStatueSelectionGui(self, species): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.toonStatueSelectionDoneEvent, self.__handleToonStatueSelectionDone, extraArgs=[species]) + self.toonStatueSelectionGui = ToonStatueSelectionGUI.ToonStatueSelectionGUI(self.toonStatueSelectionDoneEvent, True) + self.accept('stoppedAsleep', self.__handleToonStatueSelectionDone) + + def popupToonStatueSelectionGuiAgain(self, species): + self.resultDialog.destroy() + self.resultDialog = None + self.popupToonStatueSelectionGui(species) + return + + def __handleToonStatueSelectionDone(self, species, willPlant = 0, recipeStr = '', dnaCode = -1): + self.ignore(self.toonStatueSelectionDoneEvent) + self.ignore('stoppedAsleep') + self.toonStatueSelectionGui.destroy() + self.toonStatueSelectionGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + if willPlant: + self.sendUpdate('plantToonStatuary', [species, dnaCode]) + else: + self.popupItemPlantingGui() + return + + def popupTreePlantingGui(self): + base.localAvatar.hideGardeningGui() + self.acceptOnce(self.plantingGuiDoneEvent, self.__handleTreePlantingDone) + self.plantingGui = PlantTreeGUI.PlantTreeGUI(self.plantingGuiDoneEvent) + self.accept('stoppedAsleep', self.__handleTreePlantingDone) + + def __handleTreePlantingDone(self, willPlant = False, gagTrack = None, gagLevel = None): + self.ignore(self.plantingGuiDoneEvent) + self.ignore('stoppedAsleep') + self.plantingGui.destroy() + self.plantingGui = None + base.localAvatar.showGardeningGui() + base.localAvatar.removeShovelRelatedDoId(self.doId) + if willPlant: + self.sendUpdate('plantGagTree', [gagTrack, gagLevel]) + else: + self.finishInteraction() + return + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_PLANT: + self.doPlaceItemTrack(avId) + elif mode == GardenGlobals.MOVIE_FINISHREMOVING: + self.doFinishRemovingTrack(avId) + elif mode == GardenGlobals.MOVIE_PLANT_REJECTED: + self.doPlantRejectedTrack(avId) + + def doPlantRejectedTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + self.movie.append(Func(toon.detachShovel)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def doFinishRemovingTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + self.movie.append(Func(toon.detachShovel)) + if self.model: + pos = self.model.getPos() + pos.setZ(pos[2] - 1) + animProp = LerpPosInterval(self.model, 3, self.model.getPos(), pos) + shrinkProp = LerpScaleInterval(self.model, 3, scale=self.plotScale, startScale=0.01) + objAnimShrink = ParallelEndTogether(animProp, shrinkProp) + self.movie.append(objAnimShrink) + self.movie.append(self.stopCamIval(avId)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def doPlaceItemTrack(self, avId, item = None): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + if avId == localAvatar.doId: + self.startInteraction() + shovel = toon.attachShovel() + shovel.hide() + moveTrack = self.generateToonMoveTrack(toon) + placeItemTrack = self.generatePlaceItemTrack(toon, item) + self.movie = Sequence(self.startCamIval(avId), moveTrack, Func(shovel.show), placeItemTrack) + if avId == localAvatar.doId: + self.expectingReplacement = 1 + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def generatePlaceItemTrack(self, toon, item): + sound = loader.loadSfx('phase_5.5/audio/sfx/burrow.mp3') + sound.setPlayRate(0.5) + placeItemTrack = Parallel() + placeItemTrack.append(Sequence(ActorInterval(toon, 'start-dig'), Parallel(ActorInterval(toon, 'loop-dig', loop=1, duration=5.13), Sequence(Wait(0.25), SoundInterval(sound, node=toon, duration=0.55), Wait(0.8), SoundInterval(sound, node=toon, duration=0.55), Wait(1.35), SoundInterval(sound, node=toon, duration=0.55))), ActorInterval(toon, 'start-dig', playRate=-1), Func(toon.loop, 'neutral'), Func(toon.detachShovel))) + if self.model: + pos = self.model.getPos() + pos.setZ(pos[2] - 1) + animProp = LerpPosInterval(self.model, 3, pos) + shrinkProp = LerpScaleInterval(self.model, 3, scale=0.01, startScale=self.model.getScale()) + objAnimShrink = ParallelEndTogether(animProp, shrinkProp) + placeItemTrack.append(objAnimShrink) + if item: + placeItemTrack.append(Sequence(Func(item.reparentTo, toon.rightHand), Wait(0.55), Func(item.wrtReparentTo, render), Parallel(LerpHprInterval(item, hpr=self.getHpr(render), duration=1.2), ProjectileInterval(item, endPos=self.getPos(render), duration=1.2, gravityMult=0.45)), Func(item.removeNode))) + return placeItemTrack + + def makeMovieNode(self): + if self.plotType == GardenGlobals.FLOWER_TYPE: + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, 3, 0) + self.movieNode.setH(180) + self.stick2Ground() + else: + DistributedLawnDecor.DistributedLawnDecor.makeMovieNode(self) diff --git a/toontown/estate/DistributedHouse.py b/toontown/estate/DistributedHouse.py new file mode 100644 index 0000000..d8b26ef --- /dev/null +++ b/toontown/estate/DistributedHouse.py @@ -0,0 +1,374 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from toontown.minigame.OrthoWalk import * +from string import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from toontown.toonbase import TTLocalizer +import random +from direct.showbase import PythonUtil +from toontown.hood import Place +import HouseGlobals +from toontown.building import ToonInteriorColors +from direct.showbase.MessengerGlobal import messenger + +class DistributedHouse(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedHouse') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.houseType = None + self.avId = -1 + self.ownerId = 0 + self.colorIndex = 0 + self.house = None + self.name = '' + self.namePlate = None + self.nameText = None + self.nametag = None + self.floorMat = None + self.matText = None + self.randomGenerator = None + self.housePosInd = 0 + self.house_loaded = 0 + return + + def disable(self): + DistributedObject.DistributedObject.disable(self) + + def delete(self): + self.notify.debug('delete') + self.unload() + self.clearNametag() + if self.namePlate: + self.namePlate.removeNode() + del self.namePlate + self.namePlate = None + if self.floorMat: + self.floorMat.removeNode() + del self.floorMat + self.floorMat = None + if self.house: + self.house.removeNode() + del self.house + self.house_loaded = 0 + del self.randomGenerator + DistributedObject.DistributedObject.delete(self) + return + + def clearNametag(self): + if self.nametag != None: + self.nametag.unmanage(base.marginManager) + self.nametag.setAvatar(NodePath()) + self.nametag = None + return + + def load(self): + self.notify.debug('load') + if not self.house_loaded: + if self.housePosInd == 1: + houseModelIndex = base.config.GetInt('want-custom-house', HouseGlobals.HOUSE_DEFAULT) + else: + houseModelIndex = HouseGlobals.HOUSE_DEFAULT + houseModelIndex = base.config.GetInt('want-custom-house-all', houseModelIndex) + houseModel = self.cr.playGame.hood.loader.houseModels[houseModelIndex] + self.house = houseModel.copyTo(self.cr.playGame.hood.loader.houseNode[self.housePosInd]) + self.house_loaded = 1 + self.cr.playGame.hood.loader.houseId2house[self.doId] = self.house + if houseModelIndex == HouseGlobals.HOUSE_DEFAULT: + self.__setHouseColor() + if houseModelIndex == HouseGlobals.HOUSE_DEFAULT: + self.__setupDoor() + else: + self.__setupDoorCustom() + messenger.send('houseLoaded-%d' % self.doId) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + messenger.send('setBuilding-' + str(self.doId)) + + def __setupDoor(self): + self.notify.debug('setupDoor') + self.dnaStore = self.cr.playGame.dnaStore + 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 = self.house.find('**/door_origin') + door_origin.setHpr(90, 0, 0) + door_origin.setScale(0.6, 0.6, 0.8) + door_origin.setPos(door_origin, 0.5, 0, 0.0) + doorNP = door.copyTo(door_origin) + self.door_origin = door_origin + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.doId) + houseColor = HouseGlobals.stairWood + color = Vec4(houseColor[0], houseColor[1], houseColor[2], 1) + DNADoor.setupDoor(doorNP, door_origin, door_origin, self.dnaStore, str(self.doId), color) + self.__setupNamePlate() + self.__setupFloorMat() + self.__setupNametag() + + def __setupDoorCustom(self): + self.randomGenerator = random.Random() + self.randomGenerator.seed(self.doId) + self.notify.debug('setupDoorCustom') + self.dnaStore = self.cr.playGame.dnaStore + door = self.house.find('**/door_0') + door_origin = self.house.find('**/door_origin') + door_origin.setHpr(90, 0, 0) + door_origin.setScale(0.6, 0.6, 0.8) + doorNP = door + self.door_origin = door_origin + color = Vec4(1, 1, 1, 1) + parent = door_origin + rightDoor = door.find('**/rightDoor') + rightDoor.setHpr(door_origin, Vec3(0, 0, 0)) + leftDoor = door.find('**/leftDoor') + leftDoor.setHpr(door_origin, Vec3(0, 0, 0)) + doorTrigger = doorNP.find('**/door_*_trigger') + doorTrigger.wrtReparentTo(door_origin) + doorTrigger.node().setName('door_trigger_' + str(self.doId)) + self.__setupFloorMat(changeColor=False) + self.__setupNametag() + self.__setupNamePlateCustom() + + def __setupNamePlate(self): + self.notify.debug('__setupNamePlate') + if self.namePlate: + self.namePlate.removeNode() + del self.namePlate + self.namePlate = None + nameText = TextNode('nameText') + r = self.randomGenerator.random() + g = self.randomGenerator.random() + b = self.randomGenerator.random() + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setFont(ToontownGlobals.getBuildingNametagFont()) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + nameText.setShadow(*TTLocalizer.BuildingNametagShadow) + nameText.setWordwrap(16.0) + xScale = 1.0 + numLines = 0 + if self.name == '': + return + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + nameText.setText(houseName) + self.nameText = nameText + textHeight = nameText.getHeight() - 2 + textWidth = nameText.getWidth() + xScale = 1.0 + if textWidth > 16: + xScale = 16.0 / textWidth + sign_origin = self.house.find('**/sign_origin') + pos = sign_origin.getPos() + sign_origin.setPosHpr(pos[0], pos[1], pos[2] + 0.15 * textHeight, 90, 0, 0) + self.namePlate = sign_origin.attachNewNode(self.nameText) + self.namePlate.setDepthWrite(0) + self.namePlate.setPos(0, -0.05, 0) + self.namePlate.setScale(xScale) + return nameText + + def __setupFloorMat(self, changeColor = True): + if self.floorMat: + self.floorMat.removeNode() + del self.floorMat + self.floorMat = None + mat = self.house.find('**/mat') + if changeColor: + mat.setColor(0.4, 0.357, 0.259, 1.0) + color = HouseGlobals.houseColors[self.housePosInd] + matText = TextNode('matText') + matText.setTextColor(color[0], color[1], color[2], 1) + matText.setAlign(matText.ACenter) + matText.setFont(ToontownGlobals.getBuildingNametagFont()) + matText.setShadowColor(0, 0, 0, 1) + matText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + matText.setShadow(*TTLocalizer.BuildingNametagShadow) + matText.setWordwrap(10.0) + xScale = 1.0 + numLines = 0 + if self.name == '': + return + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + matText.setText(houseName) + self.matText = matText + textHeight = matText.getHeight() - 2 + textWidth = matText.getWidth() + xScale = 1.0 + if textWidth > 8: + xScale = 8.0 / textWidth + mat_origin = self.house.find('**/mat_origin') + pos = mat_origin.getPos() + mat_origin.setPosHpr(pos[0] - 0.15 * textHeight, pos[1], pos[2], 90, -90, 0) + self.floorMat = mat_origin.attachNewNode(self.matText) + self.floorMat.setDepthWrite(0) + self.floorMat.setPos(0, -.025, 0) + self.floorMat.setScale(0.45 * xScale) + return + + def __setupNametag(self): + if self.nametag: + self.clearNametag() + if self.name == '': + houseName = '' + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + 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.CCHouseBuilding) + self.nametag.setActive(0) + self.nametag.setAvatar(self.house) + self.nametag.setObjectCode(self.doId) + self.nametag.setName(houseName) + self.nametag.manage(base.marginManager) + + def unload(self): + self.notify.debug('unload') + self.ignoreAll() + + def setHouseReady(self): + self.notify.debug('setHouseReady') + try: + self.House_initialized + except: + self.House_initialized = 1 + self.load() + + def setHousePos(self, index): + self.notify.debug('setHousePos') + self.housePosInd = index + self.__setHouseColor() + + def setHouseType(self, index): + self.notify.debug('setHouseType') + self.houseType = index + + def setFavoriteNum(self, index): + self.notify.debug('setFavoriteNum') + self.favoriteNum = index + + def __setHouseColor(self): + if self.house: + bwall = self.house.find('**/*back') + rwall = self.house.find('**/*right') + fwall = self.house.find('**/*front') + lwall = self.house.find('**/*left') + kd = 0.8 + color = HouseGlobals.houseColors[self.colorIndex] + dark = (kd * color[0], kd * color[1], kd * color[2]) + if not bwall.isEmpty(): + bwall.setColor(color[0], color[1], color[2], 1) + if not fwall.isEmpty(): + fwall.setColor(color[0], color[1], color[2], 1) + if not rwall.isEmpty(): + rwall.setColor(dark[0], dark[1], dark[2], 1) + if not lwall.isEmpty(): + lwall.setColor(dark[0], dark[1], dark[2], 1) + aColor = HouseGlobals.atticWood + attic = self.house.find('**/attic') + if not attic.isEmpty(): + attic.setColor(aColor[0], aColor[1], aColor[2], 1) + color = HouseGlobals.houseColors2[self.colorIndex] + chimneyList = self.house.findAllMatches('**/chim*') + for chimney in chimneyList: + chimney.setColor(color[0], color[1], color[2], 1) + + def setAvId(self, id): + self.avId = id + + def setAvatarId(self, avId): + self.notify.debug('setAvatarId = %s' % avId) + self.ownerId = avId + + def getAvatarId(self): + self.notify.debug('getAvatarId') + return self.ownerId + + def setName(self, name): + self.name = name + if self.nameText and self.nameText.getText() != self.name: + if self.name == '': + self.nameText.setText('') + else: + self.nameText.setText(self.name + "'s\n House") + + def getName(self): + return self.name + + def b_setColor(self, colorInd): + self.setColor(colorInd) + self.d_setColor(colorInd) + + def d_setColor(self, colorInd): + self.sendUpdate('setColor', [colorInd]) + + def setColor(self, colorInd): + self.colorIndex = colorInd + if self.house: + self.__setHouseColor() + + def getColor(self): + return self.colorIndex + + def __setupNamePlateCustom(self): + self.notify.debug('__setupNamePlateCustom') + if self.namePlate: + self.namePlate.removeNode() + del self.namePlate + self.namePlate = None + nameText = TextNode('nameText') + nameText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + nameText.setCardDecal(True) + nameText.setCardColor(1.0, 1.0, 1.0, 0.0) + r = self.randomGenerator.random() + g = self.randomGenerator.random() + b = self.randomGenerator.random() + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setFont(ToontownGlobals.getBuildingNametagFont()) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + nameText.setShadow(*TTLocalizer.BuildingNametagShadow) + nameText.setWordwrap(16.0) + xScale = 1.0 + numLines = 0 + if self.name == '': + return + else: + houseName = TTLocalizer.AvatarsHouse % TTLocalizer.GetPossesive(self.name) + nameText.setText(houseName) + self.nameText = nameText + textHeight = nameText.getHeight() - 2 + textWidth = nameText.getWidth() + xScale = 1.0 + if textWidth > 16: + xScale = 16.0 / textWidth + sign_origin = self.house.find('**/sign_origin') + pos = sign_origin.getPos() + sign_origin.setPosHpr(pos[0], pos[1], pos[2] + 0.15 * textHeight, 90, 0, 0) + self.namePlate = sign_origin.attachNewNode(self.nameText) + self.namePlate.setDepthWrite(0) + self.namePlate.setPos(0, -0.05, 0) + self.namePlate.setScale(xScale) + return nameText diff --git a/toontown/estate/DistributedHouseDoor.py b/toontown/estate/DistributedHouseDoor.py new file mode 100644 index 0000000..7695728 --- /dev/null +++ b/toontown/estate/DistributedHouseDoor.py @@ -0,0 +1,123 @@ +from toontown.toonbase.ToonBaseGlobal import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.MessengerGlobal import messenger +from direct.fsm import ClassicFSM +from toontown.building import DistributedDoor +from toontown.hood import ZoneUtil +from toontown.suit import Suit +from toontown.building import FADoorCodes +from toontown.building import DoorTypes + +class DistributedHouseDoor(DistributedDoor.DistributedDoor): + + def __init__(self, cr): + DistributedDoor.DistributedDoor.__init__(self, cr) + + def disable(self): + DistributedDoor.DistributedDoor.disable(self) + self.ignoreAll() + + def setZoneIdAndBlock(self, zoneId, block): + self.houseId = block + DistributedDoor.DistributedDoor.setZoneIdAndBlock(self, zoneId, block) + + def getTriggerName(self): + return 'door_trigger_' + str(self.houseId) + + def hideDoorParts(self): + pass + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if self.doorType == DoorTypes.EXT_STANDARD: + house = base.cr.doId2do.get(self.houseId) + if house and house.house_loaded: + self.__gotRelatedHouse() + else: + self.acceptOnce('houseLoaded-%d' % self.houseId, self.__gotRelatedHouse) + elif self.doorType == DoorTypes.INT_STANDARD: + door = render.find('**/leftDoor;+s') + if door.isEmpty(): + self.acceptOnce('houseInteriorLoaded-%d' % self.zoneId, self.__gotRelatedHouse) + else: + self.__gotRelatedHouse() + + def __gotRelatedHouse(self): + self.doPostAnnounceGenerate() + 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.zoneDoneLoading = 0 + + def getBuilding(self, allowEmpty = False): + 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.EXT_STANDARD: + if self.houseId: + self.building = self.cr.playGame.hood.loader.houseId2house.get(self.houseId, None) + if allowEmpty: + return self.building + return self.building + + def isInterior(self): + if self.doorType == DoorTypes.INT_STANDARD: + return 1 + return 0 + + def getDoorNodePath(self): + if self.doorType == DoorTypes.INT_STANDARD: + otherNP = render.find('**/door_origin') + elif self.doorType == DoorTypes.EXT_STANDARD: + building = self.getBuilding() + otherNP = building.find('**/door') + if otherNP.isEmpty(): + otherNP = building.find('**/door_origin') + else: + self.notify.error('No such door type as ' + str(self.doorType)) + return otherNP + + 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'): + base.cr.playGame.hood.loader.setHouse(self.houseId) + zoneId = self.otherZoneId + if self.doorType == DoorTypes.EXT_STANDARD: + whereTo = 'house' + else: + whereTo = 'estate' + request = {'loader': 'safeZoneLoader', + 'where': whereTo, + 'how': 'doorIn', + 'hoodId': ToontownGlobals.MyEstate, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1, + 'allowRedirect': 0, + 'doorDoId': self.otherDoId} + messenger.send('doorDoneEvent', [request]) + return diff --git a/toontown/estate/DistributedHouseInterior.py b/toontown/estate/DistributedHouseInterior.py new file mode 100644 index 0000000..7094e1e --- /dev/null +++ b/toontown/estate/DistributedHouseInterior.py @@ -0,0 +1,202 @@ +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase.ToonBaseGlobal import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +import HouseGlobals +from toontown.catalog import CatalogItemList +from toontown.catalog import CatalogItem +from toontown.catalog import CatalogSurfaceItem +from toontown.catalog import CatalogWallpaperItem +from toontown.catalog import CatalogFlooringItem +from toontown.catalog import CatalogMouldingItem +from toontown.catalog import CatalogWainscotingItem +WindowPlugNames = ('**/windowcut_a*', '**/windowcut_b*', '**/windowcut_c*', '**/windowcut_d*', '**/windowcut_e*', '**/windowcut_f*') +RoomNames = ('**/group2', '**/group1') +WallNames = ('ceiling*', 'wall_side_middle*', 'wall_front_middle*', 'windowcut_*') +MouldingNames = ('wall_side_top*', 'wall_front_top*') +FloorNames = ('floor*',) +WainscotingNames = ('wall_side_bottom*', 'wall_front_bottom*') +BorderNames = ('wall_side_middle*_border', 'wall_front_middle*_border', 'windowcut_*_border') +WallpaperPieceNames = (WallNames, + MouldingNames, + FloorNames, + WainscotingNames, + BorderNames) + +class DistributedHouseInterior(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.houseId = 0 + self.houseIndex = 0 + self.interior = None + self.exteriorWindowsHidden = 0 + return + + 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): + self.ignore(self.uniqueName('enterclosetSphere')) + DistributedObject.DistributedObject.delete(self) + + def setup(self): + dnaStore = base.cr.playGame.dnaStore + self.interior = loader.loadModel('phase_5.5/models/estate/tt_m_ara_int_estateHouseA') + self.interior.reparentTo(render) + doorModelName = 'door_double_round_ur' + door = dnaStore.findNode(doorModelName) + door_origin = self.interior.find('**/door_origin') + door_origin.setHpr(180, 0, 0) + door_origin.setScale(0.8, 0.8, 0.8) + door_origin.setPos(door_origin, 0, -0.025, 0) + doorNP = door.copyTo(door_origin) + houseColor = HouseGlobals.atticWood + color = Vec4(houseColor[0], houseColor[1], houseColor[2], 1) + DNADoor.setupDoor(doorNP, door_origin, door_origin, dnaStore, str(self.houseId), color) + doorFrame = doorNP.find('door_*_flat') + doorFrame.setColor(color) + self.interior.flattenMedium() + self.windowSlots = [] + for name in WindowPlugNames: + plugNodes = self.interior.findAllMatches(name) + if plugNodes.isEmpty(): + self.windowSlots.append((None, None)) + else: + viewBase = plugNodes[0].getParent().attachNewNode('view') + viewBase.setTransform(plugNodes[0].getTransform()) + plug = plugNodes[0].getParent().attachNewNode('plug') + plugNodes.reparentTo(plug) + plug.flattenLight() + self.windowSlots.append((plug, viewBase)) + + self.windowSlots[2][1].setPosHpr(16.0, -12.0, 5.51, -90, 0, 0) + self.windowSlots[4][1].setPosHpr(-12.0, 26.0, 5.51, 0, 0, 0) + self.__colorWalls() + self.__setupWindows() + messenger.send('houseInteriorLoaded-%d' % self.zoneId) + return None + + def __colorWalls(self): + if not self.wallpaper: + self.notify.info('No wallpaper in interior; clearing.') + for str in WallNames + WainscotingNames: + nodes = self.interior.findAllMatches('**/%s' % str) + for node in nodes: + node.setTextureOff(1) + + return + numSurfaceTypes = CatalogSurfaceItem.NUM_ST_TYPES + numRooms = min(len(self.wallpaper) / numSurfaceTypes, len(RoomNames)) + for room in range(numRooms): + roomName = RoomNames[room] + roomNode = self.interior.find(roomName) + if not roomNode.isEmpty(): + for surface in range(numSurfaceTypes): + slot = room * numSurfaceTypes + surface + wallpaper = self.wallpaper[slot] + color = wallpaper.getColor() + texture = wallpaper.loadTexture() + for str in WallpaperPieceNames[surface]: + nodes = roomNode.findAllMatches('**/%s' % str) + for node in nodes: + if str == 'ceiling*': + r, g, b, a = color + scale = 0.66 + r *= scale + g *= scale + b *= scale + node.setColorScale(r, g, b, a) + else: + node.setColorScale(*color) + node.setTexture(texture, 1) + + if wallpaper.getSurfaceType() == CatalogSurfaceItem.STWallpaper: + color2 = wallpaper.getBorderColor() + texture2 = wallpaper.loadBorderTexture() + nodes = roomNode.findAllMatches('**/%s_border' % str) + for node in nodes: + node.setColorScale(*color2) + node.setTexture(texture2, 1) + + nodes = self.interior.findAllMatches('**/arch*') + for node in nodes: + node.setColorScale(*(HouseGlobals.archWood + (1,))) + + def __setupWindows(self): + for plug, viewBase in self.windowSlots: + if plug: + plug.show() + if viewBase: + viewBase.getChildren().detach() + + if not self.windows: + self.notify.info('No windows in interior; returning.') + return + for item in self.windows: + plug, viewBase = self.windowSlots[item.placement] + if plug: + plug.hide() + if viewBase: + model = item.loadModel() + model.reparentTo(viewBase) + if self.exteriorWindowsHidden: + model.findAllMatches('**/outside').stash() + + def hideExteriorWindows(self): + self.exteriorWindowsHidden = 1 + for item in self.windows: + plug, viewBase = self.windowSlots[item.placement] + if viewBase: + viewBase.findAllMatches('**/outside').stash() + + def showExteriorWindows(self): + self.exteriorWindowsHidden = 0 + for item in self.windows: + plug, viewBase = self.windowSlots[item.placement] + if viewBase: + viewBase.findAllMatches('**/outside;+s').unstash() + + def setHouseId(self, index): + self.houseId = index + + def setHouseIndex(self, index): + self.houseIndex = index + + def setWallpaper(self, items): + self.wallpaper = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization) + if self.interior: + self.__colorWalls() + + def setWindows(self, items): + self.windows = CatalogItemList.CatalogItemList(items, store=CatalogItem.Customization | CatalogItem.WindowPlacement) + if self.interior: + self.__setupWindows() + + def testWallpaperCombo(self, wallpaperType, wallpaperColorIndex, borderIndex, borderColorIndex, mouldingType, mouldingColorIndex, flooringType, flooringColorIndex, wainscotingType, wainscotingColorIndex): + wallpaperItem = CatalogWallpaperItem.CatalogWallpaperItem(wallpaperType, wallpaperColorIndex, borderIndex, borderColorIndex) + mouldingItem = CatalogMouldingItem.CatalogMouldingItem(mouldingType, mouldingColorIndex) + flooringItem = CatalogFlooringItem.CatalogFlooringItem(flooringType, flooringColorIndex) + wainscotingItem = CatalogWainscotingItem.CatalogWainscotingItem(wainscotingType, wainscotingColorIndex) + self.wallpaper = CatalogItemList.CatalogItemList([wallpaperItem, + mouldingItem, + flooringItem, + wainscotingItem, + wallpaperItem, + mouldingItem, + flooringItem, + wainscotingItem], store=CatalogItem.Customization) + if self.interior: + self.__colorWalls() diff --git a/toontown/estate/DistributedHouseItem.py b/toontown/estate/DistributedHouseItem.py new file mode 100644 index 0000000..f1f1267 --- /dev/null +++ b/toontown/estate/DistributedHouseItem.py @@ -0,0 +1,28 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from toontown.toonbase import TTLocalizer + +class DistributedHouseItem(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedHouseItem') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.load() + + def load(self): + pass + + def disable(self): + DistributedObject.DistributedObject.disable(self) + + def delete(self): + DistributedObject.DistributedObject.delete(self) diff --git a/toontown/estate/DistributedLawnDecor.py b/toontown/estate/DistributedLawnDecor.py new file mode 100644 index 0000000..23113d8 --- /dev/null +++ b/toontown/estate/DistributedLawnDecor.py @@ -0,0 +1,375 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed import ClockDelta +from direct.showbase.PythonUtil import lerp +import math +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.distributed import DistributedNode +from direct.showbase import PythonUtil +from otp.avatar import ShadowCaster +import random +from otp.otpbase import OTPGlobals +from toontown.estate import GardenGlobals + +def recurseParent(intoNode, ParentName): + parent = intoNode.getParent(0) + if not parent or parent.getName() == 'render': + return 0 + elif parent.getName() == ParentName: + return 1 + else: + return recurseParent(parent, ParentName) + + +class DistributedLawnDecor(DistributedNode.DistributedNode, NodePath, ShadowCaster.ShadowCaster): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawnDecor') + + def __init__(self, cr): + DistributedNode.DistributedNode.__init__(self, cr) + NodePath.__init__(self, 'decor') + ShadowCaster.ShadowCaster.__init__(self, False) + self.plantPath = NodePath('plantPath') + self.plantPath.reparentTo(self) + self.defaultModel = 'phase_9/models/cogHQ/woodCrateB' + self.messageName = None + self.model = None + self.colSphereNode = None + self.rotateNode = None + self.collSphereOffset = 0.0 + self.collSphereRadius = 1.0 + self.stickUp = 0.0 + self.movieNode = None + self.shadowJoint = None + self.shadowScale = 1 + self.expectingReplacement = 0 + self.movie = None + return + + def setHeading(self, h): + self.notify.debug('setting h') + DistributedNode.DistributedNode.setH(self, h) + + def generateInit(self): + self.notify.debug('generateInit') + DistributedNode.DistributedNode.generateInit(self) + + def generate(self): + self.notify.debug('generate') + self.reparentTo(render) + DistributedNode.DistributedNode.generate(self) + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedNode.DistributedNode.announceGenerate(self) + self.doModelSetup() + self.loadModel() + self.setupShadow() + self.makeMovieNode() + self.stick2Ground() + self.setupCollision() + + def doModelSetup(self): + pass + + def disable(self): + self.notify.debug('disable') + self.finishMovies() + self.handleExitPlot() + self.ignoreAll() + DistributedNode.DistributedNode.disable(self) + if hasattr(self, 'nodePath'): + self.nodePath.detachNode() + + def delete(self): + self.notify.debug('delete') + ShadowCaster.ShadowCaster.delete(self) + self.unloadModel() + DistributedNode.DistributedNode.delete(self) + + def loadModel(self): + if not self.rotateNode: + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + if __dev__: + self.model = loader.loadModel(self.defaultModel) + self.model.setScale(0.4, 0.4, 0.1) + self.model.reparentTo(self.rotateNode) + return + + def setupShadow(self): + self.shadowJoint = self.rotateNode.attachNewNode('shadow') + self.initializeDropShadow(False) + self.shadowJoint.setScale(self.shadowScale) + self.setActiveShadow() + + def makeMovieNode(self): + self.movieNode = self.rotateNode.attachNewNode('moviePos') + self.movieNode.setPos(0, -3, 0) + + def setupCollision(self): + self.messageName = self.uniqueName('enterplotSphere') + self.messageStartName = self.uniqueName('plotSphere') + self.exitMessageName = self.uniqueName('exitplotSphere') + if self.collSphereOffset <= 0.1: + colSphere = CollisionSphere(0, 0, 0, self.collSphereRadius) + else: + colSphere = CollisionTube(0, -self.collSphereOffset, 0, 0, self.collSphereOffset, 0, self.collSphereRadius) + colSphere.setTangible(0) + colNode = CollisionNode(self.messageStartName) + colNode.addSolid(colSphere) + colSphereNode = self.attachNewNode(colNode) + self.colSphereNode = colSphereNode + self.accept(self.messageName, self.handleEnterPlot) + self.accept(self.exitMessageName, self.handleExitPlot) + + def handleEnterPlot(self, optional = None): + self.notify.debug('handleEnterPlot %d' % self.doId) + self.sendUpdate('plotEntered', []) + + def handleExitPlot(self, optional = None): + if base.localAvatar.inGardenAction == self: + base.localAvatar.handleEndPlantInteraction(self, replacement=self.expectingReplacement) + + def handleWatering(self): + self.handleExitPlot() + base.localAvatar.removeShovelRelatedDoId(self.doId) + + def unloadModel(self): + if self.model: + self.model.removeNode() + del self.model + self.model = None + if hasattr(self, 'nodePath') and self.nodePath: + self.nodePath.removeNode() + self.nodePath = None + taskMgr.remove(self.uniqueName('adjust tree')) + return + + def setPos(self, x, y, z): + DistributedNode.DistributedNode.setPos(self, x, y, z) + self.stick2Ground() + + def setPosition(self, x, y, z): + DistributedNode.DistributedNode.setPos(self, x, y, z) + self.stick2Ground() + + def stick2Ground(self, taskfooler = 0): + if self.isEmpty(): + return Task.done + testPath = NodePath('testPath') + testPath.reparentTo(render) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode(self.uniqueName('estate-FloorRay')) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + cRayNodePath = testPath.attachNewNode(cRayNode) + queue = CollisionHandlerQueue() + picker = CollisionTraverser() + picker.addCollider(cRayNodePath, queue) + if self.movieNode: + testPath.setPos(self.movieNode.getX(render), self.movieNode.getY(render), 0) + picker.traverse(render) + if queue.getNumEntries() > 0: + queue.sortEntries() + for index in range(queue.getNumEntries()): + entry = queue.getEntry(index) + if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): + self.movieNode.setZ(entry.getSurfacePoint(self)[2]) + + testPath.setPos(self.getX(), self.getY(), 0) + picker.traverse(render) + if queue.getNumEntries() > 0: + queue.sortEntries() + for index in range(queue.getNumEntries()): + entry = queue.getEntry(index) + if recurseParent(entry.getIntoNode(), 'terrain_DNARoot'): + self.setZ(entry.getSurfacePoint(render)[2] + self.stickUp + 0.1) + self.stickParts() + return Task.done + + taskMgr.doMethodLater(1.0, self.stick2Ground, uniqueName('groundsticker')) + return Task.done + + def stickParts(self): + pass + + def setPlot(self, plot): + self.plot = plot + + def setH(self, h): + DistributedNode.DistributedNode.setH(self, h) + + def getPlot(self): + return self.plot + + def setOwnerIndex(self, index): + self.ownerIndex = index + + def getOwnerIndex(self): + return self.ownerIndex + + def getOwnerId(self): + retval = 0 + estate = base.cr.doFind('DistributedEstate') + if estate and hasattr(estate, 'idList') and estate.idList: + if self.ownerIndex < len(estate.idList): + retval = estate.idList[self.ownerIndex] + return retval + + def canBePicked(self): + retval = True + self.notify.debug('base.localAvatar.doId : %s' % base.localAvatar.doId) + self.notify.debug('self.getOwnerId : %s ' % self.getOwnerId()) + self.notify.debug("statue's DoId : %s " % self.doId) + if not hasattr(base, 'localAvatar') or not base.localAvatar.doId == self.getOwnerId(): + retval = False + return retval + + def allowedToPick(self): + return True + + def unlockPick(self): + return True + + def handleRemove(self): + if not self.canBePicked(): + self.notify.debug("I don't own this item, just returning") + return + base.localAvatar.hideShovelButton() + base.localAvatar.hideWateringCanButton() + self.startInteraction() + self.sendUpdate('removeItem', []) + + def generateToonMoveTrack(self, toon): + node = NodePath('tempNode') + displacement = Vec3(toon.getPos(render) - self.getPos(render)) + displacement.setZ(0) + displacement.normalize() + movieDistance = self.movieNode.getDistance(self.rotateNode) + displacement *= movieDistance + node.reparentTo(render) + node.setPos(displacement + self.getPos(render)) + node.lookAt(self) + heading = PythonUtil.fitDestAngle2Src(toon.getH(render), node.getH(render)) + hpr = toon.getHpr(render) + hpr.setX(heading) + finalX = node.getX(render) + finalY = node.getY(render) + finalZ = node.getZ(render) + node.removeNode() + toonTrack = Sequence(Parallel(ActorInterval(toon, 'walk', loop=True, duration=1), Parallel(LerpPosInterval(toon, 1.0, Point3(finalX, finalY, toon.getZ(render)), fluid=True, bakeInStart=False)), LerpHprInterval(toon, 1.0, hpr=hpr)), Func(toon.loop, 'neutral')) + return toonTrack + + def unprint(self, string): + print string + + def startInteraction(self): + place = base.cr.playGame.getPlace() + if place: + place.detectedGardenPlotUse() + base.localAvatar.setInGardenAction(self) + + def finishInteraction(self): + if hasattr(base.cr.playGame.getPlace(), 'detectedGardenPlotDone'): + base.cr.playGame.getPlace().detectedGardenPlotDone() + self.notify.debug('done interaction') + else: + self.notify.warning('base.cr.playGame.getPlace() does not have detectedGardenPlotDone') + if hasattr(base, 'localAvatar'): + base.localAvatar.handleEndPlantInteraction(self) + + def startCamIval(self, avId): + track = Sequence() + if avId == localAvatar.doId: + track = Sequence(Func(base.localAvatar.disableSmartCameraViews), Func(base.localAvatar.setCameraPosForPetInteraction)) + return track + + def stopCamIval(self, avId): + track = Sequence() + if avId == localAvatar.doId: + track = Sequence(Func(base.localAvatar.unsetCameraPosForPetInteraction), Wait(0.8), Func(base.localAvatar.enableSmartCameraViews)) + return track + + def canBeWatered(self): + return 0 + + def getShovelAction(self): + return None + + def getShovelCommand(self): + return None + + def canBePlanted(self): + return 0 + + def movieDone(self): + self.sendUpdate('movieDone', []) + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_FINISHPLANTING: + self.doFinishPlantingTrack(avId) + elif mode == GardenGlobals.MOVIE_REMOVE: + self.doDigupTrack(avId) + + def finishMovies(self): + if self.movie: + self.movie.finish() + self.movie = None + return + + def doDigupTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.model.setTransparency(1) + self.model.setAlphaScale(1) + shovel = toon.attachShovel() + shovel.hide() + moveTrack = self.generateToonMoveTrack(toon) + digupTrack = self.generateDigupTrack(toon) + self.movie = Sequence(self.startCamIval(avId), moveTrack, Func(shovel.show), digupTrack) + if avId == localAvatar.doId: + self.expectingReplacement = 1 + self.movie.append(Func(self.movieDone)) + self.movie.start() + + def generateDigupTrack(self, toon): + sound = loader.loadSfx('phase_5.5/audio/sfx/burrow.mp3') + sound.setPlayRate(0.5) + pos = self.model.getPos() + pos.setZ(pos[2] - 1) + track = Parallel() + track.append(Sequence(ActorInterval(toon, 'start-dig'), Parallel(ActorInterval(toon, 'loop-dig', loop=1, duration=5.13), Sequence(Wait(0.25), SoundInterval(sound, node=toon, duration=0.55), Wait(0.8), SoundInterval(sound, node=toon, duration=0.55), Wait(1.35), SoundInterval(sound, node=toon, duration=0.55))), ActorInterval(toon, 'start-dig', playRate=-1), LerpFunc(self.model.setAlphaScale, fromData=1, toData=0, duration=1), Func(toon.loop, 'neutral'), Func(toon.detachShovel))) + return track + + def doFinishPlantingTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + self.finishMovies() + self.movie = Sequence() + if avId == localAvatar.doId: + self.startInteraction() + if self.model: + self.model.setTransparency(1) + self.model.setAlphaScale(0) + self.movie.append(LerpFunc(self.model.setAlphaScale, fromData=0, toData=1, duration=3)) + self.movie.append(self.stopCamIval(avId)) + self.movie.append(Func(toon.detachShovel)) + self.movie.append(Func(toon.loop, 'neutral')) + if avId == localAvatar.doId: + self.movie.append(Func(self.finishInteraction)) + self.movie.append(Func(self.movieDone)) + if hasattr(self, 'doResultDialog'): + self.movie.append(Func(self.doResultDialog)) + self.movie.start() + + def interactionDenied(self, avId): + if avId == localAvatar.doId: + self.finishInteraction() diff --git a/toontown/estate/DistributedMailbox.py b/toontown/estate/DistributedMailbox.py new file mode 100644 index 0000000..eedad80 --- /dev/null +++ b/toontown/estate/DistributedMailbox.py @@ -0,0 +1,275 @@ +from direct.distributed import DistributedObject +from toontown.toonbase import ToontownGlobals +import MailboxGlobals +from toontown.catalog import CatalogItem +from toontown.catalog import CatalogItemList +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.catalog import MailboxScreen +from direct.directnotify.DirectNotifyGlobal import * +from direct.distributed.ClockDelta import * +from pandac.PandaModules import * +import random +from direct.interval.IntervalGlobal import SoundInterval +FlagPitchEmpty = -70 +FlagPitchFull = 0 + +class DistributedMailbox(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedMailbox') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.model = None + self.flag = None + self.flagIval = None + self.nameText = None + self.fullIndicator = 0 + self.mailboxGui = None + self.mailboxDialog = None + self.mailboxSphereEvent = None + self.mailboxSphereEnterEvent = None + self.mailboxGuiDoneEvent = 'mailboxGuiDone' + return + + def announceGenerate(self): + DistributedMailbox.notify.debug('announceGenerate') + DistributedObject.DistributedObject.announceGenerate(self) + self.mailboxSphereEvent = self.taskName('mailboxSphere') + self.mailboxSphereEnterEvent = 'enter' + self.mailboxSphereEvent + if self.houseId == base.localAvatar.houseId: + self.accept(self.mailboxSphereEnterEvent, self.__handleEnterSphere) + self.load() + + def load(self): + DistributedMailbox.notify.debug('load') + randomGenerator = random.Random() + randomGenerator.seed(self.houseId) + r = randomGenerator.random() + g = randomGenerator.random() + b = randomGenerator.random() + self.nameColor = (r, + g, + b, + 1) + houseNode = self.cr.playGame.hood.loader.houseNode[self.housePosInd] + estateNode = houseNode.getParent() + zOffset = 0 + if self.housePosInd == 3: + zOffset = -1 + elif self.housePosInd == 2: + zOffset = 0.5 + self.model = loader.loadModel('phase_5.5/models/estate/mailboxHouse') + self.model.reparentTo(estateNode) + self.model.setPos(houseNode, 19, -4, 0 + zOffset) + self.model.setH(houseNode, 90) + self.flag = self.model.find('**/mailbox_flag') + if self.fullIndicator: + self.flag.setP(FlagPitchFull) + else: + self.flag.setP(FlagPitchEmpty) + self.__setupName() + collision = self.model.find('**/mailbox_collision') + collision.setName(self.mailboxSphereEvent) + + def disable(self): + DistributedMailbox.notify.debug('disable') + self.notify.debug('disable') + self.ignoreAll() + if self.flagIval: + self.flagIval.finish() + self.flagIval = None + if self.model: + self.model.removeNode() + self.model = None + if self.nameText: + self.nameText.removeNode() + self.nameText = None + if self.mailboxGui: + self.mailboxGui.hide() + self.mailboxGui.unload() + self.mailboxGui = None + if self.mailboxDialog: + self.mailboxDialog.cleanup() + self.mailboxDialog = None + self.mailboxSphereEvent = None + self.mailboxSphereEnterEvent = None + DistributedObject.DistributedObject.disable(self) + return + + def setHouseId(self, houseId): + DistributedMailbox.notify.debug('setHouseId( houseId=%d )' % houseId) + self.houseId = houseId + + def setHousePos(self, housePosInd): + DistributedMailbox.notify.debug('setHousePos') + self.housePosInd = housePosInd + + def setName(self, name): + DistributedMailbox.notify.debug('setName( name=%s )' % name) + self.name = name + + def setFullIndicator(self, full): + DistributedMailbox.notify.debug('setFullIndicator( full=%s )' % full) + if self.fullIndicator != full: + self.fullIndicator = full + if self.flag: + if self.flagIval: + self.flagIval.pause() + self.flagIval = None + p = FlagPitchEmpty + if self.fullIndicator: + p = FlagPitchFull + self.flagIval = self.flag.hprInterval(0.5, VBase3(0, p, 0), blendType='easeInOut') + self.flagIval.start() + return + + def __handleEnterSphere(self, collEntry): + DistributedMailbox.notify.debug('Entering Mailbox Sphere....') + self.ignore(self.mailboxSphereEnterEvent) + self.cr.playGame.getPlace().detectedMailboxCollision() + self.accept('mailboxAsleep', self.__handleMailboxSleep) + self.sendUpdate('avatarEnter', []) + + def __handleMailboxSleep(self): + DistributedMailbox.notify.debug('Mailbox Sleep') + if self.mailboxGui: + self.mailboxGui.hide() + self.mailboxGui.unload() + self.mailboxGui = None + if self.mailboxDialog: + self.mailboxDialog.cleanup() + self.mailboxDialog = None + self.__handleMailboxDone() + return + + def __handleMailboxDone(self): + DistributedMailbox.notify.debug('Mailbox Done') + self.sendUpdate('avatarExit', []) + self.ignore(self.mailboxGuiDoneEvent) + self.mailboxGui = None + return + + def freeAvatar(self): + DistributedMailbox.notify.debug('freeAvatar') + self.notify.debug('freeAvatar') + curState = base.cr.playGame.getPlace().getState() + self.notify.debug('Estate.getState() == %s' % curState) + if not curState == 'stopped': + base.cr.playGame.getPlace().setState('walk') + self.ignore('mailboxAsleep') + self.accept(self.mailboxSphereEnterEvent, self.__handleEnterSphere) + + def setMovie(self, mode, avId): + isLocalToon = avId == base.localAvatar.doId + if isLocalToon: + DistributedMailbox.notify.debug('setMovie( mode=%d, avId=%d ) called on a local toon' % (mode, avId)) + else: + DistributedMailbox.notify.debug('setMovie( mode=%d, avId=%d ) called on a non-local toon' % (mode, avId)) + if mode == MailboxGlobals.MAILBOX_MOVIE_CLEAR: + DistributedMailbox.notify.debug('setMovie: clear') + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_EXIT: + if random.random() < 0.5: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_close_1.mp3') + else: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_close_2.mp3') + sfxTrack = SoundInterval(sfx, node=self.model) + sfxTrack.start() + DistributedMailbox.notify.debug('setMovie: exit') + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_EMPTY: + DistributedMailbox.notify.debug('setMovie: empty') + if isLocalToon: + self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxEmpty', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxEmpty, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_WAITING: + DistributedMailbox.notify.debug('setMovie: waiting') + if isLocalToon: + self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxWaiting', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxWaiting, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_READY: + DistributedMailbox.notify.debug('setMovie: ready') + if random.random() < 0.5: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_open_1.mp3') + else: + sfx = base.loadSfx('phase_5.5/audio/sfx/mailbox_open_2.mp3') + sfxTrack = SoundInterval(sfx, node=self.model) + sfxTrack.start() + if isLocalToon: + self.mailboxGui = MailboxScreen.MailboxScreen(self, base.localAvatar, self.mailboxGuiDoneEvent) + self.mailboxGui.show() + self.accept(self.mailboxGuiDoneEvent, self.__handleMailboxDone) + return + elif mode == MailboxGlobals.MAILBOX_MOVIE_NOT_OWNER: + DistributedMailbox.notify.debug('setMovie: not owner') + if isLocalToon: + self.mailboxDialog = TTDialog.TTDialog(dialogName='MailboxNotOwner', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedMailboxNotOwner, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + return + else: + DistributedMailbox.notify.warning('unknown mode in setMovie: %s' % mode) + + def acceptItem(self, item, index, callback, optional = -1): + DistributedMailbox.notify.debug('acceptItem') + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item, index]) + self.sendUpdate('acceptItemMessage', [context, + blob, + index, + optional]) + + def acceptInvite(self, item, acceptingIndex, callback, optional = -1): + DistributedMailbox.notify.debug('acceptInvite') + context = self.getCallbackContext(callback, [item, acceptingIndex]) + self.sendUpdate('acceptInviteMessage', [context, item.inviteKey]) + + def acceptItemResponse(self, context, retcode): + DistributedMailbox.notify.debug('acceptItemResponse') + if retcode == ToontownGlobals.P_UserCancelled: + print 'DistributedMailbox User Canceled' + self.doCallbackContext(context, [retcode]) + + def discardItem(self, item, index, callback, optional = -1): + DistributedMailbox.notify.debug('discardItem') + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item, index]) + self.sendUpdate('discardItemMessage', [context, + blob, + index, + optional]) + + def rejectInvite(self, item, acceptingIndex, callback, optional = -1): + DistributedMailbox.notify.debug('rejectInvite') + context = self.getCallbackContext(callback, [item, acceptingIndex]) + self.sendUpdate('rejectInviteMessage', [context, item.inviteKey]) + + def discardItemResponse(self, context, retcode): + DistributedMailbox.notify.debug('discardItemResponse') + self.doCallbackContext(context, [retcode]) + + def __setupName(self): + DistributedMailbox.notify.debug('__setupName') + if self.nameText: + self.nameText.removeNode() + self.nameText = None + nameOrigin = self.model.find('**/nameLocator') + if not nameOrigin.isEmpty(): + text = TextNode('nameText') + text.setTextColor(*self.nameColor) + text.setAlign(TextNode.ACenter) + text.setFont(ToontownGlobals.getToonFont()) + text.setWordwrap(7.5) + text.setText(self.name) + self.nameText = nameOrigin.attachNewNode(text) + self.nameText.setH(90) + self.nameText.setScale(0.2) + return + + def __clearDialog(self, event): + DistributedMailbox.notify.debug('__clearDialog') + self.mailboxDialog.cleanup() + self.mailboxDialog = None + self.freeAvatar() + return + + def sendInviteReadButNotReplied(self, inviteKey): + self.sendUpdate('markInviteReadButNotReplied', [inviteKey]) diff --git a/toontown/estate/DistributedPhone.py b/toontown/estate/DistributedPhone.py new file mode 100644 index 0000000..bd04793 --- /dev/null +++ b/toontown/estate/DistributedPhone.py @@ -0,0 +1,379 @@ +from toontown.toonbase import ToontownGlobals +import PhoneGlobals +from toontown.catalog import CatalogScreen +from toontown.catalog import CatalogItem +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +import DistributedHouseInterior +from direct.actor import Actor +import DistributedFurnitureItem +from direct.distributed import ClockDelta +from direct.showbase import PythonUtil +from direct.showutil import Rope +from direct.directnotify.DirectNotifyGlobal import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import string +from toontown.quest import Quests +from direct.task import Task + +class DistributedPhone(DistributedFurnitureItem.DistributedFurnitureItem): + notify = directNotify.newCategory('DistributedPhone') + movieDelay = 0.5 + + def __init__(self, cr): + DistributedFurnitureItem.DistributedFurnitureItem.__init__(self, cr) + self.lastAvId = 0 + self.hasLocalAvatar = 0 + self.lastTime = 0 + self.initialScale = None + self.usedInitialScale = 0 + self.toonScale = None + self.phoneGui = None + self.phoneDialog = None + self.model = None + self.cord = None + self.receiverGeom = None + self.receiverJoint = None + self.phoneSphereEvent = 'phoneSphere' + self.phoneSphereEnterEvent = 'enter' + self.phoneSphereEvent + self.phoneGuiDoneEvent = 'phoneGuiDone' + self.pickupMovieDoneEvent = 'phonePickupDone' + self.numHouseItems = None + self.interval = None + self.intervalAvatar = None + self.phoneInUse = 0 + self.origToonHpr = None + return + + def announceGenerate(self): + self.notify.debug('announceGenerate') + DistributedFurnitureItem.DistributedFurnitureItem.announceGenerate(self) + self.accept(self.phoneSphereEnterEvent, self.__handleEnterSphere) + self.load() + taskMgr.doMethodLater(6, self.ringIfHasPhoneQuest, self.uniqueName('ringDoLater')) + + def loadModel(self): + self.model = Actor.Actor('phase_5.5/models/estate/prop_phone-mod', {'SS_phoneOut': 'phase_5.5/models/estate/prop_phone-SS_phoneOut', + 'SS_takePhone': 'phase_5.5/models/estate/prop_phone-SS_takePhone', + 'SS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SS_phoneNeutral', + 'SS_phoneBack': 'phase_5.5/models/estate/prop_phone-SS_phoneBack', + 'SM_phoneOut': 'phase_5.5/models/estate/prop_phone-SM_phoneOut', + 'SM_takePhone': 'phase_5.5/models/estate/prop_phone-SM_takePhone', + 'SM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SM_phoneNeutral', + 'SM_phoneBack': 'phase_5.5/models/estate/prop_phone-SM_phoneBack', + 'SL_phoneOut': 'phase_5.5/models/estate/prop_phone-SL_phoneOut', + 'SL_takePhone': 'phase_5.5/models/estate/prop_phone-SL_takePhone', + 'SL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-SL_phoneNeutral', + 'SL_phoneBack': 'phase_5.5/models/estate/prop_phone-SL_phoneBack', + 'MS_phoneOut': 'phase_5.5/models/estate/prop_phone-MS_phoneOut', + 'MS_takePhone': 'phase_5.5/models/estate/prop_phone-MS_takePhone', + 'MS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MS_phoneNeutral', + 'MS_phoneBack': 'phase_5.5/models/estate/prop_phone-MS_phoneBack', + 'MM_phoneOut': 'phase_5.5/models/estate/prop_phone-MM_phoneOut', + 'MM_takePhone': 'phase_5.5/models/estate/prop_phone-MM_takePhone', + 'MM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-MM_phoneNeutral', + 'MM_phoneBack': 'phase_5.5/models/estate/prop_phone-MM_phoneBack', + 'ML_phoneOut': 'phase_5.5/models/estate/prop_phone-ML_phoneOut', + 'ML_takePhone': 'phase_5.5/models/estate/prop_phone-ML_takePhone', + 'ML_phoneNeutral': 'phase_5.5/models/estate/prop_phone-ML_phoneNeutral', + 'ML_phoneBack': 'phase_5.5/models/estate/prop_phone-ML_phoneBack', + 'LS_phoneOut': 'phase_5.5/models/estate/prop_phone-LS_phoneOut', + 'LS_takePhone': 'phase_5.5/models/estate/prop_phone-LS_takePhone', + 'LS_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LS_phoneNeutral', + 'LS_phoneBack': 'phase_5.5/models/estate/prop_phone-LS_phoneBack', + 'LM_phoneOut': 'phase_5.5/models/estate/prop_phone-LM_phoneOut', + 'LM_takePhone': 'phase_5.5/models/estate/prop_phone-LM_takePhone', + 'LM_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LM_phoneNeutral', + 'LM_phoneBack': 'phase_5.5/models/estate/prop_phone-LM_phoneBack', + 'LL_phoneOut': 'phase_5.5/models/estate/prop_phone-LL_phoneOut', + 'LL_takePhone': 'phase_5.5/models/estate/prop_phone-LL_takePhone', + 'LL_phoneNeutral': 'phase_5.5/models/estate/prop_phone-LL_phoneNeutral', + 'LL_phoneBack': 'phase_5.5/models/estate/prop_phone-LL_phoneBack'}) + self.model.pose('SS_phoneOut', 0) + self.receiverJoint = self.model.find('**/joint_receiver') + self.receiverGeom = self.receiverJoint.getChild(0) + mount = loader.loadModel('phase_5.5/models/estate/phoneMount-mod') + mount.setTransparency(0, 1) + self.model.reparentTo(mount) + self.ringSfx = loader.loadSfx('phase_3.5/audio/sfx/telephone_ring.mp3') + self.handleSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_handle2.mp3') + self.hangUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_hang_up.mp3') + self.pickUpSfx = loader.loadSfx('phase_5.5/audio/sfx/telephone_pickup1.mp3') + if self.initialScale: + mount.setScale(*self.initialScale) + self.usedInitialScale = 1 + phoneSphere = CollisionSphere(0, -0.66, 0, 0.2) + phoneSphere.setTangible(0) + phoneSphereNode = CollisionNode(self.phoneSphereEvent) + phoneSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + phoneSphereNode.addSolid(phoneSphere) + mount.attachNewNode(phoneSphereNode) + if not self.model.find('**/CurveNode7').isEmpty(): + self.setupCord() + return mount + + def setupCamera(self, mode): + camera.wrtReparentTo(render) + if mode == PhoneGlobals.PHONE_MOVIE_PICKUP: + camera.lerpPosHpr(4, -4, base.localAvatar.getHeight() - 0.5, 35, -8, 0, 1, other=base.localAvatar, blendType='easeOut', task=self.uniqueName('lerpCamera')) + + def setupCord(self): + if self.cord: + self.cord.detachNode() + self.cord = None + self.cord = Rope.Rope(self.uniqueName('phoneCord')) + self.cord.setColor(0, 0, 0, 1) + self.cord.setup(4, ((self.receiverGeom, (0, 0, 0)), + (self.model.find('**/joint_curveNode1'), (0, 0, 0)), + (self.model.find('**/joint_curveNode2'), (0, 0, 0)), + (self.model.find('**/joint_curveNode3'), (0, 0, 0)), + (self.model.find('**/joint_curveNode4'), (0, 0, 0)), + (self.model.find('**/joint_curveNode5'), (0, 0, 0)), + (self.model.find('**/joint_curveNode6'), (0, 0, 0)), + (self.model.find('**/CurveNode7'), (0, 0, 0)))) + self.cord.reparentTo(self.model) + self.cord.node().setBounds(BoundingSphere(Point3(-1.0, -3.2, 2.6), 2.0)) + return + + def disable(self): + self.notify.debug('disable') + taskMgr.remove(self.uniqueName('ringDoLater')) + self.clearInterval() + if self.phoneGui: + self.phoneGui.hide() + self.phoneGui.unload() + self.phoneGui = None + if self.phoneDialog: + self.phoneDialog.cleanup() + self.phoneDialog = None + self.__receiverToPhone() + if self.hasLocalAvatar: + self.freeAvatar() + self.ignoreAll() + DistributedFurnitureItem.DistributedFurnitureItem.disable(self) + return + + def delete(self): + self.notify.debug('delete') + self.model.cleanup() + DistributedFurnitureItem.DistributedFurnitureItem.delete(self) + + def setInitialScale(self, sx, sy, sz): + self.initialScale = (sx, sy, sz) + if not self.usedInitialScale and self.model: + self.setScale(*self.initialScale) + self.usedInitialScale = 1 + + def __handleEnterSphere(self, collEntry): + if self.smoothStarted: + return + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameTime() <= self.lastTime + 0.5: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + if self.hasLocalAvatar: + self.freeAvatar() + base.localAvatar.lookupPetDNA() + self.notify.debug('Entering Phone Sphere....') + taskMgr.remove(self.uniqueName('ringDoLater')) + self.ignore(self.phoneSphereEnterEvent) + self.cr.playGame.getPlace().detectedPhoneCollision() + self.hasLocalAvatar = 1 + self.sendUpdate('avatarEnter', []) + + def __handlePhoneDone(self): + self.sendUpdate('avatarExit', []) + self.ignore(self.phoneGuiDoneEvent) + self.phoneGui = None + return + + def freeAvatar(self): + if self.hasLocalAvatar: + base.localAvatar.speed = 0 + taskMgr.remove(self.uniqueName('lerpCamera')) + base.localAvatar.posCamera(0, 0) + if base.cr.playGame.place != None: + base.cr.playGame.getPlace().setState('walk') + self.hasLocalAvatar = 0 + self.ignore(self.pickupMovieDoneEvent) + self.accept(self.phoneSphereEnterEvent, self.__handleEnterSphere) + self.lastTime = globalClock.getFrameTime() + return + + def setLimits(self, numHouseItems): + self.numHouseItems = numHouseItems + + def setMovie(self, mode, avId, timestamp): + elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp, bits=32) + elapsed = max(elapsed - self.movieDelay, 0) + self.ignore(self.pickupMovieDoneEvent) + if avId != 0: + self.lastAvId = avId + self.lastTime = globalClock.getFrameTime() + isLocalToon = avId == base.localAvatar.doId + avatar = self.cr.doId2do.get(avId) + self.notify.debug('setMovie: %s %s %s' % (mode, avId, isLocalToon)) + if mode == PhoneGlobals.PHONE_MOVIE_CLEAR: + self.notify.debug('setMovie: clear') + self.numHouseItems = None + if self.phoneInUse: + self.clearInterval() + self.phoneInUse = 0 + elif mode == PhoneGlobals.PHONE_MOVIE_EMPTY: + self.notify.debug('setMovie: empty') + if isLocalToon: + self.phoneDialog = TTDialog.TTDialog(dialogName='PhoneEmpty', style=TTDialog.Acknowledge, text=TTLocalizer.DistributedPhoneEmpty, text_wordwrap=15, fadeScreen=1, command=self.__clearDialog) + self.numHouseItems = None + self.phoneInUse = 0 + elif mode == PhoneGlobals.PHONE_MOVIE_PICKUP: + self.notify.debug('setMovie: gui') + if avatar: + interval = self.takePhoneInterval(avatar) + if isLocalToon: + self.setupCamera(mode) + interval.setDoneEvent(self.pickupMovieDoneEvent) + self.acceptOnce(self.pickupMovieDoneEvent, self.__showPhoneGui) + self.playInterval(interval, elapsed, avatar) + self.phoneInUse = 1 + elif mode == PhoneGlobals.PHONE_MOVIE_HANGUP: + self.notify.debug('setMovie: gui') + if avatar: + interval = self.replacePhoneInterval(avatar) + self.playInterval(interval, elapsed, avatar) + self.numHouseItems = None + self.phoneInUse = 0 + else: + self.notify.warning('unknown mode in setMovie: %s' % mode) + return + + def __showPhoneGui(self): + if self.toonScale: + self.sendUpdate('setNewScale', [self.toonScale[0], self.toonScale[1], self.toonScale[2]]) + self.phoneGui = CatalogScreen.CatalogScreen(phone=self, doneEvent=self.phoneGuiDoneEvent) + self.phoneGui.show() + self.accept(self.phoneGuiDoneEvent, self.__handlePhoneDone) + self.accept('phoneAsleep', self.__handlePhoneAsleep) + + def __handlePhoneAsleep(self): + self.ignore('phoneAsleep') + if self.phoneGui: + self.phoneGui.unload() + self.__handlePhoneDone() + + def requestPurchase(self, item, callback, optional = -1): + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item]) + self.sendUpdate('requestPurchaseMessage', [context, blob, optional]) + + def requestGiftPurchase(self, item, targetDoID, callback, optional = -1): + print 'in the client phone' + blob = item.getBlob(store=CatalogItem.Customization) + context = self.getCallbackContext(callback, [item]) + self.sendUpdate('requestGiftPurchaseMessage', [context, + targetDoID, + blob, + optional]) + + def requestPurchaseResponse(self, context, retcode): + self.doCallbackContext(context, [retcode]) + + def requestGiftPurchaseResponse(self, context, retcode): + self.doCallbackContext(context, [retcode]) + + def __clearDialog(self, event): + self.phoneDialog.cleanup() + self.phoneDialog = None + self.freeAvatar() + return + + def takePhoneInterval(self, toon): + torso = TextEncoder.upper(toon.style.torso[0]) + legs = TextEncoder.upper(toon.style.legs[0]) + phoneOutAnim = '%s%s_phoneOut' % (torso, legs) + takePhoneAnim = '%s%s_takePhone' % (torso, legs) + phoneNeutralAnim = '%s%s_phoneNeutral' % (torso, legs) + self.toonScale = toon.getGeomNode().getChild(0).getScale(self.getParent()) + walkTime = 1.0 + scaleTime = 1.0 + origScale = self.getScale() + origToonPos = toon.getPos() + origToonHpr = toon.getHpr() + self.origToonHpr = origToonHpr + self.setScale(self.toonScale) + toon.setPosHpr(self, 0, -4.5, 0, 0, 0, 0) + destToonPos = toon.getPos() + destToonHpr = toon.getHpr() + destToonHpr = VBase3(PythonUtil.fitSrcAngle2Dest(destToonHpr[0], origToonHpr[0]), destToonHpr[1], destToonHpr[2]) + self.setScale(origScale) + toon.setPos(origToonPos) + toon.setHpr(origToonHpr) + walkToPhone = Sequence(Func(toon.stopSmooth), Func(toon.loop, 'walk'), Func(base.playSfx, base.localAvatar.soundWalk), toon.posHprInterval(walkTime, destToonPos, destToonHpr, blendType='easeInOut'), Func(toon.loop, 'neutral'), Func(toon.startSmooth)) + interval = Sequence(Parallel(walkToPhone, ActorInterval(self.model, phoneOutAnim), self.scaleInterval(scaleTime, self.toonScale, blendType='easeInOut')), Parallel(ActorInterval(self.model, takePhoneAnim), ActorInterval(toon, 'takePhone'), Sequence(Wait(0.625), Func(base.playSfx, self.pickUpSfx), Func(self.__receiverToHand, toon), Wait(1), Func(base.playSfx, self.handleSfx))), Func(self.model.loop, phoneNeutralAnim), Func(toon.loop, 'phoneNeutral'), Func(base.playSfx, self.ringSfx)) + return interval + + def replacePhoneInterval(self, toon): + torso = TextEncoder.upper(toon.style.torso[0]) + legs = TextEncoder.upper(toon.style.legs[0]) + phoneBackAnim = '%s%s_phoneBack' % (torso, legs) + scaleTime = 1.0 + interval = Sequence(Parallel(ActorInterval(self.model, phoneBackAnim), ActorInterval(toon, 'phoneBack'), Sequence(Wait(1.0), Func(self.__receiverToPhone), Func(base.playSfx, self.hangUpSfx))), self.scaleInterval(scaleTime, localAvatar.getGeomNode().getScale()[2], blendType='easeInOut'), Func(toon.loop, 'neutral')) + if self.origToonHpr: + interval.append(Func(toon.setHpr, self.origToonHpr)) + self.origToonHpr = None + if toon == base.localAvatar: + interval.append(Func(self.freeAvatar)) + return interval + + def __receiverToHand(self, toon): + self.receiverGeom.reparentTo(toon.leftHand) + self.receiverGeom.setPosHpr(0.0906813, 0.380375, 0.1, 32.41, 70.68, 137.04) + + def __receiverToPhone(self): + self.receiverGeom.reparentTo(self.receiverJoint) + self.receiverGeom.setPosHpr(0, 0, 0, 0, 0, 0) + + def playInterval(self, interval, elapsed, avatar): + if self.interval != None: + self.interval.finish() + self.interval = None + self.interval = interval + self.interval.start(elapsed) + if self.intervalAvatar != avatar: + if self.intervalAvatar: + self.ignore(self.intervalAvatar.uniqueName('disable')) + if avatar: + self.accept(avatar.uniqueName('disable'), self.clearInterval) + self.intervalAvatar = avatar + return + + def clearInterval(self): + if self.interval != None: + self.interval.finish() + self.interval = None + if self.intervalAvatar: + self.ignore(self.intervalAvatar.uniqueName('disable')) + self.intervalAvatar = None + self.__receiverToPhone() + self.model.pose('SS_phoneOut', 0) + self.phoneInUse = 0 + return + + def ringIfHasPhoneQuest(self, task): + if Quests.avatarHasPhoneQuest(base.localAvatar) and not Quests.avatarHasCompletedPhoneQuest(base.localAvatar): + self.ring() + return Task.done + + def ring(self): + if self.phoneInUse: + return 0 + phone = self.find('**/prop_phone') + r = 2.0 + w = 0.05 + shakeOnce = Sequence(Func(phone.setR, r), Wait(w), Func(phone.setR, -r), Wait(w)) + shakeSeq = Sequence() + for i in range(16): + shakeSeq.append(shakeOnce) + + ringIval = Parallel(Func(base.playSfx, self.ringSfx), shakeSeq, Func(phone.setR, 0)) + self.playInterval(ringIval, 0.0, None) + return diff --git a/toontown/estate/DistributedPlantBase.py b/toontown/estate/DistributedPlantBase.py new file mode 100644 index 0000000..fc9e487 --- /dev/null +++ b/toontown/estate/DistributedPlantBase.py @@ -0,0 +1,223 @@ +import DistributedLawnDecor +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +import GardenGlobals +from toontown.toonbase import TTLocalizer + +class DistributedPlantBase(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPlantBase') + + def __init__(self, cr): + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.model = None + self.growthLevel = -1 + self.waterTrackDict = {} + return + + def delete(self): + self.notify.debug('delete') + for waterTrack in self.waterTrackDict.values(): + if waterTrack: + waterTrack.finish() + + self.waterTrackDict = None + DistributedLawnDecor.DistributedLawnDecor.delete(self) + return + + def disable(self): + self.notify.debug('disable') + DistributedLawnDecor.DistributedLawnDecor.disable(self) + + def loadModel(self): + if hasattr(self, 'rotateNode') and self.rotateNode: + self.rotateNode.removeNode() + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = None + modelName = self.getModelName() + self.model = loader.loadModel(modelName) + self.model.reparentTo(self.rotateNode) + self.stick2Ground() + return + + def setupShadow(self): + DistributedLawnDecor.DistributedLawnDecor.setupShadow(self) + self.adjustWaterIndicator() + + def setTypeIndex(self, typeIndex): + self.typeIndex = typeIndex + self.attributes = GardenGlobals.PlantAttributes[typeIndex] + self.name = self.attributes['name'] + self.plantType = self.attributes['plantType'] + self.growthThresholds = self.attributes['growthThresholds'] + self.maxWaterLevel = self.attributes['maxWaterLevel'] + self.minWaterLevel = self.attributes['minWaterLevel'] + self.seedlingModel = self.attributes['seedlingModel'] + self.establishedModel = self.attributes['establishedModel'] + self.fullGrownModel = self.attributes['fullGrownModel'] + + def getTypeIndex(self): + return self.typeIndex + + def setWaterLevel(self, waterLevel): + self.waterLevel = waterLevel + + def getWaterLevel(self): + return self.waterLevel + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + if self.model: + self.model.setScale(growthLevel) + + def getGrowthLevel(self): + return self.growthLevel + + def getShovelAction(self): + if self.isFruiting() and not self.isWilted() and self.canBeHarvested(): + return TTLocalizer.GardeningPick + else: + return TTLocalizer.GardeningRemove + + def getShovelCommand(self): + return self.handlePicking + + def canBeHarvested(self): + return True + + def handleEnterPlot(self, colEntry = None): + dist = self.getDistance(localAvatar) + self.accept('water-plant', self.__handleWatering) + base.localAvatar.addShovelRelatedDoId(self.doId) + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + base.localAvatar.removeShovelRelatedDoId(self.doId) + self.ignore('water-plant') + + def handleWatering(self): + self.startInteraction() + self.sendUpdate('waterPlant') + + def __handleWatering(self, plantToWaterId): + if plantToWaterId == self.doId: + self.sendUpdate('waterPlant') + else: + self.notify.debug('not sending water plant') + + def isFruiting(self): + retval = self.growthLevel >= self.growthThresholds[2] + return retval + + def isGTEFruiting(self): + retval = self.growthLevel >= self.growthThresholds[2] + return retval + + def isFullGrown(self): + if self.growthLevel >= self.growthThresholds[2]: + return False + elif self.growthLevel >= self.growthThresholds[1]: + return True + return False + + def isGTEFullGrown(self): + retval = self.growthLevel >= self.growthThresholds[1] + return retval + + def isEstablished(self): + if self.growthLevel >= self.growthThresholds[2]: + return False + elif self.growthLevel >= self.growthThresholds[1]: + return False + elif self.growthLevel >= self.growthThresholds[0]: + return True + return False + + def isGTEEstablished(self): + if self.growthLevel >= self.growthThresholds[0]: + return True + return False + + def isSeedling(self): + if self.growthLevel >= self.growthThresholds[2]: + return False + elif self.growthLevel >= self.growthThresholds[1]: + return False + elif self.growthLevel >= self.growthThresholds[0]: + return False + elif self.growthLevel < self.growthThresholds[0]: + return True + return False + + def isGTESeedling(self): + return True + + def isWilted(self): + return self.waterLevel < 0 + + def getModelName(self): + if self.growthLevel >= self.growthThresholds[1]: + modelName = self.fullGrownModel + elif self.growthLevel >= self.growthThresholds[1]: + modelName = self.fullGrownModel + elif self.growthLevel >= self.growthThresholds[0]: + modelName = self.establishedModel + else: + modelName = self.seedlingModel + return modelName + + def setMovie(self, mode, avId): + if mode == GardenGlobals.MOVIE_WATER: + self.doWaterTrack(avId) + elif mode == GardenGlobals.MOVIE_FINISHPLANTING: + self.doFinishPlantingTrack(avId) + elif mode == GardenGlobals.MOVIE_REMOVE: + self.doDigupTrack(avId) + + def doWaterTrack(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return + can = toon.getWateringCanModel() + can.hide() + can.reparentTo(toon.rightHand) + track = Sequence() + track.append(self.startCamIval(avId)) + track.append(self.generateToonMoveTrack(toon)) + track.append(Func(can.show)) + track.append(self.generateWaterTrack(toon)) + track.append(Func(can.removeNode)) + track.append(self.stopCamIval(avId)) + if avId == localAvatar.doId: + track.append(Func(self.sendUpdate, 'waterPlantDone')) + track.append(Func(self.finishInteraction)) + track.start() + self.waterTrackDict[avId] = track + + def generateWaterTrack(self, toon): + sound = loader.loadSfx('phase_5/audio/sfx/firehose_spray.mp3') + sound.setPlayRate(0.75) + waterTrack = Parallel() + waterTrack.append(Sequence(Parallel(ActorInterval(toon, 'water'), SoundInterval(sound, node=toon, volume=0.5)), Func(toon.loop, 'neutral'))) + if hasattr(self, 'dropShadow') and self.dropShadow: + newColor = self.dropShadow.getColor() + alpha = min(1.0, newColor.getW() + 1 / 5.0) + newColor.setW(alpha) + waterTrack.append(LerpColorInterval(self.dropShadow, 2.1, newColor)) + return waterTrack + + def adjustWaterIndicator(self): + if self.model: + color = self.waterLevel / 5.0 + 1 / 5.0 + self.notify.debug('%s %s' % (self.waterLevel, color)) + if color < 0.2: + color = 0.2 + if hasattr(self, 'dropShadow') and self.dropShadow: + self.dropShadow.setColor(0.0, 0.0, 0.0, color) + + def canBeWatered(self): + return 1 + + def finishInteraction(self): + DistributedLawnDecor.DistributedLawnDecor.finishInteraction(self) + base.localAvatar.handleEndPlantInteraction(self) diff --git a/toontown/estate/DistributedStatuary.py b/toontown/estate/DistributedStatuary.py new file mode 100644 index 0000000..4af314d --- /dev/null +++ b/toontown/estate/DistributedStatuary.py @@ -0,0 +1,135 @@ +import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import NodePath +from pandac.PandaModules import Point3 + +class DistributedStatuary(DistributedLawnDecor.DistributedLawnDecor): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedStatuary') + DistributedLawnDecor.DistributedLawnDecor.__init__(self, cr) + self.confirmDialog = None + self.resultDialog = None + return + + def loadModel(self): + self.rotateNode = self.plantPath.attachNewNode('rotate') + self.model = loader.loadModel(self.modelPath) + colNode = self.model.find('**/+CollisionNode') + if not colNode.isEmpty(): + score, multiplier = ToontownGlobals.PinballScoring[ToontownGlobals.PinballStatuary] + if self.pinballScore: + score = self.pinballScore[0] + multiplier = self.pinballScore[1] + scoreNodePath = NodePath('statuary-%d-%d' % (score, multiplier)) + colNode.setName('statuaryCol') + scoreNodePath.reparentTo(colNode.getParent()) + colNode.reparentTo(scoreNodePath) + self.model.setScale(self.worldScale) + self.model.reparentTo(self.rotateNode) + attrib = GardenGlobals.PlantAttributes[self.typeIndex] + self.stick2Ground() + + def setTypeIndex(self, typeIndex): + self.typeIndex = typeIndex + self.name = GardenGlobals.PlantAttributes[typeIndex]['name'] + self.plantType = GardenGlobals.PlantAttributes[typeIndex]['plantType'] + self.modelPath = GardenGlobals.PlantAttributes[typeIndex]['model'] + self.pinballScore = None + if GardenGlobals.PlantAttributes[typeIndex].has_key('pinballScore'): + self.pinballScore = GardenGlobals.PlantAttributes[typeIndex]['pinballScore'] + self.worldScale = 1.0 + if GardenGlobals.PlantAttributes[typeIndex].has_key('worldScale'): + self.worldScale = GardenGlobals.PlantAttributes[typeIndex]['worldScale'] + return + + def getTypeIndex(self): + return self.typeIndex + + def setWaterLevel(self, waterLevel): + self.waterLevel = waterLevel + + def getWaterLevel(self): + return self.waterLevel + + def setGrowthLevel(self, growthLevel): + self.growthLevel = growthLevel + + def getGrowthLevel(self): + return self.growthLevel + + def setupCollision(self): + DistributedLawnDecor.DistributedLawnDecor.setupCollision(self) + minPt = Point3(0, 0, 0) + maxPt = Point3(0, 0, 0) + self.model.calcTightBounds(minPt, maxPt) + self.notify.debug('max=%s min=%s' % (maxPt, minPt)) + xDiff = maxPt[0] - minPt[0] + yDiff = maxPt[1] - minPt[1] + radius = (xDiff * xDiff + yDiff * yDiff) ** 0.5 + radius /= 3 + self.notify.debug('xDiff=%s yDiff=%s radius = %s' % (xDiff, yDiff, radius)) + self.colSphereNode.setScale(radius) + + def getShovelCommand(self): + return self.handlePicking + + def getShovelAction(self): + return TTLocalizer.GardeningRemove + + def handleEnterPlot(self, colEntry = None): + if self.canBePicked(): + self.notify.debug('entering if') + base.localAvatar.addShovelRelatedDoId(self.doId) + base.localAvatar.setShovelAbility(TTLocalizer.GardeningRemove) + else: + self.notify.debug('entering else') + + def handlePicking(self): + fullName = self.name + messenger.send('wakeup') + self.confirmDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.ConfirmRemoveStatuary % {'item': fullName}, command=self.confirmCallback) + self.confirmDialog.show() + base.cr.playGame.getPlace().detectedGardenPlotUse() + + def confirmCallback(self, value): + self.notify.debug('value=%d' % value) + if self.confirmDialog: + self.confirmDialog.destroy() + self.confirmDialog = None + if value > 0: + self.doPicking() + else: + base.cr.playGame.getPlace().detectedGardenPlotDone() + return + + def doPicking(self): + if not self.canBePicked(): + self.notify.debug("I don't own this flower, just returning") + return + self.handleRemove() + + def handleExitPlot(self, entry = None): + DistributedLawnDecor.DistributedLawnDecor.handleExitPlot(self, entry) + base.localAvatar.removeShovelRelatedDoId(self.doId) + + def doResultDialog(self): + self.startInteraction() + itemName = GardenGlobals.PlantAttributes[self.typeIndex]['name'] + stringToShow = TTLocalizer.getResultPlantedSomethingSentence(itemName) + self.resultDialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=stringToShow, command=self.resultsCallback) + + def resultsCallback(self, value): + self.notify.debug('value=%d' % value) + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.finishInteraction() + return diff --git a/toontown/estate/DistributedTarget.py b/toontown/estate/DistributedTarget.py new file mode 100644 index 0000000..ec927de --- /dev/null +++ b/toontown/estate/DistributedTarget.py @@ -0,0 +1,245 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import ToontownTimer +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class DistributedTarget(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTarget') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.geom = None + self.numConsecutiveHits = 0 + self.enabled = 0 + self.score = 0 + self.hitTime = 0 + self.targetBounceTrack = None + self.pinballInfo = {} + self.pinballHiScore = 0 + self.pinballHiScorer = '' + self.onscreenMessage = None + self.fadeTrack = None + return + + def disable(self): + self.ignoreAll() + DistributedObject.DistributedObject.disable(self) + if self.targetBounceTrack: + self.targetBounceTrack.finish() + self.targetBounceTrack = None + if self.fadeTrack: + self.fadeTrack.pause() + self.fadeTrack = None + self.__clearOnscreenMessage() + return + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.load() + + def load(self): + self.timer = ToontownTimer.ToontownTimer() + self.timer.setPos(1.1, 0, -0.15) + self.timer.hide() + self.geom = loader.loadModel('phase_5.5/models/estate/target') + self.geom.reparentTo(base.cr.playGame.hood.loader.geom) + self.geom.setPos(0, 0, 40) + self.geom.setScale(3) + self.geom.stash() + self.hitSound = base.loadSfx('phase_4/audio/sfx/MG_Tag_A.mp3') + self.rewardSound = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.wav') + self.scoreText = TextNode('scoreText') + self.scoreText.setTextColor(1, 0, 0, 1) + self.scoreText.setAlign(self.scoreText.ACenter) + self.scoreText.setFont(getSignFont()) + self.scoreText.setText('0') + self.scoreNode = self.timer.attachNewNode(self.scoreText) + self.scoreNode.setPos(0, 0, 0.35) + self.scoreNode.setScale(0.25) + self.curPinballScoreText = TextNode('pinballScoreText') + self.curPinballScoreText.setTextColor(1, 0, 0, 1) + self.curPinballScoreText.setAlign(self.scoreText.ACenter) + self.curPinballScoreText.setFont(getSignFont()) + self.curPinballScoreText.setText('') + self.curPinballScoreNode = render.attachNewNode(self.curPinballScoreText) + self.curPinballScoreNode.setPos(0.5, 0.5, 0.3) + self.curPinballScoreNode.setScale(0.25) + colSphere = CollisionSphere(0, 0, 0, 3.5) + colSphere.setTangible(0) + colNode = CollisionNode('targetSphere') + colNode.addSolid(colSphere) + colSphereNode = self.geom.attachNewNode(colNode) + self.accept('hitTarget', self.handleHitTarget) + self.accept('missedTarget', self.handleMissedTarget) + self.accept('entertargetSphere', self.handleEnterTarget) + + def delete(self): + self.ignoreAll() + self.scoreNode.removeNode() + del self.scoreNode + self.curPinballScoreNode.removeNode() + del self.curPinballScoreNode + self.geom.removeNode() + del self.geom + self.timer.destroy() + del self.timer + del self.rewardSound + del self.hitSound + DistributedObject.DistributedObject.delete(self) + + def setState(self, enabled, score, time): + if self.enabled != enabled: + if self.fadeTrack: + self.fadeTrack.pause() + if enabled: + self.fadeTrack = Sequence(Func(base.localAvatar.setSystemMessage, 0, TTLocalizer.EstateTargetGameStart), Func(self.geom.unstash), Func(self.geom.lerpColorScale, Vec4(1.0, 1.0, 1.0, 0.0), Vec4(1.0, 1.0, 1.0, 1.0), 1.0), Wait(1), Func(base.localAvatar.setSystemMessage, 0, TTLocalizer.EstateTargetGameInst)) + else: + self.fadeTrack = Sequence(Func(self.geom.lerpColorScale, Vec4(1.0, 1.0, 1.0, 1.0), Vec4(1.0, 1.0, 1.0, 0.0), 1.0), Func(self.geom.stash), Func(self.hideTimer), Func(base.localAvatar.setSystemMessage, 0, TTLocalizer.EstateTargetGameEnd)) + self.fadeTrack.start() + self.enabled = enabled + if score != self.score: + self.setLevel(score) + if time != self.hitTime: + self.setTimer(time) + + def setReward(self, reward): + base.playSfx(self.rewardSound) + + def handleEnterTarget(self, collEntry): + self.handleHitTarget() + + def handleHitTarget(self, avId = None, vel = None): + if not avId: + avId = base.localAvatar.doId + if self.enabled: + self.sendUpdate('setResult', [avId]) + if vel: + if self.targetBounceTrack: + self.targetBounceTrack.finish() + pos = self.geom.getPos() + dist = Vec3(vel) + dist.normalize() + newPos = pos - dist * 1.5 + springPos = pos + dist + self.notify.debug('reaction distance = %s,%s,%s' % (vel[0], vel[1], vel[2])) + self.targetBounceTrack = Sequence(LerpPosInterval(self.geom, duration=0.1, pos=newPos, blendType='easeOut'), LerpPosInterval(self.geom, duration=0.25, pos=springPos, blendType='easeOut'), LerpPosInterval(self.geom, duration=0.2, pos=pos, blendType='easeOut')) + self.targetBounceTrack.start() + + def handleMissedTarget(self): + if self.enabled: + self.sendUpdate('setResult', [0]) + + def handleHitCloud(self): + if self.enabled: + self.sendUpdate('setBonus', [0.5]) + + def setLevel(self, level): + self.notify.debug('setLevel(%s)' % level) + self.score = level + base.playSfx(self.hitSound) + self.scoreText.setText('+' + str(int(self.score))) + + def setTimer(self, time): + self.hitTime = time + self.notify.debug('updateTimer(%s)' % self.enabled) + if self.enabled: + self.showTimer() + self.notify.debug('hitTime = %s' % self.hitTime) + self.timer.setTime(self.hitTime) + self.timer.countdown(self.hitTime) + + def showTimer(self): + if base.localAvatar.animFSM.getCurrentState().getName() != 'ReadBook': + base.setCellsAvailable([base.rightCells[0]], 0) + self.timer.show() + + def hideTimer(self): + self.timer.hide() + base.setCellsAvailable([base.rightCells[0]], 1) + + def setPosition(self, x, y, z): + self.geom.setPos(x, y, z) + + def showScore(self): + scoreName = self.pinballHiScorer[0:12] + if scoreName: + if len(self.pinballHiScorer) > 12: + scoreName += TTLocalizer.PinballHiScoreAbbrev + titleText = TTLocalizer.PinballHiScore % scoreName + scoreText = TTLocalizer.PinballScoreHolder % self.pinballHiScore + pinballEntry = self.pinballInfo.get(base.localAvatar.doId) + if pinballEntry: + titleText += TTLocalizer.PinballYourBestScore + scoreText += TTLocalizer.PinballScoreHolder % pinballEntry[0] + titleText += TTLocalizer.PinballScore % (pinballEntry[1], pinballEntry[2]) + scoreText += TTLocalizer.PinballScoreHolder % (pinballEntry[1] * pinballEntry[2]) + self.__showOnscreenMessage(titleText, scoreText) + + def setCurPinballScore(self, avId, score, multiplier): + self.notify.debug('setCurPinballScore %d %d %d' % (avId, score, multiplier)) + if self.pinballInfo.get(avId) == None: + self.pinballInfo[avId] = [0, 0, 0] + pinballEntry = self.pinballInfo[avId] + pinballEntry[1] = score + pinballEntry[2] = multiplier + curScore = score * multiplier + if curScore > pinballEntry[0]: + pinballEntry[0] = curScore + if curScore > self.pinballHiScore: + self.pinballHiScore = pinballEntry[0] + toon = base.cr.doId2do.get(avId) + if toon: + self.pinballHiScorer = toon.getName() + self.showScore() + return + + def b_setCurPinballScore(self, avId, score, multiplier): + self.setCurPinballScore(avId, score, multiplier) + self.sendUpdate('setCurPinballScore', [avId, score, multiplier]) + + def __showOnscreenMessage(self, titleText, scoreText): + self.notify.debug('----- __showOnscreenmessage') + if not self.onscreenMessage: + self.onscreenMessage = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(12, 1, 3), pos=(0, 0, 0.8), scale=0.1) + titles = DirectLabel(parent=self.onscreenMessage, relief=None, text=titleText, text_fg=VBase4(0, 0, 0, 1), text_align=TextNode.ALeft, text_scale=0.7, pos=(-5.75, 0, 0.5)) + scores = DirectLabel(parent=self.onscreenMessage, relief=None, text=scoreText, text_fg=VBase4(1, 0, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(5.75, 0, 0.5)) + self.onscreenMessage.titles = titles + self.onscreenMessage.scores = scores + else: + self.onscreenMessage.titles['text'] = titleText + self.onscreenMessage.scores['text'] = scoreText + base.foobar = self.onscreenMessage + return + + def __clearOnscreenMessage(self): + self.notify.debug('----- __clearOnscreenMessage') + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + return + + def setPinballHiScore(self, score): + self.pinballHiScore = score + self.showScore() + + def setPinballHiScorer(self, name): + self.pinballHiScorer = name + self.showScore() + + def hideGui(self): + if self.timer: + self.hideTimer() + if self.onscreenMessage: + self.onscreenMessage.hide() + + def showGui(self): + if self.timer: + if self.enabled: + self.showTimer() + if self.onscreenMessage: + self.onscreenMessage.show() diff --git a/toontown/estate/DistributedToonStatuary.py b/toontown/estate/DistributedToonStatuary.py new file mode 100644 index 0000000..4449a2d --- /dev/null +++ b/toontown/estate/DistributedToonStatuary.py @@ -0,0 +1,224 @@ +from toontown.estate import DistributedStatuary +from toontown.estate import DistributedLawnDecor +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +from pandac.PandaModules import * +from toontown.toon import Toon +from toontown.toon import ToonDNA +import GardenGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import NodePath +from pandac.PandaModules import Point3 + +def dnaCodeFromToonDNA(dna): + + def findItemNumInList(wantItem, wantList): + i = 0 + for item in wantList: + if item == wantItem: + break + i += 1 + + return i + + if dna.gender == 'f': + genderTypeNum = 0 + else: + genderTypeNum = 1 + legTypeNum = findItemNumInList(dna.legs, ToonDNA.toonLegTypes) << 1 + torsoTypeNum = findItemNumInList(dna.torso, ToonDNA.toonTorsoTypes) << 3 + headTypeNum = findItemNumInList(dna.head, ToonDNA.toonHeadTypes) << 7 + return headTypeNum | torsoTypeNum | legTypeNum | genderTypeNum + + +class DistributedToonStatuary(DistributedStatuary.DistributedStatuary): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonStatuary') + + def __init__(self, cr): + self.notify.debug('constructing DistributedToonStatuary') + DistributedStatuary.DistributedStatuary.__init__(self, cr) + self.toon = None + return + + def loadModel(self): + DistributedStatuary.DistributedStatuary.loadModel(self) + self.model.setScale(self.worldScale * 1.5, self.worldScale * 1.5, self.worldScale) + self.getToonPropertiesFromOptional() + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(self.headType, self.torsoType, self.legType, self.gender, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + self.setupStoneToon(dna) + self.poseToonFromTypeIndex(self.typeIndex) + self.toon.reparentTo(self.model) + + def delete(self): + self.deleteToon() + DistributedStatuary.DistributedStatuary.delete(self) + + def setupStoneToon(self, dna): + self.toon = Toon.Toon() + self.toon.setPos(0, 0, 0) + self.toon.setDNA(dna) + self.toon.initializeBodyCollisions('toonStatue') + self.toon.stopBlink() + self.toon.stopLookAround() + self.gender = self.toon.style.gender + self.speciesType = self.toon.style.getAnimal() + self.headType = self.toon.style.head + self.removeTextures() + self.setStoneTexture() + self.toon.dropShadow.hide() + self.toon.setZ(70) + self.toon.setScale(20 / 1.5, 20 / 1.5, 20) + + def deleteToon(self): + self.notify.debug('entering deleteToon') + self.toon.delete() + self.toon = None + return + + def copyLocalAvatarToon(self): + self.toon = Toon.Toon() + self.toon.reparentTo(render) + self.toon.setDNA(base.localAvatar.style) + self.toon.setPos(base.localAvatar, 0, 0, 0) + self.toon.pose('victory', 30) + self.toon.setH(180) + self.speciesType = self.toon.style.getAnimal() + self.gender = self.toon.style.gender + + def setupCollision(self): + DistributedStatuary.DistributedStatuary.setupCollision(self) + self.colSphereNode.setScale(self.colSphereNode.getScale() * 1.5) + + def setupShadow(self): + pass + + def removeTextures(self): + for node in self.toon.findAllMatches('**/*'): + node.setState(RenderState.makeEmpty()) + + desatShirtTex = loader.loadTexture('phase_3/maps/desat_shirt_1.jpg') + desatSleeveTex = loader.loadTexture('phase_3/maps/desat_sleeve_1.jpg') + desatShortsTex = loader.loadTexture('phase_3/maps/desat_shorts_1.jpg') + desatSkirtTex = loader.loadTexture('phase_3/maps/desat_skirt_1.jpg') + if self.toon.hasLOD(): + for lodName in self.toon.getLODNames(): + torso = self.toon.getPart('torso', lodName) + torsoTop = torso.find('**/torso-top') + if torsoTop: + torsoTop.setTexture(desatShirtTex, 1) + sleeves = torso.find('**/sleeves') + if sleeves: + sleeves.setTexture(desatSleeveTex, 1) + bottoms = torso.findAllMatches('**/torso-bot*') + for bottomNum in range(0, bottoms.getNumPaths()): + bottom = bottoms.getPath(bottomNum) + if bottom: + if self.toon.style.torso[1] == 's': + bottom.setTexture(desatShortsTex, 1) + else: + bottom.setTexture(desatSkirtTex, 1) + + def setStoneTexture(self): + gray = VBase4(1.6, 1.6, 1.6, 1) + self.toon.setColor(gray, 10) + stoneTex = loader.loadTexture('phase_5.5/maps/smoothwall_1.jpg') + ts = TextureStage('ts') + ts.setPriority(1) + self.toon.setTexture(ts, stoneTex) + tsDetail = TextureStage('tsDetail') + tsDetail.setPriority(2) + tsDetail.setSort(10) + tsDetail.setCombineRgb(tsDetail.CMInterpolate, tsDetail.CSTexture, tsDetail.COSrcColor, tsDetail.CSPrevious, tsDetail.COSrcColor, tsDetail.CSConstant, tsDetail.COSrcColor) + tsDetail.setColor(VBase4(0.5, 0.5, 0.5, 1)) + if self.toon.hasLOD(): + for lodName in self.toon.getLODNames(): + head = self.toon.getPart('head', lodName) + eyes = head.find('**/eye*') + if not eyes.isEmpty(): + eyes.setColor(Vec4(1.4, 1.4, 1.4, 0.3), 10) + ears = head.find('**/ears*') + animal = self.toon.style.getAnimal() + if animal != 'dog': + muzzle = head.find('**/muzzle*neutral') + else: + muzzle = head.find('**/muzzle*') + if ears != ears.notFound(): + if self.speciesType == 'cat': + ears.setTexture(tsDetail, stoneTex) + elif self.speciesType == 'horse': + pass + elif self.speciesType == 'rabbit': + ears.setTexture(tsDetail, stoneTex) + elif self.speciesType == 'monkey': + ears.setTexture(tsDetail, stoneTex) + ears.setColor(VBase4(0.6, 0.9, 1, 1), 10) + if muzzle != muzzle.notFound(): + muzzle.setTexture(tsDetail, stoneTex) + if self.speciesType == 'dog': + nose = head.find('**/nose') + if nose != nose.notFound(): + nose.setTexture(tsDetail, stoneTex) + + tsLashes = TextureStage('tsLashes') + tsLashes.setPriority(2) + tsLashes.setMode(tsLashes.MDecal) + if self.gender == 'f': + if self.toon.hasLOD(): + head = self.toon.getPart('head', '1000') + else: + head = self.toon.getPart('head', 'lodRoot') + if self.headType[1] == 'l': + openString = 'open-long' + closedString = 'closed-long' + else: + openString = 'open-short' + closedString = 'closed-short' + lashesOpen = head.find('**/' + openString) + lashesClosed = head.find('**/' + closedString) + if lashesOpen != lashesOpen.notFound(): + lashesOpen.setTexture(tsLashes, stoneTex) + lashesOpen.setColor(VBase4(1, 1, 1, 0.4), 10) + if lashesClosed != lashesClosed.notFound(): + lashesClosed.setTexture(tsLashes, stoneTex) + lashesClosed.setColor(VBase4(1, 1, 1, 0.4), 10) + + def setOptional(self, optional): + self.optional = optional + + def getToonPropertiesFromOptional(self): + genderTypeNum = self.optional & 1 + legTypeNum = (self.optional & 6) >> 1 + torsoTypeNum = (self.optional & 120) >> 3 + headTypeNum = (self.optional & 65408) >> 7 + if genderTypeNum == 0: + self.gender = 'f' + else: + self.gender = 'm' + if legTypeNum <= len(ToonDNA.toonLegTypes): + self.legType = ToonDNA.toonLegTypes[legTypeNum] + if torsoTypeNum <= len(ToonDNA.toonTorsoTypes): + self.torsoType = ToonDNA.toonTorsoTypes[torsoTypeNum] + if headTypeNum <= len(ToonDNA.toonHeadTypes): + self.headType = ToonDNA.toonHeadTypes[headTypeNum] + + def poseToonFromTypeIndex(self, typeIndex): + if typeIndex == 205: + self.toon.pose('wave', 18) + elif typeIndex == 206: + self.toon.pose('victory', 116) + elif typeIndex == 207: + self.toon.pose('bored', 96) + elif typeIndex == 208: + self.toon.pose('think', 59) + + def poseToonFromSpecialsIndex(self, specialsIndex): + if specialsIndex == 105: + self.toon.pose('wave', 18) + elif specialsIndex == 106: + self.toon.pose('victory', 116) + elif specialsIndex == 107: + self.toon.pose('bored', 96) + elif specialsIndex == 108: + self.toon.pose('think', 59) diff --git a/toontown/estate/DistributedTrunk.py b/toontown/estate/DistributedTrunk.py new file mode 100644 index 0000000..3c99b4e --- /dev/null +++ b/toontown/estate/DistributedTrunk.py @@ -0,0 +1,384 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +import DistributedCloset +import ClosetGlobals +import TrunkGUI +from toontown.toon import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +N_A = 0 + +class DistributedTrunk(DistributedCloset.DistributedCloset): + notify = directNotify.newCategory('DistributedTrunk') + + def __init__(self, cr): + DistributedCloset.DistributedCloset.__init__(self, cr) + self.hatList = [] + self.glassesList = [] + self.backpackList = [] + self.shoesList = [] + self.oldHatList = [] + self.oldGlassesList = [] + self.oldBackpackList = [] + self.oldShoesList = [] + self.swapHatEvent = '' + self.swapGlassesEvent = '' + self.swapBackpackEvent = '' + self.swapShoesEvent = '' + self.hatDeleted = 0 + self.glassesDeleted = 0 + self.backpackDeleted = 0 + self.shoesDeleted = 0 + self.isFreePlayer = 0 + + def printInfo(self): + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current hat = %s, glasses = %s, backpack = %s, shoes = %s' % (self.av.getHat(), + self.av.getGlasses(), + self.av.getBackpack(), + self.av.getShoes()) + print 'hatList = %s' % self.av.getHatList() + print 'glassesList = %s' % self.av.getGlassesList() + print 'backpackList = %s' % self.av.getBackpackList() + print 'shoesList = %s' % self.av.getShoesList() + + def setState(self, mode, avId, ownerId, gender, hatList, glassesList, backpackList, shoesList): + self.notify.debug('setState, mode=%s, avId=%s, ownerId=%d' % (mode, avId, ownerId)) + self.isOwner = avId == ownerId + self.ownerGender = gender + if mode == ClosetGlobals.CLOSED: + self.fsm.request('closed') + return + elif mode == ClosetGlobals.OPEN: + self.customerId = avId + self.av = self.cr.doId2do.get(self.customerId, None) + if self.av: + if self.av.getGameAccess() != ToontownGlobals.AccessFull: + self.isOwner = 0 + self.isFreePlayer = 1 + else: + self.isFreePlayer = 0 + if base.localAvatar.getDoId() == self.customerId: + self.gender = self.av.style.gender + self.hatList = hatList + self.glassesList = glassesList + self.backpackList = backpackList + self.shoesList = shoesList + self.oldHatList = self.hatList[0:] + self.oldGlassesList = self.glassesList[0:] + self.oldBackpackList = self.backpackList[0:] + self.oldShoesList = self.shoesList[0:] + print '-----------Starting trunk interaction-----------' + self.printInfo() + print '-------------------------------------------------' + if not self.isOwner: + self.__popupNotOwnerPanel() + else: + taskMgr.doMethodLater(0.5, self.popupChangeClothesGUI, self.uniqueName('popupChangeClothesGUI')) + self.fsm.request('open') + return + + def load(self): + lNode = self.find('**/lid_origin') + lLid = self.find('**/lid') + if lNode.isEmpty() or lLid.isEmpty(): + self.lid = None + else: + lLid.wrtReparentTo(lNode) + self.lid = lNode + if not lNode.isEmpty(): + self.scale = lLid.getScale()[0] * 0.6 + return + + def popupChangeClothesGUI(self, task): + self.notify.debug('popupChangeClothesGUI') + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapHatEvent = self.uniqueName('swapHat') + self.swapGlassesEvent = self.uniqueName('swapGlasses') + self.swapBackpackEvent = self.uniqueName('swapBackpack') + self.swapShoesEvent = self.uniqueName('swapShoes') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapHatEvent, self.__handleSwapHat) + self.accept(self.swapGlassesEvent, self.__handleSwapGlasses) + self.accept(self.swapBackpackEvent, self.__handleSwapBackpack) + self.accept(self.swapShoesEvent, self.__handleSwapShoes) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + if not self.closetGUI: + self.closetGUI = TrunkGUI.TrunkGUI(self.isOwner, self.purchaseDoneEvent, self.cancelEvent, self.swapHatEvent, self.swapGlassesEvent, self.swapBackpackEvent, self.swapShoesEvent, self.deleteEvent, self.hatList, self.glassesList, self.backpackList, self.shoesList) + self.closetGUI.load() + if self.gender != self.ownerGender: + self.closetGUI.setGender(self.ownerGender) + self.closetGUI.enter(base.localAvatar) + self.closetGUI.showButtons() + oldHat = self.av.getHat() + oldGlasses = self.av.getGlasses() + oldBackpack = self.av.getBackpack() + oldShoes = self.av.getShoes() + self.oldStyle = {ToonDNA.HAT: oldHat, + ToonDNA.GLASSES: oldGlasses, + ToonDNA.BACKPACK: oldBackpack, + ToonDNA.SHOES: oldShoes} + return Task.done + + def resetCloset(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupChangeClothesGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + taskMgr.remove(self.uniqueName('lerpToon')) + if self.closetGUI: + self.closetGUI.hideButtons() + self.closetGUI.exit() + self.closetGUI.unload() + self.closetGUI = None + del self.av + self.av = base.localAvatar + oldHat = self.av.getHat() + oldGlasses = self.av.getGlasses() + oldBackpack = self.av.getBackpack() + oldShoes = self.av.getShoes() + self.oldStyle = {ToonDNA.HAT: oldHat, + ToonDNA.GLASSES: oldGlasses, + ToonDNA.BACKPACK: oldBackpack, + ToonDNA.SHOES: oldShoes} + self.hatDeleted = 0 + self.glassesDeleted = 0 + self.backpackDeleted = 0 + self.shoesDeleted = 0 + return Task.done + + def _handleCancel(self): + if self.oldStyle: + oldHat = self.oldStyle[ToonDNA.HAT] + oldGlasses = self.oldStyle[ToonDNA.GLASSES] + oldBackpack = self.oldStyle[ToonDNA.BACKPACK] + oldShoes = self.oldStyle[ToonDNA.SHOES] + self.d_setDNA(oldHat[0], oldHat[1], oldHat[2], oldGlasses[0], oldGlasses[1], oldGlasses[2], oldBackpack[0], oldBackpack[1], oldBackpack[2], oldShoes[0], oldShoes[1], oldShoes[2], 1) + else: + self.notify.info('avoided crash in handleCancel') + self._handlePurchaseDone() + if self.closetGUI: + self.closetGUI.resetClothes(self.oldStyle) + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + return + + def __handleSwapHat(self): + item = self.av.getHat() + self.d_setDNA(item[0], item[1], item[2], N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, 0, ToonDNA.HAT) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleSwapGlasses(self): + item = self.av.getGlasses() + self.d_setDNA(N_A, N_A, N_A, item[0], item[1], item[2], N_A, N_A, N_A, N_A, N_A, N_A, 0, ToonDNA.GLASSES) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleSwapBackpack(self): + item = self.av.getBackpack() + self.d_setDNA(N_A, N_A, N_A, N_A, N_A, N_A, item[0], item[1], item[2], N_A, N_A, N_A, 0, ToonDNA.BACKPACK) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleSwapShoes(self): + item = self.av.getShoes() + self.d_setDNA(N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, N_A, item[0], item[1], item[2], 0, ToonDNA.SHOES) + if self.closetGUI: + self.closetGUI.updateTrashButtons() + + def __handleDelete(self, which): + if which == ToonDNA.HAT: + itemList = self.closetGUI.hats + trashIndex = self.closetGUI.hatChoice + swapFunc = self.closetGUI.swapHat + removeFunc = self.closetGUI.removeHat + trashItem = self.av.getHat() + self.hatDeleted = self.hatDeleted | 1 + elif which == ToonDNA.GLASSES: + itemList = self.closetGUI.glasses + trashIndex = self.closetGUI.glassesChoice + swapFunc = self.closetGUI.swapGlasses + removeFunc = self.closetGUI.removeGlasses + trashItem = self.av.getGlasses() + self.glassesDeleted = self.glassesDeleted | 1 + elif which == ToonDNA.BACKPACK: + itemList = self.closetGUI.backpacks + trashIndex = self.closetGUI.backpackChoice + swapFunc = self.closetGUI.swapBackpack + removeFunc = self.closetGUI.removeBackpack + trashItem = self.av.getBackpack() + self.backpackDeleted = self.backpackDeleted | 1 + elif which == ToonDNA.SHOES: + itemList = self.closetGUI.shoes + trashIndex = self.closetGUI.shoesChoice + swapFunc = self.closetGUI.swapShoes + removeFunc = self.closetGUI.removeShoes + trashItem = self.av.getShoes() + self.shoesDeleted = self.shoesDeleted | 1 + else: + self.notify.warning("we don't know about this item(type = %s)" % which) + return + if len(itemList) > 1: + if trashIndex == 0: + swapFunc(1) + else: + swapFunc(-1) + removeFunc(trashIndex) + self.sendUpdate('removeItem', [trashItem[0], + trashItem[1], + trashItem[2], + which]) + swapFunc(0) + self.closetGUI.updateTrashButtons() + else: + self.notify.warning("cant delete this item(type = %s), since we don't have a replacement" % which) + + def resetItemLists(self): + self.hatList = self.oldHatList[0:] + self.glassesList = self.oldGlassesList[0:] + self.backpackList = self.oldBackpackList[0:] + self.shoesList = self.oldShoesList[0:] + self.closetGUI.hat = self.hatList + self.closetGUI.glasses = self.glassesList + self.closetGUI.backpack = self.backpackList + self.closetGUI.shoes = self.shoesList + self.hatDeleted = 0 + self.glassesDeleted = 0 + self.backpackDeleted = 0 + self.shoesDeleted = 0 + + def __proceedToCheckout(self): + if self.hatDeleted or self.glassesDeleted or self.backpackDeleted or self.shoesDeleted: + self.__popupAreYouSurePanel() + else: + self._handlePurchaseDone() + + def _handlePurchaseDone(self, timeout = 0): + if timeout == 1: + oldHat = self.oldStyle[ToonDNA.HAT] + oldGlasses = self.oldStyle[ToonDNA.GLASSES] + oldBackpack = self.oldStyle[ToonDNA.BACKPACK] + oldShoes = self.oldStyle[ToonDNA.SHOES] + self.d_setDNA(oldHat[0], oldHat[1], oldHat[2], oldGlasses[0], oldGlasses[1], oldGlasses[2], oldBackpack[0], oldBackpack[1], oldBackpack[2], oldShoes[0], oldShoes[1], oldShoes[2], 1) + else: + which = 0 + if hasattr(self.closetGUI, 'hatChoice') and hasattr(self.closetGUI, 'glassesChoice') and hasattr(self.closetGUI, 'backpackChoice') and hasattr(self.closetGUI, 'shoesChoice'): + if self.closetGUI.hatChoice != 0 or self.hatDeleted: + which = which | ToonDNA.HAT + if self.closetGUI.glassesChoice != 0 or self.glassesDeleted: + which = which | ToonDNA.GLASSES + if self.closetGUI.backpackChoice != 0 or self.backpackDeleted: + which = which | ToonDNA.BACKPACK + if self.closetGUI.shoesChoice != 0 or self.shoesDeleted: + which = which | ToonDNA.SHOES + hat = self.av.getHat() + glasses = self.av.getGlasses() + backpack = self.av.getBackpack() + shoes = self.av.getShoes() + self.d_setDNA(hat[0], hat[1], hat[2], glasses[0], glasses[1], glasses[2], backpack[0], backpack[1], backpack[2], shoes[0], shoes[1], shoes[2], 2, which) + + def d_setDNA(self, hatIdx, hatTexture, hatColor, glassesIdx, glassesTexture, glassesColor, backpackIdx, backpackTexture, backpackColor, shoesIdx, shoesTexture, shoesColor, finished, which = ToonDNA.HAT | ToonDNA.GLASSES | ToonDNA.BACKPACK | ToonDNA.SHOES): + self.sendUpdate('setDNA', [hatIdx, + hatTexture, + hatColor, + glassesIdx, + glassesTexture, + glassesColor, + backpackIdx, + backpackTexture, + backpackColor, + shoesIdx, + shoesTexture, + shoesColor, + finished, + which]) + + def setCustomerDNA(self, avId, hatIdx, hatTexture, hatColor, glassesIdx, glassesTexture, glassesColor, backpackIdx, backpackTexture, backpackColor, shoesIdx, shoesTexture, shoesColor, which): + if avId and avId != base.localAvatar.doId: + av = base.cr.doId2do.get(avId, None) + if av: + if self.av == base.cr.doId2do[avId]: + if which & ToonDNA.HAT: + self.av.setHat(hatIdx, hatTexture, hatColor) + if which & ToonDNA.GLASSES: + self.av.setGlasses(glassesIdx, glassesTexture, glassesColor) + if which & ToonDNA.BACKPACK: + self.av.setBackpack(backpackIdx, backpackTexture, backpackColor) + if which & ToonDNA.SHOES: + self.av.setShoes(shoesIdx, shoesTexture, shoesColor) + self.av.generateToonAccessories() + return + + def __popupNotOwnerPanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = None + self.purchaseDoneEvent = self.uniqueName('purchaseDone') + self.swapHatEvent = self.uniqueName('swapHat') + self.swapGlassesEvent = self.uniqueName('swapGlasses') + self.swapBackpackEvent = self.uniqueName('swapBackpack') + self.swapShoesEvent = self.uniqueName('swapShoes') + self.cancelEvent = self.uniqueName('cancel') + self.accept(self.purchaseDoneEvent, self.__proceedToCheckout) + self.accept(self.swapHatEvent, self.__handleSwapHat) + self.accept(self.swapGlassesEvent, self.__handleSwapGlasses) + self.accept(self.swapBackpackEvent, self.__handleSwapBackpack) + self.accept(self.swapShoesEvent, self.__handleSwapShoes) + self.accept(self.cancelEvent, self._handleCancel) + self.deleteEvent = self.uniqueName('delete') + if self.isOwner: + self.accept(self.deleteEvent, self.__handleDelete) + 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')) + if self.isFreePlayer: + textMsg = TTLocalizer.TrunkNotPaidMessage + else: + textMsg = TTLocalizer.TrunkNotOwnerMessage + self.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=textMsg, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.06)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.21), command=self._handleNotOwnerMessageOK) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def __popupAreYouSurePanel(self): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = 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.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=TTLocalizer.TrunkAreYouSureMessage, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(0.88, 1, 0.55), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.08)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.ClosetPopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.1, 0.0, -0.21), command=self._handleYesImSure) + DirectButton(self.popupInfo, image=cancelButtonImage, relief=None, text=TTLocalizer.ClosetPopupCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.1, 0.0, -0.21), command=self._handleNotSure) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def _openDoors(self): + if self.closetTrack: + self.closetTrack.finish() + openHpr = Vec3(0, -80, 0) + if self.av: + self.av.applyCheesyEffect(ToontownGlobals.CENormal) + self.closetTrack = Parallel() + if self.lid: + self.closetTrack.append(self.lid.hprInterval(0.5, openHpr)) + self.closetTrack.start() + + def _closeDoors(self): + if self.closetTrack: + self.closetTrack.finish() + closeHpr = Vec3(0, 0, 0) + if self.av: + self.av.reconsiderCheesyEffect() + self.closetTrack = Parallel() + if self.lid: + self.closetTrack.append(self.lid.hprInterval(0.5, closeHpr)) + self.closetTrack.start() diff --git a/toontown/estate/Estate.py b/toontown/estate/Estate.py new file mode 100644 index 0000000..857a1ba --- /dev/null +++ b/toontown/estate/Estate.py @@ -0,0 +1,397 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from toontown.hood import Place +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +import random +from direct.showbase import PythonUtil +from toontown.hood import Place +from toontown.hood import SkyUtil +from toontown.pets import PetTutorial +from direct.controls.GravityWalker import GravityWalker +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs, TLNull +import HouseGlobals + +class Estate(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('Estate') + + def __init__(self, loader, avId, zoneId, parentFSMState, doneEvent): + Place.Place.__init__(self, None, doneEvent) + self.id = MyEstate + self.avId = avId + self.zoneId = zoneId + self.loader = loader + self.cameraSubmerged = -1 + self.toonSubmerged = -1 + self.fsm = ClassicFSM.ClassicFSM('Estate', [State.State('init', self.enterInit, self.exitInit, ['final', + 'teleportIn', + 'doorIn', + 'walk']), + State.State('petTutorial', self.enterPetTutorial, self.exitPetTutorial, ['walk']), + State.State('walk', self.enterWalk, self.exitWalk, ['final', + 'sit', + 'stickerBook', + 'options', + 'quest', + 'fishing', + 'mailbox', + 'stopped', + 'DFA', + 'trialerFA', + 'doorOut', + 'push', + 'pet']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('push', self.enterPush, self.exitPush, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'sit', + 'quest', + 'fishing', + 'mailbox', + 'stopped', + 'doorOut', + 'push', + 'pet', + 'DFA', + 'trialerFA']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', 'petTutorial']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'walk', 'final']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['final', 'walk']), + State.State('final', self.enterFinal, self.exitFinal, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk']), + State.State('fishing', self.enterFishing, self.exitFishing, ['walk', 'stopped']), + State.State('mailbox', self.enterMailbox, self.exitMailbox, ['walk', 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('pet', self.enterPet, self.exitPet, ['walk', 'trialerFA']), + 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'])], 'init', 'final') + self.fsm.enterInitialState() + self.doneEvent = doneEvent + self.parentFSMState = parentFSMState + return + + def delete(self): + self.unload() + + def load(self): + Place.Place.load(self) + self.fog = Fog('EstateFog') + taskMgr.add(self.__checkCameraUnderwater, 'estate-check-cam-underwater') + path = self.loader.geom.find('**/Path') + path.setBin('ground', 10, 1) + self.parentFSMState.addChild(self.fsm) + + def unload(self): + self.ignoreAll() + self.notify.info('remove estate-check-toon-underwater to TaskMgr in unload()') + taskMgr.remove('estate-check-toon-underwater') + taskMgr.remove('estate-check-cam-underwater') + self.parentFSMState.removeChild(self.fsm) + del self.fsm + self.fog = None + Place.Place.unload(self) + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + newsManager = base.cr.newsManager + if config.GetBool('want-estate-telemetry-limiter', 1): + limiter = TLGatherAllAvs('Estate', RotationLimitToH) + else: + limiter = TLNull() + self._telemLimiter = limiter + if newsManager: + holidayIds = base.cr.newsManager.getDecorationHolidayId() + if (ToontownGlobals.HALLOWEEN_COSTUMES in holidayIds or ToontownGlobals.SPOOKY_COSTUMES in holidayIds) and self.loader.hood.spookySkyFile: + lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.55, 0.55, 0.65, 1)), Func(self.loader.hood.startSpookySky)) + lightsOff.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + self.loader.hood.sky.setFogOff() + self.__setFaintFog() + for i in self.loader.nodeList: + self.loader.enterAnimatedProps(i) + + self.loader.geom.reparentTo(render) + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds or ToontownGlobals.SILLYMETER_EXT_HOLIDAY in holidayIds: + self.startAprilFoolsControls() + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def startAprilFoolsControls(self): + if isinstance(base.localAvatar.controlManager.currentControls, GravityWalker): + base.localAvatar.controlManager.currentControls.setGravity(32.174 * 0.75) + + def stopAprilFoolsControls(self): + if isinstance(base.localAvatar.controlManager.currentControls, GravityWalker): + base.localAvatar.controlManager.currentControls.setGravity(32.174 * 2.0) + + def exit(self): + base.localAvatar.stopChat() + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds or ToontownGlobals.SILLYMETER_EXT_HOLIDAY in holidayIds: + self.stopAprilFoolsControls() + self._telemLimiter.destroy() + del self._telemLimiter + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self.loader.geom.reparentTo(hidden) + for i in self.loader.nodeList: + self.loader.exitAnimatedProps(i) + + self.loader.hood.stopSky() + render.setFogOff() + base.cr.cache.flush() + + def __setZoneId(self, zoneId): + self.zoneId = zoneId + + def detectedMailboxCollision(self): + self.fsm.request('mailbox') + + def detectedGardenPlotUse(self): + if hasattr(self, 'fsm'): + self.fsm.request('stopped') + + def detectedGardenPlotDone(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + + def detectedFlowerSellUse(self): + if hasattr(self, 'fsm'): + self.fsm.request('stopped') + + def detectedFlowerSellDone(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + + def doRequestLeave(self, requestStatus): + self.fsm.request('trialerFA', [requestStatus]) + + def enterInit(self): + pass + + def exitInit(self): + pass + + def enterPetTutorial(self, bDummy = True): + self.notify.info('remove estate-check-toon-underwater to TaskMgr in enterPetTutorial()') + taskMgr.remove('estate-check-toon-underwater') + self.petTutorialDoneEvent = 'PetTutorialDone' + self.acceptOnce(self.petTutorialDoneEvent, self.petTutorialDone) + self.petTutorial = PetTutorial.PetTutorial(self.petTutorialDoneEvent) + + def exitPetTutorial(self): + self.notify.info('add estate-check-toon-underwater to TaskMgr in exitPetTutorial()') + if hasattr(self, 'fsm'): + taskMgr.add(self.__checkToonUnderwater, 'estate-check-toon-underwater') + if hasattr(self, 'petTutorial') and self.petTutorial is not None: + self.petTutorial.destroy() + return + + def petTutorialDone(self): + self.ignore(self.petTutorialDoneEvent) + self.petTutorial.destroy() + self.petTutorial = None + self.fsm.request('walk', [1]) + return + + def enterMailbox(self): + Place.Place.enterPurchase(self) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepMailbox) + self.enablePeriodTimer() + + def __handleFallingAsleepMailbox(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + messenger.send('mailboxAsleep') + base.localAvatar.forceGotoSleep() + + def exitMailbox(self): + Place.Place.exitPurchase(self) + base.localAvatar.stopSleepWatch() + self.disablePeriodTimer() + + def enterTeleportIn(self, requestStatus): + self._etiToken = self.addSetZoneCompleteCallback(Functor(self._teleportToHouse, requestStatus)) + Place.Place.enterTeleportIn(self, requestStatus) + + def _teleportToHouse(self, requestStatus): + try: + houseDo = base.cr.doId2do.get(base.localAvatar.houseId) + house = houseDo.house + pos = house.getPos(render) + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(house, 17, 3, 0, 125, 0, 0) + except: + x, y, z, h, p, r = HouseGlobals.defaultEntryPoint + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(render, x, y, z, h, p, r) + + base.localAvatar.setScale(1, 1, 1) + self.toonSubmerged = -1 + self.notify.info('remove estate-check-toon-underwater to TaskMgr in enterTeleportIn()') + taskMgr.remove('estate-check-toon-underwater') + if base.wantPets: + if base.localAvatar.hasPet() and not base.localAvatar.bPetTutorialDone: + self.nextState = 'petTutorial' + + def teleportInDone(self): + self.notify.debug('teleportInDone') + self.toonSubmerged = -1 + if self.nextState is not 'petTutorial': + self.notify.info('add estate-check-toon-underwater to TaskMgr in teleportInDone()') + if hasattr(self, 'fsm'): + taskMgr.add(self.__checkToonUnderwater, 'estate-check-toon-underwater') + Place.Place.teleportInDone(self) + + def exitTeleportIn(self): + self.removeSetZoneCompleteCallback(self._etiToken) + Place.Place.exitTeleportIn(self) + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == ToontownGlobals.MyEstate and zoneId == self.getZoneId() and shardId == None: + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate and shardId == None: + self.doneStatus = requestStatus + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent, [self.doneStatus]) + 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) + + def exitDoorIn(self): + self.toonSubmerged = -1 + self.notify.info('add estate-check-toon-underwater to TaskMgr in exitDoorIn()') + if hasattr(self, 'fsm'): + taskMgr.add(self.__checkToonUnderwater, 'estate-check-toon-underwater') + Place.Place.exitDoorIn(self) + + def getZoneId(self): + if self.zoneId: + return self.zoneId + else: + self.notify.warning('no zone id available') + + def __checkCameraUnderwater(self, task): + if camera.getZ(render) < -1.2: + self.__submergeCamera() + else: + self.__emergeCamera() + return Task.cont + + def __checkToonUnderwater(self, task): + if base.localAvatar.getZ() < -4.0: + self.__submergeToon() + else: + self.__emergeToon() + return Task.cont + + def __submergeCamera(self): + if self.cameraSubmerged == 1: + return + self.__setUnderwaterFog() + base.playSfx(self.loader.underwaterSound, looping=1, volume=0.8) + self.cameraSubmerged = 1 + self.walkStateData.setSwimSoundAudible(1) + + def __emergeCamera(self): + if self.cameraSubmerged == 0: + return + self.loader.underwaterSound.stop() + self.loader.hood.sky.setFogOff() + self.__setFaintFog() + self.cameraSubmerged = 0 + self.walkStateData.setSwimSoundAudible(0) + + def forceUnderWater(self): + self.toonSubmerged = 0 + self.__submergeToon() + + def __submergeToon(self): + if self.toonSubmerged == 1: + return + self.notify.debug('continuing in __submergeToon') + if hasattr(self, 'loader') and self.loader: + base.playSfx(self.loader.submergeSound) + if base.config.GetBool('disable-flying-glitch') == 0: + self.fsm.request('walk') + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + pos = base.localAvatar.getPos(render) + base.localAvatar.d_playSplashEffect(pos[0], pos[1], -2.3) + self.toonSubmerged = 1 + + def __emergeToon(self): + if self.toonSubmerged == 0: + return + self.notify.debug('continuing in __emergeToon') + if hasattr(self, 'walkStateData'): + self.walkStateData.fsm.request('walking') + self.toonSubmerged = 0 + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds or ToontownGlobals.SILLYMETER_EXT_HOLIDAY in holidayIds: + self.startAprilFoolsControls() + else: + self.stopAprilFoolsControls() + + def __setUnderwaterFog(self): + if base.wantFog: + self.fog.setColor(Vec4(0.0, 0.0, 0.6, 1.0)) + self.fog.setLinearRange(0.1, 100.0) + render.setFog(self.fog) + self.loader.hood.sky.setFog(self.fog) + + def __setWhiteFog(self): + if base.wantFog: + self.fog.setColor(Vec4(0.8, 0.8, 0.8, 1.0)) + self.fog.setLinearRange(0.0, 400.0) + render.setFog(self.fog) + self.loader.hood.sky.setFog(self.fog) + + def __setFaintFog(self): + if base.wantFog: + self.fog.setColor(Vec4(0.8, 0.8, 0.8, 1.0)) + self.fog.setLinearRange(0.0, 700.0) + render.setFog(self.fog) diff --git a/toontown/estate/EstateLoader.py b/toontown/estate/EstateLoader.py new file mode 100644 index 0000000..5b03553 --- /dev/null +++ b/toontown/estate/EstateLoader.py @@ -0,0 +1,345 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from toontown.safezone import SafeZoneLoader +import random +from toontown.launcher import DownloadForceAcknowledge +import House +import Estate +import HouseGlobals +import random +import math +from toontown.coghq import MovingPlatform +from direct.directnotify import DirectNotifyGlobal + +class EstateLoader(SafeZoneLoader.SafeZoneLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('EstateLoader') + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('EstateLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'estate', 'house']), + State.State('estate', self.enterEstate, self.exitEstate, ['quietZone']), + State.State('house', self.enterHouse, self.exitHouse, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['house', 'estate']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.musicFile = 'phase_4/audio/bgm/TC_nbrhood.mid' + self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.mid' + self.dnaFile = 'phase_5.5/dna/estate_1.dna' + self.safeZoneStorageDNAFile = None + self.cloudSwitch = 0 + self.id = MyEstate + self.estateOwnerId = None + self.branchZone = None + self.houseDoneEvent = 'houseDone' + self.estateDoneEvent = 'estateDone' + self.enteredHouse = None + self.houseNode = [None] * 6 + self.houseModels = [None] * HouseGlobals.NUM_HOUSE_TYPES + self.houseId2house = {} + self.barrel = None + self.clouds = [] + self.cloudTrack = None + self.sunMoonNode = None + self.fsm.enterInitialState() + return + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.music = base.loadMusic('phase_4/audio/bgm/TC_nbrhood.mid') + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.mp3') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.mp3') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.mp3') + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + self.cricketSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + if base.goonsEnabled: + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + self.invModels = [] + from toontown.toonbase import ToontownBattleGlobals + for track in range(len(ToontownBattleGlobals.AvPropsNew)): + itemList = [] + for item in range(len(ToontownBattleGlobals.AvPropsNew[track])): + itemList.append(invModel.find('**/' + ToontownBattleGlobals.AvPropsNew[track][item])) + + self.invModels.append(itemList) + + invModel.removeNode() + del invModel + + def unload(self): + self.ignoreAll() + base.cr.estateMgr.leaveEstate() + self.estateOwnerId = None + self.estateZoneId = None + if self.place: + self.place.exit() + self.place.unload() + del self.place + del self.underwaterSound + del self.swimSound + del self.submergeSound + del self.birdSound + del self.cricketSound + for node in self.houseNode: + node.removeNode() + + del self.houseNode + for model in self.houseModels: + model.removeNode() + + del self.houseModels + del self.houseId2house + if self.sunMoonNode: + self.sunMoonNode.removeNode() + del self.sunMoonNode + self.sunMoonNode = None + if self.clouds: + for cloud in self.clouds: + cloud[0].removeNode() + del cloud[1] + + del self.clouds + if self.barrel: + self.barrel.removeNode() + SafeZoneLoader.SafeZoneLoader.unload(self) + return + + def enter(self, requestStatus): + self.estateOwnerId = requestStatus.get('ownerId', base.localAvatar.doId) + base.localAvatar.inEstate = 1 + self.loadCloudPlatforms() + if base.cloudPlatformsEnabled and 0: + self.setCloudSwitch(1) + if self.cloudSwitch: + self.setCloudSwitch(self.cloudSwitch) + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + + def exit(self): + self.ignoreAll() + base.cr.cache.flush() + base.localAvatar.stopChat() + base.localAvatar.inEstate = 0 + SafeZoneLoader.SafeZoneLoader.exit(self) + + def createSafeZone(self, dnaFile): + SafeZoneLoader.SafeZoneLoader.createSafeZone(self, dnaFile) + self.loadHouses() + self.loadSunMoon() + + def loadHouses(self): + for i in range(HouseGlobals.NUM_HOUSE_TYPES): + self.houseModels[i] = loader.loadModel(HouseGlobals.houseModels[i]) + + for i in range(6): + posHpr = HouseGlobals.houseDrops[i] + self.houseNode[i] = self.geom.attachNewNode('esHouse_' + str(i)) + self.houseNode[i].setPosHpr(*posHpr) + + def loadSunMoon(self): + self.sun = loader.loadModel('phase_4/models/props/sun.bam') + self.moon = loader.loadModel('phase_5.5/models/props/moon.bam') + self.sunMoonNode = self.geom.attachNewNode('sunMoon') + self.sunMoonNode.setPosHpr(0, 0, 0, 0, 0, 0) + if self.sun: + self.sun.reparentTo(self.sunMoonNode) + self.sun.setY(270) + self.sun.setScale(2) + self.sun.setBillboardPointEye() + if self.moon: + self.moon.setP(180) + self.moon.reparentTo(self.sunMoonNode) + self.moon.setY(-270) + self.moon.setScale(15) + self.moon.setBillboardPointEye() + self.sunMoonNode.setP(30) + + def enterEstate(self, requestStatus): + self.notify.debug('enterEstate: requestStatus = %s' % requestStatus) + ownerId = requestStatus.get('ownerId') + if ownerId: + self.estateOwnerId = ownerId + zoneId = requestStatus['zoneId'] + self.notify.debug('enterEstate, ownerId = %s, zoneId = %s' % (self.estateOwnerId, zoneId)) + self.accept(self.estateDoneEvent, self.handleEstateDone) + self.place = Estate.Estate(self, self.estateOwnerId, zoneId, self.fsm.getStateNamed('estate'), self.estateDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + self.estateZoneId = zoneId + + def exitEstate(self): + self.notify.debug('exitEstate') + self.ignore(self.estateDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + base.cr.cache.flush() + return + + def handleEstateDone(self, doneStatus = None): + if not doneStatus: + doneStatus = self.place.getDoneStatus() + how = doneStatus['how'] + shardId = doneStatus['shardId'] + hoodId = doneStatus['hoodId'] + zoneId = doneStatus['zoneId'] + avId = doneStatus.get('avId', -1) + ownerId = doneStatus.get('ownerId', -1) + if shardId != None or hoodId != MyEstate: + self.notify.debug('estate done, and we are backing out to a different hood/shard') + self.notify.debug('hoodId = %s, avId = %s' % (hoodId, avId)) + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + return + if how in ['tunnelIn', + 'teleportIn', + 'doorIn', + 'elevatorIn']: + self.notify.debug('staying in estateloader') + self.fsm.request('quietZone', [doneStatus]) + else: + self.notify.error('Exited hood with unexpected mode %s' % how) + return + + def enterHouse(self, requestStatus): + ownerId = requestStatus.get('ownerId') + if ownerId: + self.estateOwnerId = ownerId + self.acceptOnce(self.houseDoneEvent, self.handleHouseDone) + self.place = House.House(self, self.estateOwnerId, self.fsm.getStateNamed('house'), self.houseDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitHouse(self): + self.ignore(self.houseDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleHouseDone(self, doneStatus = None): + if not doneStatus: + doneStatus = self.place.getDoneStatus() + shardId = doneStatus['shardId'] + hoodId = doneStatus['hoodId'] + if shardId != None or hoodId != MyEstate: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + return + how = doneStatus['how'] + if how in ['tunnelIn', + 'teleportIn', + 'doorIn', + 'elevatorIn']: + self.fsm.request('quietZone', [doneStatus]) + else: + self.notify.error('Exited hood with unexpected mode %s' % how) + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def atMyEstate(self): + if self.estateOwnerId != None: + if self.estateOwnerId == base.localAvatar.getDoId(): + return 1 + else: + return 0 + else: + self.notify.warning("We aren't in an estate") + return + + def setHouse(self, houseId): + try: + houseDo = base.cr.doId2do[houseId] + self.enteredHouse = houseDo.house + except KeyError: + self.notify.debug("can't find house: %d" % houseId) + + def startCloudPlatforms(self): + return + if len(self.clouds): + self.cloudTrack = self.__cloudTrack() + self.cloudTrack.loop() + + def stopCloudPlatforms(self): + if self.cloudTrack: + self.cloudTrack.pause() + del self.cloudTrack + self.cloudTrack = None + return + + def __cloudTrack(self): + track = Parallel() + for cloud in self.clouds: + axis = cloud[1] + pos = cloud[0].getPos(render) + newPos = pos + axis * 30 + reversePos = pos - axis * 30 + track.append(Sequence(LerpPosInterval(cloud[0], 10, newPos), LerpPosInterval(cloud[0], 20, reversePos), LerpPosInterval(cloud[0], 10, pos))) + + return track + + def debugGeom(self, decomposed): + print 'numPrimitives = %d' % decomposed.getNumPrimitives() + for primIndex in range(decomposed.getNumPrimitives()): + prim = decomposed.getPrimitive(primIndex) + print 'prim = %s' % prim + print 'isIndexed = %d' % prim.isIndexed() + print 'prim.getNumPrimitives = %d' % prim.getNumPrimitives() + for basicPrim in range(prim.getNumPrimitives()): + print '%d start=%d' % (basicPrim, prim.getPrimitiveStart(basicPrim)) + print '%d end=%d' % (basicPrim, prim.getPrimitiveEnd(basicPrim)) + + def loadOnePlatform(self, version, radius, zOffset, score, multiplier): + self.notify.debug('loadOnePlatform version=%d' % version) + cloud = NodePath('cloud-%d-%d' % (score, multiplier)) + cloudModel = loader.loadModel('phase_5.5/models/estate/bumper_cloud') + cc = cloudModel.copyTo(cloud) + colCube = cc.find('**/collision') + colCube.setName('cloudSphere-0') + dTheta = 2.0 * math.pi / self.numClouds + cloud.reparentTo(self.cloudOrigin) + axes = [Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)] + cloud.setPos(radius * math.cos(version * dTheta), radius * math.sin(version * dTheta), 4 * random.random() + zOffset) + cloud.setScale(4.0) + self.clouds.append([cloud, random.choice(axes)]) + + def loadSkyCollision(self): + plane = CollisionPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 300))) + plane.setTangible(0) + planeNode = CollisionNode('cloudSphere-0') + planeNode.addSolid(plane) + self.cloudOrigin.attachNewNode(planeNode) + + def loadCloudPlatforms(self): + self.cloudOrigin = self.geom.attachNewNode('cloudOrigin') + self.cloudOrigin.setZ(30) + self.loadSkyCollision() + self.numClouds = 12 + pinballScore = PinballScoring[PinballCloudBumperLow] + for i in range(12): + self.loadOnePlatform(i, 40, 0, pinballScore[0], pinballScore[1]) + + pinballScore = PinballScoring[PinballCloudBumperMed] + for i in range(12): + self.loadOnePlatform(i, 60, 40, pinballScore[0], pinballScore[1]) + + pinballScore = PinballScoring[PinballCloudBumperHigh] + for i in range(12): + self.loadOnePlatform(i, 20, 80, pinballScore[0], pinballScore[1]) + + self.cloudOrigin.stash() + + def setCloudSwitch(self, on): + self.cloudSwitch = on + if hasattr(self, 'cloudOrigin'): + if on: + self.cloudOrigin.unstash() + else: + self.cloudOrigin.stash() diff --git a/toontown/estate/EstateManager.py b/toontown/estate/EstateManager.py new file mode 100644 index 0000000..df12ad2 --- /dev/null +++ b/toontown/estate/EstateManager.py @@ -0,0 +1,82 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +import random +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import HouseGlobals +import Estate + +class EstateManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('EstateManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.availableZones = 0 + self.popupInfo = None + return + + def disable(self): + self.notify.debug("i'm disabling EstateManager rightnow.") + self.ignore('getLocalEstateZone') + self.ignoreAll() + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + DistributedObject.DistributedObject.disable(self) + return + + def allocateMyEstateZone(self): + self.getLocalEstateZone(base.localAvatar.getDoId()) + + def getLocalEstateZone(self, avId): + name = '' + if base.localAvatar.doId == avId: + name = base.cr.userName + self.sendUpdate('getEstateZone', [avId, name]) + + def setEstateZone(self, ownerId, zoneId): + self.notify.debug('setEstateZone(%s, %s)' % (ownerId, zoneId)) + messenger.send('setLocalEstateZone', [ownerId, zoneId]) + + def generate(self): + self.notify.debug('BASE: generate') + DistributedObject.DistributedObject.generate(self) + base.cr.estateMgr = self + self.accept('getLocalEstateZone', self.getLocalEstateZone) + self.announceGenerateName = self.uniqueName('generate') + + def setAvHouseId(self, avId, houseIds): + self.notify.debug('setAvHouseId %d' % base.localAvatar.doId) + for av in base.cr.avList: + if av.id == avId: + houseId = houseIds[av.position] + ownerAv = base.cr.doId2do.get(avId) + if ownerAv: + ownerAv.b_setHouseId(houseId) + return + + def sendAvToPlayground(self, avId, retCode): + self.notify.debug('sendAvToPlayground: %d' % avId) + messenger.send('kickToPlayground', [retCode]) + + def leaveEstate(self): + if self.isDisabled(): + self.notify.warning('EstateManager disabled; unable to leave estate.') + return + self.sendUpdate('exitEstate') + + def removeFriend(self, ownerId, avId): + self.notify.debug('removeFriend ownerId = %s, avId = %s' % (ownerId, avId)) + self.sendUpdate('removeFriend', [ownerId, avId]) + + def startAprilFools(self): + if isinstance(base.cr.playGame.getPlace(), Estate.Estate): + base.cr.playGame.getPlace().startAprilFoolsControls() + + def stopAprilFools(self): + if isinstance(base.cr.playGame.getPlace(), Estate.Estate): + base.cr.playGame.getPlace().stopAprilFoolsControls() diff --git a/toontown/estate/FireworkItemPanel.py b/toontown/estate/FireworkItemPanel.py new file mode 100644 index 0000000..1e0384a --- /dev/null +++ b/toontown/estate/FireworkItemPanel.py @@ -0,0 +1,48 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.effects import FireworkGlobals +from toontown.effects import Fireworks +import FireworksGui + +class FireworkItemPanel(DirectFrame): + + def __init__(self, itemName, itemNum, *extraArgs): + self.gui = extraArgs[0][0] + self.type = extraArgs[0][1][itemNum] + self.shootEvent = extraArgs[0][2] + self.name = FireworkGlobals.Names[self.type] + DirectFrame.__init__(self, image=DGG.getDefaultDialogGeom(), image_color=(0.75, 0.75, 0.75, 1), image_scale=(0.25, 0, 0.25), relief=None) + self.initialiseoptions(FireworkItemPanel) + self.load() + return + + def load(self): + self.picture = DirectButton(parent=self, image=(DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom()), relief=None, command=self.__launchFirework, extraArgs=[self.type], image_color=(0.8, 0.9, 1, 1)) + self.picture.setScale(0.2) + self.picture.setPos(0, 0, 0) + self.picture.initialiseoptions(self.picture) + panelWidth = 7 + nameFont = ToontownGlobals.getInterfaceFont() + self.quantityLabel = DirectLabel(parent=self.picture, relief=None, pos=(0, 0, 0.0), scale=0.45, text=self.name, text_scale=0.6, text_fg=(0, 0, 0, 1), text_pos=(0, -.14, 0), text_font=nameFont, text_wordwrap=panelWidth) + return + + def unload(self): + del self.picture + self.quantityLabel.destroy() + del self.quantityLabel + DirectFrame.destroy(self) + + def destroy(self): + self.unload() + + def __launchFirework(self, index): + messenger.send(self.shootEvent, [index]) + + def __dimSky(self): + self.oldSkyScale = base.cr.playGame.hood.loader.sky.getColorScale() + base.cr.playGame.hood.loader.sky.setColorScale(0.3, 0.3, 0.3, 1) + + def __resetSky(self): + base.cr.playGame.hood.loader.sky.setColorScale(self.oldSkyScale) diff --git a/toontown/estate/FireworksGui.py b/toontown/estate/FireworksGui.py new file mode 100644 index 0000000..2e8ec85 --- /dev/null +++ b/toontown/estate/FireworksGui.py @@ -0,0 +1,100 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.gui.DirectScrolledList import * +from toontown.toonbase import ToontownGlobals +import FireworkItemPanel +from direct.directnotify import DirectNotifyGlobal +from toontown.effects import FireworkGlobals +from toontown.effects import Fireworks +NUM_ITEMS_SHOWN = 4 + +class FireworksGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FireworksGui') + + def __init__(self, doneEvent, shootEvent): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=(0, 0.5, 1, 1), geom_scale=(0.43, 1, 1.4), pos=(1.1, 0, 0)) + self.initialiseoptions(FireworksGui) + self.doneEvent = doneEvent + self.shootEvent = shootEvent + self.itemList = [] + self.type = None + self.load() + return + + def load(self): + itemTypes = [0, + 1, + 2, + 3, + 4, + 5] + itemStrings = [] + for i in itemTypes: + itemStrings.append(FireworkGlobals.Names[i]) + + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.panelPicker = DirectScrolledList(parent=self, items=itemStrings, command=self.scrollItem, itemMakeFunction=FireworkItemPanel.FireworkItemPanel, itemMakeExtraArgs=[self, itemTypes, self.shootEvent], numItemsVisible=NUM_ITEMS_SHOWN, incButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(0.5, 1, -1), incButton_pos=(0, 0, -1.08), incButton_image3_color=Vec4(1, 1, 1, 0.3), decButton_image=(gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(0.5, 1, 1), decButton_pos=(0, 0, 0.2), decButton_image3_color=Vec4(1, 1, 1, 0.3)) + self.panelPicker.setPos(-.06, 0, 0.42) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(0.15, 0, -0.62), text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + buttons.removeNode() + self.hilightColor = VBase4(1, 1, 1, 1) + self.bgColor = VBase4(0.8, 0.8, 0.8, 1) + self.colorButtons = [] + for i in Fireworks.colors.keys(): + color = Fireworks.colors[i] + height = 0.07 + paddedHeight = 0.1 + buttonBg = DirectFrame(self, geom=DGG.getDefaultDialogGeom(), geom_scale=paddedHeight, geom_color=self.bgColor, pos=(0.15, 0, 0.5 - (paddedHeight + 0.025) * i), relief=None) + self.initialiseoptions(buttonBg) + button = DirectButton(buttonBg, image=(DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom(), DGG.getDefaultDialogGeom()), relief=None, command=self.__handleColor, extraArgs=[i]) + button.setScale(height) + button.setColor(color) + self.colorButtons.append([button, buttonBg]) + + self.__initColor(0) + return + + def unload(self): + del self.parent + del self.itemList + del self.panelPicker + + def update(self): + pass + + def __cancel(self): + messenger.send(self.doneEvent) + + def __initColor(self, index): + self.colorButtons[index][1]['geom_color'] = self.hilightColor + self.colorButtons[index][1].setScale(1.2) + self.curColor = index + self.fadeColor = 0 + + def __handleColor(self, index): + color = Fireworks.colors[index] + for i in range(len(self.colorButtons)): + self.colorButtons[i][1]['geom_color'] = self.bgColor + self.colorButtons[i][1].setScale(1) + + self.colorButtons[index][1].setScale(1.2) + if index == self.curColor: + self.fadeColor = (self.fadeColor + 1) % len(Fireworks.colors) + else: + self.fadeColor = 0 + self.colorButtons[index][1]['geom_color'] = Fireworks.colors[self.fadeColor] + self.curColor = index + + def scrollItem(self): + pass + + def getCurColor(self): + return (self.curColor, self.fadeColor) diff --git a/toontown/estate/FlowerBase.py b/toontown/estate/FlowerBase.py new file mode 100644 index 0000000..bb12d1a --- /dev/null +++ b/toontown/estate/FlowerBase.py @@ -0,0 +1,50 @@ +import GardenGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class FlowerBase: + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerBase') + + def __init__(self, species, variety): + self.species = species + self.variety = variety + if self.species not in GardenGlobals.PlantAttributes.keys(): + print 'remove me when everyone is updated' + self.species = 56 + species = 56 + + def getSpecies(self): + return self.species + + def setSpecies(self, species): + self.species = species + + def getVariety(self): + return self.variety + + def setVariety(self, variety): + self.variety = variety + + def getVitals(self): + return (self.species, self.variety) + + def getValue(self): + return GardenGlobals.PlantAttributes[self.species]['varieties'][self.variety][2] + + def getSpeciesName(self): + return TTLocalizer.FlowerSpeciesNames[self.species] + + def getVarietyName(self): + return self.getFullName() + + def getFullName(self): + return GardenGlobals.getFlowerVarietyName(self.species, self.variety) + + def getFullNameWithRecipe(self): + name = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + recipeKey = GardenGlobals.PlantAttributes[self.species]['varieties'][self.variety][0] + name += ' (%s)' % GardenGlobals.Recipes[recipeKey]['beans'] + return name + + def __str__(self): + return '%s, value: %s' % (self.getFullName(), self.getValue()) diff --git a/toontown/estate/FlowerBasket.py b/toontown/estate/FlowerBasket.py new file mode 100644 index 0000000..0dd9495 --- /dev/null +++ b/toontown/estate/FlowerBasket.py @@ -0,0 +1,73 @@ +import GardenGlobals +from direct.directnotify import DirectNotifyGlobal +import FlowerBase + +class FlowerBasket: + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerBasket') + + def __init__(self): + self.flowerList = [] + + def __len__(self): + return len(self.flowerList) + + def getFlower(self): + return self.flowerList + + def makeFromNetLists(self, speciesList, varietyList): + self.flowerList = [] + for species, variety in zip(speciesList, varietyList): + self.flowerList.append(FlowerBase.FlowerBase(species, variety)) + + def getNetLists(self): + speciesList = [] + varietyList = [] + for flower in self.flowerList: + speciesList.append(flower.getSpecies()) + varietyList.append(flower.getVariety()) + + return [speciesList, varietyList] + + def hasFlower(self, species, variety): + for flower in self.flowerList: + if flower.getSpecies() == species and flower.getVariety() == variety: + return 1 + + return 0 + + def addFlower(self, species, variety): + self.flowerList.append(FlowerBase.FlowerBase(species, variety)) + return 1 + + def removeFishAtIndex(self, index): + if index >= len(self.flowerList): + return 0 + else: + del self.flowerList[i] + return 1 + + def generateRandomBasket(self): + import random + numFish = random.randint(1, 20) + self.flowerList = [] + for i in range(numFish): + species, variety = GardenGlobals.getRandomFlower() + self.addFlower(species, variety) + + def getTotalValue(self): + value = 0 + for flower in self.flowerList: + value += flower.getValue() + + return value + + def __str__(self): + numFlower = len(self.flowerList) + value = 0 + txt = 'Flower Basket (%s flower):' % numFlower + for flower in self.flowerList: + txt += '\n' + str(flower) + + value = self.getTotalValue() + txt += '\nTotal value: %s' % value + return txt diff --git a/toontown/estate/FlowerBrowser.py b/toontown/estate/FlowerBrowser.py new file mode 100644 index 0000000..dcdc018 --- /dev/null +++ b/toontown/estate/FlowerBrowser.py @@ -0,0 +1,58 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import FlowerSpeciesPanel +import GardenGlobals + +class FlowerBrowser(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerBrowser') + + def __init__(self, parent = aspect2d, **kw): + self.parent = parent + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.3, 1.3, -1.3), None), + ('incButton_pos', (0, 0, -0.525), None), + ('incButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.3, 1.3, 1.3), None), + ('decButton_pos', (0, 0, 0.525), None), + ('decButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('numItemsVisible', 1, None), + ('items', map(str, GardenGlobals.getFlowerSpecies()), None), + ('scrollSpeed', 4, None), + ('itemMakeFunction', FlowerSpeciesPanel.FlowerSpeciesPanel, None), + ('itemMakeExtraArgs', base.localAvatar.flowerCollection, None)) + gui.removeNode() + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FlowerBrowser) + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + return + + def update(self): + pass + + def show(self): + self['items'][self.index].show() + DirectScrolledList.show(self) + + def hide(self): + self['items'][self.index].hide() + DirectScrolledList.hide(self) diff --git a/toontown/estate/FlowerCollection.py b/toontown/estate/FlowerCollection.py new file mode 100644 index 0000000..ce8ad26 --- /dev/null +++ b/toontown/estate/FlowerCollection.py @@ -0,0 +1,74 @@ +import GardenGlobals +from direct.directnotify import DirectNotifyGlobal +import FlowerBase + +class FlowerCollection: + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerCollection') + + def __init__(self): + self.flowerlist = [] + + def __len__(self): + return len(self.flowerlist) + + def getFlower(self): + return self.flowerlist + + def makeFromNetLists(self, speciesList, varietyList): + self.flowerlist = [] + for species, variety in zip(speciesList, varietyList): + self.flowerlist.append(FlowerBase.FlowerBase(species, variety)) + + def getNetLists(self): + speciesList = [] + varietyList = [] + for flower in self.flowerlist: + speciesList.append(flower.getSpecies()) + varietyList.append(flower.getVariety()) + + return [speciesList, varietyList] + + def hasFlower(self, species, variety): + for flower in self.flowerlist: + if flower.getSpecies() == species and flower.getVariety() == variety: + return 1 + + return 0 + + def hasSpecies(self, species): + for flower in self.flowerlist: + if flower.getSpecies() == species: + return 1 + + return 0 + + def getInitialVariety(self, species): + retVal = 100000 + for flower in self.flowerlist: + if flower.getSpecies() == species: + if flower.getVariety() < retVal: + retVal = flower.getVariety() + + if retVal == 100000: + retVal = 0 + return retVal + + def __collect(self, newFlower, updateCollection): + for flower in self.flowerlist: + if flower.getVariety() == newFlower.getVariety() and flower.getSpecies() == newFlower.getSpecies(): + return GardenGlobals.COLLECT_NO_UPDATE + + if updateCollection: + self.flowerlist.append(newFlower) + return GardenGlobals.COLLECT_NEW_ENTRY + + def collectFlower(self, newFlower): + return self.__collect(newFlower, updateCollection=1) + + def __str__(self): + numFlower = len(self.flowerlist) + txt = 'Flower Collection (%s flowers):' % numFlower + for flower in self.flowerlist: + txt += '\n' + str(flower) + + return txt diff --git a/toontown/estate/FlowerPanel.py b/toontown/estate/FlowerPanel.py new file mode 100644 index 0000000..7c5aeaa --- /dev/null +++ b/toontown/estate/FlowerPanel.py @@ -0,0 +1,95 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * +import GardenGlobals +import FlowerPhoto + +class FlowerPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerPanel') + + def __init__(self, flower = None, parent = aspect2d, doneEvent = None, **kw): + optiondefs = (('relief', None, None), + ('state', DGG.DISABLED, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('image_color', ToontownGlobals.GlobalDialogColor, None), + ('image_scale', (0.65, 1, 0.85), None), + ('text', '', None), + ('text_scale', 0.06, None), + ('text_fg', (0, 0, 0, 1), None), + ('text_pos', (0, 0.35, 0), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(FlowerPanel) + self.doneEvent = doneEvent + self.flower = flower + self.parent = parent + self.photo = None + return + + def destroy(self): + if self.photo: + self.photo.destroy() + self.photo = None + self.flower = None + DirectFrame.destroy(self) + self.parent = None + return + + def load(self): + self.weight = DirectLabel(parent=self, pos=(0, 0, -0.28), relief=None, state=DGG.NORMAL, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, 0.0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.value = DirectLabel(parent=self, pos=TTLocalizer.FPvaluePos, relief=None, state=DGG.NORMAL, text='', text_scale=TTLocalizer.FPvalue, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.mystery = DirectLabel(parent=self, pos=(-0.025, 0, -0.055), relief=None, state=DGG.NORMAL, text='?', text_scale=0.25, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.extraLabel = DirectLabel(parent=self, relief=None, state=DGG.NORMAL, text='', text_fg=(0.2, 0.8, 0.4, 1), text_font=ToontownGlobals.getSignFont(), text_scale=0.08, pos=(0, 0, 0.26)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.cancel = DirectButton(parent=self, pos=(0.275, 0, -0.375), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(0.6, 1, 0.6), command=self.handleCancel) + buttons.removeNode() + self.photo = FlowerPhoto.FlowerPhoto(parent=self) + self.update(self.flower) + return + + def update(self, flower): + self.flower = flower + if self.flower == None: + return + self['text'] = self.flower.getFullName() + value = self.flower.getValue() + if value == 1: + self.value['text'] = TTLocalizer.GardenPageValueS % value + else: + self.value['text'] = TTLocalizer.GardenPageValueP % value + self.photo.update(flower.getSpecies(), flower.getVariety()) + return + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def handleCancel(self): + self.hide() + if self.doneEvent: + messenger.send(self.doneEvent) + + def show(self, code = GardenGlobals.FlowerItem): + messenger.send('wakeup') + apply(self.photo.setSwimBounds, self.swimBounds) + apply(self.photo.setSwimColor, self.swimColor) + if code == GardenGlobals.FlowerItem: + self.extraLabel.hide() + elif code == GardenGlobals.FlowerItemNewEntry: + self.extraLabel.show() + self.extraLabel['text'] = TTLocalizer.FloweringNewEntry + self.extraLabel['text_scale'] = 0.08 + self.extraLabel.setPos(0, 0, 0.26) + self.photo.show() + DirectFrame.show(self) + + def hide(self): + self.photo.hide() + DirectFrame.hide(self) diff --git a/toontown/estate/FlowerPhoto.py b/toontown/estate/FlowerPhoto.py new file mode 100644 index 0000000..956327b --- /dev/null +++ b/toontown/estate/FlowerPhoto.py @@ -0,0 +1,202 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.fishing import FishGlobals +import GardenGlobals +from direct.actor import Actor + +class DirectRegion(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectRegion') + + def __init__(self, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('DirectRegion')) + + def destroy(self): + self.unload() + self.parent = None + return + + def setBounds(self, *bounds): + self.bounds = bounds + + def setColor(self, *colors): + self.color = colors + + def show(self): + pass + + def hide(self): + pass + + def load(self): + if not hasattr(self, 'cRender'): + self.cRender = NodePath('fishSwimRender') + self.fishSwimCamera = self.cRender.attachNewNode('fishSwimCamera') + self.cCamNode = Camera('fishSwimCam') + 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.fishSwimCam = self.fishSwimCamera.attachNewNode(self.cCamNode) + cm = CardMaker('displayRegionCard') + apply(cm.setFrame, self.bounds) + self.card = card = self.attachNewNode(cm.generate()) + apply(card.setColor, self.color) + newBounds = card.getTightBounds() + ll = render2d.getRelativePoint(card, newBounds[0]) + ur = render2d.getRelativePoint(card, newBounds[1]) + newBounds = [ll.getX(), + ur.getX(), + ll.getZ(), + ur.getZ()] + newBounds = map(lambda x: max(0.0, min(1.0, (x + 1.0) / 2.0)), newBounds) + self.cDr = base.win.makeDisplayRegion(*newBounds) + self.cDr.setSort(10) + self.cDr.setClearColor(card.getColor()) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setCamera(self.fishSwimCam) + return self.cRender + + def unload(self): + if hasattr(self, 'cRender'): + base.win.removeDisplayRegion(self.cDr) + del self.cRender + del self.fishSwimCamera + del self.cCamNode + del self.cLens + del self.fishSwimCam + del self.cDr + + +class FlowerPhoto(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerPhoto') + + def __init__(self, species = None, variety = None, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('FlowerPhoto')) + self.species = species + self.variety = variety + self.actor = None + self.sound = None + self.soundTrack = None + self.track = None + self.flowerFrame = None + return + + def destroy(self): + self.hide() + if hasattr(self, 'background'): + del self.background + self.fish = None + del self.soundTrack + del self.track + self.parent = None + return + + def update(self, species, variety): + self.species = species + self.variety = variety + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def load(self): + pass + + def makeFlowerFrame(self, actor): + actor.setDepthTest(1) + actor.setDepthWrite(1) + if not hasattr(self, 'flowerDisplayRegion'): + self.flowerDisplayRegion = DirectRegion(parent=self) + apply(self.flowerDisplayRegion.setBounds, self.swimBounds) + apply(self.flowerDisplayRegion.setColor, self.swimColor) + frame = self.flowerDisplayRegion.load() + pitch = frame.attachNewNode('pitch') + rotate = pitch.attachNewNode('rotate') + scale = rotate.attachNewNode('scale') + actor.reparentTo(scale) + bMin, bMax = actor.getTightBounds() + center = (bMin + bMax) / 2.0 + actor.setPos(-center[0], -center[1], -center[2]) + attrib = GardenGlobals.PlantAttributes[self.species] + if attrib.has_key('photoPos'): + self.notify.debug('oldPos = %s' % actor.getPos()) + photoPos = attrib['photoPos'] + self.notify.debug('newPos = %s' % str(photoPos)) + actor.setPos(photoPos[0], photoPos[1], photoPos[2]) + scale.setScale(attrib['photoScale']) + rotate.setH(attrib['photoHeading']) + pitch.setP(attrib['photoPitch']) + pitch.setY(1.75) + return frame + + def loadModel(self, species, variety): + modelName = GardenGlobals.PlantAttributes[species]['fullGrownModel'] + nodePath = loader.loadModel(modelName) + desat = None + flowerColorIndex = GardenGlobals.PlantAttributes[species]['varieties'][variety][1] + colorTuple = GardenGlobals.FlowerColors[flowerColorIndex] + useWilted = 0 + wilt = nodePath.find('**/*wilt*') + bloom = nodePath.find('**/*bloom*') + if useWilted: + wilt.show() + desat = wilt.find('**/*desat*') + bloom.hide() + else: + bloom.show() + desat = bloom.find('**/*desat*') + wilt.hide() + if desat and not desat.isEmpty(): + desat.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + else: + nodePath.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + return nodePath + + def show(self, showBackground = 0): + self.notify.debug('show') + messenger.send('wakeup') + if self.flowerFrame: + if hasattr(self.actor, 'cleanup'): + self.actor.cleanup() + if hasattr(self, 'flowerDisplayRegion'): + self.flowerDisplayRegion.unload() + self.hide() + self.actor = self.loadModel(self.species, self.variety) + self.flowerFrame = self.makeFlowerFrame(self.actor) + if showBackground: + if not hasattr(self, 'background'): + background = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + background = background.find('**/Fish_BG') + self.background = background + self.background.setPos(0, 15, 0) + self.background.setScale(11) + self.background.reparentTo(self.flowerFrame) + + def hide(self): + if hasattr(self, 'flowerDisplayRegion'): + self.flowerDisplayRegion.unload() + if self.actor: + if hasattr(self.actor, 'stop'): + self.actor.stop() + self.actor.hide() + if self.sound: + self.sound.stop() + self.sound = None + if self.soundTrack: + self.soundTrack.pause() + self.soundTrack = None + if self.track: + self.track.pause() + self.track = None + return + + def changeVariety(self, variety): + self.variety = variety diff --git a/toontown/estate/FlowerPicker.py b/toontown/estate/FlowerPicker.py new file mode 100644 index 0000000..1ed4f5b --- /dev/null +++ b/toontown/estate/FlowerPicker.py @@ -0,0 +1,126 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import FlowerPanel + +class FlowerPicker(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerPicker') + + def __init__(self, parent = aspect2d, **kw): + self.flowerList = [] + self.parent = parent + self.shown = 0 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.6, 1.6, -1.6), None), + ('incButton_pos', (0.16, 0, -0.47), None), + ('incButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.6, 1.6, 1.6), None), + ('decButton_pos', (0.16, 0, 0.09), None), + ('decButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('itemFrame_pos', (-0.025, 0, 0), None), + ('itemFrame_scale', 0.54, None), + ('itemFrame_relief', None, None), + ('itemFrame_frameSize', (-0.05, + 0.75, + -0.75, + 0.05), None), + ('numItemsVisible', 10, None), + ('items', [], None)) + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FlowerPicker) + self.flowerGui = loader.loadModel('phase_3.5/models/gui/fishingBook').find('**/bucket') + self.flowerGui.find('**/fram1').removeNode() + self.flowerGui.find('**/bubble').removeNode() + self.flowerGui.find('**/bucket').removeNode() + self.flowerGui.reparentTo(self, -1) + self.flowerGui.setPos(0.63, 0.1, -0.1) + self.flowerGui.setScale(0.035) + self.basketGui = loader.loadModel('phase_5.5/models/estate/flowerBasket') + self.basketGui.reparentTo(self, -2) + self.basketGui.setPos(0.17, 0.1, 0.0) + self.basketGui.setScale(1.25) + self.info = DirectLabel(parent=self, relief=None, text='', text_scale=TTLocalizer.FPinfo, pos=(0.18, 0, -0.67)) + self.flowerPanel = FlowerPanel.FlowerPanel(parent=self) + self.flowerPanel.setSwimBounds(-0.3, 0.3, -0.235, 0.25) + self.flowerPanel.setSwimColor(1.0, 1.0, 0.74901, 1.0) + gui.removeNode() + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + self.flowerList = [] + self.flowerPanel = None + return + + def hideFlowerPanel(self): + self.flowerPanel.hide() + + def hide(self): + if not hasattr(self, 'loaded'): + return + self.hideFlowerPanel() + DirectScrolledList.hide(self) + self.shown = 0 + + def show(self): + if not hasattr(self, 'loaded'): + self.load() + self.updatePanel() + DirectScrolledList.show(self) + self.shown = 1 + + def load(self): + self.loaded = 1 + self.flowerPanel.load() + self.flowerPanel.setPos(1.05, 0, 0.1) + self.flowerPanel.setScale(0.9) + + def update(self, newFloweres): + for flower, flowerButton in self.flowerList[:]: + self.removeItem(flowerButton) + flowerButton.destroy() + self.flowerList.remove([flower, flowerButton]) + + for flower in newFloweres: + flowerButton = self.makeFlowerButton(flower) + self.addItem(flowerButton) + self.flowerList.append([flower, flowerButton]) + + value = 0 + for flower in newFloweres: + value += flower.getValue() + + maxFlower = base.localAvatar.getMaxFlowerBasket() + self.info['text'] = TTLocalizer.FlowerPickerTotalValue % (len(newFloweres), maxFlower, value) + if self.shown: + self.updatePanel() + + def updatePanel(self): + if len(self.flowerList) >= 1: + self.showFlowerPanel(self.flowerList[0][0]) + else: + self.hideFlowerPanel() + + def makeFlowerButton(self, flower): + return DirectScrolledListItem(parent=self, relief=None, text=flower.getFullName(), text_scale=0.07, text_align=TextNode.ALeft, text1_fg=Vec4(1, 1, 0, 1), text2_fg=Vec4(0.5, 0.9, 1, 1), text3_fg=Vec4(0.4, 0.8, 0.4, 1), command=self.showFlowerPanel, extraArgs=[flower]) + + def showFlowerPanel(self, flower): + self.flowerPanel.update(flower) + self.flowerPanel.show() + messenger.send('wakeup') diff --git a/toontown/estate/FlowerSellGUI.py b/toontown/estate/FlowerSellGUI.py new file mode 100644 index 0000000..ae457dc --- /dev/null +++ b/toontown/estate/FlowerSellGUI.py @@ -0,0 +1,51 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +import FlowerBase +import FlowerPicker + +class FlowerSellGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerGui') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(2.0, 1, 1.5), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text='', text_wordwrap=26, text_scale=TTLocalizer.FSGUIdirectFrame, text_pos=(0, 0.65)) + self.initialiseoptions(FlowerSellGUI) + self.doneEvent = doneEvent + self.picker = FlowerPicker.FlowerPicker(self) + self.picker.load() + self.picker.setPos(-0.59, 0, 0.03) + self.picker.setScale(0.93) + newBasketFlower = base.localAvatar.flowerBasket.getFlower() + self.picker.update(newBasketFlower) + self.picker.show() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + 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')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(0.3, 0, -0.58), text=TTLocalizer.FlowerGuiCancel, text_scale=TTLocalizer.FSGUIcancelButton, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.6, 0, -0.58), text=TTLocalizer.FlowerGuiOk, text_scale=TTLocalizer.FSGUIokButton, text_pos=(0, -0.1), command=self.__sellFlower) + buttons.removeNode() + self.__updateFlowerValue() + base.cr.playGame.getPlace().detectedFlowerSellUse() + return + + def destroy(self): + DirectFrame.destroy(self) + base.cr.playGame.getPlace().detectedFlowerSellDone() + + def __cancel(self): + messenger.send(self.doneEvent, [0]) + + def __sellFlower(self): + messenger.send(self.doneEvent, [1]) + + def __updateFlowerValue(self): + flowerBasket = base.localAvatar.getFlowerBasket() + num = len(flowerBasket) + value = flowerBasket.getTotalValue() + self['text'] = TTLocalizer.FlowerBasketValue % {'name': base.localAvatar.getName(), + 'num': num, + 'value': value} + self.setText() diff --git a/toontown/estate/FlowerSpeciesPanel.py b/toontown/estate/FlowerSpeciesPanel.py new file mode 100644 index 0000000..104098f --- /dev/null +++ b/toontown/estate/FlowerSpeciesPanel.py @@ -0,0 +1,153 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import GardenGlobals +import FlowerPhoto +from toontown.estate import BeanRecipeGui + +class FlowerSpeciesPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FlowerSpeciesPanel') + + def __init__(self, species = None, itemIndex = 0, *extraArgs): + flowerGui = loader.loadModel('phase_3.5/models/gui/fishingBook') + albumGui = flowerGui.find('**/photo_frame1') + pictureGroup = albumGui.attachNewNode('PictureGroup') + hideList = ['corner_backs', + 'shadow', + 'bg', + 'corners', + 'picture'] + for name in hideList: + temp = flowerGui.find('**/%s' % name) + if not temp.isEmpty(): + temp.wrtReparentTo(pictureGroup) + + pictureGroup.setPos(0, 0, 1.0) + albumGui.find('**/arrows').removeNode() + optiondefs = (('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', albumGui, None), + ('image_scale', (0.025, 0.025, 0.025), None), + ('image_pos', (0, 1, 0), None), + ('text', TTLocalizer.FlowerUnknown, None), + ('text_scale', 0.065, None), + ('text_fg', (0.2, 0.1, 0.0, 1), None), + ('text_pos', (-0.5, -0.34), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None), + ('text_align', TextNode.ALeft, None)) + self.defineoptions({}, optiondefs) + DirectFrame.__init__(self) + self.initialiseoptions(FlowerSpeciesPanel) + self.flowerPanel = None + self.species = None + self.variety = 0 + self.flowerCollection = extraArgs[0] + self.setSpecies(int(species)) + self.setScale(1.2) + albumGui.removeNode() + self.beanRecipeGui = None + return + + def destroy(self): + if self.flowerPanel: + self.flowerPanel.destroy() + del self.flowerPanel + self.flowerCollection = None + self.cleanupBeanRecipeGui() + DirectFrame.destroy(self) + return + + def load(self): + pass + + def setSpecies(self, species): + if self.species == species: + return + self.species = species + if self.species != None: + if self.flowerPanel: + self.flowerPanel.destroy() + varietyToUse = self.flowerCollection.getInitialVariety(self.species) + self.variety = varietyToUse + self.flowerPanel = FlowerPhoto.FlowerPhoto(species=self.species, variety=varietyToUse, parent=self) + zAdj = 0.0131 + xAdj = -0.002 + self.flowerPanel.setPos(-0.229 + xAdj, 1, -0.01 + zAdj) + self.flowerPanel.setSwimBounds(-0.2461, 0.2367, -0.207 + zAdj, 0.2664 + zAdj) + self.flowerPanel.setSwimColor(0.75, 0.75, 0.75, 1.0) + varietyList = GardenGlobals.getFlowerVarieties(self.species) + self.speciesLabels = [] + offset = 0.075 + startPos = len(varietyList) / 2 * offset + if not len(varietyList) % 2: + startPos -= offset / 2 + for variety in range(len(varietyList)): + label = DirectButton(parent=self, frameSize=(0, + 0.445, + -0.02, + 0.04), relief=None, state=DGG.DISABLED, pos=(0.06, 0, startPos - variety * offset), text=TTLocalizer.FlowerUnknown, text_fg=(0.2, 0.1, 0.0, 1), text_scale=(0.045, 0.045, 0.45), text_align=TextNode.ALeft, text_font=ToontownGlobals.getInterfaceFont(), command=self.changeVariety, extraArgs=[variety], 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)) + self.speciesLabels.append(label) + + return + + def show(self): + self.update() + DirectFrame.show(self) + + def hide(self): + if self.flowerPanel is not None: + self.flowerPanel.hide() + if self.beanRecipeGui is not None: + self.beanRecipeGui.hide() + DirectFrame.hide(self) + return + + def showRecipe(self): + if base.localAvatar.flowerCollection.hasSpecies(self.species): + self['text'] = TTLocalizer.FlowerSpeciesNames[self.species] + if base.localAvatar.flowerCollection.hasFlower(self.species, self.variety): + name = GardenGlobals.getFlowerVarietyName(self.species, self.variety) + recipeKey = GardenGlobals.PlantAttributes[self.species]['varieties'][self.variety][0] + self['text'] = name + self.createBeanRecipeGui(GardenGlobals.Recipes[recipeKey]['beans']) + else: + self.cleanupBeanRecipeGui() + else: + self['text'] = TTLocalizer.FlowerUnknown + self.cleanupBeanRecipeGui() + + def update(self): + if base.localAvatar.flowerCollection.hasSpecies(self.species): + self.flowerPanel.show(showBackground=0) + self['text'] = TTLocalizer.FlowerSpeciesNames[self.species] + for variety in range(len(GardenGlobals.getFlowerVarieties(self.species))): + if base.localAvatar.flowerCollection.hasFlower(self.species, variety): + name = GardenGlobals.getFlowerVarietyName(self.species, variety) + self.speciesLabels[variety]['text'] = name + self.speciesLabels[variety]['state'] = DGG.NORMAL + + self.showRecipe() + + def changeVariety(self, variety): + self.variety = variety + self.flowerPanel.changeVariety(variety) + self.flowerPanel.show() + self.showRecipe() + + def createBeanRecipeGui(self, recipe): + if self.beanRecipeGui: + self.beanRecipeGui.destroy() + pos1 = (-0.2, 0, -0.365) + pos2 = (-0.46, 0, 0.3) + pos3 = (-0.46, 0, -0.3) + pos4 = (-0.6, 0, -0.27) + self.beanRecipeGui = BeanRecipeGui.BeanRecipeGui(aspect2dp, recipe, pos=pos4, scale=1.3, frameColor=(0.8, 0.8, 0.8, 1.0)) + + def cleanupBeanRecipeGui(self): + if self.beanRecipeGui: + self.beanRecipeGui.destroy() + self.beanRecipeGui = None + return diff --git a/toontown/estate/GameSprite.py b/toontown/estate/GameSprite.py new file mode 100644 index 0000000..17c720d --- /dev/null +++ b/toontown/estate/GameSprite.py @@ -0,0 +1,87 @@ +import math + +class GameSprite: + colorRed = (1, 0, 0, 1) + colorBlue = (0, 0, 1, 1) + colorGreen = (0, 1, 0, 1) + colorGhostRed = (1, 0, 0, 0.5) + colorGhostBlue = (0, 0, 1, 0.5) + colorGhostGreen = (0, 1, 0, 0.5) + colorWhite = (1, 1, 1, 1) + colorBlack = (0, 0, 0, 1.0) + colorShadow = (0, 0, 0, 0.5) + + def __init__(self, nodeObj, colorType = 0, foundation = 0): + self.nodeObj = nodeObj + self.foundation = foundation + self.velX = 0 + self.velZ = 0 + self.prevX = 0 + self.prevZ = 0 + self.isActive = 1 + self.size = 0.04 + self.isQue = 0 + self.colorType = colorType + if not foundation: + if colorType == 0: + self.setColor(GameSprite.colorGhostRed) + elif colorType == 1: + self.setColor(GameSprite.colorGhostBlue) + elif colorType == 2: + self.setColor(GameSprite.colorGhostGreen) + elif colorType == 0: + self.setColor(GameSprite.colorRed) + elif colorType == 1: + self.setColor(GameSprite.colorBlue) + elif colorType == 2: + self.setColor(GameSprite.colorGreen) + self.markedForDeath = 0 + + def delete(self): + self.nodeObj.destroy() + + def run(self, timeDelta): + if self.isActive and not self.isQue: + self.prevX = self.nodeObj.getX() + self.prevZ = self.nodeObj.getZ() + self.setX(self.getX() + self.velX * timeDelta) + self.setZ(self.getZ() + self.velZ * timeDelta) + self.velX = self.velX * (1 - timeDelta * 4) + self.velZ = self.velZ * (1 - timeDelta * 4) + + def setActive(self, active): + if active: + self.isActive = 1 + else: + self.isActive = 0 + self.velX = 0 + self.velZ = 0 + + def getX(self): + return self.nodeObj.getX() + + def getZ(self): + return self.nodeObj.getZ() + + def setX(self, x): + self.prevX = self.nodeObj.getX() + self.nodeObj.setX(x) + + def setZ(self, z): + self.prevZ = self.nodeObj.getZ() + self.nodeObj.setZ(z) + + def addForce(self, force, direction): + if self.isActive: + forceX = math.cos(direction) * force + forceZ = math.sin(direction) * force + self.velX += forceX + self.velZ += forceZ + + def setColor(self, trip): + self.nodeObj.setColorScale(trip[0], trip[1], trip[2], trip[3]) + + def collide(self): + if self.isActive: + self.setX(self.prevX) + self.setZ(self.prevZ) diff --git a/toontown/estate/GardenDropGame.py b/toontown/estate/GardenDropGame.py new file mode 100644 index 0000000..2d9e760 --- /dev/null +++ b/toontown/estate/GardenDropGame.py @@ -0,0 +1,530 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.gui.DirectScrolledList import * +from direct.distributed.ClockDelta import * +from toontown.toontowngui import TTDialog +import math +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from toontown.toonbase import TTLocalizer +import random +import random +import cPickle +from direct.showbase import PythonUtil +import GameSprite +from math import pi +import GardenProgressMeter + +class GardenDropGame(DirectObject.DirectObject): + + def __init__(self): + self.acceptErrorDialog = None + self.doneEvent = 'game Done' + self.sprites = [] + self.load() + thing = self.model.find('**/item_board') + self.block = self.model1.find('**/minnieCircle') + self.colorRed = (1, 0, 0, 1) + self.colorBlue = (0, 0, 1, 1) + self.colorGreen = (0, 1, 0, 1) + self.colorGhostRed = (1, 0, 0, 0.5) + self.colorGhostBlue = (0, 0, 1, 0.5) + self.colorGhostGreen = (0, 1, 0, 0.5) + self.colorWhite = (1, 1, 1, 1) + self.colorBlack = (0, 0, 0, 1.0) + self.colorShadow = (0, 0, 0, 0.5) + self.lastTime = None + self.running = 0 + self.massCount = 0 + self.foundCount = 0 + self.maxX = 0.47 + self.minX = -0.47 + self.maxZ = 0.65 + self.minZ = -0.1 + self.newBallX = 0.0 + self.newBallZ = 0.6 + self.rangeX = self.maxX - self.minX + self.rangeZ = self.maxZ - self.minZ + size = 0.085 + sizeZ = size * 0.8 + gX = int(self.rangeX / size) + gZ = int(self.rangeZ / sizeZ) + self.maxX = self.minX + gX * size + self.maxZ = self.minZ + gZ * sizeZ + self.controlOffsetX = 0.0 + self.controlOffsetZ = 0.0 + self.queExtent = 3 + print 'Grid Dimensions X%s Z%s' % (gX, gZ) + self.grid = [] + self.gridDimX = gX + self.gridDimZ = gZ + self.gridBrick = False + base.gardenGame = self + for countX in range(self.gridDimX): + newRow = [] + for countZ in range(self.gridDimZ): + offset = 0 + if countZ % 2 == 0: + offset = size / 2 + newRow.append([None, countX * size + self.minX + offset, countZ * sizeZ + self.minZ]) + + self.grid.append(newRow) + + self.controlSprite = None + self.cogSprite = self.addUnSprite(self.block, posX=0.25, posZ=0.5) + self.cogSprite.setColor(self.colorShadow) + for ball in range(0, 3): + place = random.random() * self.rangeX + newSprite = self.addSprite(self.block, size=0.5, posX=self.minX + place, posZ=0.0, found=1) + self.stickInGrid(newSprite, 1) + + self.queBall = self.addSprite(self.block, posX=0.25, posZ=0.5, found=0) + self.queBall.setColor(self.colorWhite) + self.queBall.isQue = 1 + self.matchList = [] + self.newBallTime = 1.0 + self.newBallCountUp = 0.0 + self.cogX = 0 + self.cogZ = 0 + self.__run() + return + + def findGrid(self, x, z, force = 0): + currentClosest = None + currentDist = 10000000 + for countX in range(self.gridDimX): + for countZ in range(self.gridDimZ): + testDist = self.testPointDistanceSquare(x, z, self.grid[countX][countZ][1], self.grid[countX][countZ][2]) + if self.grid[countX][countZ][0] == None and testDist < currentDist and (force or self.hasNeighbor(countX, countZ)): + currentClosest = self.grid[countX][countZ] + self.closestX = countX + self.closestZ = countZ + currentDist = testDist + + return currentClosest + + def hasNeighbor(self, cellX, cellZ): + gotNeighbor = 0 + if cellZ % 2 == 0: + if self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ - 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX + 1, cellZ)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ + 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX, cellZ - 1)): + gotNeighbor = 1 + elif self.testGridfull(self.getValidGrid(cellX - 1, cellZ - 1)): + gotNeighbor = 1 + return gotNeighbor + + def clearMatchList(self): + for entry in self.matchList: + gridEntry = self.grid[entry[0]][entry[1]] + sprite = gridEntry[0] + gridEntry[0] = None + sprite.markedForDeath = 1 + + return + + def createMatchList(self, x, z): + self.matchList = [] + self.fillMatchList(x, z) + + def fillMatchList(self, cellX, cellZ): + if (cellX, cellZ) in self.matchList: + return + self.matchList.append((cellX, cellZ)) + colorType = self.grid[cellX][cellZ][0].colorType + if cellZ % 2 == 0: + if self.getColorType(cellX - 1, cellZ) == colorType: + self.fillMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) == colorType: + self.fillMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) == colorType: + self.fillMatchList(cellX, cellZ + 1) + if self.getColorType(cellX + 1, cellZ + 1) == colorType: + self.fillMatchList(cellX + 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) == colorType: + self.fillMatchList(cellX, cellZ - 1) + if self.getColorType(cellX + 1, cellZ - 1) == colorType: + self.fillMatchList(cellX + 1, cellZ - 1) + else: + if self.getColorType(cellX - 1, cellZ) == colorType: + self.fillMatchList(cellX - 1, cellZ) + if self.getColorType(cellX + 1, cellZ) == colorType: + self.fillMatchList(cellX + 1, cellZ) + if self.getColorType(cellX, cellZ + 1) == colorType: + self.fillMatchList(cellX, cellZ + 1) + if self.getColorType(cellX - 1, cellZ + 1) == colorType: + self.fillMatchList(cellX - 1, cellZ + 1) + if self.getColorType(cellX, cellZ - 1) == colorType: + self.fillMatchList(cellX, cellZ - 1) + if self.getColorType(cellX - 1, cellZ - 1) == colorType: + self.fillMatchList(cellX - 1, cellZ - 1) + + def testGridfull(self, cell): + if not cell: + return 0 + elif cell[0] != None: + return 1 + else: + return 0 + return + + def getValidGrid(self, x, z): + if x < 0 or x >= self.gridDimX: + return None + elif z < 0 or z >= self.gridDimZ: + return None + else: + return self.grid[x][z] + return None + + def getColorType(self, x, z): + if x < 0 or x >= self.gridDimX: + return -1 + elif z < 0 or z >= self.gridDimZ: + return -1 + elif self.grid[x][z][0] == None: + return -1 + else: + return self.grid[x][z][0].colorType + return + + def findGridCog(self): + self.cogX = 0 + self.cogZ = 0 + self.massCount = 0 + for row in self.grid: + for cell in row: + if cell[0] != None: + self.cogX += cell[1] + self.cogZ += cell[2] + self.massCount += 1 + + if self.massCount > 0: + self.cogX = self.cogX / self.massCount + self.cogZ = self.cogZ / self.massCount + self.cogSprite.setX(self.cogX) + self.cogSprite.setZ(self.cogZ) + else: + self.doOnClearGrid() + return + + def doOnClearGrid(self): + secondSprite = self.addSprite(self.block, posX=self.newBallX, posZ=0.0, found=1) + secondSprite.addForce(0, 1.55 * pi) + self.stickInGrid(secondSprite, 1) + + def findGrid2(self, x, z): + rangeX = self.maxX - self.minX + rangeZ = self.maxZ - self.minZ + framedX = x - self.minX + framedZ = z - self.minZ + tileDimX = rangeX / self.gridDimX + tileDimZ = rangeZ / self.gridDimZ + tileX = int(framedX / tileDimX) + tileZ = int(framedZ / tileDimZ) + print 'find Grid tileX%s tileZ%s' % (tileX, tileZ) + return (tileX, tileZ) + + def findPos(self, x, z): + rangeX = self.maxX - self.minX + rangeZ = self.maxZ - self.minZ + tileDimX = rangeX / self.gridDimX + tileDimZ = rangeZ / self.gridDimZ + posX = tileDimX * x + self.minX + posZ = tileDimZ * z + self.minZ + print 'find Pos X%s Z%s' % (posX, posZ) + return (posX, posZ) + + def placeIntoGrid(self, sprite, x, z): + if self.grid[x][z][0] == None: + self.grid[x][z][0] = sprite + sprite.setActive(0) + newX, newZ = self.findPos(x, z) + sprite.setX(newX) + sprite.setZ(newZ) + print 'Setting Final Pos X%s Z%s' % (newX, newZ) + else: + self.placeIntoGrid(sprite, x + 1, z - 1) + return + + def stickInGrid(self, sprite, force = 0): + if sprite.isActive and not sprite.isQue: + gridCell = self.findGrid(sprite.getX(), sprite.getZ(), force) + if gridCell: + gridCell[0] = sprite + sprite.setActive(0) + sprite.setX(gridCell[1]) + sprite.setZ(gridCell[2]) + self.createMatchList(self.closestX, self.closestZ) + if len(self.matchList) >= 3: + self.clearMatchList() + self.findGridCog() + + def stickInGrid2(self, sprite): + if sprite.isActive and not sprite.isQue: + tileX, tileZ = self.findGrid(sprite.getX(), sprite.getZ()) + self.placeIntoGrid(sprite, tileX, tileZ) + sprite.isActive = 0 + + def load(self): + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel') + model1 = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + self.model = model + self.model1 = model1 + 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)) + 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='Exit Mini Game', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01), command=self.__handleExit) + return + + def unload(self): + self.frame.destroy() + del self.frame + if self.acceptErrorDialog: + self.acceptErrorDialog.cleanup() + self.acceptErrorDialog = None + taskMgr.remove('gameTask') + self.ignoreAll() + return + + def show(self): + self.frame.show() + + def hide(self): + self.frame.hide() + + def __handleExit(self): + self.__acceptExit() + + def __acceptExit(self, buttonValue = None): + if hasattr(self, 'frame'): + self.hide() + self.unload() + messenger.send(self.doneEvent) + + def addSprite(self, image, size = 0.5, posX = 0, posZ = 0, found = 0): + nodeObj = DirectLabel(parent=self.frame, relief=None, image=image, pos=(posX, 0.0, posZ), scale=size, image_color=(1.0, 1.0, 1.0, 1)) + colorChoice = random.choice(range(0, 3)) + newSprite = GameSprite.GameSprite(nodeObj, colorChoice, found) + self.sprites.append(newSprite) + if found: + self.foundCount += 1 + return newSprite + + def addUnSprite(self, image, size = 0.5, posX = 0, posZ = 0): + nodeObj = DirectLabel(parent=self.frame, relief=None, image=image, pos=(posX, 0.0, posZ), scale=size, image_color=(1.0, 1.0, 1.0, 1)) + newSprite = GameSprite.GameSprite(nodeObj) + newSprite = GameSprite.GameSprite(nodeObj) + return newSprite + + def __run(self, cont = 1): + if self.lastTime == None: + self.lastTime = globalClock.getRealTime() + timeDelta = globalClock.getRealTime() - self.lastTime + self.lastTime = globalClock.getRealTime() + self.newBallCountUp += timeDelta + if base.mouseWatcherNode.hasMouse(): + x = base.mouseWatcherNode.getMouseX() + y = base.mouseWatcherNode.getMouseY() + self.queBall.setX(x) + self.queBall.setZ(y) + for sprite in self.sprites: + sprite.run(timeDelta) + if sprite.getX() > self.maxX: + sprite.setX(self.maxX) + sprite.velX = -sprite.velX + if sprite.getX() < self.minX: + sprite.setX(self.minX) + sprite.velX = -sprite.velX + if sprite.getZ() > self.maxZ: + sprite.setZ(self.maxZ) + sprite.velZ = -sprite.velZ + if sprite.getZ() < self.minZ: + self.stickInGrid(sprite, 1) + if sprite.isActive: + sprite.addForce(timeDelta * 0.9, pi * 1.5) + + self.queBall.velX = (self.queBall.getX() - self.queBall.prevX) / timeDelta + self.queBall.velZ = (self.queBall.getZ() - self.queBall.prevZ) / timeDelta + self.__colTest() + for sprite in self.sprites: + if sprite.markedForDeath: + if sprite.foundation: + self.foundCount -= 1 + self.sprites.remove(sprite) + sprite.delete() + + if self.controlSprite == None: + self.addControlSprite(self.newBallX, self.newBallZ) + self.newBallCountUp = 0.0 + if self.newBallCountUp >= self.newBallTime: + self.addControlSprite(self.newBallX, self.newBallZ) + self.newBallCountUp = 0.0 + if not self.controlSprite.isActive: + self.controlSprite = None + if self.foundCount <= 0: + self.__handleWin() + if cont and not self.running: + taskMgr.add(self.__run, 'gameTask') + self.running = 1 + return Task.cont + + def __handleWin(self): + GardenProgressMeter.GardenProgressMeter() + self.__handleExit() + + def addControlSprite(self, x = 0.0, z = 0.0): + newSprite = self.addSprite(self.block, posX=x, posZ=z) + self.controlSprite = newSprite + + def __colTest(self): + if not hasattr(self, 'tick'): + self.tick = 0 + self.tick += 1 + if self.tick > 5: + self.tick = 0 + sizeSprites = len(self.sprites) + for movingSpriteIndex in range(len(self.sprites)): + for testSpriteIndex in range(movingSpriteIndex, len(self.sprites)): + movingSprite = self.getSprite(movingSpriteIndex) + testSprite = self.getSprite(testSpriteIndex) + if testSprite and movingSprite: + if movingSpriteIndex != testSpriteIndex and (movingSprite.isActive or testSprite.isActive): + if movingSprite.isQue or testSprite.isQue: + if self.testDistance(movingSprite.nodeObj, testSprite.nodeObj) < self.queExtent * (movingSprite.size + testSprite.size): + self.push(movingSprite, testSprite) + elif self.testDistance(movingSprite.nodeObj, testSprite.nodeObj) < movingSprite.size + testSprite.size: + if not (movingSprite.isActive and testSprite.isActive): + self.__collide(movingSprite, testSprite) + if self.tick == 5: + pass + + def getSprite(self, spriteIndex): + if spriteIndex >= len(self.sprites) or self.sprites[spriteIndex].markedForDeath: + return None + else: + return self.sprites[spriteIndex] + return None + + def testDistance(self, nodeA, nodeB): + distX = nodeA.getX() - nodeB.getX() + distZ = nodeA.getZ() - nodeB.getZ() + distC = distX * distX + distZ * distZ + dist = math.sqrt(distC) + return dist + + def testPointDistance(self, x1, z1, x2, z2): + distX = x1 - x2 + distZ = z1 - z2 + distC = distX * distX + distZ * distZ + dist = math.sqrt(distC) + if dist == 0: + dist = 1e-10 + return dist + + def testPointDistanceSquare(self, x1, z1, x2, z2): + distX = x1 - x2 + distZ = z1 - z2 + distC = distX * distX + distZ * distZ + if distC == 0: + distC = 1e-10 + return distC + + def angleTwoSprites(self, sprite1, sprite2): + x1 = sprite1.getX() + z1 = sprite1.getZ() + x2 = sprite2.getX() + z2 = sprite2.getZ() + x = x2 - x1 + z = z2 - z1 + angle = math.atan2(-x, z) + return angle + pi * 0.5 + + def angleTwoPoints(self, x1, z1, x2, z2): + x = x2 - x1 + z = z2 - z1 + angle = math.atan2(-x, z) + return angle + pi * 0.5 + + def __collide(self, move, test): + queHit = 0 + if move.isQue: + que = move + hit = test + queHit = 1 + elif test.isQue: + que = test + hit = move + queHit = 1 + else: + test.velX = 0 + test.velZ = 0 + move.velX = 0 + move.velZ = 0 + test.collide() + move.collide() + self.stickInGrid(move) + self.stickInGrid(test) + if queHit: + forceM = 0.1 + distX = que.getX() - hit.getX() + distZ = que.getZ() - hit.getZ() + + def push(self, move, test): + queHit = 0 + if move.isQue: + que = move + hit = test + queHit = 1 + elif test.isQue: + que = test + hit = move + queHit = 1 + if queHit: + forceM = 0.1 + dist = self.testDistance(move.nodeObj, test.nodeObj) + if abs(dist) < self.queExtent * que.size and abs(dist) > 0: + scaleSize = self.queExtent * que.size * 0.5 + distFromPara = abs(abs(dist) - scaleSize) + force = (scaleSize - distFromPara) / scaleSize * (dist / abs(dist)) + angle = self.angleTwoSprites(que, hit) + if angle < 0: + angle = angle + 2 * pi + if angle > pi * 2.0: + angle = angle - 2 * pi + newAngle = pi * 1.0 + if angle > pi * 1.5 or angle < pi * 0.5: + newAngle = pi * 0.0 + hit.addForce(forceM * force, newAngle) diff --git a/toontown/estate/GardenGlobals.py b/toontown/estate/GardenGlobals.py new file mode 100644 index 0000000..dfe4ff6 --- /dev/null +++ b/toontown/estate/GardenGlobals.py @@ -0,0 +1,1498 @@ +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +import random +FLOWERS_PER_BONUS = 10 +ACCELERATOR_USED_FROM_SHTIKER_BOOK = True +COLLECT_NO_UPDATE = 0 +COLLECT_NEW_ENTRY = 1 +gardenNotify = DirectNotifyGlobal.directNotify.newCategory('GardenGlobals') +FlowerItem = 2 +FlowerItemNewEntry = 9 +INVALID_TYPE = -1 +GAG_TREE_TYPE = 0 +FLOWER_TYPE = 1 +STATUARY_TYPE = 2 +WATERING_CAN_SMALL = 0 +WATERING_CAN_MEDIUM = 1 +WATERING_CAN_LARGE = 2 +WATERING_CAN_HUGE = 3 +MAX_WATERING_CANS = 4 +WateringCanAttributes = {0: {'numBoxes': 2, + 'skillPts': 100, + 'name': TTLocalizer.WateringCanSmall}, + 1: {'numBoxes': 2, + 'skillPts': 200, + 'name': TTLocalizer.WateringCanMedium}, + 2: {'numBoxes': 2, + 'skillPts': 400, + 'name': TTLocalizer.WateringCanLarge}, + 3: {'numBoxes': 2, + 'skillPts': 1000, + 'name': TTLocalizer.WateringCanHuge}} +WateringMult = 2 + +def getWateringCanPower(wateringCan, wateringCanSkill): + numBoxes = 0 + for curWateringCan in range(wateringCan + 1): + wateringCanAttrib = WateringCanAttributes[curWateringCan] + curBoxes = wateringCanAttrib['numBoxes'] + skill = wateringCanAttrib['skillPts'] + if wateringCanSkill >= skill: + if curWateringCan == wateringCan: + gardenNotify.warning("this shouldn't happen wateringCanSkill %d >= skill %d" % (wateringCanSkill, skill)) + wateringCanSkill = skill - 1 + if curWateringCan == wateringCan: + skillPtPerBox = skill / curBoxes + numBoxes += 1 + int(wateringCanSkill) / int(skillPtPerBox) + else: + numBoxes += curBoxes + + return numBoxes * WateringMult + + +def getMaxWateringCanPower(): + retval = 0 + for wateringCanAttrib in WateringCanAttributes.values(): + retval += wateringCanAttrib['numBoxes'] + + return retval * WateringMult + + +FlowerColors = [(0.804, 0.2, 0.2), + (0.922, 0.463, 0.0), + (0.5, 0.2, 1.0), + (0.4, 0.4, 1.0), + (0.953, 0.545, 0.757), + (0.992, 0.843, 0.392), + (1.0, 1.0, 1.0), + (0.5, 0.8, 0.5)] +FLOWER_RED = 0 +FLOWER_ORANGE = 1 +FLOWER_VIOLET = 2 +FLOWER_BLUE = 3 +FLOWER_PINK = 4 +FLOWER_YELLOW = 5 +FLOWER_WHITE = 6 +FLOWER_GREEN = 7 +ToonStatuaryTypeIndices = xrange(205, 209) +ChangingStatuaryTypeIndices = xrange(230, 232) +AnimatedStatuaryTypeIndices = xrange(234, 238) +PlantAttributes = {49: {'name': TTLocalizer.FlowerSpeciesNames[49], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/daisy.bam', + 'fullGrownModel': 'phase_5.5/models/estate/daisy.bam', + 'photoPos': (0.0, -0.35, -0.361882), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((10, FLOWER_YELLOW, 1), + (11, FLOWER_PINK, 2), + (12, FLOWER_WHITE, 3), + (13, FLOWER_RED, 4), + (14, FLOWER_ORANGE, 5), + (15, FLOWER_BLUE, 6), + (16, FLOWER_GREEN, 7), + (17, FLOWER_VIOLET, 8))}, + 50: {'name': TTLocalizer.FlowerSpeciesNames[50], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/tulip.bam', + 'fullGrownModel': 'phase_5.5/models/estate/tulip.bam', + 'photoPos': (0.0, -0.35, -0.35), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((20, FLOWER_VIOLET, 5), (21, FLOWER_RED, 6), (22, FLOWER_YELLOW, 8))}, + 51: {'name': TTLocalizer.FlowerSpeciesNames[51], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/carnation.bam', + 'fullGrownModel': 'phase_5.5/models/estate/carnation.bam', + 'photoPos': (0.0, -0.35, -0.4), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((30, FLOWER_PINK, 1), + (31, FLOWER_YELLOW, 2), + (32, FLOWER_RED, 3), + (33, FLOWER_WHITE, 5), + (34, FLOWER_GREEN, 7))}, + 52: {'name': TTLocalizer.FlowerSpeciesNames[52], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/lily.bam', + 'fullGrownModel': 'phase_5.5/models/estate/lily.bam', + 'photoPos': (0.0174745, -0.05, -0.670513), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((40, FLOWER_WHITE, 1), + (41, FLOWER_GREEN, 2), + (42, FLOWER_ORANGE, 3), + (43, FLOWER_PINK, 4), + (44, FLOWER_RED, 5), + (45, FLOWER_VIOLET, 6), + (46, FLOWER_BLUE, 7), + (47, FLOWER_YELLOW, 8))}, + 53: {'name': TTLocalizer.FlowerSpeciesNames[53], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/narcissi.bam', + 'fullGrownModel': 'phase_5.5/models/estate/narcissi.bam', + 'photoPos': (-0.0403175, 0.060933, -0.548368), + 'photoScale': 1, + 'photoHeading': 20, + 'photoPitch': 0, + 'varieties': ((50, FLOWER_GREEN, 1), + (51, FLOWER_WHITE, 2), + (52, FLOWER_YELLOW, 4), + (53, FLOWER_PINK, 5))}, + 54: {'name': TTLocalizer.FlowerSpeciesNames[54], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/pansy.bam', + 'fullGrownModel': 'phase_5.5/models/estate/pansy.bam', + 'photoScale': 2.5, + 'photoHeading': 0, + 'photoPitch': 0, + 'varieties': ((60, FLOWER_ORANGE, 1), + (61, FLOWER_WHITE, 2), + (62, FLOWER_RED, 3), + (63, FLOWER_YELLOW, 4), + (64, FLOWER_PINK, 6))}, + 55: {'name': TTLocalizer.FlowerSpeciesNames[55], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -2, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/petunia.bam', + 'fullGrownModel': 'phase_5.5/models/estate/petunia.bam', + 'photoPos': (0.02, -0.0324585, -0.167735), + 'photoScale': 1.5, + 'photoHeading': -20, + 'photoPitch': 35, + 'varieties': ((70, FLOWER_BLUE, 7), (71, FLOWER_PINK, 8))}, + 56: {'name': TTLocalizer.FlowerSpeciesNames[56], + 'plantType': FLOWER_TYPE, + 'growthThresholds': (1, 1, 1), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -1, + 'seedlingModel': 'phase_5.5/models/estate/seedling.bam', + 'establishedModel': 'phase_5.5/models/estate/rose.bam', + 'fullGrownModel': 'phase_5.5/models/estate/rose.bam', + 'photoPos': (0.04396, 0.124797, -0.877291), + 'photoScale': 1, + 'photoHeading': 0, + 'photoPitch': 35, + 'varieties': ((0, FLOWER_RED, 3), + (1, FLOWER_YELLOW, 4), + (2, FLOWER_PINK, 6), + (3, FLOWER_WHITE, 7), + (4, FLOWER_BLUE, 8))}, + 200: {'name': TTLocalizer.StatuaryDonald, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_donald.bam', + 'worldScale': 0.05, + 'varieties': ((1000, 1, 0),), + 'pinballScore': (10, 1)}, + 201: {'name': TTLocalizer.StatuaryMickey1, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_mickey_flute', + 'worldScale': 0.05, + 'varieties': ((1001, 1, 0),), + 'pinballScore': (50, 1)}, + 202: {'name': TTLocalizer.StatuaryGardenAccelerator, + 'plantType': STATUARY_TYPE, + 'model': 'phase_4/models/props/goofy_statue', + 'varieties': ((1002, 1, 0),)}, + 203: {'name': TTLocalizer.StatuaryMinnie, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_minnie', + 'worldScale': 0.05, + 'varieties': ((1003, 1, 0),), + 'pinballScore': (150, 1)}, + 204: {'name': TTLocalizer.StatuaryMickey2, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_mickey_shovel', + 'worldScale': 0.05, + 'varieties': ((1004, 1, 0),), + 'pinballScore': (250, 1)}, + 205: {'name': TTLocalizer.StatuaryToonWave, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1005, 1, 0),), + 'pinballScore': (500, 1)}, + 206: {'name': TTLocalizer.StatuaryToonVictory, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1006, 1, 0),), + 'pinballScore': (500, 1)}, + 207: {'name': TTLocalizer.StatuaryToonCrossedArms, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1007, 1, 0),), + 'pinballScore': (500, 1)}, + 208: {'name': TTLocalizer.StatuaryToonThinking, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_pedestal', + 'worldScale': 0.05, + 'varieties': ((1008, 1, 0),), + 'pinballScore': (500, 1)}, + 230: {'name': TTLocalizer.StatuaryMeltingSnowman, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/tt_m_prp_ext_snowman', + 'worldScale': 1.0, + 'varieties': ((1030, 1, 0),), + 'pinballScore': (500, 1), + 'growthThresholds': (1, 2)}, + 231: {'name': TTLocalizer.StatuaryMeltingSnowDoodle, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/tt_m_prp_ext_snowDoodle', + 'worldScale': 1.0, + 'varieties': ((1031, 1, 0),), + 'pinballScore': (500, 1), + 'growthThresholds': (1, 2)}, + 234: {'name': TTLocalizer.AnimatedStatuaryFlappyCog, + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_', + 'anims': ['default', 'wave'], + 'worldScale': 0.5, + 'varieties': ((1035, 1, 0),), + 'pinballScore': (500, 1)}, + 254: {'name': 'reserved tag', + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_minnie', + 'worldScale': 0.05, + 'varieties': ((2001, 1, 0),), + 'pinballScore': (15, 1)}, + 255: {'name': 'reserved tag', + 'plantType': STATUARY_TYPE, + 'model': 'phase_5.5/models/estate/garden_minnie', + 'worldScale': 0.05, + 'varieties': ((2002, 1, 0),), + 'pinballScore': (15, 1)}} +if ACCELERATOR_USED_FROM_SHTIKER_BOOK: + del PlantAttributes[202] + +def getTreeTrackAndLevel(typeIndex): + track = typeIndex / 7 + level = typeIndex % 7 + return (track, level) + + +def getTreeTypeIndex(track, level): + return track * 7 + level + + +NUM_GAGS = 7 * 7 +for i in range(NUM_GAGS): + track, level = getTreeTrackAndLevel(i) + if level <= 6: + name = TTLocalizer.BattleGlobalAvPropStrings[track][level] + TTLocalizer.GardenGagTree + else: + name = TTLocalizer.GardenUberGag + attr = {'name': name, + 'plantType': GAG_TREE_TYPE, + 'growthThresholds': (level + 1, (level + 1) * 2, (level + 1) * 3), + 'maxWaterLevel': getMaxWateringCanPower(), + 'minWaterLevel': -1, + 'maxFruit': 9, + 'filename': 'phase_5.5/models/estate/gag_tree_stages.bam', + 'seedlingModel': 'gag_tree_small', + 'establishedModel': 'gag_tree_med', + 'fullGrownModel': 'gag_tree_large', + 'varieties': ((),)} + PlantAttributes[i] = attr + +BeanColors = [(255, 0, 0), + (0, 255, 0), + (255, 165, 0), + (148, 0, 211), + (0, 0, 255), + (255, 192, 203), + (255, 255, 0), + (0, 255, 255), + (192, 192, 192)] +BeanColorLetters = ['R', + 'G', + 'O', + 'V', + 'B', + 'P', + 'Y', + 'C', + 'S'] +Recipes = {0: {'beans': 'RRR', + 'special': -1}, + 1: {'beans': 'RYOY', + 'special': -1}, + 2: {'beans': 'RPOROP', + 'special': -1}, + 3: {'beans': 'RCOPVCC', + 'special': -1}, + 4: {'beans': 'RBVVBBPB', + 'special': -1}, + 10: {'beans': 'Y', + 'special': -1}, + 11: {'beans': 'YR', + 'special': -1}, + 12: {'beans': 'YRG', + 'special': -1}, + 13: {'beans': 'YRCO', + 'special': -1}, + 14: {'beans': 'YROOO', + 'special': -1}, + 15: {'beans': 'YBCVBB', + 'special': -1}, + 16: {'beans': 'YGROGGG', + 'special': -1}, + 17: {'beans': 'YBVCVROV', + 'special': -1}, + 20: {'beans': 'VRBVV', + 'special': -1}, + 21: {'beans': 'VRRRVV', + 'special': -1}, + 22: {'beans': 'VYYVYOVY', + 'special': -1}, + 30: {'beans': 'P', + 'special': -1}, + 31: {'beans': 'PY', + 'special': -1}, + 32: {'beans': 'PRR', + 'special': -1}, + 33: {'beans': 'PRGBR', + 'special': -1}, + 34: {'beans': 'PGGGGYG', + 'special': -1}, + 40: {'beans': 'C', + 'special': -1}, + 41: {'beans': 'CG', + 'special': -1}, + 42: {'beans': 'COO', + 'special': -1}, + 43: {'beans': 'COOP', + 'special': -1}, + 44: {'beans': 'CRRRR', + 'special': -1}, + 45: {'beans': 'CRVVVV', + 'special': -1}, + 46: {'beans': 'CVCBCBB', + 'special': -1}, + 47: {'beans': 'CBYYCBYY', + 'special': -1}, + 50: {'beans': 'G', + 'special': -1}, + 51: {'beans': 'GC', + 'special': -1}, + 52: {'beans': 'GPYY', + 'special': -1}, + 53: {'beans': 'GPBPP', + 'special': -1}, + 60: {'beans': 'O', + 'special': -1}, + 61: {'beans': 'OC', + 'special': -1}, + 62: {'beans': 'ORR', + 'special': -1}, + 63: {'beans': 'OYYR', + 'special': -1}, + 64: {'beans': 'OPPOBP', + 'special': -1}, + 70: {'beans': 'BVBVCBB', + 'special': -1}, + 71: {'beans': 'BPPBROYY', + 'special': -1}, + 1000: {'beans': 'GG', + 'special': 100}, + 1001: {'beans': 'SSSS', + 'special': 101}, + 1002: {'beans': 'S', + 'special': 102}, + 1003: {'beans': 'VVVVVV', + 'special': 103}, + 1004: {'beans': 'OOOOOOOO', + 'special': 104}, + 1005: {'beans': 'RRRRRRRR', + 'special': 105}, + 1006: {'beans': 'GGGGGGGG', + 'special': 106}, + 1007: {'beans': 'BBBBBBBB', + 'special': 107}, + 1008: {'beans': 'SSSSSSSS', + 'special': 108}, + 1030: {'beans': 'S', + 'special': 130}, + 1031: {'beans': 'S', + 'special': 131}, + 1035: {'beans': 'S', + 'special': 135}, + 2001: {'beans': 'ZVOVOVO', + 'special': -1}, + 2002: {'beans': 'ZOVOVOV', + 'special': -1}} + +def getRecipeKey(beans, special): + testDict = {'beans': beans, + 'special': special} + for key in Recipes.keys(): + recipe = Recipes[key] + if testDict == recipe: + return key + + return -1 + + +def getRecipeKeyUsingSpecial(special): + for key in Recipes.keys(): + recipe = Recipes[key] + if recipe['special'] == special: + return key + + return -1 + + +SHOVEL_TIN = 0 +SHOVEL_STEEL = 1 +SHOVEL_SILVER = 2 +SHOVEL_GOLD = 3 +MAX_SHOVELS = 4 +ShovelAttributes = {0: {'numBoxes': 2, + 'skillPts': 80, + 'name': TTLocalizer.ShovelTin}, + 1: {'numBoxes': 2, + 'skillPts': 160, + 'name': TTLocalizer.ShovelSteel}, + 2: {'numBoxes': 2, + 'skillPts': 320, + 'name': TTLocalizer.ShovelSilver}, + 3: {'numBoxes': 2, + 'skillPts': 640, + 'name': TTLocalizer.ShovelGold}} + +def getShovelPower(shovel, shovelSkill): + numBoxes = 0 + for curShovel in range(shovel + 1): + shovelAttrib = ShovelAttributes[curShovel] + curBoxes = shovelAttrib['numBoxes'] + skill = shovelAttrib['skillPts'] + if curShovel == shovel: + if shovelSkill >= skill: + gardenNotify.warning("this shouldn't happen shovelSkill %d >= skill %d" % (shovelSkill, skill)) + shovelSkill = skill - 1 + skillPtPerBox = skill / curBoxes + numBoxes += 1 + int(shovelSkill) / int(skillPtPerBox) + else: + numBoxes += curBoxes + + return numBoxes + + +def getMaxShovelSkill(): + retVal = 0 + retVal += ShovelAttributes[MAX_SHOVELS - 1]['skillPts'] - 1 + return retVal + + +def getNumberOfShovelBoxes(): + retVal = 0 + for attrib in ShovelAttributes.values(): + retVal += attrib['numBoxes'] + + return retVal + + +def getNumberOfWateringCanBoxes(): + retVal = 0 + for attrib in WateringCanAttributes.values(): + retVal += attrib['numBoxes'] + + return retVal + + +def getNumberOfFlowerVarieties(): + retVal = 0 + for attrib in PlantAttributes.values(): + if attrib['plantType'] == FLOWER_TYPE: + retVal += len(attrib['varieties']) + + return retVal + + +def getNumberOfFlowerSpecies(): + retVal = 0 + for attrib in PlantAttributes.values(): + if attrib['plantType'] == FLOWER_TYPE: + retVal += 1 + + return retVal + + +def getFlowerVarieties(species): + retval = () + if species in PlantAttributes.keys(): + attrib = PlantAttributes[species] + if attrib['plantType'] == FLOWER_TYPE: + retval = attrib['varieties'] + return retval + + +def getFlowerSpecies(): + retVal = [] + for key in PlantAttributes.keys(): + attrib = PlantAttributes[key] + if attrib['plantType'] == FLOWER_TYPE: + retVal.append(key) + + return retVal + + +def getRandomFlower(): + species = random.choice(getFlowerSpecies()) + variety = random.randint(0, len(PlantAttributes[species]['varieties']) - 1) + return (species, variety) + + +def getFlowerVarietyName(species, variety): + retVal = TTLocalizer.FlowerUnknown + if species in PlantAttributes.keys(): + attrib = PlantAttributes[species] + if variety < len(attrib['varieties']): + funnySpeciesNameList = TTLocalizer.FlowerFunnyNames.get(species) + if funnySpeciesNameList: + if variety < len(funnySpeciesNameList): + retVal = TTLocalizer.FlowerFunnyNames[species][variety] + else: + gardenNotify.warning('warning unknown species=%d variety= %d' % (species, variety)) + else: + gardenNotify.warning('warning unknown species %d' % species) + return retVal + + +def getSpeciesVarietyGivenRecipe(recipeKey): + for species in PlantAttributes.keys(): + attrib = PlantAttributes[species] + if attrib['plantType'] == GAG_TREE_TYPE: + continue + if attrib.has_key('varieties'): + for variety in range(len(attrib['varieties'])): + if attrib['varieties'][variety][0] == recipeKey: + return (species, variety) + + return (-1, -1) + + +def getNumBeansRequired(species, variety): + retval = -1 + if not PlantAttributes.get(species): + return retval + if not PlantAttributes[species].has_key('varieties'): + return retval + if variety >= len(PlantAttributes[species]['varieties']): + return -1 + recipeKey = PlantAttributes[species]['varieties'][variety][0] + recipe = Recipes.get(recipeKey) + if recipe: + if recipe.has_key('beans'): + retval = len(recipe['beans']) + return retval + + +def validateRecipes(notify): + uniqueRecipes = [] + uniqueBeans = [] + numBoxes = getNumberOfShovelBoxes() + uniqueSpecials = [] + for key in Recipes.keys(): + recipe = Recipes[key] + beans = recipe['beans'] + if len(beans) > numBoxes: + notify.warning('numBoxes=%d beans=%s, truncating to %s' % (numBoxes, beans, beans[:numBoxes])) + beans = beans[:numBoxes] + for letter in beans: + if key not in (2001, 2002): + pass + + testTuple = (beans, recipe['special']) + uniqueRecipes.append(testTuple) + if beans: + if beans in uniqueBeans: + notify.warning('duplicate beans=%s in key=%d' % (beans, key)) + else: + uniqueBeans.append(beans) + special = recipe['special'] + if special != -1: + uniqueSpecials.append(special) + + notify.debug('recipes are ok') + + +def validatePlantAttributes(notify): + uniqueRecipes = [] + flowerRecipeDistribution = [] + for i in range(getNumberOfShovelBoxes() + 1): + flowerRecipeDistribution.append([]) + + for key in PlantAttributes.keys(): + plant = PlantAttributes[key] + notify.debug('now validating %s' % plant['name']) + if plant['plantType'] in (GAG_TREE_TYPE, FLOWER_TYPE): + growthThresholds = plant['growthThresholds'] + lastValue = 0 + for testValue in growthThresholds: + lastValue = testValue + + if plant['plantType'] in (STATUARY_TYPE, FLOWER_TYPE): + varieties = plant['varieties'] + for variety in varieties: + recipeNum = variety[0] + uniqueRecipes.append(recipeNum) + if plant['plantType'] == FLOWER_TYPE: + recipeLength = len(Recipes[recipeNum]['beans']) + newInfo = (getFlowerVarietyName(key, list(varieties).index(variety)), Recipes[recipeNum]['beans'], TTLocalizer.FlowerColorStrings[variety[1]]) + flowerRecipeDistribution[recipeLength].append(newInfo) + + for numBeans in range(len(flowerRecipeDistribution)): + notify.debug('%d flowers with %d beans' % (len(flowerRecipeDistribution[numBeans]), numBeans)) + for flower in flowerRecipeDistribution[numBeans]: + notify.debug(' %s, beans = %s, color=%s' % (flower[0], flower[1], flower[2])) + + notify.debug('plant attributes are ok') + + +plots0 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (-54, + -13.5, + 276.0, + GAG_TREE_TYPE), + (-7, + -48, + 343.0, + GAG_TREE_TYPE), + (-40, + -75, + 27.0, + GAG_TREE_TYPE), + (-78, + -44, + 309.0, + GAG_TREE_TYPE), + (-72, + -15, + 260.0, + GAG_TREE_TYPE), + (-24, + -19, + 294.0, + GAG_TREE_TYPE), + (11, + -26, + 0.0, + GAG_TREE_TYPE), + (-92, + -4, + 0.0, + GAG_TREE_TYPE), + (-100, + -43, + -90.0, + STATUARY_TYPE)) +plots1 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (62, + -81, + 194.0, + GAG_TREE_TYPE), + (101, + -52, + 250.0, + GAG_TREE_TYPE), + (93, + -104, + 214.0, + GAG_TREE_TYPE), + (69, + -122, + 188.0, + GAG_TREE_TYPE), + (92, + -120, + 184.0, + GAG_TREE_TYPE), + (113, + -29, + 250.0, + GAG_TREE_TYPE), + (125, + -57, + 0.0, + GAG_TREE_TYPE), + (114, + -40, + 0.0, + GAG_TREE_TYPE), + (47, + -82, + -30.0, + STATUARY_TYPE)) +plots2 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (-40, + -114, + 176.0, + GAG_TREE_TYPE), + (-44, + -148, + 162.0, + GAG_TREE_TYPE), + (-97, + -99, + 138.0, + GAG_TREE_TYPE), + (-82, + -94, + 134.0, + GAG_TREE_TYPE), + (-27, + -106, + 195.0, + GAG_TREE_TYPE), + (-76, + -147, + 110.0, + GAG_TREE_TYPE), + (-29, + -164, + 0.0, + GAG_TREE_TYPE), + (-107, + -94, + 0.0, + GAG_TREE_TYPE), + (-97, + -114, + -60.0, + STATUARY_TYPE)) +plots3 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (59, + 35, + 187.0, + GAG_TREE_TYPE), + (87, + 28, + 114.0, + GAG_TREE_TYPE), + (67, + -16, + 78.0, + GAG_TREE_TYPE), + (24, + 19, + 155.0, + GAG_TREE_TYPE), + (18, + 31, + 172.0, + GAG_TREE_TYPE), + (74, + 36, + 133.0, + GAG_TREE_TYPE), + (35, + -34, + 0.0, + GAG_TREE_TYPE), + (116, + 17, + 0.0, + GAG_TREE_TYPE), + (117, + 27, + 102.0, + STATUARY_TYPE)) +plots4 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (37, + 101, + 350.0, + GAG_TREE_TYPE), + (15, + 100, + 342.0, + GAG_TREE_TYPE), + (73, + 92, + 0.0, + GAG_TREE_TYPE), + (74, + 69, + 347.0, + GAG_TREE_TYPE), + (102, + 62, + 334.0, + GAG_TREE_TYPE), + (86, + 76, + 350.0, + GAG_TREE_TYPE), + (100, + 78, + 327.0, + GAG_TREE_TYPE), + (15, + 73, + 50.0, + GAG_TREE_TYPE), + (16, + 87, + -140.0, + STATUARY_TYPE)) +plots5 = ((0, + 0, + 0.0, + FLOWER_TYPE), + (1, + 0, + 0.0, + FLOWER_TYPE), + (2, + 0, + 0.0, + FLOWER_TYPE), + (2, + 1, + 0.0, + FLOWER_TYPE), + (2, + 2, + 0.0, + FLOWER_TYPE), + (3, + 0, + 0.0, + FLOWER_TYPE), + (3, + 1, + 0.0, + FLOWER_TYPE), + (3, + 2, + 0.0, + FLOWER_TYPE), + (4, + 0, + 0.0, + FLOWER_TYPE), + (4, + 1, + 0.0, + FLOWER_TYPE), + (-26, + 92, + 41.0, + GAG_TREE_TYPE), + (-71, + 58, + 37.0, + GAG_TREE_TYPE), + (-67, + 21, + 243.0, + GAG_TREE_TYPE), + (-10, + -2.6, + 178.0, + GAG_TREE_TYPE), + (-60, + 13.7, + 250.0, + GAG_TREE_TYPE), + (-13, + 84, + 2.0, + GAG_TREE_TYPE), + (-62, + 65, + 0.0, + GAG_TREE_TYPE), + (-16.6, + 52.7, + 0.0, + GAG_TREE_TYPE), + (-55, + 70, + 213.0, + STATUARY_TYPE)) +estatePlots = (plots0, + plots1, + plots2, + plots3, + plots4, + plots5) +BOX_ONE = 1 +BOX_TWO = 2 +BOX_THREE = 3 +flowerBoxes0 = ((-62.5, + -52.5, + 182.0, + BOX_ONE), + (-52, + -52, + 182, + BOX_ONE), + (-64.5, + -42, + 92.0, + BOX_THREE), + (-49, + -43, + 266.0, + BOX_THREE), + (-57, + -33, + 0.0, + BOX_TWO)) +flowerBoxes1 = ((85.0, + -67.0, + 26.0, + BOX_ONE), + (75, + -72, + 26.0, + BOX_ONE), + (91.0, + -74.0, + -63.0, + BOX_THREE), + (77, + -81, + 117.0, + BOX_THREE), + (88, + -86, + 206.0, + BOX_TWO)) +flowerBoxes2 = ((-62, + -112, + 350.0, + BOX_ONE), + (-72, + -110, + 350.0, + BOX_ONE), + (-62, + -122, + 257.0, + BOX_THREE), + (-76, + -118, + 79.0, + BOX_THREE), + (-71, + -129, + 169.0, + BOX_TWO)) +flowerBoxes3 = ((72, + 5, + 265.0, + BOX_ONE), + (72.5, + 16, + 265.0, + BOX_ONE), + (63, + 3, + 178.0, + BOX_THREE), + (64, + 19, + 355.0, + BOX_THREE), + (54, + 12, + 86.0, + BOX_TWO)) +flowerBoxes4 = ((35.5, + 70, + 152.0, + BOX_ONE), + (46, + 66, + 152.0, + BOX_ONE), + (36.5, + 79.5, + 71.0, + BOX_THREE), + (51.5, + 74, + 247.0, + BOX_THREE), + (47, + 86, + -19.0, + BOX_TWO)) +flowerBoxes5 = ((-26.5, + 37.5, + 318.0, + BOX_ONE), + (-33, + 46, + 318.0, + BOX_ONE), + (-32, + 30, + 217.0, + BOX_THREE), + (-42, + 42, + 37.0, + BOX_THREE), + (-45, + 31, + 124.0, + BOX_TWO)) +estateBoxes = (flowerBoxes0, + flowerBoxes1, + flowerBoxes2, + flowerBoxes3, + flowerBoxes4, + flowerBoxes5) + +def whatCanBePlanted(plotIndex, hardPointIndex): + retval = INVALID_TYPE + if plotIndex < len(estatePlots) and plotIndex >= 0: + if hardPointIndex < len(estatePlots[plotIndex]) and hardPointIndex >= 0: + if len(estatePlots[plotIndex][hardPointIndex]) >= 4: + retval = estatePlots[plotIndex][hardPointIndex][3] + return retval + + +MAGIC_BEAN_SUBTYPE = 0 +GARDEN_ITEM_SUBTYPE = 1 +Specials = {0: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 1, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeans, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 1: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 2: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 1, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeans, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 3: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 4: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 1, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeans, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 5: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 6: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscription, + 'beanCost': 125}, + 7: {'subtype': MAGIC_BEAN_SUBTYPE, + 'gagbonus': 2, + 'photoModel': 'phase_4/models/props/goofy_statue', + 'photoScale': 0.1, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.GardenTextMagicBeansB, + 'description': TTLocalizer.GardenSpecialDiscriptionB, + 'beanCost': 125}, + 100: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_donald', + 'photoScale': 0.04, + 'photoPos': (0, 0, -1), + 'photoName': TTLocalizer.StatuaryDonald, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 125}, + 101: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_mickey_flute', + 'photoScale': 0.025, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryMickey1, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 250}, + 102: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/sack', + 'photoScale': 1.0, + 'photoPos': (0, 0, -1.0), + 'photoName': TTLocalizer.StatuaryGardenAccelerator, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 7500, + 'useFromShtiker': False}, + 103: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_minnie', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryMinnie, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 500}, + 104: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_mickey_shovel', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryMickey2, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 1000}, + 105: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonWave, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 106: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonVictory, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 107: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonCrossedArms, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 108: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/garden_pedestal', + 'photoScale': 0.02, + 'photoPos': (0, 0, -1.05), + 'photoName': TTLocalizer.StatuaryToonThinking, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 5000, + 'minSkill': 639}, + 130: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/tt_m_prp_ext_snowman_icon', + 'photoScale': 90.0, + 'photoPos': (0, 0, 0.0), + 'photoName': TTLocalizer.StatuaryMeltingSnowman, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 25, + 'minSkill': 0}, + 131: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/tt_m_prp_ext_snowDoodle_icon', + 'photoScale': 90.0, + 'photoPos': (0, 0, 0.0), + 'photoName': TTLocalizer.StatuaryMeltingSnowDoodle, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 50, + 'minSkill': 0}, + 135: {'subtype': GARDEN_ITEM_SUBTYPE, + 'photoModel': 'phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_', + 'photoAnimation': ['default', 'wave'], + 'photoScale': 1.25, + 'photoPos': (0, 0, -0.04), + 'photoName': TTLocalizer.AnimatedStatuaryFlappyCog, + 'description': TTLocalizer.GardenSpecialDiscription, + 'isCatalog': True, + 'beanCost': 50, + 'minSkill': 1}} +GardenAcceleratorSpecial = 102 +GardenAcceleratorSpecies = 202 +if ACCELERATOR_USED_FROM_SHTIKER_BOOK: + Specials[GardenAcceleratorSpecial]['useFromShtiker'] = True + +def getPlantItWithString(special): + retval = '' + recipeKey = getRecipeKeyUsingSpecial(special) + if not recipeKey == -1: + beanTuple = [] + beanStr = Recipes[recipeKey]['beans'] + for letter in beanStr: + index = BeanColorLetters.index(letter) + beanTuple.append(index) + + beanText = TTLocalizer.getRecipeBeanText(beanTuple) + retval += TTLocalizer.PlantItWith % beanText + return retval + + +for specialKey in Specials.keys(): + recipeKey = getRecipeKeyUsingSpecial(specialKey) + if not recipeKey == -1: + Specials[specialKey]['description'] = getPlantItWithString(specialKey) + if specialKey == GardenAcceleratorSpecial: + if ACCELERATOR_USED_FROM_SHTIKER_BOOK: + Specials[specialKey]['description'] = TTLocalizer.UseFromSpecialsTab + Specials[specialKey]['description'] += TTLocalizer.MakeSureWatered + +TIME_OF_DAY_FOR_EPOCH = 3 +MOVIE_HARVEST = 0 +MOVIE_PLANT = 1 +MOVIE_REMOVE = 2 +MOVIE_WATER = 3 +MOVIE_FINISHPLANTING = 4 +MOVIE_FINISHREMOVING = 5 +MOVIE_CLEAR = 6 +MOVIE_PLANT_REJECTED = 7 +TrophyDict = {0: (TTLocalizer.GardenTrophyNameDict[0],), + 1: (TTLocalizer.GardenTrophyNameDict[1],), + 2: (TTLocalizer.GardenTrophyNameDict[2],), + 3: (TTLocalizer.GardenTrophyNameDict[3],)} diff --git a/toontown/estate/GardenProgressMeter.py b/toontown/estate/GardenProgressMeter.py new file mode 100644 index 0000000..ff9917c --- /dev/null +++ b/toontown/estate/GardenProgressMeter.py @@ -0,0 +1,94 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.gui.DirectScrolledList import * +from direct.distributed.ClockDelta import * +from toontown.toontowngui import TTDialog +import math +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from toontown.toonbase import TTLocalizer +import random +import random +import cPickle +from direct.showbase import PythonUtil +import GameSprite +from math import pi +from toontown.estate import GardenGlobals +SHOVEL = 0 +WATERINGCAN = 1 +GAMEWIN = 2 + +class GardenProgressMeter(DirectObject.DirectObject): + + def __init__(self, typePromotion = 'game', level = 0): + if typePromotion == 'shovel': + self.typePromotion = SHOVEL + elif typePromotion == 'wateringCan': + self.typePromotion = WATERINGCAN + elif typePromotion == 'game': + self.typePromotion == GAMEWIN + else: + print 'No type of %s' % typePromotion + self.level = level + self.acceptErrorDialog = None + self.doneEvent = 'game Done' + self.sprites = [] + self.load() + thing = self.model.find('**/item_board') + self.block = self.model1.find('**/minnieCircle') + return + + def load(self): + model = loader.loadModel('phase_5.5/models/gui/package_delivery_panel') + model1 = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + self.model = model + self.model1 = model1 + background = model.find('**/bg') + itemBoard = model.find('**/item_board') + congratsMessage = 'Super Congratulations!!' + if self.typePromotion == SHOVEL: + congratsMessage = TTLocalizer.GardenShovelLevelUp + ' \n' + GardenGlobals.ShovelAttributes[self.level]['name'] + elif self.typePromotion == WATERINGCAN: + congratsMessage = TTLocalizer.GardenWateringCanLevelUp + ' \n' + GardenGlobals.WateringCanAttributes[self.level]['name'] + elif self.typePromotion == GAMEWIN: + congratsMessage = TTLocalizer.GardenMiniGameWon + self.frame = DirectFrame(scale=1.1, relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.75, 1, 0.75), image_color=ToontownGlobals.GlobalDialogColor, frameSize=(-0.5, + 0.5, + -0.45, + -0.05)) + self.congratsText = DirectLabel(scale=1.1, relief=None, text_pos=(0, 0.2), text_wordwrap=16, text=congratsMessage, text_font=ToontownGlobals.getSignFont(), pos=(0.0, 0.0, 0.0), text_scale=0.1, text0_fg=(1, 1, 1, 1), parent=self.frame) + 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.32), scale=0.9, text='Exit', text_font=ToontownGlobals.getSignFont(), text0_fg=(1, 1, 1, 1), text1_fg=(1, 1, 1, 1), text2_fg=(1, 1, 1, 1), text_scale=0.045, text_pos=(0, -0.01), command=self.__handleExit) + return + + def unload(self): + self.frame.destroy() + del self.frame + if self.acceptErrorDialog: + self.acceptErrorDialog.cleanup() + self.acceptErrorDialog = None + taskMgr.remove('gameTask') + self.ignoreAll() + return + + def show(self): + self.frame.show() + + def hide(self): + self.frame.hide() + + def __handleExit(self): + self.__acceptExit() + + def __acceptExit(self, buttonValue = None): + if hasattr(self, 'frame'): + self.hide() + self.unload() + messenger.send(self.doneEvent) diff --git a/toontown/estate/GardenTutorial.py b/toontown/estate/GardenTutorial.py new file mode 100644 index 0000000..fc0eff4 --- /dev/null +++ b/toontown/estate/GardenTutorial.py @@ -0,0 +1,117 @@ +from direct.gui.DirectGui import * +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import * + +class GardenTutorial(DirectFrame, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('GardenTutorial') + + def __init__(self, doneEvent = None, callback = None): + FSM.FSM.__init__(self, 'GardenTutorial') + self.doneEvent = doneEvent + self.callback = callback + self.setStateArray(['Page1', + 'Page2', + 'Page3', + 'Page4', + 'Page5']) + DirectFrame.__init__(self, pos=(0.0, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.5, 1.5, 0.9), text='', text_scale=0.06) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.32), text_fg=(1, 0, 0, 1), text_scale=0.13, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_5.5/models/estate/gardenTutorialPages') + self.iPage1 = DirectFrame(self, image=images.find('**/GardenTutorialPage1'), scale=0.35, pos=(-0.51, -0.1, 0.05)) + self.iPage1.hide() + self.iPage2 = DirectFrame(self, image=images.find('**/GardenTutorialPage2'), scale=1.25, pos=(0.43, -0.1, 0.05)) + self.iPage2.hide() + self.iPage3 = DirectFrame(self, image=images.find('**/GardenTutorialPage3'), scale=0.5, pos=(-0.52, -0.1, 0.02)) + self.iPage3.hide() + self.iPage4 = DirectFrame(self, image=images.find('**/GardenTutorialPage4'), scale=0.75, pos=(0, -0.1, -0.05)) + self.iPage4.hide() + self.iPage5 = DirectFrame(self, image=images.find('**/GardenTutorialPage5'), scale=0.7, pos=(-0.51, -0.1, 0.05)) + self.iPage5.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.GardenTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.2, -0.3, -0.25), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.GardenTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.2, -0.3, -0.25), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.GardenTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.55, -0.3, -0.25), command=self.__handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.notify.debug('garden tutorial detectedGardenPlotUse') + base.cr.playGame.getPlace().detectedGardenPlotUse() + curState = base.cr.playGame.getPlace().getState() + self.notify.debug('Estate.getState() == %s' % curState) + self.request('Page1') + return + + def enterPage1(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle1,) + self['text'] = TTLocalizer.GardenTutorialPage1 + self['text_pos'] = (0.15, 0.13) + self['text_wordwrap'] = 16.5 + self.bPrev['state'] = DGG.DISABLED + self.iPage1.show() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + + def enterPage2(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle2,) + self['text'] = TTLocalizer.GardenTutorialPage2 + self['text_pos'] = (-0.27, 0.16) + self['text_wordwrap'] = TTLocalizer.GTenterPage2Wordwrap + self.iPage2.show() + + def exitPage2(self, *args): + self.iPage2.hide() + + def enterPage3(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle3,) + self['text'] = TTLocalizer.GardenTutorialPage3 + self['text_pos'] = (0.15, 0.13) + self['text_wordwrap'] = 16.5 + self.iPage3.show() + + def exitPage3(self, *args): + self.iPage3.hide() + + def enterPage4(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle4,) + self['text'] = TTLocalizer.GardenTutorialPage4 + self['text_pos'] = (0.0, 0.19) + self['text_wordwrap'] = TTLocalizer.GTenterPage4Wordwrap + self.iPage4.show() + + def exitPage4(self, *args): + self.iPage4.hide() + + def enterPage5(self, *args): + self.title['text'] = (TTLocalizer.GardenTutorialTitle5,) + self['text'] = TTLocalizer.GardenTutorialPage5 + self['text_pos'] = (0.15, 0.13) + self['text_wordwrap'] = 16.5 + self.bQuit.show() + self.bNext['state'] = DGG.DISABLED + self.iPage5.show() + + def exitPage5(self, *args): + self.bNext['state'] = DGG.NORMAL + self.iPage5.hide() + self.bQuit.hide() + + def __handleQuit(self): + self.notify.debug('garden tutorial detectedGardenPlotDone') + base.cr.playGame.getPlace().detectedGardenPlotDone() + if self.callback: + self.callback() + else: + messenger.send(self.doneEvent) diff --git a/toontown/estate/House.py b/toontown/estate/House.py new file mode 100644 index 0000000..20ef593 --- /dev/null +++ b/toontown/estate/House.py @@ -0,0 +1,181 @@ +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 direct.task import Task +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class House(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('House') + + def __init__(self, loader, avId, parentFSMState, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + self.id = ToontownGlobals.MyEstate + self.ownersAvId = avId + self.dnaFile = 'phase_7/models/modules/toon_interior' + self.isInterior = 1 + self.tfaDoneEvent = 'tfaDoneEvent' + self.oldStyle = None + self.fsm = ClassicFSM.ClassicFSM('House', [State.State('start', self.enterStart, self.exitStart, ['doorIn', 'teleportIn', 'tutorial']), + State.State('walk', self.enterWalk, self.exitWalk, ['sit', + 'stickerBook', + 'doorOut', + 'DFA', + 'teleportOut', + 'quest', + 'purchase', + 'closet', + 'banking', + 'phone', + 'stopped']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'DFA', + 'sit', + 'doorOut', + 'teleportOut', + 'quest', + 'purchase', + 'closet', + 'banking', + 'phone', + 'stopped']), + State.State('DFA', self.enterDFA, self.exitDFA, ['DFAReject', 'teleportOut', 'doorOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['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('closet', self.enterCloset, self.exitCloset, ['walk']), + State.State('banking', self.enterBanking, self.exitBanking, ['walk']), + State.State('phone', self.enterPhone, self.exitPhone, ['walk']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start', 'teleportIn'])], 'start', 'final') + self.parentFSMState = parentFSMState + return + + 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('enterHouse') + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + self._telemLimiter = TLGatherAllAvs('House', RotationLimitToH) + NametagGlobals.setMasterArrowsOn(1) + self.fsm.request(requestStatus['how'], [requestStatus]) + + def exit(self): + self.ignoreAll() + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self._telemLimiter.destroy() + del self._telemLimiter + messenger.send('exitHouse') + NametagGlobals.setMasterArrowsOn(0) + + def setState(self, state): + if hasattr(self, 'fsm'): + self.fsm.request(state) + + def getZoneId(self): + return self.zoneId + + 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 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): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self.notify.debug('House: teleportOutDone: requestStatus = %s' % requestStatus) + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == ToontownGlobals.MyEstate and zoneId == self.getZoneId(): + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent, [self.doneStatus]) + + 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) + + def enterPurchase(self): + Place.Place.enterPurchase(self) + + def exitPurchase(self): + Place.Place.exitPurchase(self) + + def enterCloset(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepCloset) + self.enablePeriodTimer() + + def __handleFallingAsleepCloset(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + messenger.send('closetAsleep') + base.localAvatar.forceGotoSleep() + + def exitCloset(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + base.localAvatar.stopSleepWatch() + self.disablePeriodTimer() + + def enterBanking(self): + Place.Place.enterBanking(self) + + def exitBanking(self): + Place.Place.exitBanking(self) diff --git a/toontown/estate/HouseGlobals.py b/toontown/estate/HouseGlobals.py new file mode 100644 index 0000000..9dfda1a --- /dev/null +++ b/toontown/estate/HouseGlobals.py @@ -0,0 +1,112 @@ +NUM_HOUSE_TYPES = 6 +HOUSE_DEFAULT = 0 +HOUSE_CRAFTSMAN = 1 +HOUSE_TEST = 5 +CLEANUP_DELAY = 8 +BOOT_GRACE_PERIOD = 5 +CLEANUP_DELAY_AFTER_BOOT = 2 +WANT_TELEPORT_TIMEOUT = 0 +TELEPORT_TIMEOUT = 15 +defaultEntryPoint = (23.875, -13.052, 10.092, 7.52, 0, 0) +houseModels = ['phase_5.5/models/estate/houseA.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_tiki.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_tepee.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_castle.bam', + 'phase_5.5/models/estate/tt_m_ara_est_house_cupcake.bam', + 'phase_5.5/models/estate/test_houseA.bam'] +houseDrops = [(-56.7788, -42.8756, 4.06471, -90, 0, 0), + (83.3909, -77.5085, 0.0708361, 116.565, 0, 0), + (-69.077, -119.496, 0.025, 77.1957, 0, 0), + (63.4545, 11.0656, 8.05158, 356.6, 0, 0), + (43.9315, 76.72, 0.0377455, 248.962, 0, 0), + (-36.9122, 36.3429, 2.49382, 36.8699, 0, 0)] +gardenDrops = [(25, 68, 0), + (68, -6, 0), + (27, -59, 0), + (-54, -72, 1), + (-95, -29, 0), + (-30, 58, 0)] +houseColors = [(0.892, 0.453, 0.39), + (0.276, 0.692, 0.539), + (0.639, 0.624, 0.882), + (0.525, 0.78, 0.935), + (0.953, 0.545, 0.757), + (0.992, 0.843, 0.392)] +houseColors2 = [(0.792, 0.353, 0.29), + (0.176, 0.592, 0.439), + (0.439, 0.424, 0.682), + (0.325, 0.58, 0.835), + (0.753, 0.345, 0.557), + (0.992, 0.843, 0.392)] +interiorColors = [(0.789, 1, 0.7), + (1, 1, 0.7), + (1, 0.82, 0.7), + (0.839, 0.651, 0.549), + (0.5, 0.586, 0.4), + (0.808, 0.678, 0.51), + (0.875, 0.937, 1.0)] +interiorWood = [(1.0, 1.0, 1.0), + (0.69, 0.741, 0.71), + (1.0, 1.0, 1.0), + (1.0, 1.0, 1.0), + (1.0, 1.0, 1.0), + (0.69, 0.741, 0.71)] +archWood = (0.7, 0.6, 0.5) +atticWood = (0.49, 0.314, 0.224) +stairWood = (0.651, 0.376, 0.31) +doorWood = (0.647, 0.392, 0.353) +windowWood = (0.557, 0.388, 0.2) +interiors = [['phase_5.5/dna/house_interior3.dna', + [-19.45, + 24.7018, + 0, + 0, + 0, + 0], + [-21.4932, + 5.76027, + 0, + 120, + 0, + 0], + []], ['phase_5.5/dna/house_interior7.dna', + [-19.45, + 24.7018, + 0, + 0, + 0, + 0], + [-21.4932, + 5.76027, + 0, + 120, + 0, + 0], + []], ['phase_5.5/dna/house_interior10.dna', + [-22.5835, + 21.8784, + 0, + 90, + 0, + 0], + [-20.96, + 6.49, + 0, + 120, + 0, + 0], + ['c', 'e']]] +NUM_PROPS = 3 +PROP_ICECUBE = 0 +PROP_FLOWER = 1 +PROP_SNOWFLAKE = 2 +FURNITURE_MODE_OFF = 0 +FURNITURE_MODE_STOP = 1 +FURNITURE_MODE_START = 2 +DAY_NIGHT_PERIOD = 2700 +DAY_PERIOD = 2100 +NIGHT_PERIOD = 600 +HALF_DAY_PERIOD = 1050 +HALF_NIGHT_PERIOD = 300 +FIREWORKS_MOVIE_CLEAR = 0 +FIREWORKS_MOVIE_GUI = 1 diff --git a/toontown/estate/MailboxGlobals.py b/toontown/estate/MailboxGlobals.py new file mode 100644 index 0000000..edc967d --- /dev/null +++ b/toontown/estate/MailboxGlobals.py @@ -0,0 +1,6 @@ +MAILBOX_MOVIE_CLEAR = 2 +MAILBOX_MOVIE_EMPTY = 3 +MAILBOX_MOVIE_WAITING = 4 +MAILBOX_MOVIE_READY = 5 +MAILBOX_MOVIE_NOT_OWNER = 6 +MAILBOX_MOVIE_EXIT = 7 diff --git a/toontown/estate/PhoneGlobals.py b/toontown/estate/PhoneGlobals.py new file mode 100644 index 0000000..7a47dcb --- /dev/null +++ b/toontown/estate/PhoneGlobals.py @@ -0,0 +1,4 @@ +PHONE_MOVIE_CLEAR = 2 +PHONE_MOVIE_EMPTY = 3 +PHONE_MOVIE_PICKUP = 4 +PHONE_MOVIE_HANGUP = 5 diff --git a/toontown/estate/PlantTreeGUI.py b/toontown/estate/PlantTreeGUI.py new file mode 100644 index 0000000..0efebca --- /dev/null +++ b/toontown/estate/PlantTreeGUI.py @@ -0,0 +1,43 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.ShowBase import * +from toontown.toonbase import TTLocalizer +import string +from direct.fsm import StateData +from toontown.toonbase.ToontownBattleGlobals import gagIsPaidOnly +from toontown.toontowngui.TeaserPanel import TeaserPanel + +class PlantTreeGUI(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantTreeGUI') + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.oldActivateMode = base.localAvatar.inventory.activateMode + self._teaserPanel = None + base.localAvatar.inventory.setActivateMode('plantTree') + base.localAvatar.inventory.show() + self.accept('inventory-selection', self.__handleInventory) + self.accept('inventory-pass', self.__handleCancel) + return + + def destroy(self): + self.ignore('inventory-selection') + self.ignore('inventory-pass') + base.localAvatar.inventory.setActivateMode(self.oldActivateMode) + base.localAvatar.inventory.hide() + if self._teaserPanel: + self._teaserPanel.destroy() + self._teaserPanel = None + return + + def __handleInventory(self, track, level): + if gagIsPaidOnly(track, level) and not base.cr.isPaid(): + self._teaserPanel = TeaserPanel('plantGags') + return + if base.localAvatar.inventory.numItem(track, level) > 0: + messenger.send(self.doneEvent, [True, track, level]) + else: + self.notify.error("An item we don't have: track %s level %s was selected." % (track, level)) + + def __handleCancel(self): + messenger.send(self.doneEvent, [False, None, None]) + return diff --git a/toontown/estate/PlantingGUI.py b/toontown/estate/PlantingGUI.py new file mode 100644 index 0000000..9a1908e --- /dev/null +++ b/toontown/estate/PlantingGUI.py @@ -0,0 +1,410 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.estate import GardenGlobals +from direct.interval.IntervalGlobal import * +from toontown.estate import SpecialsPhoto +USE_SCROLLING_BEAN_BOX = False +JELLY_BEAN_PICKER_HAS_EMPTY_BOX = False +CAN_CHANGE_BEAN_COLOR = True +FORCE_LEFT_TO_RIGHT = True +ONLY_ONE_SPIFFY_BOX_CAN_BE_CLICKED = True +DO_PICKER_INTERVAL = False +PICKER_ALWAYS_UP = True + +def loadJellyBean(parent, beanIndex): + gui = loader.loadModel('phase_5.5/models/estate/jellyBean') + newBean = gui.instanceTo(parent) + parent.setScale(0.09) + colors = GardenGlobals.BeanColors[beanIndex] + parent.setColorScale(colors[0] / 255.0 * 1.0, colors[1] / 255.0 * 1.0, colors[2] / 255.0 * 1.0, 1) + + +class GenericBoxScrollList(DirectScrolledList): + + def __init__(self, parent, items, **kw): + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.forceHeight = 1 + optiondefs = (('parent', parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.3, 1.3, -1.3), None), + ('incButton_pos', (0, 0, -0.525), None), + ('incButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.3, 1.3, 1.3), None), + ('decButton_pos', (0, 0, 0.525), None), + ('decButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('numItemsVisible', 1, None), + ('items', items, None), + ('scrollSpeed', 1.0, None), + ('forceHeight', 1, None)) + gui.removeNode() + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent, forceHeight=self.forceHeight) + self.initialiseoptions(GenericBoxScrollList) + return None + + +class BoxItem(NodePath): + + def getHeight(self): + return 0.05 + + +JellyBeanPickerEndPos = (0, 0, 0.1) +JellyBeanPickerScale = (1.1, 1.0, 0.13) +if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + JellyBeanPickerGeomScale = (1.0, 1.0, 1.0) +else: + JellyBeanPickerGeomScale = (0.9, 1.0, 1.0) +JellyBeanPickerScaleInverse = (1.0 / JellyBeanPickerScale[0], 1.0 / JellyBeanPickerScale[1], 1.0 / JellyBeanPickerScale[2]) + +class JellyBeanPicker(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantingGUI') + + def __init__(self, parent, callBack, boxPosition, **kw): + self.index = 0 + self.selectedIndex = 0 + self.callBack = callBack + self.boxPosition = boxPosition + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=(0.8, 0.8, 0.8, 1.0), geom_scale=JellyBeanPickerGeomScale, scale=JellyBeanPickerScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, 0)) + self.initialiseoptions(JellyBeanPicker) + self.jellyBeanBoxList = [] + self.createJellyBeanBoxes() + return + + def jellyBeanBoxClicked(self, beanIndex): + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + self.callBack(self.boxPosition, beanIndex) + else: + self.callBack(self.boxPosition, beanIndex + 1) + + def createJellyBeanBox(self, beanIndex, xPos, zPos): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = self.jellyBeanBoxClicked + newBox = DirectButton(parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, scale=JellyBeanPickerScaleInverse, relief=None, state=state, command=command, extraArgs=[beanIndex], text='', text_pos=(0.0, 0.1), text_scale=0.07, text_fg=Vec4(0, 0, 0, 0), text1_fg=Vec4(0, 0, 0, 0), text2_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 0)) + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + if beanIndex: + beanParent = newBox.attachNewNode('bean_%d' % (beanIndex - 1)) + loadJellyBean(beanParent, beanIndex - 1) + else: + beanParent = newBox.attachNewNode('bean_%d' % beanIndex) + loadJellyBean(beanParent, beanIndex) + self.jellyBeanBoxList.append(newBox) + return + + def setColorText(self): + for beanIndex in range(len(self.jellyBeanBoxList)): + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + if beanIndex: + box = self.jellyBeanBoxList[beanIndex] + box['text'] = TTLocalizer.BeanColorWords[beanIndex - 1] + else: + box = self.jellyBeanBoxList[beanIndex] + box['text'] = TTLocalizer.BeanColorWords[beanIndex] + + def createJellyBeanBoxes(self): + zCoord = 0 + xIncrement = 0.095 + xPos = 0 + maxBoxes = len(GardenGlobals.BeanColors) + if JELLY_BEAN_PICKER_HAS_EMPTY_BOX: + maxBoxes += 1 + startingXCoord = -0.1 * float(maxBoxes) / 2.0 + 0.075 + for activeBox in range(maxBoxes): + xPos = xIncrement * activeBox + startingXCoord + self.createJellyBeanBox(activeBox, xPos, zCoord) + + +class SpiffyBeanBox(DirectButton): + + def __init__(self, parent, index, **kw): + self.boxIndex = index + self.selectedIndex = 0 + optiondefs = () + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, parent=parent) + self.initialiseoptions(SpiffyBeanBox) + self.selectedBean = self.attachNewNode('selectedBean') + + def getSelectedIndex(self): + if hasattr(self, 'selectedIndex'): + return self.selectedIndex + return 0 + + def setSelectedIndex(self, newIndex): + self.selectedIndex = newIndex + self.selectedBean.removeNode() + self.selectedBean = self.attachNewNode('selectedBean') + if newIndex: + newIndex -= 1 + loadJellyBean(self.selectedBean, newIndex) + + +class PlantingGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PlantingGUI') + + def __init__(self, doneEvent, specialBoxActive = False): + if specialBoxActive: + instructions = TTLocalizer.GardeningChooseBeansItem + instructionsPos = (0, 0.4) + else: + instructions = TTLocalizer.GardeningChooseBeans + instructionsPos = (0, 0.35) + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.5, 1.0, 1.0), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text=instructions, text_wordwrap=20, text_scale=0.08, text_pos=instructionsPos) + self.initialiseoptions(PlantingGUI) + self.doneEvent = doneEvent + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + 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')) + resetImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(-0.3, 0, -0.35), text=TTLocalizer.PlantingGuiCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.3, 0, -0.35), text=TTLocalizer.PlantingGuiOk, text_scale=0.06, text_pos=(0, -0.1), command=self.__doPlant) + self.resetButton = DirectButton(parent=self, relief=None, image=resetImageList, pos=(0.0, 0, -0.35), text=TTLocalizer.PlantingGuiReset, text_scale=0.06, text_pos=(0, -0.1), command=self.__reset) + buttons.removeNode() + self.availableBoxes = base.localAvatar.getBoxCapability() + self.maxBoxes = GardenGlobals.getNumberOfShovelBoxes() + self.activeBoxesList = [] + self.specialBox = None + self.specialBoxActive = specialBoxActive + self.boxList = [] + self.jellyBeanPicker = None + self.jellyBeanPickerInterval = None + self.createBoxes() + guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui') + 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(), pos=(-0.85, 0, 0.2), scale=0.5) + self.matchBoxesToAvailableMoney() + if PICKER_ALWAYS_UP: + self.spiffyBeanBoxClicked(0) + return + + def destroy(self): + if self.boxList: + for box in self.boxList: + box.destroy() + + self.boxList = [] + DirectFrame.destroy(self) + self.doneEvent = None + if self.jellyBeanPickerInterval: + self.jellyBeanPickerInterval.finish() + self.jellyBeanPickerInterval = None + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + self.jellyBeanPicker = None + if hasattr(self, 'specialPhotoList') and self.specialPhotoList: + for photo in self.specialPhotoList: + photo.destroy() + + self.specialPhotoList = [] + return + + def __cancel(self): + messenger.send(self.doneEvent, [0, '', -1]) + messenger.send('wakeup') + + def __reset(self): + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + for box in self.boxList: + box.setSelectedIndex(0) + + self.beanBank['text'] = str(base.localAvatar.getMoney() + base.localAvatar.getBankMoney()) + self.matchBoxesToAvailableMoney() + if PICKER_ALWAYS_UP: + self.spiffyBeanBoxClicked(0) + messenger.send('wakeup') + + def getRecipeStr(self): + retval = '' + for box in self.boxList: + beanIndex = box.getSelectedIndex() + if beanIndex: + beanIndex -= 1 + beanLetter = GardenGlobals.BeanColorLetters[beanIndex] + retval += beanLetter + + return retval + + def __doPlant(self): + recipeStr = self.getRecipeStr() + selectedSpecial = self.specialButton.getSelectedIndex() + selectedSpecial -= 1 + messenger.send(self.doneEvent, [1, recipeStr, selectedSpecial]) + messenger.send('wakeup') + + def createBoxes(self): + zCoord = -0.15 + xIncrement = 0.1 + xPos = 0 + startingXCoord = -0.1 * float(self.maxBoxes) / 2.0 + 0.05 + for activeBox in range(self.availableBoxes): + xPos = xIncrement * activeBox + startingXCoord + if USE_SCROLLING_BEAN_BOX: + self.createScrollingBeanBox(activeBox, xPos, zCoord, True) + else: + self.createSpiffyBeanBox(activeBox, xPos, zCoord, True) + + for disabledBox in range(self.availableBoxes, self.maxBoxes): + xPos = xIncrement * disabledBox + startingXCoord + if USE_SCROLLING_BEAN_BOX: + self.createScrollingBeanBox(disabledBox, xPos, zCoord, False) + else: + self.createSpiffyBeanBox(disabledBox, xPos, zCoord, False) + + xPos += xIncrement * 2 + self.createSpecialBox(xPos, zCoord) + + def createSpecialBox(self, xPos, zPos): + geomColor = (1, 1, 1, 1) + if not self.specialBoxActive: + geomColor = (0.5, 0.5, 0.5, 1) + geomScaleX = 0.2 + geomScaleZ = 0.2 + self.specialButtonFrame = DirectFrame(parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(geomScaleX, 1.0, geomScaleZ), geom_color=geomColor, relief=None) + items = [] + if self.specialBoxActive: + gardenSpecials = base.localAvatar.getGardenSpecials() + tempItem = BoxItem(self.attachNewNode('blankSpecial')) + items.append(tempItem) + self.specialPhotoList = [] + for item in gardenSpecials: + tempItem = BoxItem(self.specialButtonFrame.attachNewNode('temp1')) + specialsPhoto = SpecialsPhoto.SpecialsPhoto(item[0], parent=tempItem) + specialsPhoto.setBackBounds(-geomScaleX / 2.0, geomScaleX / 2.0, -geomScaleZ / 2.0, geomScaleZ / 2.0) + specialsPhoto.setBackColor(1.0, 1.0, 1.0, 1.0) + items.append(tempItem) + self.specialPhotoList.append(specialsPhoto) + + self.specialButton = GenericBoxScrollList(self.specialButtonFrame, items, incButton_pos=(0, 0, -0.135), incButton_scale=(0.75, 1.0, -1.0), decButton_pos=(0, 0, 0.135), decButton_scale=(0.75, 1.0, 1.0), command=self.photoSpecialChanged) + return + + def photoSpecialChanged(self): + if not hasattr(self, 'specialButton'): + return + messenger.send('wakeup') + selectedSpecial = self.specialButton.getSelectedIndex() + if selectedSpecial == 0: + for photo in self.specialPhotoList: + photo.hide() + + else: + selectedSpecial -= 1 + self.specialPhotoList[selectedSpecial].show() + + def showFirstSpecial(self): + if len(self.specialButton['items']) > 1: + self.specialButton.scrollTo(self.specialButton.getSelectedIndex() + 1) + + def createScrollingBeanBox(self, index, xPos, zPos, active): + geomColor = (1, 1, 1, 1) + if not active: + geomColor = (0.5, 0.5, 0.5, 1) + boxFrame = DirectFrame(parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, relief=None) + items = [] + if active: + tempItem = BoxItem(self.attachNewNode('emptyBean')) + items.append(tempItem) + for curBean in range(len(GardenGlobals.BeanColors)): + tempItem = BoxItem(self.attachNewNode('bean-%d-%d' % (index, curBean))) + loadJellyBean(tempItem, curBean) + items.append(tempItem) + + box = GenericBoxScrollList(boxFrame, items, incButton_pos=(0, 0, -0.07), incButton_scale=(0.4, 1.0, -1.0), decButton_pos=(0, 0, 0.065), decButton_scale=(0.4, 1.0, 1.0)) + self.boxList.append(box) + return + + def spiffyBeanBoxClicked(self, index): + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + outOfMoney = int(self.beanBank['text']) <= 0 + if self.boxList[index].getSelectedIndex() == 0 and outOfMoney: + return + if not CAN_CHANGE_BEAN_COLOR and self.boxList[index].getSelectedIndex(): + return + self.jellyBeanPicker = JellyBeanPicker(self, self.selectedNewBeanColor, index) + self.jellyBeanPicker.setPos(*JellyBeanPickerEndPos) + if DO_PICKER_INTERVAL: + boxPos = self.boxList[index].getPos() + self.jellyBeanPicker.setPos(boxPos) + self.jellyBeanPickerInterval = Sequence(Parallel(self.jellyBeanPicker.posInterval(duration=0.3, pos=VBase3(*JellyBeanPickerEndPos)), self.jellyBeanPicker.scaleInterval(duration=0.3, startScale=VBase3(JellyBeanPickerScale[0] / 10.0, JellyBeanPickerScale[1], JellyBeanPickerScale[2]), scale=VBase3(*JellyBeanPickerScale))), Func(self.jellyBeanPicker.setColorText)) + self.jellyBeanPickerInterval.start() + else: + self.jellyBeanPicker.setColorText() + + def matchBoxesToAvailableMoney(self): + outOfMoney = int(self.beanBank['text']) <= 0 + if outOfMoney: + for box in self.boxList: + if box.getSelectedIndex() == 0: + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.5, 0.5, 0.5, 1) + + else: + for box in self.boxList: + box['state'] = DGG.NORMAL + box.setState() + box.setColorScale(1, 1, 1, 1) + + if FORCE_LEFT_TO_RIGHT: + boxIndexToEnable = len(self.getRecipeStr()) + for i in range(0, boxIndexToEnable): + box = self.boxList[i] + if ONLY_ONE_SPIFFY_BOX_CAN_BE_CLICKED: + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.875, 0.875, 0.875, 1) + + if boxIndexToEnable < self.maxBoxes: + box = self.boxList[boxIndexToEnable] + if boxIndexToEnable >= self.availableBoxes: + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.5, 0.5, 0.5, 1) + for i in range(boxIndexToEnable + 1, len(self.boxList)): + box = self.boxList[i] + box['state'] = DGG.DISABLED + box.setState() + box.setColorScale(0.5, 0.5, 0.5, 1) + + def selectedNewBeanColor(self, boxPosition, beanIndex): + self.boxList[boxPosition].setSelectedIndex(beanIndex) + if self.jellyBeanPicker: + self.jellyBeanPicker.destroy() + self.jellyBeanPicker = None + cost = len(self.getRecipeStr()) + newMoney = base.localAvatar.getMoney() + base.localAvatar.getBankMoney() - cost + self.beanBank['text'] = str(newMoney) + self.matchBoxesToAvailableMoney() + messenger.send('wakeup') + if PICKER_ALWAYS_UP: + numBoxesFilled = len(self.getRecipeStr()) + if numBoxesFilled < self.availableBoxes: + self.spiffyBeanBoxClicked(numBoxesFilled) + return + + def createSpiffyBeanBox(self, index, xPos, zPos, active): + geomColor = (1, 1, 1, 1) + state = DGG.NORMAL + command = self.spiffyBeanBoxClicked + if not active: + geomColor = (0.5, 0.5, 0.5, 1) + command = None + state = DGG.DISABLED + newBox = SpiffyBeanBox(index=index, parent=self, pos=(xPos, 0, zPos), geom=DGG.getDefaultDialogGeom(), geom_scale=(0.1, 1.0, 0.1), geom_color=geomColor, relief=None, state=state, command=command, extraArgs=[index]) + self.boxList.append(newBox) + return diff --git a/toontown/estate/SpecialsPhoto.py b/toontown/estate/SpecialsPhoto.py new file mode 100644 index 0000000..6fd3813 --- /dev/null +++ b/toontown/estate/SpecialsPhoto.py @@ -0,0 +1,220 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.fishing import FishGlobals +import GardenGlobals +from direct.actor import Actor +import random + +class DirectRegion(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectRegion') + + def __init__(self, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('DirectRegion')) + + def destroy(self): + self.unload() + self.parent = None + return + + def setBounds(self, *bounds): + self.bounds = bounds + + def setColor(self, *colors): + self.color = colors + + def show(self): + pass + + def hide(self): + NodePath.NodePath.hide(self) + + def load(self): + if not hasattr(self, 'cRender'): + self.cRender = NodePath('fishSwimRender') + self.fishSwimCamera = self.cRender.attachNewNode('fishSwimCamera') + self.cCamNode = Camera('fishSwimCam') + 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.fishSwimCam = self.fishSwimCamera.attachNewNode(self.cCamNode) + cm = CardMaker('displayRegionCard') + apply(cm.setFrame, self.bounds) + self.card = card = self.attachNewNode(cm.generate()) + apply(card.setColor, self.color) + newBounds = card.getTightBounds() + ll = render2d.getRelativePoint(card, newBounds[0]) + ur = render2d.getRelativePoint(card, newBounds[1]) + newBounds = [ll.getX(), + ur.getX(), + ll.getZ(), + ur.getZ()] + newBounds = map(lambda x: max(0.0, min(1.0, (x + 1.0) / 2.0)), newBounds) + self.cDr = base.win.makeDisplayRegion(*newBounds) + self.cDr.setSort(10) + self.cDr.setClearColor(card.getColor()) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setCamera(self.fishSwimCam) + return self.cRender + + def unload(self): + if hasattr(self, 'cRender'): + base.win.removeDisplayRegion(self.cDr) + del self.cRender + del self.fishSwimCamera + del self.cCamNode + del self.cLens + del self.fishSwimCam + del self.cDr + + +class SpecialsPhoto(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('SpecialsPhoto') + + def __init__(self, type = None, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('SpecialsPhoto')) + self.type = type + self.actor = None + self.sound = None + self.soundTrack = None + self.track = None + self.specialsFrame = None + return + + def destroy(self): + self.hide() + if hasattr(self, 'background'): + self.background.destroy() + del self.background + if hasattr(self, 'specialsFrame') and hasattr(self.specialsFrame, 'destroy'): + self.specialsFrame.destroy() + if hasattr(self, 'toonStatuary'): + if self.toonStatuary.toon: + self.toonStatuary.deleteToon() + self.type = None + del self.soundTrack + del self.track + self.parent = None + return + + def update(self, type): + self.type = type + + def setBackBounds(self, *bounds): + self.backBounds = bounds + + def setBackColor(self, *colors): + self.backColor = colors + + def load(self): + pass + + def makeSpecialsFrame(self, actor): + actor.setDepthTest(1) + actor.setDepthWrite(1) + if not hasattr(self, 'specialsDisplayRegion'): + self.specialsDisplayRegion = DirectRegion(parent=self) + apply(self.specialsDisplayRegion.setBounds, self.backBounds) + apply(self.specialsDisplayRegion.setColor, self.backColor) + frame = self.specialsDisplayRegion.load() + pitch = frame.attachNewNode('pitch') + rotate = pitch.attachNewNode('rotate') + scale = rotate.attachNewNode('scale') + actor.reparentTo(scale) + bMin, bMax = actor.getTightBounds() + center = (bMin + bMax) / 2.0 + actor.setPos(-center[0], -center[1], -center[2]) + pitch.setY(2.5) + return frame + + def loadModel(self, specialsIndex): + if specialsIndex == -1: + nodePath = self.attachNewNode('blank') + return nodePath + if specialsIndex >= 105 and specialsIndex <= 108: + from toontown.estate import DistributedToonStatuary + self.toonStatuary = DistributedToonStatuary.DistributedToonStatuary(None) + self.toonStatuary.setupStoneToon(base.localAvatar.style) + self.toonStatuary.poseToonFromSpecialsIndex(specialsIndex) + self.toonStatuary.toon.setH(180) + pedestalModelPath = GardenGlobals.Specials[specialsIndex]['photoModel'] + pedestal = loader.loadModel(pedestalModelPath) + self.toonStatuary.toon.reparentTo(pedestal) + pedestal.setScale(GardenGlobals.Specials[specialsIndex]['photoScale'] * 0.5) + return pedestal + elif specialsIndex == 135: + model = Actor.Actor() + modelPath = GardenGlobals.Specials[specialsIndex]['photoModel'] + anims = GardenGlobals.Specials[specialsIndex]['photoAnimation'] + animPath = modelPath + anims[1] + model.loadModel(modelPath + anims[0]) + model.loadAnims(dict([[anims[1], animPath]])) + frameNo = random.randint(1, 2) + model.pose(anims[1], 1) + model.setScale(GardenGlobals.Specials[specialsIndex]['photoScale'] * 0.1) + return model + else: + modelName = GardenGlobals.Specials[specialsIndex]['photoModel'] + nodePath = loader.loadModel(modelName) + desat = None + colorTuple = (1, 1, 1) + if desat and not desat.isEmpty(): + desat.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + else: + nodePath.setColorScale(colorTuple[0], colorTuple[1], colorTuple[2], 1.0) + nodePath.setScale(GardenGlobals.Specials[specialsIndex]['photoScale'] * 0.5) + return nodePath + return + + def show(self, showBackground = 0): + self.notify.debug('show') + messenger.send('wakeup') + if self.specialsFrame: + if hasattr(self.actor, 'cleanup'): + self.actor.cleanup() + if hasattr(self, 'specialsDisplayRegion'): + self.specialsDisplayRegion.unload() + self.hide() + self.actor = self.loadModel(self.type) + self.specialsFrame = self.makeSpecialsFrame(self.actor) + if showBackground: + if not hasattr(self, 'background'): + background = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + background = background.find('**/Fish_BG') + self.background = background + self.background.setPos(0, 15, 0) + self.background.setScale(11) + self.background.reparentTo(self.specialsFrame) + + def hide(self): + NodePath.NodePath.hide(self) + if hasattr(self, 'specialsDisplayRegion'): + self.specialsDisplayRegion.unload() + if hasattr(self, 'background'): + self.background.hide() + if self.actor: + if hasattr(self.actor, 'stop'): + self.actor.stop() + self.actor.hide() + if self.sound: + self.sound.stop() + self.sound = None + if self.soundTrack: + self.soundTrack.pause() + self.soundTrack = None + if self.track: + self.track.pause() + self.track = None + if hasattr(self, 'toonStatuary'): + if self.toonStatuary.toon: + self.toonStatuary.deleteToon() + return + + def changeVariety(self, variety): + self.variety = variety diff --git a/toontown/estate/ToonStatueSelectionGUI.py b/toontown/estate/ToonStatueSelectionGUI.py new file mode 100644 index 0000000..0bf5e6b --- /dev/null +++ b/toontown/estate/ToonStatueSelectionGUI.py @@ -0,0 +1,167 @@ +from toontown.estate import PlantingGUI +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.estate import GardenGlobals +from toontown.estate import DistributedToonStatuary +from direct.interval.IntervalGlobal import * +from direct.gui.DirectScrolledList import * +from toontown.toon import Toon +from toontown.toon import DistributedToon +from direct.distributed import DistributedObject + +class ToonStatueSelectionGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonStatueSelectionGUI') + + def __init__(self, doneEvent, specialBoxActive = False): + base.tssGUI = self + instructions = TTLocalizer.GardeningChooseToonStatue + instructionsPos = (0, 0.4) + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.5, 1.0, 1.0), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text=instructions, text_wordwrap=18, text_scale=0.08, text_pos=instructionsPos) + self.initialiseoptions(ToonStatueSelectionGUI) + self.doneEvent = doneEvent + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + 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')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(-0.3, 0, -0.35), text=TTLocalizer.PlantingGuiCancel, text_scale=0.06, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.3, 0, -0.35), text=TTLocalizer.PlantingGuiOk, text_scale=0.06, text_pos=(0, -0.1), command=self.__accept) + buttons.removeNode() + self.ffList = [] + self.friends = {} + self.doId2Dna = {} + 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.createFriendsList() + return + + def destroy(self): + self.doneEvent = None + self.previewToon.delete() + self.previewToon = None + for ff in self.ffList: + self.friends[ff].destroy() + + self.ffList = [] + self.friends = {} + self.doId2Dna = {} + self.scrollList.destroy() + DirectFrame.destroy(self) + return + + def __cancel(self): + messenger.send(self.doneEvent, [0, '', -1]) + messenger.send('wakeup') + + def __accept(self): + messenger.send(self.doneEvent, [1, '', DistributedToonStatuary.dnaCodeFromToonDNA(self.dnaSelected)]) + messenger.send('wakeup') + + def createFriendsList(self): + self.__makeFFlist() + if len(self.ffList) > 0: + 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=DGG.SUNKEN, itemFrame_frameSize=(-0.01, + 0.35, + -0.35, + 0.04), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=8, itemFrame_scale=1.0, items=[]) + gui.removeNode() + self.scrollList.setPos(0.35, 0, 0.125) + self.scrollList.setScale(1.25) + 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() + return + + def checkFamily(self, doId): + test = 0 + for familyMember in base.cr.avList: + if familyMember.id == doId: + test = 1 + + return test + + def __makeFFlist(self): + playerAvatar = (base.localAvatar.doId, base.localAvatar.name, NametagGroup.CCNonPlayer) + self.ffList.append(playerAvatar) + self.dnaSelected = base.localAvatar.style + self.createPreviewToon(self.dnaSelected) + 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') + + 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') + if self.checkFamily(friendId): + if friendId == base.localAvatar.doId: + self.createPreviewToon(base.localAvatar.style) + elif self.doId2Dna.has_key(friendId): + self.createPreviewToon(self.doId2Dna[friendId]) + else: + familyAvatar = DistributedToon.DistributedToon(base.cr) + familyAvatar.doId = friendId + familyAvatar.forceAllowDelayDelete() + base.cr.getAvatarDetails(familyAvatar, self.__handleFamilyAvatar, 'DistributedToon') + else: + friend = base.cr.identifyFriend(friendId) + if friend: + self.createPreviewToon(friend.style) + + def __handleFamilyAvatar(self, gotData, avatar, dclass): + self.doId2Dna[avatar.doId] = avatar.style + self.createPreviewToon(avatar.style) + avatar.delete() + + def createPreviewToon(self, dna): + if hasattr(self, 'previewToon'): + self.previewToon.delete() + self.dnaSelected = dna + self.previewToon = Toon.Toon() + self.previewToon.setDNA(dna) + self.previewToon.loop('neutral') + self.previewToon.setH(180) + self.previewToon.setPos(-0.3, 0, -0.3) + self.previewToon.setScale(0.13) + self.previewToon.reparentTo(self) + self.previewToon.startBlink() + self.previewToon.startLookAround() + self.previewToon.getGeomNode().setDepthWrite(1) + self.previewToon.getGeomNode().setDepthTest(1) diff --git a/toontown/estate/TrunkGUI.py b/toontown/estate/TrunkGUI.py new file mode 100644 index 0000000..11f0b5f --- /dev/null +++ b/toontown/estate/TrunkGUI.py @@ -0,0 +1,536 @@ +from direct.showbase.PythonUtil import Functor +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.distributed import ClockDelta +from direct.fsm import StateData +from direct.task.Task import Task +import ClosetGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.toon import ToonDNA +from toontown.makeatoon.MakeAToonGlobals import * +from toontown.makeatoon import ShuffleButton + +class TrunkGUI(StateData.StateData): + notify = directNotify.newCategory('TrunkGUI') + + def __init__(self, isOwner, doneEvent, cancelEvent, swapHatEvent, swapGlassesEvent, swapBackpackEvent, swapShoesEvent, deleteEvent, hatList = None, glassesList = None, backpackList = None, shoesList = None): + StateData.StateData.__init__(self, doneEvent) + self.toon = None + self.hatList = hatList + self.glassesList = glassesList + self.backpackList = backpackList + self.shoesList = shoesList + self.isOwner = isOwner + self.swapHatEvent = swapHatEvent + self.swapGlassesEvent = swapGlassesEvent + self.swapBackpackEvent = swapBackpackEvent + self.swapShoesEvent = swapShoesEvent + self.deleteEvent = deleteEvent + self.cancelEvent = cancelEvent + self.genderChange = 0 + self.verify = None + return + + def load(self): + self.gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowRollover = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.gui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowDisabled = self.gui.find('**/tt_t_gui_mat_arrowDisabled') + guiArrowRotateUp = self.gui.find('**/tt_t_gui_mat_arrowRotateUp') + guiArrowRotateDown = self.gui.find('**/tt_t_gui_mat_arrowRotateDown') + shuffleFrame = self.gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleArrowUp = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = self.gui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowRollover = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDisabled = self.gui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + self.parentFrame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.416), frameColor=(1, 0, 0, 0)) + + def addFrame(posZ, text): + return DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, posZ), hpr=(0, 0, 3), scale=1.2, frameColor=(1, 1, 1, 1), text=text, text_scale=0.0575, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + + def addButton(parent, scale, hoverScale, posX, command, extraArg): + return DirectButton(parent=parent, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=scale, image1_scale=hoverScale, image2_scale=hoverScale, pos=(posX, 0, 0), command=command, extraArgs=[extraArg]) + + self.hatFrame = addFrame(0.1, TTLocalizer.TrunkHatGUI) + self.hatLButton = addButton(self.hatFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapHat, -1) + self.hatRButton = addButton(self.hatFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapHat, 1) + self.glassesFrame = addFrame(-0.15, TTLocalizer.TrunkGlassesGUI) + self.glassesLButton = addButton(self.glassesFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapGlasses, -1) + self.glassesRButton = addButton(self.glassesFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapGlasses, 1) + self.backpackFrame = addFrame(-0.4, TTLocalizer.TrunkBackpackGUI) + self.backpackLButton = addButton(self.backpackFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapBackpack, -1) + self.backpackRButton = addButton(self.backpackFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapBackpack, 1) + self.shoesFrame = addFrame(-0.65, TTLocalizer.TrunkShoesGUI) + self.shoesLButton = addButton(self.shoesFrame, halfButtonScale, halfButtonHoverScale, -0.2, self.swapShoes, -1) + self.shoesRButton = addButton(self.shoesFrame, halfButtonInvertScale, halfButtonInvertHoverScale, 0.2, self.swapShoes, 1) + self.parentFrame.hide() + self.shuffleFetchMsg = 'TrunkShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + self.gui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + self.cancelButton = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn2_UP'), self.gui.find('**/CrtAtoon_Btn2_DOWN'), self.gui.find('**/CrtAtoon_Btn2_RLLVR')), pos=(0.15, 0, -0.85), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.cancelButton.hide() + self.rotateL = DirectButton(relief=None, pos=(-0.15, 0, 0.85), image=(guiArrowRotateUp, + guiArrowRotateDown, + guiArrowRotateUp, + guiArrowRotateDown), image_scale=(-0.7, 0.7, 0.7), image1_scale=(-0.8, 0.8, 0.8), image2_scale=(-0.8, 0.8, 0.8)) + self.rotateL.hide() + self.rotateL.bind(DGG.B1PRESS, self.__rotateLDown) + self.rotateL.bind(DGG.B1RELEASE, self.__rotateLUp) + self.rotateR = DirectButton(relief=None, pos=(0.15, 0, 0.85), image=(guiArrowRotateUp, + guiArrowRotateDown, + guiArrowRotateUp, + guiArrowRotateDown), image_scale=(0.7, 0.7, 0.7), image1_scale=(0.8, 0.8, 0.8), image2_scale=(0.8, 0.8, 0.8)) + self.rotateR.hide() + self.rotateR.bind(DGG.B1PRESS, self.__rotateRDown) + self.rotateR.bind(DGG.B1RELEASE, self.__rotateRUp) + if self.isOwner: + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui.bam') + trashImage = (trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')) + self.trashPanel = DirectFrame(parent=aspect2d, image=DGG.getDefaultDialogGeom(), image_color=(1, 1, 0.75, 0.8), image_scale=(0.36, 0, 1.2), pos=(-.86, 0, 0.1), relief=None) + + def addTrashButton(posZ, text, extraArg): + return DirectButton(parent=self.trashPanel, image=trashImage, relief=None, pos=(-0.09, 0, posZ), command=self.__handleDelete, text=text, extraArgs=[extraArg], scale=(0.5, 0.5, 0.5), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.12, text_pos=(0.3, 0), text_fg=(0.8, 0.2, 0.2, 1), text_shadow=(0, 0, 0, 1), textMayChange=0) + + self.hatTrashButton = addTrashButton(0.5, TTLocalizer.TrunkDeleteHat, ToonDNA.HAT) + self.glassesTrashButton = addTrashButton(0.2, TTLocalizer.TrunkDeleteGlasses, ToonDNA.GLASSES) + self.backpackTrashButton = addTrashButton(-0.1, TTLocalizer.TrunkDeleteBackpack, ToonDNA.BACKPACK) + self.shoesTrashButton = addTrashButton(-0.4, TTLocalizer.TrunkDeleteShoes, ToonDNA.SHOES) + self.button = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn1_UP'), self.gui.find('**/CrtAtoon_Btn1_DOWN'), self.gui.find('**/CrtAtoon_Btn1_RLLVR')), pos=(-0.15, 0, -0.85), command=self.__handleButton, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + trashcanGui.removeNode() + return + + def unload(self): + taskMgr.remove(self.taskName('rotateL')) + taskMgr.remove(self.taskName('rotateR')) + self.ignore('verifyDone') + self.gui.removeNode() + del self.gui + self.parentFrame.destroy() + self.hatFrame.destroy() + self.glassesFrame.destroy() + self.backpackFrame.destroy() + self.shoesFrame.destroy() + self.hatLButton.destroy() + self.hatRButton.destroy() + self.glassesLButton.destroy() + self.glassesRButton.destroy() + self.backpackLButton.destroy() + self.backpackRButton.destroy() + self.shoesLButton.destroy() + self.shoesRButton.destroy() + del self.parentFrame + del self.hatFrame + del self.glassesFrame + del self.backpackFrame + del self.shoesFrame + del self.hatLButton + del self.hatRButton + del self.glassesLButton + del self.glassesRButton + del self.backpackLButton + del self.backpackRButton + del self.shoesLButton + del self.shoesRButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + self.cancelButton.destroy() + del self.cancelButton + self.rotateL.destroy() + del self.rotateL + self.rotateR.destroy() + del self.rotateR + if self.isOwner: + self.hatTrashButton.destroy() + self.glassesTrashButton.destroy() + self.backpackTrashButton.destroy() + self.shoesTrashButton.destroy() + self.button.destroy() + del self.hatTrashButton + del self.glassesTrashButton + del self.backpackTrashButton + del self.shoesTrashButton + del self.button + self.trashPanel.destroy() + del self.trashPanel + if self.verify: + self.verify.cleanup() + del self.verify + + def showButtons(self): + self.parentFrame.show() + self.cancelButton.show() + self.rotateL.show() + self.rotateR.show() + if self.isOwner: + self.hatTrashButton.show() + self.glassesTrashButton.show() + self.backpackTrashButton.show() + self.shoesTrashButton.show() + self.button.show() + + def hideButtons(self): + self.parentFrame.hide() + self.cancelButton.hide() + self.rotateL.hide() + self.rotateR.hide() + if self.isOwner: + self.hatTrashButton.hide() + self.glassesTrashButton.hide() + self.backpackTrashButton.hide() + self.shoesTrashButton.hide() + self.button.hide() + + def enter(self, toon): + self.notify.debug('enter') + base.disableMouse() + self.toon = toon + self.setupScrollInterface() + currHat = self.toon.hat + currHatIdx = self.hats.index(currHat) + self.swapHat(currHatIdx - self.hatChoice) + currGlasses = self.toon.glasses + currGlassesIdx = self.glasses.index(currGlasses) + self.swapGlasses(currGlassesIdx - self.glassesChoice) + currBackpack = self.toon.backpack + currBackpackIdx = self.backpacks.index(currBackpack) + self.swapBackpack(currBackpackIdx - self.backpackChoice) + currShoes = self.toon.shoes + currShoesIdx = self.shoes.index(currShoes) + self.swapShoes(currShoesIdx - self.shoesChoice) + choicePool = [self.hats, + self.glasses, + self.backpacks, + self.shoes] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeAccessories) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + + def exit(self): + try: + del self.toon + except: + self.notify.warning('TrunkGUI: toon not found') + + self.hideButtons() + self.ignore('enter') + self.ignore('next') + self.ignore('last') + self.ignore(self.shuffleFetchMsg) + + def setupButtons(self): + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('next', self.__handleForward) + return None + + def setupScrollInterface(self): + self.notify.debug('setupScrollInterface') + if self.hatList == None: + self.hatList = self.toon.getHatList() + if self.glassesList == None: + self.glassesList = self.toon.getGlassesList() + if self.backpackList == None: + self.backpackList = self.toon.getBackpackList() + if self.shoesList == None: + self.shoesList = self.toon.getShoesList() + self.hats = [] + self.glasses = [] + self.backpacks = [] + self.shoes = [] + self.hats.append((self.toon.hat[0], self.toon.hat[1], self.toon.hat[2])) + self.glasses.append((self.toon.glasses[0], self.toon.glasses[1], self.toon.glasses[2])) + self.backpacks.append((self.toon.backpack[0], self.toon.backpack[1], self.toon.backpack[2])) + self.shoes.append((self.toon.shoes[0], self.toon.shoes[1], self.toon.shoes[2])) + i = 0 + while i < len(self.hatList): + self.hats.append((self.hatList[i], self.hatList[i + 1], self.hatList[i + 2])) + i = i + 3 + + i = 0 + while i < len(self.glassesList): + self.glasses.append((self.glassesList[i], self.glassesList[i + 1], self.glassesList[i + 2])) + i = i + 3 + + i = 0 + while i < len(self.backpackList): + self.backpacks.append((self.backpackList[i], self.backpackList[i + 1], self.backpackList[i + 2])) + i = i + 3 + + i = 0 + while i < len(self.shoesList): + self.shoes.append((self.shoesList[i], self.shoesList[i + 1], self.shoesList[i + 2])) + i = i + 3 + + self.hatChoice = 0 + self.glassesChoice = 0 + self.backpackChoice = 0 + self.shoesChoice = 0 + self.swapHat(0) + self.swapGlasses(0) + self.swapBackpack(0) + self.swapShoes(0) + self.updateTrashButtons() + self.setupButtons() + return + + def updateTrashButtons(self): + if not self.isOwner: + return + if len(self.hats) < 2 or self.toon.hat[0] == 0: + self.hatTrashButton['state'] = DGG.DISABLED + else: + self.hatTrashButton['state'] = DGG.NORMAL + if len(self.glasses) < 2 or self.toon.glasses[0] == 0: + self.glassesTrashButton['state'] = DGG.DISABLED + else: + self.glassesTrashButton['state'] = DGG.NORMAL + if len(self.backpacks) < 2 or self.toon.backpack[0] == 0: + self.backpackTrashButton['state'] = DGG.DISABLED + else: + self.backpackTrashButton['state'] = DGG.NORMAL + if len(self.shoes) < 2 or self.toon.shoes[0] == 0: + self.shoesTrashButton['state'] = DGG.DISABLED + else: + self.shoesTrashButton['state'] = DGG.NORMAL + + def rotateToonL(self, task): + self.toon.setH(self.toon.getH() - 4) + return Task.cont + + def rotateToonR(self, task): + self.toon.setH(self.toon.getH() + 4) + return Task.cont + + def __rotateLUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('rotateL')) + + def __rotateLDown(self, event): + messenger.send('wakeup') + task = Task(self.rotateToonL) + taskMgr.add(task, self.taskName('rotateL')) + + def __rotateRUp(self, event): + messenger.send('wakeup') + taskMgr.remove(self.taskName('rotateR')) + + def __rotateRDown(self, event): + messenger.send('wakeup') + task = Task(self.rotateToonR) + taskMgr.add(task, self.taskName('rotateR')) + + def setGender(self, gender): + self.ownerGender = gender + self.genderChange = 1 + + def swapHat(self, offset): + length = len(self.hats) + self.hatChoice += offset + if self.hatChoice <= 0: + self.hatChoice = 0 + self.updateScrollButtons(self.hatChoice, length, 0, self.hatLButton, self.hatRButton) + if self.hatChoice < 0 or self.hatChoice >= len(self.hats) or len(self.hats[self.hatChoice]) != 3: + self.notify.warning('hatChoice index is out of range!') + return None + hat = self.hats[self.hatChoice] + self.toon.setHat(hat[0], hat[1], hat[2]) + if self.swapHatEvent != None: + messenger.send(self.swapHatEvent) + messenger.send('wakeup') + + def swapGlasses(self, offset): + length = len(self.glasses) + self.glassesChoice += offset + if self.glassesChoice <= 0: + self.glassesChoice = 0 + self.updateScrollButtons(self.glassesChoice, length, 0, self.glassesLButton, self.glassesRButton) + if self.glassesChoice < 0 or self.glassesChoice >= len(self.glasses) or len(self.glasses[self.glassesChoice]) != 3: + self.notify.warning('glassesChoice index is out of range!') + return None + glasses = self.glasses[self.glassesChoice] + self.toon.setGlasses(glasses[0], glasses[1], glasses[2]) + if self.swapGlassesEvent != None: + messenger.send(self.swapGlassesEvent) + messenger.send('wakeup') + + def swapBackpack(self, offset): + length = len(self.backpacks) + self.backpackChoice += offset + if self.backpackChoice <= 0: + self.backpackChoice = 0 + self.updateScrollButtons(self.backpackChoice, length, 0, self.backpackLButton, self.backpackRButton) + if self.backpackChoice < 0 or self.backpackChoice >= len(self.backpacks) or len(self.backpacks[self.backpackChoice]) != 3: + self.notify.warning('backpackChoice index is out of range!') + return None + backpack = self.backpacks[self.backpackChoice] + self.toon.setBackpack(backpack[0], backpack[1], backpack[2]) + if self.swapBackpackEvent != None: + messenger.send(self.swapBackpackEvent) + messenger.send('wakeup') + + def swapShoes(self, offset): + length = len(self.shoes) + self.shoesChoice += offset + if self.shoesChoice <= 0: + self.shoesChoice = 0 + self.updateScrollButtons(self.shoesChoice, length, 0, self.shoesLButton, self.shoesRButton) + if self.shoesChoice < 0 or self.shoesChoice >= len(self.shoes) or len(self.shoes[self.shoesChoice]) != 3: + self.notify.warning('shoesChoice index is out of range!') + return None + shoes = self.shoes[self.shoesChoice] + self.toon.setShoes(shoes[0], shoes[1], shoes[2]) + if self.swapShoesEvent != None: + messenger.send(self.swapShoesEvent) + messenger.send('wakeup') + + def updateScrollButtons(self, choice, length, startTex, lButton, rButton): + if choice >= length - 1: + rButton['state'] = DGG.DISABLED + else: + rButton['state'] = DGG.NORMAL + if choice <= 0: + lButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def resetClothes(self, style): + if self.toon: + oldHat = style[ToonDNA.HAT] + oldGlasses = style[ToonDNA.GLASSES] + oldBackpack = style[ToonDNA.BACKPACK] + oldShoes = style[ToonDNA.SHOES] + self.toon.setHat(oldHat[0], oldHat[1], oldHat[2]) + self.toon.setGlasses(oldGlasses[0], oldGlasses[1], oldGlasses[2]) + self.toon.setBackpack(oldBackpack[0], oldBackpack[1], oldBackpack[2]) + self.toon.setShoes(oldShoes[0], oldShoes[1], oldShoes[2]) + self.toon.loop('neutral', 0) + + def changeAccessories(self): + self.notify.debug('Entering changeAccessories') + NoItem = (0, 0, 0) + newChoice = self.shuffleButton.getCurrChoice() + if newChoice[0] in self.hats: + newHatIndex = self.hats.index(newChoice[0]) + else: + newHatIndex = self.hats.index(NoItem) + if newChoice[1] in self.glasses: + newGlassesIndex = self.glasses.index(newChoice[1]) + else: + newGlassesIndex = self.glasses.index(NoItem) + if newChoice[2] in self.backpacks: + newBackpackIndex = self.backpacks.index(newChoice[2]) + else: + newBackpackIndex = self.backpacks.index(NoItem) + if newChoice[3] in self.shoes: + newShoesIndex = self.shoes.index(newChoice[3]) + else: + newShoesIndex = self.shoes.index(NoItem) + oldHatIndex = self.hatChoice + oldGlassesIndex = self.glassesChoice + oldBackpackIndex = self.backpackChoice + oldShoesIndex = self.shoesChoice + self.swapHat(newHatIndex - oldHatIndex) + self.swapGlasses(newGlassesIndex - oldGlassesIndex) + self.swapBackpack(newBackpackIndex - oldBackpackIndex) + self.swapShoes(newShoesIndex - oldShoesIndex) + + def getCurrToonSetting(self): + return [self.hats[self.hatChoice], + self.glasses[self.glassesChoice], + self.backpacks[self.backpackChoice], + self.shoes[self.shoesChoice]] + + def removeHat(self, index): + listLen = len(self.hats) + if index < listLen: + del self.hats[index] + if self.hatChoice > index: + self.hatChoice -= 1 + elif self.hatChoice == index: + self.hatChoice = 0 + return 1 + return 0 + + def removeGlasses(self, index): + listLen = len(self.glasses) + if index < listLen: + del self.glasses[index] + if self.glassesChoice > index: + self.glassesChoice -= 1 + elif self.glassesChoice == index: + self.glassesChoice = 0 + return 1 + return 0 + + def removeBackpack(self, index): + listLen = len(self.backpacks) + if index < listLen: + del self.backpacks[index] + if self.backpackChoice > index: + self.backpackChoice -= 1 + elif self.backpackChoice == index: + self.backpackChoice = 0 + return 1 + return 0 + + def removeShoes(self, index): + listLen = len(self.shoes) + if index < listLen: + del self.shoes[index] + if self.shoesChoice > index: + self.shoesChoice -= 1 + elif self.shoesChoice == index: + self.shoesChoice = 0 + return 1 + return 0 + + def __handleButton(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + messenger.send('wakeup') + + def __handleCancel(self): + messenger.send(self.cancelEvent) + messenger.send('wakeup') + + def __handleDelete(self, which): + abortDeletion = False + if which == ToonDNA.HAT: + item = TTLocalizer.TrunkHat + elif which == ToonDNA.GLASSES: + item = TTLocalizer.TrunkGlasses + elif which == ToonDNA.BACKPACK: + item = TTLocalizer.TrunkBackpack + else: + item = TTLocalizer.TrunkShoes + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=TTLocalizer.ClosetVerifyDelete % item, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyDone', Functor(self.__handleVerifyDelete, which)) + messenger.send('wakeup') + + def __handleVerifyDelete(self, which): + status = self.verify.doneStatus + self.ignore('verifyDone') + self.verify.cleanup() + del self.verify + self.verify = None + if status == 'ok': + messenger.send(self.deleteEvent, [which]) + messenger.send('wakeup') + return + + def taskName(self, idString): + return idString + '-TrunkGUI' diff --git a/toontown/estate/__init__.py b/toontown/estate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/estate/houseDesign.py b/toontown/estate/houseDesign.py new file mode 100644 index 0000000..9721a21 --- /dev/null +++ b/toontown/estate/houseDesign.py @@ -0,0 +1,1655 @@ +from direct.directtools.DirectSelection import * +from direct.directtools.DirectUtil import ROUND_TO +from direct.directtools.DirectGeometry import LineNodePath +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from toontown.catalog import CatalogFurnitureItem +from toontown.catalog import CatalogItemTypes +from direct.showbase import PythonUtil +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +camPos50 = (Point3(0.0, -10.0, 50.0), + Point3(0.0, -9.66, 49.06), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPos40 = (Point3(0.0, -15.0, 40.0), + Point3(0.0, -14.5, 39.13), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPos30 = (Point3(0.0, -20.0, 30.0), + Point3(0.0, -19.29, 29.29), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPos20 = (Point3(0.0, -20.0, 20.0), + Point3(0.0, -19.13, 19.5), + Point3(0.0, 1.5, 12.38), + Point3(0.0, 1.5, -3.1), + 1) +camPosList = [camPos20, + camPos30, + camPos40, + camPos50] +DEFAULT_CAM_INDEX = 2 +NormalPickerPanelColor = (1, 0.9, 0.745, 1) +DisabledPickerPanelColor = (0.7, 0.65, 0.58, 1) +DeletePickerPanelColor = (1, 0.4, 0.4, 1) +DisabledDeletePickerPanelColor = (0.7, 0.3, 0.3, 1) + +class FurnitureItemPanel(DirectButton): + + def __init__(self, item, itemId, command = None, deleteMode = 0, withinFunc = None, helpCategory = None): + self.item = item + self.itemId = itemId + self.command = command + self.origHelpCategory = helpCategory + self.deleteMode = deleteMode + if self.deleteMode: + framePanelColor = DeletePickerPanelColor + else: + framePanelColor = NormalPickerPanelColor + DirectButton.__init__(self, relief=DGG.RAISED, frameSize=(-0.25, + 0.25, + -0.2, + 0.2), frameColor=framePanelColor, borderWidth=(0.02, 0.02), command=self.clicked) + if self.deleteMode: + helpCategory = 'FurnitureItemPanelDelete' + self.bindHelpText(helpCategory) + if withinFunc: + self.bind(DGG.WITHIN, lambda event: withinFunc(self.itemId)) + self.initialiseoptions(FurnitureItemPanel) + self.load() + + def show(self): + DirectFrame.show(self) + if self.ival: + self.ival.resume() + + def hide(self): + DirectFrame.hide(self) + if self.ival: + self.ival.pause() + + def load(self): + panelWidth = 7 + panelCenter = 0 + self.picture, self.ival = self.item.getPicture(base.localAvatar) + if self.picture: + self.picture.reparentTo(self) + self.picture.setScale(0.14) + self.picture.setPos(0, 0, -0.02) + text = self.item.getName() + text_pos = (0, -0.1, 0) + else: + text = self.item.getTypeName() + ': ' + self.item.getName() + text_pos = (0, -0.3, 0) + if self.ival: + self.ival.loop() + self.ival.pause() + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.17), scale=0.45, text=text, text_scale=0.15, text_fg=(0, 0, 0, 1), text_pos=text_pos, text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=panelWidth) + return + + def clicked(self): + self.command(self.item, self.itemId) + + def unload(self): + if self.item.hasPicture: + self.item.cleanupPicture() + del self.item + self.nameLabel.destroy() + del self.nameLabel + if self.ival: + self.ival.finish() + del self.ival + del self.picture + self.command = None + return + + def destroy(self): + self.unload() + DirectButton.destroy(self) + + def bindHelpText(self, category): + self.unbind(DGG.ENTER) + self.unbind(DGG.EXIT) + if category is None: + category = self.origHelpCategory + self.bind(DGG.ENTER, base.cr.objectManager.showHelpText, extraArgs=[category, self.item.getName()]) + self.bind(DGG.EXIT, base.cr.objectManager.hideHelpText) + return + + def setDeleteMode(self, deleteMode): + self.deleteMode = deleteMode + self.__updateAppearance() + + def enable(self, enabled): + if enabled: + self['state'] = DGG.NORMAL + else: + self['state'] = DGG.DISABLED + self.__updateAppearance() + + def __updateAppearance(self): + color = NormalPickerPanelColor + relief = DGG.RAISED + if self.deleteMode: + if self['state'] == DGG.DISABLED: + color = DisabledDeletePickerPanelColor + relief = DGG.SUNKEN + else: + color = DeletePickerPanelColor + relief = DGG.RAISED + elif self['state'] == DGG.DISABLED: + color = DisabledPickerPanelColor + relief = DGG.SUNKEN + else: + color = NormalPickerPanelColor + relief = DGG.RAISED + self['frameColor'] = color + + +class MovableObject(NodePath, DirectObject): + + def __init__(self, dfitem, parent = render): + NodePath.__init__(self) + self.assign(dfitem) + self.dfitem = dfitem + dfitem.transmitRelativeTo = dfitem.getParent() + self.reparentTo(parent) + self.setTag('movableObject', '1') + self.builtInCNodes = self.findAllMatches('**/+CollisionNode') + self.numBuiltInNodes = self.builtInCNodes.getNumPaths() + self.stashBuiltInCollisionNodes() + shadows = self.findAllMatches('**/*shadow*') + shadows.addPathsFrom(self.findAllMatches('**/*Shadow*')) + shadows.stash() + flags = self.dfitem.item.getFlags() + if flags & CatalogFurnitureItem.FLPainting: + self.setOnFloor(0) + self.setOnWall(1) + else: + self.setOnFloor(1) + self.setOnWall(0) + if flags & CatalogFurnitureItem.FLOnTable: + self.setOnTable(1) + else: + self.setOnTable(0) + if flags & CatalogFurnitureItem.FLRug: + self.setIsRug(1) + else: + self.setIsRug(0) + if flags & CatalogFurnitureItem.FLIsTable: + self.setIsTable(1) + else: + self.setIsTable(0) + m = self.getTransform() + self.iPosHpr() + bMin, bMax = self.bounds = self.getTightBounds() + bMin -= Vec3(0.1, 0.1, 0) + bMax += Vec3(0.1, 0.1, 0) + self.c0 = Point3(bMin[0], bMin[1], 0.2) + self.c1 = Point3(bMax[0], bMin[1], 0.2) + self.c2 = Point3(bMax[0], bMax[1], 0.2) + self.c3 = Point3(bMin[0], bMax[1], 0.2) + self.center = (bMin + bMax) / 2.0 + if flags & CatalogFurnitureItem.FLPainting: + self.dragPoint = Vec3(self.center[0], bMax[1], self.center[2]) + else: + self.dragPoint = Vec3(self.center[0], self.center[1], bMin[2]) + delta = self.dragPoint - self.c0 + self.radius = min(delta[0], delta[1]) + if self.getOnWall(): + self.setWallOffset(0.1) + else: + self.setWallOffset(self.radius + 0.1) + self.makeCollisionBox() + self.setTransform(m) + self.unstashBuiltInCollisionNodes() + shadows.unstash() + + def resetMovableObject(self): + self.unstashBuiltInCollisionNodes() + self.collisionNodePath.removeNode() + self.clearTag('movableObject') + + def setOnFloor(self, fOnFloor): + self.fOnFloor = fOnFloor + + def getOnFloor(self): + return self.fOnFloor + + def setOnWall(self, fOnWall): + self.fOnWall = fOnWall + + def getOnWall(self): + return self.fOnWall + + def setOnTable(self, fOnTable): + self.fOnTable = fOnTable + + def getOnTable(self): + return self.fOnTable + + def setIsRug(self, fIsRug): + self.fIsRug = fIsRug + + def getIsRug(self): + return self.fIsRug + + def setIsTable(self, fIsTable): + self.fIsTable = fIsTable + + def getIsTable(self): + return self.fIsTable + + def setWallOffset(self, offset): + self.wallOffset = offset + + def getWallOffset(self): + return self.wallOffset + + def destroy(self): + self.removeNode() + + def stashBuiltInCollisionNodes(self): + self.builtInCNodes.stash() + + def unstashBuiltInCollisionNodes(self): + self.builtInCNodes.unstash() + + def getFloorBitmask(self): + if self.getOnTable(): + return ToontownGlobals.FloorBitmask | ToontownGlobals.FurnitureTopBitmask + else: + return ToontownGlobals.FloorBitmask + + def getWallBitmask(self): + if self.getIsRug() or self.getOnWall(): + return ToontownGlobals.WallBitmask + else: + return ToontownGlobals.WallBitmask | ToontownGlobals.FurnitureSideBitmask + + def makeCollisionBox(self): + self.collisionNodePath = self.attachNewNode('furnitureCollisionNode') + if self.getIsRug() or self.getOnWall(): + return + mx = self.bounds[0][0] - 0.01 + Mx = self.bounds[1][0] + 0.01 + my = self.bounds[0][1] - 0.01 + My = self.bounds[1][1] + 0.01 + mz = self.bounds[0][2] + Mz = self.bounds[1][2] + cn = CollisionNode('sideCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.FurnitureSideBitmask) + self.collisionNodePath.attachNewNode(cn) + cp = CollisionPolygon(Point3(mx, My, mz), Point3(mx, my, mz), Point3(mx, my, Mz), Point3(mx, My, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, my, mz), Point3(Mx, My, mz), Point3(Mx, My, Mz), Point3(Mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(mx, my, mz), Point3(Mx, my, mz), Point3(Mx, my, Mz), Point3(mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, My, mz), Point3(mx, My, mz), Point3(mx, My, Mz), Point3(Mx, My, Mz)) + cn.addSolid(cp) + if self.getIsTable(): + cn = CollisionNode('topCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.FurnitureTopBitmask) + self.collisionNodePath.attachNewNode(cn) + cp = CollisionPolygon(Point3(mx, my, Mz), Point3(Mx, my, Mz), Point3(Mx, My, Mz), Point3(mx, My, Mz)) + cn.addSolid(cp) + + +class ObjectManager(NodePath, DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('ObjectManager') + + def __init__(self): + NodePath.__init__(self) + self.assign(render.attachNewNode('objectManager')) + self.objectDict = {} + self.selectedObject = None + self.movingObject = 0 + self.deselectEvent = None + self.startPose = render.attachNewNode('startPose') + self.dragPointNP = self.attachNewNode('dragPoint') + self.gridSnapNP = self.dragPointNP.attachNewNode('gridSnap') + self.collisionOffsetNP = self.gridSnapNP.attachNewNode('collisionResponse') + self.iRay = SelectionRay() + self.iSegment = SelectionSegment(numSegments=6) + self.iSegment4 = SelectionSegment(numSegments=4) + self.iSphere = SelectionSphere() + self.houseExtents = None + self.doorBlocker = None + cp = CollisionPolygon(Point3(-100, -100, 0), Point3(100, -100, 0), Point3(100, 100, 0), Point3(-100, 100, 0)) + cn = CollisionNode('dragCollisionNode') + cn.addSolid(cp) + cn.setIntoCollideMask(ToontownGlobals.FurnitureDragBitmask) + self.collisionNP = NodePath(cn) + self.lnp = LineNodePath() + self.fRecenter = 0 + self.gridSpacing = None + self.firstTime = 0 + guiModels = loader.loadModel('phase_5.5/models/gui/house_design_gui') + self.createSelectedObjectPanel(guiModels) + self.createMainControls(guiModels) + self.furnitureManager = None + self.atticPicker = None + self.inRoomPicker = None + self.inTrashPicker = None + self.dialog = None + self.deleteMode = 0 + self.nonDeletableItem = None + self.verifyFrame = None + self.deleteItemText = None + self.okButton = None + self.cancelButton = None + self.itemIval = None + self.itemPanel = None + self.guiInterval = None + self.accept('enterFurnitureMode', self.enterFurnitureMode) + self.accept('exitFurnitureMode', self.exitFurnitureMode) + return + + def enterFurnitureMode(self, furnitureManager, fDirector): + if not fDirector: + if self.furnitureManager: + self.exitFurnitureMode(self.furnitureManager) + return + if furnitureManager == self.furnitureManager: + return + if self.furnitureManager != None: + self.exitFurnitureMode(self.furnitureManager) + self.notify.info('enterFurnitureMode, fDirector = %s' % fDirector) + self.furnitureManager = furnitureManager + self.furnitureManager.d_avatarEnter() + house = furnitureManager.getInteriorObject() + house.hideExteriorWindows() + self.setTargetNodePath(house.interior) + self.createAtticPicker() + self.initializeDistributedFurnitureItems(furnitureManager.dfitems) + self.setCamPosIndex(DEFAULT_CAM_INDEX) + base.localAvatar.setGhostMode(1) + taskMgr.remove('editModeTransition') + self.orientCamH(base.localAvatar.getH(self.targetNodePath)) + self.accept('mouse1', self.moveObjectStart) + self.accept('mouse1-up', self.moveObjectStop) + self.furnitureGui.show() + self.deleteMode = 0 + self.__updateDeleteButtons() + self.showAtticPicker() + base.localAvatar.laffMeter.stop() + base.setCellsAvailable(base.leftCells + [base.bottomCells[0]], 0) + if self.guiInterval: + self.guiInterval.finish() + self.guiInterval = self.furnitureGui.posHprScaleInterval(1.0, Point3(-1.16, 1, -0.03), Vec3(0), Vec3(0.06), startPos=Point3(-1.19, 1, 0.33), startHpr=Vec3(0), startScale=Vec3(0.04), blendType='easeInOut', name='lerpFurnitureButton') + self.guiInterval.start() + taskMgr.add(self.recenterButtonFrameTask, 'recenterButtonFrameTask', 10) + messenger.send('wakeup') + return + + def exitFurnitureMode(self, furnitureManager): + if furnitureManager != self.furnitureManager: + return + self.notify.info('exitFurnitureMode') + house = furnitureManager.getInteriorObject() + if house: + house.showExteriorWindows() + self.furnitureManager.d_avatarExit() + self.furnitureManager = None + base.localAvatar.setCameraPositionByIndex(0) + self.exitDeleteMode() + self.houseExtents.detachNode() + self.doorBlocker.detachNode() + self.deselectObject() + self.ignore('mouse1') + self.ignore('mouse1-up') + if self.atticPicker: + self.atticPicker.destroy() + self.atticPicker = None + if self.inRoomPicker: + self.inRoomPicker.destroy() + self.inRoomPicker = None + if self.inTrashPicker: + self.inTrashPicker.destroy() + self.inTrashPicker = None + self.__cleanupVerifyDelete() + self.furnitureGui.hide() + base.setCellsAvailable(base.leftCells + [base.bottomCells[0]], 1) + base.localAvatar.laffMeter.start() + taskMgr.remove('recenterButtonFrameTask') + self.cleanupDialog() + taskMgr.remove('showHelpTextDoLater') + messenger.send('wakeup') + return + + def initializeDistributedFurnitureItems(self, dfitems): + self.objectDict = {} + for item in dfitems: + mo = MovableObject(item, parent=self.targetNodePath) + self.objectDict[mo.id()] = mo + + def setCamPosIndex(self, index): + self.camPosIndex = index + base.localAvatar.setCameraSettings(camPosList[index]) + + def zoomCamIn(self): + self.setCamPosIndex(max(0, self.camPosIndex - 1)) + messenger.send('wakeup') + + def zoomCamOut(self): + self.setCamPosIndex(min(len(camPosList) - 1, self.camPosIndex + 1)) + messenger.send('wakeup') + + def rotateCamCW(self): + self.orientCamH(base.localAvatar.getH(self.targetNodePath) - 90) + messenger.send('wakeup') + + def rotateCamCCW(self): + self.orientCamH(base.localAvatar.getH(self.targetNodePath) + 90) + messenger.send('wakeup') + + def orientCamH(self, toonH): + targetH = ROUND_TO(toonH, 90) + base.localAvatar.hprInterval(duration=1, hpr=Vec3(targetH, 0, 0), other=self.targetNodePath, blendType='easeInOut', name='editModeTransition').start() + + def setTargetNodePath(self, nodePath): + self.targetNodePath = nodePath + if self.houseExtents: + self.houseExtents.removeNode() + if self.doorBlocker: + self.doorBlocker.removeNode() + self.makeHouseExtentsBox() + self.makeDoorBlocker() + self.collisionNP.reparentTo(self.targetNodePath) + + def loadObject(self, filename): + mo = MovableObject(filename, parent=self.targetNodePath) + self.objectDict[mo.id()] = mo + self.selectObject(mo) + return mo + + def pickObject(self): + self.iRay.setParentNP(base.cam) + entry = self.iRay.pickGeom(targetNodePath=self.targetNodePath, skipFlags=SKIP_ALL) + if entry: + nodePath = entry.getIntoNodePath() + if self.isMovableObject(nodePath): + self.selectObject(self.findObject(nodePath)) + return + self.deselectObject() + + def pickInRoom(self, objectId): + self.selectObject(self.objectDict.get(objectId)) + + def selectObject(self, selectedObject): + messenger.send('wakeup') + if self.selectedObject: + self.deselectObject() + if selectedObject: + self.selectedObject = selectedObject + self.deselectEvent = self.selectedObject.dfitem.uniqueName('disable') + self.acceptOnce(self.deselectEvent, self.deselectObject) + self.lnp.reset() + self.lnp.reparentTo(selectedObject) + self.lnp.moveTo(selectedObject.c0) + self.lnp.drawTo(selectedObject.c1) + self.lnp.drawTo(selectedObject.c2) + self.lnp.drawTo(selectedObject.c3) + self.lnp.drawTo(selectedObject.c0) + self.lnp.create() + self.buttonFrame.show() + self.enableButtonFrameTask() + self.sendToAtticButton.show() + self.atticRoof.hide() + + def deselectObject(self): + self.moveObjectStop() + if self.deselectEvent: + self.ignore(self.deselectEvent) + self.deselectEvent = None + self.selectedObject = None + self.lnp.detachNode() + self.buttonFrame.hide() + self.disableButtonFrameTask() + self.sendToAtticButton.hide() + self.atticRoof.show() + return + + def isMovableObject(self, nodePath): + return nodePath.hasNetTag('movableObject') + + def findObject(self, nodePath): + np = nodePath.findNetTag('movableObject') + if np.isEmpty(): + return None + else: + return self.objectDict.get(np.id(), None) + return None + + def moveObjectStop(self, *args): + if self.movingObject: + self.movingObject = 0 + taskMgr.remove('moveObjectTask') + if self.selectedObject: + self.selectedObject.wrtReparentTo(self.targetNodePath) + self.selectedObject.collisionNodePath.unstash() + self.selectedObject.dfitem.stopAdjustPosHpr() + for object in self.objectDict.values(): + object.unstashBuiltInCollisionNodes() + + self.centerMarker['image'] = [self.grabUp, self.grabDown, self.grabRollover] + self.centerMarker.configure(text=['', TTLocalizer.HDMoveLabel], text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=0.3) + + def moveObjectStart(self): + self.moveObjectStop() + self.pickObject() + self.moveObjectContinue() + + def moveObjectContinue(self, *args): + messenger.send('wakeup') + if self.selectedObject: + for object in self.objectDict.values(): + object.stashBuiltInCollisionNodes() + + self.selectedObject.collisionNodePath.stash() + self.selectedObject.dfitem.startAdjustPosHpr() + self.firstTime = 1 + self.iPosHpr() + self.startPoseValid = 0 + self.centerMarker['image'] = self.grabDown + self.centerMarker.configure(text=TTLocalizer.HDMoveLabel, text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=0.3) + taskMgr.add(self.moveObjectTask, 'moveObjectTask') + self.movingObject = 1 + + def setLnpColor(self, r, g, b): + for i in range(5): + self.lnp.lineSegs.setVertexColor(i, r, g, b) + + def markNewPosition(self, isValid): + if not isValid: + if self.startPoseValid: + self.collisionOffsetNP.setPosHpr(self.startPose, self.selectedObject.dragPoint, Vec3(0)) + else: + self.startPoseValid = 1 + + def moveObjectTask(self, state): + so = self.selectedObject + target = self.targetNodePath + self.startPose.iPosHpr(so) + self.iRay.setParentNP(base.cam) + entry = self.iRay.pickBitMask(bitMask=ToontownGlobals.FurnitureDragBitmask, targetNodePath=target, skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + return Task.cont + self.setPos(base.cam, entry.getSurfacePoint(base.cam)) + if self.firstTime: + self.moveObjectInit() + self.firstTime = 0 + else: + self.gridSnapNP.iPos() + self.collisionOffsetNP.iPosHpr() + if self.gridSpacing: + pos = self.dragPointNP.getPos(target) + self.gridSnapNP.setPos(target, ROUND_TO(pos[0], self.gridSpacing), ROUND_TO(pos[1], self.gridSpacing), pos[2]) + self.iRay.setParentNP(base.cam) + entry = self.iRay.pickBitMask3D(bitMask=so.getWallBitmask(), targetNodePath=target, dir=Vec3(self.getNearProjectionPoint(self.gridSnapNP)), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + fWall = 0 + if not so.getOnTable(): + while entry: + intoMask = entry.getIntoNodePath().node().getIntoCollideMask() + fClosest = (intoMask & ToontownGlobals.WallBitmask).isZero() + if self.alignObject(entry, target, fClosest=fClosest): + fWall = 1 + break + entry = self.iRay.findNextCollisionEntry(skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + + if so.getOnWall(): + self.markNewPosition(fWall) + return Task.cont + self.iRay.setParentNP(target) + entry = self.iRay.pickBitMask3D(bitMask=so.getFloorBitmask(), targetNodePath=target, origin=Point3(self.gridSnapNP.getPos(target) + Vec3(0, 0, 10)), dir=Vec3(0, 0, -1), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + self.markNewPosition(0) + return Task.cont + nodePath = entry.getIntoNodePath() + if self.isMovableObject(nodePath): + self.gridSnapNP.setPos(target, Point3(entry.getSurfacePoint(target))) + else: + self.gridSnapNP.setPos(target, Point3(entry.getSurfacePoint(target) + Vec3(0, 0, ToontownGlobals.FloorOffset))) + if not fWall: + self.iSphere.setParentNP(self.gridSnapNP) + self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) + entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=target, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + if entry: + self.alignObject(entry, target, fClosest=1) + isValid = self.collisionTest() + self.markNewPosition(isValid) + return Task.cont + + def collisionTest(self): + so = self.selectedObject + target = self.targetNodePath + entry = self.segmentCollision() + if not entry: + return 1 + offsetDict = {} + while entry: + offset = self.computeSegmentOffset(entry) + if offset: + eid = entry.getInto() + maxOffsetVec = offsetDict.get(eid, Vec3(0)) + if offset.length() > maxOffsetVec.length(): + maxOffsetVec.assign(offset) + offsetDict[eid] = maxOffsetVec + entry = self.iSegment.findNextCollisionEntry(skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + + if offsetDict: + keys = offsetDict.keys() + ortho1 = offsetDict[keys[0]] + ortho2 = Vec3(0) + v1 = Vec3(ortho1) + v1.normalize() + for key in keys[1:]: + offset = offsetDict[key] + v2 = Vec3(offset) + v2.normalize() + dp = v1.dot(v2) + if abs(dp) > 0.95: + if offset.length() > ortho1.length(): + ortho1.assign(offset) + elif abs(dp) < 0.05: + if offset.length() > ortho2.length(): + ortho2.assign(offset) + else: + o1Len = ortho1.length() + parallelVec = Vec3(ortho1 * offset.dot(ortho1) / (o1Len * o1Len)) + perpVec = Vec3(offset - parallelVec) + if parallelVec.length() > o1Len: + ortho1.assign(parallelVec) + if perpVec.length() > ortho2.length(): + ortho2.assign(perpVec) + + totalOffset = ortho1 + ortho2 + self.collisionOffsetNP.setPos(self.collisionOffsetNP, totalOffset) + if not self.segmentCollision(): + return 1 + m = self.startPose.getMat(so) + deltaMove = Vec3(m.getRow3(3)) + if deltaMove.length() == 0: + return 1 + self.iSegment4.setParentNP(so) + entry = self.iSegment4.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=target, endPointList=[(so.c0, Point3(m.xformPoint(so.c0))), + (so.c1, Point3(m.xformPoint(so.c1))), + (so.c2, Point3(m.xformPoint(so.c2))), + (so.c3, Point3(m.xformPoint(so.c3)))], skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + maxLen = 0 + maxOffset = None + while entry: + offset = Vec3(entry.getSurfacePoint(entry.getFromNodePath()) - entry.getFrom().getPointA()) + offsetLen = Vec3(offset).length() + if offsetLen > maxLen: + maxLen = offsetLen + maxOffset = offset + entry = self.iSegment4.findNextCollisionEntry(skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + + if maxOffset: + self.collisionOffsetNP.setPos(self.collisionOffsetNP, maxOffset) + if not self.segmentCollision(): + return 1 + return 0 + + def segmentCollision(self): + so = self.selectedObject + self.iSegment.setParentNP(so) + entry = self.iSegment.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, endPointList=[(so.c0, so.c1), + (so.c1, so.c2), + (so.c2, so.c3), + (so.c3, so.c0), + (so.c0, so.c2), + (so.c1, so.c3)], skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + return entry + + def computeSegmentOffset(self, entry): + fromNodePath = entry.getFromNodePath() + if entry.hasSurfaceNormal(): + normal = entry.getSurfaceNormal(fromNodePath) + else: + return None + hitPoint = entry.getSurfacePoint(fromNodePath) + m = self.selectedObject.getMat(self.startPose) + hp = Point3(m.xformPoint(hitPoint)) + hpn = Vec3(m.xformVec(normal)) + hitPointVec = Vec3(hp - self.selectedObject.dragPoint) + if hitPointVec.dot(hpn) > 0: + return None + nLen = normal.length() + offsetVecA = hitPoint - entry.getFrom().getPointA() + offsetA = normal * offsetVecA.dot(normal) / (nLen * nLen) + if offsetA.dot(normal) > 0: + return offsetA * 1.01 + else: + offsetVecB = hitPoint - entry.getFrom().getPointB() + offsetB = normal * offsetVecB.dot(normal) / (nLen * nLen) + return offsetB * 1.01 + return None + + def alignObject(self, entry, target, fClosest = 0, wallOffset = None): + if not entry.hasSurfaceNormal(): + return 0 + normal = entry.getSurfaceNormal(target) + if abs(normal.dot(Vec3(0, 0, 1))) < 0.1: + tempNP = target.attachNewNode('temp') + normal.setZ(0) + normal.normalize() + lookAtNormal = Point3(normal) + lookAtNormal *= -1 + tempNP.lookAt(lookAtNormal) + realAngle = ROUND_TO(self.gridSnapNP.getH(tempNP), 90.0) + if fClosest: + angle = realAngle + else: + angle = 0 + self.gridSnapNP.setHpr(tempNP, angle, 0, 0) + hitPoint = entry.getSurfacePoint(target) + tempNP.setPos(hitPoint) + if wallOffset == None: + wallOffset = self.selectedObject.getWallOffset() + self.gridSnapNP.setPos(tempNP, 0, -wallOffset, 0) + tempNP.removeNode() + if realAngle == 180.0: + self.gridSnapNP.setH(self.gridSnapNP.getH() + 180.0) + return 1 + return 0 + + def rotateLeft(self): + if not self.selectedObject: + return + so = self.selectedObject + so.dfitem.startAdjustPosHpr() + self.iPosHpr(so) + self.moveObjectInit() + if so.getOnWall(): + startR = self.gridSnapNP.getR() + newR = ROUND_TO(startR + 22.5, 22.5) + self.gridSnapNP.setR(newR) + else: + startH = self.gridSnapNP.getH(self.targetNodePath) + newH = ROUND_TO(startH - 22.5, 22.5) + self.iSphere.setParentNP(self.gridSnapNP) + self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) + entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + self.gridSnapNP.setHpr(self.targetNodePath, newH, 0, 0) + self.collisionTest() + so.wrtReparentTo(self.targetNodePath) + self.disableButtonFrameTask() + so.dfitem.stopAdjustPosHpr() + + def rotateRight(self): + if not self.selectedObject: + return + so = self.selectedObject + so.dfitem.startAdjustPosHpr() + self.iPosHpr(so) + self.moveObjectInit() + if so.getOnWall(): + startR = self.gridSnapNP.getR() + newR = ROUND_TO(startR - 22.5, 22.5) + self.gridSnapNP.setR(newR) + else: + startH = self.gridSnapNP.getH(self.targetNodePath) + newH = ROUND_TO(startH + 22.5, 22.5) % 360.0 + self.iSphere.setParentNP(self.gridSnapNP) + self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) + entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) + if not entry: + self.gridSnapNP.setHpr(self.targetNodePath, newH, 0, 0) + self.collisionTest() + so.wrtReparentTo(self.targetNodePath) + self.disableButtonFrameTask() + so.dfitem.stopAdjustPosHpr() + + def moveObjectInit(self): + self.dragPointNP.setPosHpr(self.selectedObject, self.selectedObject.dragPoint, Vec3(0)) + self.gridSnapNP.iPosHpr() + self.collisionOffsetNP.iPosHpr() + self.selectedObject.wrtReparentTo(self.collisionOffsetNP) + + def resetFurniture(self): + for o in self.objectDict.values(): + o.resetMovableObject() + + self.objectDict = {} + self.deselectObject() + self.buttonFrame.hide() + + def destroy(self): + self.ignore('enterFurnitureMode') + self.ignore('exitFurnitureMode') + if self.guiInterval: + self.guiInterval.finish() + if self.furnitureManager: + self.exitFurnitureMode(self.furnitureManager) + self.cleanupDialog() + self.resetFurniture() + self.buttonFrame.destroy() + self.furnitureGui.destroy() + if self.houseExtents: + self.houseExtents.removeNode() + if self.doorBlocker: + self.doorBlocker.removeNode() + self.removeNode() + if self.verifyFrame: + self.verifyFrame.destroy() + self.verifyFrame = None + self.deleteItemText = None + self.okButton = None + self.cancelButton = None + return + + def createSelectedObjectPanel(self, guiModels): + self.buttonFrame = DirectFrame(scale=0.5) + self.grabUp = guiModels.find('**/handup') + self.grabDown = guiModels.find('**/handdown') + self.grabRollover = guiModels.find('**/handrollover') + self.centerMarker = DirectButton(parent=self.buttonFrame, text=['', TTLocalizer.HDMoveLabel], text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=[self.grabUp, self.grabDown, self.grabRollover], image_scale=0.3, relief=None, scale=0.12) + self.centerMarker.bind(DGG.B1PRESS, self.moveObjectContinue) + self.centerMarker.bind(DGG.B1RELEASE, self.moveObjectStop) + guiCCWArrowUp = guiModels.find('**/LarrowUp') + guiCCWArrowDown = guiModels.find('**/LarrowDown') + guiCCWArrowRollover = guiModels.find('**/LarrowRollover') + self.rotateLeftButton = DirectButton(parent=self.buttonFrame, relief=None, image=(guiCCWArrowUp, + guiCCWArrowDown, + guiCCWArrowRollover, + guiCCWArrowUp), image_pos=(0, 0, 0.1), image_scale=0.15, image3_color=Vec4(0.5, 0.5, 0.5, 0.75), text=('', + TTLocalizer.HDRotateCCWLabel, + TTLocalizer.HDRotateCCWLabel, + ''), text_pos=(0.135, -0.1), text_scale=0.1, text_align=TextNode.ARight, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(-.125, 0, -.2), scale=0.7, command=self.rotateLeft) + self.rotateLeftButton.bind(DGG.EXIT, self.enableButtonFrameTask) + guiCWArrowUp = guiModels.find('**/RarrowUp') + guiCWArrowDown = guiModels.find('**/RarrowDown') + guiCWArrowRollover = guiModels.find('**/RarrowRollover') + self.rotateRightButton = DirectButton(parent=self.buttonFrame, relief=None, image=(guiCWArrowUp, + guiCWArrowDown, + guiCWArrowRollover, + guiCWArrowUp), image_pos=(0, 0, 0.1), image_scale=0.15, image3_color=Vec4(0.5, 0.5, 0.5, 0.75), text=('', + TTLocalizer.HDRotateCWLabel, + TTLocalizer.HDRotateCWLabel, + ''), text_pos=(-0.135, -0.1), text_scale=0.1, text_align=TextNode.ALeft, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(0.125, 0, -0.2), scale=0.7, command=self.rotateRight) + self.rotateRightButton.bind(DGG.EXIT, self.enableButtonFrameTask) + self.buttonFrame.hide() + return + + def recenterButtonFrameTask(self, state): + if self.selectedObject and self.fRecenter: + self.buttonFrame.setPos(self.getSelectedObjectScreenXY()) + return Task.cont + + def disableButtonFrameTask(self, event = None): + self.fRecenter = 0 + + def enableButtonFrameTask(self, event = None): + self.fRecenter = 1 + + def getNearProjectionPoint(self, nodePath): + origin = nodePath.getPos(camera) + if origin[1] != 0.0: + return origin * (base.camLens.getNear() / origin[1]) + else: + return Point3(0, base.camLens.getNear(), 0) + + def getSelectedObjectScreenXY(self): + tNodePath = self.selectedObject.attachNewNode('temp') + tNodePath.setPos(self.selectedObject.center) + nearVec = self.getNearProjectionPoint(tNodePath) + nearVec *= base.camLens.getFocalLength() / base.camLens.getNear() + render2dX = CLAMP(nearVec[0] / (base.camLens.getFilmSize()[0] / 2.0), -.9, 0.9) + aspect2dX = render2dX * base.getAspectRatio() + aspect2dZ = CLAMP(nearVec[2] / (base.camLens.getFilmSize()[1] / 2.0), -.8, 0.9) + tNodePath.removeNode() + return Vec3(aspect2dX, 0, aspect2dZ) + + def createMainControls(self, guiModels): + attic = guiModels.find('**/attic') + self.furnitureGui = DirectFrame(relief=None, pos=(-1.19, 1, 0.33), scale=0.04, image=attic) + bMoveStopUp = guiModels.find('**/bu_atticX/bu_attic_up') + bMoveStopDown = guiModels.find('**/bu_atticX/bu_attic_down') + bMoveStopRollover = guiModels.find('**/bu_atticX/bu_attic_rollover') + self.bStopMoveFurniture = DirectButton(parent=self.furnitureGui, relief=None, image=[bMoveStopUp, + bMoveStopDown, + bMoveStopRollover, + bMoveStopUp], text=['', TTLocalizer.HDStopMoveFurnitureButton, TTLocalizer.HDStopMoveFurnitureButton], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), pos=(-0.3, 0, 9.4), command=base.localAvatar.stopMoveFurniture) + self.bindHelpText(self.bStopMoveFurniture, 'DoneMoving') + self.atticRoof = DirectLabel(parent=self.furnitureGui, relief=None, image=guiModels.find('**/rooftile')) + self.itemBackgroundFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/item_backgroun'), image_pos=(0, 0, -22), image_scale=(1, 1, 5)) + self.scrollUpFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/scrollup'), pos=(0, 0, -0.58)) + self.camButtonFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/low'), pos=(0, 0, -11.69)) + tagUp = guiModels.find('**/tag_up') + tagDown = guiModels.find('**/tag_down') + tagRollover = guiModels.find('**/tag_rollover') + self.inAtticButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInAtticLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, 4), scale=0.8, command=self.showAtticPicker) + self.bindHelpText(self.inAtticButton, 'Attic') + self.inRoomButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInRoomLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, 1.1), scale=0.8, command=self.showInRoomPicker) + self.bindHelpText(self.inRoomButton, 'Room') + self.inTrashButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInTrashLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, -1.8), scale=0.8, command=self.showInTrashPicker) + self.bindHelpText(self.inTrashButton, 'Trash') + for i in range(4): + self.inAtticButton.component('text%d' % i).setR(-90) + self.inRoomButton.component('text%d' % i).setR(-90) + self.inTrashButton.component('text%d' % i).setR(-90) + + backInAtticUp = guiModels.find('**/bu_backinattic_up1') + backInAtticDown = guiModels.find('**/bu_backinattic_down1') + backInAtticRollover = guiModels.find('**/bu_backinattic_rollover2') + self.sendToAtticButton = DirectButton(parent=self.furnitureGui, relief=None, pos=(0.4, 0, 12.8), text=['', TTLocalizer.HDToAtticLabel], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_pos=(1.2, -0.3), image=[backInAtticUp, backInAtticDown, backInAtticRollover], command=self.sendItemToAttic) + self.sendToAtticButton.hide() + self.bindHelpText(self.sendToAtticButton, 'SendToAttic') + zoomInUp = guiModels.find('**/bu_RzoomOut_up') + zoomInDown = guiModels.find('**/bu_RzoomOut_down') + zoomInRollover = guiModels.find('**/bu_RzoomOut_rollover') + self.zoomInButton = DirectButton(parent=self.camButtonFrame, image=[zoomInUp, zoomInDown, zoomInRollover], relief=None, pos=(0.9, 0, -0.75), command=self.zoomCamIn) + self.bindHelpText(self.zoomInButton, 'ZoomIn') + zoomOutUp = guiModels.find('**/bu_LzoomIn_up') + zoomOutDown = guiModels.find('**/bu_LzoomIn_down') + zoomOutRollover = guiModels.find('**/buLzoomIn_rollover') + self.zoomOutButton = DirectButton(parent=self.camButtonFrame, image=[zoomOutUp, zoomOutDown, zoomOutRollover], relief=None, pos=(-1.4, 0, -0.75), command=self.zoomCamOut) + self.bindHelpText(self.zoomOutButton, 'ZoomOut') + camCCWUp = guiModels.find('**/bu_Rarrow_up1') + camCCWDown = guiModels.find('**/bu_Rarrow_down1') + camCCWRollover = guiModels.find('**/bu_Rarrow_orllover') + self.rotateCamLeftButton = DirectButton(parent=self.camButtonFrame, image=[camCCWUp, camCCWDown, camCCWRollover], relief=None, pos=(0.9, 0, -3.0), command=self.rotateCamCCW) + self.bindHelpText(self.rotateCamLeftButton, 'RotateLeft') + camCWUp = guiModels.find('**/bu_Larrow_up1') + camCWDown = guiModels.find('**/bu_Larrow_down1') + camCWRollover = guiModels.find('**/bu_Larrow_rollover2') + self.rotateCamRightButton = DirectButton(parent=self.camButtonFrame, image=[camCWUp, camCWDown, camCWRollover], relief=None, pos=(-1.4, 0, -3.0), command=self.rotateCamCW) + self.bindHelpText(self.rotateCamRightButton, 'RotateRight') + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + trashcanUp = trashcanGui.find('**/TrashCan_CLSD') + trashcanDown = trashcanGui.find('**/TrashCan_OPEN') + trashcanRollover = trashcanGui.find('**/TrashCan_RLVR') + self.deleteEnterButton = DirectButton(parent=self.furnitureGui, image=(trashcanUp, + trashcanDown, + trashcanRollover, + trashcanUp), text=['', + TTLocalizer.InventoryDelete, + TTLocalizer.InventoryDelete, + ''], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_align=TextNode.ACenter, text_pos=(0, -0.12), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(3.7, 0.0, -13.8), scale=7.13, command=self.enterDeleteMode) + self.bindHelpText(self.deleteEnterButton, 'DeleteEnter') + self.deleteExitButton = DirectButton(parent=self.furnitureGui, image=(trashcanUp, + trashcanDown, + trashcanRollover, + trashcanUp), text=('', + TTLocalizer.InventoryDone, + TTLocalizer.InventoryDone, + ''), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_align=TextNode.ACenter, text_pos=(0, -0.12), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(3.7, 0.0, -13.8), scale=7.13, command=self.exitDeleteMode) + self.bindHelpText(self.deleteExitButton, 'DeleteExit') + self.deleteExitButton.hide() + self.trashcanBase = DirectLabel(parent=self.furnitureGui, image=guiModels.find('**/trashcan_base'), relief=None, pos=(0, 0, -11.64)) + self.furnitureGui.hide() + self.helpText = DirectLabel(parent=self.furnitureGui, relief=DGG.SUNKEN, frameSize=(-0.5, + 10, + -3, + 0.9), frameColor=(0.2, 0.2, 0.2, 0.5), borderWidth=(0.01, 0.01), text='', text_wordwrap=12, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.8, pos=(3, 0.0, -7), scale=1, text_align=TextNode.ALeft) + self.helpText.hide() + return + + def createAtticPicker(self): + self.atticItemPanels = [] + for itemIndex in range(len(self.furnitureManager.atticItems)): + panel = FurnitureItemPanel(self.furnitureManager.atticItems[itemIndex], itemIndex, command=self.bringItemFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticItemPanels.append(panel) + + self.atticWallpaperPanels = [] + for itemIndex in range(len(self.furnitureManager.atticWallpaper)): + panel = FurnitureItemPanel(self.furnitureManager.atticWallpaper[itemIndex], itemIndex, command=self.bringWallpaperFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWallpaperPanels.append(panel) + + self.atticWindowPanels = [] + for itemIndex in range(len(self.furnitureManager.atticWindows)): + panel = FurnitureItemPanel(self.furnitureManager.atticWindows[itemIndex], itemIndex, command=self.bringWindowFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWindowPanels.append(panel) + + self.regenerateAtticPicker() + + def regenerateAtticPicker(self): + selectedIndex = 0 + if self.atticPicker: + selectedIndex = self.atticPicker.getSelectedIndex() + for panel in self.atticItemPanels: + panel.detachNode() + + for panel in self.atticWallpaperPanels: + panel.detachNode() + + for panel in self.atticWindowPanels: + panel.detachNode() + + self.atticPicker.destroy() + self.atticPicker = None + itemList = self.atticItemPanels + self.atticWallpaperPanels + self.atticWindowPanels + if self.deleteMode: + text = TTLocalizer.HDDeletePickerLabel + else: + text = TTLocalizer.HDAtticPickerLabel + self.atticPicker = self.createScrolledList(itemList, text, 'atticPicker', selectedIndex) + if self.inRoomPicker or self.inTrashPicker: + self.atticPicker.hide() + else: + self.atticPicker.show() + return + + def createInRoomPicker(self): + self.inRoomPanels = [] + for objectId, object in self.objectDict.items(): + panel = FurnitureItemPanel(object.dfitem.item, objectId, command=self.requestReturnToAttic, deleteMode=self.deleteMode, withinFunc=self.pickInRoom, helpCategory='FurnitureItemPanelRoom') + self.inRoomPanels.append(panel) + + self.regenerateInRoomPicker() + + def regenerateInRoomPicker(self): + selectedIndex = 0 + if self.inRoomPicker: + selectedIndex = self.inRoomPicker.getSelectedIndex() + for panel in self.inRoomPanels: + panel.detachNode() + + self.inRoomPicker.destroy() + self.inRoomPicker = None + if self.deleteMode: + text = TTLocalizer.HDDeletePickerLabel + else: + text = TTLocalizer.HDInRoomPickerLabel + self.inRoomPicker = self.createScrolledList(self.inRoomPanels, text, 'inRoomPicker', selectedIndex) + return + + def createInTrashPicker(self): + self.inTrashPanels = [] + for itemIndex in range(len(self.furnitureManager.deletedItems)): + panel = FurnitureItemPanel(self.furnitureManager.deletedItems[itemIndex], itemIndex, command=self.requestReturnToAtticFromTrash, helpCategory='FurnitureItemPanelTrash') + self.inTrashPanels.append(panel) + + self.regenerateInTrashPicker() + + def regenerateInTrashPicker(self): + selectedIndex = 0 + if self.inTrashPicker: + selectedIndex = self.inTrashPicker.getSelectedIndex() + for panel in self.inTrashPanels: + panel.detachNode() + + self.inTrashPicker.destroy() + self.inTrashPicker = None + text = TTLocalizer.HDInTrashPickerLabel + self.inTrashPicker = self.createScrolledList(self.inTrashPanels, text, 'inTrashPicker', selectedIndex) + return + + def createScrolledList(self, itemList, text, name, selectedIndex): + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + picker = DirectScrolledList(parent=self.furnitureGui, pos=(-0.38, 0.0, 3), scale=7.125, relief=None, items=itemList, numItemsVisible=5, text=text, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, 0.4), 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.5, 1.5, 1.5), decButton_pos=(0, 0, 0.3), decButton_image3_color=Vec4(1, 1, 1, 0.1), 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.5, 1.5, -1.5), incButton_pos=(0, 0, -1.878), incButton_image3_color=Vec4(1, 1, 1, 0.1)) + picker.setName(name) + picker.scrollTo(selectedIndex) + return picker + + def reset(): + self.destroy() + furnitureMenu.destroy() + + def showAtticPicker(self): + if self.inRoomPicker: + self.inRoomPicker.destroy() + self.inRoomPicker = None + if self.inTrashPicker: + self.inTrashPicker.destroy() + self.inTrashPicker = None + self.atticPicker.show() + self.inAtticButton['image_color'] = Vec4(1, 1, 1, 1) + self.inRoomButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inTrashButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.deleteExitButton['state'] = 'normal' + self.deleteEnterButton['state'] = 'normal' + return + + def showInRoomPicker(self): + messenger.send('wakeup') + if not self.inRoomPicker: + self.createInRoomPicker() + self.atticPicker.hide() + if self.inTrashPicker: + self.inTrashPicker.destroy() + self.inTrashPicker = None + self.inAtticButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inRoomButton['image_color'] = Vec4(1, 1, 1, 1) + self.inTrashButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.deleteExitButton['state'] = 'normal' + self.deleteEnterButton['state'] = 'normal' + return + + def showInTrashPicker(self): + messenger.send('wakeup') + if not self.inTrashPicker: + self.createInTrashPicker() + self.atticPicker.hide() + if self.inRoomPicker: + self.inRoomPicker.destroy() + self.inRoomPicker = None + self.inAtticButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inRoomButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + self.inTrashButton['image_color'] = Vec4(1, 1, 1, 1) + self.deleteExitButton['state'] = 'disabled' + self.deleteEnterButton['state'] = 'disabled' + return + + def sendItemToAttic(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Send Item to Attic') + messenger.send('wakeup') + if self.selectedObject: + callback = PythonUtil.Functor(self.__sendItemToAtticCallback, self.selectedObject.id()) + self.furnitureManager.moveItemToAttic(self.selectedObject.dfitem, callback) + self.deselectObject() + + def __sendItemToAtticCallback(self, objectId, retcode, item): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to send item %s to attic, reason %s.' % (item.getName(), retcode)) + return + del self.objectDict[objectId] + if self.selectedObject != None and self.selectedObject.id() == objectId: + self.selectedObject.detachNode() + self.deselectObject() + itemIndex = len(self.atticItemPanels) + panel = FurnitureItemPanel(item, itemIndex, command=self.bringItemFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticItemPanels.append(panel) + self.regenerateAtticPicker() + if self.inRoomPicker: + for i in range(len(self.inRoomPanels)): + if self.inRoomPanels[i].itemId == objectId: + del self.inRoomPanels[i] + self.regenerateInRoomPicker() + return + + return + + def cleanupDialog(self, buttonValue = None): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + self.__enableItemButtons(1) + return + + def enterDeleteMode(self): + self.deleteMode = 1 + self.__updateDeleteMode() + + def exitDeleteMode(self): + self.deleteMode = 0 + self.__updateDeleteMode() + + def __updateDeleteMode(self): + if not self.atticPicker: + return + self.notify.debug('__updateDeleteMode deleteMode=%s' % self.deleteMode) + if self.deleteMode: + framePanelColor = DeletePickerPanelColor + atticText = TTLocalizer.HDDeletePickerLabel + inRoomText = TTLocalizer.HDDeletePickerLabel + helpCategory = 'FurnitureItemPanelDelete' + else: + framePanelColor = NormalPickerPanelColor + atticText = TTLocalizer.HDAtticPickerLabel + inRoomText = TTLocalizer.HDInRoomPickerLabel + helpCategory = None + if self.inRoomPicker: + self.inRoomPicker['text'] = inRoomText + for panel in self.inRoomPicker['items']: + panel.setDeleteMode(self.deleteMode) + panel.bindHelpText(helpCategory) + + if self.atticPicker: + self.atticPicker['text'] = atticText + for panel in self.atticPicker['items']: + panel.setDeleteMode(self.deleteMode) + panel.bindHelpText(helpCategory) + + self.__updateDeleteButtons() + return + + def __updateDeleteButtons(self): + if self.deleteMode: + self.deleteExitButton.show() + self.deleteEnterButton.hide() + else: + self.deleteEnterButton.show() + self.deleteExitButton.hide() + + def deleteItemFromRoom(self, dfitem, objectId, itemIndex): + messenger.send('wakeup') + callback = PythonUtil.Functor(self.__deleteItemFromRoomCallback, objectId, itemIndex) + self.furnitureManager.deleteItemFromRoom(dfitem, callback) + + def __deleteItemFromRoomCallback(self, objectId, itemIndex, retcode, item): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete item %s from room, reason %s.' % (item.getName(), retcode)) + return + del self.objectDict[objectId] + if self.selectedObject != None and self.selectedObject.id() == objectId: + self.selectedObject.detachNode() + self.deselectObject() + if self.inRoomPicker and itemIndex is not None: + del self.inRoomPanels[itemIndex] + self.regenerateInRoomPicker() + return + + def bringItemFromAttic(self, item, itemIndex): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Place Item in Room') + messenger.send('wakeup') + self.__enableItemButtons(0) + if self.deleteMode: + self.requestDelete(item, itemIndex, self.deleteItemFromAttic) + return + pos = self.targetNodePath.getRelativePoint(base.localAvatar, Point3(0, 2, 0)) + hpr = Point3(0, 0, 0) + if abs(pos[0]) > 3000 or abs(pos[1]) > 3000 or abs(pos[2]) > 300: + self.notify.warning('bringItemFromAttic extreme pos targetNodePath=%s avatar=%s %s' % (repr(self.targetNodePath.getPos(render)), repr(base.localAvatar.getPos(render)), repr(pos))) + if item.getFlags() & CatalogFurnitureItem.FLPainting: + for object in self.objectDict.values(): + object.stashBuiltInCollisionNodes() + + self.gridSnapNP.iPosHpr() + target = self.targetNodePath + self.iRay.setParentNP(base.localAvatar) + entry = self.iRay.pickBitMask3D(bitMask=ToontownGlobals.WallBitmask, targetNodePath=target, origin=Point3(0, 0, 6), dir=Vec3(0, 1, 0), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) + for object in self.objectDict.values(): + object.unstashBuiltInCollisionNodes() + + if entry: + self.alignObject(entry, target, fClosest=0, wallOffset=0.1) + pos = self.gridSnapNP.getPos(target) + hpr = self.gridSnapNP.getHpr(target) + else: + self.notify.warning('wall not found for painting') + self.furnitureManager.moveItemFromAttic(itemIndex, (pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]), self.__bringItemFromAtticCallback) + + def __bringItemFromAtticCallback(self, retcode, dfitem, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to bring furniture item %s into room, reason %s.' % (itemIndex, retcode)) + return + mo = self.loadObject(dfitem) + objectId = mo.id() + self.atticItemPanels[itemIndex].destroy() + del self.atticItemPanels[itemIndex] + for i in range(itemIndex, len(self.atticItemPanels)): + self.atticItemPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + if self.inRoomPicker: + panel = FurnitureItemPanel(dfitem.item, objectId, command=self.requestReturnToAttic, helpCategory='FurnitureItemPanelRoom') + self.inRoomPanels.append(panel) + self.regenerateInRoomPicker() + + def deleteItemFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.deleteItemFromAttic(item, itemIndex, self.__deleteItemFromAtticCallback) + + def __deleteItemFromAtticCallback(self, retcode, item, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete furniture item %s, reason %s.' % (itemIndex, retcode)) + return + self.atticItemPanels[itemIndex].destroy() + del self.atticItemPanels[itemIndex] + for i in range(itemIndex, len(self.atticItemPanels)): + self.atticItemPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def bringWallpaperFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.__enableItemButtons(0) + if self.deleteMode: + self.requestDelete(item, itemIndex, self.deleteWallpaperFromAttic) + return + if base.localAvatar.getY() < 2.3: + room = 0 + else: + room = 1 + self.furnitureManager.moveWallpaperFromAttic(itemIndex, room, self.__bringWallpaperFromAtticCallback) + + def __bringWallpaperFromAtticCallback(self, retcode, itemIndex, room): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to bring wallpaper %s into room %s, reason %s.' % (itemIndex, room, retcode)) + return + self.atticWallpaperPanels[itemIndex].destroy() + item = self.furnitureManager.atticWallpaper[itemIndex] + panel = FurnitureItemPanel(item, itemIndex, command=self.bringWallpaperFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWallpaperPanels[itemIndex] = panel + self.regenerateAtticPicker() + + def deleteWallpaperFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.deleteWallpaperFromAttic(item, itemIndex, self.__deleteWallpaperFromAtticCallback) + + def __deleteWallpaperFromAtticCallback(self, retcode, item, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete wallpaper %s, reason %s.' % (itemIndex, retcode)) + return + self.atticWallpaperPanels[itemIndex].destroy() + del self.atticWallpaperPanels[itemIndex] + for i in range(itemIndex, len(self.atticWallpaperPanels)): + self.atticWallpaperPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def bringWindowFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.__enableItemButtons(0) + if self.deleteMode: + self.requestDelete(item, itemIndex, self.deleteWindowFromAttic) + return + if base.localAvatar.getY() < 2.3: + slot = 2 + else: + slot = 4 + self.furnitureManager.moveWindowFromAttic(itemIndex, slot, self.__bringWindowFromAtticCallback) + + def __bringWindowFromAtticCallback(self, retcode, itemIndex, slot): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to bring window %s into slot %s, reason %s.' % (itemIndex, slot, retcode)) + return + if retcode == ToontownGlobals.FM_SwappedItem: + self.atticWindowPanels[itemIndex].destroy() + item = self.furnitureManager.atticWindows[itemIndex] + panel = FurnitureItemPanel(item, itemIndex, command=self.bringWindowFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + self.atticWindowPanels[itemIndex] = panel + else: + self.atticWindowPanels[itemIndex].destroy() + del self.atticWindowPanels[itemIndex] + for i in range(itemIndex, len(self.atticWindowPanels)): + self.atticWindowPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def deleteWindowFromAttic(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.deleteWindowFromAttic(item, itemIndex, self.__deleteWindowFromAtticCallback) + + def __deleteWindowFromAtticCallback(self, retcode, item, itemIndex): + self.__enableItemButtons(1) + if retcode < 0: + self.notify.info('Unable to delete window %s, reason %s.' % (itemIndex, retcode)) + return + self.atticWindowPanels[itemIndex].destroy() + del self.atticWindowPanels[itemIndex] + for i in range(itemIndex, len(self.atticWindowPanels)): + self.atticWindowPanels[i].itemId -= 1 + + self.regenerateAtticPicker() + + def setGridSpacingString(self, spacingStr): + spacing = eval(spacingStr) + self.setGridSpacing(spacing) + + def setGridSpacing(self, gridSpacing): + self.gridSpacing = gridSpacing + + def makeHouseExtentsBox(self): + houseGeom = self.targetNodePath.findAllMatches('**/group*') + targetBounds = houseGeom.getTightBounds() + self.houseExtents = self.targetNodePath.attachNewNode('furnitureCollisionNode') + mx = targetBounds[0][0] + Mx = targetBounds[1][0] + my = targetBounds[0][1] + My = targetBounds[1][1] + mz = targetBounds[0][2] + Mz = targetBounds[1][2] + cn = CollisionNode('extentsCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.GhostBitmask) + self.houseExtents.attachNewNode(cn) + cp = CollisionPolygon(Point3(mx, my, mz), Point3(mx, My, mz), Point3(mx, My, Mz), Point3(mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, My, mz), Point3(Mx, my, mz), Point3(Mx, my, Mz), Point3(Mx, My, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(Mx, my, mz), Point3(mx, my, mz), Point3(mx, my, Mz), Point3(Mx, my, Mz)) + cn.addSolid(cp) + cp = CollisionPolygon(Point3(mx, My, mz), Point3(Mx, My, mz), Point3(Mx, My, Mz), Point3(mx, My, Mz)) + cn.addSolid(cp) + + def makeDoorBlocker(self): + self.doorBlocker = self.targetNodePath.attachNewNode('doorBlocker') + cn = CollisionNode('doorBlockerCollisionNode') + cn.setIntoCollideMask(ToontownGlobals.FurnitureSideBitmask) + self.doorBlocker.attachNewNode(cn) + cs = CollisionSphere(Point3(-12, -33, 0), 7.5) + cn.addSolid(cs) + + def createVerifyDialog(self, item, verifyText, okFunc, cancelFunc): + if self.verifyFrame == 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.verifyFrame = DirectFrame(pos=(-0.4, 0.1, 0.3), scale=0.75, relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.2, 1, 1.3), text='', text_wordwrap=19, text_scale=0.06, text_pos=(0, 0.5), textMayChange=1, sortOrder=NO_FADE_SORT_INDEX) + self.okButton = DirectButton(parent=self.verifyFrame, image=okButtonImage, relief=None, text=OTPLocalizer.DialogOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.22, 0.0, -0.5)) + self.cancelButton = DirectButton(parent=self.verifyFrame, image=cancelButtonImage, relief=None, text=OTPLocalizer.DialogCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.22, 0.0, -0.5)) + self.deleteItemText = DirectLabel(parent=self.verifyFrame, relief=None, text='', text_wordwrap=16, pos=(0.0, 0.0, -0.4), scale=0.09) + self.verifyFrame['text'] = verifyText + self.deleteItemText['text'] = item.getName() + self.okButton['command'] = okFunc + self.cancelButton['command'] = cancelFunc + self.verifyFrame.show() + self.itemPanel, self.itemIval = item.getPicture(base.localAvatar) + if self.itemPanel: + self.itemPanel.reparentTo(self.verifyFrame, -1) + self.itemPanel.setPos(0, 0, 0.05) + self.itemPanel.setScale(0.35) + self.deleteItemText.setPos(0.0, 0.0, -0.4) + else: + self.deleteItemText.setPos(0, 0, 0.07) + if self.itemIval: + self.itemIval.loop() + return + + def __handleVerifyDeleteOK(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Send Item to Trash') + deleteFunction = self.verifyItems[0] + deleteFunctionArgs = self.verifyItems[1:] + self.__cleanupVerifyDelete() + deleteFunction(*deleteFunctionArgs) + + def __cleanupVerifyDelete(self, *args): + if self.nonDeletableItem: + self.nonDeletableItem.cleanup() + self.nonDeletableItem = None + if self.verifyFrame: + self.verifyFrame.hide() + if self.itemIval: + self.itemIval.finish() + self.itemIval = None + if self.itemPanel: + self.itemPanel.destroy() + self.itemPanel = None + self.verifyItems = None + return + + def __enableItemButtons(self, enabled): + self.notify.debug('__enableItemButtons %d' % enabled) + if enabled: + buttonState = DGG.NORMAL + else: + buttonState = DGG.DISABLED + if hasattr(self, 'inAtticButton'): + self.inAtticButton['state'] = buttonState + if hasattr(self, 'inRoomButton'): + self.inRoomButton['state'] = buttonState + if hasattr(self, 'inTrashButton'): + self.inTrashButton['state'] = buttonState + pickers = [self.atticPicker, self.inRoomPicker, self.inTrashPicker] + for picker in pickers: + if picker: + for panel in picker['items']: + if not panel.isEmpty(): + panel.enable(enabled) + + def __resetAndCleanup(self, *args): + self.__enableItemButtons(1) + self.__cleanupVerifyDelete() + + def requestDelete(self, item, itemIndex, deleteFunction): + self.__cleanupVerifyDelete() + if self.furnitureManager.ownerId != base.localAvatar.doId or not item.isDeletable(): + self.warnNonDeletableItem(item) + return + self.createVerifyDialog(item, TTLocalizer.HDDeleteItem, self.__handleVerifyDeleteOK, self.__resetAndCleanup) + self.verifyItems = (deleteFunction, item, itemIndex) + + def requestRoomDelete(self, dfitem, objectId, itemIndex): + self.__cleanupVerifyDelete() + item = dfitem.item + if self.furnitureManager.ownerId != base.localAvatar.doId or not item.isDeletable(): + self.warnNonDeletableItem(item) + return + self.createVerifyDialog(item, TTLocalizer.HDDeleteItem, self.__handleVerifyDeleteOK, self.__resetAndCleanup) + self.verifyItems = (self.deleteItemFromRoom, + dfitem, + objectId, + itemIndex) + + def warnNonDeletableItem(self, item): + message = TTLocalizer.HDNonDeletableItem + if not item.isDeletable(): + if item.getFlags() & CatalogFurnitureItem.FLBank: + message = TTLocalizer.HDNonDeletableBank + elif item.getFlags() & CatalogFurnitureItem.FLCloset: + message = TTLocalizer.HDNonDeletableCloset + elif item.getFlags() & CatalogFurnitureItem.FLPhone: + message = TTLocalizer.HDNonDeletablePhone + elif item.getFlags() & CatalogFurnitureItem.FLTrunk: + message = TTLocalizer.HDNonDeletableTrunk + if self.furnitureManager.ownerId != base.localAvatar.doId: + message = TTLocalizer.HDNonDeletableNotOwner % self.furnitureManager.ownerName + self.nonDeletableItem = TTDialog.TTDialog(text=message, style=TTDialog.Acknowledge, fadeScreen=0, command=self.__resetAndCleanup) + self.nonDeletableItem.show() + + def requestReturnToAttic(self, item, objectId): + self.__cleanupVerifyDelete() + itemIndex = None + for i in range(len(self.inRoomPanels)): + if self.inRoomPanels[i].itemId == objectId: + itemIndex = i + self.__enableItemButtons(0) + break + + if self.deleteMode: + dfitem = self.objectDict[objectId].dfitem + self.requestRoomDelete(dfitem, objectId, itemIndex) + return + self.createVerifyDialog(item, TTLocalizer.HDReturnVerify, self.__handleVerifyReturnOK, self.__resetAndCleanup) + self.verifyItems = (item, objectId) + return + + def __handleVerifyReturnOK(self): + item, objectId = self.verifyItems + self.__cleanupVerifyDelete() + self.pickInRoom(objectId) + self.sendItemToAttic() + + def requestReturnToAtticFromTrash(self, item, itemIndex): + self.__cleanupVerifyDelete() + self.__enableItemButtons(0) + self.createVerifyDialog(item, TTLocalizer.HDReturnFromTrashVerify, self.__handleVerifyReturnFromTrashOK, self.__resetAndCleanup) + self.verifyItems = (item, itemIndex) + + def __handleVerifyReturnFromTrashOK(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Send Item to Attic') + item, itemIndex = self.verifyItems + self.__cleanupVerifyDelete() + self.recoverDeletedItem(item, itemIndex) + + def recoverDeletedItem(self, item, itemIndex): + messenger.send('wakeup') + self.furnitureManager.recoverDeletedItem(item, itemIndex, self.__recoverDeletedItemCallback) + + def __recoverDeletedItemCallback(self, retcode, item, itemIndex): + self.__cleanupVerifyDelete() + if retcode < 0: + if retcode == ToontownGlobals.FM_HouseFull: + self.showHouseFullDialog() + self.notify.info('Unable to recover deleted item %s, reason %s.' % (itemIndex, retcode)) + return + self.__enableItemButtons(1) + self.inTrashPanels[itemIndex].destroy() + del self.inTrashPanels[itemIndex] + for i in range(itemIndex, len(self.inTrashPanels)): + self.inTrashPanels[i].itemId -= 1 + + self.regenerateInTrashPicker() + itemType = item.getTypeCode() + if itemType == CatalogItemTypes.WALLPAPER_ITEM or itemType == CatalogItemTypes.FLOORING_ITEM or itemType == CatalogItemTypes.MOULDING_ITEM or itemType == CatalogItemTypes.WAINSCOTING_ITEM: + itemIndex = len(self.atticWallpaperPanels) + bringCommand = self.bringWallpaperFromAttic + elif itemType == CatalogItemTypes.WINDOW_ITEM: + itemIndex = len(self.atticWindowPanels) + bringCommand = self.bringWindowFromAttic + else: + itemIndex = len(self.atticItemPanels) + bringCommand = self.bringItemFromAttic + panel = FurnitureItemPanel(item, itemIndex, command=bringCommand, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') + if itemType == CatalogItemTypes.WALLPAPER_ITEM or itemType == CatalogItemTypes.FLOORING_ITEM or itemType == CatalogItemTypes.MOULDING_ITEM or itemType == CatalogItemTypes.WAINSCOTING_ITEM: + self.atticWallpaperPanels.append(panel) + elif itemType == CatalogItemTypes.WINDOW_ITEM: + self.atticWindowPanels.append(panel) + else: + self.atticItemPanels.append(panel) + self.regenerateAtticPicker() + + def showHouseFullDialog(self): + self.cleanupDialog() + self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.HDHouseFull, text_wordwrap=15, command=self.cleanupDialog) + self.dialog.show() + + def bindHelpText(self, button, category): + button.bind(DGG.ENTER, self.showHelpText, extraArgs=[category, None]) + button.bind(DGG.EXIT, self.hideHelpText) + return + + def showHelpText(self, category, itemName, xy): + + def showIt(task): + helpText = TTLocalizer.HDHelpDict.get(category) + if helpText: + if itemName: + helpText = helpText % itemName + self.helpText['text'] = helpText + self.helpText.show() + else: + print 'category: %s not found' + + taskMgr.doMethodLater(0.75, showIt, 'showHelpTextDoLater') + + def hideHelpText(self, xy): + taskMgr.remove('showHelpTextDoLater') + self.helpText['text'] = '' + self.helpText.hide() diff --git a/toontown/fishing/BingoCardBase.py b/toontown/fishing/BingoCardBase.py new file mode 100644 index 0000000..4fe0763 --- /dev/null +++ b/toontown/fishing/BingoCardBase.py @@ -0,0 +1,146 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import FishGlobals +from toontown.fishing import BingoGlobals +from direct.showbase import RandomNumGen +from math import ceil, pow + +class BingoCardBase: + notify = DirectNotifyGlobal.directNotify.newCategory('BingoCardBase') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + self.rowSize = rowSize + self.colSize = colSize + self.cardSize = cardSize + self.cellList = [] + self.gameType = None + self.gameState = 1 << self.cardSize / 2 + return + + def destroy(self): + del self.cellList + + def generateCard(self, tileSeed, zoneId): + rng = RandomNumGen.RandomNumGen(tileSeed) + fishList = FishGlobals.getPondGeneraList(zoneId) + emptyCells = self.cardSize - 1 - len(fishList) + rodId = 0 + for i in xrange(emptyCells): + fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + while not fish[0]: + fish = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + + fishList.append((fish[1], fish[2])) + rodId += 1 + if rodId > 4: + rodId = 0 + + for index in xrange(self.cardSize): + if index != self.cardSize / 2: + choice = rng.randrange(0, len(fishList)) + self.cellList.append(fishList.pop(choice)) + else: + self.cellList.append((None, None)) + + return None + + def getGameType(self): + return self.gameType + + def getGameState(self): + return self.gameState + + def getCardSize(self): + return self.cardSize + + def getRowSize(self): + return self.rowSize + + def getColSize(self): + return self.colSize + + def setGameState(self, state): + self.gameState = state + + def clearCellList(self): + del self.cellList + self.cellList = [] + + def cellUpdateCheck(self, id, genus, species): + if id >= self.cardSize: + self.notify.warning('cellUpdateCheck: Invalid Cell Id %s. Id greater than Card Size.') + return + elif id < 0: + self.notify.warning('cellUpdateCheck: Invalid Cell Id %s. Id less than zero.') + return + fishTuple = (genus, species) + if self.cellList[id][0] == genus or fishTuple == FishGlobals.BingoBoot: + self.gameState = self.gameState | 1 << id + if self.checkForWin(id): + return BingoGlobals.WIN + return BingoGlobals.UPDATE + return BingoGlobals.NO_UPDATE + + def checkForWin(self, id): + pass + + def rowCheck(self, rowId): + for colId in xrange(self.colSize): + if not self.gameState & 1 << self.rowSize * rowId + colId: + return 0 + + return 1 + + def colCheck(self, colId): + for rowId in xrange(self.rowSize): + if not self.gameState & 1 << self.rowSize * rowId + colId: + return 0 + + return 1 + + def fDiagCheck(self, id): + checkNum = self.rowSize + 1 + if not id % checkNum: + for i in xrange(self.rowSize): + if not self.gameState & 1 << i * checkNum: + return 0 + + return 1 + else: + return 0 + + def bDiagCheck(self, id): + checkNum = self.rowSize - 1 + if not id % checkNum and not id == self.cardSize - 1: + for i in xrange(self.rowSize): + if not self.gameState & 1 << i * checkNum + checkNum: + return 0 + + return 1 + return 0 + + def cellCheck(self, id): + if self.gameState & 1 << id: + return 1 + return 0 + + def onRow(self, row, id): + if int(id / self.rowSize) == row: + return 1 + return 0 + + def onCol(self, col, id): + if id % BingoGlobals.CARD_COLS == col: + return 1 + return 0 + + def onFDiag(self, id): + checkNum = self.rowSize + 1 + if not id % checkNum: + return 1 + return 0 + + def onBDiag(self, id): + checkNum = self.rowSize - 1 + if not id % checkNum: + return 1 + return 0 diff --git a/toontown/fishing/BingoCardCell.py b/toontown/fishing/BingoCardCell.py new file mode 100644 index 0000000..1071c57 --- /dev/null +++ b/toontown/fishing/BingoCardCell.py @@ -0,0 +1,104 @@ +from direct.fsm import FSM +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.fishing import FishPhoto +from toontown.fishing import BingoGlobals + +class BingoCardCell(DirectButton, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('BingoCardCell') + + def __init__(self, cellId, fish, model, color, parent, **kw): + self.model = model + self.color = color + buttonToUse = self.model.find('**/mickeyButton') + optiondefs = (('relief', None, None), + ('state', DGG.DISABLED, None), + ('image', buttonToUse, None), + ('image_color', self.color, None), + ('image_hpr', (0, 90, 0), None), + ('image_pos', (0, 0, 0), None), + ('pressEffect', False, None)) + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, parent) + FSM.FSM.__init__(self, 'BingoCardCell') + self.initialiseoptions(BingoCardCell) + self.parent = parent + self.fish = fish + self.cellId = cellId + self.request('Off') + return + + def destroy(self): + DirectButton.destroy(self) + + def setImageTo(self, button): + button.setHpr(0, 90, 0) + button.setPos(0, 0, 0) + button.setScale(BingoGlobals.CellImageScale) + button.setColor(self.color[0], self.color[1], self.color[2], self.color[3]) + self['image'] = button + self.setImage() + + def getButtonName(self): + genus = self.getFishGenus() + return BingoGlobals.FishButtonDict[genus][0] + + def generateLogo(self): + buttonName = self.getButtonName() + buttonToUse = self.model.find('**/' + buttonName) + self.setImageTo(buttonToUse) + + def generateMarkedLogo(self): + self.setImageTo(self.model.find('**/mickeyButton')) + + def setFish(self, fish): + if self.fish: + del self.fish + self.fish = fish + + def getFish(self): + return self.fish + + def getFishGenus(self): + if self.fish == 'Free': + return -1 + return self.fish.getGenus() + + def getFishSpecies(self): + return self.fish.getSpecies() + + def enable(self, callback = None): + self.request('On', callback) + + def disable(self): + self.request('Off') + if not self.fish == 'Free': + self.generateMarkedLogo() + + def enterOff(self): + self['state'] = DGG.DISABLED + self['command'] = None + return + + def filterOff(self, request, args): + if request == 'On': + return (request, args) + elif request == 'Off': + return request + else: + self.notify.debug('filterOff: Invalid State Transition from Off to %s' % request) + + def enterOn(self, args): + self['state'] = DGG.NORMAL + if args[0]: + self['command'] = Func(args[0], self.cellId).start + + def filterOn(self, request, args): + if request == 'Off': + return request + else: + self.notify.debug('filterOn: Invalid State Transition from Off to %s' % request) diff --git a/toontown/fishing/BingoCardGui.py b/toontown/fishing/BingoCardGui.py new file mode 100644 index 0000000..e557d62 --- /dev/null +++ b/toontown/fishing/BingoCardGui.py @@ -0,0 +1,442 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.task import Task +import random +from toontown.fishing import BingoCardCell +from toontown.fishing import BingoGlobals +from toontown.fishing import FishBase +from toontown.fishing import FishGlobals +from direct.showbase import RandomNumGen +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +BG = BingoGlobals + +class BingoCardGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('BingoCardGui') + + def __init__(self, parent = aspect2d, **kw): + self.notify.debug('Bingo card initialized') + self.model = loader.loadModel('phase_4/models/gui/FishBingo') + optiondefs = (('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', self.model.find('**/g'), None), + ('image_color', BG.getColor(0), None), + ('image_scale', BG.CardImageScale, None), + ('image_hpr', (0.0, 90.0, 0.0), None), + ('pos', BG.CardPosition, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(BingoCardGui) + self.game = None + self.cellGuiList = [] + self.parent = parent + self.load() + self.hide() + self.taskNameFlashFish = 'flashMatchingFishTask' + return + + def show(self): + DirectFrame.show(self) + if self.game and self.game.checkForBingo(): + self.__indicateBingo(True) + if self.game.getGameType() == BG.BLOCKOUT_CARD: + self.showJackpot() + + def hide(self): + self.hideTutorial() + self.hideJackpot() + DirectFrame.hide(self) + + def loadGameTimer(self): + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self) + self.timer.setScale(0.17) + self.timer.setPos(0.24, 0, -0.18) + + def resetGameTimer(self): + self.timer.reset() + + def startGameCountdown(self, time): + self.notify.debug('startGameCountdown: %s' % time) + self.timer.countdown(time) + + def startNextGameCountdown(self, time): + self.nextGameTimer.countdown(time) + + def hideNextGameTimer(self): + self.nextGame['text'] = '' + self.nextGame.hide() + + def showNextGameTimer(self, text): + self.nextGame['text'] = text + self.nextGame.show() + + def resetNextGameTimer(self): + self.nextGameTimer.reset() + + def stopNextGameTimer(self): + self.nextGameTimer.stop() + + def setNextGameText(self, text): + self.nextGame['text'] = text + + def setJackpotText(self, text): + if text: + str = TTLocalizer.FishBingoJackpotWin % text + else: + str = '' + self.jpText['text'] = str + + def resetGameTypeText(self): + self.gameType['text'] = '' + self.gameType.setFrameSize() + self.gameType.hide() + + def loadNextGameTimer(self): + self.nextGame = DirectLabel(parent=self, relief=None, text='', text_font=ToontownGlobals.getSignFont(), text_scale=TTLocalizer.BCGnextGame * BG.CardImageScale[2], text_fg=(1.0, 1.0, 1.0, 1), pos=(BG.GridXOffset, 0, 4 * BG.CardImageScale[2])) + self.nextGameTimer = ToontownTimer.ToontownTimer() + self.nextGameTimer.reparentTo(self.nextGame) + self.nextGameTimer.setPos(0, 0, -5 * BG.CardImageScale[2]) + self.nextGameTimer.setProp('image', None) + self.nextGameTimer.setProp('text_font', ToontownGlobals.getSignFont()) + self.nextGameTimer.setProp('text_scale', 0.2 * BG.CardImageScale[2]) + self.nextGameTimer.setFontColor(Vec4(1.0, 1.0, 1.0, 1)) + return + + def setGameOver(self, text): + self.gameOver['text'] = text + + def load(self): + self.notify.debug('Bingo card loading') + self.loadGameTimer() + self.loadNextGameTimer() + textScale = 0.06 + textHeight = 0.38 * BG.CardImageScale[2] + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.bingo = DirectButton(parent=self, pos=(BG.GridXOffset, 0, 0.305), scale=(0.0343, 0.035, 0.035), relief=None, state=DGG.DISABLED, geom=self.model.find('**/BINGObutton'), geom_pos=(0, 0, 0), geom_hpr=(0, 90, 0), image=(self.model.find('**/Gold_TTButtUP'), self.model.find('**/goldTTButtDown'), self.model.find('**/RolloverBingoButton1')), image_pos=(0, 0, 0), image_hpr=(0, 90, 0), image_color=BG.getButtonColor(0), pressEffect=False) + guiButton.removeNode() + arrowModel = loader.loadModel('phase_3.5/models/gui/speedChatGui') + self.gameType = DirectButton(parent=self, pos=(BG.GridXOffset, 0, -8 * BG.CardImageScale[2] - 0.01), relief=None, image=arrowModel.find('**/chatArrow'), image_scale=-0.05, image_pos=(-0.2, 0, 0.025), text='', text_scale=0.045, text_fg=(1, 1, 1, 1), text_font=ToontownGlobals.getSignFont(), text_wordwrap=10.5, text_pos=(0.01, 0.008), pressEffect=False) + arrowModel.removeNode() + self.gameType.bind(DGG.ENTER, self.onMouseEnter) + self.gameType.bind(DGG.EXIT, self.onMouseLeave) + self.gameType.hide() + self.jpText = DirectLabel(parent=self, pos=(BG.GridXOffset, 0, 0.22), relief=None, state=DGG.NORMAL, text='', text_scale=TTLocalizer.BCGjpText, text_pos=(0, 0, 0), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=TTLocalizer.BCGjpTextWordwrap) + self.gameOver = DirectLabel(parent=self, pos=(BG.GridXOffset, 0, 0), relief=None, state=DGG.NORMAL, text='', text_scale=textScale, text_fg=(1, 1, 1, 1), text_font=ToontownGlobals.getSignFont()) + self.jpSign = DirectFrame(parent=self.parent, relief=None, state=DGG.NORMAL, pos=BG.CardPosition, scale=(0.035, 0.035, 0.035), text=TTLocalizer.FishBingoJackpot, text_scale=2, text_pos=(-1.5, 18.6), text_fg=(1, 1, 1, 1), image=self.model.find('**/jackpot'), image_pos=(0, 0, 0), image_hpr=(0, 90, 0), sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.makeJackpotLights(self.jpSign) + self.hideJackpot() + self.makeTutorial() + return + + def destroy(self): + self.cleanTutorial() + self.removeGame() + del self.cellGuiList + self.gameOver.destroy() + self.destroyJackpotLights() + self.jpSign.destroy() + self.nextGameTimer.destroy() + self.nextGame.destroy() + self.timer.destroy() + self.bingo.destroy() + self.gameType.destroy() + DirectFrame.destroy(self) + self.notify.debug('Bingo card destroyed') + + def loadCard(self): + cardSize = self.game.getCardSize() + for index in xrange(cardSize): + self.cellGuiList[index].generateLogo() + if index == cardSize / 2: + self.cellGuiList[index].generateMarkedLogo() + elif self.game.getGameState() & 1 << index: + self.cellGuiList[index].disable() + + def disableCard(self): + self.stopCellBlinking() + for index in xrange(self.game.getCardSize()): + self.cellGuiList[index].disable() + + def enableCard(self, callback = None): + self.notify.info('enable Bingo card') + self.stopCellBlinking() + for index in xrange(len(self.cellGuiList)): + if index != self.game.getCardSize() / 2: + self.cellGuiList[index].enable(callback) + + def generateCard(self, tileSeed, zoneId): + rng = RandomNumGen.RandomNumGen(tileSeed) + rowSize = self.game.getRowSize() + fishList = FishGlobals.getPondGeneraList(zoneId) + for i in xrange(len(fishList)): + fishTuple = fishList.pop(0) + weight = FishGlobals.getRandomWeight(fishTuple[0], fishTuple[1]) + fish = FishBase.FishBase(fishTuple[0], fishTuple[1], weight) + fishList.append(fish) + + emptyCells = self.game.getCardSize() - 1 - len(fishList) + rodId = 0 + for i in xrange(emptyCells): + fishVitals = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + while not fishVitals[0]: + fishVitals = FishGlobals.getRandomFishVitals(zoneId, rodId, rng) + + fish = FishBase.FishBase(fishVitals[1], fishVitals[2], fishVitals[3]) + fishList.append(fish) + rodId += 1 + if rodId > 4: + rodId = 0 + + for i in xrange(rowSize): + for j in xrange(self.game.getColSize()): + color = self.getCellColor(i * rowSize + j) + if i * rowSize + j == self.game.getCardSize() / 2: + tmpFish = 'Free' + else: + choice = rng.randrange(0, len(fishList)) + tmpFish = fishList.pop(choice) + xPos = BG.CellImageScale * (j - 2) + BG.GridXOffset + yPos = BG.CellImageScale * (i - 2) - 0.015 + cellGui = BingoCardCell.BingoCardCell(i * rowSize + j, tmpFish, self.model, color, self, image_scale=BG.CellImageScale, pos=(xPos, 0, yPos)) + self.cellGuiList.append(cellGui) + + def cellUpdateCheck(self, id, genus, species): + fishTuple = (genus, species) + if self.cellGuiList[id].getFishGenus() == genus or fishTuple == FishGlobals.BingoBoot: + self.notify.debug('Square found! Cell disabled: %s' % id) + self.stopCellBlinking() + self.cellGuiList[id].disable() + self.game.setGameState(self.game.getGameState() | 1 << id) + if self.game.checkForBingo(): + return BG.WIN + return BG.UPDATE + return BG.NO_UPDATE + + def cellUpdate(self, cellId): + self.cellGuiList[cellId].disable() + + def addGame(self, game): + self.game = game + type = game.getGameType() + self.gameType.setProp('text', BG.getGameName(type)) + self.gameType.setFrameSize() + self.gameType.show() + + def getGame(self): + return self.game + + def removeGame(self): + self.stopCellBlinking() + if self.game: + self.game.destroy() + self.game = None + self.setJackpotText(None) + self.resetGameTypeText() + for cell in self.cellGuiList: + cell.destroy() + + self.cellGuiList = [] + return + + def getUnmarkedMatches(self, fish): + if self.game is None: + return [] + matches = [] + for cell in self.cellGuiList: + if cell['state'] == DGG.NORMAL: + if cell.getFishGenus() == fish[0] or fish == FishGlobals.BingoBoot: + matches.append(cell) + + return matches + + def getCellColor(self, id): + if self.game.checkForColor(id): + return BG.CellColorActive + return BG.CellColorInactive + + def resetCellColors(self): + for cell in self.cellGuiList: + c = self.getCellColor(cell.cellId) + cell['image'].setColor(c[0], c[1], c[2], c[3]) + cell.setImage() + + def stopCellBlinking(self): + self.hideTutorial() + self.resetCellColors() + taskMgr.remove(self.taskNameFlashFish) + + def __indicateMatches(self, bFlipFlop, fish): + unmarkedMatches = self.getUnmarkedMatches(fish) + if len(unmarkedMatches) is 0: + return Task.done + if bFlipFlop: + for cell in unmarkedMatches: + cell['image'].setColor(1, 0, 0, 1) + cell.setImage() + + else: + self.resetCellColors() + taskMgr.doMethodLater(0.5, self.__indicateMatches, self.taskNameFlashFish, extraArgs=(not bFlipFlop, fish)) + return Task.done + + def fishCaught(self, fish): + self.stopCellBlinking() + self.__indicateMatches(True, fish) + + def setBingo(self, state = DGG.DISABLED, callback = None): + self.notify.debug('setBingo: %s %s' % (state, callback)) + if self.bingo['state'] == state: + return + if not self.game: + return + if state == DGG.NORMAL: + self.__indicateBingo(True) + if self.game.getGameType() == BG.BLOCKOUT_CARD: + self.showJackpot() + else: + taskMgr.remove('bingoFlash') + c = BG.getButtonColor(self.game.getGameType()) + self.bingo['image_color'] = Vec4(c[0], c[1], c[2], c[3]) + self.hideJackpot() + self.bingo['state'] = state + self.bingo['command'] = callback + + def checkForBingo(self): + return self.game.checkForBingo() + + def __indicateBingo(self, bFlipFlop): + if not self.game: + return Task.done + if bFlipFlop: + gameType = self.game.getGameType() + if gameType == BG.DIAGONAL_CARD: + color = Vec4(1, 1, 1, 1) + else: + color = Vec4(1, 0, 0, 1) + self.bingo['image_color'] = color + else: + c = BG.getButtonColor(self.game.getGameType()) + self.bingo['image_color'] = Vec4(c[0], c[1], c[2], c[3]) + taskMgr.doMethodLater(0.5, self.__indicateBingo, 'bingoFlash', extraArgs=(not bFlipFlop,)) + return Task.done + + NumLights = 32 + Off = False + On = True + + def showJackpot(self): + self.jpSign.show() + for light in self.jpLights: + light.show() + + self.flashJackpotLights(random.randrange(3)) + + def hideJackpot(self): + self.jpSign.hide() + for light in self.jpLights: + light.hide() + + taskMgr.remove('jackpotLightFlash') + + def getLightName(self, lightIndex, bOn): + lightIndex += 1 + if bOn == self.On: + return '**/LightOn_0%02d' % lightIndex + else: + return '**/LightOff_0%02d' % lightIndex + + def makeJackpotLights(self, parent): + self.jpLights = [] + for nLight in range(self.NumLights): + lightName = self.getLightName(nLight, self.Off) + light = DirectFrame(parent=parent, relief=None, image=self.model.find(lightName), image_hpr=(0, 90, 0)) + self.jpLights.append(light) + + return + + def destroyJackpotLights(self): + taskMgr.remove('jackpotLightFlash') + for light in self.jpLights: + light.destroy() + + def lightSwitch(self, bOn, lightIndex = -1): + if lightIndex == -1: + for nLight in range(self.NumLights): + self.lightSwitch(bOn, nLight) + + else: + lightIndex %= self.NumLights + light = self.jpLights[lightIndex - 1] + light['image'] = self.model.find(self.getLightName(lightIndex, bOn)) + light['image_hpr'] = (0, 90, 0) + + def flashJackpotLights(self, flashMode, nTimeIndex = 0): + if flashMode == 2: + self.lightSwitch(self.Off) + self.lightSwitch(self.On, nTimeIndex) + self.lightSwitch(self.On, self.NumLights - nTimeIndex) + self.lightSwitch(self.On, self.NumLights / 2 + nTimeIndex) + self.lightSwitch(self.On, self.NumLights / 2 - nTimeIndex) + nTimeIndex = (nTimeIndex + 1) % (self.NumLights / 2) + delay = 0.05 + elif flashMode == 1: + if nTimeIndex: + self.lightSwitch(self.On) + else: + self.lightSwitch(self.Off) + nTimeIndex = not nTimeIndex + delay = 0.5 + elif flashMode == 0: + for nLight in range(self.NumLights): + if nLight % 2 == nTimeIndex: + self.lightSwitch(self.On, nLight) + else: + self.lightSwitch(self.Off, nLight) + + nTimeIndex = (nTimeIndex + 1) % 2 + delay = 0.2 + taskMgr.doMethodLater(delay, self.flashJackpotLights, 'jackpotLightFlash', extraArgs=(flashMode, nTimeIndex)) + return Task.done + + def makeTutorial(self): + self.tutorial = TTDialog.TTDialog(fadeScreen=0, pad=(0.05, 0.05), midPad=0, topPad=0, sidePad=0, text=TTLocalizer.FishBingoHelpBlockout, style=TTDialog.NoButtons, pos=BG.TutorialPosition, scale=BG.TutorialScale) + self.tutorial.hide() + + def cleanTutorial(self): + self.tutorial.cleanup() + self.tutorial = None + return + + def showTutorial(self, messageType): + if messageType == BG.TutorialIntro: + self.tutorial['text'] = TTLocalizer.FishBingoHelpMain + elif messageType == BG.TutorialMark: + self.tutorial['text'] = TTLocalizer.FishBingoHelpFlash + elif messageType == BG.TutorialCard: + if self.game: + gameType = self.game.getGameType() + self.tutorial['text'] = BG.getHelpString(gameType) + elif messageType == BG.TutorialBingo: + self.tutorial['text'] = TTLocalizer.FishBingoHelpBingo + self.tutorial.show() + + def hideTutorial(self, event = None): + if self.tutorial: + self.tutorial.hide() + + def onMouseEnter(self, event): + if self.gameType['text'] is not '': + self.showTutorial(BG.TutorialCard) + + def onMouseLeave(self, event): + self.hideTutorial() + + def castingStarted(self): + if taskMgr.hasTaskNamed(self.taskNameFlashFish): + if not base.localAvatar.bFishBingoMarkTutorialDone: + self.showTutorial(BG.TutorialMark) + base.localAvatar.b_setFishBingoMarkTutorialDone(True) diff --git a/toontown/fishing/BingoGlobals.py b/toontown/fishing/BingoGlobals.py new file mode 100644 index 0000000..e05932e --- /dev/null +++ b/toontown/fishing/BingoGlobals.py @@ -0,0 +1,118 @@ +from toontown.toonbase import TTLocalizer +NORMAL_CARD = 0 +FOURCORNER_CARD = 1 +DIAGONAL_CARD = 2 +THREEWAY_CARD = 3 +BLOCKOUT_CARD = 4 +Style1 = ((249, 193, 41, 255), (106, 241, 233, 255), (64, 215, 206, 255)) +Style2 = ((138, 241, 106, 255), (246, 129, 220, 255), (221, 113, 197, 255)) +Style3 = ((128, 108, 250, 255), (248, 129, 56, 255), (250, 95, 26, 255)) +Style4 = ((10, 118, 251, 255), (252, 225, 97, 255), (245, 207, 29, 255)) +Style5 = ((243, 84, 253, 255), (97, 163, 253, 255), (48, 129, 240, 255)) +CardTypeDict = {NORMAL_CARD: (Style1, + 10, + 140, + TTLocalizer.FishBingoTypeNormal, + TTLocalizer.FishBingoHelpNormal), + FOURCORNER_CARD: (Style2, + 20, + 120, + TTLocalizer.FishBingoTypeCorners, + TTLocalizer.FishBingoHelpCorners), + DIAGONAL_CARD: (Style3, + 40, + 180, + TTLocalizer.FishBingoTypeDiagonal, + TTLocalizer.FishBingoHelpDiagonals), + THREEWAY_CARD: (Style4, + 80, + 180, + TTLocalizer.FishBingoTypeThreeway, + TTLocalizer.FishBingoHelpThreeway), + BLOCKOUT_CARD: (Style5, + 1000, + 90, + TTLocalizer.FishBingoTypeBlockout, + TTLocalizer.FishBingoHelpBlockout)} + +def getGameTime(typeId): + return CardTypeDict[typeId][2] + + +def getGameName(typeId): + return CardTypeDict[typeId][3] + + +def getJackpot(typeId): + return CardTypeDict[typeId][1] + + +def getColor(typeId): + float_color = map(lambda x: x / 255.0, CardTypeDict[typeId][0][0]) + return float_color + + +def getButtonColor(typeId): + float_color = map(lambda x: x / 255.0, CardTypeDict[typeId][0][1]) + return float_color + + +def getButtonRolloverColor(typeId): + float_color = map(lambda x: x / 255.0, CardTypeDict[typeId][0][2]) + return float_color + + +def getHelpString(typeId): + return CardTypeDict[typeId][4] + + +CellColorActive = (1.0, 1.0, 1.0, 1.0) +CellColorInactive = (0.8, 0.8, 0.8, 1.0) +ROLLOVER_AMOUNT = 100 +MIN_SUPER_JACKPOT = 1000 +MAX_SUPER_JACKPOT = 10000 +NO_UPDATE = 0 +UPDATE = 1 +WIN = 2 +CARD_ROWS = 5 +CARD_COLS = 5 +CARD_SIZE = 25 +INTRO_SESSION = 5.0 +TIMEOUT_SESSION = 15.0 +REWARD_TIMEOUT = 5.0 +CLOSE_EVENT_TIMEOUT = 5.0 +HOUR_BREAK_SESSION = 300 +HOUR_BREAK_MIN = 55 +NORMAL_GAME = 0 +INTERMISSION = 1 +CLOSE_EVENT = 2 +CardImageScale = (0.035, 0.035, 0.035) +CardPosition = (0.75, 1.0, -.65) +TutorialPosition = (0.2, 1.0, -0.76) +TutorialScale = 0.6 +TutorialTextScale = (0.07, 0.233) +CellImageScale = 0.088 +GridXOffset = -0.052 +FishButtonDict = {-1: ('mickeyButton',), + 0: ('BaloonFishButton',), + 2: ('CatfishButton',), + 4: ('ClownfishButton',), + 6: ('FrozenfishButton',), + 8: ('starfishButton',), + 10: ('holyMackrelButton',), + 12: ('DogfishButton',), + 14: ('amoreEelButton',), + 16: ('nursesharkButton',), + 18: ('kingcrabButton',), + 20: ('moonfishButton',), + 22: ('pPlane21',), + 24: ('poolsharkButton',), + 26: ('BearacudaButton',), + 28: ('troutButton',), + 30: ('pianotunaButton',), + 32: ('PBJfishButton',), + 34: ('DevilrayButton',)} +TutorialIntro = 1 +TutorialMark = 2 +TutorialCard = 3 +TutorialBingo = 4 diff --git a/toontown/fishing/BlockoutBingo.py b/toontown/fishing/BlockoutBingo.py new file mode 100644 index 0000000..0d7b109 --- /dev/null +++ b/toontown/fishing/BlockoutBingo.py @@ -0,0 +1,25 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class BlockoutBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('BlockoutBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.BLOCKOUT_CARD + + def checkForWin(self, id = 0): + for i in xrange(self.rowSize): + if not self.rowCheck(i): + return BingoGlobals.NO_UPDATE + + return BingoGlobals.WIN + + def checkForColor(self, id): + return 1 + + def checkForBingo(self): + if self.checkForWin(): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/DiagonalBingo.py b/toontown/fishing/DiagonalBingo.py new file mode 100644 index 0000000..f5b8063 --- /dev/null +++ b/toontown/fishing/DiagonalBingo.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class DiagonalBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DiagonalBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.DIAGONAL_CARD + self.fDiagResult = 0 + self.bDiagResult = 0 + + def checkForWin(self, id): + if self.fDiagCheck(id): + self.fDiagResult = 1 + if self.bDiagCheck(id): + self.bDiagResult = 1 + if self.fDiagResult and self.bDiagResult: + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + return self.onFDiag(id) | self.onBDiag(id) + + def checkForBingo(self): + id = self.cardSize / 2 + if self.checkForWin(id): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/DistributedFishingPond.py b/toontown/fishing/DistributedFishingPond.py new file mode 100644 index 0000000..6faaee5 --- /dev/null +++ b/toontown/fishing/DistributedFishingPond.py @@ -0,0 +1,126 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import FishGlobals +from toontown.fishing import DistributedPondBingoManager +from pandac.PandaModules import Vec3 +from direct.task import Task + +class DistributedFishingPond(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingPond') + pollInterval = 0.5 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.notify.debug('init') + self.targets = {} + self.area = None + self.localToonBobPos = None + self.localToonSpot = None + self.pondBingoMgr = None + self.visitedSpots = {} + return + + def disable(self): + self.visitedSpots.clear() + self.stopCheckingTargets() + DistributedObject.DistributedObject.disable(self) + + def setArea(self, area): + self.area = area + + def getArea(self): + return self.area + + def addTarget(self, target): + self.notify.debug('addTarget: %s' % target) + self.targets[target.getDoId()] = target + + def removeTarget(self, target): + self.notify.debug('removeTarget: %s' % target) + del self.targets[target.getDoId()] + + def startCheckingTargets(self, spot, bobPos): + self.notify.debug('startCheckingTargets') + if base.wantBingo: + pass + self.localToonSpot = spot + self.localToonBobPos = bobPos + taskMgr.doMethodLater(self.pollInterval * 2, self.checkTargets, self.taskName('checkTargets')) + + def stopCheckingTargets(self): + self.notify.debug('stopCheckingTargets') + taskMgr.remove(self.taskName('checkTargets')) + if not base.wantBingo: + self.localToonSpot = None + self.localToonBobPos = None + return + + def checkTargets(self, task = None): + self.notify.debug('checkTargets') + if self.localToonSpot != None: + for target in self.targets.values(): + targetPos = target.getPos(render) + distVec = Vec3(targetPos - self.localToonBobPos) + dist = distVec.length() + if dist < target.getRadius(): + self.notify.debug('checkTargets: hit target: %s' % target.getDoId()) + self.d_hitTarget(target) + return Task.done + + taskMgr.doMethodLater(self.pollInterval, self.checkTargets, self.taskName('checkTargets')) + else: + self.notify.warning('localToonSpot became None while checking targets') + return Task.done + + def d_hitTarget(self, target): + self.localToonSpot.hitTarget() + self.sendUpdate('hitTarget', [target.getDoId()]) + + def setPondBingoManager(self, pondBingoMgr): + self.pondBingoMgr = pondBingoMgr + + def removePondBingoManager(self): + del self.pondBingoMgr + self.pondBingoMgr = None + return + + def getPondBingoManager(self): + return self.pondBingoMgr + + def hasPondBingoManager(self): + return (self.pondBingoMgr and [1] or [0])[0] + + def handleBingoCatch(self, catch): + if self.pondBingoMgr: + self.pondBingoMgr.setLastCatch(catch) + + def handleBingoBoot(self): + if self.pondBingoMgr: + self.pondBingoMgr.handleBoot() + + def cleanupBingoMgr(self): + if self.pondBingoMgr: + self.pondBingoMgr.cleanup() + + def setLocalToonSpot(self, spot = None): + self.localToonSpot = spot + if spot is not None and not self.visitedSpots.has_key(spot.getDoId()): + self.visitedSpots[spot.getDoId()] = spot + return + + def showBingoGui(self): + if self.pondBingoMgr: + self.pondBingoMgr.showCard() + + def getLocalToonSpot(self): + return self.localToonSpot + + def resetSpotGui(self): + for spot in self.visitedSpots.values(): + spot.resetCastGui() + + def setSpotGui(self): + for spot in self.visitedSpots.values(): + spot.setCastGui() diff --git a/toontown/fishing/DistributedFishingTarget.py b/toontown/fishing/DistributedFishingTarget.py new file mode 100644 index 0000000..210ee97 --- /dev/null +++ b/toontown/fishing/DistributedFishingTarget.py @@ -0,0 +1,77 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedNode +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals +from direct.actor import Actor +import FishingTargetGlobals +import random +import math +from toontown.effects import Bubbles + +class DistributedFishingTarget(DistributedNode.DistributedNode): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingTarget') + radius = 2.5 + + def __init__(self, cr): + DistributedNode.DistributedNode.__init__(self, cr) + NodePath.__init__(self) + self.pond = None + self.centerPoint = (0, 0, 0) + self.maxRadius = 1.0 + self.track = None + return + + def generate(self): + self.assign(render.attachNewNode('DistributedFishingTarget')) + shadow = loader.loadModel('phase_3/models/props/drop_shadow') + shadow.setPos(0, 0, -0.1) + shadow.setScale(0.33) + shadow.setColorScale(1, 1, 1, 0.75) + shadow.reparentTo(self) + self.bubbles = Bubbles.Bubbles(self, render) + self.bubbles.renderParent.setDepthWrite(0) + self.bubbles.start() + DistributedNode.DistributedNode.generate(self) + + def disable(self): + if self.track: + self.track.finish() + self.track = None + self.bubbles.destroy() + del self.bubbles + self.pond.removeTarget(self) + self.pond = None + DistributedNode.DistributedNode.disable(self) + return + + def delete(self): + del self.pond + DistributedNode.DistributedNode.delete(self) + + def setPondDoId(self, pondDoId): + self.pond = base.cr.doId2do[pondDoId] + self.pond.addTarget(self) + self.centerPoint = FishingTargetGlobals.getTargetCenter(self.pond.getArea()) + self.maxRadius = FishingTargetGlobals.getTargetRadius(self.pond.getArea()) + + def getDestPos(self, angle, radius): + x = radius * math.cos(angle) + self.centerPoint[0] + y = radius * math.sin(angle) + self.centerPoint[1] + z = self.centerPoint[2] + return (x, y, z) + + def setState(self, stateIndex, angle, radius, time, timeStamp): + ts = globalClockDelta.localElapsedTime(timeStamp) + pos = self.getDestPos(angle, radius) + if self.track and self.track.isPlaying(): + self.track.finish() + self.track = Sequence(LerpPosInterval(self, time - ts, Point3(*pos), blendType='easeInOut')) + self.track.start() + + def getRadius(self): + return self.radius diff --git a/toontown/fishing/DistributedPondBingoManager.py b/toontown/fishing/DistributedPondBingoManager.py new file mode 100644 index 0000000..2a9247c --- /dev/null +++ b/toontown/fishing/DistributedPondBingoManager.py @@ -0,0 +1,346 @@ +from direct.distributed import DistributedObject +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import FSM +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.task import Task +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardGui +from toontown.fishing import FishGlobals +from toontown.fishing import NormalBingo +from toontown.fishing import FourCornerBingo +from toontown.fishing import DiagonalBingo +from toontown.fishing import ThreewayBingo +from toontown.fishing import BlockoutBingo +from direct.showbase import RandomNumGen +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import time + +class DistributedPondBingoManager(DistributedObject.DistributedObject, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPondBingoManager') + cardTypeDict = {BingoGlobals.NORMAL_CARD: NormalBingo.NormalBingo, + BingoGlobals.FOURCORNER_CARD: FourCornerBingo.FourCornerBingo, + BingoGlobals.DIAGONAL_CARD: DiagonalBingo.DiagonalBingo, + BingoGlobals.THREEWAY_CARD: ThreewayBingo.ThreewayBingo, + BingoGlobals.BLOCKOUT_CARD: BlockoutBingo.BlockoutBingo} + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedPondBingoManager') + self.cardId = 0 + self.jackpot = 0 + self.pond = None + self.spot = None + self.card = None + self.hasEntered = 0 + self.initGameState = None + self.lastCatch = None + self.typeId = BingoGlobals.NORMAL_CARD + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.card = BingoCardGui.BingoCardGui() + self.card.reparentTo(aspect2d, 1) + self.card.hideNextGameTimer() + self.notify.debug('generate: DistributedPondBingoManager') + + def delete(self): + del self.pond.pondBingoMgr + self.pond.pondBingoMgr = None + del self.pond + self.pond = None + FSM.FSM.cleanup(self) + self.card.destroy() + del self.card + self.notify.debug('delete: Deleting Local PondManager %s' % self.doId) + DistributedObject.DistributedObject.delete(self) + return + + def d_cardUpdate(self, cellId, genus, species): + self.sendUpdate('cardUpdate', [self.cardId, + cellId, + genus, + species]) + + def d_bingoCall(self): + self.sendUpdate('handleBingoCall', [self.cardId]) + + def setCardState(self, cardId, typeId, tileSeed, gameState): + self.cardId = cardId + self.typeId = typeId + self.tileSeed = tileSeed + self.jackpot = BingoGlobals.getJackpot(typeId) + self.initGameState = gameState + + def checkForUpdate(self, cellId): + if self.lastCatch is not None: + genus = self.lastCatch[0] + species = self.lastCatch[1] + self.d_cardUpdate(cellId, genus, species) + success = self.card.cellUpdateCheck(cellId, genus, species) + if success == BingoGlobals.WIN: + self.lastCatch = None + self.enableBingo() + self.pond.getLocalToonSpot().cleanupFishPanel() + self.pond.getLocalToonSpot().hideBootPanel() + elif success == BingoGlobals.UPDATE: + self.lastCatch = None + self.pond.getLocalToonSpot().cleanupFishPanel() + self.pond.getLocalToonSpot().hideBootPanel() + else: + self.notify.warning('CheckForWin: Attempt to Play Cell without a valid catch.') + return + + def updateGameState(self, gameState, cellId): + game = self.card.getGame() + if game is not None: + game.setGameState(gameState) + self.card.cellUpdate(cellId) + return + + def __generateCard(self): + self.notify.debug('__generateCard: %s' % self.typeId) + if self.card.getGame(): + self.card.removeGame() + game = self.__cardChoice() + game.setGameState(self.initGameState) + self.card.addGame(game) + self.card.generateCard(self.tileSeed, self.pond.getArea()) + color = BingoGlobals.getColor(self.typeId) + self.card.setProp('image_color', VBase4(color[0], color[1], color[2], color[3])) + color = BingoGlobals.getButtonColor(self.typeId) + self.card.bingo.setProp('image_color', VBase4(color[0], color[1], color[2], color[3])) + if self.hasEntered: + self.card.loadCard() + self.card.show() + else: + self.card.hide() + + def showCard(self): + if (self.state != 'Off' or self.state != 'CloseEvent') and self.card.getGame(): + self.card.loadCard() + self.card.show() + elif self.state == 'GameOver': + self.card.show() + elif self.state == 'Reward': + self.card.show() + elif self.state == 'WaitCountdown': + self.card.show() + self.card.showNextGameTimer(TTLocalizer.FishBingoNextGame) + elif self.state == 'Intermission': + self.card.showNextGameTimer(TTLocalizer.FishBingoIntermission) + self.card.show() + self.hasEntered = 1 + + def __cardChoice(self): + return self.cardTypeDict.get(self.typeId)() + + def checkForBingo(self): + success = self.card.checkForBingo() + if success: + self.d_bingoCall() + self.request('Reward') + + def enableBingo(self): + self.card.setBingo(DGG.NORMAL, self.checkForBingo) + + def setPondDoId(self, pondId): + self.pond = base.cr.doId2do[pondId] + self.pond.setPondBingoManager(self) + + def setState(self, state, timeStamp): + self.notify.debug('State change: %s -> %s' % (self.state, state)) + self.request(state, timeStamp) + + def setLastCatch(self, catch): + self.lastCatch = catch + self.card.fishCaught(catch) + + def castingStarted(self): + if self.card: + self.card.castingStarted() + + def setSpot(self, spot): + self.spot = spot + + def setJackpot(self, jackpot): + self.jackpot = jackpot + + def enterOff(self): + self.notify.debug('enterOff: Enter Off State') + del self.spot + self.spot = None + if self.card.getGame: + self.card.removeGame() + self.card.hide() + self.card.stopNextGameTimer() + self.hasEntered = 0 + self.lastCatch = None + return + + def filterOff(self, request, args): + if request == 'Intro': + return 'Intro' + elif request == 'WaitCountdown': + return (request, args) + elif request == 'Playing': + self.__generateCard() + self.card.setJackpotText(str(self.jackpot)) + return (request, args) + elif request == 'Intermission': + return (request, args) + elif request == 'GameOver': + return (request, args) + elif request == 'Reward': + return ('GameOver', args) + else: + self.notify.debug('filterOff: Invalid State Transition from, Off to %s' % request) + + def exitOff(self): + self.notify.debug('exitOff: Exit Off State') + + def enterIntro(self, args = None): + self.notify.debug('enterIntro: Enter Intro State') + self.pond.setSpotGui() + self.hasEntered = 1 + + def filterIntro(self, request, args): + if request == 'WaitCountdown': + return (request, args) + else: + self.notify.debug('filterIntro: Invalid State Transition from Intro to %s' % request) + + def exitIntro(self): + self.notify.debug('exitIntro: Exit Intro State') + + def enterWaitCountdown(self, timeStamp): + self.notify.debug('enterWaitCountdown: Enter WaitCountdown State') + time = BingoGlobals.TIMEOUT_SESSION - globalClockDelta.localElapsedTime(timeStamp[0]) + self.card.startNextGameCountdown(time) + if self.hasEntered: + self.card.showNextGameTimer(TTLocalizer.FishBingoNextGame) + + def filterWaitCountdown(self, request, args): + if request == 'Playing': + return (request, args) + else: + self.notify.debug('filterOff: Invalid State Transition from WaitCountdown to %s' % request) + + def exitWaitCountdown(self): + self.notify.debug('exitWaitCountdown: Exit WaitCountdown State') + if self.pond: + self.__generateCard() + self.card.setJackpotText(str(self.jackpot)) + self.card.resetGameTimer() + self.card.hideNextGameTimer() + + def enterPlaying(self, timeStamp): + self.notify.debug('enterPlaying: Enter Playing State') + self.lastCatch = None + session = BingoGlobals.getGameTime(self.typeId) + time = session - globalClockDelta.localElapsedTime(timeStamp[0]) + self.card.startGameCountdown(time) + self.card.enableCard(self.checkForUpdate) + return + + def filterPlaying(self, request, args): + if request == 'Reward': + return (request, args) + elif request == 'GameOver': + return (request, args) + else: + self.notify.debug('filterOff: Invalid State Transition from Playing to %s' % request) + + def exitPlaying(self): + self.notify.debug('exitPlaying: Exit Playing State') + self.card.resetGameTimer() + + def enterReward(self, timeStamp): + self.notify.debug('enterReward: Enter Reward State') + if self.card: + self.card.setBingo() + self.card.removeGame() + self.card.setGameOver(TTLocalizer.FishBingoVictory) + localToonSpot = self.pond.getLocalToonSpot() + if localToonSpot: + localToonSpot.setJarAmount(self.jackpot) + self.jackpot = 0 + + def filterReward(self, request, args): + if request == 'WaitCountdown': + return (request, args) + elif request == 'Intermission': + return (request, args) + elif request == 'CloseEvent': + return 'CloseEvent' + elif request == 'Off': + return 'Off' + else: + self.notify.debug('filterOff: Invalid State Transition from Reward to %s' % request) + + def exitReward(self): + self.notify.debug('exitReward: Exit Reward State') + self.card.setGameOver('') + + def enterGameOver(self, timeStamp): + self.notify.debug('enterGameOver: Enter GameOver State') + self.card.setBingo() + self.card.removeGame() + self.card.setGameOver(TTLocalizer.FishBingoGameOver) + + def filterGameOver(self, request, args): + if request == 'WaitCountdown': + return (request, args) + elif request == 'Intermission': + return (request, args) + elif request == 'CloseEvent': + return 'CloseEvent' + elif request == 'Off': + return 'Off' + else: + self.notify.debug('filterOff: Invalid State Transition from GameOver to %s' % request) + + def exitGameOver(self): + self.notify.debug('exitGameOver: Exit GameOver State') + self.card.setGameOver('') + self.card.resetGameTypeText() + + def enterIntermission(self, timeStamp): + self.notify.debug('enterIntermission: Enter Intermission State') + if self.hasEntered: + self.card.showNextGameTimer(TTLocalizer.FishBingoIntermission) + self.notify.debug('enterIntermission: timestamp %s' % timeStamp[0]) + elapsedTime = globalClockDelta.localElapsedTime(timeStamp[0]) + self.notify.debug('enterIntermission: elapsedTime %s' % elapsedTime) + waitTime = BingoGlobals.HOUR_BREAK_SESSION - elapsedTime + self.notify.debug('enterIntermission: waitTime %s' % waitTime) + self.card.startNextGameCountdown(waitTime) + + def filterIntermission(self, request, args): + if request == 'WaitCountdown': + return (request, args) + elif request == 'Off': + return 'Off' + else: + self.notify.warning('filterOff: Invalid State Transition from GameOver to %s' % request) + + def exitIntermission(self): + self.notify.debug('enterIntermission: Exit Intermission State') + + def enterCloseEvent(self, timestamp): + self.notify.debug('enterCloseEvent: Enter CloseEvent State') + self.card.hide() + self.pond.resetSpotGui() + + def filterCloseEvent(self, request, args): + if request == 'Off': + return 'Off' + else: + self.notify.warning('filterOff: Invalid State Transition from GameOver to %s' % request) + + def exitCloseEvent(self): + self.notify.debug('exitCloseEvent: Exit CloseEvent State') diff --git a/toontown/fishing/FishBase.py b/toontown/fishing/FishBase.py new file mode 100644 index 0000000..58dd800 --- /dev/null +++ b/toontown/fishing/FishBase.py @@ -0,0 +1,74 @@ +import FishGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class FishBase: + notify = DirectNotifyGlobal.directNotify.newCategory('FishBase') + + def __init__(self, genus, species, weight): + self.genus = genus + self.species = species + self.weight = weight + + def getGenus(self): + return self.genus + + def getSpecies(self): + return self.species + + def getWeight(self): + return self.weight + + def setWeight(self, weight): + self.weight = weight + + def getVitals(self): + return (self.genus, self.species, self.weight) + + def getValue(self): + return FishGlobals.getValue(self.genus, self.species, self.weight) + + def getGenusName(self): + return TTLocalizer.FishGenusNames[self.genus] + + def getSpeciesName(self): + return TTLocalizer.FishSpeciesNames[self.genus][self.species] + + def getRarity(self): + return FishGlobals.getRarity(self.genus, self.species) + + def getPhase(self): + dict = FishGlobals.FishFileDict + fileInfo = dict.get(self.genus, dict[-1]) + return fileInfo[0] + + def getActor(self): + prefix = 'phase_%s/models/char/' % self.getPhase() + dict = FishGlobals.FishFileDict + fileInfo = dict.get(self.genus, dict[-1]) + from direct.actor import Actor + actor = Actor.Actor(prefix + fileInfo[1], {'intro': prefix + fileInfo[2], + 'swim': prefix + fileInfo[3]}) + return actor + + def getSound(self): + sound = None + loop = None + delay = None + playRate = None + if base.config.GetBool('want-fish-audio', 1): + soundDict = FishGlobals.FishAudioFileDict + fileInfo = soundDict.get(self.genus, None) + if fileInfo: + prefix = 'phase_%s/audio/sfx/' % self.getPhase() + sound = loader.loadSfx(prefix + soundDict[self.genus][0]) + loop = soundDict[self.genus][1] + delay = soundDict[self.genus][2] + playRate = soundDict[self.genus][3] + return (sound, + loop, + delay, + playRate) + + def __str__(self): + return '%s, weight: %s value: %s' % (self.getSpeciesName(), self.weight, self.getValue()) diff --git a/toontown/fishing/FishBrowser.py b/toontown/fishing/FishBrowser.py new file mode 100644 index 0000000..2d14372 --- /dev/null +++ b/toontown/fishing/FishBrowser.py @@ -0,0 +1,59 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import GenusPanel +import FishGlobals + +class FishBrowser(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FishBrowser') + + def __init__(self, parent = aspect2d, **kw): + self.parent = parent + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.3, 1.3, -1.3), None), + ('incButton_pos', (0, 0, -0.525), None), + ('incButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.3, 1.3, 1.3), None), + ('decButton_pos', (0, 0, 0.525), None), + ('decButton_image3_color', Vec4(0.8, 0.8, 0.8, 0.5), None), + ('numItemsVisible', 1, None), + ('items', map(str, FishGlobals.getGenera()), None), + ('scrollSpeed', 4, None), + ('itemMakeFunction', GenusPanel.GenusPanel, None), + ('itemMakeExtraArgs', None, None)) + gui.removeNode() + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FishBrowser) + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + return + + def update(self): + pass + + def show(self): + if not self.parent.isHidden(): + self['items'][self.index].show() + DirectScrolledList.show(self) + + def hide(self): + self['items'][self.index].hide() + DirectScrolledList.hide(self) diff --git a/toontown/fishing/FishCollection.py b/toontown/fishing/FishCollection.py new file mode 100644 index 0000000..31566f3 --- /dev/null +++ b/toontown/fishing/FishCollection.py @@ -0,0 +1,71 @@ +import FishBase +import FishGlobals + +class FishCollection: + + def __init__(self): + self.fishList = [] + + def __len__(self): + return len(self.fishList) + + def getFish(self): + return self.fishList + + def makeFromNetLists(self, genusList, speciesList, weightList): + self.fishList = [] + for genus, species, weight in zip(genusList, speciesList, weightList): + self.fishList.append(FishBase.FishBase(genus, species, weight)) + + def getNetLists(self): + genusList = [] + speciesList = [] + weightList = [] + for fish in self.fishList: + genusList.append(fish.getGenus()) + speciesList.append(fish.getSpecies()) + weightList.append(fish.getWeight()) + + return [genusList, speciesList, weightList] + + def hasFish(self, genus, species): + for fish in self.fishList: + if fish.getGenus() == genus and fish.getSpecies() == species: + return 1 + + return 0 + + def hasGenus(self, genus): + for fish in self.fishList: + if fish.getGenus() == genus: + return 1 + + return 0 + + def __collect(self, newFish, updateCollection): + for fish in self.fishList: + if fish.getGenus() == newFish.getGenus() and fish.getSpecies() == newFish.getSpecies(): + if fish.getWeight() < newFish.getWeight(): + if updateCollection: + fish.setWeight(newFish.getWeight()) + return FishGlobals.COLLECT_NEW_RECORD + else: + return FishGlobals.COLLECT_NO_UPDATE + + if updateCollection: + self.fishList.append(newFish) + return FishGlobals.COLLECT_NEW_ENTRY + + def collectFish(self, newFish): + return self.__collect(newFish, updateCollection=1) + + def getCollectResult(self, newFish): + return self.__collect(newFish, updateCollection=0) + + def __str__(self): + numFish = len(self.fishList) + txt = 'Fish Collection (%s fish):' % numFish + for fish in self.fishList: + txt += '\n' + str(fish) + + return txt diff --git a/toontown/fishing/FishGlobals.py b/toontown/fishing/FishGlobals.py new file mode 100644 index 0000000..6c02d1e --- /dev/null +++ b/toontown/fishing/FishGlobals.py @@ -0,0 +1,889 @@ +from toontown.toonbase import TTLocalizer +from math import ceil, pow +import random +from toontown.toonbase import ToontownGlobals +import copy +NoMovie = 0 +EnterMovie = 1 +ExitMovie = 2 +CastMovie = 3 +PullInMovie = 4 +CastTimeout = 45.0 +Nothing = 0 +QuestItem = 1 +FishItem = 2 +JellybeanItem = 3 +BootItem = 4 +GagItem = 5 +OverTankLimit = 8 +FishItemNewEntry = 9 +FishItemNewRecord = 10 +BingoBoot = (BootItem, 99) +ProbabilityDict = {93: FishItem, + 94: JellybeanItem, + 100: BootItem} +SortedProbabilityCutoffs = ProbabilityDict.keys() +SortedProbabilityCutoffs.sort() +Rod2JellybeanDict = {0: 10, + 1: 20, + 2: 30, + 3: 75, + 4: 150} +HealAmount = 1 +JellybeanFishingHolidayScoreMultiplier = 2 +MAX_RARITY = 10 +GlobalRarityDialBase = 4.3 +FishingAngleMax = 50.0 +OVERALL_VALUE_SCALE = 15 +RARITY_VALUE_SCALE = 0.2 +WEIGHT_VALUE_SCALE = 0.05 / 16.0 +COLLECT_NO_UPDATE = 0 +COLLECT_NEW_ENTRY = 1 +COLLECT_NEW_RECORD = 2 +RodFileDict = {0: 'phase_4/models/props/pole_treebranch-mod', + 1: 'phase_4/models/props/pole_bamboo-mod', + 2: 'phase_4/models/props/pole_wood-mod', + 3: 'phase_4/models/props/pole_steel-mod', + 4: 'phase_4/models/props/pole_gold-mod'} +RodPriceDict = {0: 0, + 1: 400, + 2: 800, + 3: 1200, + 4: 2000} +RodRarityFactor = {0: 1.0 / (GlobalRarityDialBase * 1), + 1: 1.0 / (GlobalRarityDialBase * 0.975), + 2: 1.0 / (GlobalRarityDialBase * 0.95), + 3: 1.0 / (GlobalRarityDialBase * 0.9), + 4: 1.0 / (GlobalRarityDialBase * 0.85)} +MaxRodId = 4 +FishAudioFileDict = {-1: ('Clownfish.mp3', + 1, + 1.5, + 1.0), + 0: ('BalloonFish.mp3', + 1, + 0, + 1.23), + 2: ('CatFish.mp3', + 1, + 0, + 1.26), + 4: ('Clownfish.mp3', + 1, + 1.5, + 1.0), + 6: ('Frozen_Fish.mp3', + 1, + 0, + 1.0), + 8: ('Starfish.mp3', + 0, + 0, + 1.25), + 10: ('Holy_Mackerel.mp3', + 1, + 0.9, + 1.0), + 12: ('Dog_Fish.mp3', + 1, + 0, + 1.25), + 14: ('AmoreEel.mp3', + 1, + 0, + 1.0), + 16: ('Nurse_Shark.mp3', + 0, + 0, + 1.0), + 18: ('King_Crab.mp3', + 0, + 0, + 1.0), + 20: ('Moon_Fish.mp3', + 0, + 1.0, + 1.0), + 22: ('Seahorse.mp3', + 1, + 0, + 1.26), + 24: ('Pool_Shark.mp3', + 1, + 2.0, + 1.0), + 26: ('Bear_Acuda.mp3', + 1, + 0, + 1.0), + 28: ('CutThroatTrout.mp3', + 1, + 0, + 1.0), + 30: ('Piano_Tuna.mp3', + 0, + 0, + 1.0), + 32: ('PBJ_Fish.mp3', + 1, + 0, + 1.25), + 34: ('DevilRay.mp3', + 0, + 0, + 1.0)} +FishFileDict = {-1: (4, + 'clownFish-zero', + 'clownFish-swim', + 'clownFish-swim', + None, + (0.12, 0, -0.15), + 0.38, + -35, + 20), + 0: (4, + 'balloonFish-zero', + 'balloonFish-swim', + 'balloonFish-swim', + None, + (0.0, 0, 0.0), + 1.0, + 0, + 0), + 2: (4, + 'catFish-zero', + 'catFish-swim', + 'catFish-swim', + None, + (1.2, -2.0, 0.5), + 0.22, + -35, + 10), + 4: (4, + 'clownFish-zero', + 'clownFish-swim', + 'clownFish-swim', + None, + (0.12, 0, -0.15), + 0.38, + -35, + 20), + 6: (4, + 'frozenFish-zero', + 'frozenFish-swim', + 'frozenFish-swim', + None, + (0, 0, 0), + 0.5, + -35, + 20), + 8: (4, + 'starFish-zero', + 'starFish-swim', + 'starFish-swimLOOP', + None, + (0, 0, -0.38), + 0.36, + -35, + 20), + 10: (4, + 'holeyMackerel-zero', + 'holeyMackerel-swim', + 'holeyMackerel-swim', + None, + None, + 0.4, + 0, + 0), + 12: (4, + 'dogFish-zero', + 'dogFish-swim', + 'dogFish-swim', + None, + (0.8, -1.0, 0.275), + 0.33, + -38, + 10), + 14: (4, + 'amoreEel-zero', + 'amoreEel-swim', + 'amoreEel-swim', + None, + (0.425, 0, 1.15), + 0.5, + 0, + 60), + 16: (4, + 'nurseShark-zero', + 'nurseShark-swim', + 'nurseShark-swim', + None, + (0, 0, -0.15), + 0.3, + -40, + 10), + 18: (4, + 'kingCrab-zero', + 'kingCrab-swim', + 'kingCrab-swimLOOP', + None, + None, + 0.4, + 0, + 0), + 20: (4, + 'moonFish-zero', + 'moonFish-swim', + 'moonFish-swimLOOP', + None, + (-1.2, 14, -2.0), + 0.33, + 0, + -10), + 22: (4, + 'seaHorse-zero', + 'seaHorse-swim', + 'seaHorse-swim', + None, + (-0.57, 0.0, -2.1), + 0.23, + 33, + -10), + 24: (4, + 'poolShark-zero', + 'poolShark-swim', + 'poolShark-swim', + None, + (-0.45, 0, -1.8), + 0.33, + 45, + 0), + 26: (4, + 'BearAcuda-zero', + 'BearAcuda-swim', + 'BearAcuda-swim', + None, + (0.65, 0, -3.3), + 0.2, + -35, + 20), + 28: (4, + 'cutThroatTrout-zero', + 'cutThroatTrout-swim', + 'cutThroatTrout-swim', + None, + (-0.2, 0, -0.1), + 0.5, + 35, + 20), + 30: (4, + 'pianoTuna-zero', + 'pianoTuna-swim', + 'pianoTuna-swim', + None, + (0.3, 0, 0.0), + 0.6, + 40, + 30), + 32: (4, + 'PBJfish-zero', + 'PBJfish-swim', + 'PBJfish-swim', + None, + (0, 0, 0.72), + 0.31, + -35, + 10), + 34: (4, + 'devilRay-zero', + 'devilRay-swim', + 'devilRay-swim', + None, + (0, 0, 0), + 0.4, + -35, + 20)} +FISH_PER_BONUS = 10 +TrophyDict = {0: (TTLocalizer.FishTrophyNameDict[0],), + 1: (TTLocalizer.FishTrophyNameDict[1],), + 2: (TTLocalizer.FishTrophyNameDict[2],), + 3: (TTLocalizer.FishTrophyNameDict[3],), + 4: (TTLocalizer.FishTrophyNameDict[4],), + 5: (TTLocalizer.FishTrophyNameDict[5],), + 6: (TTLocalizer.FishTrophyNameDict[6],)} +WEIGHT_MIN_INDEX = 0 +WEIGHT_MAX_INDEX = 1 +RARITY_INDEX = 2 +ZONE_LIST_INDEX = 3 +Anywhere = 1 +TTG = ToontownGlobals +__fishDict = {0: ((1, + 3, + 1, + (Anywhere,)), + (1, + 1, + 4, + (TTG.ToontownCentral, Anywhere)), + (3, + 5, + 5, + (TTG.PunchlinePlace, TTG.TheBrrrgh)), + (3, + 5, + 3, + (TTG.SillyStreet, TTG.DaisyGardens)), + (1, + 5, + 2, + (TTG.LoopyLane, TTG.ToontownCentral))), + 2: ((2, + 6, + 1, + (TTG.DaisyGardens, Anywhere)), + (2, + 6, + 9, + (TTG.ElmStreet, TTG.DaisyGardens)), + (5, + 11, + 4, + (TTG.LullabyLane,)), + (2, + 6, + 3, + (TTG.DaisyGardens, TTG.MyEstate)), + (5, + 11, + 2, + (TTG.DonaldsDreamland, TTG.MyEstate))), + 4: ((2, + 8, + 1, + (TTG.ToontownCentral, Anywhere)), + (2, + 8, + 4, + (TTG.ToontownCentral, Anywhere)), + (2, + 8, + 2, + (TTG.ToontownCentral, Anywhere)), + (2, + 8, + 6, + (TTG.ToontownCentral, TTG.MinniesMelodyland))), + 6: ((8, + 12, + 1, + (TTG.TheBrrrgh,)),), + 8: ((1, + 5, + 1, + (Anywhere,)), + (2, + 6, + 2, + (TTG.MinniesMelodyland, Anywhere)), + (5, + 10, + 5, + (TTG.MinniesMelodyland, Anywhere)), + (1, + 5, + 7, + (TTG.MyEstate, Anywhere)), + (1, + 5, + 10, + (TTG.MyEstate, Anywhere))), + 10: ((6, + 10, + 9, + (TTG.MyEstate, Anywhere)),), + 12: ((7, + 15, + 1, + (TTG.DonaldsDock, Anywhere)), + (18, + 20, + 6, + (TTG.DonaldsDock, TTG.MyEstate)), + (1, + 5, + 5, + (TTG.DonaldsDock, TTG.MyEstate)), + (3, + 7, + 4, + (TTG.DonaldsDock, TTG.MyEstate)), + (1, + 2, + 2, + (TTG.DonaldsDock, Anywhere))), + 14: ((2, + 6, + 1, + (TTG.DaisyGardens, TTG.MyEstate, Anywhere)), (2, + 6, + 3, + (TTG.DaisyGardens, TTG.MyEstate))), + 16: ((4, + 12, + 5, + (TTG.MinniesMelodyland, Anywhere)), (4, + 12, + 7, + (TTG.BaritoneBoulevard, TTG.MinniesMelodyland)), (4, + 12, + 8, + (TTG.TenorTerrace, TTG.MinniesMelodyland))), + 18: ((2, + 4, + 3, + (TTG.DonaldsDock, Anywhere)), (5, + 8, + 7, + (TTG.TheBrrrgh,)), (4, + 6, + 8, + (TTG.LighthouseLane,))), + 20: ((4, + 6, + 1, + (TTG.DonaldsDreamland,)), + (14, + 18, + 10, + (TTG.DonaldsDreamland,)), + (6, + 10, + 8, + (TTG.LullabyLane,)), + (1, + 1, + 3, + (TTG.DonaldsDreamland,)), + (2, + 6, + 6, + (TTG.LullabyLane,)), + (10, + 14, + 4, + (TTG.DonaldsDreamland, TTG.DaisyGardens))), + 22: ((12, + 16, + 2, + (TTG.MyEstate, TTG.DaisyGardens, Anywhere)), + (14, + 18, + 3, + (TTG.MyEstate, TTG.DaisyGardens, Anywhere)), + (14, + 20, + 5, + (TTG.MyEstate, TTG.DaisyGardens)), + (14, + 20, + 7, + (TTG.MyEstate, TTG.DaisyGardens))), + 24: ((9, + 11, + 3, + (Anywhere,)), + (8, + 12, + 5, + (TTG.DaisyGardens, TTG.DonaldsDock)), + (8, + 12, + 6, + (TTG.DaisyGardens, TTG.DonaldsDock)), + (8, + 16, + 7, + (TTG.DaisyGardens, TTG.DonaldsDock))), + 26: ((10, + 18, + 2, + (TTG.TheBrrrgh,)), + (10, + 18, + 3, + (TTG.TheBrrrgh,)), + (10, + 18, + 4, + (TTG.TheBrrrgh,)), + (10, + 18, + 5, + (TTG.TheBrrrgh,)), + (12, + 20, + 6, + (TTG.TheBrrrgh,)), + (14, + 20, + 7, + (TTG.TheBrrrgh,)), + (14, + 20, + 8, + (TTG.SleetStreet, TTG.TheBrrrgh)), + (16, + 20, + 10, + (TTG.WalrusWay, TTG.TheBrrrgh))), + 28: ((2, + 10, + 2, + (TTG.DonaldsDock, Anywhere)), (4, + 10, + 6, + (TTG.BarnacleBoulevard, TTG.DonaldsDock)), (4, + 10, + 7, + (TTG.SeaweedStreet, TTG.DonaldsDock))), + 30: ((13, + 17, + 5, + (TTG.MinniesMelodyland, Anywhere)), + (16, + 20, + 10, + (TTG.AltoAvenue, TTG.MinniesMelodyland)), + (12, + 18, + 9, + (TTG.TenorTerrace, TTG.MinniesMelodyland)), + (12, + 18, + 6, + (TTG.MinniesMelodyland,)), + (12, + 18, + 7, + (TTG.MinniesMelodyland,))), + 32: ((1, + 5, + 2, + (TTG.ToontownCentral, TTG.MyEstate, Anywhere)), + (1, + 5, + 3, + (TTG.TheBrrrgh, TTG.MyEstate, Anywhere)), + (1, + 5, + 4, + (TTG.DaisyGardens, TTG.MyEstate)), + (1, + 5, + 5, + (TTG.DonaldsDreamland, TTG.MyEstate)), + (1, + 5, + 10, + (TTG.TheBrrrgh, TTG.DonaldsDreamland))), + 34: ((1, + 20, + 10, + (TTG.DonaldsDreamland, Anywhere)),)} + +def getSpecies(genus): + return __fishDict[genus] + + +def getGenera(): + return __fishDict.keys() + + +ROD_WEIGHT_MIN_INDEX = 0 +ROD_WEIGHT_MAX_INDEX = 1 +ROD_CAST_COST_INDEX = 2 +__rodDict = {0: (0, 4, 1), + 1: (0, 8, 2), + 2: (0, 12, 3), + 3: (0, 16, 4), + 4: (0, 20, 5)} + +def getNumRods(): + return len(__rodDict) + + +def getCastCost(rodId): + return __rodDict[rodId][ROD_CAST_COST_INDEX] + + +def getEffectiveRarity(rarity, offset): + return min(MAX_RARITY, rarity + offset) + + +def canBeCaughtByRod(genus, species, rodIndex): + minFishWeight, maxFishWeight = getWeightRange(genus, species) + minRodWeight, maxRodWeight = getRodWeightRange(rodIndex) + if minRodWeight <= maxFishWeight and maxRodWeight >= minFishWeight: + return 1 + else: + return 0 + + +def getRodWeightRange(rodIndex): + rodProps = __rodDict[rodIndex] + return (rodProps[ROD_WEIGHT_MIN_INDEX], rodProps[ROD_WEIGHT_MAX_INDEX]) + + +def __rollRarityDice(rodId, rNumGen): + if rNumGen is None: + diceRoll = random.random() + else: + diceRoll = rNumGen.random() + exp = RodRarityFactor[rodId] + rarity = int(ceil(10 * (1 - pow(diceRoll, exp)))) + if rarity <= 0: + rarity = 1 + return rarity + + +def getRandomWeight(genus, species, rodIndex = None, rNumGen = None): + minFishWeight, maxFishWeight = getWeightRange(genus, species) + if rodIndex is None: + minWeight = minFishWeight + maxWeight = maxFishWeight + else: + minRodWeight, maxRodWeight = getRodWeightRange(rodIndex) + minWeight = max(minFishWeight, minRodWeight) + maxWeight = min(maxFishWeight, maxRodWeight) + if rNumGen is None: + randNumA = random.random() + randNumB = random.random() + else: + randNumA = rNumGen.random() + randNumB = rNumGen.random() + randNum = (randNumA + randNumB) / 2.0 + randWeight = minWeight + (maxWeight - minWeight) * randNum + return int(round(randWeight * 16)) + + +def getRandomFishVitals(zoneId, rodId, rNumGen = None): + rarity = __rollRarityDice(rodId, rNumGen) + rodDict = __pondInfoDict.get(zoneId) + rarityDict = rodDict.get(rodId) + fishList = rarityDict.get(rarity) + if fishList: + if rNumGen is None: + genus, species = random.choice(fishList) + else: + genus, species = rNumGen.choice(fishList) + weight = getRandomWeight(genus, species, rodId, rNumGen) + return (1, + genus, + species, + weight) + else: + return (0, 0, 0, 0) + return + + +def getWeightRange(genus, species): + fishInfo = __fishDict[genus][species] + return (fishInfo[WEIGHT_MIN_INDEX], fishInfo[WEIGHT_MAX_INDEX]) + + +def getRarity(genus, species): + return __fishDict[genus][species][RARITY_INDEX] + + +def getValue(genus, species, weight): + rarity = getRarity(genus, species) + rarityValue = pow(RARITY_VALUE_SCALE * rarity, 1.5) + weightValue = pow(WEIGHT_VALUE_SCALE * weight, 1.1) + value = OVERALL_VALUE_SCALE * (rarityValue + weightValue) + finalValue = int(ceil(value)) + base = getBase() + if hasattr(base, 'cr') and base.cr: + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY in holidayIds or ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY_MONTH in holidayIds: + finalValue *= JellybeanFishingHolidayScoreMultiplier + elif ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY in simbase.air.holidayManager.currentHolidays or ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY_MONTH in simbase.air.holidayManager.currentHolidays: + finalValue *= JellybeanFishingHolidayScoreMultiplier + return finalValue + + +__totalNumFish = 0 +__emptyRodDict = {} +for rodIndex in __rodDict: + __emptyRodDict[rodIndex] = {} + +__anywhereDict = copy.deepcopy(__emptyRodDict) +__pondInfoDict = {} +for genus, speciesList in __fishDict.items(): + for species in range(len(speciesList)): + __totalNumFish += 1 + speciesDesc = speciesList[species] + rarity = speciesDesc[RARITY_INDEX] + zoneList = speciesDesc[ZONE_LIST_INDEX] + for zoneIndex in range(len(zoneList)): + zone = zoneList[zoneIndex] + effectiveRarity = getEffectiveRarity(rarity, zoneIndex) + if zone == Anywhere: + for rodIndex, rarityDict in __anywhereDict.items(): + if canBeCaughtByRod(genus, species, rodIndex): + fishList = rarityDict.setdefault(effectiveRarity, []) + fishList.append((genus, species)) + + else: + pondZones = [zone] + subZones = ToontownGlobals.HoodHierarchy.get(zone) + if subZones: + pondZones.extend(subZones) + for pondZone in pondZones: + if __pondInfoDict.has_key(pondZone): + rodDict = __pondInfoDict[pondZone] + else: + rodDict = copy.deepcopy(__emptyRodDict) + __pondInfoDict[pondZone] = rodDict + for rodIndex, rarityDict in rodDict.items(): + if canBeCaughtByRod(genus, species, rodIndex): + fishList = rarityDict.setdefault(effectiveRarity, []) + fishList.append((genus, species)) + +for zone, rodDict in __pondInfoDict.items(): + for rodIndex, anywhereRarityDict in __anywhereDict.items(): + for rarity, anywhereFishList in anywhereRarityDict.items(): + rarityDict = rodDict[rodIndex] + fishList = rarityDict.setdefault(rarity, []) + fishList.extend(anywhereFishList) + +def getPondDict(zoneId): + print __pondInfoDict[zoneId] + + +def getTotalNumFish(): + return __totalNumFish + + +def testRarity(rodId = 0, numIter = 100000): + d = {1: 0, + 2: 0, + 3: 0, + 4: 0, + 5: 0, + 6: 0, + 7: 0, + 8: 0, + 9: 0, + 10: 0} + for i in range(numIter): + v = __rollRarityDice(rodId) + d[v] += 1 + + for rarity, count in d.items(): + percentage = count / float(numIter) * 100 + d[rarity] = percentage + + print d + + +def getRandomFish(): + genus = random.choice(__fishDict.keys()) + species = random.randint(0, len(__fishDict[genus]) - 1) + return (genus, species) + + +def getPondInfo(): + return __pondInfoDict + + +def getSimplePondInfo(): + info = {} + for pondId, pondInfo in __pondInfoDict.items(): + pondFishList = [] + for rodId, rodInfo in pondInfo.items(): + for rarity, fishList in rodInfo.items(): + for fish in fishList: + if fish not in pondFishList: + pondFishList.append(fish) + + pondFishList.sort() + info[pondId] = pondFishList + + return info + + +def getPondGeneraList(pondId): + tmpList = [] + generaList = [] + pondInfo = getSimplePondInfo() + for fish in pondInfo[pondId]: + if fish[0] not in tmpList: + tmpList.append(fish[0]) + generaList.append(fish) + + return generaList + + +def printNumGeneraPerPond(): + pondInfo = getSimplePondInfo() + for pondId, fishList in pondInfo.items(): + generaList = [] + for fish in fishList: + if fish[0] not in generaList: + generaList.append(fish[0]) + + print 'Pond %s has %s Genera' % (pondId, len(generaList)) + + +def generateFishingReport(numCasts = 10000, hitRate = 0.8): + totalPondMoney = {} + totalRodMoney = {} + totalPondBaitCost = {} + for pond in __pondInfoDict: + totalPondMoney[pond] = 0 + totalPondBaitCost[pond] = 0 + for rod in range(MaxRodId + 1): + totalRodMoney.setdefault(rod, 0) + baitCost = getCastCost(rod) + for cast in range(numCasts): + totalPondBaitCost[pond] += baitCost + if random.random() > hitRate: + continue + rand = random.random() * 100.0 + for cutoff in SortedProbabilityCutoffs: + if rand <= cutoff: + itemType = ProbabilityDict[cutoff] + break + + if itemType == FishItem: + success, genus, species, weight = getRandomFishVitals(pond, rod) + if success: + value = getValue(genus, species, weight) + totalPondMoney[pond] += value + totalRodMoney[rod] += value + elif itemType == JellybeanItem: + value = Rod2JellybeanDict[rod] + totalPondMoney[pond] += value + totalRodMoney[rod] += value + + numPonds = len(totalPondMoney) + for pond, money in totalPondMoney.items(): + baitCost = 0 + for rod in range(MaxRodId + 1): + baitCost += getCastCost(rod) + + totalCastCost = baitCost * numCasts + print ('pond: %s totalMoney: %s profit: %s perCast: %s' % (pond, + money, + money - totalCastCost, + (money - totalCastCost) / float(numCasts * (MaxRodId + 1))),) + + for rod, money in totalRodMoney.items(): + baitCost = getCastCost(rod) + totalCastCost = baitCost * (numCasts * numPonds) + print ('rod: %s totalMoney: %s castCost: %s profit: %s perCast: %s' % (rod, + money, + totalCastCost, + money - totalCastCost, + (money - totalCastCost) / float(numCasts * numPonds)),) diff --git a/toontown/fishing/FishPanel.py b/toontown/fishing/FishPanel.py new file mode 100644 index 0000000..c2a31a1 --- /dev/null +++ b/toontown/fishing/FishPanel.py @@ -0,0 +1,112 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * +import FishGlobals +import FishPhoto + +class FishPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPanel') + + def __init__(self, fish = None, parent = aspect2d, doneEvent = None, **kw): + optiondefs = (('relief', None, None), + ('state', DGG.DISABLED, None), + ('image', DGG.getDefaultDialogGeom(), None), + ('image_color', ToontownGlobals.GlobalDialogColor, None), + ('image_scale', (0.65, 1, 0.85), None), + ('text', '', None), + ('text_scale', 0.06, None), + ('text_fg', (0, 0, 0, 1), None), + ('text_pos', (0, 0.35, 0), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.initialiseoptions(FishPanel) + self.doneEvent = doneEvent + self.fish = fish + self.parent = parent + self.photo = None + return + + def destroy(self): + if self.photo: + self.photo.destroy() + self.photo = None + self.fish = None + DirectFrame.destroy(self) + self.parent = None + return + + def load(self): + self.weight = DirectLabel(parent=self, pos=(0, 0, -0.28), relief=None, state=DGG.NORMAL, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, 0.0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.value = DirectLabel(parent=self, pos=(0, 0, -0.35), relief=None, state=DGG.NORMAL, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.mystery = DirectLabel(parent=self, pos=(-0.025, 0, -0.055), relief=None, state=DGG.NORMAL, text='?', text_scale=0.25, text_fg=(0, 0, 0, 1), text_pos=(0, 0, 0), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=10.5) + self.extraLabel = DirectLabel(parent=self, relief=None, state=DGG.NORMAL, text='', text_fg=(0.2, 0.8, 0.4, 1), text_font=ToontownGlobals.getSignFont(), text_scale=0.08, pos=(0, 0, 0.26)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.cancel = DirectButton(parent=self, pos=(0.275, 0, -0.375), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(0.6, 1, 0.6), command=self.handleCancel) + buttons.removeNode() + self.photo = FishPhoto.FishPhoto(parent=self) + self.update(self.fish) + return + + def update(self, fish): + self.fish = fish + if self.fish == None: + return + self['text'] = self.fish.getSpeciesName() + weight = self.fish.getWeight() + conv = TTLocalizer.FishPageWeightConversion + large = weight / conv + if large == 1: + largeStr = TTLocalizer.FishPageWeightLargeS % large + else: + largeStr = TTLocalizer.FishPageWeightLargeP % large + small = weight % conv + if small == 1: + smallStr = TTLocalizer.FishPageWeightSmallS % small + else: + smallStr = TTLocalizer.FishPageWeightSmallP % small + self.weight['text'] = TTLocalizer.FishPageWeightStr + largeStr + smallStr + value = self.fish.getValue() + if value == 1: + self.value['text'] = TTLocalizer.FishPageValueS % value + else: + self.value['text'] = TTLocalizer.FishPageValueP % value + self.photo.update(fish) + return + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def handleCancel(self): + self.hide() + if self.doneEvent: + messenger.send(self.doneEvent) + + def show(self, code = FishGlobals.FishItem): + messenger.send('wakeup') + apply(self.photo.setSwimBounds, self.swimBounds) + apply(self.photo.setSwimColor, self.swimColor) + if code == FishGlobals.FishItem: + self.extraLabel.hide() + elif code == FishGlobals.FishItemNewEntry: + self.extraLabel.show() + self.extraLabel['text'] = TTLocalizer.FishingNewEntry + self.extraLabel['text_scale'] = TTLocalizer.FPnewEntry + self.extraLabel.setPos(0, 0, 0.26) + elif code == FishGlobals.FishItemNewRecord: + self.extraLabel.show() + self.extraLabel['text'] = TTLocalizer.FishingNewRecord + self.extraLabel['text_scale'] = TTLocalizer.FPnewRecord + self.photo.show() + DirectFrame.show(self) + + def hide(self): + self.photo.hide() + DirectFrame.hide(self) diff --git a/toontown/fishing/FishPhoto.py b/toontown/fishing/FishPhoto.py new file mode 100644 index 0000000..56bf11b --- /dev/null +++ b/toontown/fishing/FishPhoto.py @@ -0,0 +1,183 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import FishGlobals + +class DirectRegion(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('DirectRegion') + + def __init__(self, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('DirectRegion')) + + def destroy(self): + self.unload() + + def setBounds(self, *bounds): + self.bounds = bounds + + def setColor(self, *colors): + self.color = colors + + def show(self): + pass + + def hide(self): + pass + + def load(self): + if not hasattr(self, 'cRender'): + self.cRender = NodePath('fishSwimRender') + self.fishSwimCamera = self.cRender.attachNewNode('fishSwimCamera') + self.cCamNode = Camera('fishSwimCam') + 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.fishSwimCam = self.fishSwimCamera.attachNewNode(self.cCamNode) + cm = CardMaker('displayRegionCard') + apply(cm.setFrame, self.bounds) + self.card = card = self.attachNewNode(cm.generate()) + apply(card.setColor, self.color) + newBounds = card.getTightBounds() + ll = render2d.getRelativePoint(card, newBounds[0]) + ur = render2d.getRelativePoint(card, newBounds[1]) + newBounds = [ll.getX(), + ur.getX(), + ll.getZ(), + ur.getZ()] + newBounds = map(lambda x: max(0.0, min(1.0, (x + 1.0) / 2.0)), newBounds) + self.cDr = base.win.makeDisplayRegion(*newBounds) + self.cDr.setSort(10) + self.cDr.setClearColor(card.getColor()) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setCamera(self.fishSwimCam) + return self.cRender + + def unload(self): + if hasattr(self, 'cRender'): + base.win.removeDisplayRegion(self.cDr) + del self.cRender + del self.fishSwimCamera + del self.cCamNode + del self.cLens + del self.fishSwimCam + del self.cDr + + +class FishPhoto(NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPhoto') + + def __init__(self, fish = None, parent = aspect2d): + NodePath.__init__(self) + self.assign(parent.attachNewNode('FishPhoto')) + self.fish = fish + self.actor = None + self.sound = None + self.soundTrack = None + self.track = None + self.fishFrame = None + return + + def destroy(self): + self.hide() + if hasattr(self, 'background'): + del self.background + self.fish = None + del self.soundTrack + del self.track + return + + def update(self, fish): + self.fish = fish + + def setSwimBounds(self, *bounds): + self.swimBounds = bounds + + def setSwimColor(self, *colors): + self.swimColor = colors + + def load(self): + pass + + def makeFishFrame(self, actor): + actor.setDepthTest(1) + actor.setDepthWrite(1) + if not hasattr(self, 'fishDisplayRegion'): + self.fishDisplayRegion = DirectRegion(parent=self) + apply(self.fishDisplayRegion.setBounds, self.swimBounds) + apply(self.fishDisplayRegion.setColor, self.swimColor) + frame = self.fishDisplayRegion.load() + pitch = frame.attachNewNode('pitch') + rotate = pitch.attachNewNode('rotate') + scale = rotate.attachNewNode('scale') + actor.reparentTo(scale) + bMin, bMax = actor.getTightBounds() + center = (bMin + bMax) / 2.0 + actor.setPos(-center[0], -center[1], -center[2]) + genus = self.fish.getGenus() + fishInfo = FishGlobals.FishFileDict.get(genus, FishGlobals.FishFileDict[-1]) + fishPos = fishInfo[5] + if fishPos: + actor.setPos(fishPos[0], fishPos[1], fishPos[2]) + scale.setScale(fishInfo[6]) + rotate.setH(fishInfo[7]) + pitch.setP(fishInfo[8]) + pitch.setY(2) + return frame + + def show(self, showBackground = 0): + messenger.send('wakeup') + if self.fishFrame: + self.actor.cleanup() + if hasattr(self, 'fishDisplayRegion'): + self.fishDisplayRegion.unload() + self.hide() + self.actor = self.fish.getActor() + self.actor.setTwoSided(1) + self.fishFrame = self.makeFishFrame(self.actor) + if showBackground: + if not hasattr(self, 'background'): + background = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + background = background.find('**/Fish_BG') + self.background = background + self.background.setPos(0, 15, 0) + self.background.setScale(11) + self.background.reparentTo(self.fishFrame) + self.sound, loop, delay, playRate = self.fish.getSound() + if playRate is not None: + self.actor.setPlayRate(playRate, 'intro') + self.actor.setPlayRate(playRate, 'swim') + introDuration = self.actor.getDuration('intro') + track = Parallel(Sequence(Func(self.actor.play, 'intro'), Wait(introDuration), Func(self.actor.loop, 'swim'))) + if self.sound: + soundTrack = Sequence(Wait(delay), Func(self.sound.play)) + if loop: + duration = max(introDuration, self.sound.length()) + soundTrack.append(Wait(duration - delay)) + track.append(Func(soundTrack.loop)) + self.soundTrack = soundTrack + else: + track.append(soundTrack) + self.track = track + self.track.start() + return + + def hide(self): + if hasattr(self, 'fishDisplayRegion'): + self.fishDisplayRegion.unload() + if self.actor: + self.actor.stop() + if self.sound: + self.sound.stop() + self.sound = None + if self.soundTrack: + self.soundTrack.pause() + self.soundTrack = None + if self.track: + self.track.pause() + self.track = None + return diff --git a/toontown/fishing/FishPicker.py b/toontown/fishing/FishPicker.py new file mode 100644 index 0000000..ae1a3e5 --- /dev/null +++ b/toontown/fishing/FishPicker.py @@ -0,0 +1,120 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import FishPanel + +class FishPicker(DirectScrolledList): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPicker') + + def __init__(self, parent = aspect2d, **kw): + self.fishList = [] + self.parent = parent + self.shown = 0 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('incButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('incButton_relief', None, None), + ('incButton_scale', (1.6, 1.6, -1.6), None), + ('incButton_pos', (0.16, 0, -0.47), None), + ('incButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('decButton_image', (gui.find('**/FndsLst_ScrollUp'), + gui.find('**/FndsLst_ScrollDN'), + gui.find('**/FndsLst_ScrollUp_Rllvr'), + gui.find('**/FndsLst_ScrollUp')), None), + ('decButton_relief', None, None), + ('decButton_scale', (1.6, 1.6, 1.6), None), + ('decButton_pos', (0.16, 0, 0.09), None), + ('decButton_image3_color', Vec4(0.7, 0.7, 0.7, 0.75), None), + ('itemFrame_pos', (-0.025, 0, 0), None), + ('itemFrame_scale', 0.54, None), + ('itemFrame_relief', None, None), + ('itemFrame_frameSize', (-0.05, + 0.75, + -0.75, + 0.05), None), + ('numItemsVisible', 10, None), + ('items', [], None)) + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.initialiseoptions(FishPicker) + self.fishGui = loader.loadModel('phase_3.5/models/gui/fishingBook').find('**/bucket') + self.fishGui.find('**/fram1').removeNode() + self.fishGui.find('**/bubble').removeNode() + self.fishGui.reparentTo(self, -1) + self.fishGui.setPos(0.63, 0.1, -0.1) + self.fishGui.setScale(0.035) + self.info = DirectLabel(parent=self, relief=None, text='', text_scale=0.055, pos=(0.18, 0, -0.67)) + self.fishPanel = FishPanel.FishPanel(parent=self) + self.fishPanel.setSwimBounds(-0.3, 0.3, -0.235, 0.25) + self.fishPanel.setSwimColor(1.0, 1.0, 0.74901, 1.0) + gui.removeNode() + return None + + def destroy(self): + DirectScrolledList.destroy(self) + self.parent = None + self.fishList = [] + self.fishPanel = None + return + + def hideFishPanel(self): + self.fishPanel.hide() + + def hide(self): + if not hasattr(self, 'loaded'): + return + self.hideFishPanel() + DirectScrolledList.hide(self) + self.shown = 0 + + def show(self): + if not hasattr(self, 'loaded'): + self.load() + self.updatePanel() + DirectScrolledList.show(self) + self.shown = 1 + + def load(self): + self.loaded = 1 + self.fishPanel.load() + self.fishPanel.setPos(1.05, 0, 0.1) + self.fishPanel.setScale(0.9) + + def update(self, newFishes): + for fish, fishButton in self.fishList[:]: + self.removeItem(fishButton) + fishButton.destroy() + self.fishList.remove([fish, fishButton]) + + for fish in newFishes: + fishButton = self.makeFishButton(fish) + self.addItem(fishButton) + self.fishList.append([fish, fishButton]) + + value = 0 + for fish in newFishes: + value += fish.getValue() + + maxFish = base.localAvatar.getMaxFishTank() + self.info['text'] = TTLocalizer.FishPickerTotalValue % (len(newFishes), maxFish, value) + if self.shown: + self.updatePanel() + + def updatePanel(self): + if len(self.fishList) >= 1: + self.showFishPanel(self.fishList[0][0]) + else: + self.hideFishPanel() + + def makeFishButton(self, fish): + return DirectScrolledListItem(parent=self, relief=None, text=fish.getSpeciesName(), text_scale=0.07, text_align=TextNode.ALeft, text1_fg=Vec4(1, 1, 0, 1), text2_fg=Vec4(0.5, 0.9, 1, 1), text3_fg=Vec4(0.4, 0.8, 0.4, 1), command=self.showFishPanel, extraArgs=[fish]) + + def showFishPanel(self, fish): + self.fishPanel.update(fish) + self.fishPanel.show() diff --git a/toontown/fishing/FishSellGUI.py b/toontown/fishing/FishSellGUI.py new file mode 100644 index 0000000..4cc538f --- /dev/null +++ b/toontown/fishing/FishSellGUI.py @@ -0,0 +1,57 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.task import Task +import FishBase +import FishPicker + +class FishSellGUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FishGui') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None, state='normal', geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(2.0, 1, 1.5), frameSize=(-1, 1, -1, 1), pos=(0, 0, 0), text='', text_wordwrap=26, text_scale=0.06, text_pos=(0, 0.65)) + self.initialiseoptions(FishSellGUI) + self.doneEvent = doneEvent + self.picker = FishPicker.FishPicker(self) + self.picker.load() + self.picker.setPos(-0.59, 0, 0.03) + self.picker.setScale(0.93) + newTankFish = base.localAvatar.fishTank.getFish() + self.picker.update(newTankFish) + self.picker.show() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + 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')) + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, pos=(0.3, 0, -0.58), text=TTLocalizer.FishGuiCancel, text_scale=TTLocalizer.FSGUIcancelButton, text_pos=(0, -0.1), command=self.__cancel) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.6, 0, -0.58), text=TTLocalizer.FishGuiOk, text_scale=TTLocalizer.FSGUIokButton, text_pos=(0, -0.1), command=self.__sellFish) + self.rewardDoubledJellybeanLabel = DirectLabel(text='', text_fg=(1.0, 0.125, 0.125, 1.0), relief=None, pos=(0.45, 0, -0.48), scale=0.07) + buttons.removeNode() + self.__updateFishValue() + return + + def destroy(self): + DirectFrame.destroy(self) + self.rewardDoubledJellybeanLabel.removeNode() + del self.rewardDoubledJellybeanLabel + + def __cancel(self): + messenger.send(self.doneEvent, [0]) + + def __sellFish(self): + messenger.send(self.doneEvent, [1]) + + def __updateFishValue(self): + doubledJellybean = '' + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_FISHING_HOLIDAY_MONTH): + doubledJellybean = TTLocalizer.PartyRewardDoubledJellybean + self.rewardDoubledJellybeanLabel['text'] = doubledJellybean + self.rewardDoubledJellybeanLabel.setText() + fishTank = base.localAvatar.getFishTank() + num = len(fishTank) + value = fishTank.getTotalValue() + self['text'] = TTLocalizer.FishTankValue % {'name': base.localAvatar.getName(), + 'num': num, + 'value': value} + self.setText() diff --git a/toontown/fishing/FishTank.py b/toontown/fishing/FishTank.py new file mode 100644 index 0000000..3bb2e1e --- /dev/null +++ b/toontown/fishing/FishTank.py @@ -0,0 +1,82 @@ +import FishBase +import FishGlobals + +class FishTank: + + def __init__(self): + self.fishList = [] + + def __len__(self): + return len(self.fishList) + + def getFish(self): + return self.fishList + + def makeFromNetLists(self, genusList, speciesList, weightList): + self.fishList = [] + for genus, species, weight in zip(genusList, speciesList, weightList): + self.fishList.append(FishBase.FishBase(genus, species, weight)) + + def getNetLists(self): + genusList = [] + speciesList = [] + weightList = [] + for fish in self.fishList: + genusList.append(fish.getGenus()) + speciesList.append(fish.getSpecies()) + weightList.append(fish.getWeight()) + + return [genusList, speciesList, weightList] + + def hasFish(self, genus, species): + for fish in self.fishList: + if fish.getGenus() == genus and fish.getSpecies() == species: + return 1 + + return 0 + + def hasBiggerFish(self, genus, species, weight): + for fish in self.fishList: + if fish.getGenus() == genus and fish.getSpecies() == species and fish.getWeight() >= weight: + return 1 + + return 0 + + def addFish(self, fish): + self.fishList.append(fish) + return 1 + + def removeFishAtIndex(self, index): + if index >= len(self.fishList): + return 0 + else: + del self.fishList[i] + return 1 + + def generateRandomTank(self): + import random + numFish = random.randint(1, 20) + self.fishList = [] + for i in range(numFish): + genus, species = FishGlobals.getRandomFish() + weight = FishGlobals.getRandomWeight(genus, species) + fish = FishBase.FishBase(genus, species, weight) + self.addFish(fish) + + def getTotalValue(self): + value = 0 + for fish in self.fishList: + value += fish.getValue() + + return value + + def __str__(self): + numFish = len(self.fishList) + value = 0 + txt = 'Fish Tank (%s fish):' % numFish + for fish in self.fishList: + txt += '\n' + str(fish) + value += fish.getValue() + + txt += '\nTotal value: %s' % value + return txt diff --git a/toontown/fishing/FishingTargetGlobals.py b/toontown/fishing/FishingTargetGlobals.py new file mode 100644 index 0000000..01c8a70 --- /dev/null +++ b/toontown/fishing/FishingTargetGlobals.py @@ -0,0 +1,185 @@ +from toontown.toonbase import ToontownGlobals +OFF = 0 +MOVING = 1 +StepTime = 5.0 +MinimumHunger = 1.0 +NUM_TARGETS_INDEX = 0 +POS_START_INDEX = 1 +POS_END_INDEX = 4 +RADIUS_INDEX = 4 +WATER_LEVEL_INDEX = 5 +__targetInfoDict = {ToontownGlobals.ToontownCentral: (2, + -81, + 31, + -4.8, + 14, + -1.4), + ToontownGlobals.SillyStreet: (2, + 20, + -664, + -1.4, + 14, + -1.4 - 0.438), + ToontownGlobals.LoopyLane: (2, + -234, + 175, + -1.4, + 14, + -1.4 - 0.462), + ToontownGlobals.PunchlinePlace: (2, + 529, + -70, + -1.4, + 13, + -1.4 - 0.486), + ToontownGlobals.DonaldsDock: (2, + -17, + 130, + 1.73, + 15, + 1.73 - 3.615), + ToontownGlobals.BarnacleBoulevard: (2, + 381, + -350, + -2, + 14, + -2 - 0.482), + ToontownGlobals.SeaweedStreet: (2, + -395, + -226, + -2, + 14, + -2 - 0.482), + ToontownGlobals.LighthouseLane: (2, + 350, + 100, + -2, + 14, + -2 - 0.482), + ToontownGlobals.DaisyGardens: (2, + 50, + 47, + -1.48, + 13, + -1.48 - 0.345), + ToontownGlobals.ElmStreet: (2, + 149, + 44, + -1.43, + 13, + -1.43 - 0.618), + ToontownGlobals.MapleStreet: (2, + 176, + 100, + -1.43, + 13, + -1.43 - 0.618), + ToontownGlobals.OakStreet: (2, + 134, + -70.5, + -1.5, + 13, + -1.5 - 0.377), + ToontownGlobals.MinniesMelodyland: (2, + -0.2, + -20.2, + -14.65, + 14, + -14.65 - -12), + ToontownGlobals.AltoAvenue: (2, + -580, + -90, + -0.87, + 14, + -0.87 - 1.844), + ToontownGlobals.BaritoneBoulevard: (2, + -214, + 250, + -0.87, + 14, + -0.87 - 1.844), + ToontownGlobals.TenorTerrace: (2, + 715, + -15, + -0.87, + 14, + -0.87 - 1.844), + ToontownGlobals.TheBrrrgh: (2, + -58, + -26, + 1.7, + 10, + -0.8), + ToontownGlobals.WalrusWay: (2, + 460, + 29, + -2, + 13, + -2 - 0.4), + ToontownGlobals.SleetStreet: (2, + 340, + 480, + -2, + 13, + -2 - 0.4), + ToontownGlobals.PolarPlace: (2, + 45.5, + 90.86, + -2, + 13, + -2 - 0.4), + ToontownGlobals.DonaldsDreamland: (2, + 159, + 0.2, + -17.1, + 14, + -17.1 - -14.6), + ToontownGlobals.LullabyLane: (2, + 118, + -185, + -2.1, + 14, + -2.1 - 0.378), + ToontownGlobals.PajamaPlace: (2, + 241, + -348, + -2.1, + 14, + -2.1 - 0.378), + ToontownGlobals.MyEstate: (3, + 30, + -126, + -0.3, + 16, + -0.83)} + +def getNumTargets(zone): + info = __targetInfoDict.get(zone) + if info: + return info[NUM_TARGETS_INDEX] + else: + return 2 + + +def getTargetCenter(zone): + info = __targetInfoDict.get(zone) + if info: + return info[POS_START_INDEX:POS_END_INDEX] + else: + return (0, 0, 0) + + +def getTargetRadius(zone): + info = __targetInfoDict.get(zone) + if info: + return info[RADIUS_INDEX] + else: + return 10 + + +def getWaterLevel(zone): + info = __targetInfoDict.get(zone) + if info: + return info[WATER_LEVEL_INDEX] + else: + return 0 diff --git a/toontown/fishing/FourCornerBingo.py b/toontown/fishing/FourCornerBingo.py new file mode 100644 index 0000000..1c162e9 --- /dev/null +++ b/toontown/fishing/FourCornerBingo.py @@ -0,0 +1,35 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class FourCornerBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('FourCornerBingo') + corners = [0, + BingoGlobals.CARD_ROWS - 1, + BingoGlobals.CARD_COLS * (BingoGlobals.CARD_ROWS - 1), + BingoGlobals.CARD_COLS * BingoGlobals.CARD_ROWS - 1] + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.FOURCORNER_CARD + + def checkForWin(self, id): + corners = self.corners + if self.cellCheck(corners[0]) and self.cellCheck(corners[1]) and self.cellCheck(corners[2]) and self.cellCheck(corners[3]): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + topLeft, topRight, bottomLeft, bottomRight = (0, 0, 0, 0) + if id == self.corners[0]: + topLeft = 1 + elif id == self.corners[1]: + topRight = 1 + elif id == self.corners[2]: + bottomLeft = 1 + elif id == self.corners[3]: + bottomRight = 1 + return topLeft or topRight or bottomLeft or bottomRight + + def checkForBingo(self): + return self.checkForWin(0) diff --git a/toontown/fishing/GenusPanel.py b/toontown/fishing/GenusPanel.py new file mode 100644 index 0000000..94e30d3 --- /dev/null +++ b/toontown/fishing/GenusPanel.py @@ -0,0 +1,91 @@ +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import FishBase +import FishGlobals +import FishPhoto + +class GenusPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GenusPanel') + + def __init__(self, genus = None, itemIndex = 0, *extraArgs): + fishingGui = loader.loadModel('phase_3.5/models/gui/fishingBook') + albumGui = fishingGui.find('**/photo_frame1').copyTo(hidden) + albumGui.find('**/picture_frame').reparentTo(albumGui, -1) + albumGui.find('**/arrows').removeNode() + optiondefs = (('relief', None, None), + ('state', DGG.NORMAL, None), + ('image', albumGui, None), + ('image_scale', (0.025, 0.025, 0.025), None), + ('image_pos', (0, 1, 0), None), + ('text', TTLocalizer.UnknownFish, None), + ('text_scale', 0.065, None), + ('text_fg', (0.2, 0.1, 0.0, 1), None), + ('text_pos', (-0.5, -0.34), None), + ('text_font', ToontownGlobals.getInterfaceFont(), None), + ('text_wordwrap', 13.5, None), + ('text_align', TextNode.ALeft, None)) + self.defineoptions({}, optiondefs) + DirectFrame.__init__(self) + self.initialiseoptions(GenusPanel) + self.fishPanel = None + self.genus = None + self.setGenus(int(genus)) + self.setScale(1.2) + albumGui.removeNode() + return + + def destroy(self): + if self.fishPanel: + self.fishPanel.destroy() + del self.fishPanel + DirectFrame.destroy(self) + + def load(self): + pass + + def setGenus(self, genus): + if self.genus == genus: + return + self.genus = genus + if self.genus != None: + if self.fishPanel: + self.fishPanel.destroy() + f = FishBase.FishBase(self.genus, 0, 0) + self.fishPanel = FishPhoto.FishPhoto(fish=f, parent=self) + self.fishPanel.setPos(-0.23, 1, -0.01) + self.fishPanel.setSwimBounds(-0.2461, 0.2367, -0.207, 0.2664) + self.fishPanel.setSwimColor(0.47, 1.0, 0.99, 1.0) + speciesList = FishGlobals.getSpecies(self.genus) + self.speciesLabels = [] + offset = 0.075 + startPos = len(speciesList) / 2 * offset + if not len(speciesList) % 2: + startPos -= offset / 2 + for species in range(len(speciesList)): + label = DirectLabel(parent=self, relief=None, state=DGG.NORMAL, pos=(0.06, 0, startPos - species * offset), text=TTLocalizer.UnknownFish, text_fg=(0.2, 0.1, 0.0, 1), text_scale=TTLocalizer.GPgenus, text_align=TextNode.ALeft, text_font=ToontownGlobals.getInterfaceFont()) + self.speciesLabels.append(label) + + return + + def show(self): + self.update() + DirectFrame.show(self) + + def hide(self): + if self.fishPanel is not None: + self.fishPanel.hide() + DirectFrame.hide(self) + return + + def update(self): + if base.localAvatar.fishCollection.hasGenus(self.genus) and self.fishPanel is not None: + self.fishPanel.show(showBackground=1) + self['text'] = TTLocalizer.FishGenusNames[self.genus] + for species in range(len(FishGlobals.getSpecies(self.genus))): + if base.localAvatar.fishCollection.hasFish(self.genus, species): + self.speciesLabels[species]['text'] = TTLocalizer.FishSpeciesNames[self.genus][species] + + return diff --git a/toontown/fishing/NormalBingo.py b/toontown/fishing/NormalBingo.py new file mode 100644 index 0000000..f2b7cf7 --- /dev/null +++ b/toontown/fishing/NormalBingo.py @@ -0,0 +1,37 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class NormalBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('NormalBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.NORMAL_CARD + + def checkForWin(self, id): + rowId = int(id / BingoGlobals.CARD_ROWS) + colId = id % BingoGlobals.CARD_COLS + rowResult = self.rowCheck(rowId) + colResult = self.colCheck(colId) + fDiagResult = self.fDiagCheck(id) + bDiagResult = self.bDiagCheck(id) + if rowResult or colResult or fDiagResult or bDiagResult: + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + return 1 + + def checkForBingo(self): + id = self.cardSize / 2 + if self.checkForWin(id): + return BingoGlobals.WIN + for i in xrange(BingoGlobals.CARD_ROWS): + if i != BingoGlobals.CARD_ROWS / 2: + rowResult = self.rowCheck(i) + colResult = self.colCheck(i) + if rowResult | colResult: + return BingoGlobals.WIN + + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/ThreewayBingo.py b/toontown/fishing/ThreewayBingo.py new file mode 100644 index 0000000..515f98f --- /dev/null +++ b/toontown/fishing/ThreewayBingo.py @@ -0,0 +1,35 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.fishing import BingoGlobals +from toontown.fishing import BingoCardBase + +class ThreewayBingo(BingoCardBase.BingoCardBase): + notify = DirectNotifyGlobal.directNotify.newCategory('ThreewayBingo') + + def __init__(self, cardSize = BingoGlobals.CARD_SIZE, rowSize = BingoGlobals.CARD_ROWS, colSize = BingoGlobals.CARD_COLS): + BingoCardBase.BingoCardBase.__init__(self, cardSize, rowSize, colSize) + self.gameType = BingoGlobals.THREEWAY_CARD + self.rowResult = 0 + self.fDiagResult = 0 + self.bDiagResult = 0 + + def checkForWin(self, id): + rowId = int(id / BingoGlobals.CARD_ROWS) + colId = id % BingoGlobals.CARD_COLS + if rowId == 2: + self.rowResult = self.rowCheck(rowId) + if self.fDiagCheck(id): + self.fDiagResult = 1 + if self.bDiagCheck(id): + self.bDiagResult = 1 + if self.rowResult and self.fDiagResult and self.bDiagResult: + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE + + def checkForColor(self, id): + return self.onRow(2, id) | self.onFDiag(id) | self.onBDiag(id) + + def checkForBingo(self): + id = self.cardSize / 2 + if self.checkForWin(id): + return BingoGlobals.WIN + return BingoGlobals.NO_UPDATE diff --git a/toontown/fishing/__init__.py b/toontown/fishing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/friends/FriendHandle.py b/toontown/friends/FriendHandle.py new file mode 100644 index 0000000..960306f --- /dev/null +++ b/toontown/friends/FriendHandle.py @@ -0,0 +1,124 @@ +from otp.avatar.Avatar import teleportNotify +from toontown.toonbase import ToontownGlobals +import copy +from toontown.chat import ToonChatGarbler + +class FriendHandle: + + def __init__(self, doId, name, style, petId, isAPet = False): + self.doId = doId + self.style = style + self.commonChatFlags = 0 + self.whitelistChatFlags = 0 + self.petId = petId + self.isAPet = isAPet + self.chatGarbler = ToonChatGarbler.ToonChatGarbler() + self.name = name + + def getDoId(self): + return self.doId + + def getPetId(self): + return self.petId + + def hasPet(self): + return self.getPetId() != 0 + + def isPet(self): + return self.isAPet + + def getName(self): + return self.name + + def getFont(self): + return ToontownGlobals.getToonFont() + + def getStyle(self): + return self.style + + def uniqueName(self, idString): + return idString + '-' + str(self.getDoId()) + + def d_battleSOS(self, requesterId): + base.localAvatar.sendUpdate('battleSOS', [requesterId], sendToId=self.doId) + + def d_teleportQuery(self, requesterId): + teleportNotify.debug('sending d_teleportQuery(%s)' % (requesterId,)) + base.localAvatar.sendUpdate('teleportQuery', [requesterId], sendToId=self.doId) + + def d_teleportResponse(self, avId, available, shardId, hoodId, zoneId): + teleportNotify.debug('sending teleportResponse%s' % ((avId, + available, + shardId, + hoodId, + zoneId),)) + base.localAvatar.sendUpdate('teleportResponse', [avId, + available, + shardId, + hoodId, + zoneId], sendToId=self.doId) + + def d_teleportGiveup(self, requesterId): + teleportNotify.debug('sending d_teleportGiveup(%s)' % (requesterId,)) + base.localAvatar.sendUpdate('teleportGiveup', [requesterId], sendToId=self.doId) + + def isUnderstandable(self): + if self.commonChatFlags & base.localAvatar.commonChatFlags & ToontownGlobals.CommonChat: + understandable = 1 + elif self.commonChatFlags & ToontownGlobals.SuperChat: + understandable = 1 + elif base.localAvatar.commonChatFlags & ToontownGlobals.SuperChat: + understandable = 1 + elif base.cr.getFriendFlags(self.doId) & ToontownGlobals.FriendChat: + understandable = 1 + elif self.whitelistChatFlags & base.localAvatar.whitelistChatFlags: + understandable = 1 + else: + understandable = 0 + return understandable + + def scrubTalk(self, message, mods): + scrubbed = 0 + text = copy.copy(message) + for mod in mods: + index = mod[0] + length = mod[1] - mod[0] + 1 + newText = text[0:index] + length * '\x07' + text[index + length:] + text = newText + + words = text.split(' ') + newwords = [] + for word in words: + if word == '': + newwords.append(word) + elif word[0] == '\x07': + newwords.append('\x01WLDisplay\x01' + self.chatGarbler.garbleSingle(self, word) + '\x02') + scrubbed = 1 + elif base.whiteList.isWord(word): + newwords.append(word) + else: + newwords.append('\x01WLDisplay\x01' + word + '\x02') + scrubbed = 1 + + newText = ' '.join(newwords) + return (newText, scrubbed) + + def replaceBadWords(self, text): + words = text.split(' ') + newwords = [] + for word in words: + if word == '': + newwords.append(word) + elif word[0] == '\x07': + newwords.append('\x01WLRed\x01' + self.chatGarbler.garbleSingle(self, word) + '\x02') + elif base.whiteList.isWord(word): + newwords.append(word) + else: + newwords.append('\x01WLRed\x01' + word + '\x02') + + newText = ' '.join(newwords) + return newText + + def setCommonAndWhitelistChatFlags(self, commonChatFlags, whitelistChatFlags): + self.commonChatFlags = commonChatFlags + self.whitelistChatFlags = whitelistChatFlags diff --git a/toontown/friends/FriendInvitee.py b/toontown/friends/FriendInvitee.py new file mode 100644 index 0000000..c05e3e5 --- /dev/null +++ b/toontown/friends/FriendInvitee.py @@ -0,0 +1,84 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import ToonHeadDialog +from direct.gui.DirectGui import DGG +from otp.otpbase import OTPGlobals + +class FriendInvitee(ToonHeadDialog.ToonHeadDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('FriendInvitee') + + def __init__(self, avId, avName, avDNA, context, **kw): + self.avId = avId + self.avDNA = avDNA + self.context = context + self.avName = avName + if len(base.localAvatar.friendsList) >= MaxFriends: + base.cr.friendManager.up_inviteeFriendResponse(3, self.context) + self.context = None + text = OTPLocalizer.FriendInviteeTooManyFriends % self.avName + style = TTDialog.Acknowledge + buttonTextList = [OTPLocalizer.FriendInviteeOK] + command = self.__handleOhWell + else: + text = OTPLocalizer.FriendInviteeInvitation % self.avName + style = TTDialog.TwoChoice + buttonTextList = [OTPLocalizer.FriendInviteeOK, OTPLocalizer.FriendInviteeNo] + command = self.__handleButton + optiondefs = (('dialogName', 'FriendInvitee', None), + ('text', text, None), + ('style', style, None), + ('buttonTextList', buttonTextList, None), + ('command', command, None), + ('image_color', (1.0, 0.89, 0.77, 1.0), None), + ('geom_scale', 0.2, None), + ('geom_pos', (-0.1, 0, -0.025), None), + ('pad', (0.075, 0.075), None), + ('topPad', 0, None), + ('midPad', 0, None), + ('pos', (0.45, 0, 0.75), None), + ('scale', 0.75, None)) + self.defineoptions(kw, optiondefs) + ToonHeadDialog.ToonHeadDialog.__init__(self, self.avDNA) + self.accept('cancelFriendInvitation', self.__handleCancelFromAbove) + self.initialiseoptions(FriendInvitee) + self.show() + return + + def cleanup(self): + ToonHeadDialog.ToonHeadDialog.cleanup(self) + self.ignore('cancelFriendInvitation') + if self.context != None: + base.cr.friendManager.up_inviteeFriendResponse(2, self.context) + self.context = None + if base.friendMode == 1: + base.cr.friendManager.executeGameSpecificFunction() + return + + def __handleButton(self, value): + print 'handleButton' + if value == DGG.DIALOG_OK: + if base.friendMode == 0: + base.cr.friendManager.up_inviteeFriendResponse(1, self.context) + elif base.friendMode == 1: + print 'sending Request Invite' + base.cr.avatarFriendsManager.sendRequestInvite(self.avId) + elif base.friendMode == 0: + base.cr.friendManager.up_inviteeFriendResponse(0, self.context) + elif base.friendMode == 1: + base.cr.avatarFriendsManager.sendRequestRemove(self.avId) + self.context = None + self.cleanup() + return + + def __handleOhWell(self, value): + self.cleanup() + + def __handleCancelFromAbove(self, context = None): + if context == None or context == self.context: + self.context = None + self.cleanup() + return diff --git a/toontown/friends/FriendInviter.py b/toontown/friends/FriendInviter.py new file mode 100644 index 0000000..9324645 --- /dev/null +++ b/toontown/friends/FriendInviter.py @@ -0,0 +1,548 @@ +from pandac.PandaModules import * +from direct.task.Task import Task +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.toon import ToonTeleportPanel +from toontown.suit import Suit +from toontown.pets import Pet +from otp.otpbase import OTPLocalizer +from otp.otpbase import OTPGlobals +from otp.uberdog import RejectCode +globalFriendInviter = None + +def showFriendInviter(avId, avName, avDisableName): + global globalFriendInviter + if globalFriendInviter != None: + globalFriendInviter.cleanup() + globalFriendInviter = None + globalFriendInviter = FriendInviter(avId, avName, avDisableName) + return + + +def hideFriendInviter(): + global globalFriendInviter + if globalFriendInviter != None: + globalFriendInviter.cleanup() + globalFriendInviter = None + return + + +def unloadFriendInviter(): + global globalFriendInviter + if globalFriendInviter != None: + globalFriendInviter.cleanup() + globalFriendInviter = None + return + + +class FriendInviter(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FriendInviter') + + def __init__(self, avId, avName, avDisableName): + self.wantPlayerFriends = base.config.GetBool('want-player-friends', 0) + DirectFrame.__init__(self, pos=(0.3, 0.1, 0.65), image_color=GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_wordwrap=TTLocalizer.FIdirectFrameWordwrap, text_scale=TTLocalizer.FIdirectFrame, text_pos=TTLocalizer.FIdirectFramePos) + self['image'] = DGG.getDefaultDialogGeom() + self.avId = avId + self.toonName = avName + avatar = base.cr.doId2do.get(self.avId) + self.playerId = None + self.playerName = None + if avatar: + self.playerId = avatar.DISLid + self.playerName = avatar.DISLname + ' ' + str(avatar.DISLid) + self.avDisableName = avDisableName + self.playerFriend = 0 + self.fsm = ClassicFSM.ClassicFSM('FriendInviter', [State.State('off', self.enterOff, self.exitOff), + State.State('getNewFriend', self.enterGetNewFriend, self.exitGetNewFriend), + State.State('begin', self.enterBegin, self.exitBegin), + State.State('check', self.enterCheck, self.exitCheck), + State.State('tooMany', self.enterTooMany, self.exitTooMany), + State.State('checkAvailability', self.enterCheckAvailability, self.exitCheckAvailability), + State.State('notAvailable', self.enterNotAvailable, self.exitNotAvailable), + State.State('notAcceptingFriends', self.enterNotAcceptingFriends, self.exitNotAcceptingFriends), + State.State('wentAway', self.enterWentAway, self.exitWentAway), + State.State('already', self.enterAlready, self.exitAlready), + State.State('askingCog', self.enterAskingCog, self.exitAskingCog), + State.State('askingPet', self.enterAskingPet, self.exitAskingPet), + State.State('endFriendship', self.enterEndFriendship, self.exitEndFriendship), + State.State('friendsNoMore', self.enterFriendsNoMore, self.exitFriendsNoMore), + State.State('self', self.enterSelf, self.exitSelf), + State.State('ignored', self.enterIgnored, self.exitIgnored), + State.State('asking', self.enterAsking, self.exitAsking), + State.State('yes', self.enterYes, self.exitYes), + State.State('no', self.enterNo, self.exitNo), + State.State('otherTooMany', self.enterOtherTooMany, self.exitOtherTooMany), + State.State('maybe', self.enterMaybe, self.exitMaybe), + State.State('down', self.enterDown, self.exitDown), + State.State('cancel', self.enterCancel, self.exitCancel)], 'off', 'off') + self.context = None + from toontown.toon import ToonAvatarDetailPanel + ToonTeleportPanel.hideTeleportPanel() + ToonAvatarDetailPanel.hideAvatarDetail() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.bOk = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterOK, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.__handleOk) + self.bOk.hide() + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterCancel, text_scale=0.05, text_pos=(0.0, -0.1), pos=TTLocalizer.FIbCancelPos, command=self.__handleCancel) + self.bCancel.hide() + self.bStop = DirectButton(self, image=(gui.find('**/Ignore_Btn_UP'), gui.find('**/Ignore_Btn_DN'), gui.find('**/Ignore_Btn_RLVR')), relief=None, text=OTPLocalizer.FriendInviterStopBeingFriends, text_align=TextNode.ALeft, text_scale=TTLocalizer.FIbStop, text_pos=TTLocalizer.FIbStopTextPos, pos=TTLocalizer.FIbStopPos, command=self.__handleStop) + self.bStop.hide() + self.bYes = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterYes, text_scale=0.05, text_pos=(0.0, -0.1), pos=TTLocalizer.FIbYesPos, command=self.__handleYes) + self.bYes.hide() + self.bNo = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=OTPLocalizer.FriendInviterNo, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.15, 0.0, -0.1), command=self.__handleNo) + self.bNo.hide() + self.bToon = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.FriendInviterToon, text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.35, 0.0, -0.05), command=self.__handleToon) + toonText = DirectLabel(parent=self, relief=None, pos=Vec3(0.35, 0, -0.2), text=TTLocalizer.FriendInviterToonFriendInfo, text_fg=(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.045, text_align=TextNode.ACenter) + toonText.reparentTo(self.bToon.stateNodePath[2]) + self.bToon.hide() + self.bPlayer = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.FriendInviterPlayer, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.05), command=self.__handlePlayer) + playerText = DirectLabel(parent=self, relief=None, pos=Vec3(0, 0, -0.2), text=TTLocalizer.FriendInviterPlayerFriendInfo, text_fg=(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.045, text_align=TextNode.ACenter) + playerText.reparentTo(self.bPlayer.stateNodePath[2]) + self.bPlayer.hide() + buttons.removeNode() + gui.removeNode() + self.fsm.enterInitialState() + if self.avId == None: + self.fsm.request('getNewFriend') + else: + self.fsm.request('begin') + return + + def cleanup(self): + self.fsm.request('cancel') + del self.fsm + self.destroy() + + def getName(self): + if self.playerFriend: + name = self.playerName + if name == None: + name = TTLocalizer.FriendInviterThatPlayer + else: + name = self.toonName + if name == None: + name = TTLocalizer.FriendInviterThatToon + return name + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterGetNewFriend(self): + self['text'] = TTLocalizer.FriendInviterClickToon % len(base.localAvatar.friendsList) + if base.cr.productName in ['JP', + 'DE', + 'BR', + 'FR']: + self.bOk.show() + else: + self.bCancel.show() + self.accept('clickedNametag', self.__handleClickedNametag) + + def exitGetNewFriend(self): + self.bCancel.hide() + self.ignore('clickedNametag') + + def __handleClickedNametag(self, avatar): + self.avId = avatar.doId + self.toonName = avatar.getName() + if hasattr(avatar, 'DISLid'): + self.playerId = avatar.DISLid + self.playerName = avatar.DISLname + self.avDisableName = avatar.uniqueName('disable') + self.fsm.request('begin') + + def enterBegin(self): + myId = base.localAvatar.doId + self['text'] = TTLocalizer.FriendInviterBegin + self.bCancel.setPos(0.35, 0.0, -0.05) + self.bCancel.show() + self.bToon.show() + if self.wantPlayerFriends and base.cr.productName != 'DisneyOnline-UK' and base.cr.productName != 'DisneyOnline-AP': + self.bPlayer.show() + else: + self.__handleToon() + self.accept(self.avDisableName, self.__handleDisableAvatar) + + def exitBegin(self): + self.ignore(self.avDisableName) + self.bToon.hide() + if self.wantPlayerFriends and base.cr.productName != 'DisneyOnline-UK' and base.cr.productName != 'DisneyOnline-AP': + self.bPlayer.hide() + self.bCancel.setPos(0.0, 0.0, -0.1) + self.bCancel.hide() + + def enterCheck(self): + myId = base.localAvatar.doId + self.accept(self.avDisableName, self.__handleDisableAvatar) + if self.avId == myId: + self.fsm.request('self') + elif not self.playerFriend and base.cr.isFriend(self.avId): + self.fsm.request('already') + elif self.playerFriend and base.cr.playerFriendsManager.isPlayerFriend(self.avId): + self.fsm.request('already') + else: + if not self.playerFriend: + tooMany = len(base.localAvatar.friendsList) >= MaxFriends + elif self.playerFriend: + tooMany = base.cr.playerFriendsManager.friendsListFull() + if tooMany: + self.fsm.request('tooMany') + else: + self.fsm.request('checkAvailability') + + def exitCheck(self): + self.ignore(self.avDisableName) + + def enterTooMany(self): + if self.playerFriend: + text = OTPLocalizer.FriendInviterPlayerTooMany + name = self.playerName + else: + text = OTPLocalizer.FriendInviterToonTooMany + name = self.toonName + self['text'] = text % name + self.bCancel.show() + self.bCancel.setPos(0.0, 0.0, -0.16) + + def exitTooMany(self): + self.bCancel.hide() + + def enterCheckAvailability(self): + self.accept(self.avDisableName, self.__handleDisableAvatar) + if not self.playerFriend: + if not base.cr.doId2do.has_key(self.avId): + self.fsm.request('wentAway') + return + if not base.cr.doId2do.has_key(self.avId): + self.fsm.request('wentAway') + return + else: + avatar = base.cr.doId2do.get(self.avId) + if isinstance(avatar, Suit.Suit): + self.fsm.request('askingCog') + return + if isinstance(avatar, Pet.Pet): + self.fsm.request('askingPet') + return + if not self.playerFriend: + if not base.cr.friendManager: + self.notify.warning('No FriendManager available.') + self.fsm.request('down') + return + if self.playerFriend: + self.notify.info('Inviter requesting player friend') + self['text'] = OTPLocalizer.FriendInviterAsking % self.playerName + base.cr.playerFriendsManager.sendRequestInvite(self.playerId) + self.accept(OTPGlobals.PlayerFriendRejectInviteEvent, self.__playerFriendRejectResponse) + self.accept(OTPGlobals.PlayerFriendAddEvent, self.__playerFriendAcceptResponse) + self.bOk.show() + else: + base.cr.friendManager.up_friendQuery(self.avId) + self['text'] = OTPLocalizer.FriendInviterCheckAvailability % self.toonName + self.accept('friendResponse', self.__friendResponse) + self.bCancel.show() + self.accept('friendConsidering', self.__friendConsidering) + + def exitCheckAvailability(self): + self.ignore(self.avDisableName) + self.ignore('friendConsidering') + self.ignore('friendResponse') + self.bCancel.hide() + + def enterNotAvailable(self): + self['text'] = OTPLocalizer.FriendInviterNotAvailable % self.getName() + self.context = None + self.bOk.show() + return + + def exitNotAvailable(self): + self.bOk.hide() + + def enterNotAcceptingFriends(self): + self['text'] = OTPLocalizer.FriendInviterFriendSaidNoNewFriends % self.getName() + self.context = None + self.bOk.show() + return + + def exitNotAcceptingFriends(self): + self.bOk.hide() + + def enterWentAway(self): + self['text'] = OTPLocalizer.FriendInviterWentAway % self.getName() + if not self.playerFriend: + if self.context != None: + base.cr.friendManager.up_cancelFriendQuery(self.context) + self.context = None + self.bOk.show() + return + + def exitWentAway(self): + self.bOk.hide() + + def enterAlready(self): + if self.playerFriend: + self['text'] = TTLocalizer.FriendInviterPlayerAlready % self.getName() + self.bStop['text'] = TTLocalizer.FriendInviterStopBeingPlayerFriends + else: + self['text'] = TTLocalizer.FriendInviterToonAlready % self.getName() + self.bStop['text'] = TTLocalizer.FriendInviterStopBeingToonFriends + self.context = None + if base.cr.productName in ['JP', + 'DE', + 'BR', + 'FR']: + self.bStop.setPos(-0.2, 0.0, -0.1) + self.bCancel.setPos(0.2, 0.0, -0.1) + self.bStop.show() + self.bCancel.show() + return + + def exitAlready(self): + self['text'] = '' + self.bStop.hide() + self.bCancel.hide() + + def enterAskingCog(self): + self['text'] = OTPLocalizer.FriendInviterAskingCog % self.getName() + taskMgr.doMethodLater(2.0, self.cogReplies, 'cogFriendship') + self.bCancel.show() + + def exitAskingCog(self): + taskMgr.remove('cogFriendship') + self.bCancel.hide() + + def cogReplies(self, task): + self.fsm.request('no') + return Task.done + + def enterAskingPet(self): + self['text'] = OTPLocalizer.FriendInviterAskingPet % self.getName() + if base.localAvatar.hasPet(): + if base.localAvatar.getPetId() == self.avId: + self['text'] = OTPLocalizer.FriendInviterAskingMyPet % self.getName() + self.context = None + self.bOk.show() + return + + def exitAskingPet(self): + self.bOk.hide() + + def enterEndFriendship(self): + if self.playerFriend: + self['text'] = TTLocalizer.FriendInviterEndFriendshipPlayer % self.getName() + if base.cr.isFriend(self.avId): + self['text'] = self['text'] + TTLocalizer.FriendInviterRemainToon % self.toonName + else: + self['text'] = TTLocalizer.FriendInviterEndFriendshipToon % self.getName() + if base.cr.playerFriendsManager.isPlayerFriend(self.playerId): + self['text'] = self['text'] + TTLocalizer.FriendInviterRemainPlayer % self.playerName + self.context = None + self.bYes.show() + self.bNo.show() + return + + def exitEndFriendship(self): + self.bYes.hide() + self.bNo.hide() + + def enterFriendsNoMore(self): + if self.playerFriend: + self.notify.info('### send player remove') + base.cr.playerFriendsManager.sendRequestRemove(self.playerId) + else: + self.notify.info('### send avatar remove') + base.cr.removeFriend(self.avId) + self['text'] = OTPLocalizer.FriendInviterFriendsNoMore % self.getName() + self.bOk.show() + if not base.cr.doId2do.has_key(self.avId): + messenger.send(self.avDisableName) + + def exitFriendsNoMore(self): + self.bOk.hide() + + def enterSelf(self): + self['text'] = OTPLocalizer.FriendInviterSelf + self.context = None + self.bOk.show() + return + + def exitSelf(self): + self.bOk.hide() + + def enterIgnored(self): + self['text'] = OTPLocalizer.FriendInviterIgnored % self.toonName + self.context = None + self.bOk.show() + return + + def exitIgnored(self): + self.bOk.hide() + + def enterAsking(self): + self.accept(self.avDisableName, self.__handleDisableAvatar) + self['text'] = OTPLocalizer.FriendInviterAsking % self.toonName + self.accept('friendResponse', self.__friendResponse) + self.bCancel.show() + + def exitAsking(self): + self.ignore(self.avDisableName) + self.ignore('friendResponse') + self.bCancel.hide() + + def enterYes(self): + self['text'] = OTPLocalizer.FriendInviterFriendSaidYes % self.toonName + self.context = None + self.bOk.show() + return + + def exitYes(self): + self.bOk.hide() + + def enterNo(self): + self['text'] = OTPLocalizer.FriendInviterFriendSaidNo % self.toonName + self.context = None + self.bOk.show() + return + + def exitNo(self): + self.bOk.hide() + + def enterOtherTooMany(self): + self['text'] = OTPLocalizer.FriendInviterOtherTooMany % self.toonName + self.context = None + self.bOk.show() + return + + def exitOtherTooMany(self): + self.bOk.hide() + + def enterMaybe(self): + self['text'] = OTPLocalizer.FriendInviterMaybe % self.toonName + self.context = None + self.bOk.show() + return + + def exitMaybe(self): + self.bOk.hide() + + def enterDown(self): + self['text'] = OTPLocalizer.FriendInviterDown + self.context = None + self.bOk.show() + return + + def exitDown(self): + self.bOk.hide() + + def enterCancel(self): + if not self.playerFriend: + if self.context != None: + base.cr.friendManager.up_cancelFriendQuery(self.context) + self.context = None + self.fsm.request('off') + return + + def exitCancel(self): + pass + + def __handleOk(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: MAKEAFRIENDSHIP: Make a friendship') + unloadFriendInviter() + + def __handleCancel(self): + if base.friendMode == 1: + if self.avId: + base.cr.avatarFriendsManager.sendRequestRemove(self.avId) + unloadFriendInviter() + + def __handleStop(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: BREAKAFRIENDSHIP: Break a friendship') + self.fsm.request('endFriendship') + + def __handleYes(self): + if self.fsm.getCurrentState().getName() == 'endFriendship': + self.fsm.request('friendsNoMore') + else: + unloadFriendInviter() + + def __handleToon(self): + if self.fsm.getCurrentState().getName() == 'begin': + self.fsm.request('check') + else: + unloadFriendInviter() + + def __handlePlayer(self): + if self.fsm.getCurrentState().getName() == 'begin': + self.playerFriend = 1 + self.fsm.request('check') + else: + unloadFriendInviter() + + def __handleNo(self): + unloadFriendInviter() + + def __handleList(self): + messenger.send('openFriendsList') + + def __friendConsidering(self, yesNoAlready, context): + if yesNoAlready == 1: + self.context = context + self.fsm.request('asking') + elif yesNoAlready == 0: + self.fsm.request('notAvailable') + elif yesNoAlready == 2: + self.fsm.request('already') + elif yesNoAlready == 3: + self.fsm.request('self') + elif yesNoAlready == 4: + self.fsm.request('ignored') + elif yesNoAlready == 6: + self.fsm.request('notAcceptingFriends') + elif yesNoAlready == 10: + self.fsm.request('no') + elif yesNoAlready == 13: + self.fsm.request('otherTooMany') + else: + self.notify.warning('Got unexpected response to friendConsidering: %s' % yesNoAlready) + self.fsm.request('maybe') + + def __friendResponse(self, yesNoMaybe, context): + if self.context != context: + self.notify.warning('Unexpected change of context from %s to %s.' % (self.context, context)) + self.context = context + if yesNoMaybe == 1: + self.fsm.request('yes') + elif yesNoMaybe == 0: + self.fsm.request('no') + elif yesNoMaybe == 3: + self.fsm.request('otherTooMany') + else: + self.notify.warning('Got unexpected response to friendResponse: %s' % yesNoMaybe) + self.fsm.request('maybe') + + def __playerFriendRejectResponse(self, avId, reason): + self.notify.debug('Got reject response to friendResponse: %s' % reason) + if reason == RejectCode.RejectCode.INVITATION_DECLINED: + self.fsm.request('no') + elif reason == RejectCode.RejectCode.FRIENDS_LIST_FULL: + self.fsm.request('otherTooMany') + else: + self.notify.warning('Got unexpected response to friendResponse: %s' % reason) + self.fsm.request('maybe') + + def __playerFriendAcceptResponse(self): + self.fsm.request('yes') + + def __handleDisableAvatar(self): + self.fsm.request('wentAway') diff --git a/toontown/friends/FriendNotifier.py b/toontown/friends/FriendNotifier.py new file mode 100644 index 0000000..61982fe --- /dev/null +++ b/toontown/friends/FriendNotifier.py @@ -0,0 +1,60 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import ToonHeadDialog +from direct.gui.DirectGui import DGG +from otp.otpbase import OTPGlobals + +class FriendNotifier(ToonHeadDialog.ToonHeadDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('FriendNotifier') + + def __init__(self, avId, avName, avDNA, context, **kw): + self.avId = avId + self.avName = avName + self.avDNA = avDNA + self.context = context + text = OTPLocalizer.FriendNotifictation % self.avName + style = TTDialog.Acknowledge + buttonText = [OTPLocalizer.FriendInviteeOK, OTPLocalizer.FriendInviteeOK] + command = self.__handleButton + optiondefs = (('dialogName', 'FriendInvitee', None), + ('text', text, None), + ('style', style, None), + ('buttonText', buttonText, None), + ('command', command, None), + ('image_color', (1.0, 0.89, 0.77, 1.0), None), + ('geom_scale', 0.2, None), + ('geom_pos', (-0.1, 0, -0.025), None), + ('pad', (0.075, 0.075), None), + ('topPad', 0, None), + ('midPad', 0, None), + ('pos', (0.45, 0, 0.75), None), + ('scale', 0.75, None)) + self.defineoptions(kw, optiondefs) + ToonHeadDialog.ToonHeadDialog.__init__(self, self.avDNA) + self.initialiseoptions(FriendNotifier) + self.show() + return + + def cleanup(self): + print 'cleanup calling!' + ToonHeadDialog.ToonHeadDialog.cleanup(self) + + def __handleButton(self, value): + if value == DGG.DIALOG_OK: + pass + self.context = None + self.cleanup() + return + + def __handleOhWell(self, value): + self.cleanup() + + def __handleCancelFromAbove(self, context = None): + if context == None or context == self.context: + self.context = None + self.cleanup() + return diff --git a/toontown/friends/FriendsListManager.py b/toontown/friends/FriendsListManager.py new file mode 100644 index 0000000..be5e2d1 --- /dev/null +++ b/toontown/friends/FriendsListManager.py @@ -0,0 +1,187 @@ +from pandac.PandaModules import * +import FriendsListPanel +import FriendInviter +import FriendInvitee +import FriendNotifier +from direct.directnotify import DirectNotifyGlobal +from toontown.toon import ToonTeleportPanel +from toontown.friends import ToontownFriendSecret +from toontown.pets import PetAvatarPanel +from toontown.toon import ToonAvatarPanel +from toontown.toon import PlayerInfoPanel +from toontown.suit import SuitAvatarPanel +from toontown.toon import ToonDNA +from toontown.toon import ToonAvatarDetailPanel +from toontown.toon import PlayerDetailPanel +from toontown.toonbase import ToontownGlobals +from toontown.toon import Toon +import FriendHandle +from otp.otpbase import OTPGlobals + +class FriendsListManager: + notify = DirectNotifyGlobal.directNotify.newCategory('FriendsListManager') + + def __init__(self): + self.avatarPanel = None + self._preserveFriendsList = False + self._entered = False + self.friendsRequestQueue = [] + return + + def load(self): + base.cr.friendManager.setGameSpecificFunction(self.processQueuedRequests) + self.accept(OTPGlobals.AvatarNewFriendAddEvent, self.__friendAdded) + + def unload(self): + base.cr.friendManager.setGameSpecificFunction(None) + self.exitFLM() + if self.avatarPanel: + del self.avatarPanel + FriendInviter.unloadFriendInviter() + ToonAvatarDetailPanel.unloadAvatarDetail() + ToonTeleportPanel.unloadTeleportPanel() + return + + def enterFLM(self): + self.notify.debug('FriendsListManager: enterFLM()') + if self._preserveFriendsList: + self._preserveFriendsList = 0 + return + self._entered = True + self.accept('openFriendsList', self.__openFriendsList) + self.accept('clickedNametag', self.__handleClickedNametag) + self.accept('clickedNametagPlayer', self.__handleClickedNametagPlayer) + base.localAvatar.setFriendsListButtonActive(1) + NametagGlobals.setMasterNametagsActive(1) + self.accept('gotoAvatar', self.__handleGotoAvatar) + self.accept('friendAvatar', self.__handleFriendAvatar) + self.accept('avatarDetails', self.__handleAvatarDetails) + self.accept('playerDetails', self.__handlePlayerDetails) + self.accept('friendInvitation', self.__handleFriendInvitation) + self.accept(OTPGlobals.PlayerFriendInvitationEvent, self.__handlePlayerFriendInvitation) + if base.cr.friendManager: + base.cr.friendManager.setAvailable(1) + + def exitFLM(self): + self.notify.debug('FriendsListManager: exitFLM()') + if self._preserveFriendsList: + return + if not self._entered: + return + self._entered = False + self.ignore('openFriendsList') + self.ignore('clickedNametag') + self.ignore('clickedNametagPlayer') + base.localAvatar.setFriendsListButtonActive(0) + NametagGlobals.setMasterNametagsActive(0) + if self.avatarPanel: + self.avatarPanel.cleanup() + self.avatarPanel = None + self.ignore('gotoAvatar') + self.ignore('friendAvatar') + self.ignore('avatarDetails') + self.ignore('playerDetails') + FriendsListPanel.hideFriendsList() + ToontownFriendSecret.hideFriendSecret() + if base.cr.friendManager: + base.cr.friendManager.setAvailable(0) + self.ignore('friendInvitation') + FriendInviter.hideFriendInviter() + ToonAvatarDetailPanel.hideAvatarDetail() + ToonTeleportPanel.hideTeleportPanel() + return + + def __openFriendsList(self): + FriendsListPanel.showFriendsList() + + def __handleClickedNametag(self, avatar, playerId = None): + self.notify.debug('__handleClickedNametag. doId = %s' % avatar.doId) + if avatar.isPet(): + self.avatarPanel = PetAvatarPanel.PetAvatarPanel(avatar) + elif isinstance(avatar, Toon.Toon) or isinstance(avatar, FriendHandle.FriendHandle): + if hasattr(self, 'avatarPanel'): + if self.avatarPanel: + if not hasattr(self.avatarPanel, 'getAvId') or self.avatarPanel.getAvId() == avatar.doId: + if not self.avatarPanel.isHidden(): + if self.avatarPanel.getType() == 'toon': + return + self.avatarPanel = ToonAvatarPanel.ToonAvatarPanel(avatar, playerId) + else: + self.avatarPanel = SuitAvatarPanel.SuitAvatarPanel(avatar) + + def __handleClickedNametagPlayer(self, avatar, playerId, showType = 1): + self.notify.debug('__handleClickedNametagPlayer PlayerId%s' % playerId) + if showType == 1: + if hasattr(self, 'avatarPanel'): + if self.avatarPanel: + if not hasattr(self.avatarPanel, 'getPlayerId') or self.avatarPanel.getPlayerId() == playerId: + if not self.avatarPanel.isHidden(): + if self.avatarPanel.getType() == 'player': + return + self.avatarPanel = PlayerInfoPanel.PlayerInfoPanel(playerId) + elif isinstance(avatar, Toon.Toon) or isinstance(avatar, FriendHandle.FriendHandle): + if hasattr(self, 'avatarPanel'): + if self.avatarPanel: + if not hasattr(self.avatarPanel, 'getAvId') or self.avatarPanel.getAvId() == avatar.doId: + if not self.avatarPanel.isHidden(): + if self.avatarPanel.getType() == 'toon': + return + self.avatarPanel = ToonAvatarPanel.ToonAvatarPanel(avatar, playerId) + + def __handleGotoAvatar(self, avId, avName, avDisableName): + ToonTeleportPanel.showTeleportPanel(avId, avName, avDisableName) + + def __handleFriendAvatar(self, avId, avName, avDisableName): + FriendInviter.showFriendInviter(avId, avName, avDisableName) + + def __handleFriendInvitation(self, avId, avName, inviterDna, context): + dna = ToonDNA.ToonDNA() + dna.makeFromNetString(inviterDna) + if not base.cr.avatarFriendsManager.checkIgnored(avId): + FriendInvitee.FriendInvitee(avId, avName, dna, context) + + def __handlePlayerFriendInvitation(self, avId, avName, inviterDna = None, context = None): + self.notify.debug('incoming switchboard friend event') + self.friendsRequestQueue.append((avId, + avName, + inviterDna, + context)) + if base.cr.friendManager.getAvailable(): + self.processQueuedRequests() + + def processQueuedRequests(self): + if len(self.friendsRequestQueue): + request = self.friendsRequestQueue.pop(0) + self.__processFriendRequest(request[0], request[1], request[2], request[3]) + + def __processFriendRequest(self, avId, avName, inviterDna = None, context = None): + self.notify.debug('__handleAvatarFriendInvitation') + askerToon = base.cr.doId2do.get(avId) + if askerToon: + self.notify.debug('got toon') + dna = askerToon.getStyle() + if not base.cr.avatarFriendsManager.checkIgnored(avId): + FriendInvitee.FriendInvitee(avId, avName, dna, context) + else: + self.notify.debug('no toon') + + def __handleAvatarDetails(self, avId, avName, playerId = None): + ToonAvatarDetailPanel.showAvatarDetail(avId, avName, playerId) + + def __handlePlayerDetails(self, avId, avName, playerId = None): + PlayerDetailPanel.showPlayerDetail(avId, avName, playerId) + + def preserveFriendsList(self): + self.notify.debug('Preserving Friends List') + self._preserveFriendsList = True + + def __friendAdded(self, avId): + if FriendInviter.globalFriendInviter != None: + messenger.send('FriendsListManagerAddEvent', [avId]) + else: + friendToon = base.cr.doId2do.get(avId) + if friendToon: + print 'got toon' + dna = friendToon.getStyle() + FriendNotifier.FriendNotifier(avId, friendToon.getName(), dna, None) + return diff --git a/toontown/friends/FriendsListPanel.py b/toontown/friends/FriendsListPanel.py new file mode 100644 index 0000000..eab9d65 --- /dev/null +++ b/toontown/friends/FriendsListPanel.py @@ -0,0 +1,679 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import StateData +from toontown.toon import ToonAvatarPanel +from toontown.friends import ToontownFriendSecret +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPGlobals +FLPPets = 1 +FLPOnline = 2 +FLPAll = 3 +FLPOnlinePlayers = 4 +FLPPlayers = 5 +FLPEnemies = 6 +globalFriendsList = None + +def determineFriendName(friendTuple): + friendName = None + if len(friendTuple) == 2: + avId, flags = friendTuple + playerId = None + showType = 0 + elif len(friendTuple) == 3: + avId, flags, playerId = friendTuple + showType = 0 + elif len(friendTuple) == 4: + avId, flags, playerId, showType = friendTuple + if showType == 1 and playerId: + playerInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId) + friendName = playerInfo.playerName + else: + hasManager = hasattr(base.cr, 'playerFriendsManager') + handle = base.cr.identifyFriend(avId) + if not handle and hasManager: + handle = base.cr.playerFriendsManager.getAvHandleFromId(avId) + if handle: + friendName = handle.getName() + return friendName + + +def compareFriends(f1, f2): + name1 = determineFriendName(f1) + name2 = determineFriendName(f2) + if name1 > name2: + return 1 + elif name1 == name2: + return 0 + else: + return -1 + + +def showFriendsList(): + global globalFriendsList + if globalFriendsList == None: + globalFriendsList = FriendsListPanel() + globalFriendsList.enter() + return + + +def hideFriendsList(): + if globalFriendsList != None: + globalFriendsList.exit() + return + + +def showFriendsListTutorial(): + global globalFriendsList + if globalFriendsList == None: + globalFriendsList = FriendsListPanel() + globalFriendsList.enter() + if not base.cr.isPaid(): + globalFriendsList.secrets['state'] = DGG.DISABLED + globalFriendsList.closeCommand = globalFriendsList.close['command'] + globalFriendsList.close['command'] = None + return + + +def hideFriendsListTutorial(): + if globalFriendsList != None: + if hasattr(globalFriendsList, 'closeCommand'): + globalFriendsList.close['command'] = globalFriendsList.closeCommand + if not base.cr.isPaid(): + globalFriendsList.secrets['state'] = DGG.NORMAL + globalFriendsList.exit() + return + + +def isFriendsListShown(): + if globalFriendsList != None: + return globalFriendsList.isEntered + return 0 + + +def unloadFriendsList(): + global globalFriendsList + if globalFriendsList != None: + globalFriendsList.unload() + globalFriendsList = None + return + + +class FriendsListPanel(DirectFrame, StateData.StateData): + + def __init__(self): + self.leftmostPanel = FLPPets + self.rightmostPanel = FLPPlayers + if base.cr.productName in ['DisneyOnline-UK', + 'DisneyOnline-AP', + 'JP', + 'FR', + 'BR']: + self.rightmostPanel = FLPAll + DirectFrame.__init__(self, relief=None) + self.listScrollIndex = [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0] + self.initialiseoptions(FriendsListPanel) + StateData.StateData.__init__(self, 'friends-list-done') + self.friends = {} + 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.panelType = FLPOnline + return + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + auxGui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=TTLocalizer.FLPtitle, text_fg=(0, 0.1, 0.4, 1), pos=(0.007, 0.0, 0.2)) + background_image = gui.find('**/FriendsBox_Open') + self['image'] = background_image + self.setPos(1.1, 0, 0.54) + 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_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), 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_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_pos=(-0.17, 0.0, 0.06), itemFrame_relief=None, numItemsVisible=8, items=[]) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.2, 0, 0))) + clipNP = self.scrollList.attachNewNode(clipper) + self.scrollList.setClipPlane(clipNP) + self.close = DirectButton(parent=self, relief=None, image=(auxGui.find('**/CloseBtn_UP'), auxGui.find('**/CloseBtn_DN'), auxGui.find('**/CloseBtn_Rllvr')), pos=(0.01, 0, -0.38), command=self.__close) + self.left = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), pos=(-0.15, 0.0, -0.38), scale=(-1.0, 1.0, 1.0), command=self.__left) + self.right = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(0.6, 0.6, 0.6, 0.6), pos=(0.17, 0, -0.38), command=self.__right) + self.newFriend = DirectButton(parent=self, relief=None, pos=(-0.14, 0.0, 0.14), image=(auxGui.find('**/Frnds_Btn_UP'), auxGui.find('**/Frnds_Btn_DN'), auxGui.find('**/Frnds_Btn_RLVR')), text=('', TTLocalizer.FriendsListPanelNewFriend, TTLocalizer.FriendsListPanelNewFriend), text_scale=TTLocalizer.FLPnewFriend, text_fg=(0, 0, 0, 1), text_bg=(1, 1, 1, 1), text_pos=(0.1, -0.085), textMayChange=0, command=self.__newFriend) + self.secrets = DirectButton(parent=self, relief=None, pos=TTLocalizer.FLPsecretsPos, image=(auxGui.find('**/ChtBx_ChtBtn_UP'), auxGui.find('**/ChtBx_ChtBtn_DN'), auxGui.find('**/ChtBx_ChtBtn_RLVR')), text=('', + TTLocalizer.FriendsListPanelSecrets, + TTLocalizer.FriendsListPanelSecrets, + ''), text_scale=TTLocalizer.FLPsecrets, text_fg=(0, 0, 0, 1), text_bg=(1, 1, 1, 1), text_pos=(-0.04, -0.085), textMayChange=0, command=self.__secrets) + gui.removeNode() + auxGui.removeNode() + return + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + del self.title + del self.scrollList + del self.close + del self.left + del self.right + del self.friends + DirectFrame.destroy(self) + return None + + def makeFriendButton(self, friendTuple, colorChoice = None, bold = 0): + playerName = None + toonName = None + if len(friendTuple) == 2: + avId, flags = friendTuple + playerId = None + showType = 0 + elif len(friendTuple) == 3: + avId, flags, playerId = friendTuple + showType = 0 + elif len(friendTuple) == 4: + avId, flags, playerId, showType = friendTuple + command = self.__choseFriend + playerName = None + if playerId: + playerInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId, None) + if playerInfo: + playerName = playerInfo.playerName + toonName = None + hasManager = hasattr(base.cr, 'playerFriendsManager') + handle = base.cr.identifyFriend(avId) + if not handle and hasManager: + handle = base.cr.playerFriendsManager.getAvHandleFromId(avId) + if handle: + toonName = handle.getName() + if showType == 1 and playerId: + if not playerName: + return + print 'ABORTING!!!' + friendName = playerName + rolloverName = toonName + else: + if not toonName: + base.cr.fillUpFriendsMap() + return + friendName = toonName + if playerName: + rolloverName = playerName + else: + rolloverName = 'Unknown' + if playerId: + command = self.__chosePlayerFriend + thing = playerId + else: + thing = avId + fg = ToontownGlobals.ColorNoChat + if flags & ToontownGlobals.FriendChat: + fg = ToontownGlobals.ColorAvatar + if playerId: + fg = ToontownGlobals.ColorPlayer + if colorChoice: + fg = colorChoice + fontChoice = ToontownGlobals.getToonFont() + fontScale = 0.04 + bg = None + if colorChoice and bold: + fontScale = 0.04 + colorS = 0.7 + bg = (colorChoice[0] * colorS, + colorChoice[1] * colorS, + colorChoice[2] * colorS, + colorChoice[3]) + db = DirectButton(relief=None, text=friendName, text_scale=fontScale, text_align=TextNode.ALeft, text_fg=fg, text_shadow=bg, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, text_font=fontChoice, textMayChange=0, command=command, extraArgs=[thing, showType]) + if playerId: + accountName = DirectLabel(parent=db, pos=Vec3(-0.02, 0, 0), text=rolloverName, text_fg=(0, 0, 0, 1), text_bg=(1, 1, 1, 1), text_pos=(0, 0), text_scale=0.045, text_align=TextNode.ARight) + accountName.reparentTo(db.stateNodePath[2]) + return db + + def enter(self): + if self.isEntered == 1: + return None + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + base.localAvatar.obscureFriendsListButton(1) + if ToonAvatarPanel.ToonAvatarPanel.currentAvatarPanel: + ToonAvatarPanel.ToonAvatarPanel.currentAvatarPanel.cleanup() + ToonAvatarPanel.ToonAvatarPanel.currentAvatarPanel = None + self.__updateScrollList() + self.__updateTitle() + self.__updateArrows() + self.show() + self.accept('friendOnline', self.__friendOnline) + self.accept('friendPlayers', self.__friendPlayers) + self.accept('friendOffline', self.__friendOffline) + self.accept('friendsListChanged', self.__friendsListChanged) + self.accept('ignoreListChanged', self.__ignoreListChanged) + self.accept('friendsMapComplete', self.__friendsListChanged) + self.accept(OTPGlobals.PlayerFriendAddEvent, self.__friendsListChanged) + self.accept(OTPGlobals.PlayerFriendUpdateEvent, self.__friendsListChanged) + return + + def exit(self): + if self.isEntered == 0: + return None + self.isEntered = 0 + self.listScrollIndex[self.panelType] = self.scrollList.index + self.hide() + base.cr.cleanPetsFromFriendsMap() + self.ignore('friendOnline') + self.ignore('friendOffline') + self.ignore('friendsListChanged') + self.ignore('ignoreListChanged') + self.ignore('friendsMapComplete') + self.ignore(OTPGlobals.PlayerFriendAddEvent) + self.ignore(OTPGlobals.PlayerFriendUpdateEvent) + base.localAvatar.obscureFriendsListButton(-1) + messenger.send(self.doneEvent) + return None + + def __close(self): + messenger.send('wakeup') + self.exit() + + def __left(self): + messenger.send('wakeup') + self.listScrollIndex[self.panelType] = self.scrollList.index + if self.panelType > self.leftmostPanel: + self.panelType -= 1 + self.__updateScrollList() + self.__updateTitle() + self.__updateArrows() + + def __right(self): + messenger.send('wakeup') + self.listScrollIndex[self.panelType] = self.scrollList.index + if self.panelType < self.rightmostPanel: + self.panelType += 1 + self.__updateScrollList() + self.__updateTitle() + self.__updateArrows() + + def __secrets(self): + messenger.send('wakeup') + ToontownFriendSecret.showFriendSecret(ToontownFriendSecret.AvatarSecret) + + def __newFriend(self): + messenger.send('wakeup') + messenger.send('friendAvatar', [None, None, None]) + return + + def __choseFriend(self, friendId, showType = 0): + messenger.send('wakeup') + hasManager = hasattr(base.cr, 'playerFriendsManager') + handle = base.cr.identifyFriend(friendId) + if not handle and hasManager: + handle = base.cr.playerFriendsManager.getAvHandleFromId(friendId) + if handle != None: + self.notify.info("Clicked on name in friend's list. doId = %s" % handle.doId) + messenger.send('clickedNametag', [handle]) + return + + def __chosePlayerFriend(self, friendId, showType = 1): + messenger.send('wakeup') + hasManager = hasattr(base.cr, 'playerFriendsManager') + handle = None + playerFriendInfo = base.cr.playerFriendsManager.playerId2Info.get(friendId) + handle = base.cr.identifyFriend(playerFriendInfo.avatarId) + if not handle and hasManager: + handle = base.cr.playerFriendsManager.getAvHandleFromId(playerFriendInfo.avatarId) + if playerFriendInfo != None: + self.notify.info("Clicked on name in player friend's list. Id = %s" % friendId) + messenger.send('clickedNametagPlayer', [handle, friendId, showType]) + return + + def __updateScrollList(self): + newFriends = [] + petFriends = [] + freeChatOneRef = [] + speedChatOneRef = [] + freeChatDouble = [] + speedChatDouble = [] + offlineFriends = [] + if self.panelType == FLPPlayers: + playerFriendList = base.cr.playerFriendsManager.playerFriendsList + for playerFriendId in playerFriendList: + if base.cr.playerFriendsManager.playerId2Info.has_key(playerFriendId): + playerFriendInfo = base.cr.playerFriendsManager.playerId2Info.get(playerFriendId) + if playerFriendInfo.onlineYesNo: + if playerFriendInfo.understandableYesNo: + if playerFriendInfo.avatarId: + freeChatDouble.insert(0, (playerFriendInfo.avatarId, + 0, + playerFriendId, + 1)) + else: + freeChatOneRef.insert(0, (0, + 0, + playerFriendId, + 1)) + elif playerFriendInfo.avatarId: + speedChatDouble.insert(0, (playerFriendInfo.avatarId, + 0, + playerFriendId, + 1)) + else: + speedChatOneRef.insert(0, (0, + 0, + playerFriendId, + 1)) + elif playerFriendInfo.understandableYesNo: + freeChatOneRef.insert(0, (0, + 0, + playerFriendId, + 1)) + else: + speedChatOneRef.insert(0, (0, + 0, + playerFriendId, + 1)) + + if self.panelType == FLPOnlinePlayers: + playerFriendList = base.cr.playerFriendsManager.playerFriendsList + for playerFriendId in playerFriendList: + if base.cr.playerFriendsManager.playerId2Info.has_key(playerFriendId): + playerFriendInfo = base.cr.playerFriendsManager.playerId2Info.get(playerFriendId) + if playerFriendInfo.onlineYesNo: + if playerFriendInfo.understandableYesNo: + if playerFriendInfo.avatarId: + freeChatDouble.insert(0, (playerFriendInfo.avatarId, + 0, + playerFriendId, + 1)) + else: + freeChatOneRef.insert(0, (0, + 0, + playerFriendId, + 1)) + elif playerFriendInfo.avatarId: + speedChatDouble.insert(0, (playerFriendInfo.avatarId, + 0, + playerFriendId, + 1)) + else: + speedChatOneRef.insert(0, (0, + 0, + playerFriendId, + 1)) + + if self.panelType == FLPAll: + if base.friendMode == 0: + for friendPair in base.localAvatar.friendsList: + playerId = 0 + if hasattr(base.cr, 'playerFriendsManager'): + playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(friendPair[0]) + if playerId: + if friendPair[1] & ToontownGlobals.FriendChat: + freeChatDouble.insert(0, (friendPair[0], + friendPair[1], + playerId, + 0)) + else: + speedChatDouble.insert(0, (friendPair[0], + friendPair[1], + playerId, + 0)) + elif base.cr.isFriendOnline(friendPair[0]): + if friendPair[1] & ToontownGlobals.FriendChat: + freeChatOneRef.insert(0, (friendPair[0], + friendPair[1], + 0, + 0)) + else: + speedChatOneRef.insert(0, (friendPair[0], + friendPair[1], + 0, + 0)) + elif friendPair[1] & ToontownGlobals.FriendChat: + freeChatOneRef.insert(0, (friendPair[0], + friendPair[1], + 0, + 0)) + else: + speedChatOneRef.insert(0, (friendPair[0], + friendPair[1], + 0, + 0)) + else: + offlineFriends.append((friendPair[0], + friendPair[1], + playerId, + 0)) + + if hasattr(base.cr, 'playerFriendsManager'): + for avatarId in base.cr.playerFriendsManager.getAllOnlinePlayerAvatars(): + playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(avatarId) + playerFriendInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId) + if not base.cr.playerFriendsManager.askAvatarKnownElseWhere(avatarId): + if playerFriendInfo.understandableYesNo: + freeChatDouble.insert(0, (avatarId, + 0, + playerId, + 0)) + else: + speedChatDouble.insert(0, (avatarId, + 0, + playerId, + 0)) + + elif base.friendMode == 1: + for friendId in base.cr.avatarFriendsManager.avatarFriendsList: + playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(friendId) + newFriends.append((friendId, + 0, + playerId, + 0)) + + if self.panelType == FLPOnline: + if base.friendMode == 0: + for friendPair in base.localAvatar.friendsList: + if hasattr(base.cr, 'playerFriendsManager') and base.cr.isFriendOnline(friendPair[0]): + playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(friendPair[0]) + if playerId: + if friendPair[1] & ToontownGlobals.FriendChat: + freeChatDouble.insert(0, (friendPair[0], + friendPair[1], + playerId, + 0)) + else: + speedChatDouble.insert(0, (friendPair[0], + friendPair[1], + playerId, + 0)) + elif friendPair[1] & ToontownGlobals.FriendChat: + freeChatOneRef.insert(0, (friendPair[0], + friendPair[1], + 0, + 0)) + else: + speedChatOneRef.insert(0, (friendPair[0], + friendPair[1], + 0, + 0)) + elif base.cr.isFriendOnline(friendPair[0]): + offlineFriends.append((friendPair[0], + friendPair[1], + 0, + 0)) + + if hasattr(base.cr, 'playerFriendsManager'): + for avatarId in base.cr.playerFriendsManager.getAllOnlinePlayerAvatars(): + playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(avatarId) + playerFriendInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId) + if not base.cr.playerFriendsManager.askAvatarKnownElseWhere(avatarId): + if playerFriendInfo.understandableYesNo: + freeChatDouble.insert(0, (avatarId, + 0, + playerId, + 0)) + else: + speedChatDouble.insert(0, (avatarId, + 0, + playerId, + 0)) + + elif base.friendMode == 1: + for friendId in base.cr.avatarFriendsManager.avatarFriendsList: + friendInfo = base.cr.avatarFriendsManager.avatarId2Info[friendId] + playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(friendPair[0]) + if friendInfo.onlineYesNo: + newFriends.insert(0, (friendId, + 0, + playerId, + 0)) + + if self.panelType == FLPPets: + for objId, obj in base.cr.doId2do.items(): + from toontown.pets import DistributedPet + if isinstance(obj, DistributedPet.DistributedPet): + friendPair = (objId, 0) + petFriends.append(friendPair) + + if self.panelType == FLPEnemies: + for ignored in base.localAvatar.ignoreList: + newFriends.append((ignored, 0)) + + if self.panelType == FLPAll or self.panelType == FLPOnline: + if base.wantPets and base.localAvatar.hasPet(): + petFriends.insert(0, (base.localAvatar.getPetId(), 0)) + for friendPair in self.friends.keys(): + friendButton = self.friends[friendPair] + self.scrollList.removeItem(friendButton, refresh=0) + friendButton.destroy() + del self.friends[friendPair] + + newFriends.sort(compareFriends) + petFriends.sort(compareFriends) + freeChatOneRef.sort(compareFriends) + speedChatOneRef.sort(compareFriends) + freeChatDouble.sort(compareFriends) + speedChatDouble.sort(compareFriends) + offlineFriends.sort(compareFriends) + for friendPair in newFriends: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair) + if friendButton: + self.scrollList.addItem(friendButton, refresh=0) + self.friends[friendPair] = friendButton + + for friendPair in petFriends: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair, ToontownGlobals.ColorNoChat, 0) + if friendButton: + self.scrollList.addItem(friendButton, refresh=0) + self.friends[friendPair] = friendButton + + for friendPair in freeChatDouble: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair, ToontownGlobals.ColorFreeChat, 1) + if friendButton: + self.scrollList.addItem(friendButton, refresh=0) + self.friends[friendPair] = friendButton + + for friendPair in freeChatOneRef: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair, ToontownGlobals.ColorFreeChat, 0) + if friendButton: + self.scrollList.addItem(friendButton, refresh=0) + self.friends[friendPair] = friendButton + + for friendPair in speedChatDouble: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair, ToontownGlobals.ColorSpeedChat, 1) + if friendButton: + self.scrollList.addItem(friendButton, refresh=0) + self.friends[friendPair] = friendButton + + for friendPair in speedChatOneRef: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair, ToontownGlobals.ColorSpeedChat, 0) + if friendButton: + self.scrollList.addItem(friendButton, refresh=0) + self.friends[friendPair] = friendButton + + for friendPair in offlineFriends: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair, ToontownGlobals.ColorNoChat, 0) + if friendButton: + self.scrollList.addItem(friendButton, refresh=0) + self.friends[friendPair] = friendButton + + self.scrollList.index = self.listScrollIndex[self.panelType] + self.scrollList.refresh() + + def __updateTitle(self): + if self.panelType == FLPOnline: + self.title['text'] = TTLocalizer.FriendsListPanelOnlineFriends + elif self.panelType == FLPAll: + self.title['text'] = TTLocalizer.FriendsListPanelAllFriends + elif self.panelType == FLPPets: + self.title['text'] = TTLocalizer.FriendsListPanelPets + elif self.panelType == FLPPlayers: + self.title['text'] = TTLocalizer.FriendsListPanelPlayers + elif self.panelType == FLPOnlinePlayers: + self.title['text'] = TTLocalizer.FriendsListPanelOnlinePlayers + else: + self.title['text'] = TTLocalizer.FriendsListPanelIgnoredFriends + self.title.resetFrameSize() + + def __updateArrows(self): + if self.panelType == self.leftmostPanel: + self.left['state'] = 'inactive' + else: + self.left['state'] = 'normal' + if self.panelType == self.rightmostPanel: + self.right['state'] = 'inactive' + else: + self.right['state'] = 'normal' + + def __friendOnline(self, doId, commonChatFlags, whitelistChatFlags): + if self.panelType == FLPOnline: + self.__updateScrollList() + + def __friendOffline(self, doId): + if self.panelType == FLPOnline: + self.__updateScrollList() + + def __friendPlayers(self, doId): + if self.panelType == FLPPlayers: + self.__updateScrollList() + + def __friendsListChanged(self, arg1 = None, arg2 = None): + if self.panelType != FLPEnemies: + self.__updateScrollList() + + def __ignoreListChanged(self): + if self.panelType == FLPEnemies: + self.__updateScrollList() diff --git a/toontown/friends/TTPlayerFriendsManager.py b/toontown/friends/TTPlayerFriendsManager.py new file mode 100644 index 0000000..5cb1bc4 --- /dev/null +++ b/toontown/friends/TTPlayerFriendsManager.py @@ -0,0 +1,12 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.otpbase import OTPGlobals +from otp.friends.PlayerFriendsManager import PlayerFriendsManager + +class TTPlayerFriendsManager(PlayerFriendsManager): + + def __init__(self, cr): + PlayerFriendsManager.__init__(self, cr) + + def sendRequestInvite(self, playerId): + self.sendUpdate('requestInvite', [0, playerId, False]) diff --git a/toontown/friends/ToontownFriendSecret.py b/toontown/friends/ToontownFriendSecret.py new file mode 100644 index 0000000..647ab7f --- /dev/null +++ b/toontown/friends/ToontownFriendSecret.py @@ -0,0 +1,58 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +from otp.friends.FriendSecret import AccountSecret +from otp.friends.FriendSecret import AvatarSecret +from otp.friends.FriendSecret import BothSecrets +from otp.friends import FriendSecret +from otp.friends.FriendSecret import globalFriendSecret +from otp.friends.FriendSecret import showFriendSecret +from otp.friends.FriendSecret import hideFriendSecret +from otp.friends.FriendSecret import unloadFriendSecret + +def openFriendSecret(secretType): + global globalFriendSecret + if globalFriendSecret != None: + globalFriendSecret.unload() + globalFriendSecret = ToontownFriendSecret(secretType) + globalFriendSecret.enter() + return + + +FriendSecret.openFriendSecret = openFriendSecret + +class ToontownFriendSecret(FriendSecret.FriendSecret): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownFriendSecret') + + def __init__(self, secretType): + FriendSecret.FriendSecret.__init__(self, secretType) + self.initialiseoptions(ToontownFriendSecret) + + def makeFriendTypeButtons(self): + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.avatarButton = DirectButton(parent=self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.FriendInviterToon, text_scale=0.07, text_pos=(0.0, -0.1), pos=(-0.35, 0.0, -0.05), command=self._FriendSecret__handleAvatar) + avatarText = DirectLabel(parent=self, relief=None, pos=Vec3(0.35, 0, -0.3), text=TTLocalizer.FriendInviterToonFriendInfo, 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(parent=self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.FriendInviterPlayer, text_scale=0.07, text_pos=(0.0, -0.1), pos=(0.35, 0.0, -0.05), command=self._FriendSecret__handleAccount) + accountText = DirectLabel(parent=self, relief=None, pos=Vec3(-0.35, 0, -0.3), text=TTLocalizer.FriendInviterPlayerFriendInfo, 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 __determineSecret(self): + if self.secretType == BothSecrets: + self._FriendSecret__cleanupFirstPage() + self.ok1.hide() + self.changeOptions.hide() + self.nextText['text'] = TTLocalizer.FriendInviterBegin + self.nextText.setPos(0, 0, 0.3) + self.nextText.show() + self.avatarButton.show() + self.accountButton.show() + self.cancel.show() + else: + self._FriendSecret__getSecret() diff --git a/toontown/friends/__init__.py b/toontown/friends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/golf/BuildGeometry.py b/toontown/golf/BuildGeometry.py new file mode 100644 index 0000000..f973f7a --- /dev/null +++ b/toontown/golf/BuildGeometry.py @@ -0,0 +1,309 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from math import * +import math +GEO_ID = 0 + +def circleX(angle, radius, centerX, centerY): + x = radius * cos(angle) + centerX + return x + + +def circleY(angle, radius, centerX, centerY): + y = radius * sin(angle) + centerY + return y + + +def getCirclePoints(segCount, centerX, centerY, radius, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in range(0, segCount): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + coordX = wideX * circleX(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + return returnShape + + +def addCircle(attachNode, vertexCount, radius, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + targetGN = GeomNode('Circle Geom') + zFloat = 0.025 + targetCircleShape = getCirclePoints(5 + vertexCount, 0.0, 0.0, radius) + gFormat = GeomVertexFormat.getV3cp() + targetCircleVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + targetCircleVertexWriter = GeomVertexWriter(targetCircleVertexData, 'vertex') + targetCircleColorWriter = GeomVertexWriter(targetCircleVertexData, 'color') + targetCircleVertexWriter.addData3f(0.0, 0.0, zFloat) + targetCircleColorWriter.addData4f(color[0], color[1], color[2], color[3]) + for vertex in targetCircleShape: + targetCircleVertexWriter.addData3f(0.0 + vertex[0], 0.0 + vertex[1], zFloat) + targetCircleColorWriter.addData4f(color[0], color[1], color[2], color[3]) + + targetTris = GeomTrifans(Geom.UHStatic) + sizeTarget = len(targetCircleShape) + targetTris.addVertex(0) + for countVertex in range(1, sizeTarget + 1): + targetTris.addVertex(countVertex) + + targetTris.addVertex(1) + targetTris.closePrimitive() + targetGeom = Geom(targetCircleVertexData) + targetGeom.addPrimitive(targetTris) + attachNode.addGeom(targetGeom) + return targetGeom + + +def addCircleGeom(rootNode, vertexCount, radius, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + global GEO_ID + GN = GeomNode('Circle %s' % GEO_ID) + GEO_ID += 1 + NodePathGeom = rootNode.attachNewNode(GN) + geo = addCircle(GN, vertexCount, radius, color, layer) + return (NodePathGeom, GN, geo) + + +def addSquare(attachNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + targetGN = GeomNode('Square Geom') + sX = sizeX / 2.0 + sY = sizeY / 2.0 + color1 = color + color2 = color + color3 = color + gFormat = GeomVertexFormat.getV3n3cpt2() + boxVertexData = GeomVertexData('vertices', gFormat, Geom.UHDynamic) + boxVertexWriter = GeomVertexWriter(boxVertexData, 'vertex') + boxNormalWriter = GeomVertexWriter(boxVertexData, 'normal') + boxColorWriter = GeomVertexWriter(boxVertexData, 'color') + boxTextureWriter = GeomVertexWriter(boxVertexData, 'texcoord') + boxVertexWriter.addData3f(-sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(0.0, 1.0) + boxVertexWriter.addData3f(-sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(0.0, 0.0) + boxVertexWriter.addData3f(sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(1.0, 0.0) + boxVertexWriter.addData3f(sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTextureWriter.addData2f(1.0, 1.0) + boxTris = GeomTristrips(Geom.UHStatic) + boxTris.addVertex(1) + boxTris.addVertex(2) + boxTris.addVertex(0) + boxTris.addVertex(3) + boxTris.closePrimitive() + boxGeom = Geom(boxVertexData) + boxGeom.addPrimitive(boxTris) + attachNode.addGeom(boxGeom) + return boxGeom + + +def addSquareGeom(rootNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + global GEO_ID + GN = GeomNode('Square %s' % GEO_ID) + GEO_ID += 1 + NodePathGeom = rootNode.attachNewNode(GN) + geo = addSquare(GN, sizeX, sizeY, color, layer) + return (NodePathGeom, GN, geo) + + +def addBox(attachNode, sizeX, sizeY, sizeZ, color = Vec4(1.0, 1.0, 1.0, 1.0), darken = 0): + targetGN = GeomNode('Box Geom') + sX = sizeX / 2.0 + sY = sizeY / 2.0 + sZ = sizeZ / 2.0 + color1 = color + color2 = color + color3 = color + if darken: + color1 = color * 0.75 + color2 = color * 0.5 + color3 = color * 0.25 + gFormat = GeomVertexFormat.getV3n3cp() + boxVertexData = GeomVertexData('vertices', gFormat, Geom.UHDynamic) + boxVertexWriter = GeomVertexWriter(boxVertexData, 'vertex') + boxNormalWriter = GeomVertexWriter(boxVertexData, 'normal') + boxColorWriter = GeomVertexWriter(boxVertexData, 'color') + boxVertexWriter.addData3f(sX, sY, sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, -sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, sY, -sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, sY, sZ) + boxNormalWriter.addData3f(0, 1, 0) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, -sY, sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, -sY, -sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(sX, -sY, -sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(sX, -sY, sZ) + boxNormalWriter.addData3f(0, -1, 0) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(-sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(-sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(-sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, -1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color1[0], color1[1], color1[2], color1[3]) + boxVertexWriter.addData3f(-sX, sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, -sY, -sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color3[0], color3[1], color3[2], color3[3]) + boxVertexWriter.addData3f(-sX, -sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxVertexWriter.addData3f(-sX, sY, sZ) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color2[0], color2[1], color2[2], color2[3]) + boxTris = GeomTristrips(Geom.UHStatic) + boxTris.addVertex(0) + boxTris.addVertex(1) + boxTris.addVertex(3) + boxTris.addVertex(2) + boxTris.closePrimitive() + boxTris.addVertex(5) + boxTris.addVertex(6) + boxTris.addVertex(4) + boxTris.addVertex(7) + boxTris.closePrimitive() + boxTris.addVertex(9) + boxTris.addVertex(10) + boxTris.addVertex(8) + boxTris.addVertex(11) + boxTris.closePrimitive() + boxTris.addVertex(13) + boxTris.addVertex(14) + boxTris.addVertex(12) + boxTris.addVertex(15) + boxTris.closePrimitive() + boxTris.addVertex(16) + boxTris.addVertex(17) + boxTris.addVertex(19) + boxTris.addVertex(18) + boxTris.closePrimitive() + boxTris.addVertex(21) + boxTris.addVertex(22) + boxTris.addVertex(20) + boxTris.addVertex(23) + boxTris.closePrimitive() + boxGeom = Geom(boxVertexData) + boxGeom.addPrimitive(boxTris) + attachNode.addGeom(boxGeom) + return boxGeom + + +def addBoxGeom(rootNode, sizeX, sizeY, sizeZ, color = Vec4(1.0, 1.0, 1.0, 1.0), darken = 0): + global GEO_ID + GN = GeomNode('Box %s' % GEO_ID) + GEO_ID += 1 + nodePathGeom = rootNode.attachNewNode(GN) + geo = addBox(GN, sizeX, sizeY, sizeZ, color, darken) + return (nodePathGeom, GN, geo) + + +def addArrow(attachNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + targetGN = GeomNode('Arrow Geom') + sX = sizeX / 2.0 + sY = sizeY / 2.0 + color1 = color + color2 = color + color3 = color + gFormat = GeomVertexFormat.getV3n3cp() + boxVertexData = GeomVertexData('vertices', gFormat, Geom.UHDynamic) + boxVertexWriter = GeomVertexWriter(boxVertexData, 'vertex') + boxNormalWriter = GeomVertexWriter(boxVertexData, 'normal') + boxColorWriter = GeomVertexWriter(boxVertexData, 'color') + boxVertexWriter.addData3f(-sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(-sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, -sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTris = GeomTristrips(Geom.UHStatic) + boxTris.addVertex(1) + boxTris.addVertex(2) + boxTris.addVertex(0) + boxTris.addVertex(3) + boxTris.closePrimitive() + boxVertexWriter.addData3f(-sX * 2.0, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(sX * 2.0, sY, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxVertexWriter.addData3f(0.0, sY * 2.0, 0.0) + boxNormalWriter.addData3f(0, 0, 1) + boxColorWriter.addData4f(color[0], color[1], color[2], color[3]) + boxTris.addVertex(4) + boxTris.addVertex(5) + boxTris.addVertex(6) + boxTris.closePrimitive() + boxGeom = Geom(boxVertexData) + boxGeom.addPrimitive(boxTris) + attachNode.addGeom(boxGeom) + return boxGeom + + +def addArrowGeom(rootNode, sizeX, sizeY, color = Vec4(1.0, 1.0, 1.0, 1.0), layer = 0): + global GEO_ID + GN = GeomNode('Arrow %s' % GEO_ID) + GEO_ID += 1 + NodePathGeom = rootNode.attachNewNode(GN) + geo = addArrow(GN, sizeX, sizeY, color, layer) + return (NodePathGeom, GN, geo) diff --git a/toontown/golf/DistributedGolfCourse.py b/toontown/golf/DistributedGolfCourse.py new file mode 100644 index 0000000..873edf3 --- /dev/null +++ b/toontown/golf/DistributedGolfCourse.py @@ -0,0 +1,408 @@ +from direct.interval.IntervalGlobal import Sequence, Func, Wait, LerpColorScaleInterval, Parallel +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.task.Task import Task +from direct.showbase import PythonUtil +from toontown.distributed import DelayDelete +from toontown.distributed.DelayDeletable import DelayDeletable +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from toontown.golf import GolfGlobals +from toontown.golf import GolfScoreBoard +from toontown.golf import GolfRewardDialog +from toontown.toon import ToonHeadFrame + +class DistributedGolfCourse(DistributedObject.DistributedObject, FSM, DelayDeletable): + notify = directNotify.newCategory('DistributedGolfCourse') + defaultTransitions = {'Off': ['Join'], + 'Join': ['WaitStartHole', 'Cleanup'], + 'WaitStartHole': ['PlayHole', 'Cleanup', 'WaitReward'], + 'PlayHole': ['WaitFinishCourse', + 'WaitStartHole', + 'WaitReward', + 'Cleanup'], + 'WaitReward': ['WaitFinishCourse', 'Cleanup'], + 'WaitFinishCourse': ['Cleanup'], + 'Cleanup': ['Off']} + id = 0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, base.cr) + FSM.__init__(self, 'Golf_%s_FSM' % self.id) + self.waitingStartLabel = DirectLabel(text=TTLocalizer.MinigameWaitingForOtherPlayers, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingStartLabel.hide() + self.avIdList = [] + self.remoteAvIdList = [] + self.exitedAvIdList = [] + self.toonPanels = [] + self.exitedPanels = [] + self.exitedToonsWithPanels = [] + self.localAvId = base.localAvatar.doId + self.hasLocalToon = 0 + self.modelCount = 500 + self.cleanupActions = [] + self.courseId = None + self.scores = {} + self.curHoleIndex = 0 + self.golfRewardDialog = None + self.rewardIval = None + self.scoreBoard = None + self.exit = False + self.drivingToons = [] + return + + def generate(self): + self.notify.debug('GOLF COURSE: generate, %s' % self.getTitle()) + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if not self.hasLocalToon: + return + self.notify.debug('BASE: handleAnnounceGenerate: send setAvatarJoined') + self.__delayDelete = DelayDelete.DelayDelete(self, 'GolfCourse.self') + self.request('Join') + self.normalExit = 1 + count = self.modelCount + loader.beginBulkLoad('minigame', TTLocalizer.HeadingToMinigameTitle % self.getTitle(), count, 1, TTLocalizer.TIP_GOLF) + self.load() + globalClock.syncFrameTime() + self.onstage() + self.accept('clientCleanup', self._handleClientCleanup) + + def _handleClientCleanup(self): + self._destroyDelayDelete() + + def _destroyDelayDelete(self): + if self.__delayDelete: + self.__delayDelete.destroy() + self.__delayDelete = None + return + + def delete(self): + print 'GOLF COURSE DELETE' + self.ignore('clientCleanup') + if self.scoreBoard: + self.scoreBoard.delete() + DistributedObject.DistributedObject.delete(self) + if self.golfRewardDialog: + self.golfRewardDialog.delete() + self.cleanUpReward() + if self.toonPanels: + for x in range(len(self.toonPanels)): + self.toonPanels[x].destroy() + + self.toonPanels = None + self.scores = None + self.music.stop() + self.music = None + for avId in self.avIdList: + av = base.cr.doId2do.get(avId) + if av: + av.show() + + return + + def load(self): + self.music = base.loadMusic('phase_6/audio/bgm/GZ_PlayGolf.mid') + + def setCourseReady(self, numHoles, holeIds, coursePar): + self.notify.debug('GOLF COURSE: received setCourseReady') + if self.state == 'Cleanup': + return + self.numHoles = numHoles + self.holeIds = holeIds + self.coursePar = coursePar + for avId in self.avIdList: + blankScoreList = [0] * self.numHoles + self.scores[avId] = blankScoreList + + self.request('WaitStartHole') + for avId in self.avIdList: + av = base.cr.doId2do.get(avId) + if av: + av.show() + av.reparentTo(render) + av.setPos(0, 0, -100) + else: + self.notify.warning('avId =%d does not exist') + + self.scoreBoard = GolfScoreBoard.GolfScoreBoard(self) + toonPanelsStart = 0.3 + whichToon = 0 + color = 0 + tpDiff = -0.45 + headPanel = loader.loadModel('phase_6/models/golf/headPanel') + if self.numPlayers > 0: + for avId in self.avIdList: + if not self.localAvId == avId: + av = base.cr.doId2do.get(avId) + if av: + tPanels = ToonHeadFrame.ToonHeadFrame(av, GolfGlobals.PlayerColors[color], headPanel) + tPanels.setPos(-1.17, 0, toonPanelsStart + whichToon * tpDiff) + tPanels.setScale(0.3, 1, 0.7) + tPanels.head.setPos(0, 10, 0.18) + tPanels.head.setScale(0.47, 0.2, 0.2) + tPanels.tag1.setPos(0.3, 10, 0.18) + tPanels.tag1.setScale(0.1283, 0.055, 0.055) + tPanels.tag2.setPos(0, 10, 0.43) + tPanels.tag2.setScale(0.117, 0.05, 0.05) + self.toonPanels.append(tPanels) + whichToon = whichToon + 1 + color += 1 + else: + color += 1 + + else: + self.toonPanels = None + for avId in self.exitedAvIdList: + if avId not in self.exitedToonsWithPanels: + self.exitMessageForToon(avId) + + return + + def setPlayHole(self): + self.notify.debug('GOLF COURSE: received setPlayHole') + if self.state not in ['PlayHole', 'Cleanup']: + self.request('PlayHole') + + def getTitle(self): + return GolfGlobals.getCourseName(self.courseId) + + def getInstructions(self): + return 'You should not be seeing this' + + def setGolferIds(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.hasLocalToon = self.localAvId in self.avIdList + if not self.hasLocalToon: + self.notify.warning('localToon (%s) not in list of golfers: %s' % (self.localAvId, self.avIdList)) + return + self.notify.info('GOLF COURSE: setParticipants: %s' % self.avIdList) + self.remoteAvIdList = [] + for avId in self.avIdList: + if avId != self.localAvId: + self.remoteAvIdList.append(avId) + + def setCourseAbort(self, avId): + if avId == self.localAvId or avId == 0: + if not self.hasLocalToon: + return + self.notify.warning('GOLF COURSE: setGameAbort: Aborting game') + self.normalExit = 0 + if not self.state == 'Cleanup': + self.request('Cleanup') + else: + self.notify.warning('GOLF COURSE: Attempting to clean up twice') + + def onstage(self): + self.notify.debug('GOLF COURSE: onstage') + base.playMusic(self.music, looping=1, volume=0.9) + + def avExited(self, avId): + self.exitedAvIdList.append(avId) + hole = base.cr.doId2do.get(self.curHoleDoId) + if hole: + hole.avExited(avId) + if self.localAvId == avId: + self.notify.debug('forcing setCourseAbort') + if self.state == 'Join': + loader.endBulkLoad('minigame') + self.setCourseAbort(0) + self.exitMessageForToon(avId) + + def exitMessageForToon(self, avId): + if self.toonPanels and self.localAvId != avId: + y = 0 + for x in range(len(self.avIdList)): + if avId == self.avIdList[x] and y < len(self.toonPanels): + toonPanel = self.toonPanels[y] + toonPanel.headModel.hide() + exitedToon = DirectLabel(parent=self.toonPanels[y], relief=None, pos=(0, 0, 0.4), color=(1, 1, 1, 1), text_align=TextNode.ACenter, text=TTLocalizer.GolferExited % toonPanel.av.getName(), text_scale=0.07, text_wordwrap=6) + exitedToon.setScale(2, 1, 1) + self.exitedPanels.append(exitedToon) + self.exitedToonsWithPanels.append(avId) + toonPanel.removeAvKeep() + elif not self.avIdList[x] == self.localAvId: + y += 1 + + return + + def enterJoin(self): + self.sendUpdate('setAvatarJoined', []) + + def handleFallingAsleepGolf(self, task): + base.localAvatar.stopSleepWatch() + base.localAvatar.forceGotoSleep() + self.sendUpdate('setAvatarExited', []) + + def exitJoin(self): + pass + + def enterWaitStartHole(self): + self.sendUpdate('setAvatarReadyCourse', []) + + def exitWaitStartHole(self): + pass + + def enterPlayHole(self): + loader.endBulkLoad('minigame') + + def exitPlayHole(self): + pass + + def enterCleanup(self): + print 'GOLF COURSE CLEANUP' + base.localAvatar.stopSleepWatch() + for action in self.cleanupActions: + action() + + self.cleanupActions = [] + if not self.scoreBoard == None: + self.scoreBoard.delete() + if self.toonPanels: + for x in range(len(self.toonPanels)): + self.toonPanels[x].destroy() + + self.toonPanels = None + for avId in self.avIdList: + av = base.cr.doId2do.get(avId) + if av: + av.show() + av.resetLOD() + + self.ignoreAll() + if self.hasLocalToon: + messenger.send('leavingGolf') + self._destroyDelayDelete() + return + + def exitCleanup(self): + pass + + def setCourseId(self, courseId): + self.courseId = courseId + + def calcHolesToUse(self): + retval = [] + while len(retval) < self.numHoles: + for holeId in self.courseInfo['holeIds']: + retval.append(holeId) + if len(retval) >= self.numHoles: + break + + return retval + + def calcCoursePar(self): + retval = 0 + for holeId in self.holeIds: + holeInfo = GolfGlobals.HoleInfo[holeId] + retval += holeInfo['par'] + + return retval + + def setScores(self, scoreList): + scoreList.reverse() + for avId in self.avIdList: + avScores = [] + for holeIndex in range(self.numHoles): + avScores.append(scoreList.pop()) + + self.scores[avId] = avScores + + self.notify.debug('self.scores=%s' % self.scores) + + def setCurHoleIndex(self, holeIndex): + self.curHoleIndex = holeIndex + + def setCurHoleDoId(self, holeDoId): + self.curHoleDoId = holeDoId + + def getCurGolfer(self): + if self.curHoleDoId != 0: + av = base.cr.doId2do.get(self.curHoleDoId) + if av: + return av.currentGolfer + else: + return None + return None + + def getStrokesForCurHole(self, avId): + retval = 0 + if avId in self.scores: + retval = self.scores[avId][self.curHoleIndex] + return retval + + def isGameDone(self): + retval = False + self.notify.debug('Self state is: %s' % self.state) + if self.getCurrentOrNextState() == 'WaitReward' or self.getCurrentOrNextState() == 'WaitFinishCourse': + retval = True + return retval + + def setReward(self, trophiesList, rankingsList, holeBestList, courseBestList, cupList, tieBreakWinner, aim0, aim1, aim2, aim3): + self.trophiesList = trophiesList + self.rankingsList = rankingsList + self.holeBestList = holeBestList + self.courseBestList = courseBestList + self.cupList = cupList + self.tieBreakWinner = tieBreakWinner + self.aimTimesList = [aim0, + aim1, + aim2, + aim3] + if self.state not in ['Cleanup']: + self.demand('WaitReward') + + def enterWaitReward(self): + self.scoreBoard.showBoardFinal() + if self.curHoleDoId != 0: + av = base.cr.doId2do.get(self.curHoleDoId) + av.cleanupPowerBar() + + def doneWithRewardMovie(): + if self.exit == False: + self.notify.debug('doneWithRewardMovie') + self.sendUpdate('setDoneReward', []) + self._destroyDelayDelete() + self.exit = True + + self.golfRewardDialog = GolfRewardDialog.GolfRewardDialog(self.avIdList, self.trophiesList, self.rankingsList, self.holeBestList, self.courseBestList, self.cupList, self.localAvId, self.tieBreakWinner, self.aimTimesList) + self.rewardIval = Sequence(Parallel(Wait(5), self.golfRewardDialog.getMovie()), Func(doneWithRewardMovie)) + self.rewardIval.start() + + def exitEarly(self): + if self.exit == False: + self.notify.debug('doneWithRewardMovie') + self.sendUpdate('setDoneReward', []) + self._destroyDelayDelete() + self.exit = True + + def exitReward(self): + self.cleanUpReward() + + def cleanUpReward(self): + if self.rewardIval: + self.rewardIval.pause() + self.rewardIval = None + return + + def updateScoreBoard(self): + if self.scoreBoard: + self.scoreBoard.update() + + def changeDrivePermission(self, avId, canDrive): + if canDrive: + if avId not in self.drivingToons: + self.drivingToons.append(avId) + elif avId in self.drivingToons: + self.drivingToons.remove(avId) + + def canDrive(self, avId): + retval = avId in self.drivingToons + return retval diff --git a/toontown/golf/DistributedGolfCourseAI.py b/toontown/golf/DistributedGolfCourseAI.py new file mode 100644 index 0000000..bc0183d --- /dev/null +++ b/toontown/golf/DistributedGolfCourseAI.py @@ -0,0 +1,971 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.golf import DistributedGolfHoleAI +from pandac.PandaModules import * +from direct.fsm.FSM import FSM +from toontown.ai.ToonBarrier import * +from toontown.golf import GolfGlobals +INITIAL = 0 +EXITED = 1 +EXPECTED = 2 +JOINED = 3 +READY = 4 +ONHOLE = 5 +BALLIN = 6 +JOIN_TIMEOUT = 30 +READY_TIMEOUT = 30 +EXIT_TIMEOUT = 30 +REWARD_TIMEOUT = 30 + +class DistributedGolfCourseAI(DistributedObjectAI.DistributedObjectAI, FSM): + notify = directNotify.newCategory('DistributedGolfCourseAI') + defaultTransitions = {'Off': ['WaitJoin'], 'WaitJoin': ['WaitReadyCourse', 'Cleanup'], 'WaitReadyCourse': ['WaitReadyHole', 'Cleanup'], 'WaitReadyHole': ['PlayHole', 'Cleanup', 'WaitLeaveHole', 'WaitReward'], 'PlayHole': ['PlayHole', 'WaitLeaveHole', 'Cleanup', 'WaitReward'], 'WaitLeaveHole': ['WaitReadyHole', 'WaitLeaveCourse', 'Cleanup', 'WaitReward'], 'WaitReward': ['WaitLeaveCourse', 'Cleanup', 'WaitLeaveHole'], 'WaitLeaveCourse': ['Cleanup'], 'Cleanup': ['Off']} + + def __init__(self, zoneId, avIds, courseId, preferredHoleId=None): + FSM.__init__(self, 'GolfCourse_%s_FSM' % zoneId) + DistributedObjectAI.DistributedObjectAI.__init__(self, simbase.air) + self.notify.debug('GOLF COURSE: init') + self.zoneId = zoneId + self.currentHole = None + self.avIdList = [] + self.avStateDict = {} + self.addExpectedGolfers(avIds) + self.courseId = courseId + self.preferredHoleId = preferredHoleId + self.courseInfo = GolfGlobals.CourseInfo[self.courseId] + self.numHoles = self.courseInfo['numHoles'] + self.holeIds = self.calcHolesToUse() + self.notify.debug('self.holeIds = %s' % self.holeIds) + self.numHolesPlayed = 0 + self.curHoleIndex = 0 + self.trophyListLen = 0 + self.courseBestListLen = 0 + self.holeBestListLen = 0 + self.cupListLen = 0 + self.scores = {} + self.aimTimes = {} + self.startingHistory = {} + self.endingHistory = {} + self.startingHoleBest = {} + self.endingHoleBest = {} + self.startingCourseBest = {} + self.endingCourseBest = {} + self.startingCups = {} + self.endingCups = {} + self.initHistory() + self.newTrophies = {} + self.newHoleBest = {} + self.newCourseBest = {} + self.newCups = {} + self.drivingToons = [] + self.__barrier = None + self.winnerByTieBreak = 0 + return + + def initHistory(self): + for avId in self.avIdList: + av = simbase.air.doId2do.get(avId) + if av: + history = av.getGolfHistory() + self.startingHistory[avId] = history[:] + self.endingHistory[avId] = history[:] + holeBest = av.getGolfHoleBest() + self.startingHoleBest[avId] = holeBest[:] + self.endingHoleBest[avId] = holeBest[:] + courseBest = av.getGolfCourseBest() + self.startingCourseBest[avId] = courseBest[:] + self.endingCourseBest[avId] = courseBest[:] + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.grabGolfers() + + def delete(self): + self.notify.debug('GOLF COURSE: delete: deleting AI GolfCourse object') + if hasattr(self, 'rewardBarrier'): + self.rewardBarrier.cleanup() + del self.rewardBarrier + if self.currentHole: + self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId) + self.currentHole.requestDelete() + self.currentHole = None + self.ignoreAll() + from toontown.golf import GolfManagerAI + GolfManagerAI.GolfManagerAI().removeCourse(self) + if self.__barrier: + self.__barrier.cleanup() + self.__barrier = None + DistributedObjectAI.DistributedObjectAI.delete(self) + return + + def load(self): + self.b_setCourseReady() + self.request('WaitReadyCourse') + + def getZoneId(self): + return self.zoneId + + def addExpectedGolfers(self, avIdList): + self.notify.debug('Sending %s to course %s' % (avIdList, self.zoneId)) + for avId in avIdList: + golfer = simbase.air.doId2do.get(avId) + if golfer: + if avId not in self.avIdList: + self.avIdList.append(avId) + self.avStateDict[avId] = INITIAL + elif self.avStateDict[avId] == EXITED: + if self.isGenerated(): + pass + else: + self.notify.warning('GOLF COURSE: trying to grab golfer %s that is already on the course' % avId) + + def grabGolfers(self): + for avId in self.avIdList: + golfer = simbase.air.doId2do.get(avId) + if golfer: + if self.avStateDict[avId] == INITIAL: + self.avStateDict[avId] = EXPECTED + + self.request('WaitJoin') + + def getGolferIds(self): + return self.avIdList + + def checkGolferPlaying(self, avId): + if self.avStateDict[avId] == ONHOLE: + return 1 + else: + return 0 + + def b_setCourseReady(self): + self.setCourseReady() + self.d_setCourseReady() + + def d_setCourseReady(self): + self.notify.debug('GOLF COURSE: Sending setCourseReady') + self.sendUpdate('setCourseReady', [self.numHoles, self.holeIds, self.calcCoursePar()]) + + def setCourseReady(self): + self.notify.debug('GOLF COURSE: setCourseReady: golf course ready with avatars: %s' % self.avIdList) + self.trophyListLen = 0 + self.courseBestListLen = 0 + self.holeBestListLen = 0 + self.cupListLen = 0 + self.normalExit = 1 + + def d_setPlayHole(self): + self.notify.debug('GOLF COURSE: setPlayHole: play on golf hole about to start') + self.sendUpdate('setPlayHole', []) + + def b_setCourseExit(self): + self.d_setCourseExit() + self.setCourseExit() + + def d_setCourseExit(self): + self.notify.debug('GOLF COURSE: Sending setGameExit') + self.sendUpdate('setCourseExit', []) + + def setCourseExit(self): + self.notify.debug('GOLF COURSE: setGameExit') + + def handleExitedAvatar(self, avId): + self.notify.warning('GOLF COURSE: handleExitedAvatar: avatar id exited: ' + str(avId)) + self.avStateDict[avId] = EXITED + self.sendUpdate('avExited', [avId]) + if self.currentHole and not self.haveAllGolfersExited(): + self.currentHole.avatarDropped(avId) + if self.haveAllGolfersExited(): + self.setCourseAbort() + elif self.isCurHoleDone(): + if self.isPlayingLastHole(): + if self.state not in ['WaitReward', 'WaitReadyHole']: + self.safeDemand('WaitReward') + else: + self.notify.debug('allBalls are in holes, calling holeOver') + self.holeOver() + if hasattr(self, 'rewardBarrier'): + if self.rewardBarrier: + self.rewardBarrier.clear(avId) + if hasattr(self, '__barrier'): + if self.__barrier: + self.__.clear(avId) + + def startNextHole(self): + self.notify.debugStateCall(self) + holeId = self.holeIds[self.numHolesPlayed] + self.currentHole = DistributedGolfHoleAI.DistributedGolfHoleAI(self.zoneId, golfCourse=self, holeId=holeId) + self.currentHole.generateWithRequired(self.zoneId) + self.d_setCurHoleDoId(self.currentHole.doId) + self.safeDemand('WaitReadyHole') + + def holeOver(self): + self.notify.debug('GOLF COURSE: holeOver') + self.numHolesPlayed += 1 + if self.numHolesPlayed < self.numHoles: + self.b_setCurHoleIndex(self.numHolesPlayed) + self.safeDemand('WaitLeaveHole') + + def setCourseAbort(self): + self.notify.debug('GOLF COURSE: setGameAbort') + self.normalExit = 0 + self.sendUpdate('setCourseAbort', [0]) + self.safeDemand('Cleanup') + + def enterOff(self): + self.notify.debug('GOLF COURSE: enterOff') + + def exitOff(self): + self.notify.debug('GOLF COURSE: exitOff') + + def enterWaitJoin(self): + self.notify.debug('GOLF COURSE: enterWaitJoin') + for avId in self.avIdList: + self.avStateDict[avId] = EXPECTED + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId]) + + def allAvatarsJoined(self=self): + self.notify.debug('GOLF COURSE: all avatars joined') + self.load() + + def handleTimeout(avIds, self=self): + self.notify.debug('GOLF COURSE: timed out waiting for clients %s to join' % avIds) + for avId in self.avStateDict: + if not self.avStateDict[avId] == JOINED: + self.handleExitedAvatar(avId) + + if self.haveAllGolfersExited(): + self.setCourseAbort() + else: + self.load() + + self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout) + + def exitWaitJoin(self): + self.notify.debugStateCall(self) + self.__barrier.cleanup() + self.__barrier = None + return + + def setAvatarJoined(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('GOLF COURSE: setAvatarJoined: avatar id joined: ' + str(avId)) + self.avStateDict[avId] = JOINED + self.notify.debug('GOLF COURSE: setAvatarJoined: new states: ' + str(self.avStateDict)) + if hasattr(self, '_DistributedGolfCourseAI__barrier') and self.__barrier: + self.__barrier.clear(avId) + else: + self.notify.warning('setAvatarJoined avId=%d but barrier is invalid' % avId) + + def exitFrameworkWaitClientsJoin(self): + self.__barrier.cleanup() + del self.__barrier + + def enterWaitReadyCourse(self): + self.notify.debug('GOLF COURSE: enterWaitReadyCourse') + + def allAvatarsInCourse(self=self): + self.notify.debug('GOLF COURSE: all avatars ready course') + for avId in self.avIdList: + blankScoreList = [ + 0] * self.numHoles + self.scores[avId] = blankScoreList + self.aimTimes[avId] = 0 + + self.notify.debug('self.scores = %s' % self.scores) + self.startNextHole() + + def handleTimeout(avIds, self=self): + self.notify.debug("GOLF COURSE: Course timed out waiting for clients %s to report 'ready'" % avIds) + if self.haveAllGolfersExited(): + self.setCourseAbort() + else: + allAvatarsInCourse() + + self.__barrier = ToonBarrier('WaitReadyCourse', self.uniqueName('WaitReadyCourse'), self.avIdList, READY_TIMEOUT, allAvatarsInCourse, handleTimeout) + for avId in self.avStateDict.keys(): + if self.avStateDict[avId] == READY: + self.__barrier.clear(avId) + + def setAvatarReadyCourse(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('GOLF COURSE: setAvatarReadyCourse: avatar id ready: ' + str(avId)) + self.avStateDict[avId] = READY + self.notify.debug('GOLF COURSE: setAvatarReadyCourse: new avId states: ' + str(self.avStateDict)) + if self.state == 'WaitReadyCourse': + self.__barrier.clear(avId) + + def exitWaitReadyCourse(self): + self.notify.debugStateCall(self) + self.__barrier.cleanup() + self.__barrier = None + return + + def enterWaitReadyHole(self): + self.notify.debug('GOLF COURSE: enterWaitReadyHole') + + def allAvatarsInHole(self=self): + self.notify.debug('GOLF COURSE: all avatars ready hole') + if self.safeDemand('PlayHole'): + self.d_setPlayHole() + + def handleTimeout(avIds, self=self): + self.notify.debug("GOLF COURSE: Hole timed out waiting for clients %s to report 'ready'" % avIds) + if self.haveAllGolfersExited(): + self.setCourseAbort() + else: + if self.safeDemand('PlayHole'): + self.d_setPlayHole() + + stillPlaying = self.getStillPlayingAvIds() + self.__barrier = ToonBarrier('WaitReadyHole', self.uniqueName('WaitReadyHole'), stillPlaying, READY_TIMEOUT, allAvatarsInHole, handleTimeout) + for avId in self.avStateDict.keys(): + if self.avStateDict[avId] == ONHOLE: + self.__barrier.clear(avId) + + def exitWaitReadyHole(self): + self.notify.debugStateCall(self) + if hasattr(self, '__barrier'): + self.__barrier.cleanup() + self.__barrier = None + return + + def getStillPlayingAvIds(self): + retval = [] + for avId in self.avIdList: + av = simbase.air.doId2do.get(avId) + if av: + if self.avStateDict.has_key(avId) and not self.avStateDict[avId] == EXITED: + retval.append(avId) + + return retval + + def avatarReadyHole(self, avId): + if self.state not in ['WaitJoin', 'WaitReadyCourse', 'WaitReadyHole']: + self.notify.debug('GOLF COURSE: Ignoring setAvatarReadyHole message') + return + self.notify.debug('GOLF COURSE: setAvatarReadyHole: avatar id ready: ' + str(avId)) + self.avStateDict[avId] = ONHOLE + self.notify.debug('GOLF COURSE: setAvatarReadyHole: new avId states: ' + str(self.avStateDict)) + if self.state == 'WaitReadyHole': + self.__barrier.clear(avId) + + def enterPlayHole(self): + self.notify.debug('GOLF COURSE: enterPlayHole') + if self.currentHole and not self.currentHole.playStarted: + self.currentHole.startPlay() + + def exitPlayHole(self): + self.notify.debug('GOLF COURSE: exitPlayHole') + + def enterWaitLeaveHole(self): + self.notify.debugStateCall(self) + self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId) + self.currentHole.requestDelete() + self.currentHole = None + if self.numHolesPlayed >= self.numHoles: + pass + else: + self.startNextHole() + return + + def exitWaitLeaveHole(self): + pass + + def enterWaitReward(self): + self.updateHistoryForCourseComplete() + self.awardTrophies() + self.awardCups() + self.awardHoleBest() + self.awardCourseBest() + self.recordHoleInOne() + self.recordCourseUnderPar() + trophiesList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newTrophies: + oneTrophyList = self.newTrophies[avId] + trophiesList.append(oneTrophyList) + else: + trophiesList.append([]) + + while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + trophiesList.append([]) + + holeBestList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newHoleBest: + oneTrophyList = self.newHoleBest[avId] + holeBestList.append(oneTrophyList) + else: + holeBestList.append([]) + + while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + holeBestList.append([]) + + courseBestList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newCourseBest: + oneTrophyList = self.newCourseBest[avId] + courseBestList.append(oneTrophyList) + else: + courseBestList.append([]) + + while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + courseBestList.append([]) + + cupList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId in self.newCups: + oneCupList = self.newCups[avId] + cupList.append(oneCupList) + self.cupListLen = self.cupListLen + 1 + else: + cupList.append([]) + + while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE: + cupList.append([]) + + REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen + self.courseBestListLen + self.cupListLen) * 5 + 19 + aimTimesList = [ + 0] * 4 + aimIndex = 0 + stillPlaying = self.getStillPlayingAvIds() + for avId in self.avIdList: + if avId in stillPlaying: + aimTime = 0 + if avId in self.aimTimes: + aimTime = self.aimTimes[avId] + aimTimesList[aimIndex] = aimTime + aimIndex += 1 + + self.sendUpdate('setReward', [trophiesList, self.rankings, holeBestList, courseBestList, cupList, self.winnerByTieBreak, aimTimesList[0], aimTimesList[1], aimTimesList[2], aimTimesList[3]]) + + def allAvatarsRewarded(self=self): + self.notify.debug('GOLF COURSE: all avatars rewarded') + self.rewardDone() + + def handleRewardTimeout(avIds, self=self): + self.notify.debug('GOLF COURSE: timed out waiting for clients %s to finish reward' % avIds) + self.rewardDone() + + stillPlaying = self.getStillPlayingAvIds() + self.rewardBarrier = ToonBarrier('waitReward', self.uniqueName('waitReward'), stillPlaying, REWARD_TIMEOUT, allAvatarsRewarded, handleRewardTimeout) + + def exitWaitReward(self): + pass + + def enterWaitLeaveCourse(self): + self.notify.debugStateCall(self) + self.setCourseAbort() + + def exitWaitLeaveCourse(self): + pass + + def enterCleanup(self): + self.notify.debug('GOLF COURSE: enterCleanup') + self.requestDelete() + + def exitCleanup(self): + self.notify.debug('GOLF COURSE: exitCleanup') + + def isCurHoleDone(self): + retval = False + if self.areAllBallsInHole(): + retval = True + else: + retval = True + for state in self.avStateDict.values(): + if not (state == BALLIN or state == EXITED): + retval = False + break + + return retval + + def areAllBallsInHole(self): + self.notify.debug('areAllBallsInHole, self.avStateDict=%s' % self.avStateDict) + allBallsInHole = True + for state in self.avStateDict.values(): + if state != BALLIN: + allBallsInHole = False + + return allBallsInHole + + def isPlayingLastHole(self): + retval = self.numHoles - self.numHolesPlayed == 1 + return retval + + def setBallIn(self, avId): + self.notify.debug('setBallIn %d' % avId) + if self.avStateDict[avId] == BALLIN: + self.notify.debug('setBallIn already in BALLIN state, just returning') + return + self.avStateDict[avId] = BALLIN + self.updateHistoryForBallIn(avId) + if self.isCurHoleDone(): + if self.isPlayingLastHole(): + if self.state != 'WaitReward': + self.safeDemand('WaitReward') + else: + self.notify.debug('allBalls are in holes, calling holeOver') + self.holeOver() + + def updateHistoryForBallIn(self, avId): + if self.currentHole == None: + return + holeId = self.currentHole.holeId + holeInfo = GolfGlobals.HoleInfo[holeId] + par = holeInfo['par'] + holeIndex = self.numHolesPlayed + if holeIndex >= self.numHoles: + self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex) + holeIndex = self.numHoles - 1 + else: + if holeIndex < 0: + self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex) + holeIndex = 0 + strokes = self.scores[avId][holeIndex] + self.notify.debug('self.scores = %s' % self.scores) + diff = strokes - par + if strokes == 1: + self.incrementEndingHistory(avId, GolfGlobals.HoleInOneShots) + if diff <= -2: + self.incrementEndingHistory(avId, GolfGlobals.EagleOrBetterShots) + if diff <= -1: + self.incrementEndingHistory(avId, GolfGlobals.BirdieOrBetterShots) + if diff <= 0: + self.endingHistory[avId][GolfGlobals.ParOrBetterShots] += 1 + if strokes < self.endingHoleBest[avId][holeId] or self.endingHoleBest[avId][holeId] == 0: + self.endingHoleBest[avId][holeId] = strokes + return + + def incrementEndingHistory(self, avId, historyIndex): + if self.endingHistory.has_key(avId) and GolfGlobals.TrophyRequirements.has_key(historyIndex): + maximumAmount = GolfGlobals.TrophyRequirements[historyIndex][-1] + if self.endingHistory[avId][historyIndex] < maximumAmount: + self.endingHistory[avId][historyIndex] += 1 + + def getCourseId(self): + return self.courseId + + def abortCurrentHole(self): + holeId = self.currentHole.holeId + holeDoId = self.currentHole.doId + self.currentHole.finishHole() + return ( + holeId, holeDoId) + + def calcHolesToUse(self): + retval = [] + if simbase.air.config.GetBool('golf-course-randomized', 1): + retval = self.calcHolesToUseRandomized(self.courseId) + self.notify.debug('randomized courses!') + for x in range(len(retval)): + self.notify.debug('Hole is: %s' % retval[x]) + + else: + validHoles = self.calcUniqueHoles(self.courseId) + if self.preferredHoleId in validHoles: + retval.append(self.preferredHoleId) + while len(retval) < self.numHoles: + for holeId in GolfGlobals.CourseInfo[self.courseId]['holeIds']: + if type(holeId) == type(0): + retval.append(holeId) + elif type(holeId) == type(()): + retval.append(holeId[0]) + else: + self.notify.warning('cant handle %s' % self.holeId) + if len(retval) >= self.numHoles: + break + + return retval + + def incrementScore(self, avId): + self.notify.debug('incrementScore self.scores=%s avId=%s' % (self.scores, avId)) + self.scores[avId][self.numHolesPlayed] += 1 + self.notify.debug('after increment self.score=%s' % self.scores) + self.sendScores() + + def sendScores(self): + self.notify.debug('sendScores self.scores = %s' % self.scores) + scorelist = [] + for avId in self.avIdList: + for score in self.scores[avId]: + scorelist.append(score) + + self.sendUpdate('setScores', [scorelist]) + self.notify.debug('sendScores end self.scores = %s' % self.scores) + + def getCurHoleIndex(self): + return self.curHoleIndex + + def b_setCurHoleIndex(self, holeIndex): + self.setCurHoleIndex(holeIndex) + self.d_setCurHoleIndex(holeIndex) + + def d_setCurHoleIndex(self, holeIndex): + self.sendUpdate('setCurHoleIndex', [holeIndex]) + + def setCurHoleIndex(self, holeIndex): + self.curHoleIndex = holeIndex + + def setDoneReward(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('got rewardDone from %d' % avId) + if hasattr(self, 'rewardBarrier'): + self.rewardBarrier.clear(avId) + self.sendUpdate('setCourseAbort', [avId]) + + def rewardDone(self): + self.notify.debug('rewardDone') + self.holeOver() + self.safeDemand('WaitLeaveCourse') + + def updateHistoryForCourseComplete(self): + self.calcRankings() + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + self.incrementEndingHistory(avId, GolfGlobals.CoursesCompleted) + coursePar = self.calcCoursePar() + totalScore = self.getTotalScore(avId) + if totalScore < coursePar: + self.incrementEndingHistory(avId, GolfGlobals.CoursesUnderPar) + if len(stillPlaying) > 1: + self.incrementEndingHistory(avId, GolfGlobals.MultiPlayerCoursesCompleted) + if self.rankingsById[avId] == 1: + if self.courseId == 0: + self.incrementEndingHistory(avId, GolfGlobals.CourseZeroWins) + elif self.courseId == 1: + self.incrementEndingHistory(avId, GolfGlobals.CourseOneWins) + elif self.courseId == 2: + self.incrementEndingHistory(avId, GolfGlobals.CourseTwoWins) + else: + self.notify.warning('unhandled case, self.courseId=%s' % self.courseId) + if totalScore < self.endingCourseBest[avId][self.courseId] or self.endingCourseBest[avId][self.courseId] == 0: + self.endingCourseBest[avId][self.courseId] = totalScore + + def calcRankings(self): + stillPlaying = self.getStillPlayingAvIds() + self.rankings = [] + totalScores = [] + for avId in self.avIdList: + aimTime = 0 + if avId in self.aimTimes: + aimTime = self.aimTimes[avId] + if avId in stillPlaying: + totalScores.append((avId, self.getTotalScore(avId), aimTime)) + else: + totalScores.append((avId, 255, aimTime)) + + def scoreCompareNoTime(tupleA, tupleB): + if tupleA[1] > tupleB[1]: + return 1 + elif tupleA[1] == tupleB[1]: + return 0 + else: + return -1 + + def scoreCompareWithTime(tupleA, tupleB): + if tupleA[1] > tupleB[1]: + return 1 + elif tupleA[1] == tupleB[1]: + if tupleA[2] > tupleB[2]: + return 1 + elif tupleA[2] == tupleB[2]: + return 0 + else: + return -1 + else: + return -1 + + if GolfGlobals.TIME_TIE_BREAKER: + totalScores.sort(scoreCompareWithTime) + else: + totalScores.sort(scoreCompareNoTime) + curRank = 0 + oldScore = 0 + oldTime = 0 + self.rankingsById = {} + for scoreTuple in totalScores: + time = scoreTuple[2] + score = scoreTuple[1] + avId = scoreTuple[0] + if score > oldScore or GolfGlobals.TIME_TIE_BREAKER and score == oldScore and time > oldTime: + curRank += 1 + oldScore = score + oldTime = time + self.rankingsById[avId] = curRank + + tiedForFirst = [] + tempRank = 0 + oldScore = 0 + oldTime = 0 + for scoreTuple in totalScores: + time = scoreTuple[2] + score = scoreTuple[1] + avId = scoreTuple[0] + if score > oldScore: + tempRank += 1 + oldScore = score + oldTime = time + if tempRank == 1: + tiedForFirst.append(avId) + + for avId in self.avIdList: + if avId in stillPlaying: + self.rankings.append(self.rankingsById[avId]) + else: + self.rankings.append(-1) + + if len(tiedForFirst) >= 2 and not GolfGlobals.TIME_TIE_BREAKER: + winnerAvId = random.choice(tiedForFirst) + winnerIndex = self.avIdList.index(winnerAvId) + self.winnerByTieBreak = winnerAvId + for index in xrange(len(self.rankings)): + if self.rankings[index] > 0 and index != winnerIndex: + self.rankings[index] += 1 + + for avId in self.rankingsById: + if self.rankingsById[avId] > 0 and avId != winnerAvId: + self.rankingsById[avId] += 1 + + elif len(tiedForFirst) >= 2: + winnerAvId = totalScores[0][0] + self.winnerByTieBreak = winnerAvId + + def awardTrophies(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldHistory = self.startingHistory[avId] + endingHistory = self.endingHistory[avId] + oldTrophies = GolfGlobals.calcTrophyListFromHistory(oldHistory) + endingTrophies = GolfGlobals.calcTrophyListFromHistory(endingHistory) + av.b_setGolfHistory(endingHistory) + newTrophies = [] + for index in xrange(len(oldTrophies)): + if not oldTrophies[index] and endingTrophies[index]: + self.notify.debug('New Trophy %d' % index) + self.air.writeServerEvent('golf_trophy', avId, '%s' % index) + newTrophies.append(True) + self.trophyListLen = self.trophyListLen + 1 + else: + newTrophies.append(False) + + self.newTrophies[avId] = newTrophies + + def awardCups(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldHistory = self.startingHistory[avId] + endingHistory = self.endingHistory[avId] + oldCups = GolfGlobals.calcCupListFromHistory(oldHistory) + endingCups = GolfGlobals.calcCupListFromHistory(endingHistory) + newCups = [] + for index in xrange(len(oldCups)): + if not oldCups[index] and endingCups[index]: + self.notify.debug('New Trophy %d' % index) + newCups.append(True) + self.air.writeServerEvent('golf_cup', avId, '%s' % index) + newMaxHp = av.getMaxHp() + 1 + av.b_setMaxHp(newMaxHp) + av.toonUp(newMaxHp) + else: + newCups.append(False) + + self.newCups[avId] = newCups + + def awardHoleBest(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldHoleBest = self.startingHoleBest[avId] + endingHoleBest = self.endingHoleBest[avId] + av.b_setGolfHoleBest(endingHoleBest) + newHoleBest = [] + longestHoleBestList = 0 + for index in xrange(len(oldHoleBest)): + if endingHoleBest[index] < oldHoleBest[index]: + self.notify.debug('New HoleBest %d' % index) + newHoleBest.append(True) + longestHoleBestList = longestHoleBestList + 1 + else: + newHoleBest.append(False) + + if longestHoleBestList > self.holeBestListLen: + self.holeBestListLen = longestHoleBestList + self.newHoleBest[avId] = newHoleBest + + def awardCourseBest(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + av = simbase.air.doId2do.get(avId) + if av: + oldCourseBest = self.startingCourseBest[avId] + endingCourseBest = self.endingCourseBest[avId] + av.b_setGolfCourseBest(endingCourseBest) + newCourseBest = [] + longestCourseBestList = 0 + for index in xrange(len(oldCourseBest)): + if endingCourseBest[index] < oldCourseBest[index]: + self.notify.debug('New CourseBest %d' % index) + newCourseBest.append(True) + longestCourseBestList = longestCourseBestList + 1 + else: + newCourseBest.append(False) + + if longestCourseBestList > self.courseBestListLen: + self.courseBestListLen = longestCourseBestList + self.newCourseBest[avId] = newCourseBest + + def haveAllGolfersExited(self): + retval = True + for avId in self.avStateDict: + if not self.avStateDict[avId] == EXITED: + retval = False + break + + return retval + + def getCurHoleDoId(self): + retval = 0 + if self.currentHole: + retval = self.currentHole.doId + return retval + + def d_setCurHoleDoId(self, curHoleDoId): + self.sendUpdate('setCurHoleDoId', [curHoleDoId]) + + def calcCoursePar(self): + retval = 0 + for holeId in self.holeIds: + holeInfo = GolfGlobals.HoleInfo[holeId] + retval += holeInfo['par'] + + return retval + + def getTotalScore(self, avId): + retval = 0 + if self.scores.has_key(avId): + for holeScore in self.scores[avId]: + retval += holeScore + + return retval + + def getCurHoleScore(self, avId): + retval = 0 + if avId in self.scores and self.numHolesPlayed < len(self.scores[avId]): + retval = self.scores[avId][self.numHolesPlayed] + return retval + + def toggleDrivePermission(self, avId): + if avId in self.drivingToons: + self.drivingToons.remove(avId) + self.sendUpdate('changeDrivePermission', [avId, 0]) + retval = False + else: + self.drivingToons.append(avId) + self.sendUpdate('changeDrivePermission', [avId, 1]) + retval = True + return retval + + def safeDemand(self, newState): + doingDemand = False + if self.state == 'Cleanup': + pass + else: + if self.state in self.defaultTransitions: + if newState in self.defaultTransitions[self.state]: + self.demand(newState) + doingDemand = True + else: + if self.state == None: + self.demand(newState) + doingDemand = True + if not doingDemand: + self.notify.warning('doId=%d ignoring demand from %s to %s' % (self.doId, self.state, newState)) + return doingDemand + + def setAvatarExited(self): + avId = self.air.getAvatarIdFromSender() + self.handleExitedAvatar(avId) + + def createChoicesList(self, courseId, possibleHoles): + retval = [] + holeIds = GolfGlobals.CourseInfo[courseId]['holeIds'] + for holeOrTuple in holeIds: + if type(holeOrTuple) == type(()): + holeId = holeOrTuple[0] + weight = holeOrTuple[1] + else: + if type(holeOrTuple) == type(0): + holeId = holeOrTuple + weight = 1 + else: + self.notify.warning('cant handle %s' % holeOrTuple) + continue + if holeId in possibleHoles: + retval += [holeId] * weight + + return retval + + def calcUniqueHoles(self, courseId): + uniqueHoles = set() + for holeOrTuple in GolfGlobals.CourseInfo[courseId]['holeIds']: + if type(holeOrTuple) == type(()): + uniqueHoles.add(holeOrTuple[0]) + elif type(holeOrTuple) == type(0): + uniqueHoles.add(holeOrTuple) + else: + self.notify.warning('cant handle %s' % holeOrTuple) + + return uniqueHoles + + def calcHolesToUseRandomized(self, courseId): + retval = [] + numHoles = GolfGlobals.CourseInfo[courseId]['numHoles'] + uniqueHoles = self.calcUniqueHoles(courseId) + curHolesChosen = set() + while len(retval) < numHoles: + if uniqueHoles == curHolesChosen: + curHolesChosen = set() + possibleHoles = uniqueHoles - curHolesChosen + choicesList = self.createChoicesList(courseId, possibleHoles) + if not self.preferredHoleId == None and self.preferredHoleId in choicesList and self.preferredHoleId not in curHolesChosen: + holeChosen = self.preferredHoleId + else: + holeChosen = random.choice(choicesList) + retval.append(holeChosen) + curHolesChosen.add(holeChosen) + + return retval + + def recordHoleInOne(self): + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + scoreList = self.scores[avId] + for holeIndex in xrange(len(scoreList)): + strokes = scoreList[holeIndex] + if strokes == 1: + holeId = self.holeIds[holeIndex] + self.air.writeServerEvent('golf_ace', avId, '%d|%d|%s' % (self.courseId, holeId, stillPlaying)) + + def recordCourseUnderPar(self): + coursePar = self.calcCoursePar() + stillPlaying = self.getStillPlayingAvIds() + for avId in stillPlaying: + totalScore = self.getTotalScore(avId) + netScore = totalScore - coursePar + if netScore < 0: + self.air.writeServerEvent('golf_underPar', avId, '%d|%d|%s' % (self.courseId, netScore, stillPlaying)) + + def addAimTime(self, avId, aimTime): + if avId in self.aimTimes: + self.aimTimes[avId] += aimTime diff --git a/toontown/golf/DistributedGolfHole.py b/toontown/golf/DistributedGolfHole.py new file mode 100644 index 0000000..eefea08 --- /dev/null +++ b/toontown/golf/DistributedGolfHole.py @@ -0,0 +1,1669 @@ +import math +import random +import time +from pandac.PandaModules import TextNode, BitMask32, Point3, Vec3, Vec4, deg2Rad, Mat3, NodePath, VBase4, OdeTriMeshData, OdeTriMeshGeom, OdeRayGeom, CollisionTraverser, CollisionSegment, CollisionNode, CollisionHandlerQueue +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from direct.gui.DirectGui import DirectWaitBar, DGG, DirectLabel +from direct.task import Task +from direct.fsm.FSM import FSM +from toontown.minigame import ArrowKeys +from direct.showbase import PythonUtil +from toontown.golf import BuildGeometry +from toontown.golf import DistributedPhysicsWorld +from toontown.golf import GolfGlobals +from direct.interval.IntervalGlobal import Sequence, Parallel, LerpScaleInterval, LerpFunctionInterval, Func, Wait, SoundInterval, ParallelEndTogether, LerpPosInterval, ActorInterval, LerpPosHprInterval, LerpColorScaleInterval, WaitInterval +from direct.actor import Actor +from toontown.golf import GolfHoleBase +from toontown.distributed import DelayDelete + +class DistributedGolfHole(DistributedPhysicsWorld.DistributedPhysicsWorld, FSM, GolfHoleBase.GolfHoleBase): + defaultTransitions = {'Off': ['Cleanup', 'ChooseTee', 'WatchTee'], + 'ChooseTee': ['Aim', 'Cleanup'], + 'WatchTee': ['WatchAim', + 'Cleanup', + 'WatchTee', + 'ChooseTee', + 'Aim'], + 'Wait': ['Aim', + 'WatchAim', + 'Playback', + 'Cleanup', + 'ChooseTee', + 'WatchTee'], + 'Aim': ['Shoot', + 'Playback', + 'Cleanup', + 'Aim', + 'WatchAim'], + 'WatchAim': ['WatchAim', + 'WatchShoot', + 'Playback', + 'Cleanup', + 'Aim', + 'ChooseTee', + 'WatchTee'], + 'Playback': ['Wait', + 'Aim', + 'WatchAim', + 'Cleanup', + 'ChooseTee', + 'WatchTee'], + 'Cleanup': ['Off']} + id = 0 + notify = directNotify.newCategory('DistributedGolfHole') + unlimitedAimTime = base.config.GetBool('unlimited-aim-time', 0) + unlimitedTeeTime = base.config.GetBool('unlimited-tee-time', 0) + golfPowerSpeed = base.config.GetDouble('golf-power-speed', 3) + golfPowerExponent = base.config.GetDouble('golf-power-exponent', 0.75) + DefaultCamP = -16 + MaxCamP = -90 + + def __init__(self, cr): + self.notify.debug('Hole Init') + DistributedPhysicsWorld.DistributedPhysicsWorld.__init__(self, base.cr) + GolfHoleBase.GolfHoleBase.__init__(self, 1) + FSM.__init__(self, 'Golf_%s_FSM' % self.id) + self.currentGolfer = 0 + self.ballDict = {} + self.ballShadowDict = {} + self.holeNodes = [] + self.golfCourse = None + self.golfCourseRequest = None + self.holePositions = [] + self.timer = None + self.teeTimer = None + self.aimStart = None + self.titleLabel = None + self.teeInstructions = None + self.aimInstructions = None + self.powerReminder = None + self.lastTimeHeadingSent = 0 + self.lastTempHeadingSent = 0 + self.holdCycleTime = 0.0 + self.inPlayBack = 0 + self.swingInterval = None + self.sfxInterval = None + self.isLookingAtPutt = False + self.clubs = {} + self.camInterval = None + self.flyOverInterval = None + self.needToDoFlyOver = True + self.translucentLastFrame = [] + self.translucentCurFrame = [] + self.localMissedSwings = 0 + self.localToonHitControl = False + self.warningInterval = None + self.playBackDelayDelete = None + self.aimMomentum = 0.0 + self.lastBumpSfxPos = Point3(0, 0, 0) + self.__textGen = TextNode('golfHoleText') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + if TTLocalizer.getLanguage() in ['castillian', + 'japanese', + 'german', + 'portuguese', + 'french']: + self.__textGen.setGlyphScale(0.7) + self.avIdList = [] + self.enterAimStart = 0 + return + + def generate(self): + self.notify.debug('Hole Generate') + DistributedPhysicsWorld.DistributedPhysicsWorld.generate(self) + self.golfPowerTaskName = self.uniqueName('updateGolfPower') + + def announceGenerate(self): + DistributedPhysicsWorld.DistributedPhysicsWorld.announceGenerate(self) + self.setup() + self.sendReady() + self.request('Off') + index = 1 + for avId in self.avIdList: + self.createBall(avId, index) + self.createClub(avId) + index += 1 + + if self.avIdList: + avId = self.avIdList[0] + self.currentGolfer = avId + self.currentGolferActive = False + + def delete(self): + self.removePlayBackDelayDelete() + self.request('Cleanup') + taskMgr.remove(self.golfPowerTaskName) + DistributedPhysicsWorld.DistributedPhysicsWorld.delete(self) + GolfHoleBase.GolfHoleBase.delete(self) + if hasattr(self, 'perfectIval'): + self.perfectIval.pause() + del self.perfectIval + self.golfCourse = None + if self.teeInstructions: + self.teeInstructions.destroy() + self.teeInstructions = None + if self.aimInstructions: + self.aimInstructions.destory() + self.aimInstructions = None + if self.powerReminder: + self.powerReminder.destroy() + self.powerReminder = None + if self.swingInterval: + self.swingInterval.pause() + self.swingInterval = None + if self.sfxInterval: + self.sfxInterval.pause() + self.sfxInterval = None + if self.camInterval: + self.camInterval.pause() + self.camInterval = None + for club in self.clubs: + self.clubs[club].removeNode() + + del self.clubs + if hasattr(self, 'scoreBoard'): + if hasattr(self.scoreBoard, 'maximizeB'): + if self.scoreBoard.maximizeB: + self.scoreBoard.maximizeB.hide() + if not self.titleLabel == None: + self.titleLabel.destroy() + self.notify.debug('Deleted title label') + self.notify.debug('Delete function') + if self.flyOverInterval: + self.flyOverInterval.pause() + self.flyOverInterval = None + for key in self.ballShadowDict: + self.ballShadowDict[key].removeNode() + + self.dropShadowModel.removeNode() + return + + def sendReady(self): + self.sendUpdate('setAvatarReadyHole', []) + + def createClub(self, avId): + club = NodePath('club-%s' % avId) + clubModel = loader.loadModel('phase_6/models/golf/putter') + clubModel.reparentTo(club) + clubModel.setR(clubModel, 45) + self.clubs[avId] = club + + def attachClub(self, avId, pointToBall = False): + club = self.clubs[avId] + if club: + av = base.cr.doId2do.get(avId) + if av: + av.useLOD(1000) + lHand = av.getLeftHands()[0] + club.setPos(0, 0, 0) + club.reparentTo(lHand) + netScale = club.getNetTransform().getScale()[1] + counterActToonScale = lHand.find('**/counteractToonScale') + if counterActToonScale.isEmpty(): + counterActToonScale = lHand.attachNewNode('counteractToonScale') + counterActToonScale.setScale(1 / netScale) + self.notify.debug('creating counterActToonScale for %s' % av.getName()) + club.reparentTo(counterActToonScale) + club.setX(-0.25 * netScale) + if pointToBall: + club.lookAt(self.clubLookatSpot) + + def createToonRay(self): + self.toonRay = OdeRayGeom(self.space, 10.0) + self.toonRay.setCollideBits(BitMask32(16777215)) + self.toonRay.setCategoryBits(BitMask32(0)) + self.toonRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.space.setCollideId(self.toonRay, GolfGlobals.TOON_RAY_COLLIDE_ID) + self.rayList.append(self.toonRay) + + def createSkyRay(self): + self.skyRay = OdeRayGeom(self.space, 100.0) + self.skyRay.setCollideBits(BitMask32(240)) + self.skyRay.setCategoryBits(BitMask32(0)) + self.skyRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.space.setCollideId(self.skyRay, 78) + self.rayList.append(self.skyRay) + + def createCameraRay(self): + self.cameraRay = OdeRayGeom(self.space, 30.0) + self.cameraRay.setCollideBits(BitMask32(8388608)) + self.cameraRay.setCategoryBits(BitMask32(0)) + self.space.setCollideId(self.cameraRay, GolfGlobals.CAMERA_RAY_COLLIDE_ID) + self.cameraRayNodePath = self.terrainModel.attachNewNode('cameraRayNodePath') + self.rayList.append(self.cameraRay) + + def loadLevel(self): + GolfHoleBase.GolfHoleBase.loadLevel(self) + self.teeNodePath = self.terrainModel.find('**/tee0') + if self.teeNodePath.isEmpty(): + teePos = Vec3(0, 0, 10) + else: + teePos = self.teeNodePath.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH()) + self.teePositions = [teePos] + teeIndex = 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + while not teeNode.isEmpty(): + teePos = teeNode.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.teePositions.append(teePos) + self.notify.debug('teeNodeP heading = %s' % teeNode.getH()) + teeIndex += 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + + self.holeBottomNodePath = self.terrainModel.find('**/holebottom0') + if self.holeBottomNodePath.isEmpty(): + self.holeBottomPos = Vec3(*self.holeInfo['holePos'][0]) + else: + self.holeBottomPos = self.holeBottomNodePath.getPos() + self.holePositions.append(self.holeBottomPos) + minHard = Point3(0, 0, 0) + maxHard = Point3(0, 0, 0) + self.hardSurfaceNodePath.calcTightBounds(minHard, maxHard) + centerX = (minHard[0] + maxHard[0]) / 2.0 + centerY = (minHard[1] + maxHard[1]) / 2.0 + heightX = (centerX - minHard[0]) / math.tan(deg2Rad(23)) + heightY = (centerY - minHard[1]) / math.tan(deg2Rad(18)) + height = max(heightX, heightY) + self.camTopViewPos = Point3(centerX, centerY, height) + self.camTopViewHpr = Point3(0, -90, 0) + self.createRays() + self.createToonRay() + self.createCameraRay() + + def createLocatorDict(self): + self.locDict = {} + locatorNum = 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + while not curNodePath.isEmpty(): + self.locDict[locatorNum] = curNodePath + locatorNum += 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + + def loadBlockers(self): + loadAll = base.config.GetBool('golf-all-blockers', 0) + self.createLocatorDict() + self.blockerNums = self.holeInfo['blockers'] + for locatorNum in self.locDict: + if locatorNum in self.blockerNums or loadAll: + locator = self.locDict[locatorNum] + locatorParent = locator.getParent() + locator.getChildren().wrtReparentTo(locatorParent) + else: + self.locDict[locatorNum].removeNode() + + self.hardSurfaceNodePath.flattenStrong() + + def loadSounds(self): + self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.mp3') + self.holeInOneSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hole_In_One.mp3') + self.holeInTwoPlusSfx = loader.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_fall.mp3') + self.ballGoesInStartSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Goes_In_Start.wav') + self.ballGoesInLoopSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Goes_In_Loop.wav') + self.ballGoesToRestSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Ball_Rest_In_Cup.mp3') + self.kickedOutSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Sad_Noise_Kicked_Off_Hole.mp3') + self.crowdBuildupSfx = [] + self.crowdApplauseSfx = [] + self.crowdMissSfx = [] + for i in xrange(4): + self.crowdBuildupSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Buildup.mp3')) + self.crowdApplauseSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Applause.mp3')) + self.crowdMissSfx.append(loader.loadSfx('phase_6/audio/sfx/Golf_Crowd_Miss.mp3')) + + self.bumpHardSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Barrier_3.mp3') + self.bumpMoverSfx = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_2.mp3') + self.bumpWindmillSfx = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.mp3') + + def setup(self): + self.notify.debug('setup golf hole') + self.loadLevel() + self.loadSounds() + self.camMove = 0 + self.arrowKeys = ArrowKeys.ArrowKeys() + self.arrowKeys.setPressHandlers([None, + None, + self.__leftArrowPressed, + self.__rightArrowPressed, + self.__beginTossGolf]) + self.arrowKeys.setReleaseHandlers([None, + None, + None, + None, + self.__endTossGolf]) + self.targets = render.attachNewNode('targetGameTargets') + self.ballFollow = render.attachNewNode('nodeAtBall') + self.startingTeeHeading = self.teeNodePath.getH() + self.ballFollow.setH(self.startingTeeHeading) + self.ballFollowToonSpot = self.ballFollow.attachNewNode('toonAimSpot') + self.ballFollowToonSpot.setX(-2.0) + self.ballFollowToonSpot.setY(0) + self.ballFollowToonSpot.setH(-90) + self.clubLookatSpot = self.ballFollow.attachNewNode('clubLookat') + self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1)) + camera.reparentTo(self.ballFollow) + self.camPosBallFollow = Point3(0.0, -23.0, 12.0) + self.camHprBallFollow = Point3(0, -16.0, 0) + camera.setPos(self.camPosBallFollow) + camera.setHpr(self.camHprBallFollow) + if self.holeBottomNodePath.isEmpty(): + holePositions = self.holePositions + for index in xrange(len(holePositions)): + holePos = holePositions[index] + targetNodePathGeom, t1, t2 = BuildGeometry.addCircleGeom(self.targets, 16, 1) + targetNodePathGeom.setPos(holePos) + targetNodePathGeom.setBin('ground', 0) + targetNodePathGeom.setDepthWrite(False) + targetNodePathGeom.setDepthTest(False) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + targetNodePathGeom.setColorScale(0.0, 0.0, 0.0, 1.0) + self.holeNodes.append(targetNodePathGeom) + holeSphere = CollisionSphere(0, 0, 0, 1) + holeSphere.setTangible(1) + holeCNode = CollisionNode('Hole') + holeCNode.addSolid(holeSphere) + holeC = targetNodePathGeom.attachNewNode(holeCNode) + holeC.show() + + holeC.setCollideMask(ToontownGlobals.PieBitmask) + toon = base.localAvatar + toon.setPos(0.0, 0.0, -100.0) + toon.b_setAnimState('neutral', 1.0) + self.pollingCtrl = 0 + self.timeLastCtrl = 0.0 + self.powerBar = DirectWaitBar(guiId='launch power bar', pos=(0.0, 0, -0.65), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(1.0, 0.0, 0.0, 1.0), text='', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar.hide() + self.accept('tab', self.tabKeyPressed) + self.putAwayAllToons() + base.transitions.irisOut(t=0) + self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadowModel.setColor(0, 0, 0, 0.5) + self.dropShadowModel.flattenMedium() + self.dropShadowModel.hide() + return + + def switchToAnimState(self, animStateName, forced = False): + curAnimState = base.localAvatar.animFSM.getCurrentState() + curAnimStateName = '' + if curAnimState: + curAnimStateName = curAnimState.getName() + if curAnimStateName != animStateName or forced: + base.localAvatar.b_setAnimState(animStateName) + + def __aimTask(self, task): + self.attachClub(self.currentGolfer, True) + x = -math.sin(self.ballFollow.getH() * 0.0174532925) + y = math.cos(self.ballFollow.getH() * 0.0174532925) + dt = globalClock.getDt() + b = self.curGolfBall() + forceMove = 500 + forceMoveDt = forceMove * dt + posUpdate = False + momentumChange = dt * 60.0 + if (self.arrowKeys.upPressed() or self.arrowKeys.downPressed()) and not self.golfCourse.canDrive(self.currentGolfer): + posUpdate = True + self.aimMomentum = 0.0 + self.ballFollow.headsUp(self.holeBottomNodePath) + elif self.arrowKeys.rightPressed() and not self.arrowKeys.leftPressed(): + self.aimMomentum -= momentumChange + if self.aimMomentum > 0: + self.aimMomentum = 0.0 + elif self.aimMomentum < -30.0: + self.aimMomentum = -30.0 + posUpdate = True + self.switchToAnimState('GolfRotateLeft') + self.scoreBoard.hide() + elif self.arrowKeys.leftPressed() and not self.arrowKeys.rightPressed(): + self.aimMomentum += momentumChange + if self.aimMomentum < 0.0: + self.aimMomentum = 0.0 + elif self.aimMomentum > 30.0: + self.aimMomentum = 30.0 + posUpdate = True + self.switchToAnimState('GolfRotateRight') + self.scoreBoard.hide() + else: + self.aimMomentum = 0.0 + self.switchToAnimState('GolfPuttLoop') + self.ballFollow.setH(self.ballFollow.getH() + self.aimMomentum * dt) + if self.arrowKeys.upPressed() and self.golfCourse.canDrive(self.currentGolfer): + b.enable() + b.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.downPressed() and self.golfCourse.canDrive(self.currentGolfer): + b.enable() + b.addForce(Vec3(-x * forceMoveDt, -y * forceMoveDt, 0)) + if self.arrowKeys.leftPressed() and self.arrowKeys.rightPressed() and self.golfCourse.canDrive(self.currentGolfer): + b.enable() + b.addForce(Vec3(0, 0, 3000 * dt)) + if posUpdate: + if globalClock.getFrameTime() - self.lastTimeHeadingSent > 0.2: + self.sendUpdate('setTempAimHeading', [localAvatar.doId, self.ballFollow.getH()]) + self.lastTimeHeadingSent = globalClock.getFrameTime() + self.lastTempHeadingSent = self.ballFollow.getH() + elif self.lastTempHeadingSent != self.ballFollow.getH(): + self.sendUpdate('setTempAimHeading', [localAvatar.doId, self.ballFollow.getH()]) + self.lastTimeHeadingSent = globalClock.getFrameTime() + self.lastTempHeadingSent = self.ballFollow.getH() + self.setCamera2Ball() + self.fixCurrentGolferFeet() + self.adjustClub() + self.orientCameraRay() + return task.cont + + def fixCurrentGolferFeet(self): + golfer = base.cr.doId2do.get(self.currentGolfer) + if not golfer: + return + golferPos = golfer.getPos(render) + newPos = Vec3(golferPos[0], golferPos[1], golferPos[2] + 5) + self.toonRay.setPosition(newPos) + + def adjustClub(self): + club = self.clubs[self.currentGolfer] + if club: + distance = club.getDistance(self.clubLookatSpot) + scaleFactor = distance / 2.058 + club.setScale(1, scaleFactor, 1) + + def resetPowerBar(self): + self.power = 0 + self.powerBar['value'] = self.power + self.powerBar['text'] = '' + + def sendSwingInfo(self): + kickHimOut = self.updateWarning() + if kickHimOut: + return + curAimTime = globalClock.getRealTime() - self.enterAimStart + if curAimTime < 0: + curAimTime = 0 + if curAimTime > GolfGlobals.AIM_DURATION: + curAimTime = GolfGlobals.AIM_DURATION + self.notify.debug('curAimTime = %f' % curAimTime) + x = -math.sin(self.ballFollow.getH() * 0.0174532925) + y = math.cos(self.ballFollow.getH() * 0.0174532925) + b = self.curGolfBall() + if hasattr(base, 'golfPower') and base.golfPower != None: + self.power = float(base.golfPower) + if not self.swingInfoSent: + self.sendUpdate('postSwingState', [self.getCycleTime(), + self.power, + b.getPosition()[0], + b.getPosition()[1], + b.getPosition()[2], + x, + y, + curAimTime, + self.getCommonObjectData()]) + self.swingInfoSent = True + if self.power < 15 and self.golfCourse.scores[localAvatar.doId][self.golfCourse.curHoleIndex] == 0: + self.powerReminder = DirectLabel(text=TTLocalizer.GolfPowerReminder, text_shadow=(0, 0, 0, 1), text_fg=VBase4(1, 1, 0.0, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.8), scale=0.12) + return + + def updateWarning(self): + retval = False + if not self.localToonHitControl: + self.localMissedSwings += 1 + else: + self.localMissedSwings = 0 + if self.localMissedSwings == GolfGlobals.KICKOUT_SWINGS - 1: + self.warningLabel = DirectLabel(parent=aspect2d, relief=None, pos=(0, 0, 0), text_align=TextNode.ACenter, text=TTLocalizer.GolfWarningMustSwing, text_scale=0.12, text_font=ToontownGlobals.getSignFont(), text_fg=(1, 0.1, 0.1, 1), text_wordwrap=20) + self.warningInterval = Sequence(LerpColorScaleInterval(self.warningLabel, 10, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn'), Func(self.warningLabel.destroy)) + self.warningInterval.start() + elif self.localMissedSwings >= GolfGlobals.KICKOUT_SWINGS: + self.golfCourse.handleFallingAsleepGolf(None) + retval = True + return retval + + def assignRecordSwing(self, avId, cycleTime, power, x, y, z, dirX, dirY, commonObjectData): + ball = self.ballDict[avId]['golfBall'] + holdBallPos = ball.getPosition() + self.useCommonObjectData(commonObjectData) + self.trackRecordBodyFlight(ball, cycleTime, power, Vec3(x, y, z), dirX, dirY) + ball.setPosition(holdBallPos) + self.sendUpdate('ballMovie2AI', [cycleTime, + avId, + self.recording, + self.aVRecording, + self.ballInHoleFrame, + self.ballTouchedHoleFrame, + self.ballFirstTouchedHoleFrame, + commonObjectData]) + self.ballMovie2Client(cycleTime, avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame, commonObjectData) + + def __watchAimTask(self, task): + self.setCamera2Ball() + self.attachClub(self.currentGolfer, True) + self.adjustClub() + self.fixCurrentGolferFeet() + self.orientCameraRay() + return task.cont + + def __watchTeeTask(self, task): + self.setCamera2Ball() + return task.cont + + def curGolfBall(self): + return self.ballDict[self.currentGolfer]['golfBall'] + + def curGolfBallGeom(self): + return self.ballDict[self.currentGolfer]['golfBallGeom'] + + def curBallShadow(self): + return self.ballShadowDict[self.currentGolfer] + + def cleanupGeom(self): + self.targets.remove() + self.terrainModel.remove() + self.powerBar.destroy() + + def cleanupPowerBar(self): + self.powerBar.hide() + + def cleanupPhysics(self): + pass + + def curBall(self): + return self.ballDict[self.currentGolfer]['ball'] + + def curBallANP(self): + return self.ballDict[self.currentGolfer]['ballActorNodePath'] + + def curBallActor(self): + return self.ballDict[self.currentGolfer]['ballActor'] + + def enterAim(self): + self.notify.debug('Aim') + self.notify.debug('currentGolfer = %s' % self.currentGolfer) + self.switchToAnimState('GolfPuttLoop', forced=True) + self.swingInfoSent = False + self.lastState = self.state + self.aimMomentum = 0.0 + self.enterAimStart = globalClock.getRealTime() + taskMgr.add(self.__aimTask, 'Aim Task') + self.showOnlyCurGolfer() + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + self.camPivot = self.ballFollow.attachNewNode('golf-camPivot') + self.targetCamPivot = self.ballFollow.attachNewNode('golf-targetCamPivot') + self.targetCamPivot.setP(self.DefaultCamP) + self.curCamPivot = self.ballFollow.attachNewNode('golf-curCamPivot') + self.curCamPivot.setP(self.DefaultCamP) + self.ccTrav = CollisionTraverser('golf.ccTrav') + self.ccLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 0.0, 0.0) + self.ccLineNode = CollisionNode('golf.ccLineNode') + self.ccLineNode.addSolid(self.ccLine) + self.ccLineNodePath = self.camPivot.attachNewNode(self.ccLineNode) + self.ccLineBitMask = BitMask32(1048576) + self.ccLineNode.setFromCollideMask(self.ccLineBitMask) + self.ccLineNode.setIntoCollideMask(BitMask32.allOff()) + self.camCollisionQueue = CollisionHandlerQueue() + self.ccTrav.addCollider(self.ccLineNodePath, self.camCollisionQueue) + if strokes: + self.ballFollow.headsUp(self.holeBottomNodePath) + self.camPivot.setP(self.DefaultCamP) + self._golfBarrierCollection = self.terrainModel.findAllMatches('**/collision?') + self._camAdjust = ScratchPad() + self._camAdjust.iters = 0 + self._camAdjust.lower = self.DefaultCamP + self._camAdjust.upper = self.MaxCamP + base.camera.setPos(self.camPosBallFollow) + base.camera.setHpr(self.camHprBallFollow) + self.camPivot.setP(self.DefaultCamP) + base.camera.wrtReparentTo(self.camPivot) + A = Point3(0, 0, 0) + B = base.camera.getPos() + AtoB = B - A + AtoBnorm = Point3(AtoB) + AtoBnorm.normalize() + A += AtoBnorm * 0.4 + self.ccLine.setPointA(A) + self.ccLine.setPointB(B) + self.camPivot.setP(self.DefaultCamP) + self._camAdjust.task = taskMgr.add(self._adjustCamera, 'adjustCamera') + self.resetPowerBar() + self.powerBar.show() + self.aimDuration = GolfGlobals.AIM_DURATION + if not self.unlimitedAimTime: + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(self.aimDuration) + self.timer.countdown(self.aimDuration, self.timerExpired) + self.aimInstructions = DirectLabel(text=TTLocalizer.GolfAimInstructions, text_shadow=(0, 0, 0, 1), text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, -0.8), scale=TTLocalizer.DGHaimInstructions) + self.skyContact = 1 + self.localToonHitControl = False + self._adjustCamera() + return + + def exitAim(self): + localAvatar.wrtReparentTo(render) + taskMgr.remove(self._camAdjust.task) + taskMgr.remove('Aim Task') + taskMgr.remove(self.golfPowerTaskName) + if self.timer: + self.timer.stop() + self.timer.destroy() + self.timer = None + self.powerBar.hide() + self.ccLineNodePath.detachNode() + self.targetCamPivot.detachNode() + self.curCamPivot.detachNode() + self.camPivot.detachNode() + if self.aimInstructions: + self.aimInstructions.destroy() + self.aimInstructions = None + return + + def timerExpired(self): + taskMgr.remove(self.golfPowerTaskName) + self.aimStart = None + self.sendSwingInfo() + self.resetPowerBar() + return + + def _adjustCamera(self, task=None, first=True): + if task is None and first: + while 1: + self._adjustCamera(first=False) + if self._camAdjust.iters == 0: + return Task.cont + + MaxIters = 5 + finalP = self._camAdjust.lower + + localAvatar.stash() + for barrier in self._golfBarrierCollection: + barrier.stash() + + self.ccTrav.traverse(render) + + for barrier in self._golfBarrierCollection: + barrier.unstash() + localAvatar.unstash() + + midP = (self._camAdjust.lower + self._camAdjust.upper)/2 + if self.camCollisionQueue.getNumEntries() > 0: + self.camCollisionQueue.sortEntries() + entry = self.camCollisionQueue.getEntry(0) + sPoint = entry.getSurfacePoint(self.camPivot) + self._camAdjust.lower = self.camPivot.getP() + finalP = midP + self.camPivot.setP(finalP) + else: + self._camAdjust.upper = self.camPivot.getP() + finalP = self._camAdjust.upper + self.camPivot.setP(midP) + if abs(self._camAdjust.lower - self._camAdjust.upper) < 1.0: + self._camAdjust.iters = MaxIters + + self._camAdjust.iters += 1 + if self._camAdjust.iters >= MaxIters: + self.targetCamPivot.setP(self._camAdjust.upper) + if task is None: + self.curCamPivot.setP(finalP) + self._camAdjust.iters = 0 + self._camAdjust.lower = self.DefaultCamP + self._camAdjust.upper = self.MaxCamP + self.camPivot.setP(self.DefaultCamP) + + if task is not None: + self.curCamPivot.setP(self.curCamPivot, + self.targetCamPivot.getP(self.curCamPivot)*min(1.0, 1.0*globalClock.getDt())) + + curP = self.curCamPivot.getP() + self.curCamPivot.setP(self.DefaultCamP) + base.camera.reparentTo(self.ballFollow) + base.camera.setPos(self.camPosBallFollow) + base.camera.setHpr(self.camHprBallFollow) + base.camera.wrtReparentTo(self.curCamPivot) + self.curCamPivot.setP(curP) + base.camera.wrtReparentTo(self.ballFollow) + + return Task.cont + + def enterChooseTee(self): + self.notify.debug('ChooseTee') + self.curGolfBallGeom().show() + self.curBallShadow().show() + self.lastState = self.state + taskMgr.add(self.__chooseTeeTask, 'ChooseTee Task') + self.ballFollow.setH(self.startingTeeHeading) + self.localAvatarChosenTee = False + self.localTempTee = 0 + if len(self.teePositions) > 1: + self.localTempTee = 1 + self.chooseTeeDuration = GolfGlobals.TEE_DURATION + if not self.unlimitedTeeTime: + self.teeTimer = ToontownTimer.ToontownTimer() + self.teeTimer.posInTopRightCorner() + self.teeTimer.setTime(self.chooseTeeDuration) + self.teeTimer.countdown(self.chooseTeeDuration, self.teeTimerExpired) + self.teeInstructions = DirectLabel(text=TTLocalizer.GolfChooseTeeInstructions, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, text_shadow=(0, 0, 0, 1), relief=None, pos=(0, 0, -0.75), scale=TTLocalizer.DGHteeInstructions) + self.powerBar.hide() + return + + def exitChooseTee(self): + localAvatar.wrtReparentTo(render) + if hasattr(self, 'teeInstructions') and self.teeInstructions: + self.teeInstructions.destroy() + self.teeInstructions = None + taskMgr.remove('ChooseTee Task') + taskMgr.remove(self.golfPowerTaskName) + if self.teeTimer: + self.teeTimer.stop() + self.teeTimer.destroy() + self.teeTimer = None + self.powerBar.show() + return + + def sendTeeInfo(self): + self.sendUpdate('setAvatarTee', [self.localTempTee]) + self.localAvatarChosenTee = True + + def __chooseTeeTask(self, task): + if self.localAvatarChosenTee: + return task.done + if self.arrowKeys.jumpPressed(): + if self.flyOverInterval and self.flyOverInterval.isPlaying(): + pass + else: + self.sendTeeInfo() + return task.cont + + def changeTee(self, newTee): + ball = self.curGolfBall() + ball.setPosition(self.teePositions[newTee]) + self.setCamera2Ball() + self.fixCurrentGolferFeet() + self.adjustClub() + + def changeLocalTee(self, newTee): + self.changeTee(newTee) + self.sendUpdate('setAvatarTempTee', [localAvatar.doId, newTee]) + self.fixCurrentGolferFeet() + self.adjustClub() + + def __leftArrowPressed(self): + if self.state != 'ChooseTee': + return + self.localTempTee -= 1 + if self.localTempTee < 0: + self.localTempTee = len(self.teePositions) - 1 + self.changeLocalTee(self.localTempTee) + + def __rightArrowPressed(self): + if self.state != 'ChooseTee': + return + self.localTempTee += 1 + self.localTempTee %= len(self.teePositions) + self.changeLocalTee(self.localTempTee) + + def teeTimerExpired(self): + self.sendTeeInfo() + + def enterWatchAim(self): + self.notify.debug('Watch Aim') + self.notify.debugStateCall(self) + self.notify.debug('currentGolfer = %s' % self.currentGolfer) + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + if strokes: + self.ballFollow.lookAt(self.holeBottomNodePath) + self.ballFollow.setP(0) + self.showOnlyCurGolfer() + taskMgr.add(self.__watchAimTask, 'Watch Aim Task') + + def exitWatchAim(self): + self.notify.debugStateCall(self) + av = base.cr.doId2do.get(self.currentGolfer) + if av: + heading = av.getH(render) + toonPos = av.getPos(render) + av.reparentTo(render) + av.setH(heading) + av.setPos(toonPos) + self.notify.debug('av %s now at position %s' % (av.getName(), av.getPos())) + else: + self.notify.debug('could not get avId %d' % self.currentGolfer) + taskMgr.remove('Watch Aim Task') + + def enterWatchTee(self): + self.notify.debug('Watch Tee') + self.notify.debugStateCall(self) + self.curGolfBallGeom().show() + self.ballFollow.setH(self.startingTeeHeading) + self.ballShadowDict[self.currentGolfer].show() + + def exitWatchTee(self): + self.notify.debugStateCall(self) + av = base.cr.doId2do.get(self.currentGolfer) + taskMgr.remove('Watch Tee Task') + + def enterWait(self): + self.notify.debug('Wait') + self.notify.debugStateCall(self) + + def exitWait(self): + self.notify.debugStateCall(self) + + def removePlayBackDelayDelete(self): + if self.playBackDelayDelete: + self.playBackDelayDelete.destroy() + self.playBackDelayDelete = None + return + + def enterPlayback(self): + + def shiftClubToRightHand(): + club = self.clubs[self.currentGolfer] + av = base.cr.doId2do.get(self.currentGolfer) + if av and club: + club.wrtReparentTo(av.getRightHands()[0]) + + av = base.cr.doId2do.get(self.currentGolfer) + if not av: + return + else: + self.removePlayBackDelayDelete() + self.playBackDelayDelete = DelayDelete.DelayDelete(av, 'GolfHole.enterPlayback') + self.accept('clientCleanup', self._handleClientCleanup) + self.inPlayBack = 1 + self.setLookingAtPutt(False) + self.swingInterval = Sequence(ActorInterval(av, 'swing-putt', startFrame=0, endFrame=GolfGlobals.BALL_CONTACT_FRAME), Func(self.startBallPlayback), ActorInterval(av, 'swing-putt', startFrame=GolfGlobals.BALL_CONTACT_FRAME, endFrame=23), Func(shiftClubToRightHand), Func(self.setLookingAtPutt, True), Func(self.removePlayBackDelayDelete)) + adjustedBallTouchedHoleTime = self.ballTouchedHoleTime + GolfGlobals.BALL_CONTACT_TIME + adjustedBallFirstTouchedHoleTime = self.ballFirstTouchedHoleTime + GolfGlobals.BALL_CONTACT_TIME + adjustedBallDropTime = self.ballDropTime + GolfGlobals.BALL_CONTACT_TIME + adjustedPlaybackEndTime = self.playbackMovieDuration + GolfGlobals.BALL_CONTACT_TIME + self.notify.debug('adjustedTimes ballTouched=%.2f ballFirstTouched=%.2f ballDrop=%.2f playbaybackEnd=%.2f' % (adjustedBallTouchedHoleTime, + adjustedBallFirstTouchedHoleTime, + adjustedBallDropTime, + adjustedPlaybackEndTime)) + if self.ballWillGoInHole: + curDuration = self.swingInterval.getDuration() + lookPuttInterval = ActorInterval(av, 'look-putt') + if curDuration < adjustedBallDropTime: + self.swingInterval.append(lookPuttInterval) + curDuration = self.swingInterval.getDuration() + diffTime = adjustedBallDropTime - curDuration + if diffTime > 0: + self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=diffTime)) + self.swingInterval.append(ActorInterval(av, 'good-putt', endTime=self.playbackMovieDuration, loop=1)) + elif self.ballTouchedHoleTime: + self.notify.debug('doing self.ballTouchedHoleTime') + curDuration = self.swingInterval.getDuration() + lookPuttInterval = ActorInterval(av, 'look-putt') + if curDuration < adjustedBallTouchedHoleTime: + self.swingInterval.append(lookPuttInterval) + curDuration = self.swingInterval.getDuration() + diffTime = adjustedBallTouchedHoleTime - curDuration + if diffTime > 0: + self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=diffTime)) + self.swingInterval.append(ActorInterval(av, 'bad-putt', endFrame=32)) + self.swingInterval.append(ActorInterval(av, 'badloop-putt', endTime=self.playbackMovieDuration, loop=1)) + else: + self.swingInterval.append(ActorInterval(av, 'look-putt')) + self.swingInterval.append(ActorInterval(av, 'lookloop-putt', endTime=self.playbackMovieDuration, loop=1)) + sfxInterval = Parallel() + ballHitInterval = Sequence(Wait(GolfGlobals.BALL_CONTACT_TIME), SoundInterval(self.hitBallSfx)) + sfxInterval.append(ballHitInterval) + if self.ballWillGoInHole: + ballRattle = Sequence() + timeToPlayBallRest = adjustedPlaybackEndTime - self.ballGoesToRestSfx.length() + if adjustedBallFirstTouchedHoleTime < timeToPlayBallRest: + diffTime = timeToPlayBallRest - adjustedBallFirstTouchedHoleTime + if self.ballGoesInStartSfx.length() < diffTime: + ballRattle.append(Wait(adjustedBallFirstTouchedHoleTime)) + ballRattle.append(SoundInterval(self.ballGoesInStartSfx)) + timeToPlayLoop = adjustedBallFirstTouchedHoleTime + self.ballGoesInStartSfx.length() + loopTime = timeToPlayBallRest - timeToPlayLoop + if self.ballGoesInLoopSfx.length() == 0.0: + numLoops = 0 + else: + numLoops = int(loopTime / self.ballGoesInLoopSfx.length()) + self.notify.debug('numLoops=%d loopTime=%f' % (numLoops, loopTime)) + if loopTime > 0: + ballRattle.append(SoundInterval(self.ballGoesInLoopSfx, loop=1, duration=loopTime, seamlessLoop=True)) + ballRattle.append(SoundInterval(self.ballGoesToRestSfx)) + self.notify.debug('playing full rattling') + else: + self.notify.debug('playing abbreviated rattling') + timeToPlayBallGoesIn = adjustedBallFirstTouchedHoleTime + ballRattle.append(Wait(timeToPlayBallGoesIn)) + startTime = self.ballGoesInStartSfx.length() - diffTime + self.notify.debug('adjustedBallDropTime=%s diffTime=%s starTime=%s' % (adjustedBallDropTime, diffTime, startTime)) + ballRattle.append(SoundInterval(self.ballGoesInStartSfx, startTime=startTime)) + ballRattle.append(SoundInterval(self.ballGoesToRestSfx)) + else: + self.notify.debug('playing abbreviated ball goes to rest') + ballRattle.append(Wait(adjustedBallFirstTouchedHoleTime)) + diffTime = adjustedPlaybackEndTime - adjustedBallFirstTouchedHoleTime + startTime = self.ballGoesToRestSfx.length() - diffTime + self.notify.debug('adjustedBallDropTime=%s diffTime=%s starTime=%s' % (adjustedBallDropTime, diffTime, startTime)) + ballRattle.append(SoundInterval(self.ballGoesToRestSfx, startTime=startTime)) + sfxInterval.append(ballRattle) + crowdBuildupSfx = self.crowdBuildupSfx[self.avIdList.index(self.currentGolfer)] + crowdApplauseSfx = self.crowdApplauseSfx[self.avIdList.index(self.currentGolfer)] + crowdMissSfx = self.crowdMissSfx[self.avIdList.index(self.currentGolfer)] + if self.ballWillGoInHole: + crowdIval = Sequence() + buildupLength = crowdBuildupSfx.length() + self.notify.debug('buildupLength=%s' % buildupLength) + diffTime = adjustedBallFirstTouchedHoleTime - buildupLength + if diffTime > 0: + crowdIval.append(Wait(diffTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx)) + crowdIval.append(SoundInterval(crowdApplauseSfx)) + else: + startTime = buildupLength - adjustedBallFirstTouchedHoleTime + self.notify.debug('playing abbreviated crowd build and applause diffTime=%s startTime=%s' % (diffTime, startTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx, startTime=startTime)) + crowdIval.append(SoundInterval(crowdApplauseSfx)) + sfxInterval.append(crowdIval) + elif self.ballFirstTouchedHoleTime: + crowdIval = Sequence() + buildupLength = crowdBuildupSfx.length() + self.notify.debug('touched but not going in buildupLength=%s' % buildupLength) + diffTime = adjustedBallFirstTouchedHoleTime - buildupLength + if diffTime > 0: + self.notify.debug('waiting %.2f to play crowd buildup' % diffTime) + crowdIval.append(Wait(diffTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx)) + crowdIval.append(SoundInterval(crowdMissSfx)) + else: + startTime = buildupLength - adjustedBallFirstTouchedHoleTime + self.notify.debug('playing abbreviated crowd build and miss diffTime=%s startTime=%s' % (diffTime, startTime)) + crowdIval.append(SoundInterval(crowdBuildupSfx, startTime=startTime)) + crowdIval.append(SoundInterval(crowdMissSfx)) + sfxInterval.append(crowdIval) + if self.sfxInterval: + sfxInterval.finish() + self.sfxInterval = sfxInterval + self.sfxInterval.start() + self.swingInterval.start() + + def exitPlayback(self): + self.notify.debug('Exiting Playback') + if self.swingInterval: + self.swingInterval.pause() + av = base.cr.doId2do.get(self.currentGolfer) + if av: + if self.ballWillGoInHole: + av.loop('good-putt', restart=0) + elif self.ballTouchedHoleTime: + pass + else: + av.loop('neutral') + self.setLookingAtPutt(False) + if av == base.localAvatar: + if self.ballWillGoInHole: + av.b_setAnimState('GolfGoodPutt') + elif self.ballTouchedHoleTime: + av.b_setAnimState('GolfBadPutt') + else: + av.b_setAnimState('neutral') + taskMgr.remove('playback task') + self.curGolfBall().disable() + self.readyCurrentGolfer(None) + self.inPlayBack = 0 + if self.powerReminder: + self.powerReminder.destroy() + self.powerReminder = None + return + + def setLookingAtPutt(self, newVal): + self.isLookingAtPutt = newVal + + def getLookingAtPutt(self): + return self.isLookingAtPutt + + def startBallPlayback(self): + self.playbackFrameNum = 0 + self.sourceFrame = self.recording[0] + self.destFrameNum = 1 + self.destFrame = self.recording[self.destFrameNum] + self.aVSourceFrame = self.aVRecording[0] + self.aVDestFrameNum = 1 + self.aVDestFrame = self.aVRecording[self.aVDestFrameNum] + self.inPlayBack = 2 + + def isCurBallInHole(self): + retval = False + ball = self.curGolfBall() + ballPos = ball.getPosition() + for holePos in self.holePositions: + displacement = ballPos - holePos + length = displacement.length() + self.notify.debug('hole %s length=%s' % (holePos, length)) + if length <= GolfGlobals.DistanceToBeInHole: + retval = True + break + + return retval + + def handleBallGoingInHole(self): + par = GolfGlobals.HoleInfo[self.holeId]['par'] + unlimitedSwing = False + av = base.cr.doId2do.get(self.currentGolfer) + if av: + unlimitedSwing = av.getUnlimitedSwing() + if not unlimitedSwing: + self.curGolfBall().setPosition(0, 0, -100) + self.ballShadowDict[self.currentGolfer].setPos(0, 0, -100) + self.ballShadowDict[self.currentGolfer].hide() + strokes = 3 + if self.golfCourse: + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + else: + self.notify.warning('self.golfCourse is None') + diff = strokes - par + if diff > 0: + textStr = '+' + str(diff) + else: + textStr = diff + if strokes == 1: + textStr = TTLocalizer.GolfHoleInOne + elif diff in TTLocalizer.GolfShotDesc: + if self.ballWillGoInHole: + textStr = TTLocalizer.GolfShotDesc[diff] + perfectTextSubnode = hidden.attachNewNode(self.__genText(textStr)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 - 1.35 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + animTrack = Sequence() + av = base.cr.doId2do.get(self.currentGolfer) + animTrack.append(Func(self.golfCourse.updateScoreBoard)) + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(self.sendUpdate, 'turnDone', [])) + soundTrack = Sequence() + if strokes == 1: + soundTrack.append(SoundInterval(self.holeInOneSfx)) + elif self.hasCurGolferReachedMaxSwing and not self.ballWillGoInHole: + soundTrack.append(SoundInterval(self.kickedOutSfx)) + self.perfectIval = Parallel(textTrack, soundTrack, animTrack) + self.perfectIval.start() + + def __playbackTask(self, task): + return self.playBackFrame(task) + + def toonRayCollisionCallback(self, x, y, z): + if self.state not in ('Aim', 'WatchAim', 'ChooseTee', 'WatchTee'): + return + tempPath = render.attachNewNode('temp') + tempPath.setPos(x, y, z) + relPos = tempPath.getPos(self.ballFollowToonSpot) + av = base.cr.doId2do.get(self.currentGolfer) + if av: + zToUse = relPos[2] + if zToUse < 0 - GolfGlobals.GOLF_BALL_RADIUS: + zToUse = 0 - GolfGlobals.GOLF_BALL_RADIUS + av.setPos(0, 0, zToUse) + tempPath.removeNode() + + def preStep(self): + if self.currentGolferActive: + GolfHoleBase.GolfHoleBase.preStep(self) + + def postStep(self): + if self.currentGolferActive: + GolfHoleBase.GolfHoleBase.postStep(self) + DistributedPhysicsWorld.DistributedPhysicsWorld.postStep(self) + if self.inPlayBack == 2: + self.playBackFrame() + self.makeCurGolferLookAtBall() + elif self.state == 'Playback' and self.inPlayBack == 0: + self.request('Wait') + self.updateTranslucentObjects() + + def updateTranslucentObjects(self): + for translucentNodePathLastFrame in self.translucentLastFrame: + if translucentNodePathLastFrame not in self.translucentCurFrame: + translucentNodePathLastFrame.setColorScale(1, 1, 1, 1) + + for transNpCurFrame in self.translucentCurFrame: + if transNpCurFrame not in self.translucentLastFrame: + self.notify.debug('making translucent %s' % transNpCurFrame) + transNpCurFrame.setColorScale(1, 1, 1, 0.25) + transNpCurFrame.setTransparency(1) + + def makeCurGolferLookAtBall(self): + if self.getLookingAtPutt(): + av = base.cr.doId2do.get(self.currentGolfer) + if av: + ballPos = self.curGolfBall().getPosition() + av.headsUp(ballPos[0], ballPos[1], ballPos[2]) + av.setH(av.getH() - 90) + + def playBackFrame(self): + doPrint = 0 + doAVPrint = 0 + lastFrame = self.recording[len(self.recording) - 1][0] + if self.playbackFrameNum >= self.destFrame[0]: + self.sourceFrame = self.destFrame + self.destFrameNum += 1 + doPrint = 1 + if self.destFrameNum < len(self.recording): + self.destFrame = self.recording[self.destFrameNum] + else: + self.notify.debug('recording length %s' % len(self.recording)) + if self.isCurBallInHole() or self.hasCurGolferReachedMaxSwing(): + self.handleBallGoingInHole() + self.request('Wait') + else: + self.golfCourse.updateScoreBoard() + self.request('Wait') + self.sendUpdate('turnDone', []) + return + self.projLength = self.destFrame[0] - self.sourceFrame[0] + self.projPen = self.destFrame[0] - self.playbackFrameNum + propSource = float(self.projPen) / float(self.projLength) + propDest = 1.0 - propSource + projX = self.sourceFrame[1] * propSource + self.destFrame[1] * propDest + projY = self.sourceFrame[2] * propSource + self.destFrame[2] * propDest + projZ = self.sourceFrame[3] * propSource + self.destFrame[3] * propDest + newPos = Vec3(projX, projY, projZ) + ball = self.curGolfBall() + ball.setPosition(newPos) + if self.playbackFrameNum >= self.aVDestFrame[0]: + self.aVSourceFrame = self.aVDestFrame + self.aVDestFrameNum += 1 + doAVPrint = 1 + if self.aVDestFrameNum < len(self.aVRecording): + self.aVDestFrame = self.aVRecording[self.aVDestFrameNum] + newAV = Vec3(self.aVSourceFrame[1], self.aVSourceFrame[2], self.aVSourceFrame[3]) + self.projLength = self.aVDestFrame[0] - self.aVSourceFrame[0] + self.projPen = self.aVDestFrame[0] - self.playbackFrameNum + propSource = float(self.projPen) / float(self.projLength) + propDest = 1.0 - propSource + projX = self.aVSourceFrame[1] * propSource + self.aVDestFrame[1] * propDest + projY = self.aVSourceFrame[2] * propSource + self.aVDestFrame[2] * propDest + projZ = self.aVSourceFrame[3] * propSource + self.aVDestFrame[3] * propDest + newAV = Vec3(projX, projY, projZ) + ball = self.curGolfBall() + ball.setAngularVel(newAV) + if self.playbackFrameNum < lastFrame - 1: + ball.enable() + else: + ball.disable() + self.setCamera2Ball() + self.placeBodies() + if doAVPrint: + pass + if doPrint: + self.notify.debug('. %s %s %s %s %s' % (self.playbackFrameNum, + self.sourceFrame[0], + self.destFrame[0], + self.destFrameNum, + newPos)) + self.playbackFrameNum += 1 + + def enterCleanup(self): + taskMgr.remove('update task') + if hasattr(self, 'arrowKeys'): + self.arrowKeys.destroy() + self.arrowKeys = None + self.ignoreAll() + if self.swingInterval: + self.swingInterval.pause() + self.swingInterval = None + if self.sfxInterval: + self.sfxInterval.pause() + self.sfxInterval = None + self.cleanupGeom() + return + + def exitCleanup(self): + pass + + def setCamera2Ball(self): + b = self.curGolfBall() + ballPos = Point3(b.getPosition()[0], b.getPosition()[1], b.getPosition()[2]) + self.ballFollow.setPos(ballPos) + + def hitBall(self, ball, power, x, y): + self.performSwing(self, ball, power, x, y) + + def ballMovie2Client(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData): + self.notify.debug('received Movie, number of frames %s %s ballInFrame=%d ballTouchedHoleFrame=%d ballFirstTouchedHoleFrame=%d' % (len(movie), + len(spinMovie), + ballInFrame, + ballTouchedHoleFrame, + ballFirstTouchedHoleFrame)) + if self.state == 'Playback': + self.notify.debug('SMASHED PLAYBACK') + return + self.ballShadowDict[avId].show() + self.holdCycleTime = cycleTime + self.holdCommonObjectData = commonObjectData + self.useCommonObjectData(self.holdCommonObjectData) + self.recording = movie + self.aVRecording = spinMovie + endingBallPos = Vec3(movie[-1][1], movie[-1][2], movie[-1][3]) + endingFrame = movie[-1][0] + self.playbackMovieDuration = endingFrame * self.DTAStep + self.notify.debug('playback movie duration=%s' % self.playbackMovieDuration) + displacement = self.holePositions[0] - endingBallPos + self.ballWillGoInHole = False + if displacement.length() <= GolfGlobals.DistanceToBeInHole: + self.ballWillGoInHole = True + self.notify.debug('endingBallPos=%s, distanceToHole=%s, ballWillGoInHole=%s' % (endingBallPos, displacement.length(), self.ballWillGoInHole)) + self.ballDropTime = ballInFrame * self.DTAStep + self.ballTouchedHoleTime = ballTouchedHoleFrame * self.DTAStep + self.ballFirstTouchedHoleTime = ballFirstTouchedHoleFrame * self.DTAStep + if self.state == 'WatchTee': + self.request('WatchAim') + self.request('Playback') + + def golfersTurn(self, avId): + self.readyCurrentGolfer(avId) + if avId == localAvatar.doId: + self.setCamera2Ball() + self.request('Aim') + else: + self.setCamera2Ball() + self.request('WatchAim') + + def readyCurrentGolfer(self, avId): + for index in self.ballDict: + self.ballDict[index]['golfBallOdeGeom'].setCollideBits(BitMask32(0)) + self.ballDict[index]['golfBallOdeGeom'].setCategoryBits(BitMask32(0)) + self.ballDict[index]['golfBall'].disable() + + if avId: + self.currentGolfer = avId + self.currentGolferActive = True + if avId in self.ballDict: + self.ballDict[avId]['golfBallOdeGeom'].setCollideBits(BitMask32(16777215)) + self.ballDict[avId]['golfBallOdeGeom'].setCategoryBits(BitMask32(4278190080L)) + else: + self.currentGolferActive = False + + def setGolferIds(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.teeChosen = {} + for avId in self.avIdList: + self.teeChosen[avId] = -1 + + def setHoleId(self, holeId): + self.holeId = holeId + self.holeInfo = GolfGlobals.HoleInfo[holeId] + + def createBall(self, avId, index = None): + golfBallGeom, golfBall, odeGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, index) + startPos = self.teePositions[0] + if len(self.teePositions) > 1: + startPos = self.teePositions[1] + golfBall.setPosition(startPos) + golfBallGeom.hide() + if self.notify.getDebug(): + self.notify.debug('golf ball body id') + golfBall.write() + self.notify.debug(' -') + golfBallGeom.setName('golfBallGeom%s' % avId) + self.ballDict[avId] = {'golfBall': golfBall, + 'golfBallGeom': golfBallGeom, + 'golfBallOdeGeom': odeGeom} + golfBall.disable() + shadow = self.dropShadowModel.copyTo(render) + shadow.setBin('shadow', 100) + shadow.setScale(0.09) + shadow.setDepthWrite(False) + shadow.setDepthTest(True) + self.ballShadowDict[avId] = shadow + shadow.hide() + + def setGolfCourseDoId(self, golfCourseDoId): + self.golfCourseDoId = golfCourseDoId + self.golfCourse = base.cr.doId2do.get(self.golfCourseDoId) + if not self.golfCourse: + self.cr.relatedObjectMgr.abortRequest(self.golfCourseRequest) + self.golfCourseRequest = self.cr.relatedObjectMgr.requestObjects([self.golfCourseDoId], eachCallback=self.__gotGolfCourse) + else: + self.scoreBoard = self.golfCourse.scoreBoard + self.scoreBoard.hide() + + def __gotGolfCourse(self, golfCourse): + self.golfCourseRequest = None + self.golfCourse = golfCourse + return + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def sendBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2): + self.swingBox.setPosition(pos0, pos1, pos2) + self.swingBox.setQuaternion(Quat(quat0, quat1, quat2, quat3)) + self.swingBox.setAngularVel(anV0, anV1, anV2) + self.swingBox.setLinearVel(lnV0, lnV1, lnV2) + + def hasCurGolferReachedMaxSwing(self): + strokes = self.golfCourse.getStrokesForCurHole(self.currentGolfer) + maxSwing = self.holeInfo['maxSwing'] + retval = strokes >= maxSwing + if retval: + pass + return retval + + def __getGolfPower(self, time): + elapsed = max(time - self.aimStart, 0.0) + t = elapsed / self.golfPowerSpeed + t = math.pow(t, self.golfPowerExponent) + power = int(t * 100) % 200 + if power > 100: + power = 200 - power + return power + + def __beginTossGolf(self): + if self.aimStart != None: + return + if not self.state == 'Aim': + return + if self.swingInfoSent: + return + self.localToonHitControl = True + time = globalClock.getFrameTime() + self.aimStart = time + messenger.send('wakeup') + self.scoreBoard.hide() + taskMgr.add(self.__updateGolfPower, self.golfPowerTaskName) + return + + def __endTossGolf(self): + if self.aimStart == None: + return + if not self.state == 'Aim': + return + messenger.send('wakeup') + taskMgr.remove(self.golfPowerTaskName) + self.aimStart = None + self.sendSwingInfo() + self.resetPowerBar() + return + + def __updateGolfPower(self, task): + if not self.powerBar: + print '### no power bar!!!' + return Task.done + newPower = self.__getGolfPower(globalClock.getFrameTime()) + self.power = newPower + self.powerBar['value'] = newPower + self.powerBar['text'] = TTLocalizer.GolfPowerBarText % {'power': newPower} + return Task.cont + + def golferChooseTee(self, avId): + self.readyCurrentGolfer(avId) + self.putAwayAllToons() + if self.needToDoFlyOver and self.doFlyOverMovie(avId): + pass + else: + if avId == localAvatar.doId: + self.setCamera2Ball() + if not self.state == 'ChooseTee': + self.request('ChooseTee') + else: + self.setCamera2Ball() + self.request('WatchTee') + self.takeOutToon(self.currentGolfer) + + def setAvatarTempTee(self, avId, tempTee): + if self.state != 'WatchTee': + return + if avId != self.currentGolfer: + self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) + return + self.changeTee(tempTee) + + def setAvatarFinalTee(self, avId, finalTee): + if avId != self.currentGolfer: + self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) + return + self.changeTee(finalTee) + + def setTempAimHeading(self, avId, heading): + if avId != self.currentGolfer: + self.notify.warning('setAvatarTempTee avId=%s not equal to self.currentGolfer=%s' % (avId, self.currentGolfer)) + return + if self.state != 'WatchAim': + return + if avId != localAvatar.doId: + self.ballFollow.setH(heading) + + def stickToonToBall(self, avId): + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(self.ballFollowToonSpot) + av.setPos(0, 0, 0) + av.setH(0) + + def putAwayToon(self, avId): + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(render) + av.setPos(0, 0, -1000) + av.setH(0) + + def putAwayAllToons(self): + for avId in self.avIdList: + self.putAwayToon(avId) + + def takeOutToon(self, avId): + self.stickToonToBall(avId) + self.fixCurrentGolferFeet() + self.attachClub(avId) + + def showOnlyCurGolfer(self): + self.notify.debug('curGolfer = %s' % self.currentGolfer) + self.stickToonToBall(self.currentGolfer) + self.fixCurrentGolferFeet() + self.attachClub(self.currentGolfer) + for avId in self.avIdList: + if avId != self.currentGolfer: + self.putAwayToon(avId) + + def tabKeyPressed(self): + doInterval = True + self.notify.debug('tab key pressed') + if not hasattr(self, 'ballFollow'): + return + if self.flyOverInterval and self.flyOverInterval.isPlaying(): + return + if self.camInterval and self.camInterval.isPlaying(): + self.camInterval.pause() + if base.camera.getParent() == self.ballFollow: + if doInterval: + curHpr = camera.getHpr(render) + angle = PythonUtil.closestDestAngle2(curHpr[0], 0) + self.camInterval = Sequence(Func(base.camera.wrtReparentTo, render), LerpPosHprInterval(base.camera, 2, self.camTopViewPos, self.camTopViewHpr)) + self.camInterval.start() + else: + base.camera.reparentTo(render) + base.camera.setPos(self.camTopViewPos) + base.camera.setHpr(self.camTopViewHpr) + elif doInterval: + curHpr = camera.getHpr(self.ballFollow) + angle = PythonUtil.closestDestAngle2(curHpr[0], 0) + self.camInterval = Sequence(Func(base.camera.wrtReparentTo, self.ballFollow), LerpPosHprInterval(base.camera, 2, self.camPosBallFollow, self.camHprBallFollow)) + self.camInterval.start() + else: + base.camera.reparentTo(self.ballFollow) + base.camera.setPos(self.camPosBallFollow) + base.camera.setHpr(self.camHprBallFollow) + + def doFlyOverMovie(self, avId): + title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' :\n ' + GolfGlobals.getHoleName(self.holeId) + '\n' + TTLocalizer.GolfPar + ' : ' + '%s' % self.holeInfo['par'] + self.titleLabel = DirectLabel(parent=aspect2d, relief=None, pos=(0, 0, 0.8), text_align=TextNode.ACenter, text=title, text_scale=0.12, text_font=ToontownGlobals.getSignFont(), text_fg=(1, 0.8, 0.4, 1)) + self.titleLabel.setBin('opaque', 19) + self.titleLabel.hide() + self.needToDoFlyOver = False + bamFile = self.holeInfo['terrainModel'] + fileName = bamFile.split('/')[-1] + dotIndex = fileName.find('.') + baseName = fileName[0:dotIndex] + camModelName = baseName + '_cammodel.bam' + cameraName = baseName + '_camera.bam' + path = bamFile[0:bamFile.find(fileName)] + camModelFullPath = path + camModelName + cameraAnimFullPath = path + cameraName + try: + self.flyOverActor = Actor.Actor(camModelFullPath, {'camera': cameraAnimFullPath}) + except StandardError: + self.notify.debug("Couldn't find flyover %s" % camModelFullPath) + return False + + base.transitions.noIris() + self.flyOverActor.reparentTo(render) + self.flyOverActor.setBlend(frameBlend=True) + flyOverJoint = self.flyOverActor.find('**/camera1') + children = flyOverJoint.getChildren() + numChild = children.getNumPaths() + for i in xrange(numChild): + childNodePath = children.getPath(i) + childNodePath.removeNode() + + self.flyOverJoint = flyOverJoint + self.flyOverInterval = Sequence(Func(base.camera.reparentTo, flyOverJoint), Func(base.camera.clearTransform), Func(self.titleLabel.show), ActorInterval(self.flyOverActor, 'camera'), Func(base.camera.reparentTo, self.ballFollow), Func(base.camera.setPos, self.camPosBallFollow), Func(base.camera.setHpr, self.camHprBallFollow)) + if avId == localAvatar.doId: + self.flyOverInterval.append(Func(self.setCamera2Ball)) + self.flyOverInterval.append(Func(self.safeRequestToState, 'ChooseTee')) + else: + self.flyOverInterval.append(Func(self.setCamera2Ball)) + self.flyOverInterval.append(Func(self.safeRequestToState, 'WatchTee')) + self.flyOverInterval.append(Func(self.titleLabel.hide)) + self.flyOverInterval.append(Func(self.takeOutToon, avId)) + self.flyOverInterval.start() + return True + + def avExited(self, avId): + if self.state == 'Playback' and self.currentGolfer == avId: + pass + else: + self.ballDict[avId]['golfBallGeom'].hide() + + def orientCameraRay(self): + pos = base.camera.getPos(self.terrainModel) + self.cameraRayNodePath.setPos(pos) + self.cameraRayNodePath.lookAt(self.ballFollow) + renderPos = self.cameraRayNodePath.getPos(render) + if renderPos != pos: + self.notify.debug('orientCamerRay this should not happen') + ballPos = self.ballFollow.getPos(self.terrainModel) + dirCam = Vec3(ballPos - pos) + dirCam.normalize() + self.cameraRay.set(pos, dirCam) + + def performSwing(self, ball, power, dirX, dirY): + startTime = globalClock.getRealTime() + avId = base.localAvatar.doId + position = ball.getPosition() + x = position[0] + y = position[1] + z = position[2] + if avId not in self.golfCourse.drivingToons: + x = position[0] + y = position[1] + z = position[2] + self.swingTime = cycleTime + lift = 0 + ball = self.ball + forceMove = 2500 + if power > 50: + lift = 0 + ball.enable() + ball.setPosition(x, y, z) + ball.setLinearVel(0.0, 0.0, 0.0) + ball.setAngularVel(0.0, 0.0, 0.0) + ball.addForce(Vec3(dirX * forceMove * power / 100.0, dirY * forceMove * power / 100.0, lift)) + self.initRecord() + safety = 0 + self.llv = None + self.record(ball) + while ball.isEnabled() and len(self.recording) < 2000: + self.preStep() + self.simulate() + self.postStep() + self.record(ball) + safety += 1 + + self.record(ball) + midTime = globalClock.getRealTime() + self.processRecording() + self.processAVRecording() + self.notify.debug('Recording End time %s cycle %s len %s avLen %s' % (self.timingSimTime, + self.getSimCycleTime(), + len(self.recording), + len(self.aVRecording))) + self.request('WaitPlayback') + length = len(self.recording) - 1 + x = self.recording[length][1] + y = self.recording[length][2] + z = self.recording[length][3] + self.ballPos[avId] = Vec3(x, y, z) + endTime = globalClock.getRealTime() + diffTime = endTime - startTime + fpsTime = self.frame / diffTime + self.notify.debug('Time Start %s Mid %s End %s Diff %s Fps %s frames %s' % (startTime, + midTime, + endTime, + diffTime, + fpsTime, + self.frame)) + self.ballMovie2Client(cycleTime, avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame) + return + + def handleBallHitNonGrass(self, c0, c1): + if not self.inPlayBack: + return + golfBallPos = self.curGolfBall().getPosition() + if self.lastBumpSfxPos == golfBallPos: + return + if GolfGlobals.HARD_COLLIDE_ID in [c0, c1]: + if not self.bumpHardSfx.status() == self.bumpHardSfx.PLAYING: + distance = (golfBallPos - self.lastBumpSfxPos).length() + if distance > 2.0: + base.playSfx(self.bumpHardSfx) + self.lastBumpSfxPos = golfBallPos + elif GolfGlobals.MOVER_COLLIDE_ID in [c0, c1]: + if not self.bumpMoverSfx.status() == self.bumpMoverSfx.PLAYING: + base.playSfx(self.bumpMoverSfx) + self.lastBumpSfxPos = golfBallPos + elif GolfGlobals.WINDMILL_BASE_COLLIDE_ID in [c0, c1]: + if not self.bumpWindmillSfx.status() == self.bumpWindmillSfx.PLAYING: + base.playSfx(self.bumpWindmillSfx) + self.lastBumpSfxPos = golfBallPos + + def safeRequestToState(self, newState): + doingRequest = False + if self.state in self.defaultTransitions: + if newState in self.defaultTransitions[self.state]: + self.request(newState) + doingRequest = True + if not doingRequest: + self.notify.warning('ignoring transition from %s to %s' % (self.state, newState)) + + def doMagicWordHeading(self, heading): + if self.state == 'Aim': + self.aimMomentum = 0.0 + self.ballFollow.setH(float(heading)) + + def _handleClientCleanup(self): + self.removePlayBackDelayDelete() + self.ignore('clientCleanup') diff --git a/toontown/golf/DistributedGolfHoleAI.py b/toontown/golf/DistributedGolfHoleAI.py new file mode 100644 index 0000000..41f4842 --- /dev/null +++ b/toontown/golf/DistributedGolfHoleAI.py @@ -0,0 +1,441 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +import DistributedPhysicsWorldAI +from direct.fsm.FSM import FSM +from toontown.ai.ToonBarrier import * +from toontown.golf import GolfGlobals +import random +from toontown.golf import GolfHoleBase + +class DistributedGolfHoleAI(DistributedPhysicsWorldAI.DistributedPhysicsWorldAI, FSM, GolfHoleBase.GolfHoleBase): + defaultTransitions = {'Off': ['Cleanup', 'WaitTee'], 'WaitTee': ['WaitSwing', 'Cleanup', 'WaitTee', 'WaitPlayback'], 'WaitSwing': ['WaitPlayback', 'Cleanup', 'WaitSwing', 'WaitTee'], 'WaitPlayback': ['WaitSwing', 'Cleanup', 'WaitTee', 'WaitPlayback'], 'Cleanup': ['Off']} + id = 0 + notify = directNotify.newCategory('DistributedGolfHoleAI') + + def __init__(self, zoneId, golfCourse, holeId): + FSM.__init__(self, 'Golf_%s_FSM' % self.id) + DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.__init__(self, simbase.air) + GolfHoleBase.GolfHoleBase.__init__(self) + self.zoneId = zoneId + self.golfCourse = golfCourse + self.holeId = holeId + self.avIdList = golfCourse.avIdList[:] + self.watched = [0, 0, 0, 0] + self.barrierPlayback = None + self.trustedPlayerId = None + self.activeGolferIndex = None + self.activeGolferId = None + self.holeInfo = GolfGlobals.HoleInfo[self.holeId] + self.teeChosen = {} + for avId in self.avIdList: + self.teeChosen[avId] = -1 + + self.ballPos = {} + for avId in self.avIdList: + self.ballPos[avId] = Vec3(0, 0, 0) + + self.playStarted = False + return + + def curGolfBall(self): + return self.ball + + def generate(self): + DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.generate(self) + self.ball = self.createBall() + self.createRays() + if len(self.teePositions) > 1: + startPos = self.teePositions[1] + else: + startPos = self.teePositions[0] + startPos += Vec3(0, 0, GolfGlobals.GOLF_BALL_RADIUS) + self.ball.setPosition(startPos) + + def delete(self): + self.notify.debug('__delete__') + DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.delete(self) + self.notify.debug('calling self.terrainModel.removeNode') + self.terrainModel.removeNode() + self.notify.debug('self.barrierPlayback is %s' % self.barrierPlayback) + if self.barrierPlayback: + self.notify.debug('calling self.barrierPlayback.cleanup') + self.barrierPlayback.cleanup() + self.notify.debug('calling self.barrierPlayback = None') + self.barrierPlayback = None + self.activeGolferId = None + return + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setAvatarReadyHole(self): + self.notify.debugStateCall(self) + avId = self.air.getAvatarIdFromSender() + self.golfCourse.avatarReadyHole(avId) + + def startPlay(self): + self.notify.debug('startPlay') + self.playStarted = True + self.numGolfers = len(self.golfCourse.getGolferIds()) + self.selectNextGolfer() + + def selectNextGolfer(self): + self.notify.debug('selectNextGolfer, old golferIndex=%s old golferId=%s' % (self.activeGolferIndex, self.activeGolferId)) + if self.golfCourse.isCurHoleDone(): + return + if self.activeGolferIndex == None: + self.activeGolferIndex = 0 + self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + else: + self.activeGolferIndex += 1 + if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()): + self.activeGolferIndex = 0 + self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + safety = 0 + while safety < 50 and not self.golfCourse.checkGolferPlaying(self.golfCourse.getGolferIds()[self.activeGolferIndex]): + self.activeGolferIndex += 1 + self.notify.debug('Index %s' % self.activeGolferIndex) + if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()): + self.activeGolferIndex = 0 + self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + safety += 1 + + if safety != 50: + golferId = self.golfCourse.getGolferIds()[self.activeGolferIndex] + if self.teeChosen[golferId] == -1: + self.sendUpdate('golferChooseTee', [golferId]) + self.request('WaitTee') + else: + self.sendUpdate('golfersTurn', [golferId]) + self.request('WaitSwing') + else: + self.notify.debug('safety') + self.notify.debug('selectNextGolfer, new golferIndex=%s new golferId=%s' % (self.activeGolferIndex, self.activeGolferId)) + return + + def clearWatched(self): + self.watched = [ + 1, 1, 1, 1] + for index in range(len(self.golfCourse.getGolferIds())): + self.watched[index] = 0 + + def setWatched(self, avId): + for index in range(len(self.golfCourse.getGolferIds())): + if self.golfCourse.getGolferIds()[index] == avId: + self.watched[index] = 1 + + def checkWatched(self): + if 0 not in self.watched: + return True + else: + return False + + def turnDone(self): + self.notify.debug('Turn Done') + avId = self.air.getAvatarIdFromSender() + if self.barrierPlayback: + self.barrierPlayback.clear(avId) + + def ballInHole(self, golferId=None): + self.notify.debug('ballInHole') + if golferId: + avId = golferId + else: + avId = self.air.getAvatarIdFromSender() + self.golfCourse.setBallIn(avId) + if self.golfCourse.isCurHoleDone(): + self.notify.debug('ballInHole doing nothing') + else: + self.notify.debug('ballInHole calling self.selectNextGolfer') + self.selectNextGolfer() + + def getHoleId(self): + return self.holeId + + def finishHole(self): + self.notify.debug('finishHole') + self.golfCourse.holeOver() + + def getGolferIds(self): + return self.avIdList + + def loadLevel(self): + GolfHoleBase.GolfHoleBase.loadLevel(self) + optionalObjects = self.terrainModel.findAllMatches('**/optional*') + requiredObjects = self.terrainModel.findAllMatches('**/required*') + self.parseLocators(optionalObjects, 1) + self.parseLocators(requiredObjects, 0) + self.teeNodePath = self.terrainModel.find('**/tee0') + if self.teeNodePath.isEmpty(): + teePos = Vec3(0, 0, 10) + else: + teePos = self.teeNodePath.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH()) + self.teePositions = [ + teePos] + teeIndex = 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + while not teeNode.isEmpty(): + teePos = teeNode.getPos() + teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS) + self.teePositions.append(teePos) + self.notify.debug('teeNodeP heading = %s' % teeNode.getH()) + teeIndex += 1 + teeNode = self.terrainModel.find('**/tee%d' % teeIndex) + + def createLocatorDict(self): + self.locDict = {} + locatorNum = 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + while not curNodePath.isEmpty(): + self.locDict[locatorNum] = curNodePath + locatorNum += 1 + curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum) + + def loadBlockers(self): + loadAll = simbase.config.GetBool('golf-all-blockers', 0) + self.createLocatorDict() + self.blockerNums = self.holeInfo['blockers'] + for locatorNum in self.locDict: + if locatorNum in self.blockerNums or loadAll: + locator = self.locDict[locatorNum] + locatorParent = locator.getParent() + locator.getChildren().wrtReparentTo(locatorParent) + else: + self.locDict[locatorNum].removeNode() + + self.hardSurfaceNodePath.flattenStrong() + + def createBall(self): + golfBallGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, 1)[1] + return golfBallGeom + + def preStep(self): + GolfHoleBase.GolfHoleBase.preStep(self) + + def postStep(self): + GolfHoleBase.GolfHoleBase.postStep(self) + + def postSwing(self, cycleTime, power, x, y, z, dirX, dirY): + avId = self.air.getAvatarIdFromSender() + self.storeAction = [ + avId, cycleTime, power, x, y, z, dirX, dirY] + if self.commonHoldData: + self.doAction() + + def postSwingState(self, cycleTime, power, x, y, z, dirX, dirY, curAimTime, commonObjectData): + self.notify.debug('postSwingState') + if not self.golfCourse.getStillPlayingAvIds(): + return + avId = self.air.getAvatarIdFromSender() + self.storeAction = [avId, cycleTime, power, x, y, z, dirX, dirY] + self.commonHoldData = commonObjectData + self.trustedPlayerId = self.choosePlayerToSimulate() + self.sendUpdateToAvatarId(self.trustedPlayerId, 'assignRecordSwing', [avId, cycleTime, power, x, y, z, dirX, dirY, commonObjectData]) + self.golfCourse.addAimTime(avId, curAimTime) + + def choosePlayerToSimulate(self): + stillPlaying = self.golfCourse.getStillPlayingAvIds() + playerId = 0 + if simbase.air.config.GetBool('golf-trust-driver-first', 0): + if stillPlaying: + playerId = stillPlaying[0] + else: + playerId = random.choice(stillPlaying) + return playerId + + def ballMovie2AI(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData): + sentFromId = self.air.getAvatarIdFromSender() + if sentFromId == self.trustedPlayerId: + lastFrameNum = len(movie) - 2 + if lastFrameNum < 0: + lastFrameNum = 0 + lastFrame = movie[lastFrameNum] + lastPos = Vec3(lastFrame[1], lastFrame[2], lastFrame[3]) + self.ballPos[avId] = lastPos + self.golfCourse.incrementScore(avId) + for id in self.golfCourse.getStillPlayingAvIds(): + if not id == sentFromId: + self.sendUpdateToAvatarId(id, 'ballMovie2Client', [cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData]) + + if self.state == 'WaitPlayback' or self.state == 'WaitTee': + self.notify.warning('ballMovie2AI requesting from %s to WaitPlayback' % self.state) + self.request('WaitPlayback') + else: + if self.trustedPlayerId == None: + return + else: + self.doAction() + self.trustedPlayerId = None + return + + def performReadyAction(self): + avId = self.storeAction[0] + if self.state == 'WaitPlayback': + self.notify.debugStateCall(self) + self.notify.debug('ignoring the postSwing for avId=%d since we are in WaitPlayback' % avId) + return + if avId == self.activeGolferId: + self.golfCourse.incrementScore(self.activeGolferId) + else: + self.notify.warning('activGolferId %d not equal to sender avId %d' % (self.activeGolferId, avId)) + if avId not in self.golfCourse.drivingToons: + position = self.ballPos[avId] + else: + position = Vec3(self.storeAction[3], self.storeAction[4], self.storeAction[5]) + self.useCommonObjectData(self.commonHoldData) + newPos = self.trackRecordBodyFlight(self.ball, self.storeAction[1], self.storeAction[2], position, self.storeAction[6], self.storeAction[7]) + if self.state == 'WaitPlayback' or self.state == 'WaitTee': + self.notify.warning('performReadyAction requesting from %s to WaitPlayback' % self.state) + self.request('WaitPlayback') + self.sendUpdate('ballMovie2Client', [self.storeAction[1], avId, self.recording, self.aVRecording, self.ballInHoleFrame, self.ballTouchedHoleFrame, self.ballFirstTouchedHoleFrame, self.commonHoldData]) + self.ballPos[avId] = newPos + self.trustedPlayerId = None + return + + def postResult(self, cycleTime, avId, recording, aVRecording, ballInHoleFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame): + pass + + def enterWaitSwing(self): + pass + + def exitWaitSwing(self): + pass + + def enterWaitTee(self): + pass + + def exitWaitTee(self): + pass + + def enterWaitPlayback(self): + self.notify.debug('enterWaitPlayback') + stillPlayingList = self.golfCourse.getStillPlayingAvIds() + self.barrierPlayback = ToonBarrier('waitClientsPlayback', self.uniqueName('waitClientsPlayback'), stillPlayingList, 120, self.handleWaitPlaybackDone, self.handlePlaybackTimeout) + + def hasCurGolferReachedMaxSwing(self): + strokes = self.golfCourse.getCurHoleScore(self.activeGolferId) + maxSwing = self.holeInfo['maxSwing'] + retval = strokes >= maxSwing + if retval: + av = simbase.air.doId2do.get(self.activeGolferId) + if av: + if av.getUnlimitedSwing(): + retval = False + return retval + + def handleWaitPlaybackDone(self): + if self.isCurBallInHole(self.activeGolferId) or self.hasCurGolferReachedMaxSwing(): + if self.activeGolferId: + self.ballInHole(self.activeGolferId) + else: + self.selectNextGolfer() + + def isCurBallInHole(self, golferId): + retval = False + senderId = self.air.getAvatarIdFromSender() + if golferId not in self.ballPos: + self.notify.warning('golferId=%s not in self.ballPos=%s' % (golferId, self.ballPos)) + simbase.air.writeServerEvent('suspicious', senderId, 'isCurBallInHole golferId=%s not in self.ballPos=%s' % (golferId, self.ballPos)) + return False + for holePos in self.holePositions: + displacement = self.ballPos[golferId] - holePos + length = displacement.length() + self.notify.debug('hole %s length=%s' % (holePos, length)) + if length <= GolfGlobals.DistanceToBeInHole: + retval = True + break + + return retval + + def exitWaitPlayback(self): + self.notify.debug('exitWaitPlayback') + if hasattr(self, 'barrierPlayback') and self.barrierPlayback: + self.barrierPlayback.cleanup() + self.barrierPlayback = None + return + + def enterCleanup(self): + pass + + def exitCleanup(self): + pass + + def handlePlaybackTimeout(self, task=None): + self.notify.debug('handlePlaybackTimeout') + self.handleWaitPlaybackDone() + + def getGolfCourseDoId(self): + return self.golfCourse.doId + + def avatarDropped(self, avId): + self.notify.warning('avId %d dropped, self.state=%s' % (avId, self.state)) + if self.barrierPlayback: + self.barrierPlayback.clear(avId) + else: + if avId == self.trustedPlayerId: + self.doAction() + if avId == self.activeGolferId and not self.golfCourse.haveAllGolfersExited(): + self.selectNextGolfer() + + def setAvatarTee(self, chosenTee): + golferId = self.air.getAvatarIdFromSender() + self.teeChosen[golferId] = chosenTee + self.ballPos[golferId] = self.teePositions[chosenTee] + self.sendUpdate('setAvatarFinalTee', [golferId, chosenTee]) + self.sendUpdate('golfersTurn', [golferId]) + self.request('WaitSwing') + + def setBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2): + self.sendUpdate('sendBox', [ + pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2]) + + def parseLocators(self, objectCollection, optional = 0): + if optional and objectCollection.getNumPaths(): + if self.holeInfo.has_key('optionalMovers'): + for optionalMoverId in self.holeInfo['optionalMovers']: + searchStr = 'optional_mover_' + str(optionalMoverId) + for objIndex in xrange(objectCollection.getNumPaths()): + object = objectCollection.getPath(objIndex) + if searchStr in object.getName(): + self.fillLocator(objectCollection, objIndex) + break + + else: + for index in range(objectCollection.getNumPaths()): + self.fillLocator(objectCollection, index) + + def fillLocator(self, objectCollection, index): + path = objectCollection[index] + pathName = path.getName() + pathArray = pathName.split('_') + sizeX = None + sizeY = None + move = None + type = None + for subString in pathArray: + if subString[:1] == 'X': + dataString = subString[1:] + dataString = dataString.replace('p', '.') + sizeX = float(dataString) + elif subString[:1] == 'Y': + dataString = subString[1:] + dataString = dataString.replace('p', '.') + sizeY = float(dataString) + elif subString[:1] == 'd': + dataString = subString[1:] + dataString = dataString.replace('p', '.') + move = float(dataString) + elif subString == 'mover': + type = 4 + elif subString == 'windmillLocator': + type = 3 + + if type == 4 and move and sizeX and sizeY: + self.createCommonObject(4, path.getPos(), path.getHpr(), sizeX, sizeY, move) + elif type == 3: + self.createCommonObject(3, path.getPos(), path.getHpr()) + return diff --git a/toontown/golf/DistributedPhysicsWorld.py b/toontown/golf/DistributedPhysicsWorld.py new file mode 100644 index 0000000..9ba099c --- /dev/null +++ b/toontown/golf/DistributedPhysicsWorld.py @@ -0,0 +1,107 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from math import * +import math +from direct.fsm.FSM import FSM +from toontown.minigame import ArrowKeys +from direct.showbase import PythonUtil +from direct.showutil import Rope +from direct.task import Task +from direct.distributed.ClockDelta import * +import BuildGeometry +from toontown.golf import GolfGlobals +from toontown.golf import PhysicsWorldBase +import random, time +from direct.interval.SoundInterval import SoundInterval + +def scalp(vec, scal): + vec0 = vec[0] * scal + vec1 = vec[1] * scal + vec2 = vec[2] * scal + vec = Vec3(vec0, vec1, vec2) + + +def length(vec): + return sqrt(vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2) + + +class DistributedPhysicsWorld(DistributedObject.DistributedObject, PhysicsWorldBase.PhysicsWorldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhysicsWorld') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + PhysicsWorldBase.PhysicsWorldBase.__init__(self, 1) + self.accept('ode toggle contacts', self.__handleToggleContacts) + self.physicsSfxDict = {} + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.setupSimulation() + self.startSim() + + def delete(self): + DistributedObject.DistributedObject.delete(self) + PhysicsWorldBase.PhysicsWorldBase.delete(self) + taskMgr.remove('simulation task') + self.ignoreAll() + for index in self.physicsSfxDict: + sfxPair = self.physicsSfxDict[index] + sfxPair[0].stop() + sfxPair[1].finish() + + self.physicsSfxDict = None + return + + def clientCommonObject(self, type, commonId, pos, hpr, sizeX, sizeY, moveDistance): + data = self.createCommonObject(type, commonId, pos, hpr, sizeX, sizeY, moveDistance) + index = data[1] + if type == 3: + cross = self.commonObjectDict[commonId][2] + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if odeBody == cross: + base.sfxPlayer.setCutoffDistance(240) + self.notify.debug('nodePath = %s' % pandaNodePathGeom) + windmillSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Windmill_Loop.wav') + windMillSoundInterval = SoundInterval(windmillSfx, node=pandaNodePathGeom, listenerNode=base.camera, seamlessLoop=True, volume=0.5) + windMillSoundInterval.loop() + self.physicsSfxDict[index] = (windmillSfx, windMillSoundInterval) + break + + elif type == 4: + box = self.commonObjectDict[commonId][2] + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if odeBody == box: + self.notify.debug('nodePath = %s' % pandaNodePathGeom) + moverSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Moving_Barrier.mp3') + moverSoundInterval = SoundInterval(moverSfx, node=pandaNodePathGeom, listenerNode=base.camera, seamlessLoop=True, volume=0.5) + moverSoundInterval.start() + self.physicsSfxDict[index] = (moverSfx, moverSoundInterval, index) + break + + def commonObjectEvent(self, key, model, type, force, event): + self.notify.debug('commonObjectForceEvent key %s model %s type %s force %s event %s' % (key, + model, + type, + force, + event)) + if type == 4: + if event > 0: + self.physicsSfxDict[key][1].start() + + def setCommonObjects(self, objectData): + self.useCommonObjectData(objectData) + + def upSendCommonObjects(self): + self.sendUpdate('upSetCommonObjects', [self.getCommonObjectData()]) + + def __handleToggleContacts(self, message = None): + if self.showContacts: + self.showContacts = 0 + else: + self.showContacts = 1 diff --git a/toontown/golf/DistributedPhysicsWorldAI.py b/toontown/golf/DistributedPhysicsWorldAI.py new file mode 100644 index 0000000..6086d8d --- /dev/null +++ b/toontown/golf/DistributedPhysicsWorldAI.py @@ -0,0 +1,69 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +import BuildGeometry +import random, time +from math import * +import math +from toontown.golf import PhysicsWorldBase + +class DistributedPhysicsWorldAI(DistributedObjectAI.DistributedObjectAI, PhysicsWorldBase.PhysicsWorldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhysicsWorldAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + PhysicsWorldBase.PhysicsWorldBase.__init__(self, 0) + self.commonHoldData = None + self.storeAction = None + self.holdingUpObjectData = 0 + return + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.loadLevel() + self.setupSimulation() + + def delete(self): + self.notify.debug('Calling DistributedObjectAI.delete') + DistributedObjectAI.DistributedObjectAI.delete(self) + self.notify.debug('Calling PhysicsWorldBase.delete') + PhysicsWorldBase.PhysicsWorldBase.delete(self) + + def loadLevel(self): + pass + + def createCommonObject(self, type, pos, hpr, sizeX = 0, sizeY = 0, moveDistance = 0): + commonObjectDatam = PhysicsWorldBase.PhysicsWorldBase.createCommonObject(self, type, None, pos, hpr, sizeX, sizeY, moveDistance) + self.sendUpdate('clientCommonObject', commonObjectDatam) + return + + def updateCommonObjects(self): + self.sendUpdate('setCommonObjects', [self.getCommonObjectData()]) + + def doAction(self): + print 'doing Action' + print 'before performReadyAction' + self.performReadyAction() + self.storeAction = None + self.commonHoldData = None + return + + def upSetCommonObjects(self, objectData): + self.holdingUpObjectData = 1 + self.commonHoldData = objectData + if self.storeAction: + self.doAction() + + def setupCommonObjects(self): + print 'setupCommonObjects' + print self.commonHoldData + if not self.commonHoldData: + return + elif self.commonHoldData[0][1] == 99: + print 'no common objects' + else: + self.useCommonObjectData(self.commonHoldData, 0) + + def performReadyAction(self): + print 'Wrong performReadyAction' diff --git a/toontown/golf/GolfGlobals.py b/toontown/golf/GolfGlobals.py new file mode 100644 index 0000000..a481a64 --- /dev/null +++ b/toontown/golf/GolfGlobals.py @@ -0,0 +1,486 @@ +from direct.directnotify import DirectNotifyGlobal +import random +MAX_PLAYERS_PER_HOLE = 4 +GOLF_BALL_RADIUS = 0.25 +GOLF_BALL_VOLUME = 4.0 / 3.0 * 3.14159 * GOLF_BALL_RADIUS ** 3 +GOLF_BALL_MASS = 0.5 +GOLF_BALL_DENSITY = GOLF_BALL_MASS / GOLF_BALL_VOLUME +GRASS_SURFACE = 0 +BALL_SURFACE = 1 +HARD_SURFACE = 2 +HOLE_SURFACE = 3 +SLICK_SURFACE = 4 +OOB_RAY_COLLIDE_ID = -1 +GRASS_COLLIDE_ID = 2 +HARD_COLLIDE_ID = 3 +TOON_RAY_COLLIDE_ID = 4 +MOVER_COLLIDE_ID = 7 +WINDMILL_BASE_COLLIDE_ID = 8 +CAMERA_RAY_COLLIDE_ID = 10 +BALL_COLLIDE_ID = 42 +HOLE_CUP_COLLIDE_ID = 64 +SKY_RAY_COLLIDE_ID = 78 +SLICK_COLLIDE_ID = 13 +BALL_CONTACT_FRAME = 9 +BALL_CONTACT_TIME = (BALL_CONTACT_FRAME + 1) / 24.0 +AIM_DURATION = 60 +TEE_DURATION = 15 +RANDOM_HOLES = True +KICKOUT_SWINGS = 2 +TIME_TIE_BREAKER = True +CourseInfo = {0: {'name': '', + 'numHoles': 3, + 'holeIds': (2, + 3, + 4, + 5, + 6, + 7, + 8, + 12, + 13, + 15, + 16)}, + 1: {'name': '', + 'numHoles': 6, + 'holeIds': ((0, 5), + (1, 5), + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + (11, 5), + 12, + 13, + (14, 5), + 15, + 16, + (17, 5), + (20, 5), + (21, 5), + (22, 5), + (23, 5), + (24, 5), + (25, 5), + (26, 5), + (28, 5), + (30, 5), + (31, 5), + (33, 5), + (34, 5))}, + 2: {'name': '', + 'numHoles': 9, + 'holeIds': ((1, 5), + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + (14, 5), + 15, + (17, 5), + (18, 20), + (19, 20), + (20, 20), + (21, 5), + (22, 5), + (23, 20), + (24, 20), + (25, 20), + (26, 20), + (27, 20), + (28, 20), + (29, 20), + (30, 5), + (31, 20), + (32, 20), + (33, 5), + (34, 20), + (35, 20))}} +HoleInfo = {0: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole18.bam', + 'physicsData': 'golfGreen18', + 'blockers': (), + 'optionalMovers': ()}, + 1: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole1.bam', + 'physicsData': 'golfGreen1', + 'blockers': ()}, + 2: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole2.bam', + 'physicsData': 'golfGreen2', + 'blockers': ()}, + 3: {'name': '', + 'par': 2, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole3.bam', + 'physicsData': 'golfGreen3', + 'blockers': ()}, + 4: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole4.bam', + 'physicsData': 'golfGreen4', + 'blockers': ()}, + 5: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole5.bam', + 'physicsData': 'golfGreen2', + 'blockers': ()}, + 6: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole6.bam', + 'physicsData': 'golfGreen6', + 'blockers': ()}, + 7: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole7.bam', + 'physicsData': 'golfGreen7', + 'blockers': ()}, + 8: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole8.bam', + 'physicsData': 'golfGreen8', + 'blockers': ()}, + 9: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole9.bam', + 'physicsData': 'golfGreen9', + 'blockers': 2}, + 10: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole10.bam', + 'physicsData': 'golfGreen10', + 'blockers': ()}, + 11: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole11.bam', + 'physicsData': 'golfGreen11', + 'blockers': ()}, + 12: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole12.bam', + 'physicsData': 'golfGreen12', + 'blockers': ()}, + 13: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole13.bam', + 'physicsData': 'golfGreen13', + 'blockers': ()}, + 14: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole14.bam', + 'physicsData': 'golfGreen14', + 'blockers': ()}, + 15: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole15.bam', + 'physicsData': 'golfGreen15', + 'blockers': ()}, + 16: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole16.bam', + 'physicsData': 'golfGreen16', + 'blockers': ()}, + 17: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole17.bam', + 'physicsData': 'golfGreen17', + 'blockers': ()}, + 18: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole18.bam', + 'physicsData': 'golfGreen18', + 'blockers': (1, 2), + 'optionalMovers': 1}, + 19: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole1.bam', + 'physicsData': 'golfGreen1', + 'blockers': (2, 5)}, + 20: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole2.bam', + 'physicsData': 'golfGreen2', + 'blockers': (1, 3)}, + 21: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole3.bam', + 'physicsData': 'golfGreen3', + 'blockers': (1, 2, 3)}, + 22: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole4.bam', + 'physicsData': 'golfGreen4', + 'blockers': 2}, + 23: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole5.bam', + 'physicsData': 'golfGreen5', + 'blockers': (3, 4), + 'optionalMovers': 1}, + 24: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole6.bam', + 'physicsData': 'golfGreen6', + 'blockers': 1, + 'optionalMovers': 1}, + 25: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole7.bam', + 'physicsData': 'golfGreen7', + 'blockers': 3, + 'optionalMovers': 1}, + 26: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole8.bam', + 'physicsData': 'golfGreen8', + 'blockers': (), + 'optionalMovers': 1}, + 27: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole9.bam', + 'physicsData': 'golfGreen9', + 'blockers': (), + 'optionalMovers': (1, 2)}, + 28: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole10.bam', + 'physicsData': 'golfGreen10', + 'blockers': (), + 'optionalMovers': (1, 2)}, + 29: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole11.bam', + 'physicsData': 'golfGreen11', + 'blockers': (), + 'optionalMovers': 1}, + 30: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole12.bam', + 'physicsData': 'golfGreen12', + 'blockers': (1, 2, 3)}, + 31: {'name': '', + 'par': 4, + 'maxSwing': 7, + 'terrainModel': 'phase_6/models/golf/hole13.bam', + 'physicsData': 'golfGreen13', + 'blockers': (3, 4), + 'optionalMovers': 1}, + 32: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole14.bam', + 'physicsData': 'golfGreen14', + 'blockers': 1, + 'optionalMovers': 1}, + 33: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole15.bam', + 'physicsData': 'golfGreen15', + 'blockers': (1, 2, 3), + 'optionalMovers': (1, 2)}, + 34: {'name': '', + 'par': 3, + 'maxSwing': 6, + 'terrainModel': 'phase_6/models/golf/hole16.bam', + 'physicsData': 'golfGreen16', + 'blockers': (1, + 2, + 5, + 6), + 'optionalMovers': 1}, + 35: {'name': '', + 'par': 4, + 'maxSwing': 7, + 'terrainModel': 'phase_6/models/golf/hole17.bam', + 'physicsData': 'golfGreen17', + 'blockers': (3, 4, 5)}} +for holeId in HoleInfo: + if type(HoleInfo[holeId]['blockers']) == type(0): + blockerNum = HoleInfo[holeId]['blockers'] + HoleInfo[holeId]['blockers'] = (blockerNum,) + if HoleInfo[holeId].has_key('optionalMovers'): + if type(HoleInfo[holeId]['optionalMovers']) == type(0): + blockerNum = HoleInfo[holeId]['optionalMovers'] + HoleInfo[holeId]['optionalMovers'] = (blockerNum,) + +DistanceToBeInHole = 0.75 +CoursesCompleted = 0 +CoursesUnderPar = 1 +HoleInOneShots = 2 +EagleOrBetterShots = 3 +BirdieOrBetterShots = 4 +ParOrBetterShots = 5 +MultiPlayerCoursesCompleted = 6 +CourseZeroWins = 7 +CourseOneWins = 8 +CourseTwoWins = 9 +TwoPlayerWins = 10 +ThreePlayerWins = 11 +FourPlayerWins = 12 +MaxHistoryIndex = 9 +NumHistory = MaxHistoryIndex + 1 +CalcOtherHoleBest = False +CalcOtherCourseBest = False +TrophyRequirements = {CoursesCompleted: (4, 40, 400), + CoursesUnderPar: (1, 10, 100), + HoleInOneShots: (1, 10, 100), + EagleOrBetterShots: (2, 20, 200), + BirdieOrBetterShots: (3, 30, 300), + ParOrBetterShots: (4, 40, 400), + MultiPlayerCoursesCompleted: (6, 60, 600), + CourseZeroWins: (1, 10, 100), + CourseOneWins: (1, 10, 100), + CourseTwoWins: (1, 10, 100)} +PlayerColors = [(0.925, + 0.168, + 0.168, + 1), + (0.13, + 0.59, + 0.973, + 1), + (0.973, + 0.809, + 0.129, + 1), + (0.598, + 0.402, + 0.875, + 1)] +KartColors = [[[0, 50], [90, 255], [0, 85]], [[160, 255], [-15, 15], [0, 120]], [[160, 255], [0, 110], [0, 110]]] +NumTrophies = 0 +for key in TrophyRequirements: + NumTrophies += len(TrophyRequirements[key]) + +NumCups = 3 +TrophiesPerCup = NumTrophies / NumCups + +def calcTrophyListFromHistory(history): + retval = [] + historyIndex = 0 + for trophyIndex in xrange(NumHistory): + requirements = TrophyRequirements[trophyIndex] + for amountNeeded in requirements: + if history[historyIndex] >= amountNeeded: + retval.append(True) + else: + retval.append(False) + + historyIndex += 1 + + return retval + + +def calcCupListFromHistory(history): + retval = [False] * NumCups + trophyList = calcTrophyListFromHistory(history) + numTrophiesWon = 0 + for gotTrophy in trophyList: + if gotTrophy: + numTrophiesWon += 1 + + for cupIndex in xrange(len(retval)): + threshold = (cupIndex + 1) * TrophiesPerCup + if threshold <= numTrophiesWon: + retval[cupIndex] = True + + return retval + + +def getCourseName(courseId): + from toontown.toonbase import TTLocalizer + if courseId in CourseInfo: + if not CourseInfo[courseId]['name']: + CourseInfo[courseId]['name'] = TTLocalizer.GolfCourseNames[courseId] + return CourseInfo[courseId]['name'] + else: + return '' + + +def getHoleName(holeId): + from toontown.toonbase import TTLocalizer + if holeId in HoleInfo: + if not HoleInfo[holeId]['name']: + HoleInfo[holeId]['name'] = TTLocalizer.GolfHoleNames[holeId] + return HoleInfo[holeId]['name'] + else: + return '' + + +def getHistoryIndexForTrophy(trophyIndex): + retval = -1 + divBy3 = int(trophyIndex / 3) + if divBy3 < NumHistory: + retval = divBy3 + return retval + + +def packGolfHoleBest(holeBest): + retval = [] + shiftLeft = False + for hole in holeBest: + hole &= 15 + if shiftLeft: + retval[-1] |= hole << 4 + shiftLeft = False + else: + retval.append(hole) + shiftLeft = True + + return retval + + +def unpackGolfHoleBest(packedHoleBest): + retval = [] + for packedHole in packedHoleBest: + lowbitHole = packedHole & 15 + retval.append(lowbitHole) + highBitHole = (packedHole & 240) >> 4 + retval.append(highBitHole) + + return retval diff --git a/toontown/golf/GolfHoleBase.py b/toontown/golf/GolfHoleBase.py new file mode 100644 index 0000000..ab9292b --- /dev/null +++ b/toontown/golf/GolfHoleBase.py @@ -0,0 +1,564 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +import DistributedPhysicsWorldAI +from direct.fsm.FSM import FSM +from toontown.ai.ToonBarrier import * +from toontown.golf import GolfGlobals +import random +import math + +class GolfHoleBase: + + def __init__(self, canRender = 0): + self.canRender = canRender + self.recording = [] + self.aVRecording = [] + self.holePositions = [] + self.grayCount = 0 + self.skyContact = None + self.lastSkyContactPoint = None + self.doingRecording = 0 + self.backAmount = 270 + self.ballRocket = 0 + self.inCount = 0 + self.frame = 0 + self.onSlick = 0 + self.didHoleBreak = 0 + return + + def loadLevel(self): + tm = self.holeInfo['terrainModel'] + self.terrainModel = loader.loadModel(tm) + td = self.holeInfo['physicsData'] + if self.canRender: + self.terrainModel.reparentTo(render) + if self.canRender: + self.terrainModel.find('**/softSurface').setBin('ground', 0) + terrainData = self.terrainModel.find('**/softSurface') + grassData = terrainData.findAllMatches('**/grass*') + self.terrainData = [] + for index in range(grassData.getNumPaths()): + someTerrainData = grassData[index] + terrainDataOde = OdeTriMeshData(someTerrainData) + self.meshDataList.append(terrainDataOde) + terrainGeomOde = OdeTriMeshGeom(self.space, terrainDataOde) + self.geomDataList.append(terrainGeomOde) + terrainGeomOde.setCollideBits(BitMask32(4026531840L)) + terrainGeomOde.setCategoryBits(BitMask32(240)) + self.space.setSurfaceType(terrainGeomOde, GolfGlobals.GRASS_SURFACE) + self.space.setCollideId(terrainGeomOde, 2) + + slickData = terrainData.findAllMatches('**/slick*') + self.terrainData = [] + for index in range(slickData.getNumPaths()): + someTerrainData = slickData[index] + terrainDataOde = OdeTriMeshData(someTerrainData) + self.meshDataList.append(terrainDataOde) + terrainGeomOde = OdeTriMeshGeom(self.space, terrainDataOde) + self.geomDataList.append(terrainGeomOde) + terrainGeomOde.setCollideBits(BitMask32(4026531840L)) + terrainGeomOde.setCategoryBits(BitMask32(240)) + self.space.setSurfaceType(terrainGeomOde, GolfGlobals.SLICK_SURFACE) + self.space.setCollideId(terrainGeomOde, GolfGlobals.SLICK_COLLIDE_ID) + + cupData = terrainData.find('**/hole*') + cupData = OdeTriMeshData(cupData) + self.meshDataList.append(cupData) + cupGeom = OdeTriMeshGeom(self.space, cupData) + self.geomDataList.append(cupGeom) + cupGeom.setCollideBits(BitMask32(4026531840L)) + cupGeom.setCategoryBits(BitMask32(240)) + self.space.setSurfaceType(cupGeom, GolfGlobals.HOLE_SURFACE) + self.space.setCollideId(cupGeom, GolfGlobals.HOLE_CUP_COLLIDE_ID) + if self.canRender: + self.golfBarrier = self.terrainModel.find('**/collision1') + if not self.golfBarrier.isEmpty(): + golfBarrierCollection = self.terrainModel.findAllMatches('**/collision?') + for i in xrange(golfBarrierCollection.getNumPaths()): + oneBarrier = golfBarrierCollection.getPath(i) + if oneBarrier != self.golfBarrier: + oneBarrier.wrtReparentTo(self.golfBarrier) + + self.golfBarrier.hide() + else: + self.notify.warning('Could not find collision1 node ---------') + self.hardSurfaceNodePath = self.terrainModel.find('**/hardSurface') + if self.canRender: + self.terrainModel.find('**/hardSurface').setBin('ground', 0) + self.loadBlockers() + hardData = OdeTriMeshData(self.hardSurfaceNodePath) + self.meshDataList.append(hardData) + hardGeom = OdeTriMeshGeom(self.space, hardData) + self.geomDataList.append(hardGeom) + hardGeom.setCollideBits(BitMask32(4026531840L)) + hardGeom.setCategoryBits(BitMask32(240)) + self.space.setCollideId(hardGeom, 3) + hardSurface = self.space.getSurfaceType(hardGeom) + self.notify.debug('hardSurface = %s' % hardSurface) + if self.notify.getDebug(): + self.notify.debug('self.hardGeom') + hardGeom.write() + self.notify.debug(' -') + self.holeBottomNodePath = self.terrainModel.find('**/holebottom0') + if self.holeBottomNodePath.isEmpty(): + self.holeBottomPos = Vec3(*self.holeInfo['holePos'][0]) + else: + self.holeBottomPos = self.holeBottomNodePath.getPos() + self.holePositions.append(self.holeBottomPos) + + def isBallInHole(self, ball): + retval = False + for holePos in self.holePositions: + displacement = ball.getPosition() - holePos + length = displacement.length() + self.notify.debug('hole %s length=%s' % (holePos, length)) + if length <= GolfGlobals.DistanceToBeInHole * 0.5: + retval = True + break + + return retval + + def createRays(self): + self.notify.debug('createRays') + body = OdeBody(self.world) + self.ballRay = OdeRayGeom(self.space, 50.0) + self.ballRay.setBody(body) + self.ballRay.setOffsetRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.ballRay.setOffsetPosition(0, 0, 0.0) + self.ballRay.setCollideBits(BitMask32(16773375)) + self.ballRay.setCategoryBits(BitMask32(4278190080L)) + self.ballRayBody = body + self.space.setCollideId(self.ballRay, GolfGlobals.OOB_RAY_COLLIDE_ID) + self.rayList.append(self.ballRay) + self.rayList.append(self.ballRayBody) + self.skyRay = OdeRayGeom(self.space, 100.0) + self.skyRay.setCollideBits(BitMask32(240)) + self.skyRay.setCategoryBits(BitMask32(0)) + self.skyRay.setRotation(Mat3(1, 0, 0, 0, -1, 0, 0, 0, -1)) + self.space.setCollideId(self.skyRay, GolfGlobals.SKY_RAY_COLLIDE_ID) + self.rayList.append(self.skyRay) + + def delete(self): + self.ballRay = None + self.skyRay = None + self.recording = None + self.avRecording = None + self.llv = None + return + + def initRecord(self): + del self.recording + self.recording = [] + del self.aVRecording + self.aVRecording = [] + self.skipFrame = 0.0 + self.frame = 0 + self.tXYMax = 1.0 + self.tZMax = 1.0 + self.tXYMin = 0.1 + self.tZMin = 0.1 + self.skyContact = 1 + self.doingRecording = 1 + self.ballRocket = 0 + self.inCount = 0 + self.ballInHoleFrame = 0 + self.ballTouchedHoleFrame = 0 + self.ballFirstTouchedHoleFrame = 0 + self.ballLastTouchedGrass = 0 + self.hasReset = 0 + self.resetAt = 100000 + self.greenIn = 0 + for key in self.commonObjectDict: + self.commonObjectDict[key][2].enable() + + def checkCommonObjectsNeedPass(self): + for index in self.commonObjectDict: + if self.commonObjectDict[index][1] in [4]: + return 1 + + return 0 + + def checkInRadius(self, ball): + smallestDist = None + for index in self.commonObjectDict: + if self.commonObjectDict[index][1] in [4]: + radius = self.commonObjectDict[index][8] + mover = self.commonObjectDict[index][2] + diffX = ball.getPosition()[0] - mover.getPosition()[0] + diffY = ball.getPosition()[1] - mover.getPosition()[1] + diffZ = ball.getPosition()[2] - mover.getPosition()[2] + dist = math.sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ) + if dist < radius: + if not smallestDist or smallestDist[1] > dist: + smallestDist = [radius, dist] + self.notify.debug('Ball Pos %s\nMover Pos %s' % (ball.getPosition(), mover.getPosition())) + + return smallestDist + + def trackRecordBodyFlight(self, ball, cycleTime, power, startPos, dirX, dirY): + self.notify.debug('trackRecordBodyFlight') + self.ballInHoleFrame = 0 + self.ballTouchedHoleFrame = 0 + self.ballFirstTouchedHoleFrame = 0 + self.ballLastTouchedGrass = 0 + startTime = globalClock.getRealTime() + self.notify.debug('start position %s' % startPos) + self.swingTime = cycleTime + frameCount = 0 + lift = 0 + startTime = GolfGlobals.BALL_CONTACT_FRAME / 24 + startFrame = startTime * self.FPS + for frame in range(startFrame): + self.simulate() + self.setTimeIntoCycle(self.swingTime + float(frameCount) * self.DTAStep) + frameCount += 1 + + forceMove = 1500 + if power > 50: + lift = 0 + self.didHoleBreak = 0 + ball.setPosition(startPos) + ball.setLinearVel(0.0, 0.0, 0.0) + ball.setAngularVel(0.0, 0.0, 0.0) + ball.enable() + self.preStep() + self.simulate() + self.postStep() + ball.enable() + ball.addForce(Vec3(dirX * forceMove * power / 100.0, dirY * forceMove * power / 100.0, lift)) + self.initRecord() + self.llv = None + self.lastSkyContactPoint = None + ran = 0 + self.record(ball) + self.comObjNeedPass = self.checkCommonObjectsNeedPass() + self.notify.debug('self.comObjNeedPass %s' % self.comObjNeedPass) + firstDisabled = -1 + reEnabled = 0 + lastFrameEnabled = 0 + checkFrames = self.FPS * (self.timingCycleLength + 1.0) + hasPrinted = 0 + while ball.isEnabled() and len(self.recording) < 2100 or self.comObjNeedPass or len(self.recording) < 10: + ran = 1 + if len(self.recording) > 2100 and not hasPrinted: + self.notify.debug('recording too long %s' % len(self.recording)) + hasPrinted = 1 + ball.disable() + self.preStep() + self.simulate() + self.setTimeIntoCycle(self.swingTime + float(frameCount) * self.DTAStep) + frameCount += 1 + self.postStep() + self.record(ball) + if self.comObjNeedPass: + if firstDisabled == -1 and not ball.isEnabled(): + firstDisabled = self.frame + self.notify.debug('firstDisabled %s' % firstDisabled) + check = self.checkInRadius(ball) + if check == None: + self.comObjNeedPass = 0 + self.notify.debug('out radius') + else: + self.notify.debug('in radius %s dist %s' % (check[0], check[1])) + elif ball.isEnabled() and firstDisabled != -1 and not reEnabled: + reEnabled = self.frame + self.notify.debug('reEnabled %s' % reEnabled) + if reEnabled: + if self.frame > reEnabled + checkFrames: + self.comObjNeedPass = 0 + self.notify.debug('renable limit passed') + elif self.frame > 2100 + checkFrames: + self.comObjNeedPass = 0 + print 'recording limit passed comObj' + if ball.isEnabled(): + lastFrameEnabled = self.frame + + self.notify.debug('lastFrameEnabled %s' % lastFrameEnabled) + if lastFrameEnabled < 3: + lastFrameEnabled = 3 + self.record(ball) + self.notify.debug('Frames %s' % self.frame) + midTime = globalClock.getRealTime() + self.recording = self.recording[:lastFrameEnabled] + self.aVRecording = self.aVRecording[:lastFrameEnabled] + self.frame = lastFrameEnabled + self.processRecording() + self.processAVRecording() + self.notify.debug('Recording End time %s cycle %s len %s avLen %s' % (self.timingSimTime, + self.getCycleTime(), + len(self.recording), + len(self.aVRecording))) + length = len(self.recording) - 1 + x = self.recording[length][1] + y = self.recording[length][2] + z = self.recording[length][3] + endTime = globalClock.getRealTime() + diffTime = endTime - startTime + self.doingRecording = 0 + fpsTime = self.frame / diffTime + self.notify.debug('Time Start %s Mid %s End %s Diff %s Fps %s frames %s' % (startTime, + midTime, + endTime, + diffTime, + fpsTime, + self.frame)) + return Vec3(x, y, z) + + def record(self, ball): + self.recording.append((self.frame, + ball.getPosition()[0], + ball.getPosition()[1], + ball.getPosition()[2])) + self.aVRecording.append((self.frame, + ball.getAngularVel()[0], + ball.getAngularVel()[1], + ball.getAngularVel()[2])) + if self.frame > 50 and not self.frame % 13: + curFrame = self.recording[self.frame] + pastFrame5 = self.recording[self.frame - 11] + pastFrame10 = self.recording[self.frame - 34] + currPosA = Vec3(curFrame[1], curFrame[2], curFrame[3]) + past5PosA = Vec3(pastFrame5[1], pastFrame5[2], pastFrame5[3]) + past10PosA = Vec3(pastFrame10[1], pastFrame10[2], pastFrame10[3]) + displacement1 = currPosA - past5PosA + displacement2 = currPosA - past10PosA + if displacement1.lengthSquared() < 0.002 and displacement2.lengthSquared() < 0.002 and not self.grayCount and not self.onSlick: + ball.disable() + self.frame += 1 + + def preStep(self): + if hasattr(self, 'ballRay'): + bp = self.curGolfBall().getPosition() + self.ballRayBody.setPosition(bp[0], bp[1], bp[2]) + self.skyRay.setPosition(bp[0], bp[1], 50.0) + + def getOrderedContacts(self, count): + c0 = self.space.getContactId(count, 0) + c1 = self.space.getContactId(count, 1) + if c0 > c1: + chold = c1 + c1 = c0 + c0 = chold + return (c0, c1) + + def postStep(self): + if self.canRender: + self.translucentLastFrame = self.translucentCurFrame[:] + self.translucentCurFrame = [] + self.onSlick = 0 + rayCount = 0 + skyRayHitPos = None + ballRayHitPos = None + bp = self.curGolfBall().getPosition() + for count in range(self.colCount): + c0, c1 = self.getOrderedContacts(count) + x = self.space.getContactData(count * 3 + 0) + y = self.space.getContactData(count * 3 + 1) + z = self.space.getContactData(count * 3 + 2) + if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID or c1 == GolfGlobals.OOB_RAY_COLLIDE_ID: + rayCount += 1 + if self.canRender: + if self.currentGolfer: + self.ballShadowDict[self.currentGolfer].setPos(x, y, z + 0.1) + if c1 == GolfGlobals.GRASS_COLLIDE_ID or c1 == GolfGlobals.HARD_COLLIDE_ID: + if self.curGolfBall().getPosition()[2] < z + 0.2: + ballRayHitPos = Vec3(x, y, z) + if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID and c1 == GolfGlobals.SLICK_COLLIDE_ID: + self.onSlick = 1 + elif c0 == GolfGlobals.OOB_RAY_COLLIDE_ID and c1 == GolfGlobals.HARD_COLLIDE_ID: + self.onSlick = 1 + if c0 == GolfGlobals.GRASS_COLLIDE_ID and c1 == GolfGlobals.SKY_RAY_COLLIDE_ID: + self.lastSkyContactPoint = (x, y, z) + if self.curGolfBall().getPosition()[2] < z + 0.2 and rayCount == 0: + if self.skyContact in [1, 2]: + skyRayHitPos = Vec3(x, y, z) + self.skyContact += 1 + if self.doingRecording: + if c0 == GolfGlobals.OOB_RAY_COLLIDE_ID or c1 == GolfGlobals.OOB_RAY_COLLIDE_ID: + rayCount += 1 + if c1 == GolfGlobals.GRASS_COLLIDE_ID: + self.greenIn = self.frame + self.llv = self.curGolfBall().getLinearVel() + elif GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.HOLE_CUP_COLLIDE_ID in [c0, c1]: + zCon = self.space.getContactData(count * 3 + 2) + self.ballTouchedHoleFrame = self.frame + ballUndersideZ = self.curGolfBall().getPosition()[2] - 0.05 + if zCon < ballUndersideZ: + if not self.ballInHoleFrame: + self.ballInHoleFrame = self.frame + if self.ballFirstTouchedHoleFrame < self.ballLastTouchedGrass: + self.ballFirstTouchedHoleFrame = self.frame + if self.isBallInHole(self.curGolfBall()) and self.didHoleBreak == 0: + self.comObjNeedPass = 0 + ballLV = self.curGolfBall().getLinearVel() + ballAV = self.curGolfBall().getAngularVel() + self.curGolfBall().setLinearVel(0.5 * ballLV[0], 0.5 * ballLV[1], 0.5 * ballLV[2]) + self.curGolfBall().setAngularVel(0.5 * ballAV[0], 0.5 * ballAV[1], 0.5 * ballAV[2]) + self.notify.debug('BALL IN THE HOLE!!! FOO!') + self.didHoleBreak = 1 + return + elif GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.GRASS_COLLIDE_ID in [c0, c1]: + if self.ballInHoleFrame: + self.ballInHoleFrame = 0 + self.notify.debug('setting ballInHoleFrame=0') + self.ballLastTouchedGrass = self.frame + elif self.canRender: + if c0 == GolfGlobals.TOON_RAY_COLLIDE_ID or c1 == GolfGlobals.TOON_RAY_COLLIDE_ID: + x = self.space.getContactData(count * 3 + 0) + y = self.space.getContactData(count * 3 + 1) + z = self.space.getContactData(count * 3 + 2) + self.toonRayCollisionCallback(x, y, z) + if GolfGlobals.CAMERA_RAY_COLLIDE_ID in [c0, c1] and GolfGlobals.WINDMILL_BASE_COLLIDE_ID in [c0, c1]: + self.translucentCurFrame.append(self.windmillFanNodePath) + self.translucentCurFrame.append(self.windmillBaseNodePath) + if GolfGlobals.BALL_COLLIDE_ID in [c0, c1] and GolfGlobals.GRASS_COLLIDE_ID not in [c0, c1]: + self.handleBallHitNonGrass(c0, c1) + + if not self.curGolfBall().isEnabled(): + return + if rayCount == 0: + self.notify.debug('out of bounds detected!') + self.grayCount += 1 + self.outCommon = self.getCommonObjectData() + self.inCount = 0 + if skyRayHitPos: + self.curGolfBall().setPosition(skyRayHitPos[0], skyRayHitPos[1], skyRayHitPos[2] + 0.27) + self.notify.debug('SKY RAY ADJUST?') + else: + if self.grayCount > 1: + self.notify.debug('Back in bounds') + self.grayCount = 0 + self.inCount += 1 + if ballRayHitPos: + self.curGolfBall().setPosition(ballRayHitPos[0], ballRayHitPos[1], ballRayHitPos[2] + 0.245) + ballRayHitPos = None + if self.doingRecording: + self.notify.debug('BALL RAY ADJUST!') + self.notify.debug('%s' % self.curGolfBall().getLinearVel()) + if self.ballRocket > 0 and self.inCount > 1: + self.ballRocket -= 1 + rocketVel = self.curGolfBall().getLinearVel() + self.curGolfBall().setLinearVel(2.0 * rocketVel[0], 2.0 * rocketVel[1], 2.0 * rocketVel[2]) + self.notify.debug('ROCKET!!!!') + if self.grayCount > self.backAmount and self.doingRecording: + if self.greenIn > 2: + self.greenIn -= 2 + if self.greenIn > self.resetAt: + self.greenIn = self.resetAt - 10 + if self.greenIn < 0 or self.hasReset > 3: + self.greenIn = 0 + self.hasReset += 1 + self.notify.debug('BALL RESET frame %s greenIn %s resetAt %s' % (self.frame, self.greenIn, self.resetAt)) + self.useCommonObjectData(self.outCommon) + self.curGolfBall().setPosition(self.recording[self.greenIn][1], self.recording[self.greenIn][2], self.recording[self.greenIn][3] + 0.27) + self.curGolfBall().setAngularVel(0, 0, 0) + if self.hasReset < 3 and self.llv: + self.ballRocket += 1 + self.notify.debug(' BRAKE!!!!') + self.curGolfBall().setLinearVel(0.5 * self.llv[0], 0.5 * self.llv[1], 0.5 * self.llv[2]) + else: + self.notify.debug('back disable %s' % self.frame) + if self.lastSkyContactPoint: + self.curGolfBall().setPosition(self.lastSkyContactPoint[0], self.lastSkyContactPoint[1], self.lastSkyContactPoint[2] + 0.27) + self.curGolfBall().setLinearVel(0, 0, 0) + self.curGolfBall().disable() + self.recording = self.recording[:self.greenIn] + self.aVRecording = self.aVRecording[:self.greenIn] + self.frame = self.greenIn + self.resetAt = self.greenIn + self.grayCount = 0 + if self.ballFirstTouchedHoleFrame > self.frame: + self.notify.debug('reseting first touched hole, self.frame=%d self.ballFirstTouchedHoleFrame=%d' % (self.frame, self.ballFirstTouchedHoleFrame)) + self.ballFirstTouchedHoleFrame = 0 + if self.ballLastTouchedGrass > self.frame: + self.ballLastTouchedGrass = 0 + return + + def processRecording(self, errorMult = 1.0): + self.notify.debug('processRecording') + lastFrame = self.recording[len(self.recording) - 1][0] + countRemovals = 0 + for frame in self.recording: + if frame[0] == 0 or frame[0] == lastFrame: + pass + else: + index = self.recording.index(frame) + prevFrame = self.recording[index - 1] + nextFrame = self.recording[index + 1] + if self.predict(frame, prevFrame, nextFrame, errorMult): + self.recording.remove(frame) + countRemovals += 1 + + if countRemovals > 5: + self.processRecording() + elif len(self.recording) > 120: + self.processRecording(errorMult * 1.25) + else: + for frame in self.recording: + pass + + def processAVRecording(self, errorMult = 1.0, trials = 0): + self.notify.debug('processAVRecording') + lastFrame = self.recording[len(self.recording) - 1][0] + countRemovals = 0 + countTrials = trials + for frame in self.aVRecording: + if frame[0] == 0 or frame[0] == lastFrame: + pass + else: + index = self.aVRecording.index(frame) + prevFrame = self.aVRecording[index - 1] + nextFrame = self.aVRecording[index + 1] + if self.predictAV(frame, prevFrame, nextFrame, errorMult): + self.aVRecording.remove(frame) + countRemovals += 1 + else: + countTrials += 1 + + if countRemovals > 5: + self.processAVRecording(errorMult, countTrials) + elif len(self.aVRecording) > 80: + self.processAVRecording(errorMult * 1.25, countTrials) + else: + for frame in self.aVRecording: + pass + + def predict(self, frame, sourceFrame, destFrame, errorMult = 1.0): + tXY = 0.05 * errorMult + tZ = 0.05 * errorMult + projLength = destFrame[0] - sourceFrame[0] + projPen = destFrame[0] - frame[0] + propSource = float(projPen) / float(projLength) + propDest = 1.0 - propSource + projX = sourceFrame[1] * propSource + destFrame[1] * propDest + projY = sourceFrame[2] * propSource + destFrame[2] * propDest + projZ = sourceFrame[3] * propSource + destFrame[3] * propDest + varX = abs(projX - frame[1]) + varY = abs(projY - frame[2]) + varZ = abs(projZ - frame[3]) + if varX > tXY or varY > tXY or varZ > tZ: + return 0 + else: + return 1 + + def predictAV(self, frame, sourceFrame, destFrame, errorMult = 1.0): + tXYZ = 1.5 * errorMult + projLength = destFrame[0] - sourceFrame[0] + projPen = destFrame[0] - frame[0] + propSource = float(projPen) / float(projLength) + propDest = 1.0 - propSource + projX = sourceFrame[1] * propSource + destFrame[1] * propDest + projY = sourceFrame[2] * propSource + destFrame[2] * propDest + projZ = sourceFrame[3] * propSource + destFrame[3] * propDest + varX = abs(projX - frame[1]) + varY = abs(projY - frame[2]) + varZ = abs(projZ - frame[3]) + if varX > tXYZ or varY > tXYZ or varZ > tXYZ: + return 0 + else: + return 1 + + def handleBallHitNonGrass(self, c0, c1): + pass diff --git a/toontown/golf/GolfManagerAI.py b/toontown/golf/GolfManagerAI.py new file mode 100644 index 0000000..b58fec3 --- /dev/null +++ b/toontown/golf/GolfManagerAI.py @@ -0,0 +1,58 @@ +from direct.distributed import DistributedObjectAI +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.golf import DistributedGolfCourseAI +from pandac.PandaModules import * +RequestHole = {} + +def GolfManagerAI(): + if not hasattr(simbase, 'golf'): + simbase.golf = __GolfManagerAI() + return simbase.golf + + +class __GolfManagerAI(DirectObject.DirectObject): + notify = directNotify.newCategory('GolfManagerAI') + + def __init__(self): + DirectObject.DirectObject.__init__(self) + self.courseList = [] + + def delete(self): + DirectObject.DirectObject.delete(self) + + def readyGolfCourse(self, avIds, courseId=0): + self.notify.debug('readyGolfCourse avIds=%s courseId=%d' % (avIds, courseId)) + golfZone = simbase.air.allocateZone() + preferredHoleId = None + for avId in avIds: + if avId in RequestHole: + preferredHoleId = RequestHole[avId][0] + + newCourse = DistributedGolfCourseAI.DistributedGolfCourseAI(golfZone, avIds, courseId, preferredHoleId) + newCourse.generateWithRequired(golfZone) + self.courseList.append(newCourse) + newCourse.addExpectedGolfers(avIds) + golfZone = newCourse.getZoneId() + self.notify.debug('%s' % self) + self.notify.debug('returning %d' % golfZone) + return golfZone + + def findGolfCourse(self, avId): + retval = None + for course in self.courseList: + if avId in course.avIdList: + retval = course + break + + return retval + + def removeCourse(self, course): + if course in self.courseList: + for avId in course.avIdList: + if avId in RequestHole: + if not RequestHole[avId][1]: + del RequestHole[avId] + + self.courseList.remove(course) diff --git a/toontown/golf/GolfRewardDialog.py b/toontown/golf/GolfRewardDialog.py new file mode 100644 index 0000000..0b3cdea --- /dev/null +++ b/toontown/golf/GolfRewardDialog.py @@ -0,0 +1,270 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from math import * +from direct.distributed.ClockDelta import * +from toontown.golf import GolfGlobals +from toontown.shtiker.GolfPage import GolfTrophy + +class GolfRewardDialog: + notify = directNotify.newCategory('GolfRewardDialog') + + def __init__(self, avIdList, trophyList, rankingsList, holeBestList, courseBestList, cupList, localAvId, tieBreakWinner, aimTimesList, endMovieCallback = None): + self.avIdList = avIdList + self.trophyList = trophyList + self.rankingsList = rankingsList + self.holeBestList = holeBestList + self.courseBestList = courseBestList + self.cupList = cupList + self.tieBreakWinner = tieBreakWinner + self.movie = None + self.myPlace = 0 + self.victory = None + self.endMovieCallback = endMovieCallback + self.aimTimesList = aimTimesList + self.setup(localAvId) + return + + def calcTrophyTextListForOnePlayer(self, avId): + retval = [] + av = base.cr.doId2do.get(avId) + if av and avId in self.avIdList: + playerIndex = self.avIdList.index(avId) + name = av.getName() + for trophyIndex in xrange(len(self.trophyList[playerIndex])): + wonTrophy = self.trophyList[playerIndex][trophyIndex] + if wonTrophy: + trophyName = TTLocalizer.GolfTrophyDescriptions[trophyIndex] + text = TTLocalizer.GolfAvReceivesTrophy % {'name': name, + 'award': trophyName} + retval.append(text) + + return retval + + def calcCupTextListForAllPlayers(self, localAvId): + retval = [] + for cupPlayerIndex in range(len(self.avIdList)): + if self.avIdList[cupPlayerIndex] != localAvId: + av = base.cr.doId2do.get(self.avIdList[cupPlayerIndex]) + name = '' + if av: + name = av.getName() + cupIndex = 0 + for cupIndex in range(len(self.cupList[cupPlayerIndex])): + if self.cupList[cupPlayerIndex][cupIndex]: + cupName = TTLocalizer.GolfCupDescriptions[cupIndex] + text = TTLocalizer.GolfAvReceivesCup % {'name': name, + 'cup': cupName} + retval.append(text) + + for cupPlayerIndex in range(len(self.avIdList)): + if self.avIdList[cupPlayerIndex] == localAvId: + av = base.cr.doId2do.get(self.avIdList[cupPlayerIndex]) + name = av.getName() + cupIndex = 0 + for cupIndex in range(len(self.cupList[cupPlayerIndex])): + if self.cupList[cupPlayerIndex][cupIndex]: + cupName = TTLocalizer.GolfCupDescriptions[cupIndex] + text = TTLocalizer.GolfAvReceivesCup % {'name': name, + 'cup': cupName} + retval.append(text) + + return retval + + def calcRankings(self, localAvId): + retval = [] + self.notify.debug('aimTimesList=%s' % self.aimTimesList) + for rank in range(len(self.rankingsList) + 1): + for avIndex in range(len(self.avIdList)): + if self.rankingsList[avIndex] == rank: + name = ' ' + av = base.cr.doId2do.get(self.avIdList[avIndex]) + if av: + name = av.getName() + text = '%d. ' % rank + ' ' + name + if GolfGlobals.TIME_TIE_BREAKER: + time = self.aimTimesList[avIndex] + minutes = int(time / 60) + time -= minutes * 60 + seconds = int(time) + padding = (seconds < 10 and ['0'] or [''])[0] + time -= seconds + fraction = str(time)[2:4] + fraction = fraction + '0' * (2 - len(fraction)) + timeStr = "%d'%s%d''%s" % (minutes, + padding, + seconds, + fraction) + text += ' - ' + timeStr + retval.append(text) + if self.avIdList[avIndex] == localAvId: + self.myPlace = rank + + return retval + + def calcHoleBestTextListForAllPlayers(self, localAvId): + retval = [] + if GolfGlobals.CalcOtherHoleBest: + for hbPlayerIndex in range(len(self.avIdList)): + if self.avIdList[hbPlayerIndex] != localAvId: + av = base.cr.doId2do.get(self.avIdList[hbPlayerIndex]) + name = av.getName() + for hbIndex in range(len(self.holeBestList[hbPlayerIndex])): + if self.holeBestList[hbPlayerIndex][hbIndex]: + hbName = TTLocalizer.GolfHoleNames[hbIndex] + text = TTLocalizer.GolfAvReceivesHoleBest % {'name': name, + 'hole': hbName} + retval.append(text) + + for hbPlayerIndex in range(len(self.avIdList)): + if self.avIdList[hbPlayerIndex] == localAvId: + av = base.cr.doId2do.get(self.avIdList[hbPlayerIndex]) + name = av.getName() + for hbIndex in range(len(self.holeBestList[hbPlayerIndex])): + if self.holeBestList[hbPlayerIndex][hbIndex]: + hbName = TTLocalizer.GolfHoleNames[hbIndex] + text = TTLocalizer.GolfAvReceivesHoleBest % {'name': name, + 'hole': hbName} + retval.append(text) + + return retval + + def calcCourseBestTextListForAllPlayers(self, localAvId): + retval = [] + if GolfGlobals.CalcOtherCourseBest: + for cbPlayerIndex in range(len(self.avIdList)): + if self.avIdList[cbPlayerIndex] != localAvId: + av = base.cr.doId2do.get(self.avIdList[cbPlayerIndex]) + name = av.getName() + for cbIndex in range(len(self.holeBestList[cbPlayerIndex])): + if self.holeBestList[cbPlayerIndex][cbIndex]: + cbName = TTLocalizer.GolfCourseNames[cbIndex] + text = TTLocalizer.GolfAvReceivesCourseBest % {'name': name, + 'course': cbName} + retval.append(text) + + for cbPlayerIndex in range(len(self.avIdList)): + if self.avIdList[cbPlayerIndex] == localAvId: + av = base.cr.doId2do.get(self.avIdList[cbPlayerIndex]) + name = av.getName() + for cbIndex in range(len(self.courseBestList[cbPlayerIndex])): + if self.courseBestList[cbPlayerIndex][cbIndex]: + cbName = TTLocalizer.GolfCourseNames[cbIndex] + text = TTLocalizer.GolfAvReceivesCourseBest % {'name': name, + 'course': cbName} + retval.append(text) + + return retval + + def createRewardMovie(self, localAvId): + retval = Sequence(name='Reward sequence', autoPause=1) + self.trophy = None + + def setTrophyLabelText(text, playerIndex, trophyIndex): + self.rankLabel.hide() + self.rewardLabel.hide() + self.trophy = GolfTrophy(level=self.trophyList[playerIndex][trophyIndex], parent=self.trophyLabel, pos=(1.3, 0, -0.25)) + self.trophy.setScale(0.65, 1, 0.65) + self.trophy.show() + self.trophyLabel['text'] = text + + def setRewardLabelText(text): + self.rewardLabel.show() + self.rankLabel.hide() + self.trophyLabel.hide() + if self.trophy: + self.trophy.hide() + self.rewardLabel['text'] = text + + def setRankLabelText(text): + self.rankLabel.show() + self.rewardLabel.hide() + self.trophyLabel.hide() + if self.trophy: + self.trophy.hide() + self.rankLabel['text'] = text + if len(self.avIdList) > 1: + self.victory = base.loadSfx('phase_6/audio/sfx/KART_Applause_%d.mp3' % self.myPlace) + self.victory.play() + + for avId in self.avIdList: + if avId != localAvId: + rewardTextList = self.calcTrophyTextListForOnePlayer(avId) + trophyIndex = 0 + for rewardText in rewardTextList: + playerIndex = self.avIdList.index(avId) + var = (rewardText, playerIndex, trophyIndex) + oneTrophyIval = Parallel(Func(setTrophyLabelText, rewardText, playerIndex, trophyIndex), LerpColorScaleInterval(self.trophyLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + trophyIndex = trophyIndex + 1 + retval.append(oneTrophyIval) + + rewardTextList = self.calcTrophyTextListForOnePlayer(localAvId) + trophyIndex = 0 + playerIndex = self.avIdList.index(localAvId) + for rewardText in rewardTextList: + if len(rewardTextList) > 0: + var = (rewardText, playerIndex, trophyIndex) + oneRewardIval = Parallel(Func(setTrophyLabelText, rewardText, playerIndex, trophyIndex), LerpColorScaleInterval(self.trophyLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneRewardIval) + + rewardCupList = self.calcCupTextListForAllPlayers(localAvId) + if len(rewardCupList) > 0: + for rewardText in rewardCupList: + oneCupIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='noBlend')) + retval.append(oneCupIval) + + if self.tieBreakWinner: + name = '' + av = base.cr.doId2do.get(self.tieBreakWinner) + if av: + name = av.getName() + if GolfGlobals.TIME_TIE_BREAKER: + rewardText = TTLocalizer.GolfTimeTieBreakWinner % {'name': name} + else: + rewardText = TTLocalizer.GolfTieBreakWinner % {'name': name} + randomWinnerIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 7, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='noBlend')) + retval.append(randomWinnerIval) + rankings = self.calcRankings(localAvId) + rankText = TTLocalizer.GolfRanking + '\n' + for rank in range(len(rankings)): + rankText = rankText + rankings[rank] + '\n' + + oneRankIval = Parallel(Func(setRankLabelText, rankText), LerpColorScaleInterval(self.rankLabel, 8, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneRankIval) + rewardHoleList = self.calcHoleBestTextListForAllPlayers(localAvId) + if len(rewardHoleList) > 0: + for rewardText in rewardHoleList: + oneHoleIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 8, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneHoleIval) + + rewardCourseList = self.calcCourseBestTextListForAllPlayers(localAvId) + if len(rewardCourseList) > 0: + for rewardText in rewardCourseList: + oneCourseIval = Parallel(Func(setRewardLabelText, rewardText), LerpColorScaleInterval(self.rewardLabel, 4, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1), blendType='easeIn')) + retval.append(oneCourseIval) + + if self.endMovieCallback: + retval.append(Func(self.endMovieCallback)) + return retval + + def setup(self, localAvId): + self.rewardBoard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.75, 1, 0.6), pos=(0, 0, -0.6)) + self.rewardLabel = DirectLabel(parent=self.rewardBoard, relief=None, pos=(-0, 0, 0), text_align=TextNode.ACenter, text='', text_scale=0.05, text_wordwrap=30) + self.rankLabel = DirectLabel(parent=self.rewardBoard, relief=None, pos=(-0, 0, 0.17), text_align=TextNode.ACenter, text='', text_scale=0.06) + self.trophyLabel = DirectLabel(parent=self.rewardBoard, relief=None, pos=(-0.7, 0, 0.05), text_align=TextNode.ALeft, text='', text_scale=0.06, text_wordwrap=20) + self.movie = self.createRewardMovie(localAvId) + return + + def delete(self): + self.movie.pause() + self.notify.debug('Movie is paused') + self.rewardBoard.destroy() + self.notify.debug('Reward board is destroyed') + self.movie = None + self.notify.debug('Movie is deleted') + return + + def getMovie(self): + return self.movie diff --git a/toontown/golf/GolfScoreBoard.py b/toontown/golf/GolfScoreBoard.py new file mode 100644 index 0000000..bc05b8a --- /dev/null +++ b/toontown/golf/GolfScoreBoard.py @@ -0,0 +1,229 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.task import Task +from math import * +from direct.distributed.ClockDelta import * +from toontown.golf import GolfGlobals +from pandac.PandaModules import LineSegs +AUTO_HIDE_TIMEOUT = 3 + +class GolfScoreBoard: + notify = directNotify.newCategory('GolfScoreBoard') + + def __init__(self, golfCourse): + self.golfCourse = golfCourse + self.numPlayas = len(golfCourse.avIdList) + self.avIdList = golfCourse.avIdList + self.playaTags = [] + self.scoreTags = [] + self.totalTags = [] + self.scoreLabels = [] + self.holeLabels = [] + self.parLabels = [] + self.numExited = 0 + self.setup() + + def setup(self): + self.scoreboard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.9, 1, 1.05), pos=(0, 0, 0.375)) + self.lines = LineSegs() + self.lines.setColor(0, 0, 0, 1) + self.lines.setThickness(2) + guiModel = loader.loadModel('phase_6/models/golf/golf_gui') + highlight = loader.loadModel('phase_6/models/golf/headPanel') + self.maximizeB = DirectButton(parent=aspect2d, pos=(1.2, 0, -0.85), relief=None, state=DGG.NORMAL, image=(guiModel.find('**/score_card_icon'), guiModel.find('**/score_card_icon_rollover'), guiModel.find('**/score_card_icon_rollover')), image_scale=(0.2, 1, 0.2), command=self.showBoard) + self.vertOffset = 0.13 + self.playaTop = 0.12 + horzOffset = 0.12 + holeTop = 0.3 + self.vCenter = 0.025 + totScore = 0 + totPar = 0 + self.lineVStart = -0.465 + self.lineHStart = 0.17 + self.lineHorOffset = 0.13 + self.lineVertOffset = 0.125 + self.lineVCenter = 0.025 + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.minimizeB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), command=self.hideBoard, extraArgs=[None]) + self.exitCourseB = DirectButton(parent=self.scoreboard, pos=(0, 0, self.lineHStart - 0.59), relief=None, state=DGG.NORMAL, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), text=TTLocalizer.GolfExitCourse, text_scale=0.04, text_pos=TTLocalizer.GSBexitCourseBPos, command=self.exitCourse) + self.exitCourseB.hide() + self.highlightCur = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.038), image=highlight, image_scale=(1.82, 1, 0.135)) + self.titleBar = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.003, 0, 0.166), color=(0.7, 0.7, 0.7, 0.3), image=highlight, image_scale=(1.82, 1, 0.195)) + self.titleBar.show() + self.highlightCur.show() + buttons.removeNode() + guiModel.removeNode() + title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' - ' + GolfGlobals.getHoleName(self.golfCourse.holeIds[self.golfCourse.curHoleIndex]) + self.titleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(0, 0, holeTop + 0.1), text_align=TextNode.ACenter, text=title, text_scale=TTLocalizer.GSBtitleLabel, text_font=ToontownGlobals.getSignFont(), text_fg=(0, 0.5, 0.125, 1)) + self.playaLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfHole, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) + for holeLIndex in range(self.golfCourse.numHoles): + holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeLIndex, 0, holeTop), text_align=TextNode.ACenter, text='%s' % (holeLIndex + 1), text_scale=0.05) + self.holeLabels.append(holeLabel) + + self.totalLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop), text_align=TextNode.ACenter, text=TTLocalizer.GolfTotal, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) + self.parTitleLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart - 0.23, 0, holeTop - 0.1), text_align=TextNode.ACenter, text=TTLocalizer.GolfPar, text_font=ToontownGlobals.getMinnieFont(), text_scale=0.05) + for parHoleIndex in range(self.golfCourse.numHoles): + parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * parHoleIndex, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'], text_scale=0.05, text_wordwrap=10) + totPar = totPar + GolfGlobals.HoleInfo[self.golfCourse.holeIds[parHoleIndex]]['par'] + self.parLabels.append(parLabel) + + parLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, holeTop - 0.1), text_align=TextNode.ACenter, text='%s' % totPar, text_scale=0.05, text_wordwrap=10) + self.parLabels.append(parLabel) + vert = 0.0 + self.numPlayas = len(self.golfCourse.avIdList) + for playaIndex in range(self.numPlayas): + name = TTLocalizer.GolfUnknownPlayer + av = base.cr.doId2do.get(self.golfCourse.avIdList[playaIndex]) + if av: + name = av.getName() + playaLabel = DirectLabel(parent=self.scoreboard, relief=None, text_align=TextNode.ACenter, text=name, text_scale=0.05, text_wordwrap=9) + self.playaTags.append(playaLabel) + textN = playaLabel.component(playaLabel.components()[0]) + if type(textN) == OnscreenText: + try: + if textN.textNode.getWordwrappedWtext() != name: + vert = self.playaTop - self.vertOffset * playaIndex + else: + vert = self.playaTop - self.vertOffset * playaIndex - self.vCenter + except: + vert = self.playaTop - self.vertOffset * playaIndex + + self.playaTags[playaIndex].setPos(self.lineVStart - 0.23, 0, vert) + self.notify.debug('self.text height = %f' % self.playaTags[playaIndex].getHeight()) + holeIndex = 0 + for holeIndex in range(self.golfCourse.numHoles): + holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.055 + horzOffset * holeIndex, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) + self.scoreTags.append(holeLabel) + + holeLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(self.lineVStart + 0.1 + horzOffset * 9.5, 0, self.playaTop - self.vertOffset * playaIndex - self.vCenter), text_align=TextNode.ACenter, text='-', text_scale=0.05, text_wordwrap=10) + self.totalTags.append(holeLabel) + + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.09) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.09) + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart) + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart - 0.45, 0, self.lineHStart - 4 * 0.13) + self.lines.moveTo(self.lineVStart, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart, 0, self.lineHStart - 4 * 0.13) + for x in range(4): + self.lines.moveTo(self.lineVStart - 0.45, 0, self.lineHStart - (x + 1) * self.lineHorOffset) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset + 0.005, 0, self.lineHStart - (x + 1) * self.lineHorOffset) + + for y in range(10): + self.lines.moveTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart + y * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) + + self.lines.moveTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart + 0.19) + self.lines.drawTo(self.lineVStart + 11 * self.lineVertOffset, 0, self.lineHStart - 4 * 0.13) + self.scoreboard.attachNewNode(self.lines.create()) + self.hide() + return + + def getScoreLabel(self, avIdorIndex, holeNum): + index = None + if avIdorIndex < 100: + index = avIdorIndex + else: + for playaIndex in range(self.numPlayas): + if self.golfCourse.avIdList[playaIndex] == avIdorIndex: + index = playaIndex + + return self.scoreTags[index * self.golfCourse.numHoles + holeNum] + + def update(self): + self.showBoard() + taskMgr.doMethodLater(AUTO_HIDE_TIMEOUT, self.hideBoard, 'hide score board') + + def hideBoard(self, task): + self.hide() + + def hide(self): + self.scoreboard.hide() + self.maximizeB.show() + + def showBoardFinal(self, task = None): + self.exitCourseB.show() + self.minimizeB.hide() + self.showBoard() + + def showBoard(self, task = None): + scoreDict = self.golfCourse.scores + x = 0 + currentGolfer = self.golfCourse.getCurGolfer() + for playaIndex in range(self.numPlayas): + if self.golfCourse.isGameDone(): + self.playaTags[playaIndex].setColor(0, 0, 0, 1) + elif currentGolfer == self.golfCourse.avIdList[playaIndex]: + self.highlightCur.setColor(*GolfGlobals.PlayerColors[playaIndex]) + self.highlightCur.setAlphaScale(0.4) + self.highlightCur.setPos(-0.003, 0, 0.038 - playaIndex * (self.lineVertOffset + 0.005)) + self.highlightCur.show() + else: + self.playaTags[playaIndex].setColor(0, 0, 0, 1) + + for avId in self.avIdList: + holeIndex = 0 + totScore = 0 + playerExited = False + for y in range(len(self.golfCourse.exitedAvIdList)): + if self.golfCourse.exitedAvIdList[y] == avId: + self.playaTags[x].setColor(0.7, 0.7, 0.7, 1) + holeIndex = 0 + for holeIndex in range(self.golfCourse.numHoles): + self.getScoreLabel(self.avIdList[x], holeIndex).setColor(0.7, 0.7, 0.7, 1) + + self.totalTags[x].setColor(0.7, 0.7, 0.7, 1) + playerExited = True + + if playerExited == False: + for holeIndex in range(self.golfCourse.numHoles): + if holeIndex <= self.golfCourse.curHoleIndex: + self.getScoreLabel(avId, holeIndex)['text'] = '%s' % scoreDict[avId][holeIndex] + totScore = totScore + scoreDict[avId][holeIndex] + if self.golfCourse.isGameDone() == False: + if holeIndex == self.golfCourse.curHoleIndex: + self.getScoreLabel(avId, holeIndex).setColor(1, 0, 0, 1) + self.holeLabels[holeIndex].setColor(1, 0, 0, 1) + self.parLabels[holeIndex].setColor(1, 0, 0, 1) + title = GolfGlobals.getCourseName(self.golfCourse.courseId) + ' - ' + GolfGlobals.getHoleName(self.golfCourse.holeIds[self.golfCourse.curHoleIndex]) + self.titleLabel['text'] = title + else: + self.getScoreLabel(avId, holeIndex).setColor(0, 0, 0, 1) + self.holeLabels[holeIndex].setColor(0, 0, 0, 1) + self.parLabels[holeIndex].setColor(0, 0, 0, 1) + + self.totalTags[x]['text'] = '%s' % totScore + if self.golfCourse.isGameDone(): + self.getScoreLabel(avId, self.golfCourse.numHoles - 1).setColor(0, 0, 0, 1) + self.totalTags[x].setColor(1, 0, 0, 1) + x = x + 1 + + y = 0 + if self.golfCourse.isGameDone(): + self.parLabels[self.golfCourse.numHoles - 1].setColor(0, 0, 0, 1) + self.holeLabels[self.golfCourse.numHoles - 1].setColor(0, 0, 0, 1) + self.parLabels[self.golfCourse.numHoles].setColor(1, 0, 0, 1) + self.totalLabel.setColor(1, 0, 0, 1) + self.scoreboard.show() + self.maximizeB.hide() + + def exitCourse(self): + course = self.golfCourse + self.delete() + course.exitEarly() + + def delete(self): + if self.maximizeB: + self.maximizeB.destroy() + self.maximizeB = None + if self.scoreboard: + self.scoreboard.destroy() + self.scoreboard = None + self.golfCourse = None + taskMgr.remove('hide score board') + return diff --git a/toontown/golf/PhysicsWorldBase.py b/toontown/golf/PhysicsWorldBase.py new file mode 100644 index 0000000..ec88aba --- /dev/null +++ b/toontown/golf/PhysicsWorldBase.py @@ -0,0 +1,719 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from math import * +import math +from direct.fsm.FSM import FSM +from toontown.minigame import ArrowKeys +from direct.showbase import PythonUtil +from direct.task import Task +from direct.distributed.ClockDelta import * +import BuildGeometry +from toontown.golf import GolfGlobals +import random, time + +def scalp(vec, scal): + vec0 = vec[0] * scal + vec1 = vec[1] * scal + vec2 = vec[2] * scal + vec = Vec3(vec0, vec1, vec2) + + +def length(vec): + return sqrt(vec[0] ** 2 + vec[1] ** 2 + vec[2] ** 2) + + +class PhysicsWorldBase: + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhysicsWorld') + + def __init__(self, canRender = 0): + self.canRender = canRender + self.world = OdeWorld() + self.space = OdeSimpleSpace() + self.contactgroup = OdeJointGroup() + self.bodyList = [] + self.geomList = [] + self.massList = [] + self.rayList = [] + self.showContacts = 0 + self.jointMarkers = [] + self.jointMarkerCount = 64 + self.meshDataList = [] + self.geomDataList = [] + self.commonObjectInfoDict = {} + self.maxColCount = 0 + if self.canRender: + self.odePandaRelationList = self.bodyList + self.root = render.attachNewNode('physics root node') + else: + self.root = NodePath('physics root node') + self.placerNode = self.root.attachNewNode('Placer') + self.subPlacerNode = self.placerNode.attachNewNode('Placer Sub Node') + self.commonObjectDict = {} + self.commonId = 0 + self.worldAttach = self.root.attachNewNode('physics geom attach point') + self.timingCycleLength = 10.0 + self.timingCycleOffset = 0.0 + self.timingSimTime = 0.0 + self.FPS = 90.0 + self.refFPS = 60.0 + self.DTAStep = 1.0 / self.FPS + self.refCon = 1.2 + + def delete(self): + self.notify.debug('Max Collision Count was %s' % self.maxColCount) + self.stopSim() + self.commonObjectDict = None + if self.canRender: + for pair in self.odePandaRelationList: + pair[0].remove() + pair[1].destroy() + + self.odePandaRelationList = None + else: + for body in self.bodyList: + body[1].destroy() + + self.bodyList = None + for mass in self.massList: + mass = None + + for geom in self.geomList: + geom.destroy() + geom = None + + for ray in self.rayList: + ray.destroy() + ray = None + + self.placerNode.remove() + self.root.remove() + for marker in self.jointMarkers: + marker.remove() + + self.jointMarkers = None + for data in self.geomDataList: + data.destroy() + + for data in self.meshDataList: + data.destroy() + + self.floor.destroy() + self.floor = None + self.contactgroup.empty() + self.world.destroy() + self.space.destroy() + self.world = None + self.space = None + return + + def setupSimulation(self): + self.world.setAutoDisableFlag(0) + self.world.setAutoDisableLinearThreshold(0.15) + self.world.setAutoDisableAngularThreshold(0.15) + self.world.setAutoDisableSteps(2) + self.world.setGravity(0, 0, -25) + self.world.setErp(0.8) + self.world.setCfm(1e-05) + self.world.initSurfaceTable(5) + self.world.setSurfaceEntry(0, 0, 150, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(1, 1, 1500, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.001 / self.refCon) + self.world.setSurfaceEntry(2, 2, 150, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(0, 2, 150, 0.05, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(0, 3, 150, 0.0, 0.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(1, 3, 150, 0.0, 99.1, 0.9, 1e-05, 0.0, 1.0 / self.refCon) + self.world.setSurfaceEntry(2, 3, 150, 0.0, 9.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(3, 3, 150, 0.0, 9.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(4, 4, 150, 0.0, 9.1, 0.9, 1e-05, 0.0, 0.4 / self.refCon) + self.world.setSurfaceEntry(1, 4, 150, 0.0, 99.1, 0.9, 1e-05, 0.0, 0.001 / self.refCon) + self.world.setSurfaceEntry(pos1=0, pos2=1, mu=80, bounce=0.15, bounce_vel=0.1, soft_erp=0.9, soft_cfm=1e-05, slip=0.0, dampen=0.35 / self.refCon) + self.world.setSurfaceEntry(pos1=2, pos2=1, mu=1500, bounce=0.9, bounce_vel=0.01, soft_erp=0.9, soft_cfm=1e-05, slip=0.0, dampen=0.001 / self.refCon) + self.floor = OdePlaneGeom(self.space, Vec4(0.0, 0.0, 1.0, -20.0)) + self.floor.setCollideBits(BitMask32(0)) + self.floor.setCategoryBits(BitMask32(3840)) + self.space.setAutoCollideWorld(self.world) + self.space.setAutoCollideJointGroup(self.contactgroup) + self.world.setQuickStepNumIterations(8) + self.DTA = 0.0 + self.frameCounter = 0 + if self.canRender: + for count in range(self.jointMarkerCount): + testMarker = render.attachNewNode('Joint Marker') + ballmodel = loader.loadModel('phase_3/models/misc/sphere') + ballmodel.reparentTo(testMarker) + ballmodel.setScale(0.1) + testMarker.setPos(0.0, 0.0, -100.0) + self.jointMarkers.append(testMarker) + + def setTimingCycleLength(self, time): + self.timingCycleLength = time + + def getTimingCycleLength(self): + return self.timingCycleLength + + def getCycleTime(self, doprint = 0): + cycleTime = (globalClock.getRealTime() + self.timingCycleOffset) % self.timingCycleLength + if doprint: + print 'Get Cycle Time %s' % cycleTime + return cycleTime + + def setTimeIntoCycle(self, time, doprint = 0): + trueCycleTime = globalClock.getRealTime() % self.timingCycleLength + self.timingCycleOffset = time - trueCycleTime + if doprint: + self.notify.debug('Set Cycle Time %s' % self.timingCycleOffset) + self.notify.debug('SET cycle time %s' % ((globalClock.getRealTime() + self.timingCycleOffset) % self.timingCycleLength)) + + def getSimCycleTime(self): + return + return self.timingSimTime % self.timingCycleLength + + def startSim(self): + taskMgr.add(self.__simulationTask, 'simulation task') + + def stopSim(self): + taskMgr.remove('simulation task') + + def __simulationTask(self, task): + self.DTA += globalClock.getDt() + self.frameCounter += 1 + if self.frameCounter >= 10: + self.frameCounter = 0 + startTime = globalClock.getRealTime() + colCount = 0 + while self.DTA >= self.DTAStep: + self.DTA -= self.DTAStep + self.preStep() + self.simulate() + self.postStep() + + if self.canRender: + self.placeBodies() + if self.frameCounter == 0: + endTime = globalClock.getRealTime() - startTime + return task.cont + + def simulate(self): + self.colCount = self.space.autoCollide() + if self.maxColCount < self.colCount: + self.maxColCount = self.colCount + self.notify.debug('New Max Collision Count %s' % self.maxColCount) + self.world.quickStep(self.DTAStep) + for bodyPair in self.bodyList: + self.world.applyDampening(self.DTAStep, bodyPair[1]) + + self.contactgroup.empty() + self.commonObjectControl() + self.timingSimTime = self.timingSimTime + self.DTAStep + + def placeBodies(self): + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if pandaNodePathGeom: + pandaNodePathGeom.setPos(odeBody.getPosition()) + rotation = odeBody.getRotation() * (180.0 / math.pi) + pandaNodePathGeom.setQuat(Quat(odeBody.getQuaternion()[0], odeBody.getQuaternion()[1], odeBody.getQuaternion()[2], odeBody.getQuaternion()[3])) + + def preStep(self): + pass + + def postStep(self): + if self.showContacts and self.canRender: + for count in range(self.jointMarkerCount): + pandaNodePathGeom = self.jointMarkers[count] + if count < self.colCount: + pandaNodePathGeom.setPos(self.space.getContactData(count * 3 + 0), self.space.getContactData(count * 3 + 1), self.space.getContactData(count * 3 + 2)) + else: + pandaNodePathGeom.setPos(0.0, 0.0, -100.0) + + def commonObjectControl(self): + time = self.getCycleTime() + for key in self.commonObjectDict: + if key not in self.commonObjectInfoDict: + self.commonObjectInfoDict[key] = None + entry = self.commonObjectDict[key] + if entry[1] in [2, 4]: + type = entry[1] + body = entry[2] + motor = entry[3] + timeData = entry[4] + forceData = entry[5] + eventData = entry[6] + model = entry[7] + force = 0.0 + for index in range(len(timeData)): + if index == len(timeData) - 1 and timeData[index] < time or timeData[index] < time and timeData[index + 1] > time: + force = forceData[index] + event = eventData[index] + if event != self.commonObjectInfoDict[key]: + self.commonObjectEvent(key, model, type, force, event) + self.commonObjectInfoDict[key] = event + + motor.setParamVel(force) + + return + + def commonObjectEvent(self, key, model, type, force, event): + self.notify.debug('commonObjectForceEvent %s %s %s %s %s' % (key, + model, + type, + force, + event)) + + def getCommonObjectData(self): + objectStream = [(0, + 0, + self.getCycleTime(), + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0)] + for key in self.commonObjectDict: + objectPair = self.commonObjectDict[key] + object = objectPair[2] + pos3 = object.getPosition() + quat4 = object.getQuaternion() + anV3 = object.getAngularVel() + lnV3 = object.getLinearVel() + data = (objectPair[0], + objectPair[1], + pos3[0], + pos3[1], + pos3[2], + quat4[0], + quat4[1], + quat4[2], + quat4[3], + anV3[0], + anV3[1], + anV3[2], + lnV3[0], + lnV3[1], + lnV3[2]) + objectStream.append(data) + + if len(objectStream) <= 1: + data = (0, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + objectStream.append(data) + return objectStream + + def useCommonObjectData(self, objectData, enable = 1): + if not objectData: + return + if objectData[1][1] == 99: + return + time = objectData[0] + self.setTimeIntoCycle(time[2]) + if time[2] > self.timingCycleLength: + pass + for dataIndex in range(1, len(objectData)): + data = objectData[dataIndex] + commonObject = self.commonObjectDict[data[0]] + commonObject[2].setPosition(data[2], data[3], data[4]) + commonObject[2].setQuaternion(Quat(data[5], data[6], data[7], data[8])) + commonObject[2].setAngularVel(data[9], data[10], data[11]) + commonObject[2].setLinearVel(data[12], data[13], data[14]) + if enable: + commonObject[2].enable() + else: + commonObject[2].disable() + + def createCommonObject(self, type, commonId, pos, hpr, sizeX = 0, sizeY = 0, moveDistance = 0): + if commonId == None: + commonId = self.commonId + self.commonId += 1 + vPos = Point3(float(pos[0]), float(pos[1]), float(pos[2])) + vHpr = Vec3(float(hpr[0]), float(hpr[1]), float(hpr[2])) + rHpr = Vec3(float(hpr[0]), float(hpr[1]), float(hpr[2])) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + if type == 0: + model, box = self.createBox(self.world, self.space, 10.0, 5.0, 5.0, 5.0) + box.setPosition(vPos) + self.placerNode.setHpr(vHpr) + box.setQuaternion(self.placerNode.getQuat()) + self.commonObjectDict[commonId] = (commonId, type, box) + elif type == 1: + model, cross = self.createCross(self.world, self.space, 1.0, 3.0, 12.0, 2.0, 2) + motor = OdeHingeJoint(self.world) + cross.setPosition(vPos) + cross.setQuaternion(self.placerNode.getQuat()) + ourAxis = render.getRelativeVector(self.placerNode, Vec3(0, 0, 1)) + motor.setParamVel(1.5) + motor.setParamFMax(500000000.0) + boxsize = Vec3(1.0, 1.0, 1.0) + motor.attach(0, cross) + motor.setAnchor(vPos) + motor.setAxis(ourAxis) + self.cross = cross + cross.enable() + self.commonObjectDict[commonId] = (commonId, type, cross) + elif type == 2: + ourAxis = render.getRelativeVector(self.placerNode, Vec3(0, 0, 1)) + model, box = self.createBox(self.world, self.space, 10.0, 5.0, 5.0, 5.0, 2) + box.setPosition(vPos) + box.setQuaternion(self.placerNode.getQuat()) + motor = OdeSliderJoint(self.world) + motor.attach(box, 0) + motor.setAxis(ourAxis) + motor.setParamVel(3.0) + motor.setParamFMax(5000000.0) + motor.setParamHiStop(10.0) + motor.setParamLoStop(-10.0) + timeData = (0.0, 5.0) + forceData = (3.0, -3.0) + eventData = (1, 2) + self.commonObjectDict[commonId] = (commonId, + type, + box, + motor, + timeData, + forceData, + eventData, + model) + elif type == 3: + vPos = Point3(float(pos[0]), float(pos[1]), float(pos[2])) + vHpr = Vec3(float(hpr[0]), float(hpr[1]), float(hpr[2])) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + self.subPlacerNode.setPos(0, 0, 0) + if self.canRender: + myModel = loader.loadModel('phase_6/models/golf/golf_windmill_b') + else: + myModel = loader.loadModel('phase_6/models/golf/golf_windmill_b.bam') + myModel.reparentTo(self.root) + myModel.setPos(vPos) + myModel.setHpr(vHpr) + millFan = myModel.find('**/windmillFan0') + millBase = myModel.find('**/arm') + rod = myModel.find('**/rod') + rod.wrtReparentTo(millBase) + self.windmillFanNodePath = millFan + self.windmillBaseNodePath = millBase + millData = OdeTriMeshData(millBase) + millGeom = OdeTriMeshGeom(self.space, millData) + self.meshDataList.append(millData) + millGeom.setPosition(self.subPlacerNode.getPos(self.root)) + millGeom.setQuaternion(self.subPlacerNode.getQuat()) + millGeom.setCollideBits(BitMask32(251658240)) + millGeom.setCategoryBits(BitMask32(8388608)) + self.space.setCollideId(millGeom, 8) + vPos = Point3(float(pos[0]), float(pos[1]), float(pos[2]) + 5) + vHpr = Vec3(float(hpr[0]), float(hpr[1] + 90), float(hpr[2]) - 90) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + self.subPlacerNode.setPos(-1, 0, 0.0) + model, cross = self.createPinWheel(self.world, self.space, 10.0, 1.6, 4.0, 0.6, 5, 3.7, 1.2, 1, millFan, (0, 0, 90), (-4.6, -0.5, -0.25), 20) + self.placerNode.setHpr(vHpr) + self.placerNode.setPos(vPos) + self.subPlacerNode.setPos(-1, 0, 0.0) + motor = OdeHingeJoint(self.world) + cross.setPosition(self.subPlacerNode.getPos(self.root)) + cross.setQuaternion(self.placerNode.getQuat()) + ourAxis = self.root.getRelativeVector(self.subPlacerNode, Vec3(0, 0, 1)) + motor.setParamVel(1.0) + motor.setParamFMax(50000.0) + boxsize = Vec3(1.0, 1.0, 1.0) + motor.attach(0, cross) + motor.setAnchor(self.subPlacerNode.getPos(self.root)) + motor.setAxis(ourAxis) + self.cross = cross + cross.enable() + self.commonObjectDict[commonId] = (commonId, type, cross) + elif type == 4: + ourAxis = self.root.getRelativeVector(self.placerNode, Vec3(0, 1, 0)) + model, box = self.createBox(self.world, self.space, 50.0, sizeX, sizeY, 1.0, 2) + box.setPosition(vPos) + box.setQuaternion(self.placerNode.getQuat()) + motor = OdeSliderJoint(self.world) + motor.attach(box, 0) + motor.setAxis(ourAxis) + motor.setParamVel(moveDistance / 4.0) + motor.setParamFMax(25000.0) + motor.setParamHiStop(moveDistance) + motor.setParamLoStop(0) + timeData = (0.0, 1.0, 5.0, 6.0) + forceData = (-moveDistance / 4.0, + moveDistance / 4.0, + moveDistance / 4.0, + -moveDistance / 4.0) + eventData = (-1, 1, -2, 2) + radius = moveDistance + sizeY * 0.5 + self.commonObjectDict[commonId] = (commonId, + type, + box, + motor, + timeData, + forceData, + eventData, + model, + radius) + return [type, + commonId, + (pos[0], pos[1], pos[2]), + (hpr[0], hpr[1], hpr[2]), + sizeX, + sizeY, + moveDistance] + + def createSphere(self, world, space, density, radius, ballIndex = None): + self.notify.debug('create sphere index %s' % ballIndex) + body = OdeBody(world) + M = OdeMass() + M.setSphere(density, radius) + body.setMass(M) + body.setPosition(0, 0, -100) + geom = OdeSphereGeom(space, radius) + self.space.setSurfaceType(geom, 1) + self.notify.debug('collide ID is %s' % self.space.setCollideId(geom, 42)) + self.massList.append(M) + self.geomList.append(geom) + if ballIndex == 1: + self.notify.debug('1') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + elif ballIndex == 2: + self.notify.debug('2') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + elif ballIndex == 3: + self.notify.debug('3') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + elif ballIndex == 4: + self.notify.debug('4') + geom.setCollideBits(BitMask32(16777215)) + geom.setCategoryBits(BitMask32(4278190080L)) + else: + geom.setCollideBits(BitMask32(4294967295L)) + geom.setCategoryBits(BitMask32(4294967295L)) + geom.setBody(body) + if self.notify.getDebug(): + self.notify.debug('golf ball geom id') + geom.write() + self.notify.debug(' -') + self.notify.debug('Collide Bits %s' % geom.getCollideBits()) + if self.canRender: + testball = render.attachNewNode('Ball Holder') + ballmodel = loader.loadModel('phase_6/models/golf/golf_ball') + ballmodel.reparentTo(testball) + ballmodel.setColor(*GolfGlobals.PlayerColors[ballIndex - 1]) + testball.setPos(0, 0, -100) + self.odePandaRelationList.append((testball, body)) + else: + testball = None + self.bodyList.append((None, body)) + return (testball, body, geom) + + def createBox(self, world, space, density, lx, ly, lz, colOnlyBall = 0): + body = OdeBody(self.world) + M = OdeMass() + M.setSphere(density, 0.3 * (lx + ly + lz)) + body.setMass(M) + boxsize = Vec3(lx, ly, lz) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 7) + self.massList.append(M) + self.geomList.append(geom) + if colOnlyBall: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + if self.canRender: + color = random.choice([Vec4(1.0, 0.0, 0.5, 1.0), Vec4(0.5, 0.5, 1.0, 1.0), Vec4(0.5, 1.0, 0.5, 1.0)]) + boxsize = Vec3(lx, ly, lz) + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(self.worldAttach, lx, ly, lz, color, 1) + boxNodePathGeom.setPos(0, 0, -100) + self.odePandaRelationList.append((boxNodePathGeom, body)) + else: + boxNodePathGeom = None + self.bodyList.append((None, body)) + return (boxNodePathGeom, body) + + def createCross(self, world, space, density, lx, ly, lz, colOnlyBall = 0, attachedGeo = None, aHPR = None, aPos = None): + body = OdeBody(self.world) + M = OdeMass() + M.setBox(density, lx, ly, lz) + body.setMass(M) + body.setFiniteRotationMode(1) + boxsize = Vec3(lx, ly, lz) + boxsize2 = Vec3(ly, lx, lz) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 13) + geom2 = OdeBoxGeom(space, boxsize2) + geom2.setBody(body) + self.space.setSurfaceType(geom2, 0) + self.space.setCollideId(geom2, 26) + self.massList.append(M) + self.geomList.append(geom) + self.geomList.append(geom2) + self.odePandaRelationList.append((boxNodePathGeom, body)) + if colOnlyBall == 1: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(251658240)) + geom2.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(0)) + geom2.setCategoryBits(BitMask32(0)) + if self.canRender: + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(self.worldAttach, lx, ly, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom.setPos(0, 0, -100) + boxNodePathGeom2, t1, t2 = BuildGeometry.addBoxGeom(boxNodePathGeom, ly, lx, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom2.setPos(0, 0, 0) + if attachedGeo: + attachedGeo.reparentTo(boxNodePathGeom) + attachedGeo.setHpr(0, 0, 90) + attachedGeo.setPos(-4.8, 0, -2.0) + self.odePandaRelationList.append((boxNodePathGeom, body)) + else: + boxNodePathGeom = None + self.bodyList.append((None, body)) + return (boxNodePathGeom, body) + + def createCross2(self, world, space, density, lx, ly, lz, latSlide, colOnlyBall = 0, attachedGeo = None, aHPR = None, aPos = None): + body = OdeBody(self.world) + M = OdeMass() + M.setBox(density, lx, ly, lz) + body.setMass(M) + body.setFiniteRotationMode(1) + boxsize = Vec3(lx, ly * 0.5, lz) + boxsize2 = Vec3(ly * 0.5, lx, lz) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + geom.setOffsetPosition(-latSlide, ly * 0.25, 0) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 13) + geom2 = OdeBoxGeom(space, boxsize2) + geom2.setBody(body) + geom2.setOffsetPosition(ly * 0.25, latSlide, 0) + self.space.setSurfaceType(geom2, 0) + self.space.setCollideId(geom2, 13) + geom3 = OdeBoxGeom(space, boxsize) + geom3.setBody(body) + geom3.setOffsetPosition(latSlide, -ly * 0.25, 0) + self.space.setSurfaceType(geom3, 0) + self.space.setCollideId(geom3, 13) + geom4 = OdeBoxGeom(space, boxsize2) + geom4.setBody(body) + geom4.setOffsetPosition(-ly * 0.25, -latSlide, 0) + self.space.setSurfaceType(geom4, 0) + self.space.setCollideId(geom4, 13) + self.massList.append(M) + self.geomList.append(geom) + self.geomList.append(geom2) + self.geomList.append(geom3) + self.geomList.append(geom4) + if colOnlyBall == 1: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(251658240)) + geom2.setCategoryBits(BitMask32(0)) + geom3.setCollideBits(BitMask32(251658240)) + geom3.setCategoryBits(BitMask32(0)) + geom4.setCollideBits(BitMask32(251658240)) + geom4.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + geom2.setCollideBits(BitMask32(0)) + geom2.setCategoryBits(BitMask32(0)) + geom3.setCollideBits(BitMask32(0)) + geom3.setCategoryBits(BitMask32(0)) + geom4.setCollideBits(BitMask32(0)) + geom4.setCategoryBits(BitMask32(0)) + if self.canRender: + someNodePathGeom = render.attachNewNode('pinwheel') + if attachedGeo: + attachedGeo.reparentTo(someNodePathGeom) + attachedGeo.setHpr(aHPR[0], aHPR[1], aHPR[2]) + attachedGeo.setPos(aPos[0], aPos[1], aPos[2]) + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, lx, ly * 0.5, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom.setPos(-latSlide, ly * 0.25, 0) + boxNodePathGeom2, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, ly * 0.5, lx, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom2.setPos(ly * 0.25, latSlide, 0) + boxNodePathGeom3, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, lx, ly * 0.5, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom3.setPos(latSlide, -ly * 0.25, 0) + boxNodePathGeom4, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, ly * 0.5, lx, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom4.setPos(-ly * 0.25, -latSlide, 0) + self.odePandaRelationList.append((someNodePathGeom, body)) + else: + someNodePathGeom = None + self.bodyList.append((None, body)) + return (someNodePathGeom, body) + + def createPinWheel(self, world, space, density, lx, ly, lz, numBoxes, disV, disH, colOnlyBall = 0, attachedGeo = None, aHPR = None, aPos = None, offRot = 0): + body = OdeBody(self.world) + M = OdeMass() + M.setBox(density, lx, ly, lz) + body.setMass(M) + body.setFiniteRotationMode(1) + boxsize = Vec3(lx, ly * 0.5, lz) + boxsize2 = Vec3(ly * 0.5, lx, lz) + self.massList.append(M) + self.placerNode.setPos(0, 0, 0) + self.placerNode.setHpr(0, 0, 0) + self.subPlacerNode.setHpr(0, 0, 0) + self.subPlacerNode.setPos(disH, disV, 0) + if self.canRender: + someNodePathGeom = render.attachNewNode('pinwheel') + else: + someNodePathGeom = self.root.attachNewNode('pinwheel') + for num in range(numBoxes): + spin = 360.0 * float(num) / float(numBoxes) + float(offRot) + self.placerNode.setH(spin) + geom = OdeBoxGeom(space, boxsize) + geom.setBody(body) + geom.setOffsetPosition(self.subPlacerNode.getPos(self.root)) + geom.setOffsetQuaternion(self.subPlacerNode.getQuat(self.root)) + self.geomList.append(geom) + self.space.setSurfaceType(geom, 0) + self.space.setCollideId(geom, 13) + if colOnlyBall == 1: + geom.setCollideBits(BitMask32(251658240)) + geom.setCategoryBits(BitMask32(0)) + elif colOnlyBall == 2: + geom.setCollideBits(BitMask32(0)) + geom.setCategoryBits(BitMask32(0)) + if not attachedGeo: + boxNodePathGeom, t1, t2 = BuildGeometry.addBoxGeom(someNodePathGeom, lx, ly * 0.5, lz, Vec4(1.0, 1.0, 1.0, 1.0), 1) + boxNodePathGeom.setPos(self.subPlacerNode.getPos(self.root)) + boxNodePathGeom.setHpr(self.subPlacerNode.getHpr(self.root)) + + if attachedGeo and self.canRender: + attachedGeo.reparentTo(someNodePathGeom) + attachedGeo.setHpr(aHPR[0], aHPR[1], aHPR[2]) + attachedGeo.setPos(aPos[0], aPos[1], aPos[2]) + if self.canRender: + self.odePandaRelationList.append((someNodePathGeom, body)) + else: + someNodePathGeom = None + self.bodyList.append((None, body)) + return (someNodePathGeom, body) + + def attachMarker(self, body): + if self.canRender: + testMarker = render.attachNewNode('Joint Marker') + ballmodel = loader.loadModel('models/misc/sphere') + ballmodel.reparentTo(testMarker) + ballmodel.setScale(0.25) + testMarker.setPos(0.0, 0.0, -100.0) + self.odePandaRelationList.append((testMarker, body)) diff --git a/toontown/golf/__init__.py b/toontown/golf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/hood/AnimatedProp.py b/toontown/hood/AnimatedProp.py new file mode 100644 index 0000000..722c84a --- /dev/null +++ b/toontown/hood/AnimatedProp.py @@ -0,0 +1,20 @@ +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal + +class AnimatedProp(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('AnimatedProp') + + def __init__(self, node): + self.node = node + + def delete(self): + pass + + def uniqueName(self, name): + return name + '-' + str(self.node.this) + + def enter(self): + self.notify.debug('enter') + + def exit(self): + self.notify.debug('exit') diff --git a/toontown/hood/BRHood.py b/toontown/hood/BRHood.py new file mode 100644 index 0000000..e5a5df0 --- /dev/null +++ b/toontown/hood/BRHood.py @@ -0,0 +1,35 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.town import BRTownLoader +from toontown.safezone import BRSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * + +class BRHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = TheBrrrgh + self.townLoaderClass = BRTownLoader.BRTownLoader + self.safeZoneLoaderClass = BRSafeZoneLoader.BRSafeZoneLoader + self.storageDNAFile = 'phase_8/dna/storage_BR.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_8/dna/winter_storage_BR.dna'], + WACKY_WINTER_DECORATIONS: ['phase_8/dna/winter_storage_BR.dna'], + HALLOWEEN_PROPS: ['phase_8/dna/halloween_props_storage_BR.dna'], + SPOOKY_PROPS: ['phase_8/dna/halloween_props_storage_BR.dna']} + self.skyFile = 'phase_3.5/models/props/BR_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.titleColor = (0.3, 0.6, 1.0, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('BRHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('BRHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + + def exit(self): + ToonHood.ToonHood.exit(self) diff --git a/toontown/hood/BRHoodDataAI.py b/toontown/hood/BRHoodDataAI.py new file mode 100644 index 0000000..9b86a96 --- /dev/null +++ b/toontown/hood/BRHoodDataAI.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.safezone import DistributedTrolleyAI +from toontown.safezone import BRTreasurePlannerAI +from toontown.classicchars import DistributedPlutoAI +from toontown.toon import DistributedNPCFishermanAI + +class BRHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('BRHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.TheBrrrgh + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + trolley.generateWithRequired(self.zoneId) + trolley.start() + self.addDistObj(trolley) + self.treasurePlanner = BRTreasurePlannerAI.BRTreasurePlannerAI(self.zoneId) + self.treasurePlanner.start() + self.classicChar = DistributedPlutoAI.DistributedPlutoAI(self.air) + self.classicChar.generateWithRequired(self.zoneId) + self.classicChar.start() + self.addDistObj(self.classicChar) diff --git a/toontown/hood/BossbotHQ.py b/toontown/hood/BossbotHQ.py new file mode 100644 index 0000000..24b1a3d --- /dev/null +++ b/toontown/hood/BossbotHQ.py @@ -0,0 +1,42 @@ +import CogHood +from toontown.toonbase import ToontownGlobals +from toontown.coghq import BossbotCogHQLoader +from toontown.hood import ZoneUtil + +class BossbotHQ(CogHood.CogHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + CogHood.CogHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = ToontownGlobals.BossbotHQ + self.cogHQLoaderClass = BossbotCogHQLoader.BossbotCogHQLoader + self.storageDNAFile = None + self.skyFile = 'phase_9/models/cogHQ/cog_sky' + self.titleColor = (0.5, 0.5, 0.5, 1.0) + return + + def load(self): + CogHood.CogHood.load(self) + self.sky.hide() + self.parentFSM.getStateNamed('BossbotHQ').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('BossbotHQ').removeChild(self.fsm) + del self.cogHQLoaderClass + CogHood.CogHood.unload(self) + + def enter(self, *args): + CogHood.CogHood.enter(self, *args) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.BossbotHQCameraNear, ToontownGlobals.BossbotHQCameraFar) + + def exit(self): + localAvatar.setCameraFov(ToontownGlobals.DefaultCameraFov) + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + CogHood.CogHood.exit(self) + + def spawnTitleText(self, zoneId, floorNum = None): + if ZoneUtil.isMintInteriorZone(zoneId): + text = '%s\n%s' % (ToontownGlobals.StreetNames[zoneId][-1], TTLocalizer.MintFloorTitle % (floorNum + 1)) + self.doSpawnTitleText(text) + else: + CogHood.CogHood.spawnTitleText(self, zoneId) diff --git a/toontown/hood/BossbotHQDataAI.py b/toontown/hood/BossbotHQDataAI.py new file mode 100644 index 0000000..ed048e5 --- /dev/null +++ b/toontown/hood/BossbotHQDataAI.py @@ -0,0 +1,80 @@ +from pandac.PandaModules import Point3 +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedCogHQDoorAI +from toontown.building import DistributedDoorAI +from toontown.building import DoorTypes +from toontown.coghq import LobbyManagerAI +from toontown.building import DistributedBossElevatorAI +from toontown.suit import DistributedBossbotBossAI +from toontown.building import DistributedBBElevatorAI +from toontown.building import DistributedBoardingPartyAI +from toontown.building import FADoorCodes +from toontown.coghq import DistributedCogKartAI + +class BossbotHQDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('BossbotHQDataAI') + + def __init__(self, air, zoneId=None): + self.notify.debug('__init__: zoneId:%s' % zoneId) + hoodId = ToontownGlobals.BossbotHQ + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + self.cogKarts = [] + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + + def makeOfficeElevator(index, antiShuffle=0, minLaff=0): + destZone = (ToontownGlobals.LawbotStageIntA, ToontownGlobals.LawbotStageIntB, ToontownGlobals.LawbotStageIntC, ToontownGlobals.LawbotStageIntD)[index] + elev = DistributedLawOfficeElevatorExtAI.DistributedLawOfficeElevatorExtAI(self.air, self.air.lawMgr, destZone, index, antiShuffle=0, minLaff=minLaff) + elev.generateWithRequired(ToontownGlobals.LawbotOfficeExt) + self.addDistObj(elev) + + self.lobbyMgr = LobbyManagerAI.LobbyManagerAI(self.air, DistributedBossbotBossAI.DistributedBossbotBossAI) + self.lobbyMgr.generateWithRequired(ToontownGlobals.BossbotLobby) + self.addDistObj(self.lobbyMgr) + self.lobbyElevator = DistributedBBElevatorAI.DistributedBBElevatorAI(self.air, self.lobbyMgr, ToontownGlobals.BossbotLobby, antiShuffle=1) + self.lobbyElevator.generateWithRequired(ToontownGlobals.BossbotLobby) + self.addDistObj(self.lobbyElevator) + if simbase.config.GetBool('want-boarding-groups', 1): + self.boardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, [self.lobbyElevator.doId], 8) + self.boardingParty.generateWithRequired(ToontownGlobals.BossbotLobby) + + def makeDoor(destinationZone, intDoorIndex, extDoorIndex, lock=0): + intDoor = DistributedCogHQDoorAI.DistributedCogHQDoorAI(self.air, 0, DoorTypes.INT_COGHQ, self.canonicalHoodId, doorIndex=intDoorIndex, lockValue=lock) + intDoor.zoneId = destinationZone + extDoor = DistributedCogHQDoorAI.DistributedCogHQDoorAI(self.air, 0, DoorTypes.EXT_COGHQ, destinationZone, doorIndex=extDoorIndex, lockValue=lock) + extDoor.setOtherDoor(intDoor) + intDoor.setOtherDoor(extDoor) + intDoor.generateWithRequired(destinationZone) + intDoor.sendUpdate('setDoorIndex', [intDoor.getDoorIndex()]) + self.addDistObj(intDoor) + extDoor.generateWithRequired(self.canonicalHoodId) + extDoor.sendUpdate('setDoorIndex', [extDoor.getDoorIndex()]) + self.addDistObj(extDoor) + + makeDoor(ToontownGlobals.BossbotLobby, 0, 0, FADoorCodes.BB_DISGUISE_INCOMPLETE) + kartIdList = self.createCogKarts() + if simbase.config.GetBool('want-boarding-groups', 1): + self.courseBoardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, kartIdList, 4) + self.courseBoardingParty.generateWithRequired(self.zoneId) + + def createCogKarts(self): + posList = ( + (154.762, 37.169, 0), (141.403, -81.887, 0), (-48.44, 15.308, 0)) + hprList = ((110.815, 0, 0), (61.231, 0, 0), (-105.481, 0, 0)) + mins = ToontownGlobals.FactoryLaffMinimums[3] + kartIdList = [] + for cogCourse in xrange(len(posList)): + pos = posList[cogCourse] + hpr = hprList[cogCourse] + cogKart = DistributedCogKartAI.DistributedCogKartAI(self.air, cogCourse, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], self.air.countryClubMgr, minLaff=mins[cogCourse]) + cogKart.generateWithRequired(self.zoneId) + self.cogKarts.append(cogKart) + kartIdList.append(cogKart.doId) + + return kartIdList diff --git a/toontown/hood/CSHoodDataAI.py b/toontown/hood/CSHoodDataAI.py new file mode 100644 index 0000000..772fe78 --- /dev/null +++ b/toontown/hood/CSHoodDataAI.py @@ -0,0 +1,67 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedFactoryElevatorExtAI +from toontown.coghq import DistributedCogHQDoorAI +from toontown.coghq import DistributedSellbotHQDoorAI +from toontown.building import DoorTypes +from toontown.coghq import LobbyManagerAI +from toontown.building import DistributedVPElevatorAI +from toontown.suit import DistributedSellbotBossAI +from toontown.building import DistributedBoardingPartyAI + +class CSHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('CSHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.SellbotHQ + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + mins = ToontownGlobals.FactoryLaffMinimums[0] + self.testElev0 = DistributedFactoryElevatorExtAI.DistributedFactoryElevatorExtAI(self.air, self.air.factoryMgr, ToontownGlobals.SellbotFactoryInt, 0, antiShuffle=0, minLaff=mins[0]) + self.testElev0.generateWithRequired(ToontownGlobals.SellbotFactoryExt) + self.addDistObj(self.testElev0) + self.testElev1 = DistributedFactoryElevatorExtAI.DistributedFactoryElevatorExtAI(self.air, self.air.factoryMgr, ToontownGlobals.SellbotFactoryInt, 1, antiShuffle=0, minLaff=mins[1]) + self.testElev1.generateWithRequired(ToontownGlobals.SellbotFactoryExt) + self.addDistObj(self.testElev1) + self.lobbyMgr = LobbyManagerAI.LobbyManagerAI(self.air, DistributedSellbotBossAI.DistributedSellbotBossAI) + self.lobbyMgr.generateWithRequired(ToontownGlobals.SellbotLobby) + self.addDistObj(self.lobbyMgr) + self.lobbyElevator = DistributedVPElevatorAI.DistributedVPElevatorAI(self.air, self.lobbyMgr, ToontownGlobals.SellbotLobby, antiShuffle=1) + self.lobbyElevator.generateWithRequired(ToontownGlobals.SellbotLobby) + self.addDistObj(self.lobbyElevator) + if simbase.config.GetBool('want-boarding-groups', 1): + self.boardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, [self.lobbyElevator.doId], 8) + self.boardingParty.generateWithRequired(ToontownGlobals.SellbotLobby) + factoryIdList = [self.testElev0.doId, self.testElev1.doId] + if simbase.config.GetBool('want-boarding-groups', 1): + self.factoryBoardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, factoryIdList, 4) + self.factoryBoardingParty.generateWithRequired(ToontownGlobals.SellbotFactoryExt) + destinationZone = ToontownGlobals.SellbotLobby + extDoor0 = DistributedSellbotHQDoorAI.DistributedSellbotHQDoorAI(self.air, 0, DoorTypes.EXT_COGHQ, destinationZone, doorIndex=0) + extDoor1 = DistributedSellbotHQDoorAI.DistributedSellbotHQDoorAI(self.air, 1, DoorTypes.EXT_COGHQ, destinationZone, doorIndex=1) + extDoor2 = DistributedSellbotHQDoorAI.DistributedSellbotHQDoorAI(self.air, 2, DoorTypes.EXT_COGHQ, destinationZone, doorIndex=2) + extDoor3 = DistributedSellbotHQDoorAI.DistributedSellbotHQDoorAI(self.air, 3, DoorTypes.EXT_COGHQ, destinationZone, doorIndex=3) + extDoorList = [extDoor0, extDoor1, extDoor2, extDoor3] + for sp in self.suitPlanners: + if sp.zoneId == ToontownGlobals.SellbotHQ: + sp.cogHQDoors = extDoorList + + intDoor0 = DistributedCogHQDoorAI.DistributedCogHQDoorAI(self.air, 0, DoorTypes.INT_COGHQ, ToontownGlobals.SellbotHQ, doorIndex=0) + intDoor0.setOtherDoor(extDoor0) + intDoor0.zoneId = ToontownGlobals.SellbotLobby + for extDoor in extDoorList: + extDoor.setOtherDoor(intDoor0) + extDoor.zoneId = ToontownGlobals.SellbotHQ + extDoor.generateWithRequired(ToontownGlobals.SellbotHQ) + extDoor.sendUpdate('setDoorIndex', [extDoor.getDoorIndex()]) + self.addDistObj(extDoor) + + intDoor0.generateWithRequired(ToontownGlobals.SellbotLobby) + intDoor0.sendUpdate('setDoorIndex', [intDoor0.getDoorIndex()]) + self.addDistObj(intDoor0) diff --git a/toontown/hood/CashbotHQ.py b/toontown/hood/CashbotHQ.py new file mode 100644 index 0000000..e00cd8f --- /dev/null +++ b/toontown/hood/CashbotHQ.py @@ -0,0 +1,44 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +import CogHood +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.hood import ZoneUtil +from toontown.coghq import CashbotCogHQLoader + +class CashbotHQ(CogHood.CogHood): + notify = DirectNotifyGlobal.directNotify.newCategory('CashbotHQ') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + CogHood.CogHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = ToontownGlobals.CashbotHQ + self.cogHQLoaderClass = CashbotCogHQLoader.CashbotCogHQLoader + self.storageDNAFile = None + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.titleColor = (0.5, 0.5, 0.5, 1.0) + return + + def load(self): + CogHood.CogHood.load(self) + self.parentFSM.getStateNamed('CashbotHQ').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('CashbotHQ').removeChild(self.fsm) + del self.cogHQLoaderClass + CogHood.CogHood.unload(self) + + def enter(self, *args): + CogHood.CogHood.enter(self, *args) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.CashbotHQCameraNear, ToontownGlobals.CashbotHQCameraFar) + + def exit(self): + localAvatar.setCameraFov(ToontownGlobals.DefaultCameraFov) + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + CogHood.CogHood.exit(self) + + def spawnTitleText(self, zoneId, floorNum = None): + if ZoneUtil.isMintInteriorZone(zoneId): + text = '%s\n%s' % (ToontownGlobals.StreetNames[zoneId][-1], TTLocalizer.MintFloorTitle % (floorNum + 1)) + self.doSpawnTitleText(text) + else: + CogHood.CogHood.spawnTitleText(self, zoneId) diff --git a/toontown/hood/CashbotHQDataAI.py b/toontown/hood/CashbotHQDataAI.py new file mode 100644 index 0000000..b50f337 --- /dev/null +++ b/toontown/hood/CashbotHQDataAI.py @@ -0,0 +1,65 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedMintElevatorExtAI +from toontown.coghq import DistributedCogHQDoorAI +from toontown.building import DoorTypes +from toontown.coghq import LobbyManagerAI +from toontown.building import DistributedCFOElevatorAI +from toontown.suit import DistributedCashbotBossAI +from toontown.building import FADoorCodes +from toontown.building import DistributedBoardingPartyAI + +class CashbotHQDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('CashbotHqDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.CashbotHQ + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + mins = ToontownGlobals.FactoryLaffMinimums[1] + self.testElev0 = DistributedMintElevatorExtAI.DistributedMintElevatorExtAI(self.air, self.air.mintMgr, ToontownGlobals.CashbotMintIntA, antiShuffle=0, minLaff=mins[0]) + self.testElev0.generateWithRequired(ToontownGlobals.CashbotHQ) + self.addDistObj(self.testElev0) + self.testElev1 = DistributedMintElevatorExtAI.DistributedMintElevatorExtAI(self.air, self.air.mintMgr, ToontownGlobals.CashbotMintIntB, antiShuffle=0, minLaff=mins[1]) + self.testElev1.generateWithRequired(ToontownGlobals.CashbotHQ) + self.addDistObj(self.testElev1) + self.testElev2 = DistributedMintElevatorExtAI.DistributedMintElevatorExtAI(self.air, self.air.mintMgr, ToontownGlobals.CashbotMintIntC, antiShuffle=0, minLaff=mins[2]) + self.testElev2.generateWithRequired(ToontownGlobals.CashbotHQ) + self.addDistObj(self.testElev2) + self.lobbyMgr = LobbyManagerAI.LobbyManagerAI(self.air, DistributedCashbotBossAI.DistributedCashbotBossAI) + self.lobbyMgr.generateWithRequired(ToontownGlobals.CashbotLobby) + self.addDistObj(self.lobbyMgr) + self.lobbyElevator = DistributedCFOElevatorAI.DistributedCFOElevatorAI(self.air, self.lobbyMgr, ToontownGlobals.CashbotLobby, antiShuffle=1) + self.lobbyElevator.generateWithRequired(ToontownGlobals.CashbotLobby) + self.addDistObj(self.lobbyElevator) + if simbase.config.GetBool('want-boarding-groups', 1): + self.boardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, [self.lobbyElevator.doId], 8) + self.boardingParty.generateWithRequired(ToontownGlobals.CashbotLobby) + destinationZone = ToontownGlobals.CashbotLobby + extDoor0 = DistributedCogHQDoorAI.DistributedCogHQDoorAI(self.air, 0, DoorTypes.EXT_COGHQ, destinationZone, doorIndex=0, lockValue=FADoorCodes.CB_DISGUISE_INCOMPLETE) + extDoorList = [ + extDoor0] + intDoor0 = DistributedCogHQDoorAI.DistributedCogHQDoorAI(self.air, 0, DoorTypes.INT_COGHQ, ToontownGlobals.CashbotHQ, doorIndex=0) + intDoor0.setOtherDoor(extDoor0) + intDoor0.zoneId = ToontownGlobals.CashbotLobby + mintIdList = [ + self.testElev0.doId, self.testElev1.doId, self.testElev2.doId] + if simbase.config.GetBool('want-boarding-groups', 1): + self.mintBoardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, mintIdList, 4) + self.mintBoardingParty.generateWithRequired(self.zoneId) + for extDoor in extDoorList: + extDoor.setOtherDoor(intDoor0) + extDoor.zoneId = ToontownGlobals.CashbotHQ + extDoor.generateWithRequired(ToontownGlobals.CashbotHQ) + extDoor.sendUpdate('setDoorIndex', [extDoor.getDoorIndex()]) + self.addDistObj(extDoor) + + intDoor0.generateWithRequired(ToontownGlobals.CashbotLobby) + intDoor0.sendUpdate('setDoorIndex', [intDoor0.getDoorIndex()]) + self.addDistObj(intDoor0) diff --git a/toontown/hood/CogHood.py b/toontown/hood/CogHood.py new file mode 100644 index 0000000..f64e2bc --- /dev/null +++ b/toontown/hood/CogHood.py @@ -0,0 +1,53 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import Hood + +class CogHood(Hood.Hood): + notify = DirectNotifyGlobal.directNotify.newCategory('CogHood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.fsm = ClassicFSM.ClassicFSM('Hood', [State.State('start', self.enterStart, self.exitStart, ['cogHQLoader']), + State.State('cogHQLoader', self.enterCogHQLoader, self.exitCogHQLoader, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['cogHQLoader']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'start', 'final') + self.fsm.enterInitialState() + + def load(self): + Hood.Hood.load(self) + skyInner = self.sky.find('**/InnerGroup') + skyMiddle = self.sky.find('**/MiddleGroup') + skyOuter = self.sky.find('**/OutterSky') + if not skyOuter.isEmpty(): + skyOuter.setBin('background', 0) + if not skyMiddle.isEmpty(): + skyMiddle.setDepthWrite(0) + skyMiddle.setBin('background', 10) + if not skyInner.isEmpty(): + skyInner.setDepthWrite(0) + skyInner.setBin('background', 20) + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'cogHQLoader': + self.loader = self.cogHQLoaderClass(self, self.fsm.getStateNamed('cogHQLoader'), self.loaderDoneEvent) + self.loader.load(requestStatus['zoneId']) + + def enterCogHQLoader(self, requestStatus): + self.accept(self.loaderDoneEvent, self.handleCogHQLoaderDone) + self.loader.enter(requestStatus) + + def exitCogHQLoader(self): + self.ignore(self.loaderDoneEvent) + self.loader.exit() + self.loader.unload() + del self.loader + + def handleCogHQLoaderDone(self): + doneStatus = self.loader.getDoneStatus() + if self.isSameHood(doneStatus): + self.fsm.request('quietZone', [doneStatus]) + else: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) diff --git a/toontown/hood/DDHood.py b/toontown/hood/DDHood.py new file mode 100644 index 0000000..9bd96ca --- /dev/null +++ b/toontown/hood/DDHood.py @@ -0,0 +1,61 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.town import DDTownLoader +from toontown.safezone import DDSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * + +class DDHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = DonaldsDock + self.townLoaderClass = DDTownLoader.DDTownLoader + self.safeZoneLoaderClass = DDSafeZoneLoader.DDSafeZoneLoader + self.storageDNAFile = 'phase_6/dna/storage_DD.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_6/dna/winter_storage_DD.dna'], + WACKY_WINTER_DECORATIONS: ['phase_6/dna/winter_storage_DD.dna'], + HALLOWEEN_PROPS: ['phase_6/dna/halloween_props_storage_DD.dna'], + SPOOKY_PROPS: ['phase_6/dna/halloween_props_storage_DD.dna']} + self.skyFile = 'phase_3.5/models/props/BR_sky' + self.titleColor = (0.8, 0.6, 0.5, 1.0) + self.whiteFogColor = Vec4(0.8, 0.8, 0.8, 1) + self.underwaterFogColor = Vec4(0.0, 0.0, 0.6, 1.0) + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('DDHood').addChild(self.fsm) + self.fog = Fog('DDFog') + + def unload(self): + self.parentFSM.getStateNamed('DDHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + self.fog = None + return + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + + def exit(self): + ToonHood.ToonHood.exit(self) + + def setUnderwaterFog(self): + if base.wantFog: + self.fog.setColor(self.underwaterFogColor) + self.fog.setLinearRange(0.1, 100.0) + render.setFog(self.fog) + self.sky.setFog(self.fog) + + def setWhiteFog(self): + if base.wantFog: + self.fog.setColor(self.whiteFogColor) + self.fog.setLinearRange(0.0, 400.0) + render.clearFog() + render.setFog(self.fog) + self.sky.clearFog() + self.sky.setFog(self.fog) + + def setNoFog(self): + if base.wantFog: + render.clearFog() + self.sky.clearFog() diff --git a/toontown/hood/DDHoodDataAI.py b/toontown/hood/DDHoodDataAI.py new file mode 100644 index 0000000..c008abc --- /dev/null +++ b/toontown/hood/DDHoodDataAI.py @@ -0,0 +1,34 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.safezone import DistributedTrolleyAI +from toontown.safezone import DDTreasurePlannerAI +from toontown.safezone import DistributedBoatAI +from toontown.classicchars import DistributedDonaldDockAI + +class DDHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DDHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.DonaldsDock + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + trolley.generateWithRequired(self.zoneId) + trolley.start() + self.addDistObj(trolley) + self.treasurePlanner = DDTreasurePlannerAI.DDTreasurePlannerAI(self.zoneId) + self.treasurePlanner.start() + boat = DistributedBoatAI.DistributedBoatAI(self.air) + boat.generateWithRequired(self.zoneId) + boat.start() + self.addDistObj(boat) + self.classicChar = DistributedDonaldDockAI.DistributedDonaldDockAI(self.air) + self.classicChar.generateWithRequired(self.zoneId) + self.classicChar.start() + self.addDistObj(self.classicChar) diff --git a/toontown/hood/DGHood.py b/toontown/hood/DGHood.py new file mode 100644 index 0000000..3077e19 --- /dev/null +++ b/toontown/hood/DGHood.py @@ -0,0 +1,64 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.town import DGTownLoader +from toontown.safezone import DGSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * +import SkyUtil + +class DGHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = DaisyGardens + self.townLoaderClass = DGTownLoader.DGTownLoader + self.safeZoneLoaderClass = DGSafeZoneLoader.DGSafeZoneLoader + self.storageDNAFile = 'phase_8/dna/storage_DG.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_8/dna/winter_storage_DG.dna'], + WACKY_WINTER_DECORATIONS: ['phase_8/dna/winter_storage_DG.dna'], + HALLOWEEN_PROPS: ['phase_8/dna/halloween_props_storage_DG.dna'], + SPOOKY_PROPS: ['phase_8/dna/halloween_props_storage_DG.dna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.titleColor = (0.8, 0.6, 1.0, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('DGHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('DGHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + + def exit(self): + ToonHood.ToonHood.exit(self) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SkyUtil.startCloudSky(self) + + def startSpookySky(self): + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) diff --git a/toontown/hood/DGHoodDataAI.py b/toontown/hood/DGHoodDataAI.py new file mode 100644 index 0000000..cbae15c --- /dev/null +++ b/toontown/hood/DGHoodDataAI.py @@ -0,0 +1,37 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.safezone import DistributedTrolleyAI +from toontown.safezone import DGTreasurePlannerAI +from toontown.classicchars import DistributedGoofyAI +from toontown.classicchars import DistributedDaisyAI +from toontown.safezone import DistributedDGFlowerAI +from toontown.safezone import ButterflyGlobals + +class DGHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DGHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.DaisyGardens + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + trolley.generateWithRequired(self.zoneId) + trolley.start() + self.addDistObj(trolley) + self.treasurePlanner = DGTreasurePlannerAI.DGTreasurePlannerAI(self.zoneId) + self.treasurePlanner.start() + self.classicChar = DistributedDaisyAI.DistributedDaisyAI(self.air) + self.classicChar.generateWithRequired(self.zoneId) + self.classicChar.start() + self.addDistObj(self.classicChar) + flower = DistributedDGFlowerAI.DistributedDGFlowerAI(self.air) + flower.generateWithRequired(self.zoneId) + flower.start() + self.addDistObj(flower) + self.createButterflies(ButterflyGlobals.DG) diff --git a/toontown/hood/DLHood.py b/toontown/hood/DLHood.py new file mode 100644 index 0000000..f547a81 --- /dev/null +++ b/toontown/hood/DLHood.py @@ -0,0 +1,34 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.town import DLTownLoader +from toontown.safezone import DLSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * + +class DLHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = DonaldsDreamland + self.townLoaderClass = DLTownLoader.DLTownLoader + self.safeZoneLoaderClass = DLSafeZoneLoader.DLSafeZoneLoader + self.storageDNAFile = 'phase_8/dna/storage_DL.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_8/dna/winter_storage_DL.dna'], + WACKY_WINTER_DECORATIONS: ['phase_8/dna/winter_storage_DL.dna'], + HALLOWEEN_PROPS: ['phase_8/dna/halloween_props_storage_DL.dna'], + SPOOKY_PROPS: ['phase_8/dna/halloween_props_storage_DL.dna']} + self.skyFile = 'phase_8/models/props/DL_sky' + self.titleColor = (1.0, 0.9, 0.5, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('DLHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('DLHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + + def exit(self): + ToonHood.ToonHood.exit(self) diff --git a/toontown/hood/DLHoodDataAI.py b/toontown/hood/DLHoodDataAI.py new file mode 100644 index 0000000..14270ce --- /dev/null +++ b/toontown/hood/DLHoodDataAI.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.safezone import DistributedTrolleyAI +from toontown.safezone import DLTreasurePlannerAI +from toontown.classicchars import DistributedDonaldAI +from toontown.safezone import ButterflyGlobals + +class DLHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DLHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.DonaldsDreamland + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + trolley.generateWithRequired(self.zoneId) + trolley.start() + self.addDistObj(trolley) + self.treasurePlanner = DLTreasurePlannerAI.DLTreasurePlannerAI(self.zoneId) + self.treasurePlanner.start() + self.classicChar = DistributedDonaldAI.DistributedDonaldAI(self.air) + self.classicChar.generateWithRequired(self.zoneId) + self.classicChar.start() + self.addDistObj(self.classicChar) diff --git a/toontown/hood/EstateHood.py b/toontown/hood/EstateHood.py new file mode 100644 index 0000000..69069ec --- /dev/null +++ b/toontown/hood/EstateHood.py @@ -0,0 +1,150 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.minigame import Purchase +from otp.avatar import DistributedAvatar +import SkyUtil +from direct.task.Task import Task +import Hood +from toontown.estate import EstateLoader +from toontown.estate import HouseGlobals +import ZoneUtil + +class EstateHood(Hood.Hood): + notify = DirectNotifyGlobal.directNotify.newCategory('EstateHood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.fsm = ClassicFSM.ClassicFSM('Hood', [State.State('start', self.enterStart, self.exitStart, ['safeZoneLoader']), + State.State('safeZoneLoader', self.enterSafeZoneLoader, self.exitSafeZoneLoader, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['safeZoneLoader']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'start', 'final') + self.fsm.enterInitialState() + self.id = MyEstate + self.safeZoneLoaderClass = EstateLoader.EstateLoader + self.storageDNAFile = 'phase_5.5/dna/storage_estate.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_5.5/dna/winter_storage_estate.dna'], + WACKY_WINTER_DECORATIONS: ['phase_5.5/dna/winter_storage_estate.dna'], + HALLOWEEN_PROPS: ['phase_5.5/dna/halloween_props_storage_estate.dna'], + SPOOKY_PROPS: ['phase_5.5/dna/halloween_props_storage_estate.dna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.popupInfo = None + return + + def load(self): + Hood.Hood.load(self) + + def unload(self): + del self.safeZoneLoaderClass + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + Hood.Hood.unload(self) + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + self.accept('kickToPlayground', self.kickToPlayground) + self.fsm.request(requestStatus['loader'], [requestStatus]) + + def exit(self): + if self.loader: + self.loader.exit() + self.loader.unload() + del self.loader + Hood.Hood.exit(self) + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + self.loader = self.safeZoneLoaderClass(self, self.fsm.getStateNamed('safeZoneLoader'), self.loaderDoneEvent) + self.loader.load() + + def spawnTitleText(self, zoneId): + pass + + def hideTitleTextTask(self, task): + return Task.done + + def kickToPlayground(self, retCode): + if retCode == 0: + msg = TTLocalizer.EstateOwnerLeftMessage % HouseGlobals.BOOT_GRACE_PERIOD + self.__popupKickoutMessage(msg) + elif retCode == 1: + zoneId = base.localAvatar.lastHood + self.doneStatus = {'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + messenger.send(self.doneEvent) + elif retCode == 2: + zoneId = base.localAvatar.lastHood + self.doneStatus = {'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + messenger.send(self.doneEvent) + else: + self.notify.error('unknown reason for exiting estate') + return + + def __popupKickoutMessage(self, msg): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = 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.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=msg, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(0.88, 1, 0.75), geom_pos=(0, 0, -.08), text_scale=TTLocalizer.EHpopupInfo, text_pos=(0, 0.1)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.EstatePopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.3), command=self.__handleKickoutOk) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def __handleKickoutOk(self): + self.popupInfo.reparentTo(hidden) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SkyUtil.startCloudSky(self) + if base.cloudPlatformsEnabled: + self.loader.startCloudPlatforms() + + def stopSky(self): + Hood.Hood.stopSky(self) + self.loader.stopCloudPlatforms() + + def startSpookySky(self): + if hasattr(self, 'loader') and self.loader and hasattr(self.loader, 'cloudTrack') and self.loader.cloudTrack: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) diff --git a/toontown/hood/FishAnimatedProp.py b/toontown/hood/FishAnimatedProp.py new file mode 100644 index 0000000..3152635 --- /dev/null +++ b/toontown/hood/FishAnimatedProp.py @@ -0,0 +1,56 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * +from toontown.effects.Splash import * +from toontown.effects.Ripples import * +import random + +class FishAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + parent = node.getParent() + self.fish = Actor.Actor(node, copy=0) + self.fish.reparentTo(parent) + self.fish.setTransform(node.getTransform()) + node.clearMat() + self.fish.loadAnims({'jump': 'phase_4/models/props/SZ_fish-jump', + 'swim': 'phase_4/models/props/SZ_fish-swim'}) + self.splashSfxList = (loader.loadSfx('phase_4/audio/sfx/TT_splash1.mp3'), loader.loadSfx('phase_4/audio/sfx/TT_splash2.mp3')) + self.node = self.fish + self.geom = self.fish.getGeomNode() + self.exitRipples = Ripples(self.geom) + self.exitRipples.setBin('fixed', 25, 1) + self.exitRipples.setPosHprScale(-0.3, 0.0, 1.24, 0.0, 0.0, 0.0, 0.7, 0.7, 0.7) + self.splash = Splash(self.geom, wantParticles=0) + self.splash.setPosHprScale(-1, 0.0, 1.23, 0.0, 0.0, 0.0, 0.7, 0.7, 0.7) + randomSplash = random.choice(self.splashSfxList) + self.track = Sequence(FunctionInterval(self.randomizePosition), Func(self.node.unstash), Parallel(self.fish.actorInterval('jump'), Sequence(Wait(0.25), Func(self.exitRipples.play, 0.75)), Sequence(Wait(1.14), Func(self.splash.play), SoundInterval(randomSplash, volume=0.8, node=self.node))), Wait(1), Func(self.node.stash), Wait(4 + 10 * random.random()), name=self.uniqueName('Fish')) + + def delete(self): + self.exitRipples.destroy() + del self.exitRipples + self.splash.destroy() + del self.splash + del self.track + self.fish.removeNode() + del self.fish + del self.node + del self.geom + + def randomizePosition(self): + x = 5 * (random.random() - 0.5) + y = 5 * (random.random() - 0.5) + h = 360 * random.random() + self.geom.setPos(x, y, 0) + self.geom.setHpr(h, 0, 0) + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.track.loop() + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.track.finish() + self.splash.stop() + self.exitRipples.stop() diff --git a/toontown/hood/GSHood.py b/toontown/hood/GSHood.py new file mode 100644 index 0000000..06eea54 --- /dev/null +++ b/toontown/hood/GSHood.py @@ -0,0 +1,66 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.safezone import GSSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * +from toontown.racing import DistributedVehicle +import SkyUtil + +class GSHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = GoofySpeedway + self.safeZoneLoaderClass = GSSafeZoneLoader.GSSafeZoneLoader + self.storageDNAFile = 'phase_6/dna/storage_GS.dna' + self.holidayStorageDNADict = {HALLOWEEN_PROPS: ['phase_6/dna/halloween_props_storage_GS.dna'], + SPOOKY_PROPS: ['phase_6/dna/halloween_props_storage_GS.dna'], + CRASHED_LEADERBOARD: ['phase_6/dna/crashed_leaderboard_storage_GS.dna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.titleColor = (1.0, 0.5, 0.4, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('GSHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('GSHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + base.localAvatar.chatMgr.chatInputSpeedChat.addKartRacingMenu() + base.camLens.setNearFar(SpeedwayCameraNear, SpeedwayCameraFar) + + def exit(self): + base.camLens.setNearFar(DefaultCameraNear, DefaultCameraFar) + base.localAvatar.chatMgr.chatInputSpeedChat.removeKartRacingMenu() + ToonHood.ToonHood.exit(self) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SkyUtil.startCloudSky(self) + + def startSpookySky(self): + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) diff --git a/toontown/hood/GSHoodDataAI.py b/toontown/hood/GSHoodDataAI.py new file mode 100644 index 0000000..7502097 --- /dev/null +++ b/toontown/hood/GSHoodDataAI.py @@ -0,0 +1,123 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI, ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.racing import DistributedStartingBlockAI +from pandac.PandaModules import * +from toontown.racing.RaceGlobals import * +from toontown.classicchars import DistributedGoofySpeedwayAI +if __debug__: + import pdb + +class GSHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('GSHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.GoofySpeedway + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + self.createStartingBlocks() + self.cycleDuration = 10 + self.createLeaderBoards() + self.__cycleLeaderBoards() + self.classicChar = DistributedGoofySpeedwayAI.DistributedGoofySpeedwayAI(self.air) + self.classicChar.generateWithRequired(self.zoneId) + self.classicChar.start() + self.addDistObj(self.classicChar) + messenger.send('GSHoodSpawned', [self]) + + def shutdown(self): + self.notify.debug('shutting down GSHoodDataAI: %s' % self.zoneId) + messenger.send('GSHoodDestroyed', [self]) + HoodDataAI.HoodDataAI.shutdown(self) + + def cleanup(self): + self.notify.debug('cleaning up GSHoodDataAI: %s' % self.zoneId) + taskMgr.removeTasksMatching(str(self) + '_leaderBoardSwitch') + for board in self.leaderBoards: + board.delete() + + del self.leaderBoards + + def createLeaderBoards(self): + self.leaderBoards = [] + dnaStore = DNAStorage() + dnaData = simbase.air.loadDNAFileAI(dnaStore, simbase.air.lookupDNAFileName('goofy_speedway_sz.dna')) + if isinstance(dnaData, DNAData): + self.leaderBoards = self.air.findLeaderBoards(dnaData, self.zoneId) + for distObj in self.leaderBoards: + if distObj: + if distObj.getName().count('city'): + type = 'city' + else: + if distObj.getName().count('stadium'): + type = 'stadium' + else: + if distObj.getName().count('country'): + type = 'country' + for subscription in LBSubscription[type]: + distObj.subscribeTo(subscription) + + self.addDistObj(distObj) + + def __cycleLeaderBoards(self, task=None): + messenger.send('GS_LeaderBoardSwap' + str(self.zoneId)) + taskMgr.doMethodLater(self.cycleDuration, self.__cycleLeaderBoards, str(self) + '_leaderBoardSwitch') + + def createStartingBlocks(self): + self.racingPads = [] + self.viewingPads = [] + self.viewingBlocks = [] + self.startingBlocks = [] + self.foundRacingPadGroups = [] + self.foundViewingPadGroups = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundRacingPads, foundRacingPadGroups = self.air.findRacingPads(dnaData, zoneId, area) + foundViewingPads, foundViewingPadGroups = self.air.findRacingPads(dnaData, zoneId, area, type='viewing_pad') + self.racingPads += foundRacingPads + self.foundRacingPadGroups += foundRacingPadGroups + self.viewingPads += foundViewingPads + self.foundViewingPadGroups += foundViewingPadGroups + + self.startingBlocks = [] + for dnaGroup, distRacePad in zip(self.foundRacingPadGroups, self.racingPads): + startingBlocks = self.air.findStartingBlocks(dnaGroup, distRacePad) + self.startingBlocks += startingBlocks + for startingBlock in startingBlocks: + distRacePad.addStartingBlock(startingBlock) + + for distObj in self.startingBlocks: + self.addDistObj(distObj) + + for dnaGroup, distViewPad in zip(self.foundViewingPadGroups, self.viewingPads): + viewingBlocks = self.air.findStartingBlocks(dnaGroup, distViewPad) + self.viewingBlocks += viewingBlocks + for viewingBlock in viewingBlocks: + distViewPad.addStartingBlock(viewingBlock) + + for distObj in self.viewingBlocks: + self.addDistObj(distObj) + + for viewPad in self.viewingPads: + self.addDistObj(viewPad) + + for racePad in self.racingPads: + racePad.request('WaitEmpty') + self.addDistObj(racePad) + + return + + def logPossibleRaceCondition(self, startBlock): + for sb in self.startingBlocks: + if sb == startBlock: + if not sb.kartPad: + self.notify.warning('%s is in a broken state' % str(self)) + self.notify.warning('StartingBlocks: %d, RacePads: %s, ViewPads: %s, RacePadGroups: %s, ViewPadGroups: %s' % (len(self.startingBlocks), str(self.racingPads), str(self.viewingPads), str(self.foundRacingPadGroups), str(self.foundViewingPadGroups))) diff --git a/toontown/hood/GZHood.py b/toontown/hood/GZHood.py new file mode 100644 index 0000000..6425018 --- /dev/null +++ b/toontown/hood/GZHood.py @@ -0,0 +1,65 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.safezone import GZSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * +from toontown.racing import DistributedVehicle +import SkyUtil + +class GZHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = GolfZone + self.safeZoneLoaderClass = GZSafeZoneLoader.GZSafeZoneLoader + self.storageDNAFile = 'phase_6/dna/storage_GZ.dna' + self.holidayStorageDNADict = {HALLOWEEN_PROPS: ['phase_6/dna/halloween_props_storage_GZ.dna'], + SPOOKY_PROPS: ['phase_6/dna/halloween_props_storage_GZ.dna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.titleColor = (1.0, 0.5, 0.4, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('GZHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('GZHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + base.localAvatar.chatMgr.chatInputSpeedChat.addGolfMenu() + base.camLens.setNearFar(SpeedwayCameraNear, SpeedwayCameraFar) + + def exit(self): + base.camLens.setNearFar(DefaultCameraNear, DefaultCameraFar) + base.localAvatar.chatMgr.chatInputSpeedChat.removeGolfMenu() + ToonHood.ToonHood.exit(self) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SkyUtil.startCloudSky(self) + + def startSpookySky(self): + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) diff --git a/toontown/hood/GZHoodDataAI.py b/toontown/hood/GZHoodDataAI.py new file mode 100644 index 0000000..90834c9 --- /dev/null +++ b/toontown/hood/GZHoodDataAI.py @@ -0,0 +1,178 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI, ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.racing import DistributedStartingBlockAI +from pandac.PandaModules import * +from toontown.racing.RaceGlobals import * +from toontown.classicchars import DistributedGoofySpeedwayAI +from toontown.safezone import DistributedGolfKartAI +import string +if __debug__: + import pdb + +class GZHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('GZHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.GolfZone + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + self.createStartingBlocks() + self.createGolfKarts() + + def cleanup(self): + taskMgr.removeTasksMatching(str(self) + '_leaderBoardSwitch') + for board in self.leaderBoards: + board.delete() + + del self.leaderBoards + + def createLeaderBoards(self): + self.leaderBoards = [] + dnaStore = DNAStorage() + dnaData = simbase.air.loadDNAFileAI(dnaStore, simbase.air.lookupDNAFileName('goofy_speedway_sz.dna')) + if isinstance(dnaData, DNAData): + self.leaderBoards = self.air.findLeaderBoards(dnaData, self.zoneId) + for distObj in self.leaderBoards: + if distObj: + if distObj.getName().count('city'): + type = 'city' + else: + if distObj.getName().count('stadium'): + type = 'stadium' + else: + if distObj.getName().count('country'): + type = 'country' + for subscription in LBSubscription[type]: + distObj.subscribeTo(subscription) + + self.addDistObj(distObj) + + def __cycleLeaderBoards(self, task=None): + messenger.send('GS_LeaderBoardSwap') + taskMgr.doMethodLater(self.cycleDuration, self.__cycleLeaderBoards, str(self) + '_leaderBoardSwitch') + + def createStartingBlocks(self): + self.racingPads = [] + self.viewingPads = [] + self.viewingBlocks = [] + self.startingBlocks = [] + self.foundRacingPadGroups = [] + self.foundViewingPadGroups = [] + self.golfKartPads = [] + self.golfKartPadGroups = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundRacingPads, foundRacingPadGroups = self.air.findRacingPads(dnaData, zoneId, area, overrideDNAZone=True) + foundViewingPads, foundViewingPadGroups = self.air.findRacingPads(dnaData, zoneId, area, type='viewing_pad', overrideDNAZone=True) + self.racingPads += foundRacingPads + self.foundRacingPadGroups += foundRacingPadGroups + self.viewingPads += foundViewingPads + self.foundViewingPadGroups += foundViewingPadGroups + + self.startingBlocks = [] + for dnaGroup, distRacePad in zip(self.foundRacingPadGroups, self.racingPads): + startingBlocks = self.air.findStartingBlocks(dnaGroup, distRacePad) + self.startingBlocks += startingBlocks + for startingBlock in startingBlocks: + distRacePad.addStartingBlock(startingBlock) + + for distObj in self.startingBlocks: + self.addDistObj(distObj) + + for dnaGroup, distViewPad in zip(self.foundViewingPadGroups, self.viewingPads): + startingBlocks = self.air.findStartingBlocks(dnaGroup, distViewPad) + for viewingBlock in self.viewingBlocks: + distViewPad.addStartingBlock(viewingBlocks) + + for distObj in self.viewingBlocks: + self.addDistObj(distObj) + + for viewPad in self.viewingPads: + self.addDistObj(viewPad) + + for racePad in self.racingPads: + racePad.request('WaitEmpty') + self.addDistObj(racePad) + + return + + def findAndCreateGolfKarts(self, dnaGroup, zoneId, area, overrideDNAZone = 0, type = 'golf_kart'): + golfKarts = [] + golfKartGroups = [] + if isinstance(dnaGroup, DNAGroup) and string.find(dnaGroup.getName(), type) >= 0: + golfKartGroups.append(dnaGroup) + if type == 'golf_kart': + nameInfo = dnaGroup.getName().split('_') + golfCourse = int(nameInfo[2]) + pos = Point3(0, 0, 0) + hpr = Point3(0, 0, 0) + for i in range(dnaGroup.getNumChildren()): + childDnaGroup = dnaGroup.at(i) + if string.find(childDnaGroup.getName(), 'starting_block') >= 0: + padLocation = dnaGroup.getName().split('_')[2] + pos = childDnaGroup.getPos() + hpr = childDnaGroup.getHpr() + break + + pos += Point3(0, 0, 0.05) + golfKart = DistributedGolfKartAI.DistributedGolfKartAI(self.air, golfCourse, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + else: + self.notify.warning('unhandled case') + golfKart.generateWithRequired(zoneId) + golfKarts.append(golfKart) + else: + if isinstance(dnaGroup, DNAVisGroup) and not overrideDNAZone: + zoneId = ZoneUtil.getTrueZoneId(int(dnaGroup.getName().split(':')[0]), zoneId) + for i in range(dnaGroup.getNumChildren()): + childGolfKarts, childGolfKartGroups = self.findAndCreateGolfKarts(dnaGroup.at(i), zoneId, area, overrideDNAZone, type) + golfKarts += childGolfKarts + golfKartGroups += childGolfKartGroups + + return (golfKarts, golfKartGroups) + + def createGolfKarts(self): + self.golfKarts = [] + self.golfKartGroups = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundKarts, foundKartGroups = self.findAndCreateGolfKarts(dnaData, zoneId, area, overrideDNAZone=True) + self.golfKarts += foundKarts + self.golfKartGroups += foundKartGroups + + print self.golfKarts, self.golfKartGroups + for golfKart in self.golfKarts: + golfKart.start() + self.addDistObj(golfKart) + + return + + def findStartingBlocks(self, dnaRacingPadGroup, distRacePad): + startingBlocks = [] + for i in range(dnaRacingPadGroup.getNumChildren()): + dnaGroup = dnaRacingPadGroup.at(i) + if string.find(dnaGroup.getName(), 'starting_block') >= 0: + padLocation = dnaGroup.getName().split('_')[2] + pos = dnaGroup.getPos() + hpr = dnaGroup.getHpr() + if isinstance(distRacePad, DistributedRacePadAI): + sb = DistributedStartingBlockAI(self, distRacePad, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], int(padLocation)) + else: + sb = DistributedViewingBlockAI(self, distRacePad, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], int(padLocation)) + sb.generateWithRequired(distRacePad.zoneId) + startingBlocks.append(sb) + else: + self.notify.debug('Found dnaGroup that is not a starting_block under a race pad group') + + return startingBlocks diff --git a/toontown/hood/GenericAnimatedBuilding.py b/toontown/hood/GenericAnimatedBuilding.py new file mode 100644 index 0000000..1390a75 --- /dev/null +++ b/toontown/hood/GenericAnimatedBuilding.py @@ -0,0 +1,10 @@ +from toontown.hood import GenericAnimatedProp + +class GenericAnimatedBuilding(GenericAnimatedProp.GenericAnimatedProp): + + def __init__(self, node): + GenericAnimatedProp.GenericAnimatedProp.__init__(self, node) + + def enter(self): + if base.config.GetBool('buildings-animate', False): + GenericAnimatedProp.GenericAnimatedProp.enter(self) diff --git a/toontown/hood/GenericAnimatedProp.py b/toontown/hood/GenericAnimatedProp.py new file mode 100644 index 0000000..7e3014a --- /dev/null +++ b/toontown/hood/GenericAnimatedProp.py @@ -0,0 +1,122 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +from toontown.hood import HoodUtil + +class GenericAnimatedProp(AnimatedProp.AnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('GenericAnimatedProp') + AnimsUsingWav = [] + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + self.origAnimNameToSound = {} + code = node.getTag('DNACode') + if code.startswith('interactive_prop_'): + pathStr = code[len('interactive_prop_'):].split('__')[0] + elif code.startswith('animated_prop_generic_'): + pathStr = code[len('animated_prop_generic_'):].split('__')[0] + elif code.startswith('animated_prop_'): + tempStr = code[len('animated_prop_'):] + nextUnderscore = tempStr.find('_') + finalStr = tempStr[nextUnderscore + 1:] + pathStr = finalStr.split('__')[0] + elif code.startswith('animated_building_'): + pathStr = code[len('animated_building_'):].split('__')[0] + phaseDelimeter = len('phase_') + pathStr[len('phase_'):].find('_') + phaseStr = pathStr[:phaseDelimeter] + pathTokens = pathStr[phaseDelimeter + 1:].split('_') + self.path = phaseStr + for path in pathTokens: + self.path += '/' + self.path += path + + self.notify.debug('self.path=%s' % self.path) + self.calcHoodId(node) + self.propType = HoodUtil.calcPropType(node) + self.setupActor(node) + self.code = code + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + self.node.cleanup() + del self.node + del self.trashcan + + def enter(self): + self.node.postFlatten() + AnimatedProp.AnimatedProp.enter(self) + doAnimLoop = True + try: + if type(self).__name__ == 'instance': + if self.__class__.__name__ == 'GenericAnimatedProp': + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.HYDRANTS_BUFF_BATTLES): + doAnimLoop = True + else: + doAnimLoop = False + except: + pass + + if doAnimLoop: + self.node.loop('anim') + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.node.stop() + + def getActor(self): + return self.node + + def setupActor(self, node): + anim = node.getTag('DNAAnim') + self.trashcan = Actor.Actor(node, copy=0) + self.trashcan.reparentTo(node) + self.trashcan.loadAnims({'anim': '%s/%s' % (self.path, anim)}) + self.trashcan.pose('anim', 0) + self.node = self.trashcan + + def calcHoodId(self, node): + self.hoodId = ToontownGlobals.ToontownCentral + fullString = str(node) + splits = fullString.split('/') + try: + visId = int(splits[2]) + self.visId = visId + self.hoodId = ZoneUtil.getCanonicalHoodId(visId) + self.notify.debug('calcHoodId %d from %s' % (self.hoodId, fullString)) + except Exception, generic: + if 'Editor' not in fullString: + self.notify.warning("calcHoodId couldn't parse %s using 0" % fullString) + self.hoodId = 0 + self.visId = 0 + + def createSoundInterval(self, origAnimNameWithPath, maximumDuration): + if not hasattr(base, 'localAvatar'): + return Sequence() + sfxVolume = 1.0 + cutoff = 45 + if not hasattr(self, 'soundPath'): + self.soundPath = self.path.replace('/models/char', '/audio/sfx') + origAnimName = origAnimNameWithPath.split('/')[-1] + theSound = self.origAnimNameToSound.get(origAnimName) + if not theSound: + soundfile = origAnimName.replace('tt_a_ara', 'tt_s_ara') + fullPath = self.soundPath + '/' + soundfile + if origAnimName in self.AnimsUsingWav: + theSound = loader.loadSfx(fullPath + '.wav') + else: + theSound = loader.loadSfx(fullPath + '.mp3') + self.origAnimNameToSound[origAnimName] = theSound + if theSound: + soundDur = theSound.length() + if maximumDuration < soundDur: + if base.config.GetBool('interactive-prop-info', False): + if self.visId == localAvatar.zoneId and origAnimName != 'tt_a_ara_dga_hydrant_idleIntoFight': + self.notify.warning('anim %s had duration of %s while sound has duration of %s' % (origAnimName, maximumDuration, soundDur)) + soundDur = maximumDuration + result = SoundInterval(theSound, node=self.node, listenerNode=base.localAvatar, volume=sfxVolume, cutOff=cutoff, startTime=0, duration=soundDur) + else: + result = Sequence() + return result diff --git a/toontown/hood/HQPeriscopeAnimatedProp.py b/toontown/hood/HQPeriscopeAnimatedProp.py new file mode 100644 index 0000000..f8e75e8 --- /dev/null +++ b/toontown/hood/HQPeriscopeAnimatedProp.py @@ -0,0 +1,30 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * + +class HQPeriscopeAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + parent = node.getParent() + self.periscope = Actor.Actor(node, copy=0) + self.periscope.reparentTo(parent) + self.periscope.loadAnims({'anim': 'phase_3.5/models/props/HQ_periscope-chan'}) + self.periscope.pose('anim', 0) + self.node = self.periscope + self.track = Sequence(Wait(2.0), self.periscope.actorInterval('anim', startFrame=0, endFrame=40), Wait(0.7), self.periscope.actorInterval('anim', startFrame=40, endFrame=90), Wait(0.7), self.periscope.actorInterval('anim', startFrame=91, endFrame=121), Wait(0.7), self.periscope.actorInterval('anim', startFrame=121, endFrame=91), Wait(0.7), self.periscope.actorInterval('anim', startFrame=90, endFrame=40), Wait(0.7), self.periscope.actorInterval('anim', startFrame=40, endFrame=90), Wait(0.7), self.periscope.actorInterval('anim', startFrame=91, endFrame=121), Wait(0.5), self.periscope.actorInterval('anim', startFrame=121, endFrame=148), Wait(3.0), name=self.uniqueName('HQPeriscope')) + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + self.node.cleanup() + del self.node + del self.periscope + del self.track + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.track.loop() + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.track.finish() diff --git a/toontown/hood/HQTelescopeAnimatedProp.py b/toontown/hood/HQTelescopeAnimatedProp.py new file mode 100644 index 0000000..b80587f --- /dev/null +++ b/toontown/hood/HQTelescopeAnimatedProp.py @@ -0,0 +1,30 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * + +class HQTelescopeAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + parent = node.getParent() + self.telescope = Actor.Actor(node, copy=0) + self.telescope.reparentTo(parent) + self.telescope.loadAnims({'anim': 'phase_3.5/models/props/HQ_telescope-chan'}) + self.telescope.pose('anim', 0) + self.node = self.telescope + self.track = Sequence(Wait(5.0), self.telescope.actorInterval('anim', startFrame=0, endFrame=32), Wait(0.5), self.telescope.actorInterval('anim', startFrame=32, endFrame=78), Wait(0.5), self.telescope.actorInterval('anim', startFrame=79, endFrame=112), Wait(0.5), self.telescope.actorInterval('anim', startFrame=112, endFrame=79), Wait(0.5), self.telescope.actorInterval('anim', startFrame=78, endFrame=32), Wait(0.5), self.telescope.actorInterval('anim', startFrame=32, endFrame=78), Wait(0.5), self.telescope.actorInterval('anim', startFrame=79, endFrame=112), Wait(0.5), self.telescope.actorInterval('anim', startFrame=112, endFrame=148), Wait(4.0), name=self.uniqueName('HQTelescope')) + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + self.node.cleanup() + del self.node + del self.telescope + del self.track + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.track.loop() + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.track.finish() diff --git a/toontown/hood/Hood.py b/toontown/hood/Hood.py new file mode 100644 index 0000000..fc64e07 --- /dev/null +++ b/toontown/hood/Hood.py @@ -0,0 +1,250 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.task.Task import Task +from toontown.minigame import Purchase +from direct.gui import OnscreenText +from toontown.building import SuitInterior +import QuietZoneState +import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toon.Toon import teleportDebug + +class Hood(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Hood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + StateData.StateData.__init__(self, doneEvent) + self.loader = 'not initialized' + self.parentFSM = parentFSM + self.dnaStore = dnaStore + self.loaderDoneEvent = 'loaderDone' + self.id = None + self.hoodId = hoodId + self.titleText = None + self.titleColor = (1, 1, 1, 1) + self.holidayStorageDNADict = {} + self.spookySkyFile = None + self.halloweenLights = [] + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + hoodText = self.getHoodText(zoneId) + self.titleText = OnscreenText.OnscreenText(hoodText, fg=self.titleColor, font=getSignFont(), pos=(0, -0.5), scale=TTLocalizer.HtitleText, drawOrder=0, mayChange=1) + self.fsm.request(requestStatus['loader'], [requestStatus]) + + def getHoodText(self, zoneId): + hoodText = base.cr.hoodMgr.getFullnameFromId(self.id) + if self.id != Tutorial: + streetName = StreetNames.get(ZoneUtil.getCanonicalBranchZone(zoneId)) + if streetName: + hoodText = hoodText + '\n' + streetName[-1] + return hoodText + + def spawnTitleText(self, zoneId): + hoodText = self.getHoodText(zoneId) + self.doSpawnTitleText(hoodText) + + def doSpawnTitleText(self, text): + self.titleText.setText(text) + self.titleText.show() + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.clearColorScale() + self.titleText.setFg(self.titleColor) + seq = Task.sequence(Task.pause(0.1), Task.pause(6.0), self.titleText.lerpColorScale(Vec4(1.0, 1.0, 1.0, 1.0), Vec4(1.0, 1.0, 1.0, 0.0), 0.5), Task(self.hideTitleTextTask)) + taskMgr.add(seq, 'titleText') + + def hideTitleTextTask(self, task): + self.titleText.hide() + return Task.done + + def hideTitleText(self): + if self.titleText: + self.titleText.hide() + + def exit(self): + taskMgr.remove('titleText') + if self.titleText: + self.titleText.cleanup() + self.titleText = None + base.localAvatar.stopChat() + return + + def load(self): + if self.storageDNAFile: + loader.loadDNAFile(self.dnaStore, self.storageDNAFile) + newsManager = base.cr.newsManager + if newsManager: + holidayIds = base.cr.newsManager.getDecorationHolidayId() + for holiday in holidayIds: + for storageFile in self.holidayStorageDNADict.get(holiday, []): + loader.loadDNAFile(self.dnaStore, storageFile) + + if ToontownGlobals.HALLOWEEN_COSTUMES not in holidayIds and ToontownGlobals.SPOOKY_COSTUMES not in holidayIds or not self.spookySkyFile: + self.sky = loader.loadModel(self.skyFile) + self.sky.setTag('sky', 'Regular') + self.sky.setScale(1.0) + self.sky.setFogOff() + else: + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + if not newsManager: + self.sky = loader.loadModel(self.skyFile) + self.sky.setTag('sky', 'Regular') + self.sky.setScale(1.0) + self.sky.setFogOff() + + def unload(self): + if hasattr(self, 'loader'): + self.notify.info('Aggressively cleaning up loader: %s' % self.loader) + self.loader.exit() + self.loader.unload() + del self.loader + del self.fsm + del self.parentFSM + self.dnaStore.resetHood() + del self.dnaStore + self.sky.removeNode() + del self.sky + self.ignoreAll() + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enterStart(self): + pass + + def exitStart(self): + pass + + def isSameHood(self, status): + return status['hoodId'] == self.hoodId and status['shardId'] == None + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def enterQuietZone(self, requestStatus): + teleportDebug(requestStatus, 'Hood.enterQuietZone: status=%s' % requestStatus) + self._quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self._quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self._quietZoneDoneEvent) + self._enterWaitForSetZoneResponseMsg = self.quietZoneStateData.getEnterWaitForSetZoneResponseMsg() + self.acceptOnce(self._enterWaitForSetZoneResponseMsg, self.handleWaitForSetZoneResponse) + self._quietZoneLeftEvent = self.quietZoneStateData.getQuietZoneLeftEvent() + if base.placeBeforeObjects: + self.acceptOnce(self._quietZoneLeftEvent, self.handleLeftQuietZone) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self._quietZoneDoneEvent) + self.ignore(self._quietZoneLeftEvent) + self.ignore(self._enterWaitForSetZoneResponseMsg) + del self._quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def loadLoader(self, requestStatus): + pass + + def handleWaitForSetZoneResponse(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + if not loader.inBulkBlock: + loader.beginBulkLoad('hood', TTLocalizer.HeadingToPlayground, safeZoneCountMap[self.id], 1, TTLocalizer.TIP_GENERAL) + self.loadLoader(requestStatus) + loader.endBulkLoad('hood') + elif loaderName == 'townLoader': + if not loader.inBulkBlock: + zoneId = requestStatus['zoneId'] + toPhrase = StreetNames[ZoneUtil.getCanonicalBranchZone(zoneId)][0] + streetName = StreetNames[ZoneUtil.getCanonicalBranchZone(zoneId)][-1] + loader.beginBulkLoad('hood', TTLocalizer.HeadingToStreet % {'to': toPhrase, + 'street': streetName}, townCountMap[self.id], 1, TTLocalizer.TIP_STREET) + self.loadLoader(requestStatus) + loader.endBulkLoad('hood') + elif loaderName == 'minigame': + pass + elif loaderName == 'cogHQLoader': + print 'should be loading HQ' + + def handleLeftQuietZone(self): + status = self.quietZoneStateData.getRequestStatus() + teleportDebug(status, 'handleLeftQuietZone, status=%s' % status) + teleportDebug(status, 'requesting %s' % status['loader']) + self.fsm.request(status['loader'], [status]) + + def handleQuietZoneDone(self): + if not base.placeBeforeObjects: + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['loader'], [status]) + + def enterSafeZoneLoader(self, requestStatus): + self.accept(self.loaderDoneEvent, self.handleSafeZoneLoaderDone) + self.loader.enter(requestStatus) + self.spawnTitleText(requestStatus['zoneId']) + + def exitSafeZoneLoader(self): + taskMgr.remove('titleText') + self.hideTitleText() + self.ignore(self.loaderDoneEvent) + self.loader.exit() + self.loader.unload() + del self.loader + + def handleSafeZoneLoaderDone(self): + doneStatus = self.loader.getDoneStatus() + teleportDebug(doneStatus, 'handleSafeZoneLoaderDone, doneStatus=%s' % doneStatus) + if self.isSameHood(doneStatus) and doneStatus['where'] != 'party' or doneStatus['loader'] == 'minigame': + teleportDebug(doneStatus, 'same hood') + self.fsm.request('quietZone', [doneStatus]) + else: + teleportDebug(doneStatus, 'different hood') + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def startSky(self): + self.sky.reparentTo(camera) + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + + def stopSky(self): + taskMgr.remove('skyTrack') + self.sky.reparentTo(hidden) + + def startSpookySky(self): + if not self.spookySkyFile: + return + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) + + def endSpookySky(self): + if hasattr(self, 'sky') and self.sky: + self.sky.reparentTo(hidden) + if hasattr(self, 'sky'): + self.sky = loader.loadModel(self.skyFile) + self.sky.setTag('sky', 'Regular') + self.sky.setScale(1.0) + self.startSky() diff --git a/toontown/hood/HoodDataAI.py b/toontown/hood/HoodDataAI.py new file mode 100644 index 0000000..b9c5ce7 --- /dev/null +++ b/toontown/hood/HoodDataAI.py @@ -0,0 +1,168 @@ +from direct.directnotify import DirectNotifyGlobal +import ZoneUtil +from toontown.building import DistributedBuildingMgrAI +from toontown.suit import DistributedSuitPlannerAI +from toontown.safezone import ButterflyGlobals +from toontown.safezone import DistributedButterflyAI +from pandac.PandaModules import * +from toontown.toon import NPCToons + +class HoodDataAI: + notify = DirectNotifyGlobal.directNotify.newCategory('HoodDataAI') + + def __init__(self, air, zoneId, canonicalHoodId): + self.air = air + self.zoneId = zoneId + self.canonicalHoodId = canonicalHoodId + self.treasurePlanner = None + self.buildingManagers = [] + self.suitPlanners = [] + self.doId2do = {} + self.replacementHood = None + self.redirectingToMe = [] + self.hoodPopulation = 0 + self.pgPopulation = 0 + return + + def startup(self): + self.createFishingPonds() + self.createPartyPeople() + self.createBuildingManagers() + self.createSuitPlanners() + + def shutdown(self): + self.setRedirect(None) + if self.treasurePlanner: + self.treasurePlanner.stop() + self.treasurePlanner.deleteAllTreasuresNow() + self.treasurePlanner = None + for suitPlanner in self.suitPlanners: + suitPlanner.requestDelete() + del self.air.suitPlanners[suitPlanner.zoneId] + + self.suitPlanners = [] + for buildingManager in self.buildingManagers: + buildingManager.cleanup() + del self.air.buildingManagers[buildingManager.branchID] + + self.buildingManagers = [] + ButterflyGlobals.clearIndexes(self.zoneId) + del self.fishingPonds + for distObj in self.doId2do.values(): + distObj.requestDelete() + + del self.doId2do + del self.air + return + + def addDistObj(self, distObj): + self.doId2do[distObj.doId] = distObj + + def removeDistObj(self, distObj): + del self.doId2do[distObj.doId] + + def createPartyPeople(self): + partyHats = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + foundPartyHats = self.air.findPartyHats(dnaData, zoneId) + partyHats += foundPartyHats + + for distObj in partyHats: + self.addDistObj(distObj) + + return + + def createFishingPonds(self): + self.fishingPonds = [] + fishingPondGroups = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundFishingPonds, foundFishingPondGroups = self.air.findFishingPonds(dnaData, zoneId, area) + self.fishingPonds += foundFishingPonds + fishingPondGroups += foundFishingPondGroups + + for distObj in self.fishingPonds: + self.addDistObj(distObj) + npcs = NPCToons.createNpcsInZone(self.air, distObj.zoneId) + for npc in npcs: + self.addDistObj(npc) + + fishingSpots = [] + for dnaGroup, distPond in zip(fishingPondGroups, self.fishingPonds): + fishingSpots += self.air.findFishingSpots(dnaGroup, distPond) + + for distObj in fishingSpots: + self.addDistObj(distObj) + + return + + def createBuildingManagers(self): + for zone in self.air.zoneTable[self.canonicalHoodId]: + if zone[1]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaStore = self.air.dnaStoreMap[zone[0]] + mgr = DistributedBuildingMgrAI.DistributedBuildingMgrAI(self.air, zoneId, dnaStore, self.air.trophyMgr) + self.buildingManagers.append(mgr) + self.air.buildingManagers[zoneId] = mgr + + def createSuitPlanners(self): + for zone in self.air.zoneTable[self.canonicalHoodId]: + if zone[2]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + sp = DistributedSuitPlannerAI.DistributedSuitPlannerAI(self.air, zoneId) + sp.generateWithRequired(zoneId) + sp.d_setZoneId(zoneId) + sp.initTasks() + self.suitPlanners.append(sp) + self.air.suitPlanners[zoneId] = sp + + def createButterflies(self, playground): + ButterflyGlobals.generateIndexes(self.zoneId, playground) + for i in range(0, ButterflyGlobals.NUM_BUTTERFLY_AREAS[playground]): + for j in range(0, ButterflyGlobals.NUM_BUTTERFLIES[playground]): + bfly = DistributedButterflyAI.DistributedButterflyAI(self.air, playground, i, self.zoneId) + bfly.generateWithRequired(self.zoneId) + bfly.start() + self.addDistObj(bfly) + + def setRedirect(self, replacementHood): + if self.replacementHood: + self.replacementHood[0].redirectingToMe.remove(self) + self.replacementHood = replacementHood + if self.replacementHood: + self.replacementHood[0].redirectingToMe.append(self) + + def hasRedirect(self): + return self.replacementHood != None + + def getRedirect(self): + if self.replacementHood == None: + return self + else: + return self.replacementHood[0].getRedirect() + return + + def incrementPopulation(self, zoneId, increment): + self.hoodPopulation += increment + if ZoneUtil.isPlayground(zoneId): + self.pgPopulation += increment + + def getHoodPopulation(self): + population = self.hoodPopulation + for hood in self.redirectingToMe: + population += hood.getHoodPopulation() + + return population + + def getPgPopulation(self): + population = self.pgPopulation + for pg in self.redirectingToMe: + population += pg.getPgPopulation() + + return population diff --git a/toontown/hood/HoodUtil.py b/toontown/hood/HoodUtil.py new file mode 100644 index 0000000..f66b06c --- /dev/null +++ b/toontown/hood/HoodUtil.py @@ -0,0 +1,12 @@ +from toontown.toonbase import ToontownGlobals + +def calcPropType(node): + propType = ToontownGlobals.AnimPropTypes.Unknown + fullString = str(node) + if 'hydrant' in fullString: + propType = ToontownGlobals.AnimPropTypes.Hydrant + elif 'trashcan' in fullString: + propType = ToontownGlobals.AnimPropTypes.Trashcan + elif 'mailbox' in fullString: + propType = ToontownGlobals.AnimPropTypes.Mailbox + return propType diff --git a/toontown/hood/HydrantInteractiveProp.py b/toontown/hood/HydrantInteractiveProp.py new file mode 100644 index 0000000..e612b6a --- /dev/null +++ b/toontown/hood/HydrantInteractiveProp.py @@ -0,0 +1,236 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Func +from toontown.hood import InteractiveAnimatedProp +from toontown.hood import GenericAnimatedProp +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals, TTLocalizer + +class HydrantInteractiveProp(InteractiveAnimatedProp.InteractiveAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('HydrantInteractiveProp') + BattleCheerText = TTLocalizer.InteractivePropTrackBonusTerms[ToontownBattleGlobals.SQUIRT_TRACK] + ZoneToIdles = {ToontownGlobals.ToontownCentral: (('tt_a_ara_ttc_hydrant_idle0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_hydrant_idle2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_hydrant_idle1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_hydrant_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DonaldsDock: (('tt_a_ara_ttc_hydrant_idle0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_hydrant_idle2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_hydrant_idle1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_hydrant_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DaisyGardens: (('tt_a_ara_dga_hydrant_idle0', + 3, + 10, + 'tt_a_ara_dga_hydrant_idle0settle', + 3, + 10), + ('tt_a_ara_dga_hydrant_idleLook1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dga_hydrant_idleSneeze2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dga_hydrant_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.MinniesMelodyland: (('tt_a_ara_mml_hydrant_idle0', + 3, + 10, + 'tt_a_ara_mml_hydrant_idle0settle', + 3, + 10), + ('tt_a_ara_mml_hydrant_idle2', + 3, + 10, + 'tt_a_ara_mml_hydrant_idle2settle', + 3, + 10), + ('tt_a_ara_mml_hydrant_idle1', + 3, + 10, + 'tt_a_ara_mml_hydrant_idle1settle', + 3, + 10), + ('tt_a_ara_mml_hydrant_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.TheBrrrgh: (('tt_a_ara_tbr_hydrant_idleShiver1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_hydrant_idleRubNose0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_hydrant_idleSneeze2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_hydrant_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DonaldsDreamland: (('tt_a_ara_ddl_hydrant_idle0', + 3, + 10, + None, + 0, + 0), + ('tt_a_ara_ddl_hydrant_idle1', + 1, + 1, + None, + 0, + 0), + ('tt_a_ara_ddl_hydrant_idle2', + 1, + 1, + None, + 0, + 0), + ('tt_a_ara_ddl_hydrant_idleAwesome3', + 1, + 1, + None, + 0, + 0))} + ZoneToIdleIntoFightAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_hydrant_idleIntoFight', + ToontownGlobals.DonaldsDock: 'tt_a_ara_ttc_hydrant_idleIntoFight', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_hydrant_idleIntoFight', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_hydrant_idleIntoFight', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_hydrant_idleIntoFight', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_hydrant_idleIntoFight'} + ZoneToVictoryAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_hydrant_victoryDance', + ToontownGlobals.DonaldsDock: 'tt_a_ara_ttc_hydrant_victoryDance', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_hydrant_victoryDance', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_hydrant_victoryDance', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_hydrant_victoryDance', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_hydrant_victoryDance'} + ZoneToSadAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_hydrant_fightSad', + ToontownGlobals.DonaldsDock: 'tt_a_ara_ttc_hydrant_fightSad', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_hydrant_fightSad', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_hydrant_fightSad', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_hydrant_fightSad', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_hydrant_fightSad'} + ZoneToFightAnims = {ToontownGlobals.ToontownCentral: ('tt_a_ara_ttc_hydrant_fightBoost', 'tt_a_ara_ttc_hydrant_fightCheer', 'tt_a_ara_ttc_hydrant_fightIdle'), + ToontownGlobals.DonaldsDock: ('tt_a_ara_ttc_hydrant_fightBoost', 'tt_a_ara_ttc_hydrant_fightCheer', 'tt_a_ara_ttc_hydrant_fightIdle'), + ToontownGlobals.DaisyGardens: ('tt_a_ara_dga_hydrant_fightBoost', 'tt_a_ara_dga_hydrant_fightCheer', 'tt_a_ara_dga_hydrant_fightIdle'), + ToontownGlobals.MinniesMelodyland: ('tt_a_ara_mml_hydrant_fightBoost', 'tt_a_ara_mml_hydrant_fightCheer', 'tt_a_ara_mml_hydrant_fightIdle'), + ToontownGlobals.TheBrrrgh: ('tt_a_ara_tbr_hydrant_fightBoost', 'tt_a_ara_tbr_hydrant_fightCheer', 'tt_a_ara_tbr_hydrant_fightIdle'), + ToontownGlobals.DonaldsDreamland: ('tt_a_ara_ddl_hydrant_fightBoost', 'tt_a_ara_ddl_hydrant_fightCheer', 'tt_a_ara_ddl_hydrant_fightIdle')} + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) + + def __init__(self, node): + self.leftWater = None + self.rightWater = None + InteractiveAnimatedProp.InteractiveAnimatedProp.__init__(self, node, ToontownGlobals.HYDRANTS_BUFF_BATTLES) + return + + def setupActor(self, node): + InteractiveAnimatedProp.InteractiveAnimatedProp.setupActor(self, node) + if not self.hoodId == ToontownGlobals.TheBrrrgh: + water = loader.loadModel('phase_5/models/char/tt_m_efx_hydrantSquirt') + self.leftWater = water.find('**/efx_hydrantSquirtLeft') + self.rightWater = water.find('**/efx_hydrantSquirtRight') + dx_left_water = self.node.find('**/dx_left_water') + if self.leftWater: + self.leftWater.reparentTo(dx_left_water) + base.leftWater = self.leftWater + self.leftWater.hide() + else: + self.notify.warning('couldnt find %s in rig for hood %d' % ('dx_left_water', self.hoodId)) + dx_right_water = self.node.find('**/dx_right_water') + if self.rightWater: + self.rightWater.reparentTo(dx_right_water) + self.rightWater.hide() + else: + self.notify.warning('couldnt find %s in rig for hood %d' % ('dx_left_water', self.hoodId)) + + def hideWater(self): + if self.leftWater: + self.leftWater.hide() + if self.rightWater: + self.rightWater.hide() + + def showWater(self): + if self.leftWater: + self.leftWater.show() + if self.rightWater: + self.rightWater.show() + + def hasOverrideIval(self, origAnimName): + result = False + if ('fightBoost' in origAnimName or 'fightCheer' in origAnimName) and not self.hoodId == ToontownGlobals.TheBrrrgh: + result = True + return result + + def getOverrideIval(self, origAnimName): + result = Sequence() + if ('fightBoost' in origAnimName or 'fightCheer' in origAnimName) and not self.hoodId == ToontownGlobals.TheBrrrgh: + result.append(Func(self.showWater)) + if 'fightBoost' in origAnimName: + animKey = 'fight0' + else: + animKey = 'fight1' + animAndSound = self.createAnimAndSoundIval(animKey) + result.append(animAndSound) + result.append(Func(self.hideWater)) + return result diff --git a/toontown/hood/HydrantOneAnimatedProp.py b/toontown/hood/HydrantOneAnimatedProp.py new file mode 100644 index 0000000..71f2bdd --- /dev/null +++ b/toontown/hood/HydrantOneAnimatedProp.py @@ -0,0 +1,32 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class HydrantOneAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('HydrantOneAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_ttc_hydrant_firstMoveArmUp1', 40 * PauseTimeMult), + 1: ('tt_a_ara_ttc_hydrant_firstMoveStruggle', 20 * PauseTimeMult), + 2: ('tt_a_ara_ttc_hydrant_firstMoveArmUp2', 10 * PauseTimeMult), + 3: ('tt_a_ara_ttc_hydrant_firstMoveJump', 8 * PauseTimeMult), + 4: ('tt_a_ara_ttc_hydrant_firstMoveJumpBalance', 6 * PauseTimeMult), + 5: ('tt_a_ara_ttc_hydrant_firstMoveArmUp3', 4 * PauseTimeMult), + 6: ('tt_a_ara_ttc_hydrant_firstMoveJumpSpin', 2 * PauseTimeMult)} + PhaseWeStartAnimating = 3 + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'hydrant', self.PhaseInfo, ToontownGlobals.HYDRANT_ZERO_HOLIDAY) + + def startIfNeeded(self): + try: + self.curPhase = self.getPhaseToRun() + if self.curPhase >= self.PhaseWeStartAnimating: + self.request('DoAnim') + except: + pass + + def handleNewPhase(self, newPhase): + if newPhase < self.PhaseWeStartAnimating: + self.request('Off') + else: + self.startIfNeeded() diff --git a/toontown/hood/HydrantTwoAnimatedProp.py b/toontown/hood/HydrantTwoAnimatedProp.py new file mode 100644 index 0000000..c94435e --- /dev/null +++ b/toontown/hood/HydrantTwoAnimatedProp.py @@ -0,0 +1,32 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class HydrantTwoAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('HydrantTwoAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_ttc_hydrant_firstMoveArmUp1', 40 * PauseTimeMult), + 1: ('tt_a_ara_ttc_hydrant_firstMoveStruggle', 20 * PauseTimeMult), + 2: ('tt_a_ara_ttc_hydrant_firstMoveArmUp2', 10 * PauseTimeMult), + 3: ('tt_a_ara_ttc_hydrant_firstMoveJump', 8 * PauseTimeMult), + 4: ('tt_a_ara_ttc_hydrant_firstMoveJumpBalance', 6 * PauseTimeMult), + 5: ('tt_a_ara_ttc_hydrant_firstMoveArmUp3', 4 * PauseTimeMult), + 6: ('tt_a_ara_ttc_hydrant_firstMoveJumpSpin', 2 * PauseTimeMult)} + PhaseWeStartAnimating = 5 + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'hydrant', self.PhaseInfo, ToontownGlobals.HYDRANT_ZERO_HOLIDAY) + + def startIfNeeded(self): + try: + self.curPhase = self.getPhaseToRun() + if self.curPhase >= self.PhaseWeStartAnimating: + self.request('DoAnim') + except: + pass + + def handleNewPhase(self, newPhase): + if newPhase < self.PhaseWeStartAnimating: + self.request('Off') + else: + self.startIfNeeded() diff --git a/toontown/hood/HydrantZeroAnimatedProp.py b/toontown/hood/HydrantZeroAnimatedProp.py new file mode 100644 index 0000000..98249ad --- /dev/null +++ b/toontown/hood/HydrantZeroAnimatedProp.py @@ -0,0 +1,17 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class HydrantZeroAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('HydrantZeroAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_ttc_hydrant_firstMoveArmUp1', 40 * PauseTimeMult), + 1: ('tt_a_ara_ttc_hydrant_firstMoveStruggle', 20 * PauseTimeMult), + 2: ('tt_a_ara_ttc_hydrant_firstMoveArmUp2', 10 * PauseTimeMult), + 3: ('tt_a_ara_ttc_hydrant_firstMoveJump', 8 * PauseTimeMult), + 4: ('tt_a_ara_ttc_hydrant_firstMoveJumpBalance', 6 * PauseTimeMult), + 5: ('tt_a_ara_ttc_hydrant_firstMoveArmUp3', 4 * PauseTimeMult), + 6: ('tt_a_ara_ttc_hydrant_firstMoveJumpSpin', 2 * PauseTimeMult)} + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'hydrant', self.PhaseInfo, ToontownGlobals.HYDRANT_ZERO_HOLIDAY) diff --git a/toontown/hood/InteractiveAnimatedProp.py b/toontown/hood/InteractiveAnimatedProp.py new file mode 100644 index 0000000..af0b93a --- /dev/null +++ b/toontown/hood/InteractiveAnimatedProp.py @@ -0,0 +1,508 @@ +import math +import random +import GenericAnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import Sequence, ActorInterval, Wait, Func, SoundInterval, Parallel +from direct.fsm import FSM +from direct.showbase.PythonUtil import weightedChoice +from pandac.PandaModules import TextNode, Vec3 +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil + +def clearPythonIvals(ival): + if hasattr(ival, 'function'): + ival.function = None + if hasattr(ival, 'pythonIvals'): + for oneIval in ival.pythonIvals: + clearPythonIvals(oneIval) + + ival.pythonIvals = [] + return + + +class InteractiveAnimatedProp(GenericAnimatedProp.GenericAnimatedProp, FSM.FSM): + ZoneToIdles = {} + ZoneToIdleIntoFightAnims = {} + ZoneToFightAnims = {} + ZoneToVictoryAnims = {} + ZoneToSadAnims = {} + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) + HpTextGenerator = TextNode('HpTextGenerator') + BattleCheerText = '+' + + def __init__(self, node, holidayId = -1): + FSM.FSM.__init__(self, 'InteractiveProp-%s' % str(node)) + self.holidayId = holidayId + self.numIdles = 0 + self.numFightAnims = 0 + self.idleInterval = None + self.battleCheerInterval = None + self.sadInterval = None + self.victoryInterval = None + self.lastIdleAnimName = '' + self.lastIdleTime = 0 + self.curIval = None + self.okToStartNextAnim = False + cellIndexStr = node.getTag('DNACellIndex') + self.cellIndex = ord(cellIndexStr) + self.origAnimNameToSound = {} + self.lastPlayingAnimPhase = 0 + self.buildingsMakingMeSad = set() + GenericAnimatedProp.GenericAnimatedProp.__init__(self, node) + return + + def delete(self): + self.exit() + GenericAnimatedProp.GenericAnimatedProp.delete(self) + self.idleInterval = None + self.battleCheerInterval = None + self.sadInterval = None + self.victoryInterval = None + return + + def getCellIndex(self): + return self.cellIndex + + def playBattleCheerAnim(self): + self.node.loop('battleCheer') + + def setupActor(self, node): + if self.hoodId in self.ZoneToIdles: + self.numIdles = len(self.ZoneToIdles[self.hoodId]) + if self.hoodId in self.ZoneToFightAnims: + self.numFightAnims = len(self.ZoneToFightAnims[self.hoodId]) + self.idleInterval = None + anim = node.getTag('DNAAnim') + self.trashcan = Actor.Actor(node, copy=0) + self.trashcan.reparentTo(node) + animDict = {} + animDict['anim'] = '%s/%s' % (self.path, anim) + for i in xrange(self.numIdles): + baseAnim = self.ZoneToIdles[self.hoodId][i] + if isinstance(baseAnim, tuple): + baseAnim = baseAnim[0] + animStr = self.path + '/' + baseAnim + animKey = 'idle%d' % i + animDict[animKey] = animStr + settleName = self.getSettleName(i) + if settleName: + settleStr = self.path + '/' + settleName + settleKey = 'settle%d' % i + animDict[settleKey] = settleStr + + for i in xrange(self.numFightAnims): + animStr = self.path + '/' + self.ZoneToFightAnims[self.hoodId][i] + animKey = 'fight%d' % i + animDict[animKey] = animStr + + if self.hoodId in self.ZoneToIdleIntoFightAnims: + animStr = self.path + '/' + self.ZoneToIdleIntoFightAnims[self.hoodId] + animKey = 'idleIntoFight' + animDict[animKey] = animStr + if self.hoodId in self.ZoneToIdleIntoFightAnims: + animStr = self.path + '/' + self.ZoneToVictoryAnims[self.hoodId] + animKey = 'victory' + animDict[animKey] = animStr + if self.hoodId in self.ZoneToSadAnims: + animStr = self.path + '/' + self.ZoneToSadAnims[self.hoodId] + animKey = 'sad' + animDict[animKey] = animStr + self.trashcan.loadAnims(animDict) + self.trashcan.pose('anim', 0) + self.node = self.trashcan + self.idleInterval = self.createIdleInterval() + self.battleCheerInterval = self.createBattleCheerInterval() + self.victoryInterval = self.createVictoryInterval() + self.sadInterval = self.createSadInterval() + return + + def createIdleInterval(self): + result = Sequence() + if self.numIdles >= 3: + numberOfAnimsAbove2 = self.numIdles - 2 + for rareIdle in xrange(2, self.numIdles): + for i in xrange(2): + result.append(ActorInterval(self.node, 'idle0')) + result.append(Wait(self.IdlePauseTime)) + result.append(ActorInterval(self.node, 'idle1')) + result.append(Wait(self.IdlePauseTime)) + + result.append(ActorInterval(self.node, 'idle%d' % rareIdle)) + result.append(Wait(self.IdlePauseTime)) + + else: + for i in xrange(self.numIdles): + result.append(ActorInterval(self.node, 'idle%d' % i)) + + self.notify.debug('idle interval=%s' % result) + return result + + def createBattleCheerText(self): + self.HpTextGenerator.setFont(ToontownGlobals.getSignFont()) + self.HpTextGenerator.setText(self.BattleCheerText) + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + r = 0 + g = 0 + b = 1 + a = 1 + self.HpTextGenerator.setTextColor(r, g, b, a) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = self.node.attachNewNode(self.hpTextNode) + self.hpText.setScale(1) + self.hpText.setBillboardPointEye() + self.hpText.setBin('fixed', 100) + self.hpText.setPos(0, 0, 4) + self.hpText.hide() + + def createBattleCheerInterval(self): + result = Sequence() + for i in xrange(self.numFightAnims): + animKey = 'fight%d' % i + animAndSoundIval = self.createAnimAndSoundIval(animKey) + origAnimName = self.node.getAnimFilename(animKey).split('/')[-1] + if self.hasOverrideIval(origAnimName): + result.append(self.getOverrideIval(origAnimName)) + elif self.hasSpecialIval(origAnimName): + result.append(Parallel(animAndSoundIval, self.getSpecialIval(origAnimName))) + else: + result.append(animAndSoundIval) + + self.createBattleCheerText() + battleCheerTextIval = Sequence(Func(self.hpText.show), self.hpText.posInterval(duration=4.0, pos=Vec3(0, 0, 7), startPos=(0, 0, 3)), Func(self.hpText.hide)) + ivalWithText = Parallel(battleCheerTextIval, result) + return ivalWithText + + def createSadInterval(self): + result = Sequence() + if self.hoodId in self.ZoneToSadAnims: + result = self.createAnimAndSoundIval('sad') + return result + + def hasSpecialIval(self, origAnimName): + return False + + def getSpecialIval(self, origAnimName): + return Sequence() + + def hasOverrideIval(self, origAnimName): + return False + + def getOverrideIval(self, origAnimName): + return Sequence() + + def createVictoryInterval(self): + result = Sequence() + if self.hoodId in self.ZoneToVictoryAnims: + animAndSoundIval = self.createAnimAndSoundIval('victory') + result.append(animAndSoundIval) + return result + + def enter(self): + GenericAnimatedProp.GenericAnimatedProp.enter(self) + if base.config.GetBool('props-buff-battles', True): + self.notify.debug('props buff battles is true') + if base.cr.newsManager.isHolidayRunning(self.holidayId): + self.notify.debug('holiday is running, doing idle interval') + self.node.stop() + self.node.pose('idle0', 0) + if base.config.GetBool('interactive-prop-random-idles', 1): + self.requestIdleOrSad() + else: + self.idleInterval.loop() + else: + self.notify.debug('holiday is NOT running, doing nothing') + self.node.stop() + self.node.pose('idle0', 0) + else: + self.notify.debug('props do not buff battles') + self.node.stop() + self.node.pose('idle0', 0) + + def exit(self): + self.okToStartNextAnim = False + self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim)) + GenericAnimatedProp.GenericAnimatedProp.exit(self) + self.request('Off') + + def requestIdleOrSad(self): + if not hasattr(self, 'node') or not self.node: + self.notify.warning("requestIdleOrSad returning hasattr(self,'node')=%s" % hasattr(self, 'node')) + return + if self.buildingsMakingMeSad: + self.request('Sad') + else: + self.request('DoIdleAnim') + + def enterDoIdleAnim(self): + self.notify.debug('enterDoIdleAnim numIdels=%d' % self.numIdles) + self.okToStartNextAnim = True + self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim)) + self.startNextIdleAnim() + + def exitDoIdleAnim(self): + self.notify.debug('exitDoIdlesAnim numIdles=%d' % self.numIdles) + self.okToStartNextAnim = False + self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim)) + self.calcLastIdleFrame() + self.clearCurIval() + + def calcLastIdleFrame(self): + if self.curIval and self.curIval.ivals: + firstIval = self.curIval.ivals[0] + if isinstance(firstIval, ActorInterval): + self.lastIdleFrame = firstIval.getCurrentFrame() + self.lastIdleAnimName = firstIval.animName + elif isinstance(firstIval, Parallel): + for testIval in firstIval.ivals: + if isinstance(firstIval, ActorInterval): + self.lastIdleTime = testIval.getT() + self.lastIdleAnimName = testIval.animName + break + + def chooseIdleAnimToRun(self): + result = self.numIdles - 1 + if base.config.GetBool('randomize-interactive-idles', True): + pairs = [] + for i in xrange(self.numIdles): + reversedChance = self.numIdles - i - 1 + pairs.append((math.pow(2, reversedChance), i)) + + sum = math.pow(2, self.numIdles) - 1 + result = weightedChoice(pairs, sum=sum) + self.notify.debug('chooseAnimToRun numIdles=%s pairs=%s result=%s' % (self.numIdles, pairs, result)) + else: + result = self.lastPlayingAnimPhase + 1 + if result >= len(self.ZoneToIdles[self.hoodId]): + result = 0 + return result + + def startNextIdleAnim(self): + self.notify.debug('startNextAnim self.okToStartNextAnim=%s' % self.okToStartNextAnim) + if not hasattr(self, 'node') or not self.node: + self.notify.warning("startNextIdleAnim returning hasattr(self,'node')=%s" % hasattr(self, 'node')) + return + self.curIval = None + if self.okToStartNextAnim: + self.notify.debug('got pass okToStartNextAnim') + whichAnim = self.chooseIdleAnimToRun() + if self.visId == localAvatar.zoneId: + self.notify.debug('whichAnim=%s' % whichAnim) + if __dev__: + self.notify.info('whichAnim=%s %s' % (whichAnim, self.getOrigIdleAnimName(whichAnim))) + self.lastPlayingAnimPhase = whichAnim + self.curIval = self.createIdleAnimSequence(whichAnim) + self.notify.debug('starting curIval of length %s' % self.curIval.getDuration()) + self.curIval.start() + else: + self.curIval = Wait(10) + self.notify.debug('false self.okToStartNextAnim=%s' % self.okToStartNextAnim) + return + + def createIdleAnimAndSoundInterval(self, whichIdleAnim, startingTime = 0): + animIval = self.node.actorInterval('idle%d' % whichIdleAnim, startTime=startingTime) + animIvalDuration = animIval.getDuration() + origAnimName = self.ZoneToIdles[self.hoodId][whichIdleAnim] + if isinstance(origAnimName, tuple): + origAnimName = origAnimName[0] + soundIval = self.createSoundInterval(origAnimName, animIvalDuration) + soundIvalDuration = soundIval.getDuration() + if self.hasSpecialIval(origAnimName): + specialIval = self.getSpecialIval(origAnimName) + idleAnimAndSound = Parallel(animIval, soundIval, specialIval) + else: + idleAnimAndSound = Parallel(animIval, soundIval) + return idleAnimAndSound + + def createIdleAnimSequence(self, whichIdleAnim): + dummyResult = Sequence(Wait(self.IdlePauseTime)) + if not hasattr(self, 'node') or not self.node: + self.notify.warning("createIdleAnimSequence returning dummyResult hasattr(self,'node')=%s" % hasattr(self, 'node')) + return dummyResult + idleAnimAndSound = self.createIdleAnimAndSoundInterval(whichIdleAnim) + result = Sequence(idleAnimAndSound, Wait(self.IdlePauseTime), Func(self.startNextIdleAnim)) + if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 2: + info = self.ZoneToIdles[self.hoodId][whichIdleAnim] + origAnimName = info[0] + minLoop = info[1] + maxLoop = info[2] + settleAnim = info[3] + minPauseTime = info[4] + maxPauseTime = info[5] + numberOfLoops = random.randrange(minLoop, maxLoop + 1) + pauseTime = random.randrange(minPauseTime, maxPauseTime + 1) + result = Sequence() + for i in xrange(numberOfLoops): + result.append(idleAnimAndSound) + + if self.getSettleName(whichIdleAnim): + result.append(self.node.actorInterval('settle%d' % whichIdleAnim)) + result.append(Wait(pauseTime)) + result.append(Func(self.startNextIdleAnim)) + return result + + def gotoFaceoff(self): + self.notify.debugStateCall(self) + if base.cr.newsManager.isHolidayRunning(self.holidayId): + self.request('Faceoff') + else: + self.notify.debug('not going to faceoff because holiday %d is not running' % self.holidayId) + + def gotoBattleCheer(self): + self.notify.debugStateCall(self) + if base.cr.newsManager.isHolidayRunning(self.holidayId): + self.request('BattleCheer') + else: + self.notify.debug('not going to battleCheer because holiday %d is not running' % self.holidayId) + + def gotoIdle(self): + self.notify.debugStateCall(self) + if base.cr.newsManager.isHolidayRunning(self.holidayId): + self.request('DoIdleAnim') + else: + self.notify.debug('not going to idle because holiday %d is not running' % self.holidayId) + + def gotoVictory(self): + self.notify.debugStateCall(self) + if base.cr.newsManager.isHolidayRunning(self.holidayId): + self.request('Victory') + else: + self.notify.debug('not going to victory because holiday %d is not running' % self.holidayId) + + def gotoSad(self, buildingDoId): + self.notify.debugStateCall(self) + self.buildingsMakingMeSad.add(buildingDoId) + if base.cr.newsManager.isHolidayRunning(self.holidayId): + self.request('Sad') + else: + self.notify.debug('not going to sad because holiday %d is not running' % self.holidayId) + + def buildingLiberated(self, buildingDoId): + self.buildingsMakingMeSad.discard(buildingDoId) + if not self.buildingsMakingMeSad: + self.gotoIdle() + + def enterFaceoff(self): + self.notify.debugStateCall(self) + self.curIval = self.createFaceoffInterval() + self.curIval.start() + + def exitFaceoff(self): + self.notify.debugStateCall(self) + self.curIval.pause() + self.curIval = None + return + + def calcWhichIdleAnim(self, animName): + result = 0 + info = self.ZoneToIdles[self.hoodId] + for index, curInfo in enumerate(info): + if isinstance(curInfo, tuple): + if curInfo[0] == animName: + result = index + break + elif isinstance(curInfo, str): + if curInfo == animName: + result = index + breal + + return result + + def createFaceoffInterval(self): + result = Sequence() + if self.lastIdleAnimName: + whichIdleAnim = self.calcWhichIdleAnim(self.lastIdleAnimName) + animAndSound = self.createIdleAnimAndSoundInterval(whichIdleAnim, self.lastIdleTime) + result.append(animAndSound) + idleIntoFightIval = self.createAnimAndSoundIval('idleIntoFight') + result.append(idleIntoFightIval) + result.append(Func(self.gotoBattleCheer)) + return result + + def enterBattleCheer(self): + self.notify.debugStateCall(self) + self.curIval = self.battleCheerInterval + if self.curIval: + self.curIval.loop() + + def exitBattleCheer(self): + self.notify.debugStateCall(self) + if self.curIval: + self.curIval.finish() + self.curIval = None + return + + def enterVictory(self): + self.notify.debugStateCall(self) + self.curIval = self.victoryInterval + if self.curIval: + self.curIval.loop() + + def exitVictory(self): + self.notify.debugStateCall(self) + if self.curIval: + self.curIval.finish() + self.curIval = None + return + + def enterSad(self): + self.notify.debugStateCall(self) + self.curIval = self.sadInterval + if self.curIval: + self.curIval.loop() + + def exitSad(self): + self.notify.debugStateCall(self) + if self.curIval: + self.curIval.finish() + self.curIval = None + return + + def getSettleName(self, whichIdleAnim): + result = None + if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 3: + result = self.ZoneToIdles[self.hoodId][whichIdleAnim][3] + return result + + def getOrigIdleAnimName(self, whichIdleAnim): + result = None + if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple): + result = self.ZoneToIdles[self.hoodId][whichIdleAnim][0] + else: + result = self.ZoneToIdles[self.hoodId][whichIdleAnim] + return result + + def createAnimAndSoundIval(self, animKey): + animIval = self.node.actorInterval(animKey) + animIvalDuration = animIval.getDuration() + origAnimName = self.node.getAnimFilename(animKey) + soundIval = self.createSoundInterval(origAnimName, animIvalDuration) + soundIvalDuration = soundIval.getDuration() + printFunc = Func(self.printAnimIfClose, animKey) + if self.hasSpecialIval(origAnimName): + specialIval = self.getSpecialIval(origAnimName) + idleAnimAndSound = Parallel(animIval, soundIval, specialIval) + if base.config.GetBool('interactive-prop-info', False): + idleAnimAndSound.append(printFunc) + else: + idleAnimAndSound = Parallel(animIval, soundIval) + if base.config.GetBool('interactive-prop-info', False): + idleAnimAndSound.append(printFunc) + return idleAnimAndSound + + def printAnimIfClose(self, animKey): + if base.config.GetBool('interactive-prop-info', False): + try: + animName = self.node.getAnimFilename(animKey) + baseAnimName = animName.split('/')[-1] + if localAvatar.zoneId == self.visId: + self.notify.info('playing %s' % baseAnimName) + except Exception, e: + self.notify.warning('Unknown error in printAnimIfClose, giving up:\n%s' % str(e)) + + def clearCurIval(self): + if self.curIval: + self.curIval.finish() + clearPythonIvals(self.curIval) + self.curIval = None + return diff --git a/toontown/hood/LawbotHQ.py b/toontown/hood/LawbotHQ.py new file mode 100644 index 0000000..588fc7e --- /dev/null +++ b/toontown/hood/LawbotHQ.py @@ -0,0 +1,34 @@ +import CogHood +from toontown.toonbase import ToontownGlobals +from toontown.coghq import LawbotCogHQLoader + +class LawbotHQ(CogHood.CogHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + CogHood.CogHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = ToontownGlobals.LawbotHQ + self.cogHQLoaderClass = LawbotCogHQLoader.LawbotCogHQLoader + self.storageDNAFile = None + self.skyFile = 'phase_9/models/cogHQ/cog_sky' + self.titleColor = (0.5, 0.5, 0.5, 1.0) + return + + def load(self): + CogHood.CogHood.load(self) + self.sky.hide() + self.parentFSM.getStateNamed('LawbotHQ').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('LawbotHQ').removeChild(self.fsm) + del self.cogHQLoaderClass + CogHood.CogHood.unload(self) + + def enter(self, *args): + CogHood.CogHood.enter(self, *args) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.LawbotHQCameraNear, ToontownGlobals.LawbotHQCameraFar) + + def exit(self): + localAvatar.setCameraFov(ToontownGlobals.DefaultCameraFov) + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + CogHood.CogHood.exit(self) diff --git a/toontown/hood/LawbotHQDataAI.py b/toontown/hood/LawbotHQDataAI.py new file mode 100644 index 0000000..2110383 --- /dev/null +++ b/toontown/hood/LawbotHQDataAI.py @@ -0,0 +1,70 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedLawOfficeElevatorExtAI +from toontown.coghq import DistributedCogHQDoorAI +from toontown.building import DistributedDoorAI +from toontown.building import DoorTypes +from toontown.coghq import LobbyManagerAI +from toontown.building import DistributedBossElevatorAI +from toontown.suit import DistributedLawbotBossAI +from toontown.building import DistributedCJElevatorAI +from toontown.building import FADoorCodes +from toontown.building import DistributedBoardingPartyAI + +class LawbotHQDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('LawbotHQDataAI') + + def __init__(self, air, zoneId=None): + self.notify.debug('__init__: zoneId:%s' % zoneId) + hoodId = ToontownGlobals.LawbotHQ + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + + def makeOfficeElevator(index, antiShuffle=0, minLaff=0): + destZone = (ToontownGlobals.LawbotStageIntA, ToontownGlobals.LawbotStageIntB, ToontownGlobals.LawbotStageIntC, ToontownGlobals.LawbotStageIntD)[index] + elev = DistributedLawOfficeElevatorExtAI.DistributedLawOfficeElevatorExtAI(self.air, self.air.lawMgr, destZone, index, antiShuffle=0, minLaff=minLaff) + elev.generateWithRequired(ToontownGlobals.LawbotOfficeExt) + self.addDistObj(elev) + return elev.doId + + mins = ToontownGlobals.FactoryLaffMinimums[2] + officeId0 = makeOfficeElevator(0, 0, mins[0]) + officeId1 = makeOfficeElevator(1, 0, mins[1]) + officeId2 = makeOfficeElevator(2, 0, mins[2]) + officeId3 = makeOfficeElevator(3, 0, mins[3]) + self.lobbyMgr = LobbyManagerAI.LobbyManagerAI(self.air, DistributedLawbotBossAI.DistributedLawbotBossAI) + self.lobbyMgr.generateWithRequired(ToontownGlobals.LawbotLobby) + self.addDistObj(self.lobbyMgr) + self.lobbyElevator = DistributedCJElevatorAI.DistributedCJElevatorAI(self.air, self.lobbyMgr, ToontownGlobals.LawbotLobby, antiShuffle=1) + self.lobbyElevator.generateWithRequired(ToontownGlobals.LawbotLobby) + self.addDistObj(self.lobbyElevator) + if simbase.config.GetBool('want-boarding-groups', 1): + self.boardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, [self.lobbyElevator.doId], 8) + self.boardingParty.generateWithRequired(ToontownGlobals.LawbotLobby) + + def makeDoor(destinationZone, intDoorIndex, extDoorIndex, lock=0): + intDoor = DistributedCogHQDoorAI.DistributedCogHQDoorAI(self.air, 0, DoorTypes.INT_COGHQ, self.canonicalHoodId, doorIndex=intDoorIndex, lockValue=lock) + intDoor.zoneId = destinationZone + extDoor = DistributedCogHQDoorAI.DistributedCogHQDoorAI(self.air, 0, DoorTypes.EXT_COGHQ, destinationZone, doorIndex=extDoorIndex, lockValue=lock) + extDoor.setOtherDoor(intDoor) + intDoor.setOtherDoor(extDoor) + intDoor.generateWithRequired(destinationZone) + intDoor.sendUpdate('setDoorIndex', [intDoor.getDoorIndex()]) + self.addDistObj(intDoor) + extDoor.generateWithRequired(self.canonicalHoodId) + extDoor.sendUpdate('setDoorIndex', [extDoor.getDoorIndex()]) + self.addDistObj(extDoor) + + makeDoor(ToontownGlobals.LawbotLobby, 0, 1, FADoorCodes.LB_DISGUISE_INCOMPLETE) + makeDoor(ToontownGlobals.LawbotOfficeExt, 0, 0) + officeIdList = [ + officeId0, officeId1, officeId2, officeId3] + if simbase.config.GetBool('want-boarding-parties', 1): + self.officeBoardingParty = DistributedBoardingPartyAI.DistributedBoardingPartyAI(self.air, officeIdList, 4) + self.officeBoardingParty.generateWithRequired(ToontownGlobals.LawbotOfficeExt) diff --git a/toontown/hood/MMHood.py b/toontown/hood/MMHood.py new file mode 100644 index 0000000..16549fc --- /dev/null +++ b/toontown/hood/MMHood.py @@ -0,0 +1,35 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.town import MMTownLoader +from toontown.safezone import MMSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * + +class MMHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = MinniesMelodyland + self.townLoaderClass = MMTownLoader.MMTownLoader + self.safeZoneLoaderClass = MMSafeZoneLoader.MMSafeZoneLoader + self.storageDNAFile = 'phase_6/dna/storage_MM.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_6/dna/winter_storage_MM.dna'], + WACKY_WINTER_DECORATIONS: ['phase_6/dna/winter_storage_MM.dna'], + HALLOWEEN_PROPS: ['phase_6/dna/halloween_props_storage_MM.dna'], + SPOOKY_PROPS: ['phase_6/dna/halloween_props_storage_MM.dna']} + self.skyFile = 'phase_6/models/props/MM_sky' + self.spookySkyFile = 'phase_6/models/props/MM_sky' + self.titleColor = (1.0, 0.5, 0.5, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('MMHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('MMHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + + def exit(self): + ToonHood.ToonHood.exit(self) diff --git a/toontown/hood/MMHoodDataAI.py b/toontown/hood/MMHoodDataAI.py new file mode 100644 index 0000000..2173597 --- /dev/null +++ b/toontown/hood/MMHoodDataAI.py @@ -0,0 +1,30 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.safezone import DistributedTrolleyAI +from toontown.safezone import MMTreasurePlannerAI +from toontown.classicchars import DistributedMinnieAI +from toontown.safezone import DistributedMMPianoAI + +class MMHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('MMHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.MinniesMelodyland + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + trolley.generateWithRequired(self.zoneId) + trolley.start() + self.addDistObj(trolley) + self.treasurePlanner = MMTreasurePlannerAI.MMTreasurePlannerAI(self.zoneId) + self.treasurePlanner.start() + self.classicChar = DistributedMinnieAI.DistributedMinnieAI(self.air) + self.classicChar.generateWithRequired(self.zoneId) + self.classicChar.start() + self.addDistObj(self.classicChar) diff --git a/toontown/hood/MailboxInteractiveProp.py b/toontown/hood/MailboxInteractiveProp.py new file mode 100644 index 0000000..7d6bd11 --- /dev/null +++ b/toontown/hood/MailboxInteractiveProp.py @@ -0,0 +1,202 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Func +from toontown.hood import InteractiveAnimatedProp +from toontown.hood import GenericAnimatedProp +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals, TTLocalizer + +class MailboxInteractiveProp(InteractiveAnimatedProp.InteractiveAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('MailboxInteractiveProp') + BattleCheerText = TTLocalizer.InteractivePropTrackBonusTerms[ToontownBattleGlobals.THROW_TRACK] + ZoneToIdles = {ToontownGlobals.ToontownCentral: (('tt_a_ara_ttc_mailbox_idle0', + 3, + 10, + 'tt_a_ara_ttc_mailbox_idle0settle', + 3, + 10), + ('tt_a_ara_ttc_mailbox_idleTake2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_mailbox_idleLook1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_mailbox_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DonaldsDock: (('tt_a_ara_dod_mailbox_idle0', + 3, + 10, + 'tt_a_ara_dod_mailbox_idle0settle', + 3, + 10), + ('tt_a_ara_dod_mailbox_idle2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dod_mailbox_idle1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dod_mailbox_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DaisyGardens: (('tt_a_ara_dga_mailbox_idle0', + 3, + 10, + 'tt_a_ara_dga_mailbox_idle0settle', + 3, + 10), + ('tt_a_ara_dga_mailbox_idleTake1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dga_mailbox_idleLook2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dga_mailbox_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.MinniesMelodyland: (('tt_a_ara_mml_mailbox_idle0', + 3, + 10, + 'tt_a_ara_mml_mailbox_idle0settle', + 3, + 10), + ('tt_a_ara_mml_mailbox_idleTake1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_mml_mailbox_idleLook2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_mml_mailbox_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.TheBrrrgh: (('tt_a_ara_tbr_mailbox_idleShiver1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_mailbox_idleSneeze2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_mailbox_idleSpin0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_mailbox_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DonaldsDreamland: (('tt_a_ara_ddl_mailbox_idleSleep0', + 3, + 10, + None, + 0, + 0), + ('tt_a_ara_ddl_mailbox_idleShake2', + 1, + 1, + None, + 0, + 0), + ('tt_a_ara_ddl_mailbox_idleSnore1', + 1, + 1, + None, + 0, + 0), + ('tt_a_ara_ddl_mailbox_idleAwesome3', + 1, + 1, + None, + 0, + 0))} + ZoneToIdleIntoFightAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_mailbox_idleIntoFight', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_mailbox_idleIntoFight', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_mailbox_idleIntoFight', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_mailbox_idleIntoFight', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_mailbox_idleIntoFight', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_mailbox_idleIntoFight'} + ZoneToVictoryAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_mailbox_victoryDance', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_mailbox_victoryDance', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_mailbox_victoryDance', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_mailbox_victoryDance', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_mailbox_victoryDance', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_mailbox_victoryDance'} + ZoneToSadAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_mailbox_fightSad', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_mailbox_fightSad', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_mailbox_fightSad', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_mailbox_fightSad', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_mailbox_fightSad', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_mailbox_fightSad'} + ZoneToFightAnims = {ToontownGlobals.ToontownCentral: ('tt_a_ara_ttc_mailbox_fightBoost', 'tt_a_ara_ttc_mailbox_fightCheer', 'tt_a_ara_ttc_mailbox_fightIdle'), + ToontownGlobals.DonaldsDock: ('tt_a_ara_dod_mailbox_fightBoost', 'tt_a_ara_dod_mailbox_fightCheer', 'tt_a_ara_dod_mailbox_fightIdle'), + ToontownGlobals.DaisyGardens: ('tt_a_ara_dga_mailbox_fightBoost', 'tt_a_ara_dga_mailbox_fightCheer', 'tt_a_ara_dga_mailbox_fightIdle'), + ToontownGlobals.MinniesMelodyland: ('tt_a_ara_mml_mailbox_fightBoost', 'tt_a_ara_mml_mailbox_fightCheer', 'tt_a_ara_mml_mailbox_fightIdle'), + ToontownGlobals.TheBrrrgh: ('tt_a_ara_tbr_mailbox_fightBoost', 'tt_a_ara_tbr_mailbox_fightCheer', 'tt_a_ara_tbr_mailbox_fightIdle'), + ToontownGlobals.DonaldsDreamland: ('tt_a_ara_ddl_mailbox_fightBoost', 'tt_a_ara_ddl_mailbox_fightCheer', 'tt_a_ara_ddl_mailbox_fightIdle')} + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) + + def __init__(self, node): + InteractiveAnimatedProp.InteractiveAnimatedProp.__init__(self, node, ToontownGlobals.MAILBOXES_BUFF_BATTLES) + + def setupActor(self, node): + self.pieActor = Actor.Actor('phase_5/models/char/tt_r_prp_ext_piePackage', {'fightBoost': 'phase_5/models/char/tt_a_prp_ext_piePackage_fightBoost'}) + self.pieActor.reparentTo(self.node) + self.pieActor.hide() + InteractiveAnimatedProp.InteractiveAnimatedProp.setupActor(self, node) + + def hasSpecialIval(self, origAnimName): + result = False + if 'fightBoost' in origAnimName: + result = True + return result + + def getSpecialIval(self, origAnimName): + result = Sequence() + if 'fightBoost' in origAnimName: + result.append(Func(self.pieActor.show)) + result.append(self.pieActor.actorInterval('fightBoost')) + result.append(Func(self.pieActor.hide)) + return result diff --git a/toontown/hood/MailboxOneAnimatedProp.py b/toontown/hood/MailboxOneAnimatedProp.py new file mode 100644 index 0000000..26ca187 --- /dev/null +++ b/toontown/hood/MailboxOneAnimatedProp.py @@ -0,0 +1,32 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class MailboxOneAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('MailboxOneAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin1', 40 * PauseTimeMult), + 1: (('tt_a_ara_dod_mailbox_firstMoveStruggle', 'tt_a_ara_dod_mailbox_firstMoveJump'), 20 * PauseTimeMult), + 2: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin2', 10 * PauseTimeMult), + 3: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin3', 8 * PauseTimeMult), + 4: ('tt_a_ara_dod_mailbox_firstMoveJumpSummersault', 6 * PauseTimeMult), + 5: ('tt_a_ara_dod_mailbox_firstMoveJumpFall', 4 * PauseTimeMult), + 6: ('tt_a_ara_dod_mailbox_firstMoveJump3Summersaults', 2 * PauseTimeMult)} + PhaseWeStartAnimating = 3 + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'mailbox', self.PhaseInfo, ToontownGlobals.MAILBOX_ZERO_HOLIDAY) + + def startIfNeeded(self): + try: + self.curPhase = self.getPhaseToRun() + if self.curPhase >= self.PhaseWeStartAnimating: + self.request('DoAnim') + except: + pass + + def handleNewPhase(self, newPhase): + if newPhase < self.PhaseWeStartAnimating: + self.request('Off') + else: + self.startIfNeeded() diff --git a/toontown/hood/MailboxTwoAnimatedProp.py b/toontown/hood/MailboxTwoAnimatedProp.py new file mode 100644 index 0000000..8c2472a --- /dev/null +++ b/toontown/hood/MailboxTwoAnimatedProp.py @@ -0,0 +1,32 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class MailboxTwoAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('MailboxTwoAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin1', 40 * PauseTimeMult), + 1: (('tt_a_ara_dod_mailbox_firstMoveStruggle', 'tt_a_ara_dod_mailbox_firstMoveJump'), 20 * PauseTimeMult), + 2: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin2', 10 * PauseTimeMult), + 3: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin3', 8 * PauseTimeMult), + 4: ('tt_a_ara_dod_mailbox_firstMoveJumpSummersault', 6 * PauseTimeMult), + 5: ('tt_a_ara_dod_mailbox_firstMoveJumpFall', 4 * PauseTimeMult), + 6: ('tt_a_ara_dod_mailbox_firstMoveJump3Summersaults', 2 * PauseTimeMult)} + PhaseWeStartAnimating = 5 + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'mailbox', self.PhaseInfo, ToontownGlobals.MAILBOX_ZERO_HOLIDAY) + + def startIfNeeded(self): + try: + self.curPhase = self.getPhaseToRun() + if self.curPhase >= self.PhaseWeStartAnimating: + self.request('DoAnim') + except: + pass + + def handleNewPhase(self, newPhase): + if newPhase < self.PhaseWeStartAnimating: + self.request('Off') + else: + self.startIfNeeded() diff --git a/toontown/hood/MailboxZeroAnimatedProp.py b/toontown/hood/MailboxZeroAnimatedProp.py new file mode 100644 index 0000000..2ccdf35 --- /dev/null +++ b/toontown/hood/MailboxZeroAnimatedProp.py @@ -0,0 +1,17 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class MailboxZeroAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('MailboxZeroAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin1', 40 * PauseTimeMult), + 1: (('tt_a_ara_dod_mailbox_firstMoveStruggle', 'tt_a_ara_dod_mailbox_firstMoveJump'), 20 * PauseTimeMult), + 2: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin2', 10 * PauseTimeMult), + 3: ('tt_a_ara_dod_mailbox_firstMoveFlagSpin3', 8 * PauseTimeMult), + 4: ('tt_a_ara_dod_mailbox_firstMoveJumpSummersault', 6 * PauseTimeMult), + 5: ('tt_a_ara_dod_mailbox_firstMoveJumpFall', 4 * PauseTimeMult), + 6: ('tt_a_ara_dod_mailbox_firstMoveJump3Summersaults', 2 * PauseTimeMult)} + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'mailbox', self.PhaseInfo, ToontownGlobals.MAILBOX_ZERO_HOLIDAY) diff --git a/toontown/hood/OZHood.py b/toontown/hood/OZHood.py new file mode 100644 index 0000000..0bc31eb --- /dev/null +++ b/toontown/hood/OZHood.py @@ -0,0 +1,87 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.safezone import OZSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * +from toontown.racing import DistributedVehicle +import SkyUtil + +class OZHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = OutdoorZone + self.safeZoneLoaderClass = OZSafeZoneLoader.OZSafeZoneLoader + self.storageDNAFile = 'phase_6/dna/storage_OZ.dna' + self.holidayStorageDNADict = {HALLOWEEN_PROPS: ['phase_6/dna/halloween_props_storage_OZ.dna'], + SPOOKY_PROPS: ['phase_6/dna/halloween_props_storage_OZ.dna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.titleColor = (1.0, 0.5, 0.4, 1.0) + self.whiteFogColor = Vec4(0.95, 0.95, 0.95, 1) + self.underwaterFogColor = Vec4(0.0, 0.0, 0.6, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('OZHood').addChild(self.fsm) + self.fog = Fog('OZFog') + + def unload(self): + self.parentFSM.getStateNamed('OZHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + base.camLens.setNearFar(SpeedwayCameraNear, SpeedwayCameraFar) + + def exit(self): + base.camLens.setNearFar(DefaultCameraNear, DefaultCameraFar) + ToonHood.ToonHood.exit(self) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SkyUtil.startCloudSky(self) + + def setUnderwaterFog(self): + if base.wantFog: + self.fog.setColor(self.underwaterFogColor) + self.fog.setLinearRange(0.1, 100.0) + render.setFog(self.fog) + self.sky.setFog(self.fog) + + def setWhiteFog(self): + if base.wantFog: + self.fog.setColor(self.whiteFogColor) + self.fog.setLinearRange(0.0, 400.0) + render.clearFog() + render.setFog(self.fog) + self.sky.clearFog() + self.sky.setFog(self.fog) + + def setNoFog(self): + if base.wantFog: + render.clearFog() + self.sky.clearFog() + + def startSpookySky(self): + if self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) diff --git a/toontown/hood/OZHoodDataAI.py b/toontown/hood/OZHoodDataAI.py new file mode 100644 index 0000000..4a755bf --- /dev/null +++ b/toontown/hood/OZHoodDataAI.py @@ -0,0 +1,235 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI, ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.safezone import OZTreasurePlannerAI +from toontown.racing import DistributedStartingBlockAI +from pandac.PandaModules import * +from toontown.racing.RaceGlobals import * +from toontown.classicchars import DistributedGoofySpeedwayAI +from toontown.safezone import DistributedPicnicBasketAI +from toontown.classicchars import DistributedChipAI +from toontown.classicchars import DistributedDaleAI +from toontown.distributed import DistributedTimerAI +import string +from toontown.safezone import DistributedPicnicTableAI +from toontown.safezone import DistributedChineseCheckersAI +from toontown.safezone import DistributedCheckersAI +if __debug__: + import pdb + +class OZHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('OZHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.OutdoorZone + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + self.classicChars = [] + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + if simbase.air.config.GetBool('create-chip-and-dale', 1): + chip = DistributedChipAI.DistributedChipAI(self.air) + chip.generateWithRequired(self.zoneId) + chip.start() + self.addDistObj(chip) + self.classicChars.append(chip) + dale = DistributedDaleAI.DistributedDaleAI(self.air, chip.doId) + dale.generateWithRequired(self.zoneId) + dale.start() + self.addDistObj(dale) + self.classicChars.append(dale) + chip.setDaleId(dale.doId) + self.treasurePlanner = OZTreasurePlannerAI.OZTreasurePlannerAI(self.zoneId) + self.treasurePlanner.start() + self.timer = DistributedTimerAI.DistributedTimerAI(self.air) + self.timer.generateWithRequired(self.zoneId) + self.createPicnicTables() + if simbase.config.GetBool('want-game-tables', 0): + self.createGameTables() + + def cleanup(self): + self.timer.delete() + taskMgr.removeTasksMatching(str(self) + '_leaderBoardSwitch') + for board in self.leaderBoards: + board.delete() + + del self.leaderBoards + + def createLeaderBoards(self): + self.leaderBoards = [] + dnaStore = DNAStorage() + dnaData = simbase.air.loadDNAFileAI(dnaStore, simbase.air.lookupDNAFileName('goofy_speedway_sz.dna')) + if isinstance(dnaData, DNAData): + self.leaderBoards = self.air.findLeaderBoards(dnaData, self.zoneId) + for distObj in self.leaderBoards: + if distObj: + if distObj.getName().count('city'): + type = 'city' + else: + if distObj.getName().count('stadium'): + type = 'stadium' + else: + if distObj.getName().count('country'): + type = 'country' + for subscription in LBSubscription[type]: + distObj.subscribeTo(subscription) + + self.addDistObj(distObj) + + def __cycleLeaderBoards(self, task=None): + messenger.send('GS_LeaderBoardSwap') + taskMgr.doMethodLater(self.cycleDuration, self.__cycleLeaderBoards, str(self) + '_leaderBoardSwitch') + + def createStartingBlocks(self): + self.racingPads = [] + self.viewingPads = [] + self.viewingBlocks = [] + self.startingBlocks = [] + self.foundRacingPadGroups = [] + self.foundViewingPadGroups = [] + self.golfKartPads = [] + self.golfKartPadGroups = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundRacingPads, foundRacingPadGroups = self.air.findRacingPads(dnaData, zoneId, area, overrideDNAZone=True) + foundViewingPads, foundViewingPadGroups = self.air.findRacingPads(dnaData, zoneId, area, type='viewing_pad', overrideDNAZone=True) + self.racingPads += foundRacingPads + self.foundRacingPadGroups += foundRacingPadGroups + self.viewingPads += foundViewingPads + self.foundViewingPadGroups += foundViewingPadGroups + + self.startingBlocks = [] + for dnaGroup, distRacePad in zip(self.foundRacingPadGroups, self.racingPads): + startingBlocks = self.air.findStartingBlocks(dnaGroup, distRacePad) + self.startingBlocks += startingBlocks + for startingBlock in startingBlocks: + distRacePad.addStartingBlock(startingBlock) + + for distObj in self.startingBlocks: + self.addDistObj(distObj) + + for dnaGroup, distViewPad in zip(self.foundViewingPadGroups, self.viewingPads): + startingBlocks = self.air.findStartingBlocks(dnaGroup, distViewPad) + for viewingBlock in self.viewingBlocks: + distViewPad.addStartingBlock(viewingBlocks) + + for distObj in self.viewingBlocks: + self.addDistObj(distObj) + + for viewPad in self.viewingPads: + self.addDistObj(viewPad) + + for racePad in self.racingPads: + racePad.request('WaitEmpty') + self.addDistObj(racePad) + + return + + def findAndCreateGameTables(self, dnaGroup, zoneId, area, overrideDNAZone = 0, type = 'game_table'): + picnicTables = [] + picnicTableGroups = [] + if isinstance(dnaGroup, DNAGroup) and string.find(dnaGroup.getName(), type) >= 0: + if type == 'game_table': + nameInfo = dnaGroup.getName().split('_') + pos = Point3(0, 0, 0) + hpr = Point3(0, 0, 0) + for i in range(dnaGroup.getNumChildren()): + childDnaGroup = dnaGroup.at(i) + if string.find(childDnaGroup.getName(), 'game_table') >= 0: + pos = childDnaGroup.getPos() + hpr = childDnaGroup.getHpr() + break + + picnicTable = DistributedPicnicTableAI.DistributedPicnicTableAI(self.air, zoneId, nameInfo[2], pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + picnicTables.append(picnicTable) + else: + if isinstance(dnaGroup, DNAVisGroup) and not overrideDNAZone: + zoneId = ZoneUtil.getTrueZoneId(int(dnaGroup.getName().split(':')[0]), zoneId) + for i in range(dnaGroup.getNumChildren()): + childPicnicTables = self.findAndCreateGameTables(dnaGroup.at(i), zoneId, area, overrideDNAZone, type) + picnicTables += childPicnicTables + + return picnicTables + + def findAndCreatePicnicTables(self, dnaGroup, zoneId, area, overrideDNAZone = 0, type = 'picnic_table'): + picnicTables = [] + picnicTableGroups = [] + if isinstance(dnaGroup, DNAGroup) and string.find(dnaGroup.getName(), type) >= 0: + if type == 'picnic_table': + nameInfo = dnaGroup.getName().split('_') + pos = Point3(0, 0, 0) + hpr = Point3(0, 0, 0) + for i in range(dnaGroup.getNumChildren()): + childDnaGroup = dnaGroup.at(i) + if string.find(childDnaGroup.getName(), 'picnic_table') >= 0: + pos = childDnaGroup.getPos() + hpr = childDnaGroup.getHpr() + break + + picnicTable = DistributedPicnicBasketAI.DistributedPicnicBasketAI(self.air, nameInfo[2], pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + picnicTable.generateWithRequired(zoneId) + picnicTables.append(picnicTable) + else: + if isinstance(dnaGroup, DNAVisGroup) and not overrideDNAZone: + zoneId = ZoneUtil.getTrueZoneId(int(dnaGroup.getName().split(':')[0]), zoneId) + for i in range(dnaGroup.getNumChildren()): + childPicnicTables = self.findAndCreatePicnicTables(dnaGroup.at(i), zoneId, area, overrideDNAZone, type) + picnicTables += childPicnicTables + + return picnicTables + + def createGameTables(self): + self.gameTables = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundTables = self.findAndCreateGameTables(dnaData, zoneId, area, overrideDNAZone=True) + self.gameTables += foundTables + + for picnicTable in self.gameTables: + self.addDistObj(picnicTable) + + return + + def createPicnicTables(self): + self.picnicTables = [] + for zone in self.air.zoneTable[self.canonicalHoodId]: + zoneId = ZoneUtil.getTrueZoneId(zone[0], self.zoneId) + dnaData = self.air.dnaDataMap.get(zone[0], None) + if isinstance(dnaData, DNAData): + area = ZoneUtil.getCanonicalZoneId(zoneId) + foundTables = self.findAndCreatePicnicTables(dnaData, zoneId, area, overrideDNAZone=True) + self.picnicTables += foundTables + + for picnicTable in self.picnicTables: + picnicTable.start() + self.addDistObj(picnicTable) + + return + + def findStartingBlocks(self, dnaRacingPadGroup, distRacePad): + startingBlocks = [] + for i in range(dnaRacingPadGroup.getNumChildren()): + dnaGroup = dnaRacingPadGroup.at(i) + if string.find(dnaGroup.getName(), 'starting_block') >= 0: + padLocation = dnaGroup.getName().split('_')[2] + pos = dnaGroup.getPos() + hpr = dnaGroup.getHpr() + if isinstance(distRacePad, DistributedRacePadAI): + sb = DistributedStartingBlockAI(self, distRacePad, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], int(padLocation)) + else: + sb = DistributedViewingBlockAI(self, distRacePad, pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], int(padLocation)) + sb.generateWithRequired(distRacePad.zoneId) + startingBlocks.append(sb) + else: + self.notify.debug('Found dnaGroup that is not a starting_block under a race pad group') + + return startingBlocks diff --git a/toontown/hood/PartyHood.py b/toontown/hood/PartyHood.py new file mode 100644 index 0000000..c5226a7 --- /dev/null +++ b/toontown/hood/PartyHood.py @@ -0,0 +1,120 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task +from pandac.PandaModules import * +from otp.avatar import DistributedAvatar +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.minigame import Purchase +from toontown.parties import PartyLoader +from toontown.parties import PartyGlobals +from toontown.hood import SkyUtil +from toontown.hood import Hood +from toontown.hood import ZoneUtil + +class PartyHood(Hood.Hood): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyHood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.fsm = ClassicFSM.ClassicFSM('Hood', [State.State('start', self.enterStart, self.exitStart, ['safeZoneLoader']), + State.State('safeZoneLoader', self.enterSafeZoneLoader, self.exitSafeZoneLoader, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['safeZoneLoader']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'start', 'final') + self.fsm.enterInitialState() + self.id = PartyHood + self.safeZoneLoaderClass = PartyLoader.PartyLoader + self.partyActivityDoneEvent = 'partyActivityDone' + self.storageDNAFile = 'phase_13/dna/storage_party_sz.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_5.5/dna/winter_storage_estate.dna'], + WACKY_WINTER_DECORATIONS: ['phase_5.5/dna/winter_storage_estate.dna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.popupInfo = None + return + + def load(self): + Hood.Hood.load(self) + + def unload(self): + del self.safeZoneLoaderClass + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + Hood.Hood.unload(self) + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + self.accept(PartyGlobals.KICK_TO_PLAYGROUND_EVENT, self.kickToPlayground) + self.fsm.request(requestStatus['loader'], [requestStatus]) + + def exit(self): + if self.loader: + self.loader.exit() + self.loader.unload() + del self.loader + Hood.Hood.exit(self) + + def kickToPlayground(self, retCode): + if retCode == 0: + msg = TTLocalizer.PartyOverWarningNoName + if hasattr(base, 'distributedParty') and base.distributedParty: + name = base.distributedParty.hostName + msg = TTLocalizer.PartyOverWarningWithName % TTLocalizer.GetPossesive(name) + self.__popupKickoutMessage(msg) + base.localAvatar.setTeleportAvailable(0) + if retCode == 1: + zoneId = base.localAvatar.lastHood + self.doneStatus = {'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': zoneId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + messenger.send(self.doneEvent) + return + + def __popupKickoutMessage(self, msg): + if self.popupInfo != None: + self.popupInfo.destroy() + self.popupInfo = 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.popupInfo = DirectFrame(parent=hidden, relief=None, state='normal', text=msg, frameSize=(-1, 1, -1, 1), text_wordwrap=10, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(0.88, 1, 0.75), geom_pos=(0, 0, -.08), text_scale=0.08, text_pos=(0, 0.1)) + DirectButton(self.popupInfo, image=okButtonImage, relief=None, text=TTLocalizer.EstatePopupOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.0, 0.0, -0.3), command=self.__handleKickoutOk) + buttons.removeNode() + self.popupInfo.reparentTo(aspect2d) + return + + def __handleKickoutOk(self): + self.popupInfo.reparentTo(hidden) + + def handlePartyActivityDone(self): + return None + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + self.loader = self.safeZoneLoaderClass(self, self.fsm.getStateNamed('safeZoneLoader'), self.loaderDoneEvent) + self.loader.load() + + def spawnTitleText(self, zoneId): + pass + + def hideTitleTextTask(self, task): + pass + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + SkyUtil.startCloudSky(self) + if base.cloudPlatformsEnabled: + self.loader.startCloudPlatforms() + + def stopSky(self): + Hood.Hood.stopSky(self) + self.loader.stopCloudPlatforms() diff --git a/toontown/hood/PetShopFishAnimatedProp.py b/toontown/hood/PetShopFishAnimatedProp.py new file mode 100644 index 0000000..c4cab52 --- /dev/null +++ b/toontown/hood/PetShopFishAnimatedProp.py @@ -0,0 +1,28 @@ +import AnimatedProp +from direct.actor import Actor +from direct.interval.IntervalGlobal import * + +class PetShopFishAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + parent = node.getParent() + self.fish = Actor.Actor(node, copy=0) + self.fish.reparentTo(parent) + self.fish.loadAnims({'swim': 'phase_4/models/props/exteriorfish-swim'}) + self.fish.pose('swim', 0) + self.node = self.fish + + def delete(self): + AnimatedProp.AnimatedProp.delete(self) + self.fish.cleanup() + del self.fish + del self.node + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.fish.loop('swim') + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + self.fish.stop() diff --git a/toontown/hood/Place.py b/toontown/hood/Place.py new file mode 100644 index 0000000..55cb3ba --- /dev/null +++ b/toontown/hood/Place.py @@ -0,0 +1,927 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.showbase.PythonUtil import PriorityCallbacks +from toontown.safezone import PublicWalk +from toontown.launcher import DownloadForceAcknowledge +import TrialerForceAcknowledge +import ZoneUtil +from toontown.friends import FriendsListManager +from toontown.toonbase import ToontownGlobals +from toontown.toon.Toon import teleportDebug +from toontown.estate import HouseGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +from otp.avatar import Emote +from otp.avatar.Avatar import teleportNotify +from direct.task import Task +import QuietZoneState +from toontown.distributed import ToontownDistrictStats + +class Place(StateData.StateData, FriendsListManager.FriendsListManager): + notify = DirectNotifyGlobal.directNotify.newCategory('Place') + + def __init__(self, loader, doneEvent): + StateData.StateData.__init__(self, doneEvent) + FriendsListManager.FriendsListManager.__init__(self) + self.loader = loader + self.dfaDoneEvent = 'dfaDoneEvent' + self.trialerFADoneEvent = 'trialerFADoneEvent' + self.zoneId = None + self.trialerFA = None + self._tiToken = None + self._leftQuietZoneLocalCallbacks = PriorityCallbacks() + self._leftQuietZoneSubframeCall = None + self._setZoneCompleteLocalCallbacks = PriorityCallbacks() + self._setZoneCompleteSubframeCall = None + return + + def load(self): + StateData.StateData.load(self) + FriendsListManager.FriendsListManager.load(self) + self.walkDoneEvent = 'walkDone' + self.walkStateData = PublicWalk.PublicWalk(self.fsm, self.walkDoneEvent) + self.walkStateData.load() + self._tempFSM = self.fsm + + def unload(self): + StateData.StateData.unload(self) + FriendsListManager.FriendsListManager.unload(self) + self.notify.info('Unloading Place (%s). Fsm in %s' % (self.zoneId, self._tempFSM.getCurrentState().getName())) + if self._leftQuietZoneSubframeCall: + self._leftQuietZoneSubframeCall.cleanup() + self._leftQuietZoneSubframeCall = None + if self._setZoneCompleteSubframeCall: + self._setZoneCompleteSubframeCall.cleanup() + self._setZoneCompleteSubframeCall = None + self._leftQuietZoneLocalCallbacks = None + self._setZoneCompleteLocalCallbacks = None + del self._tempFSM + taskMgr.remove('goHomeFailed') + del self.walkDoneEvent + self.walkStateData.unload() + del self.walkStateData + del self.loader + if self.trialerFA: + self.trialerFA.exit() + del self.trialerFA + return + + def _getQZState(self): + if hasattr(base, 'cr') and hasattr(base.cr, 'playGame'): + if hasattr(base.cr.playGame, 'quietZoneStateData') and base.cr.playGame.quietZoneStateData: + return base.cr.playGame.quietZoneStateData + return None + + def addLeftQuietZoneCallback(self, callback, priority = None): + qzsd = self._getQZState() + if qzsd: + return qzsd.addLeftQuietZoneCallback(callback, priority) + else: + token = self._leftQuietZoneLocalCallbacks.add(callback, priority=priority) + if not self._leftQuietZoneSubframeCall: + self._leftQuietZoneSubframeCall = SubframeCall(self._doLeftQuietZoneCallbacks, taskMgr.getCurrentTask().getPriority() - 1) + return token + + def removeLeftQuietZoneCallback(self, token): + if token is not None: + if token in self._leftQuietZoneLocalCallbacks: + self._leftQuietZoneLocalCallbacks.remove(token) + qzsd = self._getQZState() + if qzsd: + qzsd.removeLeftQuietZoneCallback(token) + return + + def _doLeftQuietZoneCallbacks(self): + self._leftQuietZoneLocalCallbacks() + self._leftQuietZoneLocalCallbacks.clear() + self._leftQuietZoneSubframeCall = None + return + + def addSetZoneCompleteCallback(self, callback, priority = None): + qzsd = self._getQZState() + if qzsd: + return qzsd.addSetZoneCompleteCallback(callback, priority) + else: + token = self._setZoneCompleteLocalCallbacks.add(callback, priority=priority) + if not self._setZoneCompleteSubframeCall: + self._setZoneCompleteSubframeCall = SubframeCall(self._doSetZoneCompleteLocalCallbacks, taskMgr.getCurrentTask().getPriority() - 1) + return token + + def removeSetZoneCompleteCallback(self, token): + if token is not None: + if token in self._setZoneCompleteLocalCallbacks: + self._setZoneCompleteLocalCallbacks.remove(token) + qzsd = self._getQZState() + if qzsd: + qzsd.removeSetZoneCompleteCallback(token) + return + + def _doSetZoneCompleteLocalCallbacks(self): + self._setZoneCompleteSubframeCall = None + localCallbacks = self._setZoneCompleteLocalCallbacks + self._setZoneCompleteLocalCallbacks() + localCallbacks.clear() + return + + def setState(self, state): + if hasattr(self, 'fsm'): + curState = self.fsm.getName() + if state == 'pet' or curState == 'pet': + self.preserveFriendsList() + self.fsm.request(state) + + def getState(self): + if hasattr(self, 'fsm'): + curState = self.fsm.getCurrentState().getName() + return curState + + def getZoneId(self): + return self.zoneId + + def getTaskZoneId(self): + return self.getZoneId() + + def isPeriodTimerEffective(self): + return 1 + + def handleTeleportQuery(self, fromAvatar, toAvatar): + if base.config.GetBool('want-tptrack', False): + if toAvatar == localAvatar: + toAvatar.doTeleportResponse(fromAvatar, toAvatar, toAvatar.doId, 1, toAvatar.defaultShard, base.cr.playGame.getPlaceId(), self.getZoneId(), fromAvatar.doId) + else: + self.notify.warning('handleTeleportQuery toAvatar.doId != localAvatar.doId' % (toAvatar.doId, localAvatar.doId)) + else: + fromAvatar.d_teleportResponse(toAvatar.doId, 1, toAvatar.defaultShard, base.cr.playGame.getPlaceId(), self.getZoneId()) + + def enablePeriodTimer(self): + if self.isPeriodTimerEffective(): + if base.cr.periodTimerExpired: + taskMgr.doMethodLater(5, self.redoPeriodTimer, 'redoPeriodTimer') + self.accept('periodTimerExpired', self.periodTimerExpired) + + def disablePeriodTimer(self): + taskMgr.remove('redoPeriodTimer') + self.ignore('periodTimerExpired') + + def redoPeriodTimer(self, task): + messenger.send('periodTimerExpired') + return Task.done + + def periodTimerExpired(self): + self.fsm.request('final') + if base.localAvatar.book.isEntered: + base.localAvatar.book.exit() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.__handlePeriodTimerBookClose) + else: + base.localAvatar.b_setAnimState('TeleportOut', 1, self.__handlePeriodTimerExitTeleport) + + def exitPeriodTimerExpired(self): + pass + + def __handlePeriodTimerBookClose(self): + base.localAvatar.b_setAnimState('TeleportOut', 1, self.__handlePeriodTimerExitTeleport) + + def __handlePeriodTimerExitTeleport(self): + base.cr.loginFSM.request('periodTimeout') + + def detectedPhoneCollision(self): + self.fsm.request('phone') + + def detectedFishingCollision(self): + self.fsm.request('fishing') + + def enterStart(self): + pass + + def exitStart(self): + pass + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def enterWalk(self, teleportIn = 0): + self.enterFLM() + self.walkStateData.enter() + if teleportIn == 0: + self.walkStateData.fsm.request('walking') + self.acceptOnce(self.walkDoneEvent, self.handleWalkDone) + if base.cr.productName in ['DisneyOnline-US', 'ES'] and not base.cr.isPaid() and base.localAvatar.tutorialAck: + base.localAvatar.chatMgr.obscure(0, 0) + base.localAvatar.chatMgr.normalButton.show() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.questPage.acceptOnscreenHooks() + base.localAvatar.invPage.acceptOnscreenHooks() + base.localAvatar.questMap.acceptOnscreenHooks() + self.walkStateData.fsm.request('walking') + self.enablePeriodTimer() + + def exitWalk(self): + self.exitFLM() + if base.cr.productName in ['DisneyOnline-US', 'ES'] and not base.cr.isPaid() and base.localAvatar.tutorialAck and not base.cr.whiteListChatEnabled: + base.localAvatar.chatMgr.obscure(1, 0) + self.disablePeriodTimer() + messenger.send('wakeup') + self.walkStateData.exit() + self.ignore(self.walkDoneEvent) + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + if base.cr.playGame.hood != None: + base.cr.playGame.hood.hideTitleText() + base.localAvatar.questPage.hideQuestsOnscreen() + base.localAvatar.questPage.ignoreOnscreenHooks() + base.localAvatar.invPage.ignoreOnscreenHooks() + base.localAvatar.invPage.hideInventoryOnscreen() + base.localAvatar.questMap.hide() + base.localAvatar.questMap.ignoreOnscreenHooks() + return + + def handleWalkDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'StickerBook': + self.last = self.fsm.getCurrentState().getName() + self.fsm.request('stickerBook') + elif mode == 'Options': + self.last = self.fsm.getCurrentState().getName() + self.fsm.request('stickerBook', [base.localAvatar.optionsPage]) + elif mode == 'Sit': + self.last = self.fsm.getCurrentState().getName() + self.fsm.request('sit') + else: + Place.notify.error('Invalid mode: %s' % mode) + + def enterSit(self): + self.enterFLM() + base.localAvatar.laffMeter.start() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.b_setAnimState('SitStart', 1) + self.accept('arrow_up', self.fsm.request, extraArgs=['walk']) + + def exitSit(self): + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + self.ignore('arrow_up') + + def enterDrive(self): + self.enterFLM() + base.localAvatar.laffMeter.start() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.b_setAnimState('SitStart', 1) + + def exitDrive(self): + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + + def enterPush(self): + self.enterFLM() + base.localAvatar.laffMeter.start() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.attachCamera() + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.startPosHprBroadcast() + base.localAvatar.b_setAnimState('Push', 1) + + def exitPush(self): + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.detachCamera() + base.localAvatar.stopPosHprBroadcast() + self.ignore('teleportQuery') + + def enterStickerBook(self, page = None): + self.enterFLM() + base.localAvatar.laffMeter.start() + target = base.cr.doFind('DistributedTarget') + if target: + target.hideGui() + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + if page: + base.localAvatar.book.setPage(page) + base.localAvatar.b_setAnimState('OpenBook', 1, self.enterStickerBookGUI) + base.localAvatar.obscureMoveFurnitureButton(1) + + def enterStickerBookGUI(self): + base.localAvatar.collisionsOn() + base.localAvatar.book.showButton() + base.localAvatar.book.enter() + base.localAvatar.setGuiConflict(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleep) + self.accept('bookDone', self.__handleBook) + base.localAvatar.b_setAnimState('ReadBook', 1) + self.enablePeriodTimer() + + def __handleFallingAsleep(self, task): + base.localAvatar.book.exit() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.__handleFallingAsleepBookClose) + return Task.done + + def __handleFallingAsleepBookClose(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + + def exitStickerBook(self): + base.localAvatar.stopSleepWatch() + self.disablePeriodTimer() + self.exitFLM() + base.localAvatar.laffMeter.stop() + base.localAvatar.setGuiConflict(0) + base.localAvatar.book.exit() + base.localAvatar.book.hideButton() + base.localAvatar.collisionsOff() + self.ignore('bookDone') + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.obscureMoveFurnitureButton(-1) + target = base.cr.doFind('DistributedTarget') + if target: + target.showGui() + + def __handleBook(self): + base.localAvatar.stopSleepWatch() + base.localAvatar.book.exit() + bookStatus = base.localAvatar.book.getDoneStatus() + if bookStatus['mode'] == 'close': + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.handleBookClose) + elif bookStatus['mode'] == 'teleport': + zoneId = bookStatus['hood'] + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.handleBookCloseTeleport, extraArgs=[zoneId, zoneId]) + elif bookStatus['mode'] == 'exit': + self.exitTo = bookStatus.get('exitTo') + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.__handleBookCloseExit) + elif bookStatus['mode'] == 'gohome': + zoneId = bookStatus['hood'] + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.goHomeNow, extraArgs=[zoneId]) + elif bookStatus['mode'] == 'startparty': + firstStart = bookStatus['firstStart'] + hostId = bookStatus['hostId'] + base.localAvatar.collisionsOff() + base.localAvatar.b_setAnimState('CloseBook', 1, callback=self.startPartyNow, extraArgs=[firstStart, hostId]) + + def handleBookCloseTeleport(self, hoodId, zoneId): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + self.requestLeave({'loader': ZoneUtil.getBranchLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1}) + return + + def __handleBookCloseExit(self): + base.localAvatar.b_setAnimState('TeleportOut', 1, self.__handleBookExitTeleport, [0]) + + def __handleBookExitTeleport(self, requestStatus): + if base.cr.timeManager: + base.cr.timeManager.setDisconnectReason(ToontownGlobals.DisconnectBookExit) + base.transitions.fadeScreen(1.0) + base.cr.gameFSM.request(self.exitTo) + + def goHomeNow(self, curZoneId): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + hoodId = ToontownGlobals.MyEstate + self.requestLeave({'loader': 'safeZoneLoader', + 'where': 'estate', + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': -1, + 'shardId': None, + 'avId': -1}) + return + + def startPartyNow(self, firstStart, hostId): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + base.localAvatar.creatingNewPartyWithMagicWord = False + base.localAvatar.aboutToPlanParty = False + hoodId = ToontownGlobals.PartyHood + if firstStart: + zoneId = 0 + 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': zoneId, + 'shardId': shardId, + 'avId': -1}) + else: + if hostId is None: + hostId = base.localAvatar.doId + base.cr.partyManager.sendAvatarToParty(hostId) + return + return + + def handleBookClose(self): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + if hasattr(self, 'toonSubmerged') and self.toonSubmerged == 1: + if hasattr(self, 'walkStateData'): + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + + def requestLeave(self, requestStatus): + teleportDebug(requestStatus, 'requestLeave(%s)' % (requestStatus,)) + if hasattr(self, 'fsm'): + self.doRequestLeave(requestStatus) + + def doRequestLeave(self, requestStatus): + teleportDebug(requestStatus, 'requestLeave(%s)' % (requestStatus,)) + self.fsm.request('DFA', [requestStatus]) + + def enterDFA(self, requestStatus): + teleportDebug(requestStatus, 'enterDFA(%s)' % (requestStatus,)) + self.acceptOnce(self.dfaDoneEvent, self.enterDFACallback, [requestStatus]) + self.dfa = DownloadForceAcknowledge.DownloadForceAcknowledge(self.dfaDoneEvent) + self.dfa.enter(base.cr.hoodMgr.getPhaseFromHood(requestStatus['hoodId'])) + + def exitDFA(self): + self.ignore(self.dfaDoneEvent) + + def handleEnterTunnel(self, requestStatus, collEntry): + if localAvatar.hasActiveBoardingGroup(): + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + dummyNP = NodePath('dummyNP') + dummyNP.reparentTo(render) + tunnelOrigin = requestStatus['tunnelOrigin'] + dummyNP.setPos(localAvatar.getPos()) + dummyNP.setH(tunnelOrigin.getH()) + dummyNP.setPos(dummyNP, 0, 4, 0) + localAvatar.setPos(dummyNP.getPos()) + dummyNP.removeNode() + del dummyNP + return + self.requestLeave(requestStatus) + + def enterDFACallback(self, requestStatus, doneStatus): + teleportDebug(requestStatus, 'enterDFACallback%s' % ((requestStatus, doneStatus),)) + self.dfa.exit() + del self.dfa + if doneStatus['mode'] == 'complete': + if requestStatus.get('tutorial', 0): + out = {'teleportIn': 'tunnelOut'} + requestStatus['zoneId'] = 22000 + requestStatus['hoodId'] = 22000 + else: + out = {'teleportIn': 'teleportOut', + 'tunnelIn': 'tunnelOut', + 'doorIn': 'doorOut'} + teleportDebug(requestStatus, 'requesting %s, requestStatus=%s' % (out[requestStatus['how']], requestStatus)) + self.fsm.request(out[requestStatus['how']], [requestStatus]) + elif doneStatus['mode'] == 'incomplete': + self.fsm.request('DFAReject') + else: + Place.notify.error('Unknown done status for DownloadForceAcknowledge: ' + `doneStatus`) + + def enterDFAReject(self): + self.fsm.request('walk') + + def exitDFAReject(self): + pass + + def enterTrialerFA(self, requestStatus): + teleportDebug(requestStatus, 'enterTrialerFA(%s)' % requestStatus) + self.acceptOnce(self.trialerFADoneEvent, self.trialerFACallback, [requestStatus]) + self.trialerFA = TrialerForceAcknowledge.TrialerForceAcknowledge(self.trialerFADoneEvent) + self.trialerFA.enter(requestStatus['hoodId']) + + def exitTrialerFA(self): + pass + + def trialerFACallback(self, requestStatus, doneStatus): + if doneStatus['mode'] == 'pass': + self.fsm.request('DFA', [requestStatus]) + elif doneStatus['mode'] == 'fail': + self.fsm.request('trialerFAReject') + else: + Place.notify.error('Unknown done status for TrialerForceAcknowledge: %s' % doneStatus) + + def enterTrialerFAReject(self): + self.fsm.request('walk') + + def exitTrialerFAReject(self): + pass + + def enterDoorIn(self, requestStatus): + NametagGlobals.setMasterArrowsOn(0) + door = base.cr.doId2do.get(requestStatus['doorDoId']) + door.readyToExit() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startQuestMap() + + def exitDoorIn(self): + NametagGlobals.setMasterArrowsOn(1) + base.localAvatar.obscureMoveFurnitureButton(-1) + + def enterDoorOut(self): + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitDoorOut(self): + base.localAvatar.obscureMoveFurnitureButton(-1) + base.localAvatar.stopQuestMap() + + def handleDoorDoneEvent(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def handleDoorTrigger(self): + self.fsm.request('doorOut') + + def enterTunnelIn(self, requestStatus): + self.notify.debug('enterTunnelIn(requestStatus=' + str(requestStatus) + ')') + tunnelOrigin = base.render.find(requestStatus['tunnelName']) + self.accept('tunnelInMovieDone', self.__tunnelInMovieDone) + base.localAvatar.reconsiderCheesyEffect() + base.localAvatar.tunnelIn(tunnelOrigin) + base.localAvatar.startQuestMap() + + def __tunnelInMovieDone(self): + self.ignore('tunnelInMovieDone') + self.fsm.request('walk') + + def exitTunnelIn(self): + pass + + def enterTunnelOut(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + how = requestStatus['how'] + tunnelOrigin = requestStatus['tunnelOrigin'] + fromZoneId = ZoneUtil.getCanonicalZoneId(self.getZoneId()) + tunnelName = requestStatus.get('tunnelName') + if tunnelName == None: + tunnelName = base.cr.hoodMgr.makeLinkTunnelName(self.loader.hood.id, fromZoneId) + self.doneStatus = {'loader': ZoneUtil.getLoaderName(zoneId), + 'where': ZoneUtil.getToonWhereName(zoneId), + 'how': how, + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'tunnelName': tunnelName} + self.accept('tunnelOutMovieDone', self.__tunnelOutMovieDone) + base.localAvatar.tunnelOut(tunnelOrigin) + base.localAvatar.stopQuestMap() + return + + def __tunnelOutMovieDone(self): + self.ignore('tunnelOutMovieDone') + messenger.send(self.doneEvent) + + def exitTunnelOut(self): + pass + + def enterTeleportOut(self, requestStatus, callback): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('TeleportOut', 1, callback, [requestStatus]) + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitTeleportOut(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.stopQuestMap() + base.localAvatar.obscureMoveFurnitureButton(-1) + + def enterDied(self, requestStatus, callback = None): + if callback == None: + callback = self.__diedDone + base.localAvatar.laffMeter.start() + camera.wrtReparentTo(render) + base.localAvatar.b_setAnimState('Died', 1, callback, [requestStatus]) + base.localAvatar.obscureMoveFurnitureButton(1) + return + + def __diedDone(self, requestStatus): + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + + def exitDied(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + + def getEstateZoneAndGoHome(self, requestStatus): + self.doneStatus = requestStatus + avId = requestStatus['avId'] + self.acceptOnce('setLocalEstateZone', self.goHome) + if avId > 0: + base.cr.estateMgr.getLocalEstateZone(avId) + else: + base.cr.estateMgr.getLocalEstateZone(base.localAvatar.getDoId()) + if HouseGlobals.WANT_TELEPORT_TIMEOUT: + taskMgr.doMethodLater(HouseGlobals.TELEPORT_TIMEOUT, self.goHomeFailed, 'goHomeFailed') + + def goHome(self, ownerId, zoneId): + self.notify.debug('goHome ownerId = %s' % ownerId) + taskMgr.remove('goHomeFailed') + if ownerId > 0 and ownerId != base.localAvatar.doId and not base.cr.isFriend(ownerId): + self.doneStatus['failed'] = 1 + self.goHomeFailed(None) + return + if ownerId == 0 and zoneId == 0: + if self.doneStatus['shardId'] is None or self.doneStatus['shardId'] is base.localAvatar.defaultShard: + self.doneStatus['failed'] = 1 + self.goHomeFailed(None) + return + else: + self.doneStatus['hood'] = ToontownGlobals.MyEstate + self.doneStatus['zone'] = base.localAvatar.lastHood + self.doneStatus['loaderId'] = 'safeZoneLoader' + self.doneStatus['whereId'] = 'estate' + self.doneStatus['how'] = 'teleportIn' + messenger.send(self.doneEvent) + return + if self.doneStatus['zoneId'] == -1: + self.doneStatus['zoneId'] = zoneId + elif self.doneStatus['zoneId'] != zoneId: + self.doneStatus['where'] = 'house' + self.doneStatus['ownerId'] = ownerId + messenger.send(self.doneEvent) + messenger.send('localToonLeft') + return + + def goHomeFailed(self, task): + self.notify.debug('goHomeFailed') + self.notifyUserGoHomeFailed() + self.ignore('setLocalEstateZone') + self.doneStatus['hood'] = base.localAvatar.lastHood + self.doneStatus['zone'] = base.localAvatar.lastHood + self.fsm.request('teleportIn', [self.doneStatus]) + return Task.done + + def notifyUserGoHomeFailed(self): + self.notify.debug('notifyUserGoHomeFailed') + failedToVisitAvId = self.doneStatus.get('avId', -1) + avName = None + if failedToVisitAvId != -1: + avatar = base.cr.identifyAvatar(failedToVisitAvId) + if avatar: + avName = avatar.getName() + if avName: + message = TTLocalizer.EstateTeleportFailedNotFriends % avName + else: + message = TTLocalizer.EstateTeleportFailed + base.localAvatar.setSystemMessage(0, message) + return + + def enterTeleportIn(self, requestStatus): + self._tiToken = self.addSetZoneCompleteCallback(Functor(self._placeTeleportInPostZoneComplete, requestStatus), 100) + + def _placeTeleportInPostZoneComplete(self, requestStatus): + teleportDebug(requestStatus, '_placeTeleportInPostZoneComplete(%s)' % (requestStatus,)) + NametagGlobals.setMasterArrowsOn(0) + base.localAvatar.laffMeter.start() + base.localAvatar.startQuestMap() + base.localAvatar.reconsiderCheesyEffect() + base.localAvatar.obscureMoveFurnitureButton(1) + avId = requestStatus.get('avId', -1) + if avId != -1: + if base.cr.doId2do.has_key(avId): + teleportDebug(requestStatus, 'teleport to avatar') + avatar = base.cr.doId2do[avId] + avatar.forceToTruePosition() + base.localAvatar.gotoNode(avatar) + base.localAvatar.b_teleportGreeting(avId) + else: + friend = base.cr.identifyAvatar(avId) + if friend != None: + teleportDebug(requestStatus, 'friend not here, giving up') + base.localAvatar.setSystemMessage(avId, OTPLocalizer.WhisperTargetLeftVisit % (friend.getName(),)) + friend.d_teleportGiveup(base.localAvatar.doId) + base.transitions.irisIn() + self.nextState = requestStatus.get('nextState', 'walk') + base.localAvatar.attachCamera() + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.startPosHprBroadcast() + globalClock.tick() + base.localAvatar.b_setAnimState('TeleportIn', 1, callback=self.teleportInDone) + base.localAvatar.d_broadcastPositionNow() + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + return + + def teleportInDone(self): + if hasattr(self, 'fsm'): + teleportNotify.debug('teleportInDone: %s' % self.nextState) + self.fsm.request(self.nextState, [1]) + + def exitTeleportIn(self): + self.removeSetZoneCompleteCallback(self._tiToken) + self._tiToken = None + NametagGlobals.setMasterArrowsOn(1) + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.detachCamera() + base.localAvatar.stopPosHprBroadcast() + return + + def requestTeleport(self, hoodId, zoneId, shardId, avId): + if avId > 0: + teleportNotify.debug('requestTeleport%s' % ((hoodId, + zoneId, + shardId, + avId),)) + if localAvatar.hasActiveBoardingGroup(): + if avId > 0: + teleportNotify.debug('requestTeleport: has active boarding group') + rejectText = TTLocalizer.BoardingCannotLeaveZone + localAvatar.elevatorNotifier.showMe(rejectText) + return + loaderId = ZoneUtil.getBranchLoaderName(zoneId) + whereId = ZoneUtil.getToonWhereName(zoneId) + if hoodId == ToontownGlobals.MyEstate: + loaderId = 'safeZoneLoader' + whereId = 'estate' + if hoodId == ToontownGlobals.PartyHood: + loaderId = 'safeZoneLoader' + whereId = 'party' + self.requestLeave({'loader': loaderId, + 'where': whereId, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': shardId, + 'avId': avId}) + + def enterQuest(self, npcToon): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitQuest(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + + def enterPurchase(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + + def exitPurchase(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + + def enterFishing(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + + def exitFishing(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + + def enterBanking(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepBanking) + self.enablePeriodTimer() + + def __handleFallingAsleepBanking(self, arg): + if hasattr(self, 'fsm'): + messenger.send('bankAsleep') + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + + def exitBanking(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + base.localAvatar.stopSleepWatch() + self.disablePeriodTimer() + + def enterPhone(self): + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepPhone) + self.enablePeriodTimer() + + def __handleFallingAsleepPhone(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + messenger.send('phoneAsleep') + base.localAvatar.forceGotoSleep() + + def exitPhone(self): + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + base.localAvatar.stopSleepWatch() + self.disablePeriodTimer() + + def enterStopped(self): + base.localAvatar.b_setAnimState('neutral', 1) + Emote.globalEmote.disableBody(base.localAvatar, 'enterStopped') + self.accept('teleportQuery', self.handleTeleportQuery) + if base.localAvatar.isDisguised: + base.localAvatar.setTeleportAvailable(0) + else: + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.laffMeter.start() + base.localAvatar.obscureMoveFurnitureButton(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepStopped) + self.enablePeriodTimer() + + def __handleFallingAsleepStopped(self, arg): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + messenger.send('stoppedAsleep') + + def exitStopped(self): + Emote.globalEmote.releaseBody(base.localAvatar, 'exitStopped') + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + base.localAvatar.obscureMoveFurnitureButton(-1) + base.localAvatar.stopSleepWatch() + self.disablePeriodTimer() + messenger.send('exitingStoppedState') + + def enterPet(self): + base.localAvatar.b_setAnimState('neutral', 1) + Emote.globalEmote.disableBody(base.localAvatar, 'enterPet') + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.setTeleportAllowed(0) + base.localAvatar.laffMeter.start() + self.enterFLM() + + def exitPet(self): + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.setTeleportAllowed(1) + Emote.globalEmote.releaseBody(base.localAvatar, 'exitPet') + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + self.exitFLM() + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + how = base.cr.handlerArgs['how'] + self.fsm.request(how, [base.cr.handlerArgs]) diff --git a/toontown/hood/QuietZoneState.py b/toontown/hood/QuietZoneState.py new file mode 100644 index 0000000..3bb24f4 --- /dev/null +++ b/toontown/hood/QuietZoneState.py @@ -0,0 +1,352 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import Functor, PriorityCallbacks +from direct.task import Task +from toontown.distributed.ToontownMsgTypes import * +from otp.otpbase import OTPGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import ZoneUtil + +class QuietZoneState(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('QuietZoneState') + Disable = False + Queue = [] + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('QuietZoneState', [State.State('off', self.enterOff, self.exitOff, ['waitForQuietZoneResponse']), + State.State('waitForQuietZoneResponse', self.enterWaitForQuietZoneResponse, self.exitWaitForQuietZoneResponse, ['waitForZoneRedirect']), + State.State('waitForZoneRedirect', self.enterWaitForZoneRedirect, self.exitWaitForZoneRedirect, ['waitForSetZoneResponse']), + State.State('waitForSetZoneResponse', self.enterWaitForSetZoneResponse, self.exitWaitForSetZoneResponse, ['waitForSetZoneComplete']), + State.State('waitForSetZoneComplete', self.enterWaitForSetZoneComplete, self.exitWaitForSetZoneComplete, ['waitForLocalAvatarOnShard']), + State.State('waitForLocalAvatarOnShard', self.enterWaitForLocalAvatarOnShard, self.exitWaitForLocalAvatarOnShard, ['off'])], 'off', 'off') + self._enqueueCount = 0 + self.fsm.enterInitialState() + + def load(self): + self.notify.debug('load()') + + def unload(self): + self._dequeue() + self.notify.debug('unload()') + del self.fsm + + @classmethod + def enqueueState(cls, state, requestStatus): + cls.Queue = [(state, requestStatus)] + cls.Queue + state._enqueueCount += 1 + if len(cls.Queue) == 1: + cls.startNextQueuedState() + + @classmethod + def dequeueState(cls, state): + s, requestStatus = cls.Queue.pop() + s._enqueueCount -= 1 + if len(cls.Queue) > 0: + cls.startNextQueuedState() + + @classmethod + def startNextQueuedState(cls): + state, requestStatus = cls.Queue[-1] + state._start(requestStatus) + + def _dequeue(self): + newQ = [] + for item in self.__class__.Queue: + state, requestStatus = item + if state is not self: + newQ.append(item) + + self.__class__.Queue = newQ + + def getEnterWaitForSetZoneResponseMsg(self): + return 'enterWaitForSetZoneResponse-%s' % (id(self),) + + def getQuietZoneLeftEvent(self): + return '%s-%s' % (base.cr.getQuietZoneLeftEvent(), id(self)) + + def getSetZoneCompleteEvent(self): + return 'setZoneComplete-%s' % (id(self),) + + def enter(self, requestStatus): + self.notify.debug('enter(requestStatus=' + str(requestStatus) + ')') + self._requestStatus = requestStatus + self._leftQuietZoneCallbacks = None + self._setZoneCompleteCallbacks = None + self._leftQuietZoneLocalCallbacks = {} + self._setZoneCompleteLocalCallbacks = {} + self.enqueueState(self, requestStatus) + return + + def _start(self, requestStatus): + base.transitions.fadeScreen(1.0) + self.fsm.request('waitForQuietZoneResponse') + + def getRequestStatus(self): + return self._requestStatus + + def exit(self): + self.notify.debug('exit()') + del self._requestStatus + base.transitions.noTransitions() + self.fsm.request('off') + self._dequeue() + + def waitForDatabase(self, description): + if base.endlessQuietZone: + return + base.cr.waitForDatabaseTimeout(requestName='quietZoneState-%s' % description) + + def clearWaitForDatabase(self): + base.cr.cleanupWaitingForDatabase() + + def addLeftQuietZoneCallback(self, callback, priority = None): + if self._leftQuietZoneCallbacks: + return self._leftQuietZoneCallbacks.add(callback, priority) + else: + token = PriorityCallbacks.GetToken() + fdc = SubframeCall(callback, taskMgr.getCurrentTask().getPriority() - 1) + self._leftQuietZoneLocalCallbacks[token] = fdc + return token + + def removeLeftQuietZoneCallback(self, token): + if token is not None: + lc = self._leftQuietZoneLocalCallbacks.pop(token, None) + if lc: + lc.cleanup() + if self._leftQuietZoneCallbacks: + self._leftQuietZoneCallbacks.remove(token) + return + + def addSetZoneCompleteCallback(self, callback, priority = None): + if self._setZoneCompleteCallbacks: + return self._setZoneCompleteCallbacks.add(callback, priority) + else: + token = PriorityCallbacks.GetToken() + fdc = SubframeCall(callback, taskMgr.getCurrentTask().getPriority() - 1) + self._setZoneCompleteLocalCallbacks[token] = fdc + return token + + def removeSetZoneCompleteCallback(self, token): + if token is not None: + lc = self._setZoneCompleteLocalCallbacks.pop(token, None) + if lc: + lc.cleanup() + if self._setZoneCompleteCallbacks: + self._setZoneCompleteCallbacks.remove(token) + return + + def handleWaitForQuietZoneResponse(self, msgType, di): + self.notify.debug('handleWaitForQuietZoneResponse(' + 'msgType=' + str(msgType) + ', di=' + str(di) + ')') + if msgType == CLIENT_CREATE_OBJECT_REQUIRED: + base.cr.handleQuietZoneGenerateWithRequired(di) + elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER: + base.cr.handleQuietZoneGenerateWithRequiredOther(di) + elif msgType == CLIENT_OBJECT_UPDATE_FIELD: + base.cr.handleQuietZoneUpdateField(di) + elif msgType in QUIET_ZONE_IGNORED_LIST: + self.notify.debug('ignoring unwanted message from previous zone') + else: + base.cr.handlePlayGame(msgType, di) + + def handleWaitForZoneRedirect(self, msgType, di): + self.notify.debug('handleWaitForZoneRedirect(' + 'msgType=' + str(msgType) + ', di=' + str(di) + ')') + if msgType == CLIENT_CREATE_OBJECT_REQUIRED: + base.cr.handleQuietZoneGenerateWithRequired(di) + elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER: + base.cr.handleQuietZoneGenerateWithRequiredOther(di) + elif msgType == CLIENT_OBJECT_UPDATE_FIELD: + base.cr.handleQuietZoneUpdateField(di) + else: + base.cr.handlePlayGame(msgType, di) + + def enterOff(self): + self.notify.debug('enterOff()') + + def exitOff(self): + self.notify.debug('exitOff()') + self._leftQuietZoneCallbacks = PriorityCallbacks() + self._setZoneCompleteCallbacks = PriorityCallbacks() + self._leftQuietZoneLocalCallbacks = {} + self._setZoneCompleteLocalCallbacks = {} + + def enterWaitForQuietZoneResponse(self): + self.notify.debug('enterWaitForQuietZoneResponse(doneStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + base.cr.handler = self.handleWaitForQuietZoneResponse + base.cr.handlerArgs = self._requestStatus + base.cr.setInQuietZone(True) + self.setZoneDoneEvent = base.cr.getNextSetZoneDoneEvent() + self.acceptOnce(self.setZoneDoneEvent, self._handleQuietZoneComplete) + self.waitForDatabase('WaitForQuietZoneResponse') + if base.slowQuietZone: + + def sQZR(task): + base.cr.sendQuietZoneRequest() + return Task.done + + taskMgr.doMethodLater(base.slowQuietZoneDelay, sQZR, 'slowQuietZone-sendQuietZoneRequest') + else: + base.cr.sendQuietZoneRequest() + + def _handleQuietZoneComplete(self): + self.fsm.request('waitForZoneRedirect') + + def exitWaitForQuietZoneResponse(self): + self.notify.debug('exitWaitForQuietZoneResponse()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + base.cr.setInQuietZone(False) + self.ignore(self.setZoneDoneEvent) + del self.setZoneDoneEvent + return + + def enterWaitForZoneRedirect(self): + self.notify.debug('enterWaitForZoneRedirect(requestStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + base.cr.handler = self.handleWaitForZoneRedirect + base.cr.handlerArgs = self._requestStatus + base.cr.setInQuietZone(True) + self.waitForDatabase('WaitForZoneRedirect') + zoneId = self._requestStatus['zoneId'] + avId = self._requestStatus.get('avId', -1) + allowRedirect = self._requestStatus.get('allowRedirect', 1) + if avId != -1: + allowRedirect = 0 + if not base.cr.welcomeValleyManager: + newZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + if newZoneId != zoneId: + self.gotZoneRedirect(newZoneId) + return + if allowRedirect and ZoneUtil.isWelcomeValley(zoneId): + self.notify.info('Requesting AI redirect from zone %s.' % zoneId) + if base.slowQuietZone: + + def rZI(task, zoneId = zoneId, self = self): + base.cr.welcomeValleyManager.requestZoneId(zoneId, self.gotZoneRedirect) + return Task.done + + taskMgr.doMethodLater(base.slowQuietZoneDelay, rZI, 'slowQuietZone-welcomeValleyRedirect') + else: + base.cr.welcomeValleyManager.requestZoneId(zoneId, self.gotZoneRedirect) + else: + self.fsm.request('waitForSetZoneResponse') + + def gotZoneRedirect(self, zoneId): + self.notify.info('Redirecting to zone %s.' % zoneId) + base.cr.handlerArgs['zoneId'] = zoneId + base.cr.handlerArgs['hoodId'] = ZoneUtil.getHoodId(zoneId) + self.fsm.request('waitForSetZoneResponse') + + def exitWaitForZoneRedirect(self): + self.notify.debug('exitWaitForZoneRedirect()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + base.cr.setInQuietZone(False) + return + + def enterWaitForSetZoneResponse(self): + self.notify.debug('enterWaitForSetZoneResponse(requestStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + messenger.send(self.getEnterWaitForSetZoneResponseMsg(), [self._requestStatus]) + base.cr.handlerArgs = self._requestStatus + zoneId = self._requestStatus['zoneId'] + base.cr.dumpAllSubShardObjects() + base.cr.resetDeletedSubShardDoIds() + base.cr.sendSetZoneMsg(zoneId) + self.waitForDatabase('WaitForSetZoneResponse') + self.fsm.request('waitForSetZoneComplete') + + def exitWaitForSetZoneResponse(self): + self.notify.debug('exitWaitForSetZoneResponse()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + return + + def enterWaitForSetZoneComplete(self): + self.notify.debug('enterWaitForSetZoneComplete(requestStatus=' + str(self._requestStatus) + ')') + if not self.Disable: + base.cr.handlerArgs = self._requestStatus + if base.slowQuietZone: + + def delayFunc(self = self): + + def hSZC(task): + self._handleSetZoneComplete() + return Task.done + + taskMgr.doMethodLater(base.slowQuietZoneDelay, hSZC, 'slowQuietZone-sendSetZoneComplete') + + nextFunc = delayFunc + else: + nextFunc = self._handleSetZoneComplete + self.waitForDatabase('WaitForSetZoneComplete') + self.setZoneDoneEvent = base.cr.getLastSetZoneDoneEvent() + self.acceptOnce(self.setZoneDoneEvent, nextFunc) + if base.placeBeforeObjects: + self._leftQuietZoneCallbacks() + self._leftQuietZoneCallbacks = None + fdcs = self._leftQuietZoneLocalCallbacks.values() + self._leftQuietZoneLocalCallbacks = {} + for fdc in fdcs: + if not fdc.isFinished(): + fdc.finish() + + messenger.send(self.getQuietZoneLeftEvent()) + return + + def _handleSetZoneComplete(self): + self.fsm.request('waitForLocalAvatarOnShard') + + def exitWaitForSetZoneComplete(self): + self.notify.debug('exitWaitForSetZoneComplete()') + self.clearWaitForDatabase() + base.cr.handler = base.cr.handlePlayGame + base.cr.handlerArgs = None + self.ignore(self.setZoneDoneEvent) + del self.setZoneDoneEvent + return + + def enterWaitForLocalAvatarOnShard(self): + self.notify.debug('enterWaitForLocalAvatarOnShard()') + if not self.Disable: + base.cr.handlerArgs = self._requestStatus + self._onShardEvent = localAvatar.getArrivedOnDistrictEvent() + self.waitForDatabase('WaitForLocalAvatarOnShard') + if localAvatar.isGeneratedOnDistrict(localAvatar.defaultShard): + self._announceDone() + else: + self.acceptOnce(self._onShardEvent, self._announceDone) + + def _announceDone(self): + base.localAvatar.startChat() + if base.endlessQuietZone: + self._dequeue() + return + doneEvent = self.doneEvent + requestStatus = self._requestStatus + self._setZoneCompleteCallbacks() + self._setZoneCompleteCallbacks = None + fdcs = self._setZoneCompleteLocalCallbacks.values() + self._setZoneCompleteLocalCallbacks = {} + for fdc in fdcs: + if not fdc.isFinished(): + fdc.finish() + + messenger.send(self.getSetZoneCompleteEvent(), [requestStatus]) + messenger.send(doneEvent) + self._dequeue() + return + + def exitWaitForLocalAvatarOnShard(self): + self.notify.debug('exitWaitForLocalAvatarOnShard()') + self.clearWaitForDatabase() + self.ignore(self._onShardEvent) + base.cr.handlerArgs = None + del self._onShardEvent + return diff --git a/toontown/hood/SellbotHQ.py b/toontown/hood/SellbotHQ.py new file mode 100644 index 0000000..8d81561 --- /dev/null +++ b/toontown/hood/SellbotHQ.py @@ -0,0 +1,34 @@ +import CogHood +from toontown.toonbase import ToontownGlobals +from toontown.coghq import SellbotCogHQLoader + +class SellbotHQ(CogHood.CogHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + CogHood.CogHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = ToontownGlobals.SellbotHQ + self.cogHQLoaderClass = SellbotCogHQLoader.SellbotCogHQLoader + self.storageDNAFile = None + self.skyFile = 'phase_9/models/cogHQ/cog_sky' + self.titleColor = (0.5, 0.5, 0.5, 1.0) + return + + def load(self): + CogHood.CogHood.load(self) + self.sky.setScale(2.0) + self.parentFSM.getStateNamed('SellbotHQ').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('SellbotHQ').removeChild(self.fsm) + del self.cogHQLoaderClass + CogHood.CogHood.unload(self) + + def enter(self, *args): + CogHood.CogHood.enter(self, *args) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + base.camLens.setNearFar(ToontownGlobals.CogHQCameraNear, ToontownGlobals.CogHQCameraFar) + + def exit(self): + localAvatar.setCameraFov(ToontownGlobals.DefaultCameraFov) + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + CogHood.CogHood.exit(self) diff --git a/toontown/hood/SkyUtil.py b/toontown/hood/SkyUtil.py new file mode 100644 index 0000000..df1af1c --- /dev/null +++ b/toontown/hood/SkyUtil.py @@ -0,0 +1,35 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +notify = DirectNotifyGlobal.directNotify.newCategory('SkyUtil') + +def cloudSkyTrack(task): + task.h += globalClock.getDt() * 0.25 + if task.cloud1.isEmpty() or task.cloud2.isEmpty(): + notify.warning("Couln't find clouds!") + return Task.done + task.cloud1.setH(task.h) + task.cloud2.setH(-task.h * 0.8) + return Task.cont + + +def startCloudSky(hood, parent = camera, effects = CompassEffect.PRot | CompassEffect.PZ): + hood.sky.reparentTo(parent) + hood.sky.setDepthTest(0) + hood.sky.setDepthWrite(0) + hood.sky.setBin('background', 100) + hood.sky.find('**/Sky').reparentTo(hood.sky, -1) + hood.sky.reparentTo(parent) + hood.sky.setZ(0.0) + hood.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), effects) + hood.sky.node().setEffect(ce) + skyTrackTask = Task(hood.skyTrack) + skyTrackTask.h = 0 + skyTrackTask.cloud1 = hood.sky.find('**/cloud1') + skyTrackTask.cloud2 = hood.sky.find('**/cloud2') + if not skyTrackTask.cloud1.isEmpty() and not skyTrackTask.cloud2.isEmpty(): + taskMgr.add(skyTrackTask, 'skyTrack') + else: + notify.warning("Couln't find clouds!") diff --git a/toontown/hood/SleepingHydrantAnimatedProp.py b/toontown/hood/SleepingHydrantAnimatedProp.py new file mode 100644 index 0000000..0b6845b --- /dev/null +++ b/toontown/hood/SleepingHydrantAnimatedProp.py @@ -0,0 +1,26 @@ +import AnimatedProp +from direct.interval.IntervalGlobal import * +from direct.task import Task +import math + +class SleepingHydrantAnimatedProp(AnimatedProp.AnimatedProp): + + def __init__(self, node): + AnimatedProp.AnimatedProp.__init__(self, node) + self.task = None + return + + def bobTask(self, task): + self.node.setSz(1.0 + 0.08 * math.sin(task.time)) + return Task.cont + + def enter(self): + AnimatedProp.AnimatedProp.enter(self) + self.task = taskMgr.add(self.bobTask, self.uniqueName('bobTask')) + + def exit(self): + AnimatedProp.AnimatedProp.exit(self) + if self.task: + taskMgr.remove(self.task) + self.task = None + return diff --git a/toontown/hood/StreetSign.py b/toontown/hood/StreetSign.py new file mode 100644 index 0000000..a0fec90 --- /dev/null +++ b/toontown/hood/StreetSign.py @@ -0,0 +1,66 @@ +import os +import shutil +import datetime +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.showbase import AppRunnerGlobal +from toontown.toonbase import TTLocalizer + +class StreetSign(DistributedObject.DistributedObject): + RedownloadTaskName = 'RedownloadStreetSign' + StreetSignFileName = config.GetString('street-sign-filename', 'texture.jpg') + StreetSignBaseDir = config.GetString('street-sign-base-dir', 'sign') + StreetSignUrl = base.config.GetString('street-sign-url', 'http://cdn.toontown.disney.go.com/toontown/en/street-signs/img/') + notify = DirectNotifyGlobal.directNotify.newCategory('StreetSign') + + def __init__(self): + self.downloadingStreetSign = False + self.percentDownloaded = 0.0 + self.startDownload = datetime.datetime.now() + self.endDownload = datetime.datetime.now() + self.notify.info('Street sign url is %s' % self.StreetSignUrl) + self.redownloadStreetSign() + + def replaceTexture(self): + searchPath = DSearchPath() + searchPath.appendDirectory(self.directory) + + def redownloadStreetSign(self): + self.precentDownload = 0.0 + self.startRedownload = datetime.datetime.now() + self.downloadingStreetSign = True + Filename(self.StreetSignBaseDir + '/.').makeDir() + http = HTTPClient.getGlobalPtr() + self.url = self.StreetSignUrl + self.StreetSignFileName + self.ch = http.makeChannel(True) + localFilename = Filename(self.StreetSignBaseDir, self.StreetSignFileName) + 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('Street Sign is up to date') + if outOfDate and self.ch.isValid(): + self.ch.beginGetDocument(doc) + self.ch.downloadToFile(localFilename) + taskMgr.add(self.downloadStreetSignTask, self.RedownloadTaskName) + + def downloadStreetSignTask(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.redownloadingStreetSign = False + return task.done + self.notify.info('Down downloading street sign') + return task.done diff --git a/toontown/hood/TTHood.py b/toontown/hood/TTHood.py new file mode 100644 index 0000000..d87f79c --- /dev/null +++ b/toontown/hood/TTHood.py @@ -0,0 +1,69 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import ToonHood +from toontown.town import TTTownLoader +from toontown.safezone import TTSafeZoneLoader +from toontown.toonbase.ToontownGlobals import * +import SkyUtil +from direct.directnotify import DirectNotifyGlobal + +class TTHood(ToonHood.ToonHood): + notify = DirectNotifyGlobal.directNotify.newCategory('TTHood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = ToontownCentral + self.townLoaderClass = TTTownLoader.TTTownLoader + self.safeZoneLoaderClass = TTSafeZoneLoader.TTSafeZoneLoader + self.storageDNAFile = 'phase_4/dna/storage_TT.dna' + self.holidayStorageDNADict = {WINTER_DECORATIONS: ['phase_4/dna/winter_storage_TT.dna', 'phase_4/dna/winter_storage_TT_sz.dna'], + WACKY_WINTER_DECORATIONS: ['phase_4/dna/winter_storage_TT.dna', 'phase_4/dna/winter_storage_TT_sz.dna'], + HALLOWEEN_PROPS: ['phase_4/dna/halloween_props_storage_TT.dna', 'phase_4/dna/halloween_props_storage_TT_sz.dna'], + SPOOKY_PROPS: ['phase_4/dna/halloween_props_storage_TT.dna', 'phase_4/dna/halloween_props_storage_TT_sz.dna']} + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.spookySkyFile = 'phase_3.5/models/props/BR_sky' + self.titleColor = (1.0, 0.5, 0.4, 1.0) + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('TTHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('TTHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + + def exit(self): + ToonHood.ToonHood.exit(self) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + self.notify.debug('The sky is: %s' % self.sky) + if not self.sky.getTag('sky') == 'Regular': + self.endSpookySky() + SkyUtil.startCloudSky(self) + + def startSpookySky(self): + if hasattr(self, 'sky') and self.sky: + self.stopSky() + self.sky = loader.loadModel(self.spookySkyFile) + self.sky.setTag('sky', 'Halloween') + self.sky.setScale(1.0) + self.sky.setDepthTest(0) + self.sky.setDepthWrite(0) + self.sky.setColor(0.5, 0.5, 0.5, 1) + self.sky.setBin('background', 100) + self.sky.setFogOff() + self.sky.reparentTo(camera) + self.sky.setTransparency(TransparencyAttrib.MDual, 1) + fadeIn = self.sky.colorScaleInterval(1.5, Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0.25), blendType='easeInOut') + fadeIn.start() + self.sky.setZ(0.0) + self.sky.setHpr(0.0, 0.0, 0.0) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot | CompassEffect.PZ) + self.sky.node().setEffect(ce) diff --git a/toontown/hood/TTHoodDataAI.py b/toontown/hood/TTHoodDataAI.py new file mode 100644 index 0000000..e26851b --- /dev/null +++ b/toontown/hood/TTHoodDataAI.py @@ -0,0 +1,53 @@ +from direct.directnotify import DirectNotifyGlobal +import HoodDataAI +from toontown.toonbase import ToontownGlobals +from toontown.safezone import DistributedTrolleyAI +from toontown.safezone import TTTreasurePlannerAI +from toontown.classicchars import DistributedMickeyAI +from toontown.safezone import ButterflyGlobals +from direct.task import Task + +class TTHoodDataAI(HoodDataAI.HoodDataAI): + notify = DirectNotifyGlobal.directNotify.newCategory('TTHoodDataAI') + + def __init__(self, air, zoneId=None): + hoodId = ToontownGlobals.ToontownCentral + if zoneId == None: + zoneId = hoodId + HoodDataAI.HoodDataAI.__init__(self, air, zoneId, hoodId) + return + + def startup(self): + HoodDataAI.HoodDataAI.startup(self) + trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + trolley.generateWithRequired(self.zoneId) + trolley.start() + self.addDistObj(trolley) + self.trolley = trolley + self.treasurePlanner = TTTreasurePlannerAI.TTTreasurePlannerAI(self.zoneId) + self.treasurePlanner.start() + self.classicChar = DistributedMickeyAI.DistributedMickeyAI(self.air) + self.classicChar.generateWithRequired(self.zoneId) + self.classicChar.start() + self.addDistObj(self.classicChar) + self.createButterflies(ButterflyGlobals.TTC) + if simbase.blinkTrolley: + taskMgr.doMethodLater(0.5, self._deleteTrolley, 'deleteTrolley') + messenger.send('TTHoodSpawned', [self]) + + def shutdown(self): + HoodDataAI.HoodDataAI.shutdown(self) + messenger.send('TTHoodDestroyed', [self]) + + def _deleteTrolley(self, task): + self.trolley.requestDelete() + taskMgr.doMethodLater(0.5, self._createTrolley, 'createTrolley') + return Task.done + + def _createTrolley(self, task): + trolley = DistributedTrolleyAI.DistributedTrolleyAI(self.air) + trolley.generateWithRequired(self.zoneId) + trolley.start() + self.trolley = trolley + taskMgr.doMethodLater(0.5, self._deleteTrolley, 'deleteTrolley') + return Task.done diff --git a/toontown/hood/ToonHood.py b/toontown/hood/ToonHood.py new file mode 100644 index 0000000..ed86084 --- /dev/null +++ b/toontown/hood/ToonHood.py @@ -0,0 +1,193 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.ToontownMsgTypes import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.minigame import Purchase +from otp.avatar import DistributedAvatar +import Hood +from toontown.building import SuitInterior +from toontown.cogdominium import CogdoInterior +from toontown.toon.Toon import teleportDebug + +class ToonHood(Hood.Hood): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonHood') + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + Hood.Hood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.suitInteriorDoneEvent = 'suitInteriorDone' + self.minigameDoneEvent = 'minigameDone' + self.safeZoneLoaderClass = None + self.townLoaderClass = None + self.fsm = ClassicFSM.ClassicFSM('Hood', [State.State('start', self.enterStart, self.exitStart, ['townLoader', 'safeZoneLoader']), + State.State('townLoader', self.enterTownLoader, self.exitTownLoader, ['quietZone', + 'safeZoneLoader', + 'suitInterior', + 'cogdoInterior']), + State.State('safeZoneLoader', self.enterSafeZoneLoader, self.exitSafeZoneLoader, ['quietZone', + 'suitInterior', + 'cogdoInterior', + 'townLoader', + 'minigame']), + State.State('purchase', self.enterPurchase, self.exitPurchase, ['quietZone', 'minigame', 'safeZoneLoader']), + State.State('suitInterior', self.enterSuitInterior, self.exitSuitInterior, ['quietZone', 'townLoader', 'safeZoneLoader']), + State.State('cogdoInterior', self.enterCogdoInterior, self.exitCogdoInterior, ['quietZone', 'townLoader', 'safeZoneLoader']), + State.State('minigame', self.enterMinigame, self.exitMinigame, ['purchase']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['safeZoneLoader', + 'townLoader', + 'suitInterior', + 'cogdoInterior', + 'minigame']), + State.State('final', self.enterFinal, self.exitFinal, [])], 'start', 'final') + self.fsm.enterInitialState() + return + + def load(self): + Hood.Hood.load(self) + + def unload(self): + del self.safeZoneLoaderClass + del self.townLoaderClass + Hood.Hood.unload(self) + + def loadLoader(self, requestStatus): + loaderName = requestStatus['loader'] + if loaderName == 'safeZoneLoader': + self.loader = self.safeZoneLoaderClass(self, self.fsm.getStateNamed('safeZoneLoader'), self.loaderDoneEvent) + self.loader.load() + elif loaderName == 'townLoader': + self.loader = self.townLoaderClass(self, self.fsm.getStateNamed('townLoader'), self.loaderDoneEvent) + self.loader.load(requestStatus['zoneId']) + + def enterTownLoader(self, requestStatus): + teleportDebug(requestStatus, 'ToonHood.enterTownLoader, status=%s' % (requestStatus,)) + self.accept(self.loaderDoneEvent, self.handleTownLoaderDone) + self.loader.enter(requestStatus) + self.spawnTitleText(requestStatus['zoneId']) + + def exitTownLoader(self): + taskMgr.remove('titleText') + self.hideTitleText() + self.ignore(self.loaderDoneEvent) + self.loader.exit() + self.loader.unload() + del self.loader + + def handleTownLoaderDone(self): + doneStatus = self.loader.getDoneStatus() + teleportDebug(doneStatus, 'handleTownLoaderDone, doneStatus=%s' % (doneStatus,)) + if self.isSameHood(doneStatus): + teleportDebug(doneStatus, 'same hood') + self.fsm.request('quietZone', [doneStatus]) + else: + teleportDebug(doneStatus, 'different hood') + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def enterPurchase(self, pointsAwarded, playerMoney, playerIds, playerStates, remain, metagameRound = -1, votesArray = None): + messenger.send('enterSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 0 + base.localAvatar.laffMeter.start() + self.purchaseDoneEvent = 'purchaseDone' + self.accept(self.purchaseDoneEvent, self.handlePurchaseDone) + self.purchase = Purchase.Purchase(base.localAvatar, pointsAwarded, playerMoney, playerIds, playerStates, remain, self.purchaseDoneEvent, metagameRound, votesArray) + self.purchase.load() + self.purchase.enter() + + def exitPurchase(self): + messenger.send('exitSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 1 + base.localAvatar.laffMeter.stop() + self.ignore(self.purchaseDoneEvent) + self.purchase.exit() + self.purchase.unload() + del self.purchase + + def handlePurchaseDone(self): + doneStatus = self.purchase.getDoneStatus() + if doneStatus['where'] == 'playground': + self.fsm.request('quietZone', [{'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'hoodId': self.hoodId, + 'zoneId': self.hoodId, + 'shardId': None, + 'avId': -1}]) + elif doneStatus['loader'] == 'minigame': + self.fsm.request('minigame') + else: + self.notify.error('handlePurchaseDone: unknown mode') + return + + def enterSuitInterior(self, requestStatus = None): + self.placeDoneEvent = 'suit-interior-done' + self.acceptOnce(self.placeDoneEvent, self.handleSuitInteriorDone) + self.place = SuitInterior.SuitInterior(self, self.fsm, self.placeDoneEvent) + self.place.load() + self.place.enter(requestStatus) + base.cr.playGame.setPlace(self.place) + + def exitSuitInterior(self): + self.ignore(self.placeDoneEvent) + del self.placeDoneEvent + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleSuitInteriorDone(self): + doneStatus = self.place.getDoneStatus() + if self.isSameHood(doneStatus): + self.fsm.request('quietZone', [doneStatus]) + else: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def enterCogdoInterior(self, requestStatus = None): + self.placeDoneEvent = 'cogdo-interior-done' + self.acceptOnce(self.placeDoneEvent, self.handleCogdoInteriorDone) + self.place = CogdoInterior.CogdoInterior(self, self.fsm, self.placeDoneEvent) + self.place.load() + self.place.enter(requestStatus) + base.cr.playGame.setPlace(self.place) + + def exitCogdoInterior(self): + self.ignore(self.placeDoneEvent) + del self.placeDoneEvent + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleCogdoInteriorDone(self): + doneStatus = self.place.getDoneStatus() + if self.isSameHood(doneStatus): + self.fsm.request('quietZone', [doneStatus]) + else: + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def enterMinigame(self, ignoredParameter = None): + messenger.send('enterSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 0 + base.localAvatar.laffMeter.start() + base.cr.forbidCheesyEffects(1) + self.acceptOnce(self.minigameDoneEvent, self.handleMinigameDone) + return None + + def exitMinigame(self): + messenger.send('exitSafeZone') + DistributedAvatar.DistributedAvatar.HpTextEnabled = 1 + base.localAvatar.laffMeter.stop() + base.cr.forbidCheesyEffects(0) + self.ignore(self.minigameDoneEvent) + minigameState = self.fsm.getStateNamed('minigame') + for childFSM in minigameState.getChildren(): + minigameState.removeChild(childFSM) + + def handleMinigameDone(self): + return None diff --git a/toontown/hood/TrashcanInteractiveProp.py b/toontown/hood/TrashcanInteractiveProp.py new file mode 100644 index 0000000..36a726b --- /dev/null +++ b/toontown/hood/TrashcanInteractiveProp.py @@ -0,0 +1,184 @@ +from direct.actor import Actor +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import InteractiveAnimatedProp +from toontown.hood import GenericAnimatedProp +from toontown.toonbase import ToontownGlobals, ToontownBattleGlobals, TTLocalizer + +class TrashcanInteractiveProp(InteractiveAnimatedProp.InteractiveAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('TrashcanInteractiveProp') + BattleCheerText = TTLocalizer.InteractivePropTrackBonusTerms[ToontownBattleGlobals.HEAL_TRACK] + ZoneToIdles = {ToontownGlobals.ToontownCentral: (('tt_a_ara_ttc_trashcan_idleTake2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_trashcan_idleHiccup0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_trashcan_idleLook1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_ttc_trashcan_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DonaldsDock: (('tt_a_ara_dod_trashcan_idleBounce2', + 3, + 10, + 'tt_a_ara_dod_trashcan_idle0settle', + 3, + 10), + ('tt_a_ara_dod_trashcan_idle0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dod_trashcan_idle1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dod_trashcan_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DaisyGardens: (('tt_a_ara_dga_trashcan_idleTake2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dga_trashcan_idleHiccup0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dga_trashcan_idleLook1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_dga_trashcan_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.MinniesMelodyland: (('tt_a_ara_mml_trashcan_idleBounce0', + 3, + 10, + 'tt_a_ara_mml_trashcan_idle0settle', + 3, + 10), + ('tt_a_ara_mml_trashcan_idleLook1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_mml_trashcan_idleHelicopter2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_mml_trashcan_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.TheBrrrgh: (('tt_a_ara_tbr_trashcan_idleShiver1', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_trashcan_idleSneeze2', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_trashcan_idle0', + 1, + 1, + None, + 3, + 10), + ('tt_a_ara_tbr_trashcan_idleAwesome3', + 1, + 1, + None, + 3, + 10)), + ToontownGlobals.DonaldsDreamland: (('tt_a_ara_ddl_trashcan_idleSleep0', + 3, + 10, + None, + 0, + 0), + ('tt_a_ara_ddl_trashcan_idleShake2', + 1, + 1, + None, + 0, + 0), + ('tt_a_ara_ddl_trashcan_idleSnore1', + 1, + 1, + None, + 0, + 0), + ('tt_a_ara_ddl_trashcan_idleAwesome3', + 1, + 1, + None, + 0, + 0))} + ZoneToIdleIntoFightAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_trashcan_idleIntoFight', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_trashcan_idleIntoFight', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_trashcan_idleIntoFight', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_trashcan_idleIntoFight', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_trashcan_idleIntoFight', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_trashcan_idleIntoFight'} + ZoneToVictoryAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_trashcan_victoryDance', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_trashcan_victoryDance', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_trashcan_victoryDance', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_trashcan_victoryDance', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_trashcan_victoryDance', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_trashcan_victoryDance'} + ZoneToSadAnims = {ToontownGlobals.ToontownCentral: 'tt_a_ara_ttc_trashcan_fightSad', + ToontownGlobals.DonaldsDock: 'tt_a_ara_dod_trashcan_fightSad', + ToontownGlobals.DaisyGardens: 'tt_a_ara_dga_trashcan_fightSad', + ToontownGlobals.MinniesMelodyland: 'tt_a_ara_mml_trashcan_fightSad', + ToontownGlobals.TheBrrrgh: 'tt_a_ara_tbr_trashcan_fightSad', + ToontownGlobals.DonaldsDreamland: 'tt_a_ara_ddl_trashcan_fightSad'} + ZoneToFightAnims = {ToontownGlobals.ToontownCentral: ('tt_a_ara_ttc_trashcan_fightBoost', 'tt_a_ara_ttc_trashcan_fightCheer', 'tt_a_ara_ttc_trashcan_fightIdle'), + ToontownGlobals.DonaldsDock: ('tt_a_ara_dod_trashcan_fightBoost', 'tt_a_ara_dod_trashcan_fightCheer', 'tt_a_ara_dod_trashcan_fightIdle'), + ToontownGlobals.DaisyGardens: ('tt_a_ara_dga_trashcan_fightBoost', 'tt_a_ara_dga_trashcan_fightCheer', 'tt_a_ara_dga_trashcan_fightIdle'), + ToontownGlobals.MinniesMelodyland: ('tt_a_ara_mml_trashcan_fightBoost', + 'tt_a_ara_mml_trashcan_fightCheer0', + 'tt_a_ara_mml_trashcan_fightCheer1', + 'tt_a_ara_mml_trashcan_fightIdle'), + ToontownGlobals.TheBrrrgh: ('tt_a_ara_tbr_trashcan_fightBoost', 'tt_a_ara_tbr_trashcan_fightCheer', 'tt_a_ara_tbr_trashcan_fightIdle'), + ToontownGlobals.DonaldsDreamland: ('tt_a_ara_ddl_trashcan_fightBoost', 'tt_a_ara_ddl_trashcan_fightCheer', 'tt_a_ara_ddl_trashcan_fightIdle')} + IdlePauseTime = base.config.GetFloat('prop-idle-pause-time', 0.0) + + def __init__(self, node): + InteractiveAnimatedProp.InteractiveAnimatedProp.__init__(self, node, ToontownGlobals.TRASHCANS_BUFF_BATTLES) diff --git a/toontown/hood/TrashcanOneAnimatedProp.py b/toontown/hood/TrashcanOneAnimatedProp.py new file mode 100644 index 0000000..68d4331 --- /dev/null +++ b/toontown/hood/TrashcanOneAnimatedProp.py @@ -0,0 +1,32 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class TrashcanOneAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('TrashcanOneAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_dga_trashcan_firstMoveLidFlip1', 40 * PauseTimeMult), + 1: ('tt_a_ara_dga_trashcan_firstMoveStruggle', 20 * PauseTimeMult), + 2: ('tt_a_ara_dga_trashcan_firstMoveLidFlip2', 10 * PauseTimeMult), + 3: ('tt_a_ara_dga_trashcan_firstMoveJump', 8 * PauseTimeMult), + 4: ('tt_a_ara_dga_trashcan_firstMoveLidFlip3', 6 * PauseTimeMult), + 5: ('tt_a_ara_dga_trashcan_firstMoveJumpHit', 4 * PauseTimeMult), + 6: ('tt_a_ara_dga_trashcan_firstMoveJumpJuggle', 2 * PauseTimeMult)} + PhaseWeStartAnimating = 3 + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'trashcan', self.PhaseInfo, ToontownGlobals.TRASHCAN_ZERO_HOLIDAY) + + def startIfNeeded(self): + try: + self.curPhase = self.getPhaseToRun() + if self.curPhase >= self.PhaseWeStartAnimating: + self.request('DoAnim') + except: + pass + + def handleNewPhase(self, newPhase): + if newPhase < self.PhaseWeStartAnimating: + self.request('Off') + else: + self.startIfNeeded() diff --git a/toontown/hood/TrashcanTwoAnimatedProp.py b/toontown/hood/TrashcanTwoAnimatedProp.py new file mode 100644 index 0000000..f0b86ce --- /dev/null +++ b/toontown/hood/TrashcanTwoAnimatedProp.py @@ -0,0 +1,32 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class TrashcanTwoAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('TrashcanTwoAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_dga_trashcan_firstMoveLidFlip1', 40 * PauseTimeMult), + 1: ('tt_a_ara_dga_trashcan_firstMoveStruggle', 20 * PauseTimeMult), + 2: ('tt_a_ara_dga_trashcan_firstMoveLidFlip2', 10 * PauseTimeMult), + 3: ('tt_a_ara_dga_trashcan_firstMoveJump', 8 * PauseTimeMult), + 4: ('tt_a_ara_dga_trashcan_firstMoveLidFlip3', 6 * PauseTimeMult), + 5: ('tt_a_ara_dga_trashcan_firstMoveJumpHit', 4 * PauseTimeMult), + 6: ('tt_a_ara_dga_trashcan_firstMoveJumpJuggle', 2 * PauseTimeMult)} + PhaseWeStartAnimating = 5 + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'trashcan', self.PhaseInfo, ToontownGlobals.TRASHCAN_ZERO_HOLIDAY) + + def startIfNeeded(self): + try: + self.curPhase = self.getPhaseToRun() + if self.curPhase >= self.PhaseWeStartAnimating: + self.request('DoAnim') + except: + pass + + def handleNewPhase(self, newPhase): + if newPhase < self.PhaseWeStartAnimating: + self.request('Off') + else: + self.startIfNeeded() diff --git a/toontown/hood/TrashcanZeroAnimatedProp.py b/toontown/hood/TrashcanZeroAnimatedProp.py new file mode 100644 index 0000000..7b432ad --- /dev/null +++ b/toontown/hood/TrashcanZeroAnimatedProp.py @@ -0,0 +1,17 @@ +from toontown.hood import ZeroAnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class TrashcanZeroAnimatedProp(ZeroAnimatedProp.ZeroAnimatedProp): + notify = DirectNotifyGlobal.directNotify.newCategory('TrashcanZeroAnimatedProp') + PauseTimeMult = base.config.GetFloat('zero-pause-mult', 1.0) + PhaseInfo = {0: ('tt_a_ara_dga_trashcan_firstMoveLidFlip1', 40 * PauseTimeMult), + 1: ('tt_a_ara_dga_trashcan_firstMoveStruggle', 20 * PauseTimeMult), + 2: ('tt_a_ara_dga_trashcan_firstMoveLidFlip2', 10 * PauseTimeMult), + 3: ('tt_a_ara_dga_trashcan_firstMoveJump', 8 * PauseTimeMult), + 4: ('tt_a_ara_dga_trashcan_firstMoveLidFlip3', 6 * PauseTimeMult), + 5: ('tt_a_ara_dga_trashcan_firstMoveJumpHit', 4 * PauseTimeMult), + 6: ('tt_a_ara_dga_trashcan_firstMoveJumpJuggle', 2 * PauseTimeMult)} + + def __init__(self, node): + ZeroAnimatedProp.ZeroAnimatedProp.__init__(self, node, 'trashcan', self.PhaseInfo, ToontownGlobals.TRASHCAN_ZERO_HOLIDAY) diff --git a/toontown/hood/TrialerForceAcknowledge.py b/toontown/hood/TrialerForceAcknowledge.py new file mode 100644 index 0000000..f4f3a84 --- /dev/null +++ b/toontown/hood/TrialerForceAcknowledge.py @@ -0,0 +1,51 @@ +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TeaserPanel + +class TrialerForceAcknowledge: + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.dialog = None + return + + def enter(self, destHood): + doneStatus = {} + + def letThrough(self = self, doneStatus = doneStatus): + doneStatus['mode'] = 'pass' + messenger.send(self.doneEvent, [doneStatus]) + + if not base.restrictTrialers: + letThrough() + return + if base.roamingTrialers: + letThrough() + return + if base.cr.isPaid(): + letThrough() + return + if ZoneUtil.getCanonicalHoodId(destHood) in (ToontownGlobals.ToontownCentral, ToontownGlobals.MyEstate, ToontownGlobals.GoofySpeedway): + letThrough() + return + else: + try: + base.localAvatar.b_setAnimState('neutral', 1) + except: + pass + + doneStatus['mode'] = 'fail' + self.doneStatus = doneStatus + self.dialog = TeaserPanel.TeaserPanel(pageName='otherHoods', doneFunc=self.handleOk) + + def exit(self): + if self.dialog: + self.dialog.cleanup() + self.dialog.unload() + self.dialog = None + return + + def handleOk(self): + messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/hood/TutorialHood.py b/toontown/hood/TutorialHood.py new file mode 100644 index 0000000..f840702 --- /dev/null +++ b/toontown/hood/TutorialHood.py @@ -0,0 +1,37 @@ +from pandac.PandaModules import * +import ToonHood +from toontown.town import TutorialTownLoader +from toontown.toonbase.ToontownGlobals import * +import SkyUtil + +class TutorialHood(ToonHood.ToonHood): + + def __init__(self, parentFSM, doneEvent, dnaStore, hoodId): + ToonHood.ToonHood.__init__(self, parentFSM, doneEvent, dnaStore, hoodId) + self.id = Tutorial + self.townLoaderClass = TutorialTownLoader.TutorialTownLoader + self.safeZoneLoaderClass = None + self.storageDNAFile = None + self.skyFile = 'phase_3.5/models/props/TT_sky' + self.titleColor = (1.0, 0.5, 0.4, 1.0) + return + + def load(self): + ToonHood.ToonHood.load(self) + self.parentFSM.getStateNamed('TutorialHood').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('TutorialHood').removeChild(self.fsm) + ToonHood.ToonHood.unload(self) + + def enter(self, *args): + ToonHood.ToonHood.enter(self, *args) + + def exit(self): + ToonHood.ToonHood.exit(self) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + SkyUtil.startCloudSky(self) diff --git a/toontown/hood/ZeroAnimatedProp.py b/toontown/hood/ZeroAnimatedProp.py new file mode 100644 index 0000000..ab7070f --- /dev/null +++ b/toontown/hood/ZeroAnimatedProp.py @@ -0,0 +1,177 @@ +import types +import math +from direct.interval.IntervalGlobal import Sequence, Wait, ActorInterval, Func, SoundInterval, Parallel +from direct.task import Task +from direct.fsm import FSM +from direct.showbase.PythonUtil import weightedChoice +from toontown.hood import GenericAnimatedProp +from toontown.hood import AnimatedProp +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal + +class ZeroAnimatedProp(GenericAnimatedProp.GenericAnimatedProp, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('ZeroAnimatedProp') + + def __init__(self, node, propString, phaseInfo, holidayId): + self.propString = propString + self.phaseInfo = phaseInfo + self.holidayId = holidayId + GenericAnimatedProp.GenericAnimatedProp.__init__(self, node) + FSM.FSM.__init__(self, '%sZeroAnimPropFsm' % self.propString) + self.node.unloadAnims('anim') + self.loadPhaseAnims() + self.phaseIvals = [] + self.curIval = None + self.curPhase = -1 + self.okToStartNextAnim = False + return + + def delete(self): + self.exit() + GenericAnimatedProp.GenericAnimatedProp.delete(self) + + def loadPhaseAnims(self): + animDict = {} + for key, info in self.phaseInfo.iteritems(): + if type(info[0]) == types.TupleType: + for index, anims in enumerate(info[0]): + fullPath = self.path + '/' + anims + animName = 'phase%d_%d' % (key, index) + animDict[animName] = fullPath + + else: + animName = 'phase%d' % key + fullPath = self.path + '/' + info[0] + animDict[animName] = fullPath + + self.node.loadAnims(animDict) + + def createPhaseIntervals(self): + if self.phaseIvals: + self.notify.debug('not creating phase ivals again') + return + self.phaseIvals = [] + for key, info in self.phaseInfo.iteritems(): + self.notify.debug('key=%s' % key) + if type(info[0]) == types.TupleType: + ival = Sequence() + for index, anims in enumerate(info[0]): + animName = 'phase%d_%d' % (key, index) + animIval = self.node.actorInterval(animName) + animIvalDuration = animIval.getDuration() + soundIval = self.createSoundInterval(anims, animIvalDuration) + soundIvalDuration = soundIval.getDuration() + animAndSound = Parallel(soundIval, animIval) + ival.append(animAndSound) + + self.phaseIvals.append(ival) + else: + animName = 'phase%d' % key + animIval = self.node.actorInterval('phase%d' % key) + animIvalDuration = animIval.getDuration() + soundIval = self.createSoundInterval(info[0], animIvalDuration) + soundIvalDuration = soundIval.getDuration() + ival = Parallel(animIval, soundIval) + self.phaseIvals.append(ival) + + def enter(self): + self.node.postFlatten() + self.createPhaseIntervals() + AnimatedProp.AnimatedProp.enter(self) + defaultAnim = self.node.getAnimControl('anim') + numFrames = defaultAnim.getNumFrames() + self.node.pose('phase0', 0) + self.accept('%sZeroPhase' % self.propString, self.handleNewPhase) + self.accept('%sZeroIsRunning' % self.propString, self.handleNewIsRunning) + self.startIfNeeded() + + def startIfNeeded(self): + try: + self.curPhase = self.getPhaseToRun() + if self.curPhase >= 0: + self.request('DoAnim') + except: + pass + + def chooseAnimToRun(self): + result = self.curPhase + if base.config.GetBool('anim-props-randomized', True): + pairs = [] + for i in xrange(self.curPhase + 1): + pairs.append((math.pow(2, i), i)) + + sum = math.pow(2, self.curPhase + 1) - 1 + result = weightedChoice(pairs, sum=sum) + self.notify.debug('chooseAnimToRun curPhase=%s pairs=%s result=%s' % (self.curPhase, pairs, result)) + return result + + def createAnimSequence(self, animPhase): + result = Sequence(self.phaseIvals[animPhase], Wait(self.phaseInfo[self.curPhase][1]), Func(self.startNextAnim)) + return result + + def startNextAnim(self): + self.notify.debug('startNextAnim self.okToStartNextAnim=%s' % self.okToStartNextAnim) + self.curIval = None + if self.okToStartNextAnim: + self.notify.debug('got pass okToStartNextAnim') + whichAnim = self.chooseAnimToRun() + self.notify.debug('whichAnim=%s' % whichAnim) + self.lastPlayingAnimPhase = whichAnim + self.curIval = self.createAnimSequence(whichAnim) + self.notify.debug('starting curIval of length %s' % self.curIval.getDuration()) + self.curIval.start() + else: + self.notify.debug('false self.okToStartNextAnim=%s' % self.okToStartNextAnim) + return + + def enterDoAnim(self): + self.notify.debug('enterDoAnim curPhase=%d' % self.curPhase) + self.okToStartNextAnim = True + self.startNextAnim() + + def exitDoAnim(self): + self.notify.debug('exitDoAnim curPhase=%d' % self.curPhase) + self.okToStartNextAnim = False + self.curIval.finish() + self.curIval = None + return + + def getPhaseToRun(self): + result = -1 + enoughInfoToRun = False + if base.cr.newsManager.isHolidayRunning(self.holidayId): + zeroMgrString = '%sZeroMgr' % self.propString + if hasattr(base.cr, zeroMgrString): + zeroMgr = eval('base.cr.%s' % zeroMgrString) + if not zeroMgr.isDisabled(): + enoughInfoToRun = True + else: + self.notify.debug('isDisabled = %s' % zeroMgr.isDisabled()) + else: + self.notify.debug('base.cr does not have %s' % zeroMgrString) + else: + self.notify.debug('holiday is not running') + self.notify.debug('enoughInfoToRun = %s' % enoughInfoToRun) + if enoughInfoToRun and zeroMgr.getIsRunning(): + curPhase = zeroMgr.getCurPhase() + if curPhase >= len(self.phaseIvals): + curPhase = len(self.phaseIvals) - 1 + self.notify.warning('zero mgr says to go to phase %d, but we only have %d ivals. forcing curPhase to %d' % (curPhase, len(self.phaseIvals), curPhase)) + result = curPhase + return result + + def exit(self): + self.okToStartNextAnim = False + self.ignore('%sZeroPhase' % self.propString) + self.ignore('%sZeroIsRunning' % self.propString) + GenericAnimatedProp.GenericAnimatedProp.exit(self) + self.request('Off') + + def handleNewPhase(self, newPhase): + self.startIfNeeded() + + def handleNewIsRunning(self, isRunning): + if isRunning: + self.startIfNeeded() + else: + self.request('Off') diff --git a/toontown/hood/ZoneUtil.py b/toontown/hood/ZoneUtil.py new file mode 100644 index 0000000..de22d9b --- /dev/null +++ b/toontown/hood/ZoneUtil.py @@ -0,0 +1,239 @@ +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +zoneUtilNotify = DirectNotifyGlobal.directNotify.newCategory('ZoneUtil') +tutorialDict = None + +def isGoofySpeedwayZone(zoneId): + return zoneId == 8000 + + +def isCogHQZone(zoneId): + return zoneId >= 10000 and zoneId < 15000 + + +def isMintInteriorZone(zoneId): + return zoneId in (CashbotMintIntA, CashbotMintIntB, CashbotMintIntC) + + +def isDynamicZone(zoneId): + return zoneId >= DynamicZonesBegin and zoneId < DynamicZonesEnd + + +def getStreetName(branchId): + global tutorialDict + if tutorialDict: + return StreetNames[20000][-1] + else: + return StreetNames[branchId][-1] + + +def getLoaderName(zoneId): + if tutorialDict: + if zoneId == ToontownCentral: + loaderName = 'safeZoneLoader' + else: + loaderName = 'townLoader' + else: + suffix = zoneId % 1000 + if suffix >= 500: + suffix -= 500 + if isCogHQZone(zoneId): + loaderName = 'cogHQLoader' + elif suffix < 100: + loaderName = 'safeZoneLoader' + else: + loaderName = 'townLoader' + return loaderName + + +def getBranchLoaderName(zoneId): + return getLoaderName(getBranchZone(zoneId)) + + +def getSuitWhereName(zoneId): + where = getWhereName(zoneId, 0) + return where + + +def getToonWhereName(zoneId): + where = getWhereName(zoneId, 1) + return where + + +def isPlayground(zoneId): + whereName = getWhereName(zoneId, False) + if whereName == 'cogHQExterior': + return True + else: + return zoneId % 1000 == 0 and zoneId < DynamicZonesBegin + + +def isPetshop(zoneId): + if zoneId == 2522 or zoneId == 1510 or zoneId == 3511 or zoneId == 4508 or zoneId == 5505 or zoneId == 9508: + return True + return False + + +def getWhereName(zoneId, isToon): + if tutorialDict: + if zoneId in tutorialDict['interiors']: + where = 'toonInterior' + elif zoneId in tutorialDict['exteriors']: + where = 'street' + elif zoneId == ToontownCentral or zoneId == WelcomeValleyToken: + where = 'playground' + else: + zoneUtilNotify.error('No known zone: ' + str(zoneId)) + else: + suffix = zoneId % 1000 + suffix = suffix - suffix % 100 + if isCogHQZone(zoneId): + if suffix == 0: + where = 'cogHQExterior' + elif suffix == 100: + where = 'cogHQLobby' + elif suffix == 200: + where = 'factoryExterior' + elif getHoodId(zoneId) == LawbotHQ and suffix in (300, 400, 500, 600): + where = 'stageInterior' + elif getHoodId(zoneId) == BossbotHQ and suffix in (500, 600, 700): + where = 'countryClubInterior' + elif suffix >= 500: + if getHoodId(zoneId) == SellbotHQ: + where = 'factoryInterior' + elif getHoodId(zoneId) == CashbotHQ: + where = 'mintInterior' + else: + zoneUtilNotify.error('unknown cogHQ interior for hood: ' + str(getHoodId(zoneId))) + else: + zoneUtilNotify.error('unknown cogHQ where: ' + str(zoneId)) + elif suffix == 0: + where = 'playground' + elif suffix >= 500: + if isToon: + where = 'toonInterior' + else: + where = 'suitInterior' + else: + where = 'street' + return where + + +def getBranchZone(zoneId): + if tutorialDict: + branchId = tutorialDict['branch'] + else: + branchId = zoneId - zoneId % 100 + if not isCogHQZone(zoneId): + if zoneId % 1000 >= 500: + branchId -= 500 + return branchId + + +def getCanonicalBranchZone(zoneId): + return getBranchZone(getCanonicalZoneId(zoneId)) + + +def isWelcomeValley(zoneId): + return zoneId == WelcomeValleyToken or zoneId >= WelcomeValleyBegin and zoneId < WelcomeValleyEnd + + +def getCanonicalZoneId(zoneId): + if zoneId == WelcomeValleyToken: + zoneId = ToontownCentral + elif zoneId >= WelcomeValleyBegin and zoneId < WelcomeValleyEnd: + zoneId = zoneId % 2000 + if zoneId < 1000: + zoneId = zoneId + ToontownCentral + else: + zoneId = zoneId - 1000 + GoofySpeedway + return zoneId + + +def getTrueZoneId(zoneId, currentZoneId): + if zoneId >= WelcomeValleyBegin and zoneId < WelcomeValleyEnd or zoneId == WelcomeValleyToken: + zoneId = getCanonicalZoneId(zoneId) + if currentZoneId >= WelcomeValleyBegin and currentZoneId < WelcomeValleyEnd: + hoodId = getHoodId(zoneId) + offset = currentZoneId - currentZoneId % 2000 + if hoodId == ToontownCentral: + return zoneId - ToontownCentral + offset + elif hoodId == GoofySpeedway: + return zoneId - GoofySpeedway + offset + 1000 + return zoneId + + +def getHoodId(zoneId): + if tutorialDict: + hoodId = Tutorial + else: + hoodId = zoneId - zoneId % 1000 + return hoodId + + +def getSafeZoneId(zoneId): + hoodId = getHoodId(zoneId) + if hoodId in HQToSafezone: + hoodId = HQToSafezone[hoodId] + return hoodId + + +def getCanonicalHoodId(zoneId): + return getHoodId(getCanonicalZoneId(zoneId)) + + +def getCanonicalSafeZoneId(zoneId): + return getSafeZoneId(getCanonicalZoneId(zoneId)) + + +def isInterior(zoneId): + if tutorialDict: + if zoneId in tutorialDict['interiors']: + r = 1 + else: + r = 0 + else: + r = zoneId % 1000 >= 500 + return r + + +def overrideOn(branch, exteriorList, interiorList): + global tutorialDict + if tutorialDict: + zoneUtilNotify.warning('setTutorialDict: tutorialDict is already set!') + tutorialDict = {'branch': branch, + 'exteriors': exteriorList, + 'interiors': interiorList} + + +def overrideOff(): + global tutorialDict + tutorialDict = None + return + + +def getWakeInfo(hoodId = None, zoneId = None): + wakeWaterHeight = 0 + showWake = 0 + try: + if hoodId is None: + hoodId = base.cr.playGame.getPlaceId() + if zoneId is None: + zoneId = base.cr.playGame.getPlace().getZoneId() + canonicalZoneId = getCanonicalZoneId(zoneId) + if canonicalZoneId == DonaldsDock: + wakeWaterHeight = DDWakeWaterHeight + showWake = 1 + elif canonicalZoneId == ToontownCentral: + wakeWaterHeight = TTWakeWaterHeight + showWake = 1 + elif canonicalZoneId == OutdoorZone: + wakeWaterHeight = OZWakeWaterHeight + showWake = 1 + elif hoodId == MyEstate: + wakeWaterHeight = EstateWakeWaterHeight + showWake = 1 + except AttributeError: + pass + + return (showWake, wakeWaterHeight) diff --git a/toontown/hood/__init__.py b/toontown/hood/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/launcher/DownloadForceAcknowledge.py b/toontown/launcher/DownloadForceAcknowledge.py new file mode 100644 index 0000000..4154dda --- /dev/null +++ b/toontown/launcher/DownloadForceAcknowledge.py @@ -0,0 +1,42 @@ +from pandac.PandaModules import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +import random + +class DownloadForceAcknowledge: + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.dialog = None + return + + def enter(self, phase): + doneStatus = {} + if launcher.getPhaseComplete(phase): + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + else: + try: + base.localAvatar.b_setAnimState('neutral', 1) + except: + pass + + doneStatus['mode'] = 'incomplete' + self.doneStatus = doneStatus + percentComplete = base.launcher.getPercentPhaseComplete(phase) + phaseName = TTLocalizer.LauncherPhaseNames[phase] + verb = random.choice(TTLocalizer.DownloadForceAcknowledgeVerbList) + msg = TTLocalizer.DownloadForceAcknowledgeMsg % {'phase': phaseName, + 'verb': verb} + self.dialog = TTDialog.TTDialog(text=msg, command=self.handleOk, style=TTDialog.Acknowledge) + self.dialog.show() + + def exit(self): + if self.dialog: + self.dialog.hide() + self.dialog.cleanup() + self.dialog = None + return + + def handleOk(self, value): + messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/launcher/QuickLauncher.py b/toontown/launcher/QuickLauncher.py new file mode 100644 index 0000000..e54eff2 --- /dev/null +++ b/toontown/launcher/QuickLauncher.py @@ -0,0 +1,403 @@ +import sys +import os +import time +import string +import bz2 +import random +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 +from direct.directnotify.DirectNotifyGlobal import * +from pandac.PandaModules import * +from otp.launcher.LauncherBase import LauncherBase +from toontown.toonbase import TTLocalizer + +class QuickLauncher(LauncherBase): + GameName = 'Toontown' + ArgCount = 3 + LauncherPhases = [1, + 2, + 3, + 3.5, + 4, + 5, + 5.5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13] + TmpOverallMap = [0.01, + 0.01, + 0.23, + 0.15, + 0.12, + 0.17, + 0.08, + 0.07, + 0.05, + 0.05, + 0.017, + 0.011, + 0.01, + 0.012, + 0.01] + ForegroundSleepTime = 0.001 + Localizer = TTLocalizer + DecompressMultifiles = True + launcherFileDbFilename = 'patcher.ver?%s' % random.randint(1, 1000000000) + CompressionExt = 'bz2' + PatchExt = 'pch' + + def __init__(self): + print 'Running: ToontownQuickLauncher' + self.toontownBlueKey = 'TOONTOWN_BLUE' + self.launcherMessageKey = 'LAUNCHER_MESSAGE' + self.game1DoneKey = 'GAME1_DONE' + self.game2DoneKey = 'GAME2_DONE' + self.tutorialCompleteKey = 'TUTORIAL_DONE' + LauncherBase.__init__(self) + self.useTTSpecificLogin = config.GetBool('tt-specific-login', 0) + if self.useTTSpecificLogin: + self.toontownPlayTokenKey = 'LOGIN_TOKEN' + else: + self.toontownPlayTokenKey = 'PLAYTOKEN' + print 'useTTSpecificLogin=%s' % self.useTTSpecificLogin + self.contentDir = '/' + self.serverDbFileHash = HashVal() + self.launcherFileDbHash = HashVal() + self.DECREASE_BANDWIDTH = 0 + self.httpChannel.setDownloadThrottle(0) + self.webAcctParams = 'WEB_ACCT_PARAMS' + self.parseWebAcctParams() + self.showPhase = -1 + self.maybeStartGame() + self.mainLoop() + + def addDownloadVersion(self, serverFilePath): + url = URLSpec(self.downloadServer) + origPath = url.getPath() + if origPath[-1] == '/': + url.setPath('%s%s' % (origPath, serverFilePath)) + else: + url.setPath('%s/%s' % (origPath, serverFilePath)) + return url + + def downloadLauncherFileDbDone(self): + settings = {} + for line in self.ramfile.readlines(): + if line.find('=') >= 0: + key, value = line.strip().split('=') + settings[key] = value + + self.requiredInstallFiles = [] + if sys.platform == 'win32': + fileList = settings['REQUIRED_INSTALL_FILES'] + elif sys.platform == 'darwin': + fileList = settings['REQUIRED_INSTALL_FILES_OSX'] + else: + self.notify.warning('Unknown sys.platform: %s' % sys.platform) + fileList = settings['REQUIRED_INSTALL_FILES'] + for fileDesc in fileList.split(): + fileName, flag = fileDesc.split(':') + directions = BitMask32(flag) + extract = directions.getBit(0) + required = directions.getBit(1) + optionalDownload = directions.getBit(2) + self.notify.info('fileName: %s, flag:=%s directions=%s, extract=%s required=%s optDownload=%s' % (fileName, + flag, + directions, + extract, + required, + optionalDownload)) + if required: + self.requiredInstallFiles.append(fileName) + + self.notify.info('requiredInstallFiles: %s' % self.requiredInstallFiles) + self.mfDetails = {} + for mfName in self.requiredInstallFiles: + currentVer = settings['FILE_%s.current' % mfName] + details = settings['FILE_%s.%s' % (mfName, currentVer)] + size, hash = details.split() + self.mfDetails[mfName] = (currentVer, int(size), hash) + self.notify.info('mfDetails[%s] = %s' % (mfName, self.mfDetails[mfName])) + + self.resumeInstall() + + def resumeMultifileDownload(self): + curVer, expectedSize, expectedMd5 = self.mfDetails[self.currentMfname] + localFilename = Filename(self.topDir, Filename('_%s.%s.%s' % (self.currentMfname, curVer, self.CompressionExt))) + serverFilename = '%s%s.%s.%s' % (self.contentDir, + self.currentMfname, + curVer, + self.CompressionExt) + if localFilename.exists(): + fileSize = localFilename.getFileSize() + self.notify.info('Previous partial download exists for: %s size=%s' % (localFilename.cStr(), fileSize)) + self.downloadMultifile(serverFilename, localFilename, self.currentMfname, self.downloadMultifileDone, 0, fileSize, self.downloadMultifileWriteToDisk) + else: + self.downloadMultifile(serverFilename, localFilename, self.currentMfname, self.downloadMultifileDone, 0, 0, self.downloadMultifileWriteToDisk) + + def resumeInstall(self): + for self.currentPhaseIndex in range(len(self.LauncherPhases)): + self.currentPhase = self.LauncherPhases[self.currentPhaseIndex] + self.currentPhaseName = self.Localizer.LauncherPhaseNames[self.currentPhase] + self.currentMfname = 'phase_%s.mf' % self.currentPhase + if sys.platform == 'darwin' and (self.currentMfname == 'phase_1.mf' or self.currentMfname == 'phase_2.mf'): + self.currentMfname = 'phase_%sOSX.mf' % self.currentPhase + if self.currentMfname in self.requiredInstallFiles: + self.requiredInstallFiles.remove(self.currentMfname) + else: + self.notify.warning('avoiding crash ValueError: list.remove(x): x not in list') + curVer, expectedSize, expectedMd5 = self.mfDetails[self.currentMfname] + self.curPhaseFile = Filename(self.topDir, Filename(self.currentMfname)) + self.notify.info('working on: %s' % self.curPhaseFile) + if self.curPhaseFile.exists(): + self.notify.info('file exists') + fileSize = self.curPhaseFile.getFileSize() + clientMd5 = HashVal() + clientMd5.hashFile(self.curPhaseFile) + self.notify.info('clientMd5: %s expectedMd5: %s' % (clientMd5, expectedMd5)) + self.notify.info('clientSize: %s expectedSize: %s' % (fileSize, expectedSize)) + if fileSize == expectedSize and clientMd5.asHex() == expectedMd5: + self.notify.info('file is up to date') + self.finalizePhase() + continue + else: + self.notify.info('file is not valid') + self.resumeMultifileDownload() + return + else: + self.notify.info('file does not exist - start download') + self.resumeMultifileDownload() + return + + if not self.requiredInstallFiles: + self.notify.info('ALL PHASES COMPLETE') + messenger.send('launcherAllPhasesComplete') + self.cleanup() + return + raise StandardError, 'Some phases not listed in LauncherPhases: %s' % self.requiredInstallFiles + + def getDecompressMultifile(self, mfname): + if not self.DecompressMultifiles: + self.decompressMultifileDone() + elif 1: + self.notify.info('decompressMultifile: Decompressing multifile: ' + mfname) + curVer, expectedSize, expectedMd5 = self.mfDetails[self.currentMfname] + localFilename = Filename(self.topDir, Filename('_%s.%s.%s' % (mfname, curVer, self.CompressionExt))) + self.decompressMultifile(mfname, localFilename, self.decompressMultifileDone) + else: + self.notify.info('decompressMultifile: Multifile already decompressed: %s' % mfname) + self.decompressMultifileDone() + + 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 = Task(self.decompressMultifileTask) + task.mfname = mfname + task.mfFilename = Filename(self.topDir, Filename('_' + task.mfname)) + task.mfFile = open(task.mfFilename.toOsSpecific(), 'wb') + task.localFilename = localFilename + task.callback = callback + task.lastUpdate = 0 + task.decompressor = bz2.BZ2File(localFilename.toOsSpecific(), 'rb') + taskMgr.add(task, 'launcher-decompressMultifile') + + def decompressMultifileTask(self, task): + data = task.decompressor.read(8192) + if data: + task.mfFile.write(data) + now = self.getTime() + if now - task.lastUpdate >= self.UserUpdateDelay: + task.lastUpdate = now + curSize = task.mfFilename.getFileSize() + curVer, expectedSize, expectedMd5 = self.mfDetails[self.currentMfname] + progress = curSize / float(expectedSize) + 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 + else: + task.mfFile.close() + task.decompressor.close() + unlinked = task.localFilename.unlink() + if not unlinked: + self.notify.warning('unlink failed on file: %s' % task.localFilename.cStr()) + realMf = Filename(self.topDir, Filename(self.currentMfname)) + renamed = task.mfFilename.renameTo(realMf) + if not renamed: + self.notify.warning('rename failed on file: %s' % task.mfFilename.cStr()) + 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()) + if self.dldb: + self.dldb.setClientMultifileDecompressed(task.mfname) + del task.decompressor + task.callback() + del task.callback + return Task.done + + def decompressMultifileDone(self): + self.finalizePhase() + 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)`) + self.resumeInstall() + + def finalizePhase(self): + mfFilename = Filename(self.topDir, Filename(self.currentMfname)) + self.MakeNTFSFilesGlobalWriteable(mfFilename) + vfs = VirtualFileSystem.getGlobalPtr() + vfs.mount(mfFilename, '.', VirtualFileSystem.MFReadOnly) + self.setPercentPhaseComplete(self.currentPhase, 100) + + def getValue(self, key, default = None): + return os.environ.get(key, default) + + def setValue(self, key, value): + os.environ[key] = str(value) + + def getVerifyFiles(self): + return config.GetInt('launcher-verify', 0) + + def getTestServerFlag(self): + return self.getValue('IS_TEST_SERVER', 0) + + def getGameServer(self): + return self.getValue('GAME_SERVER', '') + + def getLogFileName(self): + return 'toontown' + + def parseWebAcctParams(self): + s = config.GetString('fake-web-acct-params', '') + if not s: + s = self.getRegistry(self.webAcctParams) + self.notify.info('webAcctParams = %s' % s) + self.setRegistry(self.webAcctParams, '') + l = s.split('&') + length = len(l) + dict = {} + for index in range(0, len(l)): + args = l[index].split('=') + if len(args) == 3: + name, value = args[-2:] + dict[name] = int(value) + elif len(args) == 2: + name, value = args + dict[name] = int(value) + + if dict.has_key('secretsNeedsParentPassword') and 1: + self.secretNeedsParentPasswordKey = dict['secretsNeedsParentPassword'] + self.notify.info('secretNeedsParentPassword = %d' % self.secretNeedsParentPasswordKey) + else: + self.notify.warning('no secretNeedsParentPassword token in webAcctParams') + + if dict.has_key('chatEligible'): + self.chatEligibleKey = dict['chatEligible'] + self.notify.info('chatEligibleKey = %d' % self.chatEligibleKey) + else: + self.notify.warning('no chatEligible token in webAcctParams') + + def getBlue(self): + blue = self.getValue(self.toontownBlueKey) + self.setValue(self.toontownBlueKey, '') + if blue == 'NO BLUE': + blue = None + return blue + + def getPlayToken(self): + playToken = self.getValue(self.toontownPlayTokenKey) + self.setValue(self.toontownPlayTokenKey, '') + if playToken == 'NO PLAYTOKEN': + playToken = None + return playToken + + def setRegistry(self, name, value): + pass + + def getRegistry(self, name, missingValue = None): + self.notify.info('getRegistry %s' % ((name, missingValue),)) + self.notify.info('self.VISTA = %s' % self.VISTA) + self.notify.info('checking env' % os.environ) + if missingValue == None: + missingValue = '' + value = os.environ.get(name, missingValue) + try: + value = int(value) + except: + pass + + return value + + def getCDDownloadPath(self, origPath, serverFilePath): + return '%s/%s/CD_%d/%s' % (origPath, + self.ServerVersion, + self.fromCD, + serverFilePath) + + def getDownloadPath(self, origPath, serverFilePath): + return '%s/%s' % (origPath, serverFilePath) + + def hashIsValid(self, serverHash, hashStr): + return serverHash.setFromDec(hashStr) + + def getAccountServer(self): + return self.getValue('ACCOUNT_SERVER', '') + + def getGame2Done(self): + return True + + def getNeedPwForSecretKey(self): + if self.useTTSpecificLogin: + self.notify.info('getNeedPwForSecretKey using tt-specific-login') + try: + if base.cr.chatChatCodeCreationRule == 'PARENT': + return True + else: + return False + except: + return True + + else: + return self.secretNeedsParentPasswordKey + + def getParentPasswordSet(self): + if self.useTTSpecificLogin: + self.notify.info('getParentPasswordSet using tt-specific-login') + try: + if base.cr.isPaid(): + return True + else: + return False + except: + return False + + else: + return self.chatEligibleKey + + def canLeaveFirstIsland(self): + return self.getPhaseComplete(4) + + def startGame(self): + self.newTaskManager() + eventMgr.restart() + from toontown.toonbase import ToontownStart diff --git a/toontown/launcher/QuickStartLauncher.py b/toontown/launcher/QuickStartLauncher.py new file mode 100644 index 0000000..aa92ab0 --- /dev/null +++ b/toontown/launcher/QuickStartLauncher.py @@ -0,0 +1,6 @@ +import sys +sys.path = [''] +import Phase2 +from toontown.launcher.QuickLauncher import QuickLauncher +launcher = QuickLauncher() +launcher.notify.info('Reached end of StartQuickLauncher.py.') diff --git a/toontown/launcher/ToontownDownloadWatcher.py b/toontown/launcher/ToontownDownloadWatcher.py new file mode 100644 index 0000000..bd9d57b --- /dev/null +++ b/toontown/launcher/ToontownDownloadWatcher.py @@ -0,0 +1,14 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.launcher.DownloadWatcher import DownloadWatcher +from toontown.toonbase import TTLocalizer + +class ToontownDownloadWatcher(DownloadWatcher): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownDownloadWatcher') + + def __init__(self, phaseNames): + DownloadWatcher.__init__(self, phaseNames) + + def update(self, phase, percent, reqByteRate, actualByteRate): + DownloadWatcher.update(self, phase, percent, reqByteRate, actualByteRate) + phaseName = self.phaseNames[phase] + self.text['text'] = TTLocalizer.LoadingDownloadWatcherUpdate % phaseName diff --git a/toontown/launcher/ToontownDummyLauncher.py b/toontown/launcher/ToontownDummyLauncher.py new file mode 100644 index 0000000..5aefe7f --- /dev/null +++ b/toontown/launcher/ToontownDummyLauncher.py @@ -0,0 +1,80 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.launcher.DummyLauncherBase import DummyLauncherBase +from toontown.launcher.ToontownLauncher import ToontownLauncher + +class ToontownDummyLauncher(DummyLauncherBase, ToontownLauncher): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownDummyLauncher') + + def __init__(self): + DummyLauncherBase.__init__(self) + self.setPhaseComplete(1, 100) + self.setPhaseComplete(2, 100) + self.setPhaseComplete(3, 100) + self.tutorialComplete = 1 + self.frequency = 0.0 + self.windowOpen = 0 + self.firstPhase = 3.5 + self.pandaErrorCodeKey = 'PANDA_ERROR_CODE' + self.goUserName = '' + self.periodTimeRemainingKey = 'PERIOD_TIME_REMAINING' + self.periodNameKey = 'PERIOD_NAME' + self.swidKey = 'SWID' + self.reg = {} + self.startFakeDownload() + + def setTutorialComplete(self, complete): + self.tutorialComplete = complete + + def getTutorialComplete(self): + return self.tutorialComplete + + def setFrequency(self, freq): + self.frequency = freq + + def getFrequency(self): + return self.frequency + + def getInstallDir(self): + return 'C:\\Program Files\\Disney\\Disney Online\\Toontown' + + def getUserName(self): + return 'dummy' + + def getReferrerCode(self): + return None + + def setRegistry(self, name, value): + print 'setRegistry[%s] = %s' % (name, value) + self.reg[name] = value + + def getRegistry(self, name, defaultValue = None): + if name in self.reg: + value = self.reg[name] + else: + value = defaultValue + print 'getRegistry[%s] = %s' % (name, value) + return value + + def getGame2Done(self): + return 1 + + def recordPeriodTimeRemaining(self, secondsRemaining): + self.setRegistry(self.periodTimeRemainingKey, secondsRemaining) + + def recordPeriodName(self, periodName): + self.setRegistry(self.periodNameKey, periodName) + + def recordSwid(self, swid): + self.setRegistry(self.swidKey, swid) + + def getGoUserName(self): + return self.goUserName + + def setGoUserName(self, userName): + self.goUserName = userName + + def getParentPasswordSet(self): + return 0 + + def getNeedPwForSecretKey(self): + return 0 diff --git a/toontown/launcher/ToontownLauncher.py b/toontown/launcher/ToontownLauncher.py new file mode 100644 index 0000000..eb4ea7c --- /dev/null +++ b/toontown/launcher/ToontownLauncher.py @@ -0,0 +1,255 @@ +import os +import sys +import time +import types + +ltime = 1 and time.localtime() +logSuffix = '%02d%02d%02d_%02d%02d%02d' % (ltime[0] - 2000, ltime[1], ltime[2], + ltime[3], ltime[4], ltime[5]) + +logfile = 'toontownD-' + logSuffix + '.log' + +class LogAndOutput: + def __init__(self, orig, log): + self.orig = orig + self.log = log + + def write(self, str): + self.log.write(str) + self.log.flush() + self.orig.write(str) + self.orig.flush() + + def flush(self): + self.log.flush() + self.orig.flush() + +log = open(logfile, 'a') +logOut = LogAndOutput(sys.__stdout__, log) +logErr = LogAndOutput(sys.__stderr__, log) +sys.stdout = logOut +sys.stderr = logErr + +print('\n\nStarting Toontown...') + +if 1: + print 'Current time: ' + time.asctime(time.localtime(time.time())) + ' ' + time.tzname[0] + print 'sys.path = ', sys.path + print 'sys.argv = ', sys.argv + +from otp.launcher.LauncherBase import LauncherBase +from otp.otpbase import OTPLauncherGlobals +from pandac.libpandaexpressModules import * +from toontown.toonbase import TTLocalizer + +class ToontownLauncher(LauncherBase): + GameName = 'Toontown' + LauncherPhases = [3, 3.5, 4, 5, 5.5, 6, 7, 8, 9, 10, 11, 12, 13] + TmpOverallMap = [0.25, 0.15, 0.12, 0.17, 0.08, 0.07, 0.05, 0.05, 0.017, + 0.011, 0.01, 0.012, 0.01] + RegistryKey = 'Software\\Disney\\Disney Online\\Toontown' + ForegroundSleepTime = 0.01 + Localizer = TTLocalizer + VerifyFiles = 1 + DecompressMultifiles = True + + def __init__(self): + if sys.argv[2] == 'Phase2.py': + sys.argv = sys.argv[:1] + sys.argv[3:] + if len(sys.argv) == 5 or len(sys.argv) == 6: + self.gameServer = sys.argv[2] + self.accountServer = sys.argv[3] + self.testServerFlag = int(sys.argv[4]) + else: + print 'Error: Launcher: incorrect number of parameters' + sys.exit() + + self.toontownBlueKey = 'TOONTOWN_BLUE' + self.toontownPlayTokenKey = 'TOONTOWN_PLAYTOKEN' + self.launcherMessageKey = 'LAUNCHER_MESSAGE' + self.game1DoneKey = 'GAME1_DONE' + self.game2DoneKey = 'GAME2_DONE' + self.tutorialCompleteKey = 'TUTORIAL_DONE' + self.toontownRegistryKey = 'Software\\Disney\\Disney Online\\Toontown' + if self.testServerFlag: + self.toontownRegistryKey = '%s%s' % (self.toontownRegistryKey, 'Test') + self.toontownRegistryKey = '%s%s' % (self.toontownRegistryKey, self.getProductName()) + LauncherBase.__init__(self) + self.webAcctParams = 'WEB_ACCT_PARAMS' + self.parseWebAcctParams() + self.mainLoop() + + def getValue(self, key, default=None): + try: + return self.getRegistry(key, default) + except: + return self.getRegistry(key) + + def setValue(self, key, value): + self.setRegistry(key, value) + + def getVerifyFiles(self): + return 1 + + def getTestServerFlag(self): + return self.testServerFlag + + def getGameServer(self): + return self.gameServer + + def getLogFileName(self): + return 'toontown' + + def parseWebAcctParams(self): + s = config.GetString('fake-web-acct-params', '') + if not s: + s = self.getRegistry(self.webAcctParams) + self.setRegistry(self.webAcctParams, '') + l = s.split('&') + length = len(l) + dict = {} + for index in range(0, len(l)): + args = l[index].split('=') + if len(args) == 3: + [name, value] = args[-2:] + dict[name] = int(value) + elif len(args) == 2: + [name, value] = args + dict[name] = int(value) + + self.secretNeedsParentPasswordKey = 1 + if dict.has_key('secretsNeedsParentPassword'): + self.secretNeedsParentPasswordKey = dict['secretsNeedsParentPassword'] + else: + self.notify.warning('no secretNeedsParentPassword token in webAcctParams') + self.notify.info('secretNeedsParentPassword = %d' % self.secretNeedsParentPasswordKey) + + self.chatEligibleKey = 0 + if dict.has_key('chatEligible'): + self.chatEligibleKey = dict['chatEligible'] + else: + self.notify.warning('no chatEligible token in webAcctParams') + self.notify.info('chatEligibleKey = %d' % self.chatEligibleKey) + + def getBlue(self): + blue = self.getValue(self.toontownBlueKey) + self.setValue(self.toontownBlueKey, '') + if blue == 'NO BLUE': + blue = None + return blue + + def getPlayToken(self): + playToken = self.getValue(self.toontownPlayTokenKey) + self.setValue(self.toontownPlayTokenKey, '') + if playToken == 'NO PLAYTOKEN': + playToken = None + return playToken + + def setRegistry(self, name, value): + if not self.WIN32: + return + + t = type(value) + if t == types.IntType: + WindowsRegistry.setIntValue(self.toontownRegistryKey, name, value) + elif t == types.StringType: + WindowsRegistry.setStringValue(self.toontownRegistryKey, name, value) + else: + self.notify.warning('setRegistry: Invalid type for registry value: ' + `value`) + + def getRegistry(self, name, missingValue=None): + self.notify.info('getRegistry%s' % ((name, missingValue),)) + if not self.WIN32: + if missingValue == None: + missingValue = '' + value = os.environ.get(name, missingValue) + try: + value = int(value) + except: pass + return value + + t = WindowsRegistry.getKeyType(self.toontownRegistryKey, name) + if t == WindowsRegistry.TInt: + if missingValue == None: + missingValue = 0 + return WindowsRegistry.getIntValue(self.toontownRegistryKey, + name, missingValue) + elif t == WindowsRegistry.TString: + if missingValue == None: + missingValue = '' + return WindowsRegistry.getStringValue(self.toontownRegistryKey, + name, missingValue) + else: + return missingValue + + def getCDDownloadPath(self, origPath, serverFilePath): + return '%s/%s%s/CD_%d/%s' % (origPath, self.ServerVersion, self.ServerVersionSuffix, self.fromCD, serverFilePath) + + def getDownloadPath(self, origPath, serverFilePath): + return '%s/%s%s/%s' % (origPath, self.ServerVersion, self.ServerVersionSuffix, serverFilePath) + + def getPercentPatchComplete(self, bytesWritten): + if self.totalPatchDownload: + return LauncherBase.getPercentPatchComplete(self, bytesWritten) + else: + return 0 + + def hashIsValid(self, serverHash, hashStr): + return serverHash.setFromDec(hashStr) or serverHash.setFromHex(hashStr) + + def launcherMessage(self, msg): + LauncherBase.launcherMessage(self, msg) + self.setRegistry(self.launcherMessageKey, msg) + + def getAccountServer(self): + return self.accountServer + + def setTutorialComplete(self): + self.setRegistry(self.tutorialCompleteKey, 0) + + def getTutorialComplete(self): + return self.getRegistry(self.tutorialCompleteKey, 0) + + def getGame2Done(self): + return self.getRegistry(self.game2DoneKey, 0) + + def setPandaErrorCode(self, code): + self.pandaErrorCode = code + if self.WIN32: + self.notify.info('setting panda error code to %s' % code) + exitCode2exitPage = { + OTPLauncherGlobals.ExitEnableChat: 'chat', + OTPLauncherGlobals.ExitSetParentPassword: 'setparentpassword', + OTPLauncherGlobals.ExitPurchase: 'purchase'} + if code in exitCode2exitPage: + self.setRegistry('EXIT_PAGE', exitCode2exitPage[code]) + self.setRegistry(self.PandaErrorCodeKey, 0) + else: + self.setRegistry(self.PandaErrorCodeKey, code) + else: + LauncherBase.setPandaErrorCode(self, code) + + def getNeedPwForSecretKey(self): + return self.secretNeedsParentPasswordKey + + def getParentPasswordSet(self): + return self.chatEligibleKey + + def MakeNTFSFilesGlobalWriteable(self, pathToSet=None): + if not self.WIN32: + return + LauncherBase.MakeNTFSFilesGlobalWriteable(self, pathToSet) + + def startGame(self): + try: + os.remove('Phase3.py') + except: pass + + import Phase3 + + self.newTaskManager() + + from direct.showbase.EventManagerGlobal import eventMgr + eventMgr.restart() + + from toontown.toonbase import ToontownStart diff --git a/toontown/launcher/__init__.py b/toontown/launcher/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/login/AccountServerDate.py b/toontown/login/AccountServerDate.py new file mode 100644 index 0000000..eed9ee3 --- /dev/null +++ b/toontown/login/AccountServerDate.py @@ -0,0 +1,57 @@ +from pandac.PandaModules import * +from otp.login.HTTPUtil import * +from direct.directnotify import DirectNotifyGlobal +from otp.login import TTAccount +import DateObject +import TTDateObject +import time + +class AccountServerDate: + notify = DirectNotifyGlobal.directNotify.newCategory('AccountServerDate') + + def __init__(self): + self.__grabbed = 0 + + def getServer(self): + return TTAccount.getAccountServer().cStr() + + def grabDate(self, force = 0): + if self.__grabbed and not force: + self.notify.debug('using cached account server date') + return + if base.cr.accountOldAuth or base.config.GetBool('use-local-date', 0): + self.__useLocalClock() + return + url = URLSpec(self.getServer()) + url.setPath('/getDate.php') + self.notify.debug('grabbing account server date from %s' % url.cStr()) + response = getHTTPResponse(url, http) + if response[0] != 'ACCOUNT SERVER DATE': + self.notify.debug('invalid response header') + raise UnexpectedResponse, 'unexpected response, response=%s' % response + try: + epoch = int(response[1]) + except ValueError, e: + self.notify.debug(str(e)) + raise UnexpectedResponse, 'unexpected response, response=%s' % response + + timeTuple = time.gmtime(epoch) + self.year = timeTuple[0] + self.month = timeTuple[1] + self.day = timeTuple[2] + base.cr.dateObject = TTDateObject.TTDateObject(self) + self.__grabbed = 1 + + def __useLocalClock(self): + self.month = base.cr.dateObject.getMonth() + self.year = base.cr.dateObject.getYear() + self.day = base.cr.dateObject.getDay() + + def getMonth(self): + return self.month + + def getYear(self): + return self.year + + def getDay(self): + return self.day diff --git a/toontown/login/AvatarChoice.py b/toontown/login/AvatarChoice.py new file mode 100644 index 0000000..7eecb1d --- /dev/null +++ b/toontown/login/AvatarChoice.py @@ -0,0 +1,283 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject +from toontown.toon import ToonDNA +from toontown.toon import ToonHead +from toontown.toontowngui import TTDialog +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TeaserPanel +from toontown.toonbase import UserFunnel +NAME_ROTATIONS = (7, -11, 1, -5, 3.5, -5) +NAME_POSITIONS = ((0, 0, 0.26), + (-0.03, 0, 0.25), + (0, 0, 0.27), + (-0.03, 0, 0.25), + (0.03, 0, 0.26), + (0, 0, 0.26)) +DELETE_POSITIONS = ((0.187, 0, -0.26), + (0.31, 0, -0.167), + (0.231, 0, -0.241), + (0.314, 0, -0.186), + (0.243, 0, -0.233), + (0.28, 0, -0.207)) + +class AvatarChoice(DirectButton): + notify = DirectNotifyGlobal.directNotify.newCategory('AvatarChoice') + NEW_TRIALER_OPEN_POS = (1,) + OLD_TRIALER_OPEN_POS = (1, 4) + MODE_CREATE = 0 + MODE_CHOOSE = 1 + MODE_LOCKED = 2 + + def __init__(self, av = None, position = 0, paid = 0, okToLockout = 1): + DirectButton.__init__(self, relief=None, text='', text_font=ToontownGlobals.getSignFont()) + self.initialiseoptions(AvatarChoice) + self.hasPaid = paid + self.mode = None + if base.restrictTrialers and okToLockout: + if position not in AvatarChoice.NEW_TRIALER_OPEN_POS: + if not self.hasPaid: + self.mode = AvatarChoice.MODE_LOCKED + self.name = '' + self.dna = None + if self.mode is not AvatarChoice.MODE_LOCKED: + if not av: + self.mode = AvatarChoice.MODE_CREATE + self.name = '' + self.dna = None + else: + self.mode = AvatarChoice.MODE_CHOOSE + self.name = av.name + self.dna = ToonDNA.ToonDNA(av.dna) + self.wantName = av.wantName + self.approvedName = av.approvedName + self.rejectedName = av.rejectedName + self.allowedName = av.allowedName + self.position = position + self.doneEvent = 'avChoicePanel-' + str(self.position) + self.deleteWithPasswordFrame = None + self.pickAToonGui = loader.loadModel('phase_3/models/gui/tt_m_gui_pat_mainGui') + self.buttonBgs = [] + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareRed')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareGreen')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squarePurple')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareBlue')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squarePink')) + self.buttonBgs.append(self.pickAToonGui.find('**/tt_t_gui_pat_squareYellow')) + self['image'] = self.buttonBgs[position] + self.setScale(1.01) + if self.mode is AvatarChoice.MODE_LOCKED: + self['command'] = self.__handleTrialer + self['text'] = TTLocalizer.AvatarChoiceSubscribersOnly + self['text0_scale'] = 0.1 + self['text1_scale'] = TTLocalizer.ACsubscribersOnly + self['text2_scale'] = TTLocalizer.ACsubscribersOnly + self['text0_fg'] = (0, 1, 0.8, 0.0) + self['text1_fg'] = (0, 1, 0.8, 1) + self['text2_fg'] = (0.3, 1, 0.9, 1) + self['text_pos'] = (0, 0.19) + upsellModel = loader.loadModel('phase_3/models/gui/tt_m_gui_ups_mainGui') + upsellTex = upsellModel.find('**/tt_t_gui_ups_logo_noBubbles') + self.logoModelImage = loader.loadModel('phase_3/models/gui/members_only_gui').find('**/MembersOnly') + logo = DirectFrame(state=DGG.DISABLED, parent=self, relief=None, image=upsellTex, image_scale=(0.9, 0, 0.9), image_pos=(0, 0, 0), scale=0.45) + logo.reparentTo(self.stateNodePath[0], 20) + logo.instanceTo(self.stateNodePath[1], 20) + logo.instanceTo(self.stateNodePath[2], 20) + self.logo = logo + upsellModel.removeNode() + elif self.mode is AvatarChoice.MODE_CREATE: + self['command'] = self.__handleCreate + self['text'] = (TTLocalizer.AvatarChoiceMakeAToon,) + self['text_pos'] = (0, 0) + self['text0_scale'] = 0.1 + self['text1_scale'] = TTLocalizer.ACmakeAToon + self['text2_scale'] = TTLocalizer.ACmakeAToon + self['text0_fg'] = (0, 1, 0.8, 0.5) + self['text1_fg'] = (0, 1, 0.8, 1) + self['text2_fg'] = (0.3, 1, 0.9, 1) + else: + self['command'] = self.__handleChoice + self['text'] = ('', TTLocalizer.AvatarChoicePlayThisToon, TTLocalizer.AvatarChoicePlayThisToon) + self['text_scale'] = TTLocalizer.ACplayThisToon + self['text_fg'] = (1, 0.9, 0.1, 1) + self.nameText = DirectLabel(parent=self, relief=None, scale=0.08, pos=NAME_POSITIONS[position], text=self.name, hpr=(0, 0, NAME_ROTATIONS[position]), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_wordwrap=8, text_font=ToontownGlobals.getToonFont(), state=DGG.DISABLED) + if self.approvedName != '': + self.nameText['text'] = self.approvedName + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.nameYourToonButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), text=(TTLocalizer.AvatarChoiceNameYourToon, TTLocalizer.AvatarChoiceNameYourToon, TTLocalizer.AvatarChoiceNameYourToon), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.15, text_pos=(0, 0.03), text_font=ToontownGlobals.getInterfaceFont(), pos=(-0.2, 0, -0.3), scale=0.45, image_scale=(2, 1, 3), command=self.__handleNameYourToon) + guiButton.removeNode() + self.statusText = DirectLabel(parent=self, relief=None, scale=0.09, pos=(0, 0, -0.24), text='', text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_wordwrap=7.5, text_scale=TTLocalizer.ACstatusText, text_font=ToontownGlobals.getToonFont(), state=DGG.DISABLED) + if self.wantName != '': + self.nameYourToonButton.hide() + self.statusText['text'] = TTLocalizer.AvatarChoiceNameReview + elif self.approvedName != '': + self.nameYourToonButton.hide() + self.statusText['text'] = TTLocalizer.AvatarChoiceNameApproved + elif self.rejectedName != '': + self.nameYourToonButton.hide() + self.statusText['text'] = TTLocalizer.AvatarChoiceNameRejected + elif self.allowedName == 1 and (base.cr.allowFreeNames() or self.hasPaid): + self.nameYourToonButton.show() + self.statusText['text'] = '' + else: + self.nameYourToonButton.hide() + self.statusText['text'] = '' + self.head = hidden.attachNewNode('head') + self.head.setPosHprScale(0, 5, -0.1, 180, 0, 0, 0.24, 0.24, 0.24) + self.head.reparentTo(self.stateNodePath[0], 20) + self.head.instanceTo(self.stateNodePath[1], 20) + self.head.instanceTo(self.stateNodePath[2], 20) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(self.dna, forGui=1) + self.headModel.reparentTo(self.head) + animalStyle = self.dna.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + self.headModel.setScale(bodyScale / 0.75) + self.headModel.startBlink() + self.headModel.startLookAround() + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.deleteButton = DirectButton(parent=self, image=(trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.AvatarChoiceDelete, TTLocalizer.AvatarChoiceDelete), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.15, text_pos=(0, -0.1), text_font=ToontownGlobals.getInterfaceFont(), relief=None, pos=DELETE_POSITIONS[position], scale=0.45, command=self.__handleDelete) + trashcanGui.removeNode() + self.resetFrameSize() + self.avForLogging = None + if av: + self.avForLogging = str(av.id) + else: + self.avForLogging = None + return + + def destroy(self): + loader.unloadModel('phase_3/models/gui/pick_a_toon_gui') + self.pickAToonGui.removeNode() + del self.pickAToonGui + del self.dna + if self.mode in (AvatarChoice.MODE_CREATE, AvatarChoice.MODE_LOCKED): + pass + else: + self.headModel.stopBlink() + self.headModel.stopLookAroundNow() + self.headModel.delete() + self.head.removeNode() + del self.head + del self.headModel + del self.nameText + del self.statusText + self.deleteButton.destroy() + del self.deleteButton + self.nameYourToonButton.destroy() + del self.nameYourToonButton + loader.unloadModel('phase_3/models/gui/trashcan_gui') + loader.unloadModel('phase_3/models/gui/quit_button') + DirectFrame.destroy(self) + if self.deleteWithPasswordFrame: + self.deleteWithPasswordFrame.destroy() + + def __handleChoice(self): + cleanupDialog('globalDialog') + messenger.send(self.doneEvent, ['chose', self.position]) + UserFunnel.loggingAvID('write', str(self.avForLogging)) + UserFunnel.logSubmit(0, 'AvatarChoice') + + def __handleCreate(self): + cleanupDialog('globalDialog') + messenger.send(self.doneEvent, ['create', self.position]) + + def __handleDelete(self): + cleanupDialog('globalDialog') + self.verify = TTDialog.TTGlobalDialog(doneEvent='verifyDone', message=TTLocalizer.AvatarChoiceDeleteConfirm % self.name, style=TTDialog.TwoChoice) + self.verify.show() + self.accept('verifyDone', self.__handleVerifyDelete) + + def __handleNameYourToon(self): + messenger.send(self.doneEvent, ['nameIt', self.position]) + + def __handleVerifyDelete(self): + status = self.verify.doneStatus + self.ignore('verifyDone') + self.verify.cleanup() + del self.verify + if status == 'ok': + self.verifyDeleteWithPassword() + + def verifyDeleteWithPassword(self): + tt = base.cr.loginInterface + if tt.supportsAuthenticateDelete() and base.cr.productName not in ['DisneyOnline-UK', + 'JP', + 'FR', + 'BR', + 'DisneyOnline-AP']: + self.deleteWithPassword = 1 + deleteText = TTLocalizer.AvatarChoiceDeletePasswordText % self.name + else: + self.deleteWithPassword = 0 + deleteText = TTLocalizer.AvatarChoiceDeleteConfirmText % {'name': self.name, + 'confirm': TTLocalizer.AvatarChoiceDeleteConfirmUserTypes} + if self.deleteWithPasswordFrame == 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')) + self.deleteWithPasswordFrame = DirectFrame(pos=(0.0, 0.1, 0.2), parent=aspect2dp, relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.4, 1.0, 1.0), text=deleteText, text_wordwrap=19, text_scale=TTLocalizer.ACdeleteWithPasswordFrame, text_pos=(0, 0.25), textMayChange=1, sortOrder=NO_FADE_SORT_INDEX) + self.deleteWithPasswordFrame.hide() + if self.deleteWithPassword: + self.passwordLabel = DirectLabel(parent=self.deleteWithPasswordFrame, relief=None, pos=(-0.07, 0.0, -0.2), text=TTLocalizer.AvatarChoicePassword, text_scale=0.08, text_align=TextNode.ARight, textMayChange=0) + self.passwordEntry = DirectEntry(parent=self.deleteWithPasswordFrame, relief=None, image=nameBalloon, image1_color=(0.8, 0.8, 0.8, 1.0), image_pos=(0.0, 0.0, -0.45), scale=0.064, pos=(0.0, 0.0, -0.2), width=ToontownGlobals.maxLoginWidth, numLines=2, focus=1, cursorKeys=1, obscured=1, command=self.__handleDeleteWithPasswordOK) + DirectButton(parent=self.deleteWithPasswordFrame, image=okButtonImage, relief=None, text=TTLocalizer.AvatarChoiceDeletePasswordOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.22, 0.0, -0.35), command=self.__handleDeleteWithPasswordOK) + else: + self.passwordEntry = DirectEntry(parent=self.deleteWithPasswordFrame, relief=None, image=nameBalloon, image1_color=(0.8, 0.8, 0.8, 1.0), scale=0.064, pos=(-0.3, 0.0, -0.2), width=10, numLines=1, focus=1, cursorKeys=1, command=self.__handleDeleteWithConfirmOK) + DirectButton(parent=self.deleteWithPasswordFrame, image=okButtonImage, relief=None, text=TTLocalizer.AvatarChoiceDeletePasswordOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.22, 0.0, -0.35), command=self.__handleDeleteWithConfirmOK) + DirectLabel(parent=self.deleteWithPasswordFrame, relief=None, pos=(0, 0, 0.35), text=TTLocalizer.AvatarChoiceDeletePasswordTitle, textMayChange=0, text_scale=0.08) + DirectButton(parent=self.deleteWithPasswordFrame, image=cancelButtonImage, relief=None, text=TTLocalizer.AvatarChoiceDeletePasswordCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=1, pos=(0.2, 0.0, -0.35), command=self.__handleDeleteWithPasswordCancel) + buttons.removeNode() + nameBalloon.removeNode() + else: + self.deleteWithPasswordFrame['text'] = deleteText + self.passwordEntry['focus'] = 1 + self.passwordEntry.enterText('') + base.transitions.fadeScreen(0.5) + self.deleteWithPasswordFrame.show() + return + + def __handleDeleteWithPasswordOK(self, *args): + password = self.passwordEntry.get() + tt = base.cr.loginInterface + okFlag, errorMsg = tt.authenticateDelete(base.cr.userName, password) + if okFlag: + self.deleteWithPasswordFrame.hide() + base.transitions.noTransitions() + messenger.send(self.doneEvent, ['delete', self.position]) + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: DELETEATOON: Deleting A Toon') + else: + if errorMsg is not None: + self.notify.warning('authenticateDelete returned unexpected error: %s' % errorMsg) + self.deleteWithPasswordFrame['text'] = TTLocalizer.AvatarChoiceDeleteWrongPassword + self.passwordEntry['focus'] = 1 + self.passwordEntry.enterText('') + return + + def __handleDeleteWithConfirmOK(self, *args): + password = self.passwordEntry.get() + passwordMatch = TTLocalizer.AvatarChoiceDeleteConfirmUserTypes + password = TextEncoder.lower(password) + passwordMatch = TextEncoder.lower(passwordMatch) + if password == passwordMatch: + self.deleteWithPasswordFrame.hide() + base.transitions.noTransitions() + messenger.send(self.doneEvent, ['delete', self.position]) + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: DELETEATOON: Deleting A Toon') + else: + self.deleteWithPasswordFrame['text'] = TTLocalizer.AvatarChoiceDeleteWrongConfirm % {'name': self.name, + 'confirm': TTLocalizer.AvatarChoiceDeleteConfirmUserTypes} + self.passwordEntry['focus'] = 1 + self.passwordEntry.enterText('') + + def __handleDeleteWithPasswordCancel(self): + self.deleteWithPasswordFrame.hide() + base.transitions.noTransitions() + + def __handleTrialer(self): + TeaserPanel.TeaserPanel(pageName='sixToons') diff --git a/toontown/login/AvatarChooser.py b/toontown/login/AvatarChooser.py new file mode 100644 index 0000000..8311ae8 --- /dev/null +++ b/toontown/login/AvatarChooser.py @@ -0,0 +1,280 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +import AvatarChoice +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.launcher import DownloadForceAcknowledge +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import DisplayOptions +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import random +MAX_AVATARS = 6 +POSITIONS = (Vec3(-0.840167, 0, 0.359333), + Vec3(0.00933349, 0, 0.306533), + Vec3(0.862, 0, 0.3293), + Vec3(-0.863554, 0, -0.445659), + Vec3(0.00999999, 0, -0.5181), + Vec3(0.864907, 0, -0.445659)) +COLORS = (Vec4(0.917, 0.164, 0.164, 1), + Vec4(0.152, 0.75, 0.258, 1), + Vec4(0.598, 0.402, 0.875, 1), + Vec4(0.133, 0.59, 0.977, 1), + Vec4(0.895, 0.348, 0.602, 1), + Vec4(0.977, 0.816, 0.133, 1)) +chooser_notify = DirectNotifyGlobal.directNotify.newCategory('AvatarChooser') + +class AvatarChooser(StateData.StateData): + + def __init__(self, avatarList, parentFSM, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.choice = None + self.avatarList = avatarList + self.displayOptions = None + self.fsm = ClassicFSM.ClassicFSM('AvatarChooser', [State.State('Choose', self.enterChoose, self.exitChoose, ['CheckDownload']), State.State('CheckDownload', self.enterCheckDownload, self.exitCheckDownload, ['Choose'])], 'Choose', 'Choose') + self.fsm.enterInitialState() + self.parentFSM = parentFSM + self.parentFSM.getCurrentState().addChild(self.fsm) + return + + def enter(self): + self.notify.info('AvatarChooser.enter') + if not self.displayOptions: + self.displayOptions = DisplayOptions.DisplayOptions() + self.notify.info('calling self.displayOptions.restrictToEmbedded(False)') + if base.appRunner: + self.displayOptions.loadFromSettings() + self.displayOptions.restrictToEmbedded(False) + if self.isLoaded == 0: + self.load() + base.disableMouse() + self.title.reparentTo(aspect2d) + self.quitButton.show() + if base.cr.loginInterface.supportsRelogin(): + self.logoutButton.show() + self.pickAToonBG.reparentTo(base.camera) + choice = base.config.GetInt('auto-avatar-choice', -1) + for panel in self.panelList: + panel.show() + self.accept(panel.doneEvent, self.__handlePanelDone) + if panel.position == choice and panel.mode == AvatarChoice.AvatarChoice.MODE_CHOOSE: + self.__handlePanelDone('chose', panelChoice=choice) + + def exit(self): + if self.isLoaded == 0: + return None + for panel in self.panelList: + panel.hide() + + self.ignoreAll() + self.title.reparentTo(hidden) + self.quitButton.hide() + self.logoutButton.hide() + self.pickAToonBG.reparentTo(hidden) + return None + + def load(self, isPaid): + if self.isLoaded == 1: + return None + self.isPaid = isPaid + gui = loader.loadModel('phase_3/models/gui/pick_a_toon_gui') + gui2 = loader.loadModel('phase_3/models/gui/quit_button') + newGui = loader.loadModel('phase_3/models/gui/tt_m_gui_pat_mainGui') + self.pickAToonBG = newGui.find('**/tt_t_gui_pat_background') + self.pickAToonBG.reparentTo(hidden) + self.pickAToonBG.setPos(0.0, 2.73, 0.0) + self.pickAToonBG.setScale(1, 1, 1) + self.title = OnscreenText(TTLocalizer.AvatarChooserPickAToon, scale=TTLocalizer.ACtitle, parent=hidden, font=ToontownGlobals.getSignFont(), fg=(1, 0.9, 0.1, 1), pos=(0.0, 0.82)) + quitHover = gui.find('**/QuitBtn_RLVR') + self.quitButton = DirectButton(image=(quitHover, quitHover, quitHover), relief=None, text=TTLocalizer.AvatarChooserQuit, text_font=ToontownGlobals.getSignFont(), text_fg=(0.977, 0.816, 0.133, 1), text_pos=TTLocalizer.ACquitButtonPos, text_scale=TTLocalizer.ACquitButton, image_scale=1, image1_scale=1.05, image2_scale=1.05, scale=1.05, pos=(1.08, 0, -0.907), command=self.__handleQuit) + self.logoutButton = DirectButton(relief=None, image=(quitHover, quitHover, quitHover), text=TTLocalizer.OptionsPageLogout, text_font=ToontownGlobals.getSignFont(), text_fg=(0.977, 0.816, 0.133, 1), text_scale=TTLocalizer.AClogoutButton, text_pos=(0, -0.035), pos=(-1.17, 0, -0.914), image_scale=1.15, image1_scale=1.15, image2_scale=1.18, scale=0.5, command=self.__handleLogoutWithoutConfirm) + self.logoutButton.hide() + gui.removeNode() + gui2.removeNode() + newGui.removeNode() + self.panelList = [] + used_position_indexs = [] + for av in self.avatarList: + if base.cr.isPaid(): + okToLockout = 0 + else: + okToLockout = 1 + if av.position in AvatarChoice.AvatarChoice.OLD_TRIALER_OPEN_POS: + okToLockout = 0 + panel = AvatarChoice.AvatarChoice(av, position=av.position, paid=isPaid, okToLockout=okToLockout) + panel.setPos(POSITIONS[av.position]) + used_position_indexs.append(av.position) + self.panelList.append(panel) + + for panelNum in range(0, MAX_AVATARS): + if panelNum not in used_position_indexs: + panel = AvatarChoice.AvatarChoice(position=panelNum, paid=isPaid) + panel.setPos(POSITIONS[panelNum]) + self.panelList.append(panel) + + if len(self.avatarList) > 0: + self.initLookAtInfo() + self.isLoaded = 1 + + def getLookAtPosition(self, toonHead, toonidx): + lookAtChoice = random.random() + if len(self.used_panel_indexs) == 1: + lookFwdPercent = 0.33 + lookAtOthersPercent = 0 + else: + lookFwdPercent = 0.2 + if len(self.used_panel_indexs) == 2: + lookAtOthersPercent = 0.4 + else: + lookAtOthersPercent = 0.65 + lookRandomPercent = 1.0 - lookFwdPercent - lookAtOthersPercent + if lookAtChoice < lookFwdPercent: + self.IsLookingAt[toonidx] = 'f' + return Vec3(0, 1.5, 0) + elif lookAtChoice < lookRandomPercent + lookFwdPercent or len(self.used_panel_indexs) == 1: + self.IsLookingAt[toonidx] = 'r' + return toonHead.getRandomForwardLookAtPoint() + else: + other_toon_idxs = [] + for i in range(len(self.IsLookingAt)): + if self.IsLookingAt[i] == toonidx: + other_toon_idxs.append(i) + + if len(other_toon_idxs) == 1: + IgnoreStarersPercent = 0.4 + else: + IgnoreStarersPercent = 0.2 + NoticeStarersPercent = 0.5 + bStareTargetTurnsToMe = 0 + if len(other_toon_idxs) == 0 or random.random() < IgnoreStarersPercent: + other_toon_idxs = [] + for i in self.used_panel_indexs: + if i != toonidx: + other_toon_idxs.append(i) + + if random.random() < NoticeStarersPercent: + bStareTargetTurnsToMe = 1 + if len(other_toon_idxs) == 0: + return toonHead.getRandomForwardLookAtPoint() + else: + lookingAtIdx = random.choice(other_toon_idxs) + if bStareTargetTurnsToMe: + self.IsLookingAt[lookingAtIdx] = toonidx + otherToonHead = None + for panel in self.panelList: + if panel.position == lookingAtIdx: + otherToonHead = panel.headModel + + otherToonHead.doLookAroundToStareAt(otherToonHead, self.getLookAtToPosVec(lookingAtIdx, toonidx)) + self.IsLookingAt[toonidx] = lookingAtIdx + return self.getLookAtToPosVec(toonidx, lookingAtIdx) + return + + def getLookAtToPosVec(self, fromIdx, toIdx): + x = -(POSITIONS[toIdx][0] - POSITIONS[fromIdx][0]) + y = POSITIONS[toIdx][1] - POSITIONS[fromIdx][1] + z = POSITIONS[toIdx][2] - POSITIONS[fromIdx][2] + return Vec3(x, y, z) + + def initLookAtInfo(self): + self.used_panel_indexs = [] + for panel in self.panelList: + if panel.dna != None: + self.used_panel_indexs.append(panel.position) + + if len(self.used_panel_indexs) == 0: + return + self.IsLookingAt = [] + for i in range(MAX_AVATARS): + self.IsLookingAt.append('f') + + for panel in self.panelList: + if panel.dna != None: + panel.headModel.setLookAtPositionCallbackArgs((self, panel.headModel, panel.position)) + + return + + def unload(self): + if self.isLoaded == 0: + return None + cleanupDialog('globalDialog') + for panel in self.panelList: + panel.destroy() + + del self.panelList + self.title.removeNode() + del self.title + self.quitButton.destroy() + del self.quitButton + self.logoutButton.destroy() + del self.logoutButton + self.pickAToonBG.removeNode() + del self.pickAToonBG + del self.avatarList + self.parentFSM.getCurrentState().removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + self.isLoaded = 0 + ModelPool.garbageCollect() + TexturePool.garbageCollect() + return None + + def __handlePanelDone(self, panelDoneStatus, panelChoice = 0): + self.doneStatus = {} + self.doneStatus['mode'] = panelDoneStatus + self.choice = panelChoice + if panelDoneStatus == 'chose': + self.__handleChoice() + elif panelDoneStatus == 'nameIt': + self.__handleCreate() + elif panelDoneStatus == 'delete': + self.__handleDelete() + elif panelDoneStatus == 'create': + self.__handleCreate() + + def getChoice(self): + return self.choice + + def __handleChoice(self): + self.fsm.request('CheckDownload') + + def __handleCreate(self): + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent, [self.doneStatus])) + + def __handleDelete(self): + messenger.send(self.doneEvent, [self.doneStatus]) + + def __handleQuit(self): + cleanupDialog('globalDialog') + self.doneStatus = {'mode': 'exit'} + messenger.send(self.doneEvent, [self.doneStatus]) + + def enterChoose(self): + pass + + def exitChoose(self): + pass + + def enterCheckDownload(self): + self.accept('downloadAck-response', self.__handleDownloadAck) + self.downloadAck = DownloadForceAcknowledge.DownloadForceAcknowledge('downloadAck-response') + self.downloadAck.enter(4) + + def exitCheckDownload(self): + self.downloadAck.exit() + self.downloadAck = None + self.ignore('downloadAck-response') + return + + def __handleDownloadAck(self, doneStatus): + if doneStatus['mode'] == 'complete': + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent, [self.doneStatus])) + else: + self.fsm.request('Choose') + + def __handleLogoutWithoutConfirm(self): + base.cr.loginFSM.request('login') diff --git a/toontown/login/DateObject.py b/toontown/login/DateObject.py new file mode 100644 index 0000000..f354a1a --- /dev/null +++ b/toontown/login/DateObject.py @@ -0,0 +1,64 @@ +import time + +class DateObject: + + def getYear(self): + return time.localtime(time.time())[0] + + def getMonth(self): + return time.localtime(time.time())[1] + + def getDay(self): + return time.localtime(time.time())[2] + + def getDetailedAge(self, dobMonth, dobYear, dobDay = None, curMonth = None, curYear = None, curDay = None): + if curMonth is None: + curMonth = self.getMonth() + if curYear is None: + curYear = self.getYear() + if curDay is None: + curDay = self.getDay() + curMonths = curYear * 12 + (curMonth - 1) + dobMonths = dobYear * 12 + (dobMonth - 1) + if dobMonth == curMonth: + if dobDay is not None: + if dobDay > curDay: + curMonths -= 1 + ageMonths = curMonths - dobMonths + return (int(ageMonths / 12), ageMonths % 12) + + def getAge(self, dobMonth, dobYear, dobDay = None, curMonth = None, curYear = None, curDay = None): + return self.getDetailedAge(dobMonth, dobYear, dobDay=dobDay, curMonth=curMonth, curYear=curYear, curDay=curDay)[0] + + def getNumDaysInMonth(self, month = None, year = None): + + def isLeapYear(year): + if year % 4 == 0: + if year % 100 == 0: + if year % 400 == 0: + return 1 + else: + return 0 + else: + return 1 + else: + return 0 + + if month is None: + m = self.getMonth() + else: + m = month + if year is None: + y = self.getYear() + else: + y = year + if m == 2: + if isLeapYear(y): + return 29 + else: + return 28 + elif m in (1, 3, 5, 7, 8, 10, 12): + return 31 + else: + return 30 + return diff --git a/toontown/login/TTDateObject.py b/toontown/login/TTDateObject.py new file mode 100644 index 0000000..5abaac2 --- /dev/null +++ b/toontown/login/TTDateObject.py @@ -0,0 +1,21 @@ +import DateObject + +class TTDateObject(DateObject.DateObject): + + def __init__(self, accountServerDate): + self.accountServerDate = accountServerDate + + def getYear(self): + return self.accountServerDate.getYear() + + def getMonth(self): + return self.accountServerDate.getMonth() + + def getDay(self): + return self.accountServerDate.getDay() + + def getDetailedAge(self, dobMonth, dobYear, dobDay = None, curMonth = None, curYear = None, curDay = None): + return DateObject.DateObject.getDetailedAge(self, dobMonth, dobYear, dobDay, curMonth=self.getMonth(), curYear=self.getYear(), curDay=self.getDay()) + + def getAge(self, dobMonth, dobYear, dobDay = None, curMonth = None, curYear = None, curDay = None): + return TTDateObject.getDetailedAge(self, dobMonth, dobYear, dobDay=dobDay, curMonth=curMonth, curYear=curYear, curDay=curDay)[0] diff --git a/toontown/login/__init__.py b/toontown/login/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/makeatoon/BodyShop.py b/toontown/makeatoon/BodyShop.py new file mode 100644 index 0000000..d04a5ed --- /dev/null +++ b/toontown/makeatoon/BodyShop.py @@ -0,0 +1,375 @@ +from pandac.PandaModules import * +from toontown.toon import ToonDNA +from direct.fsm import StateData +from direct.gui.DirectGui import * +from MakeAToonGlobals import * +import random +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TeaserPanel +import ShuffleButton + +class BodyShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('BodyShop') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.toon = None + self.torsoChoice = 0 + self.legChoice = 0 + self.headChoice = 0 + self.speciesChoice = 0 + return + + def enter(self, toon, shopsVisited = []): + base.disableMouse() + self.toon = toon + self.dna = self.toon.getStyle() + gender = self.toon.style.getGender() + self.speciesStart = self.getSpeciesStart() + self.speciesChoice = self.speciesStart + self.headStart = 0 + self.headChoice = ToonDNA.toonHeadTypes.index(self.dna.head) - ToonDNA.getHeadStartIndex(self.species) + self.torsoStart = 0 + self.torsoChoice = ToonDNA.toonTorsoTypes.index(self.dna.torso) % 3 + self.legStart = 0 + self.legChoice = ToonDNA.toonLegTypes.index(self.dna.legs) + if CLOTHESSHOP in shopsVisited: + self.clothesPicked = 1 + else: + self.clothesPicked = 0 + self.clothesPicked = 1 + if gender == 'm' or ToonDNA.GirlBottoms[self.dna.botTex][1] == ToonDNA.SHORTS: + torsoStyle = 's' + torsoPool = ToonDNA.toonTorsoTypes[:3] + else: + torsoStyle = 'd' + torsoPool = ToonDNA.toonTorsoTypes[3:6] + self.__swapSpecies(0) + self.__swapHead(0) + self.__swapTorso(0) + self.__swapLegs(0) + choicePool = [ToonDNA.toonHeadTypes, torsoPool, ToonDNA.toonLegTypes] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeBody) + self.acceptOnce('last', self.__handleBackward) + self.accept('next', self.__handleForward) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + self.restrictHeadType(self.dna.head) + + def getSpeciesStart(self): + for species in ToonDNA.toonSpeciesTypes: + if species == self.dna.head[0]: + self.species = species + return ToonDNA.toonSpeciesTypes.index(species) + + def showButtons(self): + self.parentFrame.show() + + def hideButtons(self): + self.parentFrame.hide() + self.memberButton.hide() + + def exit(self): + try: + del self.toon + except: + self.notify.warning('BodyShop: toon not found') + + self.hideButtons() + self.ignore('last') + self.ignore('next') + self.ignore('enter') + self.ignore(self.shuffleFetchMsg) + + def load(self): + self.gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.gui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowRollover = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDisabled = self.gui.find('**/tt_t_gui_mat_arrowDisabled') + shuffleFrame = self.gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleArrowUp = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = self.gui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowRollover = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDisabled = self.gui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + self.upsellModel = loader.loadModel('phase_3/models/gui/tt_m_gui_ups_mainGui') + upsellTex = self.upsellModel.find('**/tt_t_gui_ups_banner') + self.parentFrame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.416), frameColor=(1, 0, 0, 0)) + self.speciesFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.073), hpr=(0, 0, 0), scale=1.3, frameColor=(1, 1, 1, 1), text='Species', text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.speciesLButton = DirectButton(parent=self.speciesFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapSpecies, extraArgs=[-1]) + self.speciesRButton = DirectButton(parent=self.speciesFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapSpecies, extraArgs=[1]) + self.headFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.3), hpr=(0, 0, 2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.BodyShopHead, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.headLButton = DirectButton(parent=self.headFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapHead, extraArgs=[-1]) + self.headRButton = DirectButton(parent=self.headFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapHead, extraArgs=[1]) + self.bodyFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonScale, relief=None, pos=(0, 0, -0.5), hpr=(0, 0, -2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.BodyShopBody, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.torsoLButton = DirectButton(parent=self.bodyFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapTorso, extraArgs=[-1]) + self.torsoRButton = DirectButton(parent=self.bodyFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapTorso, extraArgs=[1]) + self.legsFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.7), hpr=(0, 0, 3), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.BodyShopLegs, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.legLButton = DirectButton(parent=self.legsFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapLegs, extraArgs=[-1]) + self.legRButton = DirectButton(parent=self.legsFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapLegs, extraArgs=[1]) + self.memberButton = DirectButton(relief=None, image=(upsellTex, + upsellTex, + upsellTex, + upsellTex), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, scale=0.9, pos=(0, 0, -0.84), command=self.__restrictForward) + self.parentFrame.hide() + self.memberButton.hide() + self.shuffleFetchMsg = 'BodyShopShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + return + + def unload(self): + self.gui.removeNode() + del self.gui + self.upsellModel.removeNode() + del self.upsellModel + self.parentFrame.destroy() + self.speciesFrame.destroy() + self.headFrame.destroy() + self.bodyFrame.destroy() + self.legsFrame.destroy() + self.speciesLButton.destroy() + self.speciesRButton.destroy() + self.headLButton.destroy() + self.headRButton.destroy() + self.torsoLButton.destroy() + self.torsoRButton.destroy() + self.legLButton.destroy() + self.legRButton.destroy() + self.memberButton.destroy() + del self.parentFrame + del self.speciesFrame + del self.headFrame + del self.bodyFrame + del self.legsFrame + del self.speciesLButton + del self.speciesRButton + del self.headLButton + del self.headRButton + del self.torsoLButton + del self.torsoRButton + del self.legLButton + del self.legRButton + del self.memberButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + + def __swapTorso(self, offset): + gender = self.toon.style.getGender() + if not self.clothesPicked: + length = len(ToonDNA.toonTorsoTypes[6:]) + torsoOffset = 6 + elif gender == 'm': + length = len(ToonDNA.toonTorsoTypes[:3]) + torsoOffset = 0 + if self.dna.armColor not in ToonDNA.defaultBoyColorList: + self.dna.armColor = ToonDNA.defaultBoyColorList[0] + if self.dna.legColor not in ToonDNA.defaultBoyColorList: + self.dna.legColor = ToonDNA.defaultBoyColorList[0] + if self.dna.headColor not in ToonDNA.defaultBoyColorList: + self.dna.headColor = ToonDNA.defaultBoyColorList[0] + if self.toon.style.topTex not in ToonDNA.MakeAToonBoyShirts: + randomShirt = ToonDNA.getRandomTop(gender, ToonDNA.MAKE_A_TOON) + shirtTex, shirtColor, sleeveTex, sleeveColor = randomShirt + self.toon.style.topTex = shirtTex + self.toon.style.topTexColor = shirtColor + self.toon.style.sleeveTex = sleeveTex + self.toon.style.sleeveTexColor = sleeveColor + if self.toon.style.botTex not in ToonDNA.MakeAToonBoyBottoms: + botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON) + self.toon.style.botTex = botTex + self.toon.style.botTexColor = botTexColor + else: + length = len(ToonDNA.toonTorsoTypes[3:6]) + if self.toon.style.torso[1] == 'd': + torsoOffset = 3 + else: + torsoOffset = 0 + if self.dna.armColor not in ToonDNA.defaultGirlColorList: + self.dna.armColor = ToonDNA.defaultGirlColorList[0] + if self.dna.legColor not in ToonDNA.defaultGirlColorList: + self.dna.legColor = ToonDNA.defaultGirlColorList[0] + if self.dna.headColor not in ToonDNA.defaultGirlColorList: + self.dna.headColor = ToonDNA.defaultGirlColorList[0] + if self.toon.style.topTex not in ToonDNA.MakeAToonGirlShirts: + randomShirt = ToonDNA.getRandomTop(gender, ToonDNA.MAKE_A_TOON) + shirtTex, shirtColor, sleeveTex, sleeveColor = randomShirt + self.toon.style.topTex = shirtTex + self.toon.style.topTexColor = shirtColor + self.toon.style.sleeveTex = sleeveTex + self.toon.style.sleeveTexColor = sleeveColor + if self.toon.style.botTex not in ToonDNA.MakeAToonGirlBottoms: + if self.toon.style.torso[1] == 'd': + botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SKIRT) + self.toon.style.botTex = botTex + self.toon.style.botTexColor = botTexColor + torsoOffset = 3 + else: + botTex, botTexColor = ToonDNA.getRandomBottom(gender, ToonDNA.MAKE_A_TOON, girlBottomType=ToonDNA.SHORTS) + self.toon.style.botTex = botTex + self.toon.style.botTexColor = botTexColor + torsoOffset = 0 + self.torsoChoice = (self.torsoChoice + offset) % length + self.__updateScrollButtons(self.torsoChoice, length, self.torsoStart, self.torsoLButton, self.torsoRButton) + torso = ToonDNA.toonTorsoTypes[torsoOffset + self.torsoChoice] + self.dna.torso = torso + self.toon.swapToonTorso(torso) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + + def __swapLegs(self, offset): + length = len(ToonDNA.toonLegTypes) + self.legChoice = (self.legChoice + offset) % length + self.notify.debug('self.legChoice=%d, length=%d, self.legStart=%d' % (self.legChoice, length, self.legStart)) + self.__updateScrollButtons(self.legChoice, length, self.legStart, self.legLButton, self.legRButton) + newLeg = ToonDNA.toonLegTypes[self.legChoice] + self.dna.legs = newLeg + self.toon.swapToonLegs(newLeg) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + + def __swapHead(self, offset): + self.headList = ToonDNA.getHeadList(self.species) + length = len(self.headList) + self.headChoice = (self.headChoice + offset) % length + self.__updateHead() + + def __swapSpecies(self, offset): + length = len(ToonDNA.toonSpeciesTypes) + self.speciesChoice = (self.speciesChoice + offset) % length + self.__updateScrollButtons(self.speciesChoice, length, self.speciesStart, self.speciesLButton, self.speciesRButton) + self.species = ToonDNA.toonSpeciesTypes[self.speciesChoice] + self.headList = ToonDNA.getHeadList(self.species) + self.__changeSpeciesName(self.species) + maxHeadChoice = len(self.headList) - 1 + if self.headChoice > maxHeadChoice: + self.headChoice = maxHeadChoice + self.__updateHead() + + def __updateHead(self): + self.__updateScrollButtons(self.headChoice, len(self.headList), self.headStart, self.headLButton, self.headRButton) + headIndex = ToonDNA.getHeadStartIndex(self.species) + self.headChoice + newHead = ToonDNA.toonHeadTypes[headIndex] + self.dna.head = newHead + self.toon.swapToonHead(newHead) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + self.restrictHeadType(newHead) + + def __updateScrollButtons(self, choice, length, start, lButton, rButton): + if choice == (start - 1) % length: + rButton['state'] = DGG.DISABLED + elif choice != (start - 1) % length: + rButton['state'] = DGG.NORMAL + if choice == start % length: + lButton['state'] = DGG.DISABLED + elif choice != start % length: + lButton['state'] = DGG.NORMAL + if lButton['state'] == DGG.DISABLED and rButton['state'] == DGG.DISABLED: + self.notify.info('Both buttons got disabled! Doing fallback code. choice%d, length=%d, start=%d, lButton=%s, rButton=%s' % (choice, + length, + start, + lButton, + rButton)) + if choice == start % length: + lButton['state'] = DGG.DISABLED + rButton['state'] = DGG.NORMAL + elif choice == (start - 1) % length: + lButton['state'] = DGG.NORMAL + rButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + rButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def restrictHeadType(self, head): + if not base.cr.isPaid(): + if head[0] in ('h', 'p', 'b'): + self.accept('next', self.__restrictForward) + else: + self.accept('next', self.__handleForward) + + def __restrictForward(self): + TeaserPanel.TeaserPanel(pageName='species') + + def changeBody(self): + newChoice = self.shuffleButton.getCurrChoice() + newHead = newChoice[0] + newSpeciesIndex = ToonDNA.toonSpeciesTypes.index(ToonDNA.getSpecies(newHead)) + newHeadIndex = ToonDNA.toonHeadTypes.index(newHead) - ToonDNA.getHeadStartIndex(ToonDNA.getSpecies(newHead)) + newTorsoIndex = ToonDNA.toonTorsoTypes.index(newChoice[1]) + newLegsIndex = ToonDNA.toonLegTypes.index(newChoice[2]) + oldHead = self.toon.style.head + oldSpeciesIndex = ToonDNA.toonSpeciesTypes.index(ToonDNA.getSpecies(oldHead)) + oldHeadIndex = ToonDNA.toonHeadTypes.index(oldHead) - ToonDNA.getHeadStartIndex(ToonDNA.getSpecies(oldHead)) + oldTorsoIndex = ToonDNA.toonTorsoTypes.index(self.toon.style.torso) + oldLegsIndex = ToonDNA.toonLegTypes.index(self.toon.style.legs) + self.__swapSpecies(newSpeciesIndex - oldSpeciesIndex) + self.__swapHead(newHeadIndex - oldHeadIndex) + self.__swapTorso(newTorsoIndex - oldTorsoIndex) + self.__swapLegs(newLegsIndex - oldLegsIndex) + + def getCurrToonSetting(self): + return [self.toon.style.head, self.toon.style.torso, self.toon.style.legs] + + def __changeSpeciesName(self, species): + if species == 'd': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['dog'] + self.memberButton.hide() + elif species == 'c': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['cat'] + self.memberButton.hide() + elif species == 'm': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['mouse'] + self.memberButton.hide() + elif species == 'h': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['horse'] + self.memberButton.show() + elif species == 'r': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['rabbit'] + self.memberButton.hide() + elif species == 'f': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['duck'] + self.memberButton.hide() + elif species == 'p': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['monkey'] + self.memberButton.show() + elif species == 'b': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['bear'] + self.memberButton.show() + elif species == 's': + self.speciesFrame['text'] = TTLocalizer.AnimalToSpecies['pig'] + self.memberButton.hide() + if base.cr.isPaid(): + self.memberButton.hide() diff --git a/toontown/makeatoon/ClothesGUI.py b/toontown/makeatoon/ClothesGUI.py new file mode 100644 index 0000000..5433484 --- /dev/null +++ b/toontown/makeatoon/ClothesGUI.py @@ -0,0 +1,218 @@ +from pandac.PandaModules import * +from toontown.toon import ToonDNA +from direct.fsm import StateData +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from MakeAToonGlobals import * +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +import ShuffleButton +import random +CLOTHES_MAKETOON = 0 +CLOTHES_TAILOR = 1 +CLOTHES_CLOSET = 2 + +class ClothesGUI(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ClothesGUI') + + def __init__(self, type, doneEvent, swapEvent = None): + StateData.StateData.__init__(self, doneEvent) + self.type = type + self.toon = None + self.swapEvent = swapEvent + self.gender = '?' + self.girlInShorts = 0 + self.swappedTorso = 0 + return + + def load(self): + self.gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowRollover = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.gui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowDisabled = self.gui.find('**/tt_t_gui_mat_arrowDisabled') + shuffleFrame = self.gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleArrowUp = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = self.gui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowRollover = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDisabled = self.gui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + self.parentFrame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.416), frameColor=(1, 0, 0, 0)) + self.shirtFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.4), hpr=(0, 0, 3), scale=1.2, frameColor=(1, 1, 1, 1), text=TTLocalizer.ClothesShopShirt, text_scale=0.0575, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.topLButton = DirectButton(parent=self.shirtFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.swapTop, extraArgs=[-1]) + self.topRButton = DirectButton(parent=self.shirtFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.swapTop, extraArgs=[1]) + self.bottomFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.65), hpr=(0, 0, -2), scale=1.2, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopToon, text_scale=0.0575, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.bottomLButton = DirectButton(parent=self.bottomFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.swapBottom, extraArgs=[-1]) + self.bottomRButton = DirectButton(parent=self.bottomFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.swapBottom, extraArgs=[1]) + self.parentFrame.hide() + self.shuffleFetchMsg = 'ClothesShopShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + return + + def unload(self): + self.gui.removeNode() + del self.gui + self.parentFrame.destroy() + self.shirtFrame.destroy() + self.bottomFrame.destroy() + self.topLButton.destroy() + self.topRButton.destroy() + self.bottomLButton.destroy() + self.bottomRButton.destroy() + del self.parentFrame + del self.shirtFrame + del self.bottomFrame + del self.topLButton + del self.topRButton + del self.bottomLButton + del self.bottomRButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + + def showButtons(self): + self.parentFrame.show() + + def hideButtons(self): + self.parentFrame.hide() + + def enter(self, toon): + self.notify.debug('enter') + base.disableMouse() + self.toon = toon + self.setupScrollInterface() + if not self.type == CLOTHES_TAILOR: + currTop = (self.toon.style.topTex, + self.toon.style.topTexColor, + self.toon.style.sleeveTex, + self.toon.style.sleeveTexColor) + currTopIndex = self.tops.index(currTop) + self.swapTop(currTopIndex - self.topChoice) + currBottom = (self.toon.style.botTex, self.toon.style.botTexColor) + currBottomIndex = self.bottoms.index(currBottom) + self.swapBottom(currBottomIndex - self.bottomChoice) + choicePool = [self.tops, self.bottoms] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeClothes) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + + def exit(self): + try: + del self.toon + except: + self.notify.warning('ClothesGUI: toon not found') + + self.hideButtons() + self.ignore('enter') + self.ignore('next') + self.ignore('last') + self.ignore(self.shuffleFetchMsg) + + def setupButtons(self): + self.girlInShorts = 0 + if self.gender == 'f': + if self.bottomChoice == -1: + botTex = self.bottoms[0][0] + else: + botTex = self.bottoms[self.bottomChoice][0] + if ToonDNA.GirlBottoms[botTex][1] == ToonDNA.SHORTS: + self.girlInShorts = 1 + if self.toon.style.getGender() == 'm': + self.bottomFrame['text'] = TTLocalizer.ClothesShopShorts + else: + self.bottomFrame['text'] = TTLocalizer.ClothesShopBottoms + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('next', self.__handleForward) + return None + + def swapTop(self, offset): + length = len(self.tops) + self.topChoice += offset + if self.topChoice <= 0: + self.topChoice = 0 + self.updateScrollButtons(self.topChoice, length, 0, self.topLButton, self.topRButton) + if self.topChoice < 0 or self.topChoice >= len(self.tops) or len(self.tops[self.topChoice]) != 4: + self.notify.warning('topChoice index is out of range!') + return None + self.toon.style.topTex = self.tops[self.topChoice][0] + self.toon.style.topTexColor = self.tops[self.topChoice][1] + self.toon.style.sleeveTex = self.tops[self.topChoice][2] + self.toon.style.sleeveTexColor = self.tops[self.topChoice][3] + self.toon.generateToonClothes() + if self.swapEvent != None: + messenger.send(self.swapEvent) + messenger.send('wakeup') + + def swapBottom(self, offset): + length = len(self.bottoms) + self.bottomChoice += offset + if self.bottomChoice <= 0: + self.bottomChoice = 0 + self.updateScrollButtons(self.bottomChoice, length, 0, self.bottomLButton, self.bottomRButton) + if self.bottomChoice < 0 or self.bottomChoice >= len(self.bottoms) or len(self.bottoms[self.bottomChoice]) != 2: + self.notify.warning('bottomChoice index is out of range!') + return None + self.toon.style.botTex = self.bottoms[self.bottomChoice][0] + self.toon.style.botTexColor = self.bottoms[self.bottomChoice][1] + if self.toon.generateToonClothes() == 1: + self.toon.loop('neutral', 0) + self.swappedTorso = 1 + if self.swapEvent != None: + messenger.send(self.swapEvent) + messenger.send('wakeup') + + def updateScrollButtons(self, choice, length, startTex, lButton, rButton): + if choice >= length - 1: + rButton['state'] = DGG.DISABLED + else: + rButton['state'] = DGG.NORMAL + if choice <= 0: + lButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def resetClothes(self, style): + if self.toon: + self.toon.style.makeFromNetString(style.makeNetString()) + if self.swapEvent != None and self.swappedTorso == 1: + self.toon.swapToonTorso(self.toon.style.torso, genClothes=0) + self.toon.generateToonClothes() + self.toon.loop('neutral', 0) + return + + def changeClothes(self): + self.notify.debug('Entering changeClothes') + newChoice = self.shuffleButton.getCurrChoice() + if newChoice[0] in self.tops: + newTopIndex = self.tops.index(newChoice[0]) + else: + newTopIndex = self.topChoice + if newChoice[1] in self.bottoms: + newBottomIndex = self.bottoms.index(newChoice[1]) + else: + newBottomIndex = self.bottomChoice + oldTopIndex = self.topChoice + oldBottomIndex = self.bottomChoice + self.swapTop(newTopIndex - oldTopIndex) + self.swapBottom(newBottomIndex - oldBottomIndex) + + def getCurrToonSetting(self): + return [self.tops[self.topChoice], self.bottoms[self.bottomChoice]] diff --git a/toontown/makeatoon/ColorShop.py b/toontown/makeatoon/ColorShop.py new file mode 100644 index 0000000..fe6f752 --- /dev/null +++ b/toontown/makeatoon/ColorShop.py @@ -0,0 +1,230 @@ +from pandac.PandaModules import * +from toontown.toon import ToonDNA +from direct.fsm import StateData +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from MakeAToonGlobals import * +from toontown.toonbase import TTLocalizer +import ShuffleButton +import random +from direct.directnotify import DirectNotifyGlobal + +class ColorShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ColorShop') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.toon = None + self.colorAll = 1 + return + + def getGenderColorList(self, dna): + if self.dna.getGender() == 'm': + return ToonDNA.defaultBoyColorList + else: + return ToonDNA.defaultGirlColorList + + def enter(self, toon, shopsVisited = []): + base.disableMouse() + self.toon = toon + self.dna = toon.getStyle() + colorList = self.getGenderColorList(self.dna) + try: + self.headChoice = colorList.index(self.dna.headColor) + self.armChoice = colorList.index(self.dna.armColor) + self.legChoice = colorList.index(self.dna.legColor) + except: + self.headChoice = random.choice(colorList) + self.armChoice = self.headChoice + self.legChoice = self.headChoice + self.__swapHeadColor(0) + self.__swapArmColor(0) + self.__swapLegColor(0) + + self.startColor = 0 + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('next', self.__handleForward) + choicePool = [self.getGenderColorList(self.dna), self.getGenderColorList(self.dna), self.getGenderColorList(self.dna)] + self.shuffleButton.setChoicePool(choicePool) + self.accept(self.shuffleFetchMsg, self.changeColor) + self.acceptOnce('MAT-newToonCreated', self.shuffleButton.cleanHistory) + + def showButtons(self): + self.parentFrame.show() + + def hideButtons(self): + self.parentFrame.hide() + + def exit(self): + self.ignore('last') + self.ignore('next') + self.ignore('enter') + self.ignore(self.shuffleFetchMsg) + try: + del self.toon + except: + print 'ColorShop: toon not found' + + self.hideButtons() + + def load(self): + self.gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiRArrowUp = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowRollover = self.gui.find('**/tt_t_gui_mat_arrowUp') + guiRArrowDown = self.gui.find('**/tt_t_gui_mat_arrowDown') + guiRArrowDisabled = self.gui.find('**/tt_t_gui_mat_arrowDisabled') + shuffleFrame = self.gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleArrowUp = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = self.gui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowRollover = self.gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDisabled = self.gui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + self.parentFrame = DirectFrame(relief=DGG.RAISED, pos=(0.98, 0, 0.416), frameColor=(1, 0, 0, 0)) + self.toonFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.073), hpr=(0, 0, 0), scale=1.3, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopToon, text_scale=TTLocalizer.CStoonFrame, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.allLButton = DirectButton(parent=self.toonFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapAllColor, extraArgs=[-1]) + self.allRButton = DirectButton(parent=self.toonFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapAllColor, extraArgs=[1]) + self.headFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.3), hpr=(0, 0, 2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopHead, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.headLButton = DirectButton(parent=self.headFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapHeadColor, extraArgs=[-1]) + self.headRButton = DirectButton(parent=self.headFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapHeadColor, extraArgs=[1]) + self.bodyFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonScale, relief=None, pos=(0, 0, -0.5), hpr=(0, 0, -2), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopBody, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.armLButton = DirectButton(parent=self.bodyFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapArmColor, extraArgs=[-1]) + self.armRButton = DirectButton(parent=self.bodyFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapArmColor, extraArgs=[1]) + self.legsFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, pos=(0, 0, -0.7), hpr=(0, 0, 3), scale=0.9, frameColor=(1, 1, 1, 1), text=TTLocalizer.ColorShopLegs, text_scale=0.0625, text_pos=(-0.001, -0.015), text_fg=(1, 1, 1, 1)) + self.legLButton = DirectButton(parent=self.legsFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.2, 0, 0), command=self.__swapLegColor, extraArgs=[-1]) + self.legRButton = DirectButton(parent=self.legsFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowRollover, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.2, 0, 0), command=self.__swapLegColor, extraArgs=[1]) + self.parentFrame.hide() + self.shuffleFetchMsg = 'ColorShopShuffle' + self.shuffleButton = ShuffleButton.ShuffleButton(self, self.shuffleFetchMsg) + return + + def unload(self): + self.gui.removeNode() + del self.gui + self.parentFrame.destroy() + self.toonFrame.destroy() + self.headFrame.destroy() + self.bodyFrame.destroy() + self.legsFrame.destroy() + self.headLButton.destroy() + self.headRButton.destroy() + self.armLButton.destroy() + self.armRButton.destroy() + self.legLButton.destroy() + self.legRButton.destroy() + self.allLButton.destroy() + self.allRButton.destroy() + del self.parentFrame + del self.toonFrame + del self.headFrame + del self.bodyFrame + del self.legsFrame + del self.headLButton + del self.headRButton + del self.armLButton + del self.armRButton + del self.legLButton + del self.legRButton + del self.allLButton + del self.allRButton + self.shuffleButton.unload() + self.ignore('MAT-newToonCreated') + + def __swapAllColor(self, offset): + colorList = self.getGenderColorList(self.dna) + length = len(colorList) + choice = (self.headChoice + offset) % length + self.__updateScrollButtons(choice, length, self.allLButton, self.allRButton) + self.__swapHeadColor(offset) + oldArmColorIndex = colorList.index(self.toon.style.armColor) + oldLegColorIndex = colorList.index(self.toon.style.legColor) + self.__swapArmColor(choice - oldArmColorIndex) + self.__swapLegColor(choice - oldLegColorIndex) + + def __swapHeadColor(self, offset): + colorList = self.getGenderColorList(self.dna) + length = len(colorList) + self.headChoice = (self.headChoice + offset) % length + self.__updateScrollButtons(self.headChoice, length, self.headLButton, self.headRButton) + newColor = colorList[self.headChoice] + self.dna.headColor = newColor + self.toon.swapToonColor(self.dna) + + def __swapArmColor(self, offset): + colorList = self.getGenderColorList(self.dna) + length = len(colorList) + self.armChoice = (self.armChoice + offset) % length + self.__updateScrollButtons(self.armChoice, length, self.armLButton, self.armRButton) + newColor = colorList[self.armChoice] + self.dna.armColor = newColor + self.toon.swapToonColor(self.dna) + + def __swapLegColor(self, offset): + colorList = self.getGenderColorList(self.dna) + length = len(colorList) + self.legChoice = (self.legChoice + offset) % length + self.__updateScrollButtons(self.legChoice, length, self.legLButton, self.legRButton) + newColor = colorList[self.legChoice] + self.dna.legColor = newColor + self.toon.swapToonColor(self.dna) + + def __updateScrollButtons(self, choice, length, lButton, rButton): + if choice == (self.startColor - 1) % length: + rButton['state'] = DGG.DISABLED + else: + rButton['state'] = DGG.NORMAL + if choice == self.startColor % length: + lButton['state'] = DGG.DISABLED + else: + lButton['state'] = DGG.NORMAL + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def changeColor(self): + self.notify.debug('Entering changeColor') + colorList = self.getGenderColorList(self.dna) + newChoice = self.shuffleButton.getCurrChoice() + newHeadColorIndex = colorList.index(newChoice[0]) + newArmColorIndex = colorList.index(newChoice[1]) + newLegColorIndex = colorList.index(newChoice[2]) + oldHeadColorIndex = colorList.index(self.toon.style.headColor) + oldArmColorIndex = colorList.index(self.toon.style.armColor) + oldLegColorIndex = colorList.index(self.toon.style.legColor) + self.__swapHeadColor(newHeadColorIndex - oldHeadColorIndex) + if self.colorAll: + self.__swapArmColor(newHeadColorIndex - oldArmColorIndex) + self.__swapLegColor(newHeadColorIndex - oldLegColorIndex) + else: + self.__swapArmColor(newArmColorIndex - oldArmColorIndex) + self.__swapLegColor(newLegColorIndex - oldLegColorIndex) + + def getCurrToonSetting(self): + return [self.dna.headColor, self.dna.armColor, self.dna.legColor] diff --git a/toontown/makeatoon/GenderShop.py b/toontown/makeatoon/GenderShop.py new file mode 100644 index 0000000..79934b8 --- /dev/null +++ b/toontown/makeatoon/GenderShop.py @@ -0,0 +1,118 @@ +from pandac.PandaModules import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toon import ToonDNA +from toontown.toon import Toon +from MakeAToonGlobals import * +from direct.directnotify import DirectNotifyGlobal +import random + +class GenderShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('GenderShop') + + def __init__(self, makeAToon, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.shopsVisited = [] + self.toon = None + self.gender = 'm' + self.makeAToon = makeAToon + return + + def enter(self): + base.disableMouse() + self.accept('next', self.__handleForward) + return None + + def showButtons(self): + return None + + def exit(self): + self.ignore('next') + + def load(self): + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiBoyUp = gui.find('**/tt_t_gui_mat_boyUp') + guiBoyDown = gui.find('**/tt_t_gui_mat_boyDown') + guiGirlUp = gui.find('**/tt_t_gui_mat_girlUp') + guiGirlDown = gui.find('**/tt_t_gui_mat_girlDown') + self.boyButton = DirectButton(relief=None, image=(guiBoyUp, + guiBoyDown, + guiBoyUp, + guiBoyDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.4, 0, -0.8), command=self.createRandomBoy, text=('', + TTLocalizer.GenderShopBoyButtonText, + TTLocalizer.GenderShopBoyButtonText, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, 0.19), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.boyButton.hide() + self.girlButton = DirectButton(relief=None, image=(guiGirlUp, + guiGirlDown, + guiGirlUp, + guiGirlDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(0.4, 0, -0.8), command=self.createRandomGirl, text=('', + TTLocalizer.GenderShopGirlButtonText, + TTLocalizer.GenderShopGirlButtonText, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, 0.19), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.girlButton.hide() + gui.removeNode() + del gui + self.toon = None + return + + def unload(self): + self.boyButton.destroy() + self.girlButton.destroy() + del self.boyButton + del self.girlButton + if self.toon: + self.toon.delete() + self.makeAToon = None + return + + def setGender(self, choice): + self.__setGender(choice) + + def __setGender(self, choice): + self.gender = 'm' + if self.toon: + self.gender = self.toon.style.gender + messenger.send(self.doneEvent) + + def hideButtons(self): + self.boyButton.hide() + self.girlButton.hide() + + def showButtons(self): + self.boyButton.show() + self.girlButton.show() + + def createRandomBoy(self): + self._createRandomToon('m') + + def createRandomGirl(self): + self._createRandomToon('f') + + def _createRandomToon(self, gender): + if self.toon: + self.toon.stopBlink() + self.toon.stopLookAroundNow() + self.toon.delete() + self.dna = ToonDNA.ToonDNA() + self.dna.newToonRandom(gender=gender, stage=1) + self.toon = Toon.Toon() + self.toon.setDNA(self.dna) + self.toon.useLOD(1000) + self.toon.setNameVisible(0) + self.toon.startBlink() + self.toon.startLookAround() + self.toon.reparentTo(render) + self.toon.setPos(self.makeAToon.toonPosition) + self.toon.setHpr(self.makeAToon.toonHpr) + self.toon.setScale(self.makeAToon.toonScale) + self.toon.loop('neutral') + self.makeAToon.setNextButtonState(DGG.NORMAL) + self.makeAToon.setToon(self.toon) + messenger.send('MAT-newToonCreated') + + def __handleForward(self): + self.doneStatus = 'next' + messenger.send(self.doneEvent) diff --git a/toontown/makeatoon/MakeAToon.py b/toontown/makeatoon/MakeAToon.py new file mode 100644 index 0000000..3cb2fa3 --- /dev/null +++ b/toontown/makeatoon/MakeAToon.py @@ -0,0 +1,759 @@ +from pandac.PandaModules import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.char import Char +from otp.avatar import Avatar +from toontown.toon import Toon +from toontown.toon import LocalToon +from toontown.toon import ToonDNA +from toontown.char import CharDNA +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from toontown.toonbase import ToontownGlobals +from direct.actor.Actor import Actor +from direct.task import Task +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from MakeAToonGlobals import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +import GenderShop +import BodyShop +import ColorShop +import MakeClothesGUI +import NameShop +import random + +class MakeAToon(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('MakeAToon') + + def __init__(self, parentFSM, avList, doneEvent, index, isPaid): + self.isPaid = isPaid + StateData.StateData.__init__(self, doneEvent) + self.phase = 3 + self.names = ['', + '', + '', + ''] + self.dnastring = None + self.dna = None + self.progressing = 0 + self.toonPosition = Point3(-1.62, -3.49, 0) + self.toonScale = Point3(1, 1, 1) + self.toonHpr = Point3(180, 0, 0) + self.leftTime = 1.6 + self.rightTime = 1 + self.slide = 0 + self.nameList = [] + self.warp = 0 + for av in avList: + if av.position == index: + self.warp = 1 + self.namelessPotAv = av + self.nameList.append(av.name) + + self.fsm = ClassicFSM.ClassicFSM('MakeAToon', [State.State('Init', self.enterInit, self.exitInit, ['GenderShop', 'NameShop']), + State.State('GenderShop', self.enterGenderShop, self.exitGenderShop, ['BodyShop']), + State.State('BodyShop', self.enterBodyShop, self.exitBodyShop, ['GenderShop', 'ColorShop']), + State.State('ColorShop', self.enterColorShop, self.exitColorShop, ['BodyShop', 'ClothesShop']), + State.State('ClothesShop', self.enterClothesShop, self.exitClothesShop, ['ColorShop', 'NameShop']), + State.State('NameShop', self.enterNameShop, self.exitNameShop, ['ClothesShop']), + State.State('Done', self.enterDone, self.exitDone, [])], 'Init', 'Done') + self.parentFSM = parentFSM + self.parentFSM.getStateNamed('createAvatar').addChild(self.fsm) + self.gs = GenderShop.GenderShop(self, 'GenderShop-done') + self.bs = BodyShop.BodyShop('BodyShop-done') + self.cos = ColorShop.ColorShop('ColorShop-done') + self.cls = MakeClothesGUI.MakeClothesGUI('ClothesShop-done') + self.ns = NameShop.NameShop(self, 'NameShop-done', avList, index, self.isPaid) + self.shop = GENDERSHOP + self.shopsVisited = [] + if self.warp: + self.shopsVisited = [GENDERSHOP, + BODYSHOP, + COLORSHOP, + CLOTHESSHOP] + self.music = None + self.soundBack = None + self.fsm.enterInitialState() + self.hprDelta = -1 + self.dropIval = None + self.roomSquishIval = None + self.propSquishIval = None + self.focusOutIval = None + self.focusInIval = None + self.toon = None + return + + def getToon(self): + return self.toon + + def enter(self): + self.notify.debug('Starting Make A Toon.') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: MAKEATOON: Starting Make A Toon') + base.cr.centralLogger.writeClientEvent('MAT - startingMakeAToon') + base.camLens.setFov(ToontownGlobals.MakeAToonCameraFov) + base.playMusic(self.music, looping=1, volume=self.musicVolume) + camera.setPosHpr(-5.7, -12.3501, 2.15, -24.8499, 2.73, 0) + if self.warp: + if self.toon.style.torso[1] == 's': + self.toon.gender = 's' + else: + self.toon.gender = 'd' + self.toon.reparentTo(render) + self.toon.loop('neutral') + self.toon.setPosHpr(-4.1, -2, 0, 200, 0, 0) + self.guiTopBar.show() + self.guiBottomBar.show() + self.guiCancelButton.show() + if self.warp: + self.progressing = 0 + self.guiLastButton.hide() + self.fsm.request('NameShop') + else: + self.fsm.request('GenderShop') + + def exit(self): + base.camLens.setFov(ToontownGlobals.DefaultCameraFov) + self.guiTopBar.hide() + self.guiBottomBar.hide() + self.music.stop() + self.fsm.request('Done') + self.room.reparentTo(hidden) + + def load(self): + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + guiAcceptUp = gui.find('**/tt_t_gui_mat_okUp') + guiAcceptDown = gui.find('**/tt_t_gui_mat_okDown') + guiCancelUp = gui.find('**/tt_t_gui_mat_closeUp') + guiCancelDown = gui.find('**/tt_t_gui_mat_closeDown') + guiNextUp = gui.find('**/tt_t_gui_mat_nextUp') + guiNextDown = gui.find('**/tt_t_gui_mat_nextDown') + guiNextDisabled = gui.find('**/tt_t_gui_mat_nextDisabled') + skipTutorialUp = gui.find('**/tt_t_gui_mat_skipUp') + skipTutorialDown = gui.find('**/tt_t_gui_mat_skipDown') + rotateUp = gui.find('**/tt_t_gui_mat_arrowRotateUp') + rotateDown = gui.find('**/tt_t_gui_mat_arrowRotateDown') + self.guiTopBar = DirectFrame(relief=None, text=TTLocalizer.CreateYourToon, text_font=ToontownGlobals.getSignFont(), text_fg=(0.0, 0.65, 0.35, 1), text_scale=0.18, text_pos=(0, -0.03), pos=(0, 0, 0.86)) + self.guiTopBar.hide() + self.guiBottomBar = DirectFrame(relief=None, image_scale=(1.25, 1, 1), pos=(0.01, 0, -0.86)) + self.guiBottomBar.hide() + self.guiCheckButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiAcceptUp, + guiAcceptDown, + guiAcceptUp, + guiAcceptDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(1.165, 0, -0.018), command=self.__handleNext, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_align=TextNode.ARight, text_pos=(0.13, 0.13), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiCheckButton.hide() + self.guiCancelButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiCancelUp, + guiCancelDown, + guiCancelUp, + guiCancelDown), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-1.179, 0, -0.011), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.MATguiCancelButton, text_pos=(0, 0.115), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiCancelButton.hide() + self.guiNextButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiNextUp, + guiNextDown, + guiNextUp, + guiNextDisabled), image_scale=(0.3, 0.3, 0.3), image1_scale=(0.35, 0.35, 0.35), image2_scale=(0.35, 0.35, 0.35), pos=(1.165, 0, -0.018), command=self.__handleNext, text=('', + TTLocalizer.MakeAToonNext, + TTLocalizer.MakeAToonNext, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.MATguiNextButton, text_pos=(0, 0.115), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiNextButton.hide() + self.guiLastButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(guiNextUp, + guiNextDown, + guiNextUp, + guiNextDown), image3_color=Vec4(0.5, 0.5, 0.5, 0.75), image_scale=(-0.3, 0.3, 0.3), image1_scale=(-0.35, 0.35, 0.35), image2_scale=(-0.35, 0.35, 0.35), pos=(0.825, 0, -0.018), command=self.__handleLast, text=('', + TTLocalizer.MakeAToonLast, + TTLocalizer.MakeAToonLast, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, 0.115), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + self.guiLastButton.hide() + self.rotateLeftButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(rotateUp, + rotateDown, + rotateUp, + rotateDown), image_scale=(-0.4, 0.4, 0.4), image1_scale=(-0.5, 0.5, 0.5), image2_scale=(-0.5, 0.5, 0.5), pos=(-0.329249, 0, 0.202961)) + self.rotateLeftButton.hide() + self.rotateLeftButton.bind(DGG.B1PRESS, self.rotateToonLeft) + self.rotateLeftButton.bind(DGG.B1RELEASE, self.stopToonRotateLeftTask) + self.rotateRightButton = DirectButton(parent=self.guiBottomBar, relief=None, image=(rotateUp, + rotateDown, + rotateUp, + rotateDown), image_scale=(0.4, 0.4, 0.4), image1_scale=(0.5, 0.5, 0.5), image2_scale=(0.5, 0.5, 0.5), pos=(0.309534, 0, 0.206116)) + self.rotateRightButton.hide() + self.rotateRightButton.bind(DGG.B1PRESS, self.rotateToonRight) + self.rotateRightButton.bind(DGG.B1RELEASE, self.stopToonRotateRightTask) + gui.removeNode() + self.roomDropActor = Actor() + self.roomDropActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.roomDropActor.loadAnims({'drop': 'phase_3/models/makeatoon/roomAnim_roomDrop'}) + self.roomDropActor.reparentTo(render) + self.dropJoint = self.roomDropActor.find('**/droppingJoint') + self.roomSquishActor = Actor() + self.roomSquishActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.roomSquishActor.loadAnims({'squish': 'phase_3/models/makeatoon/roomAnim_roomSquish'}) + self.roomSquishActor.reparentTo(render) + self.squishJoint = self.roomSquishActor.find('**/scalingJoint') + self.propSquishActor = Actor() + self.propSquishActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.propSquishActor.loadAnims({'propSquish': 'phase_3/models/makeatoon/roomAnim_propSquish'}) + self.propSquishActor.reparentTo(render) + self.propSquishActor.pose('propSquish', 0) + self.propJoint = self.propSquishActor.find('**/propJoint') + self.spotlightActor = Actor() + self.spotlightActor.loadModel('phase_3/models/makeatoon/roomAnim_model') + self.spotlightActor.loadAnims({'spotlightShake': 'phase_3/models/makeatoon/roomAnim_spotlightShake'}) + self.spotlightActor.reparentTo(render) + self.spotlightJoint = self.spotlightActor.find('**/spotlightJoint') + ee = DirectFrame(pos=(-1, 1, 1), frameSize=(-.01, 0.01, -.01, 0.01), frameColor=(0, 0, 0, 0.05), state='normal') + ee.bind(DGG.B1PRESS, lambda x, ee = ee: self.toggleSlide()) + self.eee = ee + self.room = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_room') + self.genderWalls = self.room.find('**/genderWalls') + self.genderProps = self.room.find('**/genderProps') + self.bodyWalls = self.room.find('**/bodyWalls') + self.bodyProps = self.room.find('**/bodyProps') + self.colorWalls = self.room.find('**/colorWalls') + self.colorProps = self.room.find('**/colorProps') + self.clothesWalls = self.room.find('**/clothWalls') + self.clothesProps = self.room.find('**/clothProps') + self.nameWalls = self.room.find('**/nameWalls') + self.nameProps = self.room.find('**/nameProps') + self.background = self.room.find('**/background') + self.background.reparentTo(render) + self.floor = self.room.find('**/floor') + self.floor.reparentTo(render) + self.spotlight = self.room.find('**/spotlight') + self.spotlight.reparentTo(self.spotlightJoint) + self.spotlight.setColor(1, 1, 1, 0.3) + self.spotlight.setPos(1.18, -1.27, 0.41) + self.spotlight.setScale(2.6) + self.spotlight.setHpr(0, 0, 0) + smokeSeqNode = SequenceNode('smoke') + smokeModel = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_smoke') + smokeFrameList = smokeModel.findAllMatches('**/smoke_*').asList() + smokeFrameList.reverse() + for smokeFrame in smokeFrameList: + smokeSeqNode.addChild(smokeFrame.node()) + + smokeSeqNode.setFrameRate(12) + self.smoke = render.attachNewNode(smokeSeqNode) + self.smoke.setScale(1, 1, 0.75) + self.smoke.hide() + if self.warp: + self.dna = ToonDNA.ToonDNA() + self.dna.makeFromNetString(self.namelessPotAv.dna) + self.toon = Toon.Toon() + self.toon.setDNA(self.dna) + self.toon.useLOD(1000) + self.toon.setNameVisible(0) + self.toon.startBlink() + self.toon.startLookAround() + self.gs.load() + self.bs.load() + self.cos.load() + self.cls.load() + self.ns.load() + self.music = base.loadMusic('phase_3/audio/bgm/create_a_toon.mid') + self.musicVolume = base.config.GetFloat('makeatoon-music-volume', 1) + self.sfxVolume = base.config.GetFloat('makeatoon-sfx-volume', 1) + self.soundBack = base.loadSfx('phase_3/audio/sfx/GUI_create_toon_back.mp3') + self.crashSounds = [] + self.crashSounds.append(base.loadSfx('phase_3/audio/sfx/tt_s_ara_mat_crash_boing.mp3')) + self.crashSounds.append(base.loadSfx('phase_3/audio/sfx/tt_s_ara_mat_crash_glassBoing.mp3')) + self.crashSounds.append(base.loadSfx('phase_3/audio/sfx/tt_s_ara_mat_crash_wood.mp3')) + self.crashSounds.append(base.loadSfx('phase_3/audio/sfx/tt_s_ara_mat_crash_woodBoing.mp3')) + self.crashSounds.append(base.loadSfx('phase_3/audio/sfx/tt_s_ara_mat_crash_woodGlass.mp3')) + return + + def unload(self): + self.exit() + if self.toon: + self.toon.stopBlink() + self.toon.stopLookAroundNow() + self.gs.unload() + self.bs.unload() + self.cos.unload() + self.cls.unload() + self.ns.unload() + del self.gs + del self.bs + del self.cos + del self.cls + del self.ns + self.guiTopBar.destroy() + self.guiBottomBar.destroy() + self.guiCancelButton.destroy() + self.guiCheckButton.destroy() + self.eee.destroy() + self.guiNextButton.destroy() + self.guiLastButton.destroy() + self.rotateLeftButton.destroy() + self.rotateRightButton.destroy() + del self.guiTopBar + del self.guiBottomBar + del self.guiCancelButton + del self.guiCheckButton + del self.eee + del self.guiNextButton + del self.guiLastButton + del self.rotateLeftButton + del self.rotateRightButton + del self.names + del self.dnastring + del self.nameList + del self.music + del self.soundBack + del self.dna + if self.toon: + self.toon.delete() + del self.toon + self.cleanupDropIval() + self.cleanupRoomSquishIval() + self.cleanupPropSquishIval() + self.cleanupFocusInIval() + self.cleanupFocusOutIval() + self.room.removeNode() + del self.room + self.genderWalls.removeNode() + self.genderProps.removeNode() + del self.genderWalls + del self.genderProps + self.bodyWalls.removeNode() + self.bodyProps.removeNode() + del self.bodyWalls + del self.bodyProps + self.colorWalls.removeNode() + self.colorProps.removeNode() + del self.colorWalls + del self.colorProps + self.clothesWalls.removeNode() + self.clothesProps.removeNode() + del self.clothesWalls + del self.clothesProps + self.nameWalls.removeNode() + self.nameProps.removeNode() + del self.nameWalls + del self.nameProps + self.background.removeNode() + del self.background + self.floor.removeNode() + del self.floor + self.spotlight.removeNode() + del self.spotlight + self.smoke.removeNode() + del self.smoke + while len(self.crashSounds): + del self.crashSounds[0] + + self.parentFSM.getStateNamed('createAvatar').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + loader.unloadModel('phase_3/models/gui/create_a_toon_gui') + loader.unloadModel('phase_3/models/gui/create_a_toon') + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def getDNA(self): + return self.dnastring + + def __handleBodyShop(self): + self.fsm.request('BodyShop') + + def __handleClothesShop(self): + self.fsm.request('ClothesShop') + + def __handleColorShop(self): + self.fsm.request('ColorShop') + + def __handleNameShop(self): + self.fsm.request('NameShop') + + def __handleCancel(self): + self.doneStatus = 'cancel' + self.shopsVisited = [] + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent)) + + def toggleSlide(self): + self.slide = 1 - self.slide + + def goToNextShop(self): + self.progressing = 1 + if self.shop == GENDERSHOP: + self.fsm.request('BodyShop') + elif self.shop == BODYSHOP: + self.fsm.request('ColorShop') + elif self.shop == COLORSHOP: + self.fsm.request('ClothesShop') + else: + self.fsm.request('NameShop') + + def goToLastShop(self): + self.progressing = 0 + if self.shop == BODYSHOP: + self.fsm.request('GenderShop') + elif self.shop == COLORSHOP: + self.fsm.request('BodyShop') + elif self.shop == CLOTHESSHOP: + self.fsm.request('ColorShop') + else: + self.fsm.request('ClothesShop') + + def charSez(self, char, statement, dialogue = None): + import pdb + pdb.set_trace() + char.setChatAbsolute(statement, CFSpeech, dialogue) + + def enterInit(self): + pass + + def exitInit(self): + pass + + def enterGenderShop(self): + base.cr.centralLogger.writeClientEvent('MAT - enteringGenderShop') + self.shop = GENDERSHOP + if GENDERSHOP not in self.shopsVisited: + self.shopsVisited.append(GENDERSHOP) + self.genderWalls.reparentTo(self.squishJoint) + self.genderProps.reparentTo(self.propJoint) + self.roomSquishActor.pose('squish', 0) + self.guiNextButton['state'] = DGG.DISABLED + else: + self.dropRoom(self.genderWalls, self.genderProps) + self.guiTopBar['text'] = TTLocalizer.CreateYourToonTitle + self.guiTopBar['text_fg'] = (1, 0.92, 0.2, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterGenderShop + base.transitions.fadeIn() + self.accept('GenderShop-done', self.__handleGenderShopDone) + self.gs.enter() + self.guiNextButton.show() + self.gs.showButtons() + self.rotateLeftButton.hide() + self.rotateRightButton.hide() + + def exitGenderShop(self): + self.squishRoom(self.genderWalls) + self.squishProp(self.genderProps) + self.gs.exit() + self.ignore('GenderShop-done') + + def __handleGenderShopDone(self): + self.guiNextButton.hide() + self.gs.hideButtons() + self.goToNextShop() + + def bodyShopOpening(self): + self.bs.showButtons() + self.guiNextButton.show() + self.guiLastButton.show() + self.rotateLeftButton.show() + self.rotateRightButton.show() + + def enterBodyShop(self): + base.cr.centralLogger.writeClientEvent('MAT - enteringBodyShop') + self.toon.show() + self.shop = BODYSHOP + self.guiTopBar['text'] = TTLocalizer.ShapeYourToonTitle + self.guiTopBar['text_fg'] = (0.0, 0.98, 0.5, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterBodyShop + self.accept('BodyShop-done', self.__handleBodyShopDone) + self.dropRoom(self.bodyWalls, self.bodyProps) + self.bs.enter(self.toon, self.shopsVisited) + if BODYSHOP not in self.shopsVisited: + self.shopsVisited.append(BODYSHOP) + self.bodyShopOpening() + + def exitBodyShop(self): + self.squishRoom(self.bodyWalls) + self.squishProp(self.bodyProps) + self.bs.exit() + self.ignore('BodyShop-done') + + def __handleBodyShopDone(self): + self.guiNextButton.hide() + self.guiLastButton.hide() + if self.bs.doneStatus == 'next': + self.bs.hideButtons() + self.goToNextShop() + else: + self.bs.hideButtons() + self.goToLastShop() + + def colorShopOpening(self): + self.cos.showButtons() + self.guiNextButton.show() + self.guiLastButton.show() + self.rotateLeftButton.show() + self.rotateRightButton.show() + + def enterColorShop(self): + base.cr.centralLogger.writeClientEvent('MAT - enteringColorShop') + self.shop = COLORSHOP + self.guiTopBar['text'] = TTLocalizer.PaintYourToonTitle + self.guiTopBar['text_fg'] = (0, 1, 1, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterColorShop + self.accept('ColorShop-done', self.__handleColorShopDone) + self.dropRoom(self.colorWalls, self.colorProps) + self.toon.setPos(self.toonPosition) + self.colorShopOpening() + self.cos.enter(self.toon, self.shopsVisited) + if COLORSHOP not in self.shopsVisited: + self.shopsVisited.append(COLORSHOP) + + def exitColorShop(self): + self.squishRoom(self.colorWalls) + self.squishProp(self.colorProps) + self.cos.exit() + self.ignore('ColorShop-done') + + def __handleColorShopDone(self): + self.guiNextButton.hide() + self.guiLastButton.hide() + if self.cos.doneStatus == 'next': + self.cos.hideButtons() + self.goToNextShop() + else: + self.cos.hideButtons() + self.goToLastShop() + + def clothesShopOpening(self): + self.guiNextButton.show() + self.guiLastButton.show() + self.cls.showButtons() + self.rotateLeftButton.show() + self.rotateRightButton.show() + + def enterClothesShop(self): + base.cr.centralLogger.writeClientEvent('MAT - enteringClothesShop') + self.shop = CLOTHESSHOP + self.guiTopBar['text'] = TTLocalizer.PickClothesTitle + self.guiTopBar['text_fg'] = (1, 0.92, 0.2, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterClothesShop + self.accept('ClothesShop-done', self.__handleClothesShopDone) + self.dropRoom(self.clothesWalls, self.clothesProps) + self.toon.setScale(self.toonScale) + self.toon.setPos(self.toonPosition) + if not self.progressing: + self.toon.setHpr(self.toonHpr) + self.clothesShopOpening() + self.cls.enter(self.toon) + if CLOTHESSHOP not in self.shopsVisited: + self.shopsVisited.append(CLOTHESSHOP) + + def exitClothesShop(self): + self.squishRoom(self.clothesWalls) + self.squishProp(self.clothesProps) + self.cls.exit() + self.ignore('ClothesShop-done') + + def __handleClothesShopDone(self): + self.guiNextButton.hide() + self.guiLastButton.hide() + if self.cls.doneStatus == 'next': + self.cls.hideButtons() + self.goToNextShop() + else: + self.cls.hideButtons() + self.goToLastShop() + + def nameShopOpening(self, task): + self.guiCheckButton.show() + self.guiLastButton.show() + if self.warp: + self.guiLastButton.hide() + if NAMESHOP not in self.shopsVisited: + self.shopsVisited.append(NAMESHOP) + return Task.done + + def enterNameShop(self): + base.cr.centralLogger.writeClientEvent('MAT - enteringNameShop') + self.shop = NAMESHOP + self.guiTopBar['text'] = TTLocalizer.NameToonTitle + self.guiTopBar['text_fg'] = (0.0, 0.98, 0.5, 1) + self.guiTopBar['text_scale'] = TTLocalizer.MATenterNameShop + self.accept('NameShop-done', self.__handleNameShopDone) + self.dropRoom(self.nameWalls, self.nameProps) + self.spotlight.setPos(2, -1.95, 0.41) + self.spotlight.setScale(2.3) + self.toon.setPos(Point3(1.5, -4, 0)) + self.toon.setH(120) + self.rotateLeftButton.hide() + self.rotateRightButton.hide() + if self.progressing: + waittime = self.leftTime + else: + waittime = 0.2 + self.ns.enter(self.toon, self.nameList, self.warp) + taskMgr.doMethodLater(waittime, self.nameShopOpening, 'nameShopOpeningTask') + + def exitNameShop(self): + self.squishRoom(self.nameWalls) + self.squishProp(self.nameProps) + self.spotlight.setPos(1.18, -1.27, 0.41) + self.spotlight.setScale(2.6) + self.ns.exit() + self.ignore('NameShop-done') + taskMgr.remove('nameShopOpeningTask') + + def rejectName(self): + self.ns.rejectName(TTLocalizer.RejectNameText) + + def __handleNameShopDone(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: MAKEATOON: Creating A Toon') + self.guiLastButton.hide() + self.guiCheckButton.hide() + if self.ns.getDoneStatus() == 'last': + self.ns.hideAll() + self.goToLastShop() + elif self.ns.getDoneStatus() == 'paynow': + self.doneStatus = 'paynow' + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent)) + else: + self.doneStatus = 'created' + base.transitions.fadeOut(finishIval=EventInterval(self.doneEvent)) + + def __handleNext(self): + messenger.send('next') + + def __handleLast(self): + messenger.send('last') + + def __handleSkipTutorial(self): + messenger.send('skipTutorial') + + def enterDone(self): + pass + + def exitDone(self): + pass + + def create3DGui(self): + self.proto = loader.loadModel('phase_3/models/makeatoon/tt_m_ara_mat_protoMachine') + self.proto.setScale(0.2) + self.proto.reparentTo(render) + + def setup3DPicker(self): + self.accept('mouse1', self.mouseDown) + self.accept('mouse1-up', self.mouseUp) + self.pickerQueue = CollisionHandlerQueue() + self.pickerTrav = CollisionTraverser('MousePickerTraverser') + self.pickerTrav.setRespectPrevTransform(True) + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.pickerTrav.addCollider(self.pickerNP, self.pickerQueue) + + def mouseDown(self): + self.notify.debug('Mouse 1 Down') + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.pickerTrav.traverse(render) + if self.pickerQueue.getNumEntries() > 0: + self.pickerQueue.sortEntries() + self.pickedObj = self.pickerQueue.getEntry(0).getIntoNodePath() + + def mouseUp(self): + self.notify.debug('Mouse 1 Up') + + def squishRoom(self, room): + if self.roomSquishIval and self.roomSquishIval.isPlaying(): + self.roomSquishIval.finish() + squishDuration = self.roomSquishActor.getDuration('squish') + self.roomSquishIval = Sequence(Func(self.roomSquishActor.play, 'squish'), Wait(squishDuration), Func(room.hide)) + self.roomSquishIval.start() + + def squishProp(self, prop): + if not prop.isEmpty(): + if self.propSquishIval and self.propSquishIval.isPlaying(): + self.propSquishIval.finish() + squishDuration = self.propSquishActor.getDuration('propSquish') + self.propSquishIval = Sequence(Func(self.propSquishActor.play, 'propSquish'), Wait(squishDuration), Func(prop.hide)) + self.propSquishIval.start() + + def dropRoom(self, walls, props): + + def propReparentTo(props): + if not props.isEmpty(): + props.reparentTo(self.propJoint) + + if self.dropIval and self.dropIval.isPlaying(): + self.dropIval.finish() + walls.reparentTo(self.dropJoint) + walls.show() + if not props.isEmpty(): + props.reparentTo(self.dropJoint) + props.show() + dropDuration = self.roomDropActor.getDuration('drop') + self.dropIval = Parallel(Sequence(Func(self.roomDropActor.play, 'drop'), Wait(dropDuration), Func(walls.reparentTo, self.squishJoint), Func(propReparentTo, props), Func(self.propSquishActor.pose, 'propSquish', 0), Func(self.roomSquishActor.pose, 'squish', 0)), Sequence(Wait(0.25), Func(self.smoke.show), Func(self.smoke.node().play), LerpColorScaleInterval(self.smoke, 0.5, Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1)), Func(self.smoke.hide)), Func(self.spotlightActor.play, 'spotlightShake'), Func(self.playRandomCrashSound)) + self.dropIval.start() + + def startFocusOutIval(self): + if self.focusInIval.isPlaying(): + self.focusInIval.pause() + if not self.focusOutIval.isPlaying(): + self.focusOutIval = LerpScaleInterval(self.spotlight, 0.25, self.spotlightFinalScale) + self.focusOutIval.start() + + def startFocusInIval(self): + if self.focusOutIval.isPlaying(): + self.focusOutIval.pause() + if not self.focusInIval.isPlaying(): + self.focusInIval = LerpScaleInterval(self.spotlight, 0.25, self.spotlightOriginalScale) + self.focusInIval.start() + + def cleanupFocusOutIval(self): + if self.focusOutIval: + self.focusOutIval.finish() + del self.focusOutIval + + def cleanupFocusInIval(self): + if self.focusInIval: + self.focusInIval.finish() + del self.focusInIval + + def cleanupDropIval(self): + if self.dropIval: + self.dropIval.finish() + del self.dropIval + + def cleanupRoomSquishIval(self): + if self.roomSquishIval: + self.roomSquishIval.finish() + del self.roomSquishIval + + def cleanupPropSquishIval(self): + if self.propSquishIval: + self.propSquishIval.finish() + del self.propSquishIval + + def setToon(self, toon): + self.toon = toon + + def setNextButtonState(self, state): + self.guiNextButton['state'] = state + + def playRandomCrashSound(self): + index = random.randint(0, len(self.crashSounds) - 1) + base.playSfx(self.crashSounds[index], volume=self.sfxVolume) + + def rotateToonLeft(self, event): + taskMgr.add(self.rotateToonLeftTask, 'rotateToonLeftTask') + + def rotateToonLeftTask(self, task): + self.toon.setH(self.toon.getH() + self.hprDelta) + return task.cont + + def stopToonRotateLeftTask(self, event): + taskMgr.remove('rotateToonLeftTask') + + def rotateToonRight(self, event): + taskMgr.add(self.rotateToonRightTask, 'rotateToonRightTask') + + def rotateToonRightTask(self, task): + self.toon.setH(self.toon.getH() - self.hprDelta) + return task.cont + + def stopToonRotateRightTask(self, event): + taskMgr.remove('rotateToonRightTask') diff --git a/toontown/makeatoon/MakeAToonGlobals.py b/toontown/makeatoon/MakeAToonGlobals.py new file mode 100644 index 0000000..6575352 --- /dev/null +++ b/toontown/makeatoon/MakeAToonGlobals.py @@ -0,0 +1,13 @@ +GENDERSHOP = 1 +BODYSHOP = 2 +COLORSHOP = 3 +CLOTHESSHOP = 4 +NAMESHOP = 5 +halfButtonScale = (0.6, 0.6, 0.6) +halfButtonHoverScale = (0.7, 0.7, 0.7) +halfButtonInvertScale = (-0.6, 0.6, 0.6) +halfButtonInvertHoverScale = (-0.7, 0.7, 0.7) +buttonScale = (1.2, 1.2, 1.2) +buttonHoverScale = (1.4, 1.4, 1.4) +buttonInvertScale = (-1.2, 1.2, 1.2) +buttonInvertHoverScale = (-1.4, 1.4, 1.4) diff --git a/toontown/makeatoon/MakeClothesGUI.py b/toontown/makeatoon/MakeClothesGUI.py new file mode 100644 index 0000000..4cfe39b --- /dev/null +++ b/toontown/makeatoon/MakeClothesGUI.py @@ -0,0 +1,35 @@ +import ClothesGUI +from toontown.toon import ToonDNA + +class MakeClothesGUI(ClothesGUI.ClothesGUI): + notify = directNotify.newCategory('MakeClothesGUI') + + def __init__(self, doneEvent): + ClothesGUI.ClothesGUI.__init__(self, ClothesGUI.CLOTHES_MAKETOON, doneEvent) + + def setupScrollInterface(self): + self.dna = self.toon.getStyle() + gender = self.dna.getGender() + if gender != self.gender: + self.tops = ToonDNA.getRandomizedTops(gender, tailorId=ToonDNA.MAKE_A_TOON) + self.bottoms = ToonDNA.getRandomizedBottoms(gender, tailorId=ToonDNA.MAKE_A_TOON) + self.gender = gender + self.topChoice = 0 + self.bottomChoice = 0 + self.setupButtons() + + def setupButtons(self): + ClothesGUI.ClothesGUI.setupButtons(self) + if len(self.dna.torso) == 1: + if self.gender == 'm': + torsoStyle = 's' + elif self.girlInShorts == 1: + torsoStyle = 's' + else: + torsoStyle = 'd' + self.toon.swapToonTorso(self.dna.torso[0] + torsoStyle) + self.toon.loop('neutral', 0) + self.toon.swapToonColor(self.dna) + self.swapTop(0) + self.swapBottom(0) + return None diff --git a/toontown/makeatoon/NameGenerator.py b/toontown/makeatoon/NameGenerator.py new file mode 100644 index 0000000..4b2ad42 --- /dev/null +++ b/toontown/makeatoon/NameGenerator.py @@ -0,0 +1,369 @@ +from pandac.PandaModules import * +import random +import string +import copy +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import os +from direct.showbase import AppRunnerGlobal +from direct.directnotify import DirectNotifyGlobal + +class NameGenerator: + text = TextNode('text') + text.setFont(ToontownGlobals.getInterfaceFont()) + notify = DirectNotifyGlobal.directNotify.newCategory('NameGenerator') + boyTitles = [] + girlTitles = [] + neutralTitles = [] + boyFirsts = [] + girlFirsts = [] + neutralFirsts = [] + capPrefixes = [] + lastPrefixes = [] + lastSuffixes = [] + + def __init__(self): + self.generateLists() + + def generateLists(self): + self.boyTitles = [] + self.girlTitles = [] + self.neutralTitles = [] + self.boyFirsts = [] + self.girlFirsts = [] + self.neutralFirsts = [] + self.capPrefixes = [] + self.lastPrefixes = [] + self.lastSuffixes = [] + self.nameDictionary = {} + searchPath = DSearchPath() + if AppRunnerGlobal.appRunner: + searchPath.appendDirectory(Filename.expandFrom('$TT_3_ROOT/phase_3/etc')) + else: + searchPath.appendDirectory(Filename('phase_3/etc')) + base = os.path.expandvars('$TOONTOWN') or './toontown' + searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars(base + '/src/configfiles'))) + searchPath.appendDirectory(Filename('.')) + filename = Filename(TTLocalizer.NameShopNameMaster) + found = vfs.resolveFilename(filename, searchPath) + if not found: + self.notify.error("NameGenerator: Error opening name list text file '%s.'" % TTLocalizer.NameShopNameMaster) + input = StreamReader(vfs.openReadFile(filename, 1), 1) + currentLine = input.readline() + while currentLine: + if currentLine.lstrip()[0:1] != '#': + a1 = currentLine.find('*') + a2 = currentLine.find('*', a1 + 1) + self.nameDictionary[int(currentLine[0:a1])] = (int(currentLine[a1 + 1:a2]), currentLine[a2 + 1:len(currentLine) - 1]) + currentLine = input.readline() + + masterList = [self.boyTitles, + self.girlTitles, + self.neutralTitles, + self.boyFirsts, + self.girlFirsts, + self.neutralFirsts, + self.capPrefixes, + self.lastPrefixes, + self.lastSuffixes] + for tu in self.nameDictionary.values(): + masterList[tu[0]].append(tu[1]) + + return 1 + + def _getNameParts(self, cat2part): + nameParts = [{}, + {}, + {}, + {}] + for id, tpl in self.nameDictionary.iteritems(): + cat, str = tpl + if cat in cat2part: + nameParts[cat2part[cat]][str] = id + + return nameParts + + def getMaleNameParts(self): + return self._getNameParts({0: 0, + 2: 0, + 3: 1, + 5: 1, + 6: 2, + 7: 2, + 8: 3}) + + def getFemaleNameParts(self): + return self._getNameParts({1: 0, + 2: 0, + 4: 1, + 5: 1, + 6: 2, + 7: 2, + 8: 3}) + + def getLastNamePrefixesCapped(self): + return self.capPrefixes + + def returnUniqueID(self, name, listnumber): + newtu = [(), (), ()] + if listnumber == 0: + newtu[0] = (0, name) + newtu[1] = (1, name) + newtu[2] = (2, name) + elif listnumber == 1: + newtu[0] = (3, name) + newtu[1] = (4, name) + newtu[2] = (5, name) + elif listnumber == 2: + newtu[0] = (6, name) + newtu[1] = (7, name) + else: + newtu[0] = (8, name) + for tu in self.nameDictionary.items(): + for g in newtu: + if tu[1] == g: + return tu[0] + + return -1 + + def findWidestInList(self, text, nameList): + maxWidth = 0 + maxName = '' + for name in nameList: + width = text.calcWidth(name) + if width > maxWidth: + maxWidth = text.calcWidth(name) + maxName = name + + print maxName + ' ' + str(maxWidth) + return maxName + + def findWidestName(self): + longestBoyTitle = self.findWidestInList(self.text, self.boyTitles + self.neutralTitles) + longestGirlTitle = self.findWidestInList(self.text, self.girlTitles + self.neutralTitles) + longestBoyFirst = self.findWidestInList(self.text, self.boyFirsts + self.neutralFirsts) + longestGirlFirst = self.findWidestInList(self.text, self.girlFirsts + self.neutralFirsts) + longestLastPrefix = self.findWidestInList(self.text, self.lastPrefixes) + longestLastSuffix = self.findWidestInList(self.text, self.lastSuffixes) + longestBoyFront = self.findWidestInList(self.text, [longestBoyTitle, longestBoyFirst]) + longestGirlFront = self.findWidestInList(self.text, [longestGirlTitle, longestGirlFirst]) + longestBoyName = longestBoyTitle + ' ' + longestBoyFirst + ' ' + longestLastPrefix + longestLastSuffix + longestGirlName = longestGirlTitle + ' ' + longestGirlFirst + ' ' + longestLastPrefix + longestLastSuffix + longestName = self.findWidestInList(self.text, [longestBoyName, longestGirlName]) + return longestName + + def findWidestTitleFirst(self): + longestBoyTitle = self.findWidestInList(self.text, self.boyTitles + self.neutralTitles) + longestGirlTitle = self.findWidestInList(self.text, self.girlTitles + self.neutralTitles) + longestBoyFirst = self.findWidestInList(self.text, self.boyFirsts + self.neutralFirsts) + longestGirlFirst = self.findWidestInList(self.text, self.girlFirsts + self.neutralFirsts) + longestBoyName = longestBoyTitle + ' ' + longestBoyFirst + longestGirlName = longestGirlTitle + ' ' + longestGirlFirst + longestName = self.findWidestInList(self.text, [longestBoyName, longestGirlName]) + + def findWidestTitle(self): + widestTitle = self.findWidestInList(self.text, self.neutralTitles + self.boyTitles + self.girlTitles) + return widestTitle + + def findWidestFirstName(self): + widestFirst = self.findWidestInList(self.text, self.neutralFirsts + self.boyFirsts + self.girlFirsts) + return widestFirst + + def findWidestLastName(self): + longestLastPrefix = self.findWidestInList(self.text, self.lastPrefixes) + longestLastSuffix = self.findWidestInList(self.text, self.lastSuffixes) + longestLastName = longestLastPrefix + longestLastSuffix + return longestLastName + + def findWidestNameWord(self): + widestWord = self.findWidestInList(self.text, [self.findWidestTitle(), self.findWidestFirstName(), self.findWidestLastName()]) + return widestWord + + def findWidestNameWidth(self): + name = self.findWidestName() + return self.text.calcWidth(name) + + def printWidestName(self): + name = self.findWidestName() + width = self.text.calcWidth(name) + widthStr = str(width) + print 'The widest name is: ' + name + ' (' + widthStr + ' units)' + + def printWidestLastName(self): + name = self.findWidestLastName() + width = self.text.calcWidth(name) + widthStr = str(width) + print 'The widest last name is: ' + name + ' (' + widthStr + ' units)' + + def randomName(self, boy = 0, girl = 0): + if boy and girl: + self.error("A name can't be both boy and girl!") + if not boy and not girl: + boy = random.choice([0, 1]) + girl = not boy + uberFlag = random.choice(['title-first', + 'title-last', + 'first', + 'last', + 'first-last', + 'title-first-last']) + titleFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'title-last' or uberFlag == 'title-first-last': + titleFlag = 1 + firstFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'first' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + firstFlag = 1 + lastFlag = 0 + if uberFlag == 'title-last' or uberFlag == 'last' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + lastFlag = 1 + retString = '' + if titleFlag: + titleList = self.neutralTitles[:] + if boy: + titleList += self.boyTitles + elif girl: + titleList += self.girlTitles + else: + self.error('Must be boy or girl.') + retString += random.choice(titleList) + ' ' + if firstFlag: + firstList = self.neutralFirsts[:] + if boy: + firstList += self.boyFirsts + elif girl: + firstList += self.girlFirsts + else: + self.error('Must be boy or girl.') + retString += random.choice(firstList) + if lastFlag: + retString += ' ' + if lastFlag: + lastPrefix = random.choice(self.lastPrefixes) + lastSuffix = random.choice(self.lastSuffixes) + if lastPrefix in self.capPrefixes: + lastSuffix = lastSuffix.capitalize() + retString += lastPrefix + lastSuffix + return retString + + def randomNameMoreinfo(self, boy = 0, girl = 0): + if boy and girl: + self.error("A name can't be both boy and girl!") + if not boy and not girl: + boy = random.choice([0, 1]) + girl = not boy + uberFlag = random.choice(['title-first', + 'title-last', + 'first', + 'last', + 'first-last', + 'title-first-last']) + titleFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'title-last' or uberFlag == 'title-first-last': + titleFlag = 1 + firstFlag = 0 + if uberFlag == 'title-first' or uberFlag == 'first' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + firstFlag = 1 + lastFlag = 0 + if uberFlag == 'title-last' or uberFlag == 'last' or uberFlag == 'first-last' or uberFlag == 'title-first-last': + lastFlag = 1 + retString = '' + uberReturn = [0, + 0, + 0, + '', + '', + '', + ''] + uberReturn[0] = titleFlag + uberReturn[1] = firstFlag + uberReturn[2] = lastFlag + titleList = self.neutralTitles[:] + if boy: + titleList += self.boyTitles + elif girl: + titleList += self.girlTitles + else: + self.error('Must be boy or girl.') + uberReturn[3] = random.choice(titleList) + firstList = self.neutralFirsts[:] + if boy: + firstList += self.boyFirsts + elif girl: + firstList += self.girlFirsts + else: + self.error('Must be boy or girl.') + uberReturn[4] = random.choice(firstList) + lastPrefix = random.choice(self.lastPrefixes) + lastSuffix = random.choice(self.lastSuffixes) + if lastPrefix in self.capPrefixes: + lastSuffix = lastSuffix.capitalize() + uberReturn[5] = lastPrefix + uberReturn[6] = lastSuffix + if titleFlag: + retString += uberReturn[3] + ' ' + if firstFlag: + retString += uberReturn[4] + if lastFlag: + retString += ' ' + if lastFlag: + retString += uberReturn[5] + uberReturn[6] + uberReturn.append(retString) + return uberReturn + + def printRandomNames(self, boy = 0, girl = 0, total = 1): + i = 0 + origBoy = boy + origGirl = girl + while i < total: + if not origBoy and not origGirl: + boy = random.choice([0, 1]) + girl = not boy + name = self.randomName(boy, girl) + width = self.text.calcWidth(name) + widthStr = str(width) + if boy: + print 'Boy: ' + name + ' (' + widthStr + ' units)' + if girl: + print 'Girl: ' + name + ' (' + widthStr + ' units)' + i += 1 + + def percentOver(self, limit = 9.0, samples = 1000): + i = 0 + over = 0 + while i < samples: + name = self.randomName() + width = self.text.calcWidth(name) + if width > limit: + over += 1 + i += 1 + + percent = float(over) / float(samples) * 100 + print 'Samples: ' + str(samples) + ' Over: ' + str(over) + ' Percent: ' + str(percent) + + def totalNames(self): + firsts = len(self.boyFirsts) + len(self.girlFirsts) + len(self.neutralFirsts) + print 'Total firsts: ' + str(firsts) + lasts = len(self.lastPrefixes) * len(self.lastSuffixes) + print 'Total lasts: ' + str(lasts) + neutralTitleFirsts = len(self.neutralTitles) * len(self.neutralFirsts) + boyTitleFirsts = len(self.boyTitles) * (len(self.neutralFirsts) + len(self.boyFirsts)) + len(self.neutralTitles) * len(self.boyFirsts) + girlTitleFirsts = len(self.girlTitles) * (len(self.neutralFirsts) + len(self.girlFirsts)) + len(self.neutralTitles) * len(self.girlFirsts) + totalTitleFirsts = neutralTitleFirsts + boyTitleFirsts + girlTitleFirsts + print 'Total title firsts: ' + str(totalTitleFirsts) + neutralTitleLasts = len(self.neutralTitles) * lasts + boyTitleLasts = len(self.boyTitles) * lasts + girlTitleLasts = len(self.girlTitles) * lasts + totalTitleLasts = neutralTitleLasts + boyTitleFirsts + girlTitleLasts + print 'Total title lasts: ' + str(totalTitleLasts) + neutralFirstLasts = len(self.neutralFirsts) * lasts + boyFirstLasts = len(self.boyFirsts) * lasts + girlFirstLasts = len(self.girlFirsts) * lasts + totalFirstLasts = neutralFirstLasts + boyFirstLasts + girlFirstLasts + print 'Total first lasts: ' + str(totalFirstLasts) + neutralTitleFirstLasts = neutralTitleFirsts * lasts + boyTitleFirstLasts = boyTitleFirsts * lasts + girlTitleFirstLasts = girlTitleFirsts * lasts + totalTitleFirstLasts = neutralTitleFirstLasts + boyTitleFirstLasts + girlTitleFirstLasts + print 'Total title first lasts: ' + str(totalTitleFirstLasts) + totalNames = firsts + lasts + totalTitleFirsts + totalTitleLasts + totalFirstLasts + totalTitleFirstLasts + print 'Total Names: ' + str(totalNames) diff --git a/toontown/makeatoon/NameShop.py b/toontown/makeatoon/NameShop.py new file mode 100644 index 0000000..54c0341 --- /dev/null +++ b/toontown/makeatoon/NameShop.py @@ -0,0 +1,1111 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.task.TaskManagerGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.distributed.ToontownMsgTypes import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui import OnscreenText +from otp.avatar import Avatar +from otp.chat import ChatManager +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toontowngui import TTDialog +import re +import string +from toontown.toonbase import TTLocalizer +import NameGenerator +import random +from otp.distributed import PotentialAvatar +from otp.namepanel import NameCheck +from toontown.toontowngui import TeaserPanel +from direct.distributed.PyDatagram import PyDatagram +from direct.showbase import PythonUtil +from toontown.toon import NPCToons +from direct.task import Task +from toontown.makeatoon.TTPickANamePattern import TTPickANamePattern +from pandac.PandaModules import TextEncoder +MAX_NAME_WIDTH = TTLocalizer.NSmaxNameWidth +ServerDialogTimeout = 3.0 + +class NameShop(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('NameShop') + + def __init__(self, makeAToon, doneEvent, avList, index, isPaid): + StateData.StateData.__init__(self, doneEvent) + self.makeAToon = makeAToon + self.isPaid = isPaid + self.avList = avList + self.index = index + self.shopsVisited = [] + self.avId = -1 + self.avExists = 0 + self.names = ['', + '', + '', + ''] + self.toon = None + self.boy = 0 + self.girl = 0 + self.allTitles = [] + self.allFirsts = [] + self.allPrefixes = [] + self.allSuffixes = [] + self.titleIndex = 0 + self.firstIndex = 0 + self.prefixIndex = 0 + self.suffixIndex = 0 + self.titleActive = 0 + self.firstActive = 0 + self.lastActive = 0 + self.quickFind = 0 + self.listsLoaded = 0 + self.addedGenderSpecific = 0 + self.chastise = 0 + self.nameIndices = [-1, + -1, + -1, + -1] + self.nameFlags = [1, + 1, + 1, + 0] + self.dummyReturn = 2 + self.nameAction = 0 + self.pickANameGUIElements = [] + self.typeANameGUIElements = [] + 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.fsm = ClassicFSM.ClassicFSM('NameShop', [State.State('Init', self.enterInit, self.exitInit, ['PayState']), + State.State('PayState', self.enterPayState, self.exitPayState, ['PickAName']), + State.State('PickAName', self.enterPickANameState, self.exitPickANameState, ['TypeAName', 'Done']), + State.State('TypeAName', self.enterTypeANameState, self.exitTypeANameState, ['PickAName', + 'Approval', + 'Accepted', + 'Rejected']), + State.State('Approval', self.enterApprovalState, self.exitApprovalState, ['PickAName', 'ApprovalAccepted']), + State.State('ApprovalAccepted', self.enterApprovalAcceptedState, self.exitApprovalAcceptedState, ['Done']), + State.State('Accepted', self.enterAcceptedState, self.exitAcceptedState, ['Done']), + State.State('Rejected', self.enterRejectedState, self.exitRejectedState, ['TypeAName']), + State.State('Done', self.enterDone, self.exitDone, ['Init'])], 'Init', 'Done') + self.parentFSM = makeAToon.fsm + self.parentFSM.getStateNamed('NameShop').addChild(self.fsm) + self.nameGen = NameGenerator.NameGenerator() + self.fsm.enterInitialState() + self.requestingSkipTutorial = False + return + + def makeLabel(self, te, index, others): + alig = others[0] + listName = others[1] + 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=TTLocalizer.NSmakeLabel, text_pos=newpos, text_align=alig, textMayChange=0) + df.bind(DGG.B1PRESS, lambda x, df = df: self.nameClickedOn(listName, index)) + return df + + def nameClickedOn(self, listType, index): + if listType == 'title': + self.titleIndex = index + elif listType == 'first': + self.firstIndex = index + elif listType == 'prefix': + self.prefixIndex = index + else: + self.suffixIndex = index + self.updateLists() + self.__listsChanged() + + def enter(self, toon, usedNames, warp): + self.notify.debug('enter') + self.newwarp = warp + self.avExists = warp + if self.avExists: + for g in self.avList: + if g.position == self.index: + self.avId = g.id + + if toon == None: + return + else: + self.toon = toon + if self.toon.style.gender == 'm': + self.boy = 1 + self.girl = 0 + else: + self.boy = 0 + self.girl = 1 + self.usedNames = usedNames + if not self.addedGenderSpecific or self.oldBoy != self.boy: + self.oldBoy = self.boy + self.listsLoaded = 0 + self.allTitles = [' '] + [' '] + self.nameGen.boyTitles * self.boy + self.nameGen.girlTitles * self.girl + self.nameGen.neutralTitles + self.allTitles.sort() + self.allTitles += [' '] + [' '] + self.allFirsts = [' '] + [' '] + self.nameGen.boyFirsts * self.boy + self.nameGen.girlFirsts * self.girl + self.nameGen.neutralFirsts + self.allFirsts.sort() + self.allFirsts += [' '] + [' '] + try: + k = self.allFirsts.index('Von') + self.allFirsts[k] = 'von' + except: + print "NameShop: Couldn't find von" + + if not self.addedGenderSpecific: + nameShopGui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop') + self.namePanel = DirectFrame(parent=aspect2d, image=None, relief='flat', state='disabled', pos=(-0.42, 0, -0.15), image_pos=(0, 0, 0.025), frameColor=(1, 1, 1, 0.3)) + panel = nameShopGui.find('**/tt_t_gui_mat_namePanel') + self.panelFrame = DirectFrame(image=panel, scale=(0.75, 0.7, 0.7), relief='flat', frameColor=(1, 1, 1, 0), pos=(-0.0163, 0, 0.1199)) + self.panelFrame.reparentTo(self.namePanel, sort=1) + self.pickANameGUIElements.append(self.namePanel) + self.pickANameGUIElements.append(self.panelFrame) + self.nameResult.reparentTo(self.namePanel, sort=2) + self.circle = nameShopGui.find('**/tt_t_gui_mat_namePanelCircle') + self.titleCheck = self.makeCheckBox((-0.615, 0, 0.371), TTLocalizer.TitleCheckBox, (0, 0.25, 0.5, 1), self.titleToggle) + self.firstCheck = self.makeCheckBox((-0.2193, 0, 0.371), TTLocalizer.FirstCheckBox, (0, 0.25, 0.5, 1), self.firstToggle) + self.lastCheck = self.makeCheckBox((0.3886, 0, 0.371), TTLocalizer.LastCheckBox, (0, 0.25, 0.5, 1), self.lastToggle) + del self.circle + self.pickANameGUIElements.append(self.titleCheck) + self.pickANameGUIElements.append(self.firstCheck) + self.pickANameGUIElements.append(self.lastCheck) + self.titleCheck.reparentTo(self.namePanel, sort=2) + self.firstCheck.reparentTo(self.namePanel, sort=2) + self.lastCheck.reparentTo(self.namePanel, sort=2) + nameShopGui.removeNode() + self.lastprefixScrollList.reparentTo(self.namePanel) + self.lastprefixScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.lastprefixScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.lastsuffixScrollList.reparentTo(self.namePanel) + self.lastsuffixScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.lastsuffixScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.titleHigh.reparentTo(self.namePanel) + self.prefixHigh.reparentTo(self.namePanel) + self.firstHigh.reparentTo(self.namePanel) + self.suffixHigh.reparentTo(self.namePanel) + self.randomButton.reparentTo(self.namePanel, sort=2) + self.typeANameButton.reparentTo(self.namePanel, sort=2) + self.pickANameGUIElements.remove(self.titleScrollList) + self.pickANameGUIElements.remove(self.firstnameScrollList) + self.titleScrollList.destroy() + self.firstnameScrollList.destroy() + self.titleScrollList = self.makeScrollList(None, (-0.6, 0, 0.202), (1, 0.8, 0.8, 1), self.allTitles, self.makeLabel, [TextNode.ACenter, 'title']) + self.firstnameScrollList = self.makeScrollList(None, (-0.2, 0, 0.202), (0.8, 1, 0.8, 1), self.allFirsts, self.makeLabel, [TextNode.ACenter, 'first']) + self.pickANameGUIElements.append(self.titleScrollList) + self.pickANameGUIElements.append(self.firstnameScrollList) + self.titleScrollList.reparentTo(self.namePanel, sort=-1) + self.titleScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.titleScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.firstnameScrollList.reparentTo(self.namePanel, sort=-1) + self.firstnameScrollList.decButton.wrtReparentTo(self.namePanel, sort=2) + self.firstnameScrollList.incButton.wrtReparentTo(self.namePanel, sort=2) + self.listsLoaded = 1 + self.addedGenderSpecific = 1 + self.__randomName() + self.typeANameButton['text'] = TTLocalizer.TypeANameButton + if self.isLoaded == 0: + self.load() + self.ubershow(self.pickANameGUIElements) + self.acceptOnce('next', self.__handleDone) + self.acceptOnce('last', self.__handleBackward) + self.acceptOnce('skipTutorial', self.__handleSkipTutorial) + self.__listsChanged() + self.fsm.request('PayState') + return + + def __overflowNameInput(self): + self.rejectName(TTLocalizer.NameTooLong) + + def exit(self): + self.notify.debug('exit') + if self.isLoaded == 0: + return None + self.ignore('next') + self.ignore('last') + self.ignore('skipTutorial') + self.hideAll() + return None + + def __listsChanged(self): + newname = '' + if self.listsLoaded: + if self.titleActive: + self.showList(self.titleScrollList) + self.titleHigh.show() + newtitle = self.titleScrollList['items'][self.titleScrollList.index + 2]['text'] + self.nameIndices[0] = self.nameGen.returnUniqueID(newtitle, 0) + newname += newtitle + ' ' + else: + self.nameIndices[0] = -1 + self.stealth(self.titleScrollList) + self.titleHigh.hide() + if self.firstActive: + self.showList(self.firstnameScrollList) + self.firstHigh.show() + newfirst = self.firstnameScrollList['items'][self.firstnameScrollList.index + 2]['text'] + if newfirst == 'von': + nt = 'Von' + else: + nt = newfirst + self.nameIndices[1] = self.nameGen.returnUniqueID(nt, 1) + if not self.titleActive and newfirst == 'von': + newfirst = 'Von' + newname += newfirst + else: + newname += newfirst + if newfirst == 'von': + self.nameFlags[1] = 0 + else: + self.nameFlags[1] = 1 + if self.lastActive: + newname += ' ' + else: + self.firstHigh.hide() + self.stealth(self.firstnameScrollList) + self.nameIndices[1] = -1 + if self.lastActive: + self.showList(self.lastprefixScrollList) + self.showList(self.lastsuffixScrollList) + self.prefixHigh.show() + self.suffixHigh.show() + lp = self.lastprefixScrollList['items'][self.lastprefixScrollList.index + 2]['text'] + ls = self.lastsuffixScrollList['items'][self.lastsuffixScrollList.index + 2]['text'] + self.nameIndices[2] = self.nameGen.returnUniqueID(lp, 2) + self.nameIndices[3] = self.nameGen.returnUniqueID(ls, 3) + newname += lp + if lp in self.nameGen.capPrefixes: + ls = ls.capitalize() + self.nameFlags[3] = 1 + else: + self.nameFlags[3] = 0 + newname += ls + else: + self.stealth(self.lastprefixScrollList) + self.stealth(self.lastsuffixScrollList) + self.prefixHigh.hide() + self.suffixHigh.hide() + self.nameIndices[2] = -1 + self.nameIndices[3] = -1 + self.titleIndex = self.titleScrollList.index + 2 + self.firstIndex = self.firstnameScrollList.index + 2 + self.prefixIndex = self.lastprefixScrollList.index + 2 + self.suffixIndex = self.lastsuffixScrollList.index + 2 + self.nameResult['text'] = newname + self.names[0] = newname + + def makeScrollList(self, gui, ipos, mcolor, nitems, nitemMakeFunction, nitemMakeExtraArgs): + self.notify.debug('makeScrollList') + it = nitems[:] + ds = DirectScrolledList(items=it, itemMakeFunction=nitemMakeFunction, itemMakeExtraArgs=nitemMakeExtraArgs, parent=aspect2d, relief=None, command=self.__listsChanged, pos=ipos, scale=0.6, incButton_image=(self.arrowUp, + self.arrowDown, + self.arrowHover, + self.arrowUp), incButton_relief=None, incButton_scale=(1.2, 1.2, -1.2), incButton_pos=(0.0189, 0, -0.5335), incButton_image0_color=mcolor, incButton_image1_color=mcolor, incButton_image2_color=mcolor, incButton_image3_color=Vec4(1, 1, 1, 0), decButton_image=(self.arrowUp, + self.arrowDown, + self.arrowHover, + self.arrowUp), decButton_relief=None, decButton_scale=(1.2, 1.2, 1.2), decButton_pos=(0.0195, 0, 0.1779), decButton_image0_color=mcolor, decButton_image1_color=mcolor, decButton_image2_color=mcolor, decButton_image3_color=Vec4(1, 1, 1, 0), itemFrame_pos=(-.2, 0, 0.028), itemFrame_scale=1.0, itemFrame_relief=DGG.RAISED, itemFrame_frameSize=(-0.07, + 0.5, + -0.52, + 0.12), itemFrame_frameColor=mcolor, itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=5, forceHeight=TTLocalizer.NSdirectScrolleList) + return ds + + def makeCheckBox(self, npos, ntex, ntexcolor, comm): + return DirectCheckButton(parent=aspect2d, relief=None, scale=0.1, boxBorder=0.08, boxImage=self.circle, boxImageScale=4, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=npos, text=ntex, text_fg=ntexcolor, text_scale=TTLocalizer.NSmakeCheckBox, 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) + + def makeHighlight(self, npos): + return DirectFrame(parent=aspect2d, relief='flat', scale=(0.552, 0, 0.11), state='disabled', frameSize=(-0.07, + 0.52, + -0.5, + 0.1), borderWidth=(0.01, 0.01), pos=npos, frameColor=(1, 0, 1, 0.4)) + + def titleToggle(self, value): + self.titleActive = self.titleCheck['indicatorValue'] + if not (self.titleActive or self.firstActive or self.lastActive): + self.titleActive = 1 + self.__listsChanged() + if self.titleActive: + self.titleScrollList.refresh() + self.updateCheckBoxes() + + def firstToggle(self, value): + self.firstActive = self.firstCheck['indicatorValue'] + if self.chastise == 2: + messenger.send('NameShop-mickeyChange', [[TTLocalizer.ApprovalForName1, TTLocalizer.ApprovalForName2]]) + self.chastise = 0 + if not self.firstActive and not self.lastActive: + self.firstActive = 1 + messenger.send('NameShop-mickeyChange', [[TTLocalizer.MustHaveAFirstOrLast1, TTLocalizer.MustHaveAFirstOrLast2]]) + self.chastise = 1 + self.__listsChanged() + if self.firstActive: + self.firstnameScrollList.refresh() + self.updateCheckBoxes() + + def lastToggle(self, value): + self.lastActive = self.lastCheck['indicatorValue'] + if self.chastise == 1: + messenger.send('NameShop-mickeyChange', [[TTLocalizer.ApprovalForName1, TTLocalizer.ApprovalForName2]]) + self.chastise = 0 + if not self.firstActive and not self.lastActive: + self.lastActive = 1 + messenger.send('NameShop-mickeyChange', [[TTLocalizer.MustHaveAFirstOrLast1, TTLocalizer.MustHaveAFirstOrLast2]]) + self.chastise = 2 + self.__listsChanged() + if self.lastActive: + self.lastprefixScrollList.refresh() + self.lastsuffixScrollList.refresh() + self.updateCheckBoxes() + + def updateCheckBoxes(self): + self.titleCheck['indicatorValue'] = self.titleActive + self.titleCheck.setIndicatorValue() + self.firstCheck['indicatorValue'] = self.firstActive + self.firstCheck.setIndicatorValue() + self.lastCheck['indicatorValue'] = self.lastActive + self.lastCheck.setIndicatorValue() + + def load(self): + self.notify.debug('load') + if self.isLoaded == 1: + return None + nameBalloon = loader.loadModel('phase_3/models/props/chatbox_input') + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_nameShop') + self.arrowUp = gui.find('**/tt_t_gui_mat_namePanelArrowUp') + self.arrowDown = gui.find('**/tt_t_gui_mat_namePanelArrowDown') + self.arrowHover = gui.find('**/tt_t_gui_mat_namePanelArrowHover') + self.squareUp = gui.find('**/tt_t_gui_mat_namePanelSquareUp') + self.squareDown = gui.find('**/tt_t_gui_mat_namePanelSquareDown') + self.squareHover = gui.find('**/tt_t_gui_mat_namePanelSquareHover') + typePanel = gui.find('**/tt_t_gui_mat_typeNamePanel') + self.typeNamePanel = DirectFrame(parent=aspect2d, image=None, relief='flat', scale=(0.75, 0.7, 0.7), state='disabled', pos=(-0.0163333, 0, 0.075), image_pos=(0, 0, 0.025), frameColor=(1, 1, 1, 0)) + self.typePanelFrame = DirectFrame(image=typePanel, relief='flat', frameColor=(1, 1, 1, 0), pos=(-0.008, 0, 0.019)) + self.typePanelFrame.reparentTo(self.typeNamePanel, sort=1) + self.typeANameGUIElements.append(self.typeNamePanel) + self.typeANameGUIElements.append(self.typePanelFrame) + self.nameLabel = OnscreenText.OnscreenText(TTLocalizer.PleaseTypeName, parent=aspect2d, style=OnscreenText.ScreenPrompt, scale=TTLocalizer.NSnameLabel, pos=(-0.0163333, 0.53)) + self.nameLabel.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.nameLabel) + self.typeNotification = OnscreenText.OnscreenText(TTLocalizer.AllNewNames, parent=aspect2d, style=OnscreenText.ScreenPrompt, scale=TTLocalizer.NStypeNotification, pos=(-0.0163333, 0.15)) + self.typeNotification.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.typeNotification) + self.nameMessages = OnscreenText.OnscreenText(TTLocalizer.NameMessages, parent=aspect2d, style=OnscreenText.ScreenPrompt, scale=0.06, pos=(-0.0163333, -0.05)) + self.nameMessages.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.nameMessages) + self.nameEntry = DirectEntry(parent=aspect2d, relief=None, scale=TTLocalizer.NSnameEntry, entryFont=getToonFont(), width=MAX_NAME_WIDTH, numLines=2, focus=0, cursorKeys=1, pos=(0.0, 0.0, 0.39), text_align=TextNode.ACenter, command=self.__typedAName, autoCapitalize=1) + self.nameEntry.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeANameGUIElements.append(self.nameEntry) + self.submitButton = DirectButton(parent=aspect2d, relief=None, image=(self.squareUp, + self.squareDown, + self.squareHover, + self.squareUp), image_scale=(1.2, 0, 1.1), pos=(-0.01, 0, -0.25), text=TTLocalizer.NameShopSubmitButton, text_scale=0.06, text_pos=(0, -0.02), command=self.__typedAName) + self.submitButton.wrtReparentTo(self.typeNamePanel, sort=2) + self.typeNamePanel.setPos(-0.42, 0, -0.078) + self.typeANameGUIElements.append(self.submitButton) + self.randomButton = DirectButton(parent=aspect2d, relief=None, image=(self.squareUp, + self.squareDown, + self.squareHover, + self.squareUp), image_scale=(1.15, 1.1, 1.1), scale=(1.05, 1, 1), pos=(0, 0, -0.25), text=TTLocalizer.ShuffleButton, text_scale=0.06, text_pos=(0, -0.02), command=self.__randomName) + self.pickANameGUIElements.append(self.randomButton) + self.typeANameButton = DirectButton(parent=aspect2d, relief=None, image=(self.squareUp, + self.squareDown, + self.squareHover, + self.squareUp), image_scale=(1, 1.1, 0.9), pos=(0.0033, 0, -.38833), scale=(1.2, 1, 1.2), text=TTLocalizer.TypeANameButton, text_scale=TTLocalizer.NStypeANameButton, text_pos=TTLocalizer.NStypeANameButtonPos, command=self.__typeAName) + if base.cr.productName in ['DE', 'BR']: + self.typeANameButton.hide() + self.pickANameGUIElements.append(self.typeANameButton) + self.nameResult = DirectLabel(parent=aspect2d, relief=None, scale=TTLocalizer.NSnameResult, pos=(0.005, 0, 0.585), text=' \n ', text_scale=0.8, text_align=TextNode.ACenter, text_wordwrap=MAX_NAME_WIDTH) + self.pickANameGUIElements.append(self.nameResult) + self.allPrefixes = self.nameGen.lastPrefixes[:] + self.allSuffixes = self.nameGen.lastSuffixes[:] + self.allPrefixes.sort() + self.allSuffixes.sort() + self.allPrefixes = [' '] + [' '] + self.allPrefixes + [' '] + [' '] + self.allSuffixes = [' '] + [' '] + self.allSuffixes + [' '] + [' '] + self.titleScrollList = self.makeScrollList(gui, (-0.6, 0, 0.202), (1, 0.8, 0.8, 1), self.allTitles, self.makeLabel, [TextNode.ACenter, 'title']) + self.firstnameScrollList = self.makeScrollList(gui, (-0.2, 0, 0.202), (0.8, 1, 0.8, 1), self.allFirsts, self.makeLabel, [TextNode.ACenter, 'first']) + self.lastprefixScrollList = self.makeScrollList(gui, (0.2, 0, 0.202), (0.8, 0.8, 1, 1), self.allPrefixes, self.makeLabel, [TextNode.ARight, 'prefix']) + self.lastsuffixScrollList = self.makeScrollList(gui, (0.55, 0, 0.202), (0.8, 0.8, 1, 1), self.allSuffixes, self.makeLabel, [TextNode.ALeft, 'suffix']) + gui.removeNode() + self.pickANameGUIElements.append(self.lastprefixScrollList) + self.pickANameGUIElements.append(self.lastsuffixScrollList) + self.pickANameGUIElements.append(self.titleScrollList) + self.pickANameGUIElements.append(self.firstnameScrollList) + self.titleHigh = self.makeHighlight((-0.710367, 0.0, 0.122967)) + self.firstHigh = self.makeHighlight((-0.310367, 0.0, 0.122967)) + self.pickANameGUIElements.append(self.titleHigh) + self.pickANameGUIElements.append(self.firstHigh) + self.prefixHigh = self.makeHighlight((0.09, 0.0, 0.122967)) + self.suffixHigh = self.makeHighlight((0.44, 0.0, 0.122967)) + self.pickANameGUIElements.append(self.prefixHigh) + self.pickANameGUIElements.append(self.suffixHigh) + nameBalloon.removeNode() + imageList = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')) + buttonImage = [imageList, imageList] + buttonText = [TTLocalizer.NameShopPay, TTLocalizer.NameShopPlay] + self.payDialog = DirectDialog(dialogName='paystate', topPad=0, fadeScreen=0.2, pos=(0, 0.1, 0.1), button_relief=None, text_align=TextNode.ACenter, text=TTLocalizer.NameShopOnlyPaid, buttonTextList=buttonText, buttonImageList=buttonImage, image_color=GlobalDialogColor, buttonValueList=[1, 0], command=self.payAction) + self.payDialog.buttonList[0].setPos(0, 0, -.27) + self.payDialog.buttonList[1].setPos(0, 0, -.4) + self.payDialog.buttonList[0]['image_scale'] = (1.2, 1, 1.1) + self.payDialog.buttonList[1]['image_scale'] = (1.2, 1, 1.1) + self.payDialog['image_scale'] = (0.8, 1, 0.77) + self.payDialog.buttonList[0]['text_pos'] = (0, -.02) + self.payDialog.buttonList[1]['text_pos'] = (0, -.02) + self.payDialog.hide() + buttonText = [TTLocalizer.NameShopContinueSubmission, TTLocalizer.NameShopChooseAnother] + self.approvalDialog = DirectDialog(dialogName='approvalstate', topPad=0, fadeScreen=0.2, pos=(0, 0.1, 0.1), button_relief=None, image_color=GlobalDialogColor, text_align=TextNode.ACenter, text=TTLocalizer.NameShopToonCouncil, buttonTextList=buttonText, buttonImageList=buttonImage, buttonValueList=[1, 0], command=self.approvalAction) + self.approvalDialog.buttonList[0].setPos(0, 0, -.3) + self.approvalDialog.buttonList[1].setPos(0, 0, -.43) + self.approvalDialog['image_scale'] = (0.8, 1, 0.77) + for x in range(0, 2): + self.approvalDialog.buttonList[x]['text_pos'] = (0, -.01) + self.approvalDialog.buttonList[x]['text_scale'] = (0.04, 0.05999) + self.approvalDialog.buttonList[x].setScale(1.2, 1, 1) + + self.approvalDialog.hide() + guiButton.removeNode() + self.uberhide(self.typeANameGUIElements) + self.uberhide(self.pickANameGUIElements) + self.isLoaded = 1 + + def ubershow(self, guiObjectsToShow): + self.notify.debug('ubershow %s' % str(guiObjectsToShow)) + for x in guiObjectsToShow: + try: + x.show() + except: + print 'NameShop: Tried to show already removed object' + + if base.cr.productName in ['DE', 'BR']: + self.typeANameButton.hide() + + def hideAll(self): + self.uberhide(self.pickANameGUIElements) + self.uberhide(self.typeANameGUIElements) + + def uberhide(self, guiObjectsToHide): + self.notify.debug('uberhide %s' % str(guiObjectsToHide)) + for x in guiObjectsToHide: + try: + x.hide() + except: + print 'NameShop: Tried to hide already removed object' + + def uberdestroy(self, guiObjectsToDestroy): + self.notify.debug('uberdestroy %s' % str(guiObjectsToDestroy)) + for x in guiObjectsToDestroy: + try: + x.destroy() + del x + except: + print 'NameShop: Tried to destroy already removed object' + + def getNameIndices(self): + return self.nameIndices + + def getNameFlags(self): + return self.nameFlags + + def getNameAction(self): + return self.nameAction + + def unload(self): + self.notify.debug('unload') + if self.isLoaded == 0: + return None + self.exit() + cleanupDialog('globalDialog') + self.uberdestroy(self.pickANameGUIElements) + self.uberdestroy(self.typeANameGUIElements) + del self.toon + self.payDialog.cleanup() + self.approvalDialog.cleanup() + del self.payDialog + del self.approvalDialog + self.parentFSM.getStateNamed('NameShop').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.ignoreAll() + self.isLoaded = 0 + self.makeAToon = None + + def _checkNpcNames(self, name): + + def match(npcName, name = name): + name = TextEncoder().encodeWtext(name) + name = string.strip(name) + return TextEncoder.upper(npcName) == TextEncoder.upper(name) + + for npcId in NPCToons.NPCToonDict.keys(): + npcName = NPCToons.NPCToonDict[npcId][1] + if match(npcName): + self.notify.info('name matches NPC name "%s"' % npcName) + return TTLocalizer.NCGeneric + + def nameIsValid(self, name): + self.notify.debug('nameIsValid') + if name in self.usedNames: + return TTLocalizer.ToonAlreadyExists % name + problem = NameCheck.checkName(name, [self._checkNpcNames], font=self.nameEntry.getFont()) + if problem: + return problem + return None + + def setShopsVisited(self, list): + self.shopsVisited = list + + def __handleDone(self): + if self.fsm.getCurrentState().getName() == 'TypeAName': + self.__typedAName() + else: + self.__isFirstTime() + + def __handleSkipTutorial(self): + self.__createAvatar(skipTutorial=True) + + def __handleBackward(self): + self.doneStatus = 'last' + messenger.send(self.doneEvent) + + def __handleChastised(self): + self.chastiseDialog.cleanup() + + def __createAvatar(self, skipTutorial = False, *args): + self.notify.debug('__createAvatar') + if self.fsm.getCurrentState().getName() == 'TypeAName': + self.__typedAName() + return + if not self.avExists or self.avExists and self.avId == 'deleteMe': + self.serverCreateAvatar(skipTutorial) + elif self.names[0] == '': + self.rejectName(TTLocalizer.EmptyNameError) + else: + rejectReason = self.nameIsValid(self.names[0]) + if rejectReason != None: + self.rejectName(rejectReason) + else: + self.checkNamePattern() + return + + def acceptName(self): + self.notify.debug('acceptName') + self.toon.setName(self.names[0]) + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + + def rejectName(self, str): + self.notify.debug('rejectName') + self.names[0] = '' + self.rejectDialog = TTDialog.TTGlobalDialog(doneEvent='rejectDone', message=str, style=TTDialog.Acknowledge) + self.rejectDialog.show() + self.acceptOnce('rejectDone', self.__handleReject) + + def __handleReject(self): + self.rejectDialog.cleanup() + self.nameEntry['focus'] = 1 + self.typeANameButton.show() + self.acceptOnce('next', self.__handleDone) + + def restoreIndexes(self, oi): + self.titleIndex = oi[0] + self.firstIndex = oi[1] + self.prefixIndex = oi[2] + self.suffixIndex = oi[3] + + def stealth(self, listToDo): + listToDo.decButton['state'] = 'disabled' + listToDo.incButton['state'] = 'disabled' + for item in listToDo['items']: + if item.__class__.__name__ != 'str': + item.hide() + + def showList(self, listToDo): + listToDo.show() + listToDo.decButton['state'] = 'normal' + listToDo.incButton['state'] = 'normal' + + def updateLists(self): + oldindex = [self.titleIndex, + self.firstIndex, + self.prefixIndex, + self.suffixIndex] + self.titleScrollList.scrollTo(self.titleIndex - 2) + self.restoreIndexes(oldindex) + self.firstnameScrollList.scrollTo(self.firstIndex - 2) + self.restoreIndexes(oldindex) + self.lastprefixScrollList.scrollTo(self.prefixIndex - 2) + self.restoreIndexes(oldindex) + self.lastsuffixScrollList.scrollTo(self.suffixIndex - 2) + self.restoreIndexes(oldindex) + + def __randomName(self): + self.notify.debug('Finding random name') + uberReturn = self.nameGen.randomNameMoreinfo(self.boy, self.girl) + flags = uberReturn[:3] + names = uberReturn[3:7] + fullName = uberReturn[-1] + self._updateGuiWithPickAName(flags, names, fullName) + + def _updateGuiWithPickAName(self, flags, names, fullName): + uberReturn = flags + names + [fullName] + self.names[0] = uberReturn[len(uberReturn) - 1] + self.titleActive = 0 + self.firstActive = 0 + self.lastActive = 0 + if uberReturn[0]: + self.titleActive = 1 + if uberReturn[1]: + self.firstActive = 1 + if uberReturn[2]: + self.lastActive = 1 + try: + self.titleIndex = self.allTitles.index(uberReturn[3]) + self.nameIndices[0] = self.nameGen.returnUniqueID(uberReturn[3], 0) + self.nameFlags[0] = 1 + except: + print 'NameShop : Should have found title, uh oh!' + print uberReturn + + try: + self.firstIndex = self.allFirsts.index(uberReturn[4]) + self.nameIndices[1] = self.nameGen.returnUniqueID(uberReturn[4], 1) + self.nameFlags[1] = 1 + except: + print 'NameShop : Should have found first name, uh oh!' + print uberReturn + + try: + self.prefixIndex = self.allPrefixes.index(uberReturn[5]) + self.suffixIndex = self.allSuffixes.index(uberReturn[6].lower()) + self.nameIndices[2] = self.nameGen.returnUniqueID(uberReturn[5], 2) + self.nameIndices[3] = self.nameGen.returnUniqueID(uberReturn[6].lower(), 3) + if uberReturn[5] in self.nameGen.capPrefixes: + self.nameFlags[3] = 1 + else: + self.nameFlags[3] = 0 + except: + print 'NameShop : Some part of last name not found, uh oh!' + print uberReturn + + self.updateCheckBoxes() + self.updateLists() + self.nameResult['text'] = self.names[0] + + def findTempName(self): + colorstring = TTLocalizer.NumToColor[self.toon.style.headColor] + animaltype = TTLocalizer.AnimalToSpecies[self.toon.style.getAnimal()] + tempname = colorstring + ' ' + animaltype + if not TTLocalizer.NScolorPrecede: + tempname = animaltype + ' ' + colorstring + self.names[0] = tempname + tempname = '"' + tempname + '"' + return tempname + + def enterInit(self): + self.notify.debug('enterInit') + + def exitInit(self): + pass + + def enterPayState(self): + self.notify.debug('enterPayState') + if base.cr.allowFreeNames() or self.isPaid: + self.fsm.request('PickAName') + else: + tempname = self.findTempName() + self.payDialog['text'] = TTLocalizer.NameShopOnlyPaid + tempname + self.payDialog.show() + + def exitPayState(self): + pass + + def payAction(self, value): + self.notify.debug('payAction') + self.payDialog.hide() + if value: + self.doneStatus = 'paynow' + messenger.send(self.doneEvent) + else: + self.nameAction = 0 + self.__createAvatar() + + def enterPickANameState(self): + self.notify.debug('enterPickANameState') + self.ubershow(self.pickANameGUIElements) + self.nameAction = 1 + self.__listsChanged() + + def exitPickANameState(self): + self.uberhide(self.pickANameGUIElements) + + def enterTypeANameState(self): + self.notify.debug('enterTypeANameState') + self.ubershow(self.typeANameGUIElements) + self.typeANameButton.show() + self.typeANameButton.wrtReparentTo(aspect2d, sort=2) + self.nameEntry.set('') + self.nameEntry['focus'] = 1 + + def __typeAName(self): + if base.cr.productName in ['JP', + 'DE', + 'BR', + 'FR']: + if base.restrictTrialers: + if not base.cr.isPaid(): + dialog = TeaserPanel.TeaserPanel(pageName='typeAName') + return + if self.fsm.getCurrentState().getName() == 'TypeAName': + self.typeANameButton['text'] = TTLocalizer.TypeANameButton + self.typeANameButton.wrtReparentTo(self.namePanel, sort=2) + self.fsm.request('PickAName') + else: + self.typeANameButton['text'] = TTLocalizer.PickANameButton + self.typeANameButton.wrtReparentTo(aspect2d, sort=2) + self.typeANameButton.show() + self.fsm.request('TypeAName') + + def __typedAName(self, *args): + self.notify.debug('__typedAName') + self.nameEntry['focus'] = 0 + name = self.nameEntry.get() + name = TextEncoder().decodeText(name) + name = name.strip() + name = TextEncoder().encodeWtext(name) + self.nameEntry.enterText(name) + problem = self.nameIsValid(self.nameEntry.get()) + if problem: + self.rejectName(problem) + return + self.checkNameTyped(justCheck=True) + + def exitTypeANameState(self): + self.typeANameButton.wrtReparentTo(self.namePanel, sort=2) + self.uberhide(self.typeANameGUIElements) + + def enterApprovalState(self): + self.notify.debug('enterApprovalState') + tempname = self.findTempName() + self.approvalDialog['text'] = TTLocalizer.NameShopToonCouncil + tempname + self.approvalDialog.show() + + def approvalAction(self, value): + self.notify.debug('approvalAction') + self.approvalDialog.hide() + if value: + self.nameAction = 2 + if not self.makeAToon.warp: + self.__isFirstTime() + else: + self.serverCreateAvatar() + else: + self.typeANameButton['text'] = TTLocalizer.TypeANameButton + self.fsm.request('PickAName') + + def exitApprovalState(self): + pass + + def enterApprovalAcceptedState(self): + self.notify.debug('enterApprovalAcceptedState') + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + + def exitApprovalAcceptedState(self): + pass + + def enterAcceptedState(self): + self.notify.debug('enterAcceptedState') + self.acceptedDialog = TTDialog.TTGlobalDialog(doneEvent='acceptedDone', message=TTLocalizer.NameShopNameAccepted, style=TTDialog.Acknowledge) + self.acceptedDialog.show() + self.acceptOnce('acceptedDone', self.__handleAccepted) + + def __handleAccepted(self): + self.acceptedDialog.cleanup() + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + + def exitAcceptedState(self): + pass + + def enterRejectedState(self): + self.notify.debug('enterRejectedState') + self.rejectedDialog = TTDialog.TTGlobalDialog(doneEvent='rejectedDone', message=TTLocalizer.NameShopNameRejected, style=TTDialog.Acknowledge) + self.rejectedDialog.show() + self.acceptOnce('rejectedDone', self.__handleRejected) + + def __handleRejected(self): + self.rejectedDialog.cleanup() + self.fsm.request('TypeAName') + + def exitRejectedState(self): + pass + + def enterDone(self): + self.notify.debug('enterDone') + return None + + def exitDone(self): + return None + + def nameShopHandler(self, msgType, di): + self.notify.debug('nameShopHandler') + if msgType == CLIENT_CREATE_AVATAR_RESP: + self.handleCreateAvatarResponseMsg(di) + elif msgType == CLIENT_SET_NAME_PATTERN_ANSWER: + self.handleSetNamePatternAnswerMsg(di) + elif msgType == CLIENT_SET_WISHNAME_RESP: + self.handleSetNameTypedAnswerMsg(di) + return None + + def checkNamePattern(self): + self.notify.debug('checkNamePattern') + datagram = PyDatagram() + datagram.addUint16(CLIENT_SET_NAME_PATTERN) + datagram.addUint32(self.avId) + datagram.addInt16(self.nameIndices[0]) + datagram.addInt16(self.nameFlags[0]) + datagram.addInt16(self.nameIndices[1]) + datagram.addInt16(self.nameFlags[1]) + datagram.addInt16(self.nameIndices[2]) + datagram.addInt16(self.nameFlags[2]) + datagram.addInt16(self.nameIndices[3]) + datagram.addInt16(self.nameFlags[3]) + messenger.send('nameShopPost', [datagram]) + self.waitForServer() + + def handleSetNamePatternAnswerMsg(self, di): + self.notify.debug('handleSetNamePatternAnswerMsg') + self.cleanupWaitForServer() + newavId = di.getUint32() + if newavId != self.avId: + self.notify.debug("doid's don't match up!") + self.rejectName(TTLocalizer.NameError) + returnCode = di.getUint8() + if returnCode == 0: + style = self.toon.getStyle() + avDNA = style.makeNetString() + self.notify.debug('pattern name accepted') + newPotAv = PotentialAvatar.PotentialAvatar(newavId, self.names, avDNA, self.index, 0) + self.avList.append(newPotAv) + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + else: + self.notify.debug('name pattern rejected') + self.rejectName(TTLocalizer.NameError) + return None + + def _submitTypeANameAsPickAName(self): + pnp = TTPickANamePattern(self.nameEntry.get(), self.toon.style.gender) + if pnp.hasNamePattern(): + pattern = pnp.getNamePattern() + self.fsm.request('PickAName') + flags = [pattern[0] != -1, pattern[1] != -1, pattern[2] != -1] + names = [] + for i in xrange(len(pattern)): + if pattern[i] != -1: + names.append(pnp.getNamePartString(self.toon.style.gender, i, pattern[i])) + else: + names.append('') + + fullName = pnp.getNameString(pattern, self.toon.style.gender) + self._updateGuiWithPickAName(flags, names, fullName) + self.__handleDone() + return True + return False + + def checkNameTyped(self, justCheck = False): + self.notify.debug('checkNameTyped') + if self._submitTypeANameAsPickAName(): + return + datagram = PyDatagram() + datagram.addUint16(CLIENT_SET_WISHNAME) + if justCheck: + avId = 0 + else: + avId = self.avId + datagram.addUint32(avId) + datagram.addString(self.nameEntry.get()) + messenger.send('nameShopPost', [datagram]) + self.waitForServer() + + def handleSetNameTypedAnswerMsg(self, di): + self.notify.debug('handleSetNameTypedAnswerMsg') + self.cleanupWaitForServer() + newavId = di.getUint32() + if newavId and newavId != self.avId: + self.notify.debug("doid's don't match up!") + self.rejectName(TTLocalizer.NameError) + returnCode = di.getUint16() + if newavId == 0: + if returnCode == 0: + pendingname = di.getString() + approvedname = di.getString() + rejectedname = di.getString() + if pendingname != '': + self.notify.debug('name check pending') + self.fsm.request('Approval') + elif approvedname != '': + self.notify.debug('name check accepted') + self.nameAction = 2 + self.serverCreateAvatar() + elif rejectedname != '': + self.notify.debug('name check rejected') + self.fsm.request('TypeAName') + self.rejectName(TTLocalizer.NameError) + else: + self.notify.debug('typed name response did not contain any return fields') + self.rejectName(TTLocalizer.NameError) + elif returnCode == 0: + wishname = di.getString() + approvedname = di.getString() + rejectedname = di.getString() + if approvedname != '': + style = self.toon.getStyle() + avDNA = style.makeNetString() + self.names[0] = self.nameEntry.get() + self.notify.debug('typed name accepted') + newPotAv = PotentialAvatar.PotentialAvatar(newavId, self.names, avDNA, self.index, 0) + self.avList.append(newPotAv) + self.fsm.request('Accepted') + elif wishname != '': + style = self.toon.getStyle() + avDNA = style.makeNetString() + self.names[1] = self.nameEntry.get() + self.notify.debug('typed name needs approval') + newPotAv = PotentialAvatar.PotentialAvatar(newavId, self.names, avDNA, self.index, 1) + if not self.newwarp: + self.avList.append(newPotAv) + self.fsm.request('ApprovalAccepted') + elif rejectedname != '': + self.fsm.request('Rejected') + else: + self.notify.debug("name typed accepted but didn't fill any return fields") + self.rejectName(TTLocalizer.NameError) + else: + self.notify.debug('name typed rejected') + self.rejectName(TTLocalizer.NameError) + return None + + def serverCreateAvatar(self, skipTutorial = False): + self.notify.debug('serverCreateAvatar') + style = self.toon.getStyle() + self.newDNA = style.makeNetString() + if skipTutorial: + self.requestingSkipTutorial = True + else: + self.requestingSkipTutorial = False + if not self.avExists or self.avExists and self.avId == 'deleteMe': + messenger.send('nameShopCreateAvatar', [style, '', self.index]) + else: + self.checkNameTyped() + self.notify.debug('Ending Make A Toon: %s' % self.toon.style) + base.cr.centralLogger.writeClientEvent('MAT - endingMakeAToon: %s' % self.toon.style) + + def handleCreateAvatarResponseMsg(self, di): + self.notify.debug('handleCreateAvatarResponseMsg') + echoContext = di.getUint16() + returnCode = di.getUint8() + if returnCode == 0: + self.notify.debug('avatar with default name accepted') + self.avId = di.getUint32() + self.avExists = 1 + self.logAvatarCreation() + if self.nameAction == 0: + self.toon.setName(self.names[0]) + newPotAv = PotentialAvatar.PotentialAvatar(self.avId, self.names, self.newDNA, self.index, 1) + self.avList.append(newPotAv) + self.doneStatus = 'done' + self.storeSkipTutorialRequest() + messenger.send(self.doneEvent) + elif self.nameAction == 1: + self.checkNamePattern() + elif self.nameAction == 2: + self.checkNameTyped() + else: + self.notify.debug('avatar invalid nameAction') + self.rejectName(TTLocalizer.NameError) + else: + self.notify.debug('avatar rejected') + self.rejectName(TTLocalizer.NameError) + return None + + def waitForServer(self): + self.waitForServerDialog = TTDialog.TTDialog(text=TTLocalizer.WaitingForNameSubmission, style=TTDialog.NoButtons) + self.waitForServerDialog.show() + + def cleanupWaitForServer(self): + if self.waitForServerDialog != None: + self.waitForServerDialog.cleanup() + self.waitForServerDialog = None + return + + def printTypeANameInfo(self, str): + sourceFilename, lineNumber, functionName = PythonUtil.stackEntryInfo(1) + self.notify.debug('========================================\n%s : %s : %s' % (sourceFilename, lineNumber, functionName)) + self.notify.debug(str) + curPos = self.typeANameButton.getPos() + self.notify.debug('Pos = %.2f %.2f %.2f' % (curPos[0], curPos[1], curPos[2])) + parent = self.typeANameButton.getParent() + parentPos = parent.getPos() + self.notify.debug('Parent = %s' % parent) + self.notify.debug('ParentPos = %.2f %.2f %.2f' % (parentPos[0], parentPos[1], parentPos[2])) + + def storeSkipTutorialRequest(self): + base.cr.skipTutorialRequest = self.requestingSkipTutorial + + def __isFirstTime(self): + if not self.makeAToon.nameList or self.makeAToon.warp: + self.__createAvatar() + else: + self.promptTutorial() + + def promptTutorial(self): + self.promptTutorialDialog = TTDialog.TTDialog(parent=aspect2dp, text=TTLocalizer.PromptTutorial, text_scale=0.06, text_align=TextNode.ACenter, text_wordwrap=22, command=self.__openTutorialDialog, fadeScreen=0.5, style=TTDialog.TwoChoice, buttonTextList=[TTLocalizer.MakeAToonEnterTutorial, TTLocalizer.MakeAToonSkipTutorial], button_text_scale=0.06, buttonPadSF=5.5, sortOrder=NO_FADE_SORT_INDEX) + self.promptTutorialDialog.show() + + def __openTutorialDialog(self, choice = 0): + if choice == 1: + self.notify.debug('enterTutorial') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ENTERTUTORIAL: Enter Tutorial') + self.__createAvatar() + else: + self.notify.debug('skipTutorial') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SKIPTUTORIAL: Skip Tutorial') + self.__handleSkipTutorial() + self.promptTutorialDialog.destroy() + + def logAvatarCreation(self): + dislId = 0 + try: + dislId = launcher.getValue('GAME_DISL_ID') + except: + pass + + if not dislId: + self.notify.warning('No dislId, using 0') + dislId = 0 + gameSource = '0' + try: + gameSource = launcher.getValue('GAME_SOURCE') + except: + pass + + if not gameSource: + gameSource = '0' + else: + self.notify.info('got GAME_SOURCE=%s' % gameSource) + if self.avId > 0: + base.cr.centralLogger.writeClientEvent('createAvatar %s-%s-%s' % (self.avId, dislId, gameSource)) + self.notify.debug('createAvatar %s-%s-%s' % (self.avId, dislId, gameSource)) + else: + self.notify.warning('logAvatarCreation got self.avId =%s' % self.avId) diff --git a/toontown/makeatoon/ShuffleButton.py b/toontown/makeatoon/ShuffleButton.py new file mode 100644 index 0000000..4f0cf66 --- /dev/null +++ b/toontown/makeatoon/ShuffleButton.py @@ -0,0 +1,144 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from MakeAToonGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import random + +class ShuffleButton: + notify = DirectNotifyGlobal.directNotify.newCategory('ShuffleButton') + + def __init__(self, parent, fetchEvent): + self.parent = parent + self.fetchEvent = fetchEvent + self.history = [0] + self.historyPtr = 0 + self.maxHistory = 10 + self.load() + + def load(self): + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_mat_mainGui') + shuffleFrame = gui.find('**/tt_t_gui_mat_shuffleFrame') + shuffleUp = gui.find('**/tt_t_gui_mat_shuffleUp') + shuffleDown = gui.find('**/tt_t_gui_mat_shuffleDown') + shuffleArrowUp = gui.find('**/tt_t_gui_mat_shuffleArrowUp') + shuffleArrowDown = gui.find('**/tt_t_gui_mat_shuffleArrowDown') + shuffleArrowDisabled = gui.find('**/tt_t_gui_mat_shuffleArrowDisabled') + gui.removeNode() + del gui + self.parentFrame = DirectFrame(parent=self.parent.parentFrame, relief=DGG.RAISED, pos=(0, 0, -1), frameColor=(1, 0, 0, 0)) + self.shuffleFrame = DirectFrame(parent=self.parentFrame, image=shuffleFrame, image_scale=halfButtonInvertScale, relief=None, frameColor=(1, 1, 1, 1)) + self.shuffleFrame.hide() + self.shuffleBtn = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleUp, shuffleDown, shuffleUp), image_scale=halfButtonInvertScale, image1_scale=(-0.63, 0.6, 0.6), image2_scale=(-0.63, 0.6, 0.6), text=(TTLocalizer.ShuffleButton, + TTLocalizer.ShuffleButton, + TTLocalizer.ShuffleButton, + ''), text_font=ToontownGlobals.getInterfaceFont(), text_scale=TTLocalizer.SBshuffleBtn, text_pos=(0, -0.02), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), command=self.chooseRandom) + self.incBtn = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowUp, + shuffleArrowDisabled), image_scale=halfButtonInvertScale, image1_scale=halfButtonInvertHoverScale, image2_scale=halfButtonInvertHoverScale, pos=(0.202, 0, 0), command=self.__goFrontHistory) + self.incBtn.hide() + self.decBtn = DirectButton(parent=self.parentFrame, relief=None, image=(shuffleArrowUp, + shuffleArrowDown, + shuffleArrowUp, + shuffleArrowDisabled), image_scale=halfButtonScale, image1_scale=halfButtonHoverScale, image2_scale=halfButtonHoverScale, pos=(-0.202, 0, 0), command=self.__goBackHistory) + self.decBtn.hide() + self.lerpDuration = 0.5 + self.showLerp = None + self.frameShowLerp = LerpColorInterval(self.shuffleFrame, self.lerpDuration, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 0)) + self.incBtnShowLerp = LerpColorInterval(self.incBtn, self.lerpDuration, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 0)) + self.decBtnShowLerp = LerpColorInterval(self.decBtn, self.lerpDuration, Vec4(1, 1, 1, 1), Vec4(1, 1, 1, 0)) + self.__updateArrows() + return + + def unload(self): + if self.showLerp: + self.showLerp.finish() + del self.showLerp + self.parent = None + self.parentFrame.destroy() + self.shuffleFrame.destroy() + self.shuffleBtn.destroy() + self.incBtn.destroy() + self.decBtn.destroy() + del self.parentFrame + del self.shuffleFrame + del self.shuffleBtn + del self.incBtn + del self.decBtn + return + + def showButtons(self): + self.shuffleFrame.show() + self.shuffleBtn.show() + self.incBtn.show() + self.decBtn.show() + + def hideButtons(self): + self.shuffleFrame.hide() + self.shuffleBtn.hide() + self.incBtn.hide() + self.decBtn.hide() + + def setChoicePool(self, pool): + self.pool = pool + + def chooseRandom(self): + self.saveCurrChoice() + self.currChoice = [] + for prop in self.pool: + self.currChoice.append(random.choice(prop)) + + self.notify.debug('current choice : %s' % self.currChoice) + if len(self.history) == self.maxHistory: + self.history.remove(self.history[0]) + self.history.append(0) + self.historyPtr = len(self.history) - 1 + if len(self.history) == 2: + self.startShowLerp() + self.__updateArrows() + messenger.send(self.fetchEvent) + + def getCurrChoice(self): + return self.currChoice + + def saveCurrChoice(self): + self.currChoice = self.parent.getCurrToonSetting() + self.history[self.historyPtr] = self.currChoice + + def __goBackHistory(self): + self.saveCurrChoice() + self.historyPtr -= 1 + self.currChoice = self.history[self.historyPtr] + self.__updateArrows() + messenger.send(self.fetchEvent) + + def __goFrontHistory(self): + self.saveCurrChoice() + self.historyPtr += 1 + self.currChoice = self.history[self.historyPtr] + self.__updateArrows() + messenger.send(self.fetchEvent) + + def __updateArrows(self): + if self.historyPtr == 0: + self.decBtn['state'] = DGG.DISABLED + else: + self.decBtn['state'] = DGG.NORMAL + if self.historyPtr >= len(self.history) - 1: + self.incBtn['state'] = DGG.DISABLED + else: + self.incBtn['state'] = DGG.NORMAL + + def startShowLerp(self): + self.showLerp = Sequence(Parallel(Func(self.shuffleFrame.show), Func(self.incBtn.show), Func(self.decBtn.show)), Parallel(self.frameShowLerp, self.incBtnShowLerp, self.decBtnShowLerp)) + self.showLerp.start() + + def cleanHistory(self): + self.history = [0] + self.historyPtr = 0 + self.shuffleFrame.hide() + self.incBtn.hide() + self.decBtn.hide() diff --git a/toontown/makeatoon/TTPickANamePattern.py b/toontown/makeatoon/TTPickANamePattern.py new file mode 100644 index 0000000..c835f8f --- /dev/null +++ b/toontown/makeatoon/TTPickANamePattern.py @@ -0,0 +1,22 @@ +from direct.showbase.PythonUtil import listToItem2index +from otp.namepanel.PickANamePattern import PickANamePatternTwoPartLastName +from toontown.makeatoon.NameGenerator import NameGenerator +import types + +class TTPickANamePattern(PickANamePatternTwoPartLastName): + NameParts = None + LastNamePrefixesCapped = None + + def _getNameParts(self, gender): + if TTPickANamePattern.NameParts is None: + TTPickANamePattern.NameParts = {} + ng = NameGenerator() + TTPickANamePattern.NameParts['m'] = ng.getMaleNameParts() + TTPickANamePattern.NameParts['f'] = ng.getFemaleNameParts() + return TTPickANamePattern.NameParts[gender] + + def _getLastNameCapPrefixes(self): + if TTPickANamePattern.LastNamePrefixesCapped is None: + ng = NameGenerator() + TTPickANamePattern.LastNamePrefixesCapped = ng.getLastNamePrefixesCapped()[:] + return TTPickANamePattern.LastNamePrefixesCapped diff --git a/toontown/makeatoon/__init__.py b/toontown/makeatoon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/minigame/ArrowKeys.py b/toontown/minigame/ArrowKeys.py new file mode 100644 index 0000000..4874301 --- /dev/null +++ b/toontown/minigame/ArrowKeys.py @@ -0,0 +1,169 @@ +from pandac.PandaModules import ModifierButtons +from direct.showbase.DirectObject import DirectObject + +class ArrowKeys(DirectObject): + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + JUMP_KEY = 'control' + UP_INDEX = 0 + DOWN_INDEX = 1 + LEFT_INDEX = 2 + RIGHT_INDEX = 3 + JUMP_INDEX = 4 + NULL_HANDLERS = (None, None, None, None, None) + + def __init__(self): + self.__jumpPost = 0 + self.setPressHandlers(self.NULL_HANDLERS) + self.setReleaseHandlers(self.NULL_HANDLERS) + self.origMb = base.buttonThrowers[0].node().getModifierButtons() + base.buttonThrowers[0].node().setModifierButtons(ModifierButtons()) + self.enable() + + def enable(self): + self.disable() + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.accept(self.JUMP_KEY, self.__jumpKeyPressed) + + def disable(self): + self.__upPressed = 0 + self.__downPressed = 0 + self.__leftPressed = 0 + self.__rightPressed = 0 + self.__jumpPressed = 0 + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.JUMP_KEY) + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.ignore(self.JUMP_KEY + '-up') + + def destroy(self): + base.buttonThrowers[0].node().setModifierButtons(self.origMb) + events = [self.UP_KEY, + self.DOWN_KEY, + self.LEFT_KEY, + self.RIGHT_KEY, + self.JUMP_KEY] + for event in events: + self.ignore(event) + self.ignore(event + '-up') + + def upPressed(self): + return self.__upPressed + + def downPressed(self): + return self.__downPressed + + def leftPressed(self): + return self.__leftPressed + + def rightPressed(self): + return self.__rightPressed + + def jumpPressed(self): + return self.__jumpPressed + + def jumpPost(self): + jumpCache = self.__jumpPost + self.__jumpPost = 0 + return jumpCache + + def setPressHandlers(self, handlers): + if len(handlers) == 4: + handlers.append(None) + self.__checkCallbacks(handlers) + self.__pressHandlers = handlers + return + + def setReleaseHandlers(self, handlers): + if len(handlers) == 4: + handlers.append(None) + self.__checkCallbacks(handlers) + self.__releaseHandlers = handlers + return + + def clearPressHandlers(self): + self.setPressHandlers(self.NULL_HANDLERS) + + def clearReleaseHandlers(self): + self.setReleaseHandlers(self.NULL_HANDLERS) + + def __checkCallbacks(self, callbacks): + for callback in callbacks: + pass + + def __doCallback(self, callback): + if callback: + callback() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed = 1 + self.__doCallback(self.__pressHandlers[self.UP_INDEX]) + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed = 1 + self.__doCallback(self.__pressHandlers[self.DOWN_INDEX]) + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed = 1 + self.__doCallback(self.__pressHandlers[self.LEFT_INDEX]) + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed = 1 + self.__doCallback(self.__pressHandlers[self.RIGHT_INDEX]) + + def __jumpKeyPressed(self): + self.ignore(self.JUMP_KEY) + self.accept(self.JUMP_KEY + '-up', self.__jumpKeyReleased) + self.__jumpPressed = 1 + self.__jumpPost = 1 + self.__doCallback(self.__pressHandlers[self.JUMP_INDEX]) + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upPressed = 0 + self.__doCallback(self.__releaseHandlers[self.UP_INDEX]) + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downPressed = 0 + self.__doCallback(self.__releaseHandlers[self.DOWN_INDEX]) + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftPressed = 0 + self.__doCallback(self.__releaseHandlers[self.LEFT_INDEX]) + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightPressed = 0 + self.__doCallback(self.__releaseHandlers[self.RIGHT_INDEX]) + + def __jumpKeyReleased(self): + self.ignore(self.JUMP_KEY + '-up') + self.accept(self.JUMP_KEY, self.__jumpKeyPressed) + self.__jumpPressed = 0 + self.__jumpPost = 0 + self.__doCallback(self.__releaseHandlers[self.JUMP_INDEX]) diff --git a/toontown/minigame/CannonGameGlobals.py b/toontown/minigame/CannonGameGlobals.py new file mode 100644 index 0000000..84c0742 --- /dev/null +++ b/toontown/minigame/CannonGameGlobals.py @@ -0,0 +1,16 @@ +TowerYRange = 200 +GameTime = 90 +MAX_SCORE = 23 +MIN_SCORE = 5 +FUSE_TIME = 0.0 +CANNON_ROTATION_MIN = -20 +CANNON_ROTATION_MAX = 20 +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_MIN = 10 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 + +def calcScore(t): + range = MAX_SCORE - MIN_SCORE + score = MAX_SCORE - range * (float(t) / GameTime) + return int(score + 0.5) diff --git a/toontown/minigame/CatchGameGlobals.py b/toontown/minigame/CatchGameGlobals.py new file mode 100644 index 0000000..f326074 --- /dev/null +++ b/toontown/minigame/CatchGameGlobals.py @@ -0,0 +1,57 @@ +EndlessGame = config.GetBool('endless-catch-game', 0) +GameDuration = 55.0 + +class DropObject: + + def __init__(self, name, good, onscreenDurMult, modelPath): + self.name = name + self.good = good + self.onscreenDurMult = onscreenDurMult + self.modelPath = modelPath + + def isBaseline(self): + return self.onscreenDurMult == 1.0 + + +DropObjectTypes = [DropObject('apple', 1, 1.0, 'phase_4/models/minigames/apple'), + DropObject('orange', 1, 1.0, 'phase_4/models/minigames/orange'), + DropObject('pear', 1, 1.0, 'phase_4/models/minigames/pear'), + DropObject('coconut', 1, 1.0, 'phase_4/models/minigames/coconut'), + DropObject('watermelon', 1, 1.0, 'phase_4/models/minigames/watermelon'), + DropObject('pineapple', 1, 1.0, 'phase_4/models/minigames/pineapple'), + DropObject('anvil', 0, 0.4, 'phase_4/models/props/anvil-mod')] +Name2DropObjectType = {} +for type in DropObjectTypes: + Name2DropObjectType[type.name] = type + +Name2DOTypeId = {} +names = Name2DropObjectType.keys() +names.sort() +for i in range(len(names)): + Name2DOTypeId[names[i]] = i + +DOTypeId2Name = names +NumFruits = [{2000: 18, + 1000: 19, + 5000: 22, + 4000: 24, + 3000: 27, + 9000: 28}, + {2000: 30, + 1000: 33, + 5000: 38, + 4000: 42, + 3000: 46, + 9000: 50}, + {2000: 42, + 1000: 48, + 5000: 54, + 4000: 60, + 3000: 66, + 9000: 71}, + {2000: 56, + 1000: 63, + 5000: 70, + 4000: 78, + 3000: 85, + 9000: 92}] diff --git a/toontown/minigame/CatchGameToonSD.py b/toontown/minigame/CatchGameToonSD.py new file mode 100644 index 0000000..300f10f --- /dev/null +++ b/toontown/minigame/CatchGameToonSD.py @@ -0,0 +1,189 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from toontown.distributed.DelayDelete import DelayDelete +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import CatchGameGlobals +from direct.task.Task import Task + +class CatchGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('CatchGameToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + CatchNeutralAnim = 'catch-neutral' + CatchRunAnim = 'catch-run' + EatNeutralAnim = 'catch-eatneutral' + EatNRunAnim = 'catch-eatnrun' + animList = [FallBackAnim, + FallFwdAnim, + CatchNeutralAnim, + CatchRunAnim, + EatNeutralAnim, + EatNRunAnim] + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self._delayDelete = DelayDelete(self.toon, 'CatchGameToonSD') + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CatchGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['eatFruit', 'fallBack', 'fallForward']), + State.State('eatFruit', self.enterEatFruit, self.exitEatFruit, ['normal', + 'fallBack', + 'fallForward', + 'eatFruit']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal']), + State.State('fallForward', self.enterFallForward, self.exitFallForward, ['normal']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + self._delayDelete.destroy() + del self.fsm + + def enter(self): + self.fsm.enterInitialState() + self._exiting = False + + def exit(self, unexpectedExit = False): + if self._exiting: + return + self._exiting = True + self.unexpectedExit = unexpectedExit + self.fsm.requestFinalState() + del self._exiting + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + if self.isLocal: + self.game.initOrthoWalk() + self.toon.useLOD(1000) + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def exitInit(self): + pass + + def enterNormal(self): + self.notify.debug('enterNormal') + self.setAnimState('Catching', 1.0) + if self.isLocal: + self.game.orthoWalk.start() + self.toon.lerpLookAt(Vec3.forward() + Vec3.up(), time=0.2, blink=0) + + def exitNormal(self): + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.orthoWalk.stop() + self.toon.lerpLookAt(Vec3.forward(), time=0.2, blink=0) + + def eatFruit(self, fruitModel, handNode): + if self.fsm.getCurrentState().getName() == 'eatFruit': + self.fsm.request('normal') + self.fsm.request('eatFruit', [fruitModel, handNode]) + + def enterEatFruit(self, fruitModel, handNode): + self.notify.debug('enterEatFruit') + self.setAnimState('CatchEating', 1.0) + if self.isLocal: + self.game.orthoWalk.start() + self.fruitModel = fruitModel + renderScale = fruitModel.getScale(render) + fruitModel.reparentTo(handNode) + fruitModel.setScale(render, renderScale) + + def finishedEating(self = self, fruitModel = fruitModel): + self.fsm.request('normal') + return Task.done + + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpScaleInterval(fruitModel, duration / 2.0, fruitModel.getScale() * 0.5, blendType='easeInOut'), Func(fruitModel.hide))), Func(finishedEating), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitEatFruit(self): + self.eatIval.pause() + del self.eatIval + self.fruitModel.reparentTo(hidden) + self.fruitModel.removeNode() + del self.fruitModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.orthoWalk.stop() + + def enterFallBack(self): + self.notify.debug('enterFallBack') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallBackAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallBackIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + del self.fallBackIval + + def enterFallForward(self): + self.notify.debug('enterFallForward') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallFwdAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallFwdIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallFwdIval.start() + + def exitFallForward(self): + self.fallFwdIval.pause() + del self.fallFwdIval + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.toon.stopBlink() + self.toon.startLookAround() + if self.isLocal: + self.game.orthoWalk.stop() + self.game.destroyOrthoWalk() + self.toon.resetLOD() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitCleanup(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) diff --git a/toontown/minigame/ClerkPurchase.py b/toontown/minigame/ClerkPurchase.py new file mode 100644 index 0000000..5e4a901 --- /dev/null +++ b/toontown/minigame/ClerkPurchase.py @@ -0,0 +1,52 @@ +from PurchaseBase import * +from toontown.toonbase import ToontownTimer +COUNT_UP_RATE = 0.15 +DELAY_BEFORE_COUNT_UP = 1.25 +DELAY_AFTER_COUNT_UP = 1.75 +COUNT_DOWN_RATE = 0.075 +DELAY_AFTER_COUNT_DOWN = 0.0 +DELAY_AFTER_CELEBRATE = 3.0 + +class ClerkPurchase(PurchaseBase): + activateMode = 'storePurchase' + + def __init__(self, toon, remain, doneEvent): + PurchaseBase.__init__(self, toon, doneEvent) + self.remain = remain + + def load(self): + purchaseModels = loader.loadModel('phase_4/models/gui/gag_shop_purchase_gui') + PurchaseBase.load(self, purchaseModels) + self.backToPlayground = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.71, 0, -0.045), image=(purchaseModels.find('**/PurchScrn_BTN_UP'), purchaseModels.find('**/PurchScrn_BTN_DN'), purchaseModels.find('**/PurchScrn_BTN_RLVR')), text=TTLocalizer.GagShopDoneShopping, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), command=self.__handleBackToPlayground) + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self.frame) + self.timer.posInTopRightCorner() + purchaseModels.removeNode() + return + + def unload(self): + PurchaseBase.unload(self) + del self.backToPlayground + del self.timer + + def __handleBackToPlayground(self): + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + self.handleDone(0) + + def __timerExpired(self): + self.handleDone(0) + + def enterPurchase(self): + PurchaseBase.enterPurchase(self) + self.backToPlayground.reparentTo(self.toon.inventory.storePurchaseFrame) + self.pointDisplay.reparentTo(self.toon.inventory.storePurchaseFrame) + self.statusLabel.reparentTo(self.toon.inventory.storePurchaseFrame) + self.timer.countdown(self.remain, self.__timerExpired) + + def exitPurchase(self): + PurchaseBase.exitPurchase(self) + self.backToPlayground.reparentTo(self.frame) + self.pointDisplay.reparentTo(self.frame) + self.statusLabel.reparentTo(self.frame) + self.ignore('purchaseStateChange') diff --git a/toontown/minigame/CogThief.py b/toontown/minigame/CogThief.py new file mode 100644 index 0000000..79758db --- /dev/null +++ b/toontown/minigame/CogThief.py @@ -0,0 +1,474 @@ +import math +from pandac.PandaModules import CollisionSphere, CollisionNode, Point3, CollisionTube, Vec3, rad2Deg +from direct.showbase.DirectObject import DirectObject +from direct.distributed.ClockDelta import globalClockDelta +from direct.interval.IntervalGlobal import Parallel, SoundInterval, Sequence, Func, LerpScaleInterval +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownGlobals +from toontown.minigame import CogThiefGameGlobals +from toontown.battle.BattleProps import globalPropPool +from toontown.battle.BattleSounds import globalBattleSoundCache +CTGG = CogThiefGameGlobals + +class CogThief(DirectObject): + notify = directNotify.newCategory('CogThief') + DefaultSpeedWalkAnim = 4.0 + CollisionRadius = 1.25 + MaxFriendsVisible = 4 + Infinity = 100000.0 + SeparationDistance = 6.0 + MinUrgency = 0.5 + MaxUrgency = 0.75 + + def __init__(self, cogIndex, suitType, game, cogSpeed): + self.cogIndex = cogIndex + self.suitType = suitType + self.game = game + self.cogSpeed = cogSpeed + suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(suitType) + suit.setDNA(d) + suit.pose('walk', 0) + self.suit = suit + self.goal = CTGG.NoGoal + self.goalId = CTGG.InvalidGoalId + self.lastLocalTimeStampFromAI = 0 + self.lastPosFromAI = Point3(0, 0, 0) + self.lastThinkTime = 0 + self.doneAdjust = False + self.barrel = CTGG.NoBarrelCarried + self.signalledAtReturnPos = False + self.defaultPlayRate = 1.0 + self.netTimeSentToStartByHit = 0 + self.velocity = Vec3(0, 0, 0) + self.oldVelocity = Vec3(0, 0, 0) + self.acceleration = Vec3(0, 0, 0) + self.bodyLength = self.CollisionRadius * 2 + self.cruiseDistance = 2 * self.bodyLength + self.maxVelocity = self.cogSpeed + self.maxAcceleration = 5.0 + self.perceptionRange = 6 + self.notify.debug('cogSpeed=%s' % self.cogSpeed) + self.kaboomSound = loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.kaboom = loader.loadModel('phase_4/models/minigames/ice_game_kaboom') + self.kaboom.setScale(2.0) + self.kaboom.setBillboardPointEye() + self.kaboom.hide() + self.kaboomTrack = None + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splatType = globalPropPool.getPropType(splatName) + self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.mp3') + return + + def destroy(self): + self.ignoreAll() + self.suit.delete() + self.game = None + return + + def uniqueName(self, baseStr): + return baseStr + '-' + str(self.game.doId) + + def handleEnterSphere(self, collEntry): + intoNp = collEntry.getIntoNodePath() + self.notify.debug('handleEnterSphere suit %d hit %s' % (self.cogIndex, intoNp)) + if self.game: + self.game.handleEnterSphere(collEntry) + + def gameStart(self, gameStartTime): + self.gameStartTime = gameStartTime + self.initCollisions() + self.startWalkAnim() + + def gameEnd(self): + self.moveIval.pause() + del self.moveIval + self.shutdownCollisions() + self.suit.loop('neutral') + + def initCollisions(self): + self.collSphere = CollisionSphere(0, 0, 0, 1.25) + self.collSphere.setTangible(1) + name = 'CogThiefSphere-%d' % self.cogIndex + self.collSphereName = self.uniqueName(name) + self.collNode = CollisionNode(self.collSphereName) + self.collNode.setIntoCollideMask(CTGG.BarrelBitmask | ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.suit.attachNewNode(self.collNode) + self.accept('enter' + self.collSphereName, self.handleEnterSphere) + self.pieCollSphere = CollisionTube(0, 0, 0, 0, 0, 4, self.CollisionRadius) + self.pieCollSphere.setTangible(1) + name = 'CogThiefPieSphere-%d' % self.cogIndex + self.pieCollSphereName = self.uniqueName(name) + self.pieCollNode = CollisionNode(self.pieCollSphereName) + self.pieCollNode.setIntoCollideMask(ToontownGlobals.PieBitmask) + self.pieCollNode.addSolid(self.pieCollSphere) + self.pieCollNodePath = self.suit.attachNewNode(self.pieCollNode) + + def shutdownCollisions(self): + self.ignore(self.uniqueName('enter' + self.collSphereName)) + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def updateGoal(self, timestamp, inResponseClientStamp, goalType, goalId, pos): + self.notify.debug('self.netTimeSentToStartByHit =%s' % self.netTimeSentToStartByHit) + if not self.game: + self.notify.debug('updateGoal self.game is None, just returning') + return + if not self.suit: + self.notify.debug('updateGoal self.suit is None, just returning') + return + if self.goal == CTGG.NoGoal: + self.startWalkAnim() + if goalType == CTGG.NoGoal: + self.notify.debug('updateGoal setting position to %s' % pos) + self.suit.setPos(pos) + self.lastThinkTime = 0 + self.velocity = Vec3(0, 0, 0) + self.oldVelocity = Vec3(0, 0, 0) + self.acceleration = Vec3(0, 0, 0) + if goalType == CTGG.RunAwayGoal: + pass + if inResponseClientStamp < self.netTimeSentToStartByHit and self.goal == CTGG.NoGoal and goalType == CTGG.RunAwayGoal: + self.notify.warning('ignoring newGoal %s as cog %d was recently hit responsetime=%s hitTime=%s' % (CTGG.GoalStr[goalType], + self.cogIndex, + inResponseClientStamp, + self.netTimeSentToStartByHit)) + else: + self.lastLocalTimeStampFromAI = globalClockDelta.networkToLocalTime(timestamp, bits=32) + self.goal = goalType + self.goalId = goalId + self.lastPosFromAI = pos + self.doneAdjust = False + self.signalledAtReturnPos = False + + def startWalkAnim(self): + if self.suit: + self.suit.loop('walk') + speed = self.cogSpeed + self.defaultPlayRate = float(self.cogSpeed / self.DefaultSpeedWalkAnim) + self.suit.setPlayRate(self.defaultPlayRate, 'walk') + + def think(self): + if self.goal == CTGG.ToonGoal: + self.thinkAboutCatchingToon() + elif self.goal == CTGG.BarrelGoal: + self.thinkAboutGettingBarrel() + elif self.goal == CTGG.RunAwayGoal: + self.thinkAboutRunAway() + + def thinkAboutCatchingToon(self): + if not self.game: + return + av = self.game.getAvatar(self.goalId) + if av: + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + diffTime = globalClock.getFrameTime() - self.lastThinkTime + avPos = av.getPos() + myPos = self.suit.getPos() + if not self.doneAdjust: + myPos = self.lastPosFromAI + self.notify.debug('thinkAboutCatchingToon not doneAdjust setting pos %s' % myPos) + self.doneAdjust = True + self.suit.setPos(myPos) + if self.game.isToonPlayingHitTrack(self.goalId): + self.suit.headsUp(av) + self.velocity = Vec3(0, 0, 0) + self.oldVelocity = Vec3(0, 0, 0) + self.acceleration = Vec3(0, 0, 0) + else: + self.commonMove() + newPos = self.suit.getPos() + self.adjustPlayRate(newPos, myPos, diffTime) + self.lastThinkTime = globalClock.getFrameTime() + + def convertNetworkStampToGameTime(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + gameTime = self.game.local2GameTime(localStamp) + return gameTime + + def respondToToonHit(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + if self.netTimeSentToStartByHit < timestamp: + self.clearGoal() + self.showKaboom() + startPos = CTGG.CogStartingPositions[self.cogIndex] + oldPos = self.suit.getPos() + self.suit.setPos(startPos) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.notify.debug('localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToToonHit' % (localStamp, self.lastLocalTimeStampFromAI)) + self.notify.debug('respondToToonHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def clearGoal(self): + self.goal = CTGG.NoGoal + self.goalId = CTGG.InvalidGoalId + + def thinkAboutGettingBarrel(self): + if not self.game: + return + if not hasattr(self.game, 'barrels'): + return + if self.goalId not in xrange(len(self.game.barrels)): + return + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + diffTime = globalClock.getFrameTime() - self.lastThinkTime + barrel = self.game.barrels[self.goalId] + barrelPos = barrel.getPos() + myPos = self.suit.getPos() + if not self.doneAdjust: + myPos = self.lastPosFromAI + self.notify.debug('thinkAboutGettingBarrel not doneAdjust setting position to %s' % myPos) + self.suit.setPos(myPos) + self.doneAdjust = True + displacement = barrelPos - myPos + distanceToToon = displacement.length() + self.suit.headsUp(barrel) + lengthTravelled = diffTime * self.cogSpeed + if lengthTravelled > distanceToToon: + lengthTravelled = distanceToToon + displacement.normalize() + dirVector = displacement + dirVector *= lengthTravelled + newPos = myPos + dirVector + newPos.setZ(0) + self.suit.setPos(newPos) + self.adjustPlayRate(newPos, myPos, diffTime) + self.lastThinkTime = globalClock.getFrameTime() + + def stopWalking(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + if localStamp > self.lastLocalTimeStampFromAI: + self.suit.loop('neutral') + self.clearGoal() + + def thinkAboutRunAway(self): + if not self.game: + return + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + diffTime = globalClock.getFrameTime() - self.lastThinkTime + returnPos = CTGG.CogReturnPositions[self.goalId] + myPos = self.suit.getPos() + if not self.doneAdjust: + myPos = self.lastPosFromAI + self.suit.setPos(myPos) + self.doneAdjust = True + displacement = returnPos - myPos + distanceToToon = displacement.length() + tempNp = render.attachNewNode('tempRet') + tempNp.setPos(returnPos) + self.suit.headsUp(tempNp) + tempNp.removeNode() + lengthTravelled = diffTime * self.cogSpeed + if lengthTravelled > distanceToToon: + lengthTravelled = distanceToToon + displacement.normalize() + dirVector = displacement + dirVector *= lengthTravelled + newPos = myPos + dirVector + newPos.setZ(0) + self.suit.setPos(newPos) + self.adjustPlayRate(newPos, myPos, diffTime) + if (self.suit.getPos() - returnPos).length() < 0.0001: + if not self.signalledAtReturnPos and self.barrel >= 0: + self.game.sendCogAtReturnPos(self.cogIndex, self.barrel) + self.signalledAtReturnPos = True + self.lastThinkTime = globalClock.getFrameTime() + + def makeCogCarryBarrel(self, timestamp, inResponseClientStamp, barrelModel, barrelIndex, cogPos): + if not self.game: + return + localTimeStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + self.lastLocalTimeStampFromAI = localTimeStamp + inResponseGameTime = self.convertNetworkStampToGameTime(inResponseClientStamp) + self.notify.debug('inResponseGameTime =%s timeSentToStart=%s' % (inResponseGameTime, self.netTimeSentToStartByHit)) + if inResponseClientStamp < self.netTimeSentToStartByHit and self.goal == CTGG.NoGoal: + self.notify.warning('ignoring makeCogCarrybarrel') + else: + barrelModel.setPos(0, -1.0, 1.5) + barrelModel.reparentTo(self.suit) + self.suit.setPos(cogPos) + self.barrel = barrelIndex + + def makeCogDropBarrel(self, timestamp, inResponseClientStamp, barrelModel, barrelIndex, barrelPos): + localTimeStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + self.lastLocalTimeStampFromAI = localTimeStamp + barrelModel.reparentTo(render) + barrelModel.setPos(barrelPos) + self.barrel = CTGG.NoBarrelCarried + + def respondToPieHit(self, timestamp): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + if self.netTimeSentToStartByHit < timestamp: + self.clearGoal() + self.showSplat() + startPos = CTGG.CogStartingPositions[self.cogIndex] + oldPos = self.suit.getPos() + self.suit.setPos(startPos) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.notify.debug('localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToPieHit' % (localStamp, self.lastLocalTimeStampFromAI)) + self.notify.debug('respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def cleanup(self): + self.clearGoal() + self.ignoreAll() + self.suit.delete() + if self.kaboomTrack and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.suit = None + self.game = None + return + + def adjustPlayRate(self, newPos, oldPos, diffTime): + lengthTravelled = (newPos - oldPos).length() + if diffTime: + speed = lengthTravelled / diffTime + else: + speed = self.cogSpeed + rateMult = speed / self.cogSpeed + newRate = rateMult * self.defaultPlayRate + self.suit.setPlayRate(newRate, 'walk') + + def commonMove(self): + if not self.lastThinkTime: + self.lastThinkTime = globalClock.getFrameTime() + dt = globalClock.getFrameTime() - self.lastThinkTime + self.oldpos = self.suit.getPos() + pos = self.suit.getPos() + pos += self.velocity * dt + self.suit.setPos(pos) + self.seeFriends() + acc = Vec3(0, 0, 0) + self.accumulate(acc, self.getTargetVector()) + if self.numFlockmatesSeen > 0: + keepDistanceVector = self.keepDistance() + oldAcc = Vec3(acc) + self.accumulate(acc, keepDistanceVector) + if self.cogIndex == 0: + pass + if acc.length() > self.maxAcceleration: + acc.normalize() + acc *= self.maxAcceleration + self.oldVelocity = self.velocity + self.velocity += acc + if self.velocity.length() > self.maxVelocity: + self.velocity.normalize() + self.velocity *= self.maxVelocity + forwardVec = Vec3(1, 0, 0) + heading = rad2Deg(math.atan2(self.velocity[1], self.velocity[0])) + heading -= 90 + self.suit.setH(heading) + + def getTargetVector(self): + targetPos = Point3(0, 0, 0) + if self.goal == CTGG.ToonGoal: + av = self.game.getAvatar(self.goalId) + if av: + targetPos = av.getPos() + elif self.goal == CTGG.BarrelGoal: + barrel = self.game.barrels[self.goalId] + targetPos = barrel.getPos() + elif self.goal == CTGG.RunAwayGoal: + targetPos = CTGG.CogReturnPositions[self.goalId] + targetPos.setZ(0) + myPos = self.suit.getPos() + diff = targetPos - myPos + if diff.length() > 1.0: + diff.normalize() + diff *= 1.0 + return diff + + def accumulate(self, accumulator, valueToAdd): + accumulator += valueToAdd + return accumulator.length() + + def seeFriends(self): + self.clearVisibleList() + for cogIndex in self.game.cogInfo.keys(): + if cogIndex == self.cogIndex: + continue + if self.sameGoal(cogIndex): + dist = self.canISee(cogIndex) + if dist != self.Infinity: + self.addToVisibleList(cogIndex) + if dist < self.distToNearestFlockmate: + self.nearestFlockmate = cogIndex + self.distToNearestFlockmate = dist + + return self.numFlockmatesSeen + + def clearVisibleList(self): + self.visibleFriendsList = [] + self.numFlockmatesSeen = 0 + self.nearestFlockmate = None + self.distToNearestFlockmate = self.Infinity + return + + def addToVisibleList(self, cogIndex): + if self.numFlockmatesSeen < self.MaxFriendsVisible: + self.visibleFriendsList.append(cogIndex) + self.numFlockmatesSeen += 1 + if self.cogIndex == 0: + pass + + def canISee(self, cogIndex): + if self.cogIndex == cogIndex: + return self.Infinity + cogThief = self.game.getCogThief(cogIndex) + distance = self.suit.getDistance(cogThief.suit) + if distance < self.perceptionRange: + return distance + return self.Infinity + + def sameGoal(self, cogIndex): + cogThief = self.game.getCogThief(cogIndex) + result = cogThief.goalId == self.goalId and cogThief.goal == self.goal + return result + + def keepDistance(self): + ratio = self.distToNearestFlockmate / self.SeparationDistance + nearestThief = self.game.getCogThief(self.nearestFlockmate) + change = nearestThief.suit.getPos() - self.suit.getPos() + if ratio < self.MinUrgency: + ratio = self.MinUrgency + if ratio > self.MaxUrgency: + ratio = self.MaxUrgency + if self.distToNearestFlockmate < self.SeparationDistance: + change.normalize() + change *= -(1 - ratio) + elif self.distToNearestFlockmate > self.SeparationDistance: + change.normalize() + change *= ratio + else: + change = Vec3(0, 0, 0) + return change + + def showKaboom(self): + if self.kaboomTrack and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.kaboom.reparentTo(render) + self.kaboom.setPos(self.suit.getPos()) + self.kaboom.setZ(3) + self.kaboomTrack = Parallel(SoundInterval(self.kaboomSound, volume=0.5), Sequence(Func(self.kaboom.showThrough), LerpScaleInterval(self.kaboom, duration=0.5, scale=Point3(10, 10, 10), startScale=Point3(1, 1, 1), blendType='easeOut'), Func(self.kaboom.hide))) + self.kaboomTrack.start() + + def showSplat(self): + if self.kaboomTrack and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.splat.reparentTo(render) + self.splat.setPos(self.suit.getPos()) + self.splat.setZ(3) + self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, volume=1.0), Sequence(Func(self.splat.showThrough), LerpScaleInterval(self.splat, duration=0.5, scale=1.75, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Func(self.splat.hide))) + self.kaboomTrack.start() diff --git a/toontown/minigame/CogThiefGameGlobals.py b/toontown/minigame/CogThiefGameGlobals.py new file mode 100644 index 0000000..1b9784a --- /dev/null +++ b/toontown/minigame/CogThiefGameGlobals.py @@ -0,0 +1,131 @@ +from pandac.PandaModules import VBase3, BitMask32 +GameTime = 60 +NumBarrels = 4 +BarrelStartingPositions = (VBase3(4.3, 4, 0), + VBase3(4.3, -4, 0), + VBase3(-4.3, 4, 0), + VBase3(-4.3, -4, 0)) +ToonStartingPositions = (VBase3(0, 16, 0), + VBase3(0, -16, 0), + VBase3(-16, 0, 0), + VBase3(16, 0, 0)) +CogStartingPositions = (VBase3(35, 18, 0), + VBase3(35, 0, 0), + VBase3(35, -18, 0), + VBase3(-35, 18, 0), + VBase3(-35, 0, 0), + VBase3(-35, -18, 0), + VBase3(0, 27, 0), + VBase3(0, -27, 0), + VBase3(35, 9, 0), + VBase3(-35, 9, 0), + VBase3(35, -9, 0), + VBase3(-35, -9, 0)) +CogReturnPositions = (VBase3(-35, 28, 0), + VBase3(-14, 28, 0), + VBase3(14, 28, 0), + VBase3(35, 28, 0), + VBase3(35, 0, 0), + VBase3(35, -28, 0), + VBase3(-14, -28, 0), + VBase3(14, -28, 0), + VBase3(-35, -28, 0), + VBase3(-35, 0, 0)) +StageHalfWidth = 25 +StageHalfHeight = 18 +NoGoal = 0 +BarrelGoal = 1 +ToonGoal = 2 +RunAwayGoal = 3 +InvalidGoalId = -1 +GoalStr = {NoGoal: 'NoGoal', + BarrelGoal: 'BarrelGoal', + ToonGoal: 'ToonGoal', + RunAwayGoal: 'RunAwayGoal', + InvalidGoalId: 'InvalidGoa'} +BarrelBitmask = BitMask32(512) +BarrelOnGround = -1 +NoBarrelCarried = -1 +LyingDownDuration = 2.0 +MAX_SCORE = 20 +MIN_SCORE = 3 + +def calcScore(t): + range = MAX_SCORE - MIN_SCORE + score = range * (float(t) / GameTime) + MIN_SCORE + return int(score + 0.5) + + +def getMaxScore(): + result = calcScore(GameTime) + return result + + +NumCogsTable = [{2000: 5, + 1000: 5, + 5000: 5, + 4000: 5, + 3000: 5, + 9000: 5}, + {2000: 7, + 1000: 7, + 5000: 7, + 4000: 7, + 3000: 7, + 9000: 7}, + {2000: 9, + 1000: 9, + 5000: 9, + 4000: 9, + 3000: 9, + 9000: 9}, + {2000: 11, + 1000: 11, + 5000: 11, + 4000: 11, + 3000: 11, + 9000: 11}] +CogSpeedTable = [{2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0}, + {2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0}, + {2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0}, + {2000: 6.0, + 1000: 6.4, + 5000: 6.8, + 4000: 7.2, + 3000: 7.6, + 9000: 8.0}] +ToonSpeed = 9.0 +PerfectBonus = [8, + 6, + 4, + 2] + +def calculateCogs(numPlayers, safezone): + result = 5 + if numPlayers <= len(NumCogsTable): + if safezone in NumCogsTable[numPlayers - 1]: + result = NumCogsTable[numPlayers - 1][safezone] + return result + + +def calculateCogSpeed(numPlayers, safezone): + result = 6.0 + if numPlayers <= len(NumCogsTable): + if safezone in CogSpeedTable[numPlayers - 1]: + result = CogSpeedTable[numPlayers - 1][safezone] + return result diff --git a/toontown/minigame/CogThiefGameToonSD.py b/toontown/minigame/CogThiefGameToonSD.py new file mode 100644 index 0000000..c710808 --- /dev/null +++ b/toontown/minigame/CogThiefGameToonSD.py @@ -0,0 +1,187 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task.Task import Task + +class CogThiefGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('CogThiefGameToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + NeutralAnim = 'neutral' + RunAnim = 'run' + ThrowNeutralAnim = 'throw' + ThrowRunAnim = 'throw' + animList = [FallBackAnim, + FallFwdAnim, + NeutralAnim, + RunAnim, + ThrowNeutralAnim, + ThrowRunAnim] + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CogThiefGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['throwPie', 'fallBack', 'fallForward']), + State.State('throwPie', self.enterThrowPie, self.exitThrowPie, ['normal', + 'fallBack', + 'fallForward', + 'throwPie']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal']), + State.State('fallForward', self.enterFallForward, self.exitFallForward, ['normal']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + self.exitAlreadyCalled = False + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + del self.fsm + + def enter(self): + self.fsm.enterInitialState() + + def exit(self, unexpectedExit = False): + if self.exitAlreadyCalled: + return + self.exitAlreadyCalled = True + self.notify.debug('in exit self.toon.doId=%s' % self.toon.doId) + self.unexpectedExit = unexpectedExit + self.fsm.requestFinalState() + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + if self.isLocal: + self.game.initGameWalk() + self.toon.useLOD(1000) + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def exitInit(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) + + def enterNormal(self): + self.notify.debug('enterNormal') + self.setAnimState('CogThiefRunning', 1.0) + if self.isLocal: + self.game.startGameWalk() + self.toon.lerpLookAt(Vec3.forward() + Vec3.up(), time=0.2, blink=0) + + def exitNormal(self): + self.notify.debug('exitNormal') + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.stopGameWalk() + self.toon.lerpLookAt(Vec3.forward(), time=0.2, blink=0) + + def throwPie(self, pieModel, handNode): + if self.fsm.getCurrentState().getName() == 'throwPie': + self.fsm.request('normal') + self.fsm.request('throwPie', [pieModel, handNode]) + + def enterThrowPie(self, pieModel, handNode): + self.notify.debug('enterThrowPie') + self.setAnimState('CatchEating', 1.0) + if self.isLocal: + self.game.startGameWalk() + self.pieModel = pieModel + renderScale = pieModel.getScale(render) + pieModel.reparentTo(handNode) + pieModel.setScale(render, renderScale) + + def finishedEating(self = self, pieModel = pieModel): + self.fsm.request('normal') + return Task.done + + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpScaleInterval(pieModel, duration / 2.0, pieModel.getScale() * 0.5, blendType='easeInOut'), Func(pieModel.hide))), Func(finishedEating), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitThrowPie(self): + self.eatIval.pause() + del self.eatIval + self.pieModel.reparentTo(hidden) + self.pieModel.removeNode() + del self.pieModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.game.stopGameWalk() + + def enterFallBack(self): + self.notify.debug('enterFallBack') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallBackAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallBackIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + del self.fallBackIval + + def enterFallForward(self): + self.notify.debug('enterFallForward') + if self.isLocal: + base.playSfx(self.game.sndOof) + duration = 1.0 + animName = self.FallFwdAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallFwdIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallFwdIval.start() + + def exitFallForward(self): + self.fallFwdIval.pause() + del self.fallFwdIval + + def enterCleanup(self): + self.notify.debug('enterCleanup %s' % self.toon.doId) + if self.toon and not self.toon.isEmpty(): + self.toon.stopBlink() + self.toon.startLookAround() + if self.isLocal: + self.game.stopGameWalk() + self.game.destroyGameWalk() + self.toon.resetLOD() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitCleanup(self): + pass diff --git a/toontown/minigame/CogThiefWalk.py b/toontown/minigame/CogThiefWalk.py new file mode 100644 index 0000000..a1de366 --- /dev/null +++ b/toontown/minigame/CogThiefWalk.py @@ -0,0 +1,25 @@ +from toontown.safezone import Walk + +class CogThiefWalk(Walk.Walk): + notify = directNotify.newCategory('CogThiefWalk') + + def __init__(self, doneEvent): + Walk.Walk.__init__(self, doneEvent) + + def enter(self, slowWalk = 0): + base.localAvatar.startPosHprBroadcast() + base.localAvatar.startBlink() + base.localAvatar.showName() + base.localAvatar.collisionsOn() + base.localAvatar.startGlitchKiller() + base.localAvatar.enableAvatarControls() + + def exit(self): + self.fsm.request('off') + self.ignore('control') + base.localAvatar.disableAvatarControls() + base.localAvatar.stopPosHprBroadcast() + base.localAvatar.stopBlink() + base.localAvatar.stopGlitchKiller() + base.localAvatar.collisionsOff() + base.localAvatar.controlManager.placeOnFloor() diff --git a/toontown/minigame/DistributedCannonGame.py b/toontown/minigame/DistributedCannonGame.py new file mode 100644 index 0000000..9fdc425 --- /dev/null +++ b/toontown/minigame/DistributedCannonGame.py @@ -0,0 +1,993 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from direct.task.Task import Task +import Trajectory +import math +from toontown.toon import ToonHead +from toontown.effects import Splash +from toontown.effects import DustCloud +import CannonGameGlobals +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +LAND_TIME = 2 +WORLD_SCALE = 2.0 +GROUND_SCALE = 1.4 * WORLD_SCALE +CANNON_SCALE = 1.0 +FAR_PLANE_DIST = 600 * WORLD_SCALE +CANNON_Y = -int(CannonGameGlobals.TowerYRange / 2 * 1.3) +CANNON_X_SPACING = 12 +CANNON_Z = 20 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 +MAX_LOOKAT_OFFSET = 80 +TOON_TOWER_THRESHOLD = 150 +SHADOW_Z_OFFSET = 0.5 +TOWER_HEIGHT = 43.85 +TOWER_RADIUS = 10.5 +BUCKET_HEIGHT = 36 +TOWER_Y_RANGE = CannonGameGlobals.TowerYRange +TOWER_X_RANGE = int(TOWER_Y_RANGE / 2.0) +INITIAL_VELOCITY = 94.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.55 + +class DistributedCannonGame(DistributedMinigame): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigame') + font = ToontownGlobals.getToonFont() + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + REWARD_COUNTDOWN_TASK = 'cannonGameRewardCountdown' + HIT_GROUND = 0 + HIT_TOWER = 1 + HIT_WATER = 2 + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + INTRO_TASK_NAME = 'CannonGameIntro' + INTRO_TASK_NAME_CAMERA_LERP = 'CannonGameIntroCamera' + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCannonGame', [State.State('off', self.enterOff, self.exitOff, ['aim']), + State.State('aim', self.enterAim, self.exitAim, ['shoot', 'waitForToonsToLand', 'cleanup']), + State.State('shoot', self.enterShoot, self.exitShoot, ['aim', 'waitForToonsToLand', 'cleanup']), + State.State('waitForToonsToLand', self.enterWaitForToonsToLand, self.exitWaitForToonsToLand, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cannonLocationDict = {} + self.cannonPositionDict = {} + self.cannonDict = {} + self.toonModelDict = {} + self.dropShadowDict = {} + self.toonHeadDict = {} + self.toonScaleDict = {} + self.toonIntervalDict = {} + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + self.modelCount = 14 + + def getTitle(self): + return TTLocalizer.CannonGameTitle + + def getInstructions(self): + return TTLocalizer.CannonGameInstructions + + def getMaxDuration(self): + return CannonGameGlobals.GameTime + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.ground = loader.loadModel('phase_4/models/minigames/toon_cannon_gameground') + self.tower = loader.loadModel('phase_4/models/minigames/toon_cannon_water_tower') + self.cannon = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.hill = loader.loadModel('phase_4/models/minigames/cannon_hill') + self.sky.setScale(WORLD_SCALE) + self.ground.setScale(GROUND_SCALE) + self.cannon.setScale(CANNON_SCALE) + self.dropShadow.setColor(0, 0, 0, 0.5) + self.ground.setColor(0.85, 0.85, 0.85, 1.0) + self.hill.setScale(1, 1, CANNON_Z / 20.0) + self.dropShadow.setBin('fixed', 0, 1) + self.splash = Splash.Splash(render) + self.dustCloud = DustCloud.DustCloud(render) + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.jarImage = purchaseModels.find('**/Jar') + self.jarImage.reparentTo(hidden) + self.rewardPanel = DirectLabel(parent=hidden, relief=None, pos=(1.16, 0.0, 0.45), scale=0.65, text='', text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -.13), text_font=ToontownGlobals.getSignFont(), image=self.jarImage) + self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel, relief=None, pos=(0, 0, 0.06), scale=0.08, text=TTLocalizer.CannonGameReward, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1)) + self.music = base.loadMusic('phase_4/audio/bgm/MG_cannon_game.mid') + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.sndHitTower = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_tower.mp3') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.mp3') + self.sndWhizz = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.mp3') + self.sndWin = base.loadSfx('phase_4/audio/sfx/MG_win.mp3') + self.sndRewardTick = base.loadSfx('phase_3.5/audio/sfx/tick_counter.mp3') + guiModel = 'phase_4/models/gui/cannon_game_gui' + cannonGui = loader.loadModel(guiModel) + self.aimPad = DirectFrame(image=cannonGui.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + cannonGui.removeNode() + self.aimPad.hide() + self.fireButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + self.aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.DEBUG_TOWER_RANGE = 0 + self.DEBUG_CANNON_FAR_LEFT = 0 + self.DEBUG_TOWER_NEAR = 1 + self.DEBUG_TOWER_FAR_LEFT = 1 + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.sky.removeNode() + del self.sky + self.ground.removeNode() + del self.ground + self.tower.removeNode() + del self.tower + self.cannon.removeNode() + del self.cannon + del self.dropShadowDict + self.dropShadow.removeNode() + del self.dropShadow + self.splash.destroy() + del self.splash + self.dustCloud.destroy() + del self.dustCloud + self.hill.removeNode() + del self.hill + self.rewardPanel.destroy() + del self.rewardPanel + self.jarImage.removeNode() + del self.jarImage + del self.music + del self.sndCannonMove + del self.sndCannonFire + del self.sndHitGround + del self.sndHitTower + del self.sndHitWater + del self.sndWhizz + del self.sndWin + del self.sndRewardTick + self.aimPad.destroy() + del self.aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + for avId in self.toonHeadDict.keys(): + head = self.toonHeadDict[avId] + head.stopBlink() + head.stopLookAroundNow() + av = self.getAvatar(avId) + if av: + av.loop('neutral') + av.setPlayRate(1.0, 'run') + av.nametag.removeNametag(head.tag) + head.delete() + + del self.toonHeadDict + for model in self.toonModelDict.values(): + model.removeNode() + + del self.toonModelDict + del self.toonScaleDict + for interval in self.toonIntervalDict.values(): + interval.finish() + + del self.toonIntervalDict + for avId in self.avIdList: + self.cannonDict[avId][0].removeNode() + del self.cannonDict[avId][0] + + del self.cannonDict + self.timer.destroy() + del self.timer + del self.cannonLocationDict + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.__createCannons() + for avId in self.avIdList: + self.cannonDict[avId][0].reparentTo(render) + + self.towerPos = self.getTowerPosition() + self.tower.setPos(self.towerPos) + self.tower.reparentTo(render) + self.sky.reparentTo(render) + self.ground.reparentTo(render) + self.hill.setPosHpr(0, CANNON_Y + 2.33, 0, 0, 0, 0) + self.hill.reparentTo(render) + self.splash.reparentTo(render) + self.dustCloud.reparentTo(render) + self.__createToonModels(self.localAvId) + camera.reparentTo(render) + self.__oldCamFar = base.camLens.getFar() + base.camLens.setFar(FAR_PLANE_DIST) + self.__startIntro() + base.transitions.irisIn(0.4) + base.playMusic(self.music, looping=1, volume=0.8) + + def offstage(self): + self.notify.debug('offstage') + self.sky.reparentTo(hidden) + self.ground.reparentTo(hidden) + self.hill.reparentTo(hidden) + self.tower.reparentTo(hidden) + for avId in self.avIdList: + self.cannonDict[avId][0].reparentTo(hidden) + if self.dropShadowDict.has_key(avId): + self.dropShadowDict[avId].reparentTo(hidden) + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + self.splash.reparentTo(hidden) + self.splash.stop() + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + self.__stopIntro() + base.camLens.setFar(self.__oldCamFar) + self.timer.reparentTo(hidden) + self.rewardPanel.reparentTo(hidden) + DistributedMinigame.offstage(self) + + def getTowerPosition(self): + yRange = TOWER_Y_RANGE + yMin = yRange * 0.3 + yMax = yRange + if self.DEBUG_TOWER_RANGE: + if self.DEBUG_TOWER_NEAR: + y = yMin + else: + y = yMax + else: + y = self.randomNumGen.randint(yMin, yMax) + xRange = TOWER_X_RANGE + if self.DEBUG_TOWER_RANGE: + if self.DEBUG_TOWER_FAR_LEFT: + x = 0 + else: + x = xRange + else: + x = self.randomNumGen.randint(0, xRange) + x = x - int(xRange / 2.0) + if base.wantMinigameDifficulty: + diff = self.getDifficulty() + scale = 0.5 + 0.5 * diff + x *= scale + yCenter = (yMin + yMax) / 2.0 + y = (y - yCenter) * scale + yCenter + x = float(x) * (float(y) / float(yRange)) + y = y - int(yRange / 2.0) + self.notify.debug('getTowerPosition: ' + str(x) + ', ' + str(y)) + return Point3(x, y, 0.0) + + def __createCannons(self): + for avId in self.avIdList: + cannon = self.cannon.copyTo(hidden) + barrel = cannon.find('**/cannon') + self.cannonDict[avId] = [cannon, barrel] + + numAvs = self.numPlayers + for i in range(numAvs): + avId = self.avIdList[i] + self.cannonLocationDict[avId] = Point3(i * CANNON_X_SPACING - (numAvs - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) + if self.DEBUG_TOWER_RANGE: + if self.DEBUG_CANNON_FAR_LEFT: + self.cannonLocationDict[avId] = Point3(0 * CANNON_X_SPACING - (4 - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) + else: + self.cannonLocationDict[avId] = Point3(3 * CANNON_X_SPACING - (4 - 1) * CANNON_X_SPACING / 2, CANNON_Y, CANNON_Z) + self.cannonPositionDict[avId] = [0, CannonGameGlobals.CANNON_ANGLE_MIN] + self.cannonDict[avId][0].setPos(self.cannonLocationDict[avId]) + self.__updateCannonPosition(avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.avIdList: + if avId != self.localAvId: + self.__createToonModels(avId) + + def __createToonModels(self, avId): + toon = self.getAvatar(avId) + self.toonScaleDict[avId] = toon.getScale() + toon.useLOD(1000) + toonParent = render.attachNewNode('toonOriginChange') + toon.reparentTo(toonParent) + toon.setPosHpr(0, 0, -(toon.getHeight() / 2.0), 0, 0, 0) + self.toonModelDict[avId] = toonParent + head = ToonHead.ToonHead() + head.setupHead(self.getAvatar(avId).style) + head.reparentTo(hidden) + self.toonHeadDict[avId] = head + toon = self.getAvatar(avId) + tag = NametagFloat3d() + tag.setContents(Nametag.CSpeech | Nametag.CThought) + tag.setBillboardOffset(0) + tag.setAvatar(head) + toon.nametag.addNametag(tag) + tagPath = head.attachNewNode(tag.upcastToPandaNode()) + tagPath.setPos(0, 0, 1) + head.tag = tag + self.__loadToonInCannon(avId) + self.getAvatar(avId).dropShadow.hide() + self.dropShadowDict[avId] = self.dropShadow.copyTo(hidden) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.__stopIntro() + self.__putCameraBehindCannon() + if not base.config.GetBool('endless-cannon-game', 0): + self.timer.show() + self.timer.countdown(CannonGameGlobals.GameTime, self.__gameTimerExpired) + self.rewardPanel.reparentTo(aspect2d) + self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id) + self.__startRewardCountdown() + self.airborneToons = 0 + self.clockStopTime = None + self.gameFSM.request('aim') + return + + def __gameTimerExpired(self): + self.notify.debug('game timer expired') + self.gameOver() + + def __playing(self): + return self.gameFSM.getCurrentState() != self.gameFSM.getFinalState() + + def updateCannonPosition(self, avId, zRot, angle): + if not self.hasLocalToon: + return + if not self.__playing(): + return + if avId != self.localAvId: + self.cannonPositionDict[avId] = [zRot, angle] + self.__updateCannonPosition(avId) + + def setCannonWillFire(self, avId, fireTime, zRot, angle): + if not self.hasLocalToon: + return + if not self.__playing(): + return + self.notify.debug('setCannonWillFire: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle) + ', time=' + str(fireTime)) + self.cannonPositionDict[avId][0] = zRot + self.cannonPositionDict[avId][1] = angle + self.__updateCannonPosition(avId) + task = Task(self.__fireCannonTask) + task.avId = avId + task.fireTime = fireTime + timeToWait = task.fireTime - self.getCurrentGameTime() + if timeToWait > 0.0: + fireTask = Task.sequence(Task.pause(timeToWait), task) + else: + fireTask = task + fireTask = task + taskMgr.add(fireTask, 'fireCannon' + str(avId)) + self.airborneToons += 1 + + def announceToonWillLandInWater(self, avId, landTime): + if not self.hasLocalToon: + return + self.notify.debug('announceToonWillLandInWater: ' + str(avId) + ': time=' + str(landTime)) + if self.clockStopTime == None: + self.clockStopTime = landTime + return + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterAim(self): + self.notify.debug('enterAim') + self.__enableAimInterface() + self.__putCameraBehindCannon() + + def exitAim(self): + self.__disableAimInterface() + + def enterShoot(self): + self.notify.debug('enterShoot') + self.__broadcastLocalCannonPosition() + self.sendUpdate('setCannonLit', [self.cannonPositionDict[self.localAvId][0], self.cannonPositionDict[self.localAvId][1]]) + + def exitShoot(self): + pass + + def __somebodyWon(self, avId): + if avId == self.localAvId: + base.playSfx(self.sndWin) + self.__killRewardCountdown() + self.timer.stop() + self.gameFSM.request('waitForToonsToLand') + + def enterWaitForToonsToLand(self): + self.notify.debug('enterWaitForToonsToLand') + if not self.airborneToons: + self.gameOver() + + def exitWaitForToonsToLand(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.music.stop() + self.__killRewardCountdown() + if hasattr(self, 'jarIval'): + self.jarIval.finish() + del self.jarIval + for avId in self.avIdList: + taskMgr.remove('fireCannon' + str(avId)) + taskMgr.remove('flyingToon' + str(avId)) + + def exitCleanup(self): + pass + + def __enableAimInterface(self): + self.aimPad.show() + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__spawnLocalCannonMoveTask() + + def __disableAimInterface(self): + self.aimPad.hide() + self.ignore(self.FIRE_KEY) + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.FIRE_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.__killLocalCannonMoveTask() + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.__fireReleased() + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downReleased() + + def __firePressed(self): + self.notify.debug('fire pressed') + self.gameFSM.request('shoot') + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) + + def __spawnLocalCannonMoveTask(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.cannonMoving = 0 + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __killLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.cannonMoving: + self.sndCannonMove.stop() + + def __localCannonMoveTask(self, task): + pos = self.cannonPositionDict[self.localAvId] + oldRot = pos[0] + oldAng = pos[1] + rotVel = 0 + if self.leftPressed: + rotVel += CannonGameGlobals.CANNON_ROTATION_VEL + if self.rightPressed: + rotVel -= CannonGameGlobals.CANNON_ROTATION_VEL + pos[0] += rotVel * globalClock.getDt() + if pos[0] < CannonGameGlobals.CANNON_ROTATION_MIN: + pos[0] = CannonGameGlobals.CANNON_ROTATION_MIN + elif pos[0] > CannonGameGlobals.CANNON_ROTATION_MAX: + pos[0] = CannonGameGlobals.CANNON_ROTATION_MAX + angVel = 0 + if self.upPressed: + angVel += CannonGameGlobals.CANNON_ANGLE_VEL + if self.downPressed: + angVel -= CannonGameGlobals.CANNON_ANGLE_VEL + pos[1] += angVel * globalClock.getDt() + if pos[1] < CannonGameGlobals.CANNON_ANGLE_MIN: + pos[1] = CannonGameGlobals.CANNON_ANGLE_MIN + elif pos[1] > CannonGameGlobals.CANNON_ANGLE_MAX: + pos[1] = CannonGameGlobals.CANNON_ANGLE_MAX + if oldRot != pos[0] or oldAng != pos[1]: + if self.cannonMoving == 0: + self.cannonMoving = 1 + base.playSfx(self.sndCannonMove, looping=1) + self.__updateCannonPosition(self.localAvId) + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.cannonMoving: + self.cannonMoving = 0 + self.sndCannonMove.stop() + self.__broadcastLocalCannonPosition() + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.sendUpdate('setCannonPosition', [self.cannonPositionDict[self.localAvId][0], self.cannonPositionDict[self.localAvId][1]]) + + def __updateCannonPosition(self, avId): + self.cannonDict[avId][0].setHpr(self.cannonPositionDict[avId][0], 0.0, 0.0) + self.cannonDict[avId][1].setHpr(0.0, self.cannonPositionDict[avId][1], 0.0) + + def __getCameraPositionBehindCannon(self): + return Point3(self.cannonLocationDict[self.localAvId][0], CANNON_Y - 25.0, CANNON_Z + 7) + + def __putCameraBehindCannon(self): + camera.setPos(self.__getCameraPositionBehindCannon()) + camera.setHpr(0, 0, 0) + + def __loadToonInCannon(self, avId): + self.toonModelDict[avId].detachNode() + head = self.toonHeadDict[avId] + head.startBlink() + head.startLookAround() + head.reparentTo(self.cannonDict[avId][1]) + head.setPosHpr(0, 6, 0, 0, -45, 0) + sc = self.toonScaleDict[avId] + head.setScale(render, sc[0], sc[1], sc[2]) + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __calcFlightResults(self, avId, launchTime): + head = self.toonHeadDict[avId] + startPos = head.getPos(render) + startHpr = head.getHpr(render) + hpr = self.cannonDict[avId][1].getHpr(render) + towerPos = self.tower.getPos(render) + rotation = self.__toRadians(hpr[0]) + angle = self.__toRadians(hpr[1]) + horizVel = INITIAL_VELOCITY * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = INITIAL_VELOCITY * math.sin(angle) + startVel = Vec3(xVel, yVel, zVel) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + towerList = [towerPos + Point3(0, 0, BUCKET_HEIGHT), TOWER_RADIUS, TOWER_HEIGHT - BUCKET_HEIGHT] + self.notify.debug('calcFlightResults(%s): rotation(%s), angle(%s), horizVel(%s), xVel(%s), yVel(%s), zVel(%s), startVel(%s), trajectory(%s), towerList(%s)' % (avId, + rotation, + angle, + horizVel, + xVel, + yVel, + zVel, + startVel, + trajectory, + towerList)) + timeOfImpact, hitWhat = self.__calcToonImpact(trajectory, towerList) + return {'startPos': startPos, + 'startHpr': startHpr, + 'startVel': startVel, + 'trajectory': trajectory, + 'timeOfImpact': timeOfImpact, + 'hitWhat': hitWhat} + + def __fireCannonTask(self, task): + launchTime = task.fireTime + avId = task.avId + self.notify.debug('FIRING CANNON FOR AVATAR ' + str(avId)) + flightResults = self.__calcFlightResults(avId, launchTime) + if not isClient(): + print 'EXECWARNING DistributedCannonGame: %s' % flightResults + printStack() + for key in flightResults: + exec "%s = flightResults['%s']" % (key, key) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + self.notify.debug('time of impact: ' + str(timeOfImpact)) + self.notify.debug('location of impact: ' + str(trajectory.getPos(timeOfImpact))) + if hitWhat == self.HIT_WATER: + self.notify.debug('toon will land in the water') + elif hitWhat == self.HIT_TOWER: + self.notify.debug('toon will hit the tower') + else: + self.notify.debug('toon will hit the ground') + head = self.toonHeadDict[avId] + head.stopBlink() + head.stopLookAroundNow() + head.reparentTo(hidden) + av = self.toonModelDict[avId] + av.reparentTo(render) + av.setPos(startPos) + av.setHpr(startHpr) + avatar = self.getAvatar(avId) + avatar.loop('swim') + avatar.setPosHpr(0, 0, -(avatar.getHeight() / 2.0), 0, 0, 0) + shootTask = Task(self.__shootTask) + flyTask = Task(self.__flyTask) + seqDoneTask = Task(self.__flySequenceDoneTask) + info = {} + info['avId'] = avId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['timeOfImpact'] = timeOfImpact + info['hitWhat'] = hitWhat + info['toon'] = self.toonModelDict[avId] + info['hRot'] = self.cannonPositionDict[avId][0] + info['haveWhistled'] = 0 + info['maxCamPullback'] = CAMERA_PULLBACK_MIN + info['timeEnterTowerXY'], info['timeExitTowerXY'] = trajectory.calcEnterAndLeaveCylinderXY(self.tower.getPos(render), TOWER_RADIUS) + shootTask.info = info + flyTask.info = info + seqDoneTask.info = info + seqTask = Task.sequence(shootTask, flyTask, Task.pause(LAND_TIME), seqDoneTask) + taskMgr.add(seqTask, 'flyingToon' + str(avId)) + if avId == self.localAvId: + if info['hitWhat'] == self.HIT_WATER: + self.sendUpdate('setToonWillLandInWater', [info['timeOfImpact']]) + return Task.done + + def __calcToonImpact(self, trajectory, waterTower): + self.notify.debug('trajectory: %s' % trajectory) + self.notify.debug('waterTower: %s' % waterTower) + waterDiscCenter = Point3(waterTower[0]) + waterDiscCenter.setZ(waterDiscCenter[2] + waterTower[2]) + t_waterImpact = trajectory.checkCollisionWithDisc(waterDiscCenter, waterTower[1]) + self.notify.debug('t_waterImpact: %s' % t_waterImpact) + if t_waterImpact > 0: + return (t_waterImpact, self.HIT_WATER) + t_towerImpact = trajectory.checkCollisionWithCylinderSides(waterTower[0], waterTower[1], waterTower[2]) + self.notify.debug('t_towerImpact: %s' % t_towerImpact) + if t_towerImpact > 0: + return (t_towerImpact, self.HIT_TOWER) + t_groundImpact = trajectory.checkCollisionWithGround() + self.notify.debug('t_groundImpact: %s' % t_groundImpact) + if t_groundImpact >= trajectory.getStartTime(): + return (t_groundImpact, self.HIT_GROUND) + else: + self.notify.error('__calcToonImpact: toon never impacts ground?') + return (self.startTime, self.HIT_GROUND) + + def __shootTask(self, task): + base.playSfx(self.sndCannonFire) + self.dropShadowDict[task.info['avId']].reparentTo(render) + return Task.done + + def __flyTask(self, task): + curTime = task.time + task.info['launchTime'] + t = min(curTime, task.info['timeOfImpact']) + pos = task.info['trajectory'].getPos(t) + task.info['toon'].setPos(pos) + shadowPos = Point3(pos) + if t >= task.info['timeEnterTowerXY'] and t <= task.info['timeExitTowerXY'] and pos[2] >= self.tower.getPos(render)[2] + TOWER_HEIGHT: + shadowPos.setZ(self.tower.getPos(render)[2] + TOWER_HEIGHT + SHADOW_Z_OFFSET) + else: + shadowPos.setZ(SHADOW_Z_OFFSET) + self.dropShadowDict[task.info['avId']].setPos(shadowPos) + vel = task.info['trajectory'].getVel(t) + run = math.sqrt(vel[0] * vel[0] + vel[1] * vel[1]) + rise = vel[2] + theta = self.__toDegrees(math.atan(rise / run)) + task.info['toon'].setHpr(task.info['hRot'], -90 + theta, 0) + if task.info['avId'] == self.localAvId: + lookAt = self.tower.getPos(render) + lookAt.setZ(lookAt.getZ() - TOWER_HEIGHT / 2.0) + towerPos = Point3(self.towerPos) + towerPos.setZ(TOWER_HEIGHT) + ttVec = Vec3(pos - towerPos) + toonTowerDist = ttVec.length() + multiplier = 0.0 + if toonTowerDist < TOON_TOWER_THRESHOLD: + up = Vec3(0.0, 0.0, 1.0) + perp = up.cross(vel) + perp.normalize() + if ttVec.dot(perp) > 0.0: + perp = Vec3(-perp[0], -perp[1], -perp[2]) + a = 1.0 - toonTowerDist / TOON_TOWER_THRESHOLD + a_2 = a * a + multiplier = -2.0 * a_2 * a + 3 * a_2 + lookAt = lookAt + perp * (multiplier * MAX_LOOKAT_OFFSET) + foo = Vec3(pos - lookAt) + foo.normalize() + task.info['maxCamPullback'] = max(task.info['maxCamPullback'], CAMERA_PULLBACK_MIN + multiplier * (CAMERA_PULLBACK_MAX - CAMERA_PULLBACK_MIN)) + foo = foo * task.info['maxCamPullback'] + camPos = pos + Point3(foo) + camera.setPos(camPos) + camera.lookAt(pos) + if task.info['haveWhistled'] == 0: + if -vel[2] > WHISTLE_SPEED: + if t < task.info['timeOfImpact'] - 0.5: + task.info['haveWhistled'] = 1 + base.playSfx(self.sndWhizz) + if t == task.info['timeOfImpact']: + if task.info['haveWhistled']: + self.sndWhizz.stop() + self.dropShadowDict[task.info['avId']].reparentTo(hidden) + avatar = self.getAvatar(task.info['avId']) + if task.info['hitWhat'] == self.HIT_WATER: + avatar.loop('neutral') + self.splash.setPos(task.info['toon'].getPos()) + self.splash.setScale(2) + self.splash.play() + base.playSfx(self.sndHitWater) + task.info['toon'].setHpr(task.info['hRot'], 0, 0) + self.__somebodyWon(task.info['avId']) + elif task.info['hitWhat'] == self.HIT_TOWER: + toon = task.info['toon'] + pos = toon.getPos() + ttVec = Vec3(pos - self.towerPos) + ttVec.setZ(0) + ttVec.normalize() + h = rad2Deg(math.asin(ttVec[0])) + toon.setHpr(h, 94, 0) + deltaZ = TOWER_HEIGHT - BUCKET_HEIGHT + sf = min(max(pos[2] - BUCKET_HEIGHT, 0), deltaZ) / deltaZ + hitPos = pos + Point3(ttVec * (0.75 * sf)) + toon.setPos(hitPos) + hitPos.setZ(hitPos[2] - 1.0) + s = Sequence(Wait(0.5), toon.posInterval(duration=LAND_TIME - 0.5, pos=hitPos, blendType='easeIn')) + self.toonIntervalDict[task.info['avId']] = s + s.start() + avatar.iPos() + avatar.pose('slip-forward', 25) + base.playSfx(self.sndHitTower) + elif task.info['hitWhat'] == self.HIT_GROUND: + task.info['toon'].setP(render, -150.0) + self.dustCloud.setPos(task.info['toon'], 0, 0, -2.5) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + base.playSfx(self.sndHitGround) + avatar.setPlayRate(2.0, 'run') + avatar.loop('run') + return Task.done + return Task.cont + + def __flySequenceDoneTask(self, task): + self.airborneToons -= 1 + if self.gameFSM.getCurrentState().getName() == 'waitForToonsToLand': + if 0 == self.airborneToons: + self.gameOver() + else: + self.__loadToonInCannon(task.info['avId']) + if task.info['avId'] == self.localAvId: + self.gameFSM.request('aim') + return Task.done + + def __startRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + taskMgr.add(self.__updateRewardCountdown, self.REWARD_COUNTDOWN_TASK) + + def __killRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + + def __updateRewardCountdown(self, task): + if not hasattr(self, 'rewardPanel'): + return Task.cont + curTime = self.getCurrentGameTime() + if self.clockStopTime is not None: + if self.clockStopTime < curTime: + self.__killRewardCountdown() + curTime = self.clockStopTime + score = int(self.scoreMult * CannonGameGlobals.calcScore(curTime) + 0.5) + if not hasattr(task, 'curScore'): + task.curScore = score + self.rewardPanel['text'] = str(score) + if task.curScore != score: + if hasattr(self, 'jarIval'): + self.jarIval.finish() + s = self.rewardPanel.getScale() + self.jarIval = Parallel(Sequence(self.rewardPanel.scaleInterval(0.15, s * 3.0 / 4.0, blendType='easeOut'), self.rewardPanel.scaleInterval(0.15, s, blendType='easeIn')), SoundInterval(self.sndRewardTick), name='cannonGameRewardJarThrob') + self.jarIval.start() + task.curScore = score + return Task.cont + + def __startIntro(self): + self.T_WATER = 1 + self.T_WATER2LONGVIEW = 1 + self.T_LONGVIEW = 1 + self.T_LONGVIEW2TOONHEAD = 2 + self.T_TOONHEAD = 2 + self.T_TOONHEAD2CANNONBACK = 2 + taskLookInWater = Task(self.__taskLookInWater) + taskPullBackFromWater = Task(self.__taskPullBackFromWater) + taskFlyUpToToon = Task(self.__flyUpToToon) + taskFlyToBackOfCannon = Task(self.__flyToBackOfCannon) + commonData = {} + taskLookInWater.data = commonData + taskPullBackFromWater.data = commonData + taskFlyUpToToon.data = commonData + taskFlyToBackOfCannon.data = commonData + introTask = Task.sequence(taskLookInWater, Task.pause(self.T_WATER), taskPullBackFromWater, Task.pause(self.T_WATER2LONGVIEW + self.T_LONGVIEW), taskFlyUpToToon, Task.pause(self.T_LONGVIEW2TOONHEAD + self.T_TOONHEAD), taskFlyToBackOfCannon) + taskMgr.add(introTask, self.INTRO_TASK_NAME) + + def __stopIntro(self): + taskMgr.remove(self.INTRO_TASK_NAME) + taskMgr.remove(self.INTRO_TASK_NAME_CAMERA_LERP) + camera.wrtReparentTo(render) + + def __spawnCameraLookAtLerp(self, targetPos, targetLookAt, duration): + oldPos = camera.getPos() + oldHpr = camera.getHpr() + camera.setPos(targetPos) + camera.lookAt(targetLookAt) + targetHpr = camera.getHpr() + camera.setPos(oldPos) + camera.setHpr(oldHpr) + camera.lerpPosHpr(Point3(targetPos), targetHpr, duration, blendType='easeInOut', task=self.INTRO_TASK_NAME_CAMERA_LERP) + + def __taskLookInWater(self, task): + task.data['cannonCenter'] = Point3(0, CANNON_Y, CANNON_Z) + task.data['towerWaterCenter'] = Point3(self.towerPos + Point3(0, 0, TOWER_HEIGHT)) + task.data['vecTowerToCannon'] = Point3(task.data['cannonCenter'] - task.data['towerWaterCenter']) + vecAwayFromCannons = Vec3(Point3(0, 0, 0) - task.data['vecTowerToCannon']) + vecAwayFromCannons.setZ(0.0) + vecAwayFromCannons.normalize() + camLoc = Point3(vecAwayFromCannons * 20) + Point3(0, 0, 20) + camLoc = camLoc + task.data['towerWaterCenter'] + camera.setPos(camLoc) + camera.lookAt(task.data['towerWaterCenter']) + task.data['vecAwayFromCannons'] = vecAwayFromCannons + return Task.done + + def __taskPullBackFromWater(self, task): + camLoc = Point3(task.data['vecAwayFromCannons'] * 40) + Point3(0, 0, 20) + camLoc = camLoc + task.data['towerWaterCenter'] + lookAt = task.data['cannonCenter'] + self.__spawnCameraLookAtLerp(camLoc, lookAt, self.T_WATER2LONGVIEW) + return Task.done + + def __flyUpToToon(self, task): + headPos = self.toonHeadDict[self.localAvId].getPos(render) + camLoc = headPos + Point3(0, 5, 0) + lookAt = Point3(headPos) + self.__spawnCameraLookAtLerp(camLoc, lookAt, self.T_LONGVIEW2TOONHEAD) + return Task.done + + def __flyToBackOfCannon(self, task): + lerpNode = hidden.attachNewNode('CannonGameCameraLerpNode') + lerpNode.reparentTo(render) + lerpNode.setPos(self.cannonLocationDict[self.localAvId] + Point3(0, 1, 0)) + relCamPos = camera.getPos(lerpNode) + relCamHpr = camera.getHpr(lerpNode) + startRotation = lerpNode.getHpr() + endRotation = Point3(-180, 0, 0) + lerpNode.setHpr(endRotation) + camera.setPos(self.__getCameraPositionBehindCannon()) + endPos = camera.getPos(lerpNode) + lerpNode.setHpr(startRotation) + camera.reparentTo(lerpNode) + camera.setPos(relCamPos) + camera.setHpr(relCamHpr) + lerpNode.lerpHpr(endRotation, self.T_TOONHEAD2CANNONBACK, blendType='easeInOut', task=self.INTRO_TASK_NAME_CAMERA_LERP) + camera.lerpPos(endPos, self.T_TOONHEAD2CANNONBACK, blendType='easeInOut', task=self.INTRO_TASK_NAME_CAMERA_LERP) + return Task.done diff --git a/toontown/minigame/DistributedCannonGameAI.py b/toontown/minigame/DistributedCannonGameAI.py new file mode 100644 index 0000000..5350f14 --- /dev/null +++ b/toontown/minigame/DistributedCannonGameAI.py @@ -0,0 +1,129 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import CannonGameGlobals + +class DistributedCannonGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCannonGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + if not config.GetBool('endless-cannon-game', 0): + taskMgr.doMethodLater(CannonGameGlobals.GameTime, self.timerExpired, self.taskName('gameTimer')) + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameOver() + return Task.done + + def __playing(self): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() == 'play' + + def _checkCannonRange(self, zRot, angle, avId): + outOfRange = 0 + if zRot < CannonGameGlobals.CANNON_ROTATION_MIN or zRot > CannonGameGlobals.CANNON_ROTATION_MAX: + self.air.writeServerEvent('suspicious', avId, 'Cannon game z-rotation out of range: %s' % zRot) + self.notify.warning('av %s cannon z-rotation out of range: %s' % (avId, zRot)) + outOfRange = 1 + if angle < CannonGameGlobals.CANNON_ANGLE_MIN or angle > CannonGameGlobals.CANNON_ANGLE_MAX: + self.air.writeServerEvent('suspicious', avId, 'Cannon game vertical angle out of range: %s' % angle) + self.notify.warning('av %s cannon vertical angle out of range: %s' % (avId, angle)) + outOfRange = 1 + return outOfRange + + def setCannonPosition(self, zRot, angle): + if not self.__playing(): + self.notify.debug('ignoring setCannonPosition message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonPosition: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + if self._checkCannonRange(zRot, angle, avId): + return + self.sendUpdate('updateCannonPosition', [avId, zRot, angle]) + + def setCannonLit(self, zRot, angle): + if not self.__playing(): + self.notify.debug('ignoring setCannonLit message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setCannonLit: ' + str(avId) + ': zRot=' + str(zRot) + ', angle=' + str(angle)) + if self._checkCannonRange(zRot, angle, avId): + return + fireTime = self.getCurrentGameTime() + CannonGameGlobals.FUSE_TIME + self.sendUpdate('setCannonWillFire', [avId, + fireTime, + zRot, + angle]) + + def setToonWillLandInWater(self, landTime): + if not self.__playing(): + self.notify.debug('ignoring setToonWillLandInWater message') + return + senderAvId = self.air.getAvatarIdFromSender() + score = CannonGameGlobals.calcScore(landTime) + for avId in self.avIdList: + self.scoreDict[avId] = score + + self.notify.debug('setToonWillLandInWater: time=%s, score=%s' % (landTime, score)) + taskMgr.remove(self.taskName('gameTimer')) + delay = max(0, landTime - self.getCurrentGameTime()) + taskMgr.doMethodLater(delay, self.toonLandedInWater, self.taskName('game-over')) + self.sendUpdate('announceToonWillLandInWater', [senderAvId, landTime]) + + def toonLandedInWater(self, task): + self.notify.debug('toonLandedInWater') + if self.__playing(): + self.gameOver() + return Task.done + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + taskMgr.remove(self.taskName('game-over')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedCatchGame.py b/toontown/minigame/DistributedCatchGame.py new file mode 100644 index 0000000..77fa047 --- /dev/null +++ b/toontown/minigame/DistributedCatchGame.py @@ -0,0 +1,956 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.interval.IntervalGlobal import * +from OrthoWalk import * +from direct.showbase.PythonUtil import Functor, bound, lineupPos, lerp +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import TTLocalizer +import CatchGameGlobals +from direct.task.Task import Task +from toontown.toon import Toon +from toontown.suit import Suit +import MinigameAvatarScorePanel +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +import CatchGameToonSD +import Trajectory +import math +from direct.distributed import DistributedSmoothNode +from direct.showbase.RandomNumGen import RandomNumGen +import MinigameGlobals +from toontown.toon import ToonDNA +from toontown.suit import SuitDNA +from CatchGameGlobals import DropObjectTypes +from CatchGameGlobals import Name2DropObjectType +from DropPlacer import * +from DropScheduler import * + +class DistributedCatchGame(DistributedMinigame): + DropTaskName = 'dropSomething' + EndGameTaskName = 'endCatchGame' + SuitWalkTaskName = 'catchGameSuitWalk' + DropObjectPlurals = {'apple': TTLocalizer.CatchGameApples, + 'orange': TTLocalizer.CatchGameOranges, + 'pear': TTLocalizer.CatchGamePears, + 'coconut': TTLocalizer.CatchGameCoconuts, + 'watermelon': TTLocalizer.CatchGameWatermelons, + 'pineapple': TTLocalizer.CatchGamePineapples, + 'anvil': TTLocalizer.CatchGameAnvils} + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.setUsesSmoothing() + self.setUsesLookAround() + + def getTitle(self): + return TTLocalizer.CatchGameTitle + + def getInstructions(self): + return TTLocalizer.CatchGameInstructions % {'fruit': self.DropObjectPlurals[self.fruitName], + 'badThing': self.DropObjectPlurals['anvil']} + + def getMaxDuration(self): + return CatchGameGlobals.GameDuration + 5 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + groundModels = ['phase_4/models/minigames/treehouse_2players', + 'phase_4/models/minigames/treehouse_2players', + 'phase_4/models/minigames/treehouse_3players', + 'phase_4/models/minigames/treehouse_4players'] + index = self.getNumPlayers() - 1 + self.ground = loader.loadModel(groundModels[index]) + self.ground.setHpr(180, -90, 0) + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropObjModels = {} + for objType in DropObjectTypes: + if objType.name not in ['anvil', self.fruitName]: + continue + model = loader.loadModel(objType.modelPath) + self.dropObjModels[objType.name] = model + modelScales = {'apple': 0.7, + 'orange': 0.7, + 'pear': 0.5, + 'coconut': 0.7, + 'watermelon': 0.6, + 'pineapple': 0.45} + if modelScales.has_key(objType.name): + model.setScale(modelScales[objType.name]) + if objType == Name2DropObjectType['pear']: + model.setZ(-.6) + if objType == Name2DropObjectType['coconut']: + model.setP(180) + if objType == Name2DropObjectType['watermelon']: + model.setH(135) + model.setZ(-.5) + if objType == Name2DropObjectType['pineapple']: + model.setZ(-1.7) + if objType == Name2DropObjectType['anvil']: + model.setZ(-self.ObjRadius) + model.flattenMedium() + + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.mid') + self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.mp3') + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.mp3') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.mp3') + self.toonSDs = {} + avId = self.localAvId + toonSD = CatchGameToonSD.CatchGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + if self.WantSuits: + suitTypes = ['f', + 'tm', + 'pp', + 'dt'] + self.suits = [] + for type in suitTypes: + suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(type) + suit.setDNA(d) + suit.pose('walk', 0) + self.suits.append(suit) + + self.__textGen = TextNode('ringGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + self.introMovie = self.getIntroMovie() + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + self.introMovie.finish() + del self.introMovie + del self.__textGen + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + for suit in self.suits: + suit.reparentTo(hidden) + suit.delete() + + del self.suits + self.ground.removeNode() + del self.ground + self.dropShadow.removeNode() + del self.dropShadow + for model in self.dropObjModels.values(): + model.removeNode() + + del self.dropObjModels + del self.music + del self.sndGoodCatch + del self.sndOof + del self.sndAnvilLand + del self.sndPerfect + + def getObjModel(self, objName): + return self.dropObjModels[objName].copyTo(hidden) + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def calcDifficultyConstants(self, difficulty, numPlayers): + ToonSpeedRange = [16.0, 25.0] + self.ToonSpeed = lerp(ToonSpeedRange[0], ToonSpeedRange[1], difficulty) + self.SuitSpeed = self.ToonSpeed / 2.0 + self.SuitPeriodRange = [lerp(5.0, 3.0, self.getDifficulty()), lerp(15.0, 8.0, self.getDifficulty())] + + def scaledDimensions(widthHeight, scale): + w, h = widthHeight + return [math.sqrt(scale * w * w), math.sqrt(scale * h * h)] + + BaseStageDimensions = [20, 15] + areaScales = [1.0, + 1.0, + 3.0 / 2, + 4.0 / 2] + self.StageAreaScale = areaScales[numPlayers - 1] + self.StageLinearScale = math.sqrt(self.StageAreaScale) + self.notify.debug('StageLinearScale: %s' % self.StageLinearScale) + self.StageDimensions = scaledDimensions(BaseStageDimensions, self.StageAreaScale) + self.notify.debug('StageDimensions: %s' % self.StageDimensions) + self.StageHalfWidth = self.StageDimensions[0] / 2.0 + self.StageHalfHeight = self.StageDimensions[1] / 2.0 + MOHs = [24] * 2 + [26, 28] + self.MinOffscreenHeight = MOHs[self.getNumPlayers() - 1] + distance = math.sqrt(self.StageDimensions[0] * self.StageDimensions[0] + self.StageDimensions[1] * self.StageDimensions[1]) + distance /= self.StageLinearScale + if self.DropPlacerType == PathDropPlacer: + distance /= 1.5 + ToonRunDuration = distance / self.ToonSpeed + offScreenOnScreenRatio = 1.0 + fraction = 1.0 / 3 * 0.85 + self.BaselineOnscreenDropDuration = ToonRunDuration / (fraction * (1.0 + offScreenOnScreenRatio)) + self.notify.debug('BaselineOnscreenDropDuration=%s' % self.BaselineOnscreenDropDuration) + self.OffscreenTime = offScreenOnScreenRatio * self.BaselineOnscreenDropDuration + self.notify.debug('OffscreenTime=%s' % self.OffscreenTime) + self.BaselineDropDuration = self.BaselineOnscreenDropDuration + self.OffscreenTime + self.MaxDropDuration = self.BaselineDropDuration + self.DropPeriod = self.BaselineDropDuration / 2.0 + scaledNumPlayers = (numPlayers - 1.0) * 0.75 + 1.0 + self.DropPeriod /= scaledNumPlayers + typeProbs = {'fruit': 3, + 'anvil': 1} + probSum = reduce(lambda x, y: x + y, typeProbs.values()) + for key in typeProbs.keys(): + typeProbs[key] = float(typeProbs[key]) / probSum + + scheduler = DropScheduler(CatchGameGlobals.GameDuration, self.FirstDropDelay, self.DropPeriod, self.MaxDropDuration, self.FasterDropDelay, self.FasterDropPeriodMult) + self.totalDrops = 0 + while not scheduler.doneDropping(): + scheduler.stepT() + self.totalDrops += 1 + + self.numFruits = int(self.totalDrops * typeProbs['fruit']) + self.numAnvils = int(self.totalDrops - self.numFruits) + + def getNumPlayers(self): + return self.numPlayers + + def defineConstants(self): + self.notify.debug('defineConstants') + self.DropPlacerType = RegionDropPlacer + fruits = {ToontownGlobals.ToontownCentral: 'apple', + ToontownGlobals.DonaldsDock: 'orange', + ToontownGlobals.DaisyGardens: 'pear', + ToontownGlobals.MinniesMelodyland: 'coconut', + ToontownGlobals.TheBrrrgh: 'watermelon', + ToontownGlobals.DonaldsDreamland: 'pineapple'} + self.fruitName = fruits[self.getSafezoneId()] + self.ShowObjSpheres = 0 + self.ShowToonSpheres = 0 + self.ShowSuitSpheres = 0 + self.PredictiveSmoothing = 1 + self.UseGravity = 1 + self.TrickShadows = 1 + self.WantSuits = 1 + self.FirstDropDelay = 0.5 + self.FasterDropDelay = int(2.0 / 3 * CatchGameGlobals.GameDuration) + self.notify.debug('will start dropping fast after %s seconds' % self.FasterDropDelay) + self.FasterDropPeriodMult = 0.5 + self.calcDifficultyConstants(self.getDifficulty(), self.getNumPlayers()) + self.notify.debug('ToonSpeed: %s' % self.ToonSpeed) + self.notify.debug('total drops: %s' % self.totalDrops) + self.notify.debug('numFruits: %s' % self.numFruits) + self.notify.debug('numAnvils: %s' % self.numAnvils) + self.ObjRadius = 1.0 + dropGridDimensions = [[5, 5], + [5, 5], + [6, 6], + [7, 7]] + self.DropRows, self.DropColumns = dropGridDimensions[self.getNumPlayers() - 1] + self.cameraPosTable = [[0, -29.36, 28.17]] * 2 + [[0, -32.87, 30.43], [0, -35.59, 32.1]] + self.cameraHpr = [0, -35, 0] + self.CameraPosHpr = self.cameraPosTable[self.getNumPlayers() - 1] + self.cameraHpr + for objType in DropObjectTypes: + self.notify.debug('*** Object Type: %s' % objType.name) + objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration + self.notify.debug('onscreenDuration=%s' % objType.onscreenDuration) + v_0 = 0.0 + t = objType.onscreenDuration + x_0 = self.MinOffscreenHeight + x = 0.0 + g = 2.0 * (x - x_0 - v_0 * t) / (t * t) + self.notify.debug('gravity=%s' % g) + objType.trajectory = Trajectory.Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.Trajectory.gravity)) + objType.fallDuration = objType.onscreenDuration + self.OffscreenTime + + def grid2world(self, column, row): + x = column / float(self.DropColumns - 1) + y = row / float(self.DropRows - 1) + x = x * 2.0 - 1.0 + y = y * 2.0 - 1.0 + x *= self.StageHalfWidth + y *= self.StageHalfHeight + return (x, y) + + def showPosts(self): + self.hidePosts() + self.posts = [Toon.Toon(), + Toon.Toon(), + Toon.Toon(), + Toon.Toon()] + for i in range(len(self.posts)): + toon = self.posts[i] + toon.setDNA(base.localAvatar.getStyle()) + toon.reparentTo(render) + x = self.StageHalfWidth + y = self.StageHalfHeight + if i > 1: + x = -x + if i % 2: + y = -y + toon.setPos(x, y, 0) + + def hidePosts(self): + if hasattr(self, 'posts'): + for toon in self.posts: + toon.removeNode() + + del self.posts + + def showDropGrid(self): + self.hideDropGrid() + self.dropMarkers = [] + print 'dropRows: %s' % self.DropRows + print 'dropCols: %s' % self.DropColumns + for row in range(self.DropRows): + self.dropMarkers.append([]) + rowList = self.dropMarkers[row] + for column in range(self.DropColumns): + toon = Toon.Toon() + toon.setDNA(base.localAvatar.getStyle()) + toon.reparentTo(render) + toon.setScale(1.0 / 3) + x, y = self.grid2world(column, row) + toon.setPos(x, y, 0) + rowList.append(toon) + + def hideDropGrid(self): + if hasattr(self, 'dropMarkers'): + for row in self.dropMarkers: + for marker in row: + marker.removeNode() + + del self.dropMarkers + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.ground.reparentTo(render) + self.scorePanels = [] + camera.reparentTo(render) + camera.setPosHpr(*self.CameraPosHpr) + lt = base.localAvatar + lt.reparentTo(render) + self.__placeToon(self.localAvId) + lt.setSpeed(0, 0) + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + self.orthoWalk.stop() + radius = 0.7 + handler = CollisionHandlerEvent() + handler.setInPattern('ltCatch%in') + self.ltLegsCollNode = CollisionNode('catchLegsCollNode') + self.ltLegsCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + self.ltHeadCollNode = CollisionNode('catchHeadCollNode') + self.ltHeadCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + self.ltLHandCollNode = CollisionNode('catchLHandCollNode') + self.ltLHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + self.ltRHandCollNode = CollisionNode('catchRHandCollNode') + self.ltRHandCollNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + legsCollNodepath = lt.attachNewNode(self.ltLegsCollNode) + legsCollNodepath.hide() + head = base.localAvatar.getHeadParts().getPath(2) + headCollNodepath = head.attachNewNode(self.ltHeadCollNode) + headCollNodepath.hide() + lHand = base.localAvatar.getLeftHands()[0] + lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode) + lHandCollNodepath.hide() + rHand = base.localAvatar.getRightHands()[0] + rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode) + rHandCollNodepath.hide() + lt.cTrav.addCollider(legsCollNodepath, handler) + lt.cTrav.addCollider(headCollNodepath, handler) + lt.cTrav.addCollider(lHandCollNodepath, handler) + lt.cTrav.addCollider(lHandCollNodepath, handler) + if self.ShowToonSpheres: + legsCollNodepath.show() + headCollNodepath.show() + lHandCollNodepath.show() + rHandCollNodepath.show() + self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius)) + self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius)) + self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.toonCollNodes = [legsCollNodepath, + headCollNodepath, + lHandCollNodepath, + rHandCollNodepath] + if self.PredictiveSmoothing: + DistributedSmoothNode.activateSmoothing(1, 1) + self.introMovie.start() + + def offstage(self): + self.notify.debug('offstage') + DistributedSmoothNode.activateSmoothing(1, 0) + self.introMovie.finish() + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + self.hidePosts() + self.hideDropGrid() + for collNode in self.toonCollNodes: + while collNode.node().getNumSolids(): + collNode.node().removeSolid(0) + + base.localAvatar.cTrav.removeCollider(collNode) + + del self.toonCollNodes + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + self.ground.reparentTo(hidden) + DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + idx = self.avIdList.index(avId) + x = lineupPos(idx, self.numPlayers, 4.0) + toon.setPos(x, 0, 0) + toon.setHpr(180, 0, 0) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + headCollNP = base.localAvatar.find('**/catchHeadCollNode') + if headCollNP and not headCollNP.isEmpty(): + headCollNP.hide() + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toonSD = CatchGameToonSD.CatchGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + toonSD.enter() + toonSD.fsm.request('normal') + toon.startSmooth() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.introMovie.finish() + camera.reparentTo(render) + camera.setPosHpr(*self.CameraPosHpr) + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.orthoWalk.start() + for suit in self.suits: + suitCollSphere = CollisionSphere(0, 0, 0, 1.0) + suit.collSphereName = 'suitCollSphere%s' % self.suits.index(suit) + suitCollSphere.setTangible(0) + suitCollNode = CollisionNode(self.uniqueName(suit.collSphereName)) + suitCollNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + suitCollNode.addSolid(suitCollSphere) + suit.collNodePath = suit.attachNewNode(suitCollNode) + suit.collNodePath.hide() + if self.ShowSuitSpheres: + suit.collNodePath.show() + self.accept(self.uniqueName('enter' + suit.collSphereName), self.handleSuitCollision) + + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(0.75 - spacing * (self.numPlayers - 1 - i), 0.0, 0.85) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + self.fruitsCaught = 0 + self.droppedObjCaught = {} + self.dropIntervals = {} + self.droppedObjNames = [] + self.dropSchedule = [] + self.numItemsDropped = 0 + self.scheduleDrops() + self.startDropTask() + if self.WantSuits: + self.startSuitWalkTask() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(CatchGameGlobals.GameDuration) + self.timer.countdown(CatchGameGlobals.GameDuration, self.timerExpired) + self.timer.setTransparency(1) + self.timer.setColorScale(1, 1, 1, 0.75) + base.playMusic(self.music, looping=0, volume=0.9) + + def exitPlay(self): + self.stopDropTask() + self.stopSuitWalkTask() + if hasattr(self, 'perfectIval'): + self.perfectIval.pause() + del self.perfectIval + self.timer.stop() + self.timer.destroy() + del self.timer + self.music.stop() + for suit in self.suits: + self.ignore(self.uniqueName('enter' + suit.collSphereName)) + suit.collNodePath.removeNode() + + for ival in self.dropIntervals.values(): + ival.finish() + + del self.dropIntervals + del self.droppedObjNames + del self.droppedObjCaught + del self.dropSchedule + taskMgr.remove(self.EndGameTaskName) + + def timerExpired(self): + pass + + def __handleCatch(self, objNum): + self.notify.debug('catch: %s' % objNum) + self.showCatch(self.localAvId, objNum) + objName = self.droppedObjNames[objNum] + objTypeId = CatchGameGlobals.Name2DOTypeId[objName] + self.sendUpdate('claimCatch', [objNum, objTypeId]) + self.finishDropInterval(objNum) + + def showCatch(self, avId, objNum): + isLocal = avId == self.localAvId + objName = self.droppedObjNames[objNum] + objType = Name2DropObjectType[objName] + if objType.good: + if not self.droppedObjCaught.has_key(objNum): + if isLocal: + base.playSfx(self.sndGoodCatch) + fruit = self.getObjModel(objName) + toon = self.getAvatar(avId) + rHand = toon.getRightHands()[0] + self.toonSDs[avId].eatFruit(fruit, rHand) + else: + self.toonSDs[avId].fsm.request('fallForward') + self.droppedObjCaught[objNum] = 1 + + def setObjectCaught(self, avId, objNum): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId)) + return + isLocal = avId == self.localAvId + if not isLocal: + self.notify.debug('AI: avatar %s caught %s' % (avId, objNum)) + self.finishDropInterval(objNum) + self.showCatch(avId, objNum) + objName = self.droppedObjNames[objNum] + if Name2DropObjectType[objName].good: + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + self.fruitsCaught += 1 + + def finishDropInterval(self, objNum): + if self.dropIntervals.has_key(objNum): + self.dropIntervals[objNum].finish() + + def scheduleDrops(self): + self.droppedObjNames = [self.fruitName] * self.numFruits + ['anvil'] * self.numAnvils + self.randomNumGen.shuffle(self.droppedObjNames) + dropPlacer = self.DropPlacerType(self, self.getNumPlayers(), self.droppedObjNames) + while not dropPlacer.doneDropping(): + self.dropSchedule.append(dropPlacer.getNextDrop()) + + def startDropTask(self): + taskMgr.add(self.dropTask, self.DropTaskName) + + def stopDropTask(self): + taskMgr.remove(self.DropTaskName) + + def dropTask(self, task): + curT = self.getCurrentGameTime() + while self.dropSchedule[0][0] <= curT: + drop = self.dropSchedule[0] + self.dropSchedule = self.dropSchedule[1:] + dropTime, objName, dropCoords = drop + objNum = self.numItemsDropped + lastDrop = len(self.dropSchedule) == 0 + x, y = self.grid2world(*dropCoords) + dropIval = self.getDropIval(x, y, objName, objNum) + + def cleanup(self = self, objNum = objNum, lastDrop = lastDrop): + del self.dropIntervals[objNum] + if lastDrop: + self.sendUpdate('reportDone') + + dropIval.append(Func(cleanup)) + self.dropIntervals[objNum] = dropIval + self.numItemsDropped += 1 + dropIval.start(curT - dropTime) + if lastDrop: + return Task.done + + return Task.cont + + def setEveryoneDone(self): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring setEveryoneDone msg') + return + self.notify.debug('setEveryoneDone') + + def endGame(task, self = self): + if not CatchGameGlobals.EndlessGame: + self.gameOver() + return Task.done + + self.notify.debug('num fruits: %s' % self.numFruits) + self.notify.debug('num catches: %s' % self.fruitsCaught) + self.timer.hide() + if self.fruitsCaught >= self.numFruits: + self.notify.debug('perfect game!') + perfectTextSubnode = hidden.attachNewNode(self.__genText(TTLocalizer.CatchGamePerfect)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(endGame, None)) + soundTrack = SoundInterval(self.sndPerfect) + self.perfectIval = Parallel(textTrack, soundTrack) + self.perfectIval.start() + else: + taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) + return + + def getDropIval(self, x, y, dropObjName, num): + objType = Name2DropObjectType[dropObjName] + dropNode = hidden.attachNewNode('catchDropNode%s' % num) + dropNode.setPos(x, y, 0) + shadow = self.dropShadow.copyTo(dropNode) + shadow.setZ(0.2) + shadow.setColor(1, 1, 1, 1) + object = self.getObjModel(dropObjName) + object.reparentTo(dropNode) + if dropObjName in ['watermelon', 'anvil']: + objH = object.getH() + absDelta = {'watermelon': 12, + 'anvil': 15}[dropObjName] + delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta + newH = objH + delta + else: + newH = self.randomNumGen.random() * 360.0 + object.setH(newH) + sphereName = 'FallObj%s' % num + radius = self.ObjRadius + if objType.good: + radius *= lerp(1.0, 1.3, self.getDifficulty()) + collSphere = CollisionSphere(0, 0, 0, radius) + collSphere.setTangible(0) + collNode = CollisionNode(sphereName) + collNode.setCollideMask(ToontownGlobals.CatchGameBitmask) + collNode.addSolid(collSphere) + collNodePath = object.attachNewNode(collNode) + collNodePath.hide() + if self.ShowObjSpheres: + collNodePath.show() + catchEventName = 'ltCatch' + sphereName + + def eatCollEntry(forward, collEntry): + forward() + + self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, num))) + + def cleanup(self = self, dropNode = dropNode, num = num, event = catchEventName): + self.ignore(event) + dropNode.removeNode() + + duration = objType.fallDuration + onscreenDuration = objType.onscreenDuration + dropHeight = self.MinOffscreenHeight + targetShadowScale = 0.3 + if self.TrickShadows: + intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration) + shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0)) + shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale)) + else: + shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0) + targetShadowAlpha = 0.4 + shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0)) + shadowIval = Parallel(shadowScaleIval, shadowAlphaIval) + if self.UseGravity: + + def setObjPos(t, objType = objType, object = object): + z = objType.trajectory.calcZ(t) + object.setZ(z) + + setObjPos(0) + dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration) + else: + startPos = Point3(0, 0, self.MinOffscreenHeight) + object.setPos(startPos) + dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn') + ival = Sequence(Func(Functor(dropNode.reparentTo, render)), Parallel(Sequence(WaitInterval(self.OffscreenTime), dropIval), shadowIval), Func(cleanup), name='drop%s' % num) + landSound = None + if objType == Name2DropObjectType['anvil']: + landSound = self.sndAnvilLand + if landSound: + ival.append(SoundInterval(landSound)) + return ival + + def startSuitWalkTask(self): + ival = Parallel(name='catchGameMetaSuitWalk') + rng = RandomNumGen(self.randomNumGen) + delay = 0.0 + while delay < CatchGameGlobals.GameDuration: + delay += lerp(self.SuitPeriodRange[0], self.SuitPeriodRange[0], rng.random()) + walkIval = Sequence(name='catchGameSuitWalk') + walkIval.append(Wait(delay)) + + def pickY(self = self, rng = rng): + return lerp(-self.StageHalfHeight, self.StageHalfHeight, rng.random()) + + m = [2.5, + 2.5, + 2.3, + 2.1][self.getNumPlayers() - 1] + startPos = Point3(-(self.StageHalfWidth * m), pickY(), 0) + stopPos = Point3(self.StageHalfWidth * m, pickY(), 0) + if rng.choice([0, 1]): + startPos, stopPos = stopPos, startPos + walkIval.append(self.getSuitWalkIval(startPos, stopPos, rng)) + ival.append(walkIval) + + ival.start() + self.suitWalkIval = ival + + def stopSuitWalkTask(self): + self.suitWalkIval.finish() + del self.suitWalkIval + + def getSuitWalkIval(self, startPos, stopPos, rng): + data = {} + lerpNP = render.attachNewNode('catchGameSuitParent') + + def setup(self = self, startPos = startPos, stopPos = stopPos, data = data, lerpNP = lerpNP, rng = rng): + if len(self.suits) == 0: + return + suit = rng.choice(self.suits) + data['suit'] = suit + self.suits.remove(suit) + suit.reparentTo(lerpNP) + suit.loop('walk') + suit.setPlayRate(self.SuitSpeed / ToontownGlobals.SuitWalkSpeed, 'walk') + suit.setPos(0, 0, 0) + lerpNP.setPos(startPos) + suit.lookAt(stopPos) + + def cleanup(self = self, data = data, lerpNP = lerpNP): + if data.has_key('suit'): + suit = data['suit'] + suit.reparentTo(hidden) + self.suits.append(suit) + lerpNP.removeNode() + + distance = Vec3(stopPos - startPos).length() + duration = distance / self.SuitSpeed + ival = Sequence(FunctionInterval(setup), LerpPosInterval(lerpNP, duration, stopPos), FunctionInterval(cleanup)) + return ival + + def handleSuitCollision(self, collEntry): + self.toonSDs[self.localAvId].fsm.request('fallBack') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('hitBySuit', [self.localAvId, timestamp]) + + def hitBySuit(self, avId, timestamp): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + toon = self.getAvatar(avId) + if toon == None: + return + self.notify.debug('avatar %s hit by a suit' % avId) + if avId != self.localAvId: + self.toonSDs[avId].fsm.request('fallBack') + return + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def initOrthoWalk(self): + self.notify.debug('startOrthoWalk') + + def doCollisions(oldPos, newPos, self = self): + x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth) + y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight) + newPos.setX(x) + newPos.setY(y) + return newPos + + orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) + + def destroyOrthoWalk(self): + self.notify.debug('destroyOrthoWalk') + self.orthoWalk.destroy() + del self.orthoWalk + + def getIntroMovie(self): + locNode = self.ground.find('**/locator_tree') + treeNode = locNode.attachNewNode('treeNode') + treeNode.setHpr(render, 0, 0, 0) + + def cleanupTree(treeNode = treeNode): + treeNode.removeNode() + + initialCamPosHpr = (-0.21, + -19.56, + 13.94, + 0.0, + 26.57, + 0.0) + suitViewCamPosHpr = (0, -11.5, 13, 0, -35, 0) + finalCamPosHpr = self.CameraPosHpr + cameraIval = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, treeNode, *initialCamPosHpr), WaitInterval(4.0), LerpPosHprInterval(camera, 2.0, Point3(*suitViewCamPosHpr[:3]), Point3(*suitViewCamPosHpr[3:]), blendType='easeInOut', name='lerpToSuitView'), WaitInterval(4.0), LerpPosHprInterval(camera, 3.0, Point3(*finalCamPosHpr[:3]), Point3(*finalCamPosHpr[3:]), blendType='easeInOut', name='lerpToPlayView')) + + def getIntroToon(toonProperties, parent, pos): + toon = Toon.Toon() + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(*toonProperties) + toon.setDNA(dna) + toon.reparentTo(parent) + toon.setPos(*pos) + toon.setH(180) + toon.startBlink() + return toon + + def cleanupIntroToon(toon): + toon.detachNode() + toon.stopBlink() + toon.delete() + + def getThrowIval(toon, hand, object, leftToon, isAnvil = 0): + anim = 'catch-intro-throw' + grabFrame = 12 + fullSizeFrame = 30 + framePeriod = 1.0 / toon.getFrameRate(anim) + objScaleDur = (fullSizeFrame - grabFrame) * framePeriod + releaseFrame = 35 + trajDuration = 1.6 + trajDistance = 4 + if leftToon: + releaseFrame = 34 + trajDuration = 1.0 + trajDistance = 1 + animIval = ActorInterval(toon, anim, loop=0) + + def getThrowDest(object = object, offset = trajDistance): + dest = object.getPos(render) + dest += Point3(0, -offset, 0) + dest.setZ(0) + return dest + + if leftToon: + trajIval = ProjectileInterval(object, startVel=Point3(0, 0, 0), duration=trajDuration) + else: + trajIval = ProjectileInterval(object, endPos=getThrowDest, duration=trajDuration) + trajIval = Sequence(Func(object.wrtReparentTo, render), trajIval, Func(object.wrtReparentTo, hidden)) + if isAnvil: + trajIval.append(SoundInterval(self.sndAnvilLand)) + objIval = Track((grabFrame * framePeriod, Sequence(Func(object.reparentTo, hand), Func(object.setPosHpr, 0.05, -.13, 0.62, 0, 0, 336.8), LerpScaleInterval(object, objScaleDur, 1.0, startScale=0.1, blendType='easeInOut'))), (releaseFrame * framePeriod, trajIval)) + + def cleanup(object = object): + object.reparentTo(hidden) + object.removeNode() + + throwIval = Sequence(Parallel(animIval, objIval), Func(cleanup)) + return throwIval + + tY = -4.0 + tZ = 19.5 + props = ['css', + 'md', + 'm', + 'f', + 9, + 0, + 9, + 9, + 13, + 5, + 11, + 5, + 8, + 7] + leftToon = getIntroToon(props, treeNode, [-2.3, tY, tZ]) + props = ['mss', + 'ls', + 'l', + 'm', + 6, + 0, + 6, + 6, + 3, + 5, + 3, + 5, + 5, + 0] + rightToon = getIntroToon(props, treeNode, [1.8, tY, tZ]) + fruit = self.getObjModel(self.fruitName) + if self.fruitName == 'pineapple': + fruit.setZ(0.42) + fruit.flattenMedium() + anvil = self.getObjModel('anvil') + anvil.setH(100) + anvil.setZ(0.42) + anvil.flattenMedium() + leftToonIval = getThrowIval(leftToon, leftToon.getRightHands()[0], fruit, leftToon=1) + rightToonIval = getThrowIval(rightToon, rightToon.getLeftHands()[0], anvil, leftToon=0, isAnvil=1) + animDur = leftToon.getNumFrames('catch-intro-throw') / leftToon.getFrameRate('catch-intro-throw') + toonIval = Sequence(Parallel(Sequence(leftToonIval, Func(leftToon.loop, 'neutral')), Sequence(Func(rightToon.loop, 'neutral'), WaitInterval(animDur / 2.0), rightToonIval, Func(rightToon.loop, 'neutral')), WaitInterval(cameraIval.getDuration())), Func(cleanupIntroToon, leftToon), Func(cleanupIntroToon, rightToon)) + self.treeNode = treeNode + self.fruit = fruit + self.anvil = anvil + self.leftToon = leftToon + self.rightToon = rightToon + introMovie = Sequence(Parallel(cameraIval, toonIval), Func(cleanupTree)) + return introMovie diff --git a/toontown/minigame/DistributedCatchGameAI.py b/toontown/minigame/DistributedCatchGameAI.py new file mode 100644 index 0000000..704a8a7 --- /dev/null +++ b/toontown/minigame/DistributedCatchGameAI.py @@ -0,0 +1,121 @@ +from DistributedMinigameAI import * +from toontown.ai.ToonBarrier import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import CatchGameGlobals +import MinigameGlobals + +class DistributedCatchGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedCatchGameAI_initialized + except: + self.DistributedCatchGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCatchGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.notify.debug('fruits: %s, fruits caught: %s' % (self.numFruits, self.fruitsCaught)) + perfect = self.fruitsCaught >= self.numFruits + for avId in self.avIdList: + self.scoreDict[avId] = max(1, int(self.scoreDict[avId] / 2)) + if perfect: + self.notify.debug('PERFECT GAME!') + self.scoreDict[avId] += round(self.numFruits / 4.0) + self.logAllPerfect() + + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.caughtList = [0] * 100 + table = CatchGameGlobals.NumFruits[self.numPlayers - 1] + self.numFruits = table[self.getSafezoneId()] + self.notify.debug('numFruits: %s' % self.numFruits) + self.fruitsCaught = 0 + + def allToonsDone(self = self): + self.notify.debug('allToonsDone') + self.sendUpdate('setEveryoneDone') + if not CatchGameGlobals.EndlessGame: + self.gameOver() + + def handleTimeout(avIds, self = self): + self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds) + self.setGameAbort() + + self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, CatchGameGlobals.GameDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout) + + def exitPlay(self): + del self.caughtList + self.doneBarrier.cleanup() + del self.doneBarrier + + def claimCatch(self, objNum, DropObjTypeId): + if self.gameFSM.getCurrentState().getName() != 'play': + return + if DropObjTypeId < 0 or DropObjTypeId >= len(CatchGameGlobals.DOTypeId2Name): + self.air.writeServerEvent('warning', DropObjTypeId, 'CatchGameAI.claimCatch DropObjTypeId out of range') + return + if objNum < 0 or objNum > 5000 or objNum >= 2 * len(self.caughtList): + self.air.writeServerEvent('warning', objNum, 'CatchGameAI.claimCatch objNum is too high or negative') + return + if objNum >= len(self.caughtList): + self.caughtList += [0] * len(self.caughtList) + if not self.caughtList[objNum]: + self.caughtList[objNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setObjectCaught', [avId, objNum]) + objName = CatchGameGlobals.DOTypeId2Name[DropObjTypeId] + self.notify.debug('avatar %s caught object %s: %s' % (avId, objNum, objName)) + if CatchGameGlobals.Name2DropObjectType[objName].good: + self.scoreDict[avId] += 1 + self.fruitsCaught += 1 + + def reportDone(self): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('reportDone: avatar %s is done' % avId) + self.doneBarrier.clear(avId) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedCogThiefGame.py b/toontown/minigame/DistributedCogThiefGame.py new file mode 100644 index 0000000..69cf058 --- /dev/null +++ b/toontown/minigame/DistributedCogThiefGame.py @@ -0,0 +1,907 @@ +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, CollisionHandlerEvent, NodePath, TextNode +from direct.distributed.ClockDelta import globalClockDelta +from direct.interval.IntervalGlobal import Wait, LerpFunctionInterval, LerpHprInterval, Sequence, Parallel, Func, SoundInterval, ActorInterval, ProjectileInterval, Track, LerpScaleInterval, WaitInterval, LerpPosHprInterval +from direct.gui.DirectGui import DirectLabel +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.showbase import RandomNumGen +from direct.task import Task +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from toontown.minigame import CogThiefGameToonSD +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.minigame import CogThiefGameGlobals +from toontown.minigame import CogThief +from toontown.minigame.DistributedMinigame import DistributedMinigame +from toontown.minigame import Trajectory +from toontown.minigame import MinigameGlobals +from toontown.minigame import CogThiefWalk +CTGG = CogThiefGameGlobals + +class DistributedCogThiefGame(DistributedMinigame): + notify = directNotify.newCategory('DistributedCogThiefGame') + ToonSpeed = CTGG.ToonSpeed + StageHalfWidth = 200.0 + StageHalfHeight = 100.0 + BarrelScale = 0.25 + TOON_Z = 0 + UPDATE_SUITS_TASK = 'CogThiefGameUpdateSuitsTask' + REWARD_COUNTDOWN_TASK = 'cogThiefGameRewardCountdown' + ControlKeyLimitTime = 1.0 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCogThiefGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cameraTopView = (0, 0, 55, 0, -90.0, 0) + self.barrels = [] + self.cogInfo = {} + self.lastTimeControlPressed = 0 + self.stolenBarrels = [] + self.useOrthoWalk = base.config.GetBool('cog-thief-ortho', 1) + self.resultIval = None + self.gameIsEnding = False + self.__textGen = TextNode('cogThiefGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + return + + def getTitle(self): + return TTLocalizer.CogThiefGameTitle + + def getInstructions(self): + return TTLocalizer.CogThiefGameInstructions + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.music = base.loadMusic('phase_4/audio/bgm/MG_CogThief.mid') + self.initCogInfo() + for barrelIndex in range(CTGG.NumBarrels): + barrel = loader.loadModel('phase_4/models/minigames/cogthief_game_gagTank') + barrel.setPos(CTGG.BarrelStartingPositions[barrelIndex]) + barrel.setScale(self.BarrelScale) + barrel.reparentTo(render) + barrel.setTag('barrelIndex', str(barrelIndex)) + collSphere = CollisionSphere(0, 0, 0, 4) + collSphere.setTangible(0) + name = 'BarrelSphere-%d' % barrelIndex + collSphereName = self.uniqueName(name) + collNode = CollisionNode(collSphereName) + collNode.setFromCollideMask(CTGG.BarrelBitmask) + collNode.addSolid(collSphere) + colNp = barrel.attachNewNode(collNode) + handler = CollisionHandlerEvent() + handler.setInPattern('barrelHit-%fn') + base.cTrav.addCollider(colNp, handler) + self.accept('barrelHit-' + collSphereName, self.handleEnterBarrel) + nodeToHide = '**/gagMoneyTen' + if barrelIndex % 2: + nodeToHide = '**/gagMoneyFive' + iconToHide = barrel.find(nodeToHide) + if not iconToHide.isEmpty(): + iconToHide.hide() + self.barrels.append(barrel) + + self.gameBoard = loader.loadModel('phase_4/models/minigames/cogthief_game') + self.gameBoard.find('**/floor_TT').hide() + self.gameBoard.find('**/floor_DD').hide() + self.gameBoard.find('**/floor_DG').hide() + self.gameBoard.find('**/floor_MM').hide() + self.gameBoard.find('**/floor_BR').hide() + self.gameBoard.find('**/floor_DL').hide() + zone = self.getSafezoneId() + if zone == ToontownGlobals.ToontownCentral: + self.gameBoard.find('**/floor_TT').show() + elif zone == ToontownGlobals.DonaldsDock: + self.gameBoard.find('**/floor_DD').show() + elif zone == ToontownGlobals.DaisyGardens: + self.gameBoard.find('**/floor_DG').show() + elif zone == ToontownGlobals.MinniesMelodyland: + self.gameBoard.find('**/floor_MM').show() + elif zone == ToontownGlobals.TheBrrrgh: + self.gameBoard.find('**/floor_BR').show() + elif zone == ToontownGlobals.DonaldsDreamland: + self.gameBoard.find('**/floor_DL').show() + else: + self.gameBoard.find('**/floor_TT').show() + self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.gameBoard.setScale(1.0) + self.toonSDs = {} + avId = self.localAvId + toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + self.loadCogs() + self.toonHitTracks = {} + self.toonPieTracks = {} + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.sndRewardTick = base.loadSfx('phase_3.5/audio/sfx/tick_counter.mp3') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.mp3') + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.jarImage = purchaseModels.find('**/Jar') + self.jarImage.reparentTo(hidden) + self.rewardPanel = DirectLabel(parent=hidden, relief=None, pos=(1.16, 0.0, 0.45), scale=0.65, text='', text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -.13), text_font=ToontownGlobals.getSignFont(), image=self.jarImage) + self.rewardPanelTitle = DirectLabel(parent=self.rewardPanel, relief=None, pos=(0, 0, 0.06), scale=0.08, text=TTLocalizer.CannonGameReward, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1)) + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.music + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + self.gameBoard.removeNode() + del self.gameBoard + for barrel in self.barrels: + barrel.removeNode() + + del self.barrels + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + self.timer.destroy() + del self.timer + self.rewardPanel.destroy() + del self.rewardPanel + self.jarImage.removeNode() + del self.jarImage + del self.sndRewardTick + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.gameBoard.reparentTo(render) + lt = base.localAvatar + lt.reparentTo(render) + self.__placeToon(self.localAvId) + lt.setSpeed(0, 0) + self.moveCameraToTop() + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + self.stopGameWalk() + for cogIndex in xrange(self.getNumCogs()): + suit = self.cogInfo[cogIndex]['suit'].suit + pos = self.cogInfo[cogIndex]['pos'] + suit.reparentTo(self.gameBoard) + suit.setPos(pos) + + for avId in self.avIdList: + self.toonHitTracks[avId] = Wait(0.1) + + self.toonRNGs = [] + for i in xrange(self.numPlayers): + self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) + + self.sndTable = {'hitBySuit': [None] * self.numPlayers, + 'falling': [None] * self.numPlayers} + for i in xrange(self.numPlayers): + self.sndTable['hitBySuit'][i] = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.mp3') + self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.mp3') + + base.playMusic(self.music, looping=1, volume=0.8) + self.introTrack = self.getIntroTrack() + self.introTrack.start() + return + + def offstage(self): + self.notify.debug('offstage') + self.gameBoard.hide() + self.music.stop() + for barrel in self.barrels: + barrel.hide() + + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.resetLOD() + + self.timer.reparentTo(hidden) + self.rewardPanel.reparentTo(hidden) + if self.introTrack.isPlaying(): + self.introTrack.finish() + del self.introTrack + DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + DistributedMinigame.handleDisabledAvatar(self, avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.useLOD(1000) + toonSD = CogThiefGameToonSD.CogThiefGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + toonSD.enter() + toonSD.fsm.request('normal') + toon.startSmooth() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + if not base.config.GetBool('cog-thief-endless', 0): + self.timer.show() + self.timer.countdown(CTGG.GameTime, self.__gameTimerExpired) + self.clockStopTime = None + self.rewardPanel.reparentTo(aspect2d) + self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id) + self.__startRewardCountdown() + if self.introTrack.isPlaying(): + self.introTrack.finish() + self.gameFSM.request('play') + return + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.startGameWalk() + self.spawnUpdateSuitsTask() + self.accept('control', self.controlKeyPressed) + self.pieHandler = CollisionHandlerEvent() + self.pieHandler.setInPattern('pieHit-%fn') + + def exitPlay(self): + self.ignore('control') + if self.resultIval and self.resultIval.isPlaying(): + self.resultIval.finish() + self.resultIval = None + return + + def enterCleanup(self): + self.__killRewardCountdown() + if hasattr(self, 'jarIval'): + self.jarIval.finish() + del self.jarIval + for key in self.toonHitTracks: + ival = self.toonHitTracks[key] + if ival.isPlaying(): + ival.finish() + + self.toonHitTracks = {} + for key in self.toonPieTracks: + ival = self.toonPieTracks[key] + if ival.isPlaying(): + ival.finish() + + self.toonPieTracks = {} + for key in self.cogInfo: + cogThief = self.cogInfo[key]['suit'] + cogThief.cleanup() + + self.removeUpdateSuitsTask() + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + if toon: + index = self.avIdList.index(avId) + toon.setPos(CTGG.ToonStartingPositions[index]) + toon.setHpr(0, 0, 0) + + def moveCameraToTop(self): + camera.reparentTo(render) + p = self.cameraTopView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + camera.setZ(camera.getZ() + base.config.GetFloat('cog-thief-z-camera-adjust', 0.0)) + + def destroyGameWalk(self): + self.notify.debug('destroyOrthoWalk') + if self.useOrthoWalk: + self.gameWalk.destroy() + del self.gameWalk + else: + self.notify.debug('TODO destroyGameWalk') + + def initGameWalk(self): + self.notify.debug('startOrthoWalk') + if self.useOrthoWalk: + + def doCollisions(oldPos, newPos, self = self): + x = bound(newPos[0], CTGG.StageHalfWidth, -CTGG.StageHalfWidth) + y = bound(newPos[1], CTGG.StageHalfHeight, -CTGG.StageHalfHeight) + newPos.setX(x) + newPos.setY(y) + return newPos + + orthoDrive = OrthoDrive(self.ToonSpeed, customCollisionCallback=doCollisions, instantTurn=True) + self.gameWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) + else: + self.gameWalk = CogThiefWalk.CogThiefWalk('walkDone') + forwardSpeed = self.ToonSpeed / 2.0 + base.mouseInterfaceNode.setForwardSpeed(forwardSpeed) + multiplier = forwardSpeed / ToontownGlobals.ToonForwardSpeed + base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed * 4) + + def initCogInfo(self): + for cogIndex in xrange(self.getNumCogs()): + self.cogInfo[cogIndex] = {'pos': Point3(CTGG.CogStartingPositions[cogIndex]), + 'goal': CTGG.NoGoal, + 'goalId': CTGG.InvalidGoalId, + 'suit': None} + + return + + def loadCogs(self): + suitTypes = ['ds', + 'ac', + 'bc', + 'ms'] + for suitIndex in xrange(self.getNumCogs()): + st = self.randomNumGen.choice(suitTypes) + suit = CogThief.CogThief(suitIndex, st, self, self.getCogSpeed()) + self.cogInfo[suitIndex]['suit'] = suit + + def handleEnterSphere(self, colEntry): + if self.gameIsEnding: + return + intoName = colEntry.getIntoNodePath().getName() + fromName = colEntry.getFromNodePath().getName() + debugInto = intoName.split('/') + debugFrom = fromName.split('/') + self.notify.debug('handleEnterSphere gametime=%s %s into %s' % (self.getCurrentGameTime(), debugFrom[-1], debugInto[-1])) + intoName = colEntry.getIntoNodePath().getName() + if 'CogThiefSphere' in intoName: + parts = intoName.split('-') + suitNum = int(parts[1]) + self.localToonHitBySuit(suitNum) + + def localToonHitBySuit(self, suitNum): + self.notify.debug('localToonHitBySuit %d' % suitNum) + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + pos = self.cogInfo[suitNum]['suit'].suit.getPos() + self.sendUpdate('hitBySuit', [self.localAvId, + timestamp, + suitNum, + pos[0], + pos[1], + pos[2]]) + self.showToonHitBySuit(self.localAvId, timestamp) + self.makeSuitRespondToToonHit(timestamp, suitNum) + + def hitBySuit(self, avId, timestamp, suitNum, x, y, z): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + if self.gameIsEnding: + return + self.notify.debug('avatar ' + `avId` + ' hit by a suit') + if avId != self.localAvId: + self.showToonHitBySuit(avId, timestamp) + self.makeSuitRespondToToonHit(timestamp, suitNum) + + def showToonHitBySuit(self, avId, timestamp): + toon = self.getAvatar(avId) + if toon == None: + return + rng = self.toonRNGs[self.avIdList.index(avId)] + curPos = toon.getPos(render) + oldTrack = self.toonHitTracks[avId] + if oldTrack.isPlaying(): + oldTrack.finish() + toon.setPos(curPos) + toon.setZ(self.TOON_Z) + parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) + parentNode.setPos(toon.getPos()) + toon.reparentTo(parentNode) + toon.setPos(0, 0, 0) + startPos = parentNode.getPos() + dropShadow = toon.dropShadow.copyTo(parentNode) + dropShadow.setScale(toon.dropShadow.getScale(render)) + trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=1.0) + oldFlyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + trajectory = Trajectory.Trajectory(0, Point3(0, 0, 0), Point3(0, 0, 50), gravMult=0.55) + flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + avIndex = self.avIdList.index(avId) + endPos = CTGG.ToonStartingPositions[avIndex] + + def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): + u = t / dur + moveNode.setX(startPos[0] + u * (endPos[0] - startPos[0])) + moveNode.setY(startPos[1] + u * (endPos[1] - startPos[1])) + flyNode.setPos(trajectory.getPos(t)) + + flyTrack = Sequence(LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), name=toon.uniqueName('hitBySuit-fly')) + geomNode = toon.getGeomNode() + startHpr = geomNode.getHpr() + destHpr = Point3(startHpr) + hRot = rng.randrange(1, 8) + if rng.choice([0, 1]): + hRot = -hRot + destHpr.setX(destHpr[0] + hRot * 360) + spinHTrack = Sequence(LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), Func(geomNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinH')) + parent = geomNode.getParent() + rotNode = parent.attachNewNode('rotNode') + geomNode.reparentTo(rotNode) + rotNode.setZ(toon.getHeight() / 2.0) + oldGeomNodeZ = geomNode.getZ() + geomNode.setZ(-toon.getHeight() / 2.0) + startHpr = rotNode.getHpr() + destHpr = Point3(startHpr) + pRot = rng.randrange(1, 3) + if rng.choice([0, 1]): + pRot = -pRot + destHpr.setY(destHpr[1] + pRot * 360) + spinPTrack = Sequence(LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), Func(rotNode.setHpr, startHpr), name=toon.uniqueName('hitBySuit-spinP')) + i = self.avIdList.index(avId) + soundTrack = Sequence(Func(base.playSfx, self.sndTable['hitBySuit'][i]), Wait(flyDur * (2.0 / 3.0)), SoundInterval(self.sndTable['falling'][i], duration=flyDur * (1.0 / 3.0)), name=toon.uniqueName('hitBySuit-soundTrack')) + + def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): + forwardSpeed = toon.forwardSpeed + rotateSpeed = toon.rotateSpeed + if avId == self.localAvId: + self.stopGameWalk() + else: + toon.stopSmooth() + if forwardSpeed or rotateSpeed: + toon.setSpeed(forwardSpeed, rotateSpeed) + toon.dropShadow.hide() + + def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): + if avId == self.localAvId: + base.localAvatar.setPos(endPos) + if hasattr(self, 'gameWalk'): + toon = base.localAvatar + toon.setSpeed(0, 0) + self.startGameWalk() + dropShadow.removeNode() + del dropShadow + toon = self.getAvatar(avId) + if toon: + toon.dropShadow.show() + geomNode = toon.getGeomNode() + rotNode = geomNode.getParent() + baseNode = rotNode.getParent() + geomNode.reparentTo(baseNode) + rotNode.removeNode() + del rotNode + geomNode.setZ(oldGeomNodeZ) + if toon: + toon.reparentTo(render) + toon.setPos(endPos) + parentNode.removeNode() + del parentNode + if avId != self.localAvId: + if toon: + toon.startSmooth() + + preFunc() + slipBack = Parallel(Sequence(ActorInterval(toon, 'slip-backward', endFrame=24), Wait(CTGG.LyingDownDuration - (flyDur - oldFlyDur)), ActorInterval(toon, 'slip-backward', startFrame=24))) + if toon.doId == self.localAvId: + slipBack.append(SoundInterval(self.sndOof)) + hitTrack = Sequence(Parallel(flyTrack, spinHTrack, spinPTrack, soundTrack), slipBack, Func(postFunc), name=toon.uniqueName('hitBySuit')) + self.notify.debug('hitTrack duration = %s' % hitTrack.getDuration()) + self.toonHitTracks[avId] = hitTrack + hitTrack.start(globalClockDelta.localElapsedTime(timestamp)) + return + + def updateSuitGoal(self, timestamp, inResponseToClientStamp, suitNum, goalType, goalId, x, y, z): + if not self.hasLocalToon: + return + self.notify.debug('updateSuitGoal gameTime=%s timeStamp=%s cog=%s goal=%s goalId=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(), + timestamp, + suitNum, + CTGG.GoalStr[goalType], + goalId, + x, + y, + z)) + cog = self.cogInfo[suitNum] + cog['goal'] = goalType + cog['goalId'] = goalId + newPos = Point3(x, y, z) + cog['pos'] = newPos + suit = cog['suit'] + suit.updateGoal(timestamp, inResponseToClientStamp, goalType, goalId, newPos) + + def spawnUpdateSuitsTask(self): + self.notify.debug('spawnUpdateSuitsTask') + for cogIndex in self.cogInfo: + suit = self.cogInfo[cogIndex]['suit'] + suit.gameStart(self.gameStartTime) + + taskMgr.remove(self.UPDATE_SUITS_TASK) + taskMgr.add(self.updateSuitsTask, self.UPDATE_SUITS_TASK) + + def removeUpdateSuitsTask(self): + taskMgr.remove(self.UPDATE_SUITS_TASK) + + def updateSuitsTask(self, task): + if self.gameIsEnding: + return task.done + for cogIndex in self.cogInfo: + suit = self.cogInfo[cogIndex]['suit'] + suit.think() + + return task.cont + + def makeSuitRespondToToonHit(self, timestamp, suitNum): + cog = self.cogInfo[suitNum]['suit'] + cog.respondToToonHit(timestamp) + + def handleEnterBarrel(self, colEntry): + if self.gameIsEnding: + return + intoName = colEntry.getIntoNodePath().getName() + fromName = colEntry.getFromNodePath().getName() + debugInto = intoName.split('/') + debugFrom = fromName.split('/') + self.notify.debug('handleEnterBarrel gameTime=%s %s into %s' % (self.getCurrentGameTime(), debugFrom[-1], debugInto[-1])) + if 'CogThiefSphere' in intoName: + parts = intoName.split('-') + cogIndex = int(parts[1]) + barrelName = colEntry.getFromNodePath().getName() + barrelParts = barrelName.split('-') + barrelIndex = int(barrelParts[1]) + cog = self.cogInfo[cogIndex]['suit'] + if cog.barrel == CTGG.NoBarrelCarried and barrelIndex not in self.stolenBarrels: + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + if cog.suit: + cogPos = cog.suit.getPos() + collisionPos = colEntry.getContactPos(render) + if (cogPos - collisionPos).length() > 4: + import pdb + pdb.set_trace() + self.sendUpdate('cogHitBarrel', [timestamp, + cogIndex, + barrelIndex, + cogPos[0], + cogPos[1], + cogPos[2]]) + + def makeCogCarryBarrel(self, timestamp, inResponseToClientStamp, cogIndex, barrelIndex, x, y, z): + if not self.hasLocalToon: + return + if self.gameIsEnding: + return + self.notify.debug('makeCogCarryBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(), + timestamp, + cogIndex, + barrelIndex, + x, + y, + z)) + barrel = self.barrels[barrelIndex] + self.notify.debug('barrelPos= %s' % barrel.getPos()) + cog = self.cogInfo[cogIndex]['suit'] + cogPos = Point3(x, y, z) + cog.makeCogCarryBarrel(timestamp, inResponseToClientStamp, barrel, barrelIndex, cogPos) + + def makeCogDropBarrel(self, timestamp, inResponseToClientStamp, cogIndex, barrelIndex, x, y, z): + if not self.hasLocalToon: + return + self.notify.debug('makeCogDropBarrel gameTime=%s timeStamp=%s cog=%s barrel=%s (%.1f, %.1f,%.1f)' % (self.getCurrentGameTime(), + timestamp, + cogIndex, + barrelIndex, + x, + y, + z)) + barrel = self.barrels[barrelIndex] + self.notify.debug('barrelPos= %s' % barrel.getPos()) + cog = self.cogInfo[cogIndex]['suit'] + cogPos = Point3(x, y, z) + cog.makeCogDropBarrel(timestamp, inResponseToClientStamp, barrel, barrelIndex, cogPos) + + def controlKeyPressed(self): + if self.isToonPlayingHitTrack(self.localAvId): + return + if self.gameIsEnding: + return + if self.getCurrentGameTime() - self.lastTimeControlPressed > self.ControlKeyLimitTime: + self.lastTimeControlPressed = self.getCurrentGameTime() + self.notify.debug('controlKeyPressed') + toonSD = self.toonSDs[self.localAvId] + curState = toonSD.fsm.getCurrentState().getName() + toon = self.getAvatar(self.localAvId) + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + pos = toon.getPos() + heading = toon.getH() + self.sendUpdate('throwingPie', [self.localAvId, + timestamp, + heading, + pos[0], + pos[1], + pos[2]]) + self.showToonThrowingPie(self.localAvId, timestamp, heading, pos) + + def throwingPie(self, avId, timestamp, heading, x, y, z): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + self.notify.debug('avatar ' + `avId` + ' throwing pie') + if avId != self.localAvId: + pos = Point3(x, y, z) + self.showToonThrowingPie(avId, timestamp, heading, pos) + + def showToonThrowingPie(self, avId, timestamp, heading, pos): + toon = self.getAvatar(avId) + if toon: + tossTrack, pieTrack, flyPie = self.getTossPieInterval(toon, pos[0], pos[1], pos[2], heading, 0, 0, 0) + + def removePieFromTraverser(flyPie = flyPie): + if base.cTrav: + if flyPie: + base.cTrav.removeCollider(flyPie) + + if avId == self.localAvId: + flyPie.setTag('throwerId', str(avId)) + collSphere = CollisionSphere(0, 0, 0, 0.5) + collSphere.setTangible(0) + name = 'PieSphere-%d' % avId + collSphereName = self.uniqueName(name) + collNode = CollisionNode(collSphereName) + collNode.setFromCollideMask(ToontownGlobals.PieBitmask) + collNode.addSolid(collSphere) + colNp = flyPie.attachNewNode(collNode) + colNp.show() + base.cTrav.addCollider(colNp, self.pieHandler) + self.accept('pieHit-' + collSphereName, self.handlePieHitting) + + def matchRunningAnim(toon = toon): + toon.playingAnim = None + toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed) + return + + newTossTrack = Sequence(tossTrack, Func(matchRunningAnim)) + pieTrack = Parallel(newTossTrack, pieTrack) + elapsedTime = globalClockDelta.localElapsedTime(timestamp) + if elapsedTime < 16.0 / 24.0: + elapsedTime = 16.0 / 24.0 + pieTrack.start(elapsedTime) + self.toonPieTracks[avId] = pieTrack + + def getTossPieInterval(self, toon, x, y, z, h, p, r, power, beginFlyIval = Sequence()): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pie = toon.getPieModel() + pie.setScale(0.9) + flyPie = pie.copyTo(NodePath('a')) + pieName = ToontownBattleGlobals.pieNames[toon.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=48) + sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.mp3') + t = power / 100.0 + dist = 100 - 70 * t + time = 1 + 0.5 * t + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + + def getVelocity(toon = toon, relVel = relVel): + return render.getRelativeVector(toon, relVel) * 0.6 + + toss = Track((0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, toon.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), Parallel(ActorInterval(toon, 'throw', startFrame=48, partName='torso'), animPie), Func(toon.loop, 'neutral'))), (16.0 / 24.0, Func(pie.detachNode))) + fly = Track((14.0 / 24.0, SoundInterval(sound, node=toon)), (16.0 / 24.0, Sequence(Func(flyPie.reparentTo, render), Func(flyPie.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), beginFlyIval, ProjectileInterval(flyPie, startVel=getVelocity, duration=6), Func(flyPie.detachNode)))) + return (toss, fly, flyPie) + + def handlePieHitting(self, colEntry): + if self.gameIsEnding: + return + into = colEntry.getIntoNodePath() + intoName = into.getName() + if 'CogThiefPieSphere' in intoName: + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + parts = intoName.split('-') + suitNum = int(parts[1]) + pos = self.cogInfo[suitNum]['suit'].suit.getPos() + if pos in CTGG.CogStartingPositions: + self.notify.debug('Cog %d hit at starting pos %s, ignoring' % (suitNum, pos)) + else: + self.sendUpdate('pieHitSuit', [self.localAvId, + timestamp, + suitNum, + pos[0], + pos[1], + pos[2]]) + self.makeSuitRespondToPieHit(timestamp, suitNum) + + def pieHitSuit(self, avId, timestamp, suitNum, x, y, z): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + if self.gameIsEnding: + return + self.notify.debug('avatar ' + `avId` + ' hit by a suit') + if avId != self.localAvId: + self.makeSuitRespondToPieHit(timestamp, suitNum) + + def makeSuitRespondToPieHit(self, timestamp, suitNum): + cog = self.cogInfo[suitNum]['suit'] + cog.respondToPieHit(timestamp) + + def sendCogAtReturnPos(self, cogIndex, barrelIndex): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('cogAtReturnPos', [timestamp, cogIndex, barrelIndex]) + + def markBarrelStolen(self, timestamp, inResponseToClientStamp, barrelIndex): + if not self.hasLocalToon: + return + if barrelIndex not in self.stolenBarrels: + self.stolenBarrels.append(barrelIndex) + barrel = self.barrels[barrelIndex] + barrel.hide() + if base.config.GetBool('cog-thief-check-barrels', 1): + if not base.config.GetBool('cog-thief-endless', 0): + if len(self.stolenBarrels) == len(self.barrels): + localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) + gameTime = self.local2GameTime(localStamp) + self.clockStopTime = gameTime + self.notify.debug('clockStopTime = %s' % gameTime) + score = int(self.scoreMult * CTGG.calcScore(gameTime) + 0.5) + self.rewardPanel['text'] = str(score) + self.showResults() + + def __gameTimerExpired(self): + self.notify.debug('game timer expired') + self.showResults() + + def __startRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + taskMgr.add(self.__updateRewardCountdown, self.REWARD_COUNTDOWN_TASK) + + def __killRewardCountdown(self): + taskMgr.remove(self.REWARD_COUNTDOWN_TASK) + + def __updateRewardCountdown(self, task): + curTime = self.getCurrentGameTime() + if self.clockStopTime is not None: + if self.clockStopTime < curTime: + self.notify.debug('self.clockStopTime < curTime %s %s' % (self.clockStopTime, curTime)) + self.__killRewardCountdown() + curTime = self.clockStopTime + if curTime > CTGG.GameTime: + curTime = CTGG.GameTime + score = int(self.scoreMult * CTGG.calcScore(curTime) + 0.5) + if not hasattr(task, 'curScore'): + task.curScore = score + result = Task.cont + if hasattr(self, 'rewardPanel'): + self.rewardPanel['text'] = str(score) + if task.curScore != score: + if hasattr(self, 'jarIval'): + self.jarIval.finish() + s = self.rewardPanel.getScale() + self.jarIval = Parallel(Sequence(self.rewardPanel.scaleInterval(0.15, s * 3.0 / 4.0, blendType='easeOut'), self.rewardPanel.scaleInterval(0.15, s, blendType='easeIn')), SoundInterval(self.sndRewardTick), name='cogThiefGameRewardJarThrob') + self.jarIval.start() + task.curScore = score + else: + result = Task.done + return result + + def startGameWalk(self): + if self.useOrthoWalk: + self.gameWalk.start() + else: + self.gameWalk.enter() + self.gameWalk.fsm.request('walking') + + def stopGameWalk(self): + if self.useOrthoWalk: + self.gameWalk.stop() + else: + self.gameWalk.exit() + + def getCogThief(self, cogIndex): + return self.cogInfo[cogIndex]['suit'] + + def isToonPlayingHitTrack(self, avId): + if avId in self.toonHitTracks: + track = self.toonHitTracks[avId] + if track.isPlaying(): + return True + return False + + def getNumCogs(self): + result = base.config.GetInt('cog-thief-num-cogs', 0) + if not result: + safezone = self.getSafezoneId() + result = CTGG.calculateCogs(self.numPlayers, safezone) + return result + + def getCogSpeed(self): + result = 6.0 + safezone = self.getSafezoneId() + result = CTGG.calculateCogSpeed(self.numPlayers, safezone) + return result + + def showResults(self): + if not self.gameIsEnding: + self.gameIsEnding = True + for barrel in self.barrels: + barrel.wrtReparentTo(render) + + for key in self.cogInfo: + thief = self.cogInfo[key]['suit'] + thief.suit.setPos(100, 0, 0) + thief.suit.hide() + + self.__killRewardCountdown() + self.stopGameWalk() + numBarrelsSaved = len(self.barrels) - len(self.stolenBarrels) + resultStr = '' + if numBarrelsSaved == len(self.barrels): + resultStr = TTLocalizer.CogThiefPerfect + elif numBarrelsSaved > 1: + resultStr = TTLocalizer.CogThiefBarrelsSaved % {'num': numBarrelsSaved} + elif numBarrelsSaved == 1: + resultStr = TTLocalizer.CogThiefBarrelSaved % {'num': numBarrelsSaved} + else: + resultStr = TTLocalizer.CogThiefNoBarrelsSaved + perfectTextSubnode = hidden.attachNewNode(self.__genText(resultStr)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + def safeGameOver(self = self): + if not self.frameworkFSM.isInternalStateInFlux(): + self.gameOver() + + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(safeGameOver)) + if numBarrelsSaved == len(self.barrels): + soundTrack = SoundInterval(self.sndPerfect) + else: + soundTrack = Sequence() + self.resultIval = Parallel(textTrack, soundTrack) + self.resultIval.start() + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def getIntroTrack(self): + base.camera.setPosHpr(0, -13.66, 13.59, 0, -51.6, 0) + result = Sequence(Wait(2), LerpPosHprInterval(base.camera, 13, Point3(self.cameraTopView[0], self.cameraTopView[1], self.cameraTopView[2]), Point3(self.cameraTopView[3], self.cameraTopView[4], self.cameraTopView[5]), blendType='easeIn')) + return result diff --git a/toontown/minigame/DistributedCogThiefGameAI.py b/toontown/minigame/DistributedCogThiefGameAI.py new file mode 100644 index 0000000..d3dd853 --- /dev/null +++ b/toontown/minigame/DistributedCogThiefGameAI.py @@ -0,0 +1,409 @@ +import random +from pandac.PandaModules import Point3 +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.distributed.ClockDelta import globalClockDelta +from direct.task import Task +from toontown.minigame import DistributedMinigameAI +from toontown.minigame import MinigameGlobals +from toontown.minigame import CogThiefGameGlobals +CTGG = CogThiefGameGlobals + +class DistributedCogThiefGameAI(DistributedMinigameAI.DistributedMinigameAI): + notify = directNotify.newCategory('DistributedCogThiefGameAI') + ExplodeWaitTime = 6.0 + CTGG.LyingDownDuration + + def __init__(self, air, minigameId): + try: + self.DistributedCogThiefGameAI_initialized + except: + self.DistributedCogThiefGameAI_initialized = 1 + DistributedMinigameAI.DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedCogThiefGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.cogInfo = {} + self.barrelInfo = {} + self.initBarrelInfo() + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + self.removeAllTasks() + DistributedMinigameAI.DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.initCogInfo() + DistributedMinigameAI.DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + score = int(CTGG.calcScore(self.getCurrentGameTime())) + for avId in self.avIdList: + self.scoreDict[avId] = score + + if self.getNumBarrelsStolen() == 0: + for avId in self.avIdList: + self.scoreDict[avId] += CTGG.PerfectBonus[len(self.avIdList) - 1] + self.logPerfectGame(avId) + + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.startSuitGoals() + if not config.GetBool('cog-thief-endless', 0): + taskMgr.doMethodLater(CTGG.GameTime, self.timerExpired, self.taskName('gameTimer')) + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + taskMgr.remove(self.taskName('gameTimer')) + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def initCogInfo(self): + for cogIndex in xrange(self.getNumCogs()): + self.cogInfo[cogIndex] = {'pos': Point3(CogThiefGameGlobals.CogStartingPositions[cogIndex]), + 'goal': CTGG.NoGoal, + 'goalId': CTGG.InvalidGoalId, + 'barrel': CTGG.NoBarrelCarried} + + def initBarrelInfo(self): + for barrelIndex in xrange(CogThiefGameGlobals.NumBarrels): + self.barrelInfo[barrelIndex] = {'pos': Point3(CogThiefGameGlobals.BarrelStartingPositions[barrelIndex]), + 'carriedBy': CTGG.BarrelOnGround, + 'stolen': False} + + def makeCogCarryBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex): + if cogIndex in self.cogInfo and barrelIndex in self.barrelInfo: + self.barrelInfo[barrelIndex]['carriedBy'] = cogIndex + self.cogInfo[cogIndex]['barrel'] = barrelIndex + else: + self.notify.warning('makeCogCarryBarrel invalid cogIndex=%s barrelIndex=%s' % (cogIndex, barrelIndex)) + + def b_makeCogCarryBarrel(self, clientStamp, cogIndex, barrelIndex): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.notify.debug('b_makeCogCarryBarrel timeStamp=%s clientStamp=%s cog=%s barrel=%s' % (timestamp, + clientStamp, + cogIndex, + barrelIndex)) + self.makeCogCarryBarrel(timestamp, clientStamp, cogIndex, barrelIndex) + self.d_makeCogCarryBarrel(timestamp, clientStamp, cogIndex, barrelIndex) + + def d_makeCogCarryBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex): + pos = self.cogInfo[cogIndex]['pos'] + gameTime = self.getCurrentGameTime() + self.sendUpdate('makeCogCarryBarrel', [timestamp, + clientStamp, + cogIndex, + barrelIndex, + pos[0], + pos[1], + pos[2]]) + + def makeCogDropBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex, barrelPos): + if cogIndex in self.cogInfo and barrelIndex in self.barrelInfo: + self.barrelInfo[barrelIndex]['carriedBy'] = CTGG.BarrelOnGround + self.cogInfo[cogIndex]['barrel'] = CTGG.NoBarrelCarried + else: + self.notify.warning('makeCogDropBarrel invalid cogIndex=%s barrelIndex=%s' % (cogIndex, barrelIndex)) + + def b_makeCogDropBarrel(self, clientStamp, cogIndex, barrelIndex, barrelPos): + if self.barrelInfo[barrelIndex]['carriedBy'] != cogIndex: + self.notify.error("self.barrelInfo[%s]['carriedBy'] != %s" % (barrelIndex, cogIndex)) + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.makeCogDropBarrel(timestamp, clientStamp, cogIndex, barrelIndex, barrelPos) + self.d_makeCogDropBarrel(timestamp, clientStamp, cogIndex, barrelIndex, barrelPos) + + def d_makeCogDropBarrel(self, timestamp, clientStamp, cogIndex, barrelIndex, barrelPos): + pos = barrelPos + gameTime = self.getCurrentGameTime() + self.sendUpdate('makeCogDropBarrel', [timestamp, + clientStamp, + cogIndex, + barrelIndex, + pos[0], + pos[1], + pos[2]]) + + def isCogCarryingABarrel(self, cogIndex): + result = self.cogInfo[cogIndex]['barrel'] > CTGG.NoBarrelCarried + return result + + def isCogCarryingThisBarrel(self, cogIndex, barrelIndex): + result = self.cogInfo[cogIndex]['barrel'] == barrelIndex + return result + + def startSuitGoals(self): + delayTimes = [] + for cogIndex in xrange(self.getNumCogs()): + delayTimes.append(cogIndex * 1.0) + + random.shuffle(delayTimes) + for cogIndex in xrange(self.getNumCogs()): + self.doMethodLater(delayTimes[cogIndex], self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % cogIndex), extraArgs=[cogIndex]) + + def chaseToon(self, suitNum, avId): + goalType = CTGG.ToonGoal + goalId = avId + self.cogInfo[suitNum]['goal'] = goalType + self.cogInfo[suitNum]['goalId'] = goalId + pos = self.cogInfo[suitNum]['pos'] + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.notify.debug('chaseToon time=%s suitNum=%s, avId=%s' % (timestamp, suitNum, avId)) + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + timestamp, + suitNum, + goalType, + goalId, + pos[0], + pos[1], + pos[2]]) + + def hitBySuit(self, avId, timestamp, suitNum, x, y, z): + if suitNum >= self.getNumCogs(): + self.notify.warning('hitBySuit, possible hacker avId=%s' % avId) + return + barrelIndex = self.cogInfo[suitNum]['barrel'] + if barrelIndex >= 0: + barrelPos = Point3(x, y, z) + self.b_makeCogDropBarrel(timestamp, suitNum, barrelIndex, barrelPos) + startPos = CTGG.CogStartingPositions[suitNum] + self.cogInfo[suitNum]['pos'] = startPos + self.cogInfo[suitNum]['goal'] = CTGG.NoGoal + self.cogInfo[suitNum]['goalId'] = CTGG.InvalidGoalId + self.sendSuitSync(timestamp, suitNum) + self.doMethodLater(self.ExplodeWaitTime, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % suitNum), extraArgs=[suitNum]) + + def sendSuitSync(self, clientstamp, suitNum): + pos = self.cogInfo[suitNum]['pos'] + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + goalType = self.cogInfo[suitNum]['goal'] + goalId = self.cogInfo[suitNum]['goalId'] + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + clientstamp, + suitNum, + goalType, + goalId, + pos[0], + pos[1], + pos[2]]) + + def chooseSuitGoal(self, suitNum): + barrelIndex = self.findClosestUnassignedBarrel(suitNum) + if barrelIndex >= 0: + self.chaseBarrel(suitNum, barrelIndex) + else: + noOneChasing = self.avIdList[:] + for key in self.cogInfo: + if self.cogInfo[key]['goal'] == CTGG.ToonGoal: + toonId = self.cogInfo[key]['goalId'] + if toonId in noOneChasing: + noOneChasing.remove(toonId) + + chaseToonId = self.avIdList[0] + if noOneChasing: + chaseToonId = random.choice(noOneChasing) + else: + chaseToonId = random.choice(self.avIdList) + self.chaseToon(suitNum, chaseToonId) + + def chaseBarrel(self, suitNum, barrelIndex): + goalType = CTGG.BarrelGoal + goalId = barrelIndex + self.cogInfo[suitNum]['goal'] = goalType + self.cogInfo[suitNum]['goalId'] = goalId + pos = self.cogInfo[suitNum]['pos'] + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.notify.debug('chaseBarrel time=%s suitNum=%s, barrelIndex=%s' % (timestamp, suitNum, barrelIndex)) + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + timestamp, + suitNum, + goalType, + goalId, + pos[0], + pos[1], + pos[2]]) + + def getCogCarryingBarrel(self, barrelIndex): + return self.barrelInfo[barrelIndex]['carriedBy'] + + def cogHitBarrel(self, clientStamp, cogIndex, barrelIndex, x, y, z): + if cogIndex >= self.getNumCogs(): + self.notify.warning('cogHitBarrel, possible hacker cogIndex=%s' % cogIndex) + return + if barrelIndex >= CTGG.NumBarrels: + self.notify.warning('cogHitBarrel, possible hacker barrelIndex=%s' % barrelIndex) + return + if self.isCogCarryingABarrel(cogIndex): + self.notify.debug('cog is already carrying a barrel ignore') + return + if self.cogInfo[cogIndex]['goal'] == CTGG.NoGoal: + self.notify.debug('ignoring barrel hit as cog %d has no goal' % cogIndex) + return + if self.getCogCarryingBarrel(barrelIndex) == CTGG.BarrelOnGround: + pos = Point3(x, y, z) + returnPosIndex = self.chooseReturnPos(cogIndex, pos) + self.runAway(clientStamp, cogIndex, pos, barrelIndex, returnPosIndex) + + def chooseReturnPos(self, cogIndex, cogPos): + shortestDistance = 10000 + shortestReturnIndex = -1 + for retIndex in xrange(len(CTGG.CogReturnPositions)): + retPos = CTGG.CogReturnPositions[retIndex] + distance = (cogPos - retPos).length() + if distance < shortestDistance: + shortestDistance = distance + shortestReturnIndex = retIndex + self.notify.debug('shortest distance=%s index=%s' % (shortestDistance, shortestReturnIndex)) + + self.notify.debug('chooseReturnpos returning %s' % shortestReturnIndex) + return shortestReturnIndex + + def runAway(self, clientStamp, cogIndex, cogPos, barrelIndex, returnPosIndex): + self.cogInfo[cogIndex]['pos'] = cogPos + self.b_makeCogCarryBarrel(clientStamp, cogIndex, barrelIndex) + goalType = CTGG.RunAwayGoal + goalId = returnPosIndex + self.cogInfo[cogIndex]['goal'] = goalType + self.cogInfo[cogIndex]['goalId'] = 0 + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + gameTime = self.getCurrentGameTime() + self.sendUpdate('updateSuitGoal', [timestamp, + clientStamp, + cogIndex, + goalType, + goalId, + cogPos[0], + cogPos[1], + cogPos[2]]) + + def pieHitSuit(self, avId, timestamp, suitNum, x, y, z): + if suitNum >= self.getNumCogs(): + self.notify.warning('hitBySuit, possible hacker avId=%s' % avId) + return + barrelIndex = self.cogInfo[suitNum]['barrel'] + if barrelIndex >= 0: + barrelPos = Point3(x, y, z) + self.b_makeCogDropBarrel(timestamp, suitNum, barrelIndex, barrelPos) + startPos = CTGG.CogStartingPositions[suitNum] + self.cogInfo[suitNum]['pos'] = startPos + self.cogInfo[suitNum]['goal'] = CTGG.NoGoal + self.cogInfo[suitNum]['goalId'] = CTGG.InvalidGoalId + self.sendSuitSync(timestamp, suitNum) + self.doMethodLater(self.ExplodeWaitTime, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % suitNum), extraArgs=[suitNum]) + + def findClosestUnassignedBarrel(self, suitNum): + possibleBarrels = [] + for key in self.barrelInfo: + info = self.barrelInfo[key] + if info['carriedBy'] == CTGG.BarrelOnGround and not info['stolen']: + if not self.isCogGoingForBarrel(key): + possibleBarrels.append(key) + + shortestDistance = 10000 + shortestBarrelIndex = -1 + cogPos = self.cogInfo[suitNum]['pos'] + for possibleIndex in possibleBarrels: + barrelPos = self.barrelInfo[possibleIndex]['pos'] + distance = (cogPos - barrelPos).length() + if distance < shortestDistance: + shortestDistance = distance + shortestBarrelIndex = possibleIndex + + return shortestBarrelIndex + + def isCogGoingForBarrel(self, barrelIndex): + result = False + for suitNum in self.cogInfo: + cogInfo = self.cogInfo[suitNum] + if cogInfo['goal'] == CTGG.BarrelGoal and cogInfo['goalId'] == barrelIndex: + result = True + break + + return result + + def markBarrelStolen(self, clientStamp, barrelIndex): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + self.sendUpdate('markBarrelStolen', [timestamp, clientStamp, barrelIndex]) + self.barrelInfo[barrelIndex]['stolen'] = True + + def getNumBarrelsStolen(self): + numStolen = 0 + for barrel in self.barrelInfo.values(): + if barrel['stolen']: + numStolen += 1 + + return numStolen + + def cogAtReturnPos(self, clientstamp, cogIndex, barrelIndex): + if cogIndex not in self.cogInfo or barrelIndex not in self.barrelInfo: + avId = self.air.getAvatarIdFromSender() + self.air.writeServerEvent('suspicious cogAtReturnPos avId=%s, cogIndex=%s, barrelIndex=%s' % (avId, cogIndex, barrelIndex)) + return + if self.cogInfo[cogIndex]['goal'] == CTGG.RunAwayGoal: + if self.isCogCarryingThisBarrel(cogIndex, barrelIndex): + self.markBarrelStolen(clientstamp, barrelIndex) + returnPosIndex = self.cogInfo[cogIndex]['goalId'] + retPos = CTGG.CogReturnPositions[returnPosIndex] + self.b_makeCogDropBarrel(clientstamp, cogIndex, barrelIndex, retPos) + startPos = CTGG.CogStartingPositions[cogIndex] + self.cogInfo[cogIndex]['pos'] = startPos + self.cogInfo[cogIndex]['goal'] = CTGG.NoGoal + self.cogInfo[cogIndex]['goalId'] = CTGG.InvalidGoalId + self.doMethodLater(0.5, self.chooseSuitGoal, self.uniqueName('choseSuitGoal-%d-' % cogIndex), extraArgs=[cogIndex]) + self.checkForGameOver() + + def checkForGameOver(self): + numStolen = 0 + for key in self.barrelInfo: + if self.barrelInfo[key]['stolen']: + numStolen += 1 + + self.notify.debug('numStolen = %s' % numStolen) + if simbase.config.GetBool('cog-thief-check-barrels', 1): + if not simbase.config.GetBool('cog-thief-endless', 0): + if numStolen == len(self.barrelInfo): + self.gameOver() + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameOver() + return Task.done + + def getNumCogs(self): + result = simbase.config.GetInt('cog-thief-num-cogs', 0) + if not result: + safezone = self.getSafezoneId() + result = CTGG.calculateCogs(self.numPlayers, safezone) + return result diff --git a/toontown/minigame/DistributedDivingGame.py b/toontown/minigame/DistributedDivingGame.py new file mode 100644 index 0000000..5faa51f --- /dev/null +++ b/toontown/minigame/DistributedDivingGame.py @@ -0,0 +1,976 @@ +from direct.showbase.ShowBaseGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownTimer +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.task import Task +from direct.actor import Actor +from toontown.toon import LaffMeter +from direct.distributed import DistributedSmoothNode +import ArrowKeys +import Ring +import RingTrack +import DivingGameGlobals +import RingGroup +import RingTrackGroups +import random +import DivingGameToonSD +import DivingFishSpawn +import DivingTreasure +import math +import TreasureScorePanel +from otp.distributed.TelemetryLimiter import TelemetryLimiter, TLGatherAllAvs +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DivingGameRotationLimiter(TelemetryLimiter): + + def __init__(self, h, p): + self._h = h + self._p = p + + def __call__(self, obj): + obj.setHpr(self._h, self._p, obj.getR()) + + +class DistributedDivingGame(DistributedMinigame): + COLLISION_WATCH_TASK = 'DivingGameCollisionWatchTask' + TREASURE_BOUNDS_TASK = 'DivingGameTreasureBoundsTask' + CRAB_TASK = 'DivingGameCrabTask' + UPDATE_LOCALTOON_TASK = 'DivingGameUpdateLocalToonTask' + COLLISION_DETECTION_PRIORITY = 5 + MAP_DIV = 2.8 + MAP_OFF = 14.0 + LAG_COMP = 1.25 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedDivingGame', [State.State('off', self.enterOff, self.exitOff, ['swim']), State.State('swim', self.enterSwim, self.exitSwim, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.iCount = 0 + self.reachedFlag = 0 + self.dead = 0 + + def getTitle(self): + return TTLocalizer.DivingGameTitle + + def getInstructions(self): + p = self.avIdList.index(self.localAvId) + if self.isSinglePlayer(): + text = TTLocalizer.DivingInstructionsSinglePlayer + else: + text = TTLocalizer.DivingInstructionsMultiPlayer + return text + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + loadBase = 'phase_4/models/minigames/' + loadBaseShip = 'phase_5/models/props/' + self.sndAmbience = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.mp3') + self.environModel = loader.loadModel(loadBase + 'diving_game.bam') + self.boatModel = self.environModel.find('**/boat') + self.skyModel = self.environModel.find('**/sky') + self.waterModel = self.environModel.find('**/seawater') + self.frontMap = self.environModel.find('**/sea_front') + self.frontMap.setY(3) + self.frontMap.setBin('fixed', 0) + self.frontMap.setDepthTest(0) + self.waterModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles1') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles2') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles3') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles4') + bubbleModel.setY(1.0) + bubbleModel = self.environModel.find('**/bubbles5') + bubbleModel.setY(1.0) + self.mapModel = loader.loadModel(loadBase + 'diving_game.bam') + boatMap = self.mapModel.find('**/boat') + skyMap = self.mapModel.find('**/sky') + frontMap = self.mapModel.find('**/sea_front') + skyMap.hide() + frontMap.hide() + boatMap.setZ(28.5) + self.crabs = [] + self.spawners = [] + self.toonSDs = {} + avId = self.localAvId + toonSD = DivingGameToonSD.DivingGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + crabSoundName = 'King_Crab.mp3' + crabSoundPath = 'phase_4/audio/sfx/%s' % crabSoundName + self.crabSound = loader.loadSfx(crabSoundPath) + treasureSoundName = 'SZ_DD_treasure.mp3' + treasureSoundPath = 'phase_4/audio/sfx/%s' % treasureSoundName + self.treasureSound = loader.loadSfx(treasureSoundPath) + hitSoundName = 'diving_game_hit.mp3' + hitSoundPath = 'phase_4/audio/sfx/%s' % hitSoundName + self.hitSound = loader.loadSfx(hitSoundPath) + self.music = base.loadMusic('phase_4/audio/bgm/MG_Target.mid') + self.addSound('dropGold', 'diving_treasure_drop_off.mp3', 'phase_4/audio/sfx/') + self.addSound('getGold', 'diving_treasure_pick_up.mp3', 'phase_4/audio/sfx/') + self.swimSound = loader.loadSfx('phase_4/audio/sfx/diving_swim_loop.wav') + self.swimSound.setVolume(0.0) + self.swimSound.setPlayRate(1.0) + self.swimSound.setLoop(True) + self.swimSound.play() + + def addSound(self, name, soundName, path = None): + if not hasattr(self, 'soundTable'): + self.soundTable = {} + if path: + self.soundPath = path + soundSource = '%s%s' % (self.soundPath, soundName) + self.soundTable[name] = loader.loadSfx(soundSource) + + def playSound(self, name, volume = 1.0): + self.soundTable[name].setVolume(1.0) + self.soundTable[name].play() + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.mapModel.removeNode() + del self.mapModel + if hasattr(self, 'soundTable'): + del self.soundTable + del self.sndAmbience + del self.hitSound + del self.crabSound + del self.treasureSound + self.swimSound.stop() + del self.swimSound + self.environModel.removeNode() + del self.environModel + self.removeChildGameFSM(self.gameFSM) + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + del self.gameFSM + del self.music + + def fishCollision(self, collEntry): + avId = int(collEntry.getFromNodePath().getName()) + toonSD = self.toonSDs[avId] + name = collEntry.getIntoNodePath().getName() + if len(name) >= 7: + if name[0:6] == 'crabby': + self.sendUpdate('handleCrabCollision', [avId, toonSD.status]) + else: + spawnerId = int(name[2]) + spawnId = int(name[3:len(name)]) + if self.spawners[spawnerId].fishArray.has_key(spawnId): + self.sendUpdate('handleFishCollision', [avId, + spawnId, + spawnerId, + toonSD.status]) + + def fishSpawn(self, timestamp, fishcode, spawnerId, offset): + if self.dead is 1: + return + ts = globalClockDelta.localElapsedTime(timestamp) + if not hasattr(self, 'spawners'): + return + if abs(self.spawners[spawnerId].lastSpawn - timestamp) < 150: + return + fish = self.spawners[spawnerId].createFish(fishcode) + fish.offset = offset + fish.setPos(self.spawners[spawnerId].position) + func = Func(self.fishRemove, fish.code) + self.spawners[spawnerId].lastSpawn = timestamp + iName = '%s %s' % (fish.name, self.iCount) + self.iCount += 1 + if fish.name == 'clown': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=8 * self.SPEEDMULT * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, (offset - 4) / 2.0), name=iName), func) + fish.specialLerp = Sequence() + elif fish.name == 'piano': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=5 * self.SPEEDMULT * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, (offset - 4) / 2.0), name=iName), func) + fish.specialLerp = Sequence() + elif fish.name == 'pbj': + fish.moveLerp = Sequence(LerpFunc(fish.setX, duration=12 * self.SPEEDMULT * self.LAG_COMP, fromData=self.spawners[spawnerId].position.getX(), toData=self.spawners[spawnerId].position.getX() + 50 * self.spawners[spawnerId].direction, name=iName), func) + fish.specialLerp = LerpFunc(self.pbjMove, duration=5 * self.SPEEDMULT * self.LAG_COMP, fromData=0, toData=2.0 * 3.14159, extraArgs=[fish, self.spawners[spawnerId].position.getZ()], blendType='easeInOut') + elif fish.name == 'balloon': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=10 * self.SPEEDMULT * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, (offset - 4) / 2.0), name=iName), func) + fish.specialLerp = Sequence(Wait(offset / 10.0 * 2 + 1.5), Parallel(LerpScaleInterval(fish, duration=0.3, startScale=Vec3(2, 2, 2), scale=Vec3(5, 3, 5), blendType='easeInOut')), Wait(1.0), Parallel(LerpScaleInterval(fish, duration=0.4, startScale=Vec3(5, 3, 5), scale=Vec3(2, 2, 2), blendType='easeInOut'))) + elif fish.name == 'bear' or fish.name == 'nurse': + fish.moveLerp = Sequence(LerpPosInterval(fish, duration=20 * self.LAG_COMP, startPos=self.spawners[spawnerId].position, pos=self.spawners[spawnerId].position + Point3(50 * self.spawners[spawnerId].direction, 0, 0), name=iName), func) + fish.specialLerp = Sequence() + fish.moveLerp.start(ts) + fish.specialLerp.loop(ts) + + def pbjMove(self, x, fish, Z): + z = math.sin(x + fish.offset * 3) * 3 + fish.setZ(z + Z) + + def getIntroMovie(self): + seq = Sequence() + seq.append(Wait(2.0)) + seq.append(LerpFunc(camera.setZ, duration=5, fromData=36, toData=-23, blendType='easeInOut', name='intro')) + seq.append(Wait(2.0)) + seq.append(LerpFunc(camera.setZ, duration=5, fromData=-23, toData=36 + 3, blendType='easeInOut', name='intro')) + return seq + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + base.localAvatar.collisionsOff() + DistributedSmoothNode.activateSmoothing(1, 1) + numToons = self.numPlayers + self.NUMTREASURES = numToons + camera.reparentTo(render) + camera.setZ(36) + camera.setH(0) + camera.setX(0) + base.camLens.setFov(45) + camera.setY(-54) + base.camLens.setFar(1500) + self.introMovie = self.getIntroMovie() + self.introMovie.start() + self.accept('FishHit', self.fishCollision) + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + toon = base.localAvatar + toon.reparentTo(render) + toon.setPos(-9, -1, 36) + self.__placeToon(self.localAvId) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.xVel = 0 + self.zVel = 0 + self.orientNode = toon.attachNewNode('orientNode') + self.orientNode.setPos(0, 0, 1) + self.orientNode2 = toon.attachNewNode('orientNode') + self.orientNode2.setPos(0, 0, -1) + self.environNode = render.attachNewNode('environNode') + self.environModel.reparentTo(self.environNode) + self.environModel.setScale(2.8, 2.8, 2.73) + self.environModel.setPos(0, 0.5, -41) + self.skyModel.setScale(1.3, 1.0, 1.0) + boatoff = 6.75 + self.boatModel.reparentTo(self.environNode) + self.boatModel.setPos(0, 3.0, 40 - boatoff) + self.boatModel.setScale(2.8) + cSphere = CollisionSphere(0.0, 0.0, 0.0 + 2.0, 3.0) + cSphere.setTangible(0) + name = 'boat' + cSphereNode = CollisionNode(name) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.addSolid(cSphere) + self.boatNode = cSphereNode + self.boatCNP = self.boatModel.attachNewNode(cSphereNode) + self.accept('reach-boat', self.__boatReached) + self.boatTilt = Sequence(LerpFunc(self.boatModel.setR, duration=5, fromData=5, toData=-5, blendType='easeInOut', name='tilt'), LerpFunc(self.boatModel.setR, duration=5, fromData=-5, toData=5, blendType='easeInOut', name='tilt')) + self.boatTilt.loop() + self.mapScaleRatio = 40 + self.mapModel.reparentTo(aspect2d) + self.mapModel.setScale(1.0 / self.mapScaleRatio) + self.mapModel.setTransparency(1) + self.mapModel.setPos(1.15, -0.5, -0.125) + self.mapModel.setColorScale(1, 1, 1, 0.7) + self.mapModel.hide() + if None != self.sndAmbience: + self.sndAmbience.setLoop(True) + self.sndAmbience.play() + self.sndAmbience.setVolume(0.01) + return + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.introMovie.finish() + self.boatTilt.finish() + self.mapModel.hide() + DistributedSmoothNode.activateSmoothing(1, 0) + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + base.camLens.setFov(ToontownGlobals.DefaultCameraFov) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.arrowKeys.destroy() + del self.arrowKeys + self.environNode.removeNode() + del self.environNode + if None != self.sndAmbience: + self.sndAmbience.stop() + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + av.setAnimState('neutral', 1.0) + + self.dead = 1 + self.__killCrabTask() + for spawner in self.spawners: + spawner.destroy() + del spawner + + del self.spawners + for crab in self.crabs: + crab.moveLerp.finish() + crab.moveLerp = None + crab.removeNode() + del crab + + if hasattr(self, 'treasures') and self.treasures: + for i in range(self.NUMTREASURES): + self.treasures[i].destroy() + + del self.treasures + if hasattr(self, 'cSphereNodePath1'): + self.cSphereNodePath1.removeNode() + del self.cSphereNodePath1 + if hasattr(self, 'cSphereNodePath1'): + self.cSphereNodePath2.removeNode() + del self.cSphereNodePath2 + if hasattr(self, 'remoteToonCollNPs'): + for np in self.remoteToonCollNPs.values(): + np.removeNode() + + del self.remoteToonCollNPs + self.pusher = None + self.cTrav = None + self.cTrav2 = None + base.localAvatar.collisionsOn() + return + + def handleDisabledAvatar(self, avId): + self.dead = 1 + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = -10 + i * 5 + toon.setPos(x, -1, 36) + toon.setHpr(180, 180, 0) + + def getTelemetryLimiter(self): + return TLGatherAllAvs('DivingGame', Functor(DivingGameRotationLimiter, 180, 180)) + + def setGameReady(self): + self.notify.debug('setGameReady') + if not self.hasLocalToon: + return + if DistributedMinigame.setGameReady(self): + return + self.dead = 0 + self.difficultyPatterns = {ToontownGlobals.ToontownCentral: [1, + 1.5, + 65, + 3], + ToontownGlobals.DonaldsDock: [1, + 1.3, + 65, + 1], + ToontownGlobals.DaisyGardens: [2, + 1.2, + 65, + 1], + ToontownGlobals.MinniesMelodyland: [2, + 1.0, + 65, + 1], + ToontownGlobals.TheBrrrgh: [3, + 1.0, + 65, + 1], + ToontownGlobals.DonaldsDreamland: [3, + 1.0, + 65, + 1]} + pattern = self.difficultyPatterns[self.getSafezoneId()] + self.NUMCRABS = pattern[0] + self.SPEEDMULT = pattern[1] + self.TIME = pattern[2] + loadBase = 'phase_4/models/char/' + for i in range(self.NUMCRABS): + self.crabs.append(Actor.Actor(loadBase + 'kingCrab-zero.bam', {'anim': loadBase + 'kingCrab-swimLOOP.bam'})) + + for i in range(len(self.crabs)): + crab = self.crabs[i] + crab.reparentTo(render) + crab.name = 'king' + crab.crabId = i + cSphere = CollisionSphere(0.0, 0.0, 1, 1.3) + cSphereNode = CollisionNode('crabby' + str(i)) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNodePath = crab.attachNewNode(cSphereNode) + cSphereNodePath.setScale(1, 3, 1) + self.accept('hitby-' + 'crabby' + str(i), self.fishCollision) + if i % 2 is 0: + crab.setPos(20, 0, -40) + crab.direction = -1 + else: + crab.setPos(-20, 0, -40) + crab.direction = 1 + crab.loop('anim') + crab.setScale(1, 0.3, 1) + crab.moveLerp = Sequence() + + self.collHandEvent = CollisionHandlerEvent() + self.cTrav = CollisionTraverser('DistributedDiverGame') + self.cTrav2 = CollisionTraverser('DistributedDiverGame') + self.collHandEvent.addInPattern('reach-%in') + self.collHandEvent.addAgainPattern('reach-%in') + self.collHandEvent.addInPattern('into-%in') + self.collHandEvent.addInPattern('hitby-%in') + loadBase = 'phase_4/models/minigames/' + self.treasures = [] + self.chestIcons = {} + for i in range(self.NUMTREASURES): + self.chestIcons[i] = loader.loadModel(loadBase + 'treasure_chest.bam') + self.chestIcons[i].reparentTo(self.mapModel) + self.chestIcons[i].setScale(1.5) + treasure = DivingTreasure.DivingTreasure(i) + self.accept('grab-' + str(i), self.__treasureGrabbed) + self.collHandEvent.addInPattern('grab-%in') + self.collHandEvent.addAgainPattern('grab-%in') + self.treasures.append(treasure) + + self.cTrav.traverse(render) + spawnX = 24 * self.LAG_COMP + spawnY = 0.6 + self.spawners.append(DivingFishSpawn.DivingFishSpawn(0, 1, Point3(-spawnX, spawnY, 25), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(1, -1, Point3(spawnX, spawnY, 16), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(2, 1, Point3(-spawnX, spawnY, 6), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(3, -1, Point3(spawnX, spawnY, -4), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(4, 1, Point3(-spawnX, spawnY, -15), self.collHandEvent)) + self.spawners.append(DivingFishSpawn.DivingFishSpawn(5, -1, Point3(spawnX, spawnY, -23), self.collHandEvent)) + for spawner in self.spawners: + spawner.lastSpawn = 0 + + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + headparts = base.localAvatar.getHeadParts() + pos = headparts[2].getPos() + self.cSphereNodePath1 = base.localAvatar.attachNewNode(cSphereNode) + self.cSphereNodePath1.setPos(pos + Point3(0, 1.5, 1)) + self.cTrav.addCollider(self.cSphereNodePath1, self.collHandEvent) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + headparts = base.localAvatar.getHeadParts() + pos = headparts[2].getPos() + self.cSphereNodePath2 = base.localAvatar.attachNewNode(cSphereNode) + self.cSphereNodePath2.setPos(pos + Point3(0, 1.5, -1)) + self.cTrav.addCollider(self.cSphereNodePath2, self.collHandEvent) + self.pusher = CollisionHandlerPusher() + self.pusher.addCollider(self.cSphereNodePath1, base.localAvatar) + self.pusher.addCollider(self.cSphereNodePath2, base.localAvatar) + self.pusher.setHorizontal(0) + self.cTrav2.addCollider(self.cSphereNodePath1, self.pusher) + self.cTrav2.addCollider(self.cSphereNodePath2, self.pusher) + self.remoteToonCollNPs = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + headparts = toon.getHeadParts() + pos = headparts[2].getPos() + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(DivingGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + cSphereNP.setPos(pos + Point3(0, 1.5, 1)) + self.remoteToonCollNPs[int(str(avId) + str(1))] = cSphereNP + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.4) + cSphereNode = CollisionNode('%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(DivingGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + cSphereNP.setPos(pos + Point3(0, 1.5, -1)) + self.remoteToonCollNPs[int(str(avId) + str(1))] = cSphereNP + toonSD = DivingGameToonSD.DivingGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.load() + toonSD.enter() + toonSD.fsm.request('normal') + + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.startSmooth() + + self.remoteToons = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + self.remoteToons[avId] = toon + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + DistributedMinigame.setGameStart(self, timestamp) + self.notify.debug('setGameStart') + self.treasurePanel = TreasureScorePanel.TreasureScorePanel() + self.treasurePanel.setPos(-1.19, 0, 0.75) + self.treasurePanel.makeTransparent(0.7) + self.introMovie.finish() + self.gameFSM.request('swim') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterSwim(self): + self.notify.debug('enterSwim') + base.playMusic(self.music, looping=1, volume=0.9) + self.localLerp = Sequence() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(self.TIME) + self.timer.countdown(self.TIME, self.timerExpired) + self.mapModel.show() + self.mapAvatars = {} + avatarScale = 0.025 * self.mapScaleRatio + for avId in self.remoteAvIdList: + avatar = base.cr.doId2do.get(avId, False) + if avatar != False: + self.mapAvatars[avId] = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.mapAvatars[avId].reparentTo(self.mapModel) + self.mapAvatars[avId].setScale(avatarScale) + self.mapAvatars[avId].start() + + avatar = base.cr.doId2do[self.localAvId] + self.mapAvatars[self.localAvId] = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.mapAvatars[self.localAvId].reparentTo(self.mapModel) + self.mapAvatars[self.localAvId].setScale(avatarScale) + self.mapAvatars[self.localAvId].start() + self.accept('resetClock', self.__resetClock) + self.__spawnUpdateLocalToonTask() + self.__spawnCrabTask() + self.__spawnTreasureBoundsTask() + + def __resetClock(self, tOffset): + self.notify.debug('resetClock') + self.gameStartTime += tOffset + self.timer.countdown(self.timer.currentTime + tOffset, self.timerExpired) + + def timerExpired(self): + self.notify.debug('local timer expired') + self.dead = 1 + self.gameOver() + + def __initPosBroadcast(self): + self.__posBroadcastPeriod = 0.2 + self.__timeSinceLastPosBroadcast = 0.0 + self.__lastPosBroadcast = self.getAvatar(self.localAvId).getPos() + self.__storeStop = 0 + lt = self.getAvatar(self.localAvId) + lt.d_clearSmoothing() + lt.sendCurrentPosition() + + def __posBroadcast(self, dt): + self.__timeSinceLastPosBroadcast += dt + if self.__timeSinceLastPosBroadcast > self.__posBroadcastPeriod: + self.__timeSinceLastPosBroadcast -= self.__posBroadcastPeriod + self.getAvatar(self.localAvId).cnode.broadcastPosHprFull() + + def __spawnTreasureBoundsTask(self): + taskMgr.remove(self.TREASURE_BOUNDS_TASK) + taskMgr.add(self.__treasureBoundsTask, self.TREASURE_BOUNDS_TASK) + + def __killTreasureBoundsTask(self): + taskMgr.remove(self.TREASURE_BOUNDS_TASK) + + def __treasureBoundsTask(self, task): + for i in range(self.NUMTREASURES): + self.chestIcons[i].setPos(self.treasures[i].chest.getPos(render) / self.MAP_DIV) + self.chestIcons[i].setZ(self.chestIcons[i].getZ() + self.MAP_OFF) + if self.treasures[i].treasureNode.getZ() < -36: + self.treasures[i].treasureNode.setZ(-36) + if self.treasures[i].treasureNode.getX() < -20: + self.treasures[i].treasureNode.setX(-20) + if self.treasures[i].treasureNode.getX() > 20: + self.treasures[i].treasureNode.setX(20) + + return Task.cont + + def incrementScore(self, avId, newSpot, timestamp): + if not self.hasLocalToon: + return + newSpot += -15 + ts = globalClockDelta.localElapsedTime(timestamp) + toonSD = self.toonSDs[avId] + if avId == self.localAvId: + self.reachedFlag = 0 + if toonSD.status == 'treasure' and self.treasures and self.chestIcons: + for i in range(self.NUMTREASURES): + if self.treasures[i].grabbedId == avId: + self.treasures[i].treasureNode.wrtReparentTo(render) + self.treasures[i].grabbedId = 0 + seq = Sequence() + shrink = LerpScaleInterval(self.treasures[i].treasureNode, duration=1.0, startScale=self.treasures[i].treasureNode.getScale(), scale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + shrinkIcon = LerpScaleInterval(self.chestIcons[i], duration=1.0, startScale=self.chestIcons[i].getScale(), scale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + jump = ProjectileInterval(self.treasures[i].treasureNode, duration=1.0, startPos=self.treasures[i].treasureNode.getPos(), endPos=Point3(0, 0, 40), gravityMult=0.7) + shrinkJump = Parallel(shrink, shrinkIcon, jump) + toonSD.fsm.request('normal') + grow = LerpScaleInterval(self.treasures[i].treasureNode, duration=1.0, scale=self.treasures[i].treasureNode.getScale(), startScale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + growIcon = LerpScaleInterval(self.chestIcons[i], duration=1.0, scale=self.chestIcons[i].getScale(), startScale=Vec3(0.001, 0.001, 0.001), blendType='easeIn') + place = Parallel(Func(self.treasures[i].treasureNode.setPos, Vec3(newSpot, 0.25, -36)), Func(self.treasures[i].treasureNode.setHpr, Vec3(0, 0, 0))) + growItems = Parallel(grow, growIcon) + resetChest = Func(self.treasures[i].chestNode.setIntoCollideMask, DivingGameGlobals.CollideMask) + seq = Sequence(shrinkJump, Wait(1.5), place, growItems, resetChest) + self.treasures[i].moveLerp.pause() + self.treasures[i].moveLerp = seq + self.treasures[i].moveLerp.start(ts) + self.playSound('dropGold') + self.treasurePanel.incrScore() + + def __boatReached(self, collEntry): + toonSD = self.toonSDs[self.localAvId] + if toonSD.status == 'treasure' and not self.reachedFlag: + self.sendUpdate('treasureRecovered') + self.reachedFlag = 1 + + def __treasureGrabbed(self, collEntry): + avId = int(collEntry.getFromNodePath().getName()) + chestId = int(collEntry.getIntoNodePath().getName()) + toonSD = self.toonSDs[avId] + if toonSD.status == 'normal': + self.sendUpdate('pickupTreasure', [chestId]) + + def setTreasureDropped(self, avId, timestamp): + if not hasattr(self, 'treasures'): + return + ts = globalClockDelta.localElapsedTime(timestamp) + for i in range(self.NUMTREASURES): + if self.treasures[i].grabbedId == avId: + self.treasures[i].grabbedId = 0 + toonSD = self.toonSDs[avId] + dist = abs(36.0 + self.treasures[i].treasureNode.getZ(render)) + delta = dist / 72.0 + dur = 10 * delta + self.treasures[i].treasureNode.wrtReparentTo(render) + self.treasures[i].chestNode.setIntoCollideMask(BitMask32.allOff()) + resetChest = Func(self.treasures[i].chestNode.setIntoCollideMask, DivingGameGlobals.CollideMask) + self.treasures[i].moveLerp.pause() + self.treasures[i].moveLerp = Parallel(Sequence(Wait(1.0), resetChest), LerpFunc(self.treasures[i].treasureNode.setZ, duration=dur, fromData=self.treasures[i].treasureNode.getZ(render), toData=-36, blendType='easeIn')) + self.treasures[i].moveLerp.start(ts) + + def performCrabCollision(self, avId, timestamp): + if not self.hasLocalToon: + return + ts = globalClockDelta.localElapsedTime(timestamp) + toonSD = self.toonSDs[avId] + toon = self.getAvatar(avId) + distance = base.localAvatar.getDistance(toon) + volume = 0 + soundRange = 15.0 + if distance < soundRange: + volume = (soundRange - distance) / soundRange + if toonSD.status == 'normal' or toonSD.status == 'treasure': + self.localLerp.finish() + self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(3.0), Func(toonSD.fsm.request, 'normal')) + self.localLerp.start(ts) + self.hitSound.play() + self.hitSound.setVolume(volume) + + def performFishCollision(self, avId, spawnId, spawnerId, timestamp): + if not hasattr(self, 'spawners'): + return + toonSD = self.toonSDs[avId] + ts = globalClockDelta.localElapsedTime(timestamp) + toon = self.getAvatar(avId) + distance = base.localAvatar.getDistance(toon) + volume = 0 + soundRange = 15.0 + if distance < soundRange: + volume = (soundRange - distance) / soundRange + if toonSD.status == 'normal' or toonSD.status == 'treasure': + self.localLerp.finish() + self.localLerp = Sequence(Func(toonSD.fsm.request, 'freeze'), Wait(3.0), Func(toonSD.fsm.request, 'normal')) + self.localLerp.start(ts) + if self.spawners[spawnerId].fishArray.has_key(spawnId): + fish = self.spawners[spawnerId].fishArray[spawnId] + endX = self.spawners[spawnerId].position.getX() + if fish.name == 'clown': + fishSoundName = 'Clownfish.mp3' + elif fish.name == 'pbj': + fishSoundName = 'PBJ_Fish.mp3' + elif fish.name == 'balloon': + fishSoundName = 'BalloonFish.mp3' + elif fish.name == 'bear': + fishSoundName = 'Bear_Acuda.mp3' + elif fish.name == 'nurse': + fishSoundName = 'Nurse_Shark.mp3' + elif fish.name == 'piano': + fishSoundName = 'Piano_Tuna.mp3' + else: + fishSoundName = ' ' + fishSoundPath = 'phase_4/audio/sfx/%s' % fishSoundName + fish.sound = loader.loadSfx(fishSoundPath) + if fish.sound: + fish.sound.play() + fish.sound.setVolume(volume) + self.hitSound.play() + self.hitSound.setVolume(volume) + if fish.name is 'bear' or fish.name is 'nurse': + return + colList = fish.findAllMatches('**/fc*') + for col in colList: + col.removeNode() + + fish.moveLerp.pause() + if fish.name == 'clown' or fish.name == 'piano': + if fish.name != 'piano': + endHpr = Vec3(fish.getH() * -1, 0, 0) + elif fish.direction == -1: + endHpr = Vec3(180, 0, 0) + else: + endHpr = Vec3(0, 0, 0) + fish.moveLerp = Sequence(LerpHprInterval(fish, duration=0.4, startHpr=fish.getHpr(), hpr=endHpr), LerpFunc(fish.setX, duration=1.5, fromData=fish.getX(), toData=endX), Func(self.fishRemove, str(spawnerId) + str(spawnId))) + elif fish.name == 'pbj': + fish.moveLerp = Sequence(LerpFunc(fish.setX, duration=2, fromData=fish.getX(), toData=endX), Func(self.fishRemove, str(spawnerId) + str(spawnId))) + elif fish.name == 'balloon': + fish.specialLerp.pause() + anim = Func(fish.play, 'anim', fromFrame=110, toFrame=200) + fish.setH(180) + speed = Func(fish.setPlayRate, 3.0, 'anim') + fish.moveLerp = Sequence(Func(fish.stop, 'anim'), speed, anim, Wait(1.0), LerpScaleInterval(fish, duration=0.8, startScale=fish.getScale, scale=0.001, blendType='easeIn'), Func(self.fishRemove, str(spawnerId) + str(spawnId))) + fish.sound.setTime(11.5) + fish.moveLerp.start(ts) + + def fishRemove(self, code): + spawnId = int(code[1:len(code)]) + spawnerId = int(code[0]) + if self.spawners[spawnerId].fishArray.has_key(spawnId): + fish = self.spawners[spawnerId].fishArray[spawnId] + fish.specialLerp.finish() + fish.moveLerp.finish() + fish.specialLerp = None + fish.moveLerp = None + fish.removeNode() + del fish + del self.spawners[spawnerId].fishArray[spawnId] + else: + import pdb + pdb.setTrace() + return + + def setTreasureGrabbed(self, avId, chestId): + if not self.hasLocalToon: + return + toonSD = self.toonSDs.get(avId) + if toonSD and toonSD.status == 'normal': + toonSD.fsm.request('treasure') + self.treasures[chestId].moveLerp.pause() + self.treasures[chestId].moveLerp = Sequence() + self.treasures[chestId].chestNode.setIntoCollideMask(BitMask32.allOff()) + self.treasures[chestId].treasureNode.reparentTo(self.getAvatar(avId)) + headparts = self.getAvatar(avId).getHeadParts() + pos = headparts[2].getPos() + self.treasures[chestId].treasureNode.setPos(pos + Point3(0, 0.2, 3)) + self.treasures[chestId].grabbedId = avId + timestamp = globalClockDelta.getFrameNetworkTime() + self.playSound('getGold') + + def __spawnCrabTask(self): + taskMgr.remove(self.CRAB_TASK) + taskMgr.add(self.__crabTask, self.CRAB_TASK) + + def __killCrabTask(self): + taskMgr.remove(self.CRAB_TASK) + + def __crabTask(self, task): + dt = globalClock.getDt() + for crab in self.crabs: + if not crab.moveLerp.isPlaying(): + crab.moveLerp = Wait(1.0) + crab.moveLerp.loop() + self.sendUpdate('getCrabMoving', [crab.crabId, crab.getX(), crab.direction]) + return Task.cont + + return Task.cont + + def setCrabMoving(self, crabId, timestamp, rand1, rand2, crabX, dir): + if self.dead == 1: + self.__killCrabTask() + return + if not hasattr(self, 'crabs'): + return + crab = self.crabs[crabId] + ts = globalClockDelta.localElapsedTime(timestamp) + x = 0 + for i in range(self.NUMTREASURES): + x += self.treasures[i].treasureNode.getX(render) + + x /= self.NUMTREASURES + goalX = int(x + dir * (rand1 / 10.0) * 12 + 4.0) + goalZ = -40 + 5 + 5 * (rand2 / 10.0) + xTime = 1 + rand1 / 10.0 * 2 + zTime = 0.5 + rand2 / 10.0 + wait = rand1 / 10.0 + rand2 / 10.0 + 1 + crab.direction *= -1 + if goalX > 20: + goalX = 20 + elif goalX < -20: + goalX = 20 + loc = crab.getPos(render) + distance = base.localAvatar.getDistance(crab) + crabVolume = 0 + soundRange = 25.0 + if distance < soundRange: + crabVolume = (soundRange - distance) / soundRange + crabSoundInterval = SoundInterval(self.crabSound, loop=0, duration=1.6, startTime=0.0) + seq = Sequence(Wait(wait), LerpPosInterval(crab, duration=xTime, startPos=Point3(crabX, 0, -40), pos=Point3(goalX, 0, -40), blendType='easeIn'), Parallel(Func(self.grabCrapVolume, crab), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, -40), pos=Point3(goalX, 0, goalZ), blendType='easeOut')), LerpPosInterval(crab, duration=zTime, startPos=Point3(goalX, 0, goalZ), pos=Point3(goalX, 0, -40), blendType='easeInOut')) + crab.moveLerp.pause() + crab.moveLerp = seq + crab.moveLerp.start(ts) + + def grabCrapVolume(self, crab): + if crab: + distance = base.localAvatar.getDistance(crab) + self.crabVolume = 0 + soundRange = 25.0 + if distance < soundRange: + crabVolume = (soundRange - distance) / soundRange + crabSoundInterval = SoundInterval(self.crabSound, loop=0, duration=1.6, startTime=0.0, volume=crabVolume) + crabSoundInterval.start() + + def __spawnUpdateLocalToonTask(self): + self.__initPosBroadcast() + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + taskMgr.add(self.__updateLocalToonTask, self.UPDATE_LOCALTOON_TASK) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + toonPos = base.localAvatar.getPos() + toonHpr = base.localAvatar.getHpr() + self.xVel *= 0.99 + self.zVel *= 0.99 + pos = [toonPos[0], toonPos[1], toonPos[2]] + hpr = [toonHpr[0], toonHpr[1], toonHpr[2]] + r = 0 + toonSD = self.toonSDs[self.localAvId] + if toonSD.status == 'normal' or toonSD.status == 'treasure': + if self.arrowKeys.leftPressed(): + r -= 80 + if self.arrowKeys.rightPressed(): + r += 80 + hpr[2] += r * dt + pos1 = self.orientNode.getPos(render) + pos2 = self.orientNode2.getPos(render) + upVec = Vec2(pos1[0], pos1[2]) + bkVec = Vec2(pos2[0], pos2[2]) + forVec = upVec - Vec2(pos[0], pos[2]) + bckVec = bkVec - Vec2(pos[0], pos[2]) + r = 0 + if self.arrowKeys.upPressed(): + r += 20 + self.xVel = forVec[0] * 8 + self.zVel = forVec[1] * 8 + elif self.arrowKeys.downPressed(): + r -= 20 + self.xVel = bckVec[0] * 4 + self.zVel = bckVec[1] * 4 + if self.xVel > 20: + self.xVel = 20 + elif self.xVel < -20: + self.xVel = -20 + if self.zVel > 10: + self.zVel = 10 + elif self.zVel < -10: + self.zVel = -10 + swimVolume = (abs(self.zVel) + abs(self.xVel)) / 15.0 + self.swimSound.setVolume(swimVolume) + pos[0] += self.xVel * dt + pos[1] = -2 + pos[2] += self.zVel * dt + found = 0 + for i in range(self.NUMTREASURES): + if self.treasures[i].grabbedId == self.localAvId: + found = 1 + i = self.NUMTREASURES + 1 + pos[2] -= 0.8 * dt + + if found == 0: + pos[2] += 0.8 * dt + if pos[2] < -38: + pos[2] = -38 + elif pos[2] > 36: + pos[2] = 36 + if pos[0] < -20: + pos[0] = -20 + elif pos[0] > 20: + pos[0] = 20 + base.localAvatar.setPos(pos[0], pos[1], pos[2]) + base.localAvatar.setHpr(hpr[0], hpr[1], hpr[2]) + posDiv = self.MAP_DIV + self.mapAvatars[self.localAvId].setPos(pos[0] / posDiv, pos[1] / posDiv, pos[2] / posDiv + self.MAP_OFF) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + pos = toon.getPos() + self.mapAvatars[avId].setPos(pos / posDiv) + self.mapAvatars[avId].setZ(self.mapAvatars[avId].getZ() + self.MAP_OFF) + + self.cTrav.traverse(render) + self.cTrav2.traverse(render) + self.__posBroadcast(dt) + z = self.getAvatar(self.localAvId).getZ() + 3 + if z < -25: + z = -25 + camera.setZ(z) + ambVolume = abs(z - 25.0) / 50.0 + 0.1 + if ambVolume < 0.0: + ambVolume = 0.0 + if ambVolume > 1.0: + ambVolume = 1.0 + ambVolume = pow(ambVolume, 0.75) + self.sndAmbience.setVolume(ambVolume) + return Task.cont + + def exitSwim(self): + self.music.stop() + self.ignore('resetClock') + self.__killUpdateLocalToonTask() + self.__killCrabTask() + self.__killTreasureBoundsTask() + self.timer.stop() + self.timer.destroy() + self.localLerp.finish() + self.introMovie.finish() + self.boatTilt.finish() + self.treasurePanel.cleanup() + self.mapAvatars[self.localAvId].destroy() + del self.mapAvatars + for i in range(self.NUMTREASURES): + del self.chestIcons[i] + + del self.timer + + def enterCleanup(self): + pass + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedDivingGameAI.py b/toontown/minigame/DistributedDivingGameAI.py new file mode 100644 index 0000000..b70f323 --- /dev/null +++ b/toontown/minigame/DistributedDivingGameAI.py @@ -0,0 +1,436 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.actor import Actor +import DivingGameGlobals +import random +import random +import types + +class DistributedDivingGameAI(DistributedMinigameAI): + fishProportions = [] + for i in range(6): + fishProportions.append([]) + + n = 100 + fishProportions[0] + fishProportions[0].append(([0, 0.8], + [0.8, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.8], + [0.8, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.7], + [0.7, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([0, 0.5], + [0.5, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[0].append(([n, 0.5], + [0.5, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[1] + fishProportions[1].append(([0, 0.8], + [0.8, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[1].append(([0, 0.8], + [0.8, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[1].append(([0, 0.7], + [0.7, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[1].append(([0, 0.7], + [0.7, 0.9], + [n, n], + [n, n], + [n, n], + [0.9, 1])) + fishProportions[1].append(([0, 0.4], + [0.4, 0.8], + [n, n], + [n, n], + [n, n], + [0.8, 1])) + fishProportions[1].append(([n, 0.3], + [0.3, 0.6], + [n, n], + [n, n], + [n, n], + [0.6, 1])) + fishProportions[2] + fishProportions[2].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[2].append(([0, 0.6], + [0.6, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[2].append(([0, 0.6], + [0.6, 0.8], + [n, n], + [0.8, 1], + [n, n], + [n, n])) + fishProportions[2].append(([0, 0.5], + [0.5, 0.7], + [n, n], + [0.7, 0.9], + [n, n], + [0.9, 1])) + fishProportions[2].append(([0, 0.2], + [0.2, 0.4], + [n, n], + [0.4, 0.75], + [n, n], + [0.75, 1])) + fishProportions[2].append(([n, 0.2], + [0.2, 0.6], + [n, n], + [0.6, 0.8], + [n, n], + [0.8, 1])) + fishProportions[3] + fishProportions[3].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[3].append(([0, 0.6], + [0.6, 1], + [n, n], + [n, n], + [n, n], + [n, n])) + fishProportions[3].append(([0, 0.6], + [0.6, 0.8], + [n, n], + [0.95, 1], + [n, n], + [n, n])) + fishProportions[3].append(([0, 0.5], + [0.5, 0.7], + [n, n], + [0.7, 0.85], + [0.9, 0.95], + [0.95, 1])) + fishProportions[3].append(([0, 0.2], + [0.2, 0.4], + [n, n], + [0.4, 0.75], + [0.75, 0.85], + [0.85, 1])) + fishProportions[3].append(([n, 0.2], + [0.2, 0.6], + [n, n], + [0.6, 0.8], + [n, n], + [0.8, 1])) + fishProportions[4] + fishProportions[4].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[4].append(([0, 0.45], + [0.45, 0.9], + [n, n], + [0.9, 1], + [n, n], + [n, n])) + fishProportions[4].append(([0, 0.2], + [0.2, 0.5], + [n, n], + [0.5, 0.95], + [0.95, 1], + [n, n])) + fishProportions[4].append(([0, 0.1], + [0.1, 0.3], + [n, n], + [0.3, 0.75], + [0.75, 0.8], + [0.8, 1])) + fishProportions[4].append(([n, n], + [0, 0.15], + [n, n], + [0.15, 0.4], + [n, n], + [0.4, 1])) + fishProportions[4].append(([n, n], + [n, n], + [n, n], + [0, 0.4], + [n, n], + [0.6, 1])) + fishProportions[5] + fishProportions[5].append(([0, 0.7], + [0.7, 0.9], + [0.9, 1], + [n, n], + [n, n], + [n, n])) + fishProportions[5].append(([0, 0.45], + [0.45, 0.9], + [n, n], + [0.9, 1], + [n, n], + [n, n])) + fishProportions[5].append(([0, 0.2], + [0.2, 0.5], + [n, n], + [0.5, 0.95], + [0.95, 1], + [n, n])) + fishProportions[5].append(([0, 0.1], + [0.1, 0.3], + [n, n], + [0.3, 0.75], + [0.75, 0.8], + [0.8, 1])) + fishProportions[5].append(([n, n], + [0, 0.15], + [n, n], + [0.15, 0.4], + [n, n], + [0.4, 1])) + fishProportions[5].append(([n, n], + [n, n], + [n, n], + [0, 0.4], + [n, n], + [0.6, 1])) + difficultyPatternsAI = {ToontownGlobals.ToontownCentral: [3.5, fishProportions[0], 1.5], + ToontownGlobals.DonaldsDock: [3.0, fishProportions[1], 1.8], + ToontownGlobals.DaisyGardens: [2.5, fishProportions[2], 2.1], + ToontownGlobals.MinniesMelodyland: [2.0, fishProportions[3], 2.4], + ToontownGlobals.TheBrrrgh: [2.0, fishProportions[4], 2.7], + ToontownGlobals.DonaldsDreamland: [1.5, fishProportions[5], 3.0]} + + def __init__(self, air, minigameId): + try: + self.DistributedDivingGameAI_initialized + except: + self.DistributedDivingGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedDivingGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['swimming']), State.State('swimming', self.enterSwimming, self.exitSwimming, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.__timeBase = globalClockDelta.localToNetworkTime(globalClock.getRealTime()) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.sendUpdate('setTrolleyZone', [self.trolleyZone]) + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + + self.SPAWNTIME = self.difficultyPatternsAI[self.getSafezoneId()][0] + self.proportion = self.difficultyPatternsAI[self.getSafezoneId()][1] + self.REWARDMOD = self.difficultyPatternsAI[self.getSafezoneId()][2] + DistributedMinigameAI.setGameReady(self) + self.spawnings = [] + for i in range(DivingGameGlobals.NUM_SPAWNERS): + self.spawnings.append(Sequence(Func(self.spawnFish, i), Wait(self.SPAWNTIME + random.random()), Func(self.spawnFish, i), Wait(self.SPAWNTIME - 0.5 + random.random()))) + self.spawnings[i].loop() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('swimming') + self.scoreTracking = {} + for avId in self.scoreDict.keys(): + self.scoreTracking[avId] = [0, + 0, + 0, + 0, + 0] + + def getCrabMoving(self, crabId, crabX, dir): + timestamp = globalClockDelta.getFrameNetworkTime() + rand1 = int(random.random() * 10) + rand2 = int(random.random() * 10) + self.sendUpdate('setCrabMoving', [crabId, + timestamp, + rand1, + rand2, + crabX, + dir]) + + def treasureRecovered(self): + if not hasattr(self, 'scoreTracking'): + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.treasureRecovered: invalid avId') + return + timestamp = globalClockDelta.getFrameNetworkTime() + newSpot = int(random.random() * 30) + self.scoreTracking[avId][4] += 1 + for someAvId in self.scoreDict.keys(): + if someAvId == avId: + self.scoreDict[avId] += 10 * (self.REWARDMOD * 0.25) + self.scoreDict[someAvId] += 10 * (self.REWARDMOD * 0.75 / float(len(self.scoreDict.keys()))) + + self.sendUpdate('incrementScore', [avId, newSpot, timestamp]) + + def hasScoreMult(self): + return 0 + + def setGameAbort(self): + self.notify.debug('setGameAbort') + taskMgr.remove(self.taskName('gameTimer')) + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + trackingString = 'MiniGame Stats : Diving Game' + trackingString += '\nDistrict:%s' % self.getSafezoneId() + for avId in self.scoreTracking.keys(): + trackingString = trackingString + '\navId:%s fishHits:%s crabHits:%s treasureCatches:%s treasureDrops:%s treasureRecoveries:%s Score: %s' % (avId, + self.scoreTracking[avId][0], + self.scoreTracking[avId][1], + self.scoreTracking[avId][2], + self.scoreTracking[avId][3], + self.scoreTracking[avId][4], + self.scoreDict[avId]) + + self.air.writeServerEvent('MiniGame Stats', None, trackingString) + return + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def enterSwimming(self): + self.notify.debug('enterSwimming') + duration = 65.0 + taskMgr.doMethodLater(duration, self.timerExpired, self.taskName('gameTimer')) + + def timerExpired(self, task): + self.notify.debug('timer expired') + for avId in self.scoreDict.keys(): + if self.scoreDict[avId] < 5: + self.scoreDict[avId] = 5 + + self.gameOver() + return Task.done + + def exitSwimming(self): + for i in range(DivingGameGlobals.NUM_SPAWNERS): + self.spawnings[i].pause() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + for i in range(DivingGameGlobals.NUM_SPAWNERS): + self.spawnings[i].finish() + + del self.spawnings + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def pickupTreasure(self, chestId): + if not hasattr(self, 'scoreTracking'): + return + timestamp = globalClockDelta.getFrameNetworkTime() + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.pickupTreasure: invalid avId') + return + self.scoreTracking[avId][2] += 1 + self.sendUpdate('setTreasureGrabbed', [avId, chestId]) + + def spawnFish(self, spawnerId): + timestamp = globalClockDelta.getFrameNetworkTime() + props = self.proportion[spawnerId] + num = random.random() + for i in range(len(props)): + prop = props[i] + low = prop[0] + high = prop[1] + if num > low and num <= high: + offset = int(10 * random.random()) + self.sendUpdate('fishSpawn', [timestamp, + i, + spawnerId, + offset]) + return + + def handleCrabCollision(self, avId, status): + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.handleCrabCollision: invalid avId') + return + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setTreasureDropped', [avId, timestamp]) + self.scoreTracking[avId][1] += 1 + if status == 'normal' or status == 'treasure': + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('performCrabCollision', [avId, timestamp]) + if status == 'treasure': + self.scoreTracking[avId][3] += 1 + + def handleFishCollision(self, avId, spawnId, spawnerId, status): + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'DivingGameAI.handleFishCollision: invalid avId') + return + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setTreasureDropped', [avId, timestamp]) + timestamp = globalClockDelta.getFrameNetworkTime() + self.scoreTracking[avId][0] += 1 + if status == 'treasure': + self.scoreTracking[avId][3] += 1 + self.sendUpdate('performFishCollision', [avId, + spawnId, + spawnerId, + timestamp]) diff --git a/toontown/minigame/DistributedIceGame.py b/toontown/minigame/DistributedIceGame.py new file mode 100644 index 0000000..f8b2714 --- /dev/null +++ b/toontown/minigame/DistributedIceGame.py @@ -0,0 +1,1054 @@ +import math +from pandac.PandaModules import Vec3, deg2Rad, Point3, NodePath, VBase4, CollisionHandlerEvent, CollisionNode, CollisionSphere +from direct.fsm import ClassicFSM, State +from direct.distributed.ClockDelta import globalClockDelta +from direct.gui.DirectGui import DirectLabel +from direct.interval.IntervalGlobal import Sequence, LerpScaleInterval, LerpFunctionInterval, Func, Parallel, LerpPosInterval, Wait, SoundInterval, LerpColorScaleInterval +from toontown.toonbase import ToontownGlobals, TTLocalizer, ToontownTimer +from toontown.minigame import ArrowKeys +from toontown.minigame import DistributedMinigame +from toontown.minigame import DistributedIceWorld +from toontown.minigame import IceGameGlobals +from toontown.minigame import MinigameAvatarScorePanel +from toontown.minigame import IceTreasure + +class DistributedIceGame(DistributedMinigame.DistributedMinigame, DistributedIceWorld.DistributedIceWorld): + notify = directNotify.newCategory('DistributedIceGame') + MaxLocalForce = 100 + MaxPhysicsForce = 25000 + + def __init__(self, cr): + DistributedMinigame.DistributedMinigame.__init__(self, cr) + DistributedIceWorld.DistributedIceWorld.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedIceGame', [State.State('off', self.enterOff, self.exitOff, ['inputChoice']), + State.State('inputChoice', self.enterInputChoice, self.exitInputChoice, ['waitServerChoices', + 'moveTires', + 'displayVotes', + 'cleanup']), + State.State('waitServerChoices', self.enterWaitServerChoices, self.exitWaitServerChoices, ['moveTires', 'cleanup']), + State.State('moveTires', self.enterMoveTires, self.exitMoveTires, ['synch', 'cleanup']), + State.State('synch', self.enterSynch, self.exitSynch, ['inputChoice', 'scoring', 'cleanup']), + State.State('scoring', self.enterScoring, self.exitScoring, ['cleanup', 'finalResults', 'inputChoice']), + State.State('finalResults', self.enterFinalResults, self.exitFinalResults, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cameraThreeQuarterView = (0, -22, 45, 0, -62.89, 0) + self.tireDict = {} + self.forceArrowDict = {} + self.canDrive = False + self.timer = None + self.timerStartTime = None + self.curForce = 0 + self.curHeading = 0 + self.headingMomentum = 0.0 + self.forceMomentum = 0.0 + self.allTireInputs = None + self.curRound = 0 + self.curMatch = 0 + self.controlKeyWarningLabel = DirectLabel(text=TTLocalizer.IceGameControlKeyWarning, text_fg=VBase4(1, 0, 0, 1), relief=None, pos=(0.0, 0, 0), scale=0.15) + self.controlKeyWarningLabel.hide() + self.waitingMoveLabel = DirectLabel(text=TTLocalizer.IceGameWaitingForPlayersToFinishMove, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingMoveLabel.hide() + self.waitingSyncLabel = DirectLabel(text=TTLocalizer.IceGameWaitingForAISync, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingSyncLabel.hide() + self.infoLabel = DirectLabel(text='', text_fg=VBase4(0, 0, 0, 1), relief=None, pos=(0.0, 0, 0.7), scale=0.075) + self.updateInfoLabel() + self.lastForceArrowUpdateTime = 0 + self.sendForceArrowUpdateAsap = False + self.treasures = [] + self.penalties = [] + self.obstacles = [] + self.controlKeyPressed = False + self.controlKeyWarningIval = None + return + + def delete(self): + DistributedIceWorld.DistributedIceWorld.delete(self) + DistributedMinigame.DistributedMinigame.delete(self) + if self.controlKeyWarningIval: + self.controlKeyWarningIval.finish() + self.controlKeyWarningIval = None + self.controlKeyWarningLabel.destroy() + del self.controlKeyWarningLabel + self.waitingMoveLabel.destroy() + del self.waitingMoveLabel + self.waitingSyncLabel.destroy() + del self.waitingSyncLabel + self.infoLabel.destroy() + del self.infoLabel + for treasure in self.treasures: + treasure.destroy() + + del self.treasures + for penalty in self.penalties: + penalty.destroy() + + del self.penalties + for obstacle in self.obstacles: + obstacle.removeNode() + + del self.obstacles + del self.gameFSM + return + + def announceGenerate(self): + DistributedMinigame.DistributedMinigame.announceGenerate(self) + DistributedIceWorld.DistributedIceWorld.announceGenerate(self) + self.debugTaskName = self.uniqueName('debugTask') + + def getTitle(self): + return TTLocalizer.IceGameTitle + + def getInstructions(self): + szId = self.getSafezoneId() + numPenalties = IceGameGlobals.NumPenalties[szId] + result = TTLocalizer.IceGameInstructions + if numPenalties == 0: + result = TTLocalizer.IceGameInstructionsNoTnt + return result + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.DistributedMinigame.load(self) + self.music = base.loadMusic('phase_4/audio/bgm/MG_IceGame.mid') + self.gameBoard = loader.loadModel('phase_4/models/minigames/ice_game_icerink') + background = loader.loadModel('phase_4/models/minigames/ice_game_2d') + background.reparentTo(self.gameBoard) + self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.gameBoard.setScale(1.0) + self.setupSimulation() + index = 0 + for avId in self.avIdList: + self.setupTire(avId, index) + self.setupForceArrow(avId) + index += 1 + + for index in xrange(len(self.avIdList), 4): + self.setupTire(-index, index) + self.setupForceArrow(-index) + + self.showForceArrows(realPlayersOnly=True) + self.westWallModel = NodePath() + if not self.westWallModel.isEmpty(): + self.westWallModel.reparentTo(self.gameBoard) + self.westWallModel.setPos(IceGameGlobals.MinWall[0], IceGameGlobals.MinWall[1], 0) + self.westWallModel.setScale(4) + self.eastWallModel = NodePath() + if not self.eastWallModel.isEmpty(): + self.eastWallModel.reparentTo(self.gameBoard) + self.eastWallModel.setPos(IceGameGlobals.MaxWall[0], IceGameGlobals.MaxWall[1], 0) + self.eastWallModel.setScale(4) + self.eastWallModel.setH(180) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.target = loader.loadModel('phase_3/models/misc/sphere') + self.target.setScale(0.01) + self.target.reparentTo(self.gameBoard) + self.target.setPos(0, 0, 0) + self.scoreCircle = loader.loadModel('phase_4/models/minigames/ice_game_score_circle') + self.scoreCircle.setScale(0.01) + self.scoreCircle.reparentTo(self.gameBoard) + self.scoreCircle.setZ(IceGameGlobals.TireRadius / 2.0) + self.scoreCircle.setAlphaScale(0.5) + self.scoreCircle.setTransparency(1) + self.scoreCircle.hide() + self.treasureModel = loader.loadModel('phase_4/models/minigames/ice_game_barrel') + self.penaltyModel = loader.loadModel('phase_4/models/minigames/ice_game_tnt2') + self.penaltyModel.setScale(0.75, 0.75, 0.7) + szId = self.getSafezoneId() + obstacles = IceGameGlobals.Obstacles[szId] + index = 0 + cubicObstacle = IceGameGlobals.ObstacleShapes[szId] + for pos in obstacles: + newPos = Point3(pos[0], pos[1], IceGameGlobals.TireRadius) + newObstacle = self.createObstacle(newPos, index, cubicObstacle) + self.obstacles.append(newObstacle) + index += 1 + + self.countSound = loader.loadSfx('phase_3.5/audio/sfx/tick_counter.mp3') + self.treasureGrabSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bananas.mp3') + self.penaltyGrabSound = loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.tireSounds = [] + for tireIndex in xrange(4): + tireHit = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_1.mp3') + wallHit = loader.loadSfx('phase_4/audio/sfx/MG_maze_pickup.mp3') + obstacleHit = loader.loadSfx('phase_4/audio/sfx/Golf_Hit_Barrier_2.mp3') + self.tireSounds.append({'tireHit': tireHit, + 'wallHit': wallHit, + 'obstacleHit': obstacleHit}) + + self.arrowRotateSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_force_rotate.wav') + self.arrowUpSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_force_increase_3sec.mp3') + self.arrowDownSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_force_decrease_3sec.mp3') + self.scoreCircleSound = loader.loadSfx('phase_4/audio/sfx/MG_sfx_ice_scoring_1.mp3') + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.DistributedMinigame.unload(self) + del self.music + self.gameBoard.removeNode() + del self.gameBoard + for forceArrow in self.forceArrowDict.values(): + forceArrow.removeNode() + + del self.forceArrowDict + self.scoreCircle.removeNode() + del self.scoreCircle + del self.countSound + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.DistributedMinigame.onstage(self) + self.gameBoard.reparentTo(render) + self.__placeToon(self.localAvId) + self.moveCameraToTop() + self.scorePanels = [] + base.playMusic(self.music, looping=1, volume=0.8) + + def offstage(self): + self.notify.debug('offstage') + self.music.stop() + self.gameBoard.hide() + self.infoLabel.hide() + for avId in self.tireDict: + self.tireDict[avId]['tireNodePath'].hide() + + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + for obstacle in self.obstacles: + obstacle.hide() + + for treasure in self.treasures: + treasure.nodePath.hide() + + for penalty in self.penalties: + penalty.nodePath.hide() + + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + taskMgr.remove(self.uniqueName('aimtask')) + self.arrowKeys.destroy() + del self.arrowKeys + DistributedMinigame.DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.DistributedMinigame.handleDisabledAvatar(self, avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.DistributedMinigame.setGameReady(self): + return + for index in xrange(self.numPlayers): + avId = self.avIdList[index] + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.forwardSpeed = 0 + toon.rotateSpeed = False + toon.dropShadow.hide() + toon.setAnimState('Sit') + if avId in self.tireDict: + tireNp = self.tireDict[avId]['tireNodePath'] + toon.reparentTo(tireNp) + toon.setY(1.0) + toon.setZ(-3) + toon.startLookAround() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.DistributedMinigame.setGameStart(self, timestamp) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.stopLookAround() + + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(0.75 - spacing * (self.numPlayers - 1 - i), 0.0, 0.875) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + self.arrowKeys.setPressHandlers([self.__upArrowPressed, + self.__downArrowPressed, + self.__leftArrowPressed, + self.__rightArrowPressed, + self.__controlPressed]) + + def isInPlayState(self): + if not self.gameFSM.getCurrentState(): + return False + if not self.gameFSM.getCurrentState().getName() == 'play': + return False + return True + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterInputChoice(self): + self.notify.debug('enterInputChoice') + self.forceLocalToonToTire() + self.controlKeyPressed = False + if self.curRound == 0: + self.setupStartOfMatch() + else: + self.notify.debug('self.curRound = %s' % self.curRound) + self.timer = ToontownTimer.ToontownTimer() + self.timer.hide() + if self.timerStartTime != None: + self.startTimer() + self.showForceArrows(realPlayersOnly=True) + self.localForceArrow().setPosHpr(0, 0, -1.0, 0, 0, 0) + self.localForceArrow().reparentTo(self.localTireNp()) + self.localForceArrow().setY(IceGameGlobals.TireRadius) + self.localTireNp().headsUp(self.target) + self.notify.debug('self.localForceArrow() heading = %s' % self.localForceArrow().getH()) + self.curHeading = self.localTireNp().getH() + self.curForce = 25 + self.updateLocalForceArrow() + for avId in self.forceArrowDict: + forceArrow = self.forceArrowDict[avId] + forceArrow.setPosHpr(0, 0, -1.0, 0, 0, 0) + tireNp = self.tireDict[avId]['tireNodePath'] + forceArrow.reparentTo(tireNp) + forceArrow.setY(IceGameGlobals.TireRadius) + tireNp.headsUp(self.target) + self.updateForceArrow(avId, tireNp.getH(), 25) + + taskMgr.add(self.__aimTask, self.uniqueName('aimtask')) + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.stop() + self.sendForceArrowUpdateAsap = False + return + + def exitInputChoice(self): + if not self.controlKeyPressed: + if self.controlKeyWarningIval: + self.controlKeyWarningIval.finish() + self.controlKeyWarningIval = None + self.controlKeyWarningIval = Sequence(Func(self.controlKeyWarningLabel.show), self.controlKeyWarningLabel.colorScaleInterval(10, VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Func(self.controlKeyWarningLabel.hide)) + self.controlKeyWarningIval.start() + if self.timer != None: + self.timer.destroy() + self.timer = None + self.timerStartTime = None + self.hideForceArrows() + self.arrowRotateSound.stop() + self.arrowUpSound.stop() + self.arrowDownSound.stop() + taskMgr.remove(self.uniqueName('aimtask')) + return + + def enterWaitServerChoices(self): + self.waitingMoveLabel.show() + self.showForceArrows(True) + + def exitWaitServerChoices(self): + self.waitingMoveLabel.hide() + self.hideForceArrows() + + def enterMoveTires(self): + for key in self.tireDict: + body = self.tireDict[key]['tireBody'] + body.setAngularVel(0, 0, 0) + body.setLinearVel(0, 0, 0) + + for index in xrange(len(self.allTireInputs)): + input = self.allTireInputs[index] + avId = self.avIdList[index] + body = self.getTireBody(avId) + degs = input[1] + 90 + tireNp = self.getTireNp(avId) + tireH = tireNp.getH() + self.notify.debug('tireH = %s' % tireH) + radAngle = deg2Rad(degs) + foo = NodePath('foo') + dirVector = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + self.notify.debug('dirVector is now=%s' % dirVector) + inputForce = input[0] + inputForce /= self.MaxLocalForce + inputForce *= self.MaxPhysicsForce + force = dirVector * inputForce + self.notify.debug('adding force %s to %d' % (force, avId)) + body.addForce(force) + + self.enableAllTireBodies() + self.totalPhysicsSteps = 0 + self.startSim() + taskMgr.add(self.__moveTiresTask, self.uniqueName('moveTiresTtask')) + + def exitMoveTires(self): + self.forceLocalToonToTire() + self.disableAllTireBodies() + self.stopSim() + self.notify.debug('total Physics steps = %d' % self.totalPhysicsSteps) + taskMgr.remove(self.uniqueName('moveTiresTtask')) + + def enterSynch(self): + self.waitingSyncLabel.show() + + def exitSynch(self): + self.waitingSyncLabel.hide() + + def enterScoring(self): + sortedByDistance = [] + for avId in self.avIdList: + np = self.getTireNp(avId) + pos = np.getPos() + pos.setZ(0) + sortedByDistance.append((avId, pos.length())) + + def compareDistance(x, y): + if x[1] - y[1] > 0: + return 1 + elif x[1] - y[1] < 0: + return -1 + else: + return 0 + + sortedByDistance.sort(cmp=compareDistance) + self.scoreMovie = Sequence() + curScale = 0.01 + curTime = 0 + self.scoreCircle.setScale(0.01) + self.scoreCircle.show() + self.notify.debug('newScores = %s' % self.newScores) + circleStartTime = 0 + for index in xrange(len(sortedByDistance)): + distance = sortedByDistance[index][1] + avId = sortedByDistance[index][0] + scorePanelIndex = self.avIdList.index(avId) + time = (distance - curScale) / IceGameGlobals.ExpandFeetPerSec + if time < 0: + time = 0.01 + scaleXY = distance + IceGameGlobals.TireRadius + self.notify.debug('circleStartTime = %s' % circleStartTime) + self.scoreMovie.append(Parallel(LerpScaleInterval(self.scoreCircle, time, Point3(scaleXY, scaleXY, 1.0)), SoundInterval(self.scoreCircleSound, duration=time, startTime=circleStartTime))) + circleStartTime += time + startScore = self.scorePanels[scorePanelIndex].getScore() + destScore = self.newScores[scorePanelIndex] + self.notify.debug('for avId %d, startScore=%d, newScores=%d' % (avId, startScore, destScore)) + + def increaseScores(t, scorePanelIndex = scorePanelIndex, startScore = startScore, destScore = destScore): + oldScore = self.scorePanels[scorePanelIndex].getScore() + diff = destScore - startScore + newScore = int(startScore + diff * t) + if newScore > oldScore: + base.playSfx(self.countSound) + self.scorePanels[scorePanelIndex].setScore(newScore) + self.scores[scorePanelIndex] = newScore + + duration = (destScore - startScore) * IceGameGlobals.ScoreCountUpRate + tireNp = self.tireDict[avId]['tireNodePath'] + self.scoreMovie.append(Parallel(LerpFunctionInterval(increaseScores, duration), Sequence(LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 0, 0, 1)), LerpColorScaleInterval(tireNp, duration / 6.0, VBase4(1, 1, 1, 1))))) + curScale += distance + + self.scoreMovie.append(Func(self.sendUpdate, 'reportScoringMovieDone', [])) + self.scoreMovie.start() + + def exitScoring(self): + self.scoreMovie.finish() + self.scoreMovie = None + self.scoreCircle.hide() + return + + def enterFinalResults(self): + lerpTrack = Parallel() + lerpDur = 0.5 + tY = 0.6 + bY = -.05 + lX = -.5 + cX = 0 + rX = 0.5 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, tY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(IceGameGlobals.ShowScoresDuration), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitFinalResults(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.start() + + def exitCleanup(self): + pass + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + if toon: + toon.setPos(0, 0, 0) + toon.setHpr(0, 0, 0) + + def moveCameraToTop(self): + camera.reparentTo(render) + p = self.cameraThreeQuarterView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + + def setupTire(self, avId, index): + tireNp, tireBody, tireOdeGeom = self.createTire(index) + self.tireDict[avId] = {'tireNodePath': tireNp, + 'tireBody': tireBody, + 'tireOdeGeom': tireOdeGeom} + if avId <= 0: + tireBlocker = tireNp.find('**/tireblockermesh') + if not tireBlocker.isEmpty(): + tireBlocker.hide() + if avId == self.localAvId: + tireNp = self.tireDict[avId]['tireNodePath'] + self.treasureSphereName = 'treasureCollider' + self.treasureCollSphere = CollisionSphere(0, 0, 0, IceGameGlobals.TireRadius) + self.treasureCollSphere.setTangible(0) + self.treasureCollNode = CollisionNode(self.treasureSphereName) + self.treasureCollNode.setFromCollideMask(ToontownGlobals.PieBitmask) + self.treasureCollNode.addSolid(self.treasureCollSphere) + self.treasureCollNodePath = tireNp.attachNewNode(self.treasureCollNode) + self.treasureHandler = CollisionHandlerEvent() + self.treasureHandler.addInPattern('%fn-intoTreasure') + base.cTrav.addCollider(self.treasureCollNodePath, self.treasureHandler) + eventName = '%s-intoTreasure' % self.treasureCollNodePath.getName() + self.notify.debug('eventName = %s' % eventName) + self.accept(eventName, self.toonHitSomething) + + def setupForceArrow(self, avId): + arrow = loader.loadModel('phase_4/models/minigames/ice_game_arrow') + priority = 0 + if avId < 0: + priority = -avId + else: + priority = self.avIdList.index(avId) + if avId == self.localAvId: + priority = 10 + self.forceArrowDict[avId] = arrow + + def hideForceArrows(self): + for forceArrow in self.forceArrowDict.values(): + forceArrow.hide() + + def showForceArrows(self, realPlayersOnly = True): + for avId in self.forceArrowDict: + if realPlayersOnly: + if avId > 0: + self.forceArrowDict[avId].show() + else: + self.forceArrowDict[avId].hide() + else: + self.forceArrowDict[avId].show() + + def localForceArrow(self): + if self.localAvId in self.forceArrowDict: + return self.forceArrowDict[self.localAvId] + else: + return None + return None + + def setChoices(self, input0, input1, input2, input3): + pass + + def startDebugTask(self): + taskMgr.add(self.debugTask, self.debugTaskName) + + def stopDebugTask(self): + taskMgr.remove(self.debugTaskName) + + def debugTask(self, task): + if self.canDrive and self.tireDict.has_key(localAvatar.doId): + dt = globalClock.getDt() + forceMove = 25000 + forceMoveDt = forceMove + tireBody = self.tireDict[localAvatar.doId]['tireBody'] + if self.arrowKeys.upPressed() and not tireBody.isEnabled(): + x = 0 + y = 1 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.downPressed() and not tireBody.isEnabled(): + x = 0 + y = -1 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.leftPressed() and not tireBody.isEnabled(): + x = -1 + y = 0 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + if self.arrowKeys.rightPressed() and not tireBody.isEnabled(): + x = 1 + y = 0 + tireBody.enable() + tireBody.addForce(Vec3(x * forceMoveDt, y * forceMoveDt, 0)) + return task.cont + + def __upArrowPressed(self): + pass + + def __downArrowPressed(self): + pass + + def __leftArrowPressed(self): + pass + + def __rightArrowPressed(self): + pass + + def __controlPressed(self): + if self.gameFSM.getCurrentState().getName() == 'inputChoice': + self.sendForceArrowUpdateAsap = True + self.updateLocalForceArrow() + self.controlKeyPressed = True + self.sendUpdate('setAvatarChoice', [self.curForce, self.curHeading]) + self.gameFSM.request('waitServerChoices') + + def startTimer(self): + now = globalClock.getFrameTime() + elapsed = now - self.timerStartTime + self.timer.posInTopRightCorner() + self.timer.setTime(IceGameGlobals.InputTimeout) + self.timer.countdown(IceGameGlobals.InputTimeout - elapsed, self.handleChoiceTimeout) + self.timer.show() + + def setTimerStartTime(self, timestamp): + if not self.hasLocalToon: + return + self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp) + if self.timer != None: + self.startTimer() + return + + def handleChoiceTimeout(self): + self.sendUpdate('setAvatarChoice', [0, 0]) + self.gameFSM.request('waitServerChoices') + + def localTireNp(self): + ret = None + if self.localAvId in self.tireDict: + ret = self.tireDict[self.localAvId]['tireNodePath'] + return ret + + def localTireBody(self): + ret = None + if self.localAvId in self.tireDict: + ret = self.tireDict[self.localAvId]['tireBody'] + return ret + + def getTireBody(self, avId): + ret = None + if avId in self.tireDict: + ret = self.tireDict[avId]['tireBody'] + return ret + + def getTireNp(self, avId): + ret = None + if avId in self.tireDict: + ret = self.tireDict[avId]['tireNodePath'] + return ret + + def updateForceArrow(self, avId, curHeading, curForce): + forceArrow = self.forceArrowDict[avId] + tireNp = self.tireDict[avId]['tireNodePath'] + tireNp.setH(curHeading) + tireBody = self.tireDict[avId]['tireBody'] + tireBody.setQuaternion(tireNp.getQuat()) + self.notify.debug('curHeading = %s' % curHeading) + yScale = curForce / 100.0 + yScale *= 1 + headY = yScale * 15 + xScale = (yScale - 1) / 2.0 + 1.0 + shaft = forceArrow.find('**/arrow_shaft') + head = forceArrow.find('**/arrow_head') + shaft.setScale(xScale, yScale, 1) + head.setPos(0, headY, 0) + head.setScale(xScale, xScale, 1) + + def updateLocalForceArrow(self): + avId = self.localAvId + self.b_setForceArrowInfo(avId, self.curHeading, self.curForce) + + def __aimTask(self, task): + if not hasattr(self, 'arrowKeys'): + return task.done + dt = globalClock.getDt() + headingMomentumChange = dt * 60.0 + forceMomentumChange = dt * 160.0 + arrowUpdate = False + arrowRotating = False + arrowUp = False + arrowDown = False + if self.arrowKeys.upPressed() and not self.arrowKeys.downPressed(): + self.forceMomentum += forceMomentumChange + if self.forceMomentum < 0: + self.forceMomentum = 0 + if self.forceMomentum > 50: + self.forceMomentum = 50 + oldForce = self.curForce + self.curForce += self.forceMomentum * dt + arrowUpdate = True + if oldForce < self.MaxLocalForce: + arrowUp = True + elif self.arrowKeys.downPressed() and not self.arrowKeys.upPressed(): + self.forceMomentum += forceMomentumChange + if self.forceMomentum < 0: + self.forceMomentum = 0 + if self.forceMomentum > 50: + self.forceMomentum = 50 + oldForce = self.curForce + self.curForce -= self.forceMomentum * dt + arrowUpdate = True + if oldForce > 0.01: + arrowDown = True + else: + self.forceMomentum = 0 + if self.arrowKeys.leftPressed() and not self.arrowKeys.rightPressed(): + self.headingMomentum += headingMomentumChange + if self.headingMomentum < 0: + self.headingMomentum = 0 + if self.headingMomentum > 50: + self.headingMomentum = 50 + self.curHeading += self.headingMomentum * dt + arrowUpdate = True + arrowRotating = True + elif self.arrowKeys.rightPressed() and not self.arrowKeys.leftPressed(): + self.headingMomentum += headingMomentumChange + if self.headingMomentum < 0: + self.headingMomentum = 0 + if self.headingMomentum > 50: + self.headingMomentum = 50 + self.curHeading -= self.headingMomentum * dt + arrowUpdate = True + arrowRotating = True + else: + self.headingMomentum = 0 + if arrowUpdate: + self.normalizeHeadingAndForce() + self.updateLocalForceArrow() + if arrowRotating: + if not self.arrowRotateSound.status() == self.arrowRotateSound.PLAYING: + base.playSfx(self.arrowRotateSound, looping=True) + else: + self.arrowRotateSound.stop() + if arrowUp: + if not self.arrowUpSound.status() == self.arrowUpSound.PLAYING: + base.playSfx(self.arrowUpSound, looping=False) + else: + self.arrowUpSound.stop() + if arrowDown: + if not self.arrowDownSound.status() == self.arrowDownSound.PLAYING: + base.playSfx(self.arrowDownSound, looping=False) + else: + self.arrowDownSound.stop() + return task.cont + + def normalizeHeadingAndForce(self): + if self.curForce > self.MaxLocalForce: + self.curForce = self.MaxLocalForce + if self.curForce < 0.01: + self.curForce = 0.01 + + def setTireInputs(self, tireInputs): + if not self.hasLocalToon: + return + self.allTireInputs = tireInputs + self.gameFSM.request('moveTires') + + def enableAllTireBodies(self): + for avId in self.tireDict.keys(): + self.tireDict[avId]['tireBody'].enable() + + def disableAllTireBodies(self): + for avId in self.tireDict.keys(): + self.tireDict[avId]['tireBody'].disable() + + def areAllTiresDisabled(self): + for avId in self.tireDict.keys(): + if self.tireDict[avId]['tireBody'].isEnabled(): + return False + + return True + + def __moveTiresTask(self, task): + if self.areAllTiresDisabled(): + self.sendTirePositions() + self.gameFSM.request('synch') + return task.done + return task.cont + + def sendTirePositions(self): + tirePositions = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + tire = self.getTireBody(avId) + pos = Point3(tire.getPosition()) + tirePositions.append([pos[0], pos[1], pos[2]]) + + for index in xrange(len(self.avIdList), 4): + avId = -index + tire = self.getTireBody(avId) + pos = Point3(tire.getPosition()) + tirePositions.append([pos[0], pos[1], pos[2]]) + + self.sendUpdate('endingPositions', [tirePositions]) + + def setFinalPositions(self, finalPos): + if not self.hasLocalToon: + return + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + tire = self.getTireBody(avId) + np = self.getTireNp(avId) + pos = finalPos[index] + tire.setPosition(pos[0], pos[1], pos[2]) + np.setPos(pos[0], pos[1], pos[2]) + + for index in xrange(len(self.avIdList), 4): + avId = -index + tire = self.getTireBody(avId) + np = self.getTireNp(avId) + pos = finalPos[index] + tire.setPosition(pos[0], pos[1], pos[2]) + np.setPos(pos[0], pos[1], pos[2]) + + def updateInfoLabel(self): + self.infoLabel['text'] = TTLocalizer.IceGameInfo % {'curMatch': self.curMatch + 1, + 'numMatch': IceGameGlobals.NumMatches, + 'curRound': self.curRound + 1, + 'numRound': IceGameGlobals.NumRounds} + + def setMatchAndRound(self, match, round): + if not self.hasLocalToon: + return + self.curMatch = match + self.curRound = round + self.updateInfoLabel() + + def setScores(self, match, round, scores): + if not self.hasLocalToon: + return + self.newMatch = match + self.newRound = round + self.newScores = scores + + def setNewState(self, state): + if not self.hasLocalToon: + return + self.notify.debug('setNewState gameFSM=%s newState=%s' % (self.gameFSM, state)) + self.gameFSM.request(state) + + def putAllTiresInStartingPositions(self): + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + np = self.tireDict[avId]['tireNodePath'] + np.setPos(IceGameGlobals.StartingPositions[index]) + self.notify.debug('avId=%s newPos=%s' % (avId, np.getPos)) + np.setHpr(0, 0, 0) + quat = np.getQuat() + body = self.tireDict[avId]['tireBody'] + body.setPosition(IceGameGlobals.StartingPositions[index]) + body.setQuaternion(quat) + + for index in xrange(len(self.avIdList), 4): + avId = -index + np = self.tireDict[avId]['tireNodePath'] + np.setPos(IceGameGlobals.StartingPositions[index]) + self.notify.debug('avId=%s newPos=%s' % (avId, np.getPos)) + np.setHpr(0, 0, 0) + quat = np.getQuat() + body = self.tireDict[avId]['tireBody'] + body.setPosition(IceGameGlobals.StartingPositions[index]) + body.setQuaternion(quat) + + def b_setForceArrowInfo(self, avId, force, heading): + self.setForceArrowInfo(avId, force, heading) + self.d_setForceArrowInfo(avId, force, heading) + + def d_setForceArrowInfo(self, avId, force, heading): + sendIt = False + curTime = self.getCurrentGameTime() + if self.sendForceArrowUpdateAsap: + sendIt = True + elif curTime - self.lastForceArrowUpdateTime > 0.2: + sendIt = True + if sendIt: + self.sendUpdate('setForceArrowInfo', [avId, force, heading]) + self.sendForceArrowUpdateAsap = False + self.lastForceArrowUpdateTime = self.getCurrentGameTime() + + def setForceArrowInfo(self, avId, force, heading): + if not self.hasLocalToon: + return + self.updateForceArrow(avId, force, heading) + + def setupStartOfMatch(self): + self.putAllTiresInStartingPositions() + szId = self.getSafezoneId() + self.numTreasures = IceGameGlobals.NumTreasures[szId] + if self.treasures: + for treasure in self.treasures: + treasure.destroy() + + self.treasures = [] + index = 0 + treasureMargin = IceGameGlobals.TireRadius + 1.0 + while len(self.treasures) < self.numTreasures: + xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5, IceGameGlobals.MaxWall[0] - 5) + yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5, IceGameGlobals.MaxWall[1] - 5) + self.notify.debug('yPos=%s' % yPos) + pos = Point3(xPos, yPos, IceGameGlobals.TireRadius) + newTreasure = IceTreasure.IceTreasure(self.treasureModel, pos, index, self.doId, penalty=False) + goodSpot = True + for obstacle in self.obstacles: + if newTreasure.nodePath.getDistance(obstacle) < treasureMargin: + goodSpot = False + break + + if goodSpot: + for treasure in self.treasures: + if newTreasure.nodePath.getDistance(treasure.nodePath) < treasureMargin: + goodSpot = False + break + + if goodSpot: + self.treasures.append(newTreasure) + index += 1 + else: + newTreasure.destroy() + + self.numPenalties = IceGameGlobals.NumPenalties[szId] + if self.penalties: + for penalty in self.penalties: + penalty.destroy() + + self.penalties = [] + index = 0 + while len(self.penalties) < self.numPenalties: + xPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[0] + 5, IceGameGlobals.MaxWall[0] - 5) + yPos = self.randomNumGen.randrange(IceGameGlobals.MinWall[1] + 5, IceGameGlobals.MaxWall[1] - 5) + self.notify.debug('yPos=%s' % yPos) + pos = Point3(xPos, yPos, IceGameGlobals.TireRadius) + newPenalty = IceTreasure.IceTreasure(self.penaltyModel, pos, index, self.doId, penalty=True) + goodSpot = True + for obstacle in self.obstacles: + if newPenalty.nodePath.getDistance(obstacle) < treasureMargin: + goodSpot = False + break + + if goodSpot: + for treasure in self.treasures: + if newPenalty.nodePath.getDistance(treasure.nodePath) < treasureMargin: + goodSpot = False + break + + if goodSpot: + for penalty in self.penalties: + if newPenalty.nodePath.getDistance(penalty.nodePath) < treasureMargin: + goodSpot = False + break + + if goodSpot: + self.penalties.append(newPenalty) + index += 1 + else: + newPenalty.destroy() + + def toonHitSomething(self, entry): + self.notify.debug('---- treasure Enter ---- ') + self.notify.debug('%s' % entry) + name = entry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + self.notify.debug('collided with %s, but returning' % name) + return + if not int(parts[1]) == self.doId: + self.notify.debug("collided with %s, but doId doesn't match" % name) + return + treasureNum = int(parts[2]) + if 'penalty' in parts[0]: + self.__penaltyGrabbed(treasureNum) + else: + self.__treasureGrabbed(treasureNum) + + def __treasureGrabbed(self, treasureNum): + self.treasures[treasureNum].showGrab() + self.treasureGrabSound.play() + self.sendUpdate('claimTreasure', [treasureNum]) + + def setTreasureGrabbed(self, avId, treasureNum): + if not self.hasLocalToon: + return + self.notify.debug('treasure %s grabbed by %s' % (treasureNum, avId)) + if avId != self.localAvId: + self.treasures[treasureNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + + def __penaltyGrabbed(self, penaltyNum): + self.penalties[penaltyNum].showGrab() + self.sendUpdate('claimPenalty', [penaltyNum]) + + def setPenaltyGrabbed(self, avId, penaltyNum): + if not self.hasLocalToon: + return + self.notify.debug('penalty %s grabbed by %s' % (penaltyNum, avId)) + if avId != self.localAvId: + self.penalties[penaltyNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] -= 1 + self.scorePanels[i].setScore(self.scores[i]) + + def postStep(self): + DistributedIceWorld.DistributedIceWorld.postStep(self) + for count in range(self.colCount): + c0, c1 = self.getOrderedContacts(count) + if c1 in self.tireCollideIds: + tireIndex = self.tireCollideIds.index(c1) + if c0 in self.tireCollideIds: + self.tireSounds[tireIndex]['tireHit'].play() + elif c0 == self.wallCollideId: + self.tireSounds[tireIndex]['wallHit'].play() + elif c0 == self.obstacleCollideId: + self.tireSounds[tireIndex]['obstacleHit'].play() + + def forceLocalToonToTire(self): + toon = localAvatar + if toon and self.localAvId in self.tireDict: + tireNp = self.tireDict[self.localAvId]['tireNodePath'] + toon.reparentTo(tireNp) + toon.setPosHpr(0, 0, 0, 0, 0, 0) + toon.setY(1.0) + toon.setZ(-3) diff --git a/toontown/minigame/DistributedIceGameAI.py b/toontown/minigame/DistributedIceGameAI.py new file mode 100644 index 0000000..0b9f7a5 --- /dev/null +++ b/toontown/minigame/DistributedIceGameAI.py @@ -0,0 +1,354 @@ +from pandac.PandaModules import Point3 +from direct.distributed.ClockDelta import globalClockDelta +from direct.fsm import ClassicFSM, State +from direct.task import Task +from toontown.minigame import DistributedMinigameAI +from toontown.minigame import MinigameGlobals +from toontown.minigame import IceGameGlobals +from toontown.ai.ToonBarrier import ToonBarrier + +class DistributedIceGameAI(DistributedMinigameAI.DistributedMinigameAI): + notify = directNotify.newCategory('DistributedIceGameAI') + + def __init__(self, air, minigameId): + try: + self.DistributedIceGameAI_initialized + except: + self.DistributedIceGameAI_initialized = 1 + DistributedMinigameAI.DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedIceGameAI', [State.State('off', self.enterOff, self.exitOff, ['waitClientsChoices']), + State.State('waitClientsChoices', self.enterWaitClientsChoices, self.exitWaitClientsChoices, ['cleanup', 'processChoices']), + State.State('processChoices', self.enterProcessChoices, self.exitProcessChoices, ['waitEndingPositions', 'cleanup']), + State.State('waitEndingPositions', self.enterWaitEndingPositions, self.exitWaitEndingPositions, ['processEndingPositions', 'cleanup']), + State.State('processEndingPositions', self.enterProcessEndingPositions, self.exitProcessEndingPositions, ['waitClientsChoices', 'scoreMatch', 'cleanup']), + State.State('scoreMatch', self.enterScoreMatch, self.exitScoreMatch, ['waitClientsChoices', 'finalResults', 'cleanup']), + State.State('finalResults', self.enterFinalResults, self.exitFinalResults, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off') + self.addChildGameFSM(self.gameFSM) + self.avatarChoices = {} + self.avatarEndingPositions = {} + self.curRound = 0 + self.curMatch = 0 + self.finalEndingPositions = [Point3(IceGameGlobals.StartingPositions[0]), + Point3(IceGameGlobals.StartingPositions[1]), + Point3(IceGameGlobals.StartingPositions[2]), + Point3(IceGameGlobals.StartingPositions[3])] + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + taskMgr.remove(self.taskName('wait-choices-timeout')) + taskMgr.remove(self.taskName('endingPositionsTimeout')) + del self.gameFSM + DistributedMinigameAI.DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.DistributedMinigameAI.setGameReady(self) + self.numTreasures = IceGameGlobals.NumTreasures[self.getSafezoneId()] + self.numTreasuresTaken = 0 + self.takenTreasuresTable = [0] * self.numTreasures + self.numPenalties = IceGameGlobals.NumPenalties[self.getSafezoneId()] + self.numPenaltiesTaken = 0 + self.takenPenaltiesTable = [0] * self.numPenalties + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsChoices') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.DistributedMinigameAI.gameOver(self) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('off') + + def exitCleanup(self): + pass + + def enterWaitClientsChoices(self): + self.notify.debug('enterWaitClientsChoices') + self.resetChoices() + self.sendUpdate('setMatchAndRound', [self.curMatch, self.curRound]) + self.sendUpdate('setNewState', ['inputChoice']) + taskMgr.doMethodLater(IceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('wait-choices-timeout')) + self.sendUpdate('setTimerStartTime', [globalClockDelta.getFrameNetworkTime()]) + + def exitWaitClientsChoices(self): + self.notify.debug('exitWaitClientsChoices') + taskMgr.remove(self.taskName('wait-choices-timeout')) + + def enterProcessChoices(self): + forceAndHeading = [] + for avId in self.avIdList: + force = self.avatarChoices[avId][0] + heading = self.avatarChoices[avId][1] + forceAndHeading.append([force, heading]) + + self.notify.debug('tireInputs = %s' % forceAndHeading) + self.sendUpdate('setTireInputs', [forceAndHeading]) + self.gameFSM.request('waitEndingPositions') + + def exitProcessChoices(self): + pass + + def enterWaitEndingPositions(self): + if self.curRound == 0: + self.takenTreasuresTable = [0] * self.numTreasures + self.takenPenaltiesTable = [0] * self.numPenalties + taskMgr.doMethodLater(IceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('endingPositionsTimeout')) + self.avatarEndingPositions = {} + + def exitWaitEndingPositions(self): + taskMgr.remove(self.taskName('endingPositionsTimeout')) + + def enterProcessEndingPositions(self): + averagePos = [Point3(0, 0, 0), + Point3(0, 0, 0), + Point3(0, 0, 0), + Point3(0, 0, 0)] + divisor = 0 + for avId in self.avatarEndingPositions.keys(): + divisor += 1 + oneClientEndingPositions = self.avatarEndingPositions[avId] + avIndex = self.avIdList.index(avId) + for index in xrange(len(oneClientEndingPositions)): + pos = oneClientEndingPositions[index] + averagePos[index] += Point3(pos[0], pos[1], pos[2]) + self.notify.debug('index = %d averagePos = %s' % (index, averagePos)) + + sentPos = [] + if divisor: + for newPos in averagePos: + newPos /= divisor + newPos.setZ(IceGameGlobals.TireRadius) + sentPos.append([newPos[0], newPos[1], newPos[2]]) + + else: + sentPos = self.finalEndingPositions + self.sendUpdate('setFinalPositions', [sentPos]) + self.finalEndingPositions = sentPos + if self.curMatch == IceGameGlobals.NumMatches - 1 and self.curRound == IceGameGlobals.NumRounds - 1: + self.gameFSM.request('scoreMatch') + elif self.curRound == IceGameGlobals.NumRounds - 1: + self.gameFSM.request('scoreMatch') + else: + self.curRound += 1 + self.sendUpdate('setMatchAndRound', [self.curMatch, self.curRound]) + self.gameFSM.request('waitClientsChoices') + + def exitProcessEndingPositions(self): + pass + + def enterScoreMatch(self): + sortedByDistance = [] + for avId in self.avIdList: + index = self.avIdList.index(avId) + pos = Point3(*self.finalEndingPositions[index]) + pos.setZ(0) + sortedByDistance.append((avId, pos.length())) + + def compareDistance(x, y): + if x[1] - y[1] > 0: + return 1 + elif x[1] - y[1] < 0: + return -1 + else: + return 0 + + sortedByDistance.sort(cmp=compareDistance) + self.scoresAsList = [] + totalPointsAdded = 0 + for index in xrange(len(self.avIdList)): + pos = Point3(*self.finalEndingPositions[index]) + pos.setZ(0) + length = pos.length() + points = length / IceGameGlobals.FarthestLength * (IceGameGlobals.PointsInCorner - IceGameGlobals.PointsDeadCenter[self.numPlayers]) + points += IceGameGlobals.PointsDeadCenter[self.numPlayers] + self.notify.debug('length = %s points=%s avId=%d' % (length, points, avId)) + avId = self.avIdList[index] + bonusIndex = 0 + for sortIndex in xrange(len(sortedByDistance)): + if sortedByDistance[sortIndex][0] == avId: + bonusIndex = sortIndex + + bonusIndex += 4 - len(self.avIdList) + pointsToAdd = int(points + 0.5) + IceGameGlobals.BonusPointsForPlace[bonusIndex] + totalPointsAdded += pointsToAdd + self.scoreDict[avId] += pointsToAdd + self.scoresAsList.append(self.scoreDict[avId]) + + self.curMatch += 1 + self.curRound = 0 + self.sendUpdate('setScores', [self.curMatch, self.curRound, self.scoresAsList]) + self.sendUpdate('setNewState', ['scoring']) + + def allToonsScoringMovieDone(self = self): + self.notify.debug('allToonsScoringMovieDone') + if self.curMatch == IceGameGlobals.NumMatches: + self.gameFSM.request('finalResults') + else: + self.gameFSM.request('waitClientsChoices') + + def handleTimeout(avIds, self = self): + self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds) + if self.curMatch == IceGameGlobals.NumMatches: + self.gameFSM.request('finalResults') + else: + self.gameFSM.request('waitClientsChoices') + + scoreMovieDuration = IceGameGlobals.FarthestLength * IceGameGlobals.ExpandFeetPerSec + scoreMovieDuration += totalPointsAdded * IceGameGlobals.ScoreCountUpRate + self.scoringMovieDoneBarrier = ToonBarrier('waitScoringMovieDone', self.uniqueName('waitScoringMovieDone'), self.avIdList, scoreMovieDuration + MinigameGlobals.latencyTolerance, allToonsScoringMovieDone, handleTimeout) + + def exitScoreMatch(self): + self.scoringMovieDoneBarrier.cleanup() + self.scoringMovieDoneBarrier = None + return + + def enterFinalResults(self): + self.checkScores() + self.sendUpdate('setNewState', ['finalResults']) + taskMgr.doMethodLater(IceGameGlobals.ShowScoresDuration, self.__doneShowingScores, self.taskName('waitShowScores')) + + def exitFinalResults(self): + taskMgr.remove(self.taskName('waitShowScores')) + + def __doneShowingScores(self, task): + self.notify.debug('doneShowingScores') + self.gameOver() + return Task.done + + def waitClientsChoicesTimeout(self, task): + self.notify.debug('waitClientsChoicesTimeout: did not hear from all clients') + for avId in self.avatarChoices.keys(): + if self.avatarChoices[avId] == (-1, 0): + self.avatarChoices[avId] = (0, 0) + + self.gameFSM.request('processChoices') + return Task.done + + def resetChoices(self): + for avId in self.avIdList: + self.avatarChoices[avId] = (-1, 0) + + def setAvatarChoice(self, force, direction): + avatarId = self.air.getAvatarIdFromSender() + self.notify.debug('setAvatarChoice: avatar: ' + str(avatarId) + ' votes: ' + str(force) + ' direction: ' + str(direction)) + self.avatarChoices[avatarId] = self.checkChoice(avatarId, force, direction) + if self.allAvatarsChosen(): + self.notify.debug('setAvatarChoice: all avatars have chosen') + self.gameFSM.request('processChoices') + else: + self.notify.debug('setAvatarChoice: still waiting for more choices') + + def checkChoice(self, avId, force, direction): + retForce = force + retDir = direction + if retForce < 0: + retForce = 0 + if retForce > 100: + retForce = 100 + return (retForce, retDir) + + def allAvatarsChosen(self): + for avId in self.avatarChoices.keys(): + choice = self.avatarChoices[avId] + if choice[0] == -1 and not self.stateDict[avId] == DistributedMinigameAI.EXITED: + return False + + return True + + def endingPositions(self, positions): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions': + return + self.notify.debug('got endingPositions from client %s' % positions) + avId = self.air.getAvatarIdFromSender() + self.avatarEndingPositions[avId] = positions + if self.allAvatarsSentEndingPositions(): + self.gameFSM.request('processEndingPositions') + + def allAvatarsSentEndingPositions(self): + if len(self.avatarEndingPositions) == len(self.avIdList): + return True + return False + + def endingPositionsTimeout(self, task): + self.notify.debug('endingPositionsTimeout : did not hear from all clients') + self.gameFSM.request('processEndingPositions') + return Task.done + + def reportScoringMovieDone(self): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'scoreMatch': + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('reportScoringMovieDone: avatar %s is done' % avId) + self.scoringMovieDoneBarrier.clear(avId) + + def claimTreasure(self, treasureNum): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions': + return + avId = self.air.getAvatarIdFromSender() + if not self.scoreDict.has_key(avId): + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + if treasureNum < 0 or treasureNum >= self.numTreasures: + self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range') + return + if self.takenTreasuresTable[treasureNum]: + return + self.takenTreasuresTable[treasureNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setTreasureGrabbed', [avId, treasureNum]) + self.scoreDict[avId] += 1 + self.numTreasuresTaken += 1 + + def claimPenalty(self, penaltyNum): + if not self.gameFSM or not self.gameFSM.getCurrentState() or self.gameFSM.getCurrentState().getName() != 'waitEndingPositions': + return + avId = self.air.getAvatarIdFromSender() + if not self.scoreDict.has_key(avId): + self.notify.warning('PROBLEM: avatar %s called claimPenalty(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + penaltyNum, + self.scoreDict, + self.avIdList)) + return + if penaltyNum < 0 or penaltyNum >= self.numPenalties: + self.air.writeServerEvent('warning', penaltyNum, 'IceGameAI.claimPenalty penaltyNum out of range') + return + if self.takenPenaltiesTable[penaltyNum]: + return + self.takenPenaltiesTable[penaltyNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setPenaltyGrabbed', [avId, penaltyNum]) + self.scoreDict[avId] -= 1 + self.numPenaltiesTaken += 1 + + def checkScores(self): + self.scoresAsList = [] + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if self.scoreDict[avId] < 0: + self.scoreDict[avId] = 1 + self.scoresAsList.append(self.scoreDict[avId]) diff --git a/toontown/minigame/DistributedIceWorld.py b/toontown/minigame/DistributedIceWorld.py new file mode 100644 index 0000000..d486003 --- /dev/null +++ b/toontown/minigame/DistributedIceWorld.py @@ -0,0 +1,180 @@ +from pandac.PandaModules import Vec4, BitMask32, Quat, Point3, NodePath +from pandac.PandaModules import OdePlaneGeom, OdeBody, OdeSphereGeom, OdeMass, OdeUtil, OdeBoxGeom +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import DistributedMinigamePhysicsWorld +from toontown.minigame import IceGameGlobals +from toontown.golf import BuildGeometry +MetersToFeet = 3.2808399 +FeetToMeters = 1.0 / MetersToFeet + +class DistributedIceWorld(DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigamePhysicsWorld') + floorCollideId = 1 + floorMask = BitMask32(floorCollideId) + wallCollideId = 1 << 1 + wallMask = BitMask32(wallCollideId) + obstacleCollideId = 1 << 2 + obstacleMask = BitMask32(obstacleCollideId) + tireCollideIds = [1 << 8, + 1 << 9, + 1 << 10, + 1 << 11] + tire0Mask = BitMask32(tireCollideIds[0]) + tire1Mask = BitMask32(tireCollideIds[1]) + tire2Mask = BitMask32(tireCollideIds[2]) + tire3Mask = BitMask32(tireCollideIds[3]) + allTiresMask = tire0Mask | tire1Mask | tire2Mask | tire3Mask + tireMasks = (tire0Mask, + tire1Mask, + tire2Mask, + tire3Mask) + tireDensity = 1 + tireSurfaceType = 0 + iceSurfaceType = 1 + fenceSurfaceType = 2 + + def __init__(self, cr): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.__init__(self, cr) + + def delete(self): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.delete(self) + if hasattr(self, 'floor'): + self.floor = None + return + + def setupSimulation(self): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.setupSimulation(self) + self.world.setGravity(0, 0, -32.174) + self.world.setAutoDisableFlag(1) + self.world.setAutoDisableLinearThreshold(0.5 * MetersToFeet) + self.world.setAutoDisableAngularThreshold(OdeUtil.getInfinity()) + self.world.setAutoDisableSteps(10) + self.world.setCfm(1e-05 * MetersToFeet) + self.world.initSurfaceTable(3) + self.world.setSurfaceEntry(0, 1, 0.2, 0, 0, 0, 0, 0, 0.1) + self.world.setSurfaceEntry(0, 0, 0.1, 0.9, 0.1, 0, 0, 0, 0) + self.world.setSurfaceEntry(0, 2, 0.9, 0.9, 0.1, 0, 0, 0, 0) + self.floor = OdePlaneGeom(self.space, Vec4(0.0, 0.0, 1.0, -20.0)) + self.floor.setCollideBits(self.allTiresMask) + self.floor.setCategoryBits(self.floorMask) + self.westWall = OdePlaneGeom(self.space, Vec4(1.0, 0.0, 0.0, IceGameGlobals.MinWall[0])) + self.westWall.setCollideBits(self.allTiresMask) + self.westWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.westWall, self.fenceSurfaceType) + self.space.setCollideId(self.westWall, self.wallCollideId) + self.eastWall = OdePlaneGeom(self.space, Vec4(-1.0, 0.0, 0.0, -IceGameGlobals.MaxWall[0])) + self.eastWall.setCollideBits(self.allTiresMask) + self.eastWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.eastWall, self.fenceSurfaceType) + self.space.setCollideId(self.eastWall, self.wallCollideId) + self.southWall = OdePlaneGeom(self.space, Vec4(0.0, 1.0, 0.0, IceGameGlobals.MinWall[1])) + self.southWall.setCollideBits(self.allTiresMask) + self.southWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.southWall, self.fenceSurfaceType) + self.space.setCollideId(self.southWall, self.wallCollideId) + self.northWall = OdePlaneGeom(self.space, Vec4(0.0, -1.0, 0.0, -IceGameGlobals.MaxWall[1])) + self.northWall.setCollideBits(self.allTiresMask) + self.northWall.setCategoryBits(self.wallMask) + self.space.setSurfaceType(self.northWall, self.fenceSurfaceType) + self.space.setCollideId(self.northWall, self.wallCollideId) + self.floorTemp = OdePlaneGeom(self.space, Vec4(0.0, 0.0, 1.0, 0.0)) + self.floorTemp.setCollideBits(self.allTiresMask) + self.floorTemp.setCategoryBits(self.floorMask) + self.space.setSurfaceType(self.floorTemp, self.iceSurfaceType) + self.space.setCollideId(self.floorTemp, self.floorCollideId) + self.space.setAutoCollideWorld(self.world) + self.space.setAutoCollideJointGroup(self.contactgroup) + self.totalPhysicsSteps = 0 + + def createTire(self, tireIndex): + if tireIndex < 0 or tireIndex >= len(self.tireMasks): + self.notify.error('invalid tireIndex %s' % tireIndex) + self.notify.debug('create tireindex %s' % tireIndex) + zOffset = 0 + body = OdeBody(self.world) + mass = OdeMass() + mass.setSphere(self.tireDensity, IceGameGlobals.TireRadius) + body.setMass(mass) + body.setPosition(IceGameGlobals.StartingPositions[tireIndex][0], IceGameGlobals.StartingPositions[tireIndex][1], IceGameGlobals.StartingPositions[tireIndex][2]) + body.setAutoDisableDefaults() + geom = OdeSphereGeom(self.space, IceGameGlobals.TireRadius) + self.space.setSurfaceType(geom, self.tireSurfaceType) + self.space.setCollideId(geom, self.tireCollideIds[tireIndex]) + self.massList.append(mass) + self.geomList.append(geom) + geom.setCollideBits(self.allTiresMask | self.wallMask | self.floorMask | self.obstacleMask) + geom.setCategoryBits(self.tireMasks[tireIndex]) + geom.setBody(body) + if self.notify.getDebug(): + self.notify.debug('tire geom id') + geom.write() + self.notify.debug(' -') + if self.canRender: + testTire = render.attachNewNode('tire holder %d' % tireIndex) + smileyModel = NodePath() + if not smileyModel.isEmpty(): + smileyModel.setScale(IceGameGlobals.TireRadius) + smileyModel.reparentTo(testTire) + smileyModel.setAlphaScale(0.5) + smileyModel.setTransparency(1) + testTire.setPos(IceGameGlobals.StartingPositions[tireIndex]) + tireModel = loader.loadModel('phase_4/models/minigames/ice_game_tire') + tireHeight = 1 + tireModel.setZ(-IceGameGlobals.TireRadius + 0.01) + tireModel.reparentTo(testTire) + self.odePandaRelationList.append((testTire, body)) + else: + testTire = None + self.bodyList.append((None, body)) + return (testTire, body, geom) + + def placeBodies(self): + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if pandaNodePathGeom: + pandaNodePathGeom.setPos(odeBody.getPosition()) + pandaNodePathGeom.setQuat(Quat(odeBody.getQuaternion()[0], odeBody.getQuaternion()[1], odeBody.getQuaternion()[2], odeBody.getQuaternion()[3])) + pandaNodePathGeom.setP(0) + pandaNodePathGeom.setR(0) + newQuat = pandaNodePathGeom.getQuat() + odeBody.setQuaternion(newQuat) + + def postStep(self): + DistributedMinigamePhysicsWorld.DistributedMinigamePhysicsWorld.postStep(self) + self.placeBodies() + self.totalPhysicsSteps += 1 + + def createObstacle(self, pos, obstacleIndex, cubicObstacle): + if cubicObstacle: + return self.createCubicObstacle(pos, obstacleIndex) + else: + return self.createCircularObstacle(pos, obstacleIndex) + + def createCircularObstacle(self, pos, obstacleIndex): + self.notify.debug('create obstacleindex %s' % obstacleIndex) + geom = OdeSphereGeom(self.space, IceGameGlobals.TireRadius) + geom.setCollideBits(self.allTiresMask) + geom.setCategoryBits(self.obstacleMask) + self.space.setCollideId(geom, self.obstacleCollideId) + tireModel = loader.loadModel('phase_4/models/minigames/ice_game_tirestack') + tireHeight = 1 + tireModel.setPos(pos) + tireModel.reparentTo(render) + geom.setPosition(tireModel.getPos()) + tireModel.setZ(0) + return tireModel + + def createCubicObstacle(self, pos, obstacleIndex): + self.notify.debug('create obstacleindex %s' % obstacleIndex) + sideLength = IceGameGlobals.TireRadius * 2 + geom = OdeBoxGeom(self.space, sideLength, sideLength, sideLength) + geom.setCollideBits(self.allTiresMask) + geom.setCategoryBits(self.obstacleMask) + self.space.setCollideId(geom, self.obstacleCollideId) + tireModel = loader.loadModel('phase_4/models/minigames/ice_game_crate') + tireModel.setPos(pos) + tireModel.reparentTo(render) + geom.setPosition(tireModel.getPos()) + tireModel.setZ(0) + return tireModel diff --git a/toontown/minigame/DistributedMazeGame.py b/toontown/minigame/DistributedMazeGame.py new file mode 100644 index 0000000..cf55e5f --- /dev/null +++ b/toontown/minigame/DistributedMazeGame.py @@ -0,0 +1,1136 @@ +from direct.interval.IntervalGlobal import LerpPosInterval, LerpHprInterval, LerpPosHprInterval +from direct.interval.IntervalGlobal import SoundInterval, LerpScaleInterval, LerpFunctionInterval +from direct.interval.IntervalGlobal import Wait, Func +from direct.interval.MetaInterval import Sequence, Parallel +from direct.gui.DirectGui import DirectWaitBar, DGG +from direct.showbase import PythonUtil +from direct.fsm import ClassicFSM, State +from direct.showbase import RandomNumGen +from direct.task.Task import Task +from direct.distributed.ClockDelta import globalClockDelta +from pandac.PandaModules import Point3, Vec3 +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from DistributedMinigame import DistributedMinigame +from MazeSuit import MazeSuit +from OrthoWalk import OrthoWalk +from OrthoDrive import OrthoDrive +import MazeGameGlobals +import MazeData +import MazeTreasure +import Trajectory +import Maze +import MinigameAvatarScorePanel +import MinigameGlobals + +class DistributedMazeGame(DistributedMinigame): + notify = directNotify.newCategory('DistributedMazeGame') + CAMERA_TASK = 'MazeGameCameraTask' + UPDATE_SUITS_TASK = 'MazeGameUpdateSuitsTask' + TREASURE_GRAB_EVENT_NAME = 'MazeTreasureGrabbed' + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMazeGame', [State.State('off', self.enterOff, self.exitOff, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'showScores']), + State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.usesLookAround = 1 + + def getTitle(self): + return TTLocalizer.MazeGameTitle + + def getInstructions(self): + return TTLocalizer.MazeGameInstructions + + def getMaxDuration(self): + return MazeGameGlobals.GAME_DURATION + + def __defineConstants(self): + self.TOON_SPEED = 8.0 + self.TOON_Z = 0 + self.MinSuitSpeedRange = [0.8 * self.TOON_SPEED, 0.6 * self.TOON_SPEED] + self.MaxSuitSpeedRange = [1.1 * self.TOON_SPEED, 2.0 * self.TOON_SPEED] + self.FASTER_SUIT_CURVE = 1 + self.SLOWER_SUIT_CURVE = self.getDifficulty() < 0.5 + self.slowerSuitPeriods = {2000: {4: [128, 76], + 8: [128, + 99, + 81, + 68], + 12: [128, + 108, + 93, + 82, + 74, + 67], + 16: [128, + 112, + 101, + 91, + 83, + 76, + 71, + 66]}, + 1000: {4: [110, 69], + 8: [110, + 88, + 73, + 62], + 12: [110, + 95, + 83, + 74, + 67, + 61], + 16: [110, + 98, + 89, + 81, + 75, + 69, + 64, + 60]}, + 5000: {4: [96, 63], + 8: [96, + 79, + 66, + 57], + 12: [96, + 84, + 75, + 67, + 61, + 56], + 16: [96, + 87, + 80, + 73, + 68, + 63, + 59, + 55]}, + 4000: {4: [86, 58], + 8: [86, + 71, + 61, + 53], + 12: [86, + 76, + 68, + 62, + 56, + 52], + 16: [86, + 78, + 72, + 67, + 62, + 58, + 54, + 51]}, + 3000: {4: [78, 54], + 8: [78, + 65, + 56, + 49], + 12: [78, + 69, + 62, + 57, + 52, + 48], + 16: [78, + 71, + 66, + 61, + 57, + 54, + 51, + 48]}, + 9000: {4: [71, 50], + 8: [71, + 60, + 52, + 46], + 12: [71, + 64, + 58, + 53, + 49, + 45], + 16: [71, + 65, + 61, + 57, + 53, + 50, + 47, + 45]}} + self.slowerSuitPeriodsCurve = {2000: {4: [128, 65], + 8: [128, + 78, + 66, + 64], + 12: [128, + 88, + 73, + 67, + 64, + 64], + 16: [128, + 94, + 79, + 71, + 67, + 65, + 64, + 64]}, + 1000: {4: [110, 59], + 8: [110, + 70, + 60, + 58], + 12: [110, + 78, + 66, + 61, + 59, + 58], + 16: [110, + 84, + 72, + 65, + 61, + 59, + 58, + 58]}, + 5000: {4: [96, 55], + 8: [96, + 64, + 56, + 54], + 12: [96, + 71, + 61, + 56, + 54, + 54], + 16: [96, + 76, + 65, + 59, + 56, + 55, + 54, + 54]}, + 4000: {4: [86, 51], + 8: [86, + 59, + 52, + 50], + 12: [86, + 65, + 56, + 52, + 50, + 50], + 16: [86, + 69, + 60, + 55, + 52, + 51, + 50, + 50]}, + 3000: {4: [78, 47], + 8: [78, + 55, + 48, + 47], + 12: [78, + 60, + 52, + 48, + 47, + 47], + 16: [78, + 63, + 55, + 51, + 49, + 47, + 47, + 47]}, + 9000: {4: [71, 44], + 8: [71, + 51, + 45, + 44], + 12: [71, + 55, + 48, + 45, + 44, + 44], + 16: [71, + 58, + 51, + 48, + 45, + 44, + 44, + 44]}} + self.fasterSuitPeriods = {2000: {4: [54, 42], + 8: [59, + 52, + 47, + 42], + 12: [61, + 56, + 52, + 48, + 45, + 42], + 16: [61, + 58, + 54, + 51, + 49, + 46, + 44, + 42]}, + 1000: {4: [50, 40], + 8: [55, + 48, + 44, + 40], + 12: [56, + 52, + 48, + 45, + 42, + 40], + 16: [56, + 53, + 50, + 48, + 45, + 43, + 41, + 40]}, + 5000: {4: [47, 37], + 8: [51, + 45, + 41, + 37], + 12: [52, + 48, + 45, + 42, + 39, + 37], + 16: [52, + 49, + 47, + 44, + 42, + 40, + 39, + 37]}, + 4000: {4: [44, 35], + 8: [47, + 42, + 38, + 35], + 12: [48, + 45, + 42, + 39, + 37, + 35], + 16: [49, + 46, + 44, + 42, + 40, + 38, + 37, + 35]}, + 3000: {4: [41, 33], + 8: [44, + 40, + 36, + 33], + 12: [45, + 42, + 39, + 37, + 35, + 33], + 16: [45, + 43, + 41, + 39, + 38, + 36, + 35, + 33]}, + 9000: {4: [39, 32], + 8: [41, + 37, + 34, + 32], + 12: [42, + 40, + 37, + 35, + 33, + 32], + 16: [43, + 41, + 39, + 37, + 35, + 34, + 33, + 32]}} + self.fasterSuitPeriodsCurve = {2000: {4: [62, 42], + 8: [63, + 61, + 54, + 42], + 12: [63, + 63, + 61, + 56, + 50, + 42], + 16: [63, + 63, + 62, + 60, + 57, + 53, + 48, + 42]}, + 1000: {4: [57, 40], + 8: [58, + 56, + 50, + 40], + 12: [58, + 58, + 56, + 52, + 46, + 40], + 16: [58, + 58, + 57, + 56, + 53, + 49, + 45, + 40]}, + 5000: {4: [53, 37], + 8: [54, + 52, + 46, + 37], + 12: [54, + 53, + 52, + 48, + 43, + 37], + 16: [54, + 54, + 53, + 51, + 49, + 46, + 42, + 37]}, + 4000: {4: [49, 35], + 8: [50, + 48, + 43, + 35], + 12: [50, + 49, + 48, + 45, + 41, + 35], + 16: [50, + 50, + 49, + 48, + 46, + 43, + 39, + 35]}, + 3000: {4: [46, 33], + 8: [47, + 45, + 41, + 33], + 12: [47, + 46, + 45, + 42, + 38, + 33], + 16: [47, + 46, + 46, + 45, + 43, + 40, + 37, + 33]}, + 9000: {4: [43, 32], + 8: [44, + 42, + 38, + 32], + 12: [44, + 43, + 42, + 40, + 36, + 32], + 16: [44, + 44, + 43, + 42, + 40, + 38, + 35, + 32]}} + self.CELL_WIDTH = MazeData.CELL_WIDTH + self.MAX_FRAME_MOVE = self.CELL_WIDTH / 2 + startOffset = 3 + self.startPosHTable = [[Point3(0, startOffset, self.TOON_Z), 0], + [Point3(0, -startOffset, self.TOON_Z), 180], + [Point3(startOffset, 0, self.TOON_Z), 270], + [Point3(-startOffset, 0, self.TOON_Z), 90]] + self.camOffset = Vec3(0, -19, 45) + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.__defineConstants() + mazeName = MazeGameGlobals.getMazeName(self.doId, self.numPlayers, MazeData.mazeNames) + self.maze = Maze.Maze(mazeName) + model = loader.loadModel('phase_3.5/models/props/mickeySZ') + self.treasureModel = model.find('**/mickeySZ') + model.removeNode() + self.treasureModel.setScale(1.6) + self.treasureModel.setP(-90) + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.mid') + self.toonHitTracks = {} + self.scorePanels = [] + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.toonHitTracks + self.maze.destroy() + del self.maze + self.treasureModel.removeNode() + del self.treasureModel + del self.music + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.maze.onstage() + self.randomNumGen.shuffle(self.startPosHTable) + lt = base.localAvatar + lt.reparentTo(render) + lt.hideName() + self.__placeToon(self.localAvId) + lt.setAnimState('Happy', 1.0) + lt.setSpeed(0, 0) + self.camParent = render.attachNewNode('mazeGameCamParent') + self.camParent.reparentTo(base.localAvatar) + self.camParent.setPos(0, 0, 0) + self.camParent.setHpr(render, 0, 0, 0) + camera.reparentTo(self.camParent) + camera.setPos(self.camOffset) + self.__spawnCameraTask() + self.toonRNGs = [] + for i in xrange(self.numPlayers): + self.toonRNGs.append(RandomNumGen.RandomNumGen(self.randomNumGen)) + + self.treasures = [] + for i in xrange(self.maze.numTreasures): + self.treasures.append(MazeTreasure.MazeTreasure(self.treasureModel, self.maze.treasurePosList[i], i, self.doId)) + + self.__loadSuits() + for suit in self.suits: + suit.onstage() + + self.sndTable = {'hitBySuit': [None] * self.numPlayers, + 'falling': [None] * self.numPlayers} + for i in xrange(self.numPlayers): + self.sndTable['hitBySuit'][i] = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.mp3') + self.sndTable['falling'][i] = base.loadSfx('phase_4/audio/sfx/MG_cannon_whizz.mp3') + + self.grabSounds = [] + for i in xrange(5): + self.grabSounds.append(base.loadSfx('phase_4/audio/sfx/MG_maze_pickup.mp3')) + + self.grabSoundIndex = 0 + for avId in self.avIdList: + self.toonHitTracks[avId] = Wait(0.1) + + self.scores = [0] * self.numPlayers + self.goalBar = DirectWaitBar(parent=render2d, relief=DGG.SUNKEN, frameSize=(-0.35, + 0.35, + -0.15, + 0.15), borderWidth=(0.02, 0.02), scale=0.42, pos=(0.84, 0, 0.5 - 0.28 * self.numPlayers + 0.05), barColor=(0, 0.7, 0, 1)) + self.goalBar.setBin('unsorted', 0) + self.goalBar.hide() + self.introTrack = self.getIntroTrack() + self.introTrack.start() + return + + def offstage(self): + self.notify.debug('offstage') + if self.introTrack.isPlaying(): + self.introTrack.finish() + del self.introTrack + for avId in self.toonHitTracks.keys(): + track = self.toonHitTracks[avId] + if track.isPlaying(): + track.finish() + + self.__killCameraTask() + camera.wrtReparentTo(render) + self.camParent.removeNode() + del self.camParent + for panel in self.scorePanels: + panel.cleanup() + + self.scorePanels = [] + self.goalBar.destroy() + del self.goalBar + base.setCellsAvailable(base.rightCells, 1) + for suit in self.suits: + suit.offstage() + + self.__unloadSuits() + for treasure in self.treasures: + treasure.destroy() + + del self.treasures + del self.sndTable + del self.grabSounds + del self.toonRNGs + self.maze.offstage() + base.localAvatar.showName() + DistributedMinigame.offstage(self) + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + if self.numPlayers == 1: + toon.setPos(0, 0, self.TOON_Z) + toon.setHpr(180, 0, 0) + else: + posIndex = self.avIdList.index(avId) + toon.setPos(self.startPosHTable[posIndex][0]) + toon.setHpr(self.startPosHTable[posIndex][1], 0, 0) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.setAnimState('Happy', 1.0) + toon.startSmooth() + toon.startLookAround() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + if self.introTrack.isPlaying(): + self.introTrack.finish() + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.stopLookAround() + + self.gameFSM.request('play') + + def handleDisabledAvatar(self, avId): + hitTrack = self.toonHitTracks[avId] + if hitTrack.isPlaying(): + hitTrack.finish() + DistributedMinigame.handleDisabledAvatar(self, avId) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setPos(1.12, 0.0, 0.5 - 0.28 * i) + self.scorePanels.append(scorePanel) + + self.goalBar.show() + self.goalBar['value'] = 0.0 + base.setCellsAvailable(base.rightCells, 0) + self.__spawnUpdateSuitsTask() + orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doMazeCollisions, priority=1) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) + self.orthoWalk.start() + self.accept(MazeSuit.COLLISION_EVENT_NAME, self.__hitBySuit) + self.accept(self.TREASURE_GRAB_EVENT_NAME, self.__treasureGrabbed) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(MazeGameGlobals.GAME_DURATION) + self.timer.countdown(MazeGameGlobals.GAME_DURATION, self.timerExpired) + self.accept('resetClock', self.__resetClock) + base.playMusic(self.music, looping=0, volume=0.8) + + def exitPlay(self): + self.notify.debug('exitPlay') + self.ignore('resetClock') + self.ignore(MazeSuit.COLLISION_EVENT_NAME) + self.ignore(self.TREASURE_GRAB_EVENT_NAME) + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + self.__killUpdateSuitsTask() + self.timer.stop() + self.timer.destroy() + del self.timer + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + toon.loop('neutral') + + def __resetClock(self, tOffset): + self.notify.debug('resetClock') + self.gameStartTime += tOffset + self.timer.countdown(self.timer.currentTime + tOffset, self.timerExpired) + + def __treasureGrabbed(self, treasureNum): + self.treasures[treasureNum].showGrab() + self.grabSounds[self.grabSoundIndex].play() + self.grabSoundIndex = (self.grabSoundIndex + 1) % len(self.grabSounds) + self.sendUpdate('claimTreasure', [treasureNum]) + + def setTreasureGrabbed(self, avId, treasureNum): + if not self.hasLocalToon: + return + if avId != self.localAvId: + self.treasures[treasureNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + total = 0 + for score in self.scores: + total += score + + self.goalBar['value'] = 100.0 * (float(total) / float(self.maze.numTreasures)) + + def __hitBySuit(self, suitNum): + self.notify.debug('hitBySuit') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('hitBySuit', [self.localAvId, timestamp]) + self.__showToonHitBySuit(self.localAvId, timestamp) + + def hitBySuit(self, avId, timestamp): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['play', 'showScores']: + self.notify.warning('ignoring msg: av %s hit by suit' % avId) + return + self.notify.debug('avatar ' + `avId` + ' hit by a suit') + if avId != self.localAvId: + self.__showToonHitBySuit(avId, timestamp) + + def __showToonHitBySuit(self, avId, timestamp): + toon = self.getAvatar(avId) + if toon == None: + return + rng = self.toonRNGs[self.avIdList.index(avId)] + curPos = toon.getPos(render) + oldTrack = self.toonHitTracks[avId] + if oldTrack.isPlaying(): + oldTrack.finish() + toon.setPos(curPos) + toon.setZ(self.TOON_Z) + parentNode = render.attachNewNode('mazeFlyToonParent-' + `avId`) + parentNode.setPos(toon.getPos()) + toon.reparentTo(parentNode) + toon.setPos(0,0,0) + startPos = parentNode.getPos() + dropShadow = toon.dropShadow.copyTo(parentNode) + dropShadow.setScale(toon.dropShadow.getScale(render)) + trajectory = Trajectory.Trajectory( + 0, + Point3(0,0,0), + Point3(0,0,50), + gravMult=1.0) + flyDur = trajectory.calcTimeOfImpactOnPlane(0.0) + while 1: + endTile = [rng.randint(2, self.maze.width-1), rng.randint(2, self.maze.height-1)] + if self.maze.isWalkable(endTile[0], endTile[1]): + break + endWorldCoords = self.maze.tile2world(endTile[0], endTile[1]) + endPos = Point3(endWorldCoords[0], endWorldCoords[1], startPos[2]) + def flyFunc(t, trajectory, startPos = startPos, endPos = endPos, dur = flyDur, moveNode = parentNode, flyNode = toon): + u = t/dur + moveNode.setX(startPos[0] + u * (endPos[0]-startPos[0])) + moveNode.setY(startPos[1] + u * (endPos[1]-startPos[1])) + flyNode.setPos(trajectory.getPos(t)) + flyTrack = Sequence( + LerpFunctionInterval(flyFunc, fromData=0.0, toData=flyDur, duration=flyDur, extraArgs=[trajectory]), + name=toon.uniqueName('hitBySuit-fly')) + if avId != self.localAvId: + cameraTrack = Sequence() + else: + self.camParent.reparentTo(parentNode) + startCamPos = camera.getPos() + destCamPos = camera.getPos() + zenith = trajectory.getPos(flyDur/2.0)[2] + destCamPos.setZ(zenith*1.3) + destCamPos.setY(destCamPos[1]*0.3) + def camTask(task, zenith = zenith, flyNode = toon, startCamPos = startCamPos, camOffset = destCamPos - startCamPos): + u = flyNode.getZ()/zenith + camera.setPos(startCamPos + camOffset*u) + camera.lookAt(toon) + return Task.cont + camTaskName = 'mazeToonFlyCam-' + `avId` + taskMgr.add(camTask, camTaskName, priority=20) + def cleanupCamTask(self = self, toon = toon, camTaskName = camTaskName, startCamPos = startCamPos): + taskMgr.remove(camTaskName) + self.camParent.reparentTo(toon) + camera.setPos(startCamPos) + camera.lookAt(toon) + + cameraTrack = Sequence( + Wait(flyDur), + Func(cleanupCamTask), + name='hitBySuit-cameraLerp') + + geomNode = toon.getGeomNode() + startHpr = geomNode.getHpr() + destHpr = Point3(startHpr) + hRot = rng.randrange(1, 8) + if rng.choice([0, 1]): + hRot = -hRot + destHpr.setX(destHpr[0] + hRot*360) + spinHTrack = Sequence( + LerpHprInterval(geomNode, flyDur, destHpr, startHpr=startHpr), + Func(geomNode.setHpr, startHpr), + name=toon.uniqueName('hitBySuit-spinH')) + parent = geomNode.getParent() + rotNode = parent.attachNewNode('rotNode') + geomNode.reparentTo(rotNode) + rotNode.setZ(toon.getHeight()/2.0) + oldGeomNodeZ = geomNode.getZ() + geomNode.setZ(-toon.getHeight()/2.0) + startHpr = rotNode.getHpr() + destHpr = Point3(startHpr) + pRot = rng.randrange(1,3) + if rng.choice([0, 1]): + pRot = -pRot + destHpr.setY(destHpr[1] + pRot*360) + spinPTrack = Sequence( + LerpHprInterval(rotNode, flyDur, destHpr, startHpr=startHpr), + Func(rotNode.setHpr, startHpr), + name=toon.uniqueName('hitBySuit-spinP')) + i = self.avIdList.index(avId) + soundTrack = Sequence( + Func(base.playSfx, self.sndTable['hitBySuit'][i]), + Wait(flyDur * (2.0/3.0)), + SoundInterval(self.sndTable['falling'][i], + duration=flyDur * (1.0/3.0)), + name=toon.uniqueName('hitBySuit-soundTrack')) + + def preFunc(self = self, avId = avId, toon = toon, dropShadow = dropShadow): + forwardSpeed = toon.forwardSpeed + rotateSpeed = toon.rotateSpeed + if avId == self.localAvId: + self.orthoWalk.stop() + else: + toon.stopSmooth() + if forwardSpeed or rotateSpeed: + toon.setSpeed(forwardSpeed, rotateSpeed) + toon.dropShadow.hide() + + def postFunc(self = self, avId = avId, oldGeomNodeZ = oldGeomNodeZ, dropShadow = dropShadow, parentNode = parentNode): + if avId == self.localAvId: + base.localAvatar.setPos(endPos) + if hasattr(self, 'orthoWalk'): + if self.gameFSM.getCurrentState().getName() == 'play': + self.orthoWalk.start() + dropShadow.removeNode() + del dropShadow + toon.dropShadow.show() + geomNode = toon.getGeomNode() + rotNode = geomNode.getParent() + baseNode = rotNode.getParent() + geomNode.reparentTo(baseNode) + rotNode.removeNode() + del rotNode + geomNode.setZ(oldGeomNodeZ) + toon.reparentTo(render) + toon.setPos(endPos) + parentNode.removeNode() + del parentNode + if avId != self.localAvId: + toon.startSmooth() + + preFunc() + + hitTrack = Sequence(Parallel(flyTrack, cameraTrack, + spinHTrack, spinPTrack, soundTrack), + Func(postFunc), + name=toon.uniqueName('hitBySuit')) + + self.toonHitTracks[avId] = hitTrack + + hitTrack.start(globalClockDelta.localElapsedTime(timestamp)) + + def allTreasuresTaken(self): + if not self.hasLocalToon: + return + self.notify.debug('all treasures taken') + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('showScores') + + def timerExpired(self): + self.notify.debug('local timer expired') + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('showScores') + + def __doMazeCollisions(self, oldPos, newPos): + offset = newPos - oldPos + WALL_OFFSET = 1.0 + curX = oldPos[0] + curY = oldPos[1] + curTX, curTY = self.maze.world2tile(curX, curY) + + def calcFlushCoord(curTile, newTile, centerTile): + EPSILON = 0.01 + if newTile > curTile: + return (newTile - centerTile) * self.CELL_WIDTH - EPSILON - WALL_OFFSET + else: + return (curTile - centerTile) * self.CELL_WIDTH + WALL_OFFSET + + offsetX = offset[0] + offsetY = offset[1] + WALL_OFFSET_X = WALL_OFFSET + if offsetX < 0: + WALL_OFFSET_X = -WALL_OFFSET_X + WALL_OFFSET_Y = WALL_OFFSET + if offsetY < 0: + WALL_OFFSET_Y = -WALL_OFFSET_Y + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + newTX, newTY = self.maze.world2tile(newX, newY) + if newTX != curTX: + if self.maze.collisionTable[newTY][newTX]: + offset.setX(calcFlushCoord(curTX, newTX, self.maze.originTX) - curX) + newX = curX + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.maze.world2tile(newX, newY) + if newTY != curTY: + if self.maze.collisionTable[newTY][newTX]: + offset.setY(calcFlushCoord(curTY, newTY, self.maze.originTY) - curY) + offsetX = offset[0] + offsetY = offset[1] + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.maze.world2tile(newX, newY) + if self.maze.collisionTable[newTY][newTX]: + cX = calcFlushCoord(curTX, newTX, self.maze.originTX) + cY = calcFlushCoord(curTY, newTY, self.maze.originTY) + if abs(cX - curX) < abs(cY - curY): + offset.setX(cX - curX) + else: + offset.setY(cY - curY) + return oldPos + offset + + def __spawnCameraTask(self): + self.notify.debug('spawnCameraTask') + camera.lookAt(base.localAvatar) + taskMgr.remove(self.CAMERA_TASK) + taskMgr.add(self.__cameraTask, self.CAMERA_TASK, priority=45) + + def __killCameraTask(self): + self.notify.debug('killCameraTask') + taskMgr.remove(self.CAMERA_TASK) + + def __cameraTask(self, task): + self.camParent.setHpr(render, 0, 0, 0) + return Task.cont + + def __loadSuits(self): + self.notify.debug('loadSuits') + self.suits = [] + self.numSuits = 4 * self.numPlayers + safeZone = self.getSafezoneId() + slowerTable = self.slowerSuitPeriods + if self.SLOWER_SUIT_CURVE: + slowerTable = self.slowerSuitPeriodsCurve + slowerPeriods = slowerTable[safeZone][self.numSuits] + fasterTable = self.fasterSuitPeriods + if self.FASTER_SUIT_CURVE: + fasterTable = self.fasterSuitPeriodsCurve + fasterPeriods = fasterTable[safeZone][self.numSuits] + suitPeriods = slowerPeriods + fasterPeriods + self.notify.debug('suit periods: ' + `suitPeriods`) + self.randomNumGen.shuffle(suitPeriods) + for i in xrange(self.numSuits): + self.suits.append(MazeSuit(i, self.maze, self.randomNumGen, suitPeriods[i], self.getDifficulty())) + + def __unloadSuits(self): + self.notify.debug('unloadSuits') + for suit in self.suits: + suit.destroy() + + del self.suits + + def __spawnUpdateSuitsTask(self): + self.notify.debug('spawnUpdateSuitsTask') + for suit in self.suits: + suit.gameStart(self.gameStartTime) + + taskMgr.remove(self.UPDATE_SUITS_TASK) + taskMgr.add(self.__updateSuitsTask, self.UPDATE_SUITS_TASK) + + def __killUpdateSuitsTask(self): + self.notify.debug('killUpdateSuitsTask') + taskMgr.remove(self.UPDATE_SUITS_TASK) + for suit in self.suits: + suit.gameEnd() + + def __updateSuitsTask(self, task): + curT = globalClock.getFrameTime() - self.gameStartTime + curTic = int(curT * float(MazeGameGlobals.SUIT_TIC_FREQ)) + suitUpdates = [] + for i in xrange(len(self.suits)): + updateTics = self.suits[i].getThinkTimestampTics(curTic) + suitUpdates.extend(zip(updateTics, [i] * len(updateTics))) + + suitUpdates.sort(lambda a, b: a[0] - b[0]) + if len(suitUpdates) > 0: + curTic = 0 + for i in xrange(len(suitUpdates)): + update = suitUpdates[i] + tic = update[0] + suitIndex = update[1] + suit = self.suits[suitIndex] + if tic > curTic: + curTic = tic + j = i + 1 + while j < len(suitUpdates): + if suitUpdates[j][0] > tic: + break + self.suits[suitUpdates[j][1]].prepareToThink() + j += 1 + + unwalkables = [] + for si in xrange(suitIndex): + unwalkables.extend(self.suits[si].occupiedTiles) + + for si in xrange(suitIndex + 1, len(self.suits)): + unwalkables.extend(self.suits[si].occupiedTiles) + + suit.think(curTic, curT, unwalkables) + + return Task.cont + + def enterShowScores(self): + self.notify.debug('enterShowScores') + lerpTrack = Parallel() + lerpDur = 0.5 + lerpTrack.append(Parallel(LerpPosInterval(self.goalBar, lerpDur, Point3(0, 0, -.6), blendType='easeInOut'), LerpScaleInterval(self.goalBar, lerpDur, Vec3(self.goalBar.getScale()) * 2.0, blendType='easeInOut'))) + tY = 0.6 + bY = -.05 + lX = -.5 + cX = 0 + rX = 0.5 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, tY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(MazeGameGlobals.SHOWSCORES_DURATION), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitShowScores(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def getIntroTrack(self): + self.__cameraTask(None) + origCamParent = camera.getParent() + origCamPos = camera.getPos() + origCamHpr = camera.getHpr() + iCamParent = base.localAvatar.attachNewNode('iCamParent') + iCamParent.setH(180) + camera.reparentTo(iCamParent) + toonHeight = base.localAvatar.getHeight() + camera.setPos(0, -15, toonHeight * 3) + camera.lookAt(0, 0, toonHeight / 2.0) + iCamParent.wrtReparentTo(origCamParent) + waitDur = 5.0 + lerpDur = 4.5 + lerpTrack = Parallel() + startHpr = iCamParent.getHpr() + startHpr.setX(PythonUtil.reduceAngle(startHpr[0])) + lerpTrack.append(LerpPosHprInterval(iCamParent, lerpDur, pos=Point3(0, 0, 0), hpr=Point3(0, 0, 0), startHpr=startHpr, name=self.uniqueName('introLerpParent'))) + lerpTrack.append(LerpPosHprInterval(camera, lerpDur, pos=origCamPos, hpr=origCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos'))) + base.localAvatar.startLookAround() + + def cleanup(origCamParent = origCamParent, origCamPos = origCamPos, origCamHpr = origCamHpr, iCamParent = iCamParent): + camera.reparentTo(origCamParent) + camera.setPos(origCamPos) + camera.setHpr(origCamHpr) + iCamParent.removeNode() + del iCamParent + base.localAvatar.stopLookAround() + + return Sequence(Wait(waitDur), + lerpTrack, + Func(cleanup)) diff --git a/toontown/minigame/DistributedMazeGameAI.py b/toontown/minigame/DistributedMazeGameAI.py new file mode 100644 index 0000000..dc0567c --- /dev/null +++ b/toontown/minigame/DistributedMazeGameAI.py @@ -0,0 +1,129 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import PatternGameGlobals +from direct.task.Task import Task +import MazeGameGlobals +import MazeData + +class DistributedMazeGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedMinigameTemplateAI_initialized + except: + self.DistributedMinigameTemplateAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMazeGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['waitShowScores', 'cleanup']), + State.State('waitShowScores', self.enterWaitShowScores, self.exitWaitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + mazeName = MazeGameGlobals.getMazeName(self.doId, self.numPlayers, MazeData.mazeNames) + mData = MazeData.mazeData[mazeName] + self.numTreasures = len(mData['treasurePosList']) + self.numTreasuresTaken = 0 + self.takenTable = [0] * self.numTreasures + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def enterPlay(self): + self.notify.debug('enterPlay') + taskMgr.doMethodLater(MazeGameGlobals.GAME_DURATION, self.timerExpired, self.taskName('gameTimer')) + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + + def claimTreasure(self, treasureNum): + if self.gameFSM.getCurrentState() is None or self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + if not self.scoreDict.has_key(avId): + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + if treasureNum < 0 or treasureNum >= self.numTreasures: + self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range') + return + if self.takenTable[treasureNum]: + return + self.takenTable[treasureNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setTreasureGrabbed', [avId, treasureNum]) + self.scoreDict[avId] += 1 + self.numTreasuresTaken += 1 + if self.numTreasuresTaken >= self.numTreasures: + self.logAllPerfect() + self.sendUpdate('allTreasuresTaken', []) + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('waitShowScores') + return + + def timerExpired(self, task): + self.notify.debug('timer expired') + if not MazeGameGlobals.ENDLESS_GAME: + self.gameFSM.request('waitShowScores') + return Task.done + + def enterWaitShowScores(self): + self.notify.debug('enterWaitShowScores') + taskMgr.doMethodLater(MazeGameGlobals.SHOWSCORES_DURATION, self.__doneShowingScores, self.taskName('waitShowScores')) + + def __doneShowingScores(self, task): + self.notify.debug('doneShowingScores') + for key in self.scoreDict.keys(): + self.scoreDict[key] = max(1, self.scoreDict[key] / 12) + + if self.numTreasuresTaken >= self.numTreasures: + for key in self.scoreDict.keys(): + self.scoreDict[key] += 8 + + self.gameOver() + return Task.done + + def exitWaitShowScores(self): + taskMgr.remove(self.taskName('waitShowScores')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + del self.takenTable + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedMinigame.py b/toontown/minigame/DistributedMinigame.py new file mode 100644 index 0000000..1be9dce --- /dev/null +++ b/toontown/minigame/DistributedMinigame.py @@ -0,0 +1,444 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import MinigameRulesPanel +from direct.task.Task import Task +from toontown.toon import Toon +from direct.showbase import RandomNumGen +from toontown.toonbase import TTLocalizer +import random +import MinigameGlobals +from direct.showbase import PythonUtil +from toontown.toon import TTEmote +from otp.avatar import Emote +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs + +class DistributedMinigame(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigame') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.waitingStartLabel = DirectLabel(text=TTLocalizer.MinigameWaitingForOtherPlayers, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingStartLabel.hide() + self.avIdList = [] + self.remoteAvIdList = [] + self.localAvId = base.localAvatar.doId + self.frameworkFSM = ClassicFSM.ClassicFSM('DistributedMinigame', [State.State('frameworkInit', self.enterFrameworkInit, self.exitFrameworkInit, ['frameworkRules', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkRules', self.enterFrameworkRules, self.exitFrameworkRules, ['frameworkWaitServerStart', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkWaitServerStart', self.enterFrameworkWaitServerStart, self.exitFrameworkWaitServerStart, ['frameworkGame', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkGame', self.enterFrameworkGame, self.exitFrameworkGame, ['frameworkWaitServerFinish', 'frameworkCleanup', 'frameworkAvatarExited']), + State.State('frameworkWaitServerFinish', self.enterFrameworkWaitServerFinish, self.exitFrameworkWaitServerFinish, ['frameworkCleanup']), + State.State('frameworkAvatarExited', self.enterFrameworkAvatarExited, self.exitFrameworkAvatarExited, ['frameworkCleanup']), + State.State('frameworkCleanup', self.enterFrameworkCleanup, self.exitFrameworkCleanup, [])], 'frameworkInit', 'frameworkCleanup') + hoodMinigameState = self.cr.playGame.hood.fsm.getStateNamed('minigame') + hoodMinigameState.addChild(self.frameworkFSM) + self.rulesDoneEvent = 'rulesDone' + self.acceptOnce('minigameAbort', self.d_requestExit) + base.curMinigame = self + self.modelCount = 500 + self.cleanupActions = [] + self.usesSmoothing = 0 + self.usesLookAround = 0 + self.difficultyOverride = None + self.trolleyZoneOverride = None + self.hasLocalToon = 0 + self.frameworkFSM.enterInitialState() + self.startingVotes = {} + self.metagameRound = -1 + self._telemLimiter = None + return + + def addChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM) + + def removeChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM) + + def setUsesSmoothing(self): + self.usesSmoothing = 1 + + def setUsesLookAround(self): + self.usesLookAround = 1 + + def getTitle(self): + return TTLocalizer.DefaultMinigameTitle + + def getInstructions(self): + return TTLocalizer.DefaultMinigameInstructions + + def getMaxDuration(self): + raise Exception('Minigame implementer: you must override getMaxDuration()') + + def __createRandomNumGen(self): + self.notify.debug('BASE: self.doId=0x%08X' % self.doId) + self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) + + def destroy(self = self): + self.notify.debug('BASE: destroying random num gen') + del self.randomNumGen + + self.cleanupActions.append(destroy) + + def generate(self): + self.notify.debug('BASE: generate, %s' % self.getTitle()) + DistributedObject.DistributedObject.generate(self) + self.__createRandomNumGen() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if not self.hasLocalToon: + return + self.notify.debug('BASE: handleAnnounceGenerate: send setAvatarJoined') + if base.randomMinigameNetworkPlugPull and random.random() < 1.0 / 25: + print '*** DOING RANDOM MINIGAME NETWORK-PLUG-PULL BEFORE SENDING setAvatarJoined ***' + base.cr.pullNetworkPlug() + self.sendUpdate('setAvatarJoined', []) + self.normalExit = 1 + count = self.modelCount + loader.beginBulkLoad('minigame', TTLocalizer.HeadingToMinigameTitle % self.getTitle(), count, 1, TTLocalizer.TIP_MINIGAME) + self.load() + loader.endBulkLoad('minigame') + globalClock.syncFrameTime() + self.onstage() + + def cleanup(self = self): + self.notify.debug('BASE: cleanup: normalExit=%s' % self.normalExit) + self.offstage() + base.cr.renderFrame() + if self.normalExit: + self.sendUpdate('setAvatarExited', []) + + self.cleanupActions.append(cleanup) + self._telemLimiter = self.getTelemetryLimiter() + self.frameworkFSM.request('frameworkRules') + + def disable(self): + self.notify.debug('BASE: disable') + if self._telemLimiter: + self._telemLimiter.destroy() + self._telemLimiter = None + self.frameworkFSM.request('frameworkCleanup') + taskMgr.remove(self.uniqueName('random-abort')) + taskMgr.remove(self.uniqueName('random-disconnect')) + taskMgr.remove(self.uniqueName('random-netplugpull')) + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.notify.debug('BASE: delete') + if self.hasLocalToon: + self.unload() + self.ignoreAll() + if self.cr.playGame.hood: + hoodMinigameState = self.cr.playGame.hood.fsm.getStateNamed('minigame') + hoodMinigameState.removeChild(self.frameworkFSM) + self.waitingStartLabel.destroy() + del self.waitingStartLabel + del self.frameworkFSM + DistributedObject.DistributedObject.delete(self) + + def getTelemetryLimiter(self): + return TLGatherAllAvs('Minigame', RotationLimitToH) + + def load(self): + self.notify.debug('BASE: load') + Toon.loadMinigameAnims() + + def onstage(self): + self.notify.debug('BASE: onstage') + + def calcMaxDuration(self = self): + return (self.getMaxDuration() + MinigameGlobals.rulesDuration) * 1.1 + + if not base.cr.networkPlugPulled(): + if base.randomMinigameAbort: + maxDuration = calcMaxDuration() + self.randomAbortDelay = random.random() * maxDuration + taskMgr.doMethodLater(self.randomAbortDelay, self.doRandomAbort, self.uniqueName('random-abort')) + if base.randomMinigameDisconnect: + maxDuration = calcMaxDuration() + self.randomDisconnectDelay = random.random() * maxDuration + taskMgr.doMethodLater(self.randomDisconnectDelay, self.doRandomDisconnect, self.uniqueName('random-disconnect')) + if base.randomMinigameNetworkPlugPull: + maxDuration = calcMaxDuration() + self.randomNetPlugPullDelay = random.random() * maxDuration + taskMgr.doMethodLater(self.randomNetPlugPullDelay, self.doRandomNetworkPlugPull, self.uniqueName('random-netplugpull')) + + def doRandomAbort(self, task): + print '*** DOING RANDOM MINIGAME ABORT AFTER %.2f SECONDS ***' % self.randomAbortDelay + self.d_requestExit() + return Task.done + + def doRandomDisconnect(self, task): + print '*** DOING RANDOM MINIGAME DISCONNECT AFTER %.2f SECONDS ***' % self.randomDisconnectDelay + self.sendUpdate('setGameReady') + return Task.done + + def doRandomNetworkPlugPull(self, task): + print '*** DOING RANDOM MINIGAME NETWORK-PLUG-PULL AFTER %.2f SECONDS ***' % self.randomNetPlugPullDelay + base.cr.pullNetworkPlug() + return Task.done + + def offstage(self): + self.notify.debug('BASE: offstage') + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.detachNode() + + messenger.send('minigameOffstage') + + def unload(self): + self.notify.debug('BASE: unload') + if hasattr(base, 'curMinigame'): + del base.curMinigame + Toon.unloadMinigameAnims() + + def setParticipants(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.hasLocalToon = self.localAvId in self.avIdList + if not self.hasLocalToon: + self.notify.warning('localToon (%s) not in list of minigame players: %s' % (self.localAvId, self.avIdList)) + return + self.notify.info('BASE: setParticipants: %s' % self.avIdList) + self.remoteAvIdList = [] + for avId in self.avIdList: + if avId != self.localAvId: + self.remoteAvIdList.append(avId) + + def setTrolleyZone(self, trolleyZone): + if not self.hasLocalToon: + return + self.notify.debug('BASE: setTrolleyZone: %s' % trolleyZone) + self.trolleyZone = trolleyZone + + def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride): + if not self.hasLocalToon: + return + if difficultyOverride != MinigameGlobals.NoDifficultyOverride: + self.difficultyOverride = difficultyOverride / float(MinigameGlobals.DifficultyOverrideMult) + if trolleyZoneOverride != MinigameGlobals.NoTrolleyZoneOverride: + self.trolleyZoneOverride = trolleyZoneOverride + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('BASE: setGameReady: Ready for game with avatars: %s' % self.avIdList) + self.notify.debug(' safezone: %s' % self.getSafezoneId()) + self.notify.debug('difficulty: %s' % self.getDifficulty()) + self.__serverFinished = 0 + for avId in self.remoteAvIdList: + if not self.cr.doId2do.has_key(avId): + self.notify.warning('BASE: toon %s already left or has not yet arrived; waiting for server to abort the game' % avId) + return 1 + + for avId in self.remoteAvIdList: + avatar = self.cr.doId2do[avId] + event = avatar.uniqueName('disable') + self.acceptOnce(event, self.handleDisabledAvatar, [avId]) + + def ignoreToonDisable(self = self, event = event): + self.ignore(event) + + self.cleanupActions.append(ignoreToonDisable) + + for avId in self.avIdList: + avatar = self.getAvatar(avId) + if avatar: + if not self.usesSmoothing: + avatar.stopSmooth() + if not self.usesLookAround: + avatar.stopLookAround() + + def cleanupAvatars(self = self): + for avId in self.avIdList: + avatar = self.getAvatar(avId) + if avatar: + avatar.stopSmooth() + avatar.startLookAround() + + self.cleanupActions.append(cleanupAvatars) + return 0 + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('BASE: setGameStart: Starting game') + self.gameStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.frameworkFSM.request('frameworkGame') + + def setGameAbort(self): + if not self.hasLocalToon: + return + self.notify.warning('BASE: setGameAbort: Aborting game') + self.normalExit = 0 + self.frameworkFSM.request('frameworkCleanup') + + def gameOver(self): + if not self.hasLocalToon: + return + self.notify.debug('BASE: gameOver') + self.frameworkFSM.request('frameworkWaitServerFinish') + + def getAvatar(self, avId): + if self.cr.doId2do.has_key(avId): + return self.cr.doId2do[avId] + else: + self.notify.warning('BASE: getAvatar: No avatar in doId2do with id: ' + str(avId)) + return None + return None + + def getAvatarName(self, avId): + avatar = self.getAvatar(avId) + if avatar: + return avatar.getName() + else: + return 'Unknown' + + def isSinglePlayer(self): + if self.numPlayers == 1: + return 1 + else: + return 0 + + def handleDisabledAvatar(self, avId): + self.notify.warning('BASE: handleDisabledAvatar: disabled avId: ' + str(avId)) + self.frameworkFSM.request('frameworkAvatarExited') + + def d_requestExit(self): + self.notify.debug('BASE: Sending requestExit') + self.sendUpdate('requestExit', []) + + def enterFrameworkInit(self): + self.notify.debug('BASE: enterFrameworkInit') + self.setEmotes() + self.cleanupActions.append(self.unsetEmotes) + + def exitFrameworkInit(self): + pass + + def enterFrameworkRules(self): + self.notify.debug('BASE: enterFrameworkRules') + self.accept(self.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel.MinigameRulesPanel('MinigameRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent) + self.rulesPanel.load() + self.rulesPanel.enter() + + def exitFrameworkRules(self): + self.ignore(self.rulesDoneEvent) + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + + def handleRulesDone(self): + self.notify.debug('BASE: handleRulesDone') + self.sendUpdate('setAvatarReady', []) + self.frameworkFSM.request('frameworkWaitServerStart') + + def enterFrameworkWaitServerStart(self): + self.notify.debug('BASE: enterFrameworkWaitServerStart') + if self.numPlayers > 1: + msg = TTLocalizer.MinigameWaitingForOtherPlayers + else: + msg = TTLocalizer.MinigamePleaseWait + self.waitingStartLabel['text'] = msg + self.waitingStartLabel.show() + + def exitFrameworkWaitServerStart(self): + self.waitingStartLabel.hide() + + def enterFrameworkGame(self): + self.notify.debug('BASE: enterFrameworkGame') + + def exitFrameworkGame(self): + pass + + def enterFrameworkWaitServerFinish(self): + self.notify.debug('BASE: enterFrameworkWaitServerFinish') + if self.__serverFinished: + self.frameworkFSM.request('frameworkCleanup') + + def setGameExit(self): + print 'setGameExit' + if not self.hasLocalToon: + return + self.notify.debug('BASE: setGameExit: now safe to exit game') + if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitServerFinish': + print 'not waiting' + self.__serverFinished = 1 + else: + print 'waiting' + self.frameworkFSM.request('frameworkCleanup') + + def exitFrameworkWaitServerFinish(self): + pass + + def enterFrameworkAvatarExited(self): + self.notify.debug('BASE: enterFrameworkAvatarExited') + + def exitFrameworkAvatarExited(self): + pass + + def enterFrameworkCleanup(self): + self.notify.debug('BASE: enterFrameworkCleanup') + print 'cleanup' + for action in self.cleanupActions: + action() + + self.cleanupActions = [] + self.ignoreAll() + if self.hasLocalToon: + messenger.send(self.cr.playGame.hood.minigameDoneEvent) + + def exitFrameworkCleanup(self): + pass + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def getDifficulty(self): + if self.difficultyOverride is not None: + return self.difficultyOverride + if hasattr(base, 'minigameDifficulty'): + return float(base.minigameDifficulty) + return MinigameGlobals.getDifficulty(self.getSafezoneId()) + + def getSafezoneId(self): + if self.trolleyZoneOverride is not None: + return self.trolleyZoneOverride + if hasattr(base, 'minigameSafezoneId'): + return MinigameGlobals.getSafezoneId(base.minigameSafezoneId) + return MinigameGlobals.getSafezoneId(self.trolleyZone) + + def setEmotes(self): + Emote.globalEmote.disableAll(base.localAvatar) + + def unsetEmotes(self): + Emote.globalEmote.releaseAll(base.localAvatar) + + def setStartingVotes(self, startingVotesArray): + if not len(startingVotesArray) == len(self.avIdList): + self.notify.error('length does not match, startingVotes=%s, avIdList=%s' % (startingVotesArray, self.avIdList)) + return + for index in range(len(self.avIdList)): + avId = self.avIdList[index] + self.startingVotes[avId] = startingVotesArray[index] + + self.notify.debug('starting votes = %s' % self.startingVotes) + + def setMetagameRound(self, metagameRound): + self.metagameRound = metagameRound diff --git a/toontown/minigame/DistributedMinigameAI.py b/toontown/minigame/DistributedMinigameAI.py new file mode 100644 index 0000000..44ef1c8 --- /dev/null +++ b/toontown/minigame/DistributedMinigameAI.py @@ -0,0 +1,448 @@ +from otp.ai.AIBase import * +from direct.distributed.ClockDelta import * +from toontown.ai.ToonBarrier import * +from direct.distributed import DistributedObjectAI +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.shtiker import PurchaseManagerAI +from toontown.shtiker import NewbiePurchaseManagerAI +import MinigameCreatorAI +from direct.task import Task +import random +import MinigameGlobals +from direct.showbase import PythonUtil +import TravelGameGlobals +from toontown.toonbase import ToontownGlobals +EXITED = 0 +EXPECTED = 1 +JOINED = 2 +READY = 3 +DEFAULT_POINTS = 1 +MAX_POINTS = 7 +JOIN_TIMEOUT = 40.0 + MinigameGlobals.latencyTolerance +READY_TIMEOUT = MinigameGlobals.MaxLoadTime + MinigameGlobals.rulesDuration + MinigameGlobals.latencyTolerance +EXIT_TIMEOUT = 20.0 + MinigameGlobals.latencyTolerance + +class DistributedMinigameAI(DistributedObjectAI.DistributedObjectAI): + notify = directNotify.newCategory('DistributedMinigameAI') + + def __init__(self, air, minigameId): + try: + self.DistributedMinigameAI_initialized + except: + self.DistributedMinigameAI_initialized = 1 + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.minigameId = minigameId + self.frameworkFSM = ClassicFSM.ClassicFSM('DistributedMinigameAI', [State.State('frameworkOff', self.enterFrameworkOff, self.exitFrameworkOff, ['frameworkWaitClientsJoin']), + State.State('frameworkWaitClientsJoin', self.enterFrameworkWaitClientsJoin, self.exitFrameworkWaitClientsJoin, ['frameworkWaitClientsReady', 'frameworkWaitClientsExit', 'frameworkCleanup']), + State.State('frameworkWaitClientsReady', self.enterFrameworkWaitClientsReady, self.exitFrameworkWaitClientsReady, ['frameworkGame', 'frameworkWaitClientsExit', 'frameworkCleanup']), + State.State('frameworkGame', self.enterFrameworkGame, self.exitFrameworkGame, ['frameworkWaitClientsExit', 'frameworkCleanup']), + State.State('frameworkWaitClientsExit', self.enterFrameworkWaitClientsExit, self.exitFrameworkWaitClientsExit, ['frameworkCleanup']), + State.State('frameworkCleanup', self.enterFrameworkCleanup, self.exitFrameworkCleanup, ['frameworkOff'])], 'frameworkOff', 'frameworkOff') + self.frameworkFSM.enterInitialState() + self.avIdList = [] + self.stateDict = {} + self.scoreDict = {} + self.difficultyOverride = None + self.trolleyZoneOverride = None + self.metagameRound = -1 + self.startingVotes = {} + + return + + def addChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').addChild(gameFSM) + + def removeChildGameFSM(self, gameFSM): + self.frameworkFSM.getStateNamed('frameworkGame').removeChild(gameFSM) + + def setExpectedAvatars(self, avIds): + self.avIdList = avIds + self.numPlayers = len(self.avIdList) + self.notify.debug('BASE: setExpectedAvatars: expecting avatars: ' + str(self.avIdList)) + + def setNewbieIds(self, newbieIds): + self.newbieIdList = newbieIds + if len(self.newbieIdList) > 0: + self.notify.debug('BASE: setNewbieIds: %s' % self.newbieIdList) + + def setTrolleyZone(self, trolleyZone): + self.trolleyZone = trolleyZone + + def setDifficultyOverrides(self, difficultyOverride, trolleyZoneOverride): + self.difficultyOverride = difficultyOverride + if self.difficultyOverride is not None: + self.difficultyOverride = MinigameGlobals.QuantizeDifficultyOverride(difficultyOverride) + self.trolleyZoneOverride = trolleyZoneOverride + return + + def setMetagameRound(self, roundNum): + self.metagameRound = roundNum + + def _playing(self): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() == 'play' + + def _inState(self, states): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() in makeList(states) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.frameworkFSM.request('frameworkWaitClientsJoin') + + def delete(self): + self.notify.debug('BASE: delete: deleting AI minigame object') + del self.frameworkFSM + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def isSinglePlayer(self): + if self.numPlayers == 1: + return 1 + else: + return 0 + + def getParticipants(self): + return self.avIdList + + def getTrolleyZone(self): + return self.trolleyZone + + def getDifficultyOverrides(self): + response = [self.difficultyOverride, self.trolleyZoneOverride] + if response[0] is None: + response[0] = MinigameGlobals.NoDifficultyOverride + else: + response[0] *= MinigameGlobals.DifficultyOverrideMult + response[0] = int(response[0]) + if response[1] is None: + response[1] = MinigameGlobals.NoTrolleyZoneOverride + return response + + def b_setGameReady(self): + self.setGameReady() + self.d_setGameReady() + + def d_setGameReady(self): + self.notify.debug('BASE: Sending setGameReady') + self.sendUpdate('setGameReady', []) + + def setGameReady(self): + self.notify.debug('BASE: setGameReady: game ready with avatars: %s' % self.avIdList) + self.normalExit = 1 + + def b_setGameStart(self, timestamp): + self.d_setGameStart(timestamp) + self.setGameStart(timestamp) + + def d_setGameStart(self, timestamp): + self.notify.debug('BASE: Sending setGameStart') + self.sendUpdate('setGameStart', [timestamp]) + + def setGameStart(self, timestamp): + self.notify.debug('BASE: setGameStart') + + def b_setGameExit(self): + self.d_setGameExit() + self.setGameExit() + + def d_setGameExit(self): + self.notify.debug('BASE: Sending setGameExit') + self.sendUpdate('setGameExit', []) + + def setGameExit(self): + self.notify.debug('BASE: setGameExit') + + def setGameAbort(self): + self.notify.debug('BASE: setGameAbort') + self.normalExit = 0 + self.sendUpdate('setGameAbort', []) + self.frameworkFSM.request('frameworkCleanup') + + def handleExitedAvatar(self, avId): + self.notify.warning('BASE: handleExitedAvatar: avatar id exited: ' + str(avId)) + self.stateDict[avId] = EXITED + self.setGameAbort() + + def gameOver(self): + self.notify.debug('BASE: gameOver') + self.frameworkFSM.request('frameworkWaitClientsExit') + + def enterFrameworkOff(self): + self.notify.debug('BASE: enterFrameworkOff') + + def exitFrameworkOff(self): + pass + + def enterFrameworkWaitClientsJoin(self): + self.notify.debug('BASE: enterFrameworkWaitClientsJoin') + for avId in self.avIdList: + self.stateDict[avId] = EXPECTED + self.scoreDict[avId] = DEFAULT_POINTS + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId]) + + def allAvatarsJoined(self = self): + self.notify.debug('BASE: all avatars joined') + self.b_setGameReady() + self.frameworkFSM.request('frameworkWaitClientsReady') + + def handleTimeout(avIds, self = self): + self.notify.debug('BASE: timed out waiting for clients %s to join' % avIds) + self.setGameAbort() + + self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout) + + def setAvatarJoined(self): + if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsJoin': + self.notify.debug('BASE: Ignoring setAvatarJoined message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('BASE: setAvatarJoined: avatar id joined: ' + str(avId)) + self.air.writeServerEvent('minigame_joined', avId, '%s|%s' % (self.minigameId, self.trolleyZone)) + self.stateDict[avId] = JOINED + self.notify.debug('BASE: setAvatarJoined: new states: ' + str(self.stateDict)) + self.__barrier.clear(avId) + + def exitFrameworkWaitClientsJoin(self): + self.__barrier.cleanup() + del self.__barrier + + def enterFrameworkWaitClientsReady(self): + self.notify.debug('BASE: enterFrameworkWaitClientsReady') + + def allAvatarsReady(self = self): + self.notify.debug('BASE: all avatars ready') + self.frameworkFSM.request('frameworkGame') + + def handleTimeout(avIds, self = self): + self.notify.debug("BASE: timed out waiting for clients %s to report 'ready'" % avIds) + self.setGameAbort() + + self.__barrier = ToonBarrier('waitClientsReady', self.uniqueName('waitClientsReady'), self.avIdList, READY_TIMEOUT, allAvatarsReady, handleTimeout) + for avId in self.stateDict.keys(): + if self.stateDict[avId] == READY: + self.__barrier.clear(avId) + + self.notify.debug(' safezone: %s' % self.getSafezoneId()) + self.notify.debug('difficulty: %s' % self.getDifficulty()) + + def setAvatarReady(self): + if self.frameworkFSM.getCurrentState().getName() not in ['frameworkWaitClientsReady', 'frameworkWaitClientsJoin']: + self.notify.debug('BASE: Ignoring setAvatarReady message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('BASE: setAvatarReady: avatar id ready: ' + str(avId)) + self.stateDict[avId] = READY + self.notify.debug('BASE: setAvatarReady: new avId states: ' + str(self.stateDict)) + if self.frameworkFSM.getCurrentState().getName() == 'frameworkWaitClientsReady': + self.__barrier.clear(avId) + + def exitFrameworkWaitClientsReady(self): + self.__barrier.cleanup() + del self.__barrier + + def enterFrameworkGame(self): + self.notify.debug('BASE: enterFrameworkGame') + self.gameStartTime = globalClock.getRealTime() + self.b_setGameStart(globalClockDelta.localToNetworkTime(self.gameStartTime)) + + def exitFrameworkGame(self): + pass + + def enterFrameworkWaitClientsExit(self): + self.notify.debug('BASE: enterFrameworkWaitClientsExit') + self.b_setGameExit() + + def allAvatarsExited(self = self): + self.notify.debug('BASE: all avatars exited') + self.frameworkFSM.request('frameworkCleanup') + + def handleTimeout(avIds, self = self): + self.notify.debug('BASE: timed out waiting for clients %s to exit' % avIds) + self.frameworkFSM.request('frameworkCleanup') + + self.__barrier = ToonBarrier('waitClientsExit', self.uniqueName('waitClientsExit'), self.avIdList, EXIT_TIMEOUT, allAvatarsExited, handleTimeout) + for avId in self.stateDict.keys(): + if self.stateDict[avId] == EXITED: + self.__barrier.clear(avId) + + def setAvatarExited(self): + if self.frameworkFSM.getCurrentState().getName() != 'frameworkWaitClientsExit': + self.notify.debug('BASE: Ignoring setAvatarExit message') + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('BASE: setAvatarExited: avatar id exited: ' + str(avId)) + self.stateDict[avId] = EXITED + self.notify.debug('BASE: setAvatarExited: new avId states: ' + str(self.stateDict)) + self.__barrier.clear(avId) + + def exitFrameworkWaitClientsExit(self): + self.__barrier.cleanup() + del self.__barrier + + def hasScoreMult(self): + return 1 + + def enterFrameworkCleanup(self): + self.notify.debug('BASE: enterFrameworkCleanup: normalExit=%s' % self.normalExit) + scoreMult = MinigameGlobals.getScoreMult(self.getSafezoneId()) + if not self.hasScoreMult(): + scoreMult = 1.0 + self.notify.debug('score multiplier: %s' % scoreMult) + for avId in self.avIdList: + self.scoreDict[avId] *= scoreMult + + scoreList = [] + if not self.normalExit: + randReward = random.randrange(DEFAULT_POINTS, MAX_POINTS + 1) + for avId in self.avIdList: + if self.normalExit: + score = int(self.scoreDict[avId] + 0.5) + else: + score = randReward + if ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY in simbase.air.holidayManager.currentHolidays or ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH in simbase.air.holidayManager.currentHolidays: + score *= MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier + logEvent = False + if score > 255: + score = 255 + logEvent = True + elif score < 0: + score = 0 + logEvent = True + if logEvent: + self.air.writeServerEvent('suspicious', avId, 'got %s jellybeans playing minigame %s in zone %s' % (score, self.minigameId, self.getSafezoneId())) + scoreList.append(score) + + self.requestDelete() + if self.metagameRound > -1: + self.handleMetagamePurchaseManager(scoreList) + else: + self.handleRegularPurchaseManager(scoreList) + self.frameworkFSM.request('frameworkOff') + + def handleMetagamePurchaseManager(self, scoreList): + self.notify.debug('self.newbieIdList = %s' % self.newbieIdList) + votesToUse = self.startingVotes + if hasattr(self, 'currentVotes'): + votesToUse = self.currentVotes + votesArray = [] + for avId in self.avIdList: + if votesToUse.has_key(avId): + votesArray.append(votesToUse[avId]) + else: + self.notify.warning('votesToUse=%s does not have avId=%d' % (votesToUse, avId)) + votesArray.append(0) + + if self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex: + newRound = self.metagameRound + if not self.minigameId == ToontownGlobals.TravelGameId: + for index in range(len(scoreList)): + votesArray[index] += scoreList[index] + + self.notify.debug('votesArray = %s' % votesArray) + desiredNextGame = None + if hasattr(self, 'desiredNextGame'): + desiredNextGame = self.desiredNextGame + numToons = 0 + lastAvId = 0 + for avId in self.avIdList: + av = simbase.air.doId2do.get(avId) + if av: + numToons += 1 + lastAvId = avId + + doNewbie = False + if numToons == 1 and lastAvId in self.newbieIdList: + doNewbie = True + if doNewbie: + pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, lastAvId, self.avIdList, scoreList, self.minigameId, self.trolleyZone) + MinigameCreatorAI.acquireMinigameZone(self.zoneId) + pm.generateWithRequired(self.zoneId) + else: + pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList, votesArray, newRound, desiredNextGame) + pm.generateWithRequired(self.zoneId) + else: + self.notify.debug('last minigame, handling newbies') + if ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY in simbase.air.holidayManager.currentHolidays or ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH in simbase.air.holidayManager.currentHolidays: + votesArray = map(lambda x: MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier * x, votesArray) + for id in self.newbieIdList: + pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, id, self.avIdList, scoreList, self.minigameId, self.trolleyZone) + MinigameCreatorAI.acquireMinigameZone(self.zoneId) + pm.generateWithRequired(self.zoneId) + + if len(self.avIdList) > len(self.newbieIdList): + pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList, votesArray=votesArray, metagameRound=self.metagameRound) + pm.generateWithRequired(self.zoneId) + return + + def handleRegularPurchaseManager(self, scoreList): + for id in self.newbieIdList: + pm = NewbiePurchaseManagerAI.NewbiePurchaseManagerAI(self.air, id, self.avIdList, scoreList, self.minigameId, self.trolleyZone) + MinigameCreatorAI.acquireMinigameZone(self.zoneId) + pm.generateWithRequired(self.zoneId) + + if len(self.avIdList) > len(self.newbieIdList): + pm = PurchaseManagerAI.PurchaseManagerAI(self.air, self.avIdList, scoreList, self.minigameId, self.trolleyZone, self.newbieIdList) + pm.generateWithRequired(self.zoneId) + + def exitFrameworkCleanup(self): + pass + + def requestExit(self): + self.notify.debug('BASE: requestExit: client has requested the game to end') + self.setGameAbort() + + def local2GameTime(self, timestamp): + return timestamp - self.gameStartTime + + def game2LocalTime(self, timestamp): + return timestamp + self.gameStartTime + + def getCurrentGameTime(self): + return self.local2GameTime(globalClock.getFrameTime()) + + def getDifficulty(self): + if self.difficultyOverride is not None: + return self.difficultyOverride + if hasattr(self.air, 'minigameDifficulty'): + return float(self.air.minigameDifficulty) + return MinigameGlobals.getDifficulty(self.getSafezoneId()) + + def getSafezoneId(self): + if self.trolleyZoneOverride is not None: + return self.trolleyZoneOverride + if hasattr(self.air, 'minigameSafezoneId'): + return MinigameGlobals.getSafezoneId(self.air.minigameSafezoneId) + return MinigameGlobals.getSafezoneId(self.trolleyZone) + + def logPerfectGame(self, avId): + self.air.writeServerEvent('perfectMinigame', avId, '%s|%s|%s' % (self.minigameId, self.trolleyZone, self.avIdList)) + + def logAllPerfect(self): + for avId in self.avIdList: + self.logPerfectGame(avId) + + def getStartingVotes(self): + retval = [] + for avId in self.avIdList: + if self.startingVotes.has_key(avId): + retval.append(self.startingVotes[avId]) + else: + self.notify.warning('how did this happen? avId=%d not in startingVotes %s' % (avId, self.startingVotes)) + retval.append(0) + + return retval + + def setStartingVote(self, avId, startingVote): + self.startingVotes[avId] = startingVote + self.notify.debug('setting starting vote of avId=%d to %d' % (avId, startingVote)) + + def getMetagameRound(self): + return self.metagameRound diff --git a/toontown/minigame/DistributedMinigamePhysicsWorld.py b/toontown/minigame/DistributedMinigamePhysicsWorld.py new file mode 100644 index 0000000..fe1f4fc --- /dev/null +++ b/toontown/minigame/DistributedMinigamePhysicsWorld.py @@ -0,0 +1,14 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import MinigamePhysicsWorldBase + +class DistributedMinigamePhysicsWorld(DistributedObject.DistributedObject, MinigamePhysicsWorldBase.MinigamePhysicsWorldBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMinigamePhysicsWorld') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + MinigamePhysicsWorldBase.MinigamePhysicsWorldBase.__init__(self, canRender=1) + + def delete(self): + MinigamePhysicsWorldBase.MinigamePhysicsWorldBase.delete(self) + DistributedObject.DistributedObject.delete(self) diff --git a/toontown/minigame/DistributedMinigameTemplate.py b/toontown/minigame/DistributedMinigameTemplate.py new file mode 100644 index 0000000..c6c018d --- /dev/null +++ b/toontown/minigame/DistributedMinigameTemplate.py @@ -0,0 +1,78 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import TTLocalizer + +class DistributedMinigameTemplate(DistributedMinigame): + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMinigameTemplate', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + + def getTitle(self): + return TTLocalizer.MinigameTemplateTitle + + def getInstructions(self): + return TTLocalizer.MinigameTemplateInstructions + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.handleDisabledAvatar(self, avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.gameOver() + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedMinigameTemplateAI.py b/toontown/minigame/DistributedMinigameTemplateAI.py new file mode 100644 index 0000000..9d38daa --- /dev/null +++ b/toontown/minigame/DistributedMinigameTemplateAI.py @@ -0,0 +1,63 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State + +class DistributedMinigameTemplateAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedMinigameTemplateAI_initialized + except: + self.DistributedMinigameTemplateAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedMinigameTemplateAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.gameOver() + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedPairingGame.py b/toontown/minigame/DistributedPairingGame.py new file mode 100644 index 0000000..d258fa0 --- /dev/null +++ b/toontown/minigame/DistributedPairingGame.py @@ -0,0 +1,532 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import TTLocalizer, ToontownTimer +from toontown.toonbase import ToontownBattleGlobals +from toontown.minigame import PlayingCardGlobals +from toontown.minigame import PairingGameCard +from toontown.minigame import PlayingCardDeck +from toontown.minigame import PairingGameGlobals +from OrthoWalk import OrthoWalk +from OrthoDrive import OrthoDrive +from direct.interval.IntervalGlobal import Sequence, Parallel, Func, LerpColorScaleInterval, LerpScaleInterval, LerpFunctionInterval, Wait, SoundInterval +from toontown.toonbase.ToontownGlobals import GlobalDialogColor + +class DistributedPairingGame(DistributedMinigame): + TOON_SPEED = 11 + MAX_FRAME_MOVE = 1 + MAX_FACE_UP_CARDS = 2 + notify = directNotify.newCategory('DistributedPairingGame') + bonusGlowTime = 0.5 + EndGameTaskName = 'endPairingGame' + xCardInc = 4 + cardsPerRow = 8 + cardsPerCol = 5 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPairingGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cameraTopView = (17.6, 6.18756, 43.9956, 0, -89, 0) + self.cameraThreeQuarterView = (14.0, -8.93352, 33.4497, 0, -62.89, 0) + self.deckSeed = 0 + self.faceUpList = [] + self.localFaceUpList = [] + self.inList = [] + self.inactiveList = [] + self.points = 0 + self.flips = 0 + self.matches = 0 + self.yCardInc = 4 + self.startingPositions = [(0, 0, 0, -45), + ((self.cardsPerRow - 1) * self.xCardInc, + (self.cardsPerCol - 1) * self.yCardInc, + 0, + 135), + ((self.cardsPerRow - 1) * self.xCardInc, + 0, + 0, + 45), + (0, + (self.cardsPerCol - 1) * self.yCardInc, + 0, + -135)] + self.stageMin = Point2(0, 0) + self.stageMax = Point2((self.cardsPerRow - 1) * self.xCardInc, (self.cardsPerCol - 1) * self.yCardInc) + self.gameDuration = PairingGameGlobals.EasiestGameDuration + + def moveCameraToTop(self): + camera.reparentTo(render) + p = self.cameraThreeQuarterView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + + def getTitle(self): + return TTLocalizer.PairingGameTitle + + def getInstructions(self): + if self.numPlayers > 1: + return TTLocalizer.PairingGameInstructionsMulti + else: + return TTLocalizer.PairingGameInstructions + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.gameDuration = PairingGameGlobals.calcGameDuration(self.getDifficulty()) + self.gameBoard = loader.loadModel('phase_4/models/minigames/memory_room') + self.gameBoard.setPosHpr(0.5, 0, 0, 0, 0, 0) + self.gameBoard.setScale(1.0) + self.deck = PairingGameGlobals.createDeck(self.deckSeed, self.numPlayers) + self.notify.debug('%s' % self.deck.cards) + testCard = self.getDeckOrderIndex(self.cardsPerCol - 1, 0) + if not testCard > -1: + self.yCardInc *= 1.25 + self.cards = [] + for index in xrange(len(self.deck.cards)): + cardValue = self.deck.cards[index] + oneCard = PairingGameCard.PairingGameCard(cardValue) + oneCard.load() + xPos, yPos = self.getCardPos(index) + oneCard.setPos(xPos, yPos, 0) + oneCard.reparentTo(render) + self.notify.debug('%s' % oneCard.getPos()) + self.notify.debug('suit %s rank %s value %s' % (oneCard.suit, oneCard.rank, oneCard.value)) + self.accept('entercardCollision-%d' % oneCard.value, self.enterCard) + self.accept('exitcardCollision-%d' % oneCard.value, self.exitCard) + oneCard.turnDown(doInterval=False) + self.cards.append(oneCard) + + self.bonusTraversal = range(len(self.cards)) + self.bonusGlow = render.attachNewNode('bonusGlow') + sign = loader.loadModel('phase_4/models/minigames/garden_sign_memory') + sign.find('**/sign1').removeNode() + sign.find('**/sign2').removeNode() + sign.find('**/collision').removeNode() + sign.setPos(0, 0, 0.05) + sign.reparentTo(self.bonusGlow) + self.bonusGlow.setScale(2.5) + self.pointsFrame = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(-0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGamePoints, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGpointsFrame, text_pos=(-1.94, -0.1, 0.0)) + self.pointsLabel = DirectLabel(parent=self.pointsFrame, relief=None, text='0', text_fg=VBase4(0, 0.5, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15)) + self.flipsFrame = DirectFrame(relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(4, 1, 1), pos=(0.33, 0, 0.9), scale=0.1, text=TTLocalizer.PairingGameFlips, text_align=TextNode.ALeft, text_scale=TTLocalizer.DPGflipsFrame, text_pos=(-1.94, -0.1, 0.0)) + self.flipsLabel = DirectLabel(parent=self.flipsFrame, relief=None, text='0', text_fg=VBase4(0, 1.0, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(1.82, 0, -0.15)) + self.__textGen = TextNode('ringGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/MG_pairing_all_matched.mp3') + self.calcBonusTraversal() + self.music = base.loadMusic('phase_4/audio/bgm/MG_Pairing.mid') + self.matchSfx = base.loadSfx('phase_4/audio/sfx/MG_pairing_match.mp3') + self.matchWithBonusSfx = base.loadSfx('phase_4/audio/sfx/MG_pairing_match_bonus_both.mp3') + self.signalSfx = [] + for i in range(4): + self.signalSfx.append(base.loadSfx('phase_4/audio/sfx/MG_pairing_jumping_signal.mp3')) + + self.bonusMovesSfx = base.loadSfx('phase_4/audio/sfx/MG_pairing_bonus_moves.mp3') + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + self.gameBoard.removeNode() + del self.gameBoard + for card in self.cards: + card.unload() + del card + + self.cards = [] + self.pointsFrame.removeNode() + del self.pointsFrame + self.flipsFrame.removeNode() + del self.flipsFrame + del self.__textGen + del self.sndPerfect + self.bonusGlow.removeNode() + del self.bonusGlow + del self.music + del self.matchSfx + del self.matchWithBonusSfx + for i in range(4): + del self.signalSfx[0] + + self.signalSfx = [] + del self.bonusMovesSfx + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.gameBoard.reparentTo(render) + for card in self.cards: + card.reparentTo(render) + + lt = base.localAvatar + lt.reparentTo(render) + lt.hideName() + self.__placeToon(self.localAvId) + lt.setAnimState('Happy', 1.0) + lt.setSpeed(0, 0) + self.moveCameraToTop() + + def offstage(self): + self.notify.debug('offstage') + self.gameBoard.hide() + for card in self.cards: + card.hide() + + DistributedMinigame.offstage(self) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.handleDisabledAvatar(self, avId) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for index in xrange(self.numPlayers): + avId = self.avIdList[index] + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.setAnimState('Happy', 1.0) + toon.startSmooth() + toon.startLookAround() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.stopLookAround() + + self.gameFSM.request('play') + + def isInPlayState(self): + if not self.gameFSM.getCurrentState(): + return False + if not self.gameFSM.getCurrentState().getName() == 'play': + return False + return True + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + base.playMusic(self.music, looping=1, volume=0.9) + orthoDrive = OrthoDrive(self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE, customCollisionCallback=self.__doPairingGameCollisions) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=not self.isSinglePlayer()) + self.orthoWalk.start() + self.accept('insert', self.__flipKeyPressed) + self.accept('delete', self.__flipKeyPressed) + self.accept('time-control', self.__beginSignal) + self.accept('time-control-up', self.__endSignal) + self.bonusGlowIndex = 0 + self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] + self.startBonusTask() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(self.gameDuration) + self.timer.countdown(self.gameDuration, self.timerExpired) + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.stop() + + def exitPlay(self): + self.music.stop() + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + self.bonusGlow.hide() + self.stopBonusTask() + self.timer.stop() + self.timer.destroy() + del self.timer + self.ignoreAll() + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.start() + if hasattr(self, 'perfectIval'): + self.perfectIval.pause() + del self.perfectIval + taskMgr.remove(self.EndGameTaskName) + taskMgr.remove('pairGameContinueSignal') + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + if self.numPlayers == 1: + toon.setPos(0, 0, 0) + toon.setHpr(0, 0, 0) + else: + posIndex = self.avIdList.index(avId) + pos = self.startingPositions[posIndex] + toon.setPos(pos[0], pos[1], pos[2]) + toon.setHpr(pos[3], 0, 0) + + def __doPairingGameCollisions(self, oldPos, newPos): + x = bound(newPos[0], self.stageMin[0], self.stageMax[0]) + y = bound(newPos[1], self.stageMin[1], self.stageMax[1]) + newPos.setX(x) + newPos.setY(y) + if self.inList: + newPos.setZ(0.15) + else: + newPos.setZ(0.0) + if not oldPos == newPos: + taskMgr.remove('pairGameContinueSignal') + return newPos + + def getDeckOrderFromValue(self, value): + for index in xrange(len(self.cards)): + if self.cards[index].value == value: + return index + + return -1 + + def getDeckOrderFromPairingGameCard(self, into): + try: + index = self.cards.index(into) + except ValueError: + index = -1 + + return index + + def enterCard(self, colEntry): + intoName = colEntry.getIntoNodePath().getName() + parts = intoName.split('-') + value = int(parts[1]) + self.notify.debug('entered cardValue %d' % value) + deckOrder = self.getDeckOrderFromValue(value) + if deckOrder not in self.inList: + self.inList.append(deckOrder) + + def exitCard(self, colEntry): + intoName = colEntry.getIntoNodePath().getName() + parts = intoName.split('-') + value = int(parts[1]) + self.notify.debug('exited cardValue %d' % value) + deckOrder = self.getDeckOrderFromValue(value) + if deckOrder in self.inList: + self.inList.remove(deckOrder) + + def handleMatch(self, cardA, cardB, withBonus): + self.notify.debug('we got a match %d %d' % (cardA, cardB)) + self.matches += 1 + if cardA in self.faceUpList: + self.faceUpList.remove(cardA) + if cardB in self.faceUpList: + self.faceUpList.remove(cardB) + self.inactiveList.append(cardA) + self.inactiveList.append(cardB) + matchIval = Parallel() + for card in [cardA, cardB]: + self.cards[card].setTransparency(1) + cardSeq = Sequence(LerpColorScaleInterval(self.cards[card], duration=1, colorScale=Vec4(1.0, 1.0, 1.0, 0.0)), Func(self.cards[card].hide)) + matchIval.append(cardSeq) + + if withBonus: + matchIval.append(SoundInterval(self.matchWithBonusSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) + else: + matchIval.append(SoundInterval(self.matchSfx, node=self.cards[card], listenerNode=base.localAvatar, cutOff=240)) + matchIval.start() + if len(self.inactiveList) == len(self.cards): + self.sendUpdate('reportDone') + + def turnUpCard(self, deckOrder): + self.cards[deckOrder].turnUp() + self.faceUpList.append(deckOrder) + + def turnDownCard(self, deckOrder): + self.cards[deckOrder].turnDown() + if deckOrder in self.faceUpList: + self.faceUpList.remove(deckOrder) + + def __flipKeyPressed(self): + if self.inList: + shortestDistance = 10000 + cardToFlip = -1 + for deckOrder in self.inList: + dist = base.localAvatar.getDistance(self.cards[deckOrder]) + if dist < shortestDistance: + shortestDistance = dist + cardToFlip = deckOrder + + deckOrderIndex = cardToFlip + card = self.cards[deckOrderIndex] + if card.isFaceDown() and deckOrderIndex not in self.inactiveList: + self.sendUpdate('openCardRequest', [deckOrderIndex, self.bonusGlowCard]) + elif card.isFaceUp() and deckOrderIndex in self.faceUpList: + pass + + def moveBonusGlowTask(self, task): + if len(self.cards) == 0: + return Task.done + curT = self.getCurrentGameTime() + intTime = int(curT / self.bonusGlowTime) + newIndex = intTime % len(self.cards) + if not newIndex == self.bonusGlowIndex: + self.bonusGlowIndex = newIndex + self.bonusGlowCard = self.bonusTraversal[self.bonusGlowIndex] + card = self.cards[self.bonusGlowCard] + self.bonusGlow.setPos(card.getPos()) + base.playSfx(self.bonusMovesSfx, node=card, volume=0.25) + return Task.cont + + def timerExpired(self): + self.sendUpdate('reportDone') + + def setDeckSeed(self, deckSeed): + if not self.hasLocalToon: + return + self.deckSeed = deckSeed + + def updateFlipText(self): + self.flipsLabel['text'] = str(self.flips) + lowFlipModifier = PairingGameGlobals.calcLowFlipModifier(self.matches, self.flips) + red = 1.0 - lowFlipModifier + green = lowFlipModifier + self.flipsLabel['text_fg'] = Vec4(red, green, 0, 1.0) + + def openCardResult(self, cardToTurnUp, avId, matchingCard, points, cardsToTurnDown): + if not self.hasLocalToon: + return + if not self.isInPlayState(): + return + if avId == base.localAvatar.doId: + self.localFaceUpList.append(cardToTurnUp) + self.turnUpCard(cardToTurnUp) + gotBonus = False + if points - self.points > 1: + gotBonus = True + if matchingCard > -1: + self.handleMatch(cardToTurnUp, matchingCard, gotBonus) + self.flips += 1 + self.updateFlipText() + self.points = points + self.pointsLabel['text'] = str(self.points) + for card in cardsToTurnDown: + self.turnDownCard(card) + + def startBonusTask(self): + taskMgr.add(self.moveBonusGlowTask, self.taskName('moveBonusGlowTask')) + + def stopBonusTask(self): + taskMgr.remove(self.taskName('moveBonusGlowTask')) + + def setEveryoneDone(self): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring setEveryoneDone msg') + return + self.notify.debug('setEveryoneDone') + + def endGame(task, self = self): + if not PairingGameGlobals.EndlessGame: + self.gameOver() + return Task.done + + self.timer.hide() + self.bonusGlow.hide() + if len(self.inactiveList) == len(self.cards): + self.notify.debug('perfect game!') + perfectTextSubnode = hidden.attachNewNode(self.__genText(TTLocalizer.PairingGamePerfect)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5), Func(endGame, None)) + soundTrack = SoundInterval(self.sndPerfect) + self.perfectIval = Parallel(textTrack, soundTrack) + self.perfectIval.start() + else: + taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) + return + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def b_setSignaling(self, avId): + self.setSignaling(avId) + self.sendUpdate('setSignaling', [self.localAvId]) + + def setSignaling(self, avId): + if not self.hasLocalToon: + return + avIndex = self.avIdList.index(avId) + av = base.cr.doId2do.get(avId) + if av and avIndex >= 0 and hasattr(self, 'signalSfx') and self.signalSfx: + base.playSfx(self.signalSfx[avIndex], node=av) + + def __beginSignal(self, mouseParam): + self.notify.debug('beginSignal') + base.localAvatar.b_setEmoteState(1, 1.0) + self.b_setSignaling(self.localAvId) + taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') + + def __endSignal(self, mouseParam): + self.notify.debug('endSignal') + base.localAvatar.b_setEmoteState(-1, 1.0) + taskMgr.remove('pairGameContinueSignal') + + def __continueSignal(self, task): + base.localAvatar.b_setEmoteState(1, 1.0) + self.b_setSignaling(self.localAvId) + taskMgr.doMethodLater(1.67, self.__continueSignal, 'pairGameContinueSignal') + + def getCardPos(self, deckOrderIndex): + col = deckOrderIndex % self.cardsPerRow + row = deckOrderIndex / self.cardsPerRow + x = col * self.xCardInc + y = row * self.yCardInc + return (x, y) + + def getDeckOrderIndex(self, row, col): + retval = row * self.cardsPerRow + retval += col + if retval >= len(self.deck.cards): + retval = -1 + return retval + + def calcBonusTraversal(self): + self.bonusTraversal = [] + halfRow = self.cardsPerRow / 2 + if self.cardsPerRow % 2: + halfRow += 1 + for i in xrange(halfRow): + for j in xrange(2): + col = i + j * halfRow + for row in xrange(self.cardsPerCol): + card = self.getDeckOrderIndex(row, col) + if card > -1: + self.bonusTraversal.append(card) diff --git a/toontown/minigame/DistributedPairingGameAI.py b/toontown/minigame/DistributedPairingGameAI.py new file mode 100644 index 0000000..b4de770 --- /dev/null +++ b/toontown/minigame/DistributedPairingGameAI.py @@ -0,0 +1,229 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.minigame import PlayingCardGlobals +from toontown.minigame import PlayingCard +from toontown.minigame.PlayingCard import PlayingCardBase +from toontown.minigame import PlayingCardDeck +from toontown.minigame import PairingGameGlobals +from toontown.ai.ToonBarrier import ToonBarrier + +class DistributedPairingGameAI(DistributedMinigameAI): + notify = directNotify.newCategory('DistributedPairingGameAI') + OneCardInMultiplayer = True + TurnDownTwoAtATime = True + + def __init__(self, air, minigameId): + try: + self.DistributedPairingGameAI_initialized + except: + self.DistributedPairingGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPairingGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.gameFSM.enterInitialState() + self.deckSeed = random.randint(0, 4000000) + self.faceUpDict = {} + self.inactiveList = [] + self.maxOpenCards = 2 + self.points = 0 + self.flips = 0 + self.matches = 0 + self.cards = [] + self.gameDuration = 90 + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + if self.OneCardInMultiplayer and len(self.avIdList) > 1: + self.maxOpenCards = 1 + self.sendUpdate('setMaxOpenCards', [self.maxOpenCards]) + DistributedMinigameAI.setGameReady(self) + for avId in self.avIdList: + self.faceUpDict[avId] = [] + + self.deck = PairingGameGlobals.createDeck(self.deckSeed, self.numPlayers) + for index in xrange(len(self.deck.cards)): + cardValue = self.deck.cards[index] + oneCard = PlayingCardBase(cardValue) + self.cards.append(oneCard) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def calcLowFlipBonus(self): + lowFlipModifier = PairingGameGlobals.calcLowFlipModifier(self.matches, self.flips) + bonus = lowFlipModifier * self.matches + self.notify.debug('low flip bonus = %d' % bonus) + return bonus + + def gameOver(self): + self.notify.debug('gameOver') + lowFlipBonus = 0 + for avId in self.avIdList: + self.scoreDict[avId] = max(1, self.points) + lowFlipBonus = self.calcLowFlipBonus() + self.scoreDict[avId] += lowFlipBonus + if self.matches == len(self.cards) / 2: + self.scoreDict[avId] += round(len(self.cards) / 4.0) + self.logAllPerfect() + + logAvId = self.avIdList[0] + self.air.writeServerEvent('minigame_pairing', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.PairingGameId, + self.getSafezoneId(), + self.avIdList, + self.scoreDict[logAvId], + self.gameDuration, + self.matches, + self.flips, + lowFlipBonus)) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + + def allToonsDone(self = self): + self.notify.debug('allToonsDone') + self.sendUpdate('setEveryoneDone') + if not PairingGameGlobals.EndlessGame: + self.gameOver() + + def handleTimeout(avIds, self = self): + self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds) + self.setGameAbort() + + self.gameDuration = PairingGameGlobals.calcGameDuration(self.getDifficulty()) + self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, self.gameDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout) + + def exitPlay(self): + self.doneBarrier.cleanup() + del self.doneBarrier + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def getDeckSeed(self): + return self.deckSeed + + def isCardFaceUp(self, deckOrderIndex): + retval = False + for key in self.faceUpDict.keys(): + if deckOrderIndex in self.faceUpDict[key]: + retval = True + break + + return retval + + def isCardFaceDown(self, deckOrderIndex): + return not self.isCardFaceUp(deckOrderIndex) + + def checkForMatch(self): + faceUpList = [] + for oneToonFaceUpList in self.faceUpDict.values(): + faceUpList += oneToonFaceUpList + + for i in range(len(faceUpList)): + cardA = faceUpList[i] + for j in range(i + 1, len(faceUpList)): + cardB = faceUpList[j] + if self.cards[cardA].rank == self.cards[cardB].rank: + return (cardA, cardB) + + return (-1, -1) + + def handleMatch(self, cardA, cardB): + self.notify.debug('we got a match %d %d' % (cardA, cardB)) + for key in self.faceUpDict.keys(): + if cardA in self.faceUpDict[key]: + self.faceUpDict[key].remove(cardA) + if cardB in self.faceUpDict[key]: + self.faceUpDict[key].remove(cardB) + + self.inactiveList.append(cardA) + self.inactiveList.append(cardB) + + def turnDownCard(self, cardA): + self.notify.debug('turning down card %d' % cardA) + for key in self.faceUpDict.keys(): + if cardA in self.faceUpDict[key]: + self.faceUpDict[key].remove(cardA) + + def openCardRequest(self, deckOrderIndex, bonusGlowCard): + if self.isCardFaceUp(deckOrderIndex): + return + if self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'openCardRequest from non-player av %s' % avId) + return + if deckOrderIndex < 0 or deckOrderIndex >= len(self.cards): + self.logSuspicious(avId, 'openCardRequest: invalid deckOrderIndex: %s' % deckOrderIndex) + return + if bonusGlowCard < 0 or bonusGlowCard >= len(self.cards): + self.logSuspicious(avId, 'openCardRequest: invalid bonusGlowCard: %s' % bonusGlowCard) + return + cardsToTurnDown = [] + faceUpList = self.faceUpDict[avId] + numCardsFaceUpAtStart = len(faceUpList) + if len(faceUpList) >= self.maxOpenCards: + oldestCard = faceUpList.pop(0) + cardsToTurnDown.append(oldestCard) + if self.TurnDownTwoAtATime and numCardsFaceUpAtStart == 2: + secondOldestCard = faceUpList.pop(0) + cardsToTurnDown.append(secondOldestCard) + cardToTurnUp = deckOrderIndex + self.faceUpDict[avId].append(cardToTurnUp) + cardA, cardB = self.checkForMatch() + matchingCard = -1 + if cardA > -1: + self.handleMatch(cardA, cardB) + if cardA == deckOrderIndex: + matchingCard = cardB + else: + matchingCard = cardA + pointsToGive = 1 + if bonusGlowCard in [cardA, cardB]: + pointsToGive += 1 + self.points += pointsToGive + self.matches += 1 + self.flips += 1 + self.sendUpdate('openCardResult', [cardToTurnUp, + avId, + matchingCard, + self.points, + cardsToTurnDown]) + + def reportDone(self): + if self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('reportDone: avatar %s is done' % avId) + self.doneBarrier.clear(avId) diff --git a/toontown/minigame/DistributedPatternGame.py b/toontown/minigame/DistributedPatternGame.py new file mode 100644 index 0000000..e3b5bac --- /dev/null +++ b/toontown/minigame/DistributedPatternGame.py @@ -0,0 +1,820 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from DistributedMinigame import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownTimer +import PatternGameGlobals +from toontown.toon import ToonHead +from toontown.char import CharDNA +from toontown.char import Char +import ArrowKeys +import random +from toontown.toonbase import ToontownGlobals +import string +from toontown.toonbase import TTLocalizer + +class DistributedPatternGame(DistributedMinigame): + phase4snd = 'phase_4/audio/sfx/' + ButtonSoundNames = (phase4snd + 'm_match_trumpet.mp3', + phase4snd + 'm_match_guitar.mp3', + phase4snd + 'm_match_drums.mp3', + phase4snd + 'm_match_piano.mp3') + bgm = 'phase_4/audio/bgm/m_match_bg1.mid' + strWatch = TTLocalizer.PatternGameWatch + strGo = TTLocalizer.PatternGameGo + strRight = TTLocalizer.PatternGameRight + strWrong = TTLocalizer.PatternGameWrong + strPerfect = TTLocalizer.PatternGamePerfect + strBye = TTLocalizer.PatternGameBye + strWaitingOtherPlayers = TTLocalizer.PatternGameWaitingOtherPlayers + strPleaseWait = TTLocalizer.PatternGamePleaseWait + strRound = TTLocalizer.PatternGameRound + minnieAnimNames = ['up', + 'left', + 'down', + 'right'] + toonAnimNames = ['up', + 'left', + 'down', + 'right', + 'slip-forward', + 'slip-backward', + 'victory'] + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPatternGame', [State.State('off', self.enterOff, self.exitOff, ['waitForServerPattern']), + State.State('waitForServerPattern', self.enterWaitForServerPattern, self.exitWaitForServerPattern, ['showServerPattern', 'cleanup']), + State.State('showServerPattern', self.enterShowServerPattern, self.exitShowServerPattern, ['getUserInput', 'playBackPatterns', 'cleanup']), + State.State('getUserInput', self.enterGetUserInput, self.exitGetUserInput, ['waitForPlayerPatterns', 'playBackPatterns', 'cleanup']), + State.State('waitForPlayerPatterns', self.enterWaitForPlayerPatterns, self.exitWaitForPlayerPatterns, ['playBackPatterns', 'cleanup', 'checkGameOver']), + State.State('playBackPatterns', self.enterPlayBackPatterns, self.exitPlayBackPatterns, ['checkGameOver', 'cleanup']), + State.State('checkGameOver', self.enterCheckGameOver, self.exitCheckGameOver, ['waitForServerPattern', 'cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.arrowColor = VBase4(1, 0, 0, 1) + self.xColor = VBase4(1, 0, 0, 1) + self.celebrate = 0 + self.oldBgColor = None + self.trans = VBase4(1, 0, 0, 0) + self.opaq = VBase4(1, 0, 0, 1) + self.normalTextColor = VBase4(0.537, 0.84, 0.33, 1.0) + self.__otherToonIndex = {} + return + + def getTitle(self): + return TTLocalizer.PatternGameTitle + + def getInstructions(self): + return TTLocalizer.PatternGameInstructions + + def getMaxDuration(self): + inputDur = PatternGameGlobals.NUM_ROUNDS * PatternGameGlobals.InputTime + return inputDur * 1.3 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.room = loader.loadModel('phase_4/models/minigames/matching_room') + self.buttonSounds = [] + for soundName in self.ButtonSoundNames: + self.buttonSounds.append(base.loadSfx(soundName)) + + self.correctSound = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.wav') + self.incorrectSound = base.loadSfx('phase_4/audio/sfx/MG_neg_buzzer.wav') + self.perfectSound = base.loadSfx('phase_4/audio/sfx/MG_win.mp3') + self.fallSound = base.loadSfx('phase_4/audio/sfx/MG_Tag_A.mp3') + self.music = base.loadMusic(self.bgm) + self.waitingText = DirectLabel(text=self.strPleaseWait, text_fg=(0.9, 0.9, 0.9, 1.0), frameColor=(1, 1, 1, 0), text_font=ToontownGlobals.getSignFont(), pos=(0, 0, -.78), scale=0.12) + self.roundText = DirectLabel(text=self.strRound % 1, text_fg=self.normalTextColor, frameColor=(1, 1, 1, 0), text_font=ToontownGlobals.getSignFont(), pos=(0.014, 0, -.84), scale=0.12) + self.roundText.hide() + self.waitingText.hide() + matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + minnieArrow = matchingGameGui.find('**/minnieArrow') + minnieX = matchingGameGui.find('**/minnieX') + minnieCircle = matchingGameGui.find('**/minnieCircle') + self.arrows = [None] * 5 + for x in range(0, 5): + self.arrows[x] = minnieArrow.copyTo(hidden) + self.arrows[x].hide() + + self.xs = [None] * 5 + for x in range(0, 5): + self.xs[x] = minnieX.copyTo(hidden) + self.xs[x].hide() + + self.statusBalls = [] + self.totalMoves = PatternGameGlobals.INITIAL_ROUND_LENGTH + PatternGameGlobals.ROUND_LENGTH_INCREMENT * (PatternGameGlobals.NUM_ROUNDS - 1) + for x in range(0, 4): + self.statusBalls.append([None] * self.totalMoves) + + for x in range(0, 4): + for y in range(0, self.totalMoves): + self.statusBalls[x][y] = minnieCircle.copyTo(hidden) + self.statusBalls[x][y].hide() + + minnieArrow.removeNode() + minnieX.removeNode() + minnieCircle.removeNode() + matchingGameGui.removeNode() + self.minnie = Char.Char() + m = self.minnie + dna = CharDNA.CharDNA() + dna.newChar('mn') + m.setDNA(dna) + m.setName(TTLocalizer.Minnie) + m.reparentTo(hidden) + self.backRowHome = Point3(3, 11, 0) + self.backRowXSpacing = 1.8 + self.frontRowHome = Point3(0, 18, 0) + self.frontRowXSpacing = 3.0 + self.stdNumDanceStepPingFrames = self.minnie.getNumFrames(self.minnieAnimNames[0]) + self.stdNumDanceStepPingPongFrames = self.__numPingPongFrames(self.stdNumDanceStepPingFrames) + self.buttonPressDelayPercent = (self.stdNumDanceStepPingFrames - 1.0) / self.stdNumDanceStepPingPongFrames + self.animPlayRates = [] + animPlayRate = 1.4 + animPlayRateMult = 1.06 + for i in range(PatternGameGlobals.NUM_ROUNDS): + self.animPlayRates.append(animPlayRate) + animPlayRate *= animPlayRateMult + + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.timer.destroy() + del self.timer + del self.lt + del self.buttonSounds + del self.music + del self.__otherToonIndex + del self.correctSound + del self.incorrectSound + del self.perfectSound + del self.fallSound + self.waitingText.destroy() + del self.waitingText + self.roundText.destroy() + del self.roundText + for x in self.arrowDict.values(): + x[0].removeNode() + x[1].removeNode() + if len(x) == 3: + for y in x[2]: + y.removeNode() + + del self.arrowDict + for x in self.arrows: + if x: + x.removeNode() + + del self.arrows + for x in self.xs: + if x: + x.removeNode() + + del self.xs + for x in self.statusBalls: + if x: + for y in x: + if y: + y.removeNode() + del y + + del self.statusBalls + self.room.removeNode() + del self.room + self.minnie.delete() + del self.minnie + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.arrowDict = {} + self.lt = base.localAvatar + camera.reparentTo(render) + camera.setPosHpr(0.0, -14.59, 10.56, 0.0, -16.39, 0.0) + base.camLens.setFov(24.66) + NametagGlobals.setGlobalNametagScale(0.6) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.room.reparentTo(render) + self.room.setPosHpr(0.0, 18.39, -ToontownGlobals.FloorOffset, 0.0, 0.0, 0.0) + self.room.setScale(1) + for anim in self.minnieAnimNames: + self.minnie.pose(anim, 0) + + for anim in self.toonAnimNames: + self.lt.pose(anim, 0) + + self.minnieAnimSpeedMult = {} + self.toonAnimSpeedMult = {} + for anim in self.minnieAnimNames: + numFrames = self.minnie.getNumFrames(anim) + self.minnieAnimSpeedMult[anim] = float(self.__numPingPongFrames(numFrames)) / float(self.stdNumDanceStepPingPongFrames) + + for anim in self.toonAnimNames: + numFrames = self.lt.getNumFrames(anim) + self.toonAnimSpeedMult[anim] = float(self.__numPingPongFrames(numFrames)) / float(self.stdNumDanceStepPingPongFrames) + + lt = self.lt + lt.reparentTo(render) + lt.useLOD(1000) + lt.setPos(-3.5, 11, 0.0) + lt.setScale(1) + self.makeToonLookatCamera(lt) + lt.loop('neutral') + lt.startBlink() + lt.startLookAround() + self.arrowDict['lt'] = [self.arrows.pop(), self.xs.pop(), self.statusBalls.pop()] + jj = self.lt.nametag3d + for k in range(0, 2): + self.arrowDict['lt'][k].setBillboardAxis() + self.arrowDict['lt'][k].setBin('fixed', 100) + self.arrowDict['lt'][k].reparentTo(jj) + if k == 0: + self.arrowDict['lt'][k].setScale(2.5) + self.arrowDict['lt'][k].setColor(self.arrowColor) + else: + self.arrowDict['lt'][k].setScale(4, 4, 4) + self.arrowDict['lt'][k].setColor(self.xColor) + self.arrowDict['lt'][k].setPos(0, 0, 1) + + self.formatStatusBalls(self.arrowDict['lt'][2], jj) + m = self.minnie + m.reparentTo(render) + m.setPos(-1.6, 20, 0) + m.setScale(1) + self.makeToonLookatCamera(m) + m.loop('neutral') + m.startBlink() + self.minnie.nametag.manage(base.marginManager) + self.minnie.startEarTask() + self.minnie.setPickable(0) + self.minnie.nametag.getNametag3d().setChatWordwrap(8) + self.arrowDict['m'] = [self.arrows.pop(), self.xs.pop()] + jj = self.minnie.nametag3d + for k in range(0, 2): + self.arrowDict['m'][k].setBillboardAxis() + self.arrowDict['m'][k].setBin('fixed', 100) + self.arrowDict['m'][k].setColor(self.arrowColor) + self.arrowDict['m'][k].reparentTo(jj) + self.arrowDict['m'][k].setScale(4) + self.arrowDict['m'][k].setPos(0, 0, 1.7) + + base.playMusic(self.music, looping=1, volume=1) + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + base.camLens.setFov(ToontownGlobals.DefaultCameraFov) + NametagGlobals.setGlobalNametagScale(1.0) + self.arrowKeys.destroy() + del self.arrowKeys + self.room.reparentTo(hidden) + self.roundText.hide() + self.minnie.nametag.unmanage(base.marginManager) + self.minnie.stopEarTask() + self.minnie.stop() + self.minnie.stopBlink() + self.minnie.reparentTo(hidden) + self.lt.setScale(1) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.setScale(1) + + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.resetLOD() + for anim in self.toonAnimNames: + av.setPlayRate(1.0, anim) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + self.arrowDict[avId] = [self.arrows.pop(), self.xs.pop(), self.statusBalls.pop()] + jj = toon.nametag3d + for k in range(0, 2): + self.arrowDict[avId][k].setBillboardAxis() + self.arrowDict[avId][k].setBin('fixed', 100) + self.arrowDict[avId][k].reparentTo(jj) + if k == 0: + self.arrowDict[avId][k].setScale(2.5) + self.arrowDict[avId][k].setColor(self.arrowColor) + else: + self.arrowDict[avId][k].setScale(4, 4, 4) + self.arrowDict[avId][k].setColor(self.xColor) + self.arrowDict[avId][k].setPos(0, 0, 1) + + self.formatStatusBalls(self.arrowDict[avId][2], jj) + toon.reparentTo(render) + toon.useLOD(1000) + toon.setPos(self.getBackRowPos(avId)) + toon.setScale(0.9) + self.makeToonLookatCamera(toon) + for anim in self.toonAnimNames: + toon.pose(anim, 0) + + toon.loop('neutral') + + if self.isSinglePlayer(): + self.waitingText['text'] = self.strPleaseWait + else: + self.waitingText['text'] = self.strWaitingOtherPlayers + self.animTracks = {} + for avId in self.avIdList: + self.animTracks[avId] = None + + self.__initGameVars() + return + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('waitForServerPattern') + + def __initGameVars(self): + self.round = 0 + self.perfectGame = 1 + + def __numPingPongFrames(self, numFrames): + return numFrames * 2 - 1 + + def makeToonLookatCamera(self, toon): + toon.headsUp(camera) + + def setText(self, t, newtext): + t['text'] = newtext + + def setTextFG(self, t, fg): + t['text_fg'] = fg + + def getWalkTrack(self, toon, posList, startPos = None, lookAtCam = 1, endHeading = 180): + walkSpeed = 7 + origPos = toon.getPos() + origHpr = toon.getHpr() + track = Sequence(Func(toon.loop, 'run')) + if startPos: + toon.setPos(startPos) + track.append(Func(toon.setPos, startPos)) + for endPos in posList: + toon.headsUp(Point3(endPos)) + track.append(Func(toon.setHpr, Point3(toon.getH(), 0, 0))) + lastPos = toon.getPos() + distance = Vec3(endPos - lastPos).length() + duration = distance / walkSpeed + toon.setPos(endPos) + track.append(LerpPosInterval(toon, duration=duration, pos=Point3(endPos), startPos=Point3(lastPos))) + + if lookAtCam: + saveHpr = toon.getHpr() + toon.headsUp(camera) + endHeading = toon.getHpr()[0] + toon.setHpr(saveHpr) + curHeading = toon.getH() + if endHeading - curHeading > 180.0: + endHeading -= 360 + elif endHeading - curHeading < -180.0: + endHeading += 360 + endHpr = Point3(endHeading, 0, 0) + duration = abs(endHeading - curHeading) / 180.0 * 0.3 + track.extend([Func(toon.loop, 'walk'), LerpHprInterval(toon, duration, endHpr), Func(toon.loop, 'neutral')]) + toon.setPos(origPos) + toon.setHpr(origHpr) + return track + + def getDanceStepDuration(self): + numFrames = self.stdNumDanceStepPingPongFrames + return numFrames / abs(self.animPlayRate * self.minnieAnimSpeedMult[self.minnieAnimNames[0]] * self.minnie.getFrameRate(self.minnieAnimNames[0])) + + def __getDanceStepAnimTrack(self, toon, anim, speedScale): + numFrames = toon.getNumFrames(anim) + return Sequence(Func(toon.pingpong, anim, fromFrame=0, toFrame=numFrames - 1), Wait(self.getDanceStepDuration())) + + def __getMinnieDanceStepAnimTrack(self, minnie, direction): + animName = self.minnieAnimNames[direction] + return self.__getDanceStepAnimTrack(minnie, animName, self.minnieAnimSpeedMult[animName]) + + def __getToonDanceStepAnimTrack(self, toon, direction): + animName = self.toonAnimNames[direction] + return self.__getDanceStepAnimTrack(toon, animName, self.toonAnimSpeedMult[animName]) + + def getDanceStepButtonSoundTrack(self, index): + duration = self.getDanceStepDuration() + wait = duration * self.buttonPressDelayPercent + return Sequence(Wait(wait), Func(base.playSfx, self.__getButtonSound(index)), Wait(duration - wait)) + + def getDanceArrowAnimTrack(self, toonID, pattern, speedy): + track = Sequence() + track.append(Func(self.showArrow, toonID)) + for buttonIndex in pattern: + track.append(self.getDanceArrowSingleTrack(toonID, buttonIndex, speedy)) + + track.append(Func(self.hideArrow, toonID)) + return track + + def changeArrow(self, toonID, index): + self.arrowDict[toonID][0].setR(-(90 - 90 * index)) + + def showArrow(self, toonID): + self.arrowDict[toonID][0].show() + + def hideArrow(self, toonID): + self.arrowDict[toonID][0].hide() + + def showX(self, toonID): + self.arrowDict[toonID][1].show() + + def hideX(self, toonID): + self.arrowDict[toonID][1].hide() + + def celebrated(self): + self.celebrate = 1 + + def returnCelebrationIntervals(self, turnOn): + ri = [] + if turnOn: + ri.append(ActorInterval(actor=self.lt, animName='victory', duration=5.5)) + else: + ri.append(Func(self.lt.loop, 'neutral')) + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + if turnOn: + ri.append(ActorInterval(actor=toon, animName='victory', duration=5.5)) + else: + ri.append(Func(toon.loop, 'neutral')) + + if len(self.remoteAvIdList) == 0: + return ri + else: + return Parallel(ri) + + def formatStatusBalls(self, sb, jj): + for x in range(0, self.totalMoves): + sb[x].setBillboardAxis() + sb[x].setBin('fixed', 100) + sb[x].reparentTo(jj) + sb[x].setScale(1) + xpos = +(int(self.totalMoves / 2) * 0.25) - 0.25 * x + sb[x].setPos(xpos, 0, 0.3) + + def showStatusBalls(self, toonID): + sb = self.arrowDict[toonID][2] + for x in range(0, len(self.__serverPattern)): + sb[x].setColor(1, 1, 1, 1) + sb[x].show() + + def hideStatusBalls(self, toonID): + sb = self.arrowDict[toonID][2] + for x in range(0, len(sb)): + sb[x].hide() + + def colorStatusBall(self, toonID, which, good): + if good: + self.arrowDict[toonID][2][which].setColor(0, 1, 0, 1) + else: + self.arrowDict[toonID][2][which].setColor(1, 0, 0, 1) + + def getDanceArrowSingleTrack(self, toonID, index, speedy): + duration = self.getDanceStepDuration() + wait = duration * self.buttonPressDelayPercent + d = duration - wait + if speedy: + track = Sequence(Func(self.changeArrow, toonID, index), Wait(wait)) + else: + track = Sequence(Func(self.changeArrow, toonID, index), Wait(wait), LerpColorInterval(self.arrowDict[toonID][0], d, self.trans, self.opaq)) + return track + + def getDanceSequenceAnimTrack(self, toon, pattern): + getDanceStepTrack = self.__getToonDanceStepAnimTrack + if toon == self.minnie: + getDanceStepTrack = self.__getMinnieDanceStepAnimTrack + tracks = Sequence() + for direction in pattern: + tracks.append(getDanceStepTrack(toon, direction)) + + if len(pattern): + tracks.append(Func(toon.loop, 'neutral')) + return tracks + + def getDanceSequenceButtonSoundTrack(self, pattern): + track = Sequence() + for buttonIndex in pattern: + track.append(self.getDanceStepButtonSoundTrack(buttonIndex)) + + return track + + def __getRowPos(self, rowHome, xSpacing, index, numSpots): + xOffset = xSpacing * index - xSpacing * (numSpots - 1) / 2.0 + return rowHome + Point3(xOffset, 0, 0) + + def getBackRowPos(self, avId): + index = self.remoteAvIdList.index(avId) + return self.__getRowPos(self.backRowHome, self.backRowXSpacing, index, len(self.remoteAvIdList)) + + def getFrontRowPos(self, avId): + index = self.avIdList.index(avId) + return self.__getRowPos(self.frontRowHome, self.frontRowXSpacing, index, len(self.avIdList)) + + def __setMinnieChat(self, str, giggle): + str = string.replace(str, '%s', self.getAvatar(self.localAvId).getName()) + self.minnie.setChatAbsolute(str, CFSpeech) + if giggle: + self.minnie.playDialogue('statementA', 1) + + def __clearMinnieChat(self): + self.minnie.clearChat() + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterWaitForServerPattern(self): + self.notify.debug('enterWaitForServerPattern') + self.sendUpdate('reportPlayerReady', []) + + def setPattern(self, pattern): + if not self.hasLocalToon: + return + self.notify.debug('setPattern: ' + str(pattern)) + self.__serverPattern = pattern + self.gameFSM.request('showServerPattern') + + def exitWaitForServerPattern(self): + pass + + def enterShowServerPattern(self): + self.notify.debug('enterShowServerPattern') + self.round += 1 + self.roundText.show() + self.roundText.setScale(0.12) + self.roundText['text'] = self.strRound % self.round + self.animPlayRate = self.animPlayRates[self.round - 1] + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + for anim in self.toonAnimNames: + toon.setPlayRate(self.animPlayRate * self.toonAnimSpeedMult[anim], anim) + + for anim in self.minnieAnimNames: + self.minnie.setPlayRate(self.animPlayRate * self.minnieAnimSpeedMult[anim], anim) + + text = self.strWatch + danceTrack = self.getDanceSequenceAnimTrack(self.minnie, self.__serverPattern) + arrowTrack = self.getDanceArrowAnimTrack('m', self.__serverPattern, 0) + soundTrack = self.getDanceSequenceButtonSoundTrack(self.__serverPattern) + self.showTrack = Sequence(Func(self.__setMinnieChat, text, 1), Wait(0.5), Parallel(danceTrack, soundTrack, arrowTrack), Wait(0.2), Func(self.__clearMinnieChat), Func(self.gameFSM.request, 'getUserInput')) + self.showTrack.start() + + def exitShowServerPattern(self): + if self.showTrack.isPlaying(): + self.showTrack.pause() + del self.showTrack + + def enterGetUserInput(self): + self.notify.debug('enterGetUserInput') + self.setupTrack = None + self.proceedTrack = None + + def startTimer(self = self): + self.currentStartTime = globalClock.getFrameTime() + self.timer.show() + self.timer.countdown(PatternGameGlobals.InputTime, self.__handleInputTimeout) + + def enableKeys(self = self): + + def keyPress(self, index): + self.__pressHandler(index) + + def keyRelease(self, index): + self.__releaseHandler(index) + + self.arrowKeys.setPressHandlers([lambda self = self, keyPress = keyPress: keyPress(self, 0), + lambda self = self, keyPress = keyPress: keyPress(self, 2), + lambda self = self, keyPress = keyPress: keyPress(self, 3), + lambda self = self, keyPress = keyPress: keyPress(self, 1)]) + self.arrowKeys.setReleaseHandlers([lambda self = self, keyRelease = keyRelease: keyRelease(self, 0), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 2), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 3), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 1)]) + + self.__localPattern = [] + self.__otherToonIndex.clear() + self.showStatusBalls('lt') + for avId in self.remoteAvIdList: + self.showStatusBalls(avId) + self.__otherToonIndex[avId] = 0 + + self.setupTrack = Sequence(Func(self.__setMinnieChat, self.strGo, 0), Func(self.setText, self.roundText, TTLocalizer.PatternGameGo), Func(self.roundText.setScale, 0.3), Func(enableKeys), Func(startTimer), Wait(0.8), Func(self.__clearMinnieChat), Func(self.setText, self.roundText, ' '), Func(self.roundText.setScale, 0.12), Func(self.setTextFG, self.roundText, self.normalTextColor)) + self.setupTrack.start() + return + + def __handleInputTimeout(self): + self.__doneGettingInput(self.__localPattern) + + def __pressHandler(self, index): + self.__buttonPressed(index) + + def __releaseHandler(self, index): + pass + + def remoteButtonPressed(self, avId, index, wrong): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() not in ['getUserInput', 'waitForPlayerPatterns']: + return + if avId != self.localAvId: + if self.animTracks[avId]: + self.animTracks[avId].finish() + av = self.getAvatar(avId) + if wrong: + acts = ['slip-forward', 'slip-backward'] + ag = random.choice(acts) + self.arrowDict[avId][0].hide() + self.animTracks[avId] = Sequence(Func(self.showX, avId), Func(self.colorStatusBall, avId, self.__otherToonIndex[avId], 0), ActorInterval(actor=av, animName=ag, duration=2.35), Func(av.loop, 'neutral'), Func(self.hideX, avId)) + else: + self.colorStatusBall(avId, self.__otherToonIndex[avId], 1) + arrowTrack = self.getDanceArrowAnimTrack(avId, [index], 1) + potTrack = self.getDanceSequenceAnimTrack(av, [index]) + self.animTracks[avId] = Parallel(potTrack, arrowTrack) + self.__otherToonIndex[avId] += 1 + self.animTracks[avId].start() + + def __getButtonSound(self, index): + return self.buttonSounds[index] + + def __buttonPressed(self, index): + if len(self.__localPattern) >= len(self.__serverPattern): + return + if self.animTracks[self.localAvId]: + self.animTracks[self.localAvId].finish() + badd = 0 + if index != self.__serverPattern[len(self.__localPattern)]: + badd = 1 + acts = ['slip-forward', 'slip-backward'] + ag = random.choice(acts) + self.animTracks[self.localAvId] = Sequence(Func(self.showX, 'lt'), Func(self.colorStatusBall, 'lt', len(self.__localPattern), 0), ActorInterval(actor=self.lt, animName=ag, duration=2.35), Func(self.lt.loop, 'neutral'), Func(self.hideX, 'lt')) + self.arrowDict['lt'][0].hide() + base.playSfx(self.fallSound) + else: + self.colorStatusBall('lt', len(self.__localPattern), 1) + base.playSfx(self.__getButtonSound(index)) + arrowTrack = self.getDanceArrowAnimTrack('lt', [index], 1) + potTrack = self.getDanceSequenceAnimTrack(self.lt, [index]) + self.animTracks[self.localAvId] = Parallel(potTrack, arrowTrack) + self.sendUpdate('reportButtonPress', [index, badd]) + self.animTracks[self.localAvId].start() + self.__localPattern.append(index) + if len(self.__localPattern) == len(self.__serverPattern) or badd: + self.__doneGettingInput(self.__localPattern) + + def __doneGettingInput(self, pattern): + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.currentTotalTime = globalClock.getFrameTime() - self.currentStartTime + self.proceedTrack = Sequence(Wait(self.getDanceStepDuration()), Func(self.sendUpdate, 'reportPlayerPattern', [pattern, self.currentTotalTime]), Func(self.gameFSM.request, 'waitForPlayerPatterns')) + self.proceedTrack.start() + + def exitGetUserInput(self): + self.timer.stop() + self.timer.hide() + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + if self.setupTrack and self.setupTrack.isPlaying(): + self.setupTrack.pause() + if self.proceedTrack and self.proceedTrack.isPlaying(): + self.proceedTrack.pause() + del self.setupTrack + del self.proceedTrack + self.__clearMinnieChat() + + def enterWaitForPlayerPatterns(self): + self.notify.debug('enterWaitForPlayerPatterns') + + def setPlayerPatterns(self, pattern1, pattern2, pattern3, pattern4, fastestAvId): + if not self.hasLocalToon: + return + self.fastestAvId = fastestAvId + self.notify.debug('setPlayerPatterns:' + ' pattern1:' + str(pattern1) + ' pattern2:' + str(pattern2) + ' pattern3:' + str(pattern3) + ' pattern4:' + str(pattern4)) + self.playerPatterns = {} + patterns = [pattern1, + pattern2, + pattern3, + pattern4] + for i in range(len(self.avIdList)): + self.playerPatterns[self.avIdList[i]] = patterns[i] + + self.gameFSM.request('playBackPatterns') + + def exitWaitForPlayerPatterns(self): + self.waitingText.hide() + + def enterPlayBackPatterns(self): + self.notify.debug('enterPlayBackPatterns') + if self.fastestAvId == self.localAvId: + self.roundText.setScale(0.1) + if self.numPlayers != 2: + self.roundText['text'] = TTLocalizer.PatternGameFastest + else: + self.roundText['text'] = TTLocalizer.PatternGameFaster + jumpTrack = Sequence(ActorInterval(actor=self.lt, animName='jump', duration=1.7), Func(self.lt.loop, 'neutral')) + elif self.fastestAvId == 0: + if self.round == PatternGameGlobals.NUM_ROUNDS: + self.roundText['text'] = ' ' + else: + self.roundText.setScale(0.1) + self.roundText['text'] = TTLocalizer.PatternGameYouCanDoIt + jumpTrack = Sequence(Wait(0.5), Wait(0.5)) + elif self.fastestAvId == 1: + self.roundText.setScale(0.1) + self.roundText['text'] = TTLocalizer.PatternGameGreatJob + jumpTrack = Sequence(Wait(0.5), Wait(0.5)) + else: + self.roundText.setScale(0.08) + av = self.getAvatar(self.fastestAvId) + jumpTrack = Sequence(ActorInterval(actor=av, animName='jump', duration=1.7), Func(av.loop, 'neutral')) + if self.numPlayers != 2: + rewardStr = TTLocalizer.PatternGameOtherFastest + else: + rewardStr = TTLocalizer.PatternGameOtherFaster + self.roundText['text'] = av.getName() + rewardStr + success = self.playerPatterns[self.localAvId] == self.__serverPattern + self.hideStatusBalls('lt') + for avId in self.remoteAvIdList: + self.hideStatusBalls(avId) + + if success: + sound = self.correctSound + text = self.strRight + else: + self.perfectGame = 0 + sound = self.incorrectSound + text = self.strWrong + soundTrack = Sequence(Func(base.playSfx, sound), Wait(1.6)) + textTrack = Sequence(Wait(0.2), Func(self.__setMinnieChat, text, 0), Wait(1.3), Func(self.__clearMinnieChat)) + self.playBackPatternsTrack = Sequence(Parallel(soundTrack, textTrack, jumpTrack), Func(self.gameFSM.request, 'checkGameOver')) + self.playBackPatternsTrack.start() + + def exitPlayBackPatterns(self): + if self.playBackPatternsTrack.isPlaying(): + self.playBackPatternsTrack.pause() + del self.playBackPatternsTrack + + def enterCheckGameOver(self): + self.notify.debug('enterCheckGameOver') + self.__winTrack = None + if self.round < PatternGameGlobals.NUM_ROUNDS: + self.gameFSM.request('waitForServerPattern') + else: + text = self.strBye + sound = None + delay = 2.0 + if self.perfectGame: + text = self.strPerfect + sound = self.perfectSound + delay = 2.2 + if self.celebrate: + text = TTLocalizer.PatternGameImprov + self.__winTrack = Sequence(Func(self.__setMinnieChat, text, 1), Func(base.playSfx, self.perfectSound), Sequence(self.returnCelebrationIntervals(1)), Sequence(self.returnCelebrationIntervals(0)), Func(self.__clearMinnieChat), Func(self.gameOver)) + else: + self.__winTrack = Sequence(Func(self.__setMinnieChat, text, 1), Func(base.playSfx, sound), Wait(delay), Func(self.__clearMinnieChat), Func(self.gameOver)) + self.__winTrack.start() + return + + def exitCheckGameOver(self): + if self.__winTrack and self.__winTrack.isPlaying(): + self.__winTrack.pause() + del self.__winTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + for track in self.animTracks.values(): + if track and track.isPlaying(): + track.pause() + + del self.animTracks + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedPatternGameAI.py b/toontown/minigame/DistributedPatternGameAI.py new file mode 100644 index 0000000..198660a --- /dev/null +++ b/toontown/minigame/DistributedPatternGameAI.py @@ -0,0 +1,195 @@ +from DistributedMinigameAI import * +from toontown.ai.ToonBarrier import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import random +import PatternGameGlobals +import copy + +class DistributedPatternGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedPatternGameAI_initialized + except: + self.DistributedPatternGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPatternGameAI', [State.State('off', self.enterInactive, self.exitInactive, ['waitClientsReady', 'cleanup']), + State.State('waitClientsReady', self.enterWaitClientsReady, self.exitWaitClientsReady, ['generatePattern', 'cleanup']), + State.State('generatePattern', self.enterGeneratePattern, self.exitGeneratePattern, ['waitForResults', 'cleanup']), + State.State('waitForResults', self.enterWaitForResults, self.exitWaitForResults, ['waitClientsReady', 'cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + self.__initGameVars() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsReady') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def __initGameVars(self): + self.pattern = [] + self.round = 0 + self.perfectResults = {} + for avId in self.avIdList: + self.perfectResults[avId] = 1 + + self.readyClients = [] + self.timeoutTaskName = self.uniqueName('PatternGameResultsTimeout') + + def enterWaitClientsReady(self): + self.notify.debug('enterWaitClientsReady') + self.nextRoundBarrier = ToonBarrier('nextRoundReady', self.uniqueName('nextRoundReady'), self.avIdList, PatternGameGlobals.ClientsReadyTimeout, self.__allPlayersReady, self.__clientsReadyTimeout) + for avId in self.readyClients: + self.nextRoundBarrier.clear(avId) + + def reportPlayerReady(self): + if not self._inState('waitClientsReady'): + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.notify.warning('Got reportPlayerReady from an avId: %s not in our list: %s' % (avId, self.avIdList)) + else: + self.readyClients.append(avId) + self.nextRoundBarrier.clear(avId) + + def __allPlayersReady(self): + self.readyClients = [] + self.gameFSM.request('generatePattern') + + def __clientsReadyTimeout(self, avIds): + self.notify.debug('__clientsReadyTimeout: clients %s have not responded' % avIds) + self.setGameAbort() + + def exitWaitClientsReady(self): + self.nextRoundBarrier.cleanup() + del self.nextRoundBarrier + + def enterGeneratePattern(self): + self.notify.debug('enterGeneratePattern') + self.round += 1 + targetLen = PatternGameGlobals.INITIAL_ROUND_LENGTH + PatternGameGlobals.ROUND_LENGTH_INCREMENT * (self.round - 1) + count = targetLen - len(self.pattern) + for i in range(0, count): + self.pattern.append(random.randint(0, 3)) + + self.gameFSM.request('waitForResults') + self.sendUpdate('setPattern', [self.pattern]) + + def exitGeneratePattern(self): + pass + + def enterWaitForResults(self): + self.notify.debug('enterWaitForResults') + self.results = [None] * self.numPlayers + self.fastestTime = PatternGameGlobals.InputTime * 2 + self.fastestAvId = 0 + self.resultsBarrier = ToonBarrier('results', self.uniqueName('results'), self.avIdList, PatternGameGlobals.InputTimeout + 1.0 * self.round, self.__gotAllPatterns, self.__resultsTimeout) + return + + def reportButtonPress(self, index, wrong): + if not self._inState('waitForResults'): + return + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'PatternGameAI.reportButtonPress avId not on list') + return + if index < 0 or index > 3: + self.air.writeServerEvent('warning', index, 'PatternGameAI.reportButtonPress got bad index') + return + if wrong not in [0, 1]: + self.air.writeServerEvent('warning', wrong, "PatternGameAI.reportButtonPress got bad 'wrong'") + return + self.sendUpdate('remoteButtonPressed', [avId, index, wrong]) + + def __resultsTimeout(self, avIds): + self.notify.debug('__resultsTimeout: %s' % avIds) + for avId in avIds: + index = self.avIdList.index(avId) + self.__acceptPlayerPattern(avId, [], PatternGameGlobals.InputTime * 2) + + self.__gotAllPatterns() + + def reportPlayerPattern(self, pattern, totalTime): + if not self._inState('waitForResults'): + return + avId = self.air.getAvatarIdFromSender() + self.__acceptPlayerPattern(avId, pattern, totalTime) + self.resultsBarrier.clear(avId) + + def __acceptPlayerPattern(self, avId, pattern, totalTime): + index = self.avIdList.index(avId) + if self.results[index] != None: + return + self.results[index] = pattern + if totalTime < self.fastestTime and pattern == self.pattern: + self.fastestTime = totalTime + self.fastestAvId = avId + if self.numPlayers == 1: + self.fastestAvId = 1 + else: + self.scoreDict[self.fastestAvId] += 2 + return + + def __gotAllPatterns(self): + patterns = [[]] * 4 + for i in range(0, len(self.results)): + patterns[i] = self.results[i] + if patterns[i] is None: + patterns[i] = [] + + self.sendUpdate('setPlayerPatterns', patterns + [self.fastestAvId]) + for i in range(0, self.numPlayers): + avId = self.avIdList[i] + if not self.results[i] == self.pattern: + self.perfectResults[avId] = 0 + else: + self.scoreDict[avId] += self.round + + if self.round < PatternGameGlobals.NUM_ROUNDS: + self.gameFSM.request('waitClientsReady') + else: + for avId in self.avIdList: + if self.perfectResults[avId]: + self.scoreDict[avId] += 4 + self.logPerfectGame(avId) + + self.gameOver() + self.gameFSM.request('cleanup') + return + + def exitWaitForResults(self): + self.resultsBarrier.cleanup() + del self.resultsBarrier + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedPhotoGame.py b/toontown/minigame/DistributedPhotoGame.py new file mode 100644 index 0000000..e412722 --- /dev/null +++ b/toontown/minigame/DistributedPhotoGame.py @@ -0,0 +1,1661 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownTimer +from direct.task.Task import Task +import math +from toontown.toon import ToonHead +import PhotoGameGlobals +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.golf import BuildGeometry +from toontown.toon import Toon +from toontown.toon import ToonDNA +from direct.interval.IntervalGlobal import * +import random +from direct.showbase import PythonUtil +import math +import time +from toontown.makeatoon import NameGenerator +from otp.otpbase import OTPGlobals +from toontown.battle import BattleParticles +from toontown.minigame import PhotoGameBase +WORLD_SCALE = 2.0 +FAR_PLANE_DIST = 600 * WORLD_SCALE +STAGE_Z_OFFSET = 7.0 +GOODROWS = 13 +BADROWS = 4 +RAYSPREADX = 0.08 +RAYSPREADY = 0.06 +ZOOMRATIO = 0.4 +ZOOMTIME = 0.5 +WANTDOTS = 1 +NUMSTARS = PhotoGameGlobals.NUMSTARS +STARSIZE = 0.06 +VIEWSIZEX = (GOODROWS - BADROWS) * RAYSPREADX +VIEWSIZEY = (GOODROWS - BADROWS) * RAYSPREADY + +def toRadians(angle): + return angle * 2.0 * math.pi / 360.0 + + +def toDegrees(angle): + return angle * 360.0 / (2.0 * math.pi) + + +class DistributedPhotoGame(DistributedMinigame, PhotoGameBase.PhotoGameBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhotoGame') + font = ToontownGlobals.getToonFont() + LOCAL_PHOTO_MOVE_TASK = 'localPhotoMoveTask' + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + INTRO_TASK_NAME = 'PhotoGameIntro' + INTRO_TASK_NAME_CAMERA_LERP = 'PhotoGameIntroCamera' + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + PhotoGameBase.PhotoGameBase.__init__(self) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPhotoGame', [State.State('off', self.enterOff, self.exitOff, ['aim']), + State.State('aim', self.enterAim, self.exitAim, ['showResults', 'cleanup', 'zoom']), + State.State('zoom', self.enterZoom, self.exitZoom, ['showResults', 'cleanup', 'aim']), + State.State('showResults', self.enterShowResults, self.exitShowResults, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.tripod = None + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.photoMoving = 0 + self.introSequence = None + self.subjects = [] + self.scenery = [] + self.subjectNode = render.attachNewNode('subjects') + self.subjectTracks = {} + self.nameCounter = 0 + self.zoomedView = 0 + self.zoomFlip = 1 + self.cameraTrack = None + self.assignments = [] + self.currentAssignment = 0 + self.assignmentPanels = [] + self.toonList = [] + self.assignmentDataDict = {} + self.starDict = {} + self.starParentDict = {} + self.textureBuffers = [] + self.filmCount = 20 + self.edgeUp = 0 + self.edgeRight = 0 + self.edgeDown = 0 + self.edgeLeft = 0 + self.scorePanel = None + return + + def getTitle(self): + return TTLocalizer.PhotoGameTitle + + def getInstructions(self): + return TTLocalizer.PhotoGameInstructions + + def getMaxDuration(self): + return PhotoGameGlobals.GAME_TIME + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + PhotoGameBase.PhotoGameBase.load(self) + self.filmCount = self.data['FILMCOUNT'] + self.safeZoneStorageDNAFile = self.data['DNA_TRIO'][0] + self.storageDNAFile = self.data['DNA_TRIO'][1] + self.dnaFile = self.data['DNA_TRIO'][2] + self.dnaStore = DNAStorage() + loader.loadDNAFile(self.dnaStore, 'phase_4/dna/storage.dna') + loader.loadDNAFile(self.dnaStore, self.storageDNAFile) + loader.loadDNAFile(self.dnaStore, self.safeZoneStorageDNAFile) + node = loader.loadDNAFile(self.dnaStore, self.dnaFile) + self.scene = hidden.attachNewNode(node) + self.construct() + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.filmImage = loader.loadModel('phase_4/models/minigames/photogame_filmroll') + self.filmImage.reparentTo(hidden) + self.tripodModel = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.filmPanel = DirectLabel(parent=hidden, relief=None, pos=(1.16, 0.0, 0.45), scale=0.65, text=str(self.filmCount), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0.08, -0.15), text_font=ToontownGlobals.getSignFont(), image=self.filmImage, image_scale=Point3(1.0, 0.0, 0.85)) + self.filmPanelTitle = DirectLabel(parent=self.filmPanel, relief=None, pos=(0.08, 0, 0.04), scale=0.08, text=TTLocalizer.PhotoGameFilm, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1)) + self.music = base.loadMusic('phase_4/audio/bgm/MG_cannon_game.mid') + self.sndPhotoMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3') + self.sndPhotoFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.sndWin = base.loadSfx('phase_4/audio/sfx/MG_win.mp3') + self.sndFilmTick = base.loadSfx('phase_4/audio/sfx/Photo_instamatic.mp3') + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.viewfinderNode = base.aspect2d.attachNewNode('camera node') + self.viewfinderNode.setTransparency(TransparencyAttrib.MAlpha) + self.viewfinderNode.setDepthWrite(1) + self.viewfinderNode.setDepthTest(1) + self.viewfinderNode.setY(-1.0) + self.screenSizeMult = 0.5 + self.screenSizeX = (base.a2dRight - base.a2dLeft) * self.screenSizeMult + self.screenSizeZ = (base.a2dTop - base.a2dBottom) * self.screenSizeMult + viewfinderImage = loader.loadModel('phase_4/models/minigames/photo_game_viewfinder') + viewfinderImage.reparentTo(self.viewfinderNode) + viewfinderImage.setScale(0.55, 1.0, 0.55) + self.blackoutNode = base.aspect2d.attachNewNode('blackout node') + self.blackoutNode.setP(90) + BuildGeometry.addSquareGeom(self.blackoutNode, self.screenSizeX * 2.2, self.screenSizeZ * 2.2, Vec4(1.0, 1.0, 1.0, 1.0)) + self.blackoutNode.setTransparency(TransparencyAttrib.MAlpha) + self.blackoutNode.setColorScale(0.0, 0.0, 0.0, 0.5) + self.blackoutNode.setDepthWrite(1) + self.blackoutNode.setDepthTest(1) + self.blackoutNode.hide() + self.subjectToon = Toon.Toon() + self.addSound('zoom', 'Photo_zoom.mp3', 'phase_4/audio/sfx/') + self.addSound('snap', 'Photo_shutter.mp3', 'phase_4/audio/sfx/') + return + + def __setupCapture(self): + self.captureCam = NodePath(Camera('CaptureCamera')) + self.captureCam.reparentTo(self.pointer) + self.captureLens = PerspectiveLens() + self.captureOutFOV = VIEWSIZEX / self.screenSizeX * self.outFov * 0.5 + self.captureZoomFOV = VIEWSIZEX / self.screenSizeX * self.zoomFov * 0.5 + self.captureLens.setFov(self.captureOutFOV) + self.captureLens.setAspectRatio(1.33) + self.captureCam.node().setLens(self.captureLens) + + def __removeCapture(self): + del self.captureCam + del self.captureLens + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + if self.cameraTrack: + self.cameraTrack.finish() + self.cameraTrack = None + self.__removeCapture() + for textureBuffer in self.textureBuffers: + base.graphicsEngine.removeWindow(textureBuffer) + + del self.textureBuffers + self.viewfinderNode.removeNode() + self.blackoutNode.removeNode() + for key in self.assignmentDataDict: + assignmentData = self.assignmentDataDict[key] + assignmentData[7].delete() + + del self.assignmentDataDict + self.assignments = [] + for subject in self.subjects: + subject.delete() + + self.subjects = [] + self.subjectToon.delete() + self.destruct() + for scenery in self.scenery: + scenery.removeNode() + + self.scenery = None + self.subjectNode.removeNode() + self.subjectNode = None + self.sky.removeNode() + del self.sky + self.photoRoot = None + self.scene.removeNode() + del self.scene + self.pointer.removeNode() + self.tripodModel.removeNode() + del self.tripodModel + for panel in self.assignmentPanels: + panel.destroy() + + self.assignmentPanels = [] + if self.scorePanel: + self.scorePanel.destroy() + self.starDict = {} + self.starParentDict = {} + self.filmPanel.destroy() + del self.filmPanel + self.filmImage.removeNode() + del self.filmImage + del self.music + del self.sndPhotoMove + del self.sndPhotoFire + del self.sndWin + del self.sndFilmTick + self.tripod.removeNode() + del self.tripod + del self.swivel + self.timer.destroy() + del self.timer + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + self.ignoreAll() + return + + def onstage(self): + self.notify.debug('Onstage') + DistributedMinigame.onstage(self) + self.__createTripod() + self.tripod.reparentTo(render) + self.tripod.hide() + self.__loadToonInTripod(self.localAvId) + camera.reparentTo(render) + self.__oldCamFar = base.camLens.getFar() + base.camLens.setFar(FAR_PLANE_DIST) + self.__setupSubjects() + self.__startIntro() + base.transitions.irisIn() + base.playMusic(self.music, looping=1, volume=0.8) + orgFov = base.camLens.getFov() + self.outFov = orgFov.getX() + self.zoomFov = orgFov.getX() * ZOOMRATIO + self.currentFov = self.outFov + self.__setupCapture() + + def offstage(self): + self.notify.debug('offstage') + self.sky.reparentTo(hidden) + self.scene.reparentTo(hidden) + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + self.__stopIntro() + base.camLens.setFar(self.__oldCamFar) + self.timer.reparentTo(hidden) + self.filmPanel.reparentTo(hidden) + DistributedMinigame.offstage(self) + for key in self.subjectTracks: + track = self.subjectTracks[key][0] + track.pause() + del track + + self.subjectTracks = {} + base.localAvatar.laffMeter.start() + del self.soundTable + + def __setupCollisions(self): + self.queue = CollisionHandlerQueue() + self.traverser = CollisionTraverser('traverser name') + self.rayArray = [] + vRange = (GOODROWS - BADROWS) / 2 + for row in range(-(GOODROWS / 2), GOODROWS / 2 + 1): + for column in range(-(GOODROWS / 2), GOODROWS / 2 + 1): + goodRange = range(-((GOODROWS - BADROWS) / 2), (GOODROWS - BADROWS) / 2 + 1) + rayQuality = 'g' + if row not in goodRange or column not in goodRange: + rayQuality = 'l' + if row > vRange: + rayQuality = 'r' + if column > vRange: + rayQuality = 't' + if column < -vRange: + rayQuality = 'b' + columnString = '+%s' % column + if column < 0: + columnString = '%s' % column + rowString = '+%s' % row + if row < 0: + rowString = '%s' % row + pickerNode = CollisionNode('%s %s %s' % (rowString, columnString, rayQuality)) + pickerNP = camera.attachNewNode(pickerNode) + pickerNode.setFromCollideMask(GeomNode.getDefaultCollideMask()) + pickerRay = CollisionRay() + pickerNode.addSolid(pickerRay) + self.rayArray.append((row, + column, + pickerNode, + pickerNP, + pickerRay)) + self.traverser.addCollider(pickerNP, self.queue) + + if WANTDOTS: + self.markerDict = {} + for rayEntry in self.rayArray: + markerNode = self.viewfinderNode.attachNewNode('marker Node') + BuildGeometry.addSquareGeom(markerNode, 0.01, 0.01, Vec4(1.0, 1.0, 1.0, 1.0)) + markerNode.setX(RAYSPREADX * rayEntry[0]) + markerNode.setY(RAYSPREADY * rayEntry[1]) + markerNode.setDepthWrite(1) + markerNode.setZ(2.0) + self.markerDict[rayEntry[0], rayEntry[1]] = markerNode + + self.lensNode = LensNode('photo taker') + self.lensNode.setLens(base.camLens) + + def __moveViewfinder(self, task): + if base.mouseWatcherNode.hasMouse(): + mpos = base.mouseWatcherNode.getMouse() + self.viewfinderNode.setX(mpos.getX() * self.screenSizeX) + self.viewfinderNode.setZ(mpos.getY() * self.screenSizeZ) + horzAngle = self.viewfinderNode.getX() / self.screenSizeX * 0.5 * base.camLens.getFov()[0] + vertAngle = self.viewfinderNode.getZ() / self.screenSizeZ * 0.5 * base.camLens.getFov()[1] + horzPointFlat = self.viewfinderNode.getX() / self.screenSizeX + vertPointFlat = self.viewfinderNode.getZ() / self.screenSizeZ + horzLength = base.camLens.getFov()[0] * 0.5 + vertLength = base.camLens.getFov()[1] * 0.5 + horzRadianLength = toRadians(horzLength) + vertRadianLength = toRadians(vertLength) + hMRadian = math.atan(horzPointFlat * math.tan(horzRadianLength)) + vMRadian = math.atan(vertPointFlat * math.tan(vertRadianLength)) + hMDegree = toDegrees(hMRadian) + vMDegree = toDegrees(vMRadian) + self.pointer.setH(-hMDegree) + self.pointer.setP(vMDegree) + newRight = 0 + newLeft = 0 + newUp = 0 + newDown = 0 + if self.viewfinderNode.getX() > self.screenSizeX * 0.95: + newRight = 1 + if self.viewfinderNode.getX() < self.screenSizeX * -0.95: + newLeft = 1 + if self.viewfinderNode.getZ() > self.screenSizeZ * 0.95: + newUp = 1 + if self.viewfinderNode.getZ() < self.screenSizeZ * -0.95: + newDown = 1 + if not self.edgeRight and newRight: + self.edgeRight = 1 + self.__rightPressed() + elif self.edgeRight and not newRight: + self.edgeRight = 0 + self.__rightReleased() + if not self.edgeLeft and newLeft: + self.edgeLeft = 1 + self.__leftPressed() + elif self.edgeLeft and not newLeft: + self.edgeLeft = 0 + self.__leftReleased() + if not self.edgeUp and newUp: + self.edgeUp = 1 + self.__upPressed() + elif self.edgeUp and not newUp: + self.edgeUp = 0 + self.__upReleased() + if not self.edgeDown and newDown: + self.edgeDown = 1 + self.__downPressed() + elif self.edgeDown and not newDown: + self.edgeDown = 0 + self.__downReleased() + return task.cont + + def __testCollisions(self): + self.notify.debug('\nSnapping Photo') + self.playSound('snap') + if self.filmCount <= 0: + self.notify.debug('No Film') + return + for rayEntry in self.rayArray: + posX = (self.viewfinderNode.getX() + RAYSPREADX * rayEntry[0]) / self.screenSizeX + posY = (self.viewfinderNode.getZ() + RAYSPREADY * rayEntry[1]) / self.screenSizeZ + rayEntry[4].setFromLens(self.lensNode, posX, posY) + + self.traverser.traverse(self.subjectNode) + distDict = {} + hitDict = {} + centerDict = {} + for i in range(self.queue.getNumEntries()): + entry = self.queue.getEntry(i) + object = None + objectIndex = None + subjectIndexString = entry.getIntoNodePath().getNetTag('subjectIndex') + sceneryIndexString = entry.getIntoNodePath().getNetTag('sceneryIndex') + if subjectIndexString: + objectIndex = int(subjectIndexString) + object = self.subjects[objectIndex] + elif sceneryIndexString: + objectIndex = int(sceneryIndexString) + object = self.scenery[objectIndex] + marker = 'g' + if 'b' in entry.getFromNodePath().getName(): + marker = 'b' + if 't' in entry.getFromNodePath().getName(): + marker = 't' + if 'r' in entry.getFromNodePath().getName(): + marker = 'r' + if 'l' in entry.getFromNodePath().getName(): + marker = 'l' + if object: + newEntry = (entry.getFromNode(), object) + distance = Vec3(entry.getSurfacePoint(self.tripod)).lengthSquared() + name = entry.getFromNode().getName() + if not distDict.has_key(name): + distDict[name] = distance + hitDict[name] = (entry.getFromNode(), object, marker) + elif distance < distDict[name]: + distDict[name] = distance + hitDict[name] = (entry.getFromNode(), object, marker) + + for key in hitDict: + hit = hitDict[key] + superParent = hit[1] + marker = hit[2] + onCenter = 0 + overB = 0 + overT = 0 + overR = 0 + overL = 0 + quality = -1 + if marker == 'b': + overB = 1 + elif marker == 't': + overT = 1 + elif marker == 'r': + overR = 1 + elif marker == 'l': + overL = 1 + else: + quality = 1 + onCenter = 1 + if not centerDict.has_key(superParent): + centerDict[superParent] = (onCenter, + overB, + overT, + overR, + overL) + else: + centerDict[superParent] = (onCenter + centerDict[superParent][0], + overB + centerDict[superParent][1], + overT + centerDict[superParent][2], + overR + centerDict[superParent][3], + overL + centerDict[superParent][4]) + + if WANTDOTS: + for key in self.markerDict: + node = self.markerDict[key] + node.setColorScale(Vec4(1, 1, 1, 1)) + + for key in hitDict: + entry = hitDict[key] + name = entry[0].getName() + xS = int(name[0:2]) + yS = int(name[3:5]) + node = self.markerDict[xS, yS] + node.setColorScale(Vec4(1.0, 0.0, 0.0, 1.0)) + + centerDictKeys = [] + for key in centerDict: + centerDictKeys.append(key) + + for subject in centerDictKeys: + score = self.judgePhoto(subject, centerDict) + self.notify.debug('Photo is %s / 5 stars' % score) + self.notify.debug('assignment compare %s %s' % (self.determinePhotoContent(subject), self.assignments[self.currentAssignment])) + content = self.determinePhotoContent(subject) + if content: + photoAnalysisZero = (content[0], content[1]) + if score and photoAnalysisZero in self.assignments: + index = self.assignments.index(photoAnalysisZero) + assignment = self.assignments[index] + self.notify.debug('assignment complete') + if score >= self.assignmentDataDict[assignment][0]: + subjectIndex = self.subjects.index(subject) + texturePanel = self.assignmentDataDict[assignment][5] + texture = self.assignmentDataDict[assignment][6] + buffer = self.assignmentDataDict[assignment][4] + panelToon = self.assignmentDataDict[assignment][7] + panelToon.hide() + buffer.setActive(1) + texturePanel.show() + texturePanel.setColorScale(1, 1, 1, 1) + taskMgr.doMethodLater(0.2, buffer.setActive, 'capture Image', [0]) + if score > self.assignmentDataDict[assignment][0]: + self.assignmentDataDict[assignment][0] = score + self.updateAssignmentPanels() + self.sendUpdate('newClientPhotoScore', [subjectIndex, content[1], score]) + else: + self.notify.debug('assignment not complete') + + horzAngle = self.viewfinderNode.getX() / self.screenSizeX * 0.5 * base.camLens.getFov()[0] + vertAngle = self.viewfinderNode.getZ() / self.screenSizeZ * 0.5 * base.camLens.getFov()[1] + horzPointFlat = self.viewfinderNode.getX() / self.screenSizeX + vertPointFlat = self.viewfinderNode.getZ() / self.screenSizeZ + horzLength = base.camLens.getFov()[0] * 0.5 + vertLength = base.camLens.getFov()[1] * 0.5 + horzRadianLength = toRadians(horzLength) + vertRadianLength = toRadians(vertLength) + hMRadian = math.atan(horzPointFlat * math.tan(horzRadianLength)) + vMRadian = math.atan(vertPointFlat * math.tan(vertRadianLength)) + hMDegree = toDegrees(hMRadian) + vMDegree = toDegrees(vMRadian) + self.__decreaseFilmCount() + if self.filmCount == 0: + self.sendUpdate('filmOut', []) + self.notify.debug('Screen angles H:%s V:%s' % (self.swivel.getH(), self.swivel.getP())) + self.notify.debug('Viewfinder to screen angles H:%s V:%s' % (horzAngle, vertAngle)) + self.notify.debug('Viewfinder to screen angles with math H:%s V:%s' % (hMDegree, vMDegree)) + return + + def newAIPhotoScore(self, playerId, assignmentIndex, score): + if len(self.assignments) > assignmentIndex: + assignment = self.assignments[assignmentIndex] + assignmentData = self.assignmentDataDict[assignment] + if score > assignmentData[2]: + assignmentData[2] = score + assignmentData[3] = playerId + self.updateAssignmentPanels() + + def determinePhotoContent(self, subject): + if self.getSubjectTrackState(subject): + return [subject, self.getSubjectTrackState(subject)[2]] + else: + return None + return None + + def judgePhoto(self, subject, centerDict): + self.notify.debug('judgePhoto') + self.notify.debug(subject.getName()) + self.notify.debug(str(centerDict[subject])) + a1 = camera.getH(render) % 360 + a2 = subject.getH(render) % 360 + angle = abs((a1 + 180 - a2) % 360 - 180) + self.notify.debug('angle camera:%s subject:%s between:%s' % (camera.getH(render), subject.getH(render), angle)) + self.notify.debug(str(angle)) + centering = centerDict[subject] + if type(subject) == type(self.subjectToon): + facing = angle / 180.0 + interest = self.getSubjectTrackState(subject)[3] + quality = centering[0] - (centering[1] + centering[2] + centering[3] + centering[4]) + tooClose = centering[1] and centering[2] or centering[3] and centering[4] + portrait = centering[1] and not (centering[2] or centering[3] or centering[4]) + self.notify.debug('angle %s facing %s' % (angle, facing)) + self.notify.debug('Interest %s' % interest) + self.notify.debug('Quality %s' % quality) + self.notify.debug('tooClose %s' % tooClose) + if quality <= 0: + return None + else: + score = 0 + if angle >= 135: + score += 2 + elif angle >= 90: + score += 1 + elif angle <= 60: + score -= 1 + score += interest + if quality >= 5 and (not tooClose or portrait): + score += 1 + if quality >= 10: + score += 1 + if quality >= 15: + score += 1 + score -= 2 + if score > NUMSTARS: + score = float(NUMSTARS) + if score <= 0: + return 1 + else: + return score + return None + + def __toggleView(self): + self.notify.debug('Toggle View') + hCam = self.swivel.getH() + vCam = self.swivel.getP() + horzPointFlat = self.viewfinderNode.getX() / self.screenSizeX + vertPointFlat = self.viewfinderNode.getZ() / self.screenSizeZ + horzLength = base.camLens.getFov()[0] * 0.5 + vertLength = base.camLens.getFov()[1] * 0.5 + horzRadianLength = toRadians(horzLength) + vertRadianLength = toRadians(vertLength) + hMRadian = math.atan(horzPointFlat * math.tan(horzRadianLength)) + vMRadian = math.atan(vertPointFlat * math.tan(vertRadianLength)) + hMDegree = toDegrees(hMRadian) + vMDegree = toDegrees(vMRadian) + if self.zoomedView: + self.zoomedView = 0 + else: + self.zoomedView = 1 + if self.zoomedView: + self.notify.debug('Zoom In') + hMove = hMDegree * (1.0 - ZOOMRATIO) + vMove = vMDegree * (1.0 - ZOOMRATIO) + self.currentFov = self.zoomFov + base.camLens.setFov(self.zoomFov) + self.blackoutNode.show() + self.swivel.setHpr(self.swivel, hMove * -self.zoomFlip, vMove * self.zoomFlip, 0) + else: + self.notify.debug('Zoom Out') + hMove = hMDegree * ((1.0 - ZOOMRATIO) / ZOOMRATIO) + vMove = vMDegree * ((1.0 - ZOOMRATIO) / ZOOMRATIO) + self.currentFov = self.outFov + base.camLens.setFov(self.outFov) + self.blackoutNode.hide() + self.swivel.setHpr(self.swivel, hMove * self.zoomFlip, vMove * -self.zoomFlip, 0) + + def __doZoom(self): + self.notify.debug('Toggle View') + self.playSound('zoom') + hCam = self.swivel.getH() + vCam = self.swivel.getP() + horzPointFlat = self.viewfinderNode.getX() / self.screenSizeX + vertPointFlat = self.viewfinderNode.getZ() / self.screenSizeZ + horzLength = base.camLens.getFov()[0] * 0.5 + vertLength = base.camLens.getFov()[1] * 0.5 + horzRadianLength = toRadians(horzLength) + vertRadianLength = toRadians(vertLength) + hMRadian = math.atan(horzPointFlat * math.tan(horzRadianLength)) + vMRadian = math.atan(vertPointFlat * math.tan(vertRadianLength)) + hMDegree = toDegrees(hMRadian) + vMDegree = toDegrees(vMRadian) + if self.zoomedView: + self.zoomedView = 0 + else: + self.zoomedView = 1 + self.cameraTrack = Sequence() + if self.zoomedView: + self.notify.debug('Zoom In') + hMove = hMDegree * (1.0 - ZOOMRATIO) + vMove = vMDegree * (1.0 - ZOOMRATIO) + self.currentFov = self.zoomFov + base.camLens.setFov(self.zoomFov) + self.blackoutNode.show() + orgQuat = self.swivel.getQuat() + self.swivel.setHpr(self.swivel, hMove * -self.zoomFlip, vMove * self.zoomFlip, 0) + self.swivel.setR(0.0) + newQuat = self.swivel.getQuat() + self.swivel.setQuat(orgQuat) + zoomTrack = Parallel() + zoomTrack.append(LerpQuatInterval(self.swivel, ZOOMTIME, newQuat)) + zoomTrack.append(LerpFunc(base.camLens.setFov, fromData=self.outFov, toData=self.zoomFov, duration=ZOOMTIME)) + zoomTrack.append(LerpFunc(self.setBlackout, fromData=0.0, toData=0.5, duration=ZOOMTIME)) + self.cameraTrack.append(zoomTrack) + self.cameraTrack.append(Func(self.finishZoom, 1)) + else: + self.notify.debug('Zoom Out') + hMove = hMDegree * ((1.0 - ZOOMRATIO) / ZOOMRATIO) + vMove = vMDegree * ((1.0 - ZOOMRATIO) / ZOOMRATIO) + self.currentFov = self.outFov + base.camLens.setFov(self.outFov) + orgQuat = self.swivel.getQuat() + self.swivel.setHpr(self.swivel, hMove * self.zoomFlip, vMove * -self.zoomFlip, 0) + self.swivel.setR(0.0) + newQuat = self.swivel.getQuat() + self.swivel.setQuat(orgQuat) + zoomTrack = Parallel() + zoomTrack.append(LerpQuatInterval(self.swivel, ZOOMTIME, newQuat)) + zoomTrack.append(LerpFunc(base.camLens.setFov, fromData=self.zoomFov, toData=self.outFov, duration=ZOOMTIME)) + zoomTrack.append(LerpFunc(self.setBlackout, fromData=0.5, toData=0.0, duration=ZOOMTIME)) + self.cameraTrack.append(zoomTrack) + self.cameraTrack.append(Func(self.blackoutNode.hide)) + self.cameraTrack.append(Func(self.finishZoom, 0)) + self.cameraTrack.start() + + def setBlackout(self, black): + self.blackoutNode.setColorScale(0.0, 0.0, 0.0, black) + + def getSubjectTrackState(self, subject): + subjectTrack = self.subjectTracks.get(subject) + if subjectTrack: + interval = subjectTrack[0] + timeline = subjectTrack[1] + time = interval.getT() + timeCount = time + timelineIndex = 0 + while timeCount >= 0.0: + timeCount -= timeline[timelineIndex][1] + if timeCount >= 0.0: + timelineIndex += 1 + + return timeline[timelineIndex] + else: + return None + return None + + def __setupSubjects(self): + self.__setupCollisions() + self.subjects = [] + self.subjectTracks = {} + self.photoRoot.reparentTo(self.subjectNode) + self.photoRoot.setTag('sceneryIndex', '%s' % len(self.scenery)) + self.scenery.append(self.photoRoot) + random.seed(time.time()) + namegen = NameGenerator.NameGenerator() + for pathIndex in range(len(self.data['PATHS'])): + path = self.data['PATHS'][pathIndex] + subject = Toon.Toon() + gender = random.choice(['m', 'f']) + seed = int(random.random() * 571) + if gender == 'm': + boy = 1 + girl = 0 + else: + boy = 0 + girl = 1 + subject.setName(namegen.randomNameMoreinfo(boy=boy, girl=girl)[-1]) + self.nameCounter += 1 + subject.setPickable(0) + subject.setPlayerType(NametagGroup.CCNonPlayer) + dna = ToonDNA.ToonDNA() + dna.newToonRandom(seed, gender, 1) + subject.setDNAString(dna.makeNetString()) + subject.animFSM.request('neutral') + subject.setTag('subjectIndex', '%s' % len(self.subjects)) + self.subjects.append(subject) + height = subject.getHeight() + self.collSphere = CollisionSphere(0, 0, height * 0.5, height * 0.5) + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.uniqueName('subject Sphere')) + self.collNode.setCollideMask(BitMask32.allOn()) + self.collNode.addSolid(self.collSphere) + self.collNodePath = subject.attachNewNode(self.collNode) + subject.reparentTo(self.subjectNode) + subject.setPos(path[0]) + subject.lookAt(path[1]) + subject.show() + subjectTrack = self.generateToonTrack(subject, path, pathIndex) + subjectTrack[0].start() + self.subjectTracks[subject] = subjectTrack + + def regenerateToonTrack(self, subject, path, pathIndex): + if not hasattr(self, 'swivel'): + return + subjectTrack = self.generateToonTrack(subject, path, pathIndex) + subjectTrack[0].start() + self.subjectTracks[subject] = subjectTrack + + def generateToonTrack(self, subject, path, pathIndex): + + def getNextIndex(curIndex, path): + return (curIndex + 1) % len(path) + + subjectTrack = Sequence() + subjectTimeline = [] + timeAccum = 0.0 + pathPointIndex = 0 + orgPos = subject.getPos() + orgQuat = subject.getQuat() + while pathPointIndex < len(path): + nextIndex = getNextIndex(pathPointIndex, path) + curPoint = path[pathPointIndex] + nextPoint = path[nextIndex] + distance = self.slowDistance(curPoint, nextPoint) + pointTime = distance * 0.25 + subject.setPos(curPoint) + subject.lookAt(nextPoint) + nextQuat = subject.getQuat() + animSetIndex = self.data['PATHANIMREL'][pathIndex] + animChoice = random.choice(self.data['ANIMATIONS'][animSetIndex])[0] + movetype = random.choice(self.data['MOVEMODES'][animSetIndex]) + turnTime = 0.2 + if movetype[0] == 'swim': + turnTime = 0.0 + nextInterval = LerpQuatInterval(subject, turnTime, quat=nextQuat) + subjectTrack.append(nextInterval) + subjectTimeline.append((timeAccum, + nextInterval.getDuration(), + 'turn', + 1.0)) + timeAccum += nextInterval.getDuration() + movetype = random.choice(self.data['MOVEMODES'][animSetIndex]) + pointTime = pointTime * movetype[1] + if movetype[0] == 'swim': + nextInterval = Sequence() + startInterval = Func(subject.setP, -60) + midInterval = Parallel(LerpPosInterval(subject, pointTime, nextPoint), ActorInterval(subject, movetype[0], loop=1, duration=pointTime)) + nextInterval.append(startInterval) + nextInterval.append(midInterval) + else: + nextInterval = Sequence() + startInterval = Func(subject.setP, 0) + midInterval = Parallel(LerpPosInterval(subject, pointTime, nextPoint), ActorInterval(subject, movetype[0], loop=1, duration=pointTime)) + nextInterval.append(startInterval) + nextInterval.append(midInterval) + subjectTrack.append(nextInterval) + subjectTimeline.append((timeAccum, + nextInterval.getDuration(), + movetype[0], + 1.0)) + timeAccum += nextInterval.getDuration() + if animChoice: + nextInterval = ActorInterval(subject, animChoice, loop=0) + subjectTrack.append(nextInterval) + subjectTimeline.append((timeAccum, + nextInterval.getDuration(), + animChoice, + 2.0)) + timeAccum += nextInterval.getDuration() + pathPointIndex += 1 + + subject.setPos(orgPos) + subject.setQuat(orgQuat) + subjectTrack.append(Func(self.regenerateToonTrack, subject, path, pathIndex)) + return (subjectTrack, subjectTimeline) + + def slowDistance(self, point1, point2): + dx = point1[0] - point2[0] + dy = point1[1] - point2[1] + dz = point1[2] - point2[2] + distance = math.sqrt(dx * dx + dy * dy + dz * dz) + return distance + + def getNextPoint(self, pointList, point): + pointIndex = 0 + length = len(pointList) + found = 0 + loop = 0 + while not found and loop < length: + if pointList[index] == point: + found = 1 + else: + index += 1 + loop += 1 + + if not found: + return None + nextPointIndex = loop + 1 + if nextPointIndex >= length: + nextPointIndex = 0 + return pointList[nextPointIndex] + + def __createTripod(self): + tripod = self.tripodModel.copyTo(hidden) + swivel = tripod.find('**/cannon') + self.tripod = tripod + self.swivel = swivel + self.pointer = self.swivel.attachNewNode('pointer') + self.tripod.setPos(self.photoRoot.getPos()) + self.tripod.setPos(self.tripod.getPos() + self.data['TRIPOD_OFFSET']) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.__stopIntro() + self.__putCameraOnTripod() + if not base.config.GetBool('endless-cannon-game', 0): + self.timer.show() + self.timer.countdown(self.data['TIME'], self.__gameTimerExpired) + self.filmPanel.reparentTo(aspect2d) + self.scoreMult = MinigameGlobals.getScoreMult(self.cr.playGame.hood.id) + self.clockStopTime = None + self.gameFSM.request('aim') + self.__putCameraOnTripod() + self.currentAssignment = 0 + assignmentTemplates = self.generateAssignmentTemplates(PhotoGameGlobals.ONSCREENASSIGNMENTS) + self.generateAssignments(assignmentTemplates) + self.generateAssignmentPanels() + self.scorePanel = self.makeScoreFrame() + self.scorePanel.reparentTo(aspect2d) + self.scorePanel.setPos(1.05, 0.0, -0.725) + self.updateAssignmentPanels() + for subject in self.subjects: + subject.useLOD(1000) + + return + + def setGameExit(self): + DistributedMinigame.setGameExit(self) + self.__gameTimerExpired() + + def __gameTimerExpired(self): + self.notify.debug('game timer expired') + self.gameOver() + + def generateAssignments(self, assignmentTemplates): + for template in assignmentTemplates: + subject = self.subjects[template[0]] + pose = template[1] + score = 0.0 + panel = None + topScore = 0.0 + topScorerId = None + textureBuffer = None + texturePanel = None + texture = None + panelToon = None + assignment = (subject, pose) + if assignment not in self.assignments: + self.assignments.append(assignment) + self.assignmentDataDict[assignment] = [score, + panel, + topScore, + topScorerId, + textureBuffer, + texturePanel, + texture, + panelToon] + + self.notify.debug('assignments') + for assignment in self.assignments: + self.notify.debug(str(assignment)) + + return + + def generateAssignmentPanels(self): + self.notify.debug('generateAssignmentPanels') + for panel in self.assignmentPanels: + panel.destroy() + + spacing = self.screenSizeX / PhotoGameGlobals.ONSCREENASSIGNMENTS * 1.61 + index = 0 + Xoff = self.screenSizeX - 0.735 + Zoff = -self.screenSizeZ + 0.25 + for assignment in self.assignments: + self.notify.debug('made assignment panel %s' % str(assignment)) + panel, texturePanel, toon = self.makeAssignmentPanel(assignment) + panel.setX(Xoff - spacing * index) + panel.setZ(Zoff) + texturePanel.setZ(0.065) + rot = random.choice([0.0, + 2.0, + -2.0, + -4.0, + 6.0]) + panel.setR(rot) + textureBuffer = base.win.makeTextureBuffer('Photo Capture', 128, 128) + dr = textureBuffer.makeDisplayRegion() + dr.setCamera(self.captureCam) + texture = textureBuffer.getTexture() + texturePanel.setTexture(texture) + texturePanel.setColorScale(0, 0, 0, 0) + textureBuffer.setActive(0) + self.textureBuffers.append(textureBuffer) + texturePanel.hide() + self.assignmentPanels.append(panel) + self.assignmentDataDict[assignment][1] = panel + self.assignmentDataDict[assignment][4] = textureBuffer + self.assignmentDataDict[assignment][5] = texturePanel + self.assignmentDataDict[assignment][6] = texture + self.assignmentDataDict[assignment][7] = toon + index += 1 + + def printAD(self): + for assignment in self.assignmentDataDict: + data = self.assignmentDataDict[assignment] + print 'Key:%s\nData:%s\n' % (str(assignment), data) + + def updateScorePanel(self): + teamScore = 0.0 + bonusScore = 0.0 + for assignment in self.assignments: + data = self.assignmentDataDict[assignment] + teamScore += data[2] + if data[3] == localAvatar.doId: + bonusScore += 1.0 + + self.scorePanel['text'] = TTLocalizer.PhotoGameScore % (int(teamScore), int(bonusScore), int(teamScore + bonusScore)) + + def updateAssignmentPanels(self): + for assignment in self.assignments: + data = self.assignmentDataDict[assignment] + leaderName = data[3] + leader = base.cr.doId2do.get(data[3]) + if not leader: + data[1]['text'] = ' ' + elif leader.doId == localAvatar.doId: + data[1]['text'] = TTLocalizer.PhotoGameScoreYou + else: + leaderName = leader.getName() + data[1]['text'] = TTLocalizer.PhotoGameScoreOther % leaderName + starList = self.starDict[data[1]] + starParent = self.starParentDict[data[1]] + score = int(data[2]) + for index in range(NUMSTARS): + if index < score: + starList[index].show() + else: + starList[index].hide() + + starParent.setX(float(NUMSTARS - score) * STARSIZE * 0.5) + + self.updateScorePanel() + + def makeAssignmentPanel(self, assignment): + if assignment != None: + assignedToon = Toon.Toon() + assignedToon.setDNA(assignment[0].getStyle()) + else: + assignedToon = None + model, ival = self.makeFrameModel(assignedToon) + if assignedToon: + assignedToon.loop(assignment[1]) + model.reparentTo(aspect2d) + assignedToon.setH(172) + assignedToon.setZ(-1.2) + assignedToon.setY(100.0) + if assignment[1] == 'swim': + assignedToon.setP(-70) + assignedToon.setH(160) + assignedToon.setZ(-0.6) + model.setH(0) + model.setScale(1.0) + model['text'] = ' ' + assignedToon.setY(-100.0) + model.setY(-10.0) + screen = model.attachNewNode('screen node') + BuildGeometry.addSquareGeom(screen, 0.36, 0.27, Vec4(1.0, 1.0, 1.0, 1.0)) + screen.setHpr(0, 90, 0) + screen.setDepthTest(1) + starImage = loader.loadModel('phase_4/models/minigames/photogame_star') + starParent = model.attachNewNode('star parent') + self.starDict[model] = [] + for index in range(NUMSTARS): + star = DirectLabel(parent=starParent, image=starImage, image_color=(1, 1, 1, 1), image_scale=Point3(STARSIZE, 0.0, STARSIZE), relief=None) + star.setX(STARSIZE * -0.5 * float(NUMSTARS) + float(index + 0.5) * STARSIZE) + star.setZ(-0.05 - STARSIZE) + self.starDict[model].append(star) + self.starParentDict[model] = starParent + star.hide() + + return (model, screen, assignedToon) + + def makeFrameModel(self, model): + frame = self.makeAssignmentFrame() + ival = None + if model: + model.setDepthTest(1) + model.setDepthWrite(1) + 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) + scaleFactor = self.screenSizeX / PhotoGameGlobals.ONSCREENASSIGNMENTS + scale.setScale(0.4 * scaleFactor / max(corner[0], corner[1], corner[2])) + return (frame, ival) + + def makeAssignmentFrame(self): + from direct.gui.DirectGui import DirectFrame + photoImage = loader.loadModel('phase_4/models/minigames/photo_game_pictureframe') + size = 1.0 + assignmentScale = self.screenSizeX / PhotoGameGlobals.ONSCREENASSIGNMENTS + frame = DirectFrame(parent=hidden, image=photoImage, image_color=(1, 1, 1, 1), image_scale=Point3(1.6 * assignmentScale, 0.0, 1.75 * assignmentScale), frameSize=(-size, + size, + -size, + size), text='HC Score', textMayChange=1, text_wordwrap=9, text_pos=Point3(0.0, -0.135, 0.0), text_scale=0.045, relief=None) + return frame + + def makeScoreFrame(self): + from direct.gui.DirectGui import DirectFrame + size = 1.0 + scoreImage = loader.loadModel('phase_4/models/minigames/photogame_camera') + frame = DirectFrame(parent=hidden, image=scoreImage, image_color=(1, 1, 1, 1), image_scale=Point3(0.64, 0.0, 0.64), frameSize=(-size, + size, + -size, + size), text='Score Frame', textMayChange=1, text_wordwrap=9, text_pos=Point3(0.0, 0.0, 0.0), text_scale=0.05, relief=None) + return frame + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterAim(self): + self.notify.debug('enterAim') + self.__enableAimInterface() + taskMgr.add(self.__moveViewfinder, 'photo game viewfinder Task') + self.accept('mouse1', self.__handleMouseClick) + base.localAvatar.laffMeter.stop() + base.transitions.noIris() + + def exitAim(self): + self.__disableAimInterface() + taskMgr.remove('photo game viewfinder Task') + self.ignore('mouse1') + + def enterZoom(self): + self.notify.debug('enterZoom') + taskMgr.add(self.__moveViewfinder, 'photo game viewfinder Task') + self.__doZoom() + + def exitZoom(self): + taskMgr.remove('photo game viewfinder Task') + self.notify.debug('exitZoom') + + def finishZoom(self, zoomed = None, task = None): + if zoomed: + self.captureLens.setFov(self.captureZoomFOV) + else: + self.captureLens.setFov(self.captureOutFOV) + self.gameFSM.request('aim') + + def enterShowResults(self): + self.notify.debug('enterShowResults') + for subject in self.subjects: + subject.resetLOD() + + def exitShowResults(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.music.stop() + if hasattr(self, 'jarIval'): + self.jarIval.finish() + del self.jarIval + for avId in self.avIdList: + taskMgr.remove('firePhoto' + str(avId)) + taskMgr.remove('flyingToon' + str(avId)) + + def exitCleanup(self): + pass + + def __enableAimInterface(self): + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__spawnLocalPhotoMoveTask() + + def __disableAimInterface(self): + self.ignore(self.FIRE_KEY) + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + self.ignore(self.FIRE_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + self.__killLocalPhotoMoveTask() + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + self.__fireReleased() + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downReleased() + + def __firePressed(self): + self.notify.debug('fire pressed') + + def __fireReleased(self): + self.gameFSM.request('zoom') + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __handleMouseClick(self): + self.notify.debug('mouse click') + self.__testCollisions() + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) + + def __spawnLocalPhotoMoveTask(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.photoMoving = 0 + task = Task(self.__localPhotoMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_PHOTO_MOVE_TASK) + + def __killLocalPhotoMoveTask(self): + taskMgr.remove(self.LOCAL_PHOTO_MOVE_TASK) + if self.photoMoving: + self.sndPhotoMove.stop() + + def __localPhotoMoveTask(self, task): + if not hasattr(self, 'swivel'): + return + pos = [self.swivel.getHpr()[0], self.swivel.getHpr()[1], self.swivel.getHpr()[2]] + oldRot = pos[0] + oldAng = pos[1] + rotVel = 0 + if self.leftPressed: + rotVel += PhotoGameGlobals.PHOTO_ROTATION_VEL + if self.rightPressed: + rotVel -= PhotoGameGlobals.PHOTO_ROTATION_VEL + pos[0] += rotVel * globalClock.getDt() + angVel = 0 + if self.upPressed: + angVel += PhotoGameGlobals.PHOTO_ANGLE_VEL + if self.downPressed: + angVel -= PhotoGameGlobals.PHOTO_ANGLE_VEL + pos[1] += angVel * globalClock.getDt() + if pos[1] < PhotoGameGlobals.PHOTO_ANGLE_MIN: + pos[1] = PhotoGameGlobals.PHOTO_ANGLE_MIN + elif pos[1] > PhotoGameGlobals.PHOTO_ANGLE_MAX: + pos[1] = PhotoGameGlobals.PHOTO_ANGLE_MAX + if oldRot != pos[0] or oldAng != pos[1]: + if self.photoMoving == 0: + self.photoMoving = 1 + base.playSfx(self.sndPhotoMove, looping=1) + posVec = Vec3(pos[0], pos[1], pos[2]) + self.swivel.setHpr(posVec) + elif self.photoMoving: + self.photoMoving = 0 + self.sndPhotoMove.stop() + return Task.cont + + def __putCameraOnTripod(self): + camera.setPosHpr(0, 0.0, 0, 0, 0, 0) + camera.reparentTo(self.swivel) + self.swivel.setHpr(self.data['START_HPR']) + + def __loadToonInTripod(self, avId): + toon = base.cr.doId2do.get(avId) + if toon: + toon.reparentTo(self.swivel) + + def __toRadians(self, angle): + return angle * 2.0 * math.pi / 360.0 + + def __toDegrees(self, angle): + return angle * 360.0 / (2.0 * math.pi) + + def __decreaseFilmCount(self): + curTime = self.getCurrentGameTime() + score = self.filmCount - 1 + if not hasattr(self, 'curScore'): + self.curScore = score + self.filmPanel['text'] = str(score) + if self.curScore != score: + if hasattr(self, 'jarIval'): + self.jarIval.finish() + s = self.filmPanel.getScale() + self.jarIval = Parallel(Sequence(self.filmPanel.scaleInterval(0.15, s * 3.0 / 4.0, blendType='easeOut'), self.filmPanel.scaleInterval(0.15, s, blendType='easeIn')), Sequence(Wait(0.25), SoundInterval(self.sndFilmTick)), name='photoGameFilmJarThrob') + self.jarIval.start() + self.curScore = score + self.filmCount = score + + def __stopIntro(self): + taskMgr.remove(self.INTRO_TASK_NAME) + taskMgr.remove(self.INTRO_TASK_NAME_CAMERA_LERP) + self.__putCameraOnTripod() + if self.introSequence: + self.introSequence.finish() + self.introSequence = None + return + + def __startIntro(self): + camera.reparentTo(render) + camera.setPos(self.data['CAMERA_INTIAL_POSTION']) + camera.setHpr(0, 0, 0) + camera.lookAt(self.tripod) + lookatHpr = camera.getHpr() + self.introSequence = LerpPosHprInterval(camera, 4.0, pos=self.tripod.getPos(render), hpr=lookatHpr, startPos=self.data['CAMERA_INTIAL_POSTION'], blendType='easeInOut') + self.introSequence.start() + + def construct(self): + zone = self.getSafezoneId() + if zone == ToontownGlobals.ToontownCentral: + self.constructTTC() + elif zone == ToontownGlobals.DonaldsDock: + self.constructDD() + elif zone == ToontownGlobals.DaisyGardens: + self.constructDG() + elif zone == ToontownGlobals.MinniesMelodyland: + self.constructMM() + elif zone == ToontownGlobals.TheBrrrgh: + self.constructBR() + elif zone == ToontownGlobals.DonaldsDreamland: + self.constructDL() + + def destruct(self): + zone = self.getSafezoneId() + if zone == ToontownGlobals.ToontownCentral: + self.destructTTC() + elif zone == ToontownGlobals.DonaldsDock: + self.destructDD() + elif zone == ToontownGlobals.DaisyGardens: + self.destructDG() + elif zone == ToontownGlobals.MinniesMelodyland: + self.destructMM() + elif zone == ToontownGlobals.TheBrrrgh: + self.destructBR() + elif zone == ToontownGlobals.DonaldsDreamland: + self.destructDL() + + def constructTTC(self): + self.photoRoot = self.scene.find('**/prop_gazebo*') + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.sky.reparentTo(render) + self.sky.setBin('background', -100) + self.sky.find('**/cloud1').setBin('background', -99) + self.sky.find('**/cloud2').setBin('background', -98) + self.scene.reparentTo(render) + self.makeDictionaries(self.dnaStore) + self.createAnimatedProps(self.nodeList) + self.startAnimatedProps() + self.scene.find('**/door_trigger_22*').hide() + self.scene.find('**/doorFrameHoleRight_0*').hide() + self.scene.find('**/doorFrameHoleLeft_0*').hide() + + def destructTTC(self): + self.stopAnimatedProps() + self.deleteAnimatedProps() + + def constructDD(self): + self.photoRoot = self.scene.find('**/center_island*') + self.sky = loader.loadModel('phase_3.5/models/props/BR_sky') + self.sky.reparentTo(render) + self.sky.setBin('background', -100) + self.sky.find('**/skypanel1').setBin('background', -98) + self.sky.find('**/skypanel2').setBin('background', -97) + self.sky.find('**/skypanel3').setBin('background', -96) + self.sky.find('**/skypanel4').setBin('background', -95) + self.sky.find('**/skypanel5').setBin('background', -94) + self.sky.find('**/skypanel6').setBin('background', -93) + self.scene.reparentTo(render) + self.makeDictionaries(self.dnaStore) + self.createAnimatedProps(self.nodeList) + self.startAnimatedProps() + boatGeom = self.scene.find('**/donalds_boat') + self.photoRoot.setPos(-22, 0, 0) + self.boat = self.photoRoot.attachNewNode('boat') + boatGeom.reparentTo(self.boat) + boatGeom.setX(45.0) + boatGeom.setY(-5.0) + boatGeom.setR(-0.0) + boatGeom.setH(-8) + self.bg = boatGeom + self.boatTrack = Sequence() + self.boatTrack.append(LerpHprInterval(self.boat, 90.0, Point3(360, 0, 0))) + self.boatTrack.loop() + self.boatTrack2 = Sequence() + self.boatTrack2.append(LerpPosInterval(self.boat, 5.0, Point3(0, 0, 2.0), Point3(0, 0, 1.0), blendType='easeInOut')) + self.boatTrack2.append(LerpPosInterval(self.boat, 5.0, Point3(0, 0, 1.0), Point3(0, 0, 2.0), blendType='easeInOut')) + self.boatTrack2.loop() + ddFog = Fog('DDFog Photo') + ddFog.setColor(Vec4(0.8, 0.8, 0.8, 1.0)) + ddFog.setLinearRange(0.0, 400.0) + render.setFog(ddFog) + water = self.scene.find('**/top_surface') + water.setTransparency(TransparencyAttrib.MAlpha) + water.setColorScale(1.0, 1.0, 1.0, 0.8) + water.setDepthWrite(1) + water.setDepthTest(1) + water.setBin('transparent', 0) + + def destructDD(self): + self.bg = None + self.boatTrack.finish() + self.boatTrack2.finish() + self.boat.removeNode() + render.clearFog() + self.stopAnimatedProps() + self.deleteAnimatedProps() + return + + def constructDG(self): + self.photoRoot = render.attachNewNode('DG PhotoRoot') + self.photoRoot.setPos(1.39, 92.91, 2.0) + self.bigFlower = loader.loadModel('phase_8/models/props/DG_flower-mod.bam') + self.bigFlower.reparentTo(self.photoRoot) + self.bigFlower.setScale(2.5) + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.sky.reparentTo(render) + self.sky.setBin('background', -100) + self.sky.find('**/cloud1').setBin('background', -99) + self.sky.find('**/cloud2').setBin('background', -98) + self.scene.reparentTo(render) + self.scene.find('**/door_trigger_5*').hide() + self.scene.find('**/doorFrameHoleRight_0*').hide() + self.scene.find('**/doorFrameHoleLeft_0*').hide() + for name in ['**/o10_2']: + maze = self.scene.find(name) + maze.reparentTo(self.subjectNode) + maze.setTag('sceneryIndex', '%s' % len(self.scenery)) + self.scenery.append(maze) + + self.makeDictionaries(self.dnaStore) + self.createAnimatedProps(self.nodeList) + self.startAnimatedProps() + + def destructDG(self): + self.bigFlower.removeNode() + self.stopAnimatedProps() + self.deleteAnimatedProps() + + def constructMM(self): + self.photoRoot = render.attachNewNode('MM PhotoRoot') + self.photoRoot.setPos(103.6, -61, -4.497) + self.sky = loader.loadModel('phase_6/models/props/MM_sky') + self.sky.reparentTo(render) + self.sky.setBin('background', -100) + self.scene.reparentTo(render) + self.scene.find('**/door_trigger_8*').hide() + self.scene.find('**/door_trigger_6*').hide() + self.scene.find('**/door_trigger_1*').hide() + self.scene.find('**/door_trigger_0*').hide() + self.scene.find('**/door_trigger_3*').hide() + self.scene.find('**/doorFrameHoleRight_0*').hide() + self.scene.find('**/doorFrameHoleLeft_0*').hide() + self.scene.find('**/doorFrameHoleRight_1*').hide() + self.scene.find('**/doorFrameHoleLeft_1*').hide() + self.scene.find('**/doorFrameHoleRight').hide() + self.scene.find('**/doorFrameHoleLeft').hide() + self.makeDictionaries(self.dnaStore) + self.createAnimatedProps(self.nodeList) + self.startAnimatedProps() + lm = self.scene.findAllMatches('**/*landmark*') + blocker = lm[2] + blocker.reparentTo(self.subjectNode) + blocker.setTag('sceneryIndex', '%s' % len(self.scenery)) + self.scenery.append(blocker) + + def destructMM(self): + self.stopAnimatedProps() + self.deleteAnimatedProps() + + def constructBR(self): + self.photoRoot = render.attachNewNode('BR PhotoRoot') + self.photoRoot.setPos(-110, -48, 8.567) + self.sky = loader.loadModel('phase_3.5/models/props/BR_sky') + self.sky.reparentTo(render) + self.sky.setBin('background', -100) + self.scene.reparentTo(render) + self.scene.find('**/door_trigger_11*').hide() + self.scene.find('**/doorFrameHoleRight_0*').hide() + self.scene.find('**/doorFrameHoleLeft_0*').hide() + self.makeDictionaries(self.dnaStore) + self.createAnimatedProps(self.nodeList) + self.startAnimatedProps() + self.snow = BattleParticles.loadParticleFile('snowdisk.ptf') + self.snow.setPos(0, 0, 5) + self.snowRender = self.scene.attachNewNode('snowRender') + self.snowRender.setDepthWrite(0) + self.snowRender.setBin('fixed', 1) + self.snowFade = None + self.snow.start(camera, self.snowRender) + return + + def destructBR(self): + self.snow.cleanup() + del self.snow + del self.snowRender + self.stopAnimatedProps() + self.deleteAnimatedProps() + + def constructDL(self): + self.photoRoot = render.attachNewNode('DL PhotoRoot') + self.photoRoot.setPos(-70.228, 87.588, 4.397) + self.sky = loader.loadModel('phase_8/models/props/DL_sky') + self.sky.reparentTo(render) + self.sky.setBin('background', -100) + self.scene.reparentTo(render) + self.scene.find('**/door_trigger_8*').hide() + self.scene.find('**/doorFrameHoleRight_0*').hide() + self.scene.find('**/doorFrameHoleLeft_0*').hide() + self.scene.find('**/doorFrameHoleRight_1*').hide() + self.scene.find('**/doorFrameHoleLeft_1*').hide() + self.makeDictionaries(self.dnaStore) + self.createAnimatedProps(self.nodeList) + self.startAnimatedProps() + + def destructDL(self): + self.stopAnimatedProps() + self.deleteAnimatedProps() + + def makeDictionaries(self, dnaStore): + self.nodeList = [] + for i in range(dnaStore.getNumDNAVisGroups()): + groupFullName = dnaStore.getDNAVisGroupName(i) + groupName = base.cr.hoodMgr.extractGroupName(groupFullName) + groupNode = self.scene.find('**/' + groupFullName) + if groupNode.isEmpty(): + self.notify.error('Could not find visgroup') + self.nodeList.append(groupNode) + + def startAnimatedProps(self): + for animPropListKey in self.animPropDict: + animPropList = self.animPropDict[animPropListKey] + for animProp in animPropList: + animProp.enter() + + def stopAnimatedProps(self): + for animPropListKey in self.animPropDict: + animPropList = self.animPropDict[animPropListKey] + for animProp in animPropList: + animProp.exit() + + def createAnimatedProps(self, nodeList): + self.animPropDict = {} + for i in nodeList: + animPropNodes = i.findAllMatches('**/animated_prop_*') + numAnimPropNodes = animPropNodes.getNumPaths() + for j in range(numAnimPropNodes): + animPropNode = animPropNodes.getPath(j) + if animPropNode.getName().startswith('animated_prop_generic'): + className = 'GenericAnimatedProp' + else: + className = animPropNode.getName()[14:-8] + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + animPropObj = classObj(animPropNode) + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(animPropObj) + + interactivePropNodes = i.findAllMatches('**/interactive_prop_*') + numInteractivePropNodes = interactivePropNodes.getNumPaths() + for j in range(numInteractivePropNodes): + interactivePropNode = interactivePropNodes.getPath(j) + className = 'GenericAnimatedProp' + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + interactivePropObj = classObj(interactivePropNode) + animPropList = self.animPropDict.get(i) + if animPropList is None: + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(interactivePropObj) + + return + + def deleteAnimatedProps(self): + for animPropListKey in self.animPropDict: + animPropList = self.animPropDict[animPropListKey] + for animProp in animPropList: + animProp.delete() + + del self.animPropDict + + def addSound(self, name, soundName, path = None): + if not hasattr(self, 'soundTable'): + self.soundTable = {} + if path: + self.soundPath = path + soundSource = '%s%s' % (self.soundPath, soundName) + self.soundTable[name] = loader.loadSfx(soundSource) + + def playSound(self, name, volume = 1.0): + if hasattr(self, 'soundTable'): + self.soundTable[name].setVolume(volume) + self.soundTable[name].play() diff --git a/toontown/minigame/DistributedPhotoGameAI.py b/toontown/minigame/DistributedPhotoGameAI.py new file mode 100644 index 0000000..7521130 --- /dev/null +++ b/toontown/minigame/DistributedPhotoGameAI.py @@ -0,0 +1,200 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import PhotoGameGlobals +from toontown.minigame import PhotoGameBase +import random + +class DistributedPhotoGameAI(DistributedMinigameAI, PhotoGameBase.PhotoGameBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhotoGameAI') + + def __init__(self, air, minigameId): + DistributedMinigameAI.__init__(self, air, minigameId) + PhotoGameBase.PhotoGameBase.__init__(self) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedPhotoGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + assignmentTemplates = self.generateAssignmentTemplates(PhotoGameGlobals.ONSCREENASSIGNMENTS) + self.assignmentData = self.generateAssignmentData(assignmentTemplates) + self.gameFSM.request('play') + self.filmCountList = [0, + 0, + 0, + 0] + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + if not config.GetBool('endless-photo-game', 0): + taskMgr.doMethodLater(self.data['TIME'], self.timerExpired, self.taskName('gameTimer')) + + def timerExpired(self, task = None): + self.notify.debug('timer expired') + self.calculateScores() + self.gameOver() + if task: + return task.done + + def __playing(self): + if not hasattr(self, 'gameFSM'): + return 0 + return self.gameFSM.getCurrentState().getName() == 'play' + + def generateAssignmentData(self, assignmentTemplates): + assignmentData = [] + for template in assignmentTemplates: + playerScores = [0, + 0, + 0, + 0] + highScorer = None + dataEntry = [template[0], + template[1], + playerScores, + highScorer] + assignmentData.append(dataEntry) + + return assignmentData + + def checkForFilmOut(self): + numOut = 0 + for entry in self.filmCountList: + if entry >= self.data['FILMCOUNT']: + numOut += 1 + + numPlayers = 0 + for entry in self.avIdList: + if entry: + numPlayers += 1 + + if numOut >= numPlayers: + self.timerExpired() + + def filmOut(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'PhotoGameAI.filmOut: unknown avatar') + return + if self.gameFSM.getCurrentState() is None or self.gameFSM.getCurrentState().getName() != 'play': + self.air.writeServerEvent('suspicious', avId, 'PhotoGameAI.filmOut: game not in play state') + return + playerIndex = self.avIdList.index(avId) + self.filmCountList[playerIndex] = self.data['FILMCOUNT'] + self.checkForFilmOut() + return + + def newClientPhotoScore(self, subjectIndex, pose, score): + avId = self.air.getAvatarIdFromSender() + if self.gameFSM.getCurrentState() is None or self.gameFSM.getCurrentState().getName() != 'play': + if self.gameFSM.getCurrentState() is None: + gameState = None + else: + gameState = self.gameFSM.getCurrentState().getName() + self.air.writeServerEvent('suspicious', avId, 'PhotoGameAI.newClientPhotoScore: game not in play state %s' % gameState) + return + if score > PhotoGameGlobals.NUMSTARS: + score = 0.0 + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'PhotoGameAI.newClientPhotoScore: non-player avatar') + return + playerIndex = self.avIdList.index(avId) + self.filmCountList[playerIndex] += 1 + self.checkForFilmOut() + if self.filmCountList[playerIndex] >= self.data['FILMCOUNT']: + self.notify.debug('player used more film than possible') + return + assignmentIndex = None + for dataIndex in range(len(self.assignmentData)): + assignment = self.assignmentData[dataIndex] + if assignment[0] == subjectIndex and assignment[1] == pose: + assignmentIndex = dataIndex + + if assignmentIndex != None and self.assignmentData[assignmentIndex][2][playerIndex] < score: + self.assignmentData[assignmentIndex][2][playerIndex] = score + highScorer = self.assignmentData[assignmentIndex][3] + if highScorer == None: + self.assignmentData[assignmentIndex][3] = playerIndex + elif self.assignmentData[assignmentIndex][2][highScorer] < self.assignmentData[assignmentIndex][2][playerIndex]: + self.assignmentData[assignmentIndex][3] = playerIndex + self.sendUpdate('newAIPhotoScore', [avId, assignmentIndex, score]) + self.notify.debug('newClientPhotoScore %s %s %s %s' % (avId, + subjectIndex, + pose, + score)) + for data in self.assignmentData: + self.notify.debug(str(data)) + + return + + def calculateScores(self): + playerBonus = [0.0, + 0.0, + 0.0, + 0.0] + teamScore = 0.0 + for data in self.assignmentData: + scores = data[2] + highestIndex = data[3] + if highestIndex != None: + highestScore = scores[highestIndex] + self.notify.debug('\nHighIndex:%s' % highestIndex) + self.notify.debug('scores') + self.notify.debug(str(scores)) + teamScore += highestScore + if highestIndex != None: + playerBonus[highestIndex] += 1.0 + + for avIdKey in self.scoreDict: + playerIndex = self.avIdList.index(avIdKey) + playerScore = playerBonus[playerIndex] + teamScore + self.scoreDict[avIdKey] = playerScore + + self.notify.debug('Calculated Scores') + self.notify.debug('playerbonus') + self.notify.debug(str(playerBonus)) + self.notify.debug('teamscore') + self.notify.debug(str(teamScore)) + self.notify.debug('dict') + self.notify.debug(str(self.scoreDict)) + return + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + taskMgr.remove(self.taskName('game-over')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedRaceGame.py b/toontown/minigame/DistributedRaceGame.py new file mode 100644 index 0000000..21b9ee0 --- /dev/null +++ b/toontown/minigame/DistributedRaceGame.py @@ -0,0 +1,876 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.distributed.ClockDelta import * +from DistributedMinigame import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task.Task import Task +from toontown.toonbase import ToontownTimer +import RaceGameGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DistributedRaceGame(DistributedMinigame): + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRaceGame', [State.State('off', self.enterOff, self.exitOff, ['inputChoice']), + State.State('inputChoice', self.enterInputChoice, self.exitInputChoice, ['waitServerChoices', 'moveAvatars', 'cleanup']), + State.State('waitServerChoices', self.enterWaitServerChoices, self.exitWaitServerChoices, ['moveAvatars', 'cleanup']), + State.State('moveAvatars', self.enterMoveAvatars, self.exitMoveAvatars, ['inputChoice', 'winMovie', 'cleanup']), + State.State('winMovie', self.enterWinMovie, self.exitWinMovie, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.posHprArray = (((-9.03, + 0.06, + 0.025, + -152.9), + (-7.43, + -2.76, + 0.025, + -152.68), + (-6.02, + -5.48, + 0.025, + -157.54), + (-5.01, + -8.32, + 0.025, + -160.66), + (-4.05, + -11.36, + 0.025, + -170.22), + (-3.49, + -14.18, + 0.025, + -175.76), + (-3.12, + -17.15, + 0.025, + -177.73), + (-3.0, + -20.32, + 0.025, + 178.49), + (-3.09, + -23.44, + 0.025, + 176.59), + (-3.43, + -26.54, + 0.025, + 171.44), + (-4.07, + -29.44, + 0.025, + 163.75), + (-5.09, + -32.27, + 0.025, + 158.2), + (-6.11, + -35.16, + 0.025, + 154.98), + (-7.57, + -37.78, + 0.025, + 154.98), + (-9.28, + -40.65, + 0.025, + 150.41)), + ((-6.12, + 1.62, + 0.025, + -152.9), + (-4.38, + -1.35, + 0.025, + -150.92), + (-3.08, + -4.3, + 0.025, + -157.9), + (-1.85, + -7.26, + 0.025, + -162.54), + (-0.93, + -10.49, + 0.025, + -167.71), + (-0.21, + -13.71, + 0.025, + -171.79), + (0.21, + -17.08, + 0.025, + -174.92), + (0.31, + -20.2, + 0.025, + 177.1), + (0.17, + -23.66, + 0.025, + 174.82), + (-0.23, + -26.91, + 0.025, + 170.51), + (-0.99, + -30.2, + 0.025, + 162.54), + (-2.02, + -33.28, + 0.025, + 160.48), + (-3.28, + -36.38, + 0.025, + 157.96), + (-4.67, + -39.17, + 0.025, + 154.13), + (-6.31, + -42.15, + 0.025, + 154.13)), + ((-2.99, + 3.09, + 0.025, + -154.37), + (-1.38, + -0.05, + 0.025, + -154.75), + (-0.19, + -3.29, + 0.025, + -159.22), + (1.17, + -6.51, + 0.025, + -162.74), + (2.28, + -9.8, + 0.025, + -168.73), + (3.09, + -13.28, + 0.025, + -173.49), + (3.46, + -16.63, + 0.025, + -176.81), + (3.69, + -20.38, + 0.025, + 179.14), + (3.61, + -24.12, + 0.025, + 175.78), + (3.0, + -27.55, + 0.025, + 170.87), + (2.15, + -30.72, + 0.025, + 167.41), + (1.04, + -34.26, + 0.025, + 162.11), + (-0.15, + -37.44, + 0.025, + 158.59), + (-1.64, + -40.52, + 0.025, + 153.89), + (-3.42, + -43.63, + 0.025, + 153.89)), + ((0.0, + 4.35, + 0.025, + -154.37), + (1.52, + 1.3, + 0.025, + -155.67), + (3.17, + -2.07, + 0.025, + -155.67), + (4.47, + -5.41, + 0.025, + -163.0), + (5.56, + -9.19, + 0.025, + -168.89), + (6.22, + -12.66, + 0.025, + -171.67), + (6.67, + -16.56, + 0.025, + -176.53), + (6.93, + -20.33, + 0.025, + 179.87), + (6.81, + -24.32, + 0.025, + 175.19), + (6.22, + -27.97, + 0.025, + 170.81), + (5.59, + -31.73, + 0.025, + 167.54), + (4.48, + -35.42, + 0.025, + 161.92), + (3.06, + -38.82, + 0.025, + 158.56), + (1.4, + -42.0, + 0.025, + 154.32), + (-0.71, + -45.17, + 0.025, + 153.27))) + self.avatarPositions = {} + self.modelCount = 8 + self.cameraTopView = (-22.78, + -41.65, + 31.53, + -51.55, + -42.68, + -2.96) + self.timer = None + self.timerStartTime = None + return None + + def getTitle(self): + return TTLocalizer.RaceGameTitle + + def getInstructions(self): + return TTLocalizer.RaceGameInstructions + + def getMaxDuration(self): + return 60 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.raceBoard = loader.loadModel('phase_4/models/minigames/race') + self.raceBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.raceBoard.setScale(0.8) + self.dice = loader.loadModel('phase_4/models/minigames/dice') + self.dice1 = self.dice.find('**/dice_button1') + self.dice2 = self.dice.find('**/dice_button2') + self.dice3 = self.dice.find('**/dice_button3') + self.dice4 = self.dice.find('**/dice_button4') + self.diceList = [self.dice1, + self.dice2, + self.dice3, + self.dice4] + self.music = base.loadMusic('phase_4/audio/bgm/minigame_race.mid') + self.posBuzzer = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.wav') + self.negBuzzer = base.loadSfx('phase_4/audio/sfx/MG_neg_buzzer.wav') + self.winSting = base.loadSfx('phase_4/audio/sfx/MG_win.mp3') + self.loseSting = base.loadSfx('phase_4/audio/sfx/MG_lose.mp3') + self.diceButtonList = [] + for i in range(1, 5): + button = self.dice.find('**/dice_button' + str(i)) + button_down = self.dice.find('**/dice_button' + str(i) + '_down') + button_ro = self.dice.find('**/dice_button' + str(i) + '_ro') + diceButton = DirectButton(image=(button, + button_down, + button_ro, + None), relief=None, pos=(-0.9 + (i - 1) * 0.2, 0.0, -0.85), scale=0.25, command=self.handleInputChoice, extraArgs=[i]) + diceButton.hide() + self.diceButtonList.append(diceButton) + + self.waitingChoicesLabel = DirectLabel(text=TTLocalizer.RaceGameWaitingChoices, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.6, 0, -0.75), scale=0.075) + self.waitingChoicesLabel.hide() + self.chanceMarker = loader.loadModel('phase_4/models/minigames/question_mark') + self.chanceCard = loader.loadModel('phase_4/models/minigames/chance_card') + self.chanceCardText = OnscreenText('', fg=(1.0, 0, 0, 1), scale=0.14, font=ToontownGlobals.getSignFont(), wordwrap=14, pos=(0.0, 0.2), mayChange=1) + self.chanceCardText.hide() + self.cardSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_turn.mp3') + self.chanceMarkers = [] + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.raceBoard.removeNode() + del self.raceBoard + self.dice.removeNode() + del self.dice + self.chanceMarker.removeNode() + del self.chanceMarker + self.chanceCardText.removeNode() + del self.chanceCardText + self.chanceCard.removeNode() + del self.chanceCard + self.waitingChoicesLabel.destroy() + del self.waitingChoicesLabel + del self.music + del self.posBuzzer + del self.negBuzzer + del self.winSting + del self.loseSting + del self.cardSound + for button in self.diceButtonList: + button.destroy() + + del self.diceButtonList + for marker in self.chanceMarkers: + marker.removeNode() + del marker + + del self.chanceMarkers + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + base.playMusic(self.music, looping=1, volume=0.8) + self.raceBoard.reparentTo(render) + camera.reparentTo(render) + p = self.cameraTopView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + base.transitions.irisIn(0.4) + base.setBackgroundColor(0.1875, 0.7929, 0) + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.raceBoard.reparentTo(hidden) + self.chanceCard.reparentTo(hidden) + self.chanceCardText.hide() + if hasattr(self, 'chanceMarkers'): + for marker in self.chanceMarkers: + marker.reparentTo(hidden) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + self.resetPositions() + for i in range(self.numPlayers): + avId = self.avIdList[i] + if self.localAvId == avId: + self.localAvLane = i + avatar = self.getAvatar(avId) + if avatar: + avatar.reparentTo(render) + avatar.setAnimState('neutral', 1) + self.positionInPlace(avatar, i, 0) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('inputChoice') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterInputChoice(self): + self.notify.debug('enterInputChoice') + for button in self.diceButtonList: + button.show() + + self.timer = ToontownTimer.ToontownTimer() + self.timer.hide() + if self.timerStartTime != None: + self.startTimer() + return + + def startTimer(self): + now = globalClock.getFrameTime() + elapsed = now - self.timerStartTime + self.timer.posInTopRightCorner() + self.timer.setTime(RaceGameGlobals.InputTimeout) + self.timer.countdown(RaceGameGlobals.InputTimeout - elapsed, self.handleChoiceTimeout) + self.timer.show() + + def setTimerStartTime(self, timestamp): + if not self.hasLocalToon: + return + self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp) + if self.timer != None: + self.startTimer() + return + + def exitInputChoice(self): + for button in self.diceButtonList: + button.hide() + + if self.timer != None: + self.timer.destroy() + self.timer = None + self.timerStartTime = None + self.ignore('diceButton') + return + + def handleChoiceTimeout(self): + self.sendUpdate('setAvatarChoice', [0]) + self.gameFSM.request('waitServerChoices') + + def handleInputChoice(self, choice): + self.sendUpdate('setAvatarChoice', [choice]) + self.gameFSM.request('waitServerChoices') + + def enterWaitServerChoices(self): + self.notify.debug('enterWaitServerChoices') + self.waitingChoicesLabel.show() + + def exitWaitServerChoices(self): + self.waitingChoicesLabel.hide() + + def localToonWon(self): + localToonPosition = self.avatarPositions[self.localAvId] + if localToonPosition >= RaceGameGlobals.NumberToWin: + self.notify.debug('localToon won') + return 1 + else: + return 0 + + def anyAvatarWon(self): + for position in self.avatarPositions.values(): + if position >= RaceGameGlobals.NumberToWin: + self.notify.debug('anyAvatarWon: Somebody won') + return 1 + + self.notify.debug('anyAvatarWon: Nobody won') + return 0 + + def showNumbers(self, task): + self.notify.debug('showing numbers...') + self.diceInstanceList = [] + for i in range(len(task.choiceList)): + avId = self.avIdList[i] + choice = task.choiceList[i] + if choice == 0: + self.diceInstanceList.append(None) + else: + diceInstance = self.diceList[choice - 1].copyTo(self.raceBoard) + self.diceInstanceList.append(diceInstance) + dicePosition = self.avatarPositions[avId] + 1 + diceInstance.setScale(4.0) + self.positionInPlace(diceInstance, i, dicePosition) + diceInstance.setP(-90) + diceInstance.setZ(0.05) + + return Task.done + + def showMatches(self, task): + self.notify.debug('showing matches...') + for i in range(len(task.choiceList)): + avId = self.avIdList[i] + choice = task.choiceList[i] + if choice != 0: + diceInstance = self.diceInstanceList[i] + freq = task.choiceList.count(choice) + if freq == 1: + diceInstance.setColor(0.2, 1, 0.2, 1) + if avId == self.localAvId: + base.playSfx(self.posBuzzer) + else: + diceInstance.setColor(1, 0.2, 0.2, 1) + if avId == self.localAvId: + base.playSfx(self.negBuzzer) + + return Task.done + + def hideNumbers(self, task): + self.notify.debug('hiding numbers...') + for dice in self.diceInstanceList: + if dice: + dice.removeNode() + + self.diceInstanceList = [] + return Task.done + + def enterMoveAvatars(self, choiceList, positionList, rewardList): + self.notify.debug('in enterMoveAvatars:') + tasks = [] + self.avatarPositionsCopy = self.avatarPositions.copy() + for i in range(0, len(choiceList) / self.numPlayers): + startIndex = i * self.numPlayers + endIndex = startIndex + self.numPlayers + self.choiceList = choiceList[startIndex:endIndex] + self.positionList = positionList[startIndex:endIndex] + self.rewardList = rewardList[startIndex:endIndex] + self.notify.debug(' turn: ' + str(i + 1)) + self.notify.debug(' choiceList: ' + str(self.choiceList)) + self.notify.debug(' positionList: ' + str(self.positionList)) + self.notify.debug(' rewardList: ' + str(self.rewardList)) + longestLerpTime = self.getLongestLerpTime(i > 0) + self.notify.debug('longestLerpTime: ' + str(longestLerpTime)) + if i == 0: + snt = Task(self.showNumbers) + snt.choiceList = self.choiceList + smt = Task(self.showMatches) + smt.choiceList = self.choiceList + tasks += [snt, Task.pause(0.5), smt] + if longestLerpTime > 0.0: + self.notify.debug('someone moved...') + mat = Task(self.moveAvatars) + mat.choiceList = self.choiceList + mat.positionList = self.positionList + mat.rewardList = self.rewardList + mat.name = 'moveAvatars' + if i == 0: + tasks += [Task.pause(0.75), + mat, + Task.pause(0.75), + Task(self.hideNumbers), + Task.pause(longestLerpTime - 0.5)] + else: + mat.chance = 1 + tasks += [mat, Task.pause(longestLerpTime)] + tasks += self.showChanceRewards() + else: + self.notify.debug('no one moved...') + tasks += [Task.pause(1.0), Task(self.hideNumbers)] + + self.notify.debug('task list : ' + str(tasks)) + wdt = Task(self.walkDone) + wdt.name = 'walk done' + tasks.append(wdt) + moveTask = Task.sequence(*tasks) + taskMgr.add(moveTask, 'moveAvatars') + + def walkDone(self, task): + self.choiceList = [] + self.positionList = [] + if self.anyAvatarWon(): + self.gameFSM.request('winMovie') + else: + self.gameFSM.request('inputChoice') + return Task.done + + def getLongestLerpTime(self, afterFirst): + self.notify.debug('afterFirst: ' + str(afterFirst)) + longestTime = 0.0 + for i in range(len(self.choiceList)): + freq = self.choiceList.count(self.choiceList[i]) + if afterFirst or freq == 1: + oldPosition = self.avatarPositionsCopy[self.avIdList[i]] + newPosition = self.positionList[i] + self.avatarPositionsCopy[self.avIdList[i]] = newPosition + squares_walked = abs(newPosition - oldPosition) + longestTime = max(longestTime, self.getWalkDuration(squares_walked)) + + return longestTime + + def showChanceRewards(self): + tasks = [] + for reward in self.rewardList: + self.notify.debug('showChanceRewards: reward = ' + str(reward)) + index = self.rewardList.index(reward) + if reward != -1: + self.notify.debug('adding tasks!') + hcc = Task(self.hideChanceMarker) + hcc.chanceMarkers = self.chanceMarkers + hcc.index = index + sct = Task(self.showChanceCard) + sct.chanceCard = self.chanceCard + sct.cardSound = self.cardSound + stt = Task(self.showChanceCardText) + rewardEntry = RaceGameGlobals.ChanceRewards[reward] + stt.rewardIdx = reward + if rewardEntry[0][0] < 0 or rewardEntry[0][1] > 0: + stt.sound = self.negBuzzer + else: + stt.sound = self.posBuzzer + stt.picker = self.avIdList[index] + rct = Task(self.resetChanceCard) + task = Task.sequence(hcc, sct, Task.pause(1.0), stt, Task.pause(3.0), rct, Task.pause(0.25)) + tasks.append(task) + + return tasks + + def showChanceCard(self, task): + base.playSfx(task.cardSound) + self.chanceCard.reparentTo(render) + self.chanceCard.lerpPosHpr(19.62, 13.41, 13.14, 270, 0, -85.24, 1.0, other=camera, task='cardLerp') + return Task.done + + def hideChanceMarker(self, task): + task.chanceMarkers[task.index].reparentTo(hidden) + return Task.done + + def showChanceCardText(self, task): + self.notify.debug('showing chance reward: ' + str(task.rewardIdx)) + name = self.getAvatar(task.picker).getName() + rewardEntry = RaceGameGlobals.ChanceRewards[task.rewardIdx] + cardText = '' + if rewardEntry[1] != -1: + rewardstr_fmt = TTLocalizer.RaceGameCardText + if rewardEntry[2] > 0: + rewardstr_fmt = TTLocalizer.RaceGameCardTextBeans + cardText = rewardstr_fmt % {'name': name, + 'reward': rewardEntry[1]} + else: + rewardstr_fmt = TTLocalizer.RaceGameCardTextHi1 + cardText = rewardstr_fmt % {'name': name} + base.playSfx(task.sound) + self.chanceCardText.setText(cardText) + self.chanceCardText.show() + return Task.done + + def resetChanceCard(self, task): + self.chanceCardText.hide() + self.chanceCard.reparentTo(hidden) + self.chanceCard.setPosHpr(0, 0, 0, 0, 0, 0) + return Task.done + + def moveCamera(self): + bestPosIdx = self.avatarPositions.values()[0] + best_lane = 0 + cur_lane = 0 + for pos in self.avatarPositions.values(): + if pos > bestPosIdx: + bestPosIdx = pos + best_lane = cur_lane + cur_lane = cur_lane + 1 + + bestPosIdx = min(RaceGameGlobals.NumberToWin, bestPosIdx) + localToonPosition = self.avatarPositions[self.localAvId] + savedCamPos = camera.getPos() + savedCamHpr = camera.getHpr() + pos1_idx = min(RaceGameGlobals.NumberToWin - 4, localToonPosition) + pos1 = self.posHprArray[self.localAvLane][pos1_idx] + bestPosLookAtIdx = bestPosIdx + 1 + localToonLookAtIdx = localToonPosition + 4 + if localToonLookAtIdx >= bestPosLookAtIdx: + pos2_idx = localToonLookAtIdx + pos2_idx = min(RaceGameGlobals.NumberToWin, pos2_idx) + pos2 = self.posHprArray[self.localAvLane][pos2_idx] + else: + pos2_idx = bestPosLookAtIdx + pos2_idx = min(RaceGameGlobals.NumberToWin, pos2_idx) + pos2 = self.posHprArray[best_lane][pos2_idx] + posDeltaVecX = pos2[0] - pos1[0] + posDeltaVecY = pos2[1] - pos1[1] + DistanceMultiplier = 0.8 + camposX = pos2[0] + DistanceMultiplier * posDeltaVecX + camposY = pos2[1] + DistanceMultiplier * posDeltaVecY + race_fraction = bestPosIdx / float(RaceGameGlobals.NumberToWin) + CamHeight = 10.0 * race_fraction + (1.0 - race_fraction) * 22.0 + CamPos = Vec3(camposX, camposY, pos2[2] + CamHeight) + camera.setPos(CamPos) + camera_lookat_idx = min(RaceGameGlobals.NumberToWin - 6, localToonPosition) + posLookAt = self.posHprArray[self.localAvLane][camera_lookat_idx] + camera.lookAt(posLookAt[0], posLookAt[1], posLookAt[2]) + CamHpr = camera.getHpr() + camera.setPos(savedCamPos) + camera.setHpr(savedCamHpr) + camera.lerpPosHpr(CamPos[0], CamPos[1], CamPos[2], CamHpr[0], CamHpr[1], CamHpr[2], 0.75) + + def getWalkDuration(self, squares_walked): + walkDuration = abs(squares_walked / 1.2) + if squares_walked > 4: + walkDuration = walkDuration * 0.3 + return walkDuration + + def moveAvatars(self, task): + self.notify.debug('In moveAvatars: ') + self.notify.debug(' choiceList: ' + str(task.choiceList)) + self.notify.debug(' positionList: ' + str(task.positionList)) + self.notify.debug(' rewardList: ' + str(task.rewardList)) + for i in range(len(self.choiceList)): + avId = self.avIdList[i] + choice = task.choiceList[i] + position = task.positionList[i] + chance = max(0, hasattr(task, 'chance')) + if choice != 0: + oldPosition = self.avatarPositions[avId] + self.avatarPositions[avId] = position + self.moveCamera() + if not chance and task.choiceList.count(choice) != 1: + self.notify.debug('duplicate choice!') + else: + avatar = self.getAvatar(avId) + if avatar: + squares_walked = abs(position - oldPosition) + if squares_walked > 4: + self.notify.debug('running') + avatar.setPlayRate(1.0, 'run') + self.runInPlace(avatar, i, oldPosition, position, self.getWalkDuration(squares_walked)) + else: + if choice > 0: + self.notify.debug('walking forwards') + avatar.setPlayRate(1.0, 'walk') + else: + self.notify.debug('walking backwards') + avatar.setPlayRate(-1.0, 'walk') + self.walkInPlace(avatar, i, position, self.getWalkDuration(squares_walked)) + + return Task.done + + def exitMoveAvatars(self): + self.notify.debug('In exitMoveAvatars: removing hooks') + taskMgr.remove('moveAvatars') + return None + + def gameOverCallback(self, task): + self.gameOver() + return Task.done + + def enterWinMovie(self): + self.notify.debug('enterWinMovie') + if self.localToonWon(): + base.playSfx(self.winSting) + else: + base.playSfx(self.loseSting) + for avId in self.avIdList: + avPosition = self.avatarPositions[avId] + if avPosition >= RaceGameGlobals.NumberToWin: + avatar = self.getAvatar(avId) + if avatar: + lane = str(self.avIdList.index(avId)) + taskMgr.remove('runAvatar-' + lane) + taskMgr.remove('walkAvatar-' + lane) + avatar.setAnimState('jump', 1.0) + + taskMgr.doMethodLater(4.0, self.gameOverCallback, 'playMovie') + + def exitWinMovie(self): + taskMgr.remove('playMovie') + self.winSting.stop() + self.loseSting.stop() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def positionInPlace(self, avatar, lane, place): + place = min(place, len(self.posHprArray[lane]) - 1) + posH = self.posHprArray[lane][place] + avatar.setPosHpr(self.raceBoard, posH[0], posH[1], posH[2], posH[3], 0, 0) + + def walkInPlace(self, avatar, lane, place, time): + place = min(place, len(self.posHprArray[lane]) - 1) + posH = self.posHprArray[lane][place] + + def startWalk(task): + task.avatar.setAnimState('walk', 1) + return Task.done + + startWalkTask = Task(startWalk, 'startWalk-' + str(lane)) + startWalkTask.avatar = avatar + + def stopWalk(task, raceBoard = self.raceBoard, posH = posH): + task.avatar.setAnimState('neutral', 1) + if raceBoard.isEmpty(): + task.avatar.setPosHpr(0, 0, 0, 0, 0, 0) + else: + task.avatar.setPosHpr(raceBoard, posH[0], posH[1], posH[2], posH[3], 0, 0) + return Task.done + + stopWalkTask = Task(stopWalk, 'stopWalk-' + str(lane)) + stopWalkTask.avatar = avatar + walkTask = Task.sequence(startWalkTask, avatar.lerpPosHpr(posH[0], posH[1], posH[2], posH[3], 0, 0, time, other=self.raceBoard), stopWalkTask) + taskMgr.add(walkTask, 'walkAvatar-' + str(lane)) + + def runInPlace(self, avatar, lane, currentPlace, newPlace, time): + place = min(newPlace, len(self.posHprArray[lane]) - 1) + step = (place - currentPlace) / 3 + pos1 = self.posHprArray[lane][currentPlace + step] + pos2 = self.posHprArray[lane][currentPlace + 2 * step] + pos3 = self.posHprArray[lane][place] + + def startRun(task): + task.avatar.setAnimState('run', 1) + return Task.done + + startRunTask = Task(startRun, 'startRun-' + str(lane)) + startRunTask.avatar = avatar + + def stopRun(task, raceBoard = self.raceBoard, pos3 = pos3): + task.avatar.setAnimState('neutral', 1) + task.avatar.setPosHpr(raceBoard, pos3[0], pos3[1], pos3[2], pos3[3], 0, 0) + return Task.done + + stopRunTask = Task(stopRun, 'stopRun-' + str(lane)) + stopRunTask.avatar = avatar + runTask = Task.sequence(startRunTask, avatar.lerpPosHpr(pos1[0], pos1[1], pos1[2], pos1[3], 0, 0, time / 3.0, other=self.raceBoard), avatar.lerpPosHpr(pos2[0], pos2[1], pos2[2], pos2[3], 0, 0, time / 3.0, other=self.raceBoard), avatar.lerpPosHpr(pos3[0], pos3[1], pos3[2], pos3[3], 0, 0, time / 3.0, other=self.raceBoard), stopRunTask) + taskMgr.add(runTask, 'runAvatar-' + str(lane)) + + def setAvatarChoice(self, choice): + self.notify.error('setAvatarChoice should not be called on the client') + + def setAvatarChose(self, avId): + if not self.hasLocalToon: + return + self.notify.debug('setAvatarChose: avatar: ' + str(avId) + ' choose a number') + + def setChancePositions(self, positions): + if not self.hasLocalToon: + return + row = 0 + for pos in positions: + marker = self.chanceMarker.copyTo(render) + posHpr = self.posHprArray[row][pos] + marker.setPosHpr(self.raceBoard, posHpr[0], posHpr[1], posHpr[2], posHpr[3] + 180, 0, 0.025) + marker.setScale(0.7) + self.chanceMarkers.append(marker) + row += 1 + + def setServerChoices(self, choices, positions, rewards): + if not self.hasLocalToon: + return + for i in range(len(positions)): + if positions[i] > RaceGameGlobals.NumberToWin: + positions[i] = RaceGameGlobals.NumberToWin + if positions[i] < 0: + positions[i] = 0 + + self.notify.debug('setServerChoices: %s positions: %s rewards: %s ' % (choices, positions, rewards)) + self.gameFSM.request('moveAvatars', [choices, positions, rewards]) + + def resetPositions(self): + for avId in self.avIdList: + self.avatarPositions[avId] = 0 diff --git a/toontown/minigame/DistributedRaceGameAI.py b/toontown/minigame/DistributedRaceGameAI.py new file mode 100644 index 0000000..23b6234 --- /dev/null +++ b/toontown/minigame/DistributedRaceGameAI.py @@ -0,0 +1,305 @@ +from math import * +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import random +from direct.task.Task import Task +import RaceGameGlobals + +class DistributedRaceGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedRaceGameAI_initialized + except: + self.DistributedRaceGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRaceGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['waitClientsChoices']), + State.State('waitClientsChoices', self.enterWaitClientsChoices, self.exitWaitClientsChoices, ['processChoices', 'cleanup']), + State.State('processChoices', self.enterProcessChoices, self.exitProcessChoices, ['waitClientsChoices', 'cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.avatarChoices = {} + self.avatarPositions = {} + self.chancePositions = {} + self.rewardDict = {} + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + self.resetChancePositions() + self.resetPositions() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsChoices') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def anyAvatarWon(self, positionDict): + for avId, position in positionDict.items(): + if position >= RaceGameGlobals.NumberToWin: + self.notify.debug('anyAvatarWon: Somebody won') + return 1 + + self.notify.debug('anyAvatarWon: Nobody won') + return 0 + + def allAvatarsChosen(self): + for choice in self.avatarChoices.values(): + if choice == -1: + return 0 + + return 1 + + def checkChoice(self, choice): + if choice not in RaceGameGlobals.ValidChoices: + self.notify.warning('checkChoice: invalid choice: ' + str(choice)) + return RaceGameGlobals.ValidChoices[0] + else: + return choice + + def resetChoices(self): + for avId in self.avIdList: + self.avatarChoices[avId] = -1 + + def resetPositions(self): + for avId in self.avIdList: + self.avatarPositions[avId] = 0 + + def resetChancePositions(self): + chancePositions = [] + for avId in self.avIdList: + pos = random.randint(5, RaceGameGlobals.NumberToWin - 1) + self.chancePositions[avId] = pos + self.rewardDict[avId] = random.randint(0, len(RaceGameGlobals.ChanceRewards) - 1) + chancePositions.append(pos) + + self.sendUpdate('setChancePositions', [chancePositions]) + + def setAvatarChoice(self, choice): + avatarId = self.air.getAvatarIdFromSender() + self.notify.debug('setAvatarChoice: avatar: ' + str(avatarId) + ' chose: ' + str(choice)) + self.avatarChoices[avatarId] = self.checkChoice(choice) + self.sendUpdate('setAvatarChose', [avatarId]) + if self.allAvatarsChosen(): + self.notify.debug('setAvatarChoice: all avatars have chosen') + self.gameFSM.request('processChoices') + else: + self.notify.debug('setAvatarChoice: still waiting for more choices') + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterWaitClientsChoices(self): + self.notify.debug('enterWaitClientsChoices') + self.resetChoices() + taskMgr.doMethodLater(RaceGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('input-timeout')) + self.sendUpdate('setTimerStartTime', [globalClockDelta.getFrameNetworkTime()]) + + def exitWaitClientsChoices(self): + taskMgr.remove(self.taskName('input-timeout')) + + def waitClientsChoicesTimeout(self, task): + self.notify.debug('waitClientsChoicesTimeout: did not hear from all clients') + for avId in self.avatarChoices.keys(): + if self.avatarChoices[avId] == -1: + self.avatarChoices[avId] = 0 + + self.gameFSM.request('processChoices') + return Task.done + + def enterProcessChoices(self): + self.notify.debug('enterProcessChoices: ') + self.choiceArray = [] + self.positionArray = [] + self.rewardArray = [] + for avId in self.avIdList: + choice = self.avatarChoices[avId] + freq = self.avatarChoices.values().count(choice) + self.processChoice(avId, choice, freq) + + masterList = [] + for avId in self.avIdList: + masterList.append(-1) + + done = 0 + rewarded = 0 + while not done: + self.notify.debug('enterProcessChoice: notDone') + rewardList = masterList[:] + for avId in self.avIdList: + reward = self.processReward(avId) + if reward != -1: + rewarded = 1 + rewardList[self.avIdList.index(avId)] = reward + self.rewardArray += rewardList + for av in self.avIdList: + if av == avId: + self.processChoice(av, RaceGameGlobals.ChanceRewards[reward][0][0]) + else: + self.processChoice(av, RaceGameGlobals.ChanceRewards[reward][0][1]) + + break + + if not rewarded: + self.rewardArray += rewardList + self.notify.debug(' rewardList: ' + str(rewardList)) + self.notify.debug(' rewardArray: ' + str(self.rewardArray)) + done = rewardList.count(-1) == len(rewardList) + + self.checkForWinners() + + def processChoice(self, avId, choice, freq = 1): + self.notify.debug('processChoice: av = ' + str(avId) + ' choice = ' + str(choice)) + if freq == 1: + if choice != 0: + if self.avatarPositions[avId] < RaceGameGlobals.NumberToWin: + self.avatarPositions[avId] += choice + if self.avatarPositions[avId] < 0: + self.avatarPositions[avId] = 0 + self.choiceArray.append(choice) + self.positionArray.append(self.avatarPositions[avId]) + self.notify.debug('Process choice (' + str(choice) + ') for av: ' + str(avId)) + self.notify.debug(' choiceArray: ' + str(self.choiceArray)) + self.notify.debug(' positionArray: ' + str(self.positionArray)) + + def processReward(self, rewardee): + self.notify.debug('processReward: ' + str(rewardee)) + reward = -1 + if self.avatarPositions[rewardee] == self.chancePositions[rewardee]: + reward = self.rewardDict[rewardee] + bonus = RaceGameGlobals.ChanceRewards[reward][2] + self.scoreDict[rewardee] = self.scoreDict[rewardee] + bonus + self.chancePositions[rewardee] = -1 + return reward + + def checkForWinners(self): + self.notify.debug('checkForWinners: ') + self.sendUpdate('setServerChoices', [self.choiceArray, self.positionArray, self.rewardArray]) + delay = 0.0 + for reward in self.rewardArray: + if reward != -1: + delay += 7.0 + + if self.anyAvatarWon(self.avatarPositions): + numWinners = 0 + for avId in self.avIdList: + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + numWinners = numWinners + 1 + + for avId in self.avIdList: + newJellybeans = ceil(self.avatarPositions[avId] * 0.5) + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + newJellybeans = RaceGameGlobals.NumberToWin + if numWinners > 1: + newJellybeans = newJellybeans - 3 + self.scoreDict[avId] = self.scoreDict[avId] + newJellybeans + + taskMgr.doMethodLater(delay, self.rewardTimeoutTaskGameOver, self.taskName('reward-timeout')) + else: + taskMgr.doMethodLater(delay, self.rewardTimeoutTask, self.taskName('reward-timeout')) + + def oldEnterProcessChoices(self, recurse = 0): + self.notify.debug('enterProcessChoices') + if not recurse: + self.choiceArray = [] + self.positionArray = [] + self.rewardArray = [] + for avId in self.avIdList: + choice = self.avatarChoices[avId] + reward = -1 + if choice != 0: + freq = self.avatarChoices.values().count(choice) + if recurse or freq == 1: + self.avatarPositions[avId] += choice + if self.avatarPositions[avId] < 0: + self.avatarPositions[avId] = 0 + if self.avatarPositions[avId] == self.chancePositions[avId]: + reward = self.rewardDict[avId] + self.scoreDict[avId] = self.scoreDict[avId] + RaceGameGlobals.ChanceRewards[reward][2] + self.chancePositions[avId] = -1 + self.choiceArray.append(choice) + self.positionArray.append(self.avatarPositions[avId]) + self.rewardArray.append(reward) + + self.notify.debug(' choiceArray: ' + str(self.choiceArray)) + self.notify.debug(' positionArray: ' + str(self.positionArray)) + self.notify.debug(' rewardArray: ' + str(self.rewardArray)) + thisTurnRewards = self.rewardArray[-len(self.avatarPositions):] + rewardIndex = 0 + for reward in thisTurnRewards: + if reward != -1: + for avId in self.avIdList: + if self.avIdList.index(avId) == rewardIndex: + self.avatarChoices[avId] = RaceGameGlobals.ChanceRewards[reward][0][0] + else: + self.avatarChoices[avId] = RaceGameGlobals.ChanceRewards[reward][0][1] + + self.enterProcessChoices(1) + rewardIndex += 1 + + if not recurse: + self.sendUpdate('setServerChoices', [self.choiceArray, self.positionArray, self.rewardArray]) + delay = 0.0 + for reward in self.rewardArray: + if reward != -1: + delay += 7.0 + + if self.anyAvatarWon(self.avatarPositions): + numWinners = 0 + for avId in self.avIdList: + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + numWinners = numWinners + 1 + + for avId in self.avIdList: + newJellybeans = ceil(self.avatarPositions[avId] * 0.5) + if self.avatarPositions[avId] >= RaceGameGlobals.NumberToWin: + newJellybeans = RaceGameGlobals.NumberToWin + if numWinners > 1: + newJellybeans = newJellybeans - 3 + self.scoreDict[avId] = self.scoreDict[avId] + newJellybeans + + taskMgr.doMethodLater(delay, self.rewardTimeoutTaskGameOver, self.taskName('reward-timeout')) + else: + taskMgr.doMethodLater(delay, self.rewardTimeoutTask, self.taskName('reward-timeout')) + return None + + def rewardTimeoutTaskGameOver(self, task): + self.notify.debug('Done waiting for rewards, game over') + self.gameOver() + return Task.done + + def rewardTimeoutTask(self, task): + self.notify.debug('Done waiting for rewards') + self.gameFSM.request('waitClientsChoices') + return Task.done + + def exitProcessChoices(self): + taskMgr.remove(self.taskName('reward-timeout')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedRingGame.py b/toontown/minigame/DistributedRingGame.py new file mode 100644 index 0000000..baa1da3 --- /dev/null +++ b/toontown/minigame/DistributedRingGame.py @@ -0,0 +1,912 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import ArrowKeys +import Ring +import RingTrack +import RingGameGlobals +import RingGroup +import RingTrackGroups +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class DistributedRingGame(DistributedMinigame): + UPDATE_ENVIRON_TASK = 'RingGameUpdateEnvironTask' + UPDATE_LOCALTOON_TASK = 'RingGameUpdateLocalToonTask' + UPDATE_RINGS_TASK = 'RingGameUpdateRingsTask' + UPDATE_SHADOWS_TASK = 'RingGameUpdateShadowsTask' + COLLISION_DETECTION_TASK = 'RingGameCollisionDetectionTask' + END_GAME_WAIT_TASK = 'RingGameCollisionDetectionTask' + COLLISION_DETECTION_PRIORITY = 5 + UPDATE_SHADOWS_PRIORITY = 47 + RT_UNKNOWN = 0 + RT_SUCCESS = 1 + RT_GROUPSUCCESS = 2 + RT_FAILURE = 3 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRingGame', [State.State('off', self.enterOff, self.exitOff, ['swim']), State.State('swim', self.enterSwim, self.exitSwim, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + + def getTitle(self): + return TTLocalizer.RingGameTitle + + def getInstructions(self): + p = self.avIdList.index(self.localAvId) + if self.isSinglePlayer(): + text = TTLocalizer.RingGameInstructionsSinglePlayer + else: + text = TTLocalizer.RingGameInstructionsMultiPlayer + return text % RingGameGlobals.ringColors[self.colorIndices[p]][0] + + def getMaxDuration(self): + return RingGameGlobals.NUM_RING_GROUPS * self.ringGroupArrivalPeriod + self.T_FIRST_RING_GROUP_ARRIVES + self.GAME_END_DELAY + + def defineConstants(self): + self.CAMERA_Y = -15 + self.TOON_Y = 0 + self.FAR_PLANE_DIST = 150 + tScreenCenterToEdge = 1.0 + self.TOONXZ_SPEED = RingGameGlobals.MAX_TOONXZ / tScreenCenterToEdge + self.WATER_DEPTH = 35.0 + self.ENVIRON_LENGTH = 150.0 + self.ENVIRON_START_OFFSET = 20.0 + self.TOON_INITIAL_SPACING = 4.0 + waterZOffset = 3.0 + self.SEA_FLOOR_Z = -self.WATER_DEPTH / 2.0 + waterZOffset + farPlaneDist = self.CAMERA_Y + self.FAR_PLANE_DIST - self.TOON_Y + self.ringGroupArrivalPeriod = 3.0 + self.RING_GROUP_SPACING = farPlaneDist / 2.0 + self.TOON_SWIM_VEL = self.RING_GROUP_SPACING / self.ringGroupArrivalPeriod + self.T_FIRST_RING_GROUP_ARRIVES = farPlaneDist / self.TOON_SWIM_VEL + self.WATER_COLOR = Vec4(0, 0, 0.6, 1) + self.SHADOW_Z_OFFSET = 0.1 + self.Y_VIS_MAX = self.FAR_PLANE_DIST + self.Y_VIS_MIN = self.CAMERA_Y + ringRadius = RingGameGlobals.RING_RADIUS * 1.025 + self.RING_RADIUS_SQRD = ringRadius * ringRadius + self.GAME_END_DELAY = 1.0 + self.RING_RESPONSE_DELAY = 0.1 + self.TOON_LOD = 1000 + self.NumRingGroups = 16 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.mid') + self.sndAmbience = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.mp3') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.mp3') + loadBase = 'phase_4/models/minigames/' + self.environModel = loader.loadModel(loadBase + 'swimming_game.bam') + self.environModel.setPos(0, self.ENVIRON_LENGTH / 2.0, self.SEA_FLOOR_Z) + self.environModel.flattenMedium() + self.ringModel = loader.loadModel(loadBase + 'swimming_game_ring.bam') + self.ringModel.setTransparency(1) + modelRadius = 4.0 + self.ringModel.setScale(RingGameGlobals.RING_RADIUS / modelRadius) + self.ringModel.flattenMedium() + self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadowModel.setColor(0, 0, 0, 0.5) + self.dropShadowModel.flattenMedium() + self.toonDropShadows = [] + self.ringDropShadows = [] + self.__textGen = TextNode('ringGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.__textGen + del self.toonDropShadows + del self.ringDropShadows + self.dropShadowModel.removeNode() + del self.dropShadowModel + self.environModel.removeNode() + del self.environModel + self.ringModel.removeNode() + del self.ringModel + del self.music + del self.sndAmbience + del self.sndPerfect + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.arrowKeys = ArrowKeys.ArrowKeys() + toon = base.localAvatar + toon.reparentTo(render) + toon.setAnimState('swim', 1.0) + toon.stopBobSwimTask() + toon.useLOD(self.TOON_LOD) + self.__placeToon(self.localAvId) + toon.dropShadow.hide() + camera.reparentTo(render) + camera.reparentTo(base.localAvatar) + camera.setPosHpr(0, self.CAMERA_Y + self.TOON_Y, 0, 0, 0, 0) + base.camLens.setFov(80) + base.camLens.setFar(self.FAR_PLANE_DIST) + base.setBackgroundColor(self.WATER_COLOR) + self.__fog = Fog('ringGameFog') + if base.wantFog: + self.__fog.setColor(self.WATER_COLOR) + self.__fog.setLinearRange(0.1, self.FAR_PLANE_DIST - 1.0) + render.setFog(self.__fog) + self.environNode = render.attachNewNode('environNode') + self.environBlocks = [] + for i in range(0, 2): + instance = self.environModel.instanceUnderNode(self.environNode, 'model') + y = self.ENVIRON_LENGTH * i + instance.setY(y) + self.environBlocks.append(instance) + for j in range(0, 2): + instance = self.environModel.instanceUnderNode(self.environNode, 'blocks') + x = self.ENVIRON_LENGTH * (j + 1) + instance.setY(y) + instance.setX(-x) + self.environBlocks.append(instance) + + for j in range(0, 2): + instance = self.environModel.instanceUnderNode(self.environNode, 'blocks') + x = self.ENVIRON_LENGTH * (j + 1) + instance.setY(y) + instance.setX(x) + self.environBlocks.append(instance) + + self.ringNode = render.attachNewNode('ringNode') + self.sndTable = {'gotRing': [None] * self.numPlayers, + 'missedRing': [None] * self.numPlayers} + for i in range(0, self.numPlayers): + self.sndTable['gotRing'][i] = base.loadSfx('phase_4/audio/sfx/ring_get.mp3') + self.sndTable['missedRing'][i] = base.loadSfx('phase_4/audio/sfx/ring_miss.mp3') + + self.__addToonDropShadow(self.getAvatar(self.localAvId)) + self.__spawnUpdateEnvironTask() + self.__spawnUpdateShadowsTask() + self.__spawnUpdateLocalToonTask() + base.playMusic(self.music, looping=0, volume=0.8) + if None != self.sndAmbience: + base.playSfx(self.sndAmbience, looping=1, volume=0.8) + return + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + if None != self.sndAmbience: + self.sndAmbience.stop() + self.__killUpdateLocalToonTask() + self.__killUpdateShadowsTask() + self.__killUpdateEnvironTask() + del self.sndTable + self.__removeAllToonDropShadows() + render.clearFog() + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + base.camLens.setFov(ToontownGlobals.DefaultCameraFov) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.arrowKeys.destroy() + del self.arrowKeys + for block in self.environBlocks: + del block + + self.environNode.removeNode() + del self.environNode + self.ringNode.removeNode() + del self.ringNode + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + av.setAnimState('neutral', 1.0) + + return + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.__removeToonDropShadow(self.remoteToons[avId]) + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = i * self.TOON_INITIAL_SPACING + x -= self.TOON_INITIAL_SPACING * (numToons - 1) / 2.0 + toon.setPosHpr(x, self.TOON_Y, 0, 0, 0, 0) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + if not self.isSinglePlayer(): + base.localAvatar.collisionsOff() + cSphere = CollisionSphere(0.0, 0.0, 0.0, RingGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(RingGameGlobals.CollideMask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.cSphereNodePath = base.localAvatar.attachNewNode(cSphereNode) + self.pusher = CollisionHandlerPusher() + self.pusher.addCollider(self.cSphereNodePath, base.localAvatar) + self.pusher.setHorizontal(0) + self.cTrav = CollisionTraverser('DistributedRingGame') + self.cTrav.addCollider(self.cSphereNodePath, self.pusher) + self.remoteToonCollNPs = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + cSphere = CollisionSphere(0.0, 0.0, 0.0, RingGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(RingGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + self.remoteToonCollNPs[avId] = cSphereNP + + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.setAnimState('swim', 1.0) + toon.stopBobSwimTask() + toon.useLOD(self.TOON_LOD) + toon.dropShadow.hide() + self.__addToonDropShadow(toon) + toon.startSmooth() + + self.remoteToons = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + self.remoteToons[avId] = toon + + self.__nextRingGroupResultIndex = 0 + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('swim') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterSwim(self): + self.notify.debug('enterSwim') + self.__ringTimeBase = self.gameStartTime + self.__numRingGroups = RingGameGlobals.NUM_RING_GROUPS + self.__ringGroupsPassed = 0 + self.__generateRings() + self.__spawnUpdateRingsTask() + self.__spawnCollisionDetectionTask() + self.__ringTracks = [] + self.colorRing = self.ringModel.copyTo(hidden) + self.colorRing.reparentTo(aspect2d) + self.colorRing.setTwoSided(0) + self.colorRing.setPos(1.19, 10, -0.86) + self.colorRing.setScale(0.04) + p = self.avIdList.index(self.localAvId) + self.colorRing.setColor(RingGameGlobals.ringColors[self.colorIndices[p]][1]) + self.resultTable = [self.RT_UNKNOWN] * self.__numRingGroups + self.__initTallyDisplay() + + def __initTallyDisplay(self): + self.__tallyTextNode = TextNode('tally') + self.__tallyTextNode.setFont(ToontownGlobals.getSignFont()) + self.__tallyTextNode.setAlign(TextNode.ACenter) + self.tallyMarkers = [None] * self.__numRingGroups + for i in range(0, self.__numRingGroups): + self.__createTallyMarker(i, self.RT_UNKNOWN) + + return + + def __destroyTallyDisplay(self): + for i in range(0, self.__numRingGroups): + self.__deleteTallyMarker(i) + + del self.tallyMarkers + del self.__tallyTextNode + + def __createTallyMarker(self, index, result): + chars = '-OOX' + colors = (Point4(0.8, 0.8, 0.8, 1), + Point4(0, 1, 0, 1), + Point4(1, 1, 0, 1), + Point4(1, 0, 0, 1)) + self.__deleteTallyMarker(index) + self.__tallyTextNode.setText(chars[result]) + node = self.__tallyTextNode.generate() + tallyText = aspect2d.attachNewNode(node) + tallyText.setColor(colors[result]) + tallyText.setScale(0.1) + zOffset = 0 + if result == self.RT_UNKNOWN: + zOffset = 0.015 + xSpacing = 0.085 + tallyText.setPos(-1.0 + xSpacing * index, 0, -.93 + zOffset) + self.tallyMarkers[index] = tallyText + + def __deleteTallyMarker(self, index): + marker = self.tallyMarkers[index] + if None != marker: + marker.removeNode() + self.tallyMarkers[index] = None + return + + def __updateTallyDisplay(self, index): + self.__createTallyMarker(index, self.resultTable[index]) + + def __generateRings(self): + self.ringGroups = [] + difficultyDistributions = {ToontownGlobals.ToontownCentral: [14, 2, 0], + ToontownGlobals.DonaldsDock: [10, 6, 0], + ToontownGlobals.DaisyGardens: [4, 12, 0], + ToontownGlobals.MinniesMelodyland: [4, 8, 4], + ToontownGlobals.TheBrrrgh: [4, 6, 6], + ToontownGlobals.DonaldsDreamland: [2, 6, 8]} + for distr in difficultyDistributions.values(): + sum = reduce(lambda x, y: x + y, distr) + + difficultyPatterns = {ToontownGlobals.ToontownCentral: [[0] * 14 + [1] * 2 + [2] * 0, [0, + 0, + 0, + 0, + 0, + 0, + 0, + 1] * 2, [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1]], + ToontownGlobals.DonaldsDock: [[0] * 10 + [1] * 6 + [2] * 0, [0, + 0, + 0, + 0, + 0, + 1, + 1, + 1] * 2, [0, + 0, + 0, + 1, + 0, + 0, + 1, + 1] * 2], + ToontownGlobals.DaisyGardens: [[0] * 4 + [1] * 12 + [2] * 0, [0, + 0, + 1, + 1, + 1, + 1, + 1, + 1] * 2, [0, + 1, + 1, + 1, + 0, + 1, + 1, + 1] * 2], + ToontownGlobals.MinniesMelodyland: [[0] * 4 + [1] * 8 + [2] * 4, + [0, + 0, + 1, + 1, + 1, + 1, + 2, + 2] * 2, + [0, + 1, + 1, + 1, + 1, + 0, + 2, + 2] * 2, + [0, + 1, + 1, + 2, + 0, + 1, + 1, + 2] * 2, + [0, + 1, + 2, + 1, + 0, + 1, + 2, + 1] * 2], + ToontownGlobals.TheBrrrgh: [[0] * 4 + [1] * 6 + [2] * 6, + [0, + 0, + 1, + 1, + 1, + 2, + 2, + 2] * 2, + [0, + 1, + 1, + 1, + 0, + 2, + 2, + 2] * 2, + [0, + 1, + 1, + 2, + 0, + 1, + 2, + 2] * 2, + [0, + 1, + 2, + 1, + 0, + 1, + 2, + 2] * 2], + ToontownGlobals.DonaldsDreamland: [[0] * 2 + [1] * 6 + [2] * 8, + [0, + 1, + 1, + 1, + 2, + 2, + 2, + 2] * 2, + [0, + 1, + 1, + 2, + 2, + 1, + 2, + 2] * 2, + [0, + 1, + 2, + 1, + 2, + 1, + 2, + 2] * 2]} + safezone = self.getSafezoneId() + numGroupsPerDifficulty = difficultyDistributions[safezone] + + def patternsAreValid(difficultyPatterns = difficultyPatterns, difficultyDistributions = difficultyDistributions): + for sz in difficultyPatterns.keys(): + for pattern in difficultyPatterns[sz]: + for difficulty in [0, 1, 2]: + numGroupsPerDifficulty = difficultyDistributions[sz] + if numGroupsPerDifficulty[difficulty] != pattern.count(difficulty): + print 'safezone:', sz + print 'pattern:', pattern + print 'difficulty:', difficulty + print 'expected %s %ss, found %s' % (numGroupsPerDifficulty[difficulty], difficulty, pattern.count(difficulty)) + return 0 + + return 1 + + pattern = self.randomNumGen.choice(difficultyPatterns[self.getSafezoneId()]) + for i in range(0, self.__numRingGroups): + numRings = self.numPlayers + trackGroup = RingTrackGroups.getRandomRingTrackGroup(pattern[i], numRings, self.randomNumGen) + ringGroup = RingGroup.RingGroup(trackGroup, self.ringModel, RingGameGlobals.MAX_TOONXZ, self.colorIndices) + for r in range(numRings): + self.__addRingDropShadow(ringGroup.getRing(r)) + + self.ringGroups.append(ringGroup) + ringGroup.reparentTo(self.ringNode) + firstGroupOffset = self.TOON_Y + self.T_FIRST_RING_GROUP_ARRIVES * self.TOON_SWIM_VEL + ringGroup.setY(i * self.RING_GROUP_SPACING + firstGroupOffset) + + def __destroyRings(self): + for group in self.ringGroups: + group.delete() + group.removeNode() + + self.__removeAllRingDropShadows() + del self.ringGroups + + def __spawnUpdateLocalToonTask(self): + self.__initPosBroadcast() + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + taskMgr.add(self.__updateLocalToonTask, self.UPDATE_LOCALTOON_TASK) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + + def __initPosBroadcast(self): + self.__posBroadcastPeriod = 0.2 + self.__timeSinceLastPosBroadcast = 0.0 + self.__lastPosBroadcast = self.getAvatar(self.localAvId).getPos() + self.__storeStop = 0 + lt = self.getAvatar(self.localAvId) + lt.d_clearSmoothing() + lt.sendCurrentPosition() + + def __posBroadcast(self, dt): + self.__timeSinceLastPosBroadcast += dt + if self.__timeSinceLastPosBroadcast > self.__posBroadcastPeriod: + self.__timeSinceLastPosBroadcast -= self.__posBroadcastPeriod + self.getAvatar(self.localAvId).cnode.broadcastPosHprFull() + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + toonPos = self.getAvatar(self.localAvId).getPos() + pos = [toonPos[0], 0, toonPos[2]] + xVel = 0.0 + if self.arrowKeys.leftPressed(): + xVel -= self.TOONXZ_SPEED + if self.arrowKeys.rightPressed(): + xVel += self.TOONXZ_SPEED + pos[0] += xVel * dt + if pos[0] < -RingGameGlobals.MAX_TOONXZ: + pos[0] = -RingGameGlobals.MAX_TOONXZ + if pos[0] > RingGameGlobals.MAX_TOONXZ: + pos[0] = RingGameGlobals.MAX_TOONXZ + zVel = 0.0 + if self.arrowKeys.upPressed(): + zVel += self.TOONXZ_SPEED + if self.arrowKeys.downPressed(): + zVel -= self.TOONXZ_SPEED + pos[2] += zVel * dt + if pos[2] < -RingGameGlobals.MAX_TOONXZ: + pos[2] = -RingGameGlobals.MAX_TOONXZ + if pos[2] > RingGameGlobals.MAX_TOONXZ: + pos[2] = RingGameGlobals.MAX_TOONXZ + self.getAvatar(self.localAvId).setPos(pos[0], self.TOON_Y, pos[2]) + if hasattr(self, 'cTrav'): + self.cTrav.traverse(render) + self.__posBroadcast(dt) + return Task.cont + + def exitSwim(self): + for track in self.__ringTracks: + track.finish() + + del self.__ringTracks + self.colorRing.removeNode() + del self.colorRing + self.__destroyTallyDisplay() + del self.resultTable + taskMgr.remove(self.END_GAME_WAIT_TASK) + self.__killUpdateRingsTask() + self.__killCollisionDetectionTask() + self.__destroyRings() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + if not self.isSinglePlayer(): + for np in self.remoteToonCollNPs.values(): + np.removeNode() + + del self.remoteToonCollNPs + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + del self.pusher + del self.cTrav + base.localAvatar.collisionsOn() + + def exitCleanup(self): + pass + + def __addDropShadow_INTERNAL(self, object, scale_x, scale_y, scale_z, list): + shadow = self.dropShadowModel.copyTo(render) + shadow.setPos(0, self.CAMERA_Y, -100) + shadow.setScale(scale_x, scale_y, scale_z) + list.append([shadow, object]) + + def __removeDropShadow_INTERNAL(self, object, list): + for i in range(len(list)): + entry = list[i] + if entry[1] == object: + entry[0].removeNode() + list.pop(i) + return + + self.notify.warning('parent object ' + str(object) + ' not found in drop shadow list!') + + def __addToonDropShadow(self, object): + self.__addDropShadow_INTERNAL(object, 0.5, 0.5, 0.5, self.toonDropShadows) + + def __removeToonDropShadow(self, object): + self.__removeDropShadow_INTERNAL(object, self.toonDropShadows) + + def __addRingDropShadow(self, object): + self.__addDropShadow_INTERNAL(object, 1.2, 0.31, 1.0, self.ringDropShadows) + + def __removeRingDropShadow(self, object): + self.__removeDropShadow_INTERNAL(object, self.ringDropShadows) + + def __removeAllToonDropShadows(self): + for entry in self.toonDropShadows: + entry[0].removeNode() + + self.toonDropShadows = [] + + def __removeAllRingDropShadows(self): + for entry in self.ringDropShadows: + entry[0].removeNode() + + self.ringDropShadows = [] + + def __spawnUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + taskMgr.add(self.__updateShadowsTask, self.UPDATE_SHADOWS_TASK, priority=self.UPDATE_SHADOWS_PRIORITY) + + def __killUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + + def __updateShadowsTask(self, task): + list = self.toonDropShadows + self.ringDropShadows + for entry in list: + object = entry[1] + y = object.getY(render) + if y > self.Y_VIS_MAX: + continue + x = object.getX(render) + z = self.SEA_FLOOR_Z + self.SHADOW_Z_OFFSET + shadow = entry[0] + shadow.setPos(x, y, z) + + return Task.cont + + def __spawnUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + taskMgr.add(self.__updateEnvironTask, self.UPDATE_ENVIRON_TASK) + + def __killUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + + def __updateEnvironTask(self, task): + t = globalClock.getFrameTime() - self.__timeBase + distance = t * self.TOON_SWIM_VEL + distance %= self.ENVIRON_LENGTH + distance += self.ENVIRON_START_OFFSET + self.environNode.setY(-distance) + return Task.cont + + def __spawnUpdateRingsTask(self): + taskMgr.remove(self.UPDATE_RINGS_TASK) + taskMgr.add(self.__updateRingsTask, self.UPDATE_RINGS_TASK) + + def __killUpdateRingsTask(self): + taskMgr.remove(self.UPDATE_RINGS_TASK) + + def __updateRingsTask(self, task): + t = globalClock.getFrameTime() - self.__ringTimeBase + distance = t * self.TOON_SWIM_VEL + self.ringNode.setY(-distance) + for ringGroup in self.ringGroups: + groupY = ringGroup.getY(render) + if groupY <= self.Y_VIS_MAX and groupY >= self.Y_VIS_MIN: + ringGroup.setT(t) + + return Task.cont + + def __spawnCollisionDetectionTask(self): + self.__ringGroupsPassed = 0 + taskMgr.remove(self.COLLISION_DETECTION_TASK) + taskMgr.add(self.__collisionDetectionTask, self.COLLISION_DETECTION_TASK, priority=self.COLLISION_DETECTION_PRIORITY) + + def __killCollisionDetectionTask(self): + taskMgr.remove(self.COLLISION_DETECTION_TASK) + + def __makeRingSuccessFadeTrack(self, ring, duration, endScale, ringIndex): + targetScale = Point3(endScale, endScale, endScale) + dFade = 0.5 * duration + dColorChange = duration - dFade + origColor = ring.getColor() + targetColor = Point4(1.0 - origColor[0], 1.0 - origColor[1], 1.0 - origColor[2], 1) + + def colorChangeFunc(t, ring = ring, targetColor = targetColor, origColor = origColor): + newColor = targetColor * t + origColor * (1.0 - t) + ring.setColor(newColor) + + def fadeFunc(t, ring = ring): + ring.setColorScale(1, 1, 1, 1.0 - t) + + fadeAwayTrack = Parallel(Sequence(LerpFunctionInterval(colorChangeFunc, fromData=0.0, toData=1.0, duration=dColorChange), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=dFade)), LerpScaleInterval(ring, duration, targetScale)) + successTrack = Sequence(Wait(self.RING_RESPONSE_DELAY), Parallel(SoundInterval(self.sndTable['gotRing'][ringIndex]), Sequence(Func(ring.wrtReparentTo, render), fadeAwayTrack, Func(self.__removeRingDropShadow, ring), Func(ring.reparentTo, hidden)))) + return successTrack + + def __makeRingFailureFadeTrack(self, ring, duration, ringIndex): + ts = 0.01 + targetScale = Point3(ts, ts, ts) + missedTextNode = self.__genText(TTLocalizer.RingGameMissed) + missedText = hidden.attachNewNode(missedTextNode) + ringColor = RingGameGlobals.ringColors[self.colorIndices[ringIndex]][1] + + def addMissedText(ring = ring, ringColor = ringColor, missedText = missedText): + missedText.reparentTo(render) + missedText.setPos(ring.getPos(render) + Point3(0, -1, 0)) + missedText.setColor(ringColor) + + def removeMissedText(missedText = missedText): + missedText.removeNode() + missedText = None + return + + failureTrack = Sequence(Wait(self.RING_RESPONSE_DELAY), Parallel(SoundInterval(self.sndTable['missedRing'][ringIndex]), Sequence(Func(ring.wrtReparentTo, render), Func(addMissedText), LerpScaleInterval(ring, duration, targetScale, blendType='easeIn'), Func(removeMissedText), Func(self.__removeRingDropShadow, ring), Func(ring.reparentTo, hidden)))) + return failureTrack + + def __makeRingFadeAway(self, ring, success, ringIndex): + if success: + track = self.__makeRingSuccessFadeTrack(ring, 0.5, 2.0, ringIndex) + else: + track = self.__makeRingFailureFadeTrack(ring, 0.5, ringIndex) + self.__ringTracks.append(track) + track.start() + + def __collisionDetectionTask(self, task): + nextRingGroupIndex = self.__ringGroupsPassed + nextRingGroup = self.ringGroups[nextRingGroupIndex] + if nextRingGroup.getY(render) < 0: + groupIndex = nextRingGroupIndex + gotRing = 0 + ourRing = nextRingGroup.getRing(self.avIdList.index(self.localAvId)) + ringPos = ourRing.getPos(render) + localToonPos = base.localAvatar.getPos(render) + distX = localToonPos[0] - ringPos[0] + distZ = localToonPos[2] - ringPos[2] + distSqrd = distX * distX + distZ * distZ + if distSqrd <= self.RING_RADIUS_SQRD: + gotRing = 1 + self.__makeRingFadeAway(ourRing, gotRing, self.avIdList.index(self.localAvId)) + if gotRing: + self.resultTable[groupIndex] = self.RT_SUCCESS + else: + self.resultTable[groupIndex] = self.RT_FAILURE + self.__updateTallyDisplay(groupIndex) + self.sendUpdate('setToonGotRing', [gotRing]) + if self.isSinglePlayer(): + self.__processRingGroupResults([gotRing]) + self.__ringGroupsPassed += 1 + if self.__ringGroupsPassed >= self.__numRingGroups: + self.__killCollisionDetectionTask() + return Task.cont + + def __endGameDolater(self, task): + self.gameOver() + return Task.done + + def setTimeBase(self, timestamp): + if not self.hasLocalToon: + return + self.__timeBase = globalClockDelta.networkToLocalTime(timestamp) + + def setColorIndices(self, a, b, c, d): + if not self.hasLocalToon: + return + self.colorIndices = [a, + b, + c, + d] + + def __getSuccessTrack(self, groupIndex): + + def makeSuccessTrack(text, holdDuration, fadeDuration = 0.5, perfect = 0, self = self): + successText = hidden.attachNewNode(self.__genText(text)) + successText.setScale(0.25) + successText.setColor(1, 1, 0.5, 1) + + def fadeFunc(t, text): + text.setColorScale(1, 1, 1, 1.0 - t) + + def destroyText(text): + text.removeNode() + text = None + return + + track = Sequence(Func(successText.reparentTo, aspect2d), Wait(holdDuration), LerpFunctionInterval(fadeFunc, extraArgs=[successText], fromData=0.0, toData=1.0, duration=fadeDuration, blendType='easeIn'), Func(destroyText, successText)) + if perfect: + track = Parallel(track, SoundInterval(self.sndPerfect)) + return track + + def isPerfect(list, goodValues): + for value in list: + if value not in goodValues: + return 0 + + return 1 + + if groupIndex >= self.__numRingGroups - 1: + if not self.isSinglePlayer(): + if isPerfect(self.resultTable, [self.RT_GROUPSUCCESS]): + return makeSuccessTrack(TTLocalizer.RingGameGroupPerfect, 1.5, perfect=1) + if isPerfect(self.resultTable, [self.RT_SUCCESS, self.RT_GROUPSUCCESS]): + return makeSuccessTrack(TTLocalizer.RingGamePerfect, 1.5, perfect=1) + return Wait(1.0) + if not self.isSinglePlayer(): + if self.resultTable[groupIndex] == self.RT_GROUPSUCCESS: + return makeSuccessTrack(TTLocalizer.RingGameGroupBonus, 0.0, fadeDuration=0.4) + return None + + def __processRingGroupResults(self, results): + groupIndex = self.__nextRingGroupResultIndex + ringGroup = self.ringGroups[self.__nextRingGroupResultIndex] + self.__nextRingGroupResultIndex += 1 + for i in range(0, self.numPlayers): + if self.avIdList[i] != self.localAvId: + ring = ringGroup.getRing(i) + self.__makeRingFadeAway(ring, results[i], i) + + if not self.isSinglePlayer(): + if 0 not in results: + self.notify.debug('Everyone got their rings!!') + self.resultTable[groupIndex] = self.RT_GROUPSUCCESS + self.__updateTallyDisplay(groupIndex) + successTrack = self.__getSuccessTrack(groupIndex) + endGameTrack = None + if groupIndex >= self.__numRingGroups - 1: + + def endTheGame(self = self): + if not RingGameGlobals.ENDLESS_GAME: + taskMgr.doMethodLater(self.GAME_END_DELAY, self.__endGameDolater, self.END_GAME_WAIT_TASK) + + endGameTrack = Func(endTheGame) + if None != successTrack or None != endGameTrack: + track = Sequence() + if None != successTrack: + track.append(Wait(self.RING_RESPONSE_DELAY)) + track.append(successTrack) + if None != endGameTrack: + track.append(endGameTrack) + self.__ringTracks.append(track) + track.start() + return + + def setRingGroupResults(self, bitfield): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'swim': + return + results = [] + mask = 1 + for avId in self.avIdList: + results.append(not bitfield & mask) + mask <<= 1 + + self.__processRingGroupResults(results) diff --git a/toontown/minigame/DistributedRingGameAI.py b/toontown/minigame/DistributedRingGameAI.py new file mode 100644 index 0000000..cdabddc --- /dev/null +++ b/toontown/minigame/DistributedRingGameAI.py @@ -0,0 +1,146 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import RingGameGlobals +import random +import types + +class DistributedRingGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedRingGameAI_initialized + except: + self.DistributedRingGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedRingGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['swimming']), State.State('swimming', self.enterSwimming, self.exitSwimming, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.__timeBase = globalClockDelta.localToNetworkTime(globalClock.getRealTime()) + self.selectColorIndices() + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('swimming') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def getColorIndices(self): + return self.colorIndices + + def selectColorIndices(self): + self.colorIndices = [None, + None, + None, + None] + chooseFrom = RingGameGlobals.ringColorSelection[:] + for i in range(0, 4): + c = random.choice(chooseFrom) + chooseFrom.remove(c) + if isinstance(c, types.TupleType): + c = random.choice(c) + self.colorIndices[i] = c + + return + + def enterSwimming(self): + self.notify.debug('enterSwimming') + self.__nextRingGroup = {} + for avId in self.avIdList: + self.__nextRingGroup[avId] = 0 + + self.__numRingsPassed = [0] * RingGameGlobals.NUM_RING_GROUPS + self.__ringResultBitfield = [0] * RingGameGlobals.NUM_RING_GROUPS + self.perfectGames = {} + for avId in self.avIdList: + self.perfectGames[avId] = 1 + + for avId in self.avIdList: + self.scoreDict[avId] = 0 + + def exitSwimming(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def setToonGotRing(self, success): + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'RingGameAI.setToonGotRing: invalid avId') + return + if self.gameFSM.getCurrentState() is None or self.gameFSM.getCurrentState().getName() != 'swimming': + self.air.writeServerEvent('suspicious', avId, 'RingGameAI.setToonGotRing: game not in swimming state') + return + ringGroupIndex = self.__nextRingGroup[avId] + if ringGroupIndex >= RingGameGlobals.NUM_RING_GROUPS: + self.notify.warning('warning: got extra ToonGotRing msg from av %s' % avId) + return + self.__nextRingGroup[avId] += 1 + if not success: + self.__ringResultBitfield[ringGroupIndex] |= 1 << self.avIdList.index(avId) + self.perfectGames[avId] = 0 + else: + self.scoreDict[avId] += 1 + self.__numRingsPassed[ringGroupIndex] += 1 + if self.__numRingsPassed[ringGroupIndex] >= self.numPlayers: + if not self.isSinglePlayer(): + bitfield = self.__ringResultBitfield[ringGroupIndex] + if bitfield == 0: + for id in self.avIdList: + self.scoreDict[id] += 0.5 + + self.sendUpdate('setRingGroupResults', [bitfield]) + if ringGroupIndex >= RingGameGlobals.NUM_RING_GROUPS - 1: + perfectBonuses = {1: 5, + 2: 5, + 3: 10, + 4: 18} + numPerfectToons = 0 + for avId in self.avIdList: + if self.perfectGames[avId]: + numPerfectToons += 1 + + for avId in self.avIdList: + if self.perfectGames[avId]: + self.scoreDict[avId] += perfectBonuses[numPerfectToons] + self.logPerfectGame(avId) + + for avId in self.avIdList: + self.scoreDict[avId] = max(1, self.scoreDict[avId]) + + if not RingGameGlobals.ENDLESS_GAME: + self.gameOver() + return diff --git a/toontown/minigame/DistributedTagGame.py b/toontown/minigame/DistributedTagGame.py new file mode 100644 index 0000000..fcaeedc --- /dev/null +++ b/toontown/minigame/DistributedTagGame.py @@ -0,0 +1,285 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from DistributedMinigame import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.safezone import Walk +from toontown.toonbase import ToontownTimer +from direct.gui import OnscreenText +import MinigameAvatarScorePanel +from direct.distributed import DistributedSmoothNode +import random +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPGlobals +import TagGameGlobals +import Trajectory + +class DistributedTagGame(DistributedMinigame): + DURATION = TagGameGlobals.DURATION + IT_SPEED_INCREASE = 1.3 + IT_ROT_INCREASE = 1.3 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTagGame', [State.State('off', self.enterOff, self.exitOff, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off') + self.addChildGameFSM(self.gameFSM) + self.walkStateData = Walk.Walk('walkDone') + self.scorePanels = [] + self.initialPositions = ((0, 10, 0, 180, 0, 0), + (10, 0, 0, 90, 0, 0), + (0, -10, 0, 0, 0, 0), + (-10, 0, 0, -90, 0, 0)) + base.localAvatar.isIt = 0 + self.modelCount = 4 + + def getTitle(self): + return TTLocalizer.TagGameTitle + + def getInstructions(self): + return TTLocalizer.TagGameInstructions + + def getMaxDuration(self): + return self.DURATION + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.itText = OnscreenText.OnscreenText('itText', fg=(0.95, 0.95, 0.65, 1), scale=0.14, font=ToontownGlobals.getSignFont(), pos=(0.0, -0.8), wordwrap=15, mayChange=1) + self.itText.hide() + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.ground = loader.loadModel('phase_4/models/minigames/tag_arena') + self.music = base.loadMusic('phase_4/audio/bgm/MG_toontag.mid') + self.tagSfx = base.loadSfx('phase_4/audio/sfx/MG_Tag_C.mp3') + self.itPointer = loader.loadModel('phase_4/models/minigames/bboard-pointer') + self.tracks = [] + self.IT = None + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.ignoreAll() + del self.tracks + del self.IT + self.sky.removeNode() + del self.sky + self.itPointer.removeNode() + del self.itPointer + self.ground.removeNode() + del self.ground + del self.music + del self.tagSfx + self.itText.cleanup() + del self.itText + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.ground.reparentTo(render) + self.sky.reparentTo(render) + myPos = self.avIdList.index(self.localAvId) + base.localAvatar.setPosHpr(*self.initialPositions[myPos]) + base.localAvatar.reparentTo(render) + base.localAvatar.loop('neutral') + camera.reparentTo(render) + camera.setPosHpr(0, -24, 16, 0, -30, 0) + base.camLens.setFar(450.0) + base.transitions.irisIn(0.4) + NametagGlobals.setMasterArrowsOn(1) + DistributedSmoothNode.activateSmoothing(1, 1) + self.IT = None + return + + def offstage(self): + self.notify.debug('offstage') + DistributedSmoothNode.activateSmoothing(1, 0) + NametagGlobals.setMasterArrowsOn(0) + DistributedMinigame.offstage(self) + self.sky.reparentTo(hidden) + self.ground.reparentTo(hidden) + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + self.itText.hide() + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for avId in self.avIdList: + self.acceptTagEvent(avId) + + myPos = self.avIdList.index(self.localAvId) + for i in range(self.numPlayers): + avId = self.avIdList[i] + avatar = self.getAvatar(avId) + if avatar: + avatar.startSmooth() + + base.localAvatar.setPosHpr(*self.initialPositions[myPos]) + base.localAvatar.d_clearSmoothing() + base.localAvatar.sendCurrentPosition() + base.localAvatar.b_setAnimState('neutral', 1) + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + for i in range(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setPos(1.12, 0.0, 0.28 * i - 0.34) + self.scorePanels.append(scorePanel) + + base.setCellsAvailable(base.rightCells, 0) + self.walkStateData.enter() + self.walkStateData.fsm.request('walking') + if base.localAvatar.isIt: + base.mouseInterfaceNode.setForwardSpeed(ToontownGlobals.ToonForwardSpeed * self.IT_SPEED_INCREASE) + base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed * self.IT_ROT_INCREASE) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(self.DURATION) + self.timer.countdown(self.DURATION, self.timerExpired) + base.playMusic(self.music, looping=1, volume=0.9) + base.localAvatar.setIdealCameraPos(Point3(0, -24, 8)) + + def exitPlay(self): + for task in self.tracks: + task.finish() + + self.tracks = [] + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + toon.getGeomNode().clearMat() + toon.scale = 1.0 + toon.rescaleToon() + + self.walkStateData.exit() + self.music.stop() + self.timer.destroy() + del self.timer + for panel in self.scorePanels: + panel.cleanup() + + self.scorePanels = [] + base.setCellsAvailable(base.rightCells, 1) + base.mouseInterfaceNode.setForwardSpeed(ToontownGlobals.ToonForwardSpeed) + base.mouseInterfaceNode.setRotateSpeed(ToontownGlobals.ToonRotateSpeed) + self.itPointer.reparentTo(hidden) + base.localAvatar.cameraIndex = 0 + base.localAvatar.setCameraPositionByIndex(0) + + def timerExpired(self): + self.notify.debug('local timer expired') + self.gameOver() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('off') + + def exitCleanup(self): + pass + + def setIt(self, avId): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.debug('Ignoring setIt after done playing') + return + self.itText.show() + self.notify.debug(str(avId) + ' is now it') + if avId == self.localAvId: + self.itText.setText(TTLocalizer.TagGameYouAreIt) + base.localAvatar.isIt = 1 + base.localAvatar.controlManager.setSpeeds(OTPGlobals.ToonForwardSpeed * self.IT_SPEED_INCREASE, OTPGlobals.ToonJumpForce, OTPGlobals.ToonReverseSpeed * self.IT_SPEED_INCREASE, OTPGlobals.ToonRotateSpeed * self.IT_ROT_INCREASE) + else: + self.itText.setText(TTLocalizer.TagGameSomeoneElseIsIt % self.getAvatarName(avId)) + base.localAvatar.isIt = 0 + base.localAvatar.setWalkSpeedNormal() + avatar = self.getAvatar(avId) + if avatar: + self.itPointer.reparentTo(avatar) + self.itPointer.setZ(avatar.getHeight()) + base.playSfx(self.tagSfx) + toon = self.getAvatar(avId) + duration = 0.6 + if not toon: + return + spinTrack = LerpHprInterval(toon.getGeomNode(), duration, Point3(0, 0, 0), startHpr=Point3(-5.0 * 360.0, 0, 0), blendType='easeOut') + growTrack = Parallel() + gs = 2.5 + for hi in range(toon.headParts.getNumPaths()): + head = toon.headParts[hi] + growTrack.append(LerpScaleInterval(head, duration, Point3(gs, gs, gs))) + + def bounceFunc(t, trajectory, node = toon.getGeomNode()): + node.setZ(trajectory.calcZ(t)) + + def bounceCleanupFunc(node = toon.getGeomNode(), z = toon.getGeomNode().getZ()): + node.setZ(z) + + bounceTrack = Sequence() + startZ = toon.getGeomNode().getZ() + tLen = 0 + zVel = 30 + decay = 0.6 + while tLen < duration: + trajectory = Trajectory.Trajectory(0, Point3(0, 0, startZ), Point3(0, 0, zVel), gravMult=5.0) + dur = trajectory.calcTimeOfImpactOnPlane(startZ) + if dur <= 0: + break + bounceTrack.append(LerpFunctionInterval(bounceFunc, fromData=0.0, toData=dur, duration=dur, extraArgs=[trajectory])) + tLen += dur + zVel *= decay + + bounceTrack.append(Func(bounceCleanupFunc)) + tagTrack = Sequence(Func(toon.animFSM.request, 'off'), Parallel(spinTrack, growTrack, bounceTrack), Func(toon.animFSM.request, 'Happy')) + self.tracks.append(tagTrack) + tagTrack.start() + if self.IT: + it = self.getAvatar(self.IT) + shrinkTrack = Parallel() + for hi in range(it.headParts.getNumPaths()): + head = it.headParts[hi] + scale = ToontownGlobals.toonHeadScales[it.style.getAnimal()] + shrinkTrack.append(LerpScaleInterval(head, duration, scale)) + + self.tracks.append(shrinkTrack) + shrinkTrack.start() + self.IT = avId + + def acceptTagEvent(self, avId): + self.accept('enterdistAvatarCollNode-' + str(avId), self.sendTagIfIt, [avId]) + + def sendTagIfIt(self, avId, collisionEntry): + if base.localAvatar.isIt: + self.notify.debug('Tagging ' + str(avId)) + self.sendUpdate('tag', [avId]) + else: + self.notify.debug('Bumped ' + str(avId)) + + def setTreasureScore(self, scores): + if not self.hasLocalToon: + return + self.notify.debug('setTreasureScore: %s' % scores) + for i in range(len(self.scorePanels)): + self.scorePanels[i].setScore(scores[i]) diff --git a/toontown/minigame/DistributedTagGameAI.py b/toontown/minigame/DistributedTagGameAI.py new file mode 100644 index 0000000..952fe9b --- /dev/null +++ b/toontown/minigame/DistributedTagGameAI.py @@ -0,0 +1,132 @@ +from DistributedMinigameAI import * +from TagTreasurePlannerAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import random +import TagGameGlobals + +class DistributedTagGameAI(DistributedMinigameAI): + DURATION = TagGameGlobals.DURATION + + def __init__(self, air, minigameId): + try: + self.DistributedTagGameAI_initialized + except: + self.DistributedTagGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTagGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.treasureScores = {} + self.itAvId = None + self.tagBack = 1 + + return + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + for avId in self.avIdList: + self.treasureScores[avId] = 0 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.b_setIt(random.choice(self.avIdList)) + taskMgr.doMethodLater(self.DURATION, self.timerExpired, self.taskName('gameTimer')) + self.tagTreasurePlanner = TagTreasurePlannerAI(self.zoneId, self.treasureGrabCallback) + self.tagTreasurePlanner.placeRandomTreasure() + self.tagTreasurePlanner.placeRandomTreasure() + self.tagTreasurePlanner.placeRandomTreasure() + self.tagTreasurePlanner.placeRandomTreasure() + self.tagTreasurePlanner.start() + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameOver() + return Task.done + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + taskMgr.remove(self.taskName('tagBack')) + taskMgr.remove(self.taskName('clearTagBack')) + self.tagTreasurePlanner.stop() + self.tagTreasurePlanner.deleteAllTreasuresNow() + del self.tagTreasurePlanner + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def treasureGrabCallback(self, avId): + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'TagGameAI.treasureGrabCallback non-player avId') + return + self.treasureScores[avId] += 2 + self.notify.debug('treasureGrabCallback: ' + str(avId) + ' grabbed a treasure, new score: ' + str(self.treasureScores[avId])) + self.scoreDict[avId] = self.treasureScores[avId] + treasureScoreParams = [] + for avId in self.avIdList: + treasureScoreParams.append(self.treasureScores[avId]) + + self.sendUpdate('setTreasureScore', [treasureScoreParams]) + + def clearTagBack(self, task): + self.tagBack = 1 + return Task.done + + def tag(self, taggedAvId): + taggedAvatar = simbase.air.doId2do.get(taggedAvId) + if taggedAvatar == None: + self.air.writeServerEvent('suspicious', taggedAvId, 'TagGameAI.tag invalid taggedAvId') + return + itAvId = self.air.getAvatarIdFromSender() + if self.tagBack: + self.notify.debug('tag: ' + str(itAvId) + ' tagged: ' + str(taggedAvId)) + if self.itAvId == itAvId: + self.b_setIt(taggedAvId) + else: + self.notify.warning('Got tag message from avatar that is not IT') + return + self.tagBack = 0 + taskMgr.doMethodLater(2.0, self.clearTagBack, self.taskName('clearTagBack')) + return + + def b_setIt(self, avId): + self.d_setIt(avId) + self.setIt(avId) + + def d_setIt(self, avId): + self.sendUpdate('setIt', [avId]) + + def setIt(self, avId): + self.itAvId = avId diff --git a/toontown/minigame/DistributedTagTreasure.py b/toontown/minigame/DistributedTagTreasure.py new file mode 100644 index 0000000..d22bb61 --- /dev/null +++ b/toontown/minigame/DistributedTagTreasure.py @@ -0,0 +1,17 @@ +from toontown.safezone import DistributedTreasure + +class DistributedTagTreasure(DistributedTreasure.DistributedTreasure): + + def __init__(self, cr): + DistributedTreasure.DistributedTreasure.__init__(self, cr) + self.modelPath = 'phase_4/models/props/icecream' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' + self.accept('minigameOffstage', self.handleMinigameOffstage) + + def handleEnterSphere(self, collEntry): + if not base.localAvatar.isIt: + self.d_requestGrab() + return None + + def handleMinigameOffstage(self): + self.nodePath.reparentTo(hidden) diff --git a/toontown/minigame/DistributedTagTreasureAI.py b/toontown/minigame/DistributedTagTreasureAI.py new file mode 100644 index 0000000..7e7bf29 --- /dev/null +++ b/toontown/minigame/DistributedTagTreasureAI.py @@ -0,0 +1,7 @@ +from toontown.safezone import DistributedTreasureAI +from direct.distributed.ClockDelta import * + +class DistributedTagTreasureAI(DistributedTreasureAI.DistributedTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedTreasureAI.DistributedTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/minigame/DistributedTargetGame.py b/toontown/minigame/DistributedTargetGame.py new file mode 100644 index 0000000..cfc06b5 --- /dev/null +++ b/toontown/minigame/DistributedTargetGame.py @@ -0,0 +1,1550 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from DistributedMinigame import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import ArrowKeys +import TargetGameGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import math +from math import * +import random +import random +import RubberBand +import FogOverlay + +def circleX(angle, radius, centerX, centerY): + x = radius * cos(angle) + centerX + return x + + +def circleY(angle, radius, centerX, centerY): + y = radius * sin(angle) + centerY + return y + + +def getCirclePoints(segCount, centerX, centerY, radius, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in range(0, segCount): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + coordX = wideX * circleX(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(0 / segCount), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + return returnShape + + +def getRingPoints(segCount, centerX, centerY, radius, thickness = 2.0, wideX = 1.0, wideY = 1.0): + returnShape = [] + for seg in range(0, segCount): + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius - thickness, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius - thickness, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + coordX = wideX * circleX(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + coordY = wideY * circleY(pi * 2.0 * float(float(seg) / float(segCount)), radius, centerX, centerY) + returnShape.append((coordX, coordY, 1)) + + return returnShape + + +def addRing(attachNode, color, vertexCount, radius, layer = 0, thickness = 1.0): + targetGN = GeomNode('target Circle') + zFloat = 0.025 + targetCircleShape = getRingPoints(5 + vertexCount, 0.0, 0.0, radius, thickness) + gFormat = GeomVertexFormat.getV3cp() + targetCircleVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + targetCircleVertexWriter = GeomVertexWriter(targetCircleVertexData, 'vertex') + targetCircleColorWriter = GeomVertexWriter(targetCircleVertexData, 'color') + for vertex in targetCircleShape: + targetCircleVertexWriter.addData3f(0.0 + vertex[0], 0.0 + vertex[1], zFloat) + targetCircleColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + + targetTris = GeomTristrips(Geom.UHStatic) + sizeTarget = len(targetCircleShape) + for countVertex in range(0, sizeTarget): + targetTris.addVertex(countVertex) + + targetTris.addVertex(0) + targetTris.addVertex(1) + targetTris.closePrimitive() + targetGeom = Geom(targetCircleVertexData) + targetGeom.addPrimitive(targetTris) + attachNode.addGeom(targetGeom) + return targetGeom + + +def addCircle(attachNode, color, vertexCount, radius, layer = 0): + targetGN = GeomNode('target Circle') + zFloat = 0.025 + targetCircleShape = getCirclePoints(5 + vertexCount, 0.0, 0.0, radius) + gFormat = GeomVertexFormat.getV3cp() + targetCircleVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + targetCircleVertexWriter = GeomVertexWriter(targetCircleVertexData, 'vertex') + targetCircleColorWriter = GeomVertexWriter(targetCircleVertexData, 'color') + targetCircleVertexWriter.addData3f(0.0, 0.0, zFloat) + targetCircleColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + for vertex in targetCircleShape: + targetCircleVertexWriter.addData3f(0.0 + vertex[0], 0.0 + vertex[1], zFloat) + targetCircleColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + + targetTris = GeomTrifans(Geom.UHStatic) + sizeTarget = len(targetCircleShape) + targetTris.addVertex(0) + for countVertex in range(1, sizeTarget + 1): + targetTris.addVertex(countVertex) + + targetTris.addVertex(1) + targetTris.closePrimitive() + targetGeom = Geom(targetCircleVertexData) + targetGeom.addPrimitive(targetTris) + attachNode.addGeom(targetGeom) + return targetGeom + + +def checkPlace(placeX, placeY, fillSize, placeList): + goodPlacement = 1 + for place in placeList: + distance = math.sqrt((place[0] - placeX) * (place[0] - placeX) + (place[1] - placeY) * (place[1] - placeY)) + distance = distance - (fillSize + place[2]) + if distance <= 0.0: + goodPlacement = 0 + break + + return goodPlacement + + +class DistributedTargetGame(DistributedMinigame): + UPDATE_ENVIRON_TASK = 'TargetGameUpdateEnvironTask' + UPDATE_LOCALTOON_TASK = 'TargetGameUpdateLocalToonTask' + UPDATE_SHADOWS_TASK = 'TargetGameUpdateShadowsTask' + COLLISION_DETECTION_TASK = 'TargetGameCollisionDetectionTask' + END_GAME_WAIT_TASK = 'TargetGameCollisionDetectionTask' + UPDATE_POWERBAR_TASK = 'TargetGameUpdatePowerBarTask' + GAME_DONE_TASK = 'gameDoneTask' + UPDATE_COUNTDOWN_TASK = 'countdown task' + FLY2FALL_CAM_TASK = 'fly2FallCameraTask' + PRELAUNCH_CAM_TASK = 'prelaunchCameraTask' + SCORE_CAM_TASK = 'scoreCameraTask' + TOONSTRETCHTASK = 'tooncamtask' + UPDATE_LOCAL_TOON_PRIORITY = 0 + UPDATE_POWER_BAR_PRIORITY = 1 + UPDATE_RUBBER_BAND_PRIORITY = 2 + COLLISION_DETECTION_PRIORITY = 5 + UPDATE_SHADOWS_PRIORITY = 47 + UMBRELLA_TEXTURE_LIST = ['phase_4/maps/mg_slingshot_umbrella_blue.jpg', + 'phase_4/maps/mg_slingshot_umbrella_purple.jpg', + 'phase_4/maps/mg_slingshot_umbrella_red.jpg', + 'phase_4/maps/mg_slingshot_umbrella_yellow.jpg'] + RT_UNKNOWN = 0 + RT_SUCCESS = 1 + RT_GROUPSUCCESS = 2 + RT_FAILURE = 3 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTargetGame', [State.State('off', self.enterOff, self.exitOff, ['prelaunch']), + State.State('prelaunch', self.enterPrelaunch, self.exitPrelaunch, ['launch']), + State.State('launch', self.enterLaunch, self.exitLaunch, ['fly']), + State.State('fly', self.enterFly, self.exitFly, ['fall', 'bouncing', 'score']), + State.State('fall', self.enterFall, self.exitFall, ['bouncing', 'score']), + State.State('bouncing', self.enterBouncing, self.exitBouncing, ['score']), + State.State('score', self.enterScore, self.exitScore, ['cleanup', 'prelaunch']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.targets = None + self.round = 1 + self.signalLaunch = 0 + self.stretchX = 0 + self.stretchY = 0 + self.targetsPlaced = [] + self.pattern = None + self.maxDist = 50 + self.targetMarker = None + self.umbrellaColorSelect = [1, + 1, + 1, + 1] + self.localTrack = None + self.hitInfo = None + return + + def getTitle(self): + return TTLocalizer.TargetGameTitle + + def getInstructions(self): + p = self.avIdList.index(self.localAvId) + if self.isSinglePlayer(): + text = TTLocalizer.TargetGameInstructionsSinglePlayer + else: + text = TTLocalizer.TargetGameInstructionsMultiPlayer + return text + + def getMaxDuration(self): + return 60 + + def defineConstants(self): + self.CAMERA_Y = -25 + self.TOON_Y = 0 + self.FAR_PLANE_DIST = 370 + tScreenCenterToEdge = 1.0 + self.TOONXZ_SPEED = TargetGameGlobals.MAX_TOONXZ / tScreenCenterToEdge + self.WATER_DEPTH = 75.0 + self.ENVIRON_LENGTH = TargetGameGlobals.ENVIRON_LENGTH + self.ENVIRON_WIDTH = TargetGameGlobals.ENVIRON_WIDTH + self.ENVIRON_START_OFFSET = 20.0 + self.TOON_INITIAL_SPACING = 14.0 + waterZOffset = 28.0 + self.SEA_FLOOR_Z = -self.WATER_DEPTH / 2.0 + waterZOffset + self.FOGDISTGROUND = 600 + self.FOGDISTCLOUD = 200 + farPlaneDist = self.CAMERA_Y + self.FAR_PLANE_DIST - self.TOON_Y + self.ringGroupArrivalPeriod = 3.0 + self.AMB_COLOR = Vec4(0.75, 0.8, 1.0, 1.0) + self.CLOUD_COLOR = Vec4(1.0, 1.0, 1.0, 1.0) + self.SHADOW_Z_OFFSET = 0.1 + self.Y_VIS_MAX = self.FAR_PLANE_DIST + self.Y_VIS_MIN = self.CAMERA_Y + targetRadius = TargetGameGlobals.TARGET_RADIUS * 1.025 + self.GAME_END_DELAY = 1.0 + self.TOON_LOD = 1000 + self.speedLaunch = 60 + self.speedStall = 30 + self.speedForward = 0 + self.airResistance = 0.1 + self.umbrellaResistance = 0.11 + self.distance = 0 + self.zVel = 0 + self.lastPressed = None + self.lastPressTime = None + self.driftX = 0 + self.driftY = 0 + self.launchLaterial = 0 + self.laterial = 0 + self.gravity = 12.5 + self.inList = [] + self.placeShift = 10 + self.startPower = 10 + self.canPressRight = 1 + self.canPressLeft = 1 + self.jumpCount = 0 + self.trampZ = 3.6 + self.trampBounceCount = 0 + self.cameraState = 'Low' + self.updateTick = 0 + self.hitInfo = None + return + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + self.help = DirectLabel(parent=aspect2d, relief=None, pos=(-0.0, 0, -0.8), text=TTLocalizer.TargetGameCountdown, text_scale=0.08, text_fg=(1, 1, 1, 1)) + self.help.hide() + self.fogOver = FogOverlay.FogOverlay(Point3(self.CLOUD_COLOR[0], self.CLOUD_COLOR[1], self.CLOUD_COLOR[2])) + self.scoreboard = DirectFrame(parent=aspect2d, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1.75, 1, 0.75), pos=(0, 0, 0.587)) + self.roundLabel = DirectLabel(parent=self.scoreboard, relief=None, pos=(0, 0, 0.28), text=TTLocalizer.TargetGameBoard % self.round, text_scale=0.08) + av1Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, 0.14), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + av2Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, 0.02), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + av3Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, -0.1), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + av4Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(-0.8, 0, -0.22), text_align=TextNode.ALeft, text=' ', text_scale=0.08) + score1Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, 0.14), text_align=TextNode.ARight, text=' ', text_scale=0.08) + score2Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, 0.02), text_align=TextNode.ARight, text=' ', text_scale=0.08) + score3Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, -0.1), text_align=TextNode.ARight, text=' ', text_scale=0.08) + score4Label = DirectLabel(parent=self.scoreboard, relief=None, pos=(0.8, 0, -0.22), text_align=TextNode.ARight, text=' ', text_scale=0.08) + self.avLabels = [av1Label, + av2Label, + av3Label, + av4Label] + self.scoreLabels = [score1Label, + score2Label, + score3Label, + score4Label] + self.scoreboard.hide() + self.music = base.loadMusic('phase_4/audio/bgm/MG_Diving.mid') + self.sndAmbience = None + self.skyListLow = [] + self.skyListMid = [] + loadBase = 'phase_4/models/minigames/' + self.modelName = 'slingshot_game_desert4.bam' + self.trampoline = loader.loadModel('phase_4/models/minigames/slingshot_game_tramp.bam') + self.trampoline.setBin('ground', 100) + self.trampoline.find('**/tramp_shadow').hide() + self.cacti = loader.loadModel('phase_4/models/minigames/slingshot_game_cacti.bam') + self.cactusA = self.cacti.find('**/cactus_a') + self.cactusB = self.cacti.find('**/cactus_b') + self.findGround = '**/sand_layer1' + self.findSky = '**/blue_sky_layer1' + self.findCloudExtra = '**/cloud_layer1' + self.findCloud = '**/cloud_layer_alpha' + self.findLeftMount = '**/mountain_set_b' + self.findRightMount = '**/mountain_c' + self.findBack = '**/backdrop_layer1' + self.environModel = loader.loadModel(loadBase + self.modelName) + self.skyGN = GeomNode('sky Geometry') + self.skyNode1 = self.environModel.attachNewNode(self.skyGN) + leftMountModel = self.environModel.find(self.findLeftMount) + rightMountModel = self.environModel.find(self.findRightMount) + wrongCloud = self.environModel.find(self.findCloudExtra) + wrongCloud.hide() + backModel = self.environModel.find(self.findBack) + backModel.hide() + leftMountModel.hide() + rightMountModel.hide() + groundModel = self.environModel.find(self.findGround) + groundModel.setBin('ground', 0) + groundModel.setZ(-0.05) + skyModel = self.environModel.find(self.findSky) + skyModel2 = self.environModel.find(self.findCloud) + skyModel3 = self.environModel.attachNewNode('skyLow') + skyModel2.copyTo(skyModel3) + skyModel.setZ(76.0) + skyModel.setColorScale(1.0, 1.0, 1.0, 1.0) + skyModel.setBin('ground', 2) + skyModel2.setZ(63.0) + skyModel2.setDepthWrite(0) + skyModel2.setTransparency(1) + skyModel2.setColorScale(1.0, 1.0, 1.0, 1.0) + skyModel2.setTwoSided(True) + skyModel2.setBin('fixed', 3) + skyModel2.setR(180) + skyModel3.setZ(50.0) + skyModel3.setDepthWrite(0) + skyModel3.setTransparency(1) + skyModel3.setColorScale(1.0, 1.0, 1.0, 1.0) + skyModel3.setTwoSided(True) + skyModel3.setBin('fixed', 4) + self.skyListLow.append(skyModel3) + self.lowSky = skyModel3 + self.environModel.setPos(0, -5.0, 0) + self.environModel.flattenMedium() + self.environModel.setScale(15.0, 15.0, 2.0) + self.dropShadowModel = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadowModel.setColor(0, 0, 0, 0.5) + self.dropShadowModel.flattenMedium() + self.toonDropShadows = [] + self.__textGen = TextNode('targetGame') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + self.powerBar = DirectWaitBar(guiId='launch power bar', pos=(0.0, 0, -0.65), relief=DGG.SUNKEN, frameSize=(-2.0, + 2.0, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=0.25, range=100, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(1.0, 0.0, 0.0, 1.0), text='0%', text_scale=0.26, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05)) + self.power = self.startPower + self.powerBar['value'] = self.power + self.powerBar.hide() + self.rubberBands = [] + self.umbrella = loader.loadModel('phase_4/models/minigames/slingshot_game_umbrellas.bam') + pick = random.randint(0, 3) + self.umbrellaColorSelect[pick] = 0 + tex = loader.loadTexture(DistributedTargetGame.UMBRELLA_TEXTURE_LIST[pick]) + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + self.umbrella.setTexture(tex, 1) + open = self.umbrella.find('**/open_umbrella') + open.hide() + open.setPos(0.1, 0.2, -0.1) + open.setHpr(-90.0, 0.0, 15.0) + closed = self.umbrella.find('**/closed_umbrella') + closed.show() + hands = localAvatar.getRightHands() + ce = CompassEffect.make(NodePath(), CompassEffect.PRot) + closed.node().setEffect(ce) + closed.setHpr(0.0, 100.0, 35.0) + for hand in hands: + self.umbrella.instanceTo(hand) + + self.remoteUmbrellas = {} + self.addSound('wind1', 'target_cloud.mp3', 'phase_4/audio/sfx/') + self.addSound('trampoline', 'target_trampoline_2.mp3', 'phase_4/audio/sfx/') + self.addSound('launch', 'target_launch.mp3', 'phase_4/audio/sfx/') + self.addSound('miss', 'target_Lose.mp3', 'phase_4/audio/sfx/') + self.addSound('score', 'target_happydance.mp3', 'phase_4/audio/sfx/') + self.addSound('impact', 'target_impact_grunt1.mp3', 'phase_4/audio/sfx/') + self.addSound('umbrella', 'target_chute.mp3', 'phase_4/audio/sfx/') + self.addSound('bounce', 'target_impact_only.mp3', 'phase_4/audio/sfx/') + self.flySound = loader.loadSfx('phase_4/audio/sfx/target_wind_fly_loop.wav') + self.flySound.setVolume(0.0) + self.flySound.setPlayRate(1.0) + self.flySound.setLoop(True) + self.flySound.play() + self.rubberSound = loader.loadSfx('phase_4/audio/sfx/target_stretching_aim_loop.mp3') + self.rubberSound.setVolume(0.0) + self.rubberSound.setPlayRate(1.0) + self.rubberSound.setLoop(True) + self.rubberSound.play() + self.flutterSound = loader.loadSfx('phase_4/audio/sfx/target_wind_float_clothloop.wav') + self.flutterSound.setVolume(1.0) + self.flutterSound.setPlayRate(1.0) + self.flutterSound.setLoop(True) + return + + def addSound(self, name, soundName, path = None): + if not hasattr(self, 'soundTable'): + self.soundTable = {} + if path: + self.soundPath = path + soundSource = '%s%s' % (self.soundPath, soundName) + self.soundTable[name] = loader.loadSfx(soundSource) + + def playSound(self, name, volume = 1.0): + if hasattr(self, 'soundTable'): + self.soundTable[name].setVolume(volume) + self.soundTable[name].play() + + def addSkys(self, instance): + low = instance.find('**/skyLow') + mid = instance.find(self.findCloud) + self.skyListLow.append(low) + self.skyListMid.append(mid) + + def setTargetSeed(self, targetSeed, extra = None): + if not self.hasLocalToon: + return + random.seed(targetSeed) + self.pattern = TargetGameGlobals.difficultyPatterns[self.getSafezoneId()] + print 'seed %s' % targetSeed + self.setupTargets() + + def setupTargets(self): + fieldWidth = self.ENVIRON_WIDTH * 3 + fieldLength = self.ENVIRON_LENGTH * 3.7 + self.targetSubParts = [2, + 2, + 2, + 1] + self.targetList = self.pattern[0] + self.targetValue = self.pattern[1] + self.targetSize = self.pattern[2] + self.targetColors = self.pattern[3] + self.targetSubParts = self.pattern[4] + highestValue = 0 + for value in self.targetValue: + if value > highestValue: + highestValue = value + + placeList = [] + self.jumpColor = TargetGameGlobals.colorBlack + self.placeValue = highestValue * 0.5 + self.jumpSize = self.pattern[5] + self.jumpNum = self.pattern[6] + self.accept('enterJump', self.jumpIn) + self.accept('exitJump', self.jumpOut) + self.targets = render.attachNewNode('targetGameTargets') + for typeIndex in range(len(self.targetList)): + for targetIndex in range(self.targetList[typeIndex]): + goodPlacement = 0 + while not goodPlacement: + placeX = random.random() * (fieldWidth * 0.6) - fieldWidth * 0.6 * 0.5 + placeY = (random.random() * 0.6 + (0.0 + 0.4 * (self.placeValue * 1.0 / (highestValue * 1.0)))) * fieldLength + fillSize = self.targetSize[typeIndex] + goodPlacement = checkPlace(placeX, placeY, fillSize, placeList) + + placeList.append((placeX, placeY, fillSize)) + subIndex = self.targetSubParts[typeIndex] + first = 1 + while subIndex: + combinedIndex = typeIndex + subIndex - 1 + targetGN = GeomNode('target Circle') + targetNodePathGeom = self.targets.attachNewNode(targetGN) + targetNodePathGeom.setPos(placeX, placeY, 0) + points = self.targetSize[combinedIndex] / 2 + 20 + order = len(self.targetList) - combinedIndex + 10 + if first: + first = 0 + geo = addCircle(targetGN, self.targetColors[combinedIndex], points, self.targetSize[combinedIndex], order) + else: + thickness = abs(self.targetSize[combinedIndex] - self.targetSize[combinedIndex + 1]) - 0.0 + geo = addRing(targetGN, self.targetColors[combinedIndex], points, self.targetSize[combinedIndex], order, thickness) + targetNodePathGeom.setBin('ground', combinedIndex * 2 + 1) + targetNodePathGeom.setColorScale(self.targetColors[combinedIndex]['Red'], self.targetColors[combinedIndex]['Green'], self.targetColors[combinedIndex]['Blue'], self.targetColors[combinedIndex]['Alpha']) + targetNodePathGeom.setDepthWrite(False) + targetNodePathGeom.setDepthTest(False) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + self.targetsPlaced.append((placeX, + placeY, + combinedIndex, + geo)) + subIndex -= 1 + + for jump in range(self.jumpNum): + normJumpSize = 6.8 + goodPlacement = 0 + while not goodPlacement: + placeX = random.random() * (fieldWidth * 0.6) - fieldWidth * 0.6 * 0.5 + placeY = (random.random() * 0.6 + (0.0 + 0.4 * (self.placeValue * 1.0 / (highestValue * 1.0)))) * fieldLength + fillSize = self.jumpSize + goodPlacement = checkPlace(placeX, placeY, fillSize, placeList) + + placeList.append((placeX, placeY, fillSize)) + target = CollisionSphere(0, 0, 0, self.jumpSize) + target.setTangible(0) + targetNode = CollisionNode('Jump') + targetNode.addSolid(target) + targetGN = GeomNode('target jump Circle') + targetNodePathGeom = self.targets.attachNewNode(targetGN) + trampI = self.trampoline.copyTo(self.targets) + trampI.setPos(placeX, placeY, 0.05) + latScale = self.jumpSize / normJumpSize + trampI.setScale(latScale, latScale, 1.0) + targetNodePath = self.targets.attachNewNode(targetNode) + targetNodePath.setPos(placeX, placeY, 0.0) + targetNodePathGeom.setPos(placeX, placeY, 0.05) + points = self.jumpSize / 2 + 20 + order = 0 + addCircle(targetGN, self.jumpColor, points, self.jumpSize + 0.5, order) + targetNodePathGeom.setColorScale(self.jumpColor['Red'], self.jumpColor['Green'], self.jumpColor['Blue'], 0.25) + targetNodePathGeom.setBin('ground', 20) + targetNodePathGeom.setDepthWrite(True) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + + cactusCount = 30 + for cactus in range(cactusCount): + placeX = random.random() * (fieldWidth * 0.75) - fieldWidth * 0.75 * 0.5 + placeY = 5.0 + random.random() * (fieldLength - 5.0) + targetGN = GeomNode('cactus') + cactus = random.choice([self.cactusA, self.cactusB]) + targetNodePathGeom = self.targets.attachNewNode(targetGN) + targetNodePathGeom = self.targets.attachNewNode(targetGN) + cactusI = cactus.copyTo(self.targets) + cactusI.setPos(placeX, placeY, 0) + cactusI.setHpr(random.random() * 360, 0, 0) + cactusI.setScale(0.5 + random.random()) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.__textGen + del self.toonDropShadows + self.dropShadowModel.removeNode() + del self.dropShadowModel + self.environModel.removeNode() + del self.environModel + self.fogOver.delete() + self.umbrella.removeNode() + del self.umbrella + for umbrella in self.remoteUmbrellas: + self.remoteUmbrellas[umbrella].removeNode() + + self.remoteUmbrellas.clear() + del self.remoteUmbrellas + self.flySound.stop() + del self.flySound + self.rubberSound.stop() + del self.rubberSound + self.flutterSound.stop() + del self.flutterSound + del self.music + del self.sndAmbience + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + if self.targets: + self.targets.remove() + del self.targets + self.scoreboard.destroy() + del self.scoreboard + self.powerBar.destroy() + del self.powerBar + self.help.destroy() + del self.help + if self.localTrack: + self.localTrack.finish() + del self.localTrack + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.arrowKeys = ArrowKeys.ArrowKeys() + toon = base.localAvatar + toon.reparentTo(render) + toon.setAnimState('SitStart', 1.0) + toon.useLOD(self.TOON_LOD) + self.__placeToon(self.localAvId) + self.localStartPos = toon.getPos() + localBand = RubberBand.RubberBand(toon, Point3(0, -1.75, 0), taskPriority=self.UPDATE_RUBBER_BAND_PRIORITY) + tP = self.getToonPlace(self.localAvId) + pos = Point3(tP[0], tP[1] + 10, tP[2]) + localBand.setPos(pos) + self.rubberBands.append(localBand) + toon.dropShadow.hide() + toonPos = base.localAvatar.getPos() + camera.reparentTo(render) + toonPos = self.getAvatar(self.localAvId).getPos() + camera.setPosHpr(toonPos[0], toonPos[1] - 18, toonPos[2] + 10, 0, -15, 0) + base.camLens.setFov(80) + base.camLens.setFar(self.FOGDISTGROUND) + base.setBackgroundColor(self.AMB_COLOR) + self.__fog = Fog('ringGameFog') + if base.wantFog: + self.__fog.setColor(self.AMB_COLOR) + self.__fog.setLinearRange(0.1, self.FOGDISTGROUND) + render.setFog(self.__fog) + self.environNode = render.attachNewNode('environNode') + self.environBlocks = [] + maxI = 4 + self.maxDist = (maxI - 1) * self.ENVIRON_LENGTH + for i in range(-1, maxI + 1): + instance = self.environModel.copyTo(self.environNode) + y = self.ENVIRON_LENGTH * i + instance.setY(y) + self.addSkys(instance) + self.environBlocks.append(instance) + jRange = 3 + for j in range(0, jRange): + instance = self.environModel.copyTo(self.environNode) + x = self.ENVIRON_WIDTH * (j + 1) + instance.setY(y) + instance.setX(-x) + height = random.random() * 5.0 + 5.0 + heading = random.random() * 15.0 - 7.5 + self.addSkys(instance) + self.environBlocks.append(instance) + if j == jRange - 2: + mount = instance.find(self.findLeftMount) + mount.setScale(1.0, 1.1, height) + mount.setBin('ground', 1) + mount.show() + sand = mount.find('**/sand_b') + sand.hide() + mount.setPos(mount.getX() + 8.5, 0, 0.0) + if i >= maxI - 1: + mount.setHpr(310, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() - 2.0, 0, 0.0) + if i == maxI: + mount.hide() + if i == -1: + mount.setHpr(50, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() + 5.0, 3.0, 0.0) + if j == jRange - 1: + sand = instance.find(self.findGround) + sand.hide() + + for j in range(0, jRange): + instance = self.environModel.copyTo(self.environNode) + x = self.ENVIRON_WIDTH * (j + 1) + instance.setY(y) + instance.setX(x) + height = random.random() * 5.0 + 5.0 + heading = random.random() * 15.0 - 7.5 + self.addSkys(instance) + self.environBlocks.append(instance) + if j == jRange - 2: + mount = instance.find(self.findRightMount) + mount.setScale(1.0, 1.1, height) + mount.setBin('ground', 1) + mount.show() + sand = mount.find('**/sand_c') + sand.hide() + mount.setPos(mount.getX() - 8.5, 0, 0.0) + if i >= maxI - 1: + mount.setHpr(50, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() + 2.0, 0, 0.0) + if i == maxI: + mount.hide() + if i == -1: + mount.setHpr(310, 0, 0) + mount.setScale(1.0, 1.5, height * 1.2) + mount.setPos(mount.getX() - 5.0, 3.0, 0.0) + if j == jRange - 1: + sand = instance.find(self.findGround) + sand.hide() + + self.__addToonDropShadow(self.getAvatar(self.localAvId)) + self.__spawnUpdateEnvironTask() + self.__spawnUpdateShadowsTask() + self.__spawnUpdateLocalToonTask() + if self.music: + base.playMusic(self.music, looping=1, volume=0.8) + if None != self.sndAmbience: + base.playSfx(self.sndAmbience, looping=1, volume=0.8) + return + + def reset(self): + toon = base.localAvatar + toon.setAnimState('SitStart', 1.0) + toon.useLOD(self.TOON_LOD) + self.__placeToon(self.localAvId) + toon.dropShadow.hide() + self.__spawnUpdateLocalToonTask() + toonPos = base.localAvatar.getPos() + camera.setPosHpr(toonPos[0], toonPos[1] - 26, toonPos[2] + 10, 0, -15, 0) + base.camLens.setFov(80) + self.resetNums() + + def resetNums(self): + self.stretchX = 0 + self.stretchY = 0 + self.driftX = 0 + self.driftY = 0 + self.driftY = 0 + self.jumpCount = 0 + self.trampBounceCount = 0 + self.cameraState = 'Low' + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + if self.music: + self.music.stop() + if None != self.sndAmbience: + self.sndAmbience.stop() + self.__killUpdateLocalToonTask() + self.__killUpdateShadowsTask() + self.__killUpdateEnvironTask() + self.__killCountDownTask() + del self.soundTable + self.__removeAllToonDropShadows() + render.clearFog() + base.camLens.setFar(ToontownGlobals.DefaultCameraFar) + base.camLens.setFov(ToontownGlobals.DefaultCameraFov) + camera.setHpr(0, 90, 0) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.arrowKeys.destroy() + del self.arrowKeys + for block in self.environBlocks: + del block + + self.environNode.removeNode() + del self.environNode + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + for band in self.rubberBands: + band.delete() + + rubberBands = [] + self.targetsPlaced = [] + return + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.__removeToonDropShadow(self.remoteToons[avId]) + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = i * self.TOON_INITIAL_SPACING + x -= self.TOON_INITIAL_SPACING * (numToons - 1) / 2.0 + pos = self.getToonPlace(avId) + toon.setPosHpr(pos[0], pos[1], pos[2], 0, 0, 0) + + def getToonPlace(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + numToons = float(self.numPlayers) + x = i * self.TOON_INITIAL_SPACING + x -= self.TOON_INITIAL_SPACING * (numToons - 1) / 2.0 + pos = Point3(x + self.placeShift, self.TOON_Y, 1.0) + return pos + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + if not self.isSinglePlayer(): + base.localAvatar.collisionsOff() + cSphere = CollisionSphere(0.0, 0.0, 0.0, TargetGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % self.localAvId) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(TargetGameGlobals.CollideMask) + cSphereNode.setIntoCollideMask(BitMask32.allOff()) + self.cSphereNodePath = base.localAvatar.attachNewNode(cSphereNode) + self.pusher = CollisionHandlerPusher() + self.pusher.addCollider(self.cSphereNodePath, base.localAvatar) + self.pusher.setHorizontal(0) + self.cTrav = CollisionTraverser('DistributedRingGame') + self.cTrav.addCollider(self.cSphereNodePath, self.pusher) + self.remoteToonCollNPs = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + cSphere = CollisionSphere(0.0, 0.0, 0.0, TargetGameGlobals.CollisionRadius) + cSphereNode = CollisionNode('RingGameSphere-%s' % avId) + cSphereNode.addSolid(cSphere) + cSphereNode.setCollideMask(TargetGameGlobals.CollideMask) + cSphereNP = toon.attachNewNode(cSphereNode) + self.remoteToonCollNPs[avId] = cSphereNP + + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + self.__placeToon(avId) + toon.setAnimState('SitStart', 1.0) + toon.useLOD(self.TOON_LOD) + toon.dropShadow.hide() + self.__addToonDropShadow(toon) + band = RubberBand.RubberBand(toon, Point3(0, -1.75, 0), taskPriority=self.UPDATE_RUBBER_BAND_PRIORITY) + tP = self.getToonPlace(avId) + pos = Point3(tP[0], tP[1] + 10, tP[2]) + band.setPos(pos) + self.rubberBands.append(band) + umbrella = self.umbrella.copyTo(render) + self.remoteUmbrellas[avId] = umbrella + test = 0 + kill = 16 + while test == 0 and kill > 0: + kill -= 1 + pick = random.randint(0, 3) + if self.umbrellaColorSelect[pick]: + self.umbrellaColorSelect[pick] = 0 + test = 1 + + tex = loader.loadTexture(DistributedTargetGame.UMBRELLA_TEXTURE_LIST[pick]) + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + umbrella.setTexture(tex, 1) + open = umbrella.find('**/open_umbrella') + open.hide() + open.setPos(0.1, 0.2, -0.1) + open.setHpr(-90.0, 180.0, 0.0) + open.setScale(1.5) + closed = umbrella.find('**/closed_umbrella') + closed.show() + hand = toon.find('**/joint_Rhold') + ce = CompassEffect.make(NodePath(), CompassEffect.PRot) + closed.node().setEffect(ce) + closed.setHpr(0.0, 90.0, 35.0) + umbrella.reparentTo(hand) + toon.startSmooth() + + self.remoteToons = {} + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + self.remoteToons[avId] = toon + + labelIndex = 0 + for avId in self.avIdList: + av = self.getAvatar(avId) + self.avLabels[labelIndex]['text'] = av.getName() + labelIndex += 1 + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('prelaunch') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPrelaunch(self): + self.defineConstants() + self.resetNums() + self.gameFSM.request('launch') + self.powerBar.show() + self.powerBar['value'] = self.startPower + camera.reparentTo(render) + self.__placeToon(self.localAvId) + toonPos = self.getAvatar(self.localAvId).getPos() + newPos = Point3(toonPos[0], toonPos[1] - 25, toonPos[2] + 7) + newHpr = Point3(0, -15, 0) + self.cameraWork = camera.posHprInterval(2.5, newPos, newHpr, blendType='easeInOut') + self.cameraWork.start() + base.camLens.setFov(80) + self.stretchY = self.startPower + self.power = self.startPower + self.scoreboard.hide() + open = self.umbrella.find('**/open_umbrella') + open.hide() + closed = self.umbrella.find('**/closed_umbrella') + closed.show() + self.umbrella.setScale(1.0) + if self.targetMarker: + self.targetMarker.removeNode() + + def exitPrelaunch(self): + pass + + def enterLaunch(self): + self.__spawnUpdatePowerBarTask() + self.__spawnCountDownTask() + self.help.show() + self.help['text'] = TTLocalizer.TargetGameCountHelp + + def exitLaunch(self): + self.cameraWork.finish() + self.__killUpdatePowerBarTask() + self.speedLaunch = self.power + self.powerBar.hide() + self.playSound('launch') + + def enterFly(self): + self.notify.debug('enterFly') + self.__spawnCollisionDetectionTask() + self.__placeToon(self.localAvId) + self.speedForward = self.speedLaunch + self.laterial = -self.launchLaterial * 7 + self.zVel = self.speedForward * 0.5 + toon = base.localAvatar + toon.b_setAnimState('swim', 1.0) + toon.stopBobSwimTask() + toon.dropShadow.hide() + camera.reparentTo(base.localAvatar) + camera.setPosHpr(0, self.CAMERA_Y + self.TOON_Y + 12, 5, 0, -15, 0) + base.camLens.setFov(80) + self.help.show() + self.help['text'] = TTLocalizer.TargetGameFlyHelp + self.rubberSound.setVolume(0.0) + self.rubberSound.setPlayRate(1.0) + self.rubberSound.stop() + + def exitFly(self): + taskMgr.remove(self.END_GAME_WAIT_TASK) + self.__killCollisionDetectionTask() + self.help.hide() + + def enterFall(self): + self.notify.debug('enterLaunch') + toon = base.localAvatar + toon.b_setAnimState('jumpAirborne', 1.0) + toon.dropShadow.hide() + self.speedForward = self.speedForward * 0.5 + self.gravity = 4 + newHpr = Point3(0, -68, 0) + newPos = Point3(0, self.CAMERA_Y + self.TOON_Y + 15, 15) + camera.lerpPosHpr(newPos, newHpr, 2.5, blendType='easeInOut', task=self.FLY2FALL_CAM_TASK) + open = self.umbrella.find('**/open_umbrella') + open.show() + closed = self.umbrella.find('**/closed_umbrella') + closed.hide() + self.umbrella.setHpr(0, 180.0, -25.0) + self.umbrella.setScale(1.5) + self.help.show() + self.help['text'] = TTLocalizer.TargetGameFallHelp + self.playSound('umbrella') + self.flutterSound.play() + + def exitFall(self): + self.flutterSound.stop() + + def enterBouncing(self): + self.notify.debug('enterLaunch') + self.gravity = 12.5 + open = self.umbrella.find('**/open_umbrella') + open.hide() + closed = self.umbrella.find('**/closed_umbrella') + closed.hide() + self.umbrella.setHpr(0, 0, 0) + self.umbrella.setScale(1.0) + toon = base.localAvatar + toon.b_setAnimState('neutral', 1.0) + toon.dropShadow.hide() + self.help.show() + self.help['text'] = TTLocalizer.TargetGameBounceHelp + + def exitBouncing(self): + pass + + def enterScore(self): + + def count(value, list): + count = 0 + for item in list: + if value == item: + count += 1 + + return count + + self.notify.debug('enterScore') + self.__killUpdateLocalToonTask() + self.__spawnGameDoneTask() + self.scoreboard.show() + score = 1 + self.sendUpdate('setScore', [base.localAvatar.getX(), base.localAvatar.getY()]) + topValue = 0 + hitTarget = None + for target in self.targetsPlaced: + radius = self.targetSize[target[2]] + value = self.targetValue[target[2]] + posX = target[0] + posY = target[1] + dx = posX - base.localAvatar.getX() + dy = posY - base.localAvatar.getY() + distance = math.sqrt(dx * dx + dy * dy) + if distance < radius and topValue < value: + topValue = value + hitTarget = target + hitX = posX + hitY = posY + + if hitTarget: + targetGN = GeomNode('target Circle') + targetNodePathGeom = self.targets.attachNewNode(targetGN) + targetNodePathGeom.setPos(hitX, hitY, 0) + addRing(targetGN, TargetGameGlobals.colorBlack, 100, self.targetSize[hitTarget[2]] + 0.5, 0) + targetNodePathGeom.setColorScale(TargetGameGlobals.colorWhite['Red'], TargetGameGlobals.colorWhite['Green'], TargetGameGlobals.colorWhite['Blue'], TargetGameGlobals.colorWhite['Alpha']) + targetNodePathGeom.setBin('ground', hitTarget[2] * 2 + 100) + targetNodePathGeom.setDepthWrite(False) + targetNodePathGeom.setDepthTest(False) + targetNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + self.targetMarker = targetNodePathGeom + score = topValue + self.localTrack = Sequence() + if self.hitInfo == 'OnButt': + self.localTrack.append(ActorInterval(base.localAvatar, 'slip-forward', startFrame=25, endFrame=55)) + self.hitInfo = 'GettingUp' + else: + self.localTrack.append(ActorInterval(base.localAvatar, 'slip-forward', startFrame=50, endFrame=55)) + self.localTrack.append(Wait(0.5)) + if score <= 1: + self.localTrack.append(Parallel(Func(base.localAvatar.b_setAnimState, 'Sad', 1.0), Func(self.playSound, 'miss'))) + else: + self.localTrack.append(Parallel(Func(base.localAvatar.b_setAnimState, 'victory', 1.0), Func(self.playSound, 'score'))) + newHpr = Point3(180, 10, 0) + newPos = Point3(0, -(self.CAMERA_Y + self.TOON_Y + 12), 1) + camera.lerpPosHpr(newPos, newHpr, 5.0, blendType='easeInOut', task=self.SCORE_CAM_TASK) + self.help.hide() + self.localTrack.start() + return + + def exitScore(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + if not self.isSinglePlayer(): + for np in self.remoteToonCollNPs.values(): + np.removeNode() + + del self.remoteToonCollNPs + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + del self.pusher + del self.cTrav + base.localAvatar.collisionsOn() + + def exitCleanup(self): + pass + + def signalDone(self): + self.sendUpdate('setPlayerDone', []) + + def __spawnUpdatePowerBarTask(self): + taskMgr.remove(self.UPDATE_POWERBAR_TASK) + taskMgr.add(self.__updatePowerBarTask, self.UPDATE_POWERBAR_TASK, priority=self.UPDATE_POWER_BAR_PRIORITY) + self.rubberSound.play() + self.bandVolume = 0.0 + + def __killUpdatePowerBarTask(self): + taskMgr.remove(self.UPDATE_POWERBAR_TASK) + taskMgr.remove(self.TOONSTRETCHTASK) + + def __spawnCountDownTask(self): + self.countDown = 10 + self.signalLaunch = 0 + taskMgr.remove(self.UPDATE_COUNTDOWN_TASK) + self.doCountDown() + + def __killCountDownTask(self): + taskMgr.remove(self.UPDATE_COUNTDOWN_TASK) + + def __spawnGameDoneTask(self): + taskMgr.remove(self.GAME_DONE_TASK) + taskMgr.doMethodLater(5.0, self.__gameDone, self.GAME_DONE_TASK) + + def __killUpdateGameDoneTask(self): + taskMgr.remove(self.GAME_DONE_TASK) + + def __spawnUpdateLocalToonTask(self): + self.__initPosBroadcast() + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + taskMgr.add(self.__updateLocalToonTask, self.UPDATE_LOCALTOON_TASK, priority=self.UPDATE_LOCAL_TOON_PRIORITY) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UPDATE_LOCALTOON_TASK) + + def doCountDown(self, task = None): + if self.countDown < 0: + self.signalLaunch = 1 + else: + self.powerBar['text'] = TTLocalizer.TargetGameCountdown % self.countDown + self.countDown -= 1 + taskMgr.doMethodLater(1.0, self.doCountDown, self.UPDATE_COUNTDOWN_TASK) + + def __initPosBroadcast(self): + self.__posBroadcastPeriod = 0.2 + self.__timeSinceLastPosBroadcast = 0.0 + self.__lastPosBroadcast = self.getAvatar(self.localAvId).getPos() + self.__storeStop = 0 + lt = self.getAvatar(self.localAvId) + lt.d_clearSmoothing() + lt.sendCurrentPosition() + + def __posBroadcast(self, dt): + self.__timeSinceLastPosBroadcast += dt + if self.__timeSinceLastPosBroadcast > self.__posBroadcastPeriod: + self.__timeSinceLastPosBroadcast -= self.__posBroadcastPeriod + self.getAvatar(self.localAvId).cnode.broadcastPosHprFull() + + def __spawnUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + taskMgr.add(self.__updateEnvironTask, self.UPDATE_ENVIRON_TASK) + + def __killUpdateEnvironTask(self): + taskMgr.remove(self.UPDATE_ENVIRON_TASK) + + def __updateEnvironTask(self, task): + return + dt = globalClock.getDt() + self.speedForward = self.speedForward - self.speedForward * self.airResistance * dt + t = globalClock.getFrameTime() - self.__timeBase + self.distance = self.distance + dt * self.speedForward + distance = self.distance + distance %= self.ENVIRON_LENGTH + self.environNode.setY(-distance) + return Task.cont + + def __updatePowerBarTask(self, task): + powerUp = 0 + timeDiff = None + if not self.arrowKeys.rightPressed(): + self.canPressRight = 1 + elif self.arrowKeys.rightPressed() and self.canPressRight: + powerUp = 1 + self.canPressRight = 0 + if not self.arrowKeys.leftPressed(): + self.canPressLeft = 1 + elif self.arrowKeys.leftPressed() and self.canPressLeft: + powerUp = 1 + self.canPressLeft = 0 + if self.lastPressTime: + timeDiff = globalClock.getFrameTime() - self.lastPressTime + if powerUp and not self.lastPressTime: + self.lastPressTime = globalClock.getFrameTime() + self.ticker = 0.0 + elif powerUp: + splitTime = abs(timeDiff) + if splitTime < 0.15: + splitTime = 0.15 + self.power += 0.6 / abs(timeDiff) + 5.0 + self.lastPressTime = globalClock.getFrameTime() + self.powerBar['value'] = self.power + if self.signalLaunch: + self.lastPressTime = 1 + self.ticker = 1 + timeDiff = 100000 + if self.lastPressTime: + self.ticker += globalClock.getDt() + if self.ticker >= 0.1: + self.ticker = 0.0 + powerDiv = 0.05 + self.power -= 1.0 + 0.2 * (self.power * powerDiv * (self.power * powerDiv)) + if timeDiff > 0.5: + self.power = self.powerBar['value'] + self.signalLaunch = 0 + if self.power > 120: + self.power = 120 + if self.power < 40: + self.power = 40 + self.gameFSM.request('fly') + self.stretchY = 0.9 * self.stretchY + 0.1 * self.power + self.stretchX = 0.9 * self.stretchX + 0.1 * self.launchLaterial + base.localAvatar.setY(self.localStartPos[1] - self.stretchY * 0.12) + base.localAvatar.setX(self.localStartPos[0] + self.stretchX) + dt = globalClock.getDt() + self.__posBroadcast(dt) + stretchDiff = abs(self.stretchY - self.power) * 0.2 + self.bandVolume += stretchDiff + self.bandVolume -= globalClock.getDt() + self.rubberSound.setVolume(self.bandVolume) + self.rubberSound.setPlayRate(1.0 + self.stretchY / 120.0) + return task.cont + + def __updateLocalToonTask(self, task): + stateName = None + if self.gameFSM.getCurrentState(): + stateName = self.gameFSM.getCurrentState().getName() + toonPos = self.getAvatar(self.localAvId).getPos() + dt = globalClock.getDt() + if stateName == 'fall': + self.speedForward = self.speedForward - self.speedForward * self.umbrellaResistance * dt + self.laterial = self.laterial - self.laterial * self.umbrellaResistance * dt + if toonPos[2] > 350.0: + diff = toonPos[2] - 350.0 + self.speedForward += -diff * dt * 0.05 + else: + self.speedForward = self.speedForward - self.speedForward * self.airResistance * dt + self.laterial = self.laterial - self.laterial * self.airResistance * dt + t = globalClock.getFrameTime() - self.__timeBase + self.distance = self.distance + dt * self.speedForward + if self.distance > self.maxDist: + self.distance = self.maxDist + if stateName in ['fall', 'bouncing']: + if self.distance < 15.0: + self.distance = 15.0 + pos = [toonPos[0], self.distance, toonPos[2]] + if stateName in ['fly', 'fall']: + pos[0] += dt * self.laterial + liftMult = 0 + if stateName == 'fly': + if self.arrowKeys.upPressed(): + pass + if self.arrowKeys.downPressed(): + self.gameFSM.request('fall') + elif stateName == 'fall': + if self.driftX > self.TOONXZ_SPEED: + self.driftX = self.TOONXZ_SPEED + if self.driftX < -self.TOONXZ_SPEED: + self.driftX = -self.TOONXZ_SPEED + if self.driftY > self.TOONXZ_SPEED: + self.driftY = self.TOONXZ_SPEED + if self.driftY < -self.TOONXZ_SPEED: + self.driftY = -self.TOONXZ_SPEED + if self.arrowKeys.rightPressed(): + self.driftX += self.TOONXZ_SPEED * dt + if self.arrowKeys.leftPressed(): + self.driftX -= self.TOONXZ_SPEED * dt + if self.arrowKeys.upPressed(): + self.driftY += self.TOONXZ_SPEED * dt + if self.arrowKeys.downPressed(): + self.driftY -= self.TOONXZ_SPEED * dt + pos[0] += self.driftX * dt + pos[1] += self.driftY * dt + self.distance = pos[1] + liftMult = 1 + elif stateName == 'bouncing': + pos[0] += self.driftX * dt + pos[1] += self.driftY * dt + self.distance = pos[1] + else: + xVel = 0.0 + if self.arrowKeys.leftPressed(): + xVel -= self.TOONXZ_SPEED + if self.arrowKeys.rightPressed(): + xVel += self.TOONXZ_SPEED + self.launchLaterial += xVel * dt + if self.launchLaterial < -TargetGameGlobals.MAX_LAT: + self.launchLaterial = -TargetGameGlobals.MAX_LAT + if self.launchLaterial > TargetGameGlobals.MAX_LAT: + self.launchLaterial = TargetGameGlobals.MAX_LAT + pos[0] = self.getToonPlace(self.localAvId)[0] - self.launchLaterial + lift = (self.speedForward - self.speedStall) / self.speedStall * liftMult + onScreenDebug.removeAllWithPrefix(self.getDisplayPrefix()) + onScreenDebug.add('%s Vel' % self.getDisplayPrefix(), self.zVel) + onScreenDebug.add('%s Lift' % self.getDisplayPrefix(), lift) + onScreenDebug.add('%s Gravity' % self.getDisplayPrefix(), self.gravity) + onScreenDebug.add('%s Pos' % self.getDisplayPrefix(), pos[2]) + onScreenDebug.add('%s Drifty' % self.getDisplayPrefix(), self.driftY) + if lift < 0: + lift = 0 + upforce = lift * 10.0 - self.gravity + self.zVel += (lift - self.gravity) * dt + pos[2] += self.zVel * dt + if pos[2] < self.trampZ: + if self.jumpCount: + self.playSound('trampoline') + rebound = 0.75 + if self.trampBounceCount >= 1: + rebound = 0.5 + self.gameFSM.request('bouncing') + pos[2] = self.trampZ + if self.zVel != 0: + signZ = self.zVel / abs(self.zVel) + else: + signZ = 1 + self.zVel = -(self.zVel * rebound - signZ * 1.0) + self.trampBounceCount += 1 + if abs(self.zVel) < 1.0: + self.zVel = 0 + toon = base.localAvatar + if stateName in ['fly', 'fall', 'bouncing']: + toon.b_setAnimState('neutral', 1.0) + toon.dropShadow.hide() + self.gameFSM.request('score') + elif pos[2] < 0.1: + toon = base.localAvatar + if stateName == 'fall' or stateName == 'fly': + self.gameFSM.request('bouncing') + if stateName == 'fly': + ivolume = math.sqrt(abs(self.zVel) / 40.0) + self.hitInfo = 'OnButt' + self.localTrack = ActorInterval(toon, 'slip-forward', startFrame=12, endFrame=24) + self.localTrack.start() + self.playSound('impact', ivolume) + self.playSound('bounce', ivolume) + elif stateName == 'fall' or stateName == 'bouncing': + ivolume = math.sqrt(abs(self.zVel) / 40.0) + if ivolume > 0.4: + if self.hitInfo == 'OnButt': + self.localTrack = ActorInterval(toon, 'slip-forward', startFrame=13, endFrame=24) + self.localTrack.start() + else: + self.localTrack = ActorInterval(toon, 'jump-land', startFrame=6, endFrame=21) + self.localTrack.start() + self.playSound('bounce', ivolume) + pos[2] = 0.1 + rebound = 0.5 + self.speedForward = self.speedForward * rebound + self.driftX = self.driftX * rebound + self.driftY = self.driftY * rebound + toon = base.localAvatar + if self.zVel != 0: + signZ = self.zVel / abs(self.zVel) + else: + signZ = 1 + self.zVel = -(self.zVel * rebound - signZ * 1.0) + if abs(self.zVel) < 1.0: + self.zVel = 0 + if stateName == 'fly' or stateName == 'bouncing': + self.gameFSM.request('score') + if pos[0] > TargetGameGlobals.MAX_FIELD_SPAN: + pos[0] = TargetGameGlobals.MAX_FIELD_SPAN + self.laterial = 0 + if pos[0] < -TargetGameGlobals.MAX_FIELD_SPAN: + pos[0] = -TargetGameGlobals.MAX_FIELD_SPAN + self.laterial = 0 + self.getAvatar(self.localAvId).setPos(pos[0], pos[1], pos[2]) + if hasattr(self, 'cTrav'): + self.cTrav.traverse(render) + if stateName in ['fly', 'fall', 'bouncing']: + self.__posBroadcast(dt) + visZ = camera.getZ(render) + lowHeight = 100 + lowRange = 20 + opacityLow = 1 + baseOpLow = 0.6 + baseTransLow = 1.0 - baseOpLow + if visZ < lowHeight - lowRange or visZ > lowHeight + lowRange: + transLow = 1.0 + else: + dif = abs(visZ - lowHeight) + transLow = dif / lowRange + for item in self.skyListLow: + item.setColorScale(1.0, 1.0, 1.0, transLow * baseOpLow) + + midHeight = 126 + midRange = 20 + opacityMid = 1 + baseOpMid = 1.0 + baseTransMid = 1.0 - baseOpMid + if visZ < midHeight - midRange or visZ > midHeight + midRange: + transMid = 1.0 + else: + dif = abs(visZ - midHeight) + transMid = dif / midRange + for item in self.skyListMid: + item.setColorScale(1.0, 1.0, 1.0, transMid * baseOpMid) + + minTrans = min(1.0, transMid, transLow) + fogVis = 1.0 - minTrans + fogColor = self.AMB_COLOR * minTrans + self.CLOUD_COLOR * fogVis + fogRange = minTrans * self.FOGDISTGROUND + fogVis * self.FOGDISTCLOUD + newCamState = 'Low' + if visZ > midHeight: + newCamState = 'High' + if self.cameraState != newCamState: + self.cameraState = newCamState + self.cloudRendering('High') + elif visZ > lowHeight: + newCamState = 'Mid' + if self.cameraState != newCamState: + if self.cameraState == 'Low': + self.playSound('wind1') + self.cameraState = newCamState + self.cloudRendering('Mid') + else: + newCamState = 'Low' + if self.cameraState != newCamState: + self.cameraState = newCamState + self.cloudRendering('Low') + overlayOp = 0.5 + self.fogOver.setOpacity(fogVis * overlayOp) + vol = (self.speedForward + abs(self.zVel)) / 120.0 + self.flySound.setVolume(vol) + self.flySound.setPlayRate(1.0 + abs(self.zVel) * 0.005) + if self.updateTick % 5 == 0: + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + state = toon.animFSM.getCurrentState().getName() + umbrella = self.remoteUmbrellas.get(avId) + if umbrella: + if state == 'Sit': + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.show() + elif state == 'swim': + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.show() + elif state == 'jumpAirborne': + open = umbrella.find('**/open_umbrella') + open.show() + closed = umbrella.find('**/closed_umbrella') + closed.hide() + elif state == 'neutral': + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.hide() + else: + open = umbrella.find('**/open_umbrella') + open.hide() + closed = umbrella.find('**/closed_umbrella') + closed.hide() + + self.updateTick += 1 + return Task.cont + + def cloudRendering(self, layer): + if layer == 'Low': + for item in self.skyListMid: + item.setBin('fixed', 3) + + for item in self.skyListLow: + item.setBin('fixed', 4) + + elif layer == 'Mid': + for item in self.skyListMid: + item.setBin('fixed', 3) + + for item in self.skyListLow: + item.setBin('fixed', 3) + + elif layer == 'High': + for item in self.skyListMid: + item.setBin('fixed', 4) + + for item in self.skyListLow: + item.setBin('fixed', 3) + + def __addDropShadow_INTERNAL(self, object, scale_x, scale_y, scale_z, list): + shadow = self.dropShadowModel.copyTo(render) + shadow.setPos(0, self.CAMERA_Y, -100) + shadow.setBin('shadow', 100) + shadow.setDepthWrite(False) + shadow.setDepthTest(True) + shadow.setScale(scale_x, scale_y, scale_z) + list.append([shadow, object]) + + def __removeDropShadow_INTERNAL(self, object, list): + for i in range(len(list)): + entry = list[i] + if entry[1] == object: + entry[0].removeNode() + list.pop(i) + return + + self.notify.warning('parent object ' + str(object) + ' not found in drop shadow list!') + + def __addToonDropShadow(self, object): + self.__addDropShadow_INTERNAL(object, 0.5, 0.5, 0.5, self.toonDropShadows) + + def __removeToonDropShadow(self, object): + self.__removeDropShadow_INTERNAL(object, self.toonDropShadows) + + def __removeAllToonDropShadows(self): + for entry in self.toonDropShadows: + entry[0].removeNode() + + self.toonDropShadows = [] + + def __spawnUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + taskMgr.add(self.__updateShadowsTask, self.UPDATE_SHADOWS_TASK, priority=self.UPDATE_SHADOWS_PRIORITY) + + def __killUpdateShadowsTask(self): + taskMgr.remove(self.UPDATE_SHADOWS_TASK) + + def __updateShadowsTask(self, task): + list = self.toonDropShadows + for entry in list: + object = entry[1] + y = object.getY(render) + x = object.getX(render) + if self.jumpCount and object == base.localAvatar: + z = self.trampZ + self.SHADOW_Z_OFFSET + else: + z = 0.0 + self.SHADOW_Z_OFFSET + shadow = entry[0] + shadow.setPos(x, y, z) + + return Task.cont + + def __spawnCollisionDetectionTask(self): + self.__ringGroupsPassed = 0 + taskMgr.remove(self.COLLISION_DETECTION_TASK) + taskMgr.add(self.__collisionDetectionTask, self.COLLISION_DETECTION_TASK, priority=self.COLLISION_DETECTION_PRIORITY) + + def __killCollisionDetectionTask(self): + taskMgr.remove(self.COLLISION_DETECTION_TASK) + + def __collisionDetectionTask(self, task): + return Task.cont + + def __endGameDolater(self, task): + self.gameOver() + return Task.done + + def setTimeBase(self, timestamp): + if not self.hasLocalToon: + return + self.__timeBase = globalClockDelta.networkToLocalTime(timestamp) + + def setColorIndices(self, a, b, c, d): + if not self.hasLocalToon: + return + self.colorIndices = [a, + b, + c, + d] + + def getDisplayPrefix(self): + return 'TargetGame' + + def __gameDone(self, task = None): + self.signalDone() + return task.done + + def setGameDone(self, extra = None): + self.gameOver() + + def setRoundDone(self): + if not self.hasLocalToon: + return + self.round += 1 + self.roundLabel['text'] = TTLocalizer.TargetGameBoard % self.round + self.reset() + self.gameFSM.request('prelaunch') + + def setSingleScore(self, score, avId): + if not self.hasLocalToon: + return + for existIndex in range(len(self.avIdList)): + if self.avIdList[existIndex] == avId: + self.scoreLabels[existIndex]['text'] = '%s' % score + + def jumpIn(self, colEntry): + self.jumpCount += 1 + + def jumpOut(self, colEntry): + self.jumpCount -= 1 + if self.jumpCount < 0: + self.jumpCount = 0 diff --git a/toontown/minigame/DistributedTargetGameAI.py b/toontown/minigame/DistributedTargetGameAI.py new file mode 100644 index 0000000..0550452 --- /dev/null +++ b/toontown/minigame/DistributedTargetGameAI.py @@ -0,0 +1,231 @@ +from DistributedMinigameAI import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import TargetGameGlobals +import random +import types + +def checkPlace(placeX, placeY, fillSize, placeList): + goodPlacement = 1 + for place in placeList: + distance = math.sqrt((place[0] - placeX) * (place[0] - placeX) + (place[1] - placeY) * (place[1] - placeY)) + distance = distance - (fillSize + place[2]) + if distance <= 0.0: + goodPlacement = 0 + break + + return goodPlacement + + +class DistributedTargetGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedTargetGameAI_initialized + except: + self.DistributedTargetGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTargetGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['fly']), + State.State('fly', self.enterFly, self.exitFly, ['cleanup', 'resetRound']), + State.State('resetRound', self.enterResetRound, self.exitResetRound, ['cleanup', 'fly']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.__timeBase = globalClockDelta.localToNetworkTime(globalClock.getRealTime()) + self.round = 2 + self.barrierScore = None + self.scoreTrack = [] + + return + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + del self.scoreTrack + if hasattr(self, 'barrierScore'): + if self.barrierScore: + self.barrierScore.cleanup() + del self.barrierScore + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.sendUpdate('setTrolleyZone', [self.trolleyZone]) + DistributedMinigameAI.setGameReady(self) + import time + random.seed(time.time()) + seed = int(random.random() * 4000.0) + self.sendUpdate('setTargetSeed', [seed]) + random.seed(seed) + self.setupTargets() + + def setupTargets(self): + fieldWidth = TargetGameGlobals.ENVIRON_WIDTH * 3 + fieldLength = TargetGameGlobals.ENVIRON_LENGTH * 3.7 + self.pattern = TargetGameGlobals.difficultyPatterns[self.getSafezoneId()] + self.targetList = self.pattern[0] + self.targetValue = self.pattern[1] + self.targetSize = self.pattern[2] + self.targetColors = self.pattern[3] + self.targetSubParts = self.pattern[4] + highestValue = 0 + for value in self.targetValue: + if value > highestValue: + highestValue = value + + self.placeValue = highestValue * 0.5 + self.targetsPlaced = [] + placeList = [] + for typeIndex in range(len(self.targetList)): + for targetIndex in range(self.targetList[typeIndex]): + goodPlacement = 0 + while not goodPlacement: + placeX = random.random() * (fieldWidth * 0.6) - fieldWidth * 0.6 * 0.5 + placeY = (random.random() * 0.6 + (0.0 + 0.4 * (self.placeValue * 1.0 / (highestValue * 1.0)))) * fieldLength + fillSize = self.targetSize[typeIndex] + goodPlacement = checkPlace(placeX, placeY, fillSize, placeList) + + placeList.append((placeX, placeY, fillSize)) + subIndex = self.targetSubParts[typeIndex] + while subIndex: + combinedIndex = typeIndex + subIndex - 1 + self.targetsPlaced.append((placeX, placeY, combinedIndex)) + subIndex -= 1 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('fly') + + def setScore(self, scoreX, scoreY, other = None): + avId = self.air.getAvatarIdFromSender() + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'RingGameAI.setScore: invalid avId') + return + topValue = 0 + hitTarget = None + for target in self.targetsPlaced: + radius = self.targetSize[target[2]] + value = self.targetValue[target[2]] + posX = target[0] + posY = target[1] + dx = posX - scoreX + dy = posY - scoreY + distance = math.sqrt(dx * dx + dy * dy) + if distance < radius and topValue < value: + topValue = value + hitTarget = target + hitX = posX + hitY = posY + + if self.scoreDict[avId] < topValue: + self.scoreDict[avId] = topValue + self.sendUpdate('setSingleScore', [topValue, avId]) + return + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def getTimeBase(self): + return self.__timeBase + + def enterFly(self): + self.notify.debug('enterFly') + self.barrierScore = ToonBarrier('waitClientsScore', self.uniqueName('waitClientsScore'), self.avIdList, 120, self.allAvatarsScore, self.handleTimeout) + + def exitFly(self): + pass + + def handleTimeout(self, other = None): + pass + + def allAvatarsScore(self, other = None): + if self.round == 0: + self.gameOver() + else: + self.round -= 1 + self.gameFSM.request('resetRound') + + def getScoreList(self): + scoreList = [0, + 0, + 0, + 0] + avList = [0, + 0, + 0, + 0] + scoreIndex = 0 + for avId in self.scoreDict.keys(): + scoreList[scoreIndex] = self.scoreDict[avId] + avList[scoreIndex] = avId + scoreIndex += 1 + + return scoreList + + def enterResetRound(self): + scoreList = self.getScoreList() + self.scoreTrack.append(scoreList) + self.sendUpdate('setRoundDone', []) + self.barrierScore.cleanup() + del self.barrierScore + taskMgr.doMethodLater(0.1, self.gotoFly, self.taskName('roundReset')) + + def exitResetRound(self): + pass + + def gotoFly(self, extra = None): + if hasattr(self, 'gameFSM'): + self.gameFSM.request('fly') + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def setPlayerDone(self, other = None): + if not hasattr(self, 'barrierScore') or self.barrierScore == None: + return + avId = self.air.getAvatarIdFromSender() + self.barrierScore.clear(avId) + for avId in self.stateDict.keys(): + if self.stateDict[avId] == EXITED: + self.barrierScore.clear(avId) + + return + + def gameOver(self): + self.notify.debug('gameOver') + for entry in self.scoreDict: + if self.scoreDict[entry] == 0: + self.scoreDict[entry] = 1 + + self.scoreTrack.append(self.getScoreList()) + statMessage = 'MiniGame Stats : Target Game' + '\nScores' + '%s' % self.scoreTrack + '\nAvIds' + '%s' % self.scoreDict.keys() + '\nSafeZone' + '%s' % self.getSafezoneId() + self.air.writeServerEvent('MiniGame Stats', None, statMessage) + self.sendUpdate('setGameDone', []) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + return + + def hasScoreMult(self): + return 0 diff --git a/toontown/minigame/DistributedTravelGame.py b/toontown/minigame/DistributedTravelGame.py new file mode 100644 index 0000000..4984ccd --- /dev/null +++ b/toontown/minigame/DistributedTravelGame.py @@ -0,0 +1,1011 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import GlobalDialogColor +from DistributedMinigame import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +import TravelGameGlobals +import math +from pandac.PandaModules import rad2Deg +from toontown.toontowngui import TTDialog +from direct.interval.IntervalGlobal import * +import VoteResultsPanel +import VoteResultsTrolleyPanel +IconDict = {ToontownGlobals.RaceGameId: 'mg_trolley_sign_race', + ToontownGlobals.CannonGameId: 'mg_trolley_sign_cannon', + ToontownGlobals.TagGameId: 'mg_trolley_sign_tag', + ToontownGlobals.PatternGameId: 'mg_trolley_sign_minnie', + ToontownGlobals.RingGameId: 'mg_trolley_sign_ring', + ToontownGlobals.MazeGameId: 'mg_trolley_sign_maze', + ToontownGlobals.TugOfWarGameId: 'mg_trolley_sign_tugawar', + ToontownGlobals.CatchGameId: 'mg_trolley_sign_catch', + ToontownGlobals.DivingGameId: 'mg_trolley_sign_dive', + ToontownGlobals.TargetGameId: 'mg_trolley_sign_umbrella', + ToontownGlobals.PairingGameId: 'mg_trolley_sign_card', + ToontownGlobals.VineGameId: 'mg_trolley_sign_vine', + ToontownGlobals.IceGameId: 'mg_trolley_sign_ice', + ToontownGlobals.PhotoGameId: 'mg_trolley_sign_photo', + ToontownGlobals.TwoDGameId: 'mg_trolley_sign_2d', + ToontownGlobals.CogThiefGameId: 'mg_trolley_sign_theif'} +MinigameNameDict = {ToontownGlobals.RaceGameId: TTLocalizer.RaceGameTitle, + ToontownGlobals.CannonGameId: TTLocalizer.CannonGameTitle, + ToontownGlobals.TagGameId: TTLocalizer.TagGameTitle, + ToontownGlobals.PatternGameId: TTLocalizer.PatternGameTitle, + ToontownGlobals.RingGameId: TTLocalizer.RingGameTitle, + ToontownGlobals.MazeGameId: TTLocalizer.MazeGameTitle, + ToontownGlobals.TugOfWarGameId: TTLocalizer.TugOfWarGameTitle, + ToontownGlobals.CatchGameId: TTLocalizer.CatchGameTitle, + ToontownGlobals.DivingGameId: TTLocalizer.DivingGameTitle, + ToontownGlobals.TargetGameId: TTLocalizer.TargetGameTitle, + ToontownGlobals.PairingGameId: TTLocalizer.PairingGameTitle, + ToontownGlobals.VineGameId: TTLocalizer.VineGameTitle, + ToontownGlobals.TravelGameId: TTLocalizer.TravelGameTitle, + ToontownGlobals.IceGameId: TTLocalizer.IceGameTitle, + ToontownGlobals.PhotoGameId: TTLocalizer.PhotoGameTitle, + ToontownGlobals.TwoDGameId: TTLocalizer.TwoDGameTitle, + ToontownGlobals.CogThiefGameId: TTLocalizer.CogThiefGameTitle} + +def makeLabel(itemName, itemNum, *extraArgs): + intVersion = int(itemName) + if intVersion < 0: + textColor = Vec4(0, 0, 1, 1) + intVersion = -intVersion + elif intVersion == 0: + textColor = Vec4(0, 0, 0, 1) + else: + textColor = Vec4(1, 0, 0, 1) + return DirectLabel(text=str(intVersion), text_fg=textColor, relief=DGG.RIDGE, frameSize=(-1.2, + 1.2, + -0.225, + 0.8), scale=1.0) + + +def map3dToAspect2d(node, point): + p3 = base.cam.getRelativePoint(node, point) + p2 = Point2() + if not base.camLens.project(p3, p2): + return None + r2d = Point3(p2[0], 0, p2[1]) + a2d = aspect2d.getRelativePoint(render2d, r2d) + return a2d + + +def invertTable(table): + index = {} + for key in table.keys(): + value = table[key] + if not index.has_key(value): + index[value] = key + + return index + + +class DistributedTravelGame(DistributedMinigame): + notify = directNotify.newCategory('DistributedTravelGame') + idToNames = MinigameNameDict + TrolleyMoveDuration = 3 + UseTrolleyResultsPanel = True + FlyCameraUp = True + FocusOnTrolleyWhileMovingUp = False + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTravelGame', [State.State('off', self.enterOff, self.exitOff, ['inputChoice']), + State.State('inputChoice', self.enterInputChoice, self.exitInputChoice, ['waitServerChoices', 'displayVotes', 'cleanup']), + State.State('waitServerChoices', self.enterWaitServerChoices, self.exitWaitServerChoices, ['displayVotes', 'cleanup']), + State.State('displayVotes', self.enterDisplayVotes, self.exitDisplayVotes, ['moveTrolley', 'cleanup']), + State.State('moveTrolley', self.enterMoveTrolley, self.exitMoveTrolley, ['inputChoice', 'winMovie', 'cleanup']), + State.State('winMovie', self.enterWinMovie, self.exitWinMovie, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.currentVotes = {} + self.cameraTopView = (100, -20, 280, 0, -89, 0) + self.timer = None + self.timerStartTime = None + self.currentSwitch = 0 + self.destSwitch = 0 + self.minigameLabels = [] + self.minigameIcons = [] + self.bonusLabels = [] + self.trolleyAwaySfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_away.mp3') + self.trolleyBellSfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_bell.mp3') + self.turntableRotateSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_turntble_rotate_2.mp3') + self.wonGameSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_bonus.mp3') + self.lostGameSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_no_bonus_2.mp3') + self.noWinnerSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_no_bonus.mp3') + self.boardIndex = 0 + self.avNames = [] + self.disconnectedAvIds = [] + return + + def getTitle(self): + return TTLocalizer.TravelGameTitle + + def getInstructions(self): + return TTLocalizer.TravelGameInstructions + + def getMaxDuration(self): + return 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.gameBoard = loader.loadModel('phase_4/models/minigames/toon_cannon_gameground') + self.gameBoard.setPosHpr(100, 0, 0, 0, 0, 0) + self.gameBoard.setScale(1.0) + station = loader.loadModel('phase_4/models/modules/trolley_station_TT.bam') + self.trolleyCar = station.find('**/trolley_car') + self.trolleyCar.reparentTo(hidden) + self.trolleyCarOrigPos = self.trolleyCar.getPos() + self.trolleyCarOrigHpr = self.trolleyCar.getHpr() + self.trolleyCar.setPosHpr(0, 0, 0, 0, 0, 0) + self.trolleyCar.setScale(1.0) + self.trolleyCar.setX(self.trolleyCar.getX() - TravelGameGlobals.xInc) + station.removeNode() + self.keys = self.trolleyCar.findAllMatches('**/key') + self.numKeys = self.keys.getNumPaths() + self.keyInit = [] + self.keyRef = [] + for i in range(self.numKeys): + key = self.keys[i] + key.setTwoSided(1) + ref = self.trolleyCar.attachNewNode('key' + `i` + 'ref') + ref.iPosHpr(key) + self.keyRef.append(ref) + self.keyInit.append(key.getTransform()) + + self.frontWheels = self.trolleyCar.findAllMatches('**/front_wheels') + self.numFrontWheels = self.frontWheels.getNumPaths() + self.frontWheelInit = [] + self.frontWheelRef = [] + for i in range(self.numFrontWheels): + wheel = self.frontWheels[i] + ref = self.trolleyCar.attachNewNode('frontWheel' + `i` + 'ref') + ref.iPosHpr(wheel) + self.frontWheelRef.append(ref) + self.frontWheelInit.append(wheel.getTransform()) + + self.backWheels = self.trolleyCar.findAllMatches('**/back_wheels') + self.numBackWheels = self.backWheels.getNumPaths() + self.backWheelInit = [] + self.backWheelRef = [] + for i in range(self.numBackWheels): + wheel = self.backWheels[i] + ref = self.trolleyCar.attachNewNode('backWheel' + `i` + 'ref') + ref.iPosHpr(wheel) + self.backWheelRef.append(ref) + self.backWheelInit.append(wheel.getTransform()) + + trolleyAnimationReset = Func(self.resetAnimation) + self.trainSwitches = {} + self.trainTracks = {} + self.tunnels = {} + self.extraTrainTracks = [] + turnTable = loader.loadModel('phase_4/models/minigames/trolley_game_turntable') + minPoint = Point3(0, 0, 0) + maxPoint = Point3(0, 0, 0) + turnTable.calcTightBounds(minPoint, maxPoint) + self.fullLength = maxPoint[0] + for key in TravelGameGlobals.BoardLayouts[self.boardIndex].keys(): + info = TravelGameGlobals.BoardLayouts[self.boardIndex][key] + switchModel = turnTable.find('**/turntable1').copyTo(render) + switchModel.setPos(*info['pos']) + switchModel.reparentTo(hidden) + self.trainSwitches[key] = switchModel + zAdj = 0 + for otherSwitch in info['links']: + info2 = TravelGameGlobals.BoardLayouts[self.boardIndex][otherSwitch] + x1, y1, z1 = info['pos'] + x2, y2, z2 = info2['pos'] + linkKey = (key, otherSwitch) + trainTrack = self.loadTrainTrack(x1, y1, x2, y2, zAdj) + trainTrack.reparentTo(hidden) + self.trainTracks[linkKey] = trainTrack + zAdj += 0.005 + + rootInfo = TravelGameGlobals.BoardLayouts[self.boardIndex][0] + rootX, rootY, rootZ = rootInfo['pos'] + startX = rootX - TravelGameGlobals.xInc + trainTrack = self.loadTrainTrack(startX, rootY, rootX, rootY) + self.extraTrainTracks.append(trainTrack) + tunnelX = None + for key in TravelGameGlobals.BoardLayouts[self.boardIndex].keys(): + if self.isLeaf(key): + info = TravelGameGlobals.BoardLayouts[self.boardIndex][key] + switchX, switchY, switchZ = info['pos'] + endX = switchX + TravelGameGlobals.xInc + trainTrack = self.loadTrainTrack(switchX, switchY, endX, switchY) + self.extraTrainTracks.append(trainTrack) + tempModel = loader.loadModel('phase_4/models/minigames/trolley_game_turntable') + tunnel = tempModel.find('**/tunnel1') + tunnel.reparentTo(render) + tempModel.removeNode() + if not tunnelX: + minTrackPoint = Point3(0, 0, 0) + maxTrackPoint = Point3(0, 0, 0) + trainTrack.calcTightBounds(minTrackPoint, maxTrackPoint) + tunnelX = maxTrackPoint[0] + tunnel.setPos(tunnelX, switchY, 0) + tunnel.wrtReparentTo(trainTrack) + self.tunnels[key] = tunnel + + turnTable.removeNode() + self.loadGui() + self.introMovie = self.getIntroMovie() + self.music = base.loadMusic('phase_4/audio/bgm/MG_Travel.mid') + self.flashWinningBeansTrack = None + return + + def loadTrainTrack(self, x1, y1, x2, y2, zAdj = 0): + turnTable = loader.loadModel('phase_4/models/minigames/trolley_game_turntable') + trainPart = turnTable.find('**/track_a2') + trackHeight = 0.03 + trainTrack = render.attachNewNode('trainTrack%d%d%d%d' % (x1, + y1, + x2, + y2)) + trainTrack.setPos(x1, y1, trackHeight) + xDiff = abs(x2 - x1) + yDiff = abs(y2 - y1) + angleInRadians = math.atan((float(y2) - y1) / (x2 - x1)) + angle = rad2Deg(angleInRadians) + desiredLength = math.sqrt(xDiff * xDiff + yDiff * yDiff) + lengthToGo = desiredLength + partIndex = 0 + lengthCovered = 0 + while lengthToGo > self.fullLength / 2.0: + onePart = trainPart.copyTo(trainTrack) + onePart.setX(lengthCovered) + lengthToGo -= self.fullLength + lengthCovered += self.fullLength + + trainTrack.setH(angle) + newX = x1 + (x2 - x1) / 2.0 + newY = y1 + (y2 - y1) / 2.0 + trainTrack.setPos(x1, y1, trackHeight + zAdj) + turnTable.removeNode() + return trainTrack + + def loadGui(self): + scoreText = [str(self.currentVotes[self.localAvId])] + self.gui = DirectFrame() + self.remainingVotesFrame = DirectFrame(parent=self.gui, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=GlobalDialogColor, geom_scale=(7, 1, 1), pos=(-0.9, 0, 0.8), scale=0.1, text=TTLocalizer.TravelGameRemainingVotes, text_align=TextNode.ALeft, text_scale=TTLocalizer.DTGremainingVotesFrame, text_pos=(-3.4, -0.1, 0.0)) + self.localVotesRemaining = DirectLabel(parent=self.remainingVotesFrame, relief=None, text=scoreText, text_fg=VBase4(0, 0.5, 0, 1), text_align=TextNode.ARight, text_scale=0.7, pos=(3.2, 0, -0.15)) + guiModel = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.choiceFrame = DirectFrame(parent=self.gui, relief=None, pos=(-0.55, 0, -0.85), image=DGG.getDefaultDialogGeom(), image_scale=(1.4, 1, 0.225), image_color=GlobalDialogColor) + self.useLabel = DirectLabel(text=TTLocalizer.TravelGameUse, parent=self.choiceFrame, pos=(-0.59, 0, -0.01), text_scale=TTLocalizer.DTGuseLabel, relief=None) + self.votesPeriodLabel = DirectLabel(text=TTLocalizer.TravelGameVotesWithPeriod, parent=self.choiceFrame, pos=(-0.21, 0, -0.01), text_scale=TTLocalizer.DTGvotesPeriodLabel, relief=None, text_align=TextNode.ALeft) + self.votesToGoLabel = DirectLabel(text=TTLocalizer.TravelGameVotesToGo, parent=self.choiceFrame, pos=(-0.21, 0, -0.01), text_scale=TTLocalizer.DTGvotesToGoLabel, relief=None, text_align=TextNode.ALeft) + self.upLabel = DirectLabel(text=TTLocalizer.TravelGameUp, parent=self.choiceFrame, pos=(0.31, 0, -0.01), text_scale=TTLocalizer.DTGupLabel, text_fg=Vec4(0, 0, 1, 1), relief=None, text_align=TextNode.ALeft) + self.downLabel = DirectLabel(text=TTLocalizer.TravelGameDown, parent=self.choiceFrame, pos=(0.31, 0, -0.01), text_scale=TTLocalizer.DTGdownLabel, text_fg=Vec4(1, 0, 0, 1), relief=None, text_align=TextNode.ALeft) + self.scrollList = DirectScrolledList(parent=self.choiceFrame, relief=None, pos=(-0.36, 0, -0.02), incButton_image=(guiModel.find('**/FndsLst_ScrollUp'), + guiModel.find('**/FndsLst_ScrollDN'), + guiModel.find('**/FndsLst_ScrollUp_Rllvr'), + guiModel.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.04), incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(guiModel.find('**/FndsLst_ScrollUp'), + guiModel.find('**/FndsLst_ScrollDN'), + guiModel.find('**/FndsLst_ScrollUp_Rllvr'), + guiModel.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.095), decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_pos=(0.0, 0.0, 0.0), itemFrame_relief=DGG.GROOVE, numItemsVisible=1, itemMakeFunction=makeLabel, items=[], scrollSpeed=3.0, itemFrame_scale=0.1, command=self.scrollChoiceChanged) + self.putChoicesInScrollList() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.voteButton = DirectButton(parent=self.choiceFrame, relief=None, image=okImageList, image_scale=3.0, pos=(0.85, 0, 0.0), text=TTLocalizer.TravelGameVoteWithExclamation, text_scale=TTLocalizer.DTGvoteButton, text_pos=(0, 0), command=self.handleInputChoice) + self.waitingChoicesLabel = DirectLabel(text=TTLocalizer.TravelGameWaitingChoices, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(-0.2, 0, -0.85), scale=0.075) + self.waitingChoicesLabel.hide() + self.gui.hide() + return + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.introMovie.finish() + del self.introMovie + self.gameBoard.removeNode() + del self.gameBoard + self.sky.removeNode() + del self.sky + self.trolleyCar.removeNode() + del self.trolleyCar + for key in self.trainSwitches.keys(): + self.trainSwitches[key].removeNode() + del self.trainSwitches[key] + + self.trainSwitches = {} + for key in self.tunnels.keys(): + self.tunnels[key].removeNode() + del self.tunnels[key] + + self.tunnels = {} + for key in self.trainTracks.keys(): + self.trainTracks[key].removeNode() + del self.trainTracks[key] + + self.trainTracks = {} + for trainTrack in self.extraTrainTracks: + trainTrack.removeNode() + del trainTrack + + self.extraTrainTracks = [] + self.gui.removeNode() + del self.gui + self.waitingChoicesLabel.destroy() + del self.waitingChoicesLabel + if self.flashWinningBeansTrack: + self.flashWinningBeansTrack.finish() + del self.flashWinningBeansTrack + for label in self.minigameLabels: + label.destroy() + del label + + self.minigameLabels = [] + for icon in self.minigameIcons: + icon.destroy() + icon.removeNode() + + self.minigameIcons = [] + if hasattr(self, 'mg_icons'): + del self.mg_icons + for label in self.bonusLabels: + label.destroy() + del label + + self.bonusLabels = [] + self.scrollList.destroy() + del self.scrollList + self.voteButton.destroy() + del self.voteButton + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + del self.music + + def moveCameraToTop(self): + camera.reparentTo(render) + p = self.cameraTopView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + + def moveCameraToTrolley(self): + camera.reparentTo(self.trolleyCar) + camera.setPos(-25, 0, 7.5) + camera.setHpr(-90, 0, 0) + + def onstage(self): + self.notify.debug('onstage') + NametagGlobals.setOnscreenChatForced(1) + DistributedMinigame.onstage(self) + self.gameBoard.reparentTo(render) + self.sky.reparentTo(render) + self.moveCameraToTop() + self.trolleyCar.reparentTo(render) + for key in self.trainSwitches.keys(): + self.trainSwitches[key].reparentTo(render) + + for key in self.trainTracks.keys(): + self.trainTracks[key].reparentTo(render) + + for trainTrack in self.extraTrainTracks: + trainTrack.reparentTo(render) + + base.transitions.irisIn(0.4) + base.setBackgroundColor(0.1875, 0.7929, 0) + base.playMusic(self.music, looping=1, volume=0.9) + self.introMovie.start() + + def offstage(self): + self.notify.debug('offstage') + NametagGlobals.setOnscreenChatForced(0) + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.introMovie.finish() + self.gameBoard.hide() + self.sky.hide() + self.trolleyCar.hide() + self.gui.hide() + self.hideMinigamesAndBonuses() + for key in self.trainSwitches.keys(): + self.trainSwitches[key].hide() + + for key in self.trainTracks.keys(): + self.trainTracks[key].hide() + + for trainTrack in self.extraTrainTracks: + trainTrack.hide() + + DistributedMinigame.offstage(self) + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.start() + self.music.stop() + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + for index in range(self.numPlayers): + avId = self.avIdList[index] + name = '' + avatar = self.getAvatar(avId) + if avatar: + avatar.reparentTo(self.trolleyCar) + avatar.animFSM.request('Sit') + avatar.setPosHpr(-4, -4.5 + index * 3, 2.8, 90, 0, 0) + name = avatar.getName() + self.avNames.append(name) + + self.trolleyCar.setH(90) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.introMovie.finish() + self.gameFSM.request('inputChoice') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterInputChoice(self): + self.notify.debug('enterInputChoice') + NametagGlobals.setOnscreenChatForced(1) + self.timer = ToontownTimer.ToontownTimer() + self.timer.hide() + if self.timerStartTime != None: + self.startTimer() + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.stop() + self.gui.show() + self.showMinigamesAndBonuses() + return + + def exitInputChoice(self): + NametagGlobals.setOnscreenChatForced(0) + if self.timer != None: + self.timer.destroy() + self.timer = None + self.timerStartTime = None + self.gui.hide() + return + + def enterWaitServerChoices(self): + self.notify.debug('enterWaitServerChoices') + self.waitingChoicesLabel.show() + self.gui.hide() + + def exitWaitServerChoices(self): + self.waitingChoicesLabel.hide() + + def enterDisplayVotes(self, votes, directions, directionToGo, directionReason): + if self.UseTrolleyResultsPanel: + self.moveCameraToTrolley() + self.hideMinigamesAndBonuses() + else: + self.moveCameraToTop() + self.resultVotes = votes + self.resultDirections = directions + self.directionToGo = directionToGo + self.directionReason = directionReason + self.resultsStr = '' + directionTotals = [0] * TravelGameGlobals.MaxDirections + for index in range(len(votes)): + if index < len(self.avNames): + avId = self.avIdList[index] + dir = directions[index] + numVotes = votes[index] + directionTotals[dir] += numVotes + curStr = TTLocalizer.TravelGameOneToonVote % {'name': self.avNames[index], + 'numVotes': numVotes, + 'dir': TTLocalizer.TravelGameDirections[dir]} + if not (numVotes == 0 and avId in self.disconnectedAvIds): + self.resultsStr += curStr + + directionStr = TTLocalizer.TravelGameTotals + for index in range(len(directionTotals)): + directionStr += ' ' + TTLocalizer.TravelGameDirections[index] + ':' + directionStr += str(directionTotals[index]) + + directionStr += '\n' + self.resultsStr += directionStr + reasonStr = '' + if directionReason == TravelGameGlobals.ReasonVote: + if directionToGo == 0: + losingDirection = 1 + else: + losingDirection = 0 + diffVotes = directionTotals[directionToGo] - directionTotals[losingDirection] + reasonStr = '' + if diffVotes > 1: + reasonStr = TTLocalizer.TravelGameReasonVotesPlural % {'dir': TTLocalizer.TravelGameDirections[directionToGo], + 'numVotes': diffVotes} + else: + reasonStr = TTLocalizer.TravelGameReasonVotesSingular % {'dir': TTLocalizer.TravelGameDirections[directionToGo], + 'numVotes': diffVotes} + elif directionReason == TravelGameGlobals.ReasonRandom: + reasonStr = TTLocalizer.TravelGameReasonRandom % {'dir': TTLocalizer.TravelGameDirections[directionToGo], + 'numVotes': directionTotals[directionToGo]} + elif directionReason == TravelGameGlobals.ReasonPlaceDecider: + reasonStr = TravelGameReasonPlace % {'name': 'TODO NAME', + 'dir': TTLocalizer.TravelGameDirections[directionToGo]} + self.resultsStr += reasonStr + self.dialog = TTDialog.TTDialog(text=self.resultsStr, command=self.__cleanupDialog, style=TTDialog.NoButtons, pos=(0, 0, 1)) + self.dialog.hide() + if self.UseTrolleyResultsPanel: + self.votesPanel = VoteResultsTrolleyPanel.VoteResultsTrolleyPanel(len(self.avIdList), self.avIdList, votes, directions, self.avNames, self.disconnectedAvIds, directionToGo, directionReason, directionTotals) + else: + self.votesPanel = VoteResultsPanel.VoteResultsPanel(len(self.avIdList), self.avIdList, votes, directions, self.avNames, self.disconnectedAvIds, directionToGo, directionReason, directionTotals) + self.votesPanel.startMovie() + numPlayers = len(self.avIdList) + if TravelGameGlobals.SpoofFour: + numPlayers = 4 + delay = TravelGameGlobals.DisplayVotesTimePerPlayer * (numPlayers + 1) + taskMgr.doMethodLater(delay, self.displayVotesTimeoutTask, self.taskName('displayVotes-timeout')) + curSwitch = TravelGameGlobals.BoardLayouts[self.boardIndex][self.currentSwitch] + self.destSwitch = curSwitch['links'][directionToGo] + self.updateCurrentVotes() + + def exitDisplayVotes(self): + taskMgr.remove(self.taskName('displayVotes-timeout')) + self.__cleanupDialog(0) + if not self.UseTrolleyResultsPanel: + self.showMinigamesAndBonuses() + self.votesPanel.destroy() + + def enterMoveTrolley(self): + self.notify.debug('enterMoveTrolley') + camera.wrtReparentTo(render) + keyAngle = round(self.TrolleyMoveDuration) * 360 + dist = Vec3(self.trainSwitches[self.destSwitch].getPos() - self.trainSwitches[self.currentSwitch].getPos()).length() + wheelAngle = dist / (2.0 * math.pi * 0.95) * 360 + trolleyAnimateInterval = LerpFunctionInterval(self.animateTrolley, duration=self.TrolleyMoveDuration, blendType='easeInOut', extraArgs=[keyAngle, wheelAngle], name='TrolleyAnimate') + moveTrolley = Sequence() + moveTrolley.append(Func(self.resetAnimation)) + newPos = self.trainSwitches[self.destSwitch].getPos() + linkKey = (self.currentSwitch, self.destSwitch) + origHeading = self.trainTracks[linkKey].getH() + heading = origHeading + 90 + firstTurn = Parallel() + firstTurn.append(LerpHprInterval(self.trolleyCar, 1, Vec3(heading, 0, 0))) + firstTurn.append(LerpHprInterval(self.trainSwitches[self.currentSwitch], 1, Vec3(origHeading, 0, 0))) + firstTurn.append(LerpHprInterval(self.trainSwitches[self.destSwitch], 1, Vec3(origHeading, 0, 0))) + moveTrolley.append(firstTurn) + moveTrolley.append(Parallel(LerpPosInterval(self.trolleyCar, self.TrolleyMoveDuration, newPos, blendType='easeInOut'), trolleyAnimateInterval)) + secondTurn = Parallel() + secondTurn.append(LerpHprInterval(self.trolleyCar, 1, Vec3(90, 0, 0))) + secondTurn.append(LerpHprInterval(self.trainSwitches[self.currentSwitch], 1, Vec3(0, 0, 0))) + secondTurn.append(LerpHprInterval(self.trainSwitches[self.destSwitch], 1, Vec3(0, 0, 0))) + moveTrolley.append(secondTurn) + soundTrack = Sequence() + trolleyExitBellInterval = Parallel(SoundInterval(self.trolleyBellSfx, duration=1), SoundInterval(self.turntableRotateSfx, duration=1, volume=0.5)) + trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, duration=3) + soundTrack.append(trolleyExitBellInterval) + soundTrack.append(trolleyExitAwayInterval) + soundTrack.append(trolleyExitBellInterval) + self.moveTrolleyIval = Parallel(moveTrolley, soundTrack) + duration = self.moveTrolleyIval.getDuration() + + def focusOnTrolley(t, self = self): + pos = self.trolleyCar.getPos() + pos.setZ(pos.getZ() + 7.5) + camera.lookAt(pos) + self.lastFocusHpr = camera.getHpr() + + setRightHprTime = 0 + if self.FlyCameraUp: + setRightHprTime = 1.0 + camIval1 = Parallel() + camIval1.append(LerpFunc(focusOnTrolley, duration - setRightHprTime, name='focusOnTrolley')) + finalPos = Vec3(self.cameraTopView[0], self.cameraTopView[1], self.cameraTopView[2]) + finalHpr = Vec3(self.cameraTopView[3], self.cameraTopView[4], self.cameraTopView[5]) + if self.FlyCameraUp: + if self.FocusOnTrolleyWhileMovingUp: + camIval1.append(LerpPosInterval(camera, duration - setRightHprTime, finalPos, name='cameraMove')) + camIval2 = Sequence(LerpHprInterval(camera, setRightHprTime, finalHpr, name='cameraHpr')) + else: + camIval2 = Sequence(LerpPosHprInterval(camera, setRightHprTime, finalPos, finalHpr, blendType='easeIn', name='cameraHpr')) + camIval = Sequence(camIval1, camIval2) + else: + camIval = Sequence(camIval1) + if self.UseTrolleyResultsPanel: + self.moveTrolleyIval.append(camIval) + temp = self.moveTrolleyIval + self.moveTrolleyIval = Sequence(temp) + if self.isLeaf(self.destSwitch): + self.moveTrolleyIval.append(Func(self.gameFSM.request, 'winMovie')) + else: + self.moveTrolleyIval.append(Func(self.gameFSM.request, 'inputChoice')) + self.moveTrolleyIval.start() + + def exitMoveTrolley(self): + self.notify.debug('exitMoveTrolley') + self.currentSwitch = self.destSwitch + self.moveTrolleyIval.finish() + self.moveCameraToTop() + self.showMinigamesAndBonuses() + + def enterWinMovie(self): + resultStr = TTLocalizer.TravelGamePlaying % {'game': self.idToNames[self.switchToMinigameDict[self.currentSwitch]]} + numToons = 0 + for avId in self.avIdList: + if avId not in self.disconnectedAvIds: + numToons += 1 + + if numToons <= 1: + resultStr = TTLocalizer.TravelGameGoingBackToShop + reachedGoalStr = None + localAvatarWon = False + localAvatarLost = False + noWinner = True + for avId in self.avIdBonuses.keys(): + name = '' + avatar = self.getAvatar(avId) + if avatar: + name = avatar.getName() + if self.avIdBonuses[avId][0] == self.currentSwitch: + noWinner = False + reachedGoalStr = TTLocalizer.TravelGameGotBonus % {'name': name, + 'numBeans': self.avIdBonuses[avId][1]} + if avId == base.localAvatar.doId: + if not TravelGameGlobals.ReverseWin: + self.wonGameSfx.play() + bonusLabel = self.switchToBonusLabelDict[self.currentSwitch] + self.flashWinningBeansTrack = Sequence(LerpColorScaleInterval(bonusLabel, 0.75, Vec4(0.5, 1, 0.5, 1)), LerpColorScaleInterval(bonusLabel, 0.75, Vec4(1, 1, 1, 1))) + self.flashWinningBeansTrack.loop() + else: + self.lostGameSfx.play() + elif not TravelGameGlobals.ReverseWin: + self.lostGameSfx.play() + else: + self.wonGameSfx.play() + + if noWinner: + self.noWinnerSfx.play() + resultStr += '\n\n' + resultStr += TTLocalizer.TravelGameNoOneGotBonus + if reachedGoalStr: + resultStr += '\n\n' + resultStr += reachedGoalStr + self.winDialog = TTDialog.TTDialog(text=resultStr, command=self.__cleanupWinDialog, style=TTDialog.NoButtons) + info = TravelGameGlobals.BoardLayouts[self.boardIndex][self.currentSwitch] + leafX, leafY, leafZ = info['pos'] + endX = leafX + TravelGameGlobals.xInc + heading = 90 + moveTrolley = Sequence() + moveTrolley.append(LerpHprInterval(self.trolleyCar, 1, Vec3(heading, 0, 0))) + moveTrolley.append(LerpPosInterval(self.trolleyCar, 3, Vec3(endX + 20, leafY, 0))) + soundTrack = Sequence() + trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, duration=1) + trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, duration=3) + soundTrack.append(trolleyExitBellInterval) + soundTrack.append(trolleyExitAwayInterval) + soundTrack.append(trolleyExitBellInterval) + self.moveTrolleyIval = Parallel(moveTrolley, soundTrack) + self.moveTrolleyIval.start() + delay = 8 + taskMgr.doMethodLater(delay, self.gameOverCallback, self.taskName('playMovie')) + return + + def exitWinMovie(self): + taskMgr.remove(self.taskName('playMovie')) + self.moveTrolleyIval.finish() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + + def exitCleanup(self): + pass + + def setStartingVotes(self, startingVotesArray): + if not len(startingVotesArray) == len(self.avIdList): + self.notify.error('length does not match, startingVotes=%s, avIdList=%s' % (startingVotesArray, self.avIdList)) + return + for index in range(len(self.avIdList)): + avId = self.avIdList[index] + self.startingVotes[avId] = startingVotesArray[index] + if not self.currentVotes.has_key(avId): + self.currentVotes[avId] = startingVotesArray[index] + + self.notify.debug('starting votes = %s' % self.startingVotes) + + def startTimer(self): + now = globalClock.getFrameTime() + elapsed = now - self.timerStartTime + self.timer.setPos(1.16, 0, -0.83) + self.timer.setTime(TravelGameGlobals.InputTimeout) + self.timer.countdown(TravelGameGlobals.InputTimeout - elapsed, self.handleChoiceTimeout) + self.timer.show() + + def setTimerStartTime(self, timestamp): + if not self.hasLocalToon: + return + self.timerStartTime = globalClockDelta.networkToLocalTime(timestamp) + if self.timer != None: + self.startTimer() + return + + def handleChoiceTimeout(self): + self.sendUpdate('setAvatarChoice', [0, 0]) + self.gameFSM.request('waitServerChoices') + + def putChoicesInScrollList(self): + available = self.currentVotes[self.localAvId] + if len(self.scrollList['items']) > 0: + self.scrollList.removeAllItems() + self.indexToVotes = {} + index = 0 + for vote in range(available)[::-1]: + self.scrollList.addItem(str(-(vote + 1))) + self.indexToVotes[index] = vote + 1 + index += 1 + + self.scrollList.addItem(str(0)) + self.indexToVotes[index] = 0 + self.zeroVoteIndex = index + index += 1 + for vote in range(available): + self.scrollList.addItem(str(vote + 1)) + self.indexToVotes[index] = vote + 1 + index += 1 + + self.scrollList.scrollTo(self.zeroVoteIndex) + + def getAbsVoteChoice(self): + available = self.currentVotes[self.localAvId] + retval = 0 + if hasattr(self, 'scrollList'): + selectedIndex = self.scrollList.getSelectedIndex() + if self.indexToVotes.has_key(selectedIndex): + retval = self.indexToVotes[selectedIndex] + return retval + + def getAbsDirectionChoice(self): + selectedIndex = self.scrollList.getSelectedIndex() + if selectedIndex < self.zeroVoteIndex: + retval = 0 + elif selectedIndex == self.zeroVoteIndex: + retval = 0 + else: + retval = 1 + return retval + + def makeTextMatchChoice(self): + self.votesPeriodLabel.hide() + self.votesToGoLabel.hide() + self.upLabel.hide() + self.downLabel.hide() + if not hasattr(self, 'scrollList') or not hasattr(self, 'zeroVoteIndex'): + return + selectedIndex = self.scrollList.getSelectedIndex() + if selectedIndex < self.zeroVoteIndex: + self.votesToGoLabel.show() + self.upLabel.show() + elif selectedIndex == self.zeroVoteIndex: + self.votesPeriodLabel.show() + else: + self.votesToGoLabel.show() + self.downLabel.show() + + def scrollChoiceChanged(self): + choiceVotes = self.getAbsVoteChoice() + if choiceVotes == 1: + self.votesToGoLabel['text'] = TTLocalizer.TravelGameVoteToGo + else: + self.votesToGoLabel['text'] = TTLocalizer.TravelGameVotesToGo + available = self.currentVotes[self.localAvId] + self.localVotesRemaining['text'] = str(available - choiceVotes) + self.makeTextMatchChoice() + + def setAvatarChose(self, avId): + if not self.hasLocalToon: + return + self.notify.debug('setAvatarChose: avatar: ' + str(avId) + ' choose a number') + + def handleInputChoice(self): + numVotes = self.getAbsVoteChoice() + direction = self.getAbsDirectionChoice() + self.sendUpdate('setAvatarChoice', [numVotes, direction]) + self.gameFSM.request('waitServerChoices') + + def setServerChoices(self, votes, directions, directionToGo, directionReason): + if not self.hasLocalToon: + return + self.notify.debug('requesting displayVotes, curState=%s' % self.gameFSM.getCurrentState().getName()) + self.gameFSM.request('displayVotes', [votes, + directions, + directionToGo, + directionReason]) + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return + + def displayVotesTimeoutTask(self, task): + self.notify.debug('Done waiting for display votes') + self.gameFSM.request('moveTrolley') + return Task.done + + def updateCurrentVotes(self): + for index in range(len(self.resultVotes)): + avId = self.avIdList[index] + oldCurrentVotes = self.currentVotes[avId] + self.currentVotes[avId] -= self.resultVotes[index] + + self.putChoicesInScrollList() + self.makeTextMatchChoice() + + def isLeaf(self, switchIndex): + retval = False + links = TravelGameGlobals.BoardLayouts[self.boardIndex][switchIndex]['links'] + if len(links) == 0: + retval = True + return retval + + def __cleanupWinDialog(self, value): + if hasattr(self, 'winDialog') and self.winDialog: + self.winDialog.cleanup() + self.winDialog = None + return + + def gameOverCallback(self, task): + self.__cleanupWinDialog(0) + self.gameOver() + return Task.done + + def setMinigames(self, switches, minigames): + if not self.hasLocalToon: + return + self.switchToMinigameDict = {} + for index in range(len(switches)): + switch = switches[index] + minigame = minigames[index] + self.switchToMinigameDict[switch] = minigame + + self.notify.debug('minigameDict = %s' % self.switchToMinigameDict) + self.loadMinigameIcons() + + def loadMinigameIcons(self): + self.mg_icons = loader.loadModel('phase_4/models/minigames/mg_icons') + for switch in self.switchToMinigameDict.keys(): + minigame = self.switchToMinigameDict[switch] + switchPos = self.trainSwitches[switch].getPos() + labelPos = map3dToAspect2d(render, switchPos) + useText = True + iconName = None + if minigame in IconDict.keys(): + iconName = IconDict[minigame] + icon = None + if self.mg_icons: + icon = self.mg_icons.find('**/%s' % iconName) + if not icon.isEmpty(): + useText = False + if labelPos: + if useText: + labelPos.setZ(labelPos.getZ() - 0.1) + label = DirectLabel(text=self.idToNames[minigame], relief=None, scale=0.1, pos=labelPos, text_fg=(1.0, 1.0, 1.0, 1.0)) + label.hide() + self.minigameLabels.append(label) + else: + placeHolder = DirectButton(image=icon, relief=None, text=('', + '', + self.idToNames[minigame], + ''), text_scale=0.3, text_pos=(0, -0.7, 0), text_fg=(1, 1, 1, 1), clickSound=None, pressEffect=0) + placeHolder.setPos(labelPos) + placeHolder.setScale(0.2) + placeHolder.hide() + self.minigameIcons.append(placeHolder) + tunnel = self.tunnels[switch] + sign = tunnel.attachNewNode('sign') + icon.copyTo(sign) + sign.setH(-90) + sign.setZ(26) + sign.setScale(10) + + return + + def showMinigamesAndBonuses(self): + for label in self.minigameLabels: + label.show() + + for label in self.bonusLabels: + label.show() + + for icon in self.minigameIcons: + icon.show() + + def hideMinigamesAndBonuses(self): + for label in self.minigameLabels: + label.hide() + + for label in self.bonusLabels: + label.hide() + + for icon in self.minigameIcons: + icon.hide() + + def loadBonuses(self): + self.switchToBonusLabelDict = {} + for avId in self.avIdBonuses.keys(): + if avId == self.localAvId: + switch = self.avIdBonuses[avId][0] + beans = self.avIdBonuses[avId][1] + switchPos = self.trainSwitches[switch].getPos() + labelPos = map3dToAspect2d(render, switchPos) + if labelPos: + labelPos.setX(labelPos.getX() + 0.1) + labelPos.setZ(labelPos.getZ() - 0.02) + bonusStr = TTLocalizer.TravelGameBonusBeans % {'numBeans': beans} + label = DirectLabel(text=bonusStr, relief=None, scale=0.1, pos=labelPos, text_fg=(1.0, 1.0, 1.0, 1.0), text_align=TextNode.ALeft) + label.hide() + self.bonusLabels.append(label) + self.switchToBonusLabelDict[switch] = label + break + + return + + def setBonuses(self, switches, beans): + if not self.hasLocalToon: + return + self.avIdBonuses = {} + for index in range(len(self.avIdList)): + avId = self.avIdList[index] + switch = switches[index] + bean = beans[index] + self.avIdBonuses[avId] = (switch, bean) + + self.notify.debug('self.avIdBonuses = %s' % self.avIdBonuses) + self.loadBonuses() + + def handleDisabledAvatar(self, avId): + self.notify.warning('DistrbutedTravelGame: handleDisabledAvatar: disabled avId: ' + str(avId)) + self.disconnectedAvIds.append(avId) + + def setBoardIndex(self, boardIndex): + self.boardIndex = boardIndex + + def getIntroMovie(self): + rootInfo = TravelGameGlobals.BoardLayouts[self.boardIndex][0] + rootX, rootY, rootZ = rootInfo['pos'] + startX = rootX - TravelGameGlobals.xInc + heading = 90 + moveTrolley = Sequence() + moveTrolley.append(Func(self.trolleyCar.setH, 90)) + moveTrolley.append(LerpPosInterval(self.trolleyCar, 3, Vec3(rootX, rootY, 0), startPos=Vec3(startX, rootY, 0))) + moveTrolley.append(LerpHprInterval(self.trolleyCar, 1, Vec3(heading, 0, 0))) + soundTrack = Sequence() + trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, duration=3) + trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, duration=1) + soundTrack.append(trolleyExitAwayInterval) + soundTrack.append(trolleyExitBellInterval) + retval = Parallel(moveTrolley, soundTrack) + return retval + + def animateTrolley(self, t, keyAngle, wheelAngle): + for i in range(self.numKeys): + key = self.keys[i] + ref = self.keyRef[i] + key.setH(ref, t * keyAngle) + + for i in range(self.numFrontWheels): + frontWheel = self.frontWheels[i] + ref = self.frontWheelRef[i] + frontWheel.setH(ref, t * wheelAngle) + + for i in range(self.numBackWheels): + backWheel = self.backWheels[i] + ref = self.backWheelRef[i] + backWheel.setH(ref, t * wheelAngle) + + def resetAnimation(self): + for i in range(self.numKeys): + self.keys[i].setTransform(self.keyInit[i]) + + for i in range(self.numFrontWheels): + self.frontWheels[i].setTransform(self.frontWheelInit[i]) + + for i in range(self.numBackWheels): + self.backWheels[i].setTransform(self.backWheelInit[i]) diff --git a/toontown/minigame/DistributedTravelGameAI.py b/toontown/minigame/DistributedTravelGameAI.py new file mode 100644 index 0000000..ebac2c4 --- /dev/null +++ b/toontown/minigame/DistributedTravelGameAI.py @@ -0,0 +1,322 @@ +from toontown.minigame.DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import TravelGameGlobals +from toontown.toonbase import ToontownGlobals + +class DistributedTravelGameAI(DistributedMinigameAI): + notify = directNotify.newCategory('DistributedTravelGameAI') + + def __init__(self, air, minigameId): + try: + self.DistributedTravelGameAI_initialized + except: + self.DistributedTravelGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTravelGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['waitClientsChoices']), + State.State('waitClientsChoices', self.enterWaitClientsChoices, self.exitWaitClientsChoices, ['processChoices', 'cleanup']), + State.State('processChoices', self.enterProcessChoices, self.exitProcessChoices, ['waitClientsChoices', 'cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.currentVotes = {} + self.avatarChoices = {} + self.currentSwitch = 0 + self.destSwitch = 0 + self.gotBonus = {} + self.desiredNextGame = -1 + self.boardIndex = random.choice(range(len(TravelGameGlobals.BoardLayouts))) + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + self.calcMinigames() + self.calcBonusBeans() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsChoices') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + scoreList = [] + curVotesList = [] + bonusesList = [] + for avId in self.avIdList: + scoreList.append(self.scoreDict[avId]) + curVotesList.append(self.currentVotes[avId]) + bonusesList.append((self.avIdBonuses[avId][0], self.avIdBonuses[avId][1])) + + self.air.writeServerEvent('minigame_travel', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.TravelGameId, + self.getSafezoneId(), + self.avIdList, + scoreList, + self.boardIndex, + curVotesList, + bonusesList, + self.desiredNextGame)) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterWaitClientsChoices(self): + self.notify.debug('enterWaitClientsChoices') + self.resetChoices() + taskMgr.doMethodLater(TravelGameGlobals.InputTimeout, self.waitClientsChoicesTimeout, self.taskName('input-timeout')) + self.sendUpdate('setTimerStartTime', [globalClockDelta.getFrameNetworkTime()]) + + def exitWaitClientsChoices(self): + taskMgr.remove(self.taskName('input-timeout')) + + def enterProcessChoices(self): + self.directionVotes = [] + for dir in range(TravelGameGlobals.MaxDirections): + self.directionVotes.append([dir, 0]) + + for key in self.avatarChoices: + choice = self.avatarChoices[key] + numVotes = choice[0] + direction = choice[1] + self.directionVotes[direction][1] += numVotes + + def voteCompare(directionVoteA, directionVoteB): + if directionVoteA[1] < directionVoteB[1]: + return -1 + elif directionVoteA[1] == directionVoteB[1]: + return 0 + else: + return 1 + + self.directionVotes.sort(voteCompare, reverse=True) + winningVotes = self.directionVotes[0][1] + self.winningDirections = [] + self.notify.debug('self.directionVotes = %s' % self.directionVotes) + for vote in self.directionVotes: + if vote[1] == winningVotes: + self.winningDirections.append(vote[0]) + self.notify.debug('add direction %d to winning directions' % vote[0]) + + self.directionReason = TravelGameGlobals.ReasonVote + if len(self.winningDirections) > 1: + self.notify.debug('multiple winningDirections=%s' % self.winningDirections) + self.directionReason = TravelGameGlobals.ReasonRandom + self.directionToGo = random.choice(self.winningDirections) + self.notify.debug('self.directionToGo =%d' % self.directionToGo) + self.votesArray = [] + self.directionArray = [] + for avId in self.avIdList: + vote = self.avatarChoices[avId][0] + direction = self.avatarChoices[avId][1] + if vote < 0: + vote = 0 + self.votesArray.append(vote) + self.directionArray.append(direction) + + curSwitch = TravelGameGlobals.BoardLayouts[self.boardIndex][self.currentSwitch] + self.destSwitch = curSwitch['links'][self.directionToGo] + self.checkForEndGame() + + def exitProcessChoices(self): + taskMgr.remove(self.taskName('move-timeout')) + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def setExpectedAvatars(self, avIds): + DistributedMinigameAI.setExpectedAvatars(self, avIds) + + def createDefaultStartingVotes(self): + for avId in self.avIdList: + self.startingVotes[avId] = TravelGameGlobals.DefaultStartingVotes + self.currentVotes[avId] = TravelGameGlobals.DefaultStartingVotes + + def waitClientsChoicesTimeout(self, task): + self.notify.debug('waitClientsChoicesTimeout: did not hear from all clients') + for avId in self.avatarChoices.keys(): + if self.avatarChoices[avId] == (-1, 0): + self.avatarChoices[avId] = (0, 0) + + self.gameFSM.request('processChoices') + return Task.done + + def resetChoices(self): + for avId in self.avIdList: + self.avatarChoices[avId] = (-1, 0) + + def setAvatarChoice(self, votes, direction): + avatarId = self.air.getAvatarIdFromSender() + self.notify.debug('setAvatarChoice: avatar: ' + str(avatarId) + ' votes: ' + str(votes) + ' direction: ' + str(direction)) + self.avatarChoices[avatarId] = self.checkChoice(avatarId, votes, direction) + self.currentVotes[avatarId] -= self.avatarChoices[avatarId][0] + if self.currentVotes[avatarId] < 0: + self.notify.warning('currentVotes < 0 avId=%s, currentVotes=%s' % (avatarId, self.currentVotes[avatarId])) + self.notify.debug('currentVotes = %s' % self.currentVotes) + self.notify.debug('avatarChoices = %s' % self.avatarChoices) + self.sendUpdate('setAvatarChose', [avatarId]) + if self.allAvatarsChosen(): + self.notify.debug('setAvatarChoice: all avatars have chosen') + self.gameFSM.request('processChoices') + else: + self.notify.debug('setAvatarChoice: still waiting for more choices') + + def checkChoice(self, avId, votes, direction): + retDir = direction + if direction < 0 or direction >= TravelGameGlobals.MaxDirections: + self.notify.debug('invalid direction %d. Using 0.' % direction) + retDir = 0 + availableVotes = self.currentVotes[avId] + retVotes = min(votes, availableVotes) + retVotes = max(votes, 0) + return (retVotes, retDir) + + def allAvatarsChosen(self): + for avId in self.avatarChoices.keys(): + choice = self.avatarChoices[avId] + if choice[0] == -1 and not self.stateDict[avId] == EXITED: + return False + + return True + + def isLeaf(self, switchIndex): + retval = False + links = TravelGameGlobals.BoardLayouts[self.boardIndex][switchIndex]['links'] + if len(links) == 0: + retval = True + return retval + + def giveBonusBeans(self, endingSwitch): + noOneGotBonus = True + for avId in self.avIdBonuses.keys(): + self.scoreDict[avId] = 0 + if self.avIdBonuses[avId][0] == endingSwitch and not self.stateDict[avId] == EXITED: + noOneGotBonus = False + self.scoreDict[avId] = self.avIdBonuses[avId][1] + self.gotBonus[avId] = self.avIdBonuses[avId][1] + + if noOneGotBonus: + for avId in self.avIdBonuses.keys(): + self.scoreDict[avId] = 1 + + def checkForEndGame(self): + self.notify.debug('checkForEndgame: ') + self.currentSwitch = self.destSwitch + didWeReachMiniGame = self.isLeaf(self.currentSwitch) + numPlayers = len(self.avIdList) + if TravelGameGlobals.SpoofFour: + numPlayers = 4 + delay = TravelGameGlobals.DisplayVotesTimePerPlayer * (numPlayers + 1) + TravelGameGlobals.MoveTrolleyTime + TravelGameGlobals.FudgeTime + if didWeReachMiniGame: + self.desiredNextGame = self.switchToMinigameDict[self.currentSwitch] + taskMgr.doMethodLater(delay, self.moveTimeoutTaskGameOver, self.taskName('move-timeout')) + self.giveBonusBeans(self.currentSwitch) + else: + taskMgr.doMethodLater(delay, self.moveTimeoutTask, self.taskName('move-timeout')) + self.sendUpdate('setServerChoices', [self.votesArray, + self.directionArray, + self.directionToGo, + self.directionReason]) + + def moveTimeoutTask(self, task): + self.notify.debug('Done waiting for trolley move') + self.gameFSM.request('waitClientsChoices') + return Task.done + + def moveTimeoutTaskGameOver(self, task): + self.notify.debug('Done waiting for trolley move, gmae over') + self.gameOver() + return Task.done + + def calcMinigames(self): + numPlayers = len(self.avIdList) + allowedGames = list(ToontownGlobals.MinigamePlayerMatrix[numPlayers]) + from toontown.minigame import MinigameCreatorAI + allowedGames = MinigameCreatorAI.removeUnreleasedMinigames(allowedGames) + self.switchToMinigameDict = {} + for switch in TravelGameGlobals.BoardLayouts[self.boardIndex].keys(): + if self.isLeaf(switch): + if len(allowedGames) == 0: + allowedGames = list(ToontownGlobals.MinigamePlayerMatrix[numPlayers]) + allowedGames = MinigameCreatorAI.removeUnreleasedMinigames(allowedGames) + minigame = random.choice(allowedGames) + self.switchToMinigameDict[switch] = minigame + allowedGames.remove(minigame) + + switches = [] + minigames = [] + for key in self.switchToMinigameDict.keys(): + switches.append(key) + minigames.append(self.switchToMinigameDict[key]) + + self.sendUpdate('setMinigames', [switches, minigames]) + + def calcBonusBeans(self): + possibleLeaves = [] + for switch in TravelGameGlobals.BoardLayouts[self.boardIndex].keys(): + if self.isLeaf(switch): + possibleLeaves.append(switch) + + self.avIdBonuses = {} + for avId in self.avIdList: + switch = random.choice(possibleLeaves) + possibleLeaves.remove(switch) + beans = TravelGameGlobals.BoardLayouts[self.boardIndex][switch]['baseBonus'] + baseBeans = TravelGameGlobals.BaseBeans + numPlayerMultiplier = len(self.avIdList) / 4.0 + roundMultiplier = self.metagameRound / 2.0 + 1.0 + beans *= baseBeans * numPlayerMultiplier * roundMultiplier + self.avIdBonuses[avId] = (switch, beans) + + switches = [] + beans = [] + for avId in self.avIdList: + switches.append(self.avIdBonuses[avId][0]) + beans.append(self.avIdBonuses[avId][1]) + + self.sendUpdate('setBonuses', [switches, beans]) + + def setStartingVote(self, avId, startingVote): + DistributedMinigameAI.setStartingVote(self, avId, startingVote) + self.currentVotes[avId] = startingVote + self.notify.debug('setting current vote of avId=%d to %d' % (avId, startingVote)) + + def handleExitedAvatar(self, avId): + self.notify.warning('DistrbutedTravelGameAI: handleExitedAvatar: avatar id exited: ' + str(avId)) + self.stateDict[avId] = EXITED + allExited = True + for avId in self.avIdList: + if avId in self.stateDict.keys() and self.stateDict[avId] != EXITED: + allExited = False + break + + if allExited: + self.setGameAbort() + + def getBoardIndex(self): + return self.boardIndex + + def hasScoreMult(self): + return 0 diff --git a/toontown/minigame/DistributedTugOfWarGame.py b/toontown/minigame/DistributedTugOfWarGame.py new file mode 100644 index 0000000..7651a5b --- /dev/null +++ b/toontown/minigame/DistributedTugOfWarGame.py @@ -0,0 +1,1245 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from DistributedMinigame import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.toonbase import ToontownTimer +from toontown.toon import ToonHead +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.char import Char +import ArrowKeys +import random +from toontown.toonbase import ToontownGlobals +import string +from toontown.toonbase import TTLocalizer +import TugOfWarGameGlobals +from direct.showutil import Rope +from toontown.effects import Splash +from toontown.effects import Ripples +from toontown.toonbase import TTLocalizer +import MinigamePowerMeter +from direct.task.Task import Task + +class DistributedTugOfWarGame(DistributedMinigame): + bgm = 'phase_4/audio/bgm/MG_tug_o_war.mid' + toonAnimNames = ['neutral', + 'tug-o-war', + 'slip-forward', + 'slip-backward', + 'victory', + 'sad-neutral'] + suitAnimNames = ['neutral', + 'tug-o-war', + 'slip-forward', + 'slip-backward', + 'flail', + 'victory'] + UPDATE_TIMER_TASK = 'TugOfWarGameUpdateTimerTask' + UPDATE_KEY_PRESS_RATE_TASK = 'TugOfWarGameUpdateKeyPressRateTask' + UPDATE_ROPE_TASK = 'TugOfWarGameUpdateRopeTask' + H_TO_L = 0 + L_TO_H = 1 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTugOfWarGame', [State.State('off', self.enterOff, self.exitOff, ['waitForGoSignal']), + State.State('waitForGoSignal', self.enterWaitForGoSignal, self.exitWaitForGoSignal, ['tug', 'cleanup']), + State.State('tug', self.enterTug, self.exitTug, ['gameDone', 'cleanup']), + State.State('gameDone', self.enterGameDone, self.exitGameDone, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.gameType = TugOfWarGameGlobals.TOON_VS_TOON + self.suit = None + self.suitId = 666 + self.suitType = 'f' + self.suitLevel = 1 + self.sides = {} + self.avList = [[], []] + self.buttons = [0, 1] + self.mouseMode = 0 + self.mouseSide = 0 + self.fallenList = [] + self.handycap = 2.0 + self.advantage = 1.0 + self.tugRopes = [] + self.ropePts = [] + self.ropeTex = [] + self.rightHandDict = {} + self.posDict = {} + self.hprDict = {} + self.offsetDict = {} + self.pullingDict = {} + self.dropShadowDict = {} + self.arrowKeys = None + self.keyTTL = [] + self.idealRate = 2.0 + self.idealForce = 0.0 + self.keyRate = 0 + self.allOutMode = 0 + self.rateMatchAward = 0 + self.targetRateList = [[8, 6], + [5, 7], + [6, 8], + [6, 10], + [7, 11], + [8, 12]] + self.nextRateIndex = 0 + self.drinkPositions = [] + for k in range(4): + self.drinkPositions.append(VBase3(-.2 + 0.2 * k, 16 + 2 * k, 0.0)) + + self.rng = RandomNumGen.RandomNumGen(1000) + self.introTrack = None + self.showTrack = None + self.setupTrack = None + self.animTracks = {} + self.randomNumGen = None + return + + def getTitle(self): + return TTLocalizer.TugOfWarGameTitle + + def getInstructions(self): + return TTLocalizer.TugOfWarInstructions + + def getMaxDuration(self): + return TugOfWarGameGlobals.GAME_DURATION + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.room = loader.loadModel('phase_4/models/minigames/tug_of_war_dock') + self.room.reparentTo(hidden) + ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope') + self.ropeTexture = ropeModel.findTexture('*') + ropeModel.removeNode() + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.correctSound = base.loadSfx('phase_4/audio/sfx/MG_pos_buzzer.wav') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.mp3') + self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.mp3') + self.music = base.loadMusic(self.bgm) + self.roundText = DirectLabel(text=' ', text_fg=(0, 1, 0, 1), frameColor=(1, 1, 1, 0), text_font=ToontownGlobals.getSignFont(), pos=(0.014, 0, -.84), scale=0.2) + self.powerMeter = MinigamePowerMeter.MinigamePowerMeter(17) + self.powerMeter.reparentTo(aspect2d) + self.powerMeter.setPos(0, 0, 0.4) + self.powerMeter.setPower(8) + self.powerMeter.setTarget(8) + self.arrows = [None] * 2 + for x in range(len(self.arrows)): + self.arrows[x] = loader.loadModel('phase_3/models/props/arrow') + self.arrows[x].reparentTo(self.powerMeter) + self.arrows[x].hide() + self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2) + self.arrows[x].setPos(0.12 - 0.24 * x, 0, -.26) + self.disableArrow(self.arrows[x]) + + self.splash = Splash.Splash(render) + self.suitSplash = Splash.Splash(render) + self.ripples = Ripples.Ripples(render) + self.suitRipples = Ripples.Ripples(render) + return + + def toggleMouseMode(self, param): + self.mouseMode = not self.mouseMode + if self.mouseMode: + mpos = param.getMouse() + if mpos[0] < 0: + self.hilightArrow(self.arrows[1]) + else: + self.hilightArrow(self.arrows[0]) + self.__spawnMouseSpeedTask() + else: + self.__releaseHandler(0) + self.__releaseHandler(1) + self.__killMouseSpeedTask() + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + del self.lt + self.timer.destroy() + del self.timer + self.room.removeNode() + del self.room + self.sky.removeNode() + del self.sky + del self.dropShadowDict + self.dropShadow.removeNode() + del self.dropShadow + del self.correctSound + del self.sndHitWater + del self.whistleSound + del self.music + self.roundText.destroy() + del self.roundText + if self.powerMeter: + self.powerMeter.destroy() + del self.powerMeter + for x in self.arrows: + if x: + x.removeNode() + del x + + del self.arrows + self.splash.destroy() + del self.splash + self.suitSplash.destroy() + del self.suitSplash + if self.ripples != None: + self.ripples.stop() + self.ripples.detachNode() + del self.ripples + if self.suitRipples != None: + self.suitRipples.stop() + self.suitRipples.detachNode() + del self.suitRipples + for x in self.avList: + del x + + del self.avList + for x in self.tugRopes: + if x != None: + x.detachNode() + del x + + del self.tugRopes + for x in self.ropePts: + if x: + for t in x: + del t + + del x + + del self.ropePts + for x in self.ropeTex: + if x: + for t in x: + t.destroy() + del t + + del x + + del self.ropeTex + del self.posDict + del self.hprDict + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + if self.suit: + self.suit.delete() + del self.suit + del self.sides + del self.buttons + del self.pullingDict + del self.rightHandDict + for x in self.drinkPositions: + del x + + del self.drinkPositions + del self.offsetDict + del self.keyTTL + del self.rng + return + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.lt = base.localAvatar + NametagGlobals.setGlobalNametagScale(1) + self.arrowKeys = ArrowKeys.ArrowKeys() + self.room.reparentTo(render) + self.room.setPosHpr(0.0, 18.39, -ToontownGlobals.FloorOffset, 0.0, 0.0, 0.0) + self.room.setScale(0.4) + self.sky.setZ(-5) + self.sky.reparentTo(render) + self.dropShadow.setColor(0, 0, 0, 0.5) + camera.reparentTo(render) + camera.setPosHpr(-11.4427, 9.03559, 2.80094, -49.104, -0.89, 0) + self.dropShadow.setBin('fixed', 0, 1) + self.splash.reparentTo(render) + self.suitSplash.reparentTo(render) + base.playMusic(self.music, looping=1, volume=1) + for x in range(len(self.arrows)): + self.arrows[x].show() + + for avId in self.avIdList: + self.pullingDict[avId] = 0 + + def offstage(self): + self.notify.debug('offstage') + DistributedMinigame.offstage(self) + self.music.stop() + if self.introTrack: + self.introTrack.finish() + del self.introTrack + self.introTrack = None + for track in self.animTracks.values(): + if track: + track.finish() + del track + self.animTracks = {} + + if self.showTrack: + self.showTrack.finish() + del self.showTrack + self.showTrack = None + if self.setupTrack: + self.setupTrack.finish() + del self.setupTrack + self.setupTrack = None + base.camLens.setFov(ToontownGlobals.DefaultCameraFov) + base.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + NametagGlobals.setGlobalNametagScale(1.0) + if self.arrowKeys: + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.destroy() + del self.arrowKeys + self.arrowKeys = None + self.room.reparentTo(hidden) + self.sky.reparentTo(hidden) + self.splash.reparentTo(hidden) + self.splash.stop() + self.suitSplash.reparentTo(hidden) + self.suitSplash.stop() + self.ripples.reparentTo(hidden) + self.ripples.stop() + self.hideControls() + self.roundText.hide() + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.loop('neutral') + av.resetLOD() + av.dropShadow.show() + + for x in self.tugRopes: + if x != None: + x.reparentTo(hidden) + + if self.suit: + self.suit.reparentTo(hidden) + for avId in self.avIdList: + if self.dropShadowDict.has_key(avId): + self.dropShadowDict[avId].reparentTo(hidden) + + if self.dropShadowDict.has_key(self.suitId): + self.dropShadowDict[self.suitId].reparentTo(hidden) + return + + def initCamera(self): + birdseyePosHpr = [1.95461, + 18.4891, + 38.4646, + 1.18185, + -87.5308, + 0] + introPosHpr = [None] * 2 + introPosHpr[0] = [VBase3(-11.4427, 9.03559, 2.80094), VBase3(-49.104, -0.732374, 0)] + introPosHpr[1] = [VBase3(16.9291, 13.9302, 2.64282), VBase3(66.9685, -6.195, 0)] + gameCamHpr = VBase3(-1.13, 1.042, 0) + gameCamPos = VBase3(0, 1.0838, 2.745) + camera.reparentTo(render) + camera.setPosHpr(introPosHpr[self.sides[self.localAvId]][0], introPosHpr[self.sides[self.localAvId]][1]) + lerpDur = 8 + self.introTrack = LerpPosHprInterval(camera, lerpDur, pos=gameCamPos, hpr=gameCamHpr, blendType='easeInOut', name=self.uniqueName('introLerpCameraPos')) + self.introTrack.start() + base.camLens.setFov(60 + 2 * self.numPlayers) + base.camLens.setFar(450.0) + return + + def sendGameType(self, index, suit): + if not self.hasLocalToon: + return + self.gameType = index + self.suitLevel = suit + if suit == 1: + self.suitType = 'pp' + elif suit == 2: + self.suitType = 'dt' + elif suit == 3: + self.suitType = 'gh' + elif suit == 4: + self.suitType = 'cr' + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + self.initToons() + self.createSuits() + self.calculatePositions() + self.initHandycaps() + self.initRopes() + self.initCamera() + self.animTracks = {} + for avId in self.avIdList: + self.animTracks[avId] = None + + self.animTracks[self.suitId] = None + self.showTrack = None + self.setupTrack = None + self.__initGameVars() + return + + def hideControls(self): + for x in range(len(self.arrows)): + self.arrows[x].hide() + + for rope in self.tugRopes: + if rope != None: + rope.reparentTo(hidden) + + for tex in self.ropeTex: + if tex != None: + for texi in tex: + if texi: + texi.reparentTo(hidden) + + if self.powerMeter != None: + self.powerMeter.unbind(DGG.B1PRESS) + self.powerMeter.unbind(DGG.B1RELEASE) + self.powerMeter.hide() + return + + def setUpRopes(self, notTaut): + if self.numPlayers == 1: + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[0]] + if notTaut: + self.tugRopes[0].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[0].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + elif self.numPlayers == 2: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[1]] + if notTaut: + self.tugRopes[1].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[1].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + else: + if notTaut: + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + elif self.numPlayers == 3: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[2]] + if notTaut: + self.tugRopes[2].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[2].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + self.tugRopes[2].reparentTo(render) + else: + if notTaut: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + elif self.numPlayers == 4: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.tugRopes[2].setup(3, ((self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[3]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + suitRightHand = self.suit.getRightHand() + toonRightHand = self.rightHandDict[self.avIdList[3]] + if notTaut: + self.tugRopes[3].setup(3, ((toonRightHand, (0, 0, 0)), (render, (0, 18, -1)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + midPt = (suitRightHand.getPos() - toonRightHand.getPos()) / 2.0 + self.tugRopes[3].setup(3, ((toonRightHand, (0, 0, 0)), (toonRightHand, (0, 0, 0)), (suitRightHand, (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + self.tugRopes[2].reparentTo(render) + self.tugRopes[3].reparentTo(render) + else: + self.tugRopes[2].setup(3, ((self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0)), (self.rightHandDict[self.avIdList[3]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].setup(3, ((self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[0]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + if notTaut: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (render, (0, 18, -1)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + else: + self.tugRopes[1].setup(3, ((self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[1]], (0, 0, 0)), (self.rightHandDict[self.avIdList[2]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[0].reparentTo(render) + self.tugRopes[1].reparentTo(render) + self.tugRopes[2].reparentTo(render) + + def initToons(self): + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + toon.useLOD(1000) + toon.startBlink() + toon.startLookAround() + for anim in self.toonAnimNames: + toon.pose(anim, 0) + + toon.pose('tug-o-war', 3) + self.rightHandDict[avId] = toon.getRightHands()[0] + toon.loop('neutral') + toon.dropShadow.hide() + self.dropShadowDict[avId] = self.dropShadow.copyTo(hidden) + self.dropShadowDict[avId].reparentTo(toon) + self.dropShadowDict[avId].setScale(0.35) + + def calculatePositions(self): + hprPositions = [VBase3(240, 0, 0), VBase3(120, 0, 0)] + dockPositions = [] + for k in range(5): + dockPositions.append(VBase3(-9.0 + 1.5 * k, 18, 0.1)) + + for k in range(5): + dockPositions.append(VBase3(3 + 1.5 * k, 18, 0.1)) + + self.sendUpdate('sendNewAvIdList', [self.avIdList]) + if self.numPlayers == 1: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.posDict[self.suitId] = dockPositions[7] + self.posDict[self.avIdList[0]] = dockPositions[2] + self.hprDict[self.avIdList[0]] = hprPositions[0] + else: + self.notify.warning("can't play toon vs. toon with one player") + elif self.numPlayers == 2: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1) + self.posDict[self.suitId] = dockPositions[7] + self.posDict[self.avIdList[0]] = dockPositions[1] + self.posDict[self.avIdList[1]] = dockPositions[2] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + else: + self.randomNumGen.shuffle(self.avIdList) + self.posDict[self.avIdList[0]] = dockPositions[2] + self.posDict[self.avIdList[1]] = dockPositions[7] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[1] + elif self.numPlayers == 3: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 2) + self.posDict[self.suitId] = dockPositions[7] + self.posDict[self.avIdList[0]] = dockPositions[0] + self.posDict[self.avIdList[1]] = dockPositions[1] + self.posDict[self.avIdList[2]] = dockPositions[2] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[0] + else: + self.randomNumGen.shuffle(self.avIdList) + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1) + self.posDict[self.avIdList[0]] = dockPositions[1] + self.posDict[self.avIdList[1]] = dockPositions[2] + self.posDict[self.avIdList[2]] = dockPositions[7] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[1] + elif self.numPlayers == 4: + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 3) + self.posDict[self.suitId] = dockPositions[6] + self.posDict[self.avIdList[0]] = dockPositions[0] + self.posDict[self.avIdList[1]] = dockPositions[1] + self.posDict[self.avIdList[2]] = dockPositions[2] + self.posDict[self.avIdList[3]] = dockPositions[3] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[0] + self.hprDict[self.avIdList[3]] = hprPositions[0] + else: + self.randomNumGen.shuffle(self.avIdList) + self.arrangeByHeight(self.avIdList, self.H_TO_L, 0, 1) + self.arrangeByHeight(self.avIdList, self.L_TO_H, 2, 3) + self.posDict[self.avIdList[0]] = dockPositions[1] + self.posDict[self.avIdList[1]] = dockPositions[2] + self.posDict[self.avIdList[2]] = dockPositions[7] + self.posDict[self.avIdList[3]] = dockPositions[8] + self.hprDict[self.avIdList[0]] = hprPositions[0] + self.hprDict[self.avIdList[1]] = hprPositions[0] + self.hprDict[self.avIdList[2]] = hprPositions[1] + self.hprDict[self.avIdList[3]] = hprPositions[1] + for x in self.avIdList: + self.offsetDict[x] = 0 + if self.posDict[x][0] < 0: + self.sides[x] = 0 + self.avList[0].append(x) + else: + self.sides[x] = 1 + self.avList[1].append(x) + + for avId in self.avIdList: + toon = self.getAvatar(avId) + toon.setPos(self.posDict[avId]) + toon.setHpr(self.hprDict[avId]) + + def arrangeByHeight(self, avIdList, order, iStart, iFin): + for i in range(iStart, iFin + 1): + for j in range(i + 1, iFin + 1): + if order == self.H_TO_L and self.rightHandDict[avIdList[i]].getZ() < self.rightHandDict[avIdList[j]].getZ() or order == self.L_TO_H and self.rightHandDict[avIdList[i]].getZ() > self.rightHandDict[avIdList[j]].getZ(): + temp = avIdList[i] + avIdList[i] = avIdList[j] + avIdList[j] = temp + + def disableArrow(self, a): + a.setColor(1, 0, 0, 0.3) + + def enableArrow(self, a): + a.setColor(1, 0, 0, 1) + + def hilightArrow(self, a): + a.setColor(1, 0.7, 0, 1) + + def unhilightArrow(self, a): + self.enableArrow(a) + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.handleDisabledAvatar(self, avId) + + def __playing(self): + return self.gameFSM.getCurrentState() != self.gameFSM.getFinalState() + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + if not self.__playing(): + return + DistributedMinigame.setGameStart(self, timestamp) + self.gameFSM.request('waitForGoSignal') + + def __initGameVars(self): + pass + + def makeToonLookatCamera(self, toon): + toon.headsUp(camera) + + def setText(self, t, newtext): + t['text'] = newtext + + def setTextFG(self, t, fg): + t['text_fg'] = fg + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterWaitForGoSignal(self): + self.notify.debug('enterWaitForGoSignal') + self.powerMeter.show() + self.sendUpdate('reportPlayerReady', [self.sides[self.localAvId]]) + self.roundText.show() + taskMgr.doMethodLater(TugOfWarGameGlobals.WAIT_FOR_GO_TIMEOUT, self.waitForGoTimeoutTask, self.taskName('wait-for-go-timeout')) + + def exitWaitForGoSignal(self): + taskMgr.remove(self.taskName('wait-for-go-timeout')) + + def enterTug(self): + self.notify.debug('enterTug') + self.__spawnUpdateIdealRateTask() + self.__spawnUpdateTimerTask() + self.__spawnUpdateKeyPressRateTask() + taskMgr.doMethodLater(TugOfWarGameGlobals.TUG_TIMEOUT, self.tugTimeoutTask, self.taskName('tug-timeout')) + if self.suit: + self.suit.loop('tug-o-war') + + def exitTug(self): + self.notify.debug('exitTug') + if self.suit: + self.suit.loop('neutral') + self.timer.stop() + self.timer.hide() + taskMgr.remove(self.taskName('tug-timeout')) + + def enterGameDone(self): + pass + + def exitGameDone(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.__killUpdateIdealRateTask() + self.__killUpdateTimerTask() + self.__killUpdateKeyPressRateTask() + self.__killUpdateRopeTask() + + def exitCleanup(self): + pass + + def __gameTimerExpired(self): + self.notify.debug('game timer expired') + if self.arrowKeys: + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + + def __pressHandler(self, index): + self.notify.debug('pressHandler') + if index == self.buttons[0]: + self.hilightArrow(self.arrows[index]) + self.keyTTL.insert(0, 1.0) + self.buttons.reverse() + + def __releaseHandler(self, index): + self.notify.debug('releaseHandler') + if index in self.buttons: + self.unhilightArrow(self.arrows[index]) + + def __updateKeyPressRateTask(self, task): + if self.gameFSM.getCurrentState().getName() != 'tug': + return Task.done + for i in range(len(self.keyTTL)): + self.keyTTL[i] -= 0.1 + + for i in range(len(self.keyTTL)): + if self.keyTTL[i] <= 0: + a = self.keyTTL[0:i] + del self.keyTTL + self.keyTTL = a + break + + self.keyRate = len(self.keyTTL) + if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1: + self.rateMatchAward += 0.3 + else: + self.rateMatchAward = 0 + self.__spawnUpdateKeyPressRateTask() + return Task.done + + def __updateTimerTask(self, task): + if self.gameFSM.getCurrentState().getName() != 'tug': + return Task.done + self.currentForce = self.computeForce(self.keyRate) + self.sendUpdate('reportCurrentKeyRate', [self.keyRate, self.currentForce]) + self.setSpeedGauge() + self.setAnimState(self.localAvId, self.keyRate) + self.__spawnUpdateTimerTask() + return Task.done + + def __spawnUpdateTimerTask(self): + taskMgr.remove(self.taskName(self.UPDATE_TIMER_TASK)) + taskMgr.doMethodLater(TugOfWarGameGlobals.SEND_UPDATE, self.__updateTimerTask, self.taskName(self.UPDATE_TIMER_TASK)) + + def __killUpdateTimerTask(self): + taskMgr.remove(self.taskName(self.UPDATE_TIMER_TASK)) + + def __spawnUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + taskMgr.doMethodLater(0.1, self.__updateKeyPressRateTask, self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __killUpdateKeyPressRateTask(self): + taskMgr.remove(self.taskName(self.UPDATE_KEY_PRESS_RATE_TASK)) + + def __spawnUpdateIdealRateTask(self): + self.idealRate = self.targetRateList[self.nextRateIndex][1] + self.idealForce = self.advantage * (4 + 0.4 * self.idealRate) + taskMgr.doMethodLater(self.targetRateList[self.nextRateIndex][0], self.__updateIdealRateTask, self.taskName('targetRateTimer')) + + def __updateIdealRateTask(self, task): + self.nextRateIndex = self.nextRateIndex + 1 + if self.nextRateIndex < len(self.targetRateList): + if self.nextRateIndex == len(self.targetRateList) - 1: + self.allOutMode = 1 + self.idealRate = self.targetRateList[self.nextRateIndex][1] + self.idealForce = self.advantage * (4 + 0.4 * self.idealRate) + taskMgr.doMethodLater(self.targetRateList[self.nextRateIndex][0], self.__updateIdealRateTask, self.taskName('targetRateTimer')) + return Task.done + + def __killUpdateIdealRateTask(self): + taskMgr.remove(self.taskName('targetRateTimer')) + + def sendGoSignal(self, index): + if not self.hasLocalToon: + return + self.notify.debug('sendGoSignal') + self.buttons = index + self.setupTrack = None + self.showTrack = None + + def startTimer(self = self): + self.currentStartTime = int(globalClock.getFrameTime() * 1000) + time = 10 + self.timer.show() + self.timer.setTime(TugOfWarGameGlobals.GAME_DURATION) + self.timer.countdown(TugOfWarGameGlobals.GAME_DURATION, self.__gameTimerExpired) + + def enableKeys(self = self): + + def keyPress(self, index): + self.__pressHandler(index) + + def keyRelease(self, index): + self.__releaseHandler(index) + + self.arrowKeys.setPressHandlers([lambda self = self, keyPress = keyPress: keyPress(self, 2), + lambda self = self, keyPress = keyPress: keyPress(self, 3), + lambda self = self, keyPress = keyPress: keyPress(self, 1), + lambda self = self, keyPress = keyPress: keyPress(self, 0)]) + self.arrowKeys.setReleaseHandlers([lambda self = self, keyRelease = keyRelease: keyRelease(self, 2), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 3), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 1), + lambda self = self, keyRelease = keyRelease: keyRelease(self, 0)]) + for x in index: + self.enableArrow(self.arrows[x]) + + if self.introTrack != None: + self.introTrack.finish() + self.introTrack = None + self.setupTrack = Sequence(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameReady), Wait(1.5), Func(base.playSfx, self.whistleSound), Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameGo), Func(self.roundText.setScale, 0.3), Wait(1.5), Func(startTimer), Func(enableKeys), Func(self.gameFSM.request, 'tug'), Func(self.setText, self.roundText, ' '), Func(self.roundText.setScale, 0.2)) + self.setupTrack.start() + return + + def sendStopSignal(self, winners, losers, tieers): + if not self.hasLocalToon: + return + self.notify.debug('sendStopSignal') + self.gameFSM.request('gameDone') + self.hideControls() + reactSeq = Sequence() + exitSeq = Sequence() + suitSlipTime = 0 + if self.gameFSM.getCurrentState().getName() == 'cleanup' or not self.randomNumGen: + return + if self.suit: + if self.suitId in winners: + newPos = VBase3(2.65, 18, 0.1) + randInt = self.randomNumGen.randrange(0, 10) + oopsTrack = Wait(0) + if randInt < 3: + suitSlipTime = 2.2 + waterPos = VBase3(0, 16, -5) + newPos -= VBase3(0.4, 0, 0) + self.suitSplash.stop() + self.suitSplash.setPos(waterPos[0], waterPos[1], -1.8) + self.suitSplash.setScale(3.5, 3.5, 1) + self.suitRipples.setPos(waterPos[0], waterPos[1], -1.7) + self.suitRipples.setScale(1, 1, 1) + startHpr = self.suit.getHpr() + destHpr = startHpr + VBase3(0, 0, -30) + oopsTrack = Sequence(Parallel(Func(self.suit.play, 'flail', None, 26, 38), LerpHprInterval(self.suit, 0.5, destHpr, startHpr=startHpr)), Parallel(Func(self.suit.play, 'slip-forward'), LerpPosInterval(self.suit, duration=1, pos=waterPos), Sequence(Wait(0.55), Func(base.playSfx, self.sndHitWater), Func(self.suitSplash.play), Func(self.ripples.play)))) + reactSeq.append(Sequence(Func(self.suit.loop, 'victory'), Wait(2.6), LerpPosInterval(self.suit, duration=2, pos=newPos), oopsTrack, Func(self.suit.loop, 'neutral'))) + for avId in self.avIdList: + toon = self.getAvatar(avId) + toon.loop('neutral') + if avId in winners: + reactSeq.append(Func(toon.loop, 'victory')) + elif avId in losers: + reactSeq.append(Func(toon.loop, 'neutral')) + else: + reactSeq.append(Func(toon.loop, 'neutral')) + + if self.localAvId in winners: + exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameEnd)) + exitSeq.append(Wait(5.0)) + elif self.localAvId in losers: + exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameEnd)) + exitSeq.append(Wait(4.8 + suitSlipTime)) + else: + exitSeq.append(Func(self.setText, self.roundText, TTLocalizer.TugOfWarGameTie)) + exitSeq.append(Wait(2.5)) + exitSeq.append(Func(self.gameOver)) + self.showTrack = Parallel(reactSeq, exitSeq) + for x in self.animTracks.values(): + if x != None: + x.finish() + + self.showTrack.start() + if self.arrowKeys: + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + return + + def remoteKeyRateUpdate(self, avId, keyRate): + if not self.hasLocalToon: + return + if avId != self.localAvId: + self.setAnimState(avId, keyRate) + + def sendSuitPosition(self, suitOffset): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'tug': + return + self.suitOffset = suitOffset + self.moveSuits() + + def sendCurrentPosition(self, avIdList, offsetList): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'tug': + return + for i in range(len(avIdList)): + self.offsetDict[avIdList[i]] = offsetList[i] + + self.moveToons() + self.setUpRopes(0) + + def createSuits(self): + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.suit = Suit.Suit() + self.origSuitPosHpr = [VBase3(6.0, 18, 0.1), VBase3(120, 0, 0)] + self.suitOffset = 0 + d = SuitDNA.SuitDNA() + d.newSuit(self.suitType) + self.suit.setDNA(d) + self.suit.reparentTo(render) + self.suit.setPos(self.origSuitPosHpr[0]) + self.suit.setHpr(self.origSuitPosHpr[1]) + for anim in self.suitAnimNames: + self.suit.pose(anim, 0) + + self.suit.pose('tug-o-war', 0) + self.dropShadowDict[self.suitId] = self.dropShadow.copyTo(hidden) + self.dropShadowDict[self.suitId].reparentTo(self.suit) + self.dropShadowDict[self.suitId].setScale(0.45) + + def initHandycaps(self): + if self.numPlayers == 3 and self.gameType == TugOfWarGameGlobals.TOON_VS_TOON: + if len(self.avList[0]) == 1: + toon = self.getAvatar(self.avList[0][0]) + if self.avList[0][0] == self.localAvId: + self.advantage = 2.0 + toon.applyCheesyEffect(ToontownGlobals.CEBigHead) + elif len(self.avList[1]) == 1: + toon = self.getAvatar(self.avList[1][0]) + if self.avList[1][0] == self.localAvId: + self.advantage = 2.0 + toon.applyCheesyEffect(ToontownGlobals.CEBigHead) + + def setSpeedGauge(self): + self.powerMeter.setPower(self.keyRate) + self.powerMeter.setTarget(self.idealRate) + if not self.allOutMode: + self.powerMeter.updateTooSlowTooFast() + if not self.allOutMode: + index = float(self.currentForce) / self.idealForce + bonus = 0.0 + if index > 1: + bonus = max(1, index - 1) + index = 1 + color = (0, + 0.75 * index + 0.25 * bonus, + 0.75 * (1 - index), + 0.5) + self.powerMeter.setBarColor(color) + else: + self.powerMeter.setBarColor((0, 1, 0, 0.5)) + + def setAnimState(self, avId, keyRate): + if self.gameFSM.getCurrentState().getName() != 'tug': + return + toon = self.getAvatar(avId) + if keyRate > 0 and self.pullingDict[avId] == 0: + toon.loop('tug-o-war') + self.pullingDict[avId] = 1 + if keyRate <= 0 and self.pullingDict[avId] == 1: + toon.pose('tug-o-war', 3) + toon.startLookAround() + self.pullingDict[avId] = 0 + + def moveSuits(self): + if self.gameType != TugOfWarGameGlobals.TOON_VS_COG: + return + origPos = self.origSuitPosHpr[0] + curPos = self.suit.getPos() + newPos = VBase3(origPos[0] + self.suitOffset, curPos[1], curPos[2]) + if self.animTracks[self.suitId] != None: + if self.animTracks[self.suitId].isPlaying(): + self.animTracks[self.suitId].finish() + self.checkIfFallen() + if self.suitId not in self.fallenList: + self.animTracks[self.suitId] = Sequence(LerpPosInterval(self.suit, duration=TugOfWarGameGlobals.SEND_UPDATE, pos=newPos), Func(self.checkIfFallen)) + self.animTracks[self.suitId].start() + return + + def moveToons(self): + for avId in self.avIdList: + if avId not in self.fallenList: + toon = self.getAvatar(avId) + if toon: + origPos = self.posDict[avId] + curPos = toon.getPos() + newPos = VBase3(origPos[0] + self.offsetDict[avId] / self.handycap, curPos[1], curPos[2]) + if self.animTracks[avId] != None: + if self.animTracks[avId].isPlaying(): + self.animTracks[avId].finish() + self.checkIfFallen(avId) + if avId not in self.fallenList: + self.animTracks[avId] = Sequence(LerpPosInterval(toon, duration=TugOfWarGameGlobals.SEND_UPDATE, pos=newPos), Func(self.checkIfFallen, avId)) + self.animTracks[avId].start() + + return + + def checkIfFallen(self, avId = None): + if avId == None: + if self.suitId not in self.fallenList: + curPos = self.suit.getPos() + if curPos[0] < 0 and curPos[0] > -2 or curPos[0] > 0 and curPos[0] < 2: + self.hideControls() + self.throwInWater() + losingSide = 1 + self.sendUpdate('reportEndOfContest', [losingSide]) + elif avId not in self.fallenList: + toon = self.getAvatar(avId) + if toon: + curPos = toon.getPos() + if curPos[0] < 0 and curPos[0] > -2 or curPos[0] > 0 and curPos[0] < 2: + self.hideControls() + losingSide = self.sides[avId] + for avId in self.avIdList: + if self.sides[avId] == losingSide: + self.throwInWater(avId) + + self.sendUpdate('reportEndOfContest', [losingSide]) + return + + def throwInWater(self, avId = None): + if avId == None: + self.fallenList.append(self.suitId) + waterPos = self.drinkPositions.pop() + newPos = VBase3(waterPos[0], waterPos[1], waterPos[2] - self.suit.getHeight() / 1.5) + self.suit.loop('neutral') + self.dropShadowDict[self.suitId].reparentTo(hidden) + loser = self.suit + animId = self.suitId + else: + self.fallenList.append(avId) + toon = self.getAvatar(avId) + waterPos = self.drinkPositions.pop() + newPos = VBase3(waterPos[0], waterPos[1], waterPos[2] - toon.getHeight()) + toon.loop('neutral') + self.dropShadowDict[avId].reparentTo(hidden) + loser = toon + animId = avId + if self.animTracks[animId] != None: + if self.animTracks[animId].isPlaying(): + self.animTracks[animId].finish() + self.splash.setPos(newPos[0], newPos[1], -1.8) + self.splash.setScale(2.5, 2.5, 1) + self.ripples.setPos(newPos[0], newPos[1], -1.7) + self.ripples.setScale(1, 1, 1) + self.animTracks[animId] = Sequence(Parallel(ActorInterval(actor=loser, animName='slip-forward', duration=2.0), LerpPosInterval(loser, duration=2.0, pos=newPos), Sequence(Wait(1.0), Parallel(Func(base.playSfx, self.sndHitWater), Func(self.splash.play), Func(self.ripples.play)))), Func(loser.loop, 'neutral')) + self.animTracks[animId].start() + return + + def computeForce(self, keyRate): + F = 0 + if self.allOutMode == 1: + F = 0.75 * keyRate + else: + stdDev = 0.25 * self.idealRate + F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2))) + return F + + def initRopes(self): + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + numRopes = self.numPlayers + else: + numRopes = self.numPlayers - 1 + for x in range(0, numRopes): + rope = Rope.Rope(self.uniqueName('TugRope' + str(x))) + if rope.showRope: + rope.ropeNode.setRenderMode(RopeNode.RMBillboard) + rope.ropeNode.setThickness(0.2) + rope.setTexture(self.ropeTexture) + rope.ropeNode.setUvMode(RopeNode.UVDistance) + rope.ropeNode.setUvDirection(1) + rope.setTransparency(1) + rope.setColor(0.89, 0.89, 0.6, 1) + self.tugRopes.append(rope) + + self.setUpRopes(1) + + def __spawnUpdateRopeTask(self): + taskMgr.remove(self.taskName(self.UPDATE_ROPE_TASK)) + taskMgr.add(self.__updateRopeTask, self.taskName(self.UPDATE_ROPE_TASK)) + + def __updateRopeTask(self, task): + if self.tugRopes != None: + for i in range(len(self.tugRopes)): + if self.tugRopes[i] != None: + self.ropePts[i] = self.tugRopes[i].getPoints(len(self.ropeTex[i])) + for j in range(len(self.ropePts[i])): + self.ropeTex[i][j].setPos(self.ropePts[i][j]) + + return Task.cont + + def __killUpdateRopeTask(self): + taskMgr.remove(self.taskName(self.UPDATE_ROPE_TASK)) + + def tugTimeoutTask(self, task): + self.gameOver() + return Task.done + + def waitForGoTimeoutTask(self, task): + self.gameOver() + return Task.done + + def __spawnMouseSpeedTask(self): + taskMgr.remove(self.taskName('mouseSpeed')) + taskMgr.add(self.__mouseSpeedTask, self.taskName('mouseSpeed')) + + def __killMouseSpeedTask(self): + taskMgr.remove(self.taskName('mouseSpeed')) + + def __mouseSpeedTask(self, task): + dx = 0.1 + if self.mouseMode: + mx = base.mouseWatcherNode.getMouseX() + my = base.mouseWatcherNode.getMouseY() + if self.mouseSide == 0: + if mx > dx: + self.mouseSide = 1 + self.__releaseHandler(1) + self.__pressHandler(0) + elif mx > -dx: + self.__releaseHandler(1) + elif self.mouseSide == 1: + if mx < -dx: + self.mouseSide = 0 + self.__releaseHandler(0) + self.__pressHandler(1) + elif mx < dx: + self.__releaseHandler(0) + return Task.cont diff --git a/toontown/minigame/DistributedTugOfWarGameAI.py b/toontown/minigame/DistributedTugOfWarGameAI.py new file mode 100644 index 0000000..1d43803 --- /dev/null +++ b/toontown/minigame/DistributedTugOfWarGameAI.py @@ -0,0 +1,333 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import random +from direct.task.Task import Task +import copy +import TugOfWarGameGlobals +import math + +class DistributedTugOfWarGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedTugOfWarGameAI_initialized + except: + self.DistributedTugOfWarGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTugOfWarGameAI', [State.State('off', self.enterInactive, self.exitInactive, ['waitClientsReady', 'cleanup']), + State.State('waitClientsReady', self.enterWaitClientsReady, self.exitWaitClientsReady, ['sendGoSignal', 'cleanup']), + State.State('sendGoSignal', self.enterSendGoSignal, self.exitSendGoSignal, ['waitForResults', 'cleanup']), + State.State('waitForResults', self.enterWaitForResults, self.exitWaitForResults, ['waitClientsReady', 'contestOver', 'cleanup']), + State.State('contestOver', self.enterContestOver, self.exitContestOver, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['off'])], 'off', 'off') + self.addChildGameFSM(self.gameFSM) + + self.switched = 0 + self.side = {} + self.forceDict = [{}, {}] + self.keyRateDict = {} + self.howManyReported = 0 + self.offsetDict = {} + self.kMovement = 0.02 + self.suitId = 666 + self.suitForces = [(6, 4), + (2, 5), + (2, 6.5), + (3, 8.0), + (5, 6), + (8, 8), + (9, 8.5), + (4, 9)] + self.suitForceMultiplier = 0.75 + self.curSuitForce = 0 + self.suitOffset = 0 + self.contestEnded = 0 + self.losingSide = -1 + self.winners = [] + self.losers = [] + self.tieers = [] + self.timeBonus = float(TugOfWarGameGlobals.TIME_BONUS_RANGE) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + del self.side + del self.forceDict + del self.keyRateDict + del self.offsetDict + del self.suitForces + del self.winners + del self.tieers + del self.losers + DistributedMinigameAI.delete(self) + + def setGameReady(self): + self.notify.debug('setGameReady') + self.suitType = random.randrange(1, 5) + self.suitJellybeanReward = math.pow(2, self.suitType - 1) + if self.isSinglePlayer(): + self.gameType = TugOfWarGameGlobals.TOON_VS_COG + self.suitForceMultiplier = 0.58 + float(self.suitType) / 10.0 + else: + randInt = random.randrange(0, 10) + if randInt < 8: + self.gameType = TugOfWarGameGlobals.TOON_VS_COG + self.suitForceMultiplier = 0.65 + float(self.suitType) / 16.0 + self.kMovement = 0.02 + else: + self.gameType = TugOfWarGameGlobals.TOON_VS_TOON + self.kMovement = 0.04 + self.sendUpdate('sendGameType', [self.gameType, self.suitType]) + DistributedMinigameAI.setGameReady(self) + self.__initGameVars() + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('waitClientsReady') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def __initGameVars(self): + self.currentButtons = [0, 0] + self.readyClients = [] + + def enterWaitClientsReady(self): + self.notify.debug('enterWaitClientsReady') + taskMgr.doMethodLater(TugOfWarGameGlobals.WAIT_FOR_CLIENTS_TIMEOUT, self.waitForClientsTimeout, self.taskName('clients-timeout')) + + def exitWaitClientsReady(self): + taskMgr.remove(self.taskName('clients-timeout')) + + def waitForClientsTimeout(self, task): + self.notify.debug('Done waiting for clients') + self.sendUpdate('sendStopSignal', [self.winners, self.losers, self.tieers]) + self.gameOver() + return Task.done + + def reportPlayerReady(self, side): + avId = self.air.getAvatarIdFromSender() + if avId in self.readyClients: + self.notify.warning('Got reportPlayerReady from an avId more than once: %s' % avId) + return + if avId not in self.avIdList or side not in [0, 1]: + self.notify.warning('Got reportPlayerReady from an avId: %s not in our list: %s' % (avId, self.avIdList)) + else: + self.readyClients.append(avId) + self.side[avId] = side + self.forceDict[side][avId] = 0 + self.offsetDict[avId] = 0 + if len(self.readyClients) == self.numPlayers: + self.readyClients = [] + self.gameFSM.request('sendGoSignal') + + def sendNewAvIdList(self, newAvIdList): + for avId in newAvIdList: + if avId not in self.scoreDict: + self.notify.debug('invalid avId in new list from %s' % self.air.getAvatarIdFromSender()) + return + + if not self.switched: + self.switched = 1 + self.avIdList = newAvIdList + elif self.avIdList != newAvIdList: + self.notify.debug('Big trouble in little TugOWar Town') + + def enterSendGoSignal(self): + self.notify.debug('enterSendGoSignal') + taskMgr.doMethodLater(TugOfWarGameGlobals.GAME_DURATION, self.timerExpired, self.taskName('gameTimer')) + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.curSuitForceInd = 0 + taskMgr.add(self.timeForNewSuitForce, self.taskName('suitForceTimer')) + taskMgr.doMethodLater(1, self.calcTimeBonus, self.taskName('timeBonusTimer')) + self.sendUpdate('sendGoSignal', [[0, 1]]) + self.gameFSM.request('waitForResults') + + def timerExpired(self, task): + self.notify.debug('timer expired') + self.gameFSM.request('contestOver') + return Task.done + + def timeForNewSuitForce(self, task): + self.notify.debug('timeForNewSuitForce') + if self.curSuitForceInd < len(self.suitForces): + randForce = random.random() - 0.5 + self.curSuitForce = self.suitForceMultiplier * self.numPlayers * (self.suitForces[self.curSuitForceInd][1] + randForce) + taskMgr.doMethodLater(self.suitForces[self.curSuitForceInd][0], self.timeForNewSuitForce, self.taskName('suitForceTimer')) + self.curSuitForceInd += 1 + return Task.done + + def calcTimeBonus(self, task): + delta = float(TugOfWarGameGlobals.TIME_BONUS_RANGE) / float(TugOfWarGameGlobals.GAME_DURATION) + self.timeBonus = self.timeBonus - delta + taskMgr.doMethodLater(1, self.calcTimeBonus, self.taskName('timeBonusTimer')) + return Task.done + + def exitSendGoSignal(self): + pass + + def enterWaitForResults(self): + self.notify.debug('enterWaitForResults') + + def calculateOffsets(self): + f = [0, 0] + for i in [0, 1]: + for x in self.forceDict[i].values(): + f[i] += x + + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + f[1] += self.curSuitForce + deltaF = f[1] - f[0] + deltaX = deltaF * self.kMovement + for avId in self.avIdList: + offset = deltaX + if self.side[avId] == 0: + if deltaX < 0: + offset = deltaX / 2.0 + elif deltaX > 0: + offset = deltaX / 2.0 + self.offsetDict[avId] += offset + + if deltaX < 0: + self.suitOffset += deltaX + else: + self.suitOffset += deltaX / 2.0 + + def reportCurrentKeyRate(self, keyRate, force): + avId = self.air.getAvatarIdFromSender() + if not self.side.has_key(avId): + self.notify.warning('Avatar %s sent reportCurrentKeyRate too early %s' % (avId, self.side)) + return + self.keyRateDict[avId] = keyRate + self.forceDict[self.side[avId]][avId] = force + self.sendUpdate('remoteKeyRateUpdate', [avId, self.keyRateDict[avId]]) + self.howManyReported += 1 + if self.howManyReported == self.numPlayers: + self.howManyReported = 0 + self.calculateOffsets() + self.sendUpdate('sendCurrentPosition', [self.offsetDict.keys(), self.offsetDict.values()]) + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + self.sendUpdate('sendSuitPosition', [self.suitOffset]) + + def reportEndOfContest(self, index): + if index not in [0, 1]: + self.notify.warning('Got a bad index %s ' % index) + return + self.losingSide = index + self.notify.debug('losing side = %d' % self.losingSide) + if self.contestEnded == 0: + self.contestEnded = 1 + self.gameFSM.request('contestOver') + + def __processResults(self): + if self.contestEnded: + self.timeBonus = TugOfWarGameGlobals.TIME_BONUS_MIN + int(self.timeBonus + 0.5) + if self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + if self.losingSide == 1: + self.losers.append(self.suitId) + else: + self.winners.append(self.suitId) + for i in range(0, self.numPlayers): + avId = self.avIdList[i] + if self.side[avId] != self.losingSide: + self.scoreDict[avId] = self.suitJellybeanReward + TugOfWarGameGlobals.WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.LOSS_JELLYBEANS + self.losers.append(avId) + + else: + for i in range(0, self.numPlayers): + avId = self.avIdList[i] + if self.side[avId] != self.losingSide: + self.scoreDict[avId] = TugOfWarGameGlobals.WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.LOSS_JELLYBEANS + self.losers.append(avId) + + elif self.gameType == TugOfWarGameGlobals.TOON_VS_COG: + for i in range(0, self.numPlayers): + avId = self.avIdList[i] + if -self.offsetDict[avId] > self.suitOffset: + self.scoreDict[avId] = self.suitJellybeanReward / 2 + TugOfWarGameGlobals.TIE_WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = self.suitJellybeanReward / 2 + TugOfWarGameGlobals.TIE_LOSS_JELLYBEANS + self.losers.append(avId) + self.winners.append(self.suitId) + + else: + maxOffset = -100 + minOffset = 100 + for i in range(0, self.numPlayers): + avId = self.avIdList[i] + if self.side[avId] == 0: + if -self.offsetDict[avId] > maxOffset: + maxOffset = -self.offsetDict[avId] + elif -self.offsetDict[avId] < minOffset: + minOffset = -self.offsetDict[avId] + elif self.side[avId] == 1: + if self.offsetDict[avId] > maxOffset: + maxOffset = self.offsetDict[avId] + elif self.offsetDict[avId] < minOffset: + minOffset = self.offsetDict[avId] + + for i in range(0, self.numPlayers): + avId = self.avIdList[i] + if maxOffset != minOffset: + if self.side[avId] == 0: + if -self.offsetDict[avId] == maxOffset: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_LOSS_JELLYBEANS + self.losers.append(avId) + elif self.side[avId] == 1: + if self.offsetDict[avId] == maxOffset: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_WIN_JELLYBEANS + self.winners.append(avId) + else: + self.scoreDict[avId] = TugOfWarGameGlobals.TIE_LOSS_JELLYBEANS + self.losers.append(avId) + else: + self.scoreDict[avId] += TugOfWarGameGlobals.TIE_JELLYBEANS + self.tieers.append(avId) + + self.gameOver() + + def exitWaitForResults(self): + pass + + def enterContestOver(self): + self.__processResults() + self.sendUpdate('sendStopSignal', [self.winners, self.losers, self.tieers]) + + def exitContestOver(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + taskMgr.remove(self.taskName('gameTimer')) + taskMgr.remove(self.taskName('timeBonusTimer')) + taskMgr.remove(self.taskName('suitForceTimer')) + self.gameFSM.request('off') + + def exitCleanup(self): + pass diff --git a/toontown/minigame/DistributedTwoDGame.py b/toontown/minigame/DistributedTwoDGame.py new file mode 100644 index 0000000..90e7e34 --- /dev/null +++ b/toontown/minigame/DistributedTwoDGame.py @@ -0,0 +1,546 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase import TTLocalizer +from direct.gui.DirectFrame import DirectFrame +from direct.gui.DirectGui import DGG +from direct.task.Task import Task +from direct.fsm import ClassicFSM, State +from direct.directnotify import DirectNotifyGlobal +from DistributedMinigame import * +import MinigameAvatarScorePanel, ArrowKeys, ToonBlitzAssetMgr, TwoDCamera +import TwoDSectionMgr, ToonBlitzGlobals, TwoDGameToonSD +from toontown.toonbase import ToontownTimer +from TwoDWalk import * +from TwoDDrive import * +COLOR_RED = VBase4(1, 0, 0, 0.3) + +class DistributedTwoDGame(DistributedMinigame): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTwoDGame') + UpdateLocalToonTask = 'ToonBlitzUpdateLocalToonTask' + EndGameTaskName = 'endTwoDGame' + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTwoDGame', [State.State('off', self.enterOff, self.exitOff, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'pause', 'showScores']), + State.State('pause', self.enterPause, self.exitPause, ['cleanup', 'play', 'showScores']), + State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.reportedDone = False + self.showCollSpheres = False + + def getTitle(self): + return TTLocalizer.TwoDGameTitle + + def getInstructions(self): + return TTLocalizer.TwoDGameInstructions + + def getMaxDuration(self): + return 200 + + def __defineConstants(self): + self.TOON_SPEED = 12.0 + self.MAX_FRAME_MOVE = 1 + self.isHeadInFloor = False + self.timeToRunToElevator = 1.5 + + def setSectionsSelected(self, sectionsSelected): + self.sectionsSelected = sectionsSelected + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.__defineConstants() + self.assetMgr = ToonBlitzAssetMgr.ToonBlitzAssetMgr(self) + self.cameraMgr = TwoDCamera.TwoDCamera(camera) + self.sectionMgr = TwoDSectionMgr.TwoDSectionMgr(self, self.sectionsSelected) + self.gameStartX = -40.0 + endSection = self.sectionMgr.sections[-1] + self.gameEndX = endSection.spawnPointMgr.gameEndX + self.gameLength = self.gameEndX - self.gameStartX + self.toonSDs = {} + avId = self.localAvId + toonSD = TwoDGameToonSD.TwoDGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + self.toonSDs[avId].createHeadFrame(0) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + taskMgr.remove(self.UpdateLocalToonTask) + for avId in self.toonSDs.keys(): + toonSD = self.toonSDs[avId] + toonSD.destroy() + + del self.toonSDs + self.cameraMgr.destroy() + del self.cameraMgr + self.sectionMgr.destroy() + del self.sectionMgr + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + self.assetMgr.destroy() + del self.assetMgr + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + self.scorePanels = [] + self.assetMgr.onstage() + lt = base.localAvatar + lt.reparentTo(render) + lt.hideName() + self.__placeToon(self.localAvId) + lt.setAnimState('Happy', 1.0) + lt.setSpeed(0, 0) + base.localAvatar.collisionsOn() + base.localAvatar.setTransparency(1) + self.setupHeadCollision() + self.cameraMgr.onstage() + toonSD = self.toonSDs[self.localAvId] + toonSD.enter() + toonSD.fsm.request('normal') + self.twoDDrive = TwoDDrive(self, self.TOON_SPEED, maxFrameMove=self.MAX_FRAME_MOVE) + + def offstage(self): + self.notify.debug('offstage') + self.assetMgr.offstage() + for avId in self.toonSDs.keys(): + self.toonSDs[avId].exit() + + base.localAvatar.setTransparency(0) + self.ignore('enterheadCollSphere-into-floor1') + base.localAvatar.controlManager.currentControls.cTrav.removeCollider(self.headCollNP) + self.headCollNP.removeNode() + del self.headCollNP + base.localAvatar.laffMeter.start() + DistributedMinigame.offstage(self) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + drawNum = 0 + for avId in self.remoteAvIdList: + toon = self.getAvatar(avId) + if toon: + drawNum += 1 + toon.reparentTo(render) + toon.setAnimState('Happy', 1.0) + toon.hideName() + toon.startSmooth() + toon.startLookAround() + distCNP = toon.find('**/distAvatarCollNode*') + distCNP.node().setIntoCollideMask(BitMask32.allOff()) + toonSD = TwoDGameToonSD.TwoDGameToonSD(avId, self) + self.toonSDs[avId] = toonSD + toonSD.enter() + toonSD.fsm.request('normal') + self.toonSDs[avId].createHeadFrame(drawNum) + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + self.twoDWalk = TwoDWalk(self.twoDDrive, broadcast=not self.isSinglePlayer()) + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(0.75 - spacing * (self.numPlayers - 1 - i), 0.0, 0.85) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + self.gameFSM.request('play', [timestamp]) + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self, timestamp): + self.notify.debug('enterPlay') + elapsedTime = globalClockDelta.localElapsedTime(timestamp) + self.sectionMgr.enterPlay(elapsedTime) + handlers = [None, + None, + None, + None, + self.shootKeyHandler] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + self.twoDWalk.start() + self.accept('jumpStart', self.startJump) + self.accept('enemyHit', self.localToonHitByEnemy) + self.accept('twoDTreasureGrabbed', self.__treasureGrabbed) + self.accept('enemyShot', self.__enemyShot) + taskMgr.remove(self.UpdateLocalToonTask) + taskMgr.add(self.__updateLocalToonTask, self.UpdateLocalToonTask, priority=1) + base.localAvatar.laffMeter.stop() + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(ToonBlitzGlobals.GameDuration[self.getSafezoneId()]) + self.timer.countdown(ToonBlitzGlobals.GameDuration[self.getSafezoneId()], self.timerExpired) + return + + def exitPlay(self): + handlers = [None, + None, + None, + None, + None] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + if self.toonSDs[self.localAvId].fsm.getCurrentState().getName() != 'victory': + base.localAvatar.b_setAnimState('Happy', 1.0) + self.ignore('jumpStart') + self.ignore('enemyHit') + self.ignore('twoDTreasureGrabbed') + return + + def enterPause(self): + self.notify.debug('enterPause') + + def exitPause(self): + pass + + def enterShowScores(self): + self.notify.debug('enterShowScores') + lerpTrack = Parallel() + lerpDur = 0.5 + tY = 0.6 + bY = -.6 + lX = -.7 + cX = 0 + rX = 0.7 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, bY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 1.5, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, self.getElevatorCloseTrack(), Sequence(Wait(ToonBlitzGlobals.ShowScoresDuration), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitShowScores(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.timer.stop() + self.timer.destroy() + del self.timer + taskMgr.remove(self.EndGameTaskName) + self.twoDWalk.stop() + self.twoDWalk.destroy() + del self.twoDWalk + self.twoDDrive = None + del self.twoDDrive + return + + def exitCleanup(self): + pass + + def acceptInputs(self): + if hasattr(self, 'twoDDrive'): + handlers = [None, + None, + None, + None, + self.shootKeyHandler] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + self.twoDDrive.start() + return + + def ignoreInputs(self): + if hasattr(self, 'twoDDrive'): + handlers = [None, + None, + None, + None, + None] + self.twoDDrive.arrowKeys.setPressHandlers(handlers) + self.twoDDrive.lastAction = None + self.twoDDrive.stop() + return + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + self.cameraMgr.update() + if self.gameFSM.getCurrentState().getName() == 'play': + if not self.toonSDs[self.localAvId].fsm.getCurrentState().getName() == 'victory': + if not base.localAvatar.getY() == 0: + base.localAvatar.setY(0) + if base.localAvatar.getZ() < -2.0: + self.localToonFellDown() + for avId in self.toonSDs.keys(): + self.toonSDs[avId].update() + + return task.cont + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + DistributedMinigame.handleDisabledAvatar(self, avId) + + def setupHeadCollision(self): + collSphere = CollisionSphere(0, 0, 0, 1) + collSphere.setTangible(1) + collNode = CollisionNode('headCollSphere') + collNode.setFromCollideMask(ToontownGlobals.WallBitmask) + collNode.setIntoCollideMask(BitMask32.allOff()) + collNode.addSolid(collSphere) + head = base.localAvatar.getPart('head', '1000') + self.headCollNP = head.attachNewNode(collNode) + self.headCollNP.setPos(0, 0, 0.0) + animal = base.localAvatar.style.getAnimal() + if animal == 'dog' or animal == 'bear' or animal == 'horse': + torso = base.localAvatar.style.torso + legs = base.localAvatar.style.legs + if (torso == 'ls' or torso == 'ld') and legs == 'l': + self.headCollNP.setZ(-1.3) + else: + self.headCollNP.setZ(-0.7) + elif animal == 'mouse' or animal == 'duck': + self.headCollNP.setZ(0.5) + elif animal == 'cat': + self.headCollNP.setZ(-0.3) + elif animal == 'rabbit': + self.headCollNP.setZ(-0.5) + elif animal == 'monkey': + self.headCollNP.setZ(0.3) + elif animal == 'pig': + self.headCollNP.setZ(-0.7) + self.headCollNP.hide() + if self.showCollSpheres: + self.headCollNP.show() + headCollisionEvent = CollisionHandlerEvent() + headCollisionEvent.addInPattern('enter%fn-into-%in') + headCollisionEvent.addOutPattern('%fn-exit-%in') + cTrav = base.localAvatar.controlManager.currentControls.cTrav + cTrav.addCollider(self.headCollNP, headCollisionEvent) + self.accept('enterheadCollSphere-into-floor1', self.__handleHeadCollisionIntoFloor) + self.accept('headCollSphere-exit-floor1', self.__handleHeadCollisionExitFloor) + + def __handleHeadCollisionIntoFloor(self, cevent): + self.isHeadInFloor = True + if base.localAvatar.controlManager.currentControls.lifter.getVelocity() > 0: + base.localAvatar.controlManager.currentControls.lifter.setVelocity(0.0) + self.assetMgr.playHeadCollideSound() + + def __handleHeadCollisionExitFloor(self, cevent): + self.isHeadInFloor = False + + def __placeToon(self, avId): + toon = self.getAvatar(avId) + i = self.avIdList.index(avId) + pos = Point3(ToonBlitzGlobals.ToonStartingPosition[0] + i, ToonBlitzGlobals.ToonStartingPosition[1], ToonBlitzGlobals.ToonStartingPosition[2]) + toon.setPos(pos) + toon.setHpr(-90, 0, 0) + + def startJump(self): + self.assetMgr.playJumpSound() + + def checkValidity(self, avId): + if not self.hasLocalToon: + return False + if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring msg: av %s performing some action.' % avId) + return False + toon = self.getAvatar(avId) + if toon == None: + return False + return True + + def shootKeyHandler(self): + self.toonSDs[self.localAvId].shootGun() + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('showShootGun', [self.localAvId, timestamp]) + + def showShootGun(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s is shooting water gun' % avId) + if avId != self.localAvId: + self.toonSDs[avId].shootGun() + + def localToonFellDown(self): + if self.toonSDs[self.localAvId].fsm.getCurrentState().getName() != 'fallDown': + self.toonSDs[self.localAvId].fsm.request('fallDown') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.updateScore(self.localAvId, ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()]) + self.sendUpdate('toonFellDown', [self.localAvId, timestamp]) + + def toonFellDown(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s fell down.' % avId) + if avId != self.localAvId: + self.updateScore(avId, ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()]) + self.toonSDs[avId].fsm.request('fallDown') + + def localToonHitByEnemy(self): + currToonState = self.toonSDs[self.localAvId].fsm.getCurrentState().getName() + if not (currToonState == 'fallBack' or currToonState == 'squish'): + self.toonSDs[self.localAvId].fsm.request('fallBack') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.updateScore(self.localAvId, ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()]) + self.sendUpdate('toonHitByEnemy', [self.localAvId, timestamp]) + + def toonHitByEnemy(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s hit by a suit' % avId) + if avId != self.localAvId: + self.updateScore(avId, ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()]) + self.toonSDs[avId].fsm.request('fallBack') + + def localToonSquished(self): + currToonState = self.toonSDs[self.localAvId].fsm.getCurrentState().getName() + if not (currToonState == 'fallBack' or currToonState == 'squish'): + self.toonSDs[self.localAvId].fsm.request('squish') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.updateScore(self.localAvId, ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()]) + self.sendUpdate('toonSquished', [self.localAvId, timestamp]) + + def toonSquished(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s is squished.' % avId) + if avId != self.localAvId: + self.updateScore(avId, ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()]) + self.toonSDs[avId].fsm.request('squish') + + def localToonVictory(self): + if not ToonBlitzGlobals.EndlessGame: + self.ignoreInputs() + if not self.toonSDs[self.localAvId].fsm.getCurrentState().getName() == 'victory': + self.toonSDs[self.localAvId].fsm.request('victory') + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime()) + self.sendUpdate('toonVictory', [self.localAvId, timestamp]) + + def toonVictory(self, avId, timestamp): + if self.checkValidity(avId): + self.notify.debug('avatar %s achieves victory' % avId) + if avId != self.localAvId: + self.toonSDs[avId].fsm.request('victory') + + def addVictoryScore(self, avId, score): + if not self.hasLocalToon: + return + self.updateScore(avId, score) + if avId == self.localAvId: + self.assetMgr.threeSparkles.play() + + def __treasureGrabbed(self, sectionIndex, treasureIndex): + self.notify.debug('treasure %s-%s grabbed' % (sectionIndex, treasureIndex)) + section = self.sectionMgr.sections[sectionIndex] + section.treasureMgr.treasures[treasureIndex].hideTreasure() + self.assetMgr.treasureGrabSound.play() + self.sendUpdate('claimTreasure', [sectionIndex, treasureIndex]) + + def setTreasureGrabbed(self, avId, sectionIndex, treasureIndex): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() == 'play': + self.notify.debug('treasure %s-%s grabbed by %s' % (sectionIndex, treasureIndex, avId)) + numSections = len(self.sectionMgr.sections) + if sectionIndex < numSections: + section = self.sectionMgr.sections[sectionIndex] + numTreasures = len(section.treasureMgr.treasures) + if treasureIndex < numTreasures: + treasure = section.treasureMgr.treasures[treasureIndex] + if avId != self.localAvId: + treasure.hideTreasure() + self.updateScore(avId, ToonBlitzGlobals.ScoreGainPerTreasure * treasure.value) + else: + self.notify.error('WHOA!! treasureIndex %s is out of range; numTreasures = %s' % (treasureIndex, numTreasures)) + base.localAvatar.sendLogMessage('treasureIndex %s is out of range; numTreasures = %s' % (treasureIndex, numTreasures)) + else: + self.notify.error('WHOA!! sectionIndex %s is out of range; numSections = %s' % (sectionIndex, numSections)) + base.localAvatar.sendLogMessage('sectionIndex %s is out of range; numSections = %s' % (sectionIndex, numSections)) + + def __enemyShot(self, sectionIndex, enemyIndex): + self.sectionMgr.sections[sectionIndex].enemyMgr.enemies[enemyIndex].doShotTrack() + self.sendUpdate('claimEnemyShot', [sectionIndex, enemyIndex]) + + def setEnemyShot(self, avId, sectionIndex, enemyIndex, enemyHealth): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() == 'play': + self.notify.debug('enemy %s is shot by %s. Health left %s' % (enemyIndex, avId, enemyHealth)) + if enemyHealth > 0: + if not avId == self.localAvId: + self.sectionMgr.sections[sectionIndex].enemyMgr.enemies[enemyIndex].doShotTrack() + else: + enemy = self.sectionMgr.sections[sectionIndex].enemyMgr.enemies[enemyIndex] + treasureSpawnPoint = Point3(enemy.suit.getX(), enemy.suit.getY(), enemy.suit.getZ() + enemy.suit.height / 2.0) + self.spawnTreasure(sectionIndex, enemyIndex, treasureSpawnPoint) + enemy.doDeathTrack() + + def updateScore(self, avId, deltaScore): + i = self.avIdList.index(avId) + self.scores[i] += deltaScore + self.scorePanels[i].setScore(self.scores[i]) + self.toonSDs[avId].showScoreText(deltaScore) + + def spawnTreasure(self, sectionIndex, enemyIndex, pos): + if self.gameFSM.getCurrentState().getName() == 'play': + section = self.sectionMgr.sections[sectionIndex] + treasure = section.treasureMgr.enemyTreasures[enemyIndex] + treasure.setTreasurePos(pos) + treasure.popupEnemyTreasure() + + def timerExpired(self): + self.notify.debug('timer expired') + if not self.reportedDone: + if not ToonBlitzGlobals.EndlessGame: + self.ignoreInputs() + self.reportedDone = True + self.sendUpdate('reportDone') + + def setEveryoneDone(self): + if not self.hasLocalToon: + return + if self.gameFSM.getCurrentState().getName() != 'play': + self.notify.warning('ignoring setEveryoneDone msg') + return + self.notify.debug('setEveryoneDone') + + def endGame(task, self = self): + if not ToonBlitzGlobals.EndlessGame: + self.gameFSM.request('showScores') + return Task.done + + self.timer.hide() + taskMgr.doMethodLater(1, endGame, self.EndGameTaskName) + + def getElevatorCloseTrack(self): + leftDoor = self.sectionMgr.exitElevator.find('**/doorL') + rightDoor = self.sectionMgr.exitElevator.find('**/doorR') + leftDoorClose = LerpPosInterval(leftDoor, 2, Point3(3.02, 0, 0)) + rightDoorClose = LerpPosInterval(rightDoor, 2, Point3(-3.02, 0, 0)) + return Sequence(Wait(self.timeToRunToElevator), Parallel(leftDoorClose, rightDoorClose)) + + def debugStartPause(self): + self.sectionMgr.enterPause() + + def debugEndPause(self): + self.sectionMgr.exitPause() diff --git a/toontown/minigame/DistributedTwoDGameAI.py b/toontown/minigame/DistributedTwoDGameAI.py new file mode 100644 index 0000000..5c80063 --- /dev/null +++ b/toontown/minigame/DistributedTwoDGameAI.py @@ -0,0 +1,418 @@ +from DistributedMinigameAI import * +from toontown.ai.ToonBarrier import * +from direct.fsm import ClassicFSM, State +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import ToonBlitzGlobals +from math import sqrt + +class DistributedTwoDGameAI(DistributedMinigameAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTwoDGameAI') + + def __init__(self, air, minigameId): + try: + self.DistributedTwoDGameAI_initialized + except: + self.DistributedTwoDGame_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedTwoDGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.addChildGameFSM(self.gameFSM) + self.finishedBonusDict = {} + self.finishedTimeLeftDict = {} + self.numFallDownDict = {} + self.numHitByEnemyDict = {} + self.numSquishDict = {} + self.treasuresCollectedDict = {} + self.sectionsSelected = [] + self.enemyHealthTable = [] + self.treasureTakenTable = [] + self.sectionIndexList = [] + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def setTrolleyZone(self, trolleyZone): + DistributedMinigameAI.setTrolleyZone(self, trolleyZone) + self.setupSections() + + def setGameReady(self): + self.notify.debug('setGameReady') + DistributedMinigameAI.setGameReady(self) + self.numTreasures = ToonBlitzGlobals.NumTreasures + self.numEnemies = ToonBlitzGlobals.NumEnemies + self.numTreasuresTaken = 0 + self.numEnemiesKilled = 0 + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + self.finishedBonusDict[avId] = 0 + self.finishedTimeLeftDict[avId] = -1 + self.numFallDownDict[avId] = 0 + self.numHitByEnemyDict[avId] = 0 + self.numSquishDict[avId] = 0 + self.treasuresCollectedDict[avId] = [0, + 0, + 0, + 0] + + for i in xrange(len(self.sectionsSelected)): + sectionIndex = self.sectionsSelected[i][0] + attribs = ToonBlitzGlobals.SectionTypes[sectionIndex] + enemiesPool = attribs[3] + self.enemyHealthTable += [[]] + enemyIndicesSelected = self.sectionsSelected[i][1] + for j in xrange(len(enemyIndicesSelected)): + enemyIndex = enemyIndicesSelected[j] + enemyType = enemiesPool[enemyIndex][0] + self.enemyHealthTable[i] += [ToonBlitzGlobals.EnemyBaseHealth] + self.enemyHealthTable[i][j] *= self.numPlayers + if ToonBlitzGlobals.EnemyHealthMultiplier.has_key(enemyType): + self.enemyHealthTable[i][j] *= ToonBlitzGlobals.EnemyHealthMultiplier[enemyType] + + self.treasureTakenTable += [[]] + treasureIndicesSelected = self.sectionsSelected[i][2] + for j in xrange(len(treasureIndicesSelected)): + self.treasureTakenTable[i] += [0] + + enemyIndicesSelected = self.sectionsSelected[i][1] + for j in xrange(len(enemyIndicesSelected)): + self.treasureTakenTable[i] += [0] + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + scoreList = [] + finishedBonusList = [] + timeLeftList = [] + treasureCollectedList = [] + playerErrorList = [] + for avId in self.avIdList: + scoreList.append(self.scoreDict[avId]) + finishedBonusList.append(self.finishedBonusDict[avId]) + timeLeftList.append(self.finishedTimeLeftDict[avId]) + treasureCollectedList.append(self.treasuresCollectedDict[avId]) + playerError = [self.numFallDownDict[avId], self.numHitByEnemyDict[avId], self.numSquishDict[avId]] + playerErrorList.append(playerError) + self.scoreDict[avId] = max(0, self.scoreDict[avId]) + jellybeans = sqrt(self.scoreDict[avId] * ToonBlitzGlobals.ScoreToJellyBeansMultiplier) + self.scoreDict[avId] = max(1, int(jellybeans)) + + self.air.writeServerEvent('minigame_twoD', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.TwoDGameId, + self.getSafezoneId(), + self.avIdList, + scoreList, + finishedBonusList, + timeLeftList, + treasureCollectedList, + playerErrorList, + self.sectionIndexList)) + self.notify.debug('minigame_twoD%s: %s|%s|%s|%s|%s|%s|%s|%s|%s' % (self.doId, + ToontownGlobals.TwoDGameId, + self.getSafezoneId(), + self.avIdList, + scoreList, + finishedBonusList, + timeLeftList, + treasureCollectedList, + playerErrorList, + self.sectionIndexList)) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + + def allToonsDone(self = self): + self.notify.debug('allToonsDone') + self.sendUpdate('setEveryoneDone') + if not ToonBlitzGlobals.EndlessGame: + self.gameOver() + + def handleTimeout(avIds, self = self): + self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds) + self.setGameAbort() + + self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, ToonBlitzGlobals.GameDuration[self.getSafezoneId()] + ToonBlitzGlobals.ShowScoresDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout) + + def exitPlay(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.doneBarrier.cleanup() + del self.doneBarrier + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def claimTreasure(self, sectionIndex, treasureIndex): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('treasure %s-%s claimed by %s' % (sectionIndex, treasureIndex, avId)) + if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected): + self.air.writeServerEvent('warning', sectionIndex, 'TwoDGameAI.claimTreasure sectionIndex out of range.') + return + if treasureIndex < 0 or treasureIndex >= len(self.treasureTakenTable[sectionIndex]): + self.notify.warning('Treasure %s: TwoDGameAI.claimTreasure treasureIndex out of range.' % treasureIndex) + self.air.writeServerEvent('warning', treasureIndex, 'TwoDGameAI.claimTreasure treasureIndex out of range.') + return + if self.treasureTakenTable[sectionIndex][treasureIndex]: + return + initialTreasureList = self.sectionsSelected[sectionIndex][2] + if treasureIndex < len(initialTreasureList): + treasureValue = initialTreasureList[treasureIndex][1] + else: + treasureValue = self.numPlayers + self.treasureTakenTable[sectionIndex][treasureIndex] = treasureValue + self.treasuresCollectedDict[avId][treasureValue - 1] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreGainPerTreasure * treasureValue + self.numTreasuresTaken += 1 + self.sendUpdate('setTreasureGrabbed', [avId, sectionIndex, treasureIndex]) + + def claimEnemyShot(self, sectionIndex, enemyIndex): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('enemy %s-%s shot claimed by %s' % (sectionIndex, enemyIndex, avId)) + if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected): + self.air.writeServerEvent('warning', sectionIndex, 'TwoDGameAI.claimEnemyShot sectionIndex out of range.') + return + if enemyIndex < 0 or enemyIndex >= len(self.sectionsSelected[sectionIndex][1]): + self.air.writeServerEvent('warning', enemyIndex, 'TwoDGameAI.claimEnemyShot enemyIndex out of range.') + return + if self.enemyHealthTable[sectionIndex][enemyIndex] > 0: + self.enemyHealthTable[sectionIndex][enemyIndex] -= ToonBlitzGlobals.DamagePerBullet + if self.enemyHealthTable[sectionIndex][enemyIndex] <= 0: + self.numEnemiesKilled += 1 + self.sendUpdate('setEnemyShot', [avId, + sectionIndex, + enemyIndex, + self.enemyHealthTable[sectionIndex][enemyIndex]]) + + def reportDone(self): + if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play': + return + avId = self.air.getAvatarIdFromSender() + self.notify.debug('reportDone: avatar %s is done' % avId) + self.doneBarrier.clear(avId) + return + + def toonVictory(self, avId, timestamp): + if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play': + msg = 'TwoDGameAI.toonVictory not in play state!' + self.notify.warning('suspicious: ' + str(avId) + ' ' + msg) + self.air.writeServerEvent('suspicious: ', avId, msg) + return + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('suspicious: ', avId, 'TwoDGameAI.toonVictory toon not in list.') + return + curTime = self.getCurrentGameTime() + timeLeft = ToonBlitzGlobals.GameDuration[self.getSafezoneId()] - curTime + self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft)) + addBonus = int(ToonBlitzGlobals.BaseBonusOnCompletion[self.getSafezoneId()] + ToonBlitzGlobals.BonusPerSecondLeft * timeLeft) + self.notify.debug('addBOnus = %d' % addBonus) + if addBonus < 0: + addBonus = 0 + self.finishedBonusDict[avId] = addBonus + timeLeftStr = '%.1f' % timeLeft + self.finishedTimeLeftDict[avId] = timeLeftStr + self.scoreDict[avId] += addBonus + self.sendUpdate('addVictoryScore', [avId, addBonus]) + self.doneBarrier.clear(avId) + return + + def toonFellDown(self, avId, timestamp): + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonFellDown toon not in list.') + return + self.numFallDownDict[avId] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()] + + def toonHitByEnemy(self, avId, timestamp): + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonHitByEnemy toon not in list.') + return + self.numHitByEnemyDict[avId] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()] + + def toonSquished(self, avId, timestamp): + if avId not in self.scoreDict.keys(): + self.notify.warning('Avatar %s not in list.' % avId) + self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonSquished toon not in list.') + return + self.numSquishDict[avId] += 1 + self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()] + + def setupSections(self): + szId = self.getSafezoneId() + sectionWeights = ToonBlitzGlobals.SectionWeights[szId] + numSections = ToonBlitzGlobals.NumSections[szId] + difficultyPool = [] + difficultyList = [] + sectionsPool = ToonBlitzGlobals.SectionsPool + sectionTypes = ToonBlitzGlobals.SectionTypes + sectionsPoolByDifficulty = [[], + [], + [], + [], + [], + []] + sectionsSelectedByDifficulty = [[], + [], + [], + [], + [], + []] + sectionIndicesSelected = [] + for weight in sectionWeights: + difficulty, probability = weight + difficultyPool += [difficulty] * probability + + for i in xrange(numSections): + difficulty = random.choice(difficultyPool) + difficultyList.append(difficulty) + + difficultyList.sort() + for sectionIndex in sectionsPool: + difficulty = sectionTypes[sectionIndex][0] + sectionsPoolByDifficulty[difficulty] += [sectionIndex] + + for targetDifficulty in difficultyList: + whileCount = 0 + difficulty = targetDifficulty + while not len(sectionsPoolByDifficulty[difficulty]) > 0: + difficulty += 1 + if difficulty >= 5: + difficulty = 0 + whileCount += 1 + if whileCount > 1: + break + else: + sectionIndexChoice = random.choice(sectionsPoolByDifficulty[difficulty]) + sectionsSelectedByDifficulty[difficulty] += [sectionIndexChoice] + sectionsPoolByDifficulty[difficulty].remove(sectionIndexChoice) + + if whileCount > 1: + self.notify.debug('We need more sections than we have choices. We have to now repeat.') + + for i in xrange(len(sectionsSelectedByDifficulty)): + for j in xrange(len(sectionsSelectedByDifficulty[i])): + sectionIndicesSelected.append(sectionsSelectedByDifficulty[i][j]) + + for i in xrange(len(sectionIndicesSelected)): + sectionIndex = sectionIndicesSelected[i] + self.sectionIndexList.append(sectionIndex) + attribs = sectionTypes[sectionIndex] + difficulty = attribs[0] + length = attribs[1] + blocksPool = attribs[2] + enemiesPool = attribs[3] + treasuresPool = attribs[4] + spawnPointsPool = attribs[5] + stompersPool = attribs[6] + enemyIndicesPool = [] + enemyIndicesSelected = [] + if enemiesPool != None: + minEnemies, maxEnemies = attribs[7] + for i in xrange(len(enemiesPool)): + enemyIndicesPool += [i] + + numEnemies = maxEnemies * ToonBlitzGlobals.PercentMaxEnemies[szId] / 100 + numEnemies = max(numEnemies, minEnemies) + for j in xrange(int(numEnemies)): + if len(enemyIndicesPool) == 0: + break + enemyIndex = random.choice(enemyIndicesPool) + enemyIndicesSelected.append(enemyIndex) + enemyIndicesPool.remove(enemyIndex) + + enemyIndicesSelected.sort() + treasureIndicesPool = [] + treasureValuePool = [] + for value in range(1, 5): + treasureValuePool += [value] * ToonBlitzGlobals.TreasureValueProbability[value] + + treasureIndicesSelected = [] + if treasuresPool != None: + minTreasures, maxTreasures = attribs[8] + for i in xrange(len(treasuresPool)): + treasureIndicesPool += [i] + + numTreasures = maxTreasures * ToonBlitzGlobals.PercentMaxTreasures[szId] / 100 + numTreasures = max(numTreasures, minTreasures) + for i in xrange(int(numTreasures)): + if len(treasureIndicesPool) == 0: + break + treasureIndex = random.choice(treasureIndicesPool) + treasureValue = random.choice(treasureValuePool) + treasure = (treasureIndex, treasureValue) + treasureIndicesPool.remove(treasureIndex) + treasureIndicesSelected.append(treasure) + + treasureIndicesSelected.sort() + spawnPointIndicesPool = [] + spawnPointIndicesSelected = [] + if spawnPointsPool != None: + minSpawnPoints, maxSpawnPoints = attribs[9] + for i in xrange(len(spawnPointsPool)): + spawnPointIndicesPool += [i] + + numSpawnPoints = maxSpawnPoints * ToonBlitzGlobals.PercentMaxSpawnPoints[szId] / 100 + numSpawnPoints = max(numSpawnPoints, minSpawnPoints) + for i in xrange(int(numSpawnPoints)): + if len(spawnPointIndicesPool) == 0: + break + spawnPoint = random.choice(spawnPointIndicesPool) + spawnPointIndicesSelected.append(spawnPoint) + spawnPointIndicesPool.remove(spawnPoint) + + spawnPointIndicesSelected.sort() + stomperIndicesPool = [] + stomperIndicesSelected = [] + if stompersPool != None: + minStompers, maxStompers = attribs[10] + for i in xrange(len(stompersPool)): + stomperIndicesPool += [i] + + numStompers = maxStompers * ToonBlitzGlobals.PercentMaxStompers[szId] / 100 + numStompers = max(numStompers, minStompers) + for i in xrange(int(numStompers)): + if len(stomperIndicesPool) == 0: + break + stomper = random.choice(stomperIndicesPool) + stomperIndicesSelected.append(stomper) + stomperIndicesPool.remove(stomper) + + stomperIndicesSelected.sort() + sctionTuple = (sectionIndex, + enemyIndicesSelected, + treasureIndicesSelected, + spawnPointIndicesSelected, + stomperIndicesSelected) + self.sectionsSelected.append(sctionTuple) + + return + + def getSectionsSelected(self): + return self.sectionsSelected diff --git a/toontown/minigame/DistributedVineGame.py b/toontown/minigame/DistributedVineGame.py new file mode 100644 index 0000000..26b05a1 --- /dev/null +++ b/toontown/minigame/DistributedVineGame.py @@ -0,0 +1,1539 @@ +from pandac.PandaModules import Point3, ForceNode, LinearVectorForce, CollisionHandlerEvent, CollisionNode, CollisionSphere, Camera, PerspectiveLens, Vec4, Point2, ActorNode, Vec3, BitMask32 +from direct.interval.IntervalGlobal import Sequence, Parallel, Func, Wait, LerpPosInterval, ActorInterval, LerpScaleInterval, ProjectileInterval, SoundInterval +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectFrame import DirectFrame +from direct.gui.DirectGui import DGG +from toontown.toonbase import ToontownGlobals +from direct.task.Task import Task +from direct.fsm import ClassicFSM, State +from toontown.toonbase import TTLocalizer +from toontown.minigame.DistributedMinigame import DistributedMinigame +from toontown.minigame import SwingVine +from toontown.minigame import ArrowKeys +from toontown.minigame import VineGameGlobals +from toontown.minigame import VineTreasure +from toontown.minigame import MinigameAvatarScorePanel +from toontown.toonbase import ToontownTimer +from toontown.minigame import VineHeadFrame +from toontown.minigame import VineBat + +class DistributedVineGame(DistributedMinigame): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVineGame') + UpdateLocalToonTask = 'VineGameUpdateLocalToonTask' + LocalPhysicsRadius = 1.5 + Gravity = 32 + ToonVerticalRate = 0.25 + FallingNot = 0 + FallingNormal = 1 + FallingSpider = 2 + FallingBat = 3 + JumpingFrame = 128 + ToonMaxRightFrame = 35 + + def __init__(self, cr): + DistributedMinigame.__init__(self, cr) + self.JumpSpeed = 64 + self.TwoVineView = True + self.ArrowToChangeFacing = True + self.gameFSM = ClassicFSM.ClassicFSM('DistributedVineGame', [State.State('off', self.enterOff, self.exitOff, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'showScores', 'waitShowScores']), + State.State('waitShowScores', self.enterWaitShowScores, self.exitWaitShowScores, ['cleanup', 'showScores']), + State.State('showScores', self.enterShowScores, self.exitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'off', 'cleanup') + self.addChildGameFSM(self.gameFSM) + self.cameraTopView = (17.6, 6.18756, 43.9956, 0, -89, 0) + self.cameraThreeQuarterView = (0, -63.2, 16.3, 0, 0, 0) + self.cameraSidePos = Point3(0, -63.2, 16.3) + self.cameraTwoVineSidePos = Point3(0, -53, 17.3) + self.cameraTwoVineAdj = 5 + self.cameraSideView = (-15, -53, 17.3, 0, 0, 0) + self.localPhysicsNP = None + self.vines = [] + self.physicsHandler = None + self.lastJumpVine = -1 + self.lastJumpPos = None + self.lastJumpT = 0 + self.lastJumpFacingRight = True + self.lastJumpTime = 0 + self.toonInfo = {} + self.otherToonPhysics = {} + self.headFrames = {} + self.changeFacingInterval = None + self.attachingToVineCamIval = None + self.localToonJumpAnimIval = None + self.endingTracks = {} + self.endingTrackTaskNames = [] + self.sendNewVineTUpdateAsap = False + self.lastNewVineTUpdate = 0 + self.defaultMaxX = (VineGameGlobals.NumVines + 1) * VineGameGlobals.VineXIncrement + return + + def getClimbDir(self, avId): + retval = 0 + if self.toonInfo.has_key(avId): + retval = self.toonInfo[avId][5] + return retval + + def moveCameraToSide(self): + camera.reparentTo(render) + p = self.cameraSideView + camera.setPosHpr(p[0], p[1], p[2], p[3], p[4], p[5]) + + def getTitle(self): + return TTLocalizer.VineGameTitle + + def getInstructions(self): + return TTLocalizer.VineGameInstructions + + def getMaxDuration(self): + return 0 + + def defineConstants(self): + self.ShowToonSpheres = 0 + + def load(self): + self.notify.debug('load') + DistributedMinigame.load(self) + self.defineConstants() + self.music = base.loadMusic('phase_4/audio/bgm/MG_Vine.mid') + self.gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + self.gameBoard = self.gameAssets.find('**/background') + self.gameBoard.reparentTo(render) + self.gameBoard.show() + self.gameBoard.setPosHpr(0, 0, 0, 0, 0, 0) + self.gameBoard.setTransparency(1) + self.gameBoard.setColorScale(1, 1, 1, 0.5) + self.gameBoard.setScale(1.0) + self.gameBoard.hide(VineGameGlobals.RadarCameraBitmask) + self.treasureModel = self.gameAssets.find('**/bananas') + self.setupVineCourse() + self.grabSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bananas.mp3') + self.jumpSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_jump.mp3') + self.catchSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_catch.mp3') + self.spiderHitSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_spider_hit.mp3') + self.batHitVineSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_hit.mp3') + self.batHitMidairSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_hit_midair.mp3') + self.winSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_finish.mp3') + self.fallSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_fall.mp3') + self.loadBats() + self.createBatIvals() + bothPlatform = loader.loadModel('phase_4/models/minigames/vine_game_shelf') + self.startPlatform = bothPlatform.find('**/start1') + self.startPlatform.setPos(-16, 0, 15) + self.startPlatform.reparentTo(render) + self.startPlatform.setScale(1.0, 1.0, 0.75) + self.endPlatform = bothPlatform.find('**/end1') + endPos = self.vines[VineGameGlobals.NumVines - 1].getPos() + self.endPlatform.setPos(endPos[0] + 20, 0, 15) + self.endPlatform.reparentTo(render) + self.endPlatform.setScale(1.0, 1.0, 0.75) + + def unload(self): + self.notify.debug('unload') + DistributedMinigame.unload(self) + self.removeChildGameFSM(self.gameFSM) + del self.gameFSM + del self.music + self.gameAssets.removeNode() + self.gameBoard.removeNode() + del self.gameBoard + self.treasureModel.removeNode() + del self.treasureModel + for vine in self.vines: + vine.unload() + del vine + + self.vines = [] + self.destroyBatIvals() + for bat in self.bats: + bat.destroy() + del bat + + self.bats = [] + del self.grabSound + del self.jumpSound + del self.catchSound + del self.spiderHitSound + del self.batHitVineSound + del self.batHitMidairSound + del self.winSound + del self.fallSound + if self.localToonJumpAnimIval: + self.localToonJumpAnimIval.finish() + del self.localToonJumpAnimIval + self.startPlatform.removeNode() + self.endPlatform.removeNode() + + def onstage(self): + self.notify.debug('onstage') + DistributedMinigame.onstage(self) + for avId in self.avIdList: + self.updateToonInfo(avId, vineIndex=0, vineT=VineGameGlobals.VineStartingT, posX=0, posZ=0, facingRight=0, climbDir=0, fallingInfo=self.FallingNot) + + self.scorePanels = [] + self.gameBoard.reparentTo(render) + self.moveCameraToSide() + self.arrowKeys = ArrowKeys.ArrowKeys() + handlers = [self.upArrowKeyHandler, + self.downArrowKeyHandler, + self.leftArrowKeyHandler, + self.rightArrowKeyHandler, + None] + self.arrowKeys.setPressHandlers(handlers) + self.numTreasures = len(self.vines) - 1 + self.treasures = [] + for i in xrange(self.numTreasures): + height = self.randomNumGen.randrange(10, 25) + xPos = self.randomNumGen.randrange(12, 18) + pos = Point3(self.vines[i].getX() + 15, 0, height) + self.treasures.append(VineTreasure.VineTreasure(self.treasureModel, pos, i, self.doId)) + + gravityFN = ForceNode('world-forces') + gravityFNP = render.attachNewNode(gravityFN) + gravityForce = LinearVectorForce(0, 0, -self.Gravity) + gravityFN.addForce(gravityForce) + base.physicsMgr.addLinearForce(gravityForce) + self.gravityForce = gravityForce + self.gravityForceFNP = gravityFNP + lt = base.localAvatar + radius = 0.7 + handler = CollisionHandlerEvent() + handler.setInPattern('ltCatch-%fn') + self.bodyColEventNames = [] + self.ltLegsCollNode = CollisionNode('catchLegsCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchLegsCollNode') + self.ltLegsCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltTorsoCollNode = CollisionNode('catchTorsoCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchTorsoCollNode') + self.ltTorsoCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltHeadCollNode = CollisionNode('catchHeadCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchHeadCollNode') + self.ltHeadCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltLHandCollNode = CollisionNode('catchLHandCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchLHandCollNode') + self.ltLHandCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + self.ltRHandCollNode = CollisionNode('catchRHandCollNode') + self.bodyColEventNames.append('ltCatch-%s' % 'catchRHandCollNode') + self.ltRHandCollNode.setCollideMask(VineGameGlobals.SpiderBitmask) + legsCollNodepath = lt.attachNewNode(self.ltLegsCollNode) + legsCollNodepath.hide() + head = base.localAvatar.getHeadParts().getPath(2) + headCollNodepath = head.attachNewNode(self.ltHeadCollNode) + headCollNodepath.hide() + torso = base.localAvatar.getTorsoParts().getPath(2) + torsoCollNodepath = torso.attachNewNode(self.ltTorsoCollNode) + torsoCollNodepath.hide() + self.torso = torsoCollNodepath + lHand = base.localAvatar.getLeftHands()[0] + lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode) + lHandCollNodepath.hide() + rHand = base.localAvatar.getRightHands()[0] + rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode) + rHandCollNodepath.hide() + lt.cTrav.addCollider(legsCollNodepath, handler) + lt.cTrav.addCollider(headCollNodepath, handler) + lt.cTrav.addCollider(lHandCollNodepath, handler) + lt.cTrav.addCollider(rHandCollNodepath, handler) + lt.cTrav.addCollider(torsoCollNodepath, handler) + if self.ShowToonSpheres: + legsCollNodepath.show() + headCollNodepath.show() + lHandCollNodepath.show() + rHandCollNodepath.show() + torsoCollNodepath.show() + self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius)) + self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius)) + self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltTorsoCollNode.addSolid(CollisionSphere(0, 0, radius, radius * 2)) + self.toonCollNodes = [legsCollNodepath, + headCollNodepath, + lHandCollNodepath, + rHandCollNodepath, + torsoCollNodepath] + for eventName in self.bodyColEventNames: + self.accept(eventName, self.toonHitSomething) + + self.introTrack = self.getIntroTrack() + self.introTrack.start() + return + + def offstage(self): + self.notify.debug('offstage') + for panel in self.scorePanels: + panel.cleanup() + + del self.scorePanels + self.gameBoard.hide() + DistributedMinigame.offstage(self) + self.arrowKeys.destroy() + del self.arrowKeys + for avId in self.avIdList: + av = self.getAvatar(avId) + if av: + av.dropShadow.show() + av.resetLOD() + + for treasure in self.treasures: + treasure.destroy() + + del self.treasures + base.physicsMgr.removeLinearForce(self.gravityForce) + self.gravityForceFNP.removeNode() + for collNode in self.toonCollNodes: + while collNode.node().getNumSolids(): + collNode.node().removeSolid(0) + + base.localAvatar.cTrav.removeCollider(collNode) + + del self.toonCollNodes + for eventName in self.bodyColEventNames: + self.ignore(eventName) + + self.ignoreAll() + if self.introTrack.isPlaying(): + self.introTrack.finish() + del self.introTrack + for vine in self.vines: + vine.stopSwing() + vine.hide() + + self.startPlatform.hide() + self.endPlatform.hide() + + def handleDisabledAvatar(self, avId): + self.notify.debug('handleDisabledAvatar') + self.notify.debug('avatar ' + str(avId) + ' disabled') + DistributedMinigame.handleDisabledAvatar(self, avId) + + def updateToonInfo(self, avId, vineIndex = None, vineT = None, posX = None, posZ = None, facingRight = None, climbDir = None, velX = None, velZ = None, fallingInfo = None): + newVineIndex = vineIndex + newVineT = vineT + newPosX = posX + newPosZ = posZ + newFacingRight = facingRight + newClimbDir = climbDir + newVelX = velX + newVelZ = velZ + newFallingInfo = fallingInfo + oldInfo = None + if self.toonInfo.has_key(avId): + oldInfo = self.toonInfo[avId] + if vineIndex == None: + newVineIndex = oldInfo[0] + if vineT == None: + newVineT = oldInfo[1] + if posX == None: + newPosX = oldInfo[2] + if posZ == None: + newPosZ = oldInfo[3] + if facingRight == None: + newFacingRight = oldInfo[4] + if climbDir == None: + newClimbDir = oldInfo[5] + if velX == None: + newVelX = oldInfo[6] + if velZ == None: + newVelZ = oldInfo[7] + if fallingInfo == None: + newFallingInfo = oldInfo[8] + if newVineIndex < -1 or newVineIndex >= len(self.vines): + self.notify.warning('invalid vineIndex for %d, forcing 0' % avId) + newVineIndex = 0 + if newVineT < 0 or newVineT > 1: + self.notify.warning('invalid vineT for %d, setting to 0' % avId) + if not (newFacingRight == 0 or newFacingRight == 1): + self.notify.warning('invalid facingRight for %d, forcing to 1' % avId) + newFacingRight = 1 + if newPosX < -1000 or newPosX > 2000: + self.notify.warning('invalid posX for %d, forcing to 0' % avId) + newPosX = 0 + if newPosZ < -100 or newPosZ > 1000: + self.notify.warning('invalid posZ for %d, forcing to 0' % avId) + newPosZ = 0 + if newVelX < -1000 or newVelX > 1000: + self.notify.warning('invalid velX %s for %d, forcing to 0' % (newVelX, avId)) + newVelX = 0 + if newVelZ < -1000 or newVelZ > 1000: + self.notify.warning('invalid velZ %s for %d, forcing to 0' % (newVelZ, avId)) + newVelZ = 0 + if newFallingInfo < self.FallingNot or newFallingInfo > self.FallingBat: + self.notify.warning('invalid fallingInfo for %d, forcing to 0' % avId) + newFallingInfo = 0 + newInfo = [newVineIndex, + newVineT, + newPosX, + newPosZ, + newFacingRight, + newClimbDir, + newVelX, + newVelZ, + newFallingInfo] + self.toonInfo[avId] = newInfo + if oldInfo: + self.applyToonInfoChange(avId, newInfo, oldInfo) + self.sanityCheck() + return + + def applyToonInfoChange(self, avId, newInfo, oldInfo): + if not self.isInPlayState(): + return + oldVine = oldInfo[0] + newVine = newInfo[0] + if not oldVine == newVine: + self.notify.debug('we have a vine change') + if oldVine == -1: + self.notify.debug(' we were jumping and now attaching to a new vine') + newVineT = newInfo[1] + newFacingRight = newInfo[4] + self.vines[newVine].attachToon(avId, newVineT, newFacingRight) + if newVine == VineGameGlobals.NumVines - 1: + self.doEndingTrackTask(avId) + elif newVine == -1: + self.notify.debug('we were attached to a vine and we are now jumping') + curInfo = self.vines[oldVine].getAttachedToonInfo(avId) + self.vines[oldVine].detachToon(avId) + if not avId == self.localAvId: + posX = newInfo[2] + posZ = newInfo[3] + velX = newInfo[6] + velZ = newInfo[7] + self.makeOtherToonJump(avId, posX, posZ, velX, velZ) + else: + self.notify.warning('should not happen directly going from one vine to another') + self.vines[oldVine].detachToon(avId) + newVineT = newInfo[1] + newFacingRight = newInfo[4] + self.vines[newVine].attachToon(avId, newVineT, newFacingRight) + elif newVine == oldVine and newInfo[4] != oldInfo[4]: + self.notify.debug('# still on the same vine, but we changed facing') + self.vines[newVine].changeAttachedToonFacing(avId, newInfo[4]) + elif newVine >= 0: + self.vines[newVine].changeAttachedToonT(avId, newInfo[1]) + else: + self.notify.debug('we are still falling') + if oldInfo[8] != newInfo[8]: + if not avId == self.localAvId: + posX = newInfo[2] + posZ = newInfo[3] + velX = newInfo[6] + velZ = newInfo[7] + self.makeOtherToonFallFromMidair(avId, posX, posZ, velX, velZ) + + def sanityCheck(self): + if not self.isInPlayState(): + return + for avId in self.toonInfo.keys(): + myVineIndex = self.toonInfo[avId][0] + foundVines = [] + foundVineIndex = -1 + for curVine in xrange(len(self.vines)): + curInfo = self.vines[curVine].getAttachedToonInfo(avId) + if curInfo: + foundVines.append(curVine) + foundVineIndex = curVine + + if len(foundVines) > 1: + self.notify.warning('toon %d is attached to vines %s' % (avId, foundVines)) + if not foundVineIndex == myVineIndex and not myVineIndex == VineGameGlobals.NumVines - 1: + self.notify.warning('avId=%d foundVineIndex=%d != myVineIndex=%d' % (avId, foundVineIndex, myVineIndex)) + + def getVineAndVineInfo(self, avId): + retVine = -1 + retInfo = None + for curVine in xrange(len(self.vines)): + curInfo = self.vines[curVine].getAttachedToonInfo(avId) + if curInfo: + retVine = curVine + retInfo = curInfo + break + + if self.toonInfo[avId][0] != retVine: + self.notify.warning("getVineAndVineInfo don't agree, toonInfo[%d]=%s, retVine=%d" % (avId, self.toonInfo[avId][0], retVine)) + return (retVine, curInfo) + + def setGameReady(self): + if not self.hasLocalToon: + return + self.notify.debug('setGameReady') + if DistributedMinigame.setGameReady(self): + return + self.toonOffsets = {} + self.toonOffsetsFalling = {} + for index in xrange(self.numPlayers): + avId = self.avIdList[index] + toon = self.getAvatar(avId) + if toon: + toon.reparentTo(render) + toon.setPos(0, 0, 0) + toon.setHpr(0, 0, 0) + self.toonOffsets[avId] = self.calcToonOffset(toon) + toon.dropShadow.hide() + newHeadFrame = VineHeadFrame.VineHeadFrame(toon) + newHeadFrame.hide() + self.headFrames[avId] = newHeadFrame + toon.useLOD(1000) + toon.setX(-100) + + def calcToonOffset(self, toon): + offset = Point3(0, 0, 0) + toon.pose('swing', 74) + leftHand = toon.find('**/leftHand') + if not leftHand.isEmpty(): + offset = leftHand.getPos(toon) + return offset + + def setGameStart(self, timestamp): + if not self.hasLocalToon: + return + self.notify.debug('setGameStart') + DistributedMinigame.setGameStart(self, timestamp) + if self.introTrack.isPlaying(): + self.introTrack.finish() + self.gameFSM.request('play') + + def enterOff(self): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.stop() + self.createRadar() + self.scores = [0] * self.numPlayers + spacing = 0.4 + for i in xrange(self.numPlayers): + avId = self.avIdList[i] + avName = self.getAvatarName(avId) + scorePanel = MinigameAvatarScorePanel.MinigameAvatarScorePanel(avId, avName) + scorePanel.setScale(0.9) + scorePanel.setPos(0.75 - spacing * (self.numPlayers - 1 - i), 0.0, 0.85) + scorePanel.makeTransparent(0.75) + self.scorePanels.append(scorePanel) + + for vine in self.vines: + vine.show() + vine.startSwing() + + self.startBatIvals() + for avId in self.avIdList: + toon = self.getAvatar(avId) + if toon: + if avId == self.localAvId: + self.attachLocalToonToVine(0, VineGameGlobals.VineStartingT) + else: + self.vines[0].attachToon(avId, VineGameGlobals.VineStartingT, self.lastJumpFacingRight) + + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.setTime(VineGameGlobals.GameDuration) + self.timer.countdown(VineGameGlobals.GameDuration, self.timerExpired) + base.playMusic(self.music, looping=1, volume=0.9) + self.__spawnUpdateLocalToonTask() + + def exitPlay(self): + self.notify.debug('exitPlay') + if base.localAvatar.laffMeter: + base.localAvatar.laffMeter.start() + self.timer.stop() + self.timer.destroy() + del self.timer + self.clearLocalPhysics() + for ival in self.batIvals: + ival.finish() + del ival + + self.batIvals = [] + self.music.stop() + + def enterWaitShowScores(self): + self.notify.debug('enterWaitShowScores') + + def exitWaitShowScores(self): + self.notify.debug('exitWaitShowScores') + + def enterShowScores(self): + self.notify.debug('enterShowScores') + lerpTrack = Parallel() + lerpDur = 0.5 + tY = 0.6 + bY = -.05 + lX = -.5 + cX = 0 + rX = 0.5 + scorePanelLocs = (((cX, bY),), + ((lX, bY), (rX, bY)), + ((cX, tY), (lX, bY), (rX, bY)), + ((lX, tY), + (rX, tY), + (lX, bY), + (rX, bY))) + scorePanelLocs = scorePanelLocs[self.numPlayers - 1] + for i in xrange(self.numPlayers): + panel = self.scorePanels[i] + pos = scorePanelLocs[i] + lerpTrack.append(Parallel(LerpPosInterval(panel, lerpDur, Point3(pos[0], 0, pos[1]), blendType='easeInOut'), LerpScaleInterval(panel, lerpDur, Vec3(panel.getScale()) * 2.0, blendType='easeInOut'))) + + self.showScoreTrack = Parallel(lerpTrack, Sequence(Wait(VineGameGlobals.ShowScoresDuration), Func(self.gameOver))) + self.showScoreTrack.start() + + def exitShowScores(self): + self.showScoreTrack.pause() + del self.showScoreTrack + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.destroyRadar() + self.__killUpdateLocalToonTask() + self.cleanupEndingTracks() + for avId in self.headFrames: + self.headFrames[avId].destroy() + + def exitCleanup(self): + pass + + def __spawnUpdateLocalToonTask(self): + taskMgr.remove(self.UpdateLocalToonTask) + taskMgr.add(self.__updateLocalToonTask, self.UpdateLocalToonTask) + + def __killUpdateLocalToonTask(self): + taskMgr.remove(self.UpdateLocalToonTask) + + def clearLocalPhysics(self): + if self.localPhysicsNP: + an = self.localPhysicsNP.node() + base.physicsMgr.removePhysicalNode(an) + self.localPhysicsNP.removeNode() + del self.localPhysicsNP + self.localPhysicsNP = None + if hasattr(self, 'treasureCollNodePath') and self.treasureCollNodePath: + self.treasureCollNodePath.removeNode() + return + + def handleLocalToonFellDown(self): + self.notify.debug('attaching toon back to vine since he fell') + self.fallSound.play() + vineToAttach = self.lastJumpVine + if self.toonInfo[self.localAvId][8] == self.FallingSpider: + if self.vines[vineToAttach].hasSpider: + vineToAttach -= 1 + if vineToAttach < 0: + vineToAttach = 0 + self.attachLocalToonToVine(vineToAttach, VineGameGlobals.VineFellDownT) + + def makeCameraFollowJumpingToon(self): + camera.setHpr(0, 0, 0) + if self.TwoVineView: + camera.setPos(self.cameraTwoVineSidePos) + maxVine = self.lastJumpVine + 1 + if maxVine >= len(self.vines): + maxVine = len(self.vines) - 1 + maxX = self.defaultMaxX + if self.vines: + maxX = self.vines[maxVine].getX() + minVine = self.lastJumpVine - 1 + if minVine < 0: + minVine = 0 + minX = 0 + if self.vines: + minX = self.vines[minVine].getX() + camera.setX(base.localAvatar.getX(render)) + if camera.getX() > maxX: + camera.setX(maxX) + if camera.getX() < minX: + camera.setX(minX) + else: + camera.setPos(self.cameraSidePos) + maxVine = self.lastJumpVine + 1 + if maxVine >= len(self.vines): + maxVine = len(self.vines) - 1 + maxX = self.vines[maxVine].getX() + minVine = self.lastJumpVine - 1 + if minVine < 0: + minVine = 0 + minX = self.vines[minVine].getX() + camera.setX(base.localAvatar.getX(render)) + if camera.getX() > maxX: + camera.setX(maxX) + if camera.getX() < minX: + camera.setX(minX) + + def __updateOtherToonsClimbing(self): + for avId in self.toonInfo.keys(): + if avId == self.localAvId: + continue + toonInfo = self.toonInfo[avId] + vineIndex = toonInfo[0] + if vineIndex == -1: + continue + climbDir = toonInfo[5] + if climbDir == None or climbDir == 0: + continue + curT = toonInfo[1] + dt = globalClock.getDt() + diffT = 0 + baseVerticalRate = self.ToonVerticalRate + if climbDir == -1: + diffT -= baseVerticalRate * dt + if climbDir == 1: + diffT += baseVerticalRate * dt + newT = curT + diffT + if newT > 1: + newT = 1 + if newT < 0: + newT = 0 + if not newT == curT: + toonInfo[1] = newT + self.vines[vineIndex].changeAttachedToonT(avId, newT) + + return + + def __updateLocalToonTask(self, task): + dt = globalClock.getDt() + self.updateRadar() + self.__updateOtherToonsClimbing() + for bat in self.bats: + bat.checkScreech() + + if self.localPhysicsNP: + pos = self.localPhysicsNP.getPos(render) + if pos[2] < 0: + self.handleLocalToonFellDown() + avId = self.localAvId + curInfo = None + for vineIndex in xrange(len(self.vines)): + curInfo = self.vines[vineIndex].getAttachedToonInfo(avId) + if curInfo: + break + + if not curInfo: + if not self.endingTracks.has_key(avId): + self.makeCameraFollowJumpingToon() + if self.localPhysicsNP: + pos = self.localPhysicsNP.getPos(render) + else: + curT = curInfo[0] + diffT = 0 + baseVerticalRate = self.ToonVerticalRate + onEndVine = vineIndex == len(self.vines) - 1 + if self.arrowKeys.upPressed() and not onEndVine and self.isInPlayState(): + diffT -= baseVerticalRate * dt + if self.arrowKeys.downPressed() and not onEndVine and self.isInPlayState(): + diffT += baseVerticalRate * dt + newT = curT + diffT + if newT > 1: + newT = 1 + if newT < 0: + newT = 0 + oldClimbDir = self.getClimbDir(avId) + climbDir = 0 + if diffT < 0: + climbDir = -1 + elif diffT > 0: + climbDir = 1 + if not newT == curT: + self.vines[vineIndex].changeAttachedToonT(avId, newT) + if newT != curT or self.getClimbDir(avId) and not curT == 1.0 or oldClimbDir != climbDir: + if oldClimbDir != climbDir: + self.sendNewVineTUpdateAsap = True + self.b_setNewVineT(avId, newT, climbDir) + return task.cont + + def setupCollisions(self, anp): + fromObject = anp.attachNewNode(CollisionNode('colNode')) + fromObject.node().addSolid(CollisionSphere(0, 0, 0, self.LocalPhysicsRadius)) + fromCollideMask = ToontownGlobals.PieBitmask + self.notify.debug('fromCollideMask = %s' % fromCollideMask) + fromObject.node().setFromCollideMask(fromCollideMask) + self.handler = CollisionHandlerEvent() + self.handler.addInPattern('%fn-into') + base.cTrav.setRespectPrevTransform(True) + base.cTrav.addCollider(fromObject, self.handler) + eventName = '%s-into' % fromObject.getName() + self.accept(eventName, self.swingVineEnter) + height = base.localAvatar.getHeight() + self.treasureSphereName = 'treasureCollider' + center = Point3(0, 0, 0) + radius = VineTreasure.VineTreasure.RADIUS + self.treasureCollSphere = CollisionSphere(center[0], center[1], center[2], radius) + self.treasureCollSphere.setTangible(0) + self.treasureCollNode = CollisionNode(self.treasureSphereName) + self.treasureCollNode.setFromCollideMask(ToontownGlobals.WallBitmask) + self.treasureCollNode.addSolid(self.treasureCollSphere) + self.treasureCollNodePath = self.torso.attachNewNode(self.treasureCollNode) + self.treasureHandler = CollisionHandlerEvent() + self.treasureHandler.addInPattern('%fn-intoTreasure') + base.cTrav.addCollider(self.treasureCollNodePath, self.treasureHandler) + eventName = '%s-intoTreasure' % self.treasureCollNodePath.getName() + self.notify.debug('eventName = %s' % eventName) + self.accept(eventName, self.treasureEnter) + + def getFocusCameraPos(self, vineIndex, facingRight): + retPos = Point3(0, 0, 0) + if self.TwoVineView: + pos = self.vines[vineIndex].getPos() + retPos = Point3(self.cameraTwoVineSidePos) + if vineIndex == 0: + nextVinePos = self.vines[vineIndex + 1].getPos() + newX = (pos.getX() + nextVinePos.getX()) / 2.0 + newX -= self.cameraTwoVineAdj + retPos.setX(newX) + elif vineIndex == VineGameGlobals.NumVines - 1: + nextVinePos = self.vines[vineIndex].getPos() + nextVinePos.setX(nextVinePos.getX() + VineGameGlobals.VineXIncrement) + newX = (pos.getX() + nextVinePos.getX()) / 2.0 + newX += self.cameraTwoVineAdj + retPos.setX(newX) + else: + otherVineIndex = vineIndex - 1 + if self.lastJumpFacingRight: + otherVineIndex = vineIndex + 1 + nextVinePos = self.vines[otherVineIndex].getPos() + newX = (pos.getX() + nextVinePos.getX()) / 2.0 + if self.lastJumpFacingRight: + newX -= self.cameraTwoVineAdj + else: + newX += self.cameraTwoVineAdj + retPos.setX(newX) + else: + pos = self.vines[vineIndex].getPos() + retPos.setX(pos.getX()) + self.notify.debug('getFocusCameraPos returning %s' % retPos) + return retPos + + def focusCameraOnVine(self, vineIndex): + camera.setHpr(0, 0, 0) + newCameraPos = self.getFocusCameraPos(vineIndex, self.lastJumpFacingRight) + camera.setPos(newCameraPos) + + def attachLocalToonToVine(self, vineIndex, vineT, focusCameraImmediately = True, playSfx = False): + self.notify.debug('focusCameraImmediately = %s' % focusCameraImmediately) + if playSfx: + self.catchSound.play() + self.clearLocalPhysics() + vine = self.vines[vineIndex] + self.lastJumpVine = -1 + self.lastJumpPos = None + self.lastJumpT = 0 + if focusCameraImmediately: + self.focusCameraOnVine(vineIndex) + self.b_setNewVine(self.localAvId, vineIndex, vineT, self.lastJumpFacingRight) + return + + def setupJumpingTransitionIval(self, vineIndex): + self.doingJumpTransition = False + self.localToonJumpAnimIval = self.createJumpingTransitionIval(vineIndex) + if self.localToonJumpAnimIval: + self.doingJumpTransition = True + + def createJumpingTransitionIval(self, vineIndex): + retval = None + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + swingSeq = curInfo[6] + if swingSeq: + curFrame = -1 + for i in range(len(swingSeq)): + self.notify.debug('testing actor interval i=%d' % i) + actorIval = swingSeq[i] + if not actorIval.isStopped(): + testFrame = actorIval.getCurrentFrame() + self.notify.debug('actor ival is not stopped, testFrame=%f' % testFrame) + if testFrame: + curFrame = testFrame + break + + if curFrame > -1: + duration = 0.25 + if curFrame > self.ToonMaxRightFrame: + desiredFps = abs(self.JumpingFrame - curFrame) / duration + playRate = desiredFps / 24.0 + retval = ActorInterval(base.localAvatar, 'swing', startFrame=curFrame, endFrame=self.JumpingFrame, playRate=playRate) + else: + numFrames = curFrame + 1 + numFrames += SwingVine.SwingVine.MaxNumberOfFramesInSwingAnim - self.JumpingFrame + 1 + desiredFps = numFrames / duration + playRate = desiredFps / 24.0 + toonJump1 = ActorInterval(base.localAvatar, 'swing', startFrame=curFrame, endFrame=0, playRate=playRate) + toonJump2 = ActorInterval(base.localAvatar, 'swing', startFrame=SwingVine.SwingVine.MaxNumberOfFramesInSwingAnim - 1, endFrame=self.JumpingFrame, playRate=playRate) + retval = Sequence(toonJump1, toonJump2) + return retval + + def detachLocalToonFromVine(self, vineIndex, facingRight): + vine = self.vines[vineIndex] + self.lastJumpVine = vineIndex + self.lastJumpTime = self.getCurrentGameTime() + self.curFrame = base.localAvatar.getCurrentFrame() + curInfo = vine.getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + self.lastJumpPos = curInfo[1] + self.lastJumpT = curInfo[0] + else: + self.lastJumpPos = None + self.lastJumpT = 0 + self.notify.warning('vine %d failed get tooninfo %d' % (vineIndex, base.localAvatar.doId)) + self.setupJumpingTransitionIval(vineIndex) + self.vines[vineIndex].detachToon(base.localAvatar.doId) + return + + def treasureEnter(self, entry): + self.notify.debug('---- treasure Enter ---- ') + self.notify.debug('%s' % entry) + name = entry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + self.notify.debug('collided with %s, but returning' % name) + return + if not int(parts[1]) == self.doId: + self.notify.debug("collided with %s, but doId doesn't match" % name) + return + treasureNum = int(parts[2]) + self.__treasureGrabbed(treasureNum) + + def swingVineEnter(self, entry): + self.notify.debug('%s' % entry) + self.notify.debug('---- swingVine Enter ---- ') + name = entry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + self.notify.debug('collided with %s, but returning' % name) + return + vineIndex = int(parts[1]) + if vineIndex < 0 or vineIndex >= len(self.vines): + self.notify.warning('invalid vine index %d' % vineIndex) + return + if vineIndex == self.lastJumpVine: + if self.lastJumpPos: + diff = self.lastJumpPos - entry.getSurfacePoint(render) + if diff.length() < self.LocalPhysicsRadius: + return + if self.getCurrentGameTime() - self.lastJumpTime < VineGameGlobals.JumpTimeBuffer: + return + fallingInfo = self.toonInfo[self.localAvId][8] + if fallingInfo == self.FallingSpider or fallingInfo == self.FallingBat: + return + if abs(self.lastJumpVine - vineIndex) > 1: + return + vine = self.vines[vineIndex] + vineT = vine.calcTFromTubeHit(entry) + self.attachLocalToonToVine(vineIndex, vineT, False, playSfx=True) + self.setupAttachingToVineCamIval(vineIndex, self.lastJumpFacingRight) + + def makeOtherToonJump(self, avId, posX, posZ, velX, velZ): + if not self.otherToonPhysics.has_key(avId): + an = ActorNode('other-physics%s' % avId) + anp = render.attachNewNode(an) + base.physicsMgr.attachPhysicalNode(an) + self.otherToonPhysics[avId] = (an, anp) + an, anp = self.otherToonPhysics[avId] + anp.setPos(posX, 0, posZ) + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(anp) + av.setPos(-self.toonOffsets[self.localAvId]) + av.pose('swing', self.JumpingFrame) + self.notify.debug('pose set to swing jumping frame.') + if velX >= 0: + anp.setH(-90) + else: + anp.setH(90) + physObject = an.getPhysicsObject() + physObject.setVelocity(velX, 0, velZ) + + def makeOtherToonFallFromMidair(self, avId, posX, posZ, velX, velZ): + if not self.otherToonPhysics.has_key(avId): + an = ActorNode('other-physics%s' % avId) + anp = render.attachNewNode(an) + base.physicsMgr.attachPhysicalNode(an) + self.otherToonPhysics[avId] = (an, anp) + an, anp = self.otherToonPhysics[avId] + anp.setPos(posX, 0, posZ) + av = base.cr.doId2do.get(avId) + if av: + av.reparentTo(anp) + av.setPos(-self.toonOffsets[self.localAvId]) + if velX >= 0: + anp.setH(-90) + else: + anp.setH(90) + physObject = an.getPhysicsObject() + physObject.setVelocity(velX, 0, velZ) + + def makeLocalToonJump(self, vineIndex, t, pos, normal): + self.jumpSound.play() + self.clearChangeFacingInterval() + self.clearAttachingToVineCamIval() + an = ActorNode('av-physics') + anp = render.attachNewNode(an) + anp.setPos(pos) + base.localAvatar.reparentTo(anp) + base.localAvatar.setPos(-self.toonOffsets[self.localAvId]) + if normal.getX() >= 0: + self.lastJumpFacingRight = True + anp.setH(-90) + else: + anp.setH(90) + self.lastJumpFacingRight = False + base.physicsMgr.attachPhysicalNode(an) + physObject = an.getPhysicsObject() + velocity = normal * self.JumpSpeed + velocity *= t + self.notify.debug('jumping from vine with velocity of %s' % velocity) + physObject.setVelocity(velocity) + self.localPhysicsNP = anp + self.setupCollisions(anp) + if self.doingJumpTransition: + self.localToonJumpAnimIval.start() + else: + base.localAvatar.pose('swing', self.JumpingFrame) + self.b_setJumpingFromVine(self.localAvId, vineIndex, self.lastJumpFacingRight, pos[0], pos[2], velocity[0], velocity[2]) + + def makeLocalToonFallFromVine(self, fallingInfo): + self.clearAttachingToVineCamIval() + self.clearChangeFacingInterval() + vineIndex, vineInfo = self.getVineAndVineInfo(self.localAvId) + if vineIndex == -1: + self.notify.warning('we are not attached to a vine') + return + pos = vineInfo[1] + self.detachLocalToonFromVine(vineIndex, self.lastJumpFacingRight) + self.clearChangeFacingInterval() + an = ActorNode('av-physics') + anp = render.attachNewNode(an) + anp.setPos(vineInfo[1]) + base.localAvatar.reparentTo(anp) + base.localAvatar.setPos(-self.toonOffsets[self.localAvId]) + if self.lastJumpFacingRight: + anp.setH(-90) + else: + anp.setH(90) + base.physicsMgr.attachPhysicalNode(an) + physObject = an.getPhysicsObject() + velocity = Vec3(0, 0, -0.1) + physObject.setVelocity(velocity) + self.localPhysicsNP = anp + self.b_setFallingFromVine(self.localAvId, vineIndex, self.lastJumpFacingRight, pos[0], pos[2], velocity[0], velocity[2], fallingInfo) + + def makeLocalToonFallFromMidair(self, fallingInfo): + vineIndex, vineInfo = self.getVineAndVineInfo(self.localAvId) + if not vineIndex == -1: + self.notify.warning(' makeLocalToonFallFromMidair we are still attached to a vine') + return + if not self.localPhysicsNP: + self.notify.warning('self.localPhysicsNP is invalid') + return + pos = self.localPhysicsNP.getPos() + an = self.localPhysicsNP.node() + physObject = an.getPhysicsObject() + velocity = Vec3(0, 0, -0.1) + physObject.setVelocity(velocity) + self.b_setFallingFromMidair(self.localAvId, self.lastJumpFacingRight, pos[0], pos[2], velocity[0], velocity[2], fallingInfo) + + def changeLocalToonFacing(self, vineIndex, swingVineInfo, newFacingRight): + self.lastJumpFacingRight = newFacingRight + self.attachLocalToonToVine(vineIndex, swingVineInfo[0], focusCameraImmediately=False) + self.setupChangeFacingInterval(vineIndex, newFacingRight) + + def upArrowKeyHandler(self): + self.sendNewVineTUpdateAsap = True + + def downArrowKeyHandler(self): + self.sendNewVineTUpdateAsap = True + + def rightArrowKeyHandler(self): + curInfo = None + for vineIndex in xrange(len(self.vines)): + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + break + + if not curInfo: + return + if vineIndex == len(self.vines) - 1: + return + if not self.isInPlayState(): + return + doJump = True + if self.ArrowToChangeFacing: + if not self.lastJumpFacingRight: + doJump = False + self.changeLocalToonFacing(vineIndex, curInfo, True) + if doJump: + self.detachLocalToonFromVine(vineIndex, 1) + normal = curInfo[2] + self.makeLocalToonJump(vineIndex, curInfo[0], curInfo[1], normal) + return + + def leftArrowKeyHandler(self): + curInfo = None + for vineIndex in xrange(len(self.vines)): + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if curInfo: + break + + curInfo = self.vines[vineIndex].getAttachedToonInfo(base.localAvatar.doId) + if not curInfo: + return + if vineIndex == 0: + return + if vineIndex == len(self.vines) - 1: + return + if not self.isInPlayState(): + return + doJump = True + if self.ArrowToChangeFacing: + if self.lastJumpFacingRight: + doJump = False + self.changeLocalToonFacing(vineIndex, curInfo, False) + if doJump: + self.detachLocalToonFromVine(vineIndex, 0) + normal = curInfo[2] + normal *= -1 + self.makeLocalToonJump(vineIndex, curInfo[0], curInfo[1], normal) + return + + def b_setNewVine(self, avId, vineIndex, vineT, facingRight): + self.setNewVine(avId, vineIndex, vineT, facingRight) + self.d_setNewVine(avId, vineIndex, vineT, facingRight) + + def d_setNewVine(self, avId, vineIndex, vineT, facingRight): + self.notify.debug('setNewVine avId=%d vineIndex=%s' % (avId, vineIndex)) + self.sendUpdate('setNewVine', [avId, + vineIndex, + vineT, + facingRight]) + + def setNewVine(self, avId, vineIndex, vineT, facingRight): + self.updateToonInfo(avId, vineIndex=vineIndex, vineT=vineT, facingRight=facingRight, climbDir=0, fallingInfo=self.FallingNot) + + def b_setNewVineT(self, avId, vineT, climbDir): + self.setNewVineT(avId, vineT, climbDir) + self.d_setNewVineT(avId, vineT, climbDir) + + def d_setNewVineT(self, avId, vineT, climbDir): + sendIt = False + curTime = self.getCurrentGameTime() + if self.sendNewVineTUpdateAsap: + sendIt = True + elif curTime - self.lastNewVineTUpdate > 0.2: + sendIt = True + if sendIt: + self.sendUpdate('setNewVineT', [avId, vineT, climbDir]) + self.sendNewVineTUpdateAsap = False + self.lastNewVineTUpdate = self.getCurrentGameTime() + + def setNewVineT(self, avId, vineT, climbDir): + self.updateToonInfo(avId, vineT=vineT, climbDir=climbDir) + + def b_setJumpingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ): + self.setJumpingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ) + self.d_setJumpingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ) + + def d_setJumpingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ): + self.sendUpdate('setJumpingFromVine', [avId, + vineIndex, + facingRight, + posX, + posZ, + velX, + velZ]) + + def setJumpingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ): + self.updateToonInfo(avId, vineIndex=-1, facingRight=facingRight, posX=posX, posZ=posZ, velX=velX, velZ=velZ) + + def b_setFallingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.setFallingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo) + self.d_setFallingFromVine(avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo) + + def d_setFallingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.sendUpdate('setFallingFromVine', [avId, + vineIndex, + facingRight, + posX, + posZ, + velX, + velZ, + fallingInfo]) + + def setFallingFromVine(self, avId, vineIndex, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.updateToonInfo(avId, vineIndex=-1, facingRight=facingRight, posX=posX, posZ=posZ, velX=velX, velZ=velZ, fallingInfo=fallingInfo) + + def b_setFallingFromMidair(self, avId, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.setFallingFromMidair(avId, facingRight, posX, posZ, velX, velZ, fallingInfo) + self.d_setFallingFromMidair(avId, facingRight, posX, posZ, velX, velZ, fallingInfo) + + def d_setFallingFromMidair(self, avId, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.sendUpdate('setFallingFromMidair', [avId, + facingRight, + posX, + posZ, + velX, + velZ, + fallingInfo]) + + def setFallingFromMidair(self, avId, facingRight, posX, posZ, velX, velZ, fallingInfo): + self.updateToonInfo(avId=avId, facingRight=facingRight, posX=posX, posZ=posZ, velX=velX, velZ=velZ, fallingInfo=fallingInfo) + + def d_setFallingPos(self, avId, posX, posZ): + self.sendUpdate('setFallingPos', [avId, posX, posZ]) + + def setFallingPos(self, avId, posX, posZ): + self.updateToonInfo(avId, posX=posX, posZ=posZ) + + def __treasureGrabbed(self, treasureNum): + self.treasures[treasureNum].showGrab() + self.grabSound.play() + self.sendUpdate('claimTreasure', [treasureNum]) + + def setTreasureGrabbed(self, avId, treasureNum): + if not self.hasLocalToon: + return + self.notify.debug('treasure %s grabbed by %s' % (treasureNum, avId)) + if avId != self.localAvId: + self.treasures[treasureNum].showGrab() + i = self.avIdList.index(avId) + self.scores[i] += 1 + self.scorePanels[i].setScore(self.scores[i]) + + def setScore(self, avId, score): + if not self.hasLocalToon: + return + i = self.avIdList.index(avId) + if hasattr(self, 'scorePanels'): + self.scores[i] += score + self.scorePanels[i].setScore(score) + + def timerExpired(self): + self.notify.debug('game timer expired') + if not VineGameGlobals.EndlessGame: + if hasattr(self, 'gameFSM'): + self.gameFSM.request('showScores') + + def allAtEndVine(self): + if not self.hasLocalToon: + return + self.notify.debug('all at end vine') + if not VineGameGlobals.EndlessGame: + self.gameFSM.request('showScores') + + def clearChangeFacingInterval(self): + if self.changeFacingInterval: + self.changeFacingInterval.pause() + del self.changeFacingInterval + self.changeFacingInterval = None + return + + def setupChangeFacingInterval(self, vineIndex, newFacingRight): + self.clearChangeFacingInterval() + self.changeFacingInterval = Sequence() + if not (vineIndex == 0 or vineIndex == VineGameGlobals.NumVines - 1): + destPos = self.getFocusCameraPos(vineIndex, newFacingRight) + self.changeFacingInterval.append(LerpPosInterval(base.camera, 0.5, destPos)) + self.changeFacingInterval.append(Func(self.clearChangeFacingInterval)) + self.changeFacingInterval.start() + + def clearAttachingToVineCamIval(self): + if self.attachingToVineCamIval: + self.attachingToVineCamIval.pause() + del self.attachingToVineCamIval + self.attachingToVineCamIval = None + return + + def setupAttachingToVineCamIval(self, vineIndex, facingRight): + self.clearAttachingToVineCamIval() + self.attachingToVineCamIval = Sequence() + destPos = self.getFocusCameraPos(vineIndex, facingRight) + self.attachingToVineCamIval.append(LerpPosInterval(base.camera, 0.5, destPos)) + self.attachingToVineCamIval.append(Func(self.clearAttachingToVineCamIval)) + self.attachingToVineCamIval.start() + + def createRadar(self): + self.cCamera = render.attachNewNode('cCamera') + self.cCamNode = Camera('cCam') + self.cCamNode.setCameraMask(VineGameGlobals.RadarCameraBitmask) + self.cLens = PerspectiveLens() + xFov = 40 + yFov = 2.5 + self.cLens.setFov(xFov, yFov) + self.cLens.setNear(0.1) + self.cLens.setFar(1300.0) + self.cCamNode.setLens(self.cLens) + self.cCamNode.setScene(render) + self.cCam = self.cCamera.attachNewNode(self.cCamNode) + self.cCam.setPos(300, -850, 16.3) + endY = yFov / xFov + self.endZRadar = 0.09375 + self.cDr = base.win.makeDisplayRegion(0, 1, 0, self.endZRadar) + self.cDr.setSort(1) + self.cDr.setClearDepthActive(1) + self.cDr.setClearColorActive(1) + self.cDr.setClearColor(Vec4(0.85, 0.95, 0.95, 1)) + self.cDr.setCamera(self.cCam) + self.radarSeparator = DirectFrame(relief=None, image=DGG.getDefaultDialogGeom(), image_color=(0.2, 0.0, 0.8, 1), image_scale=(2.65, 1.0, 0.01), pos=(0, 0, -0.8125)) + self.oldBaseCameraMask = base.camNode.getCameraMask() + base.camNode.setCameraMask(BitMask32.bit(0)) + return + + def destroyRadar(self): + base.win.removeDisplayRegion(self.cDr) + self.cCamera.removeNode() + del self.cCamera + del self.cCamNode + del self.cLens + del self.cCam + del self.cDr + self.radarSeparator.destroy() + del self.radarSeparator + base.camNode.setCameraMask(self.oldBaseCameraMask) + + def updateRadar(self): + for avId in self.headFrames: + av = base.cr.doId2do.get(avId) + headFrameShown = False + if av: + avPos = av.getPos(render) + newPoint = self.mapRadarToAspect2d(render, avPos) + if newPoint: + headFrameShown = True + self.headFrames[avId].setPos(newPoint[0], newPoint[1], newPoint[2]) + if headFrameShown: + self.headFrames[avId].show() + else: + self.headFrames[avId].hide() + + def mapRadarToAspect2d(self, node, point): + if point[0] > 26: + pass + p3 = self.cCam.getRelativePoint(node, point) + p2 = Point2() + if not self.cLens.project(p3, p2): + return None + r2d = Point3(p2[0], 0, p2[1]) + a2d = aspect2d.getRelativePoint(render2d, r2d) + zAspect2DRadar = self.endZRadar * 2.0 - 1 + oldZ = a2d.getZ() + newZ = (oldZ + 1) / 2.0 * (zAspect2DRadar + 1) + newZ -= 1 + a2d.setZ(newZ) + return a2d + + def localToonHitSpider(self, colEntry): + self.notify.debug('toonHitSpider') + if self.toonInfo[self.localAvId][0] == -1: + fallingInfo = self.toonInfo[self.localAvId][8] + if not (fallingInfo == self.FallingBat or fallingInfo == self.FallingSpider): + self.spiderHitSound.play() + self.makeLocalToonFallFromMidair(self.FallingSpider) + else: + self.spiderHitSound.play() + self.makeLocalToonFallFromVine(self.FallingSpider) + + def localToonHitBat(self, colEntry): + self.notify.debug('toonHitBat') + if self.toonInfo[self.localAvId][0] == VineGameGlobals.NumVines - 1: + return + if self.toonInfo[self.localAvId][0] == -1: + self.batHitMidairSound.play() + fallingInfo = self.toonInfo[self.localAvId][8] + if not (fallingInfo == self.FallingBat or fallingInfo == self.FallingSpider): + self.makeLocalToonFallFromMidair(self.FallingBat) + else: + self.batHitVineSound.play() + self.makeLocalToonFallFromVine(self.FallingBat) + + def toonHitSomething(self, colEntry): + if not self.isInPlayState(): + return + intoName = colEntry.getIntoNodePath().getName() + if 'spider' in intoName: + self.localToonHitSpider(colEntry) + elif 'bat' in intoName: + self.localToonHitBat(colEntry) + + def setVineSections(self, vineSections): + self.vineSections = vineSections + + def setupVineCourse(self): + vineIndex = 0 + for section in self.vineSections: + for vineInfo in VineGameGlobals.CourseSections[section]: + length, baseAngle, vinePeriod, spiderPeriod = vineInfo + posX = vineIndex * VineGameGlobals.VineXIncrement + newVine = SwingVine.SwingVine(vineIndex, posX, 0, VineGameGlobals.VineHeight, length=length, baseAngle=baseAngle, period=vinePeriod, spiderPeriod=spiderPeriod) + self.vines.append(newVine) + vineIndex += 1 + + def loadBats(self): + self.bats = [] + szId = self.getSafezoneId() + self.batInfo = VineGameGlobals.BatInfo[szId] + batIndex = 0 + for batTuple in self.batInfo: + newBat = VineBat.VineBat(batIndex, self.batInfo[batIndex][0]) + xPos = VineGameGlobals.VineXIncrement * VineGameGlobals.NumVines + 100 + newBat.setX(xPos) + self.bats.append(newBat) + batIndex += 1 + + def createBatIvals(self): + self.batIvals = [] + for batIndex in xrange(len(self.bats)): + newBatIval = self.createBatIval(batIndex) + self.batIvals.append(newBatIval) + + def startBatIvals(self): + for batIval in self.batIvals: + batIval.start() + + def destroyBatIvals(self): + for batIval in self.batIvals: + batIval.finish() + + self.batIvals = [] + + def createBatIval(self, batIndex): + timeToTraverseField = self.batInfo[batIndex][0] + initialDelay = self.batInfo[batIndex][1] + startMultiplier = 1 + if len(self.batInfo[batIndex]) >= 3: + startMultiplier = 0.25 + batIval = Sequence() + batIval.append(Wait(initialDelay)) + batIval.append(Func(self.bats[batIndex].startFlying)) + startX = VineGameGlobals.VineXIncrement * VineGameGlobals.NumVines + endX = -VineGameGlobals.VineXIncrement + firstInterval = True + while batIval.getDuration() < VineGameGlobals.GameDuration: + batHeight = self.randomNumGen.randrange(VineGameGlobals.BatMinHeight, VineGameGlobals.BatMaxHeight) + batIval.append(Func(self.bats[batIndex].startLap)) + if firstInterval: + newIval = LerpPosInterval(self.bats[batIndex], duration=timeToTraverseField * startMultiplier, pos=Point3(endX, 0, batHeight), startPos=Point3(startX * startMultiplier, 0, batHeight)) + else: + newIval = LerpPosInterval(self.bats[batIndex], duration=timeToTraverseField, pos=Point3(endX, 0, batHeight), startPos=Point3(startX, 0, batHeight)) + batIval.append(newIval) + firstInterval = False + + batIval.append(Func(self.bats[batIndex].stopFlying)) + return batIval + + def isInPlayState(self): + if not self.gameFSM.getCurrentState(): + return False + if not self.gameFSM.getCurrentState().getName() == 'play': + return False + return True + + def getIntroTrack(self): + retval = Sequence() + toonTakeoffs = Parallel() + didCameraMove = False + for index in xrange(len(self.avIdList)): + avId = self.avIdList[index] + if avId != self.localAvId: + continue + oneSeq = Sequence() + oneSeqAndHowl = Parallel() + av = base.cr.doId2do.get(avId) + if av: + toonOffset = self.calcToonOffset(av) + platformPos = self.startPlatform.getPos() + endPos = self.vines[0].getPos() + endPos.setZ(endPos.getZ() - toonOffset.getZ() - VineGameGlobals.VineStartingT * self.vines[0].cableLength) + xPos = platformPos[0] - 0.5 + takeOffPos = Point3(xPos, platformPos[1], platformPos[2]) + leftPos = Point3(xPos - 27, platformPos[1], platformPos[2]) + self.notify.debug('leftPos = %s platformPos=%s' % (leftPos, platformPos)) + startRunningPos = Point3(takeOffPos) + startRunningPos.setX(startRunningPos.getX() - 7) + oneSeq.append(Func(av.dropShadow.show)) + oneSeq.append(Func(av.setH, -90)) + oneSeq.append(Func(av.setPos, takeOffPos[0], takeOffPos[1], takeOffPos[2])) + exclamationSfx = av.getDialogueSfx('exclamation', 0) + oneSeq.append(Parallel(ActorInterval(av, 'confused', duration=3))) + questionSfx = av.getDialogueSfx('question', 0) + oneSeq.append(Parallel(ActorInterval(av, 'think', duration=3.0), SoundInterval(questionSfx, duration=3))) + oneSeq.append(Func(av.setH, 90)) + oneSeq.append(Func(av.setPlayRate, 1, 'walk')) + oneSeq.append(Func(av.loop, 'walk')) + oneSeq.append(Parallel(LerpPosInterval(av, pos=leftPos, duration=5.25), SoundInterval(av.soundWalk, loop=1, duration=5.25))) + oneSeq.append(Func(av.setH, -90)) + oneSeq.append(Func(av.loop, 'run')) + oneSeq.append(Parallel(LerpPosInterval(av, pos=takeOffPos, duration=2.5), SoundInterval(av.soundRun, loop=1, duration=2.5))) + oneSeq.append(Func(av.dropShadow.hide)) + howlTime = oneSeq.getDuration() + oneSeq.append(Parallel(LerpPosInterval(av, pos=endPos, duration=0.5), Func(av.pose, 'swing', self.JumpingFrame))) + attachingToVineSeq = Sequence(ActorInterval(av, 'swing', startFrame=self.JumpingFrame, endFrame=143, playRate=2.0), ActorInterval(av, 'swing', startFrame=0, endFrame=86, playRate=2.0)) + attachingToVine = Parallel(attachingToVineSeq, SoundInterval(self.catchSound)) + if didCameraMove: + oneSeq.append(attachingToVine) + else: + attachAndMoveCam = Parallel(attachingToVine, LerpPosInterval(base.camera, pos=self.getFocusCameraPos(0, True), duration=2)) + oneSeq.append(attachAndMoveCam) + oneSeq.append(Func(av.setPos, 0, 0, 0)) + oneSeq.append(Func(av.setH, 0)) + oneSeq.append(Func(self.vines[0].attachToon, avId, VineGameGlobals.VineStartingT, self.lastJumpFacingRight, setupAnim=False)) + oneSeq.append(Func(self.vines[0].updateAttachedToons)) + howlSfx = av.getDialogueSfx('special', 0) + howlSeq = Sequence(Wait(howlTime), SoundInterval(howlSfx)) + exclamationSeq = Sequence(Wait(0.5), SoundInterval(exclamationSfx)) + oneSeqAndHowl = Parallel(oneSeq, howlSeq, exclamationSeq) + toonTakeoffs.append(oneSeqAndHowl) + + retval.append(toonTakeoffs) + return retval + + def doEndingTrackTask(self, avId): + taskName = 'VineGameEnding-%s' % avId + if not self.endingTracks.has_key(avId): + taskMgr.doMethodLater(0.5, self.setupEndingTrack, taskName, extraArgs=(avId,)) + self.endingTrackTaskNames.append(taskName) + + def debugCameraPos(self): + self.notify.debug('cameraPos = %s' % base.camera.getPos()) + + def setupEndingTrack(self, avId): + if self.endingTracks.has_key(avId): + self.notify.warning('setupEndingTrack duplicate call avId=%d' % avId) + return + if len(self.vines) == 0: + return + endVine = VineGameGlobals.NumVines - 1 + platformPos = self.endPlatform.getPos() + avIndex = self.avIdList.index(avId) + landingPos = Point3(platformPos) + landingPos.setX(landingPos.getX() + avIndex * 2) + endingTrack = Sequence() + av = base.cr.doId2do.get(avId) + avPos = av.getPos(render) + cameraPos = base.camera.getPos() + self.notify.debug('avPos=%s cameraPos=%s' % (avPos, base.camera.getPos())) + if av: + midX = landingPos[0] + midZ = platformPos[2] + 6 + jumpingTransition = self.createJumpingTransitionIval(endVine) + endingTrack.append(Func(self.vines[endVine].detachToon, avId)) + endingTrack.append(Func(av.wrtReparentTo, render)) + endingTrack.append(Func(self.debugCameraPos)) + endingTrack.append(Func(av.loop, 'jump-idle')) + landingIval = Parallel(ProjectileInterval(av, startPos=av.getPos(render), endZ=landingPos[2], wayPoint=Point3(midX, 0, midZ), timeToWayPoint=1)) + endingTrack.append(landingIval) + endingTrack.append(Func(av.dropShadow.show)) + endingTrack.append(Func(av.play, 'jump-land')) + endingTrack.append(Func(self.winSound.play)) + endingTrack.append(Func(av.loop, 'victory')) + self.endingTracks[avId] = endingTrack + endingTrack.start() + return Task.done + + def cleanupEndingTracks(self): + for taskName in self.endingTrackTaskNames: + taskMgr.remove(taskName) + + for endingTrack in self.endingTracks.values(): + endingTrack.finish + del endingTrack + + self.endingTracks = {} diff --git a/toontown/minigame/DistributedVineGameAI.py b/toontown/minigame/DistributedVineGameAI.py new file mode 100644 index 0000000..86a6eec --- /dev/null +++ b/toontown/minigame/DistributedVineGameAI.py @@ -0,0 +1,320 @@ +from DistributedMinigameAI import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import VineGameGlobals + +class DistributedVineGameAI(DistributedMinigameAI): + + def __init__(self, air, minigameId): + try: + self.DistributedVineGameAI_initialized + except: + self.DistributedVineGameAI_initialized = 1 + DistributedMinigameAI.__init__(self, air, minigameId) + self.gameFSM = ClassicFSM.ClassicFSM('DistributedVineGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), + State.State('play', self.enterPlay, self.exitPlay, ['cleanup', 'waitShowScores']), + State.State('waitShowScores', self.enterWaitShowScores, self.exitWaitShowScores, ['cleanup']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive') + self.toonInfo = {} + self.addChildGameFSM(self.gameFSM) + self.vineSections = [] + self.finishedBonus = {} + self.finishedTimeLeft = {} + self.totalSpiders = 0 + self.calculatedPartialBeans = False + + def generate(self): + self.notify.debug('generate') + DistributedMinigameAI.generate(self) + + def delete(self): + self.notify.debug('delete') + del self.gameFSM + DistributedMinigameAI.delete(self) + + def _playing(self): + if not hasattr(self, 'gameFSM'): + return False + if self.gameFSM.getCurrentState() == None: + return False + return self.gameFSM.getCurrentState().getName() == 'play' + + def setGameReady(self): + self.notify.debug('setGameReady') + for avId in self.avIdList: + self.updateToonInfo(avId, vineIndex=0, vineT=VineGameGlobals.VineStartingT) + + DistributedMinigameAI.setGameReady(self) + self.numTreasures = VineGameGlobals.NumVines - 1 + self.numTreasuresTaken = 0 + self.takenTable = [0] * self.numTreasures + for avId in self.scoreDict.keys(): + self.scoreDict[avId] = 0 + self.finishedBonus[avId] = 0 + self.finishedTimeLeft[avId] = -1 + + def setGameStart(self, timestamp): + self.notify.debug('setGameStart') + DistributedMinigameAI.setGameStart(self, timestamp) + self.gameFSM.request('play') + + def setGameAbort(self): + self.notify.debug('setGameAbort') + if self.gameFSM.getCurrentState(): + self.gameFSM.request('cleanup') + DistributedMinigameAI.setGameAbort(self) + + def gameOver(self): + self.notify.debug('gameOver') + vineReached = [] + scoreList = [] + finishedList = [] + timeLeftList = [] + for avId in self.avIdList: + vineReached.append(self.toonInfo[avId][0]) + scoreList.append(self.scoreDict[avId]) + finishedList.append(self.finishedBonus[avId]) + timeLeftList.append(self.finishedTimeLeft[avId]) + + totalBats = len(VineGameGlobals.BatInfo[self.getSafezoneId()]) + self.air.writeServerEvent('minigame_vine', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.VineGameId, + self.getSafezoneId(), + self.avIdList, + scoreList, + self.vineSections, + finishedList, + timeLeftList, + vineReached, + self.totalSpiders, + totalBats)) + self.gameFSM.request('cleanup') + DistributedMinigameAI.gameOver(self) + + def enterInactive(self): + self.notify.debug('enterInactive') + + def exitInactive(self): + pass + + def enterPlay(self): + self.notify.debug('enterPlay') + self.vines = [] + index = 0 + taskMgr.doMethodLater(VineGameGlobals.GameDuration, self.timerExpired, self.taskName('gameTimer')) + + def exitPlay(self): + taskMgr.remove(self.taskName('gameTimer')) + for vine in self.vines: + vine.requestDelete() + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.gameFSM.request('inactive') + + def exitCleanup(self): + pass + + def claimTreasure(self, treasureNum): + if not self._playing(): + return + avId = self.air.getAvatarIdFromSender() + if not self.scoreDict.has_key(avId): + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + if treasureNum < 0 or treasureNum >= self.numTreasures: + self.air.writeServerEvent('warning', treasureNum, 'MazeGameAI.claimTreasure treasureNum out of range') + return + if self.takenTable[treasureNum]: + return + self.takenTable[treasureNum] = 1 + avId = self.air.getAvatarIdFromSender() + self.sendUpdate('setTreasureGrabbed', [avId, treasureNum]) + self.scoreDict[avId] += 1 + self.numTreasuresTaken += 1 + + def timerExpired(self, task): + self.notify.debug('timer expired') + if not VineGameGlobals.EndlessGame: + self.gameFSM.request('waitShowScores') + return Task.done + + def enterWaitShowScores(self): + self.notify.debug('enterWaitShowScores') + self.awardPartialBeans() + taskMgr.doMethodLater(VineGameGlobals.ShowScoresDuration, self.__doneShowingScores, self.taskName('waitShowScores')) + + def __doneShowingScores(self, task): + self.notify.debug('doneShowingScores') + self.gameOver() + return Task.done + + def exitWaitShowScores(self): + taskMgr.remove(self.taskName('waitShowScores')) + + def reachedEndVine(self, vineIndex): + self.notify.debug('reachedEndVine') + return + avId = self.air.getAvatarIdFromSender() + oldVineIndex = self.toonInfo[avId][0] + self.updateToonInfo(avId, vineIndex=vineIndex) + if not oldVineIndex == vineIndex: + self.checkForEndVine(avId) + self.checkForEndGame() + + def setNewVine(self, avId, vineIndex, vineT, facingRight): + self.notify.debug('setNewVine') + if not self._playing(): + return + if avId not in self.avIdList: + self.air.writeServerEvent('suspicious', avId, 'VineGameAI.setNewVine: invalid avId') + return + oldVineIndex = self.toonInfo[avId][0] + debugStr = 'setNewVine doId=%s avId=%d vineIndex=%s oldVineIndex=%s' % (self.doId, + avId, + vineIndex, + oldVineIndex) + self.updateToonInfo(avId, vineIndex=vineIndex, vineT=vineT, facingRight=facingRight) + if not oldVineIndex == vineIndex: + self.checkForEndVine(avId) + self.checkForEndGame() + + def checkForEndGame(self): + allDone = True + for avId in self.toonInfo: + if not self.toonInfo[avId][0] == VineGameGlobals.NumVines - 1: + allDone = False + break + + if allDone: + if not VineGameGlobals.EndlessGame: + self.awardPartialBeans() + self.sendUpdate('allAtEndVine', []) + self.gameOver() + + def checkForEndVine(self, avId): + if self.toonInfo[avId][0] == VineGameGlobals.NumVines - 1: + curTime = self.getCurrentGameTime() + timeLeft = VineGameGlobals.GameDuration - curTime + self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft)) + if not self.scoreDict.has_key(avId): + self.notify.warning('PROBLEM: avatar %s called claimTreasure(%s) but he is not in the scoreDict: %s. avIdList is: %s' % (avId, + treasureNum, + self.scoreDict, + self.avIdList)) + return + addBonus = int(VineGameGlobals.BaseBonusOnEndVine[self.getSafezoneId()] + VineGameGlobals.BonusPerSecondLeft * timeLeft) + self.notify.debug('addBOnus = %d' % addBonus) + if addBonus < 0: + addBonus = 0 + self.finishedBonus[avId] = addBonus + timeLeftStr = '%.1f' % timeLeft + self.finishedTimeLeft[avId] = timeLeftStr + self.scoreDict[avId] += addBonus + self.sendUpdate('setScore', [avId, self.scoreDict[avId]]) + + def updateToonInfo(self, avId, vineIndex = None, vineT = None, posX = None, posZ = None, facingRight = None, climbDir = None, velX = None, velZ = None): + newVineIndex = vineIndex + newVineT = vineT + newPosX = posX + newPosZ = posZ + newFacingRight = facingRight + newClimbDir = climbDir + newVelX = velX + newVelZ = velZ + oldInfo = None + if self.toonInfo.has_key(avId): + oldInfo = self.toonInfo[avId] + if vineIndex == None: + newVineIndex = oldInfo[0] + if vineT == None: + newVineT = oldInfo[1] + if posX == None: + newPosX = oldInfo[2] + if posZ == None: + newPosZ = oldInfo[3] + if facingRight == None: + newFacingRight = oldInfo[4] + if climbDir == None: + newClimbDir = oldInfo[5] + if velX == None: + newVelX = oldInfo[6] + if velZ == None: + newVelZ = oldInfo[7] + if newVineIndex < -1 or newVineIndex >= VineGameGlobals.NumVines: + newVineIndex = 0 + if newVineT < 0 or newVineT > 1: + pass + if not newFacingRight == 0 and not newFacingRight == 1: + newFacingRight = 1 + if newPosX < -1000 or newPosX > 2000: + newPosX = 0 + if newPosZ < -100 or newPosZ > 1000: + newPosZ = 0 + if newVelX < -1000 or newVelX > 1000: + newVelX = 0 + if newVelZ < -1000 or newVelZ > 1000: + newVelZ = 0 + newInfo = [newVineIndex, + newVineT, + newPosX, + newPosZ, + newFacingRight, + newClimbDir, + newVelX, + newVelZ] + self.toonInfo[avId] = newInfo + return + + def setupVineSections(self): + szId = self.getSafezoneId() + courseWeights = VineGameGlobals.CourseWeights[szId] + pool = [[], + [], + [], + [], + [], + []] + for weights in courseWeights: + section, chances = weights + numSpiders = VineGameGlobals.getNumSpidersInSection(section) + pool[numSpiders] += [section] * chances + + maxSpiders = VineGameGlobals.SpiderLimits[szId] + curSpiders = 0 + for i in range(4): + spidersLeft = maxSpiders - curSpiders + validChoices = [] + for numSpiders in range(spidersLeft + 1): + validChoices += pool[numSpiders] + + if not validChoices: + self.notify.warning('we ran out of valid choices szId=%s, vineSections=%s' % (szId, self.vineSections)) + validChoices += [0] + section = random.choice(validChoices) + curSpiders += VineGameGlobals.getNumSpidersInSection(section) + self.vineSections.append(section) + + self.totalSpiders = curSpiders + self.notify.debug('calc vineSections = %s' % self.vineSections) + + def getVineSections(self): + return self.vineSections + + def setTrolleyZone(self, trolleyZone): + DistributedMinigameAI.setTrolleyZone(self, trolleyZone) + self.setupVineSections() + + def awardPartialBeans(self): + if self.calculatedPartialBeans: + return + self.calculatedPartialBeans = True + for avId in self.avIdList: + vineIndex = self.toonInfo[avId][0] + if not vineIndex == VineGameGlobals.NumVines - 1: + partialBeans = int(vineIndex / 5.0) + if self.scoreDict.has_key(avId): + self.scoreDict[avId] += partialBeans + self.sendUpdate('setScore', [avId, self.scoreDict[avId]]) diff --git a/toontown/minigame/DivingFishSpawn.py b/toontown/minigame/DivingFishSpawn.py new file mode 100644 index 0000000..9a1830f --- /dev/null +++ b/toontown/minigame/DivingFishSpawn.py @@ -0,0 +1,120 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.actor import Actor +import random +import DivingGameGlobals + +class DivingFishSpawn(DirectObject): + RADIUS = 0.7 + + def __init__(self, spawnId, direction, position, cHandler): + loadBase = 'phase_4/models/char/' + self.entryNode = render.attachNewNode('entryNode') + self.direction = direction + self.fishArray = {} + self.inactiveArray = [] + self.fishActive = 0 + self.position = position + self.spawnId = spawnId + self.id = -1 + + def getUniqueNumber(self): + self.id += 1 + return self.id + + def createFish(self, fishcode): + loadBase = 'phase_4/models/char/' + if fishcode is 0: + fish = Actor.Actor('phase_4/models/char/clownFish-zero.bam', {'anim': loadBase + 'clownFish-swim.bam'}) + fish.name = 'clown' + elif fishcode is 1: + fish = Actor.Actor('phase_4/models/char/PBJfish-zero.bam', {'anim': 'phase_4/models/char/PBJfish-swim.bam'}) + fish.name = 'pbj' + elif fishcode is 2: + fish = Actor.Actor('phase_4/models/char/BearAcuda-zero.bam', {'anim': 'phase_4/models/char/BearAcuda-swim.bam'}) + fish.name = 'bear' + elif fishcode is 3: + fish = Actor.Actor(loadBase + 'balloonFish-zero.bam', {'anim': loadBase + 'balloonFish-swim.bam'}) + fish.name = 'balloon' + elif fishcode is 4: + fish = Actor.Actor(loadBase + 'nurseShark-zero.bam', {'anim': loadBase + 'nurseShark-swim.bam'}) + fish.name = 'nurse' + elif fishcode is 5: + fish = Actor.Actor(loadBase + 'pianoTuna-zero.bam', {'anim': loadBase + 'pianoTuna-swim.bam'}) + fish.name = 'piano' + else: + return + fish.active = 1 + fish.direction = self.direction + idCode = self.getUniqueNumber() + fish.code = str(self.spawnId) + str(idCode) + self.fishArray[idCode] = fish + fish.reparentTo(render) + fish.setScale(1) + fish.moveLerp = Sequence() + if fish.name == 'clown': + fish.setH(90 * self.direction) + fish.loop('anim') + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.2) + elif fish.name == 'pbj': + fish.setH(15 * self.direction) + fish.loop('anim') + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1) + elif fish.name == 'balloon': + fish.setH(0) + fish.loop('anim', fromFrame=0, toFrame=94) + fish.setScale(2) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 0.2) + elif fish.name == 'bear': + fish.setH(90 * self.direction) + cSphere = CollisionSphere(0.0, -1.0, 3.5, 3.0) + fish.loop('anim') + fish.setScale(0.4, 1.7, 1.7) + elif fish.name == 'nurse': + fish.setH(90 * self.direction) + cSphere = CollisionSphere(0.0, -1.0, 0.0, 1) + fish.setScale(0.5, 1.7, 1.7) + fish.loop('anim') + elif fish.name == 'mackerel': + fish.setH(90 * self.direction) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.5) + fish.loop('anim', fromFrame=36, toFrame=80) + elif fish.name == 'piano': + fish.loop('anim') + fish.setScale(1.4) + cSphere = CollisionSphere(0, 0, 0, 1) + fishSoundName = 'Piano_Tuna.mp3' + if self.direction is -1: + fish.setH(0) + else: + fish.setH(180) + cSphere.setTangible(0) + fish.offset = 0 + cSphereNode = CollisionNode('fc' + str(fish.code)) + cSphereNode.addSolid(cSphere) + cSphereNode.setFromCollideMask(BitMask32.allOff()) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNodePath = fish.attachNewNode(cSphereNode) + self.accept('into-' + 'fc' + str(fish.code), self.__handleFishCollide) + fish.moveloop = Sequence(Wait(4), LerpScaleInterval(fish, startScale=1, scale=3, duration=1), Wait(1.5), LerpScaleInterval(fish, startScale=3, scale=1, duration=0.5)) + return fish + + def destroy(self): + self.ignoreAll() + for fish in self.fishArray.values(): + fish.moveLerp.pause() + fish.specialLerp.finish() + if hasattr(fish, 'sound'): + fish.sound.stop() + fish.sound = None + fish.moveLerp = None + fish.specialLerp = None + fish.removeNode() + del fish + + return + + def __handleFishCollide(self, collEntry): + messenger.send('FishHit', [collEntry]) diff --git a/toontown/minigame/DivingGameGlobals.py b/toontown/minigame/DivingGameGlobals.py new file mode 100644 index 0000000..c4b9186 --- /dev/null +++ b/toontown/minigame/DivingGameGlobals.py @@ -0,0 +1,5 @@ +from toontown.toonbase import ToontownGlobals +ENDLESS_GAME = config.GetBool('endless-maze-game', 0) +NUM_SPAWNERS = 6 +GAME_DURATION = 60.0 +CollideMask = ToontownGlobals.CatchGameBitmask diff --git a/toontown/minigame/DivingGameToonSD.py b/toontown/minigame/DivingGameToonSD.py new file mode 100644 index 0000000..9278f55 --- /dev/null +++ b/toontown/minigame/DivingGameToonSD.py @@ -0,0 +1,99 @@ +from direct.showbase.ShowBaseGlobal import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM +from direct.fsm import State +import CatchGameGlobals + +class DivingGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('DivingGameToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + CatchNeutralAnim = 'catch-neutral' + CatchRunAnim = 'catch-run' + EatNeutralAnim = 'catch-eatneutral' + EatNRunAnim = 'catch-eatnrun' + status = '' + animList = [FallBackAnim, + FallFwdAnim, + CatchNeutralAnim, + CatchRunAnim, + EatNeutralAnim, + EatNRunAnim] + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CatchGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['freeze', 'treasure']), + State.State('treasure', self.enterTreasure, self.exitTreasure, ['freeze', 'normal']), + State.State('freeze', self.enterFreeze, self.exitFreeze, ['normal']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + del self.fsm + self.game = None + return + + def enter(self): + self.fsm.enterInitialState() + + def exit(self, unexpectedExit = False): + self.unexpectedExit = unexpectedExit + if not unexpectedExit: + self.fsm.requestFinalState() + + def enterInit(self): + self.notify.debug('enterInit') + self.status = 'init' + + def exitInit(self): + pass + + def enterNormal(self): + self.status = 'normal' + self.notify.debug('enterNormal') + self.setAnimState('dive', 1.0) + + def exitNormal(self): + self.setAnimState('off', 1.0) + + def enterTreasure(self): + self.status = 'treasure' + self.notify.debug('enterTreasure') + self.setAnimState('swimhold', 1.0) + + def exitTreasure(self): + self.notify.debug('exitTreasure') + + def enterFreeze(self): + self.status = 'freeze' + self.notify.debug('enterFreeze') + self.setAnimState('cringe', 1.0) + + def exitFreeze(self): + pass + + def enterCleanup(self): + self.status = 'cleanup' + self.notify.debug('enterCleanup') + if self.toon: + self.toon.resetLOD() + + def exitCleanup(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) diff --git a/toontown/minigame/DivingTreasure.py b/toontown/minigame/DivingTreasure.py new file mode 100644 index 0000000..fbb219a --- /dev/null +++ b/toontown/minigame/DivingTreasure.py @@ -0,0 +1,36 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +import DivingGameGlobals + +class DivingTreasure(DirectObject): + + def __init__(self, i): + self.treasureNode = render.attachNewNode('treasure') + loadBase = 'phase_4/models/minigames/' + self.chest = loader.loadModel(loadBase + 'treasure.bam') + self.chest.reparentTo(self.treasureNode) + self.chest.setPos(0, 0, -25) + self.chest.setScale(1, 0.7, 1) + self.chestId = i + self.grabbedId = 0 + self.moveLerp = Sequence() + self.treasureNode.setScale(0.04) + self.treasureNode.setPos(-15 + 10.0 * i, 0.25, -36.0) + cSphere = CollisionSphere(0.0, 0.0, 0.0, 45) + cSphere.setTangible(0) + name = str(i) + cSphereNode = CollisionNode(name) + cSphereNode.setIntoCollideMask(DivingGameGlobals.CollideMask) + cSphereNode.addSolid(cSphere) + self.chestNode = cSphereNode + self.chestCNP = self.treasureNode.attachNewNode(cSphereNode) + + def destroy(self): + self.ignoreAll() + del self.chest + self.moveLerp.finish() + del self.moveLerp + self.treasureNode.removeNode() + del self.treasureNode diff --git a/toontown/minigame/DropPlacer.py b/toontown/minigame/DropPlacer.py new file mode 100644 index 0000000..c857847 --- /dev/null +++ b/toontown/minigame/DropPlacer.py @@ -0,0 +1,1080 @@ +from direct.showbase.RandomNumGen import RandomNumGen +import CatchGameGlobals +import DropScheduler +from toontown.parties.PartyGlobals import CatchActivityDuration as PartyCatchDuration + +class DropPlacer: + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + self.game = game + self.numPlayers = numPlayers + self.dropTypes = dropTypes + self.dtIndex = 0 + self._createScheduler(startTime) + self._createRng() + + def _createScheduler(self, startTime): + self.scheduler = DropScheduler.DropScheduler(CatchGameGlobals.GameDuration, self.game.FirstDropDelay, self.game.DropPeriod, self.game.MaxDropDuration, self.game.FasterDropDelay, self.game.FasterDropPeriodMult, startTime=startTime) + + def _createRng(self): + self.rng = self.game.randomNumGen + + def skipPercent(self, percent): + numSkips = self.scheduler.skipPercent(percent) + self.dtIndex += numSkips + return numSkips + + def doneDropping(self, continuous = None): + return self.scheduler.doneDropping(continuous) + + def getDuration(self): + return self.scheduler.getDuration() + + def getT(self): + return self.scheduler.getT() + + def stepT(self): + self.scheduler.stepT() + + def getNextDropTypeName(self): + if self.dtIndex >= len(self.dropTypes): + self.game.notify.debug('warning: defaulting to anvil') + typeName = 'anvil' + else: + typeName = self.dropTypes[self.dtIndex] + self.dtIndex += 1 + return typeName + + def getRandomColRow(self): + col = self.rng.randrange(0, self.game.DropColumns) + row = self.rng.randrange(0, self.game.DropRows) + return [col, row] + + def getNextDrop(self): + raise RuntimeError, 'DropPlacer.getNextDrop should never be called' + + +class RandomDropPlacer(DropPlacer): + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + + def getNextDrop(self): + col, row = self.getRandomColRow() + drop = [self.getT(), self.getNextDropTypeName(), [col, row]] + self.stepT() + return drop + + +class RegionDropPlacer(DropPlacer): + DropRegionTables = [[[1, + 1, + 2, + 3, + 3], + [1, + 1, + 2, + 3, + 3], + [0, + 1, + 2, + 3, + 4], + [0, + 1, + 2, + 3, + 4], + [0, + 1, + 2, + 3, + 4]], + [[1, + 2, + 2, + 3, + 3, + 4], + [1, + 1, + 2, + 3, + 4, + 4], + [1, + 1, + 2, + 3, + 4, + 4], + [0, + 1, + 2, + 3, + 4, + 5], + [0, + 1, + 2, + 3, + 4, + 5], + [0, + 1, + 2, + 3, + 4, + 5]], + [[1, + 1, + 2, + 2, + 2, + 3, + 3], + [1, + 1, + 2, + 2, + 2, + 3, + 3], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4], + [0, + 1, + 2, + 2, + 2, + 3, + 4]], + [[1, + 2, + 2, + 5, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 8, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 8, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 8, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 10, + 9, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 10, + 9, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 10, + 10, + 9, + 6, + 3, + 4, + 4]], + [[1, + 2, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 7, + 3], + [1, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 3], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 1, + 2, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 7, + 3, + 4], + [0, + 0, + 1, + 5, + 8, + 10, + 11, + 12, + 9, + 6, + 3, + 4, + 4]]] + Players2dropTable = [DropRegionTables[0], + DropRegionTables[0], + DropRegionTables[0], + DropRegionTables[1], + DropRegionTables[1], + DropRegionTables[2], + DropRegionTables[3], + DropRegionTables[3], + DropRegionTables[4], + DropRegionTables[4], + DropRegionTables[5], + DropRegionTables[5], + DropRegionTables[5], + DropRegionTables[6], + DropRegionTables[6], + DropRegionTables[7], + DropRegionTables[7], + DropRegionTables[7], + DropRegionTables[8], + DropRegionTables[8]] + + @classmethod + def getDropRegionTable(cls, numPlayers): + return cls.Players2dropTable[min(len(cls.Players2dropTable) - 1, numPlayers)] + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + self.DropRegionTable = self.getDropRegionTable(self.numPlayers) + self.DropRegion2GridCoordList = {} + for row in range(len(self.DropRegionTable)): + rowList = self.DropRegionTable[row] + for column in range(len(rowList)): + region = rowList[column] + if not self.DropRegion2GridCoordList.has_key(region): + self.DropRegion2GridCoordList[region] = [] + self.DropRegion2GridCoordList[region].append([row, column]) + + self.DropRegions = self.DropRegion2GridCoordList.keys() + self.DropRegions.sort() + self.emptyDropRegions = self.DropRegions[:] + self.fallingObjs = [] + + def getNextDrop(self): + t = self.getT() + while len(self.fallingObjs): + landTime, dropRegion = self.fallingObjs[0] + if landTime > t: + break + self.fallingObjs = self.fallingObjs[1:] + if dropRegion not in self.emptyDropRegions: + self.emptyDropRegions.append(dropRegion) + + candidates = self.emptyDropRegions + if len(candidates) == 0: + candidates = self.DropRegions + dropRegion = self.rng.choice(candidates) + row, col = self.rng.choice(self.DropRegion2GridCoordList[dropRegion]) + dropTypeName = self.getNextDropTypeName() + drop = [t, dropTypeName, [row, col]] + duration = self.game.BaselineDropDuration + self.fallingObjs.append([t + duration, dropRegion]) + if dropRegion in self.emptyDropRegions: + self.emptyDropRegions.remove(dropRegion) + self.stepT() + return drop + + +class PartyRegionDropPlacer(RegionDropPlacer): + + def __init__(self, game, numPlayers, generationId, dropTypes, startTime = None): + self.generationId = generationId + RegionDropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + + def _createRng(self): + self.rng = RandomNumGen(self.generationId + self.game.doId) + + def _createScheduler(self, startTime): + self.scheduler = DropScheduler.ThreePhaseDropScheduler(PartyCatchDuration, self.game.FirstDropDelay, self.game.DropPeriod, self.game.MaxDropDuration, self.game.SlowerDropPeriodMult, self.game.NormalDropDelay, self.game.FasterDropDelay, self.game.FasterDropPeriodMult, startTime=startTime) + + +class PathDropPlacer(DropPlacer): + + def __init__(self, game, numPlayers, dropTypes, startTime = None): + DropPlacer.__init__(self, game, numPlayers, dropTypes, startTime=startTime) + self.moves = [[0, -1], + [1, -1], + [1, 0], + [1, 1], + [0, 1], + [-1, 1], + [-1, 0], + [-1, -1]] + self.paths = [] + for i in xrange(self.numPlayers): + dir = self.rng.randrange(0, len(self.moves)) + col, row = self.getRandomColRow() + path = {'direction': dir, + 'location': [col, row]} + self.paths.append(path) + + self.curPathIndex = 0 + + def getValidDirection(self, col, row, dir): + redirectTop = [(6, 2), + 2, + 2, + 3, + 4, + 5, + 6, + 6] + redirectRight = [0, + 0, + (0, 4), + 4, + 4, + 5, + 6, + 7] + redirectBottom = [0, + 1, + 2, + 2, + (2, 6), + 6, + 6, + 7] + redirectLeft = [0, + 1, + 2, + 3, + 4, + 4, + (4, 0), + 0] + redirectTopRight = [6, + (6, 4), + 4, + 4, + 4, + 5, + 6, + 6] + redirectBottomRight = [0, + 0, + 0, + (0, 6), + 6, + 6, + 6, + 7] + redirectBottomLeft = [0, + 1, + 2, + 2, + 2, + (2, 0), + 0, + 0] + redirectTopLeft = [2, + 2, + 2, + 3, + 4, + 4, + 4, + (4, 2)] + tables = [None, + redirectTop, + redirectBottom, + None, + redirectLeft, + redirectTopLeft, + redirectBottomLeft, + None, + redirectRight, + redirectTopRight, + redirectBottomRight] + if col == 0: + colIndex = 1 + elif col == self.game.DropColumns - 1: + colIndex = 2 + else: + colIndex = 0 + if row == 0: + rowIndex = 1 + elif row == self.game.DropRows - 1: + rowIndex = 2 + else: + rowIndex = 0 + index = (colIndex << 2) + rowIndex + redirectTable = tables[index] + if not redirectTable: + return dir + newDir = redirectTable[dir] + if type(newDir) != type(1): + newDir = self.rng.choice(newDir) + return newDir + + def getNextDrop(self): + path = self.paths[self.curPathIndex] + col, row = path['location'] + dir = path['direction'] + turns = [-1, + 0, + 0, + 1] + turn = self.rng.choice(turns) + if turn: + dir = (dir + turn) % len(self.moves) + dir = self.getValidDirection(col, row, dir) + dCol, dRow = self.moves[dir] + col += dCol + row += dRow + col = min(max(col, 0), self.game.DropColumns - 1) + row = min(max(row, 0), self.game.DropRows - 1) + path['location'] = [col, row] + path['direction'] = dir + self.curPathIndex = (self.curPathIndex + 1) % len(self.paths) + drop = [self.getT(), self.getNextDropTypeName(), [col, row]] + self.stepT() + return drop diff --git a/toontown/minigame/DropScheduler.py b/toontown/minigame/DropScheduler.py new file mode 100644 index 0000000..9aec11b --- /dev/null +++ b/toontown/minigame/DropScheduler.py @@ -0,0 +1,72 @@ + + +class DropScheduler: + + def __init__(self, gameDuration, firstDropDelay, dropPeriod, maxDropDuration, fasterDropDelay, fasterDropPeriodMult, startTime = None): + self.gameDuration = gameDuration + self.firstDropDelay = firstDropDelay + self._dropPeriod = dropPeriod + self.maxDropDuration = maxDropDuration + self.fasterDropDelay = fasterDropDelay + self.fasterDropPeriodMult = fasterDropPeriodMult + if startTime is None: + startTime = 0 + self._startTime = startTime + self.curT = self._startTime + self.firstDropDelay + return + + def getT(self): + return self.curT + + def getDuration(self): + return self.gameDuration + + def getDropPeriod(self): + delay = self._dropPeriod + if self.curT - self._startTime >= self.fasterDropDelay: + delay *= self.fasterDropPeriodMult + return delay + + def doneDropping(self, continuous = None): + landTime = self.getT() - self._startTime + self.maxDropDuration + if continuous is None: + continuous = False + else: + continuous = True + if continuous: + maxTime = self.gameDuration + self.maxDropDuration + else: + maxTime = self.gameDuration + self.getDropPeriod() + return landTime >= maxTime + + def skipPercent(self, percent): + numSkips = 0 + while True: + prevT = self.curT + self.stepT() + if self.curT >= percent * self.gameDuration: + self.curT = prevT + break + else: + numSkips += 1 + + return numSkips + + def stepT(self): + self.curT += self.getDropPeriod() + + +class ThreePhaseDropScheduler(DropScheduler): + + def __init__(self, gameDuration, firstDropDelay, dropPeriod, maxDropDuration, slowerDropPeriodMult, normalDropDelay, fasterDropDelay, fasterDropPeriodMult, startTime = None): + self._slowerDropPeriodMult = slowerDropPeriodMult + self._normalDropDelay = normalDropDelay + DropScheduler.__init__(self, gameDuration, firstDropDelay, dropPeriod, maxDropDuration, fasterDropDelay, fasterDropPeriodMult, startTime) + + def getDropPeriod(self): + delay = self._dropPeriod + if self.curT - self._startTime < self._normalDropDelay: + delay *= self._slowerDropPeriodMult + elif self.curT - self._startTime >= self.fasterDropDelay: + delay *= self.fasterDropPeriodMult + return delay diff --git a/toontown/minigame/FogOverlay.py b/toontown/minigame/FogOverlay.py new file mode 100644 index 0000000..5a65044 --- /dev/null +++ b/toontown/minigame/FogOverlay.py @@ -0,0 +1,61 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from toontown.toonbase import ToontownGlobals +import math +from math import * + +class FogOverlay: + SomeCounter = 0 + + def __init__(self, color = Point3(1.0, 1.0, 1.0)): + self.color = color + self.opacity = 1.0 + self.setup() + + def setup(self): + self.baseNode = aspect2d.attachNewNode('targetGameTargets') + self.overlayGN = GeomNode('Overlay Geometry') + self.overlayNodePathGeom = self.baseNode.attachNewNode(self.overlayGN) + self.overlayNodePathGeom.setDepthWrite(False) + self.overlayNodePathGeom.setTransparency(TransparencyAttrib.MAlpha) + shapeVertexs = [] + shapeVertexs.append((-2.0, 0.0, 1.0)) + shapeVertexs.append((-2.0, 0.0, -1.0)) + shapeVertexs.append((2.0, 0.0, 1.0)) + shapeVertexs.append((2.0, 0.0, -1.0)) + gFormat = GeomVertexFormat.getV3cp() + overlayVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + overlayVertexWriter = GeomVertexWriter(overlayVertexData, 'vertex') + overlayColorWriter = GeomVertexWriter(overlayVertexData, 'color') + for index in range(len(shapeVertexs)): + overlayVertexWriter.addData3f(shapeVertexs[index][0], shapeVertexs[index][1], shapeVertexs[index][2]) + overlayColorWriter.addData4f(1.0, 1.0, 1.0, 1.0) + + overlayTris = GeomTristrips(Geom.UHStatic) + for index in range(len(shapeVertexs)): + overlayTris.addVertex(index) + + overlayTris.closePrimitive() + overlayGeom = Geom(overlayVertexData) + overlayGeom.addPrimitive(overlayTris) + self.overlayGN.addGeom(overlayGeom) + + def setOpacity(self, opacity): + self.opacity = opacity + self.__applyColor() + + def setColor(self, color): + self.color = color + self.__applyColor() + + def __applyColor(self): + self.overlayNodePathGeom.setColorScale(self.color[0], self.color[1], self.color[2], self.opacity) + + def delete(self): + self.overlayGN.removeAllGeoms() + self.baseNode.remove() diff --git a/toontown/minigame/IceGameGlobals.py b/toontown/minigame/IceGameGlobals.py new file mode 100644 index 0000000..572d099 --- /dev/null +++ b/toontown/minigame/IceGameGlobals.py @@ -0,0 +1,59 @@ +import math +from pandac.PandaModules import Point3 +from toontown.toonbase import ToontownGlobals +InputTimeout = 15 +TireMovieTimeout = 120 +MinWall = (-20.0, -15.0) +MaxWall = (20.0, 15.0) +TireRadius = 1.5 +WallMargin = 1 + TireRadius +StartingPositions = (Point3(MinWall[0] + WallMargin, MinWall[1] + WallMargin, TireRadius), + Point3(MaxWall[0] - WallMargin, MaxWall[1] - WallMargin, TireRadius), + Point3(MinWall[0] + WallMargin, MaxWall[1] - WallMargin, TireRadius), + Point3(MaxWall[0] - WallMargin, MinWall[1] + WallMargin, TireRadius)) +NumMatches = 3 +NumRounds = 2 +PointsDeadCenter = {0: 5, + 1: 5, + 2: 5, + 3: 4, + 4: 3} +PointsInCorner = 1 +FarthestLength = math.sqrt((MaxWall[0] - TireRadius) * (MaxWall[0] - TireRadius) + (MaxWall[1] - TireRadius) * (MaxWall[1] - TireRadius)) +BonusPointsForPlace = (3, + 2, + 1, + 0) +ExpandFeetPerSec = 5 +ScoreCountUpRate = 0.15 +ShowScoresDuration = 4.0 +NumTreasures = {ToontownGlobals.ToontownCentral: 2, + ToontownGlobals.DonaldsDock: 2, + ToontownGlobals.DaisyGardens: 2, + ToontownGlobals.MinniesMelodyland: 2, + ToontownGlobals.TheBrrrgh: 1, + ToontownGlobals.DonaldsDreamland: 1} +NumPenalties = {ToontownGlobals.ToontownCentral: 0, + ToontownGlobals.DonaldsDock: 1, + ToontownGlobals.DaisyGardens: 1, + ToontownGlobals.MinniesMelodyland: 1, + ToontownGlobals.TheBrrrgh: 2, + ToontownGlobals.DonaldsDreamland: 2} +Obstacles = {ToontownGlobals.ToontownCentral: (), + ToontownGlobals.DonaldsDock: ((0, 0),), + ToontownGlobals.DaisyGardens: ((MinWall[0] / 2, 0), (MaxWall[0] / 2, 0)), + ToontownGlobals.MinniesMelodyland: ((0, MinWall[1] / 2), (0, MaxWall[1] / 2)), + ToontownGlobals.TheBrrrgh: ((MinWall[0] / 2, 0), + (MaxWall[0] / 2, 0), + (0, MinWall[1] / 2), + (0, MaxWall[1] / 2)), + ToontownGlobals.DonaldsDreamland: ((MinWall[0] / 2, MinWall[1] / 2), + (MinWall[0] / 2, MaxWall[1] / 2), + (MaxWall[0] / 2, MinWall[1] / 2), + (MaxWall[0] / 2, MaxWall[1] / 2))} +ObstacleShapes = {ToontownGlobals.ToontownCentral: True, + ToontownGlobals.DonaldsDock: True, + ToontownGlobals.DaisyGardens: True, + ToontownGlobals.MinniesMelodyland: True, + ToontownGlobals.TheBrrrgh: False, + ToontownGlobals.DonaldsDreamland: False} diff --git a/toontown/minigame/IceTreasure.py b/toontown/minigame/IceTreasure.py new file mode 100644 index 0000000..61466b7 --- /dev/null +++ b/toontown/minigame/IceTreasure.py @@ -0,0 +1,68 @@ +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, BitMask32 +from direct.interval.IntervalGlobal import Sequence, LerpScaleInterval, Parallel, Func, SoundInterval +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase import ToontownGlobals +from toontown.battle import BattleParticles + +class IceTreasure(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('IceTreasure') + RADIUS = 1.0 + + def __init__(self, model, pos, serialNum, gameId, penalty = False): + self.serialNum = serialNum + self.penalty = penalty + center = model.getBounds().getCenter() + center = Point3(0, 0, 0) + self.nodePath = model.copyTo(render) + self.nodePath.setPos(pos[0] - center[0], pos[1] - center[1], pos[2] - center[2]) + self.nodePath.setZ(0) + self.notify.debug('newPos = %s' % self.nodePath.getPos()) + if self.penalty: + self.sphereName = 'penaltySphere-%s-%s' % (gameId, self.serialNum) + else: + self.sphereName = 'treasureSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(ToontownGlobals.PieBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = render.attachNewNode(self.collNode) + self.collNodePath.setPos(pos[0] - center[0], pos[1] - center[1], pos[2] - center[2]) + self.collNodePath.hide() + self.track = None + if self.penalty: + self.tip = self.nodePath.find('**/fusetip') + sparks = BattleParticles.createParticleEffect(file='icetnt') + self.sparksEffect = sparks + sparks.start(self.tip) + self.penaltyGrabSound = loader.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.penaltyGrabSound.setVolume(0.75) + kaboomAttachPoint = self.nodePath.attachNewNode('kaboomAttach') + kaboomAttachPoint.setZ(3) + self.kaboom = loader.loadModel('phase_4/models/minigames/ice_game_kaboom') + self.kaboom.reparentTo(kaboomAttachPoint) + self.kaboom.setScale(2.0) + self.kaboom.setBillboardPointEye() + return + + def destroy(self): + self.ignoreAll() + if self.penalty: + self.sparksEffect.cleanup() + if self.track: + self.track.finish() + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def showGrab(self): + self.nodePath.hide() + self.collNodePath.hide() + self.collNode.setIntoCollideMask(BitMask32(0)) + if self.penalty: + self.track = Parallel(SoundInterval(self.penaltyGrabSound), Sequence(Func(self.kaboom.showThrough), LerpScaleInterval(self.kaboom, duration=0.5, scale=Point3(10, 10, 10), blendType='easeOut'), Func(self.kaboom.hide))) + self.track.start() diff --git a/toontown/minigame/Maze.py b/toontown/minigame/Maze.py new file mode 100644 index 0000000..f431bc8 --- /dev/null +++ b/toontown/minigame/Maze.py @@ -0,0 +1,11 @@ +from MazeBase import MazeBase +import MazeData + +class Maze(MazeBase): + + def __init__(self, mapName, mazeData = MazeData.mazeData, cellWidth = MazeData.CELL_WIDTH): + model = loader.loadModel(mapName) + mData = mazeData[mapName] + self.treasurePosList = mData['treasurePosList'] + self.numTreasures = len(self.treasurePosList) + MazeBase.__init__(self, model, mData, cellWidth) diff --git a/toontown/minigame/MazeBase.py b/toontown/minigame/MazeBase.py new file mode 100644 index 0000000..7dd29cf --- /dev/null +++ b/toontown/minigame/MazeBase.py @@ -0,0 +1,158 @@ +from pandac.PandaModules import VBase3 +from direct.showbase.RandomNumGen import RandomNumGen + +class MazeBase: + + def __init__(self, model, mazeData, cellWidth, parent = None): + if parent is None: + parent = render + self.width = mazeData['width'] + self.height = mazeData['height'] + self.originTX = mazeData['originX'] + self.originTY = mazeData['originY'] + self.collisionTable = mazeData['collisionTable'] + self._initialCellWidth = cellWidth + self.cellWidth = self._initialCellWidth + self.maze = model + self.maze.setPos(0, 0, 0) + self.maze.reparentTo(parent) + self.maze.stash() + return + + def destroy(self): + self.maze.removeNode() + del self.maze + + def onstage(self): + self.maze.unstash() + + def offstage(self): + self.maze.stash() + + def setScale(self, xy = 1, z = 1): + self.maze.setScale(VBase3(xy, xy, z)) + self.cellWidth = self._initialCellWidth * xy + + def isWalkable(self, tX, tY, rejectList = ()): + if tX <= 0 or tY <= 0 or tX >= self.width or tY >= self.height: + return 0 + return not self.collisionTable[tY][tX] and not self.collisionTable[tY - 1][tX] and not self.collisionTable[tY][tX - 1] and not self.collisionTable[tY - 1][tX - 1] and (tX, tY) not in rejectList + + def tile2world(self, TX, TY): + return [(TX - self.originTX) * self.cellWidth, (TY - self.originTY) * self.cellWidth] + + def world2tile(self, x, y): + return [int(x / self.cellWidth + self.originTX), int(y / self.cellWidth + self.originTY)] + + def world2tileClipped(self, x, y): + coords = [int(x / self.cellWidth + self.originTX), int(y / self.cellWidth + self.originTY)] + coords[0] = min(max(coords[0], 0), self.width - 1) + coords[1] = min(max(coords[1], 0), self.height - 1) + return coords + + def doOrthoCollisions(self, oldPos, newPos): + offset = newPos - oldPos + WALL_OFFSET = 1.0 + curX = oldPos[0] + curY = oldPos[1] + curTX, curTY = self.world2tile(curX, curY) + + def calcFlushCoord(curTile, newTile, centerTile): + EPSILON = 0.01 + if newTile > curTile: + return (newTile - centerTile) * self.cellWidth - EPSILON - WALL_OFFSET + else: + return (curTile - centerTile) * self.cellWidth + WALL_OFFSET + + offsetX = offset[0] + offsetY = offset[1] + WALL_OFFSET_X = WALL_OFFSET + if offsetX < 0: + WALL_OFFSET_X = -WALL_OFFSET_X + WALL_OFFSET_Y = WALL_OFFSET + if offsetY < 0: + WALL_OFFSET_Y = -WALL_OFFSET_Y + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + newTX, newTY = self.world2tile(newX, newY) + if newTX != curTX: + if self.collisionTable[newTY][newTX] == 1: + offset.setX(calcFlushCoord(curTX, newTX, self.originTX) - curX) + newX = curX + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.world2tile(newX, newY) + if newTY != curTY: + if self.collisionTable[newTY][newTX] == 1: + offset.setY(calcFlushCoord(curTY, newTY, self.originTY) - curY) + offsetX = offset[0] + offsetY = offset[1] + newX = curX + offsetX + WALL_OFFSET_X + newY = curY + offsetY + WALL_OFFSET_Y + newTX, newTY = self.world2tile(newX, newY) + if self.collisionTable[newTY][newTX] == 1: + cX = calcFlushCoord(curTX, newTX, self.originTX) + cY = calcFlushCoord(curTY, newTY, self.originTY) + if abs(cX - curX) < abs(cY - curY): + offset.setX(cX - curX) + else: + offset.setY(cY - curY) + return oldPos + offset + + def createRandomSpotsList(self, numSpots, randomNumGen): + randomNumGen = RandomNumGen(randomNumGen) + width = self.width + height = self.height + halfWidth = int(width / 2) + halfHeight = int(height / 2) + quadrants = [(0, + 0, + halfWidth - 1, + halfHeight - 1), + (halfWidth, + 0, + width - 1, + halfHeight - 1), + (0, + halfHeight, + halfWidth - 1, + height - 1), + (halfWidth, + halfHeight, + width - 1, + height - 1)] + spotsTaken = [] + + def getEmptySpotInQuadrant(quadrant): + tX = -1 + tY = -1 + while tX < 0 or not self.isWalkable(tX, tY, spotsTaken): + tX = randomNumGen.randint(quadrant[0], quadrant[2]) + tY = randomNumGen.randint(quadrant[1], quadrant[3]) + + spot = (tX, tY) + spotsTaken.append(spot) + return spot + + def getSpotList(length): + randomNumGen.shuffle(quadrants) + l = [] + remaining = length + for quadrant in quadrants: + for u in range(int(length / 4)): + l.append(getEmptySpotInQuadrant(quadrant)) + + remaining -= int(length / 4) + + for u in range(remaining): + quadrant = quadrants[randomNumGen.randint(0, len(quadrants) - 1)] + l.append(getEmptySpotInQuadrant(quadrant)) + + return l + + if type(numSpots) == tuple or type(numSpots) == list: + spots = [] + for i in numSpots: + spots.append(getSpotList(i)) + + return spots + return getSpotList(numSpots) diff --git a/toontown/minigame/MazeData.py b/toontown/minigame/MazeData.py new file mode 100644 index 0000000..92bb52c --- /dev/null +++ b/toontown/minigame/MazeData.py @@ -0,0 +1,8167 @@ +CELL_WIDTH = 2 +mazeNames = [['phase_4/models/minigames/maze_1player'], + ['phase_4/models/minigames/maze_2player'], + ['phase_4/models/minigames/maze_3player'], + ['phase_4/models/minigames/maze_4player']] +mazeData = {} +mazeData['phase_4/models/minigames/maze_1player'] = {} +data = mazeData['phase_4/models/minigames/maze_1player'] +data['width'] = 28 +data['height'] = 22 +data['originX'] = 14 +data['originY'] = 11 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1], + [1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 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, + 0, + 1, + 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, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 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, + 1, + 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, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 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, + 1, + 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, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-20, -18, 0.1), + (-18, -18, 0.1), + (-16, -18, 0.1), + (-14, -18, 0.1), + (-8, -18, 0.1), + (-6, -18, 0.1), + (-4, -18, 0.1), + (4, -18, 0.1), + (6, -18, 0.1), + (8, -18, 0.1), + (14, -18, 0.1), + (16, -18, 0.1), + (18, -18, 0.1), + (20, -18, 0.1), + (-20, -16, 0.1), + (-14, -16, 0.1), + (-4, -16, 0.1), + (4, -16, 0.1), + (14, -16, 0.1), + (20, -16, 0.1), + (-20, -14, 0.1), + (-14, -14, 0.1), + (-4, -14, 0.1), + (4, -14, 0.1), + (14, -14, 0.1), + (20, -14, 0.1), + (-24, -12, 0.1), + (-22, -12, 0.1), + (-20, -12, 0.1), + (-14, -12, 0.1), + (-12, -12, 0.1), + (-10, -12, 0.1), + (-8, -12, 0.1), + (-6, -12, 0.1), + (-4, -12, 0.1), + (-2, -12, 0.1), + (0, -12, 0.1), + (2, -12, 0.1), + (4, -12, 0.1), + (6, -12, 0.1), + (8, -12, 0.1), + (10, -12, 0.1), + (12, -12, 0.1), + (14, -12, 0.1), + (20, -12, 0.1), + (22, -12, 0.1), + (24, -12, 0.1), + (-24, -10, 0.1), + (-14, -10, 0.1), + (-8, -10, 0.1), + (0, -10, 0.1), + (8, -10, 0.1), + (14, -10, 0.1), + (24, -10, 0.1), + (-24, -8, 0.1), + (-14, -8, 0.1), + (-8, -8, 0.1), + (0, -8, 0.1), + (8, -8, 0.1), + (14, -8, 0.1), + (24, -8, 0.1), + (-24, -6, 0.1), + (-18, -6, 0.1), + (-16, -6, 0.1), + (-14, -6, 0.1), + (-8, -6, 0.1), + (0, -6, 0.1), + (8, -6, 0.1), + (14, -6, 0.1), + (16, -6, 0.1), + (18, -6, 0.1), + (24, -6, 0.1), + (-24, -4, 0.1), + (-18, -4, 0.1), + (-8, -4, 0.1), + (8, -4, 0.1), + (18, -4, 0.1), + (24, -4, 0.1), + (-24, -2, 0.1), + (-18, -2, 0.1), + (-8, -2, 0.1), + (8, -2, 0.1), + (18, -2, 0.1), + (24, -2, 0.1), + (-24, 0, 0.1), + (-18, 0, 0.1), + (-16, 0, 0.1), + (-14, 0, 0.1), + (-12, 0, 0.1), + (-10, 0, 0.1), + (-8, 0, 0.1), + (-6, 0, 0.1), + (6, 0, 0.1), + (8, 0, 0.1), + (10, 0, 0.1), + (12, 0, 0.1), + (14, 0, 0.1), + (16, 0, 0.1), + (18, 0, 0.1), + (24, 0, 0.1), + (-24, 2, 0.1), + (-12, 2, 0.1), + (-6, 2, 0.1), + (6, 2, 0.1), + (12, 2, 0.1), + (24, 2, 0.1), + (-24, 4, 0.1), + (-12, 4, 0.1), + (-6, 4, 0.1), + (6, 4, 0.1), + (12, 4, 0.1), + (24, 4, 0.1), + (-24, 6, 0.1), + (-22, 6, 0.1), + (-20, 6, 0.1), + (-18, 6, 0.1), + (-16, 6, 0.1), + (-14, 6, 0.1), + (-12, 6, 0.1), + (-6, 6, 0.1), + (-4, 6, 0.1), + (-2, 6, 0.1), + (0, 6, 0.1), + (2, 6, 0.1), + (4, 6, 0.1), + (6, 6, 0.1), + (12, 6, 0.1), + (14, 6, 0.1), + (16, 6, 0.1), + (18, 6, 0.1), + (20, 6, 0.1), + (22, 6, 0.1), + (24, 6, 0.1), + (-18, 8, 0.1), + (-12, 8, 0.1), + (0, 8, 0.1), + (12, 8, 0.1), + (18, 8, 0.1), + (-18, 10, 0.1), + (-12, 10, 0.1), + (0, 10, 0.1), + (12, 10, 0.1), + (18, 10, 0.1), + (-24, 12, 0.1), + (-22, 12, 0.1), + (-20, 12, 0.1), + (-18, 12, 0.1), + (-12, 12, 0.1), + (-10, 12, 0.1), + (-8, 12, 0.1), + (-6, 12, 0.1), + (-4, 12, 0.1), + (-2, 12, 0.1), + (0, 12, 0.1), + (2, 12, 0.1), + (4, 12, 0.1), + (6, 12, 0.1), + (8, 12, 0.1), + (10, 12, 0.1), + (12, 12, 0.1), + (18, 12, 0.1), + (20, 12, 0.1), + (22, 12, 0.1), + (24, 12, 0.1), + (-24, 14, 0.1), + (-8, 14, 0.1), + (8, 14, 0.1), + (24, 14, 0.1), + (-24, 16, 0.1), + (-8, 16, 0.1), + (8, 16, 0.1), + (24, 16, 0.1), + (-24, 18, 0.1), + (-22, 18, 0.1), + (-20, 18, 0.1), + (-18, 18, 0.1), + (-16, 18, 0.1), + (-14, 18, 0.1), + (-12, 18, 0.1), + (-10, 18, 0.1), + (-8, 18, 0.1), + (-6, 18, 0.1), + (-4, 18, 0.1), + (-2, 18, 0.1), + (0, 18, 0.1), + (2, 18, 0.1), + (4, 18, 0.1), + (6, 18, 0.1), + (8, 18, 0.1), + (10, 18, 0.1), + (12, 18, 0.1), + (14, 18, 0.1), + (16, 18, 0.1), + (18, 18, 0.1), + (20, 18, 0.1), + (22, 18, 0.1), + (24, 18, 0.1)] +mazeData['phase_4/models/minigames/maze_2player'] = {} +data = mazeData['phase_4/models/minigames/maze_2player'] +data['width'] = 32 +data['height'] = 50 +data['originX'] = 16 +data['originY'] = 25 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 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, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 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, + 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, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 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, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 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, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 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, + 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, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 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, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-28, -46, 0.1), + (-26, -46, 0.1), + (-24, -46, 0.1), + (-22, -46, 0.1), + (-20, -46, 0.1), + (-18, -46, 0.1), + (-16, -46, 0.1), + (-14, -46, 0.1), + (-12, -46, 0.1), + (12, -46, 0.1), + (18, -46, 0.1), + (20, -46, 0.1), + (22, -46, 0.1), + (24, -46, 0.1), + (26, -46, 0.1), + (28, -46, 0.1), + (-28, -44, 0.1), + (-12, -44, 0.1), + (12, -44, 0.1), + (18, -44, 0.1), + (28, -44, 0.1), + (-28, -42, 0.1), + (-12, -42, 0.1), + (12, -42, 0.1), + (18, -42, 0.1), + (28, -42, 0.1), + (-28, -40, 0.1), + (-26, -40, 0.1), + (-24, -40, 0.1), + (-22, -40, 0.1), + (-12, -40, 0.1), + (12, -40, 0.1), + (18, -40, 0.1), + (28, -40, 0.1), + (-28, -38, 0.1), + (-22, -38, 0.1), + (-12, -38, 0.1), + (12, -38, 0.1), + (18, -38, 0.1), + (28, -38, 0.1), + (-28, -36, 0.1), + (-22, -36, 0.1), + (-20, -36, 0.1), + (-18, -36, 0.1), + (-16, -36, 0.1), + (-14, -36, 0.1), + (-12, -36, 0.1), + (-10, -36, 0.1), + (-8, -36, 0.1), + (-6, -36, 0.1), + (-4, -36, 0.1), + (-2, -36, 0.1), + (0, -36, 0.1), + (2, -36, 0.1), + (4, -36, 0.1), + (6, -36, 0.1), + (8, -36, 0.1), + (10, -36, 0.1), + (12, -36, 0.1), + (14, -36, 0.1), + (16, -36, 0.1), + (18, -36, 0.1), + (20, -36, 0.1), + (22, -36, 0.1), + (24, -36, 0.1), + (26, -36, 0.1), + (28, -36, 0.1), + (-28, -34, 0.1), + (-18, -34, 0.1), + (-8, -34, 0.1), + (8, -34, 0.1), + (18, -34, 0.1), + (-28, -32, 0.1), + (-18, -32, 0.1), + (-8, -32, 0.1), + (8, -32, 0.1), + (18, -32, 0.1), + (-28, -30, 0.1), + (-26, -30, 0.1), + (-24, -30, 0.1), + (-22, -30, 0.1), + (-20, -30, 0.1), + (-18, -30, 0.1), + (-12, -30, 0.1), + (-10, -30, 0.1), + (-8, -30, 0.1), + (-6, -30, 0.1), + (-4, -30, 0.1), + (-2, -30, 0.1), + (0, -30, 0.1), + (2, -30, 0.1), + (4, -30, 0.1), + (6, -30, 0.1), + (8, -30, 0.1), + (10, -30, 0.1), + (12, -30, 0.1), + (18, -30, 0.1), + (20, -30, 0.1), + (22, -30, 0.1), + (24, -30, 0.1), + (26, -30, 0.1), + (28, -30, 0.1), + (-18, -28, 0.1), + (-12, -28, 0.1), + (-2, -28, 0.1), + (0, -28, 0.1), + (2, -28, 0.1), + (12, -28, 0.1), + (18, -28, 0.1), + (-18, -26, 0.1), + (-12, -26, 0.1), + (-2, -26, 0.1), + (0, -26, 0.1), + (2, -26, 0.1), + (12, -26, 0.1), + (18, -26, 0.1), + (-18, -24, 0.1), + (-12, -24, 0.1), + (-6, -24, 0.1), + (-4, -24, 0.1), + (-2, -24, 0.1), + (0, -24, 0.1), + (2, -24, 0.1), + (4, -24, 0.1), + (6, -24, 0.1), + (12, -24, 0.1), + (18, -24, 0.1), + (-18, -22, 0.1), + (-16, -22, 0.1), + (-14, -22, 0.1), + (-12, -22, 0.1), + (-6, -22, 0.1), + (0, -22, 0.1), + (6, -22, 0.1), + (12, -22, 0.1), + (14, -22, 0.1), + (16, -22, 0.1), + (18, -22, 0.1), + (-18, -20, 0.1), + (-6, -20, 0.1), + (0, -20, 0.1), + (6, -20, 0.1), + (-18, -18, 0.1), + (-6, -18, 0.1), + (0, -18, 0.1), + (6, -18, 0.1), + (-18, -16, 0.1), + (-16, -16, 0.1), + (-14, -16, 0.1), + (-12, -16, 0.1), + (-10, -16, 0.1), + (-8, -16, 0.1), + (-6, -16, 0.1), + (0, -16, 0.1), + (6, -16, 0.1), + (8, -16, 0.1), + (10, -16, 0.1), + (12, -16, 0.1), + (14, -16, 0.1), + (16, -16, 0.1), + (18, -16, 0.1), + (-6, -14, 0.1), + (0, -14, 0.1), + (6, -14, 0.1), + (18, -14, 0.1), + (-6, -12, 0.1), + (0, -12, 0.1), + (6, -12, 0.1), + (18, -12, 0.1), + (-18, -10, 0.1), + (-16, -10, 0.1), + (-14, -10, 0.1), + (-12, -10, 0.1), + (-10, -10, 0.1), + (-8, -10, 0.1), + (-6, -10, 0.1), + (6, -10, 0.1), + (8, -10, 0.1), + (10, -10, 0.1), + (12, -10, 0.1), + (14, -10, 0.1), + (16, -10, 0.1), + (18, -10, 0.1), + (-18, -8, 0.1), + (-12, -8, 0.1), + (-6, -8, 0.1), + (6, -8, 0.1), + (12, -8, 0.1), + (18, -8, 0.1), + (-18, -6, 0.1), + (-12, -6, 0.1), + (-6, -6, 0.1), + (6, -6, 0.1), + (12, -6, 0.1), + (18, -6, 0.1), + (-18, -4, 0.1), + (-12, -4, 0.1), + (-6, -4, 0.1), + (6, -4, 0.1), + (12, -4, 0.1), + (18, -4, 0.1), + (-18, -2, 0.1), + (-12, -2, 0.1), + (-6, -2, 0.1), + (6, -2, 0.1), + (12, -2, 0.1), + (18, -2, 0.1), + (-18, 0, 0.1), + (-16, 0, 0.1), + (-14, 0, 0.1), + (-12, 0, 0.1), + (-10, 0, 0.1), + (-8, 0, 0.1), + (-6, 0, 0.1), + (6, 0, 0.1), + (8, 0, 0.1), + (10, 0, 0.1), + (12, 0, 0.1), + (14, 0, 0.1), + (16, 0, 0.1), + (18, 0, 0.1), + (-18, 2, 0.1), + (-12, 2, 0.1), + (-6, 2, 0.1), + (6, 2, 0.1), + (12, 2, 0.1), + (18, 2, 0.1), + (-18, 4, 0.1), + (-12, 4, 0.1), + (-6, 4, 0.1), + (6, 4, 0.1), + (12, 4, 0.1), + (18, 4, 0.1), + (-18, 6, 0.1), + (-12, 6, 0.1), + (-6, 6, 0.1), + (6, 6, 0.1), + (12, 6, 0.1), + (18, 6, 0.1), + (-18, 8, 0.1), + (-12, 8, 0.1), + (-6, 8, 0.1), + (6, 8, 0.1), + (12, 8, 0.1), + (18, 8, 0.1), + (-18, 10, 0.1), + (-16, 10, 0.1), + (-14, 10, 0.1), + (-12, 10, 0.1), + (-10, 10, 0.1), + (-8, 10, 0.1), + (-6, 10, 0.1), + (6, 10, 0.1), + (8, 10, 0.1), + (10, 10, 0.1), + (12, 10, 0.1), + (14, 10, 0.1), + (16, 10, 0.1), + (18, 10, 0.1), + (-6, 12, 0.1), + (0, 12, 0.1), + (6, 12, 0.1), + (18, 12, 0.1), + (-6, 14, 0.1), + (0, 14, 0.1), + (6, 14, 0.1), + (18, 14, 0.1), + (-18, 16, 0.1), + (-16, 16, 0.1), + (-14, 16, 0.1), + (-12, 16, 0.1), + (-10, 16, 0.1), + (-8, 16, 0.1), + (-6, 16, 0.1), + (0, 16, 0.1), + (6, 16, 0.1), + (8, 16, 0.1), + (10, 16, 0.1), + (12, 16, 0.1), + (14, 16, 0.1), + (16, 16, 0.1), + (18, 16, 0.1), + (-18, 18, 0.1), + (-6, 18, 0.1), + (0, 18, 0.1), + (6, 18, 0.1), + (-18, 20, 0.1), + (-6, 20, 0.1), + (0, 20, 0.1), + (6, 20, 0.1), + (-18, 22, 0.1), + (-16, 22, 0.1), + (-14, 22, 0.1), + (-12, 22, 0.1), + (-6, 22, 0.1), + (0, 22, 0.1), + (6, 22, 0.1), + (12, 22, 0.1), + (14, 22, 0.1), + (16, 22, 0.1), + (18, 22, 0.1), + (-18, 24, 0.1), + (-12, 24, 0.1), + (-6, 24, 0.1), + (-4, 24, 0.1), + (-2, 24, 0.1), + (0, 24, 0.1), + (2, 24, 0.1), + (4, 24, 0.1), + (6, 24, 0.1), + (12, 24, 0.1), + (18, 24, 0.1), + (-18, 26, 0.1), + (-12, 26, 0.1), + (-2, 26, 0.1), + (0, 26, 0.1), + (2, 26, 0.1), + (12, 26, 0.1), + (18, 26, 0.1), + (-18, 28, 0.1), + (-12, 28, 0.1), + (-2, 28, 0.1), + (0, 28, 0.1), + (2, 28, 0.1), + (12, 28, 0.1), + (18, 28, 0.1), + (-28, 30, 0.1), + (-26, 30, 0.1), + (-24, 30, 0.1), + (-22, 30, 0.1), + (-20, 30, 0.1), + (-18, 30, 0.1), + (-12, 30, 0.1), + (-10, 30, 0.1), + (-8, 30, 0.1), + (-6, 30, 0.1), + (-4, 30, 0.1), + (-2, 30, 0.1), + (0, 30, 0.1), + (2, 30, 0.1), + (4, 30, 0.1), + (6, 30, 0.1), + (8, 30, 0.1), + (10, 30, 0.1), + (12, 30, 0.1), + (18, 30, 0.1), + (20, 30, 0.1), + (22, 30, 0.1), + (24, 30, 0.1), + (26, 30, 0.1), + (28, 30, 0.1), + (-28, 32, 0.1), + (-18, 32, 0.1), + (-8, 32, 0.1), + (8, 32, 0.1), + (18, 32, 0.1), + (-28, 34, 0.1), + (-18, 34, 0.1), + (-8, 34, 0.1), + (8, 34, 0.1), + (18, 34, 0.1), + (-28, 36, 0.1), + (-22, 36, 0.1), + (-20, 36, 0.1), + (-18, 36, 0.1), + (-16, 36, 0.1), + (-14, 36, 0.1), + (-12, 36, 0.1), + (-10, 36, 0.1), + (-8, 36, 0.1), + (-6, 36, 0.1), + (-4, 36, 0.1), + (-2, 36, 0.1), + (0, 36, 0.1), + (2, 36, 0.1), + (4, 36, 0.1), + (6, 36, 0.1), + (8, 36, 0.1), + (10, 36, 0.1), + (12, 36, 0.1), + (14, 36, 0.1), + (16, 36, 0.1), + (18, 36, 0.1), + (20, 36, 0.1), + (22, 36, 0.1), + (24, 36, 0.1), + (26, 36, 0.1), + (28, 36, 0.1), + (-28, 38, 0.1), + (-22, 38, 0.1), + (-12, 38, 0.1), + (12, 38, 0.1), + (18, 38, 0.1), + (28, 38, 0.1), + (-28, 40, 0.1), + (-26, 40, 0.1), + (-24, 40, 0.1), + (-22, 40, 0.1), + (-12, 40, 0.1), + (12, 40, 0.1), + (18, 40, 0.1), + (28, 40, 0.1), + (-28, 42, 0.1), + (-12, 42, 0.1), + (12, 42, 0.1), + (18, 42, 0.1), + (28, 42, 0.1), + (-28, 44, 0.1), + (-12, 44, 0.1), + (12, 44, 0.1), + (18, 44, 0.1), + (28, 44, 0.1), + (-28, 46, 0.1), + (-26, 46, 0.1), + (-24, 46, 0.1), + (-22, 46, 0.1), + (-20, 46, 0.1), + (-18, 46, 0.1), + (-16, 46, 0.1), + (-14, 46, 0.1), + (-12, 46, 0.1), + (12, 46, 0.1), + (18, 46, 0.1), + (20, 46, 0.1), + (22, 46, 0.1), + (24, 46, 0.1), + (26, 46, 0.1), + (28, 46, 0.1)] +mazeData['phase_4/models/minigames/maze_3player'] = {} +data = mazeData['phase_4/models/minigames/maze_3player'] +data['width'] = 46 +data['height'] = 45 +data['originX'] = 23 +data['originY'] = 19 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 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, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 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, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 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, + 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, + 1, + 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, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 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, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 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, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 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, + 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, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 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, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 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, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 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, + 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, + 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, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 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, + 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, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 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, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 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, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-22, -34, 0.1), + (-20, -34, 0.1), + (-18, -34, 0.1), + (-16, -34, 0.1), + (-14, -34, 0.1), + (-12, -34, 0.1), + (-10, -34, 0.1), + (-8, -34, 0.1), + (-6, -34, 0.1), + (-4, -34, 0.1), + (-2, -34, 0.1), + (0, -34, 0.1), + (2, -34, 0.1), + (4, -34, 0.1), + (6, -34, 0.1), + (8, -34, 0.1), + (10, -34, 0.1), + (12, -34, 0.1), + (14, -34, 0.1), + (16, -34, 0.1), + (18, -34, 0.1), + (20, -34, 0.1), + (22, -34, 0.1), + (-22, -32, 0.1), + (-6, -32, 0.1), + (6, -32, 0.1), + (22, -32, 0.1), + (-22, -30, 0.1), + (-6, -30, 0.1), + (6, -30, 0.1), + (22, -30, 0.1), + (-22, -28, 0.1), + (-20, -28, 0.1), + (-18, -28, 0.1), + (-16, -28, 0.1), + (-14, -28, 0.1), + (-12, -28, 0.1), + (-6, -28, 0.1), + (6, -28, 0.1), + (12, -28, 0.1), + (14, -28, 0.1), + (16, -28, 0.1), + (18, -28, 0.1), + (20, -28, 0.1), + (22, -28, 0.1), + (-22, -26, 0.1), + (-12, -26, 0.1), + (-6, -26, 0.1), + (6, -26, 0.1), + (12, -26, 0.1), + (22, -26, 0.1), + (-22, -24, 0.1), + (-12, -24, 0.1), + (-6, -24, 0.1), + (6, -24, 0.1), + (12, -24, 0.1), + (22, -24, 0.1), + (-22, -22, 0.1), + (-12, -22, 0.1), + (-6, -22, 0.1), + (6, -22, 0.1), + (12, -22, 0.1), + (22, -22, 0.1), + (-22, -20, 0.1), + (-16, -20, 0.1), + (-14, -20, 0.1), + (-12, -20, 0.1), + (-10, -20, 0.1), + (-8, -20, 0.1), + (-6, -20, 0.1), + (-4, -20, 0.1), + (-2, -20, 0.1), + (0, -20, 0.1), + (2, -20, 0.1), + (4, -20, 0.1), + (6, -20, 0.1), + (8, -20, 0.1), + (10, -20, 0.1), + (12, -20, 0.1), + (14, -20, 0.1), + (16, -20, 0.1), + (22, -20, 0.1), + (-22, -18, 0.1), + (-16, -18, 0.1), + (0, -18, 0.1), + (16, -18, 0.1), + (22, -18, 0.1), + (-22, -16, 0.1), + (-16, -16, 0.1), + (0, -16, 0.1), + (16, -16, 0.1), + (22, -16, 0.1), + (-42, -14, 0.1), + (-40, -14, 0.1), + (-38, -14, 0.1), + (-36, -14, 0.1), + (-34, -14, 0.1), + (-32, -14, 0.1), + (-30, -14, 0.1), + (-28, -14, 0.1), + (-26, -14, 0.1), + (-24, -14, 0.1), + (-22, -14, 0.1), + (-16, -14, 0.1), + (-14, -14, 0.1), + (-12, -14, 0.1), + (-10, -14, 0.1), + (-8, -14, 0.1), + (-6, -14, 0.1), + (-4, -14, 0.1), + (-2, -14, 0.1), + (0, -14, 0.1), + (2, -14, 0.1), + (4, -14, 0.1), + (6, -14, 0.1), + (8, -14, 0.1), + (10, -14, 0.1), + (12, -14, 0.1), + (14, -14, 0.1), + (16, -14, 0.1), + (22, -14, 0.1), + (24, -14, 0.1), + (26, -14, 0.1), + (28, -14, 0.1), + (30, -14, 0.1), + (32, -14, 0.1), + (34, -14, 0.1), + (36, -14, 0.1), + (38, -14, 0.1), + (40, -14, 0.1), + (42, -14, 0.1), + (-42, -12, 0.1), + (-16, -12, 0.1), + (0, -12, 0.1), + (16, -12, 0.1), + (42, -12, 0.1), + (-42, -10, 0.1), + (-16, -10, 0.1), + (0, -10, 0.1), + (16, -10, 0.1), + (42, -10, 0.1), + (-42, -8, 0.1), + (-40, -8, 0.1), + (-38, -8, 0.1), + (-36, -8, 0.1), + (-34, -8, 0.1), + (-32, -8, 0.1), + (-30, -8, 0.1), + (-24, -8, 0.1), + (-22, -8, 0.1), + (-20, -8, 0.1), + (-18, -8, 0.1), + (-16, -8, 0.1), + (-14, -8, 0.1), + (-12, -8, 0.1), + (-10, -8, 0.1), + (-8, -8, 0.1), + (8, -8, 0.1), + (10, -8, 0.1), + (12, -8, 0.1), + (14, -8, 0.1), + (16, -8, 0.1), + (18, -8, 0.1), + (20, -8, 0.1), + (22, -8, 0.1), + (24, -8, 0.1), + (30, -8, 0.1), + (32, -8, 0.1), + (34, -8, 0.1), + (36, -8, 0.1), + (38, -8, 0.1), + (40, -8, 0.1), + (42, -8, 0.1), + (-42, -6, 0.1), + (-36, -6, 0.1), + (-30, -6, 0.1), + (-24, -6, 0.1), + (-16, -6, 0.1), + (16, -6, 0.1), + (24, -6, 0.1), + (30, -6, 0.1), + (36, -6, 0.1), + (42, -6, 0.1), + (-42, -4, 0.1), + (-36, -4, 0.1), + (-30, -4, 0.1), + (-24, -4, 0.1), + (-16, -4, 0.1), + (16, -4, 0.1), + (24, -4, 0.1), + (30, -4, 0.1), + (36, -4, 0.1), + (42, -4, 0.1), + (-42, -2, 0.1), + (-36, -2, 0.1), + (-30, -2, 0.1), + (-24, -2, 0.1), + (-16, -2, 0.1), + (16, -2, 0.1), + (24, -2, 0.1), + (30, -2, 0.1), + (36, -2, 0.1), + (42, -2, 0.1), + (-42, 0, 0.1), + (-36, 0, 0.1), + (-30, 0, 0.1), + (-24, 0, 0.1), + (-16, 0, 0.1), + (16, 0, 0.1), + (24, 0, 0.1), + (30, 0, 0.1), + (36, 0, 0.1), + (42, 0, 0.1), + (-42, 2, 0.1), + (-36, 2, 0.1), + (-30, 2, 0.1), + (-24, 2, 0.1), + (-16, 2, 0.1), + (16, 2, 0.1), + (24, 2, 0.1), + (30, 2, 0.1), + (36, 2, 0.1), + (42, 2, 0.1), + (-42, 4, 0.1), + (-40, 4, 0.1), + (-38, 4, 0.1), + (-36, 4, 0.1), + (-34, 4, 0.1), + (-32, 4, 0.1), + (-30, 4, 0.1), + (-28, 4, 0.1), + (-26, 4, 0.1), + (-24, 4, 0.1), + (-22, 4, 0.1), + (-20, 4, 0.1), + (-18, 4, 0.1), + (-16, 4, 0.1), + (-14, 4, 0.1), + (-12, 4, 0.1), + (-10, 4, 0.1), + (-8, 4, 0.1), + (8, 4, 0.1), + (10, 4, 0.1), + (12, 4, 0.1), + (14, 4, 0.1), + (16, 4, 0.1), + (18, 4, 0.1), + (20, 4, 0.1), + (22, 4, 0.1), + (24, 4, 0.1), + (26, 4, 0.1), + (28, 4, 0.1), + (30, 4, 0.1), + (32, 4, 0.1), + (34, 4, 0.1), + (36, 4, 0.1), + (38, 4, 0.1), + (40, 4, 0.1), + (42, 4, 0.1), + (-42, 6, 0.1), + (-16, 6, 0.1), + (16, 6, 0.1), + (42, 6, 0.1), + (-42, 8, 0.1), + (-16, 8, 0.1), + (16, 8, 0.1), + (42, 8, 0.1), + (-42, 10, 0.1), + (-40, 10, 0.1), + (-38, 10, 0.1), + (-36, 10, 0.1), + (-34, 10, 0.1), + (-32, 10, 0.1), + (-30, 10, 0.1), + (-24, 10, 0.1), + (-22, 10, 0.1), + (-20, 10, 0.1), + (-18, 10, 0.1), + (-16, 10, 0.1), + (-14, 10, 0.1), + (-12, 10, 0.1), + (-10, 10, 0.1), + (-8, 10, 0.1), + (-6, 10, 0.1), + (0, 10, 0.1), + (6, 10, 0.1), + (8, 10, 0.1), + (10, 10, 0.1), + (12, 10, 0.1), + (14, 10, 0.1), + (16, 10, 0.1), + (18, 10, 0.1), + (20, 10, 0.1), + (22, 10, 0.1), + (24, 10, 0.1), + (30, 10, 0.1), + (32, 10, 0.1), + (34, 10, 0.1), + (36, 10, 0.1), + (38, 10, 0.1), + (40, 10, 0.1), + (42, 10, 0.1), + (-42, 12, 0.1), + (-36, 12, 0.1), + (-30, 12, 0.1), + (-24, 12, 0.1), + (-16, 12, 0.1), + (-6, 12, 0.1), + (0, 12, 0.1), + (6, 12, 0.1), + (16, 12, 0.1), + (24, 12, 0.1), + (30, 12, 0.1), + (36, 12, 0.1), + (42, 12, 0.1), + (-42, 14, 0.1), + (-36, 14, 0.1), + (-30, 14, 0.1), + (-24, 14, 0.1), + (-16, 14, 0.1), + (-6, 14, 0.1), + (0, 14, 0.1), + (6, 14, 0.1), + (16, 14, 0.1), + (24, 14, 0.1), + (30, 14, 0.1), + (36, 14, 0.1), + (42, 14, 0.1), + (-42, 16, 0.1), + (-36, 16, 0.1), + (-30, 16, 0.1), + (-24, 16, 0.1), + (-16, 16, 0.1), + (-6, 16, 0.1), + (-4, 16, 0.1), + (-2, 16, 0.1), + (0, 16, 0.1), + (2, 16, 0.1), + (4, 16, 0.1), + (6, 16, 0.1), + (16, 16, 0.1), + (24, 16, 0.1), + (30, 16, 0.1), + (36, 16, 0.1), + (42, 16, 0.1), + (-42, 18, 0.1), + (-36, 18, 0.1), + (-30, 18, 0.1), + (-24, 18, 0.1), + (-16, 18, 0.1), + (-6, 18, 0.1), + (0, 18, 0.1), + (6, 18, 0.1), + (16, 18, 0.1), + (24, 18, 0.1), + (30, 18, 0.1), + (36, 18, 0.1), + (42, 18, 0.1), + (-42, 20, 0.1), + (-36, 20, 0.1), + (-30, 20, 0.1), + (-24, 20, 0.1), + (-16, 20, 0.1), + (-6, 20, 0.1), + (0, 20, 0.1), + (6, 20, 0.1), + (16, 20, 0.1), + (24, 20, 0.1), + (30, 20, 0.1), + (36, 20, 0.1), + (42, 20, 0.1), + (-42, 22, 0.1), + (-40, 22, 0.1), + (-38, 22, 0.1), + (-36, 22, 0.1), + (-34, 22, 0.1), + (-32, 22, 0.1), + (-30, 22, 0.1), + (-28, 22, 0.1), + (-26, 22, 0.1), + (-24, 22, 0.1), + (-22, 22, 0.1), + (-20, 22, 0.1), + (-18, 22, 0.1), + (-16, 22, 0.1), + (-14, 22, 0.1), + (-12, 22, 0.1), + (-10, 22, 0.1), + (-8, 22, 0.1), + (-6, 22, 0.1), + (0, 22, 0.1), + (6, 22, 0.1), + (8, 22, 0.1), + (10, 22, 0.1), + (12, 22, 0.1), + (14, 22, 0.1), + (16, 22, 0.1), + (18, 22, 0.1), + (20, 22, 0.1), + (22, 22, 0.1), + (24, 22, 0.1), + (26, 22, 0.1), + (28, 22, 0.1), + (30, 22, 0.1), + (32, 22, 0.1), + (34, 22, 0.1), + (36, 22, 0.1), + (38, 22, 0.1), + (40, 22, 0.1), + (42, 22, 0.1), + (-42, 24, 0.1), + (-16, 24, 0.1), + (0, 24, 0.1), + (16, 24, 0.1), + (42, 24, 0.1), + (-42, 26, 0.1), + (-16, 26, 0.1), + (0, 26, 0.1), + (16, 26, 0.1), + (42, 26, 0.1), + (-42, 28, 0.1), + (-40, 28, 0.1), + (-38, 28, 0.1), + (-36, 28, 0.1), + (-34, 28, 0.1), + (-32, 28, 0.1), + (-30, 28, 0.1), + (-28, 28, 0.1), + (-26, 28, 0.1), + (-24, 28, 0.1), + (-22, 28, 0.1), + (-20, 28, 0.1), + (-18, 28, 0.1), + (-16, 28, 0.1), + (-14, 28, 0.1), + (-12, 28, 0.1), + (-10, 28, 0.1), + (-8, 28, 0.1), + (-6, 28, 0.1), + (-4, 28, 0.1), + (-2, 28, 0.1), + (0, 28, 0.1), + (2, 28, 0.1), + (4, 28, 0.1), + (6, 28, 0.1), + (8, 28, 0.1), + (10, 28, 0.1), + (12, 28, 0.1), + (14, 28, 0.1), + (16, 28, 0.1), + (18, 28, 0.1), + (20, 28, 0.1), + (22, 28, 0.1), + (24, 28, 0.1), + (26, 28, 0.1), + (28, 28, 0.1), + (30, 28, 0.1), + (32, 28, 0.1), + (34, 28, 0.1), + (36, 28, 0.1), + (38, 28, 0.1), + (40, 28, 0.1), + (42, 28, 0.1), + (-22, 30, 0.1), + (-16, 30, 0.1), + (0, 30, 0.1), + (16, 30, 0.1), + (22, 30, 0.1), + (-22, 32, 0.1), + (-16, 32, 0.1), + (0, 32, 0.1), + (16, 32, 0.1), + (22, 32, 0.1), + (-22, 34, 0.1), + (-16, 34, 0.1), + (-14, 34, 0.1), + (-12, 34, 0.1), + (-6, 34, 0.1), + (-4, 34, 0.1), + (-2, 34, 0.1), + (0, 34, 0.1), + (2, 34, 0.1), + (4, 34, 0.1), + (6, 34, 0.1), + (12, 34, 0.1), + (14, 34, 0.1), + (16, 34, 0.1), + (22, 34, 0.1), + (-22, 36, 0.1), + (-12, 36, 0.1), + (-6, 36, 0.1), + (6, 36, 0.1), + (12, 36, 0.1), + (22, 36, 0.1), + (-22, 38, 0.1), + (-12, 38, 0.1), + (-6, 38, 0.1), + (6, 38, 0.1), + (12, 38, 0.1), + (22, 38, 0.1), + (-22, 40, 0.1), + (-12, 40, 0.1), + (-6, 40, 0.1), + (6, 40, 0.1), + (12, 40, 0.1), + (22, 40, 0.1), + (-22, 42, 0.1), + (-20, 42, 0.1), + (-18, 42, 0.1), + (-16, 42, 0.1), + (-14, 42, 0.1), + (-12, 42, 0.1), + (-6, 42, 0.1), + (-4, 42, 0.1), + (-2, 42, 0.1), + (0, 42, 0.1), + (2, 42, 0.1), + (4, 42, 0.1), + (6, 42, 0.1), + (12, 42, 0.1), + (14, 42, 0.1), + (16, 42, 0.1), + (18, 42, 0.1), + (20, 42, 0.1), + (22, 42, 0.1), + (-22, 44, 0.1), + (-6, 44, 0.1), + (6, 44, 0.1), + (22, 44, 0.1), + (-22, 46, 0.1), + (-6, 46, 0.1), + (6, 46, 0.1), + (22, 46, 0.1), + (-22, 48, 0.1), + (-20, 48, 0.1), + (-18, 48, 0.1), + (-16, 48, 0.1), + (-14, 48, 0.1), + (-12, 48, 0.1), + (-10, 48, 0.1), + (-8, 48, 0.1), + (-6, 48, 0.1), + (-4, 48, 0.1), + (-2, 48, 0.1), + (0, 48, 0.1), + (2, 48, 0.1), + (4, 48, 0.1), + (6, 48, 0.1), + (8, 48, 0.1), + (10, 48, 0.1), + (12, 48, 0.1), + (14, 48, 0.1), + (16, 48, 0.1), + (18, 48, 0.1), + (20, 48, 0.1), + (22, 48, 0.1)] +mazeData['phase_4/models/minigames/maze_4player'] = {} +data = mazeData['phase_4/models/minigames/maze_4player'] +data['width'] = 50 +data['height'] = 40 +data['originX'] = 25 +data['originY'] = 20 +data['collisionTable'] = [[1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 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, + 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, + 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, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 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, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 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, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 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, + 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, + 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, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1], + [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1]] +data['treasurePosList'] = [(-46, -36, 0.1), + (-44, -36, 0.1), + (-42, -36, 0.1), + (-40, -36, 0.1), + (-34, -36, 0.1), + (-32, -36, 0.1), + (-30, -36, 0.1), + (-28, -36, 0.1), + (-18, -36, 0.1), + (-10, -36, 0.1), + (-8, -36, 0.1), + (-6, -36, 0.1), + (-4, -36, 0.1), + (-2, -36, 0.1), + (0, -36, 0.1), + (2, -36, 0.1), + (4, -36, 0.1), + (6, -36, 0.1), + (8, -36, 0.1), + (10, -36, 0.1), + (18, -36, 0.1), + (28, -36, 0.1), + (30, -36, 0.1), + (32, -36, 0.1), + (34, -36, 0.1), + (40, -36, 0.1), + (42, -36, 0.1), + (44, -36, 0.1), + (46, -36, 0.1), + (-46, -34, 0.1), + (-40, -34, 0.1), + (-34, -34, 0.1), + (-28, -34, 0.1), + (-18, -34, 0.1), + (-10, -34, 0.1), + (0, -34, 0.1), + (10, -34, 0.1), + (18, -34, 0.1), + (28, -34, 0.1), + (34, -34, 0.1), + (40, -34, 0.1), + (46, -34, 0.1), + (-46, -32, 0.1), + (-40, -32, 0.1), + (-38, -32, 0.1), + (-36, -32, 0.1), + (-34, -32, 0.1), + (-28, -32, 0.1), + (-18, -32, 0.1), + (-10, -32, 0.1), + (0, -32, 0.1), + (10, -32, 0.1), + (18, -32, 0.1), + (28, -32, 0.1), + (34, -32, 0.1), + (36, -32, 0.1), + (38, -32, 0.1), + (40, -32, 0.1), + (46, -32, 0.1), + (-46, -30, 0.1), + (-40, -30, 0.1), + (-28, -30, 0.1), + (-18, -30, 0.1), + (-10, -30, 0.1), + (0, -30, 0.1), + (10, -30, 0.1), + (18, -30, 0.1), + (28, -30, 0.1), + (40, -30, 0.1), + (46, -30, 0.1), + (-46, -28, 0.1), + (-40, -28, 0.1), + (-28, -28, 0.1), + (-18, -28, 0.1), + (-10, -28, 0.1), + (0, -28, 0.1), + (10, -28, 0.1), + (18, -28, 0.1), + (28, -28, 0.1), + (40, -28, 0.1), + (46, -28, 0.1), + (-46, -26, 0.1), + (-40, -26, 0.1), + (-38, -26, 0.1), + (-36, -26, 0.1), + (-34, -26, 0.1), + (-32, -26, 0.1), + (-30, -26, 0.1), + (-28, -26, 0.1), + (-26, -26, 0.1), + (-24, -26, 0.1), + (-22, -26, 0.1), + (-20, -26, 0.1), + (-18, -26, 0.1), + (-16, -26, 0.1), + (-14, -26, 0.1), + (-12, -26, 0.1), + (-10, -26, 0.1), + (-8, -26, 0.1), + (-6, -26, 0.1), + (-4, -26, 0.1), + (-2, -26, 0.1), + (0, -26, 0.1), + (2, -26, 0.1), + (4, -26, 0.1), + (6, -26, 0.1), + (8, -26, 0.1), + (10, -26, 0.1), + (12, -26, 0.1), + (14, -26, 0.1), + (16, -26, 0.1), + (18, -26, 0.1), + (20, -26, 0.1), + (22, -26, 0.1), + (24, -26, 0.1), + (26, -26, 0.1), + (28, -26, 0.1), + (30, -26, 0.1), + (32, -26, 0.1), + (34, -26, 0.1), + (36, -26, 0.1), + (38, -26, 0.1), + (40, -26, 0.1), + (46, -26, 0.1), + (-46, -24, 0.1), + (-34, -24, 0.1), + (-28, -24, 0.1), + (-16, -24, 0.1), + (0, -24, 0.1), + (16, -24, 0.1), + (28, -24, 0.1), + (34, -24, 0.1), + (46, -24, 0.1), + (-46, -22, 0.1), + (-34, -22, 0.1), + (-28, -22, 0.1), + (-16, -22, 0.1), + (0, -22, 0.1), + (16, -22, 0.1), + (28, -22, 0.1), + (34, -22, 0.1), + (46, -22, 0.1), + (-46, -20, 0.1), + (-40, -20, 0.1), + (-38, -20, 0.1), + (-36, -20, 0.1), + (-34, -20, 0.1), + (-28, -20, 0.1), + (-26, -20, 0.1), + (-24, -20, 0.1), + (-22, -20, 0.1), + (-20, -20, 0.1), + (-18, -20, 0.1), + (-16, -20, 0.1), + (-14, -20, 0.1), + (-12, -20, 0.1), + (-6, -20, 0.1), + (-4, -20, 0.1), + (-2, -20, 0.1), + (0, -20, 0.1), + (2, -20, 0.1), + (4, -20, 0.1), + (6, -20, 0.1), + (12, -20, 0.1), + (14, -20, 0.1), + (16, -20, 0.1), + (18, -20, 0.1), + (20, -20, 0.1), + (22, -20, 0.1), + (24, -20, 0.1), + (26, -20, 0.1), + (28, -20, 0.1), + (34, -20, 0.1), + (36, -20, 0.1), + (38, -20, 0.1), + (40, -20, 0.1), + (46, -20, 0.1), + (-46, -18, 0.1), + (-40, -18, 0.1), + (-34, -18, 0.1), + (-28, -18, 0.1), + (-22, -18, 0.1), + (-12, -18, 0.1), + (-6, -18, 0.1), + (0, -18, 0.1), + (6, -18, 0.1), + (12, -18, 0.1), + (22, -18, 0.1), + (28, -18, 0.1), + (34, -18, 0.1), + (40, -18, 0.1), + (46, -18, 0.1), + (-46, -16, 0.1), + (-44, -16, 0.1), + (-42, -16, 0.1), + (-40, -16, 0.1), + (-34, -16, 0.1), + (-32, -16, 0.1), + (-30, -16, 0.1), + (-28, -16, 0.1), + (-22, -16, 0.1), + (-12, -16, 0.1), + (-10, -16, 0.1), + (-8, -16, 0.1), + (-6, -16, 0.1), + (0, -16, 0.1), + (6, -16, 0.1), + (8, -16, 0.1), + (10, -16, 0.1), + (12, -16, 0.1), + (22, -16, 0.1), + (28, -16, 0.1), + (30, -16, 0.1), + (32, -16, 0.1), + (34, -16, 0.1), + (40, -16, 0.1), + (42, -16, 0.1), + (44, -16, 0.1), + (46, -16, 0.1), + (-46, -14, 0.1), + (-34, -14, 0.1), + (-22, -14, 0.1), + (-12, -14, 0.1), + (0, -14, 0.1), + (12, -14, 0.1), + (22, -14, 0.1), + (34, -14, 0.1), + (46, -14, 0.1), + (-46, -12, 0.1), + (-34, -12, 0.1), + (-22, -12, 0.1), + (-12, -12, 0.1), + (0, -12, 0.1), + (12, -12, 0.1), + (22, -12, 0.1), + (34, -12, 0.1), + (46, -12, 0.1), + (-46, -10, 0.1), + (-44, -10, 0.1), + (-42, -10, 0.1), + (-40, -10, 0.1), + (-34, -10, 0.1), + (-32, -10, 0.1), + (-30, -10, 0.1), + (-28, -10, 0.1), + (-22, -10, 0.1), + (-12, -10, 0.1), + (-10, -10, 0.1), + (-8, -10, 0.1), + (-6, -10, 0.1), + (0, -10, 0.1), + (6, -10, 0.1), + (8, -10, 0.1), + (10, -10, 0.1), + (12, -10, 0.1), + (22, -10, 0.1), + (28, -10, 0.1), + (30, -10, 0.1), + (32, -10, 0.1), + (34, -10, 0.1), + (40, -10, 0.1), + (42, -10, 0.1), + (44, -10, 0.1), + (46, -10, 0.1), + (-46, -8, 0.1), + (-40, -8, 0.1), + (-34, -8, 0.1), + (-28, -8, 0.1), + (-22, -8, 0.1), + (-12, -8, 0.1), + (12, -8, 0.1), + (22, -8, 0.1), + (28, -8, 0.1), + (34, -8, 0.1), + (40, -8, 0.1), + (46, -8, 0.1), + (-46, -6, 0.1), + (-40, -6, 0.1), + (-38, -6, 0.1), + (-36, -6, 0.1), + (-34, -6, 0.1), + (-28, -6, 0.1), + (-26, -6, 0.1), + (-24, -6, 0.1), + (-22, -6, 0.1), + (-20, -6, 0.1), + (-18, -6, 0.1), + (-16, -6, 0.1), + (-14, -6, 0.1), + (-12, -6, 0.1), + (12, -6, 0.1), + (14, -6, 0.1), + (16, -6, 0.1), + (18, -6, 0.1), + (20, -6, 0.1), + (22, -6, 0.1), + (24, -6, 0.1), + (26, -6, 0.1), + (28, -6, 0.1), + (34, -6, 0.1), + (36, -6, 0.1), + (38, -6, 0.1), + (40, -6, 0.1), + (46, -6, 0.1), + (-46, -4, 0.1), + (-34, -4, 0.1), + (-28, -4, 0.1), + (-16, -4, 0.1), + (16, -4, 0.1), + (28, -4, 0.1), + (34, -4, 0.1), + (46, -4, 0.1), + (-46, -2, 0.1), + (-34, -2, 0.1), + (-28, -2, 0.1), + (-16, -2, 0.1), + (16, -2, 0.1), + (28, -2, 0.1), + (34, -2, 0.1), + (46, -2, 0.1), + (-46, 0, 0.1), + (-40, 0, 0.1), + (-38, 0, 0.1), + (-36, 0, 0.1), + (-34, 0, 0.1), + (-28, 0, 0.1), + (-26, 0, 0.1), + (-24, 0, 0.1), + (-22, 0, 0.1), + (-20, 0, 0.1), + (-18, 0, 0.1), + (-16, 0, 0.1), + (-14, 0, 0.1), + (-12, 0, 0.1), + (-10, 0, 0.1), + (10, 0, 0.1), + (12, 0, 0.1), + (14, 0, 0.1), + (16, 0, 0.1), + (18, 0, 0.1), + (20, 0, 0.1), + (22, 0, 0.1), + (24, 0, 0.1), + (26, 0, 0.1), + (28, 0, 0.1), + (34, 0, 0.1), + (36, 0, 0.1), + (38, 0, 0.1), + (40, 0, 0.1), + (46, 0, 0.1), + (-46, 2, 0.1), + (-34, 2, 0.1), + (-28, 2, 0.1), + (-16, 2, 0.1), + (16, 2, 0.1), + (28, 2, 0.1), + (34, 2, 0.1), + (46, 2, 0.1), + (-46, 4, 0.1), + (-34, 4, 0.1), + (-28, 4, 0.1), + (-16, 4, 0.1), + (16, 4, 0.1), + (28, 4, 0.1), + (34, 4, 0.1), + (46, 4, 0.1), + (-46, 6, 0.1), + (-40, 6, 0.1), + (-38, 6, 0.1), + (-36, 6, 0.1), + (-34, 6, 0.1), + (-28, 6, 0.1), + (-26, 6, 0.1), + (-24, 6, 0.1), + (-22, 6, 0.1), + (-20, 6, 0.1), + (-18, 6, 0.1), + (-16, 6, 0.1), + (-14, 6, 0.1), + (-12, 6, 0.1), + (12, 6, 0.1), + (14, 6, 0.1), + (16, 6, 0.1), + (18, 6, 0.1), + (20, 6, 0.1), + (22, 6, 0.1), + (24, 6, 0.1), + (26, 6, 0.1), + (28, 6, 0.1), + (34, 6, 0.1), + (36, 6, 0.1), + (38, 6, 0.1), + (40, 6, 0.1), + (46, 6, 0.1), + (-46, 8, 0.1), + (-40, 8, 0.1), + (-34, 8, 0.1), + (-28, 8, 0.1), + (-22, 8, 0.1), + (-12, 8, 0.1), + (12, 8, 0.1), + (22, 8, 0.1), + (28, 8, 0.1), + (34, 8, 0.1), + (40, 8, 0.1), + (46, 8, 0.1), + (-46, 10, 0.1), + (-44, 10, 0.1), + (-42, 10, 0.1), + (-40, 10, 0.1), + (-34, 10, 0.1), + (-32, 10, 0.1), + (-30, 10, 0.1), + (-28, 10, 0.1), + (-22, 10, 0.1), + (-12, 10, 0.1), + (-10, 10, 0.1), + (-8, 10, 0.1), + (-6, 10, 0.1), + (0, 10, 0.1), + (6, 10, 0.1), + (8, 10, 0.1), + (10, 10, 0.1), + (12, 10, 0.1), + (22, 10, 0.1), + (28, 10, 0.1), + (30, 10, 0.1), + (32, 10, 0.1), + (34, 10, 0.1), + (40, 10, 0.1), + (42, 10, 0.1), + (44, 10, 0.1), + (46, 10, 0.1), + (-46, 12, 0.1), + (-34, 12, 0.1), + (-22, 12, 0.1), + (-12, 12, 0.1), + (0, 12, 0.1), + (12, 12, 0.1), + (22, 12, 0.1), + (34, 12, 0.1), + (46, 12, 0.1), + (-46, 14, 0.1), + (-34, 14, 0.1), + (-22, 14, 0.1), + (-12, 14, 0.1), + (0, 14, 0.1), + (12, 14, 0.1), + (22, 14, 0.1), + (34, 14, 0.1), + (46, 14, 0.1), + (-46, 16, 0.1), + (-44, 16, 0.1), + (-42, 16, 0.1), + (-40, 16, 0.1), + (-34, 16, 0.1), + (-32, 16, 0.1), + (-30, 16, 0.1), + (-28, 16, 0.1), + (-22, 16, 0.1), + (-12, 16, 0.1), + (-10, 16, 0.1), + (-8, 16, 0.1), + (-6, 16, 0.1), + (0, 16, 0.1), + (6, 16, 0.1), + (8, 16, 0.1), + (10, 16, 0.1), + (12, 16, 0.1), + (22, 16, 0.1), + (28, 16, 0.1), + (30, 16, 0.1), + (32, 16, 0.1), + (34, 16, 0.1), + (40, 16, 0.1), + (42, 16, 0.1), + (44, 16, 0.1), + (46, 16, 0.1), + (-46, 18, 0.1), + (-40, 18, 0.1), + (-34, 18, 0.1), + (-28, 18, 0.1), + (-22, 18, 0.1), + (-12, 18, 0.1), + (-6, 18, 0.1), + (0, 18, 0.1), + (6, 18, 0.1), + (12, 18, 0.1), + (22, 18, 0.1), + (28, 18, 0.1), + (34, 18, 0.1), + (40, 18, 0.1), + (46, 18, 0.1), + (-46, 20, 0.1), + (-40, 20, 0.1), + (-38, 20, 0.1), + (-36, 20, 0.1), + (-34, 20, 0.1), + (-28, 20, 0.1), + (-26, 20, 0.1), + (-24, 20, 0.1), + (-22, 20, 0.1), + (-20, 20, 0.1), + (-18, 20, 0.1), + (-16, 20, 0.1), + (-14, 20, 0.1), + (-12, 20, 0.1), + (-6, 20, 0.1), + (-4, 20, 0.1), + (-2, 20, 0.1), + (0, 20, 0.1), + (2, 20, 0.1), + (4, 20, 0.1), + (6, 20, 0.1), + (12, 20, 0.1), + (14, 20, 0.1), + (16, 20, 0.1), + (18, 20, 0.1), + (20, 20, 0.1), + (22, 20, 0.1), + (24, 20, 0.1), + (26, 20, 0.1), + (28, 20, 0.1), + (34, 20, 0.1), + (36, 20, 0.1), + (38, 20, 0.1), + (40, 20, 0.1), + (46, 20, 0.1), + (-46, 22, 0.1), + (-34, 22, 0.1), + (-28, 22, 0.1), + (-16, 22, 0.1), + (0, 22, 0.1), + (16, 22, 0.1), + (28, 22, 0.1), + (34, 22, 0.1), + (46, 22, 0.1), + (-46, 24, 0.1), + (-34, 24, 0.1), + (-28, 24, 0.1), + (-16, 24, 0.1), + (0, 24, 0.1), + (16, 24, 0.1), + (28, 24, 0.1), + (34, 24, 0.1), + (46, 24, 0.1), + (-46, 26, 0.1), + (-40, 26, 0.1), + (-38, 26, 0.1), + (-36, 26, 0.1), + (-34, 26, 0.1), + (-32, 26, 0.1), + (-30, 26, 0.1), + (-28, 26, 0.1), + (-26, 26, 0.1), + (-24, 26, 0.1), + (-22, 26, 0.1), + (-20, 26, 0.1), + (-18, 26, 0.1), + (-16, 26, 0.1), + (-14, 26, 0.1), + (-12, 26, 0.1), + (-10, 26, 0.1), + (-8, 26, 0.1), + (-6, 26, 0.1), + (-4, 26, 0.1), + (-2, 26, 0.1), + (0, 26, 0.1), + (2, 26, 0.1), + (4, 26, 0.1), + (6, 26, 0.1), + (8, 26, 0.1), + (10, 26, 0.1), + (12, 26, 0.1), + (14, 26, 0.1), + (16, 26, 0.1), + (18, 26, 0.1), + (20, 26, 0.1), + (22, 26, 0.1), + (24, 26, 0.1), + (26, 26, 0.1), + (28, 26, 0.1), + (30, 26, 0.1), + (32, 26, 0.1), + (34, 26, 0.1), + (36, 26, 0.1), + (38, 26, 0.1), + (40, 26, 0.1), + (46, 26, 0.1), + (-46, 28, 0.1), + (-40, 28, 0.1), + (-28, 28, 0.1), + (-18, 28, 0.1), + (-10, 28, 0.1), + (0, 28, 0.1), + (10, 28, 0.1), + (18, 28, 0.1), + (28, 28, 0.1), + (40, 28, 0.1), + (46, 28, 0.1), + (-46, 30, 0.1), + (-40, 30, 0.1), + (-28, 30, 0.1), + (-18, 30, 0.1), + (-10, 30, 0.1), + (0, 30, 0.1), + (10, 30, 0.1), + (18, 30, 0.1), + (28, 30, 0.1), + (40, 30, 0.1), + (46, 30, 0.1), + (-46, 32, 0.1), + (-40, 32, 0.1), + (-38, 32, 0.1), + (-36, 32, 0.1), + (-34, 32, 0.1), + (-28, 32, 0.1), + (-18, 32, 0.1), + (-10, 32, 0.1), + (0, 32, 0.1), + (10, 32, 0.1), + (18, 32, 0.1), + (28, 32, 0.1), + (34, 32, 0.1), + (36, 32, 0.1), + (38, 32, 0.1), + (40, 32, 0.1), + (46, 32, 0.1), + (-46, 34, 0.1), + (-40, 34, 0.1), + (-34, 34, 0.1), + (-28, 34, 0.1), + (-18, 34, 0.1), + (-10, 34, 0.1), + (0, 34, 0.1), + (10, 34, 0.1), + (18, 34, 0.1), + (28, 34, 0.1), + (34, 34, 0.1), + (40, 34, 0.1), + (46, 34, 0.1), + (-46, 36, 0.1), + (-44, 36, 0.1), + (-42, 36, 0.1), + (-40, 36, 0.1), + (-34, 36, 0.1), + (-32, 36, 0.1), + (-30, 36, 0.1), + (-28, 36, 0.1), + (-18, 36, 0.1), + (-10, 36, 0.1), + (-8, 36, 0.1), + (-6, 36, 0.1), + (-4, 36, 0.1), + (-2, 36, 0.1), + (0, 36, 0.1), + (2, 36, 0.1), + (4, 36, 0.1), + (6, 36, 0.1), + (8, 36, 0.1), + (10, 36, 0.1), + (18, 36, 0.1), + (28, 36, 0.1), + (30, 36, 0.1), + (32, 36, 0.1), + (34, 36, 0.1), + (40, 36, 0.1), + (42, 36, 0.1), + (44, 36, 0.1), + (46, 36, 0.1)] diff --git a/toontown/minigame/MazeGameGlobals.py b/toontown/minigame/MazeGameGlobals.py new file mode 100644 index 0000000..4ae9caa --- /dev/null +++ b/toontown/minigame/MazeGameGlobals.py @@ -0,0 +1,32 @@ +from direct.showbase import RandomNumGen + +def getMazeName(gameDoId, numPlayers, mazeNames): + try: + return forcedMaze + except: + names = mazeNames[numPlayers - 1] + return names[RandomNumGen.randHash(gameDoId) % len(names)] + + +ENDLESS_GAME = config.GetBool('endless-maze-game', 0) +GAME_DURATION = 60.0 +SHOWSCORES_DURATION = 2.0 +SUIT_TIC_FREQ = int(256) +WALK_SAME_DIRECTION_PROB = 4 +WALK_TURN_AROUND_PROB = 30 +SUIT_START_POSITIONS = ((0.25, 0.25), + (0.75, 0.75), + (0.25, 0.75), + (0.75, 0.25), + (0.2, 0.5), + (0.8, 0.5), + (0.5, 0.2), + (0.5, 0.8), + (0.33, 0.0), + (0.66, 0.0), + (0.33, 1.0), + (0.66, 1.0), + (0.0, 0.33), + (0.0, 0.66), + (1.0, 0.33), + (1.0, 0.66)) diff --git a/toontown/minigame/MazeMapGui.py b/toontown/minigame/MazeMapGui.py new file mode 100644 index 0000000..e29a160 --- /dev/null +++ b/toontown/minigame/MazeMapGui.py @@ -0,0 +1,245 @@ +from direct.showbase.PythonUtil import Enum +from direct.gui.DirectGui import DirectFrame, DGG +from pandac.PandaModules import Vec2, VBase4D +from pandac.PandaModules import CardMaker, NodePath +from pandac.PandaModules import Texture, PNMImage +DEFAULT_MASK_RESOLUTION = 32 +DEFAULT_RADIUS_RATIO = 0.05 +MAP_RESOLUTION = 320 +MazeRevealType = Enum(('SmoothCircle', 'HardCircle', 'Square')) +MAZE_REVEAL_TYPE = MazeRevealType.SmoothCircle + +class MazeMapGui(DirectFrame): + notify = directNotify.newCategory('MazeMapGui') + + def __init__(self, mazeCollTable, maskResolution = None, radiusRatio = None, bgColor = (0.8, 0.8, 0.8), fgColor = (0.5, 0.5, 0.5, 1.0)): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.hide() + self._bgColor = bgColor + self._fgColor = fgColor + self._mazeCollTable = mazeCollTable + self._mazeWidth = len(self._mazeCollTable[0]) + self._mazeHeight = len(self._mazeCollTable) + self._maskResolution = maskResolution or DEFAULT_MASK_RESOLUTION + if radiusRatio is None: + self._radius = self._maskResolution * DEFAULT_RADIUS_RATIO + else: + self._radius = self._maskResolution * radiusRatio + self._revealedCells = [] + for y in range(self._mazeHeight): + self._revealedCells.append([]) + for u in range(self._mazeWidth): + self._revealedCells[y].append(False) + + self._revealFunctions = {MazeRevealType.SmoothCircle: self._revealSmoothCircle, + MazeRevealType.HardCircle: self._revealHardCircle, + MazeRevealType.Square: self._revealSquare} + self._revealFunction = MAZE_REVEAL_TYPE + self.map = self._createMapTextureCard() + self.map.reparentTo(self) + self.maskedLayer = self.attachNewNode('maskedLayer') + self.mask = self._createMaskTextureCard() + self.mask.reparentTo(self) + self.visibleLayer = self.attachNewNode('visibleLayer') + self._laffMeterModel = loader.loadModel('phase_3/models/gui/laff_o_meter') + self._toon2marker = {} + return + + def _createMapTextureCard(self): + mapImage = PNMImage(MAP_RESOLUTION, MAP_RESOLUTION) + mapImage.fill(*self._bgColor) + fgColor = VBase4D(*self._fgColor) + for x in range(self._mazeHeight): + for y in range(self._mazeWidth): + if self._mazeCollTable[y][x] == 1: + ax = float(x) / self._mazeWidth * MAP_RESOLUTION + invertedY = self._mazeHeight - 1 - y + ay = float(invertedY) / self._mazeHeight * MAP_RESOLUTION + self._drawSquare(mapImage, int(ax), int(ay), 10, fgColor) + + mapTexture = Texture('mapTexture') + mapTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) + mapTexture.setMinfilter(Texture.FTLinear) + mapTexture.load(mapImage) + mapTexture.setWrapU(Texture.WMClamp) + mapTexture.setWrapV(Texture.WMClamp) + mapImage.clear() + del mapImage + cm = CardMaker('map_cardMaker') + cm.setFrame(-1.0, 1.0, -1.0, 1.0) + map = self.attachNewNode(cm.generate()) + map.setTexture(mapTexture, 1) + return map + + def _createMaskTextureCard(self): + self._maskImage = PNMImage(self._maskResolution, self._maskResolution, 4) + for x in range(self._maskResolution): + for y in range(self._maskResolution): + self._maskImage.setXelA(x, y, 0, 0, 0, 1) + + self.maskTexture = Texture('maskTexture') + self.maskTexture.setupTexture(Texture.TT2dTexture, self._maskResolution, self._maskResolution, 1, Texture.TUnsignedByte, Texture.FRgba) + self.maskTexture.setMinfilter(Texture.FTLinear) + self.maskTexture.setWrapU(Texture.WMClamp) + self.maskTexture.setWrapV(Texture.WMClamp) + self.maskTexture.load(self._maskImage) + base.graphicsEngine.renderFrame() + cm = CardMaker('mask_cardMaker') + cm.setFrame(-1.1, 1.1, -1.1, 1.1) + mask = self.attachNewNode(cm.generate()) + mask.setTexture(self.maskTexture, 1) + mask.setTransparency(1) + return mask + + def _drawSquare(self, image, ulx, uly, size, color): + x = int(ulx) + while x <= ulx + size: + y = int(uly) + while y <= uly + size: + if x > 0 and y > 0 and x < image.getXSize() and y < image.getYSize(): + image.setXelA(x, y, color) + y += 1 + + x += 1 + + def destroy(self): + del self._mazeCollTable + del self._maskResolution + del self._radius + del self._revealedCells + del self._revealFunctions + del self._revealFunction + self.map.removeNode() + del self.map + self.mask.removeNode() + del self.mask + self.maskedLayer.removeNode() + del self.maskedLayer + self.visibleLayer.removeNode() + del self.visibleLayer + self._maskImage.clear() + del self._maskImage + self.maskTexture.clear() + del self.maskTexture + self._laffMeterModel.removeNode() + del self._laffMeterModel + DirectFrame.destroy(self) + + def _revealSmoothCircle(self, x, y, center): + length = (Vec2(x, y) - center).length() + goalAlpha = max(0.0, length / float(self._radius) - 0.5) + self._maskImage.setXelA(x, y, VBase4D(0.0, 0.0, 0.0, min(self._maskImage.getAlpha(x, y), goalAlpha * 2.0))) + + def _revealHardCircle(self, x, y, center): + length = (Vec2(x, y) - center).length() + if length <= self._radius: + self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0)) + + def _revealSquare(self, x, y, center): + self._maskImage.setXelA(x, y, VBase4D(0, 0, 0, 0)) + + def _drawHole(self, x, y): + center = Vec2(x, y) + ul = center - Vec2(self._radius, self._radius) + lr = center + Vec2(self._radius, self._radius) + x = int(ul[0]) + while x <= lr[0]: + y = int(ul[1]) + while y <= lr[1]: + if x > 0 and y > 0 and x < self._maskResolution and y < self._maskResolution: + self._revealFunctions[self._revealFunction](x, y, center) + y += 1 + + x += 1 + + self.maskTexture.load(self._maskImage) + self.mask.setTexture(self.maskTexture, 1) + + def _createSimpleMarker(self, size, color = (1, 1, 1)): + halfSize = size * 0.5 + cm = CardMaker('mazemap_simple_marker') + cm.setFrame(-halfSize, halfSize, -halfSize, halfSize) + markerNP = self.maskedLayer.attachNewNode(cm.generate()) + markerNP.setColor(*color) + return markerNP + + def tile2gui(self, x, y): + y = self._mazeHeight - y + cellWidth = self._maskResolution / self._mazeWidth + cellHeight = self._maskResolution / self._mazeHeight + ax = float(x) / self._mazeWidth * self._maskResolution + ax += cellWidth + ay = float(y) / self._mazeHeight * self._maskResolution + ay += cellHeight + return (ax, ay) + + def gui2pos(self, x, y): + return (x / self._maskResolution * 2.0 - 0.97, 0, y / self._maskResolution * -2.0 + 1.02) + + def _getToonMarker(self, toon): + hType = toon.style.getType() + if hType == 'rabbit': + hType = 'bunny' + return self._laffMeterModel.find('**/' + hType + 'head') + + def addToon(self, toon, tX, tY): + 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(0.07) + else: + marker.setScale(0.05) + marker.flattenStrong() + marker.setPos(*self.gui2pos(*self.tile2gui(tX, tY))) + self._toon2marker[toon] = marker + + def removeToon(self, toon): + if not self._toon2marker.has_key(toon): + return + self._toon2marker[toon].removeNode() + del self._toon2marker[toon] + + def updateToon(self, toon, tX, tY): + if not self._toon2marker.has_key(toon): + return + x, y = self.tile2gui(tX, tY) + self._toon2marker[toon].setPos(*self.gui2pos(x, y)) + if tY < 0 or tY >= len(self._revealedCells): + self.notify.warning('updateToon earlying out:') + self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) + self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),)) + if len(self._revealedCells) > 0: + self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),)) + return + if tX < 0 or tX >= len(self._revealedCells[tY]): + self.notify.warning('updateToon earlying out:') + self.notify.warning('(tX, tY): (%s, %s)' % (tX, tY)) + self.notify.warning('len(_revealedCells): %s' % (len(self._revealedCells),)) + if tY < len(self._revealedCells): + self.notify.warning('len(_revealedCells[tY]): %s' % (len(self._revealedCells[tY]),)) + elif len(self._revealedCells) > 0: + self.notify.warning('len(_revealedCells[0]): %s' % (len(self._revealedCells[0]),)) + return + if not self._revealedCells[tY][tX]: + self._drawHole(x, y) + self._revealedCells[tY][tX] = True + + def revealCell(self, x, y): + ax, ay = self.tile2gui(x, y) + if not self._revealedCells[y][x]: + self._drawHole(ax, ay) + self._revealedCells[y][x] = True + + def revealAll(self): + for x in range(self._maskResolution): + for y in range(self._maskResolution): + self._maskImage.setXelA(x, y, 0, 0, 0, 0) + + self.revealCell(0, 0) + + def reset(self): + for x in range(self._maskResolution): + for y in range(self._maskResolution): + self._maskImage.setXelA(x, y, 0, 0, 0, 1) diff --git a/toontown/minigame/MazeSuit.py b/toontown/minigame/MazeSuit.py new file mode 100644 index 0000000..cfb768e --- /dev/null +++ b/toontown/minigame/MazeSuit.py @@ -0,0 +1,260 @@ +from direct.showbase.DirectObject import DirectObject +from direct.interval.MetaInterval import Parallel +from direct.interval.LerpInterval import LerpPosInterval, LerpHprInterval +from direct.showbase.RandomNumGen import RandomNumGen +from pandac.PandaModules import Point3, WaitInterval +from pandac.PandaModules import CollisionSphere, CollisionNode +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.toonbase import ToontownGlobals +import MazeGameGlobals + +class MazeSuit(DirectObject): + COLL_SPHERE_NAME = 'MazeSuitSphere' + COLLISION_EVENT_NAME = 'MazeSuitCollision' + MOVE_IVAL_NAME = 'moveMazeSuit' + DIR_UP = 0 + DIR_DOWN = 1 + DIR_LEFT = 2 + DIR_RIGHT = 3 + oppositeDirections = [DIR_DOWN, + DIR_UP, + DIR_RIGHT, + DIR_LEFT] + directionHs = [0, + 180, + 90, + 270] + DEFAULT_SPEED = 4.0 + SUIT_Z = 0.1 + + def __init__(self, serialNum, maze, randomNumGen, cellWalkPeriod, difficulty, suitDnaName = 'f', startTile = None, ticFreq = MazeGameGlobals.SUIT_TIC_FREQ, walkSameDirectionProb = MazeGameGlobals.WALK_SAME_DIRECTION_PROB, walkTurnAroundProb = MazeGameGlobals.WALK_TURN_AROUND_PROB, uniqueRandomNumGen = True, walkAnimName = None): + self.serialNum = serialNum + self.maze = maze + if uniqueRandomNumGen: + self.rng = RandomNumGen(randomNumGen) + else: + self.rng = randomNumGen + self.difficulty = difficulty + self._walkSameDirectionProb = walkSameDirectionProb + self._walkTurnAroundProb = walkTurnAroundProb + self._walkAnimName = walkAnimName or 'walk' + self.suit = Suit.Suit() + d = SuitDNA.SuitDNA() + d.newSuit(suitDnaName) + self.suit.setDNA(d) + if startTile is None: + defaultStartPos = MazeGameGlobals.SUIT_START_POSITIONS[self.serialNum] + self.startTile = (defaultStartPos[0] * self.maze.width, defaultStartPos[1] * self.maze.height) + else: + self.startTile = startTile + self.ticFreq = ticFreq + self.ticPeriod = int(cellWalkPeriod) + self.cellWalkDuration = float(self.ticPeriod) / float(self.ticFreq) + self.turnDuration = 0.6 * self.cellWalkDuration + return + + def destroy(self): + self.suit.delete() + + def uniqueName(self, str): + return str + `(self.serialNum)` + + def gameStart(self, gameStartTime): + self.gameStartTime = gameStartTime + self.initCollisions() + self.startWalkAnim() + self.occupiedTiles = [(self.nextTX, self.nextTY)] + n = 20 + self.nextThinkTic = self.serialNum * self.ticFreq / n + self.fromPos = Point3(0, 0, 0) + self.toPos = Point3(0, 0, 0) + self.fromHpr = Point3(0, 0, 0) + self.toHpr = Point3(0, 0, 0) + self.moveIval = WaitInterval(1.0) + + def gameEnd(self): + self.moveIval.pause() + del self.moveIval + self.shutdownCollisions() + self.suit.loop('neutral') + + def initCollisions(self): + self.collSphere = CollisionSphere(0, 0, 0, 2.0) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName(self.COLL_SPHERE_NAME)) + self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.suit.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept(self.uniqueName('enter' + self.COLL_SPHERE_NAME), self.handleEnterSphere) + + def shutdownCollisions(self): + self.ignore(self.uniqueName('enter' + self.COLL_SPHERE_NAME)) + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def handleEnterSphere(self, collEntry): + messenger.send(self.COLLISION_EVENT_NAME, [self.serialNum]) + + def __getWorldPos(self, sTX, sTY): + wx, wy = self.maze.tile2world(sTX, sTY) + return Point3(wx, wy, self.SUIT_Z) + + def onstage(self): + sTX = int(self.startTile[0]) + sTY = int(self.startTile[1]) + c = 0 + lim = 0 + toggle = 0 + direction = 0 + while not self.maze.isWalkable(sTX, sTY): + if 0 == direction: + sTX -= 1 + elif 1 == direction: + sTY -= 1 + elif 2 == direction: + sTX += 1 + elif 3 == direction: + sTY += 1 + c += 1 + if c > lim: + c = 0 + direction = (direction + 1) % 4 + toggle += 1 + if not toggle & 1: + lim += 1 + + self.TX = sTX + self.TY = sTY + self.direction = self.DIR_DOWN + self.lastDirection = self.direction + self.nextTX = self.TX + self.nextTY = self.TY + self.suit.setPos(self.__getWorldPos(self.TX, self.TY)) + self.suit.setHpr(self.directionHs[self.direction], 0, 0) + self.suit.reparentTo(render) + self.suit.pose(self._walkAnimName, 0) + self.suit.loop('neutral') + + def offstage(self): + self.suit.reparentTo(hidden) + + def startWalkAnim(self): + self.suit.loop(self._walkAnimName) + speed = float(self.maze.cellWidth) / self.cellWalkDuration + self.suit.setPlayRate(speed / self.DEFAULT_SPEED, self._walkAnimName) + + def __applyDirection(self, dir, TX, TY): + if self.DIR_UP == dir: + TY += 1 + elif self.DIR_DOWN == dir: + TY -= 1 + elif self.DIR_LEFT == dir: + TX -= 1 + elif self.DIR_RIGHT == dir: + TX += 1 + return (TX, TY) + + def __chooseNewWalkDirection(self, unwalkables): + if not self.rng.randrange(self._walkSameDirectionProb): + newTX, newTY = self.__applyDirection(self.direction, self.TX, self.TY) + if self.maze.isWalkable(newTX, newTY, unwalkables): + return self.direction + if self.difficulty >= 0.5: + if not self.rng.randrange(self._walkTurnAroundProb): + oppositeDir = self.oppositeDirections[self.direction] + newTX, newTY = self.__applyDirection(oppositeDir, self.TX, self.TY) + if self.maze.isWalkable(newTX, newTY, unwalkables): + return oppositeDir + candidateDirs = [self.DIR_UP, + self.DIR_DOWN, + self.DIR_LEFT, + self.DIR_RIGHT] + candidateDirs.remove(self.oppositeDirections[self.direction]) + while len(candidateDirs): + dir = self.rng.choice(candidateDirs) + newTX, newTY = self.__applyDirection(dir, self.TX, self.TY) + if self.maze.isWalkable(newTX, newTY, unwalkables): + return dir + candidateDirs.remove(dir) + + return self.oppositeDirections[self.direction] + + def getThinkTimestampTics(self, curTic): + if curTic < self.nextThinkTic: + return [] + else: + r = range(self.nextThinkTic, curTic + 1, self.ticPeriod) + self.lastTicBeforeRender = r[-1] + return r + + def prepareToThink(self): + self.occupiedTiles = [(self.nextTX, self.nextTY)] + + def think(self, curTic, curT, unwalkables): + self.TX = self.nextTX + self.TY = self.nextTY + self.lastDirection = self.direction + self.direction = self.__chooseNewWalkDirection(unwalkables) + self.nextTX, self.nextTY = self.__applyDirection(self.direction, self.TX, self.TY) + self.occupiedTiles = [(self.TX, self.TY), (self.nextTX, self.nextTY)] + if curTic == self.lastTicBeforeRender: + fromCoords = self.maze.tile2world(self.TX, self.TY) + toCoords = self.maze.tile2world(self.nextTX, self.nextTY) + self.fromPos.set(fromCoords[0], fromCoords[1], self.SUIT_Z) + self.toPos.set(toCoords[0], toCoords[1], self.SUIT_Z) + self.moveIval = LerpPosInterval(self.suit, self.cellWalkDuration, self.toPos, startPos=self.fromPos, name=self.uniqueName(self.MOVE_IVAL_NAME)) + if self.direction != self.lastDirection: + self.fromH = self.directionHs[self.lastDirection] + toH = self.directionHs[self.direction] + if self.fromH == 270 and toH == 0: + self.fromH = -90 + elif self.fromH == 0 and toH == 270: + self.fromH = 360 + self.fromHpr.set(self.fromH, 0, 0) + self.toHpr.set(toH, 0, 0) + turnIval = LerpHprInterval(self.suit, self.turnDuration, self.toHpr, startHpr=self.fromHpr, name=self.uniqueName('turnMazeSuit')) + self.moveIval = Parallel(self.moveIval, turnIval, name=self.uniqueName(self.MOVE_IVAL_NAME)) + else: + self.suit.setH(self.directionHs[self.direction]) + moveStartT = float(self.nextThinkTic) / float(self.ticFreq) + self.moveIval.start(curT - (moveStartT + self.gameStartTime)) + self.nextThinkTic += self.ticPeriod + + @staticmethod + def thinkSuits(suitList, startTime, ticFreq = MazeGameGlobals.SUIT_TIC_FREQ): + curT = globalClock.getFrameTime() - startTime + curTic = int(curT * float(ticFreq)) + suitUpdates = [] + for i in xrange(len(suitList)): + updateTics = suitList[i].getThinkTimestampTics(curTic) + suitUpdates.extend(zip(updateTics, [i] * len(updateTics))) + + suitUpdates.sort(lambda a, b: a[0] - b[0]) + if len(suitUpdates) > 0: + curTic = 0 + for i in xrange(len(suitUpdates)): + update = suitUpdates[i] + tic = update[0] + suitIndex = update[1] + suit = suitList[suitIndex] + if tic > curTic: + curTic = tic + j = i + 1 + while j < len(suitUpdates): + if suitUpdates[j][0] > tic: + break + suitList[suitUpdates[j][1]].prepareToThink() + j += 1 + + unwalkables = [] + for si in xrange(suitIndex): + unwalkables.extend(suitList[si].occupiedTiles) + + for si in xrange(suitIndex + 1, len(suitList)): + unwalkables.extend(suitList[si].occupiedTiles) + + suit.think(curTic, curT, unwalkables) diff --git a/toontown/minigame/MazeTreasure.py b/toontown/minigame/MazeTreasure.py new file mode 100644 index 0000000..8d8aa99 --- /dev/null +++ b/toontown/minigame/MazeTreasure.py @@ -0,0 +1,38 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal + +class MazeTreasure(DirectObject): + RADIUS = 0.7 + + def __init__(self, model, pos, serialNum, gameId): + self.serialNum = serialNum + self.nodePath = model.copyTo(render) + self.nodePath.setPos(pos[0], pos[1], 1.0) + self.sphereName = 'treasureSphere%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(0, 0, 0, self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.nodePath.flattenLight() + + def destroy(self): + self.ignoreAll() + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def __handleEnterSphere(self, collEntry): + self.ignoreAll() + messenger.send('MazeTreasureGrabbed', [self.serialNum]) + + def showGrab(self): + self.nodePath.reparentTo(hidden) + self.collNode.setIntoCollideMask(BitMask32(0)) diff --git a/toontown/minigame/MinigameAvatarScorePanel.py b/toontown/minigame/MinigameAvatarScorePanel.py new file mode 100644 index 0000000..f609d18 --- /dev/null +++ b/toontown/minigame/MinigameAvatarScorePanel.py @@ -0,0 +1,46 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toon import LaffMeter + +class MinigameAvatarScorePanel(DirectFrame): + + def __init__(self, avId, avName): + self.avId = avId + if base.cr.doId2do.has_key(self.avId): + self.avatar = base.cr.doId2do[self.avId] + else: + self.avatar = None + DirectFrame.__init__(self, relief=None, image_color=GlobalDialogColor, image_scale=(0.4, 1.0, 0.24), image_pos=(0.0, 0.1, 0.0)) + self['image'] = DGG.getDefaultDialogGeom() + self.scoreText = DirectLabel(self, relief=None, text='0', text_scale=TTLocalizer.MASPscoreText, pos=(0.1, 0.0, -0.09)) + if self.avatar: + self.laffMeter = LaffMeter.LaffMeter(self.avatar.style, self.avatar.hp, self.avatar.maxHp) + self.laffMeter.reparentTo(self) + self.laffMeter.setPos(-0.085, 0, -0.035) + self.laffMeter.setScale(0.05) + self.laffMeter.start() + else: + self.laffMeter = None + self.nameText = DirectLabel(self, relief=None, text=avName, text_scale=TTLocalizer.MASPnameText, text_pos=(0.0, 0.06), text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.show() + return + + def cleanup(self): + if self.laffMeter: + self.laffMeter.destroy() + del self.laffMeter + del self.scoreText + del self.nameText + self.destroy() + + def setScore(self, score): + self.scoreText['text'] = str(score) + + def getScore(self): + return int(self.scoreText['text']) + + def makeTransparent(self, alpha): + self.setTransparency(1) + self.setColorScale(1, 1, 1, alpha) diff --git a/toontown/minigame/MinigameCreatorAI.py b/toontown/minigame/MinigameCreatorAI.py new file mode 100644 index 0000000..c5bc3e1 --- /dev/null +++ b/toontown/minigame/MinigameCreatorAI.py @@ -0,0 +1,186 @@ +import copy +import random +import time +from toontown.toonbase import ToontownGlobals +import DistributedMinigameTemplateAI +import DistributedRaceGameAI +import DistributedCannonGameAI +import DistributedTagGameAI +import DistributedPatternGameAI +import DistributedRingGameAI +import DistributedMazeGameAI +import DistributedTugOfWarGameAI +import DistributedCatchGameAI +import DistributedDivingGameAI +import DistributedTargetGameAI +import DistributedPairingGameAI +import DistributedPhotoGameAI +import DistributedVineGameAI +import DistributedIceGameAI +import DistributedCogThiefGameAI +import DistributedTwoDGameAI +import DistributedTravelGameAI +import TravelGameGlobals +ALLOW_TEMP_MINIGAMES = simbase.config.GetBool('allow-temp-minigames', False) +if ALLOW_TEMP_MINIGAMES: + from toontown.minigame.TempMinigameAI import * +simbase.forcedMinigameId = simbase.config.GetInt('minigame-id', 0) +RequestMinigame = {} +MinigameZoneRefs = {} + +def createMinigame(air, playerArray, trolleyZone, minigameZone = None, previousGameId = ToontownGlobals.NoPreviousGameId, newbieIds = [], startingVotes = None, metagameRound = -1, desiredNextGame = None): + if minigameZone == None: + minigameZone = air.allocateZone() + acquireMinigameZone(minigameZone) + mgId = None + mgDiff = None + mgSzId = None + for avId in playerArray: + request = RequestMinigame.get(avId) + if request != None: + mgId, mgKeep, mgDiff, mgSzId = request + if not mgKeep: + del RequestMinigame[avId] + break + + if mgId != None: + pass + elif simbase.forcedMinigameId: + mgId = simbase.forcedMinigameId + else: + randomList = list(copy.copy(ToontownGlobals.MinigamePlayerMatrix[len(playerArray)])) + if simbase.air.useAllMinigames and len(playerArray) > 1: + randomList = list(copy.copy(ToontownGlobals.MinigameIDs)) + for gameId in [ToontownGlobals.TravelGameId]: + if gameId in randomList: + randomList.remove(gameId) + + for gameId in [ToontownGlobals.TravelGameId]: + if gameId in randomList: + randomList.remove(gameId) + + if previousGameId != ToontownGlobals.NoPreviousGameId: + if randomList.count(previousGameId) != 0: + randomList.remove(previousGameId) + randomList = removeUnreleasedMinigames(randomList, True) + mgId = random.choice(randomList) + if metagameRound > -1: + if metagameRound % 2 == 0: + mgId = ToontownGlobals.TravelGameId + elif desiredNextGame: + mgId = desiredNextGame + mgCtors = {ToontownGlobals.RaceGameId: DistributedRaceGameAI.DistributedRaceGameAI, + ToontownGlobals.CannonGameId: DistributedCannonGameAI.DistributedCannonGameAI, + ToontownGlobals.TagGameId: DistributedTagGameAI.DistributedTagGameAI, + ToontownGlobals.PatternGameId: DistributedPatternGameAI.DistributedPatternGameAI, + ToontownGlobals.RingGameId: DistributedRingGameAI.DistributedRingGameAI, + ToontownGlobals.MazeGameId: DistributedMazeGameAI.DistributedMazeGameAI, + ToontownGlobals.TugOfWarGameId: DistributedTugOfWarGameAI.DistributedTugOfWarGameAI, + ToontownGlobals.CatchGameId: DistributedCatchGameAI.DistributedCatchGameAI, + ToontownGlobals.DivingGameId: DistributedDivingGameAI.DistributedDivingGameAI, + ToontownGlobals.TargetGameId: DistributedTargetGameAI.DistributedTargetGameAI, + ToontownGlobals.MinigameTemplateId: DistributedMinigameTemplateAI.DistributedMinigameTemplateAI, + ToontownGlobals.PairingGameId: DistributedPairingGameAI.DistributedPairingGameAI, + ToontownGlobals.VineGameId: DistributedVineGameAI.DistributedVineGameAI, + ToontownGlobals.IceGameId: DistributedIceGameAI.DistributedIceGameAI, + ToontownGlobals.CogThiefGameId: DistributedCogThiefGameAI.DistributedCogThiefGameAI, + ToontownGlobals.TwoDGameId: DistributedTwoDGameAI.DistributedTwoDGameAI, + ToontownGlobals.TravelGameId: DistributedTravelGameAI.DistributedTravelGameAI, + ToontownGlobals.PhotoGameId: DistributedPhotoGameAI.DistributedPhotoGameAI} + if ALLOW_TEMP_MINIGAMES: + from TempMinigameAI import TempMgCtors + for key, value in TempMgCtors.items(): + mgCtors[key] = value + + try: + mg = mgCtors[mgId](air, mgId) + except KeyError: + raise Exception, 'unknown minigame ID: %s' % mgId + + mg.setExpectedAvatars(playerArray) + mg.setNewbieIds(newbieIds) + mg.setTrolleyZone(trolleyZone) + mg.setDifficultyOverrides(mgDiff, mgSzId) + if startingVotes == None: + for avId in playerArray: + mg.setStartingVote(avId, TravelGameGlobals.DefaultStartingVotes) + + else: + for index in range(len(startingVotes)): + avId = playerArray[index] + votes = startingVotes[index] + if votes < 0: + print 'createMinigame negative votes, avId=%s votes=%s' % (avId, votes) + votes = 0 + mg.setStartingVote(avId, votes) + + mg.setMetagameRound(metagameRound) + mg.generateWithRequired(minigameZone) + toons = [] + for id in playerArray: + toon = simbase.air.doId2do.get(id) + if toon != None: + toons.append(toon) + + for toon in toons: + simbase.air.questManager.toonPlayedMinigame(toon, toons) + + retVal = {} + retVal['minigameZone'] = minigameZone + retVal['minigameId'] = mgId + return retVal + + +def acquireMinigameZone(zoneId): + if zoneId not in MinigameZoneRefs: + MinigameZoneRefs[zoneId] = 0 + MinigameZoneRefs[zoneId] += 1 + + +def releaseMinigameZone(zoneId): + MinigameZoneRefs[zoneId] -= 1 + if MinigameZoneRefs[zoneId] <= 0: + del MinigameZoneRefs[zoneId] + simbase.air.deallocateZone(zoneId) + + +def removeUnreleasedMinigames(startList, increaseChanceOfNewGames = 0): + randomList = startList[:] + for gameId in ToontownGlobals.MinigameReleaseDates: + dateTuple = ToontownGlobals.MinigameReleaseDates[gameId] + currentTime = time.time() + releaseTime = time.mktime((dateTuple[0], + dateTuple[1], + dateTuple[2], + 0, + 0, + 0, + 0, + 0, + -1)) + releaseTimePlus1Week = releaseTime + 7 * 24 * 60 * 60 + if currentTime < releaseTime: + if gameId in randomList: + doRemove = True + if gameId == ToontownGlobals.CogThiefGameId and simbase.air.config.GetBool('force-allow-thief-game', 0): + doRemove = False + if increaseChanceOfNewGames: + randomList += [gameId] * 4 + elif gameId == ToontownGlobals.IceGameId and simbase.air.config.GetBool('force-allow-ice-game', 0): + doRemove = False + if increaseChanceOfNewGames: + randomList += [gameId] * 4 + elif gameId == ToontownGlobals.TwoDGameId and simbase.air.config.GetBool('force-allow-2d-game', 0): + doRemove = False + if increaseChanceOfNewGames: + randomList += [gameId] * 4 + elif gameId == ToontownGlobals.PhotoGameId and simbase.air.config.GetBool('force-allow-photo-game', 0): + doRemove = False + if increaseChanceOfNewGames: + randomList += [gameId] * 4 + if doRemove: + randomList.remove(gameId) + if releaseTime < currentTime and currentTime < releaseTimePlus1Week and gameId in randomList and increaseChanceOfNewGames: + randomList += [gameId] * 4 + + return randomList diff --git a/toontown/minigame/MinigameGlobals.py b/toontown/minigame/MinigameGlobals.py new file mode 100644 index 0000000..ce86888 --- /dev/null +++ b/toontown/minigame/MinigameGlobals.py @@ -0,0 +1,35 @@ +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil +latencyTolerance = 10.0 +MaxLoadTime = 40.0 +rulesDuration = 16 +JellybeanTrolleyHolidayScoreMultiplier = 2 +DifficultyOverrideMult = int(1 << 16) + +def QuantizeDifficultyOverride(diffOverride): + return int(round(diffOverride * DifficultyOverrideMult)) / float(DifficultyOverrideMult) + + +NoDifficultyOverride = 2147483647 +NoTrolleyZoneOverride = -1 +SafeZones = [ToontownGlobals.ToontownCentral, + ToontownGlobals.DonaldsDock, + ToontownGlobals.DaisyGardens, + ToontownGlobals.MinniesMelodyland, + ToontownGlobals.TheBrrrgh, + ToontownGlobals.DonaldsDreamland] + +def getDifficulty(trolleyZone): + hoodZone = getSafezoneId(trolleyZone) + return float(SafeZones.index(hoodZone)) / (len(SafeZones) - 1) + + +def getSafezoneId(trolleyZone): + return ZoneUtil.getCanonicalHoodId(trolleyZone) + + +def getScoreMult(trolleyZone): + szId = getSafezoneId(trolleyZone) + multiplier = PythonUtil.lerp(1.0, 1.5, float(SafeZones.index(szId)) / (len(SafeZones) - 1)) + return multiplier diff --git a/toontown/minigame/MinigamePhysicsWorldBase.py b/toontown/minigame/MinigamePhysicsWorldBase.py new file mode 100644 index 0000000..ccd41e7 --- /dev/null +++ b/toontown/minigame/MinigamePhysicsWorldBase.py @@ -0,0 +1,166 @@ +from pandac.PandaModules import Quat +from pandac.PandaModules import OdeWorld, OdeSimpleSpace, OdeJointGroup, OdeUtil +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta + +class MinigamePhysicsWorldBase: + notify = DirectNotifyGlobal.directNotify.newCategory('MinigamePhysicsWorldBase') + + def __init__(self, canRender = 0): + self.canRender = canRender + self.world = OdeWorld() + self.space = OdeSimpleSpace() + self.contactgroup = OdeJointGroup() + self.bodyList = [] + self.geomList = [] + self.massList = [] + self.rayList = [] + self.showContacts = 0 + self.jointMarkers = [] + self.jointMarkerCount = 64 + self.meshDataList = [] + self.geomDataList = [] + self.commonObjectInfoDict = {} + self.maxColCount = 0 + if self.canRender: + self.odePandaRelationList = self.bodyList + self.root = render.attachNewNode('physics root node') + else: + self.root = NodePath('physics root node') + self.placerNode = self.root.attachNewNode('Placer') + self.subPlacerNode = self.placerNode.attachNewNode('Placer Sub Node') + self.commonObjectDict = {} + self.commonId = 0 + self.worldAttach = self.root.attachNewNode('physics geom attach point') + self.timingCycleLength = 10.0 + self.timingCycleOffset = 0.0 + self.timingSimTime = 0.0 + self.FPS = 60.0 + self.DTAStep = 1.0 / self.FPS + self.DTA = 0 + self.useQuickStep = False + self.deterministic = True + self.numStepsInSimulateTask = 0 + + def delete(self): + self.notify.debug('Max Collision Count was %s' % self.maxColCount) + self.stopSim() + self.commonObjectDict = None + if self.canRender: + for pair in self.odePandaRelationList: + pair[0].remove() + pair[1].destroy() + + self.odePandaRelationList = None + else: + for body in self.bodyList: + body[1].destroy() + + self.bodyList = None + for mass in self.massList: + mass = None + + for geom in self.geomList: + geom.destroy() + geom = None + + for ray in self.rayList: + ray.destroy() + ray = None + + self.placerNode.remove() + self.root.remove() + for marker in self.jointMarkers: + marker.remove() + + self.jointMarkers = None + for data in self.geomDataList: + data.destroy() + + for data in self.meshDataList: + data.destroy() + + self.contactgroup.empty() + self.world.destroy() + self.space.destroy() + self.world = None + self.space = None + return + + def setupSimulation(self): + if self.canRender: + for count in range(self.jointMarkerCount): + testMarker = render.attachNewNode('Joint Marker') + ballmodel = loader.loadModel('phase_3/models/misc/sphere') + ballmodel.reparentTo(testMarker) + ballmodel.setScale(0.1) + testMarker.setPos(0.0, 0.0, -100.0) + self.jointMarkers.append(testMarker) + + def startSim(self): + taskMgr.add(self.__simulationTask, 'simulation task') + + def stopSim(self): + taskMgr.remove('simulation task') + + def __simulationTask(self, task): + self.DTA += globalClock.getDt() + numSteps = int(self.DTA / self.DTAStep) + if numSteps > 10: + self.notify.warning('phyics steps = %d' % numSteps) + startTime = globalClock.getRealTime() + while self.DTA >= self.DTAStep: + if self.deterministic: + OdeUtil.randSetSeed(0) + self.DTA -= self.DTAStep + self.preStep() + self.simulate() + self.postStep() + + if self.canRender: + self.placeBodies() + return task.cont + + def preStep(self): + pass + + def postStep(self): + if self.showContacts and self.canRender: + for count in range(self.jointMarkerCount): + pandaNodePathGeom = self.jointMarkers[count] + if count < self.colCount: + pandaNodePathGeom.setPos(self.space.getContactData(count * 3 + 0), self.space.getContactData(count * 3 + 1), self.space.getContactData(count * 3 + 2)) + else: + pandaNodePathGeom.setPos(0.0, 0.0, -100.0) + + def simulate(self): + self.colCount = self.space.autoCollide() + if self.maxColCount < self.colCount: + self.maxColCount = self.colCount + self.notify.debug('New Max Collision Count %s' % self.maxColCount) + if self.useQuickStep: + self.world.quickStep(self.DTAStep) + else: + self.world.step(self.DTAStep) + for bodyPair in self.bodyList: + self.world.applyDampening(self.DTAStep, bodyPair[1]) + + self.contactgroup.empty() + self.timingSimTime = self.timingSimTime + self.DTAStep + + def placeBodies(self): + for pair in self.odePandaRelationList: + pandaNodePathGeom = pair[0] + odeBody = pair[1] + if pandaNodePathGeom: + pandaNodePathGeom.setPos(odeBody.getPosition()) + pandaNodePathGeom.setQuat(Quat(odeBody.getQuaternion()[0], odeBody.getQuaternion()[1], odeBody.getQuaternion()[2], odeBody.getQuaternion()[3])) + + def getOrderedContacts(self, count): + c0 = self.space.getContactId(count, 0) + c1 = self.space.getContactId(count, 1) + if c0 > c1: + chold = c1 + c1 = c0 + c0 = chold + return (c0, c1) diff --git a/toontown/minigame/MinigamePowerMeter.py b/toontown/minigame/MinigamePowerMeter.py new file mode 100644 index 0000000..a409e7b --- /dev/null +++ b/toontown/minigame/MinigamePowerMeter.py @@ -0,0 +1,82 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer + +class MinigamePowerMeter(DirectFrame): + + def __init__(self, size, label = None): + DirectFrame.__init__(self, relief=None, state=DGG.NORMAL, image_color=GlobalDialogColor, image_scale=(0.48, 1.0, 0.7), image_pos=(0.0, 0.1, 0.0), sortOrder=DGG.BACKGROUND_SORT_INDEX) + self['image'] = DGG.getDefaultDialogGeom() + self.resetFrameSize() + if label == None: + label = TTLocalizer.MinigamePowerMeterLabel + self.powerText = DirectLabel(self, relief=None, text=label, text_scale=TTLocalizer.MPMpowerText, pos=(0.01, 0.0, 0.29)) + self.tooSlow = DirectLabel(parent=self, relief=None, text=TTLocalizer.MinigamePowerMeterTooSlow, scale=TTLocalizer.MPMtooSlow, pos=(-.15, 0, 0.05), color=(0.1, 0.3, 0.6)) + self.tooFast = DirectLabel(parent=self, relief=None, text=TTLocalizer.MinigamePowerMeterTooFast, scale=TTLocalizer.MPMtooFast, pos=(0.15, 0, 0.05), color=(0.1, 0.3, 0.6)) + self.tooSlow.hide() + self.tooFast.hide() + self.largeGauge = [] + self.gaugeSize = size + self.__createSpeedGauge() + self.show() + return + + def cleanup(self): + del self.powerText + for gauge in self.largeGauge: + if gauge: + del gauge + + del self.largeGauge + self.destroy() + + def __createSpeedGauge(self): + gaugeA = DirectWaitBar(parent=self, relief=DGG.RAISED, range=self.gaugeSize, frameSize=(-0.6, + 0.6, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=TTLocalizer.MPMgaugeA, pos=(0, 0, 0), frameColor=(0.0, 0.0, 0.0, 0.0), barColor=(0, 1, 0, 0.6), sortOrder=DGG.FOREGROUND_SORT_INDEX) + gaugeA.setR(-90) + gaugeA['value'] = 0 + self.largeGauge.append(gaugeA) + gaugeTargetTop = DirectWaitBar(parent=self, relief=DGG.RAISED, range=self.gaugeSize, frameSize=(-0.6, + 0.6, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=TTLocalizer.MPMgaugeTargetTop, pos=(0, 0, 0), frameColor=(1, 1, 1, 1), barColor=(1, 0, 0, 1), sortOrder=DGG.BACKGROUND_SORT_INDEX + 1) + gaugeTargetTop.setR(-90) + gaugeTargetTop['value'] = 1 + self.largeGauge.append(gaugeTargetTop) + gaugeTargetBot = DirectWaitBar(parent=self, relief=DGG.RAISED, range=self.gaugeSize, frameSize=(-0.6, + 0.6, + -0.2, + 0.2), borderWidth=(0.02, 0.02), scale=TTLocalizer.MPMgaugeTargetBot, pos=(0, 0, 0), frameColor=(1, 1, 1, 0), barColor=(1, 1, 1, 1), sortOrder=DGG.BACKGROUND_SORT_INDEX + 2) + gaugeTargetBot['value'] = 0 + gaugeTargetBot.setR(-90) + self.largeGauge.append(gaugeTargetBot) + for gauge in self.largeGauge: + gauge.show() + + def setPower(self, power): + self.largeGauge[0]['value'] = power + + def setTarget(self, target): + self.largeGauge[2]['value'] = target + self.largeGauge[1]['value'] = target + 1 + + def clearTooSlowTooFast(self): + self.tooSlow.hide() + self.tooFast.hide() + + def updateTooSlowTooFast(self): + curSpeed = self.largeGauge[0]['value'] + target = self.largeGauge[2]['value'] + self.tooSlow.hide() + self.tooFast.hide() + if curSpeed < target - 2: + self.tooSlow.show() + elif curSpeed > target + 2: + self.tooFast.show() + + def setBarColor(self, color): + self.largeGauge[0]['barColor'] = color diff --git a/toontown/minigame/MinigameRulesPanel.py b/toontown/minigame/MinigameRulesPanel.py new file mode 100644 index 0000000..6854a04 --- /dev/null +++ b/toontown/minigame/MinigameRulesPanel.py @@ -0,0 +1,54 @@ +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 +import MinigameGlobals + +class MinigameRulesPanel(StateData.StateData): + + 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_4/models/gui/minigame_rules_gui') + buttonGui = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.frame = DirectFrame(image=minigameGui.find('**/minigame-rules-panel'), relief=None, pos=(0.1375, 0, -0.6667)) + self.gameTitleText = DirectLabel(parent=self.frame, text=self.gameTitle, scale=TTLocalizer.MRPgameTitleText, text_align=TextNode.ACenter, text_font=getSignFont(), text_fg=(1.0, 0.33, 0.33, 1.0), pos=TTLocalizer.MRgameTitleTextPos, 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, image=(buttonGui.find('**/InventoryButtonUp'), buttonGui.find('**/InventoryButtonDown'), buttonGui.find('**/InventoryButtonRollover')), image_color=Vec4(0, 0.9, 0.1, 1), text=TTLocalizer.MinigameRulesPanelPlay, text_fg=(1, 1, 1, 1), text_pos=(0, -0.02, 0), text_scale=TTLocalizer.MRPplayButton, pos=(1.0025, 0, -0.203), scale=1.05, command=self.playCallback) + minigameGui.removeNode() + buttonGui.removeNode() + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self.frame) + self.timer.setScale(0.4) + self.timer.setPos(0.997, 0, 0.064) + 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 + + 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') + + def playCallback(self): + messenger.send(self.doneEvent) diff --git a/toontown/minigame/OrthoDrive.py b/toontown/minigame/OrthoDrive.py new file mode 100644 index 0000000..3a26913 --- /dev/null +++ b/toontown/minigame/OrthoDrive.py @@ -0,0 +1,136 @@ +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from otp.otpbase import OTPGlobals +from toontown.toonbase.ToonBaseGlobal import * +import ArrowKeys + +class OrthoDrive: + notify = DirectNotifyGlobal.directNotify.newCategory('OrthoDrive') + TASK_NAME = 'OrthoDriveTask' + SET_ATREST_HEADING_TASK = 'setAtRestHeadingTask' + + def __init__(self, speed, maxFrameMove = None, customCollisionCallback = None, priority = 0, setHeading = 1, upHeading = 0, instantTurn = False, wantSound = False): + self.wantSound = wantSound + self.speed = speed + self.maxFrameMove = maxFrameMove + self.customCollisionCallback = customCollisionCallback + self.priority = priority + self.setHeading = setHeading + self.upHeading = upHeading + self.arrowKeys = ArrowKeys.ArrowKeys() + self.lt = base.localAvatar + self.instantTurn = instantTurn + + def destroy(self): + self.arrowKeys.destroy() + del self.arrowKeys + del self.customCollisionCallback + + def start(self): + self.notify.debug('start') + self.__placeToonHOG(self.lt.getPos()) + taskMgr.add(self.__update, OrthoDrive.TASK_NAME, priority=self.priority) + self.lastAction = None + return + + def __placeToonHOG(self, pos, h = None): + if h == None: + h = self.lt.getH() + self.lt.setPos(pos) + self.lt.setH(h) + self.lastPos = pos + self.atRestHeading = h + self.lastXVel = 0 + self.lastYVel = 0 + return + + def stop(self): + self.notify.debug('stop') + self.lt.stopSound() + taskMgr.remove(OrthoDrive.TASK_NAME) + taskMgr.remove(OrthoDrive.SET_ATREST_HEADING_TASK) + if hasattr(self, 'turnLocalToonIval'): + if self.turnLocalToonIval.isPlaying(): + self.turnLocalToonIval.pause() + del self.turnLocalToonIval + base.localAvatar.setSpeed(0, 0) + + def __update(self, task): + vel = Vec3(0, 0, 0) + xVel = 0 + yVel = 0 + if self.arrowKeys.upPressed(): + yVel += 1 + if self.arrowKeys.downPressed(): + yVel -= 1 + if self.arrowKeys.leftPressed(): + xVel -= 1 + if self.arrowKeys.rightPressed(): + xVel += 1 + vel.setX(xVel) + vel.setY(yVel) + vel.normalize() + vel *= self.speed + speed = vel.length() + action = self.lt.setSpeed(speed, 0) + if action != self.lastAction: + self.lastAction = action + if self.wantSound: + if action == OTPGlobals.WALK_INDEX or action == OTPGlobals.REVERSE_INDEX: + self.lt.walkSound() + elif action == OTPGlobals.RUN_INDEX: + self.lt.runSound() + else: + self.lt.stopSound() + if self.setHeading: + self.__handleHeading(xVel, yVel) + toonPos = self.lt.getPos() + dt = globalClock.getDt() + posOffset = vel * dt + posOffset += toonPos - self.lastPos + toonPos = self.lastPos + if self.maxFrameMove: + posOffsetLen = posOffset.length() + if posOffsetLen > self.maxFrameMove: + posOffset *= self.maxFrameMove + posOffset /= posOffsetLen + if self.customCollisionCallback: + toonPos = self.customCollisionCallback(toonPos, toonPos + posOffset) + else: + toonPos = toonPos + posOffset + self.lt.setPos(toonPos) + self.lastPos = toonPos + return Task.cont + + def __handleHeading(self, xVel, yVel): + def getHeading(xVel, yVel): + angTab = [[None, 0, 180], [-90, -45, -135], [90, 45, 135]] + return angTab[xVel][yVel] + self.upHeading + + def orientToon(angle, self = self): + startAngle = self.lt.getH() + startAngle = fitSrcAngle2Dest(startAngle, angle) + dur = 0.1 * abs(startAngle - angle) / 90 + self.turnLocalToonIval = LerpHprInterval(self.lt, dur, Point3(angle, 0, 0), startHpr=Point3(startAngle, 0, 0), name='OrthoDriveLerpHpr') + if self.instantTurn: + self.turnLocalToonIval.finish() + else: + self.turnLocalToonIval.start() + + if xVel != self.lastXVel or yVel != self.lastYVel: + taskMgr.remove(OrthoDrive.SET_ATREST_HEADING_TASK) + if not (xVel or yVel): + orientToon(self.atRestHeading) + else: + curHeading = getHeading(xVel, yVel) + if ((self.lastXVel and self.lastYVel) and not (xVel and yVel)): + def setAtRestHeading(task, self = self, angle = curHeading): + self.atRestHeading = angle + return Task.done + + taskMgr.doMethodLater(0.05, setAtRestHeading, OrthoDrive.SET_ATREST_HEADING_TASK) + else: + self.atRestHeading = curHeading + orientToon(curHeading) + self.lastXVel = xVel + self.lastYVel = yVel diff --git a/toontown/minigame/OrthoWalk.py b/toontown/minigame/OrthoWalk.py new file mode 100644 index 0000000..1ac10f0 --- /dev/null +++ b/toontown/minigame/OrthoWalk.py @@ -0,0 +1,76 @@ +from toontown.toonbase.ToonBaseGlobal import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from OrthoDrive import * +from direct.directnotify import DirectNotifyGlobal + +class OrthoWalk: + notify = DirectNotifyGlobal.directNotify.newCategory('OrthoWalk') + BROADCAST_POS_TASK = 'OrthoWalkBroadcastPos' + + def __init__(self, orthoDrive, collisions = 1, broadcast = 1, broadcastPeriod = 0.2): + self.orthoDrive = orthoDrive + self.collisions = collisions + self.broadcast = broadcast + self.broadcastPeriod = broadcastPeriod + self.priority = self.orthoDrive.priority + 1 + self.lt = base.localAvatar + + def destroy(self): + self.orthoDrive.destroy() + del self.orthoDrive + + def start(self): + self.notify.debug('OrthoWalk start') + if self.collisions: + self.initCollisions() + if self.broadcast: + self.initBroadcast() + self.orthoDrive.start() + + def stop(self): + self.notify.debug('OrthoWalk stop') + self.shutdownCollisions() + self.shutdownBroadcast() + self.orthoDrive.stop() + + def initCollisions(self): + self.notify.debug('OrthoWalk initCollisions') + lt = base.localAvatar + lt.collisionsOn() + self.__collisionsOn = 1 + + def shutdownCollisions(self): + if not hasattr(self, '_OrthoWalk__collisionsOn'): + return + del self.__collisionsOn + self.notify.debug('OrthoWalk shutdownCollisions') + lt = base.localAvatar + lt.collisionsOff() + + def initBroadcast(self): + self.notify.debug('OrthoWalk initBroadcast') + self.timeSinceLastPosBroadcast = 0.0 + self.lastPosBroadcast = self.lt.getPos() + self.lastHprBroadcast = self.lt.getHpr() + self.storeStop = 0 + lt = self.lt + lt.d_clearSmoothing() + lt.sendCurrentPosition() + taskMgr.remove(self.BROADCAST_POS_TASK) + taskMgr.add(self.doBroadcast, self.BROADCAST_POS_TASK, priority=self.priority) + + def shutdownBroadcast(self): + self.notify.debug('OrthoWalk shutdownBroadcast') + taskMgr.remove(self.BROADCAST_POS_TASK) + + def doBroadcast(self, task): + dt = globalClock.getDt() + self.timeSinceLastPosBroadcast += dt + if self.timeSinceLastPosBroadcast >= self.broadcastPeriod: + self.sendCurrentPosition() + return Task.cont + + def sendCurrentPosition(self): + self.timeSinceLastPosBroadcast -= self.broadcastPeriod + self.lt.cnode.broadcastPosHprXyh() diff --git a/toontown/minigame/PairingGameCard.py b/toontown/minigame/PairingGameCard.py new file mode 100644 index 0000000..79db1b0 --- /dev/null +++ b/toontown/minigame/PairingGameCard.py @@ -0,0 +1,94 @@ +from PlayingCard import PlayingCardNodePath +import PlayingCardGlobals +from pandac.PandaModules import NodePath, Vec3 +from direct.interval.IntervalGlobal import LerpHprInterval, Parallel, SoundInterval + +class PairingGameCard(PlayingCardNodePath): + DoIntervalDefault = True + FlipTime = 0.25 + UseDifferentCardColors = True + CardColors = [(0.933594, 0.265625, 0.28125, 1.0), + (0.550781, 0.824219, 0.324219, 1.0), + (0.347656, 0.820312, 0.953125, 1.0), + (0.460938, 0.378906, 0.824219, 1.0), + (0.710938, 0.234375, 0.4375, 1.0), + (0.285156, 0.328125, 0.726562, 1.0), + (0.242188, 0.742188, 0.515625, 1.0), + (0.96875, 0.691406, 0.699219, 1.0), + (0.996094, 0.957031, 0.597656, 1.0), + (0.992188, 0.480469, 0.167969, 1.0)] + + def __init__(self, value): + style = PlayingCardGlobals.Styles[0] + PlayingCardNodePath.__init__(self, style, value) + self.enterCallback = None + self.exitCallback = None + return + + def load(self): + oneCard = loader.loadModel('phase_4/models/minigames/garden_sign_memory') + prop = self.attachNewNode('prop') + PlayingCardGlobals.getImage(self.style, self.suit, self.rank).copyTo(prop) + prop.setScale(7) + oneCard.find('**/glow').removeNode() + cs = oneCard.find('**/collision') + for solidIndex in range(cs.node().getNumSolids()): + cs.node().modifySolid(solidIndex).setTangible(False) + + cs.node().setName('cardCollision-%d' % self.value) + sign = oneCard.find('**/sign1') + if self.UseDifferentCardColors: + index = self.rank % len(self.CardColors) + color = self.CardColors[index] + sign.setColorScale(*color) + prop.setPos(0.0, 0.0, 0.08) + prop.setP(-90) + prop.reparentTo(oneCard) + oneCard.reparentTo(self) + cardBack = oneCard.find('**/sign2') + cardBack.setColorScale(0.12, 0.35, 0.5, 1.0) + cardModel = loader.loadModel('phase_3.5/models/gui/playingCard') + logo = cardModel.find('**/logo') + logo.reparentTo(self) + logo.setScale(0.45) + logo.setP(90) + logo.setZ(0.025) + logo.setX(-0.05) + logo.setH(180) + cardModel.remove() + self.setR(0) + self.setScale(2.5) + self.flipIval = None + self.turnUpSound = base.loadSfx('phase_4/audio/sfx/MG_pairing_card_flip_face_up.mp3') + self.turnDownSound = base.loadSfx('phase_4/audio/sfx/MG_pairing_card_flip_face_down.mp3') + return + + def unload(self): + self.clearFlipIval() + self.removeNode() + del self.turnUpSound + del self.turnDownSound + + def turnUp(self, doInterval = DoIntervalDefault): + self.faceUp = 1 + if doInterval: + self.clearFlipIval() + self.flipIval = Parallel(LerpHprInterval(self, self.FlipTime, Vec3(0, 0, 0)), SoundInterval(self.turnUpSound, node=self, listenerNode=base.localAvatar, cutOff=240)) + self.flipIval.start() + else: + self.setR(0) + + def clearFlipIval(self): + if self.flipIval: + self.flipIval.finish() + self.flipIval = None + return + + def turnDown(self, doInterval = DoIntervalDefault): + self.faceUp = 0 + if doInterval: + self.clearFlipIval() + self.flipIval = Parallel(LerpHprInterval(self, self.FlipTime, Vec3(0, 0, 180)), SoundInterval(self.turnDownSound, node=self, listenerNode=base.localAvatar, cutOff=240)) + self.flipIval.start() + else: + self.setR(180) diff --git a/toontown/minigame/PairingGameGlobals.py b/toontown/minigame/PairingGameGlobals.py new file mode 100644 index 0000000..618cbda --- /dev/null +++ b/toontown/minigame/PairingGameGlobals.py @@ -0,0 +1,39 @@ +import PlayingCardDeck +EasiestGameDuration = 120 +HardestGameDuration = 90 +EndlessGame = config.GetBool('endless-pairing-game', 0) +MaxRankIndexUsed = [7, + 7, + 7, + 8, + 9] + +def createDeck(deckSeed, numPlayers): + deck = PlayingCardDeck.PlayingCardDeck() + deck.shuffleWithSeed(deckSeed) + deck.removeRanksAbove(MaxRankIndexUsed[numPlayers]) + return deck + + +def calcGameDuration(difficulty): + difference = EasiestGameDuration - HardestGameDuration + adjust = difference * difficulty + retval = EasiestGameDuration - adjust + return retval + + +def calcLowFlipModifier(matches, flips): + idealFlips = round(matches * 2 * 1.6) + if idealFlips < 2: + idealFlips = 2 + maxFlipsForBonus = idealFlips * 2 + retval = 0 + if flips < idealFlips: + retval = 1 + elif maxFlipsForBonus < flips: + retval = 0 + else: + divisor = maxFlipsForBonus - idealFlips + difference = maxFlipsForBonus - flips + retval = float(difference) / divisor + return retval diff --git a/toontown/minigame/PatternGameGlobals.py b/toontown/minigame/PatternGameGlobals.py new file mode 100644 index 0000000..22e7d27 --- /dev/null +++ b/toontown/minigame/PatternGameGlobals.py @@ -0,0 +1,8 @@ +import MinigameGlobals +INITIAL_ROUND_LENGTH = 2 +ROUND_LENGTH_INCREMENT = 2 +NUM_ROUNDS = 4 +TOONTOWN_WORK = 1 +InputTime = 10 +ClientsReadyTimeout = 5 + MinigameGlobals.latencyTolerance +InputTimeout = InputTime + MinigameGlobals.latencyTolerance diff --git a/toontown/minigame/PhotoGameBase.py b/toontown/minigame/PhotoGameBase.py new file mode 100644 index 0000000..ba8b074 --- /dev/null +++ b/toontown/minigame/PhotoGameBase.py @@ -0,0 +1,35 @@ +import PhotoGameGlobals +import random + +class PhotoGameBase: + + def __init__(self): + pass + + def load(self): + self.data = PhotoGameGlobals.AREA_DATA[self.getSafezoneId()] + + def generateAssignmentTemplates(self, numAssignments): + self.data = PhotoGameGlobals.AREA_DATA[self.getSafezoneId()] + random.seed(self.doId) + assignmentTemplates = [] + numPathes = len(self.data['PATHS']) + if numPathes == 0: + return assignmentTemplates + while len(assignmentTemplates) < numAssignments: + subjectIndex = random.choice(range(numPathes)) + pose = (None, None) + while pose[0] == None: + animSetIndex = self.data['PATHANIMREL'][subjectIndex] + pose = random.choice(self.data['ANIMATIONS'][animSetIndex] + self.data['MOVEMODES'][animSetIndex]) + + newTemplate = (subjectIndex, pose[0]) + if newTemplate not in assignmentTemplates: + assignmentTemplates.append((subjectIndex, pose[0])) + + self.notify.debug('assignment templates') + self.notify.debug(str(assignmentTemplates)) + for template in assignmentTemplates: + self.notify.debug(str(template)) + + return assignmentTemplates diff --git a/toontown/minigame/PhotoGameGlobals.py b/toontown/minigame/PhotoGameGlobals.py new file mode 100644 index 0000000..a3fba67 --- /dev/null +++ b/toontown/minigame/PhotoGameGlobals.py @@ -0,0 +1,276 @@ +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +GAME_TIME = 60 +MAX_SCORE = 23 +MIN_SCORE = 5 +NUMSTARS = 5 +ONSCREENASSIGNMENTS = 5 +PHOTO_ROTATION_MIN = -20 +PHOTO_ROTATION_MAX = 20 +PHOTO_ROTATION_VEL = 25.0 +PHOTO_ANGLE_MIN = -15 +PHOTO_ANGLE_MAX = 10 +PHOTO_ANGLE_VEL = 25.0 + +def calcScore(t): + range = MAX_SCORE - MIN_SCORE + score = MAX_SCORE - range * (float(t) / GAME_TIME) + return int(score + 0.5) + + +AREA_DATA = {} +AREA_DATA[ToontownGlobals.ToontownCentral] = {} +AREA_DATA[ToontownGlobals.ToontownCentral]['FILMCOUNT'] = 32 +AREA_DATA[ToontownGlobals.ToontownCentral]['TIME'] = 120 +AREA_DATA[ToontownGlobals.ToontownCentral]['CAMERA_INTIAL_POSTION'] = Point3(0, 50, 20) +AREA_DATA[ToontownGlobals.ToontownCentral]['DNA_TRIO'] = ('phase_4/dna/storage_TT_sz.dna', 'phase_4/dna/storage_TT.dna', 'phase_4/dna/toontown_central_sz.dna') +AREA_DATA[ToontownGlobals.ToontownCentral]['TRIPOD_OFFSET'] = Point3(0, 0, 7.0) +AREA_DATA[ToontownGlobals.ToontownCentral]['START_HPR'] = Point3(-87.8752, -0.378549, 0) +AREA_DATA[ToontownGlobals.ToontownCentral]['PATHS'] = ([Point3(10, 20, 4.025), + Point3(10, -3, 4.025), + Point3(32, -5, 4.025), + Point3(32, 12, 4.025)], + [Point3(-58.396, -51.972, -1.386), + Point3(-59.967, -66.906, 0.025), + Point3(-56.416, -77.651, 0.025), + Point3(-26.619, -74.156, 0.025), + Point3(-10.664, -73.82, 0.025), + Point3(5.421, -81.282, 2.525), + Point3(5.421, -57.383, 0.025), + Point3(-27.497, -34.525, 0.025), + Point3(-41.784, -48.023, 0.025)], + [Point3(-29.015, 36.498, 0.028), + Point3(-45.701, 42.963, -0.904), + Point3(-54.659, 65.987, 0.025), + Point3(-45.458, 81.478, 0.025), + Point3(-17.325, 83.591, 0.525), + Point3(4.169, 69.942, 1.133), + Point3(-10.625, 38.357, 0.036)], + [Point3(-84.761, -74.52, 0.034), + Point3(-101.685, -75.619, 0.525), + Point3(-113.606, -71.806, 0.525), + Point3(-111.463, -63.672, 0.525), + Point3(-102.072, -60.867, 0.525), + Point3(-94.049, -60.519, 0.035), + Point3(-66.868, -64.715, 0.025)]) +AREA_DATA[ToontownGlobals.ToontownCentral]['PATHANIMREL'] = (0, + 0, + 1, + 2) +AREA_DATA[ToontownGlobals.ToontownCentral]['ANIMATIONS'] = ([('wave', 2.0), (None, 1.0), (None, 1.0)], [('slip-forward', 2.0), (None, 1.0), (None, 1.0)], [('shrug', 2.0), (None, 1.0)]) +AREA_DATA[ToontownGlobals.ToontownCentral]['MOVEMODES'] = ([('walk', 1.0), ('run', 0.4)], [('run', 0.4)], [('walk', 1.0), ('sad-walk', 2.5)]) +AREA_DATA[ToontownGlobals.DonaldsDock] = {} +AREA_DATA[ToontownGlobals.DonaldsDock]['FILMCOUNT'] = 28 +AREA_DATA[ToontownGlobals.DonaldsDock]['TIME'] = 110 +AREA_DATA[ToontownGlobals.DonaldsDock]['CAMERA_INTIAL_POSTION'] = Point3(0, 50, 20) +AREA_DATA[ToontownGlobals.DonaldsDock]['DNA_TRIO'] = ('phase_6/dna/storage_DD_sz.dna', 'phase_6/dna/storage_DD.dna', 'phase_6/dna/donalds_dock_sz.dna') +AREA_DATA[ToontownGlobals.DonaldsDock]['TRIPOD_OFFSET'] = Point3(0, -4.0, 9.0) +AREA_DATA[ToontownGlobals.DonaldsDock]['START_HPR'] = Point3(218.211, -6.7879, 0) +AREA_DATA[ToontownGlobals.DonaldsDock]['PATHS'] = ([Point3(-115.6, 39.4, 5.692), + Point3(-109.9, -14, 5.692), + Point3(-112.652, -46.7, 5.692), + Point3(-68.9, -74.7, 5.692), + Point3(-0.2, -82.0, 5.692), + Point3(-73.5, -72.2, 5.692), + Point3(-112.0, -45, 5.692), + Point3(-110.3, 17.5, 5.692)], + [Point3(-67.143, 131.117, 3.28), + Point3(-56.4, 101.4, 3.279), + Point3(-51.425, 80.1, 3.279), + Point3(-87.632, 56.718, 2.392), + Point3(-114.1, 54.7, 3.28), + Point3(-108.5, 80.5, 3.281), + Point3(-82.672, 68.971, 3.28), + Point3(-83.122, 101.9, 3.28)], + [Point3(11.456, -82.521, 3.28), + Point3(49.735, -70.46, 3.28), + Point3(60.666, -42.735, 3.28), + Point3(40.81, -76.633, 3.28)], + [Point3(68.924, -33.681, 5.692), + Point3(60.51, -31.574, 5.692), + Point3(58.672, 12.253, 5.692), + Point3(69.809, 48.261, 5.692), + Point3(57.857, 46.134, 5.692), + Point3(66.057, -22.476, 5.692)], + [Point3(-24.439, 37.429, 0.2), + Point3(-3.549, 19.343, 0.2), + Point3(3.218, -1.475, 0.2), + Point3(-12.292, -25.318, 0.2), + Point3(-42.954, -25.706, 0.2), + Point3(-55.102, 4.041, 0.2), + Point3(-54.247, 16.051, 0.2)]) +AREA_DATA[ToontownGlobals.DonaldsDock]['PATHANIMREL'] = (0, + 0, + 1, + 2, + 3) +AREA_DATA[ToontownGlobals.DonaldsDock]['ANIMATIONS'] = ([('wave', 2.0), (None, 1.0)], + [('slip-forward', 2.0), (None, 1.0), (None, 1.0)], + [('shrug', 2.0), (None, 1.0)], + [(None, 1.0)]) +AREA_DATA[ToontownGlobals.DonaldsDock]['MOVEMODES'] = ([('walk', 1.0)], + [('run', 0.4)], + [('walk', 1.0), ('sad-walk', 2.5)], + [('swim', 1.0)]) +AREA_DATA[ToontownGlobals.DaisyGardens] = {} +AREA_DATA[ToontownGlobals.DaisyGardens]['FILMCOUNT'] = 26 +AREA_DATA[ToontownGlobals.DaisyGardens]['TIME'] = 100 +AREA_DATA[ToontownGlobals.DaisyGardens]['CAMERA_INTIAL_POSTION'] = Point3(0, 50, 20) +AREA_DATA[ToontownGlobals.DaisyGardens]['DNA_TRIO'] = ('phase_8/dna/storage_DG_sz.dna', 'phase_8/dna/storage_DG.dna', 'phase_8/dna/daisys_garden_sz.dna') +AREA_DATA[ToontownGlobals.DaisyGardens]['TRIPOD_OFFSET'] = Point3(0, 0, 6.0) +AREA_DATA[ToontownGlobals.DaisyGardens]['START_HPR'] = Point3(0.0, 0.0, 0.0) +AREA_DATA[ToontownGlobals.DaisyGardens]['PATHS'] = ([Point3(-37.252, 25.513, 0.025), + Point3(-30.032, 37.9, 0.025), + Point3(-38.694, 50.631, 0.025), + Point3(-52.1, 52.926, 0.025), + Point3(-62.807, 43.957, 0.025)], + [Point3(36.214, 117.447, 5.0), + Point3(3.534, 137.083, 5.0), + Point3(-9.384, 136.973, 5.0), + Point3(-26.377, 120.564, 5.0), + Point3(-43.268, 122.335, 5.0), + Point3(-26.377, 120.564, 5.0), + Point3(-9.384, 136.973, 5.0), + Point3(3.534, 137.083, 5.0)], + [Point3(15.675, 111.21, 0.025), + Point3(21.261, 119.429, 0.025), + Point3(6.201, 129.047, 0.025), + Point3(-10.984, 124.684, 0.025), + Point3(4.039, 130.275, 0.025), + Point3(22.645, 118.204, 0.025)], + [Point3(27.348, 16.836, 0.025), + Point3(47.152, 18.038, 0.025), + Point3(65.325, 22.322, 0.025), + Point3(73.063, 43.545, 0.025), + Point3(75.228, 66.869, 0.025), + Point3(69.468, 23.798, 0.025), + Point3(46.43, 20.473, 0.025)]) +AREA_DATA[ToontownGlobals.DaisyGardens]['PATHANIMREL'] = (0, + 0, + 1, + 2) +AREA_DATA[ToontownGlobals.DaisyGardens]['ANIMATIONS'] = ([('wave', 2.0), (None, 1.0)], [('jump', 2.0), (None, 1.0), (None, 1.0)], [('bow', 2.0), ('happy-dance', 2.0), (None, 1.0)]) +AREA_DATA[ToontownGlobals.DaisyGardens]['MOVEMODES'] = ([('walk', 1.0), ('run', 0.4)], [('run', 0.4)], [('walk', 1.0), ('run', 0.4)]) +AREA_DATA[ToontownGlobals.MinniesMelodyland] = {} +AREA_DATA[ToontownGlobals.MinniesMelodyland]['FILMCOUNT'] = 23 +AREA_DATA[ToontownGlobals.MinniesMelodyland]['TIME'] = 95 +AREA_DATA[ToontownGlobals.MinniesMelodyland]['CAMERA_INTIAL_POSTION'] = Point3(0, -50, 20) +AREA_DATA[ToontownGlobals.MinniesMelodyland]['DNA_TRIO'] = ('phase_6/dna/storage_MM_sz.dna', 'phase_6/dna/storage_MM.dna', 'phase_6/dna/minnies_melody_land_sz.dna') +AREA_DATA[ToontownGlobals.MinniesMelodyland]['TRIPOD_OFFSET'] = Point3(0, 0, 6.0) +AREA_DATA[ToontownGlobals.MinniesMelodyland]['START_HPR'] = Point3(71.3028, -3.12932, 0) +AREA_DATA[ToontownGlobals.MinniesMelodyland]['PATHS'] = ([Point3(-42.35, -16.0, -12.476), + Point3(-23.7, -49.0, -12.476), + Point3(12.4, -62.1, -12.476), + Point3(37.8, -31.163, -12.476), + Point3(29.5, 8.9, -12.476), + Point3(-11.1, 19.6, -12.476), + Point3(-34.4, -3.4, -12.476)], + [Point3(133.976, -67.686, -6.618), + Point3(126.749, -47.351, 0.404), + Point3(130.493, -33.429, 4.534), + Point3(126.239, -15.853, 5.25), + Point3(129.774, -0.535, 3.114), + Point3(130.83, 25.08, -5.688), + Point3(128.077, -5.33, 4.291), + Point3(128.399, -23.142, 5.457), + Point3(129.239, -39.886, 2.914), + Point3(133.063, -78.315, -10.756)], + [Point3(50.757, -102.705, 6.725), + Point3(-23.276, -108.546, 6.725), + Point3(-57.283, -72.081, 6.725), + Point3(-21.699, -109.808, 6.725), + Point3(19.614, -108.591, 6.725)], + [Point3(51.761, -36.129, -14.627), + Point3(26.374, -73.664, -14.645), + Point3(14.322, -89.378, -14.562), + Point3(61.594, -91.931, -14.524)]) +AREA_DATA[ToontownGlobals.MinniesMelodyland]['PATHANIMREL'] = (0, + 0, + 1, + 2) +AREA_DATA[ToontownGlobals.MinniesMelodyland]['ANIMATIONS'] = ([('wave', 2.0), (None, 1.0)], [('jump', 2.0), + ('slip-forward', 2.0), + (None, 1.0), + (None, 1.0)], [('shrug', 2.0), ('confused', 2.0), (None, 1.0)]) +AREA_DATA[ToontownGlobals.MinniesMelodyland]['MOVEMODES'] = ([('walk', 1.0), ('run', 0.4)], [('run', 0.4)], [('walk', 1.0), ('sad-walk', 2.5)]) +AREA_DATA[ToontownGlobals.TheBrrrgh] = {} +AREA_DATA[ToontownGlobals.TheBrrrgh]['FILMCOUNT'] = 21 +AREA_DATA[ToontownGlobals.TheBrrrgh]['TIME'] = 90 +AREA_DATA[ToontownGlobals.TheBrrrgh]['CAMERA_INTIAL_POSTION'] = Point3(0, 50, 20) +AREA_DATA[ToontownGlobals.TheBrrrgh]['DNA_TRIO'] = ('phase_8/dna/storage_BR_sz.dna', 'phase_8/dna/storage_BR.dna', 'phase_8/dna/the_burrrgh_sz.dna') +AREA_DATA[ToontownGlobals.TheBrrrgh]['TRIPOD_OFFSET'] = Point3(0, 0, 6.0) +AREA_DATA[ToontownGlobals.TheBrrrgh]['START_HPR'] = Point3(-49.401, -11.6266, 0) +AREA_DATA[ToontownGlobals.TheBrrrgh]['PATHS'] = ([Point3(-82.52, -28.727, 3.009), + Point3(-77.642, -4.616, 3.009), + Point3(-51.006, 1.05, 3.009), + Point3(-28.618, -28.449, 4.026), + Point3(-37.948, -64.705, 4.714)], + [Point3(-74.672, 62.15, 6.192), + Point3(-92.602, 38.734, 6.192), + Point3(-126.887, 34.757, 6.192), + Point3(-138.12, 56.002, 6.192), + Point3(-113.968, 32.661, 6.192), + Point3(-84.717, 46.837, 6.192)], + [Point3(-55.239, -126.921, 6.192), + Point3(-20.35, -108.696, 6.192), + Point3(5.028, -61.737, 6.192), + Point3(27.967, -19.683, 6.192), + Point3(11.487, 2.913, 6.192), + Point3(-23.727, -47.344, 6.192)], + [Point3(-143.22, -104.727, 6.192), + Point3(-152.989, -64.372, 6.192), + Point3(-139.328, -6.628, 4.018), + Point3(-135.815, -39.935, 3.009)]) +AREA_DATA[ToontownGlobals.TheBrrrgh]['PATHANIMREL'] = (0, + 0, + 1, + 2) +AREA_DATA[ToontownGlobals.TheBrrrgh]['ANIMATIONS'] = ([('wave', 2.0), (None, 1.0)], [('applause', 2.0), + ('slip-forward', 2.0), + (None, 1.0), + (None, 1.0)], [('shrug', 2.0), + ('confused', 2.0), + ('angry', 2.0), + (None, 1.0)]) +AREA_DATA[ToontownGlobals.TheBrrrgh]['MOVEMODES'] = ([('walk', 1.0), ('running-jump', 0.4)], [('run', 0.4)], [('walk', 1.0), ('sad-walk', 2.5)]) +AREA_DATA[ToontownGlobals.DonaldsDreamland] = {} +AREA_DATA[ToontownGlobals.DonaldsDreamland]['FILMCOUNT'] = 18 +AREA_DATA[ToontownGlobals.DonaldsDreamland]['TIME'] = 85 +AREA_DATA[ToontownGlobals.DonaldsDreamland]['CAMERA_INTIAL_POSTION'] = Point3(0, 50, 20) +AREA_DATA[ToontownGlobals.DonaldsDreamland]['DNA_TRIO'] = ('phase_8/dna/storage_DL_sz.dna', 'phase_8/dna/storage_DL.dna', 'phase_8/dna/donalds_dreamland_sz.dna') +AREA_DATA[ToontownGlobals.DonaldsDreamland]['TRIPOD_OFFSET'] = Point3(0, 0, 6.0) +AREA_DATA[ToontownGlobals.DonaldsDreamland]['START_HPR'] = Point3(-137.183, -9.06236, 0) +AREA_DATA[ToontownGlobals.DonaldsDreamland]['PATHS'] = ([Point3(-51.222, 90.874, 0.025), + Point3(0.715, 94.789, 0.025), + Point3(30.715, 94.0, 0.025), + Point3(69.181, 93.45, 0.025), + Point3(30.715, 94.0, 0.025), + Point3(-8.696, 91.799, 0.025)], + [Point3(-70.403, 29.575, 2.125), + Point3(-68.829, -21.076, 2.125), + Point3(-104.669, -24.758, 2.125), + Point3(-114.801, 21.777, 2.125)], + [Point3(15.923, -41.824, -16.089), + Point3(18.217, 34.322, -14.732), + Point3(7.087, 61.964, -16.058), + Point3(50.447, 8.17, -13.975), + Point3(-15.793, -19.916, -14.041), + Point3(-26.41, 26.786, -14.371), + Point3(20.853, 1.639, -13.975)], + [Point3(-35.391, -24.112, -14.241), + Point3(-54.364, -12.996, -13.975), + Point3(-51.578, 21.964, -14.139), + Point3(-10.49, 15.537, -13.974), + Point3(-25.728, -14.665, -13.975), + Point3(-43.923, -32.324, -14.634)]) +AREA_DATA[ToontownGlobals.DonaldsDreamland]['PATHANIMREL'] = (0, + 0, + 1, + 2) +AREA_DATA[ToontownGlobals.DonaldsDreamland]['ANIMATIONS'] = ([('wave', 2.0), (None, 1.0)], [('applause', 2.0), + ('jump', 2.0), + ('slip-forward', 2.0), + (None, 1.0), + (None, 1.0)], [('shrug', 2.0), ('angry', 2.0), (None, 1.0)]) +AREA_DATA[ToontownGlobals.DonaldsDreamland]['MOVEMODES'] = ([('walk', 1.0), ('catch-run', 0.4)], [('run', 0.4), ('running-jump', 0.4)], [('walk', 1.0), ('sad-walk', 2.5)]) diff --git a/toontown/minigame/PlayingCard.py b/toontown/minigame/PlayingCard.py new file mode 100644 index 0000000..290a528 --- /dev/null +++ b/toontown/minigame/PlayingCard.py @@ -0,0 +1,114 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.task import Task +from toontown.toonbase import TTLocalizer +import PlayingCardGlobals + +class PlayingCardBase: + + def __init__(self, value): + self.faceUp = 1 + self.setValue(value) + + def getCardName(self): + PlayingCardGlobals.getCardName(self.value) + + def getRank(self): + return self.rank + + def getSuit(self): + return self.suit + + def getValue(self): + return self.value + + def setImage(self): + pass + + def setValue(self, value): + self.value = value + if self.value == PlayingCardGlobals.Unknown: + self.suit = None + self.rank = None + self.turnDown() + else: + self.suit = value / PlayingCardGlobals.MaxRank + self.rank = value % PlayingCardGlobals.MaxRank + self.setImage() + return + + def isFaceUp(self): + return self.faceUp + + def isFaceDown(self): + return not self.faceUp + + def turnUp(self): + self.faceUp = 1 + self.setImage() + + def turnDown(self): + self.faceUp = 0 + self.setImage() + + +class PlayingCardNodePath(NodePath, PlayingCardBase): + + def __init__(self, style, value): + self.image = None + self.style = style + NodePath.__init__(self, 'PlayingCard') + PlayingCardBase.__init__(self, value) + return + + def setImage(self): + if self.faceUp: + image = PlayingCardGlobals.getImage(self.style, self.suit, self.rank) + else: + image = PlayingCardGlobals.getBack(self.style) + if self.image: + self.image.removeNode() + self.image = image.copyTo(self) + + +class PlayingCardButton(PlayingCardBase, DirectButton): + + def __init__(self, style, value): + PlayingCardBase.__init__(self, value) + self.style = style + DirectButton.__init__(self, relief=None) + self.initialiseoptions(PlayingCardButton) + self.bind(DGG.B1PRESS, self.dragStart) + self.bind(DGG.B1RELEASE, self.dragStop) + return + + def setImage(self): + if self.faceUp: + image = PlayingCardGlobals.getImage(self.style, self.suit, self.rank) + else: + image = PlayingCardGlobals.getBack(self.style) + self['image'] = image + + def dragStart(self, event): + taskMgr.remove(self.taskName('dragTask')) + vWidget2render2d = self.getPos(render2d) + vMouse2render2d = Point3(event.getMouse()[0], 0, event.getMouse()[1]) + editVec = Vec3(vWidget2render2d - vMouse2render2d) + task = taskMgr.add(self.dragTask, self.taskName('dragTask')) + task.editVec = editVec + + def dragTask(self, task): + mwn = base.mouseWatcherNode + if mwn.hasMouse(): + vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1]) + newPos = vMouse2render2d + task.editVec + self.setPos(render2d, newPos) + return Task.cont + + def dragStop(self, event): + taskMgr.remove(self.taskName('dragTask')) + messenger.send('PlayingCardDrop', sentArgs=[self]) + + def destroy(self): + taskMgr.remove(self.taskName('dragTask')) + DirectButton.destroy(self) diff --git a/toontown/minigame/PlayingCardDeck.py b/toontown/minigame/PlayingCardDeck.py new file mode 100644 index 0000000..a8bc582 --- /dev/null +++ b/toontown/minigame/PlayingCardDeck.py @@ -0,0 +1,44 @@ +import random +import PlayingCardGlobals +from toontown.minigame.PlayingCard import PlayingCardBase + +class PlayingCardDeck: + + def __init__(self): + self.shuffle() + + def shuffle(self): + self.cards = range(0, PlayingCardGlobals.MaxSuit * PlayingCardGlobals.MaxRank) + random.shuffle(self.cards) + + def shuffleWithSeed(self, seed): + generator = random.Random() + generator.seed(seed) + self.cards = range(0, PlayingCardGlobals.MaxSuit * PlayingCardGlobals.MaxRank) + generator.shuffle(self.cards) + + def dealCard(self): + return self.cards.pop(0) + + def dealCards(self, num): + cardList = [] + for i in range(num): + cardList.append(self.cards.pop(0)) + + return cardList + + def count(self): + return len(self.cards) + + def removeRanksAbove(self, maxRankInDeck): + done = False + while not done: + removedOne = False + for cardValue in self.cards: + tempCard = PlayingCardBase(cardValue) + if tempCard.rank > maxRankInDeck: + self.cards.remove(cardValue) + removedOne = True + + if not removedOne: + done = True diff --git a/toontown/minigame/PlayingCardGlobals.py b/toontown/minigame/PlayingCardGlobals.py new file mode 100644 index 0000000..acc02b2 --- /dev/null +++ b/toontown/minigame/PlayingCardGlobals.py @@ -0,0 +1,85 @@ +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from pandac.PandaModules import Vec4 +Up = 1 +Down = 0 +MaxRank = 13 +MaxSuit = 4 +Hearts = 0 +Diamonds = 1 +Clubs = 2 +Spades = 3 +Suits = [Hearts, + Diamonds, + Clubs, + Spades] +Unknown = 255 +UpColor = Vec4(1, 1, 1, 1) +RolloverColor = Vec4(1, 1, 0.5, 1) +DownColor = Vec4(1, 0.9, 0.9, 1) +DisabledColor = Vec4(1, 1, 1, 0.5) +CardColors = (UpColor, + DownColor, + RolloverColor, + DisabledColor) + +def getCardName(value): + if value == Unknown: + return TTLocalizer.PlayingCardUnknown + else: + rank = value % MaxRank + suit = value / MaxRank + return TTLocalizer.getPlayingCardName(suit, rank) + + +Styles = ['standard'] +CardImages = {} +_cardImagesInitialized = 0 +_modelPathBase = 'phase_3.5/models/gui/inventory_icons' + +def convertValueToGagTrackAndLevel(value): + imageNum = int(rank / MaxSuit) + track = imageNum % (ToontownBattleGlobals.MAX_TRACK_INDEX + 1) + level = imageNum / (ToontownBattleGlobals.MAX_TRACK_INDEX + 1) + return (track, level) + + +def convertRankToGagTrackAndLevel(rank): + track = rank % (ToontownBattleGlobals.MAX_TRACK_INDEX + 1) + level = rank / (ToontownBattleGlobals.MAX_TRACK_INDEX + 1) + return (track, level) + + +def initCardImages(): + global _cardImagesInitialized + suitCodes = ('h', 'd', 'c', 's') + rankCodes = ('02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '01') + for style in Styles: + modelPath = _modelPathBase + cardModel = loader.loadModel(modelPath) + cardModel.hide() + CardImages[style] = {} + for suitIndex in range(MaxSuit): + CardImages[style][suitIndex] = {} + for rankIndex in range(MaxRank): + track, level = convertRankToGagTrackAndLevel(rankIndex) + propName = ToontownBattleGlobals.AvPropsNew[track][level] + cardNode = cardModel.find('**/%s' % propName) + CardImages[style][suitIndex][rankIndex] = cardNode + + propName = ToontownBattleGlobals.AvPropsNew[ToontownBattleGlobals.MAX_TRACK_INDEX][ToontownBattleGlobals.MAX_LEVEL_INDEX] + CardImages[style]['back'] = cardModel.find(propName) + + _cardImagesInitialized = 1 + + +def getImage(style, suit, rank): + if _cardImagesInitialized == 0: + initCardImages() + return CardImages[style][suit][rank] + + +def getBack(style): + if _cardImagesInitialized == 0: + initCardImages() + return CardImages[style]['back'] diff --git a/toontown/minigame/Purchase.py b/toontown/minigame/Purchase.py new file mode 100644 index 0000000..800d18c --- /dev/null +++ b/toontown/minigame/Purchase.py @@ -0,0 +1,788 @@ +from PurchaseBase import * +from direct.task.Task import Task +from toontown.toon import ToonHead +from toontown.toonbase import ToontownTimer +from direct.gui import DirectGuiGlobals as DGG +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import Functor +from toontown.minigame import TravelGameGlobals +from toontown.distributed import DelayDelete +from toontown.toonbase import ToontownGlobals +import MinigameGlobals +COUNT_UP_RATE = 0.15 +COUNT_UP_DURATION = 0.5 +DELAY_BEFORE_COUNT_UP = 1.0 +DELAY_AFTER_COUNT_UP = 1.0 +COUNT_DOWN_RATE = 0.075 +COUNT_DOWN_DURATION = 0.5 +DELAY_AFTER_COUNT_DOWN = 0.0 +DELAY_AFTER_CELEBRATE = 2.6 +COUNT_SFX_MIN_DELAY = 0.034 +COUNT_SFX_START_T = 0.079 +OVERMAX_SFX_MIN_DELAY = 0.067 +OVERMAX_SFX_START_T = 0.021 + +class Purchase(PurchaseBase): + notify = DirectNotifyGlobal.directNotify.newCategory('Purchase') + + def __init__(self, toon, pointsArray, playerMoney, ids, states, remain, doneEvent, metagameRound = -1, votesArray = None): + PurchaseBase.__init__(self, toon, doneEvent) + self.ids = ids + self.pointsArray = pointsArray + self.playerMoney = playerMoney + self.states = states + self.remain = remain + self.tutorialMode = 0 + self.metagameRound = metagameRound + self.votesArray = votesArray + self.voteMultiplier = 1 + self.fsm.addState(State.State('reward', self.enterReward, self.exitReward, ['purchase'])) + doneState = self.fsm.getStateNamed('done') + doneState.addTransition('reward') + self.unexpectedEventNames = [] + self.unexpectedExits = [] + self.setupUnexpectedExitHooks() + + def load(self): + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + PurchaseBase.load(self, purchaseModels) + interiorPhase = 3.5 + self.bg = loader.loadModel('phase_%s/models/modules/toon_interior' % interiorPhase) + self.bg.setPos(0.0, 5.0, -1.0) + self.wt = self.bg.find('**/random_tc1_TI_wallpaper') + wallTex = loader.loadTexture('phase_%s/maps/wall_paper_a5.jpg' % interiorPhase) + self.wt.setTexture(wallTex, 100) + self.wt.setColorScale(0.8, 0.67, 0.549, 1.0) + self.bt = self.bg.find('**/random_tc1_TI_wallpaper_border') + wallTex = loader.loadTexture('phase_%s/maps/wall_paper_a5.jpg' % interiorPhase) + self.bt.setTexture(wallTex, 100) + self.bt.setColorScale(0.8, 0.67, 0.549, 1.0) + self.wb = self.bg.find('**/random_tc1_TI_wainscotting') + wainTex = loader.loadTexture('phase_%s/maps/wall_paper_b4.jpg' % interiorPhase) + self.wb.setTexture(wainTex, 100) + self.wb.setColorScale(0.473, 0.675, 0.488, 1.0) + self.playAgain = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.72, 0, -0.24), image=(purchaseModels.find('**/PurchScrn_BTN_UP'), + purchaseModels.find('**/PurchScrn_BTN_DN'), + purchaseModels.find('**/PurchScrn_BTN_RLVR'), + purchaseModels.find('**/PurchScrn_BTN_UP')), text=TTLocalizer.GagShopPlayAgain, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), image3_color=Vec4(0.6, 0.6, 0.6, 1), text3_fg=Vec4(0, 0, 0.4, 1), command=self.__handlePlayAgain) + self.backToPlayground = DirectButton(parent=self.frame, relief=None, scale=1.04, pos=(0.72, 0, -0.045), image=(purchaseModels.find('**/PurchScrn_BTN_UP'), + purchaseModels.find('**/PurchScrn_BTN_DN'), + purchaseModels.find('**/PurchScrn_BTN_RLVR'), + purchaseModels.find('**/PurchScrn_BTN_UP')), text=TTLocalizer.GagShopBackToPlayground, text_fg=(0, 0.1, 0.7, 1), text_scale=0.05, text_pos=(0, 0.015, 0), image3_color=Vec4(0.6, 0.6, 0.6, 1), text3_fg=Vec4(0, 0, 0.4, 1), command=self.__handleBackToPlayground) + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self.frame) + self.timer.posInTopRightCorner() + numAvs = 0 + count = 0 + localToonIndex = 0 + for index in range(len(self.ids)): + avId = self.ids[index] + if avId == base.localAvatar.doId: + localToonIndex = index + if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE: + numAvs = numAvs + 1 + + layoutList = (None, + (0,), + (0, 2), + (0, 1, 3), + (0, 1, 2, 3)) + layout = layoutList[numAvs] + headFramePosList = (Vec3(0.105, 0, -0.384), + Vec3(0.105, 0, -0.776), + Vec3(0.85, 0, -0.555), + Vec3(-0.654, 0, -0.555)) + AVID_INDEX = 0 + LAYOUT_INDEX = 1 + TOON_INDEX = 2 + self.avInfoArray = [(base.localAvatar.doId, headFramePosList[0], localToonIndex)] + pos = 1 + for index in range(len(self.ids)): + avId = self.ids[index] + if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE: + if avId != base.localAvatar.doId: + if base.cr.doId2do.has_key(avId): + self.avInfoArray.append((avId, headFramePosList[layout[pos]], index)) + pos = pos + 1 + + self.headFrames = [] + for avInfo in self.avInfoArray: + av = base.cr.doId2do.get(avInfo[AVID_INDEX]) + if av: + headFrame = PurchaseHeadFrame(av, purchaseModels) + headFrame.setAvatarState(self.states[avInfo[TOON_INDEX]]) + headFrame.setPos(avInfo[LAYOUT_INDEX]) + self.headFrames.append((avInfo[AVID_INDEX], headFrame)) + + purchaseModels.removeNode() + self.foreground = loader.loadModel('phase_3.5/models/modules/TT_A1') + self.foreground.setPos(12.5, -20, -5.5) + self.foreground.setHpr(180, 0, 0) + self.backgroundL = loader.loadModel('phase_3.5/models/modules/TT_A1') + self.backgroundL.setPos(-12.5, -25, -5) + self.backgroundL.setHpr(180, 0, 0) + self.backgroundR = self.backgroundL.copyTo(hidden) + self.backgroundR.setPos(20, -25, -5) + streets = loader.loadModel('phase_3.5/models/modules/street_modules') + sidewalk = streets.find('**/street_sidewalk_40x40') + self.sidewalk = sidewalk.copyTo(hidden) + self.sidewalk.setPos(-20, -25, -5.5) + self.sidewalk.setColor(0.9, 0.6, 0.4) + streets.removeNode() + doors = loader.loadModel('phase_4/models/modules/doors') + door = doors.find('**/door_single_square_ur_door') + self.door = door.copyTo(hidden) + self.door.setH(180) + self.door.setPos(0, -16.75, -5.5) + self.door.setScale(1.5, 1.5, 2.0) + self.door.setColor(1.0, 0.8, 0, 1) + doors.removeNode() + self.convertingVotesToBeansLabel = DirectLabel(text=TTLocalizer.TravelGameConvertingVotesToBeans, text_fg=VBase4(1, 1, 1, 1), relief=None, pos=(0.0, 0, -0.58), scale=0.075) + self.convertingVotesToBeansLabel.hide() + self.rewardDoubledJellybeanLabel = DirectLabel(text=TTLocalizer.PartyRewardDoubledJellybean, text_fg=(1.0, 0.125, 0.125, 1.0), text_shadow=(0, 0, 0, 1), relief=None, pos=(0.0, 0, -0.67), scale=0.08) + self.rewardDoubledJellybeanLabel.hide() + self.countSound = base.loadSfx('phase_3.5/audio/sfx/tick_counter.mp3') + self.overMaxSound = base.loadSfx('phase_3.5/audio/sfx/AV_collision.mp3') + self.celebrateSound = base.loadSfx('phase_4/audio/sfx/MG_win.mp3') + return + + def unload(self): + PurchaseBase.unload(self) + self.cleanupUnexpectedExitHooks() + self.bg.removeNode() + del self.bg + self.notify.debug('destroying head frames') + for headFrame in self.headFrames: + if not headFrame[1].isEmpty(): + headFrame[1].reparentTo(hidden) + headFrame[1].destroy() + + del self.headFrames + self.playAgain.destroy() + del self.playAgain + self.backToPlayground.destroy() + del self.backToPlayground + self.timer.stop() + del self.timer + for counter in self.counters: + counter.destroy() + del counter + + del self.counters + for total in self.totalCounters: + total.destroy() + del total + + del self.totalCounters + loader.unloadModel('phase_3.5/models/modules/TT_A1') + loader.unloadModel('phase_3.5/models/modules/street_modules') + loader.unloadModel('phase_4/models/modules/doors') + taskMgr.remove('countUpTask') + taskMgr.remove('countVotesUpTask') + taskMgr.remove('countDownTask') + taskMgr.remove('countVotesDownTask') + taskMgr.remove('celebrate') + taskMgr.remove('purchase-trans') + taskMgr.remove('delayAdd') + taskMgr.remove('delaySubtract') + self.foreground.removeNode() + del self.foreground + self.backgroundL.removeNode() + del self.backgroundL + self.backgroundR.removeNode() + del self.backgroundR + self.sidewalk.removeNode() + del self.sidewalk + self.door.removeNode() + del self.door + self.collisionFloor.removeNode() + del self.collisionFloor + del self.countSound + del self.celebrateSound + self.convertingVotesToBeansLabel.removeNode() + self.rewardDoubledJellybeanLabel.removeNode() + del self.convertingVotesToBeansLabel + del self.rewardDoubledJellybeanLabel + + def showStatusText(self, text): + self.statusLabel['text'] = text + taskMgr.remove('resetStatusText') + taskMgr.doMethodLater(2.0, self.resetStatusText, 'resetStatusText') + + def resetStatusText(self, task): + self.statusLabel['text'] = '' + return Task.done + + def __handlePlayAgain(self): + for headFrame in self.headFrames: + headFrame[1].wrtReparentTo(aspect2d) + + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + taskMgr.remove('resetStatusText') + taskMgr.remove('showBrokeMsgTask') + self.statusLabel['text'] = TTLocalizer.GagShopWaitingOtherPlayers + messenger.send('purchasePlayAgain') + + def handleDone(self, playAgain): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + if playAgain: + self.doneStatus = {'loader': 'minigame', + 'where': 'minigame'} + else: + self.doneStatus = {'loader': 'safeZoneLoader', + 'where': 'playground'} + messenger.send(self.doneEvent) + + def __handleBackToPlayground(self): + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + messenger.send('purchaseBackToToontown') + + def __timerExpired(self): + messenger.send('purchaseTimeout') + + def findHeadFrame(self, id): + for headFrame in self.headFrames: + if headFrame[0] == id: + return headFrame[1] + + return None + + def __handleStateChange(self, playerStates): + self.states = playerStates + for avInfo in self.avInfoArray: + index = avInfo[2] + headFrame = self.findHeadFrame(avInfo[0]) + state = self.states[index] + headFrame.setAvatarState(state) + + def enter(self): + base.playMusic(self.music, looping=1, volume=0.8) + self.fsm.request('reward') + + def enterReward(self): + numToons = 0 + toonLayouts = ((2,), + (1, 3), + (0, 2, 4), + (0, 1, 3, 4)) + toonPositions = (5.0, + 1.75, + -0.25, + -1.75, + -5.0) + self.toons = [] + self.toonsKeep = [] + self.counters = [] + self.totalCounters = [] + camera.reparentTo(render) + base.camLens.setFov(ToontownGlobals.DefaultCameraFov) + camera.setPos(0, 16.0, 2.0) + camera.lookAt(0, 0, 0.75) + base.transitions.irisIn(0.4) + self.title.reparentTo(aspect2d) + self.foreground.reparentTo(render) + self.backgroundL.reparentTo(render) + self.backgroundR.reparentTo(render) + self.sidewalk.reparentTo(render) + self.door.reparentTo(render) + size = 20 + z = -2.5 + floor = CollisionPolygon(Point3(-size, -size, z), Point3(size, -size, z), Point3(size, size, z), Point3(-size, size, z)) + floor.setTangible(1) + floorNode = CollisionNode('collision_floor') + floorNode.addSolid(floor) + self.collisionFloor = render.attachNewNode(floorNode) + NametagGlobals.setOnscreenChatForced(1) + for index in range(len(self.ids)): + avId = self.ids[index] + if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE and avId in base.cr.doId2do: + numToons += 1 + toon = base.cr.doId2do[avId] + toon.stopSmooth() + self.toons.append(toon) + self.toonsKeep.append(DelayDelete.DelayDelete(toon, 'Purchase.enterReward')) + counter = DirectLabel(parent=hidden, relief=None, pos=(0.0, 0.0, 0.0), text=str(0), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -0.1, 0), text_font=ToontownGlobals.getSignFont()) + counter['image'] = DGG.getDefaultDialogGeom() + counter['image_scale'] = (0.33, 1, 0.33) + counter.setScale(0.5) + counter.count = 0 + counter.max = self.pointsArray[index] + self.counters.append(counter) + money = self.playerMoney[index] + totalCounter = DirectLabel(parent=hidden, relief=None, pos=(0.0, 0.0, 0.0), text=str(money), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_pos=(0, -0.1, 0), text_font=ToontownGlobals.getSignFont(), image=self.jarImage) + totalCounter.setScale(0.5) + totalCounter.count = money + totalCounter.max = toon.getMaxMoney() + self.totalCounters.append(totalCounter) + + self.accept('clientCleanup', self._handleClientCleanup) + pos = 0 + toonLayout = toonLayouts[numToons - 1] + for toon in self.toons: + thisPos = toonPositions[toonLayout[pos]] + toon.setPos(Vec3(thisPos, 1.0, -2.5)) + toon.setHpr(Vec3(0, 0, 0)) + toon.setAnimState('neutral', 1) + toon.setShadowHeight(0) + if not toon.isDisabled(): + toon.reparentTo(render) + self.counters[pos].setPos(thisPos * -0.17, 0, toon.getHeight() / 10 + 0.25) + self.counters[pos].reparentTo(aspect2d) + self.totalCounters[pos].setPos(thisPos * -0.17, 0, -0.825) + self.totalCounters[pos].reparentTo(aspect2d) + pos += 1 + + self.maxPoints = max(self.pointsArray) + if self.votesArray: + self.maxVotes = max(self.votesArray) + numToons = len(self.toons) + self.voteMultiplier = TravelGameGlobals.PercentOfVotesConverted[numToons] / 100.0 + self.maxBeansFromVotes = int(self.voteMultiplier * self.maxVotes) + else: + self.maxVotes = 0 + self.maxBeansFromVotes = 0 + + def reqCountUp(state): + self.countUp() + return Task.done + + countUpDelay = DELAY_BEFORE_COUNT_UP + taskMgr.doMethodLater(countUpDelay, reqCountUp, 'countUpTask') + + def reqCountDown(state): + self.countDown() + return Task.done + + countDownDelay = countUpDelay + COUNT_UP_DURATION + DELAY_AFTER_COUNT_UP + taskMgr.doMethodLater(countDownDelay, reqCountDown, 'countDownTask') + + def celebrate(task): + for counter in task.counters: + counter.hide() + + winningPoints = max(task.pointsArray) + for i in range(len(task.ids)): + if task.pointsArray[i] == winningPoints: + avId = task.ids[i] + if base.cr.doId2do.has_key(avId): + toon = base.cr.doId2do[avId] + toon.setAnimState('jump', 1.0) + + base.playSfx(task.celebrateSound) + return Task.done + + celebrateDelay = countDownDelay + COUNT_DOWN_DURATION + DELAY_AFTER_COUNT_DOWN + celebrateTask = taskMgr.doMethodLater(celebrateDelay, celebrate, 'celebrate') + celebrateTask.counters = self.counters + celebrateTask.pointsArray = self.pointsArray + celebrateTask.ids = self.ids + celebrateTask.celebrateSound = self.celebrateSound + + def reqCountVotesUp(state): + self.countVotesUp() + return Task.done + + def reqCountVotesDown(state): + self.countVotesDown() + return Task.done + + if self.metagameRound == TravelGameGlobals.FinalMetagameRoundIndex: + countVotesUpDelay = celebrateDelay + DELAY_AFTER_CELEBRATE + taskMgr.doMethodLater(countVotesUpDelay, reqCountVotesUp, 'countVotesUpTask') + countVotesUpTime = self.maxVotes * COUNT_UP_RATE + DELAY_AFTER_COUNT_UP + countVotesDownDelay = countVotesUpDelay + countVotesUpTime + taskMgr.doMethodLater(countVotesDownDelay, reqCountVotesDown, 'countVotesDownTask') + celebrateDelay += countVotesUpTime + self.maxVotes * COUNT_DOWN_RATE + DELAY_AFTER_COUNT_DOWN + + def reqPurchase(state): + self.fsm.request('purchase') + return Task.done + + purchaseDelay = celebrateDelay + DELAY_AFTER_CELEBRATE + taskMgr.doMethodLater(purchaseDelay, reqPurchase, 'purchase-trans') + if base.skipMinigameReward: + self.fsm.request('purchase') + return + + def _changeCounterUp(self, task, counter, newCount, toonId): + counter.count = newCount + counter['text'] = str(counter.count) + if toonId == base.localAvatar.doId: + now = globalClock.getRealTime() + if task.lastSfxT + COUNT_SFX_MIN_DELAY < now: + base.playSfx(task.countSound, time=COUNT_SFX_START_T) + task.lastSfxT = now + + def _countUpTask(self, task): + now = globalClock.getRealTime() + startT = task.getStartTime() + if now >= startT + task.duration: + for counter, toonId in zip(self.counters, self.ids): + if counter.count != counter.max: + self._changeCounterUp(task, counter, counter.max, toonId) + + return Task.done + t = (now - startT) / task.duration + for counter, toonId in zip(self.counters, self.ids): + curCount = int(triglerp(0, counter.max, t)) + if curCount != counter.count: + self._changeCounterUp(task, counter, curCount, toonId) + + return Task.cont + + def countUp(self): + totalDelay = 0 + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH): + self.rewardDoubledJellybeanLabel.show() + countUpTask = taskMgr.add(self._countUpTask, 'countUp') + countUpTask.duration = COUNT_UP_DURATION + countUpTask.countSound = self.countSound + countUpTask.lastSfxT = 0 + + def _changeCounterDown(self, task, counter, newCount, total, toonId): + counter.count = newCount + counter['text'] = str(counter.count) + total.count = total.startAmount + (counter.max - newCount) + if total.count > total.max: + total.count = total.max + total['text'] = str(total.count) + if total.count == total.max: + total['text_fg'] = (1, 0, 0, 1) + if toonId == base.localAvatar.doId: + now = globalClock.getRealTime() + if total.count < total.max: + minDelay = COUNT_SFX_MIN_DELAY + snd = task.countSound + startT = COUNT_SFX_START_T + else: + minDelay = OVERMAX_SFX_MIN_DELAY + snd = task.overMaxSound + startT = OVERMAX_SFX_START_T + if task.lastSfxT + minDelay < now: + task.lastSfxT = now + base.playSfx(snd, time=startT) + + def _countDownTask(self, task): + now = globalClock.getRealTime() + startT = task.getStartTime() + if now >= startT + task.duration: + for counter, total, toonId in zip(self.counters, self.totalCounters, self.ids): + if counter.count != 0: + self._changeCounterDown(task, counter, 0, total, toonId) + + return Task.done + t = (now - startT) / task.duration + for counter, total, toonId in zip(self.counters, self.totalCounters, self.ids): + curCount = int(triglerp(counter.max, 0, t)) + if curCount != counter.count: + self._changeCounterDown(task, counter, curCount, total, toonId) + + return Task.cont + + def countDown(self): + totalDelay = 0 + for total in self.totalCounters: + total.startAmount = total.count + + countDownTask = taskMgr.add(self._countDownTask, 'countDown') + countDownTask.duration = COUNT_DOWN_DURATION + countDownTask.countSound = self.countSound + countDownTask.overMaxSound = self.overMaxSound + countDownTask.lastSfxT = 0 + + def countVotesUp(self): + totalDelay = 0 + self.convertingVotesToBeansLabel.show() + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH): + self.rewardDoubledJellybeanLabel.show() + counterIndex = 0 + for index in range(len(self.ids)): + avId = self.ids[index] + if self.states[index] != PURCHASE_NO_CLIENT_STATE and self.states[index] != PURCHASE_DISCONNECTED_STATE and avId in base.cr.doId2do: + self.counters[counterIndex].count = 0 + self.counters[counterIndex].max = self.votesArray[index] + self.counters[counterIndex].show() + counterIndex += 1 + + def delayAdd(state): + state.counter.count += 1 + state.counter['text'] = str(state.counter.count) + if state.toonId == base.localAvatar.doId: + base.playSfx(state.countSound) + return Task.done + + for count in range(0, self.maxVotes): + for counter in self.counters: + index = self.counters.index(counter) + if count < counter.max: + addTask = taskMgr.doMethodLater(totalDelay, delayAdd, 'delayAdd') + addTask.counter = counter + addTask.toonId = self.ids[index] + addTask.countSound = self.countSound + + totalDelay += COUNT_UP_RATE + + def countVotesDown(self): + totalDelay = 0 + + def delaySubtract(state): + state.counter.count -= 1 + state.counter['text'] = str(state.counter.count) + state.total.count += 1 * self.voteMultiplier + if state.total.count <= state.total.max: + state.total['text'] = str(int(state.total.count)) + if state.total.count == state.total.max + 1: + state.total['text_fg'] = (1, 0, 0, 1) + if state.toonId == base.localAvatar.doId: + if state.total.count <= state.total.max: + base.playSfx(state.countSound) + else: + base.playSfx(state.overMaxSound) + return Task.done + + for count in range(0, self.maxVotes): + for counter in self.counters: + if count < counter.max: + index = self.counters.index(counter) + subtractTask = taskMgr.doMethodLater(totalDelay, delaySubtract, 'delaySubtract') + subtractTask.counter = counter + subtractTask.total = self.totalCounters[index] + subtractTask.toonId = self.ids[index] + subtractTask.countSound = self.countSound + subtractTask.overMaxSound = self.overMaxSound + + totalDelay += COUNT_DOWN_RATE + + def exitReward(self): + self.ignore('clientCleanup') + taskMgr.remove('countUpTask') + taskMgr.remove('countVotesUpTask') + taskMgr.remove('countDownTask') + taskMgr.remove('countVotesDownTask') + taskMgr.remove('celebrate') + taskMgr.remove('purchase-trans') + taskMgr.remove('delayAdd') + taskMgr.remove('delaySubtract') + for toon in self.toons: + toon.detachNode() + + del self.toons + if hasattr(self, 'toonsKeep'): + for delayDelete in self.toonsKeep: + delayDelete.destroy() + + del self.toonsKeep + for counter in self.counters: + counter.reparentTo(hidden) + + for total in self.totalCounters: + total.reparentTo(hidden) + + self.foreground.reparentTo(hidden) + self.backgroundL.reparentTo(hidden) + self.backgroundR.reparentTo(hidden) + self.sidewalk.reparentTo(hidden) + self.door.reparentTo(hidden) + self.title.reparentTo(self.frame) + self.convertingVotesToBeansLabel.hide() + self.rewardDoubledJellybeanLabel.hide() + NametagGlobals.setOnscreenChatForced(0) + + def _handleClientCleanup(self): + if hasattr(self, 'toonsKeep'): + for delayDelete in self.toonsKeep: + delayDelete.destroy() + + del self.toonsKeep + self.ignore('clientCleanup') + + def enterPurchase(self): + PurchaseBase.enterPurchase(self) + self.convertingVotesToBeansLabel.hide() + self.rewardDoubledJellybeanLabel.hide() + self.bg.reparentTo(render) + base.setBackgroundColor(0.05, 0.14, 0.4) + self.accept('purchaseStateChange', self.__handleStateChange) + self.playAgain.reparentTo(self.toon.inventory.purchaseFrame) + self.backToPlayground.reparentTo(self.toon.inventory.purchaseFrame) + self.pointDisplay.reparentTo(self.toon.inventory.purchaseFrame) + self.statusLabel.reparentTo(self.toon.inventory.purchaseFrame) + for headFrame in self.headFrames: + headFrame[1].show() + headFrame[1].reparentTo(self.toon.inventory.purchaseFrame) + + if base.cr.periodTimerExpired: + base.cr.loginFSM.request('periodTimeout') + return + if not self.tutorialMode: + if not config.GetBool('disable-purchase-timer', 0): + self.timer.countdown(self.remain, self.__timerExpired) + if config.GetBool('metagame-disable-playAgain', 0): + if self.metagameRound > -1: + self.disablePlayAgain() + else: + self.timer.hide() + self.disablePlayAgain() + self.accept('disableGagPanel', Functor(self.toon.inventory.setActivateMode, 'gagTutDisabled', gagTutMode=1)) + self.accept('disableBackToPlayground', self.disableBackToPlayground) + self.accept('enableGagPanel', self.handleEnableGagPanel) + self.accept('enableBackToPlayground', self.enableBackToPlayground) + for avId, headFrame in self.headFrames: + if avId != self.newbieId: + headFrame.hide() + + messenger.send('gagScreenIsUp') + if base.autoPlayAgain or self.doMetagamePlayAgain(): + base.transitions.fadeOut(0) + self.__handlePlayAgain() + + def exitPurchase(self): + PurchaseBase.exitPurchase(self) + self.ignore('disableGagPanel') + self.ignore('disableBackToPlayground') + self.ignore('enableGagPanel') + self.ignore('enableBackToPlayground') + self.bg.reparentTo(hidden) + self.playAgain.reparentTo(self.frame) + self.backToPlayground.reparentTo(self.frame) + self.pointDisplay.reparentTo(self.frame) + self.statusLabel.reparentTo(self.frame) + self.ignore('purchaseStateChange') + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + if base.autoPlayAgain or self.doMetagamePlayAgain(): + base.transitions.fadeIn() + + def disableBackToPlayground(self): + self.backToPlayground['state'] = DGG.DISABLED + + def enableBackToPlayground(self): + self.backToPlayground['state'] = DGG.NORMAL + + def disablePlayAgain(self): + self.playAgain['state'] = DGG.DISABLED + + def enablePlayAgain(self): + self.playAgain['state'] = DGG.NORMAL + + def enterTutorialMode(self, newbieId): + self.tutorialMode = 1 + self.newbieId = newbieId + + def handleEnableGagPanel(self): + self.toon.inventory.setActivateMode('purchase', gagTutMode=1) + self.checkForBroke() + + def handleGagTutorialDone(self): + self.enableBackToPlayground() + + def doMetagamePlayAgain(self): + if hasattr(self, 'metagamePlayAgainResult'): + return self.metagamePlayAgainResult + numToons = 0 + for avId in self.ids: + if base.cr.doId2do.has_key(avId) and avId not in self.unexpectedExits: + numToons += 1 + + self.metagamePlayAgainResult = False + if numToons > 1: + if self.metagameRound > -1 and self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex: + self.metagamePlayAgainResult = True + return self.metagamePlayAgainResult + + def setupUnexpectedExitHooks(self): + for avId in self.ids: + if base.cr.doId2do.has_key(avId): + toon = base.cr.doId2do[avId] + eventName = toon.uniqueName('disable') + self.accept(eventName, self.__handleUnexpectedExit, extraArgs=[avId]) + self.unexpectedEventNames.append(eventName) + + def cleanupUnexpectedExitHooks(self): + for eventName in self.unexpectedEventNames: + self.ignore(eventName) + + def __handleUnexpectedExit(self, avId): + self.unexpectedExits.append(avId) + + +class PurchaseHeadFrame(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('Purchase') + + def __init__(self, av, purchaseModels): + DirectFrame.__init__(self, relief=None, image=purchaseModels.find('**/Char_Pnl')) + self.initialiseoptions(PurchaseHeadFrame) + self.statusLabel = DirectLabel(parent=self, relief=None, text='', text_scale=TTLocalizer.PstatusLabel, text_wordwrap=7.5, text_fg=(0.05, 0.14, 0.4, 1), text_pos=(0.1, 0, 0)) + self.av = av + self.avKeep = DelayDelete.DelayDelete(av, 'PurchaseHeadFrame.av') + self.accept('clientCleanup', self._handleClientCleanup) + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(-0.22, 10.0, -0.1, 180.0, 0.0, 0.0, 0.1, 0.1, 0.1) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(self.av.style, forGui=1) + self.headModel.reparentTo(self.head) + self.tag2Node = NametagFloat2d() + self.tag2Node.setContents(Nametag.CName) + self.av.nametag.addNametag(self.tag2Node) + self.tag2 = self.attachNewNode(self.tag2Node.upcastToPandaNode()) + self.tag2.setPosHprScale(-0.22, 10.0, 0.12, 0, 0, 0, 0.046, 0.046, 0.046) + self.tag1Node = NametagFloat2d() + self.tag1Node.setContents(Nametag.CSpeech | Nametag.CThought) + self.av.nametag.addNametag(self.tag1Node) + self.tag1 = self.attachNewNode(self.tag1Node.upcastToPandaNode()) + self.tag1.setPosHprScale(-0.15, 0, -0.1, 0, 0, 0, 0.046, 0.046, 0.046) + self.hide() + return + + def destroy(self): + DirectFrame.destroy(self) + del self.statusLabel + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + self.av.nametag.removeNametag(self.tag1Node) + self.av.nametag.removeNametag(self.tag2Node) + self.tag1.removeNode() + self.tag2.removeNode() + del self.tag1 + del self.tag2 + del self.tag1Node + del self.tag2Node + del self.av + self.removeAvKeep() + + def setAvatarState(self, state): + if state == PURCHASE_DISCONNECTED_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerDisconnected % self.av.getName() + self.statusLabel['text_pos'] = (0.015, 0.072, 0) + self.head.hide() + self.tag1.hide() + self.tag2.hide() + elif state == PURCHASE_EXIT_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerExited % self.av.getName() + self.statusLabel['text_pos'] = (0.015, 0.072, 0) + self.head.hide() + self.tag1.hide() + self.tag2.hide() + elif state == PURCHASE_PLAYAGAIN_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerPlayAgain + self.statusLabel['text_pos'] = (0.1, -0.12, 0) + elif state == PURCHASE_WAITING_STATE: + self.statusLabel['text'] = TTLocalizer.GagShopPlayerBuying + self.statusLabel['text_pos'] = (0.1, -0.12, 0) + elif state == PURCHASE_NO_CLIENT_STATE: + Purchase.notify.warning("setAvatarState('no client state'); OK for gag purchase tutorial") + else: + Purchase.notify.warning('unknown avatar state: %s' % state) + + def _handleClientCleanup(self): + self.destroy() + + def removeAvKeep(self): + if hasattr(self, 'avKeep'): + self.notify.debug('destroying avKeep %s' % self.avKeep) + self.avKeep.destroy() + del self.avKeep + self.ignore('clientCleanup') diff --git a/toontown/minigame/PurchaseBase.py b/toontown/minigame/PurchaseBase.py new file mode 100644 index 0000000..1d14c40 --- /dev/null +++ b/toontown/minigame/PurchaseBase.py @@ -0,0 +1,153 @@ +from toontown.toonbase.ToontownBattleGlobals import * +from toontown.toonbase import ToontownGlobals +from direct.fsm import StateData +from toontown.shtiker.PurchaseManagerConstants import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.task import Task +from direct.fsm import State +from direct.fsm import ClassicFSM, State +from toontown.toonbase import TTLocalizer +from toontown.toontowngui.TeaserPanel import TeaserPanel + +class PurchaseBase(StateData.StateData): + activateMode = 'purchase' + + def __init__(self, toon, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.toon = toon + self.fsm = ClassicFSM.ClassicFSM('Purchase', [State.State('purchase', self.enterPurchase, self.exitPurchase, ['done']), State.State('done', self.enterDone, self.exitDone, ['purchase'])], 'done', 'done') + self.fsm.enterInitialState() + + def load(self, purchaseModels = None): + if purchaseModels == None: + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.music = base.loadMusic('phase_4/audio/bgm/FF_safezone.mid') + self.jarImage = purchaseModels.find('**/Jar') + self.jarImage.reparentTo(hidden) + self.frame = DirectFrame(relief=None) + self.frame.hide() + self.title = DirectLabel(parent=self.frame, relief=None, pos=(0.0, 0.0, 0.83), scale=1.2, image=purchaseModels.find('**/Goofys_Sign'), text=TTLocalizer.GagShopName, text_fg=(0.6, 0.2, 0, 1), text_scale=0.09, text_wordwrap=10, text_pos=(0, 0.025, 0), text_font=ToontownGlobals.getSignFont()) + self.pointDisplay = DirectLabel(parent=self.frame, relief=None, pos=(-1.15, 0.0, 0.16), text=str(self.toon.getMoney()), text_scale=0.2, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), image=self.jarImage, text_font=ToontownGlobals.getSignFont()) + self.statusLabel = DirectLabel(parent=self.frame, relief=None, pos=(-0.25, 0, 0.625), text=TTLocalizer.GagShopYouHave % self.toon.getMoney(), text_scale=TTLocalizer.PBstatusLabel, text_fg=(0.05, 0.14, 0.4, 1)) + if self.toon.getMoney() == 1: + self.statusLabel['text'] = TTLocalizer.GagShopYouHaveOne + self.isBroke = 0 + self._teaserPanel = None + return + + def unload(self): + if self._teaserPanel: + self._teaserPanel.destroy() + self._teaserPanel = None + self.jarImage.removeNode() + del self.jarImage + self.frame.destroy() + del self.frame + del self.title + del self.pointDisplay + del self.statusLabel + del self.music + del self.fsm + return + + def __handleSelection(self, track, level): + if gagIsPaidOnly(track, level): + if not base.cr.isPaid(): + self._teaserPanel = TeaserPanel('restockGags', self._teaserDone) + return + self.handlePurchase(track, level) + + def _teaserDone(self): + self._teaserPanel.destroy() + self._teaserPanel = None + return + + def handlePurchase(self, track, level): + if self.toon.getMoney() <= 0: + return + ret = self.toon.inventory.addItem(track, level) + if ret == -2: + text = TTLocalizer.GagShopTooManyProps + elif ret == -1: + text = TTLocalizer.GagShopTooManyOfThatGag % TTLocalizer.BattleGlobalAvPropStringsPlural[track][level] + elif ret == 0: + text = TTLocalizer.GagShopInsufficientSkill + else: + text = TTLocalizer.GagShopYouPurchased % TTLocalizer.BattleGlobalAvPropStringsSingular[track][level] + self.toon.inventory.updateGUI(track, level) + self.toon.setMoney(self.toon.getMoney() - 1) + messenger.send('boughtGag') + self.showStatusText(text) + + def showStatusText(self, text): + self.statusLabel['text'] = text + taskMgr.remove('resetStatusText') + taskMgr.doMethodLater(2.0, self.resetStatusText, 'resetStatusText') + + def resetStatusText(self, task): + self.statusLabel['text'] = '' + return Task.done + + def checkForBroke(self): + money = self.toon.getMoney() + self.pointDisplay['text'] = str(money) + if money == 0: + if not self.isBroke: + self.toon.inventory.setActivateModeBroke() + taskMgr.doMethodLater(2.25, self.showBrokeMsg, 'showBrokeMsgTask') + self.isBroke = 1 + else: + if self.isBroke: + self.toon.inventory.setActivateMode(self.activateMode) + taskMgr.remove('showBrokeMsgTask') + self.isBroke = 0 + if money == 1: + self.statusLabel['text'] = TTLocalizer.GagShopYouHaveOne + else: + self.statusLabel['text'] = TTLocalizer.GagShopYouHave % money + + def showBrokeMsg(self, task): + self.statusLabel['text'] = TTLocalizer.GagShopOutOfJellybeans + return Task.done + + def handleDone(self, playAgain): + messenger.send(self.doneEvent) + + def enter(self): + self.fsm.request('purchase') + + def exit(self): + self.music.stop() + self.fsm.request('done') + + def enterPurchase(self): + self.frame.show() + self.toon.inventory.enableUberGags(0) + self.toon.inventory.show() + self.toon.inventory.reparentTo(self.frame) + self.toon.inventory.setActivateMode(self.activateMode) + self.checkForBroke() + self.acceptOnce('purchaseOver', self.handleDone) + self.accept('inventory-selection', self.__handleSelection) + self.accept(self.toon.uniqueName('moneyChange'), self.__moneyChange) + + def exitPurchase(self): + self.frame.hide() + self.toon.inventory.enableUberGags(1) + self.toon.inventory.reparentTo(hidden) + self.toon.inventory.hide() + self.ignore('purchaseOver') + self.ignore('inventory-selection') + self.ignore(self.toon.uniqueName('moneyChange')) + taskMgr.remove('resetStatusText') + taskMgr.remove('showBrokeMsgTask') + + def __moneyChange(self, money): + self.checkForBroke() + + def enterDone(self): + pass + + def exitDone(self): + pass diff --git a/toontown/minigame/RaceGameGlobals.py b/toontown/minigame/RaceGameGlobals.py new file mode 100644 index 0000000..b4ba1f8 --- /dev/null +++ b/toontown/minigame/RaceGameGlobals.py @@ -0,0 +1,38 @@ +from toontown.toonbase import TTLocalizer +ValidChoices = [0, + 1, + 2, + 3, + 4] +NumberToWin = 14 +InputTimeout = 20 +ChanceRewards = (((1, 0), TTLocalizer.RaceGameForwardOneSpace, 0), + ((1, 0), TTLocalizer.RaceGameForwardOneSpace, 0), + ((1, 0), TTLocalizer.RaceGameForwardOneSpace, 0), + ((2, 0), TTLocalizer.RaceGameForwardTwoSpaces, 0), + ((2, 0), TTLocalizer.RaceGameForwardTwoSpaces, 0), + ((2, 0), TTLocalizer.RaceGameForwardTwoSpaces, 0), + ((3, 0), TTLocalizer.RaceGameForwardThreeSpaces, 0), + ((3, 0), TTLocalizer.RaceGameForwardThreeSpaces, 0), + ((3, 0), TTLocalizer.RaceGameForwardThreeSpaces, 0), + ((0, -3), TTLocalizer.RaceGameOthersBackThree, 0), + ((0, -3), TTLocalizer.RaceGameOthersBackThree, 0), + ((-1, 0), TTLocalizer.RaceGameBackOneSpace, 0), + ((-1, 0), TTLocalizer.RaceGameBackOneSpace, 0), + ((-2, 0), TTLocalizer.RaceGameBackTwoSpaces, 0), + ((-2, 0), TTLocalizer.RaceGameBackTwoSpaces, 0), + ((-3, 0), TTLocalizer.RaceGameBackThreeSpaces, 0), + ((-3, 0), TTLocalizer.RaceGameBackThreeSpaces, 0), + ((0, 3), TTLocalizer.RaceGameOthersForwardThree, 0), + ((0, 3), TTLocalizer.RaceGameOthersForwardThree, 0), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans2, 2), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans4, 4), + ((0, 0), TTLocalizer.RaceGameJellybeans10, 10), + ((0, 0), -1, 0), + ((NumberToWin, 0), TTLocalizer.RaceGameInstantWinner, 0)) diff --git a/toontown/minigame/Ring.py b/toontown/minigame/Ring.py new file mode 100644 index 0000000..28c6908 --- /dev/null +++ b/toontown/minigame/Ring.py @@ -0,0 +1,27 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from pandac.PandaModules import NodePath +import RingTrack + +class Ring(NodePath): + + def __init__(self, moveTrack, tOffset, posScale = 1.0): + NodePath.__init__(self) + self.assign(hidden.attachNewNode(base.localAvatar.uniqueName('ring'))) + self.setMoveTrack(moveTrack) + self.setTOffset(tOffset) + self.setPosScale(posScale) + self.setT(0.0) + + def setMoveTrack(self, moveTrack): + self.__moveTrack = moveTrack + + def setTOffset(self, tOffset): + self.__tOffset = float(tOffset) + + def setPosScale(self, posScale): + self.__posScale = posScale + + def setT(self, t): + pos = self.__moveTrack.eval((t + self.__tOffset) % 1.0) + self.setPos(pos[0] * self.__posScale, 0, pos[1] * self.__posScale) diff --git a/toontown/minigame/RingAction.py b/toontown/minigame/RingAction.py new file mode 100644 index 0000000..7913cc7 --- /dev/null +++ b/toontown/minigame/RingAction.py @@ -0,0 +1,42 @@ +from direct.directnotify import DirectNotifyGlobal +import RingTrack + +class RingAction: + notify = DirectNotifyGlobal.directNotify.newCategory('RingAction') + + def __init__(self): + pass + + def eval(self, t): + return (0, 0) + + +class RingActionStaticPos(RingAction): + + def __init__(self, pos): + RingAction.__init__(self) + self.__pos = pos + + def eval(self, t): + return self.__pos + + +class RingActionFunction(RingAction): + + def __init__(self, func, args): + RingAction.__init__(self) + self.__func = func + self.__args = args + + def eval(self, t): + return self.__func(t, *self.__args) + + +class RingActionRingTrack(RingAction): + + def __init__(self, ringTrack): + RingAction.__init__(self) + self.__track = ringTrack + + def eval(self, t): + return self.__track.eval(t) diff --git a/toontown/minigame/RingGameGlobals.py b/toontown/minigame/RingGameGlobals.py new file mode 100644 index 0000000..27370b5 --- /dev/null +++ b/toontown/minigame/RingGameGlobals.py @@ -0,0 +1,21 @@ +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +ENDLESS_GAME = config.GetBool('endless-ring-game', 0) +NUM_RING_GROUPS = 16 +MAX_TOONXZ = 10.0 +CollisionRadius = 1.5 +CollideMask = ToontownGlobals.CatchGameBitmask +RING_RADIUS = MAX_TOONXZ / 3.0 * 0.9 +ringColors = ((TTLocalizer.ColorRed, VBase4(1.0, 0.4, 0.2, 1.0)), + (TTLocalizer.ColorGreen, VBase4(0.0, 0.9, 0.2, 1.0)), + (TTLocalizer.ColorOrange, VBase4(1.0, 0.5, 0.25, 1.0)), + (TTLocalizer.ColorPurple, VBase4(1.0, 0.0, 1.0, 1.0)), + (TTLocalizer.ColorWhite, VBase4(1.0, 1.0, 1.0, 1.0)), + (TTLocalizer.ColorBlack, VBase4(0.0, 0.0, 0.0, 1.0)), + (TTLocalizer.ColorYellow, VBase4(1.0, 1.0, 0.2, 1.0))) +ringColorSelection = [(0, 1, 2), + 3, + 4, + 5, + 6] diff --git a/toontown/minigame/RingGroup.py b/toontown/minigame/RingGroup.py new file mode 100644 index 0000000..e9e8e6b --- /dev/null +++ b/toontown/minigame/RingGroup.py @@ -0,0 +1,48 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from pandac.PandaModules import NodePath +import Ring +import RingTrack +import RingTrackGroup +import RingGameGlobals + +class RingGroup(NodePath): + + def __init__(self, trackGroup, ringModel, posScale, colorIndices): + NodePath.__init__(self) + self.assign(hidden.attachNewNode(base.localAvatar.uniqueName('ring-group'))) + self.__period = trackGroup.period + self.__reverseFlag = trackGroup.reverseFlag + self.__tOffset = trackGroup.tOffset + self.__numRings = len(trackGroup.tracks) + self.__rings = [] + self.__ringModels = [] + for i in range(0, self.__numRings): + track = trackGroup.tracks[i] + tOffset = trackGroup.trackTOffsets[i] + ring = Ring.Ring(track, tOffset, posScale) + ring.reparentTo(self) + model = ringModel.copyTo(ring) + model.setColor(RingGameGlobals.ringColors[colorIndices[i]][1]) + self.__rings.append(ring) + self.__ringModels.append(model) + + def delete(self): + for model in self.__ringModels: + model.removeNode() + + for ring in self.__rings: + ring.removeNode() + + del self.__rings + del self.__ringModels + + def getRing(self, index): + return self.__ringModels[index] + + def setT(self, t): + normT = (t / self.__period + self.__tOffset) % 1.0 + if self.__reverseFlag: + normT = 1.0 - normT + for ring in self.__rings: + ring.setT(normT) diff --git a/toontown/minigame/RingTrack.py b/toontown/minigame/RingTrack.py new file mode 100644 index 0000000..6836b34 --- /dev/null +++ b/toontown/minigame/RingTrack.py @@ -0,0 +1,39 @@ +from direct.directnotify import DirectNotifyGlobal +import RingAction + +class RingTrack: + notify = DirectNotifyGlobal.directNotify.newCategory('RingTrack') + + def __init__(self, actions, actionDurations = None, reverseFlag = 0): + if actionDurations == None: + actionDurations = [1.0 / float(len(actions))] * len(actions) + sum = 0.0 + for duration in actionDurations: + sum += duration + + if sum != 1.0: + self.notify.warning('action lengths do not sum to 1.; sum=' + str(sum)) + self.actions = actions + self.actionDurations = actionDurations + self.reverseFlag = reverseFlag + return + + def eval(self, t): + t = float(t) + if self.reverseFlag: + t = 1.0 - t + actionStart = 0.0 + for action, duration in zip(self.actions, self.actionDurations): + actionEnd = actionStart + duration + if t < actionEnd: + actionT = (t - actionStart) / duration + return action.eval(actionT) + else: + actionStart = actionEnd + + if t == actionStart: + self.notify.debug('time value is at end of ring track: ' + str(t) + ' == ' + str(actionStart)) + else: + self.notify.debug('time value is beyond end of ring track: ' + str(t) + ' > ' + str(actionStart)) + lastAction = self.actions[len(self.actions) - 1] + return lastAction.eval(1.0) diff --git a/toontown/minigame/RingTrackGroup.py b/toontown/minigame/RingTrackGroup.py new file mode 100644 index 0000000..a6b1dbc --- /dev/null +++ b/toontown/minigame/RingTrackGroup.py @@ -0,0 +1,13 @@ + + +class RingTrackGroup: + + def __init__(self, tracks, period, trackTOffsets = None, reverseFlag = 0, tOffset = 0.0): + if trackTOffsets == None: + trackTOffsets = [0] * len(tracks) + self.tracks = tracks + self.period = period + self.trackTOffsets = trackTOffsets + self.reverseFlag = reverseFlag + self.tOffset = tOffset + return diff --git a/toontown/minigame/RingTrackGroups.py b/toontown/minigame/RingTrackGroups.py new file mode 100644 index 0000000..3811857 --- /dev/null +++ b/toontown/minigame/RingTrackGroups.py @@ -0,0 +1,294 @@ +import math +import RingGameGlobals +import RingAction +import RingTracks +import RingTrack +import RingTrackGroup +from direct.showbase import PythonUtil +STATIC = 0 +SIMPLE = 1 +COMPLEX = 2 + +def getRandomRingTrackGroup(type, numRings, rng): + global trackListGenFuncs + funcTable = trackListGenFuncs[type][numRings - 1] + func = rng.choice(funcTable) + tracks, tOffsets, period = func(numRings, rng) + tracks, tOffsets = __scramble(tracks, tOffsets, rng) + trackGroup = RingTrackGroup.RingTrackGroup(tracks, period, trackTOffsets=tOffsets, reverseFlag=rng.choice([0, 1]), tOffset=rng.random()) + return trackGroup + + +def __scramble(tracks, tOffsets, rng): + newTracks = [] + if tOffsets == None: + newTOffsets = None + else: + newTOffsets = [] + used = [0] * len(tracks) + count = 0 + while count < len(tracks): + i = rng.randint(0, len(tracks) - 1) + if not used[i]: + used[i] = 1 + count += 1 + newTracks.append(tracks[i]) + if newTOffsets != None: + newTOffsets.append(tOffsets[i]) + + return (newTracks, newTOffsets) + + +def angleToXY(angle, radius = 1.0): + return [radius * math.sin(angle), radius * math.cos(angle)] + + +def getTightCircleStaticPositions(numRings): + positions = [] + if numRings == 1: + positions.append([0, 0]) + else: + radius = RingGameGlobals.RING_RADIUS * 1.5 / RingGameGlobals.MAX_TOONXZ + step = 2.0 * math.pi / float(numRings) + for i in range(0, numRings): + angle = i * step + step / 2.0 + positions.append(angleToXY(angle, 1.0 / 3.0)) + + return positions + + +def get_keypad(numRings, rng): + positions = (RingTracks.center, + RingTracks.up, + RingTracks.down, + RingTracks.left, + RingTracks.right, + RingTracks.ul, + RingTracks.ur, + RingTracks.lr, + RingTracks.ll) + tracks = [] + usedPositions = [None] + posScale = 0.7 + rng.random() * 0.2 + for i in range(0, numRings): + pos = None + while pos in usedPositions: + pos = rng.choice(positions) + + usedPositions.append(pos) + scaledPos = [0, 0] + scaledPos[0] = pos[0] * posScale + scaledPos[1] = pos[1] * posScale + action = RingAction.RingActionStaticPos(scaledPos) + track = RingTrack.RingTrack([action], [1.0]) + tracks.append(track) + + return (tracks, None, 1.0) + + +fullCirclePeriod = 6.0 +plusPeriod = 4.0 + +def get_evenCircle(numRings, rng): + tracks = [] + tOffsets = [] + for i in range(0, numRings): + actions, durations = RingTracks.getCircleRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + tOffsets.append(float(i) / numRings) + + return (tracks, tOffsets, fullCirclePeriod) + + +def get_followCircle(numRings, rng): + tracks = [] + tOffsets = [] + for i in range(0, numRings): + actions, durations = RingTracks.getCircleRingActions() + track = RingTrack.RingTrack(actions, durations) + delay = 0.12 + tracks.append(track) + tOffsets.append(float(i) * delay) + + return (tracks, tOffsets, fullCirclePeriod) + + +def get_evenCircle_withStationaryCenterRings(numRings, rng): + tracks = [] + tOffsets = [] + numCenterRings = rng.randint(1, numRings - 1) + positions = getTightCircleStaticPositions(numCenterRings) + for i in range(0, numCenterRings): + action = RingAction.RingActionStaticPos(positions[i]) + track = RingTrack.RingTrack([action]) + tracks.append(track) + tOffsets.append(0) + + numOuterRings = numRings - numCenterRings + for i in range(0, numOuterRings): + actions, durations = RingTracks.getCircleRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + tOffsets.append(float(i) / numOuterRings) + + return (tracks, tOffsets, fullCirclePeriod) + + +def __get_Slots(numRings, rng, vertical = 1): + tracks = [] + tOffsets = [] + fpTab = [] + for i in range(numRings): + fpTab.append(PythonUtil.lineupPos(i, numRings, 2.0 / 3)) + + offset = 1 - fpTab[-1] + offset = rng.random() * (offset * 2) - offset + fpTab = map(lambda x: x + offset, fpTab) + for i in range(0, numRings): + if vertical: + getActionsFunc = RingTracks.getVerticalSlotActions + else: + getActionsFunc = RingTracks.getHorizontalSlotActions + actions, durations = getActionsFunc(fpTab[i]) + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + tOffsets.append(float(i) / numRings * 0.5) + + return (tracks, tOffsets, fullCirclePeriod) + + +def get_verticalSlots(numRings, rng): + return __get_Slots(numRings, rng, vertical=1) + + +def get_horizontalSlots(numRings, rng): + return __get_Slots(numRings, rng, vertical=0) + + +def get_plus(numRings, rng): + up = RingTracks.getPlusUpRingActions + down = RingTracks.getPlusDownRingActions + left = RingTracks.getPlusLeftRingActions + right = RingTracks.getPlusRightRingActions + actionSets = {2: [[up, down], [left, right]], + 3: [[up, left, right], + [left, up, down], + [down, left, right], + [right, up, down]], + 4: [[up, + down, + left, + right]]} + tracks = [] + actionSet = rng.choice(actionSets[numRings]) + for i in range(0, numRings): + actions, durations = actionSet[i]() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + + return (tracks, [0] * numRings, plusPeriod) + + +infinityPeriod = 5.0 +fullCirclePeriodFaster = 5.0 +plusPeriodFaster = 2.5 +infinityTOffsets = [] + +def __initInfinityTOffsets(): + global infinityTOffsets + offsets = [[], + [], + [], + []] + offsets[0] = [0.0] + offsets[1] = [0.0, 3.0 / 4.0] + offsets[2] = [0.0, 1.0 / 3.0, 2.0 / 3.0] + inc = 14.0 / 23.0 + for numRings in range(4, 5): + o = [0] * numRings + accum = 0.0 + for i in range(0, numRings): + o[i] = accum % 1.0 + accum += inc + + offsets[numRings - 1] = o + + infinityTOffsets = offsets + + +__initInfinityTOffsets() + +def get_vertInfinity(numRings, rng): + tracks = [] + for i in range(0, numRings): + actions, durations = RingTracks.getVerticalInfinityRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + + return (tracks, infinityTOffsets[numRings - 1], infinityPeriod) + + +def get_horizInfinity(numRings, rng): + tracks = [] + for i in range(0, numRings): + actions, durations = RingTracks.getHorizontalInfinityRingActions() + track = RingTrack.RingTrack(actions, durations) + tracks.append(track) + + return (tracks, infinityTOffsets[numRings - 1], infinityPeriod) + + +def get_evenCircle_withStationaryCenterRings_FASTER(numRings, rng): + tracks, tOffsets, period = get_evenCircle_withStationaryCenterRings(numRings, rng) + return (tracks, tOffsets, fullCirclePeriodFaster) + + +def get_plus_FASTER(numRings, rng): + tracks, tOffsets, period = get_plus(numRings, rng) + return (tracks, tOffsets, plusPeriodFaster) + + +allFuncs = [[get_keypad], [get_evenCircle, + get_followCircle, + get_evenCircle_withStationaryCenterRings, + get_verticalSlots, + get_horizontalSlots, + get_plus], [get_vertInfinity, + get_horizInfinity, + get_evenCircle_withStationaryCenterRings_FASTER, + get_plus_FASTER]] +dontUseFuncs = [[get_followCircle, + get_evenCircle_withStationaryCenterRings, + get_evenCircle_withStationaryCenterRings_FASTER, + get_plus, + get_plus_FASTER], + [], + [], + []] +trackListGenFuncs = [] + +def __listComplement(list1, list2): + result = [] + for item in list1: + if item not in list2: + result.append(item) + + return result + + +def __initFuncTables(): + global trackListGenFuncs + table = [[], [], []] + for diff in range(0, len(table)): + table[diff] = [[], + [], + [], + []] + for numRings in range(0, len(table[diff])): + table[diff][numRings] = __listComplement(allFuncs[diff], dontUseFuncs[numRings]) + + trackListGenFuncs = table + + +__initFuncTables() diff --git a/toontown/minigame/RingTracks.py b/toontown/minigame/RingTracks.py new file mode 100644 index 0000000..3836124 --- /dev/null +++ b/toontown/minigame/RingTracks.py @@ -0,0 +1,98 @@ +import math +import RingTrack +import RingAction +center = (0, 0) +up = (0, 1) +down = (0, -1) +left = (-1, 0) +right = (1, 0) +ul = (-1, 1) +ur = (1, 1) +lr = (1, -1) +ll = (-1, -1) + +def ringLerp(t, a, b): + omT = 1.0 - t + return (float(a[0]) * omT + float(b[0]) * t, float(a[1]) * omT + float(b[1]) * t) + + +def ringClerp(t, a, b): + return ringLerp(t * t * (3 - 2 * t), a, b) + + +def getSquareRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [ul, ur]), + RingAction.RingActionFunction(ringClerp, [ur, lr]), + RingAction.RingActionFunction(ringClerp, [lr, ll]), + RingAction.RingActionFunction(ringClerp, [ll, ul])], [0.25, + 0.25, + 0.25, + 0.25]) + + +def getVerticalSlotActions(x): + return ([RingAction.RingActionFunction(ringClerp, [(x, 1), (x, -1)]), RingAction.RingActionFunction(ringClerp, [(x, -1), (x, 1)])], [0.5, 0.5]) + + +def getHorizontalSlotActions(y): + return ([RingAction.RingActionFunction(ringClerp, [(1, y), (-1, y)]), RingAction.RingActionFunction(ringClerp, [(-1, y), (1, y)])], [0.5, 0.5]) + + +def getCircleRingActions(): + + def circlePos(t): + return (math.sin(t * 2.0 * math.pi), math.cos(t * 2.0 * math.pi)) + + return ([RingAction.RingActionFunction(circlePos, [])], [1.0]) + + +def getVerticalInfinityRingActions(): + + def vertInfPos(t): + return (0.5 * math.sin(2.0 * t * 2.0 * math.pi), math.cos(t * 2.0 * math.pi)) + + return ([RingAction.RingActionFunction(vertInfPos, [])], [1.0]) + + +def getHorizontalInfinityRingActions(): + + def horizInfPos(t): + return (math.sin(t * 2.0 * math.pi), 0.5 * math.sin(2.0 * t * 2.0 * math.pi)) + + return ([RingAction.RingActionFunction(horizInfPos, [])], [1.0]) + + +RingOffset = 0.4 + +def getPlusUpRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(0, RingOffset), (0, 1)]), RingAction.RingActionFunction(ringClerp, [(0, 1), (0, RingOffset)])], [0.5, 0.5]) + + +def getPlusDownRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(0, -RingOffset), (0, -1)]), RingAction.RingActionFunction(ringClerp, [(0, -1), (0, -RingOffset)])], [0.5, 0.5]) + + +def getPlusRightRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(RingOffset, 0), (1, 0)]), RingAction.RingActionFunction(ringClerp, [(1, 0), (RingOffset, 0)])], [0.5, 0.5]) + + +def getPlusLeftRingActions(): + return ([RingAction.RingActionFunction(ringClerp, [(-RingOffset, 0), (-1, 0)]), RingAction.RingActionFunction(ringClerp, [(-1, 0), (-RingOffset, 0)])], [0.5, 0.5]) + + +def getHalfDomeRingActions(): + + def halfDome(t): + return (math.cos(t * math.pi), -math.sin(t * math.pi)) + + x1 = -1.0 + x2 = -1.0 / 3.0 + x3 = 1.0 / 3.0 + x4 = 1.0 + return ([RingAction.RingActionFunction(ringClerp, [(x1, 0), (x2, 0)]), + RingAction.RingActionFunction(ringClerp, [(x2, 0), (x3, 0)]), + RingAction.RingActionFunction(ringClerp, [(x3, 0), (x4, 0)]), + RingAction.RingActionFunction(halfDome, [])], [0.25, + 0.25, + 0.25, + 0.25]) diff --git a/toontown/minigame/RubberBand.py b/toontown/minigame/RubberBand.py new file mode 100644 index 0000000..7b6c63b --- /dev/null +++ b/toontown/minigame/RubberBand.py @@ -0,0 +1,143 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +from toontown.toonbase import ToontownGlobals +import math +from math import * +import random + +class RubberBand: + SomeCounter = 0 + + def __init__(self, heldObject = None, heldOffset = None, taskPriority = 0): + self.heldObject = heldObject + self.heldOffset = heldOffset + self.bandNumber = self.SomeCounter + self._taskPriority = taskPriority + self.SomeCounter += 1 + if not heldOffset: + self.heldOffset = [0, 0, 0] + self.setup() + + def setup(self): + self.baseNode = render.attachNewNode('targetGameTargets') + target = CollisionSphere(0, 0, 0, 5.0) + target.setTangible(0) + targetNode = CollisionNode('thing') + targetNode.addSolid(target) + targetNodePath = self.baseNode.attachNewNode(targetNode) + self.slingModel = loader.loadModel('phase_4/models/minigames/slingshot_game_sling.bam') + self.slingModel.reparentTo(self.baseNode) + self.slingModel.setScale(1.0) + self.slingModel.setZ(-1.0) + self.bandGN = GeomNode('Band Geometry') + self.bandNodePathGeom = self.baseNode.attachNewNode(self.bandGN) + self.bandNodePathGeom.setTwoSided(True) + height = 13 + width = 5.5 + self.bandHeight = 1.0 + self.post1Pos = Point3(-width, 0, height) + self.post2Pos = Point3(width, 0, height) + taskMgr.add(self.redraw, 'recreateBand %s' % self.bandNumber, priority=self._taskPriority) + self.colorRelax = {} + self.colorRelax['Red'] = 1.0 + self.colorRelax['Green'] = 0.3 + self.colorRelax['Blue'] = 0.2 + self.colorRelax['Alpha'] = 1.0 + self.colorStrecth = {} + self.colorStrecth['Red'] = 1.0 + self.colorStrecth['Green'] = 0.6 + self.colorStrecth['Blue'] = 0.4 + self.colorStrecth['Alpha'] = 1.0 + + def setPos(self, pos): + self.baseNode.setPos(pos[0], pos[1], pos[2]) + + def delete(self): + taskMgr.remove('recreateBand %s' % self.bandNumber) + self.bandGN.removeAllGeoms() + self.baseNode.remove() + + def redraw(self, task): + color = {} + color['Red'] = 1.0 + color['Green'] = 0.3 + color['Blue'] = 0.2 + color['Alpha'] = 0.5 + self.bandGN.removeAllGeoms() + objPosX = self.heldObject.getX(self.baseNode) + self.heldOffset[0] + objPosY = self.heldObject.getY(self.baseNode) + self.heldOffset[1] + objPosZ = self.heldObject.getZ(self.baseNode) + self.heldOffset[2] + 1.5 + if objPosY > 0: + objPosY = 0 + objPosX = 0 + objPosZ = self.post1Pos[2] + objPosYSecondary = objPosY + 0.25 + if objPosYSecondary > 0: + objPosYSecondary = 0 + midPosY = objPosY + math.sqrt(abs(objPosY)) + midPosX = objPosX + 2 + maxStretch = 25.0 + bandThickness = self.bandHeight + objPosY * (1 / maxStretch) + if bandThickness < 0.2: + bandThickness = 0.2 + colorProp = bandThickness / self.bandHeight + color = {} + color['Red'] = colorProp * self.colorRelax['Red'] + (1 - colorProp) * self.colorStrecth['Red'] + color['Green'] = colorProp * self.colorRelax['Green'] + (1 - colorProp) * self.colorStrecth['Green'] + color['Blue'] = colorProp * self.colorRelax['Blue'] + (1 - colorProp) * self.colorStrecth['Blue'] + color['Alpha'] = colorProp * self.colorRelax['Alpha'] + (1 - colorProp) * self.colorStrecth['Alpha'] + bandBottomOrigin = self.post1Pos[2] - 0.5 * bandThickness + bandTopOrigin = self.post1Pos[2] + 0.5 * bandThickness + bandBottomHeld = objPosZ - 0.5 * bandThickness + bandTopHeld = objPosZ + 0.5 * bandThickness + midSegs = 12 + colorMultList = [] + colorHigh = 1.0 + colorLow = 0.5 + shapeVertexs = [] + shapeVertexs.append((self.post1Pos[0], self.post1Pos[1], bandBottomOrigin + 0.45)) + shapeVertexs.append((self.post1Pos[0], self.post1Pos[1], bandTopOrigin + 0.45)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + s2 = pow(2, 0.5) + s2dif = s2 - 1 + objPosXSecondary = objPosX - 0.5 + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandBottomHeld)) + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandTopHeld)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + shapeVertexs.append((objPosX, objPosY, bandBottomHeld)) + shapeVertexs.append((objPosX, objPosY, bandTopHeld)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + objPosXSecondary = objPosX + 0.5 + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandBottomHeld)) + shapeVertexs.append((objPosXSecondary, objPosYSecondary, bandTopHeld)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + shapeVertexs.append((self.post2Pos[0], self.post2Pos[1], bandBottomOrigin + 0.45)) + shapeVertexs.append((self.post2Pos[0], self.post2Pos[1], bandTopOrigin + 0.45)) + colorMultList.append(colorLow) + colorMultList.append(colorHigh) + gFormat = GeomVertexFormat.getV3cp() + bandVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + bandVertexWriter = GeomVertexWriter(bandVertexData, 'vertex') + bandColorWriter = GeomVertexWriter(bandVertexData, 'color') + for index in range(len(shapeVertexs)): + bandVertexWriter.addData3f(shapeVertexs[index][0], shapeVertexs[index][1], shapeVertexs[index][2]) + bandColorWriter.addData4f(color['Red'] * colorMultList[index], color['Green'] * colorMultList[index], color['Blue'] * colorMultList[index], color['Alpha'] * colorMultList[index]) + + bandTris = GeomTristrips(Geom.UHStatic) + for index in range(len(shapeVertexs)): + bandTris.addVertex(index) + + bandTris.closePrimitive() + bandGeom = Geom(bandVertexData) + bandGeom.addPrimitive(bandTris) + self.bandGN.addGeom(bandGeom) + return task.cont diff --git a/toontown/minigame/SwingVine.py b/toontown/minigame/SwingVine.py new file mode 100644 index 0000000..3da2244 --- /dev/null +++ b/toontown/minigame/SwingVine.py @@ -0,0 +1,657 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.distributed import DistributedObject +from direct.showutil import Rope +import math +from toontown.toonbase import ToontownGlobals +import VineGameGlobals +import VineSpider + +class SwingVine(NodePath.NodePath): + notify = DirectNotifyGlobal.directNotify.newCategory('SwingVine') + defaultNormal = Vec3(1, 0, 0) + SwingAnimPeriod = 6.0 + SmallAnimAngle = 10 + NonMovingAnimAngle = 2 + SwingAnimFull = 0 + SwingAnimSmall = 1 + SwingAnimMinimal = 2 + MaxNumberOfFramesInSwingAnim = 144 + + def __init__(self, vineIndex, x, y, z, length = 20, baseAngle = 40, period = 4, spiderPeriod = 0): + NodePath.__init__(self, 'SwingVine') + self.cableLength = length + self.numLinks = 3 + self.links = [] + self.vineIndex = vineIndex + self.spider = None + self.spiderPeriod = spiderPeriod + self.hasSpider = not spiderPeriod == 0 + self.numTubes = self.numLinks * 3 + self.tubeLength = float(self.cableLength) / self.numTubes + self.tubeLength *= 1.25 + self.tIncrement = 1.0 / self.numTubes + self.tHalfIncrement = self.tIncrement / 2.0 + self.baseAngle = baseAngle + self.maxSwingAngle = deg2Rad(-90 + baseAngle) + self.minSwingAngle = deg2Rad(-90 - baseAngle) + self.period = period + self.swingingForward = True + self.swingT = 0 + self.swingAngle = 0 + self.setPos(x, y, z) + self.load() + self.attachedToons = {} + self.ival = None + self.spiderIval = None + self.unloading = False + self.spiderMovingDown = True + return + + def load(self): + self.root = self.attachNewNode('root') + self.topLink = self.root.attachNewNode('topLink') + self.setupCable() + self.reparentTo(render) + self.debugTangent = None + nearBubble = CollisionSphere(0, 0, -self.cableLength / 2.0, self.cableLength / 2.0) + nearBubble.setTangible(0) + nearBubbleNode = CollisionNode('SwingVine') + nearBubbleNode.setCollideMask(GeomNode.getDefaultCollideMask()) + nearBubbleNode.addSolid(nearBubble) + self.rope.setName('SwingVine-%d' % self.vineIndex) + self.setupTubes() + if self.hasSpider: + self.spider = VineSpider.VineSpider() + self.spiderT = 0.25 + return + + def unload(self): + self.unloading = True + if self.ival: + self.ival.finish() + self.ival = None + if self.spiderIval: + self.spiderIval.finish() + self.spiderIval = None + if self.debugTangent: + self.debugTangent.removeNode() + for tube in self.tubes: + tube.removeNode() + + self.tubes = [] + for tube in self.tubes2: + tube.removeNode() + + self.tubes2 = [] + if self.hasSpider: + self.spider.destroy() + del self.spider + for toonInfo in self.attachedToons.values(): + attachNode = toonInfo[4] + if attachNode: + attachNode.removeNode() + swingIval = toonInfo[6] + if swingIval: + swingIval.finish() + + self.removeNode() + return + + def setupCable(self): + self.links = [] + self.links.append((self.topLink, Point3(0, 0, 0))) + anchor = self.topLink + for linkNum in xrange(self.numLinks): + anchor = self.__makeLink(anchor, linkNum) + + self.bottomLink = self.links[-1][0] + self.link1 = self.links[-2][0] + self.rope = self.makeSpline() + self.rope.reparentTo(self.root) + myTexture = loader.loadTexture('phase_4/maps/swinging_vine.jpg') + gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + vine = gameAssets.find('**/vine1') + self.cableTex = vine.findTexture('*') + if self.cableTex: + self.cableTex.setWrapV(Texture.WMRepeat) + self.rope.setTexture(self.cableTex) + ts = TextureStage.getDefault() + self.rope.setTexScale(ts, 1.0, 0.15) + self.rope.setTransparency(1) + if self.vineIndex == VineGameGlobals.NumVines - 1: + pass + if self.cableTex: + self.setupStaticPart(self.cableTex) + + def setupStaticPart(self, vineTexture): + cm = CardMaker('card') + cm.setFrame(-0.5, 0.5, -0.1, 8) + self.staticVine = self.attachNewNode(cm.generate()) + self.staticVine.setTexture(vineTexture) + self.setTransparency(1) + radius = 0.5 + tubeIndex = 0 + colNode = CollisionNode('StaticVine-%d-%d' % (self.vineIndex, tubeIndex)) + bz = 0 + az = 58 + quad = CollisionPolygon(Point3(0.25, radius + 1, bz), Point3(0.25, radius + 1, az), Point3(0.25, -radius - 1, az), Point3(0.25, -radius - 1, bz)) + colNode.addSolid(quad) + colNode.setCollideMask(ToontownGlobals.PieBitmask) + colNode.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + self.staticVine.attachNewNode(colNode) + colNode2 = CollisionNode('StaticVine-%d-%d' % (self.vineIndex, tubeIndex)) + quad2 = CollisionPolygon(Point3(-0.25, -radius - 1, bz), Point3(-0.25, -radius - 1, az), Point3(-0.25, +radius + 1, az), Point3(-0.25, +radius + 1, bz)) + colNode2.addSolid(quad2) + colNode2.setCollideMask(ToontownGlobals.PieBitmask) + colNode2.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + self.staticVine.attachNewNode(colNode2) + + def setupTubes(self): + self.tubes = [] + self.tubes2 = [] + radius = 0.5 + for tubeIndex in xrange(self.numTubes): + az = self.tubeLength / 2.0 + bz = -self.tubeLength / 2.0 + ct = CollisionTube(0, 0, az, 0, 0, bz, radius) + ct.setTangible(0) + colNode = CollisionNode('SwingVine-%d-%d' % (self.vineIndex, tubeIndex)) + quad = CollisionPolygon(Point3(0.25, radius + 1, bz), Point3(0.25, radius + 1, az), Point3(0.25, -radius - 1, az), Point3(0.25, -radius - 1, bz)) + colNode.addSolid(quad) + colNode.setCollideMask(ToontownGlobals.PieBitmask) + colNode.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + colNode2 = CollisionNode('SwingVine-%d-%d' % (self.vineIndex, tubeIndex)) + quad2 = CollisionPolygon(Point3(-0.25, -radius - 1, bz), Point3(-0.25, -radius - 1, az), Point3(-0.25, +radius + 1, az), Point3(-0.25, +radius + 1, bz)) + colNode2.addSolid(quad2) + colNode2.setCollideMask(ToontownGlobals.PieBitmask) + colNode2.setBounds(BoundingSphere(Point3(0, 0, 0), 10)) + newTube = render.attachNewNode(colNode) + self.tubes.append(newTube) + newTube2 = render.attachNewNode(colNode2) + self.tubes2.append(newTube2) + + self.updateTubes() + + def __makeLink(self, anchor, linkNum): + an = ActorNode('link%s' % linkNum) + anp = NodePath(an) + anp.reparentTo(self.root) + z = float(linkNum + 1) / float(self.numLinks) * self.cableLength + anp.setPos(self.topLink.getPos()) + anp.setZ(anp.getZ() - z) + self.links.append((anp, Point3(0, 0, 0))) + return anp + + def makeSpline(self): + rope = Rope.Rope() + for i in xrange(len(self.links)): + pass + + rope.setup(min(len(self.links), 4), self.links) + for i in xrange(len(self.links)): + pass + + rope.curve.normalizeKnots() + self.notify.debug('after normalize Knots') + for i in xrange(len(self.links)): + pass + + rn = rope.ropeNode + rn.setRenderMode(RopeNode.RMBillboard) + rn.setNumSlices(3) + rn.setTubeUp(Vec3(0, -1, 0)) + rn.setUvMode(RopeNode.UVDistance) + rn.setUvDirection(False) + rn.setThickness(1.0) + return rope + + def positionLink1(self, t, angleInRadians): + degAngle = rad2Deg(angleInRadians) + diffFrom90 = degAngle - -90.0 + link1AngleDiff = diffFrom90 * 2 / 3.0 + link1AngleToUse = deg2Rad(-90 + link1AngleDiff) + lengthToUse = self.cableLength * 2.0 / 3.0 + link1X = math.cos(link1AngleToUse) * lengthToUse + link1Z = math.sin(link1AngleToUse) * lengthToUse + self.link1.setPos(link1X, 0, link1Z) + + def swingForward(self, t): + diffAngle = self.maxSwingAngle - self.minSwingAngle + multiplier = t + angleToUse = self.minSwingAngle + multiplier * diffAngle + newX = math.cos(angleToUse) * self.cableLength + newZ = math.sin(angleToUse) * self.cableLength + self.bottomLink.setPos(newX, 0, newZ) + self.positionLink1(t, angleToUse) + oldSwingingForward = self.swingingForward + self.swingingForward = True + self.swingT = t + self.swingAngle = angleToUse + self.updateAttachedStuff() + if not oldSwingingForward == self.swingingForward: + self.updateSwingAnims() + + def swingBack(self, t): + diffAngle = self.maxSwingAngle - self.minSwingAngle + multiplier = t + angleToUse = self.maxSwingAngle - multiplier * diffAngle + newX = math.cos(angleToUse) * self.cableLength + newZ = math.sin(angleToUse) * self.cableLength + self.bottomLink.setPos(newX, 0, newZ) + self.positionLink1(t, angleToUse) + oldSwingingForward = self.swingingForward + self.swingingForward = False + self.swingT = t + self.swingAngle = angleToUse + self.updateAttachedStuff() + if not oldSwingingForward == self.swingingForward: + self.updateSwingAnims() + + def moveSpiderDown(self, t): + self.spiderMovingDown = True + self.spiderT = t + + def moveSpiderUp(self, t): + self.spiderMovingDown = False + self.spiderT = 1 - t + + def startSwing(self): + forwardX = math.cos(self.maxSwingAngle) * self.cableLength + forwardZ = math.sin(self.maxSwingAngle) * self.cableLength + backX = math.cos(self.minSwingAngle) * self.cableLength + backZ = math.sin(self.minSwingAngle) * self.cableLength + self.bottomLink.setPos(backX, 0, backZ) + self.ival = Sequence(LerpFunctionInterval(self.swingForward, duration=self.period / 2.0, blendType='easeInOut')) + self.ival.append(LerpFunctionInterval(self.swingBack, duration=self.period / 2.0, blendType='easeInOut')) + self.ival.loop() + if self.hasSpider: + self.spiderIval = Sequence(LerpFunctionInterval(self.moveSpiderDown, duration=self.spiderPeriod / 2.0, blendType='easeInOut')) + self.spiderIval.append(LerpFunctionInterval(self.moveSpiderUp, duration=self.spiderPeriod / 2.0, blendType='easeInOut')) + self.spiderIval.loop() + + def stopSwing(self): + if self.ival: + self.ival.pause() + if self.hasSpider and self.spiderIval: + self.spiderIval.pause() + if self.hasSpider: + self.spider.hide() + + def getAttachNode(self, toonId): + retval = None + if self.attachedToons.has_key(toonId): + existingAttachNode = self.attachedToons[toonId][4] + if existingAttachNode: + retval = existingAttachNode + else: + retval = render.attachNewNode('vineAttachNode-%s-%s' % (self.vineIndex, toonId)) + return retval + + def calcOffset(self, toonId): + offset = Point3(0, 0, 0) + toon = base.cr.doId2do.get(toonId) + if toon: + toon.pose('swing', 86) + leftHand = toon.find('**/leftHand') + if not leftHand.isEmpty(): + offset = leftHand.getPos(toon) + self.notify.debug('offset = %s' % offset) + else: + self.notify.warning('left hand not found for toon %d' % toonId) + else: + self.notify.warning('toon %d not found' % toonId) + return offset + + def doubleCheckOffset(self, toonId): + if self.attachedToons.has_key(toonId): + curOffset = self.attachedToons[toonId][3] + if curOffset == Point3.zero(): + newOffset = self.calcOffset(toonId) + self.attachedToons[toonId][3] = newOffset + av = base.cr.doId2do.get(toonId) + self.notify.info('correcting wrong offset %s and changing to %s' % (curOffset, newOffset)) + if av: + av.setPos(-newOffset) + + def attachToon(self, toonId, t, facingRight, setupAnim = True): + self.notify.debug('attachToon toonId=%d vineIndex=%d' % (toonId, self.vineIndex)) + temp = Vec3(self.defaultNormal) + offset = self.calcOffset(toonId) + attachNode = self.getAttachNode(toonId) + if facingRight: + attachNode.setH(-90) + else: + attachNode.setH(90) + self.attachedToons[toonId] = [t, + temp, + Vec3(0, 0, 0), + offset, + attachNode, + facingRight, + None] + av = base.cr.doId2do.get(toonId) + if av: + av.reparentTo(attachNode) + if offset == Point3.zero(): + self.notify.warning('calculated offset for %d is zero' % toonId) + av.setPos(-offset) + if setupAnim: + self.setupSwingAnim(toonId) + else: + zDownTheVine = self.getPos().getZ() - t * self.cableLength + attachNode.setPos(self.getPos()) + attachNode.setZ(zDownTheVine) + else: + self.notify.warning('av %d not found' % toonId) + return + + def changeAttachedToonT(self, toonId, t): + if self.attachedToons.has_key(toonId): + oldT = self.attachedToons[toonId][0] + self.attachedToons[toonId][0] = t + oldSwingType = self.calcSwingAnimType(oldT) + newSwingType = self.calcSwingAnimType(t) + if oldSwingType != newSwingType: + self.setupSwingAnim(toonId) + else: + self.notify.warning('changeAttachedToonT avId %d was not in the dict' % toonId) + self.attachToon(toonId, t, 1) + + def changeAttachedToonFacing(self, toonId, facing): + if self.attachedToons.has_key(toonId): + curT = self.attachedToons[toonId][0] + self.detachToon(toonId) + self.attachToon(toonId, curT, facing) + else: + self.notify.warning('changeAttachedToonFacing avId %d was not in the dict' % toonId) + self.attachToon(toonId, VineGameGlobals.VineFellDownT, 1) + + def detachToon(self, toonId): + self.notify.debug('detachToon toonId=%d vineIndex=%d' % (toonId, self.vineIndex)) + if self.attachedToons.has_key(toonId): + self.attachedToons[toonId][4].removeNode() + swingIval = self.attachedToons[toonId][6] + if swingIval: + self.notify.debug('deleting swing ival %s' % swingIval) + swingIval.finish() + self.attachedToons[toonId][6] = None + del swingIval + del self.attachedToons[toonId] + return + + def getAttachedToonInfo(self, toonId): + if self.attachedToons.has_key(toonId): + return self.attachedToons[toonId] + else: + return None + return None + + def getCenterTForTube(self, tubeIndex): + retval = self.tIncrement * tubeIndex + self.tHalfIncrement + return retval + + def updateTubes(self): + newPoint = Vec3(0, 0, 0) + curve = self.rope.ropeNode.getCurve().evaluate() + for tubeIndex in xrange(self.numTubes): + tube = self.tubes[tubeIndex] + t = self.getCenterTForTube(tubeIndex) + curve.evalPoint(t, newPoint) + tube.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + tangent.normalize() + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + rAngle = -90 - degrees + tube.setR(rAngle) + + for tubeIndex in xrange(self.numTubes): + tube = self.tubes2[tubeIndex] + t = self.getCenterTForTube(tubeIndex) + curve.evalPoint(t, newPoint) + tube.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + tangent.normalize() + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + rAngle = -90 - degrees + tube.setR(rAngle) + + def updateSpiders(self): + curve = self.rope.ropeNode.getCurve().evaluate() + if self.hasSpider: + t = self.spiderT + newPoint = Vec3(0, 0, 0) + curve.evalPoint(t, newPoint) + newPoint.setY(-0.5) + self.spider.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + pAngle = degrees + 90 + pAngle = -pAngle + if self.spiderMovingDown: + self.spider.setR(pAngle) + else: + self.spider.setR(pAngle - 180) + + def updateAttachedStuff(self): + self.updateTubes() + self.updateSpiders() + self.updateAttachedToons() + + def updateAttachedToons(self): + curve = self.rope.ropeNode.getCurve().evaluate() + for avId in self.attachedToons.keys(): + self.doubleCheckOffset(avId) + t = self.attachedToons[avId][0] + newPoint = Vec3(0, 0, 0) + curve.evalPoint(t, newPoint) + attachNode = self.attachedToons[avId][4] + attachNode.setPos(newPoint) + tangent = Vec3(0, 0, 0) + curve.evalTangent(t, tangent) + tangent.normalize() + unitY = Vec3(0, 1, 0) + normal = tangent.cross(unitY) + theta = math.atan2(tangent.getZ(), tangent.getX()) + degrees = rad2Deg(theta) + pAngle = degrees + 90 + pAngle *= 0.5 + facingRight = self.attachedToons[avId][5] + if facingRight: + pass + if self.debugTangent: + self.debugTangent.setPos(newPoint + normal) + self.attachedToons[avId][1] = newPoint + self.attachedToons[avId][2] = normal + + def getAngularVelocity(self): + return deg2Rad(self.baseAngle) / self.period / 4.0 + + def getLinearSpeed(self, t): + retval = self.getAngularVelocity() * self.cableLength * t + return retval + + def calcTFromTubeHit(self, colEntry): + name = colEntry.getIntoNodePath().getName() + parts = name.split('-') + if len(parts) < 3: + return + tubeIndex = int(parts[2]) + if tubeIndex < 0 or tubeIndex >= len(self.tubes): + return + if parts[0] == 'StaticVine': + retval = 0 + else: + curve = self.rope.ropeNode.getCurve().evaluate() + tangent = Vec3(0, 0, 0) + centerT = self.getCenterTForTube(tubeIndex) + curve.evalTangent(centerT, tangent) + tangent.normalize() + endPos = colEntry.getSurfacePoint(render) + tubePos = self.tubes[tubeIndex].getPos() + vector = endPos - tubePos + projection = vector.dot(tangent) + self.notify.debug('projection = %s' % projection) + diffT = projection / self.tubeLength / 2.0 + retval = centerT + diffT + P1 = tubePos + P2 = tubePos + tangent + P3 = endPos + u = (P3.getX() - P1.getX()) * (P2.getX() - P1.getX()) + (P3.getZ() - P1.getZ()) * (P2.getZ() - P1.getZ()) + self.notify.debug('u=%s' % u) + x = P1.getX() + u * (P2.getX() - P1.getX()) + z = P1.getZ() + u * (P2.getZ() - P1.getZ()) + perpPoint = Vec3(x, 0, z) + distanceVector = perpPoint - tubePos + distance = distanceVector.length() + diffT = distance / self.cableLength + retval = centerT + diffT + if retval > 1: + retval = 1 + if retval < 0: + retval = 0 + self.notify.debug('retval = %s' % retval) + return retval + + def setupSwingAnimFull(self, av, avId): + toonT = self.attachedToons[avId][0] + playRate = self.SwingAnimPeriod / self.period + swingInterval = Sequence() + duration = (1 - self.swingT) * self.period / 2.0 + if duration < 0.001: + duration = 0.001 + facingRight = self.attachedToons[avId][5] + if self.swingingForward and facingRight or not self.swingingForward and not facingRight: + maxLeftFrame = 108 + downFrame1 = 143 + downFrame2 = 0 + maxRightFrame = 35 + numLeftFramesChoppedOff = (downFrame1 - maxLeftFrame) * (1 - toonT) + numRightFramesChoppedOff = maxRightFrame * (1 - toonT) + numLeftFramesChoppedOff = 0 + numRightFramesChoppedOff = 0 + maxLeftFrame += numLeftFramesChoppedOff + maxRightFrame -= numRightFramesChoppedOff + numFirstHalfFrames = downFrame1 - maxLeftFrame + 1 + numSecondHalfFrames = maxRightFrame - downFrame2 + 1 + numFrames = numFirstHalfFrames + numSecondHalfFrames + framesToChopOff = numFrames * self.swingT + if framesToChopOff < numFirstHalfFrames: + startingFrame = maxLeftFrame + framesToChopOff + halfDur = duration / 2.0 + swing1Dur = (1 - framesToChopOff / numFirstHalfFrames) * halfDur + toonSwing1 = ActorInterval(av, 'swing', startFrame=startingFrame, endFrame=downFrame1, playRate=playRate, name='swingForward1') + toonSwing2 = ActorInterval(av, 'swing', startFrame=downFrame2, endFrame=maxRightFrame, playRate=playRate, name='swingForward2') + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + else: + secondHalfFramesToChopOff = framesToChopOff - numFirstHalfFrames + startingFrame = downFrame2 + secondHalfFramesToChopOff + toonSwing2 = ActorInterval(av, 'swing', startFrame=startingFrame, endFrame=maxRightFrame, playRate=playRate, name='swingForward2') + swingInterval.append(toonSwing2) + else: + maxRightFrame = 35 + maxLeftFrame = 107 + midFrame = (maxLeftFrame + maxRightFrame) / 2.0 + numLeftFramesChoppedOff = 0 + numRightFramesChoppedOff = 0 + maxLeftFrame -= numLeftFramesChoppedOff + maxRightFrame += numRightFramesChoppedOff + numFrames = maxLeftFrame - maxRightFrame + 1 + framesToChopOff = numFrames * self.swingT + startingFrame = maxRightFrame + framesToChopOff + toonSwing = ActorInterval(av, 'swing', startFrame=startingFrame, endFrame=maxLeftFrame, playRate=playRate) + swingInterval.append(toonSwing) + self.attachedToons[avId][6] = swingInterval + swingInterval.start() + + def setupSwingAnimSmall(self, av, avId): + swingInterval = Sequence() + maxLeftFrame = 0 + maxRightFrame = 20 + startingLeftFrame = 5 + numFrames = maxRightFrame - maxLeftFrame + 1 + playRate = self.SwingAnimPeriod / self.period + duration = (1 - self.swingT) * self.period / 2.0 + if duration == 0: + return + toonT = self.attachedToons[avId][0] + framesPerSecondBase = self.MaxNumberOfFramesInSwingAnim / self.SwingAnimPeriod + desiredFramesPerSecond = numFrames / duration + slowedPlayRate = desiredFramesPerSecond / framesPerSecondBase + facingRight = self.attachedToons[avId][5] + if self.swingingForward and facingRight or not self.swingingForward and not facingRight: + toonSwing1 = ActorInterval(av, 'swing', startFrame=startingLeftFrame, endFrame=maxLeftFrame, playRate=slowedPlayRate) + toonSwing2 = ActorInterval(av, 'swing', startFrame=maxLeftFrame + 1, endFrame=maxRightFrame - startingLeftFrame, playRate=slowedPlayRate) + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + else: + toonSwing1 = ActorInterval(av, 'swing', startFrame=maxRightFrame - startingLeftFrame, endFrame=maxRightFrame, playRate=slowedPlayRate) + toonSwing2 = ActorInterval(av, 'swing', startFrame=maxRightFrame, endFrame=startingLeftFrame, playRate=slowedPlayRate) + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + self.attachedToons[avId][6] = swingInterval + swingInterval.start() + + def setupSwingAnimMinimal(self, av, avId): + swingInterval = Sequence() + maxLeftFrame = 88 + maxRightFrame = 84 + duration = (1 - self.swingT) * self.period / 2.0 + if duration < 0.001: + duration = 0.001 + numFrames = maxLeftFrame - maxRightFrame + 1 + numFrames *= 2 + framesPerSecondBase = self.MaxNumberOfFramesInSwingAnim / self.SwingAnimPeriod + desiredFramesPerSecond = numFrames / duration + slowedPlayRate = desiredFramesPerSecond / framesPerSecondBase + toonSwing1 = ActorInterval(av, 'swing', startFrame=maxLeftFrame, endFrame=maxRightFrame, playRate=slowedPlayRate) + toonSwing2 = ActorInterval(av, 'swing', startFrame=maxRightFrame, endFrame=maxLeftFrame, playRate=slowedPlayRate) + swingInterval.append(toonSwing1) + swingInterval.append(toonSwing2) + self.attachedToons[avId][6] = swingInterval + swingInterval.start() + + def setupSwingAnim(self, avId): + if not self.attachedToons.has_key(avId): + return + av = base.cr.doId2do.get(avId) + if not av: + return + prevIval = self.attachedToons[avId][6] + if prevIval: + prevIval.pause() + del prevIval + toonT = self.attachedToons[avId][0] + swingAnimType = self.calcSwingAnimType(toonT) + if swingAnimType == self.SwingAnimFull: + self.setupSwingAnimFull(av, avId) + elif swingAnimType == self.SwingAnimSmall: + self.setupSwingAnimSmall(av, avId) + else: + self.setupSwingAnimMinimal(av, avId) + + def calcSwingAnimType(self, toonT): + angleInDegrees = toonT * self.baseAngle + retval = self.SwingAnimFull + if angleInDegrees > 10: + retval = self.SwingAnimFull + elif angleInDegrees > 2: + retval = self.SwingAnimSmall + else: + retval = self.SwingAnimMinimal + return retval + + def updateSwingAnims(self): + if self.unloading: + return + for avId in self.attachedToons.keys(): + self.setupSwingAnim(avId) diff --git a/toontown/minigame/TagGameGlobals.py b/toontown/minigame/TagGameGlobals.py new file mode 100644 index 0000000..b8bca79 --- /dev/null +++ b/toontown/minigame/TagGameGlobals.py @@ -0,0 +1 @@ +DURATION = 60 diff --git a/toontown/minigame/TagTreasurePlannerAI.py b/toontown/minigame/TagTreasurePlannerAI.py new file mode 100644 index 0000000..429a017 --- /dev/null +++ b/toontown/minigame/TagTreasurePlannerAI.py @@ -0,0 +1,32 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase.ToontownGlobals import * +from toontown.safezone import RegenTreasurePlannerAI +import DistributedTagTreasureAI + +class TagTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('TagTreasurePlannerAI') + + def __init__(self, zoneId, callback): + self.numPlayers = 0 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedTagTreasureAI.DistributedTagTreasureAI, 'TagTreasurePlanner-' + str(zoneId), 3, 4, callback) + return None + + def initSpawnPoints(self): + self.spawnPoints = [(0, 0, 0.1), + (5, 20, 0.1), + (0, 40, 0.1), + (-5, -20, 0.1), + (0, -40, 0.1), + (20, 0, 0.1), + (40, 5, 0.1), + (-20, -5, 0.1), + (-40, 0, 0.1), + (22, 20, 0.1), + (-20, 22, 0.1), + (20, -20, 0.1), + (-25, -20, 0.1), + (20, 40, 0.1), + (20, -44, 0.1), + (-24, 40, 0.1), + (-20, -40, 0.1)] + return self.spawnPoints diff --git a/toontown/minigame/TargetGameGlobals.py b/toontown/minigame/TargetGameGlobals.py new file mode 100644 index 0000000..2adfbd2 --- /dev/null +++ b/toontown/minigame/TargetGameGlobals.py @@ -0,0 +1,197 @@ +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +ENDLESS_GAME = config.GetBool('endless-ring-game', 0) +NUM_RING_GROUPS = 16 +MAX_TOONXZ = 15.0 +MAX_LAT = 5 +MAX_FIELD_SPAN = 135 +CollisionRadius = 1.5 +CollideMask = ToontownGlobals.CatchGameBitmask +TARGET_RADIUS = MAX_TOONXZ / 3.0 * 0.9 +targetColors = ((TTLocalizer.ColorRed, VBase4(1.0, 0.4, 0.2, 1.0)), + (TTLocalizer.ColorGreen, VBase4(0.0, 0.9, 0.2, 1.0)), + (TTLocalizer.ColorOrange, VBase4(1.0, 0.5, 0.25, 1.0)), + (TTLocalizer.ColorPurple, VBase4(1.0, 0.0, 1.0, 1.0)), + (TTLocalizer.ColorWhite, VBase4(1.0, 1.0, 1.0, 1.0)), + (TTLocalizer.ColorBlack, VBase4(0.0, 0.0, 0.0, 1.0)), + (TTLocalizer.ColorYellow, VBase4(1.0, 1.0, 0.2, 1.0))) +ENVIRON_LENGTH = 300 +ENVIRON_WIDTH = 150.0 +ringColorSelection = [(0, 1, 2), + 3, + 4, + 5, + 6] +colorRed = {} +colorRed['Red'] = 1.0 +colorRed['Green'] = 0.0 +colorRed['Blue'] = 0.0 +colorRed['Alpha'] = 0.5 +colorBlue = {} +colorBlue['Red'] = 0.0 +colorBlue['Green'] = 0.0 +colorBlue['Blue'] = 1.0 +colorBlue['Alpha'] = 0.5 +colorGreen = {} +colorGreen['Red'] = 0.0 +colorGreen['Green'] = 1.0 +colorGreen['Blue'] = 0.0 +colorGreen['Alpha'] = 0.5 +colorYellow = {} +colorYellow['Red'] = 1.0 +colorYellow['Green'] = 1.0 +colorYellow['Blue'] = 0.0 +colorYellow['Alpha'] = 0.5 +colorPurple = {} +colorPurple['Red'] = 0.75 +colorPurple['Green'] = 0.0 +colorPurple['Blue'] = 1.0 +colorPurple['Alpha'] = 0.5 +colorOrange = {} +colorOrange['Red'] = 1.0 +colorOrange['Green'] = 0.6 +colorOrange['Blue'] = 0.0 +colorOrange['Alpha'] = 0.5 +colorBlack = {} +colorBlack['Red'] = 0.0 +colorBlack['Green'] = 0.0 +colorBlack['Blue'] = 0.0 +colorBlack['Alpha'] = 1.0 +colorWhite = {} +colorWhite['Red'] = 1.0 +colorWhite['Green'] = 1.0 +colorWhite['Blue'] = 1.0 +colorWhite['Alpha'] = 1.0 +difficultyPatterns = {ToontownGlobals.ToontownCentral: [[8, + 4, + 2, + 0], + [10, + 16, + 21, + 28], + [31, + 15, + 7, + 3.5], + [colorRed, + colorGreen, + colorBlue, + colorYellow], + [2, + 2, + 2, + 1], + 10, + 2], + ToontownGlobals.DonaldsDock: [[7, + 4, + 2, + 0], + [11, + 17, + 23, + 32], + [29, + 13, + 6.5, + 3.2], + [colorRed, + colorGreen, + colorBlue, + colorYellow], + [2, + 2, + 2, + 1], + 9, + 2], + ToontownGlobals.DaisyGardens: [[6, + 4, + 2, + 0], + [11, + 18, + 25, + 34], + [29, + 13, + 6.5, + 3.1], + [colorRed, + colorGreen, + colorBlue, + colorYellow], + [2, + 2, + 2, + 1], + 8, + 2], + ToontownGlobals.MinniesMelodyland: [[6, + 4, + 2, + 0], + [12, + 19, + 27, + 37], + [28, + 12, + 6, + 3.0], + [colorGreen, + colorBlue, + colorYellow, + colorPurple], + [2, + 2, + 2, + 1], + 8, + 2], + ToontownGlobals.TheBrrrgh: [[5, + 4, + 2, + 0], + [12, + 20, + 29, + 40], + [25, + 12, + 5.5, + 2.5], + [colorGreen, + colorBlue, + colorYellow, + colorPurple], + [2, + 2, + 2, + 1], + 7, + 2], + ToontownGlobals.DonaldsDreamland: [[4, + 3, + 1, + 0], + [12, + 21, + 31, + 42], + [20, + 10, + 4.5, + 2.0], + [colorBlue, + colorYellow, + colorPurple, + colorOrange], + [2, + 2, + 2, + 1], + 7, + 2]} diff --git a/toontown/minigame/TempMinigameAI.py b/toontown/minigame/TempMinigameAI.py new file mode 100644 index 0000000..c524284 --- /dev/null +++ b/toontown/minigame/TempMinigameAI.py @@ -0,0 +1,26 @@ +from toontown.toonbase import ToontownGlobals +ALLOW_TEMP_MINIGAMES = simbase.config.GetBool('allow-temp-minigames', False) +TEMP_MG_ID_COUNTER = ToontownGlobals.TravelGameId - 1 +TempMgCtors = {} + +def _printMessage(message): + print '\n\n!!!', message, '\n\n' + + +def _registerTempMinigame(name, Class, id, minPlayers = 1, maxPlayers = 4): + if not ALLOW_TEMP_MINIGAMES: + _printMessage('registerTempMinigame WARNING: allow-temp-minigames config is set to false, but we are trying to register temp minigame ' + name) + import traceback + traceback.print_stack() + return + ToontownGlobals.MinigameIDs += (id,) + ToontownGlobals.MinigameNames[name] = id + TempMgCtors[id] = Class + for i in range(minPlayers, maxPlayers): + ToontownGlobals.MinigamePlayerMatrix[i] += (id,) + + _printMessage('registerTempMinigame: ' + name) + + +if ALLOW_TEMP_MINIGAMES: + pass diff --git a/toontown/minigame/ToonBlitzAssetMgr.py b/toontown/minigame/ToonBlitzAssetMgr.py new file mode 100644 index 0000000..d1aac87 --- /dev/null +++ b/toontown/minigame/ToonBlitzAssetMgr.py @@ -0,0 +1,153 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import ToonBlitzGlobals, TwoDBlock +from pandac.PandaModules import CardMaker + +class ToonBlitzAssetMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonBlitzAssets') + + def __init__(self, game): + self.__defineConstants() + self.game = game + self.load() + + def __defineConstants(self): + pass + + def load(self): + self.world = NodePath('ToonBlitzWorld') + self.background = loader.loadModel('phase_4/models/minigames/toonblitz_game') + self.background.reparentTo(self.world) + self.startingWall = loader.loadModel('phase_4/models/minigames/toonblitz_game_wall') + self.startingPipe = loader.loadModel('phase_4/models/minigames/toonblitz_game_start') + self.exitElevator = loader.loadModel('phase_4/models/minigames/toonblitz_game_elevator') + self.arrow = loader.loadModel('phase_4/models/minigames/toonblitz_game_arrow') + self.sprayProp = loader.loadModel('phase_4/models/minigames/prop_waterspray') + self.treasureModelList = [] + salesIcon = loader.loadModel('phase_4/models/minigames/salesIcon') + self.treasureModelList.append(salesIcon) + moneyIcon = loader.loadModel('phase_4/models/minigames/moneyIcon') + self.treasureModelList.append(moneyIcon) + legalIcon = loader.loadModel('phase_4/models/minigames/legalIcon') + self.treasureModelList.append(legalIcon) + corpIcon = loader.loadModel('phase_4/models/minigames/corpIcon') + self.treasureModelList.append(corpIcon) + self.particleGlow = loader.loadModel('phase_4/models/minigames/particleGlow') + self.blockTypes = [] + for i in xrange(4): + blockType = loader.loadModel('phase_4/models/minigames/toonblitz_game_block0' + str(i)) + self.blockTypes.append(blockType) + + self.stomper = loader.loadModel('phase_4/models/minigames/toonblitz_game_stomper') + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + dropPlane = CollisionNode('dropPlane') + dropPlane.addSolid(plane) + dropPlane.setCollideMask(ToontownGlobals.FloorBitmask) + self.world.attachNewNode(dropPlane) + self.gameMusic = base.loadMusic('phase_4/audio/bgm/MG_TwoDGame.mid') + self.treasureGrabSound = loader.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.mp3') + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.soundJump = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_jump.mp3') + self.fallSound = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_fall.mp3') + self.watergunSound = base.loadSfx('phase_4/audio/sfx/AA_squirt_seltzer_miss.mp3') + self.splashSound = base.loadSfx('phase_4/audio/sfx/Seltzer_squirt_2dgame_hit.mp3') + self.threeSparkles = loader.loadSfx('phase_4/audio/sfx/threeSparkles.mp3') + self.sparkleSound = loader.loadSfx('phase_4/audio/sfx/sparkly.mp3') + self.headCollideSound = loader.loadSfx('phase_3.5/audio/sfx/AV_collision.mp3') + self.faceStartPos = Vec3(-0.8, 0, -0.87) + self.faceEndPos = Vec3(0.8, 0, -0.87) + self.aspect2dRoot = aspect2d.attachNewNode('TwoDGuiAspect2dRoot') + self.aspect2dRoot.setDepthWrite(1) + self.cardMaker = CardMaker('card') + self.cardMaker.reset() + self.cardMaker.setName('ProgressLine') + self.cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5) + self.progressLine = self.aspect2dRoot.attachNewNode(self.cardMaker.generate()) + self.progressLine.setScale(self.faceEndPos[0] - self.faceStartPos[0], 1, 0.01) + self.progressLine.setPos(0, 0, self.faceStartPos[2]) + self.cardMaker.setName('RaceProgressLineHash') + for n in xrange(ToonBlitzGlobals.NumSections[self.game.getSafezoneId()] + 1): + hash = self.aspect2dRoot.attachNewNode(self.cardMaker.generate()) + hash.setScale(self.progressLine.getScale()[2], 1, self.progressLine.getScale()[2] * 5) + t = float(n) / ToonBlitzGlobals.NumSections[self.game.getSafezoneId()] + hash.setPos(self.faceStartPos[0] * (1 - t) + self.faceEndPos[0] * t, self.faceStartPos[1], self.faceStartPos[2]) + + def destroy(self): + while len(self.blockTypes): + blockType = self.blockTypes[0] + self.blockTypes.remove(blockType) + del blockType + + self.blockTypes = None + while len(self.treasureModelList): + treasureModel = self.treasureModelList[0] + self.treasureModelList.remove(treasureModel) + del treasureModel + + self.treasureModelList = None + self.startingWall.removeNode() + del self.startingWall + self.startingPipe.removeNode() + del self.startingPipe + self.exitElevator.removeNode() + del self.exitElevator + self.stomper.removeNode() + del self.stomper + self.arrow.removeNode() + del self.arrow + self.sprayProp.removeNode() + del self.sprayProp + self.aspect2dRoot.removeNode() + del self.aspect2dRoot + self.world.removeNode() + del self.world + del self.gameMusic + del self.treasureGrabSound + del self.sndOof + del self.soundJump + del self.fallSound + del self.watergunSound + del self.splashSound + del self.threeSparkles + del self.sparkleSound + del self.headCollideSound + self.game = None + return + + def onstage(self): + self.world.reparentTo(render) + base.playMusic(self.gameMusic, looping=1, volume=0.9) + + def offstage(self): + self.world.hide() + self.gameMusic.stop() + + def enterPlay(self): + pass + + def exitPlay(self): + pass + + def enterPause(self): + pass + + def exitPause(self): + pass + + def playJumpSound(self): + base.localAvatar.soundRun.stop() + base.playSfx(self.soundJump, looping=0) + + def playWatergunSound(self): + self.watergunSound.stop() + base.playSfx(self.watergunSound, looping=0) + + def playSplashSound(self): + self.splashSound.stop() + base.playSfx(self.splashSound, looping=0) + + def playHeadCollideSound(self): + self.headCollideSound.stop() + base.playSfx(self.headCollideSound, looping=0) diff --git a/toontown/minigame/ToonBlitzGlobals.py b/toontown/minigame/ToonBlitzGlobals.py new file mode 100644 index 0000000..765082f --- /dev/null +++ b/toontown/minigame/ToonBlitzGlobals.py @@ -0,0 +1,778 @@ +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import BitMask32 +ShowScoresDuration = 4.0 +EndlessGame = config.GetBool('endless-2d-game', 0) +ScoreToJellyBeansMultiplier = 5 +ScoreGainPerTreasure = 1 +ToonStartingPosition = (-39, 0, 13.59) +CameraStartingPosition = (-28, + -53, + 17.3, + 0, + 0, + 0) +GameDuration = {ToontownGlobals.ToontownCentral: 150, + ToontownGlobals.DonaldsDock: 145, + ToontownGlobals.DaisyGardens: 140, + ToontownGlobals.MinniesMelodyland: 135, + ToontownGlobals.TheBrrrgh: 130, + ToontownGlobals.DonaldsDreamland: 125} +BaseBonusOnCompletion = {ToontownGlobals.ToontownCentral: 15, + ToontownGlobals.DonaldsDock: 17, + ToontownGlobals.DaisyGardens: 19, + ToontownGlobals.MinniesMelodyland: 21, + ToontownGlobals.TheBrrrgh: 23, + ToontownGlobals.DonaldsDreamland: 25} +BonusPerSecondLeft = 0.8 +ScoreLossPerEnemyCollision = {ToontownGlobals.ToontownCentral: -1, + ToontownGlobals.DonaldsDock: -1, + ToontownGlobals.DaisyGardens: -1, + ToontownGlobals.MinniesMelodyland: -1, + ToontownGlobals.TheBrrrgh: -1, + ToontownGlobals.DonaldsDreamland: -1} +ScoreLossPerFallDown = {ToontownGlobals.ToontownCentral: -0, + ToontownGlobals.DonaldsDock: -0, + ToontownGlobals.DaisyGardens: -0, + ToontownGlobals.MinniesMelodyland: -0, + ToontownGlobals.TheBrrrgh: -0, + ToontownGlobals.DonaldsDreamland: -0} +ScoreLossPerStomperSquish = {ToontownGlobals.ToontownCentral: -1, + ToontownGlobals.DonaldsDock: -1, + ToontownGlobals.DaisyGardens: -1, + ToontownGlobals.MinniesMelodyland: -1, + ToontownGlobals.TheBrrrgh: -1, + ToontownGlobals.DonaldsDreamland: -1} +SectionWeights = {ToontownGlobals.ToontownCentral: ((0, 25), + (1, 25), + (2, 25), + (3, 15), + (4, 10), + (5, 0)), + ToontownGlobals.DonaldsDock: ((0, 15), + (1, 25), + (2, 25), + (3, 15), + (4, 10), + (5, 10)), + ToontownGlobals.DaisyGardens: ((0, 15), + (1, 15), + (2, 25), + (3, 25), + (4, 10), + (5, 10)), + ToontownGlobals.MinniesMelodyland: ((0, 10), + (1, 10), + (2, 25), + (3, 25), + (4, 15), + (5, 15)), + ToontownGlobals.TheBrrrgh: ((0, 10), + (1, 10), + (2, 15), + (3, 25), + (4, 25), + (5, 15)), + ToontownGlobals.DonaldsDreamland: ((0, 10), + (1, 10), + (2, 15), + (3, 15), + (4, 25), + (5, 25))} +NumSections = {ToontownGlobals.ToontownCentral: 5, + ToontownGlobals.DonaldsDock: 5, + ToontownGlobals.DaisyGardens: 5, + ToontownGlobals.MinniesMelodyland: 5, + ToontownGlobals.TheBrrrgh: 5, + ToontownGlobals.DonaldsDreamland: 5} +PercentMaxEnemies = {ToontownGlobals.ToontownCentral: 50, + ToontownGlobals.DonaldsDock: 60, + ToontownGlobals.DaisyGardens: 70, + ToontownGlobals.MinniesMelodyland: 80, + ToontownGlobals.TheBrrrgh: 90, + ToontownGlobals.DonaldsDreamland: 100} +PercentMaxTreasures = {ToontownGlobals.ToontownCentral: 100, + ToontownGlobals.DonaldsDock: 100, + ToontownGlobals.DaisyGardens: 100, + ToontownGlobals.MinniesMelodyland: 100, + ToontownGlobals.TheBrrrgh: 100, + ToontownGlobals.DonaldsDreamland: 100} +PercentMaxSpawnPoints = {ToontownGlobals.ToontownCentral: 100, + ToontownGlobals.DonaldsDock: 90, + ToontownGlobals.DaisyGardens: 80, + ToontownGlobals.MinniesMelodyland: 70, + ToontownGlobals.TheBrrrgh: 60, + ToontownGlobals.DonaldsDreamland: 50} +PercentMaxStompers = {ToontownGlobals.ToontownCentral: 50, + ToontownGlobals.DonaldsDock: 60, + ToontownGlobals.DaisyGardens: 70, + ToontownGlobals.MinniesMelodyland: 80, + ToontownGlobals.TheBrrrgh: 90, + ToontownGlobals.DonaldsDreamland: 100} +TreasureValueProbability = {1: 4, + 2: 3, + 3: 2, + 4: 1} +BLOCK_H24 = 'BlockH24' +BLOCK_V24F = 'BlockV24F' +BLOCK_V24B = 'BlockV24B' +BLOCK_H12 = 'BlockH12' +BLOCK_V12F = 'BlockV12F' +BLOCK_V12B = 'BlockV12B' +BLOCK_H6 = 'BlockH6' +BLOCK_V6F = 'BlockV6F' +BLOCK_V6B = 'BlockV6B' +BLOCK_H3 = 'BlockH3' +BLOCK_V3F = 'BlockV3F' +BLOCK_V3B = 'BlockV3B' +BlockTypes = {BLOCK_H24: ('00', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V24F: ('00', + (0, 0, 0), + (0, 0, 270), + (1, 1, 1)), + BLOCK_V24B: ('00', + (0, 0, 0), + (0, 0, 90), + (1, 1, 1)), + BLOCK_H12: ('01', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V12F: ('01', + (0, 0, 0), + (0, 0, 270), + (1, 1, 1)), + BLOCK_V12B: ('01', + (0, 0, 0), + (0, 0, 90), + (1, 1, 1)), + BLOCK_H6: ('02', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V6F: ('02', + (0, 0, 0), + (0, 0, 270), + (0.86, 1, 1)), + BLOCK_V6B: ('02', + (0, 0, 0), + (0, 0, 90), + (0.86, 1, 1)), + BLOCK_H3: ('03', + (0, 0, 0), + (0, 0, 0), + (1, 1, 1)), + BLOCK_V3F: ('03', + (0, 0, 0), + (0, 0, 270), + (1, 1, 1)), + BLOCK_V3B: ('03', + (0, 0, 0), + (0, 0, 90), + (1, 1, 1))} +BlocksList = [] +SpawnPointList = [] +DamagePerBullet = 25 +EnemyBaseHealth = 25 +EnemyHealthMultiplier = {'f': 1, + 'p': 1, + 'ym': 1, + 'mm': 1, + 'bf': 1, + 'b': 1, + 'dt': 1, + 'sc': 1, + 'pp': 1, + 'tw': 1, + 'cc': 1, + 'tm': 1} +NumEnemies = 2 +EnemyList = [] +NumTreasures = 5 +TreasureList = [] +BlockListStart = [[BLOCK_H24, [(0, 0, 12)]]] +BlockListEnd = [[BLOCK_H24, [(0, 0, 12)]], [BLOCK_H24, [(24, 0, 12)]], [BLOCK_H24, [(48, 0, 12)]]] +SpawnPointListEnd = [[(45, 0, 16)]] +BlockList0 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(22.4, 0, 12)]], + [BLOCK_H24, [(42.9, 0, 12)]], + [BLOCK_H24, [(30, 0, 18.75)]], + [BLOCK_H24, [(58, 0, 18.75)]], + [BLOCK_H24, [(0, 0, 18.75)]]] +EnemyList0 = [['tm', [(35, 0, 12.65), (30, 0, 12.65), 2]], ['tm', [(24, 0, 12.65), (24, 0, 30.0), 2]]] +TreasureList0 = [[(16, 0, 16)], + [(30, 0, 23)], + [(40, 0, 23)], + [(50, 0, 16)], + [(50, 0, 23)]] +SpawnPointList0 = [[(9, 0, 16), (4, 0, 16)], [(80, 0, 16)]] +StomperTypes = {1: (1, + 2, + [0, -4], + [1, 6.75], + 3), + 2: (1, + 5, + [0.6, -0.34], + [0.2, 1.5], + 2), + 3: (1, + 2, + [0, -9.85], + [1, 15], + 5)} +BlockList1 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(16, 0, 18.75)]], + [BLOCK_H12, [(36, 0, 12)]], + [BLOCK_H12, [(64, 0, 12)]], + [BLOCK_H24, [(19, 0, 5.25)]], + [BLOCK_H24, [(43, 0, 5.25)]], + [BLOCK_H24, [(67, 0, 5.25)]], + [BLOCK_H12, [(82, 0, 18.75)]]] +TreasureList1 = [[(8, 0, 27.25)], + [(36, 0, 27.25)], + [(20, 0, 7.25)], + [(32, 0, 7.25)], + [(56, 0, 22.5)], + [(56, 0, 7.25)], + [(80, 0, 7.25)], + [(90, 0, 7.25)], + [(74, 0, 27.25)], + [(102, 0, 27.25)]] +SpawnPointList1 = [[(2, 0, 16)], [(73, 0, 16)]] +EnemyList1 = [['f', [(20, 0, 6), (50, 0, 6), 3.5]], + ['bf', [(50, 0, 6), (20, 0, 6), 3.5]], + ['f', [(62, 0, 6), (90, 0, 6), 3.5]], + ['bf', [(90, 0, 6), (62, 0, 6), 3.5]], + ['dt', [(52, 0, 6), (52, 0, 28), 3.5]], + ['b', [(60, 0, 28), (60, 0, 6), 3.5]]] +BlockList2 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(16, 0, 18.75)]], + [BLOCK_H6, [(34, 0, 12)]], + [BLOCK_H12, [(25, 0, 5.25)]], + [BLOCK_H24, [(34, 0, 25.5)]], + [BLOCK_H12, [(64, 0, 18.75)]], + [BLOCK_H6, [(52, 0, 12)]], + [BLOCK_H12, [(55, 0, 5.25)]], + [BLOCK_H12, [(80, 0, 12), (100, 0, 12), 2.5]]] +TreasureList2 = [[(8, 0, 27.25)], + [(26, 0, 7.25)], + [(36, 0, 7.25)], + [(26, 0, 34)], + [(66, 0, 34)], + [(42, 0, 23)], + [(46, 0, 21)], + [(50, 0, 23)], + [(56, 0, 7.25)], + [(66, 0, 7.25)], + [(84, 0, 28)], + [(20, 0, 14)], + [(72, 0, 14)]] +SpawnPointList2 = [[(2, 0, 16)], [(72, 0, 22.75)]] +EnemyList2 = [['p', [(31, 0, 6), (31, 0, 31), 3.5]], + ['ym', [(31, 0, 31), (31, 0, 6), 3.5]], + ['bf', [(61, 0, 6), (61, 0, 31), 3.5]], + ['b', [(61, 0, 31), (61, 0, 6), 3.5]], + ['sc', [(35, 0, 26.25), (57, 0, 26.25), 3.5]], + ['tw', [(57, 0, 26.25), (35, 0, 26.25), 3.5]]] +BlockList3 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(12, 0, 5.25), (36, 0, 25.5), 3]], + [BLOCK_H12, [(48, 0, 25.5)]], + [BLOCK_H12, [(60, 0, 25.5), (84, 0, 5.25), 3]], + [BLOCK_H12, [(102, 0, 5.25)]], + [BLOCK_H12, [(120, 0, 5.25), (138, 0, 5.25), 2.5]], + [BLOCK_H12, [(156, 0, 5.25)]], + [BLOCK_H12, [(174, 0, 5.25), (174, 0, 25.5), 2.5]]] +TreasureList3 = [[(28, 0, 32)], + [(24, 0, 28)], + [(20, 0, 24)], + [(16, 0, 20)], + [(80, 0, 32)], + [(84, 0, 28)], + [(88, 0, 24)], + [(92, 0, 20)], + [(48, 0, 34)], + [(60, 0, 34)], + [(125, 0, 15)], + [(135, 0, 15)], + [(145, 0, 15)], + [(180, 0, 7.25)], + [(168, 0, 34)], + [(192, 0, 34)]] +SpawnPointList3 = [[(2, 0, 16)], + [(56, 0, 29.5)], + [(108, 0, 9.25)], + [(162, 0, 9.25)]] +EnemyList3 = [['p', [(31, 0, 6), (31, 0, 31), 3.5]], + ['ym', [(31, 0, 31), (31, 0, 6), 3.5]], + ['bf', [(61, 0, 6), (61, 0, 31), 3.5]], + ['b', [(61, 0, 31), (61, 0, 6), 3.5]], + ['sc', [(35, 0, 26.25), (57, 0, 26.25), 3.5]], + ['tw', [(57, 0, 26.25), (35, 0, 26.25), 3.5]]] +BlockList5 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(24, 0, 12)]], + [BLOCK_H24, [(48, 0, 12)]], + [BLOCK_H6, [(12, 0, 18.75)]], + [BLOCK_H6, [(18, 0, 25.5)]], + [BLOCK_H6, [(24, 0, 18.75)]], + [BLOCK_H6, [(44, 0, 18.75)]], + [BLOCK_H6, [(50, 0, 25.5)]], + [BLOCK_H6, [(56, 0, 18.75)]]] +TreasureList5 = [[(15, 0, 20.75)], + [(21, 0, 27.5)], + [(27, 0, 20.75)], + [(47, 0, 20.75)], + [(53, 0, 27.5)], + [(59, 0, 20.75)]] +SpawnPointList5 = [[(2, 0, 16)]] +BlockList6 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(0, 0, 5.25)]], + [BLOCK_H24, [(24, 0, 5.25)]], + [BLOCK_H24, [(48, 0, 5.25)]], + [BLOCK_H12, [(72, 0, 5.25)]], + [BLOCK_H12, [(30, 0, 12)]], + [BLOCK_H12, [(48, 0, 12)]], + [BLOCK_H24, [(66, 0, 12)]]] +TreasureList6 = [[(27, 0, 20.75)], + [(45, 0, 20.75)], + [(63, 0, 20.75)], + [(3, 0, 7.25)], + [(15, 0, 7.25)], + [(36, 0, 7.25)], + [(54, 0, 7.25)], + [(70, 0, 7.25)], + [(81, 0, 7.25)]] +SpawnPointList6 = [[(2, 0, 16)]] +BlockList7 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(10, 0, 18.75)]], + [BLOCK_H12, [(20, 0, 25.5)]], + [BLOCK_H12, [(30, 0, 18.75)]], + [BLOCK_H24, [(40, 0, 12)]], + [BLOCK_H24, [(14, 0, 5.25)]], + [BLOCK_H3, [(11, 0, 5.25)]], + [BLOCK_H3, [(38, 0, 5.25)]], + [BLOCK_H12, [(62, 0, 18.75)]], + [BLOCK_H12, [(72, 0, 12)]], + [BLOCK_H12, [(82, 0, 5.25)]], + [BLOCK_H12, [(92, 0, 12)]], + [BLOCK_H12, [(102, 0, 18.75)]], + [BLOCK_H12, [(112, 0, 12)]], + [BLOCK_H24, [(76, 0, 25.5)]], + [BLOCK_H3, [(73, 0, 25.5)]], + [BLOCK_H3, [(100, 0, 25.5)]]] +TreasureList7 = [[(12, 0, 34)], + [(26, 0, 34)], + [(40, 0, 34)], + [(26, 0, 23)], + [(26, 0, 12)], + [(13, 0, 7.25)], + [(40, 0, 7.25)], + [(88, 0, 34)], + [(112, 0, 34)], + [(64, 0, 34)], + [(88, 0, 7.25)], + [(88, 0, 23)]] +SpawnPointList7 = [[(2, 0, 16)]] +BlockList8 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H24, [(0, 0, 5.25)]], + [BLOCK_H12, [(16, 0, 18.75)]], + [BLOCK_H24, [(32, 0, 25.5)]], + [BLOCK_H12, [(64, 0, 25.5)]], + [BLOCK_H12, [(84, 0, 25.5)]], + [BLOCK_H6, [(104, 0, 25.5)]], + [BLOCK_H6, [(118, 0, 25.5)]], + [BLOCK_H3, [(132, 0, 25.5)]], + [BLOCK_H3, [(143, 0, 25.5)]], + [BLOCK_H3, [(154, 0, 25.5)]], + [BLOCK_H24, [(32, 0, 5.25)]], + [BLOCK_H12, [(64, 0, 5.25)]], + [BLOCK_H12, [(84, 0, 5.25)]], + [BLOCK_H6, [(104, 0, 5.25)]], + [BLOCK_H6, [(118, 0, 5.25)]], + [BLOCK_H3, [(132, 0, 5.25)]], + [BLOCK_H3, [(143, 0, 5.25)]], + [BLOCK_H3, [(154, 0, 5.25)]], + [BLOCK_H3, [(161, 0, 18.75)]], + [BLOCK_H6, [(160, 0, 12)]]] +TreasureList8 = [[(1, 0, 7.25)], + [(4, 0, 7.25)], + [(26, 0, 34)], + [(10, 0, 28)], + [(154, 0, 20.75)], + [(60, 0, 34)], + [(80, 0, 34)], + [(100, 0, 34)], + [(114, 0, 34)], + [(128, 0, 34)], + [(139, 0, 34)], + [(150, 0, 34)], + [(60, 0, 13.75)], + [(80, 0, 13.75)], + [(100, 0, 13.75)], + [(114, 0, 13.75)], + [(128, 0, 13.75)], + [(139, 0, 13.75)], + [(150, 0, 13.75)]] +SpawnPointList8 = [[(2, 0, 16)], [(128, 0, 29.5), (121, 0, 29.5)], [(128, 0, 9.25), (121, 0, 9.25)]] +BlockList9 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H12, [(28, 0, 12), (50, 0, 12), 3]], + [BLOCK_H12, [(66, 0, 12)]], + [BLOCK_H12, [(82, 0, 12), (104, 0, 12), 3]], + [BLOCK_H12, [(120, 0, 12)]], + [BLOCK_H12, [(136, 0, 25.5), (136, 0, 5.25), 3]], + [BLOCK_H12, [(152, 0, 25.5)]], + [BLOCK_H12, [(152, 0, 5.25)]], + [BLOCK_H12, [(168, 0, 25.5), (168, 0, 5.25), 3]], + [BLOCK_H12, [(184, 0, 12)]]] +TreasureList9 = [[(36, 0, 23)], + [(45, 0, 23)], + [(54, 0, 23)], + [(90, 0, 23)], + [(99, 0, 23)], + [(108, 0, 23)], + [(142, 0, 35)], + [(142, 0, 8)], + [(130, 0, 30)], + [(158, 0, 7.25)], + [(158, 0, 27.5)], + [(174, 0, 35)], + [(174, 0, 8)], + [(186, 0, 30)]] +SpawnPointList9 = [[(2, 0, 16)], [(126, 0, 16)]] +BlockList10 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H12, [(14, 0, 12), (40, 0, 12), 3.5]], + [BLOCK_H6, [(40, 0, 25.5)]], + [BLOCK_H6, [(40, 0, 5.25)]], + [BLOCK_H12, [(54, 0, 32), (54, 0, -1.5), 3.5]], + [BLOCK_H6, [(74, 0, 25.5)]], + [BLOCK_H6, [(74, 0, 5.25)]], + [BLOCK_H12, [(82, 0, 5.25), (114, 0, 5.25), 3.7]], + [BLOCK_H6, [(130, 0, 5.25)]], + [BLOCK_H6, [(130, 0, 25.5)]], + [BLOCK_H12, [(144, 0, 32), (144, 0, -1.5), 3.5]], + [BLOCK_H6, [(164, 0, 25.5)]], + [BLOCK_H6, [(164, 0, 5.25)]], + [BLOCK_H12, [(202, 0, 25.5), (172, 0, 25.5), 4]]] +TreasureList10 = [[(43, 0, 27.5)], + [(43, 0, 7.25)], + [(32, 0, 32)], + [(77, 0, 27.5)], + [(77, 0, 7.25)], + [(88, 0, 32)], + [(97, 0, 15)], + [(105, 0, 15)], + [(113, 0, 15)], + [(133, 0, 27.5)], + [(133, 0, 7.25)], + [(122, 0, 32)], + [(167, 0, 27.5)], + [(167, 0, 7.25)], + [(173, 0, 14)]] +SpawnPointList10 = [[(2, 0, 16)], [(133, 0, 9.25)], [(77, 0, 29.5)]] +BlockList11 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(24, 0, 12)]], + [BLOCK_H24, [(24, 0, 18.75)]], + [BLOCK_H24, [(48, 0, 12)]], + [BLOCK_H6, [(12, 0, 25.5)]], + [BLOCK_H6, [(54, 0, 25.5)]], + [BLOCK_H24, [(72, 0, 5.25)]], + [BLOCK_H12, [(78, 0, 18.75)]], + [BLOCK_H24, [(96, 0, 12)]], + [BLOCK_H24, [(120, 0, 18.75)]], + [BLOCK_H6, [(110, 0, 25.5)]], + [BLOCK_H6, [(148, 0, 25.5)]]] +TreasureList11 = [[(4, 0, 34)], + [(26, 0, 34)], + [(46, 0, 34)], + [(68, 0, 34)], + [(74, 0, 7.25)], + [(94, 0, 7.25)], + [(102, 0, 34)], + [(124, 0, 34)], + [(140, 0, 34)], + [(162, 0, 34)]] +EnemyList11 = [['cc', [(26, 0, 12.75), (46, 0, 12.75), 3.5]], ['tm', [(74, 0, 6), (94, 0, 6), 3.5]], ['sc', [(122, 0, 19.5), (142, 0, 19.5), 3.5]]] +SpawnPointList11 = [[(2, 0, 16)], [(70, 0, 16)]] +BlockList12 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(9, 0, 5.25)]], + [BLOCK_H24, [(33, 0, 5.25)]], + [BLOCK_H24, [(32, 0, 12)]], + [BLOCK_H24, [(56, 0, 12)]], + [BLOCK_H24, [(80, 0, 12)]], + [BLOCK_H12, [(60, 0, 18.75)]], + [BLOCK_H24, [(88, 0, 25.5)]], + [BLOCK_V12B, [(103.2, 0, 24.72)]]] +TreasureList12 = [[(12, 0, 7.25)], + [(55, 0, 7.25)], + [(52, 0, 7.25)], + [(36, 0, 34)], + [(64, 0, 34)], + [(68, 0, 34)], + [(100, 0, 14)], + [(95, 0, 22)], + [(90, 0, 14)]] +SpawnPointList12 = [[(2, 0, 16)]] +StomperList12 = [[1, (13, 0, 20.75), 2], [1, (50, 0, 20.75), 2], [1, (82, 0, 20.75), 1.5]] +BlockList13 = [[BLOCK_H24, [(0, 0, 12)]], + [BLOCK_H24, [(24, 0, 12)]], + [BLOCK_H12, [(48, 0, 12)]], + [BLOCK_H24, [(22, 0, 25.5)]], + [BLOCK_H24, [(64, 0, 18.75)]], + [BLOCK_H24, [(96, 0, 18.75)]], + [BLOCK_H24, [(126, 0, 12)]], + [BLOCK_H24, [(150, 0, 12)]], + [BLOCK_H24, [(174, 0, 12)]], + [BLOCK_H24, [(148, 0, 25.5)]], + [BLOCK_H6, [(190, 0, 18.75)]]] +TreasureList13 = [[(2, 0, 34)], + [(66, 0, 34)], + [(91, 0, 29)], + [(12, 0, 23)], + [(20, 0, 23)], + [(48, 0, 23)], + [(56, 0, 23)], + [(72, 0, 29.75)], + [(80, 0, 29.75)], + [(104, 0, 29.75)], + [(112, 0, 29.75)], + [(138, 0, 23)], + [(146, 0, 23)], + [(174, 0, 23)], + [(182, 0, 23)], + [(128, 0, 34)], + [(192, 0, 34)]] +SpawnPointList13 = [[(2, 0, 16)], [(67, 0, 22.75)], [(130, 0, 16)]] +EnemyList13 = [['cc', [(20, 0, 12.75), (48, 0, 12.75), 3.5]], + ['cc', [(48, 0, 12.75), (20, 0, 12.75), 3.5]], + ['sc', [(92, 0, 31), (92, 0, -1), 3.5]], + ['sc', [(92, 0, -1), (92, 0, 31), 3.5]], + ['cc', [(146, 0, 12.75), (174, 0, 12.75), 3.5]], + ['cc', [(174, 0, 12.75), (146, 0, 12.75), 3.5]]] +StomperList13 = [[1, (16, 0, 20.75), 2], + [1, (52, 0, 20.75), 2], + [1, (76, 0, 27.5), 2], + [1, (108, 0, 27.5), 2], + [1, (142, 0, 20.75), 2], + [1, (178, 0, 20.75), 2]] +BlockList4 = [[BLOCK_H12, [(0, 0, 12)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H24, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H12, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H6, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_H3, [(0, 0, 0)]], + [BLOCK_V12F, [(0, 0, 0)]], + [BLOCK_V12F, [(0, 0, 0)]], + [BLOCK_V12B, [(0, 0, 0)]], + [BLOCK_V12B, [(0, 0, 0)]], + [BLOCK_V6F, [(0, 0, 0)]], + [BLOCK_V6F, [(0, 0, 0)]], + [BLOCK_V6B, [(0, 0, 0)]], + [BLOCK_V6B, [(0, 0, 0)]]] +TreasureList4 = [[(-3, 0, 25)], + [(-3, 0, 25)], + [(-3, 0, 25)], + [(-3, 0, 25)], + [(-3, 0, 25)]] +SpawnPointList4 = [[(9, 0, 16)]] +EnemyList4 = [['cc', [(26, 0, 12.75), (46, 0, 12.75), 3.5]], ['tm', [(74, 0, 6), (94, 0, 6), 3.5]], ['sc', [(122, 0, 19.5), (142, 0, 19.5), 3.5]]] +StomperList4 = [[1, (12, 0, 20.75), 2], [1, (50, 0, 20.75), 2], [1, (82, 0, 20.75), 2]] +SectionTypes = {'end': (0, + 24, + BlockListEnd, + None, + None, + SpawnPointListEnd, + None, + [], + [], + [1, 1], + []), + 1: (4, + 100, + BlockList1, + EnemyList1, + TreasureList1, + SpawnPointList1, + None, + [3, 6], + [10, 10], + [1, 2], + []), + 2: (4, + 118, + BlockList2, + EnemyList2, + TreasureList2, + SpawnPointList2, + None, + [3, 6], + [13, 13], + [1, 2], + []), + 3: (4, + 192, + BlockList3, + None, + TreasureList3, + SpawnPointList3, + None, + [], + [16, 16], + [1, 4], + []), + 5: (0, + 72, + BlockList5, + None, + TreasureList5, + SpawnPointList5, + None, + [], + [6, 6], + [1, 1], + []), + 6: (0, + 90, + BlockList6, + None, + TreasureList6, + SpawnPointList6, + None, + [], + [9, 9], + [1, 1], + []), + 7: (1, + 124, + BlockList7, + None, + TreasureList7, + SpawnPointList7, + None, + [], + [12, 12], + [1, 1], + []), + 8: (5, + 174, + BlockList8, + None, + TreasureList8, + SpawnPointList8, + None, + [], + [19, 19], + [1, 3], + []), + 9: (3, + 204, + BlockList9, + None, + TreasureList9, + SpawnPointList9, + None, + [], + [14, 14], + [1, 2], + []), + 10: (4, + 214, + BlockList10, + None, + TreasureList10, + SpawnPointList10, + None, + [], + [15, 15], + [1, 3], + []), + 11: (2, + 164, + BlockList11, + EnemyList11, + TreasureList11, + SpawnPointList11, + None, + [3, 3], + [10, 10], + [1, 2], + []), + 12: (3, + 120, + BlockList12, + None, + TreasureList12, + SpawnPointList12, + StomperList12, + [], + [10, 10], + [1, 1], + [3, 3]), + 13: (3, + 198, + BlockList13, + EnemyList13, + TreasureList13, + SpawnPointList13, + StomperList13, + [3, 6], + [17, 17], + [1, 3], + [3, 6]), + 4: (0, + 1000, + BlockList4, + None, + TreasureList4, + SpawnPointList4, + None, + [25, 25], + [10, 10], + [10, 10], + [10, 10]), + 0: (0, + 75, + BlockList0, + None, + TreasureList0, + SpawnPointList0, + None, + [2, 5], + [5, 10], + [2, 5], + [])} +SectionsPool = [1, + 2, + 3, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13] diff --git a/toontown/minigame/Trajectory.py b/toontown/minigame/Trajectory.py new file mode 100644 index 0000000..1b0b53c --- /dev/null +++ b/toontown/minigame/Trajectory.py @@ -0,0 +1,133 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from math import * + +class Trajectory: + notify = DirectNotifyGlobal.directNotify.newCategory('Trajectory') + gravity = 32.0 + __radius = 2.0 + + def __init__(self, startTime, startPos, startVel, gravMult = 1.0): + self.setStartTime(startTime) + self.setStartPos(startPos) + self.setStartVel(startVel) + self.setGravityMult(gravMult) + + def setStartTime(self, t): + self.__startTime = t + + def setStartPos(self, sp): + self.__startPos = sp + + def setStartVel(self, sv): + self.__startVel = sv + + def setGravityMult(self, mult): + self.__zAcc = mult * -Trajectory.gravity + + def getStartTime(self): + return self.__startTime + + def __str__(self): + return 'startTime: %s, startPos: %s, startVel: %s, zAcc: %s' % (self.__startTime, + repr(self.__startPos), + repr(self.__startVel), + self.__zAcc) + + def __calcTimeOfHighestPoint(self): + t = -self.__startVel[2] / self.__zAcc + if t < 0: + t = 0 + return t + self.__startTime + + def calcTimeOfImpactOnPlane(self, height = 0): + a = self.__zAcc * 0.5 + b = self.__startVel[2] + c = self.__startPos[2] - height + D = b * b - 4.0 * a * c + if D < 0: + return -1.0 + elif D == 0: + t = -b / (2.0 * a) + else: + t = (-b - sqrt(D)) / (2.0 * a) + if t < 0: + return -1.0 + return t + self.__startTime + + def calcZ(self, t): + tt = t - self.__startTime + return self.__startPos[2] + self.__startVel[2] * tt + 0.5 * self.__zAcc * tt * tt + + def __reachesHeight(self, height): + if self.calcZ(self.__calcTimeOfHighestPoint()) < height: + return 0 + return 1 + + def getPos(self, t): + tt = t - self.__startTime + return Point3(self.__startPos[0] + self.__startVel[0] * tt, self.__startPos[1] + self.__startVel[1] * tt, self.calcZ(t)) + + def getVel(self, t): + tt = t - self.__startTime + return Vec3(self.__startVel[0], self.__startVel[1], self.__startVel[2] + self.__zAcc * tt) + + def getStartTime(self): + return self.__startTime + + def checkCollisionWithGround(self, height = 0): + return self.calcTimeOfImpactOnPlane(height) + + def checkCollisionWithDisc(self, discCenter, discRadius): + if self.__reachesHeight(discCenter[2]) == 0: + return -1.0 + t_atDiscHeight = self.calcTimeOfImpactOnPlane(discCenter[2]) + if t_atDiscHeight < 0: + return -1.0 + p_atDiscHeight = self.getPos(t_atDiscHeight) + offset_x = p_atDiscHeight[0] - discCenter[0] + offset_y = p_atDiscHeight[1] - discCenter[1] + offset_from_center_SQUARED = offset_x * offset_x + offset_y * offset_y + max_offset = discRadius + max_offset_SQUARED = max_offset * max_offset + if offset_from_center_SQUARED < max_offset_SQUARED: + return t_atDiscHeight + else: + return -1.0 + + def calcEnterAndLeaveCylinderXY(self, cylBottomCenter, cylRadius): + v = Vec2(cylBottomCenter[0], cylBottomCenter[1]) + o = Vec2(self.__startPos[0], self.__startPos[1]) + d = Vec2(self.__startVel[0], self.__startVel[1]) + d.normalize() + b = d.dot(o - v) + c = (o - v).dot(o - v) - cylRadius * cylRadius + bsmc = b * b - c + if bsmc <= 0.0: + return (-1.0, -1.0) + sqrt_bsmc = sqrt(bsmc) + t1 = -b - sqrt_bsmc + t2 = -b + sqrt_bsmc + if t1 > t2: + self.notify.debug('calcEnterAndLeaveCylinderXY: t1 > t2??') + mag = Vec2(self.__startVel[0], self.__startVel[1]).length() + t1 = t1 / mag + t2 = t2 / mag + return (t1 + self.__startTime, t2 + self.__startTime) + + def checkCollisionWithCylinderSides(self, cylBottomCenter, cylRadius, cylHeight): + if self.__reachesHeight(cylBottomCenter[2]) == 0: + return -1.0 + t1, t2 = self.calcEnterAndLeaveCylinderXY(cylBottomCenter, cylRadius) + p1 = self.getPos(t1) + p2 = self.getPos(t2) + cylTopHeight = cylBottomCenter[2] + cylHeight + if p1[2] > cylTopHeight and p2[2] > cylTopHeight: + return -1.0 + if p1[2] < cylTopHeight and p1[2] > cylBottomCenter[2]: + if t1 > self.__startTime: + return t1 + return -1.0 + + def checkCollisionWithProjectile(self, projectile): + return -1.0 diff --git a/toontown/minigame/TravelGameGlobals.py b/toontown/minigame/TravelGameGlobals.py new file mode 100644 index 0000000..91edece --- /dev/null +++ b/toontown/minigame/TravelGameGlobals.py @@ -0,0 +1,184 @@ +DefaultStartingVotes = 5 +FinalMetagameRoundIndex = 5 +BaseBeans = 10 +PercentOfVotesConverted = {1: 50, + 2: 33, + 3: 66, + 4: 100} +InputTimeout = 15 +MaxDirections = 2 +ReasonVote = 0 +ReasonPlaceDecider = 1 +ReasonRandom = 2 +DisplayVotesTimePerPlayer = 2 +MoveTrolleyTime = 5 +FudgeTime = 0 +SpoofFour = False +ReverseWin = False +xInc = 60 +yInc = 15 +BoardLayout4VotingRounds = {0: {'links': (1, 2), + 'pos': (0, 0, 0)}, + 1: {'links': (3, 4), + 'pos': (xInc, yInc, 0)}, + 2: {'links': (4, 5), + 'pos': (xInc, -yInc, 0)}, + 3: {'links': (6, 7), + 'pos': (2 * xInc, 2 * yInc, 0)}, + 4: {'links': (7, 8), + 'pos': (2 * xInc, 0, 0)}, + 5: {'links': (8, 9), + 'pos': (2 * xInc, -2 * yInc, 0)}, + 6: {'links': (10, 11), + 'pos': (3 * xInc, 3 * yInc, 0)}, + 7: {'links': (11, 12), + 'pos': (3 * xInc, yInc, 0)}, + 8: {'links': (12, 13), + 'pos': (3 * xInc, -yInc, 0)}, + 9: {'links': (13, 14), + 'pos': (3 * xInc, -3 * yInc, 0)}, + 10: {'links': (), + 'pos': (4 * xInc, 4 * yInc, 0), + 'baseBonus': 3}, + 11: {'links': (), + 'pos': (4 * xInc, 2 * yInc, 0), + 'baseBonus': 2}, + 12: {'links': (), + 'pos': (4 * xInc, 0, 0), + 'baseBonus': 1}, + 13: {'links': (), + 'pos': (4 * xInc, -2 * yInc, 0), + 'baseBonus': 2}, + 14: {'links': (), + 'pos': (4 * xInc, -4 * yInc, 0), + 'baseBonus': 3}} +BoardLayout0 = {0: {'links': (1, 2), + 'pos': (0, 0, 0)}, + 1: {'links': (3, 4), + 'pos': (xInc, 2 * yInc, 0)}, + 2: {'links': (4, 5), + 'pos': (xInc, -2 * yInc, 0)}, + 3: {'links': (6, 7), + 'pos': (2 * xInc, 4 * yInc, 0)}, + 4: {'links': (8, 9), + 'pos': (2 * xInc, 0, 0)}, + 5: {'links': (10, 11), + 'pos': (2 * xInc, -4 * yInc, 0)}, + 6: {'links': (), + 'pos': (3 * xInc, 5 * yInc, 0), + 'baseBonus': 2}, + 7: {'links': (), + 'pos': (3 * xInc, 3 * yInc, 0), + 'baseBonus': 2}, + 8: {'links': (), + 'pos': (3 * xInc, 1 * yInc, 0), + 'baseBonus': 1}, + 9: {'links': (), + 'pos': (3 * xInc, -1 * yInc, 0), + 'baseBonus': 1}, + 10: {'links': (), + 'pos': (3 * xInc, -3 * yInc, 0), + 'baseBonus': 2}, + 11: {'links': (), + 'pos': (3 * xInc, -5 * yInc, 0), + 'baseBonus': 2}} +BoardLayout1 = {0: {'links': (1, 2), + 'pos': (0, 0, 0)}, + 1: {'links': (3, 4), + 'pos': (xInc, 3 * yInc, 0)}, + 2: {'links': (5, 6), + 'pos': (xInc, -3 * yInc, 0)}, + 3: {'links': (7, 8), + 'pos': (2 * xInc, 4 * yInc, 0)}, + 4: {'links': (8, 9), + 'pos': (2 * xInc, 2 * yInc, 0)}, + 5: {'links': (10, 11), + 'pos': (2 * xInc, -2 * yInc, 0)}, + 6: {'links': (11, 12), + 'pos': (2 * xInc, -4 * yInc, 0)}, + 7: {'links': (), + 'pos': (3 * xInc, 5 * yInc, 0), + 'baseBonus': 2}, + 8: {'links': (), + 'pos': (3 * xInc, 3 * yInc, 0), + 'baseBonus': 1}, + 9: {'links': (), + 'pos': (3 * xInc, 1 * yInc, 0), + 'baseBonus': 2}, + 10: {'links': (), + 'pos': (3 * xInc, -1 * yInc, 0), + 'baseBonus': 2}, + 11: {'links': (), + 'pos': (3 * xInc, -3 * yInc, 0), + 'baseBonus': 1}, + 12: {'links': (), + 'pos': (3 * xInc, -5 * yInc, 0), + 'baseBonus': 2}} +BoardLayout2 = {0: {'links': (1, 2), + 'pos': (0, 0, 0)}, + 1: {'links': (3, 4), + 'pos': (xInc, 3 * yInc, 0)}, + 2: {'links': (5, 6), + 'pos': (xInc, -3 * yInc, 0)}, + 3: {'links': (7, 8), + 'pos': (2 * xInc, 4 * yInc, 0)}, + 4: {'links': (8, 9), + 'pos': (2 * xInc, 2 * yInc, 0)}, + 5: {'links': (9, 10), + 'pos': (2 * xInc, 0 * yInc, 0)}, + 6: {'links': (11, 12), + 'pos': (2 * xInc, -4 * yInc, 0)}, + 7: {'links': (), + 'pos': (3 * xInc, 5 * yInc, 0), + 'baseBonus': 2}, + 8: {'links': (), + 'pos': (3 * xInc, 3 * yInc, 0), + 'baseBonus': 1}, + 9: {'links': (), + 'pos': (3 * xInc, 1 * yInc, 0), + 'baseBonus': 1}, + 10: {'links': (), + 'pos': (3 * xInc, -1 * yInc, 0), + 'baseBonus': 2}, + 11: {'links': (), + 'pos': (3 * xInc, -3 * yInc, 0), + 'baseBonus': 2}, + 12: {'links': (), + 'pos': (3 * xInc, -5 * yInc, 0), + 'baseBonus': 2}} +BoardLayout3 = {0: {'links': (1, 2), + 'pos': (0, 0, 0)}, + 1: {'links': (3, 4), + 'pos': (xInc, 2 * yInc, 0)}, + 2: {'links': (5, 6), + 'pos': (xInc, -3 * yInc, 0)}, + 3: {'links': (7, 8), + 'pos': (2 * xInc, 4 * yInc, 0)}, + 4: {'links': (9, 10), + 'pos': (2 * xInc, 0 * yInc, 0)}, + 5: {'links': (10, 11), + 'pos': (2 * xInc, -2 * yInc, 0)}, + 6: {'links': (11, 12), + 'pos': (2 * xInc, -4 * yInc, 0)}, + 7: {'links': (), + 'pos': (3 * xInc, 5 * yInc, 0), + 'baseBonus': 2}, + 8: {'links': (), + 'pos': (3 * xInc, 3 * yInc, 0), + 'baseBonus': 2}, + 9: {'links': (), + 'pos': (3 * xInc, 1 * yInc, 0), + 'baseBonus': 2}, + 10: {'links': (), + 'pos': (3 * xInc, -1 * yInc, 0), + 'baseBonus': 1}, + 11: {'links': (), + 'pos': (3 * xInc, -3 * yInc, 0), + 'baseBonus': 1}, + 12: {'links': (), + 'pos': (3 * xInc, -5 * yInc, 0), + 'baseBonus': 2}} +BoardLayouts = (BoardLayout0, + BoardLayout1, + BoardLayout2, + BoardLayout3) diff --git a/toontown/minigame/TreasureScorePanel.py b/toontown/minigame/TreasureScorePanel.py new file mode 100644 index 0000000..84fd42c --- /dev/null +++ b/toontown/minigame/TreasureScorePanel.py @@ -0,0 +1,28 @@ +from direct.showbase.ShowBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from toontown.toon import LaffMeter +from toontown.toonbase import TTLocalizer + +class TreasureScorePanel(DirectFrame): + + def __init__(self): + DirectFrame.__init__(self, relief=None, image_color=GlobalDialogColor, image_scale=(0.24, 1.0, 0.24), image_pos=(0.0, 0.1, 0.0)) + self.score = 0 + self.scoreText = DirectLabel(self, relief=None, text=str(self.score), text_scale=0.08, pos=(0.0, 0.0, -0.09)) + self.nameText = DirectLabel(self, relief=None, text=TTLocalizer.DivingGameTreasuresRetrieved, text_scale=0.05, text_pos=(0.0, 0.06), text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.show() + return + + def cleanup(self): + del self.scoreText + del self.nameText + self.destroy() + + def incrScore(self): + self.score += 1 + self.scoreText['text'] = str(self.score) + + def makeTransparent(self, alpha): + self.setTransparency(1) + self.setColorScale(1, 1, 1, alpha) diff --git a/toontown/minigame/TrolleyHolidayMgrAI.py b/toontown/minigame/TrolleyHolidayMgrAI.py new file mode 100644 index 0000000..92d48de --- /dev/null +++ b/toontown/minigame/TrolleyHolidayMgrAI.py @@ -0,0 +1,21 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.ai import HolidayBaseAI + +class TrolleyHolidayMgrAI(HolidayBaseAI.HolidayBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('TrolleyHolidayMgrAI') + PostName = 'TrolleyHoliday' + StartStopMsg = 'TrolleyHolidayStartStop' + + def __init__(self, air, holidayId): + HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId) + + def start(self): + bboard.post(TrolleyHolidayMgrAI.PostName, True) + simbase.air.newsManager.trolleyHolidayStart() + messenger.send(TrolleyHolidayMgrAI.StartStopMsg) + + def stop(self): + bboard.remove(TrolleyHolidayMgrAI.PostName) + simbase.air.newsManager.trolleyHolidayEnd() + messenger.send(TrolleyHolidayMgrAI.StartStopMsg) diff --git a/toontown/minigame/TrolleyWeekendMgrAI.py b/toontown/minigame/TrolleyWeekendMgrAI.py new file mode 100644 index 0000000..2e4b4d2 --- /dev/null +++ b/toontown/minigame/TrolleyWeekendMgrAI.py @@ -0,0 +1,21 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.ai import HolidayBaseAI + +class TrolleyWeekendMgrAI(HolidayBaseAI.HolidayBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('TrolleyWeekendMgrAI') + PostName = 'TrolleyWeekend' + StartStopMsg = 'TrolleyWeekendStartStop' + + def __init__(self, air, holidayId): + HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId) + + def start(self): + bboard.post(TrolleyWeekendMgrAI.PostName, True) + simbase.air.newsManager.trolleyWeekendStart() + messenger.send(TrolleyWeekendMgrAI.StartStopMsg) + + def stop(self): + bboard.remove(TrolleyWeekendMgrAI.PostName) + simbase.air.newsManager.trolleyWeekendEnd() + messenger.send(TrolleyWeekendMgrAI.StartStopMsg) diff --git a/toontown/minigame/TugOfWarGameGlobals.py b/toontown/minigame/TugOfWarGameGlobals.py new file mode 100644 index 0000000..2053f00 --- /dev/null +++ b/toontown/minigame/TugOfWarGameGlobals.py @@ -0,0 +1,18 @@ +GAME_DURATION = 40.0 +TIME_BONUS_MIN = 2 +TIME_BONUS_MAX = 5 +TIME_BONUS_RANGE = 3.0 +SEND_UPDATE = 0.2 +TOW_WIN = 0 +TOW_TIE = 1 +TOW_LOSS = 2 +TOON_VS_TOON = 0 +TOON_VS_COG = 1 +WAIT_FOR_CLIENTS_TIMEOUT = 20 +TUG_TIMEOUT = 45 +WAIT_FOR_GO_TIMEOUT = 15 +WIN_JELLYBEANS = 15 +LOSS_JELLYBEANS = 4 +TIE_WIN_JELLYBEANS = 12 +TIE_LOSS_JELLYBEANS = 8 +TIE_JELLYBEANS = 5 diff --git a/toontown/minigame/TwoDBattleMgr.py b/toontown/minigame/TwoDBattleMgr.py new file mode 100644 index 0000000..f0b2d10 --- /dev/null +++ b/toontown/minigame/TwoDBattleMgr.py @@ -0,0 +1,188 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToonBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from toontown.battle import MovieUtil +import math + +class TwoDBattleMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDBattleMgr') + + def __init__(self, game, toon): + self.game = game + self.toon = toon + self.waterBulletIval = None + self.shootTrack = None + self.showCollSpheres = False + self.WATER_SPRAY_COLOR = Point4(1, 1, 1, 1) + self.WATER_BULLET_SCALE = 0.2 + self.SHOOT_DISTANCE = 10 + self.WATER_BULLET_START_POINT = Point3(0, 1, 3) + self.WATER_BULLET_END_POINT = Point3(0, self.WATER_BULLET_START_POINT.getY() + self.SHOOT_DISTANCE, self.WATER_BULLET_START_POINT.getZ()) + self.WATER_BULLET_HIDE_POINT = Point3(0, 0, 1.5) + self.sprayProp = self.game.assetMgr.sprayProp.copyTo(self.game.assetMgr.world) + self.setupPistol() + if self.toon == base.localAvatar: + self.createShootCollision() + return + + def destroy(self): + if self.toon == base.localAvatar: + if self.waterBulletIval: + self.waterBulletIval.finish() + del self.waterBulletIval + self.waterBulletIval = None + self.ignore('enter' + self.collSphereName) + base.localAvatar.controlManager.currentControls.cTrav.removeCollider(self.waterBullet) + self.waterBullet.removeNode() + del self.waterBullet + self.hand_jointpath0.removeNode() + MovieUtil.removeProp(self.pistol) + if self.shootTrack != None: + self.shootTrack.finish() + self.shootTrack = None + self.game = None + self.toon = None + return + + def start(self): + pass + + def stop(self): + pass + + def setupPistol(self): + self.pistol = globalPropPool.getProp('water-gun') + hands = self.toon.getRightHands() + self.hand_jointpath0 = hands[0].attachNewNode('handJoint0-path') + pistolPos = Point3(0.28, 0.1, 0.08) + pistolHpr = VBase3(85.6, -4.44, 94.43) + MovieUtil.showProp(self.pistol, self.hand_jointpath0, pistolPos, pistolHpr) + + def shoot(self): + if not self.shootTrack: + self.shootTrack = Parallel(self.getToonShootTrack(), self.getSprayTrack()) + if self.toon == base.localAvatar: + self.shootTrack.append(Func(self.game.assetMgr.playWatergunSound)) + self.shootTrack.append(self.getWaterBulletIval()) + self.shootTrack.start() + return + elif self.shootTrack.isStopped(): + self.shootTrack = Parallel(self.getToonShootTrack(), self.getSprayTrack()) + if self.toon == base.localAvatar: + self.shootTrack.append(Func(self.game.assetMgr.playWatergunSound)) + self.shootTrack.append(self.getWaterBulletIval()) + self.shootTrack.start() + + def createShootCollision(self): + self.notify.debug('entering createShootCollision') + collSphere = CollisionSphere(0, 0, 0, 1) + collSphere.setTangible(0) + self.collSphereName = self.game.uniqueName('waterBullet') + collNode = CollisionNode(self.collSphereName) + collNode.setFromCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(collSphere) + self.waterBullet = base.localAvatar.attachNewNode(collNode) + self.waterBullet.setPos(self.WATER_BULLET_HIDE_POINT) + self.waterBullet.setScale(self.WATER_BULLET_SCALE) + self.waterBullet.hide() + if self.showCollSpheres: + self.waterBullet.show() + bulletEvent = CollisionHandlerEvent() + bulletEvent.addInPattern('enter%fn') + bulletEvent.addOutPattern('exit%fn') + cTrav = base.localAvatar.controlManager.currentControls.cTrav + cTrav.addCollider(self.waterBullet, bulletEvent) + self.accept('enter' + self.collSphereName, self.handleBulletCollision) + self.waterBulletIval = Sequence(Wait(0.15)) + self.waterBulletIval.append(LerpPosInterval(self.waterBullet, 0.25, pos=Point3(self.WATER_BULLET_END_POINT), startPos=Point3(self.WATER_BULLET_START_POINT), name='waterBulletMoveFront')) + self.waterBulletIval.append(Func(self.waterBullet.setPos, self.WATER_BULLET_HIDE_POINT)) + + def getToonShootTrack(self): + + def returnToLastAnim(toon): + if hasattr(toon, 'playingAnim') and toon.playingAnim: + toon.loop(toon.playingAnim) + else: + toon.loop('neutral') + + torso = self.toon.getPart('torso', '1000') + toonTrack = Sequence(ActorInterval(self.toon, 'water-gun', startFrame=48, endFrame=58, partName='torso'), ActorInterval(self.toon, 'water-gun', startFrame=107, endFrame=126, playRate=2, partName='torso'), Func(returnToLastAnim, self.toon)) + return toonTrack + + def calcSprayStartPos(self): + if self.toon: + self.toon.update(0) + joint = self.pistol.find('**/joint_nozzle') + p = joint.getPos(render) + self.origin = p + + def calcSprayEndPos(self): + if self.toon: + xDirection = -math.sin(self.toon.getH()) + else: + xDirection = -math.sin(-90) + endPos = Point3(self.origin.getX() + self.SHOOT_DISTANCE * xDirection, self.origin.getY(), self.origin.getZ()) + self.target = endPos + + def getSprayTrack(self): + dSprayScale = 0.15 + dSprayHold = 0.035 + color = self.WATER_SPRAY_COLOR + parent = render + horizScale = 1.0 + vertScale = 1.0 + + def showSpray(sprayScale, sprayRot, sprayProp, parent): + sprayRot.reparentTo(parent) + sprayRot.clearMat() + sprayScale.reparentTo(sprayRot) + sprayScale.clearMat() + sprayProp.reparentTo(sprayScale) + sprayProp.clearMat() + sprayRot.setPos(self.origin) + sprayRot.lookAt(Point3(self.target)) + + def calcTargetScale(horizScale = horizScale, vertScale = vertScale): + distance = Vec3(self.target - self.origin).length() + yScale = distance / MovieUtil.SPRAY_LEN + targetScale = Point3(yScale * horizScale, yScale, yScale * vertScale) + return targetScale + + def prepareToShrinkSpray(spray, sprayProp): + sprayProp.setPos(Point3(0.0, -MovieUtil.SPRAY_LEN, 0.0)) + spray.setPos(self.target) + + def hideSpray(spray, sprayScale, sprayRot, sprayProp, propPool): + sprayProp.detachNode() + sprayRot.removeNode() + sprayScale.removeNode() + + sprayProp = self.sprayProp + sprayScale = hidden.attachNewNode('spray-parent') + sprayRot = hidden.attachNewNode('spray-rotate') + spray = sprayRot + spray.setColor(color) + if color[3] < 1.0: + spray.setTransparency(1) + track = Sequence(Wait(0.1), Func(self.calcSprayStartPos), Func(self.calcSprayEndPos), Func(showSpray, sprayScale, sprayRot, sprayProp, parent), LerpScaleInterval(sprayScale, dSprayScale, calcTargetScale, startScale=MovieUtil.PNT3_NEARZERO), Wait(dSprayHold), Func(prepareToShrinkSpray, spray, sprayProp), LerpScaleInterval(sprayScale, dSprayScale, MovieUtil.PNT3_NEARZERO), Func(hideSpray, spray, sprayScale, sprayRot, sprayProp, globalPropPool)) + return track + + def handleBulletCollision(self, cevent): + if cevent.getIntoNodePath().getName()[:5] == 'Enemy': + sectionIndex = int(cevent.getIntoNodePath().getName()[6:8]) + enemyIndex = int(cevent.getIntoNodePath().getName()[9:11]) + messenger.send('enemyShot', [sectionIndex, enemyIndex]) + + def clearWaterBulletIval(self): + if self.waterBulletIval: + self.waterBulletIval.finish() + del self.waterBulletIval + self.waterBulletIval = None + return + + def getWaterBulletIval(self): + if not self.waterBulletIval.isPlaying(): + return self.waterBulletIval diff --git a/toontown/minigame/TwoDBlock.py b/toontown/minigame/TwoDBlock.py new file mode 100644 index 0000000..3ea5066 --- /dev/null +++ b/toontown/minigame/TwoDBlock.py @@ -0,0 +1,86 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from otp.level import BasicEntities +from toontown.coghq import MovingPlatform +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import ToonBlitzGlobals + +class TwoDBlock(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDBlock') + + def __init__(self, model, index, blockAttribs): + self.moveIval = None + self.isMovingBlock = False + self.index = index + self.createNewBlock(model, blockAttribs) + return + + def destroy(self): + if self.moveIval: + self.moveIval.pause() + del self.moveIval + if self.platform: + if self.isMovingBlock: + self.platform.destroy() + del self.platform + + def createNewBlock(self, model, blockAttribs): + initX, initY, initZ, initH, initP, initR = (0, 0, 0, 0, 0, 0) + finalX, finalY, finalZ, finalH, finalP, finalR = (0, 0, 0, 0, 0, 0) + blockType = blockAttribs[0] + typeAttribs = ToonBlitzGlobals.BlockTypes[blockType] + blockName = blockType + '-' + str(self.index) + self.model = NodePath(blockName) + typeX, typeY, typeZ = typeAttribs[1] + typeH, typeP, typeR = typeAttribs[2] + scaleX, scaleY, scaleZ = typeAttribs[3] + model.setScale(scaleX, scaleY, scaleZ) + blockPosAttribs = blockAttribs[1] + initX, initY, initZ = blockPosAttribs[0] + if len(blockPosAttribs) == 3: + self.isMovingBlock = True + finalX, finalY, finalZ = blockPosAttribs[1] + posIvalDuration = blockPosAttribs[2] + if len(blockAttribs) == 3: + blockHprAttribs = blockAttribs[2] + initH, initP, initR = blockHprAttribs[0] + if len(blockHprAttribs) == 3: + self.isMovingBlock = True + finalH, finalP, finalR = blockHprAttribs[1] + hprIvalDuration = blockHprAttribs[2] + if self.isMovingBlock: + self.platform = MovingPlatform.MovingPlatform() + self.platform.setupCopyModel(blockName, model) + self.platform.reparentTo(self.model) + self.clearMoveIval() + forwardIval = LerpPosInterval(self.model, posIvalDuration, pos=Point3(finalX, finalY, finalZ), startPos=Point3(initX, initY, initZ), name='%s-moveFront' % self.platform.name, fluid=1) + backwardIval = LerpPosInterval(self.model, posIvalDuration, pos=Point3(initX, initY, initZ), startPos=Point3(finalX, finalY, finalZ), name='%s-moveBack' % self.platform.name, fluid=1) + self.moveIval = Sequence(forwardIval, backwardIval) + else: + self.platform = model.copyTo(self.model) + self.model.flattenLight() + self.model.setPos(typeX + initX, typeY + initY, typeZ + initZ) + self.model.setHpr(typeH + initH, typeP + initP, typeR + initR) + + def clearMoveIval(self): + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def start(self, elapsedTime): + if self.moveIval: + self.moveIval.loop() + self.moveIval.setT(elapsedTime) + + def enterPause(self): + if self.moveIval: + self.moveIval.pause() + + def exitPause(self): + if self.moveIval: + self.moveIval.resume() diff --git a/toontown/minigame/TwoDCamera.py b/toontown/minigame/TwoDCamera.py new file mode 100644 index 0000000..2561db9 --- /dev/null +++ b/toontown/minigame/TwoDCamera.py @@ -0,0 +1,60 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from toontown.minigame import ToonBlitzGlobals +import math + +class TwoDCamera(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDCamera') + + def __init__(self, camera): + self.notify.debug('Constructing TwoDCamera with %s' % camera) + self.camera = camera + self.cameraSideView = ToonBlitzGlobals.CameraStartingPosition + self.threeQuarterOffset = 2 + self.changeFacingInterval = None + self.ivalControllingCamera = False + self.accept('avatarOrientationChanged', self.setupChangeFacingInterval) + return + + def onstage(self): + self.camera.reparentTo(render) + p = self.cameraSideView + self.camera.setPosHpr(render, p[0], p[1], p[2], p[3], p[4], p[5]) + self.camera.setX(render, base.localAvatar.getX(render) + self.threeQuarterOffset) + + def destroy(self): + self.ignore('avatarOrientationChanged') + p = self.cameraSideView + self.camera.setPosHpr(render, p[0], p[1], p[2], p[3], p[4], p[5]) + + def update(self): + if not self.ivalControllingCamera: + camX = base.localAvatar.getX(render) - math.sin(base.localAvatar.getH(render) * math.pi / 180) * self.threeQuarterOffset + self.camera.setX(render, camX) + + def clearChangeFacingInterval(self): + if self.changeFacingInterval: + self.changeFacingInterval.pause() + del self.changeFacingInterval + self.changeFacingInterval = None + return + + def setupChangeFacingInterval(self, newHeading): + self.clearChangeFacingInterval() + self.newHeading = newHeading + self.changeFacingInterval = LerpFunc(self.myLerpPos, duration=5.0) + self.changeFacingInterval.start() + + def myLerpPos(self, t): + self.ivalControllingCamera = True + finalCamX = base.localAvatar.getX(render) - math.sin(self.newHeading * math.pi / 180) * self.threeQuarterOffset + diffX = finalCamX - self.camera.getX(render) + self.camera.setX(render, self.camera.getX(render) + diffX * t) + if math.fabs(self.camera.getX(render) - finalCamX) < 0.01: + self.notify.debug('giving up camera control') + self.camera.setX(render, finalCamX) + self.ivalControllingCamera = False + self.clearChangeFacingInterval() diff --git a/toontown/minigame/TwoDDrive.py b/toontown/minigame/TwoDDrive.py new file mode 100644 index 0000000..7cedc8b --- /dev/null +++ b/toontown/minigame/TwoDDrive.py @@ -0,0 +1,154 @@ +from toontown.toonbase.ToonBaseGlobal import * +from otp.otpbase import OTPGlobals +from direct.interval.IntervalGlobal import * +import ArrowKeys +from direct.task.Task import Task + +class TwoDDrive: + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDDrive') + TASK_NAME = 'TwoDDriveTask' + SET_ATREST_HEADING_TASK = 'setAtRestHeadingTask' + + def __init__(self, game, speed, maxFrameMove = None, customCollisionCallback = None, priority = 0, setHeading = 1, upHeading = 0): + self.game = game + self.speed = speed + self.maxFrameMove = maxFrameMove + self.customCollisionCallback = customCollisionCallback + self.priority = priority + self.setHeading = setHeading + self.upHeading = upHeading + self.arrowKeys = ArrowKeys.ArrowKeys() + self.wasUpReleased = True + self.lt = base.localAvatar + base.localAvatar.useTwoDControls() + base.localAvatar.controlManager.currentControls.avatarControlJumpForce = 30.0 + self.ONE_JUMP_PER_UP_PRESSED = True + self.lastAction = None + self.isMovingX = False + return + + def destroy(self): + self.game = None + base.localAvatar.controlManager.currentControls.avatarControlJumpForce = 24.0 + base.localAvatar.useWalkControls() + self.arrowKeys.destroy() + del self.arrowKeys + del self.customCollisionCallback + self.lastAction = None + return + + def start(self): + self.notify.debug('start') + self.__placeToonHOG(self.lt.getPos()) + base.localAvatar.enableAvatarControls() + taskMgr.remove(TwoDDrive.TASK_NAME) + taskMgr.add(self.__update, TwoDDrive.TASK_NAME, priority=self.priority) + + def __placeToonHOG(self, pos, h = None): + if h == None: + h = self.lt.getH() + self.lt.setPos(pos) + self.lt.setH(h) + self.lastPos = pos + self.atRestHeading = h + self.oldAtRestHeading = h + self.lastXVel = 0 + self.lastYVel = 0 + return + + def stop(self): + self.notify.debug('stop') + base.localAvatar.disableAvatarControls() + taskMgr.remove(TwoDDrive.TASK_NAME) + taskMgr.remove(TwoDDrive.SET_ATREST_HEADING_TASK) + if hasattr(self, 'turnLocalToonIval'): + if self.turnLocalToonIval.isPlaying(): + self.turnLocalToonIval.pause() + del self.turnLocalToonIval + base.localAvatar.setSpeed(0, 0) + base.localAvatar.stopSound() + + def __update(self, task): + vel = Vec3(0, 0, 0) + xVel = 0 + yVel = 0 + if self.ONE_JUMP_PER_UP_PRESSED: + if not self.arrowKeys.upPressed(): + self.wasUpReleased = True + elif self.arrowKeys.upPressed() and self.wasUpReleased: + self.wasUpReleased = False + if not self.game.isHeadInFloor: + if localAvatar.controlManager.currentControls == localAvatar.controlManager.get('twoD'): + base.localAvatar.controlManager.currentControls.jumpPressed() + elif self.arrowKeys.upPressed(): + if not self.game.isHeadInFloor: + if localAvatar.controlManager.currentControls == localAvatar.controlManager.get('twoD'): + base.localAvatar.controlManager.currentControls.jumpPressed() + if self.arrowKeys.leftPressed(): + xVel -= 1 + if self.arrowKeys.rightPressed(): + xVel += 1 + vel.setX(xVel) + vel.setY(yVel) + vel.normalize() + vel *= self.speed + if abs(xVel) > 0: + if not self.isMovingX: + self.isMovingX = True + messenger.send('avatarMovingX') + elif self.isMovingX: + self.isMovingX = False + messenger.send('avatarStoppedX') + speed = vel.length() + action = self.lt.setSpeed(speed, 0) + if action != self.lastAction: + self.lastAction = action + if action == OTPGlobals.RUN_INDEX: + base.localAvatar.runSound() + else: + base.localAvatar.stopSound() + if self.setHeading: + self.__handleHeading(xVel, yVel) + toonPos = self.lt.getPos() + dt = globalClock.getDt() + posOffset = vel * dt + if self.customCollisionCallback: + toonPos = self.customCollisionCallback(toonPos, toonPos + posOffset) + else: + toonPos += posOffset + self.lt.setPos(toonPos) + self.lastPos = toonPos + return Task.cont + + def __handleHeading(self, xVel, yVel): + def getHeading(xVel, yVel): + angTab = [[None, 0, 180], [-90, -45, -135], [90, 45, 135]] + return angTab[xVel][yVel] + self.upHeading + + def orientToon(angle, self = self): + startAngle = self.lt.getH() + startAngle = fitSrcAngle2Dest(startAngle, angle) + dur = 0.1 * abs(startAngle - angle) / 90 + self.turnLocalToonIval = LerpHprInterval(self.lt, dur, Point3(angle, 0, 0), startHpr=Point3(startAngle, 0, 0), name='TwoDDriveLerpHpr') + self.turnLocalToonIval.start() + if self.atRestHeading != self.oldAtRestHeading: + self.oldAtRestHeading = self.atRestHeading + messenger.send('avatarOrientationChanged', [self.atRestHeading]) + + if xVel != self.lastXVel or yVel != self.lastYVel: + taskMgr.remove(TwoDDrive.SET_ATREST_HEADING_TASK) + if not (xVel or yVel): + orientToon(self.atRestHeading) + else: + curHeading = getHeading(xVel, yVel) + if ((self.lastXVel and self.lastYVel) and not (xVel and yVel)): + def setAtRestHeading(task, self = self, angle = curHeading): + self.atRestHeading = angle + return Task.done + + taskMgr.doMethodLater(0.05, setAtRestHeading, TwoDDrive.SET_ATREST_HEADING_TASK) + else: + self.atRestHeading = curHeading + orientToon(curHeading) + self.lastXVel = xVel + self.lastYVel = yVel diff --git a/toontown/minigame/TwoDEnemy.py b/toontown/minigame/TwoDEnemy.py new file mode 100644 index 0000000..15ab8a3 --- /dev/null +++ b/toontown/minigame/TwoDEnemy.py @@ -0,0 +1,277 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from direct.showbase import PythonUtil +from direct.interval.IntervalGlobal import * +from toontown.minigame import ToonBlitzGlobals +from toontown.toonbase import ToontownGlobals +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.battle.BattleProps import * +from toontown.battle import MovieUtil +from toontown.battle import BattleParticles, BattleProps +from direct.particles import ParticleEffect +import math +COLOR_RED = VBase4(1, 0, 0, 0.3) + +class TwoDEnemy(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDEnemy') + + def __init__(self, enemyMgr, index, suitAttribs): + self.enemyMgr = enemyMgr + self.game = self.enemyMgr.section.sectionMgr.game + self.index = index + self.moveIval = None + self.propTrack = None + self.animTrack = None + self.shotTrack = None + self.deathTrack = None + self.deathSuit = None + self.suitSound = None + self.deleteMeCallback = None + self.isMovingUpDown = False + self.isMovingLeftRight = False + self.showCollSpheres = False + self.isDestroyed = False + self.isGoingUp = False + self.setupEnemy(suitAttribs) + BattleParticles.loadParticles() + return + + def destroy(self): + if self.isDestroyed: + return + self.isDestroyed = True + if hasattr(self.suit, 'prop') and self.suit.prop: + self.suit.prop.stash() + if self.propTrack: + self.propTrack.finish() + self.propTrack = None + if self.suitSound: + self.suitSound.stop() + del self.suitSound + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + if self.shotTrack != None: + self.shotTrack.finish() + self.shotTrack = None + if self.deathTrack != None: + self.deathTrack.finish() + self.deathTrack = None + if self.deathSuit: + self.deathSuit.detachNode() + self.suit.cleanupLoseActor() + self.deathSuit = None + if self.moveIval: + self.moveIval.pause() + del self.moveIval + if self.suit: + self.suit.delete() + self.suit = None + BattleParticles.unloadParticles() + self.ignore(self.game.uniqueName('enter' + self.suitName)) + self.game = None + self.enemyMgr = None + return + + def setupEnemy(self, suitAttribs): + suitType = suitAttribs[0] + self.suit = Suit.Suit() + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit(suitType) + self.suit.setDNA(suitDNA) + self.suit.pose('walk', 0) + self.suitName = 'Enemy-%s' % self.index + self.suit.setName(self.suitName) + suitPosAttribs = suitAttribs[1] + initX, initY, initZ = suitPosAttribs[0] + initPos = Point3(initX, initY, initZ) + if len(suitPosAttribs) == 3: + finalX, finalY, finalZ = suitPosAttribs[1] + finalPos = Point3(finalX, finalY, finalZ) + posIvalDuration = suitPosAttribs[2] + self.clearMoveIval() + + def getForwardIval(blendTypeStr, self = self): + forwardIval = LerpPosInterval(self.suit, posIvalDuration, pos=finalPos, startPos=initPos, name='%s-moveFront' % self.suitName, blendType=blendTypeStr, fluid=1) + return forwardIval + + def getBackwardIval(blendTypeStr, self = self): + backwardIval = LerpPosInterval(self.suit, posIvalDuration, pos=initPos, startPos=finalPos, name='%s-moveBack' % self.suitName, blendType=blendTypeStr, fluid=1) + return backwardIval + + if abs(finalZ - initZ) > 0.0: + + def setIsGoingUp(value): + self.isGoingUp = value + + self.isMovingUpDown = True + self.suit.setH(90) + self.suit.prop = None + if self.suit.prop == None: + self.suit.prop = BattleProps.globalPropPool.getProp('propeller') + self.suit.prop.setScale(1.1) + self.suit.prop.setColor(1, 1, 0.6, 1) + head = self.suit.find('**/joint_head') + self.suit.prop.reparentTo(head) + self.propTrack = Sequence(ActorInterval(self.suit.prop, 'propeller', startFrame=8, endFrame=25, playRate=2.0)) + self.animTrack = Sequence(ActorInterval(self.suit, 'landing', startFrame=8, endFrame=28, playRate=0.5), ActorInterval(self.suit, 'landing', startFrame=8, endFrame=28, playRate=-0.5)) + self.moveIval = Sequence(Func(setIsGoingUp, True), getForwardIval('easeInOut'), Func(setIsGoingUp, False), getBackwardIval('easeInOut')) + self.suitSound = base.loadSfx('phase_4/audio/sfx/TB_propeller.wav') + else: + self.isMovingLeftRight = True + self.moveIval = Sequence(Func(self.setHeading, finalPos, initPos), getForwardIval('noBlend'), Func(self.setHeading, initPos, finalPos), getBackwardIval('noBlend')) + self.suit.setPos(initX, initY, initZ) + self.suit.dropShadow.hide() + self.setupCollision() + return + + def setupCollision(self): + collSphere = CollisionSphere(0, 0, 2, 2) + collSphere.setTangible(1) + collNode = CollisionNode(self.game.uniqueName(self.suitName)) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(collSphere) + self.collNodePath = self.suit.attachNewNode(collNode) + self.collNodePath.hide() + if self.showCollSpheres: + self.collNodePath.show() + self.accept(self.game.uniqueName('enter' + self.suitName), self.handleEnemyCollision) + + def clearMoveIval(self): + if self.moveIval: + self.moveIval.pause() + del self.moveIval + self.moveIval = None + return + + def start(self, elapsedTime): + if self.moveIval: + self.moveIval.loop() + self.moveIval.setT(elapsedTime) + if self.isMovingLeftRight: + self.suit.loop('walk') + elif self.isMovingUpDown: + self.propTrack.loop() + self.animTrack.loop() + base.playSfx(self.suitSound, node=self.suit, looping=1) + + def enterPause(self): + if hasattr(self, 'moveIval') and self.moveIval: + self.moveIval.pause() + self.suit.loop('neutral') + if self.suitSound: + self.suitSound.stop() + + def exitPause(self): + if hasattr(self, 'moveIval') and self.moveIval: + self.moveIval.resume() + if self.isMovingLeftRight: + self.suit.loop('walk') + elif self.isMovingUpDown: + self.propTrack.loop() + self.animTrack.loop() + base.playSfx(self.suitSound, node=self.suit, looping=1, volume=0.1) + + def handleEnemyCollision(self, cevent): + messenger.send('enemyHit') + + def setHeading(self, finalPos, initPos): + diffX = finalPos.getX() - initPos.getX() + angle = -90 * diffX / math.fabs(diffX) + startAngle = self.suit.getH() + startAngle = PythonUtil.fitSrcAngle2Dest(startAngle, angle) + dur = 0.1 * abs(startAngle - angle) / 90 + self.suitTurnIval = LerpHprInterval(self.suit, dur, Point3(angle, 0, 0), startHpr=Point3(startAngle, 0, 0), name='SuitLerpHpr') + self.suitTurnIval.start() + + def blinkColor(self, color, duration): + blink = Sequence(LerpColorScaleInterval(self.suit, 0.5, color, startColorScale=VBase4(1, 1, 1, 1)), LerpColorScaleInterval(self.suit, 0.5, VBase4(1, 1, 1, 1), startColorScale=color)) + track = Sequence(Func(blink.loop), Wait(duration), Func(blink.finish)) + return track + + def doShotTrack(self): + blinkRed = self.blinkColor(COLOR_RED, 2) + point = Point3(self.suit.getX(render), self.suit.getY(render), self.suit.getZ(render) + self.suit.height / 2.0) + scale = 0.3 + splashHold = 0.1 + + 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) + splashTrack = Sequence(Func(prepSplash, splash, point), ActorInterval(splash, 'splash-from-splat'), Wait(splashHold), Func(MovieUtil.removeProp, splash)) + self.shotTrack = Parallel(Func(self.game.assetMgr.playSplashSound), blinkRed, splashTrack) + self.shotTrack.start() + + def doDeathTrack(self): + + def removeDeathSuit(suit, deathSuit): + if not deathSuit.isEmpty(): + deathSuit.detachNode() + suit.cleanupLoseActor() + + if self.suitSound: + self.suitSound.stop() + self.deathSuit = self.suit.getLoseActor() + self.deathSuit.reparentTo(self.enemyMgr.enemiesNP) + self.deathSuit.setPos(render, self.suit.getPos(render)) + self.deathSuit.setHpr(render, self.suit.getHpr(render)) + self.suit.hide() + self.collNodePath.reparentTo(self.deathSuit) + treasureSpawnPoint = Point3(self.suit.getX(), self.suit.getY(), self.suit.getZ() + self.suit.height / 2.0) + gearPoint = Point3(0, 0, self.suit.height / 2.0 + 2.0) + spinningSound = base.loadSfx('phase_3.5/audio/sfx/Cog_Death.mp3') + deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3') + 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) + if self.isMovingLeftRight: + self.enterPause() + 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)) + soundTrack = Sequence(SoundInterval(spinningSound, duration=1.6, startTime=0.6, volume=0.8, node=self.deathSuit), SoundInterval(deathSound, volume=0.32, node=self.deathSuit)) + 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') + elif self.isMovingUpDown: + + def getFinalPos(): + if self.isGoingUp: + direction = 1.0 + else: + direction = -1.0 + pos = Point3(self.deathSuit.getX(), self.deathSuit.getY(), self.deathSuit.getZ() + 2.0 * direction) + return pos + + deathMoveIval = LerpPosInterval(self.deathSuit, 1.5, pos=getFinalPos(), name='%s-deathSuitMove' % self.suitName, blendType='easeInOut', fluid=1) + suitTrack = Sequence(Func(self.collNodePath.stash), Parallel(ActorInterval(self.deathSuit, 'lose', startFrame=80, endFrame=140), deathMoveIval), Func(removeDeathSuit, self.suit, self.deathSuit, name='remove-death-suit')) + explosionTrack = Sequence(Wait(1.5), MovieUtil.createKapowExplosionTrack(self.deathSuit, explosionPoint=gearPoint)) + soundTrack = Sequence(SoundInterval(spinningSound, duration=1.6, startTime=0.6, volume=0.8, node=self.deathSuit), SoundInterval(deathSound, volume=0.32, node=self.deathSuit)) + gears1Track = Sequence(ParticleInterval(smallGears, self.deathSuit, worldRelative=0, duration=4.3, cleanup=True), name='gears1Track') + gears2MTrack = Track((0.0, explosionTrack), (0.0, ParticleInterval(singleGear, self.deathSuit, worldRelative=0, duration=5.7, cleanup=True)), (2.7, ParticleInterval(smallGearExplosion, self.deathSuit, worldRelative=0, duration=1.2, cleanup=True)), (2.9, 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 = Parallel(Func(removeParticle, smallGears), Func(removeParticle, singleGear), Func(removeParticle, smallGearExplosion), Func(removeParticle, bigGearExplosion)) + self.deathTrack = Sequence(Parallel(suitTrack, gears2MTrack, gears1Track, soundTrack), removeParticles, Func(self.destroy)) + self.deathTrack.start() diff --git a/toontown/minigame/TwoDEnemyMgr.py b/toontown/minigame/TwoDEnemyMgr.py new file mode 100644 index 0000000..76eb567 --- /dev/null +++ b/toontown/minigame/TwoDEnemyMgr.py @@ -0,0 +1,50 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDEnemy + +class TwoDEnemyMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDEnemyMgr') + + def __init__(self, section, enemyList): + self.section = section + self.enemyList = enemyList + self.load() + + def destroy(self): + self.section = None + while len(self.enemies): + enemy = self.enemies[0] + enemy.destroy() + self.enemies.remove(enemy) + + self.enemies = None + return + + def load(self): + if len(self.enemyList): + self.enemiesNP = NodePath('Enemies') + self.enemiesNP.reparentTo(self.section.sectionNP) + self.enemies = [] + for index in xrange(len(self.enemyList)): + enemyId = self.section.getSectionizedId(index) + suitAttribs = self.enemyList[index] + newEnemy = TwoDEnemy.TwoDEnemy(self, enemyId, suitAttribs) + newEnemy.suit.reparentTo(self.enemiesNP) + self.enemies.append(newEnemy) + + def enterPlay(self, elapsedTime): + for enemy in self.enemies: + enemy.start(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for enemy in self.enemies: + enemy.enterPause() + + def exitPause(self): + for enemy in self.enemies: + enemy.exitPause() diff --git a/toontown/minigame/TwoDGameToonSD.py b/toontown/minigame/TwoDGameToonSD.py new file mode 100644 index 0000000..fe0afcf --- /dev/null +++ b/toontown/minigame/TwoDGameToonSD.py @@ -0,0 +1,296 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import ToonBlitzGlobals +from otp.otpbase import OTPGlobals +from direct.task.Task import Task +from toontown.minigame import TwoDBattleMgr +from toontown.racing import RaceHeadFrame +COLOR_RED = VBase4(1, 0, 0, 0.3) + +class TwoDGameToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDGameToonSD') + FallBackAnim = 'slip-backward' + NeutralAnim = 'neutral' + RunAnim = 'run' + ShootGun = 'water-gun' + Victory = 'victory' + animList = [FallBackAnim, + NeutralAnim, + RunAnim, + ShootGun, + Victory] + ScoreTextGenerator = TextNode('ScoreTextGenerator') + + def __init__(self, avId, game): + self.avId = avId + self.game = game + self.isLocal = avId == base.localAvatar.doId + self.toon = self.game.getAvatar(self.avId) + self.unexpectedExit = False + self.fallBackIval = None + self.fallDownIval = None + self.victoryIval = None + self.squishIval = None + self.fsm = ClassicFSM.ClassicFSM('TwoDGameAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['normal']), + State.State('normal', self.enterNormal, self.exitNormal, ['shootGun', + 'fallBack', + 'fallDown', + 'victory', + 'squish']), + State.State('shootGun', self.enterShootGun, self.exitShootGun, ['normal', + 'fallBack', + 'fallDown', + 'victory', + 'squish']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal', 'fallDown', 'squish']), + State.State('fallDown', self.enterFallDown, self.exitFallDown, ['normal']), + State.State('squish', self.enterSquish, self.exitSquish, ['normal']), + State.State('victory', self.enterVictory, self.exitVictory, ['normal', 'fallDown']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + self.scoreText = None + self.load() + self.progressLineLength = self.game.assetMgr.faceEndPos[0] - self.game.assetMgr.faceStartPos[0] + self.conversionRatio = self.progressLineLength / self.game.gameLength + return + + def load(self): + for anim in self.animList: + self.toon.pose(anim, 0) + + self.battleMgr = TwoDBattleMgr.TwoDBattleMgr(self.game, self.toon) + self.squishSound = base.loadSfx('phase_3.5/audio/dial/AV_' + self.toon.style.getAnimal() + '_exclaim.mp3') + + def destroy(self): + if self.fallBackIval != None: + self.fallBackIval.finish() + self.fallBackIval = None + if self.fallDownIval != None: + self.fallDownIval.finish() + self.fallDownIval = None + if self.squishIval != None: + self.squishIval.finish() + self.squishIval = None + if self.victoryIval != None: + self.victoryIval.finish() + self.victoryIval = None + self.hideScoreText() + self.headFrame.destroy() + self.battleMgr.destroy() + del self.battleMgr + del self.fsm + return + + def enter(self): + self.fsm.enterInitialState() + + def exit(self, unexpectedExit = False): + self.unexpectedExit = unexpectedExit + self.fsm.requestFinalState() + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + self.toon.useLOD(1000) + self.toon.dropShadow.hide() + + def exitInit(self): + pass + + def enterNormal(self): + self.notify.debug('enterNormal') + + def exitNormal(self): + pass + + def enterFallBack(self): + self.notify.debug('enterFallBack') + fallBackDist = 9 + self.blinkRed = self.blinkColor(COLOR_RED, 2) + duration = 4 + animName = self.FallBackAnim + startFrame = 1 + endFrame = 22 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + fallBackAnim = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=endFrame / newRate, playRate=playRate), ActorInterval(self.toon, animName, startFrame=23, playRate=1), FunctionInterval(self.resume)) + self.fallBackIval = Parallel(self.blinkRed, fallBackAnim) + if self.isLocal: + self.game.ignoreInputs() + self.toon.controlManager.currentControls.lifter.setVelocity(12) + if self.toon.getH() == 90: + endPoint = Point3(self.toon.getX() + fallBackDist, self.toon.getY(), self.toon.getZ()) + else: + endPoint = Point3(self.toon.getX() - fallBackDist, self.toon.getY(), self.toon.getZ()) + enemyHitTrajectory = LerpFunc(self.toon.setX, fromData=self.toon.getX(), toData=endPoint.getX(), duration=0.75, name='enemyHitTrajectory') + self.fallBackIval.append(enemyHitTrajectory) + base.playSfx(self.game.assetMgr.sndOof) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + self.blinkRed.finish() + if self.isLocal: + if self.game.gameFSM.getCurrentState().getName() == 'play': + self.game.acceptInputs() + + def shootGun(self): + if self.fsm.getCurrentState().getName() == 'shootGun': + self.fsm.request('normal') + self.fsm.request('shootGun') + + def enterShootGun(self): + self.battleMgr.shoot() + self.fsm.request('normal') + + def exitShootGun(self): + pass + + def enterFallDown(self): + self.notify.debug('enterFallDown') + self.fallDownIval = Sequence() + self.blinkRed = self.blinkColor(COLOR_RED, 2) + if self.isLocal: + self.game.ignoreInputs() + base.playSfx(self.game.assetMgr.fallSound) + pos = self.game.sectionMgr.getLastSpawnPoint() + toonRespawnIval = Sequence(Wait(1), Func(base.localAvatar.setPos, pos)) + self.fallDownIval.append(toonRespawnIval) + self.fallDownIval.append(Func(self.resume)) + self.fallDownIval.append(self.blinkRed) + self.fallDownIval.start() + + def exitFallDown(self): + if self.isLocal: + if self.game.gameFSM.getCurrentState().getName() == 'play': + self.game.acceptInputs() + + def enterSquish(self): + self.notify.debug('enterSquish') + if self.isLocal: + self.game.ignoreInputs() + self.toon.setAnimState('Squish') + self.toon.stunToon() + base.playSfx(self.squishSound, node=self.toon, volume=2) + self.blinkRed = self.blinkColor(COLOR_RED, 3) + self.squishIval = Parallel(self.blinkRed, Sequence(Wait(2.5), Func(self.resume))) + self.squishIval.start() + + def exitSquish(self): + if self.isLocal: + if self.game.gameFSM.getCurrentState().getName() == 'play': + self.game.acceptInputs() + + def enterVictory(self): + self.notify.debug('enterVictory') + outsideElevatorPos = self.game.sectionMgr.exitElevator.find('**/loc_elevator_front').getPos(render) + insideElevatorPos = self.game.sectionMgr.exitElevator.find('**/loc_elevator_inside').getPos(render) + runToElevator = Parallel(LerpPosInterval(self.toon, self.game.timeToRunToElevator, outsideElevatorPos), ActorInterval(self.toon, self.RunAnim, loop=1, duration=self.game.timeToRunToElevator)) + danceIval = Parallel(LerpPosInterval(self.toon, 2, insideElevatorPos), ActorInterval(self.toon, self.Victory, loop=1, duration=ToonBlitzGlobals.GameDuration[self.game.getSafezoneId()])) + waitToLand = 0.0 + if self.toon.getZ(render) > 13: + waitToLand = 1 + self.victoryIval = Sequence(Wait(waitToLand), runToElevator, danceIval) + self.victoryIval.start() + + def exitVictory(self): + pass + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.toon.stopBlink() + self.toon.startLookAround() + self.toon.resetLOD() + self.toon.dropShadow.show() + + def exitCleanup(self): + pass + + def resume(self): + self.fsm.request('normal') + messenger.send('jumpLand') + + def blinkColor(self, color, duration): + blink = Sequence(LerpColorScaleInterval(self.toon, 0.5, color, startColorScale=VBase4(1, 1, 1, 1)), LerpColorScaleInterval(self.toon, 0.5, VBase4(1, 1, 1, 1), startColorScale=color)) + track = Sequence(Func(blink.loop), Wait(duration), Func(blink.finish)) + return track + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) + + def createHeadFrame(self, drawNum): + toon = base.cr.doId2do.get(self.avId, None) + self.headFrame = RaceHeadFrame.RaceHeadFrame(av=toon) + eyes = self.headFrame.head.find('**/eyes*') + eyes.setDepthTest(1) + eyes.setDepthWrite(1) + self.headFrame.configure(geom_scale=(0.5, 0.5, 0.5)) + self.headFrame.setZ(self.game.assetMgr.faceStartPos[2]) + self.headFrame.setDepthWrite(True) + self.headFrame.setDepthTest(True) + self.headFrame.reparentTo(self.game.assetMgr.aspect2dRoot) + self.headFrame.setY(-drawNum) + if self.isLocal: + self.headFrame.setScale(0.2) + else: + self.headFrame.setScale(0.15) + self.headFrame.setX(self.game.assetMgr.faceStartPos[0]) + return + + def update(self): + toonCurrX = self.toon.getX(render) + progress = toonCurrX - self.game.gameStartX + headFrameProgress = progress * self.conversionRatio + headFrameX = self.game.assetMgr.faceStartPos[0] + headFrameProgress + headFrameX = max(self.game.assetMgr.faceStartPos[0], headFrameX) + headFrameX = min(self.game.assetMgr.faceEndPos[0], headFrameX) + self.headFrame.setX(headFrameX) + + def showScoreText(self, number, scale = 1.25): + if not number == 0: + if self.scoreText: + self.hideScoreText() + self.ScoreTextGenerator.setFont(OTPGlobals.getSignFont()) + if number < 0: + self.ScoreTextGenerator.setText(str(number)) + else: + self.ScoreTextGenerator.setText('+' + str(number)) + self.ScoreTextGenerator.clearShadow() + self.ScoreTextGenerator.setAlign(TextNode.ACenter) + if number < 0: + r, g, b, a = (0.9, 0, 0, 1) + else: + r, g, b, a = (0.9, 0.9, 0, 1) + self.scoreTextNode = self.ScoreTextGenerator.generate() + self.scoreText = self.toon.attachNewNode(self.scoreTextNode) + self.scoreText.setScale(scale) + self.scoreText.setBillboardPointEye() + self.scoreText.setBin('fixed', 100) + self.scoreText.setPos(0, 0, self.toon.height / 2) + self.scoreText.setTransparency(1) + self.scoreText.setColor(r, g, b, a) + self.scoreText.setDepthTest(0) + self.scoreText.setDepthWrite(0) + seq = Task.sequence(self.scoreText.lerpPos(Point3(0, 0, self.toon.height + 2), 0.5, blendType='easeOut'), self.scoreText.lerpColor(Vec4(r, g, b, a), Vec4(r, g, b, 0), 0.25), Task(self.hideScoreTextTask)) + taskMgr.add(seq, self.game.uniqueName('scoreText')) + + def hideScoreText(self): + if self.scoreText: + taskMgr.remove(self.game.uniqueName('scoreText')) + self.scoreText.removeNode() + self.scoreText = None + return + + def hideScoreTextTask(self, task): + self.hideScoreText() + return Task.done diff --git a/toontown/minigame/TwoDSection.py b/toontown/minigame/TwoDSection.py new file mode 100644 index 0000000..0470fac --- /dev/null +++ b/toontown/minigame/TwoDSection.py @@ -0,0 +1,127 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDBlock +from toontown.minigame import TwoDEnemyMgr +from toontown.minigame import TwoDTreasureMgr +from toontown.minigame import TwoDSpawnPointMgr +from toontown.minigame import TwoDStomperMgr + +class TwoDSection(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDSection') + + def __init__(self, indexNum, sectionInfo, sectionNP, sectionMgr): + self.indexNum = indexNum + self.sectionNP = sectionNP + self.sectionMgr = sectionMgr + self.blocks = [] + self.load(sectionInfo) + + def destroy(self): + for block in self.blocks: + block.destroy() + + self.enemyMgr.destroy() + del self.enemyMgr + self.treasureMgr.destroy() + del self.treasureMgr + self.spawnPointMgr.destroy() + del self.spawnPointMgr + self.stomperMgr.destroy() + del self.stomperMgr + self.sectionMgr = None + self.sectionNP = None + self.blockList = [] + self.enemyList = [] + self.treasureList = [] + self.spawnPointList = [] + return + + def load(self, sectionInfo): + self.sectionTypeNum = sectionInfo[0] + enemyIndicesSelected = sectionInfo[1] + treasureIndicesSelected = sectionInfo[2] + spawnPointIndicesSelected = sectionInfo[3] + stomperIndicesSelected = sectionInfo[4] + attribs = ToonBlitzGlobals.SectionTypes[self.sectionTypeNum] + self.length = attribs[1] + self.blockList = attribs[2] + enemiesPool = attribs[3] + treasuresPool = attribs[4] + spawnPointsPool = attribs[5] + stompersPool = attribs[6] + self.enemyList = [] + for enemyIndex in enemyIndicesSelected: + self.enemyList.append(enemiesPool[enemyIndex]) + + self.treasureList = [] + for treasure in treasureIndicesSelected: + treasureIndex = treasure[0] + treasureValue = treasure[1] + treasureAttribs = treasuresPool[treasureIndex] + self.treasureList.append((treasureAttribs, treasureValue)) + + self.spawnPointList = [] + for spawnPointIndex in spawnPointIndicesSelected: + self.spawnPointList.append(spawnPointsPool[spawnPointIndex]) + + self.stomperList = [] + for stomperIndex in stomperIndicesSelected: + self.stomperList.append(stompersPool[stomperIndex]) + + self.blocksNP = NodePath('Blocks') + self.blocksNP.reparentTo(self.sectionNP) + if self.blockList[0][1][0] != (0, 0, 12): + self.notify.warning('First block of section %s does not start at (0, 0, 12)' % self.sectionTypeNum) + for index in range(0, len(self.blockList)): + blockAttribs = self.blockList[index] + fileName = ToonBlitzGlobals.BlockTypes[blockAttribs[0]][0] + blockIndex = int(fileName[-1]) + blockType = self.sectionMgr.game.assetMgr.blockTypes[blockIndex] + sectionizedId = self.getSectionizedId(index) + newBlock = TwoDBlock.TwoDBlock(blockType, sectionizedId, blockAttribs) + newBlock.model.reparentTo(self.blocksNP) + self.blocks.append(newBlock) + + self.enemyMgr = TwoDEnemyMgr.TwoDEnemyMgr(self, self.enemyList) + self.treasureMgr = TwoDTreasureMgr.TwoDTreasureMgr(self, self.treasureList, self.enemyList) + self.spawnPointMgr = TwoDSpawnPointMgr.TwoDSpawnPointMgr(self, self.spawnPointList) + self.stomperMgr = TwoDStomperMgr.TwoDStomperMgr(self, self.stomperList) + if self.sectionTypeNum == 'end': + self.spawnPointMgr.setupLastSavePointHandle() + + def enterPlay(self, elapsedTime): + for block in self.blocks: + block.start(elapsedTime) + + self.enemyMgr.enterPlay(elapsedTime) + self.stomperMgr.enterPlay(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for block in self.blocks: + block.enterPause() + + self.enemyMgr.enterPause() + self.stomperMgr.enterPause() + + def exitPause(self): + for block in self.blocks: + block.exitPause() + + self.enemyMgr.exitPause() + self.stomperMgr.exitPause() + + def getSectionizedId(self, num): + + def getTwoDigitString(index): + if index < 10: + output = '0' + str(index) + else: + output = str(index) + return output + + return getTwoDigitString(self.indexNum) + '-' + getTwoDigitString(num) diff --git a/toontown/minigame/TwoDSectionMgr.py b/toontown/minigame/TwoDSectionMgr.py new file mode 100644 index 0000000..2a3cabe --- /dev/null +++ b/toontown/minigame/TwoDSectionMgr.py @@ -0,0 +1,134 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDSection +from toontown.minigame import TwoDSpawnPointMgr +from toontown.minigame import TwoDBlock +from direct.gui import DirectGui +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + +class TwoDSectionMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDSectionMgr') + + def __init__(self, game, sectionsSelected): + self.game = game + self.sectionsPool = [] + self.sectionsSelected = [] + self.sections = [] + self.sectionNPList = [] + self.activeSection = 0 + self.setupStartSection() + self.setupSections(sectionsSelected) + self.setupEndSection(len(sectionsSelected)) + + def destroy(self): + while len(self.sections): + section = self.sections[0] + section.destroy() + self.sections.remove(section) + + self.sections = [] + self.sectionsPool = [] + self.sectionsSelected = [] + self.sectionNPList = [] + self.startWall.removeNode() + del self.startWall + self.startPipe.removeNode() + del self.startPipe + self.startArrow.removeNode() + del self.startArrow + self.endArrow.removeNode() + del self.endArrow + self.game = None + self.activeSection = 0 + return + + def setupStartSection(self): + self.startSectionNP = NodePath('StartSection') + self.startSectionNP.reparentTo(self.game.assetMgr.world) + self.startSectionNP.setX(-48) + self.startWall = self.game.assetMgr.startingWall.copyTo(self.startSectionNP) + self.startWall.setPos(-28, 0, 4) + self.startWall.setScale(0.8) + self.startPipe = self.game.assetMgr.startingPipe.copyTo(self.startSectionNP) + self.startPipe.setPos(12, 0, 44) + self.startArrow = self.game.assetMgr.arrow.copyTo(self.startSectionNP) + self.startArrow.setPos(23, 1.5, 12.76) + for index in xrange(len(ToonBlitzGlobals.BlockListStart)): + blockAttribs = ToonBlitzGlobals.BlockListStart[index] + fileName = ToonBlitzGlobals.BlockTypes[blockAttribs[0]][0] + blockIndex = int(fileName[-1]) + blockType = self.game.assetMgr.blockTypes[blockIndex] + sectionizedId = 'start-' + str(index) + newBlock = TwoDBlock.TwoDBlock(blockType, sectionizedId, blockAttribs) + newBlock.model.reparentTo(self.startSectionNP) + + def setupEndSection(self, index): + aspectSF = 0.7227 + self.endSectionNP = NodePath('EndSection') + self.endSectionNP.reparentTo(self.game.assetMgr.world) + self.endSectionNP.setX(self.incrementX) + self.endWall = self.game.assetMgr.startingWall.copyTo(self.endSectionNP) + self.endWall.setPos(100, 0, 4) + self.endWall.setScale(0.8) + self.endArrow = self.game.assetMgr.arrow.copyTo(self.endSectionNP) + self.endArrow.setPos(6, 1.5, 12.76) + self.exitElevator = self.game.assetMgr.exitElevator.copyTo(self.endSectionNP) + self.exitElevator.setPos(52, -2, 12.7) + cogSignModel = loader.loadModel('phase_4/models/props/sign_sellBotHeadHQ') + cogSign = cogSignModel.find('**/sign_sellBotHeadHQ') + cogSignSF = 23 + elevatorSignSF = 15 + sideDoor = self.exitElevator.find('**/doorway2') + sdSign = cogSign.copyTo(sideDoor) + sdSign.setPosHprScale(0, 1.9, 15, 0, 0, 0, elevatorSignSF, elevatorSignSF, elevatorSignSF * aspectSF) + sdSign.node().setEffect(DecalEffect.make()) + sdText = DirectGui.OnscreenText(text=TTLocalizer.TwoDGameElevatorExit, font=ToontownGlobals.getSuitFont(), pos=(0, -0.34), scale=0.15, mayChange=False, parent=sdSign) + sdText.setDepthWrite(0) + self.sectionNPList.append(self.endSectionNP) + endSectionInfo = ('end', + [], + [], + [0], + []) + endSection = TwoDSection.TwoDSection(index, endSectionInfo, self.endSectionNP, self) + self.sections.append(endSection) + self.incrementX += endSection.length + + def setupSections(self, sectionsSelected): + self.incrementX = -24 + for index in range(0, len(sectionsSelected)): + sectionNP = NodePath('Section' + str(index)) + sectionNP.reparentTo(self.game.assetMgr.world) + sectionNP.setX(self.incrementX) + self.sectionNPList.append(sectionNP) + section = TwoDSection.TwoDSection(index, sectionsSelected[index], sectionNP, self) + self.sections.append(section) + self.incrementX += section.length + + def enterPlay(self, elapsedTime): + for section in self.sections: + section.enterPlay(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for section in self.sections: + section.enterPause() + + def exitPause(self): + for section in self.sections: + section.exitPause() + + def updateActiveSection(self, sectionIndex): + if self.activeSection != sectionIndex: + self.activeSection = sectionIndex + self.notify.debug('Toon is in section %s.' % sectionIndex) + + def getLastSpawnPoint(self): + relativePoint = Point3(self.sections[self.activeSection].spawnPointMgr.getSpawnPoint()) + relativePoint.setX(relativePoint.getX() + self.sectionNPList[self.activeSection].getX()) + return relativePoint diff --git a/toontown/minigame/TwoDSpawnPointMgr.py b/toontown/minigame/TwoDSpawnPointMgr.py new file mode 100644 index 0000000..09506ef --- /dev/null +++ b/toontown/minigame/TwoDSpawnPointMgr.py @@ -0,0 +1,88 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.toonbase import ToontownGlobals + +class TwoDSpawnPointMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDSpawnPointMgr') + + def __init__(self, section, spawnPointList): + self.section = section + self.game = self.section.sectionMgr.game + self.spawnPointList = spawnPointList + self.lastSavePoint = 0 + self.showCollSpheres = False + self.savePoints = [] + self.loadPoints = [] + self.collNPList = [] + self.collDict = {} + self.load() + + def destroy(self): + while len(self.collNPList): + item = self.collNPList[0] + self.ignore('enter' + self.collNPList[0].node().getName()) + self.collNPList.remove(item) + item.removeNode() + + self.section = None + self.game = None + self.savePoints = None + self.loadPoints = None + self.collNPList = None + self.collDict = None + return + + def load(self): + if len(self.spawnPointList): + self.spawnPointsNP = NodePath('SpawnPoints') + self.spawnPointsNP.reparentTo(self.section.sectionNP) + for point in self.spawnPointList: + if len(point) == 1: + savePoint = point[0] + loadPoint = point[0] + else: + savePoint = point[0] + loadPoint = point[1] + index = len(self.savePoints) + self.savePoints.append(savePoint) + self.loadPoints.append(loadPoint) + self.setupCollision(index) + + def setupCollision(self, index): + collSphere = CollisionSphere(0, 0, 0, 3) + collSphereName = 'savePoint%s' % self.section.getSectionizedId(index) + collSphere.setTangible(0) + collNode = CollisionNode(self.game.uniqueName(collSphereName)) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + collNode.addSolid(collSphere) + collNodePath = self.spawnPointsNP.attachNewNode(collNode) + collNodePath.hide() + if self.showCollSpheres: + collNodePath.show() + posX, posY, posZ = self.savePoints[index] + collNodePath.setPos(posX, posY, posZ) + self.collNPList.append(collNodePath) + self.collDict[collNodePath.getName()] = index + self.accept(self.game.uniqueName('enter' + collSphereName), self.handleSavePointCollision) + + def handleSavePointCollision(self, cevent): + savePointName = cevent.getIntoNodePath().getName() + self.lastSavePoint = self.collDict[savePointName] + self.section.sectionMgr.updateActiveSection(self.section.indexNum) + + def getSpawnPoint(self): + if len(self.loadPoints) > 0: + point = self.loadPoints[self.lastSavePoint] + return Point3(point[0], point[1], point[2]) + else: + return Point3(ToonBlitzGlobals.ToonStartingPosition[0], ToonBlitzGlobals.ToonStartingPosition[1], ToonBlitzGlobals.ToonStartingPosition[2]) + + def setupLastSavePointHandle(self): + if len(self.collNPList) > 0: + self.accept('enter' + self.collNPList[-1].getName(), self.handleLastSavePointCollision) + self.gameEndX = self.collNPList[-1].getX(render) + + def handleLastSavePointCollision(self, cevent): + self.game.localToonVictory() diff --git a/toontown/minigame/TwoDStomper.py b/toontown/minigame/TwoDStomper.py new file mode 100644 index 0000000..cc1283b --- /dev/null +++ b/toontown/minigame/TwoDStomper.py @@ -0,0 +1,160 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.minigame import ToonBlitzGlobals +GOING_UP = 1 +GOING_DOWN = 2 +STUCK_DOWN = 3 + +class TwoDStomper(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDStomper') + + def __init__(self, stomperMgr, index, stomperAttribs, model): + self.game = stomperMgr.section.sectionMgr.game + self.index = index + stomperName = 'stomper-' + str(self.index) + self.model = NodePath(stomperName) + self.nodePath = model.copyTo(self.model) + self.ival = None + self.stashCollisionsIval = None + self.removeHeadFloor = 0 + self.stomperState = STUCK_DOWN + self.setupStomper(stomperAttribs) + return + + def destroy(self): + self.game = None + self.ignoreAll() + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + if self.smoke: + self.smoke.removeNode() + del self.smoke + self.smoke = None + if self.stashCollisionsIval: + self.stashCollisionsIval.finish() + del self.stashCollisionsIval + self.stashCollisionsIval = None + for collSolid in self.collSolids: + collSolid.stash() + + self.nodePath.removeNode() + del self.nodePath + self.model.removeNode() + if self.model: + self.model.removeNode() + del self.model + self.model = None + return + + def setupStomper(self, stomperAttribs): + stomperType = stomperAttribs[0] + self.pos = Point3(stomperAttribs[1][0], stomperAttribs[1][1], stomperAttribs[1][2]) + self.period = stomperAttribs[2] + typeAttribs = ToonBlitzGlobals.StomperTypes[stomperType] + self.motionType = typeAttribs[0] + self.scale = typeAttribs[1] + self.headStartZ, self.headEndZ = typeAttribs[2] + self.shaftStartScaleZ, self.shaftEndScaleZ = typeAttribs[3] + self.numCollSolids = typeAttribs[4] + self.stompSound = loader.loadSfx('phase_4/audio/sfx/CHQ_FACT_stomper_small.mp3') + self.model.setPos(self.pos) + self.model.setScale(self.scale) + self.model.find('**/block').setScale(1.0 / self.scale) + self.head = self.model.find('**/head') + self.shaft = self.model.find('**/shaft') + self.collisions = self.model.find('**/stomper_collision') + originalColl = self.model.find('**/stomper_collision') + self.range = self.headEndZ - self.headStartZ + self.collSolids = [] + self.collSolids.append(originalColl) + for i in xrange(self.numCollSolids - 1): + newColl = originalColl.copyTo(self.model) + self.collSolids.append(newColl) + + self.collSolids[-1].reparentTo(self.head) + self.smoke = loader.loadModel('phase_4/models/props/test_clouds') + self.smoke.setZ(self.headEndZ - 1) + self.smoke.setColor(0.8, 0.7, 0.5, 1) + self.smoke.setBillboardPointEye() + self.smoke.setScale(1.0 / self.scale) + self.smoke.setDepthWrite(False) + + def getMotionIval(self): + + def motionFunc(t, self = self): + stickTime = 0.2 + turnaround = 0.95 + t = t % 1 + if t < stickTime: + self.head.setFluidZ(0 + self.headEndZ) + if self.stomperState != STUCK_DOWN: + self.stomperState = STUCK_DOWN + elif t < turnaround: + self.head.setFluidZ((t - stickTime) * -self.range / (turnaround - stickTime) + self.headEndZ) + if self.stomperState != GOING_UP: + self.stomperState = GOING_UP + elif t > turnaround: + self.head.setFluidZ(-self.range + (t - turnaround) * self.range / (1 - turnaround) + self.headEndZ) + if self.stomperState != GOING_DOWN: + self.stomperState = GOING_DOWN + self.checkSquashedToon() + + motionIval = Sequence(LerpFunctionInterval(motionFunc, duration=self.period)) + return motionIval + + def getSmokeTrack(self): + smokeTrack = Sequence(Parallel(LerpScaleInterval(self.smoke, 0.2, Point3(1, 1, 1.5)), LerpColorScaleInterval(self.smoke, 0.4, VBase4(1, 1, 1, 0), VBase4(1, 1, 1, 0.5))), Func(self.smoke.reparentTo, hidden), Func(self.smoke.clearColorScale)) + return smokeTrack + + def adjustShaftScale(self, t): + heightDiff = self.head.getZ() - self.headStartZ + self.shaft.setScale(1, 1, self.shaftStartScaleZ + heightDiff * (self.shaftEndScaleZ - self.shaftStartScaleZ) / self.range) + + def adjustCollSolidHeight(self, t): + heightDiff = self.head.getZ() - self.headStartZ + for i in range(1, len(self.collSolids) - 1): + self.collSolids[i].setZ(heightDiff * i / (self.numCollSolids - 1)) + + def start(self, elapsedTime): + if self.ival: + self.ival.pause() + del self.ival + self.ival = None + self.ival = Parallel() + self.ival.append(Sequence(self.getMotionIval(), Func(base.playSfx, self.stompSound, node=self.model, volume=0.3), Func(self.smoke.reparentTo, self.model), self.getSmokeTrack())) + self.ival.append(LerpFunctionInterval(self.adjustShaftScale, duration=self.period)) + self.ival.append(LerpFunctionInterval(self.adjustCollSolidHeight, duration=self.period)) + self.ival.loop() + self.ival.setT(elapsedTime) + return + + def enterPause(self): + if self.ival: + self.ival.pause() + + def exitPause(self): + if self.ival: + self.ival.loop() + + def checkSquashedToon(self): + toonXDiff = (base.localAvatar.getX(render) - self.model.getX(render)) / self.scale + toonZ = base.localAvatar.getZ(render) + headEndZAbs = self.model.getZ(render) + self.headEndZ * self.scale + if toonXDiff > -1.0 and toonXDiff < 1.0 and toonZ > headEndZAbs and toonZ < self.head.getZ(render): + if not base.localAvatar.isStunned: + + def stashCollisions(self = self): + for collSolid in self.collSolids: + collSolid.stash() + + def unstashCollisions(self = self): + for collSolid in self.collSolids: + collSolid.unstash() + + self.stashCollisionsIval = Sequence(Func(stashCollisions), Wait(2.5), Func(unstashCollisions)) + self.stashCollisionsIval.start() + self.game.localToonSquished() diff --git a/toontown/minigame/TwoDStomperMgr.py b/toontown/minigame/TwoDStomperMgr.py new file mode 100644 index 0000000..c8860cd --- /dev/null +++ b/toontown/minigame/TwoDStomperMgr.py @@ -0,0 +1,56 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDStomper + +class TwoDStomperMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDStomperMgr') + + def __init__(self, section, stomperList): + self.section = section + self.stomperList = stomperList + self.load() + + def destroy(self): + self.section = None + while len(self.stompers): + stomper = self.stompers[0] + stomper.destroy() + self.stompers.remove(stomper) + + self.stompers = None + return + + def load(self): + if len(self.stomperList): + self.stompersNP = NodePath('Stompers') + self.stompersNP.reparentTo(self.section.sectionNP) + self.stompers = [] + for index in xrange(len(self.stomperList)): + stomperAttribs = self.stomperList[index] + self.createNewStomper(stomperAttribs) + + def createNewStomper(self, attrib, model = None): + stomperId = self.section.getSectionizedId(len(self.stompers)) + if model == None: + model = self.section.sectionMgr.game.assetMgr.stomper + newStomper = TwoDStomper.TwoDStomper(self, stomperId, attrib, model) + newStomper.model.reparentTo(self.stompersNP) + self.stompers.append(newStomper) + return + + def enterPlay(self, elapsedTime): + for stomper in self.stompers: + stomper.start(elapsedTime) + + def exitPlay(self): + pass + + def enterPause(self): + for stomper in self.stompers: + stomper.enterPause() + + def exitPause(self): + for stomper in self.stompers: + stomper.exitPause() diff --git a/toontown/minigame/TwoDTreasure.py b/toontown/minigame/TwoDTreasure.py new file mode 100644 index 0000000..7d26035 --- /dev/null +++ b/toontown/minigame/TwoDTreasure.py @@ -0,0 +1,115 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.minigame import ToonBlitzGlobals +from toontown.estate.GardenGlobals import BeanColors +import random + +class TwoDTreasure(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDTreasure') + RADIUS = 1.3 + + def __init__(self, treasureMgr, index, pos, value, isEnemyGenerated, model): + self.game = treasureMgr.section.sectionMgr.game + self.index = index + self.value = value + self.isEnemyGenerated = isEnemyGenerated + center = model.getBounds().getCenter() + center = Point3(0, 0, 0) + treasureName = 'treasure-' + str(index) + model.setScale(2.5) + self.model = NodePath(treasureName) + self.nodePath = model.copyTo(self.model) + self.appearEffect = None + self.flash = None + glowParticle = self.game.assetMgr.particleGlow + self.glowCard = glowParticle.copyTo(self.model) + self.glowCard2 = glowParticle.copyTo(self.glowCard) + self.glowCard.setPos(0, 0.1, 0) + self.glowCard.setColor(model.getColor()) + self.glowCard.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.glowCard2.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.glowScalIval = Sequence(LerpScaleInterval(self.glowCard, 0.4, scale=4.1, startScale=4.9), LerpScaleInterval(self.glowCard, 0.4, scale=4.9, startScale=4.1)) + self.glowScalIval.loop() + self.modelScalIval = Sequence(LerpScaleInterval(self.model, 0.4, scale=1.0, startScale=1.03), LerpScaleInterval(self.model, 0.4, scale=1.03, startScale=1.0)) + self.modelScalIval.loop() + self.sphereName = 'treasureSphere-%s-%s' % (self.game.doId, self.index) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.model.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.model.setPos(pos[0] - center[0], 0 - center[1], pos[2] - center[2]) + self.nodePath.flattenLight() + if self.isEnemyGenerated: + self.flash = glowParticle.copyTo(self.model) + self.flash.reparentTo(treasureMgr.treasuresNP) + self.flash.setPos(self.model.getX(), self.model.getY() - 0.2, self.model.getZ()) + self.flash.setScale(6) + self.flash.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd, ColorBlendAttrib.OIncomingAlpha, ColorBlendAttrib.OOne)) + self.hideTreasure() + return + + def destroy(self): + self.game = None + self.ignoreAll() + if self.glowScalIval: + self.glowScalIval.finish() + del self.glowScalIval + if self.modelScalIval: + self.modelScalIval.finish() + del self.modelScalIval + if self.appearEffect: + self.appearEffect.finish() + del self.appearEffect + if self.flash: + self.flash.removeNode() + del self.flash + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + self.model.removeNode() + del self.model + return + + def setRandomColor(self): + beanIndex = random.randint(0, len(BeanColors) - 1) + colors = BeanColors[beanIndex] + self.model.setColor(colors[0] / 255.0, colors[1] / 255.0, colors[2] / 255.0) + + def __handleEnterSphere(self, cevent): + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + sectionIndex = int(cevent.getIntoNodePath().getName()[-5:-3]) + treasureIndex = int(cevent.getIntoNodePath().getName()[-2:]) + messenger.send('twoDTreasureGrabbed', [sectionIndex, treasureIndex]) + + def hideTreasure(self): + self.model.hide() + self.collNode.setIntoCollideMask(BitMask32(0)) + if self.isEnemyGenerated: + self.flash.hide() + + def showTreasure(self): + self.model.show() + self.collNode.setIntoCollideMask(WallBitmask) + if self.isEnemyGenerated: + self.flash.show() + + def setTreasurePos(self, pos): + self.model.setPos(pos) + if self.isEnemyGenerated: + self.flash.setPos(self.model.getX(), self.model.getY() - 0.2, self.model.getZ()) + + def popupEnemyTreasure(self): + modelFadeIn = LerpFunc(self.model.setAlphaScale, duration=0.5) + flashFadeOut = LerpFunc(self.flash.setAlphaScale, fromData=1, toData=0, duration=0.5) + self.appearEffect = Sequence(Wait(2.4), Func(self.showTreasure), Parallel(modelFadeIn, flashFadeOut, Func(base.playSfx, self.game.assetMgr.sparkleSound))) + self.appearEffect.start() diff --git a/toontown/minigame/TwoDTreasureMgr.py b/toontown/minigame/TwoDTreasureMgr.py new file mode 100644 index 0000000..75415ab --- /dev/null +++ b/toontown/minigame/TwoDTreasureMgr.py @@ -0,0 +1,65 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.minigame import ToonBlitzGlobals +from toontown.minigame import TwoDTreasure +import random + +class TwoDTreasureMgr(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDTreasureMgr') + + def __init__(self, section, treasureList, enemyList): + self.section = section + self.treasureList = treasureList + self.enemyList = enemyList + self.load() + + def destroy(self): + while len(self.treasures): + treasure = self.treasures[0] + treasure.destroy() + self.treasures.remove(treasure) + + self.treasures = None + self.section = None + return + + def load(self): + if len(self.treasureList): + self.treasuresNP = NodePath('Treasures') + self.treasuresNP.reparentTo(self.section.sectionNP) + self.treasures = [] + for index in xrange(len(self.treasureList)): + treasureAttribs = self.treasureList[index][0] + treasureValue = self.treasureList[index][1] + self.createNewTreasure(treasureAttribs, treasureValue) + + self.enemyTreasures = [] + numPlayers = self.section.sectionMgr.game.numPlayers + pos = Point3(-1, -1, -1) + for index in xrange(len(self.enemyList)): + self.createNewTreasure([pos], numPlayers, isEnemyGenerated=True) + + def createNewTreasure(self, attrib, value, isEnemyGenerated = False, model = None): + treasureId = self.section.getSectionizedId(len(self.treasures)) + if model == None: + model = self.getModel(value, self.section.sectionMgr.game.assetMgr.treasureModelList) + newTreasure = TwoDTreasure.TwoDTreasure(self, treasureId, attrib[0], value, isEnemyGenerated, model) + newTreasure.model.reparentTo(self.treasuresNP) + self.treasures.append(newTreasure) + if isEnemyGenerated: + self.enemyTreasures.append(newTreasure) + return + + def getModel(self, value, modelList): + value -= 1 + model = modelList[value] + if value == 0: + model.setColor(1, 0.8, 0.8, 1) + elif value == 1: + model.setColor(0.8, 1, 0.8, 1) + elif value == 2: + model.setColor(0.9, 0.9, 1, 1) + elif value == 3: + model.setColor(1, 1, 0.6, 1) + return model diff --git a/toontown/minigame/TwoDWalk.py b/toontown/minigame/TwoDWalk.py new file mode 100644 index 0000000..e160dea --- /dev/null +++ b/toontown/minigame/TwoDWalk.py @@ -0,0 +1,13 @@ +from OrthoWalk import * + +class TwoDWalk(OrthoWalk): + notify = DirectNotifyGlobal.directNotify.newCategory('TwoDWalk') + BROADCAST_POS_TASK = 'TwoDWalkBroadcastPos' + + def doBroadcast(self, task): + dt = globalClock.getDt() + self.timeSinceLastPosBroadcast += dt + if self.timeSinceLastPosBroadcast >= self.broadcastPeriod: + self.timeSinceLastPosBroadcast = 0 + self.lt.cnode.broadcastPosHprFull() + return Task.cont diff --git a/toontown/minigame/VineBat.py b/toontown/minigame/VineBat.py new file mode 100644 index 0000000..ff08aea --- /dev/null +++ b/toontown/minigame/VineBat.py @@ -0,0 +1,107 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +import VineGameGlobals +from direct.interval.SoundInterval import SoundInterval + +class VineBat(NodePath.NodePath, DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('VineBat') + notify.setDebug(True) + RADIUS = 1.7 + + def __init__(self, batIndex, timeToTraverseField): + NodePath.__init__(self, 'VineBat') + DirectObject.__init__(self) + pos = Point3(0, 0, 0) + serialNum = 0 + gameId = 0 + self.serialNum = serialNum + self.batIndex = batIndex + self.timeToTraverseField = timeToTraverseField + gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + bat3 = gameAssets.find('**/bat3') + bat2 = gameAssets.find('**/bat2') + bat1 = gameAssets.find('**/bat__1') + seqNode = SequenceNode.SequenceNode('bat') + seqNode.addChild(bat1.node()) + seqNode.addChild(bat2.node()) + seqNode.addChild(bat3.node()) + seqNode.setFrameRate(12) + seqNode.pingpong(False) + self.batModel = self.attachNewNode(seqNode) + self.batModel.reparentTo(self) + gameAssets.removeNode() + self.batModelIcon = self.attachNewNode('batIcon') + self.batModel.copyTo(self.batModelIcon) + regularCamMask = BitMask32.bit(0) + self.batModelIcon.hide(regularCamMask) + self.batModelIcon.show(VineGameGlobals.RadarCameraBitmask) + self.batModelIcon.setScale(0.55) + self.batModel.setScale(0.15) + self.setPos(-100, 0, 0) + center = Point3(0, 0, 0) + self.sphereName = 'batSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(VineGameGlobals.SpiderBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.screechSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_shriek_3.mp3') + self.flySfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_vine_game_bat_flying_lp.wav') + self.oldCutoffDistance = base.sfxPlayer.getCutoffDistance() + base.sfxPlayer.setCutoffDistance(240) + self.soundInterval = SoundInterval(self.flySfx, node=self, listenerNode=base.localAvatar, seamlessLoop=True, volume=0.5, cutOff=240) + self.reparentTo(render) + self.startedFlying = False + self.warnedForThisLap = False + startX = VineGameGlobals.VineXIncrement * VineGameGlobals.NumVines + endX = -VineGameGlobals.VineXIncrement + self.velocity = float(startX - endX) / self.timeToTraverseField + self.warnDistance = 35 + + def destroy(self): + self.ignoreAll() + self.batModel.removeNode() + del self.batModel + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + self.removeNode() + self.soundInterval.finish() + del self.soundInterval + del self.flySfx + del self.screechSfx + base.sfxPlayer.setCutoffDistance(self.oldCutoffDistance) + + def __handleEnterSphere(self, collEntry): + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + messenger.send('VineBatGrabbed', [self.serialNum]) + + def showGrab(self): + self.reparentTo(hidden) + self.collNode.setIntoCollideMask(BitMask32(0)) + + def startFlying(self): + self.startedFlying = True + self.soundInterval.loop() + + def stopFlying(self): + self.flySfx.setVolume(0) + self.soundInterval.finish() + + def startLap(self): + self.warnedForThisLap = False + + def checkScreech(self): + distance = base.localAvatar.getDistance(self) + if distance < self.warnDistance: + if self.getX(render) > base.localAvatar.getX(render): + if not self.warnedForThisLap: + self.screechSfx.play() + self.warnedForThisLap = True diff --git a/toontown/minigame/VineGameGlobals.py b/toontown/minigame/VineGameGlobals.py new file mode 100644 index 0000000..3c7d264 --- /dev/null +++ b/toontown/minigame/VineGameGlobals.py @@ -0,0 +1,270 @@ +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import BitMask32 +NumVines = 20 +GameDuration = 70 +ShowScoresDuration = 4.0 +VineStartingT = 0.25 +VineFellDownT = 0.1 +EndlessGame = False +BonusPerSecondLeft = 0.4 +JumpTimeBuffer = 0.5 +SpiderBitmask = ToontownGlobals.CatchGameBitmask +TreasureBitmask = ToontownGlobals.PieBitmask +VineXIncrement = 30 +VineHeight = 30 +BatMaxHeight = 28 +BatMinHeight = 10 +RadarCameraBitmask = BitMask32.bit(3) +CourseSections = (((20, + 30, + 4, + 0), + (19, + 39, + 3.1, + 0), + (18, + 41, + 4, + 0), + (19, + 38, + 3.2, + 0), + (20, + 30, + 6, + 0)), + ((20, + 30, + 3, + 0), + (18, + 40, + 4.1, + 0), + (19, + 31, + 5.1, + 0), + (18, + 41, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (15, + 39, + 4.1, + 0), + (19, + 29, + 5, + 0), + (16, + 38, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 36, + 4.1, + 0), + (19, + 30, + 5, + 9), + (18, + 38, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 15, + 4.1, + 0), + (19, + 30, + 5, + 11), + (18, + 16, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 11, + 4.1, + 0), + (15, + 12, + 5, + 0), + (18, + 16, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (15, + 39, + 4.1, + 13), + (19, + 29, + 5, + 0), + (16, + 38, + 4.2, + 0), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (18, + 26, + 4.1, + 9), + (19, + 30, + 5, + 0), + (18, + 28, + 4.2, + 12), + (20, + 30, + 5, + 0)), + ((20, + 30, + 3, + 0), + (15, + 26, + 4.1, + 9), + (19, + 30, + 5, + 0), + (15, + 28, + 4.2, + 12), + (20, + 30, + 5, + 0)), + ((15, + 50, + 4, + 0), + (15, + 40, + 4.1, + 0), + (19, + 40, + 5, + 0), + (19, + 28, + 4.2, + 0), + (20, + 30, + 5, + 0))) +CourseWeights = {ToontownGlobals.ToontownCentral: ((0, 25), + (1, 25), + (2, 25), + (3, 25)), + ToontownGlobals.DonaldsDock: ((1, 25), + (2, 25), + (3, 25), + (4, 25)), + ToontownGlobals.DaisyGardens: ((2, 25), + (3, 25), + (4, 25), + (5, 25)), + ToontownGlobals.MinniesMelodyland: ((3, 25), + (4, 25), + (5, 25), + (6, 25)), + ToontownGlobals.TheBrrrgh: ((4, 25), + (5, 25), + (6, 25), + (7, 25)), + ToontownGlobals.DonaldsDreamland: ((4, 20), + (5, 20), + (6, 20), + (7, 20), + (8, 20))} +BaseBonusOnEndVine = {ToontownGlobals.ToontownCentral: 4, + ToontownGlobals.DonaldsDock: 5, + ToontownGlobals.DaisyGardens: 6, + ToontownGlobals.MinniesMelodyland: 7, + ToontownGlobals.TheBrrrgh: 8, + ToontownGlobals.DonaldsDreamland: 9} +BatInfo = {ToontownGlobals.ToontownCentral: ((60, 0, 0.35),), + ToontownGlobals.DonaldsDock: ((60, 0, 0.25), (30, 30)), + ToontownGlobals.DaisyGardens: ((60, 0, 0.25), (15, 30)), + ToontownGlobals.MinniesMelodyland: ((60, 0, 0.25), (10, 25)), + ToontownGlobals.TheBrrrgh: ((60, 0, 0.25), (30, 30), (30, 20)), + ToontownGlobals.DonaldsDreamland: ((60, 0, 0.25), (30, 30), (10, 20))} +SpiderLimits = {ToontownGlobals.ToontownCentral: 1, + ToontownGlobals.DonaldsDock: 2, + ToontownGlobals.DaisyGardens: 2, + ToontownGlobals.MinniesMelodyland: 3, + ToontownGlobals.TheBrrrgh: 3, + ToontownGlobals.DonaldsDreamland: 4} + +def getNumSpidersInSection(sectionIndex): + if sectionIndex < 0 or sectionIndex >= len(CourseSections): + return 0 + numSpiders = 0 + for vine in CourseSections[sectionIndex]: + if vine[3]: + numSpiders += 1 + + return numSpiders diff --git a/toontown/minigame/VineHeadFrame.py b/toontown/minigame/VineHeadFrame.py new file mode 100644 index 0000000..d3f57f3 --- /dev/null +++ b/toontown/minigame/VineHeadFrame.py @@ -0,0 +1,35 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toon import ToonHead + +class VineHeadFrame(DirectFrame): + + def __init__(self, av = None, color = Vec4(1, 1, 1, 1), *args, **kwargs): + self.panelGeom = DGG.getDefaultDialogGeom() + opts = {'relief': None, + 'geom': self.panelGeom, + 'geom_scale': (0.5, 1, 0.5), + 'pos': (0, 0, 0)} + opts.update(kwargs) + apply(DirectFrame.__init__, (self,) + args, opts) + self.initialiseoptions(VineHeadFrame) + if av: + self.setAv(av) + self.setScale(0.1) + self.setTransparency(0) + return + + def setAv(self, av): + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(0, -0.5, -0.09, 180.0, 0.0, 0.0, 0.2, 0.2, 0.2) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(av.style, forGui=1) + self.headModel.reparentTo(self.head) + + def destroy(self): + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + DirectFrame.destroy(self) diff --git a/toontown/minigame/VineSpider.py b/toontown/minigame/VineSpider.py new file mode 100644 index 0000000..0dc6bee --- /dev/null +++ b/toontown/minigame/VineSpider.py @@ -0,0 +1,67 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +import VineGameGlobals + +class VineSpider(NodePath.NodePath, DirectObject): + RADIUS = 1.7 + + def __init__(self): + NodePath.__init__(self, 'VineSpider') + DirectObject.__init__(self) + pos = Point3(0, 0, 0) + serialNum = 0 + gameId = 0 + self.serialNum = serialNum + gameAssets = loader.loadModel('phase_4/models/minigames/vine_game') + spider2 = gameAssets.find('**/spider_3') + spider1 = gameAssets.find('**/spider_2') + seqNode = SequenceNode.SequenceNode('spider') + seqNode.addChild(spider1.node()) + seqNode.addChild(spider2.node()) + seqNode.setFrameRate(2) + seqNode.loop(False) + self.spiderModel = self.attachNewNode(seqNode) + self.spiderModel.reparentTo(self) + gameAssets.removeNode() + self.spiderModelIcon = self.attachNewNode('spiderIcon') + self.spiderModel.copyTo(self.spiderModelIcon) + regularCamMask = BitMask32.bit(0) + self.spiderModelIcon.hide(regularCamMask) + self.spiderModelIcon.show(VineGameGlobals.RadarCameraBitmask) + self.spiderModel.setScale(0.2) + self.spiderModelIcon.setScale(0.75) + self.setPos(-100, 0, 0) + center = Point3(0, 0, 0) + self.sphereName = 'spiderSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(VineGameGlobals.SpiderBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.attachNewNode(self.collNode) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.reparentTo(render) + + def destroy(self): + self.ignoreAll() + self.spiderModel.removeNode() + del self.spiderModel + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + self.removeNode() + + def __handleEnterSphere(self, collEntry): + print 'VineSpider.__handleEnterSphere' + print collEntry + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + messenger.send('VineSpiderGrabbed', [self.serialNum]) + + def showGrab(self): + self.reparentTo(hidden) + self.collNode.setIntoCollideMask(BitMask32(0)) diff --git a/toontown/minigame/VineTreasure.py b/toontown/minigame/VineTreasure.py new file mode 100644 index 0000000..9adac24 --- /dev/null +++ b/toontown/minigame/VineTreasure.py @@ -0,0 +1,45 @@ +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal + +class VineTreasure(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('VineTreasure') + RADIUS = 1.7 + + def __init__(self, model, pos, serialNum, gameId): + self.serialNum = serialNum + center = model.getBounds().getCenter() + center = Point3(0, 0, 0) + self.nodePath = model.copyTo(render) + self.nodePath.setPos(pos[0] - center[0], 0 - center[1], pos[2] - center[2]) + self.nodePath.setScale(0.25) + self.sphereName = 'treasureSphere-%s-%s' % (gameId, self.serialNum) + self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.sphereName) + self.collNode.setIntoCollideMask(WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = render.attachNewNode(self.collNode) + self.collNodePath.setPos(pos[0] - center[0], 0 - center[1], pos[2] - center[2]) + self.collNodePath.hide() + self.accept('enter' + self.sphereName, self.__handleEnterSphere) + self.nodePath.flattenLight() + + def destroy(self): + self.ignoreAll() + self.nodePath.removeNode() + del self.nodePath + del self.collSphere + self.collNodePath.removeNode() + del self.collNodePath + del self.collNode + + def __handleEnterSphere(self, collEntry): + self.ignoreAll() + self.notify.debug('treasuerGrabbed') + messenger.send('VineTreasureGrabbed', [self.serialNum]) + + def showGrab(self): + self.nodePath.hide() + self.collNodePath.hide() + self.collNode.setIntoCollideMask(BitMask32(0)) diff --git a/toontown/minigame/VoteResultsPanel.py b/toontown/minigame/VoteResultsPanel.py new file mode 100644 index 0000000..8fd50e0 --- /dev/null +++ b/toontown/minigame/VoteResultsPanel.py @@ -0,0 +1,108 @@ +from direct.gui.DirectGui import DirectFrame, DGG, DirectLabel +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import Point3, TextNode +from toontown.minigame import TravelGameGlobals +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import Parallel, Sequence, LerpFunc, Func, Wait + +class VoteResultsPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('VoteResultsPanel') + + def __init__(self, numPlayers, avIdList, votes, directions, namesList, disconnectedList, directionToGo, directionReason, directionTotals, *args, **kwargs): + opts = {'relief': None, + 'geom': DGG.getDefaultDialogGeom(), + 'geom_color': ToontownGlobals.GlobalDialogColor[:3] + (0.8,), + 'geom_scale': (1.75, 1, 0.75), + 'pos': (0, 0, 0.525)} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(VoteResultsPanel) + listMultiplier = 1 + if TravelGameGlobals.SpoofFour: + listMultiplier = 4 + self.avIdList = avIdList * listMultiplier + self.numPlayers = numPlayers * listMultiplier + self.votes = votes * listMultiplier + self.directions = directions * listMultiplier + self.namesList = namesList * listMultiplier + self.disconnectedList = disconnectedList * listMultiplier + self.directionToGo = directionToGo + self.directionReason = directionReason + self.directionTotals = directionTotals + self.entryList = [] + self.rowFrame = [] + self.upDownFrame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(-1)) + self.upLabel = DirectLabel(parent=self.upDownFrame, relief=None, pos=(1.2, 0, 0.0), text=TTLocalizer.TravelGameDirections[0], text_fg=(0.0, 0.0, 1.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.downLabel = DirectLabel(parent=self.upDownFrame, relief=None, pos=(1.43, 0, 0.0), text=TTLocalizer.TravelGameDirections[1], text_fg=(1.0, 0.0, 0.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.totalFrame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(self.numPlayers)) + self.totalText = DirectLabel(parent=self.totalFrame, relief=None, pos=(1.0, 0, 0.0), text='Total', text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.totalVotesUpLabel = DirectLabel(parent=self.totalFrame, relief=None, pos=(1.2, 0, 0.0), text='', text_fg=(0.0, 0.0, 1.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.totalVotesDownLabel = DirectLabel(parent=self.totalFrame, relief=None, pos=(1.43, 0, 0.0), text='', text_fg=(1.0, 0.0, 0.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.totalVotesLabels = [self.totalVotesUpLabel, self.totalVotesDownLabel] + self.resultFrame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(5)) + self.resultLabel = DirectLabel(parent=self.resultFrame, text='', text_scale=0.06, pos=(0.7, 0, 0.0), text_align=TextNode.ACenter) + self.setupResultLabel() + for index in range(self.numPlayers): + frame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(index)) + self.rowFrame.append(frame) + nameLabel = DirectFrame(parent=frame, relief=None, pos=(0.46, 0.0, 0.0), text=self.namesList[index], text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.05, text_align=TextNode.ACenter, text_font=DGG.getDefaultFont()) + votesUpLabel = DirectLabel(parent=frame, relief=None, pos=(1.2, 0.0, 0.0), text='', text_fg=(0, 0, 1, 1), text_scale=0.05, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + votesDownLabel = DirectLabel(parent=frame, relief=None, pos=(1.43, 0.0, 0.0), text='', text_fg=(1, 0, 0, 1), text_scale=0.05, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + nameLabel.hide() + self.entryList.append((nameLabel, votesUpLabel, votesDownLabel)) + + return + + def getRowPos(self, place): + return Point3(-0.72, -0.01, 0.2 - place * 0.1) + + def setupResultLabel(self): + reasonStr = '' + if self.directionReason == TravelGameGlobals.ReasonVote: + reasonStr = TTLocalizer.TravelGameReasonVotes % {'dir': TTLocalizer.TravelGameDirections[self.directionToGo], + 'numVotes': self.directionTotals[self.directionToGo]} + elif self.directionReason == TravelGameGlobals.ReasonRandom: + reasonStr = TTLocalizer.TravelGameReasonRandom % {'dir': TTLocalizer.TravelGameDirections[self.directionToGo], + 'numVotes': self.directionTotals[self.directionToGo]} + elif self.directionReason == TravelGameGlobals.ReasonPlaceDecider: + reasonStr = TravelGameReasonPlace % {'name': 'TODO NAME', + 'dir': TTLocalizer.TravelGameDirections[self.directionToGo]} + self.resultLabel['text'] = reasonStr + self.resultLabel.hide() + + def createOnePlayerSequence(self, index, duration): + numVotes = self.votes[index] + direction = self.directions[index] + + def ticketTicker(t, label = self.entryList[index][direction + 1], startVotes = 0, endVotes = numVotes): + label['text'] = str(int(t * endVotes + startVotes)) + + track = Parallel() + track.append(Func(self.entryList[index][0].show, name='showName %d' % index)) + track.append(LerpFunc(ticketTicker, duration=duration, name='countVotes %d' % index)) + startVotes = 0 + for prev in range(index): + if self.directions[prev] == direction: + startVotes += self.votes[prev] + + def totalTicker(t, label = self.totalVotesLabels[direction], startVotes = startVotes, additionalVotes = numVotes): + label['text'] = str(int(t * additionalVotes + startVotes)) + + track.append(LerpFunc(totalTicker, duration=duration, name='countTotal %d' % index)) + return track + + def startMovie(self): + self.movie = Sequence() + for index in range(self.numPlayers): + track = self.createOnePlayerSequence(index, 1.25) + self.movie.append(track) + self.movie.append(Wait(0.75)) + + self.movie.append(Func(self.resultLabel.show)) + self.movie.append(Wait(2.0)) + self.movie.start() + + def destroy(self): + self.movie.finish() + DirectFrame.destroy(self) diff --git a/toontown/minigame/VoteResultsTrolleyPanel.py b/toontown/minigame/VoteResultsTrolleyPanel.py new file mode 100644 index 0000000..f878787 --- /dev/null +++ b/toontown/minigame/VoteResultsTrolleyPanel.py @@ -0,0 +1,225 @@ +from direct.gui.DirectGui import DirectFrame, DGG, DirectLabel +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import Point3, TextNode, Vec4 +from toontown.minigame import TravelGameGlobals +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import Parallel, Sequence, LerpFunc, Func, Wait, SoundInterval +from direct.showbase.PythonUtil import pdir + +class VoteResultsTrolleyPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('VoteResultsTrolleyPanel') + + def __init__(self, numPlayers, avIdList, votes, directions, namesList, disconnectedList, directionToGo, directionReason, directionTotals, *args, **kwargs): + opts = {'relief': None, + 'geom': DGG.getDefaultDialogGeom(), + 'geom_color': ToontownGlobals.GlobalDialogColor[:3] + (0.8,), + 'geom_scale': (1.75, 1, 0.25), + 'pos': (0, 0, 0.825)} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(VoteResultsTrolleyPanel) + listMultiplier = 1 + if TravelGameGlobals.SpoofFour: + listMultiplier = 4 + self.avIdList = avIdList * listMultiplier + self.numPlayers = numPlayers * listMultiplier + self.votes = votes * listMultiplier + self.directions = directions * listMultiplier + self.namesList = namesList * listMultiplier + self.disconnectedList = disconnectedList * listMultiplier + self.directionToGo = directionToGo + self.directionReason = directionReason + self.directionTotals = directionTotals + self.entryList = [] + self.rowFrame = [] + self.upDownFrame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(-1)) + self.upLabel = DirectLabel(parent=self, relief=None, pos=(-0.5, 0, 0.06), text=TTLocalizer.TravelGameDirections[0] + ':', text_fg=(0.0, 0.0, 1.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.downLabel = DirectLabel(parent=self, relief=None, pos=(0.5, 0, 0.06), text=TTLocalizer.TravelGameDirections[1] + ':', text_fg=(1.0, 0.0, 0.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.totalVotesUpLabel = DirectLabel(parent=self.upLabel, relief=None, pos=(0.2, 0, 0.0), text='0', text_fg=(0.0, 0.0, 1.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.totalVotesDownLabel = DirectLabel(parent=self.downLabel, relief=None, pos=(0.2, 0, 0.0), text='0', text_fg=(1.0, 0.0, 0.0, 1.0), text_scale=0.05, text_align=TextNode.ARight) + self.totalFrame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(self.numPlayers)) + self.totalVotesLabels = [self.totalVotesUpLabel, self.totalVotesDownLabel] + self.resultFrame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(0.5)) + self.resultLabel = DirectLabel(parent=self.resultFrame, text='', text_scale=0.06, pos=(0.7, 0, 0.0), text_align=TextNode.ACenter) + self.setupResultLabel() + for index in range(self.numPlayers): + frame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(index)) + self.rowFrame.append(frame) + nameLabel = DirectFrame(parent=frame, relief=None, pos=(0.46, 0.0, 0.0), text=self.namesList[index], text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.05, text_align=TextNode.ACenter, text_font=DGG.getDefaultFont()) + votesUpLabel = DirectLabel(parent=frame, relief=None, pos=(1.2, 0.0, 0.0), text='', text_fg=(0, 0, 1, 1), text_scale=0.05, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + votesDownLabel = DirectLabel(parent=frame, relief=None, pos=(1.43, 0.0, 0.0), text='', text_fg=(1, 0, 0, 1), text_scale=0.05, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + nameLabel.hide() + self.entryList.append((nameLabel, votesUpLabel, votesDownLabel)) + + self.avVotesLabel = {} + self.avArrows = {} + matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + minnieArrow = matchingGameGui.find('**/minnieArrow') + from toontown.minigame.DistributedTravelGame import map3dToAspect2d + for index in range(self.numPlayers): + avId = self.avIdList[index] + av = base.cr.doId2do.get(avId) + if av: + height = av.getHeight() + avPos = av.getPos(render) + avPos.setZ(av.getZ() + 5) + labelPos = map3dToAspect2d(render, avPos) + if not labelPos: + continue + labelPos.setZ(labelPos.getZ() + 0.3) + arrow = None + if self.votes[index] > 0: + arrow = aspect2d.attachNewNode('avArrow') + minnieArrow.copyTo(arrow) + arrow.setScale(1.1, 1, 1.15) + arrow.setPos(labelPos) + if self.directions[index] == 0: + arrow.setR(-90) + arrow.setColorScale(0, 0, 1, 1) + else: + arrow.setR(90) + arrow.setColorScale(1, 0, 0, 1) + arrow.wrtReparentTo(self.resultFrame) + arrow.hide() + self.avArrows[index] = arrow + fgColor = Vec4(0, 0, 0, 1) + if self.votes[index] > 0: + if self.directions[index] == 0: + fgColor = Vec4(0, 0, 1, 1) + else: + fgColor = Vec4(1, 0, 0, 1) + if self.votes[index] > 0: + newLabel = DirectLabel(parent=aspect2d, relief=None, pos=labelPos, text='test', text_fg=(1, 1, 1, 1), text_scale=0.1, text_align=TextNode.ACenter, text_font=ToontownGlobals.getSignFont(), text_pos=(0, -0.01, 0)) + else: + newLabel = DirectLabel(parent=aspect2d, geom=DGG.getDefaultDialogGeom(), geom_scale=(0.2, 1, 0.2), relief=None, pos=labelPos, text='test', text_fg=(0.5, 0.5, 0.5, 1), text_scale=0.1, text_align=TextNode.ACenter, text_font=ToontownGlobals.getSignFont(), text_pos=(0, -0.035, 0)) + newLabel.wrtReparentTo(self.resultFrame) + newLabel.hide() + self.avVotesLabel[index] = newLabel + + matchingGameGui.removeNode() + self.curArrowSfxIndex = 0 + self.upArrowSfx = [] + self.downArrowSfx = [] + for i in range(5): + self.upArrowSfx.append(base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_blue_arrow.mp3')) + self.downArrowSfx.append(base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_red_arrow.mp3')) + + self.winVoteSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_win_vote.mp3') + self.noVoteSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_square_no_vote_1.mp3') + self.loseVoteSfx = base.loadSfx('phase_4/audio/sfx/MG_sfx_travel_game_lose_vote.mp3') + self.localAvatarWon = False + self.localAvatarLost = False + localIndex = self.avIdList.index(base.localAvatar.doId) + localDirection = self.directions[localIndex] + localVotes = self.votes[localIndex] + if localVotes: + if localDirection == self.directionToGo: + if not TravelGameGlobals.ReverseWin: + self.localAvatarWon = True + else: + self.localAvatarLost = True + elif not TravelGameGlobals.ReverseWin: + self.localAvatarLost = True + else: + self.localAvatarWon = True + return + + def getRowPos(self, place): + return Point3(-0.72, -0.01, 0.0 - place * 0.1) + + def setupResultLabel(self): + reasonStr = '' + if self.directionReason == TravelGameGlobals.ReasonVote: + if self.directionToGo == 0: + losingDirection = 1 + else: + losingDirection = 0 + diffVotes = self.directionTotals[self.directionToGo] - self.directionTotals[losingDirection] + if diffVotes > 1: + reasonStr = TTLocalizer.TravelGameReasonVotesPlural % {'dir': TTLocalizer.TravelGameDirections[self.directionToGo], + 'numVotes': diffVotes} + else: + reasonStr = TTLocalizer.TravelGameReasonVotesSingular % {'dir': TTLocalizer.TravelGameDirections[self.directionToGo], + 'numVotes': diffVotes} + elif self.directionReason == TravelGameGlobals.ReasonRandom: + reasonStr = TTLocalizer.TravelGameReasonRandom % {'dir': TTLocalizer.TravelGameDirections[self.directionToGo], + 'numVotes': self.directionTotals[self.directionToGo]} + elif self.directionReason == TravelGameGlobals.ReasonPlaceDecider: + reasonStr = TravelGameReasonPlace % {'name': 'TODO NAME', + 'dir': TTLocalizer.TravelGameDirections[self.directionToGo]} + self.resultLabel['text'] = reasonStr + self.resultLabel.hide() + + def createOnePlayerSequence(self, index, duration): + numVotes = self.votes[index] + direction = self.directions[index] + + def ticketTicker(t, label = self.entryList[index][direction + 1], startVotes = 0, endVotes = numVotes): + label['text'] = str(int(t * endVotes + startVotes)) + + track = Parallel() + startVotes = 0 + for prev in range(index): + if self.directions[prev] == direction: + startVotes += self.votes[prev] + + def totalTicker(t, label = self.totalVotesLabels[direction], startVotes = startVotes, additionalVotes = numVotes): + label['text'] = str(int(t * additionalVotes + startVotes)) + + track.append(LerpFunc(totalTicker, duration=duration, name='countTotal %d' % index)) + if self.avVotesLabel.has_key(index): + + def avVotesTicker(t, label = self.avVotesLabel[index], startVotes = 0, endVotes = numVotes, direction = direction): + oldValue = label['text'] + newValue = int(t * endVotes + startVotes) + label['text'] = str(newValue) + if not oldValue == label['text']: + if newValue: + if direction == 0: + self.upArrowSfx[self.curArrowSfxIndex].play() + else: + self.downArrowSfx[self.curArrowSfxIndex].play() + self.curArrowSfxIndex += 1 + if self.curArrowSfxIndex >= len(self.upArrowSfx): + self.curArrowSfxIndex = 0 + + label = self.avVotesLabel[index] + track.append(Func(self.avVotesLabel[index].show, name='showName %d' % index)) + if self.avArrows.has_key(index): + track.append(Func(self.avArrows[index].show, name='showArrow %d' % index)) + if direction == 0 and numVotes: + pass + elif direction == 1 and numVotes: + pass + else: + track.append(SoundInterval(self.noVoteSfx)) + track.append(LerpFunc(avVotesTicker, duration=duration, name='countAvVotes %d' % index)) + return track + + def startMovie(self): + self.movie = Sequence() + for index in range(self.numPlayers): + track = self.createOnePlayerSequence(index, 1.25) + self.movie.append(track) + self.movie.append(Wait(0.75)) + + self.movie.append(Func(self.resultLabel.show)) + soundAndWait = Parallel() + soundAndWait.append(Wait(2.0)) + if self.localAvatarWon: + soundAndWait.append(SoundInterval(self.winVoteSfx)) + elif self.localAvatarLost: + soundAndWait.append(SoundInterval(self.loseVoteSfx, duration=0.43)) + self.movie.append(soundAndWait) + self.movie.start() + + def destroy(self): + self.movie.finish() + del self.movie + del self.winVoteSfx + del self.noVoteSfx + del self.upArrowSfx + del self.loseVoteSfx + del self.downArrowSfx + DirectFrame.destroy(self) diff --git a/toontown/minigame/__init__.py b/toontown/minigame/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/parties/ActivityBase.py b/toontown/parties/ActivityBase.py new file mode 100644 index 0000000..29bef7a --- /dev/null +++ b/toontown/parties/ActivityBase.py @@ -0,0 +1,17 @@ + + +class ActivityBase: + + def __init__(self, activityId, x, y, h): + self.activityId = activityId + self.x = x + self.y = y + self.h = h + + def __str__(self): + string = ' 12: + newYear += 1 + newMonth -= 12 + + while newMonth < 1: + if newYear - 1 > 1899: + newMonth += 12 + newYear -= 1 + else: + newMonth += 1 + + self.curDate = datetime(newYear, newMonth, 1, self.curDate.time().hour, self.curDate.time().minute, self.curDate.time().second, self.curDate.time().microsecond, self.curDate.tzinfo) + self.monthLabel['text'] = (TTLocalizer.Months[self.curDate.month],) + self.yearLabel['text'] = (str(self.curDate.year),) + startTime = globalClock.getRealTime() + self.changeDateForGuiDays() + endTime = globalClock.getRealTime() + self.notify.debug('changeDate took %f seconds' % (endTime - startTime)) + self.updateSelectedDate() + if monthChange != 0: + if self.onlyFutureMonthsClickable and newMonth == self.startDate.month and newYear == self.startDate.year: + self.monthLeftArrow.hide() + + def __doMonthLeft(self): + self.changeMonth(-1) + + def __doMonthRight(self): + self.monthLeftArrow.show() + self.changeMonth(1) + + def destroy(self): + self.ignoreAll() + self.dayClickCallback = None + self.monthLeftArrow.destroy() + self.monthRightArrow.destroy() + for day in self.guiDays: + if day is not None: + day.destroy() + day = None + + self.filterList.destroy() + DirectFrame.destroy(self) + return + + def clickedOnDay(self, dayDate): + self.lastSelectedDate = dayDate + self.updateSelectedDate() + + def updateSelectedDate(self): + if self.lastSelectedDate: + for oneGuiDay in self.guiDays: + if oneGuiDay.myDate.date() == self.lastSelectedDate: + oneGuiDay.updateSelected(True) + else: + oneGuiDay.updateSelected(False) + + def clearSelectedDay(self): + for oneGuiDay in self.guiDays: + oneGuiDay.updateSelected(False) + + def filterChanged(self): + newFilter = self.filterList.getSelectedIndex() + for guiDay in self.guiDays: + guiDay.changeFilter(newFilter) diff --git a/toontown/parties/Cannon.py b/toontown/parties/Cannon.py new file mode 100644 index 0000000..cbb79b4 --- /dev/null +++ b/toontown/parties/Cannon.py @@ -0,0 +1,248 @@ +import math +from pandac.PandaModules import * +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func +from direct.interval.LerpInterval import LerpScaleInterval, LerpColorScaleInterval +from direct.showbase.PythonUtil import bound +from toontown.toon import ToonHead +from toontown.minigame.CannonGameGlobals import * +from toontown.toonbase import ToontownGlobals +from toontown.parties.PartyUtils import toRadians, calcVelocity +from direct.showbase.PythonUtil import StackTrace +CANNON_ROTATION_MIN = -70 +CANNON_ROTATION_MAX = 70 +INITIAL_VELOCITY = 80.0 +CANNON_BARREL_TOONHEAD_Y = 6.0 + +class Cannon: + notify = directNotify.newCategory('DistributedPartyCannon') + + def __init__(self, parent, pos = Point3(0, 0, 0)): + self.__previousRotation = 0.0 + self.__previousAngle = 0.0 + self._rotation = 0.0 + self._angle = 0.0 + self._position = pos + self._parent = parent + self.parentNode = None + self.cannonNode = None + self.barrelNode = None + self.sndCannonMove = None + self.sndCannonFire = None + self.collSphere = None + self.collNode = None + self.collNodePath = None + self.toonInside = None + self.toonHead = None + self.toonOriginalScale = 0.0 + self.toonParentNode = None + return + + def reset(self): + self.setRotation(0) + self.setAngle((CANNON_ANGLE_MIN + CANNON_ANGLE_MAX * 0.5) * 0.5) + self.parentNode.setPos(self._position) + self.updateModel() + + def load(self, nodeName): + self.parentNode = NodePath(nodeName) + self.cannonNode = loader.loadModel('phase_4/models/minigames/toon_cannon') + self.cannonNode.reparentTo(self.parentNode) + self.barrelNode = self.cannonNode.find('**/cannon') + self.shadowNode = self.cannonNode.find('**/square_drop_shadow') + self.smokeNode = loader.loadModel('phase_4/models/props/test_clouds') + self.smokeNode.setBillboardPointEye() + self.sndCannonMove = base.loadSfx('phase_4/audio/sfx/MG_cannon_adjust.mp3') + self.sndCannonFire = base.loadSfx('phase_4/audio/sfx/MG_cannon_fire_alt.mp3') + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.collSphere.setTangible(1) + self.collNode = CollisionNode(self.getCollisionName()) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.parentNode.attachNewNode(self.collNode) + + def unload(self): + self.__cleanupToonInside() + if self.cannonNode: + self.cannonNode.removeNode() + self.cannonNode = None + if self.smokeNode: + self.smokeNode.removeNode() + self.smokeNode = None + del self.sndCannonMove + del self.sndCannonFire + del self._position + self.ignoreAll() + return + + def updateModel(self, rotation = None, angle = None): + if rotation: + self.rotation = rotation + if angle: + self.angle = angle + self.cannonNode.setHpr(self._rotation, 0, 0) + self.barrelNode.setHpr(0, self._angle, 0) + maxP = 90 + newP = self.barrelNode.getP() + yScale = 1 - 0.5 * float(newP) / maxP + self.shadowNode.setScale(1, yScale, 1) + + def playFireSequence(self): + self.smokeNode.reparentTo(self.barrelNode) + self.smokeNode.setPos(0, 6, -3) + self.smokeNode.setScale(0.5) + self.smokeNode.wrtReparentTo(render) + track = Sequence(Parallel(LerpScaleInterval(self.smokeNode, 0.5, 3), LerpColorScaleInterval(self.smokeNode, 0.5, Vec4(2, 2, 2, 0))), Func(self.smokeNode.reparentTo, hidden), Func(self.smokeNode.clearColorScale)) + base.playSfx(self.sndCannonFire) + track.start() + + def loopMovingSound(self): + base.playSfx(self.sndCannonMove, looping=1) + + def stopMovingSound(self): + self.sndCannonMove.stop() + + def show(self): + self.reset() + self.parentNode.reparentTo(self._parent) + + def hide(self): + self.parentNode.reparentTo(hidden) + + def placeToonInside(self, toon): + self.__setToonInside(toon) + self.__createToonHead(toon) + self.__placeToon() + + def __setToonInside(self, toon): + self.toonInside = toon + toonName = 'None' + if toon: + toonName = toon.getName() + self.notify.debug('__setToonInside self.toonInside=%s\nstack=%s' % (toonName, StackTrace().compact())) + self.toonInside.stopSmooth() + self.toonOriginalScale = toon.getScale() + toon.useLOD(1000) + self.toonParentNode = render.attachNewNode('toonOriginChange') + self.toonInside.wrtReparentTo(self.toonParentNode) + self.toonInside.setPosHpr(0, 0, -(self.toonInside.getHeight() / 2.0), 0, -90, 0) + + def __createToonHead(self, toon): + self.toonHead = ToonHead.ToonHead() + self.toonHead.setupHead(toon.style) + self.toonHead.reparentTo(hidden) + tag = NametagFloat3d() + tag.setContents(Nametag.CSpeech | Nametag.CThought) + tag.setBillboardOffset(0) + tag.setAvatar(self.toonHead) + toon.nametag.addNametag(tag) + tagPath = self.toonHead.attachNewNode(tag.upcastToPandaNode()) + tagPath.setPos(0, 0, 1) + self.toonHead.tag = tag + + def __placeToon(self): + self.showToonHead() + self.toonHead.setPosHpr(0, CANNON_BARREL_TOONHEAD_Y, 0, 0, -45, 0) + scale = self.toonOriginalScale + self.toonHead.setScale(render, scale[0], scale[1], scale[2]) + self.toonParentNode.reparentTo(hidden) + self.toonParentNode.setPos(self.getToonFirePos()) + + def showToonHead(self): + if self.toonHead: + self.toonHead.startBlink() + self.toonHead.startLookAround() + self.toonHead.reparentTo(self.barrelNode) + + def hideToonHead(self): + if self.toonHead: + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.reparentTo(hidden) + + def removeToonReadyToFire(self): + toon = self.toonInside + self.toonInside.wrtReparentTo(render) + y = self.toonHead.getY(self.barrelNode) - (self.toonInside.getHeight() - self.toonHead.getHeight()) + self.toonInside.setPosHpr(self.barrelNode, 0, y, 0, 0, -90, 0) + return self.__removeToon() + + def removeToonDidNotFire(self): + self.toonInside.reparentTo(self.cannonNode) + self.toonInside.setPos(2, -4, 0) + self.toonInside.wrtReparentTo(render) + return self.__removeToon() + + def __removeToon(self): + self.stopMovingSound() + toonNode = self.toonParentNode + self.toonInside.resetLOD() + self.hideToonHead() + self.__cleanupToonInside() + return toonNode + + def __cleanupToonInside(self): + toonName = 'None' + if self.toonInside: + toonName = self.toonInside.getName() + self.notify.debug('__cleanupToonInside self.toonInside=%s\nstack=%s' % (toonName, StackTrace().compact())) + if self.toonHead != None: + self.hideToonHead() + if hasattr(self.toonInside, 'nametag'): + self.toonInside.nametag.removeNametag(self.toonHead.tag) + self.toonHead.delete() + self.toonHead = None + self.toonInside = None + self.toonParentNode = None + return + + def getSphereRadius(self): + return 1.5 + + def getCollisionName(self): + return self.parentNode.getName() + 'Collision' + + def getEnterCollisionName(self): + return 'enter' + self.getCollisionName() + + def isToonInside(self): + return self.toonHead != None + + def getToonInside(self): + return self.toonInside + + def setRotation(self, rotation): + self.__previousRotation = self._rotation + self._rotation = bound(rotation, CANNON_ROTATION_MIN, CANNON_ROTATION_MAX) + + def getRotation(self): + return self._rotation + + def setAngle(self, angle): + self.__previousAngle = self._angle + self._angle = bound(angle, CANNON_ANGLE_MIN, CANNON_ANGLE_MAX) + + def getAngle(self): + return self._angle + + def hasMoved(self): + return self.__previousRotation != self._rotation or self.__previousAngle != self._angle + + def getBarrelHpr(self, node): + return self.barrelNode.getHpr(node) + + def getToonFirePos(self): + if self.toonHead: + return self.toonHead.getPos(render) + return Point3.zero() + + def getToonFireHpr(self): + if self.toonHead: + return self.toonHead.getHpr(render) + return Point3.zero() + + def getToonFireVel(self): + hpr = self.barrelNode.getHpr(render) + rotation = toRadians(hpr[0]) + angle = toRadians(hpr[1]) + return calcVelocity(rotation, angle, initialVelocity=INITIAL_VELOCITY) diff --git a/toontown/parties/CannonGui.py b/toontown/parties/CannonGui.py new file mode 100644 index 0000000..b362a78 --- /dev/null +++ b/toontown/parties/CannonGui.py @@ -0,0 +1,222 @@ +from direct.showbase.DirectObject import DirectObject +from direct.gui.DirectGui import DirectFrame, DirectButton +import direct.gui.DirectGuiGlobals as DGG +from toontown.parties import PartyUtils + +class CannonGui(DirectObject): + notify = directNotify.newCategory('CannonGui') + FIRE_KEY = 'control' + UP_KEY = 'arrow_up' + DOWN_KEY = 'arrow_down' + LEFT_KEY = 'arrow_left' + RIGHT_KEY = 'arrow_right' + FIRE_PRESSED = 'cannongui_fire_pressed' + + def __init__(self): + self.__loaded = False + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.__aimPad = None + self.__timerPad = None + return + + def load(self): + if self.__loaded: + return + self.__timerPad = PartyUtils.getNewToontownTimer() + guiModel = 'phase_4/models/gui/cannon_game_gui' + guiNode = loader.loadModel(guiModel) + self.__aimPad = DirectFrame(image=guiNode.find('**/CannonFire_PAD'), relief=None, pos=(0.7, 0, -0.553333), scale=0.8) + guiNode.removeNode() + self.fireButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Fire_Btn_UP'), (guiModel, '**/Fire_Btn_DN'), (guiModel, '**/Fire_Btn_RLVR')), relief=None, pos=(0.0115741, 0, 0.00505051), scale=1.0, command=self.__firePressed) + self.upButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0115741, 0, 0.221717)) + self.downButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.0136112, 0, -0.210101), image_hpr=(0, 0, 180)) + self.leftButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(-0.199352, 0, -0.000505269), image_hpr=(0, 0, -90)) + self.rightButton = DirectButton(parent=self.__aimPad, image=((guiModel, '**/Cannon_Arrow_UP'), (guiModel, '**/Cannon_Arrow_DN'), (guiModel, '**/Cannon_Arrow_RLVR')), relief=None, pos=(0.219167, 0, -0.00101024), image_hpr=(0, 0, 90)) + self.__aimPad.setColor(1, 1, 1, 0.9) + + def bindButton(button, upHandler, downHandler): + button.bind(DGG.B1PRESS, lambda x, handler = upHandler: handler()) + button.bind(DGG.B1RELEASE, lambda x, handler = downHandler: handler()) + + bindButton(self.upButton, self.__upPressed, self.__upReleased) + bindButton(self.downButton, self.__downPressed, self.__downReleased) + bindButton(self.leftButton, self.__leftPressed, self.__leftReleased) + bindButton(self.rightButton, self.__rightPressed, self.__rightReleased) + self.__loaded = True + return + + def unload(self): + self.ignoreAll() + if not self.__loaded: + return + self.disable() + self.upButton.unbind(DGG.B1PRESS) + self.upButton.unbind(DGG.B1RELEASE) + self.downButton.unbind(DGG.B1PRESS) + self.downButton.unbind(DGG.B1RELEASE) + self.leftButton.unbind(DGG.B1PRESS) + self.leftButton.unbind(DGG.B1RELEASE) + self.rightButton.unbind(DGG.B1PRESS) + self.rightButton.unbind(DGG.B1RELEASE) + self.fireButton.destroy() + self.__aimPad.destroy() + del self.__aimPad + del self.fireButton + del self.upButton + del self.downButton + del self.leftButton + del self.rightButton + self.__timerPad.destroy() + del self.__timerPad + self.__loaded = False + + def enable(self, timer = 0): + self.__aimPad.show() + base.setCellsAvailable([base.bottomCells[3], base.bottomCells[4]], 0) + base.setCellsAvailable([base.rightCells[1]], 0) + if timer > 0: + self.__timerPad.setTime(timer) + self.__timerPad.countdown(timer) + self.__timerPad.show() + self.enableKeys() + + def disable(self): + self.__aimPad.hide() + base.setCellsAvailable([base.bottomCells[3], base.bottomCells[4]], 1) + base.setCellsAvailable([base.rightCells[1]], 1) + self.__timerPad.hide() + self.disableKeys() + + def enableKeys(self): + self.enableAimKeys() + self.enableFireKey() + + def disableKeys(self): + self.__aimPad.hide() + self.disableAimKeys() + self.disableFireKey() + + def enableAimKeys(self): + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.accept(self.UP_KEY, self.__upKeyPressed) + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + + def disableAimKeys(self): + self.ignore(self.UP_KEY) + self.ignore(self.DOWN_KEY) + self.ignore(self.LEFT_KEY) + self.ignore(self.RIGHT_KEY) + messenger.send(self.UP_KEY + '-up') + messenger.send(self.DOWN_KEY + '-up') + messenger.send(self.LEFT_KEY + '-up') + messenger.send(self.RIGHT_KEY + '-up') + self.ignore(self.UP_KEY + '-up') + self.ignore(self.DOWN_KEY + '-up') + self.ignore(self.LEFT_KEY + '-up') + self.ignore(self.RIGHT_KEY + '-up') + + def enableFireKey(self): + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + + def disableFireKey(self): + self.ignore(self.FIRE_KEY) + self.ignore(self.FIRE_KEY + '-up') + + def __fireKeyPressed(self): + self.ignore(self.FIRE_KEY) + self.accept(self.FIRE_KEY + '-up', self.__fireKeyReleased) + self.__firePressed() + + def __upKeyPressed(self): + self.ignore(self.UP_KEY) + self.accept(self.UP_KEY + '-up', self.__upKeyReleased) + self.__upPressed() + + def __downKeyPressed(self): + self.ignore(self.DOWN_KEY) + self.accept(self.DOWN_KEY + '-up', self.__downKeyReleased) + self.__downPressed() + + def __leftKeyPressed(self): + self.ignore(self.LEFT_KEY) + self.accept(self.LEFT_KEY + '-up', self.__leftKeyReleased) + self.__leftPressed() + + def __rightKeyPressed(self): + self.ignore(self.RIGHT_KEY) + self.accept(self.RIGHT_KEY + '-up', self.__rightKeyReleased) + self.__rightPressed() + + def __fireKeyReleased(self): + self.ignore(self.FIRE_KEY + '-up') + self.accept(self.FIRE_KEY, self.__fireKeyPressed) + + def __leftKeyReleased(self): + self.ignore(self.LEFT_KEY + '-up') + self.accept(self.LEFT_KEY, self.__leftKeyPressed) + self.__leftReleased() + + def __rightKeyReleased(self): + self.ignore(self.RIGHT_KEY + '-up') + self.accept(self.RIGHT_KEY, self.__rightKeyPressed) + self.__rightReleased() + + def __upKeyReleased(self): + self.ignore(self.UP_KEY + '-up') + self.accept(self.UP_KEY, self.__upKeyPressed) + self.__upReleased() + + def __downKeyReleased(self): + self.ignore(self.DOWN_KEY + '-up') + self.accept(self.DOWN_KEY, self.__downKeyPressed) + self.__downReleased() + + def __upPressed(self): + self.notify.debug('up pressed') + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.notify.debug('down pressed') + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftPressed(self): + self.notify.debug('left pressed') + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.notify.debug('right pressed') + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upReleased(self): + self.notify.debug('up released') + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.notify.debug('down released') + self.downPressed = self.__exitControlActive(self.downPressed) + + def __leftReleased(self): + self.notify.debug('left released') + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.notify.debug('right released') + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __firePressed(self): + self.notify.debug('fire pressed') + messenger.send(CannonGui.FIRE_PRESSED) + + def __enterControlActive(self, control): + return control + 1 + + def __exitControlActive(self, control): + return max(0, control - 1) diff --git a/toontown/parties/DecorBase.py b/toontown/parties/DecorBase.py new file mode 100644 index 0000000..44d2887 --- /dev/null +++ b/toontown/parties/DecorBase.py @@ -0,0 +1,17 @@ + + +class DecorBase: + + def __init__(self, decorId, x, y, h): + self.decorId = decorId + self.x = x + self.y = y + self.h = h + + def __str__(self): + string = 'decorId=%d ' % self.decorId + string += '(%d,%d,%d) ' % (self.x, self.y, self.h) + return string + + def __repr__(self): + return self.__str__() diff --git a/toontown/parties/Decoration.py b/toontown/parties/Decoration.py new file mode 100644 index 0000000..2466a85 --- /dev/null +++ b/toontown/parties/Decoration.py @@ -0,0 +1,291 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.actor import Actor +from toontown.toonbase import ToontownGlobals +import random +from toontown.parties import PartyGlobals +from toontown.parties.PartyUtils import getCenterPosFromGridSize + +class Decoration(NodePath): + notify = directNotify.newCategory('Decoration') + + def __init__(self, name, x, y, h): + NodePath.__init__(self, name) + self.name = name + decorId = PartyGlobals.DecorationIds.fromString(name) + centerX, centerY = getCenterPosFromGridSize(x, y, PartyGlobals.DecorationInformationDict[decorId]['gridsize']) + self.setPos(centerX, centerY, 0.0) + self.setH(h) + if self.name == 'CakeTower': + self.partyCake = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cakeTower') + tntSeqNode = self.partyCake.find('**/seqNode_tnt').node() + tntSeqNode.setFrameRate(20) + self.partyCake.reparentTo(self) + elif self.name == 'BannerJellyBean': + partyBannerModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_bannerJellybean_model') + banner = [] + banner1 = partyBannerModel.find('**/banner1') + banner2 = partyBannerModel.find('**/banner2') + temp = NodePath('Empty') + banner1.reparentTo(temp) + banner2.reparentTo(temp) + banner.append(banner1) + banner.append(banner2) + self.partyBanner = Actor.Actor(partyBannerModel, {'float': 'phase_13/models/parties/tt_m_ara_pty_bannerJellybean'}) + bannerSeqNodeParent = self.partyBanner.find('**/bannerJoint') + bannerSeqNode = SequenceNode('banner') + for bannerNode in banner: + bannerSeqNode.addChild(bannerNode.node()) + + temp.detachNode() + del temp + bannerSeqNodeParent.attachNewNode(bannerSeqNode) + bannerSeqNode.setFrameRate(4) + bannerSeqNode.loop(True) + bannerSeqNode.setPlayRate(1) + balloonLeft = self.partyBanner.find('**/balloonsLMesh') + balloonRight = self.partyBanner.find('**/balloonsRMesh') + balloonLeft.setBillboardAxis() + balloonRight.setBillboardAxis() + balloonLeftLocator = self.partyBanner.find('**/balloonJointL') + balloonRightLocator = self.partyBanner.find('**/balloonJointR') + balloonLeft.reparentTo(balloonLeftLocator) + balloonRight.reparentTo(balloonRightLocator) + self.partyBanner.loop('float') + self.partyBanner.reparentTo(self) + elif self.name == 'GagGlobe': + self.partyGlobe = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_gagGlobe_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_gagGlobe'}) + self.partyGlobe.setBillboardAxis() + confettiLocator = self.partyGlobe.find('**/uvj_confetti') + confettiMesh = self.partyGlobe.find('**/innerGlobeMesh') + confettiMesh.setTexProjector(confettiMesh.findTextureStage('default'), confettiLocator, self.partyGlobe) + collisionMesh = self.partyGlobe.find('**/collisionMesh') + collisionMesh.hide() + self.globeSphere = CollisionSphere(confettiMesh.getBounds().getCenter(), confettiMesh.getBounds().getRadius()) + self.globeSphere.setTangible(1) + self.globeSphereNode = CollisionNode('gagGlobe' + str(self.getPos())) + self.globeSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.globeSphereNode.addSolid(self.globeSphere) + self.globeSphereNodePath = self.partyGlobe.attachNewNode(self.globeSphereNode) + self.partyGlobe.loop('idle') + self.partyGlobe.reparentTo(self) + elif self.name == 'FlyingHeart': + flyingHeartModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_heartWing_model') + self.flyingHeart = Actor.Actor(flyingHeartModel, {'idle': 'phase_13/models/parties/tt_m_ara_pty_heartWing'}) + wingsSeqNodeParent = self.flyingHeart.find('**/heartWingJoint') + collisionMesh = self.flyingHeart.find('**/collision_heartWing') + collisionMesh.hide() + self.globeSphere = CollisionSphere(collisionMesh.getBounds().getCenter(), collisionMesh.getBounds().getRadius()) + self.globeSphere.setTangible(1) + self.globeSphereNode = CollisionNode('flyingHeart' + str(self.getPos())) + self.globeSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.globeSphereNode.addSolid(self.globeSphere) + self.globeSphereNodePath = self.flyingHeart.attachNewNode(self.globeSphereNode) + self.globeSphereNodePath.reparentTo(wingsSeqNodeParent) + wings = [] + wingsSeqNode = SequenceNode('wingsSeqNode') + temp = NodePath('Empty') + wing1 = self.flyingHeart.find('**/wing1') + wing2 = self.flyingHeart.find('**/wing2') + wing3 = self.flyingHeart.find('**/wing3') + wing4 = self.flyingHeart.find('**/wing4') + wing1.reparentTo(temp) + wing2.reparentTo(temp) + wing3.reparentTo(temp) + wing4.reparentTo(temp) + wings.append(wing1) + wings.append(wing2) + wings.append(wing3) + wings.append(wing4) + wingsSeqNode.addChild(wing1.node()) + wingsSeqNode.addChild(wing2.node()) + wingsSeqNode.addChild(wing3.node()) + wingsSeqNode.addChild(wing4.node()) + wingsSeqNode.addChild(wing3.node()) + wingsSeqNode.addChild(wing2.node()) + temp.detachNode() + del temp + wingsSeqNodeParent.attachNewNode(wingsSeqNode) + wingsSeqNode.setFrameRate(12) + wingsSeqNode.loop(True) + wingsSeqNode.setPlayRate(1) + self.flyingHeart.loop('idle') + self.flyingHeart.reparentTo(self) + elif self.name == 'HeartBanner': + self.heartBanner = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_bannerValentine_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_bannerValentine'}) + balloonLeft = self.heartBanner.find('**/balloonsL') + balloonRight = self.heartBanner.find('**/balloonsR') + balloonLeft.setBillboardAxis() + balloonRight.setBillboardAxis() + balloonLeftLocator = self.heartBanner.find('**/balloonJointL') + balloonRightLocator = self.heartBanner.find('**/balloonJointR') + balloonLeft.reparentTo(balloonLeftLocator) + balloonRight.reparentTo(balloonRightLocator) + self.heartBanner.loop('idle') + self.heartBanner.reparentTo(self) + elif self.name == 'Hydra' or self.name == 'StageWinter': + if self.name == 'StageWinter': + self.hydra = Actor.Actor('phase_13/models/parties/tt_r_ara_pty_winterProps', {'dance': 'phase_13/models/parties/tt_a_ara_pty_hydra_dance'}) + else: + self.hydra = Actor.Actor('phase_13/models/parties/tt_a_ara_pty_hydra_default', {'dance': 'phase_13/models/parties/tt_a_ara_pty_hydra_dance'}) + st = random.randint(0, 10) + animIval = ActorInterval(self.hydra, 'dance') + animIvalDur = animIval.getDuration() + self.decSfx = loader.loadSfx('phase_13/audio/sfx/tt_s_ara_pty_propsShow_dance.mp3') + soundIval = SoundInterval(self.decSfx, node=self.hydra, listenerNode=base.localAvatar, volume=PartyGlobals.DECORATION_VOLUME, cutOff=PartyGlobals.DECORATION_CUTOFF, duration=animIvalDur) + self.animSeq = Parallel(animIval, soundIval) + self.animSeq.loop(st) + collisions = self.hydra.find('**/*collision*') + collisions.setPos(0, 0, -5) + self.hydra.flattenStrong() + self.hydra.reparentTo(self) + if self.name == 'StageWinter': + stageBounds = self.hydra.find('**/stage').node().getBounds() + self.hydra.node().setBounds(stageBounds) + self.hydra.node().setFinal(1) + elif self.name == 'TubeCogVictory': + self.tubeCog = Actor.Actor('phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_default', {'wave': 'phase_5.5/models/estate/tt_a_ara_pty_tubeCogVictory_wave'}) + st = random.randint(0, 10) + animIval = ActorInterval(self.tubeCog, 'wave') + animIvalDur = animIval.getDuration() + self.decSfx = loader.loadSfx('phase_13/audio/sfx/tt_s_ara_pty_tubeCogVictory_wave.mp3') + soundIval = SoundInterval(self.decSfx, node=self.tubeCog, listenerNode=base.localAvatar, volume=PartyGlobals.DECORATION_VOLUME, cutOff=PartyGlobals.DECORATION_CUTOFF, duration=animIvalDur) + self.animSeq = Parallel(animIval, soundIval) + self.animSeq.loop() + self.animSeq.setT(st) + self.tubeCog.flattenStrong() + self.tubeCog.reparentTo(self) + elif self.name == 'BannerVictory': + self.bannerVictory = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_bannerVictory_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_bannerVictory'}) + balloonLeft = self.bannerVictory.find('**/balloonsLMesh') + balloonRight = self.bannerVictory.find('**/balloonsRMesh') + balloonLeft.setBillboardAxis() + balloonRight.setBillboardAxis() + balloonLeftLocator = self.bannerVictory.find('**/balloonJointL') + balloonRightLocator = self.bannerVictory.find('**/balloonJointR') + balloonLeft.reparentTo(balloonLeftLocator) + balloonRight.reparentTo(balloonRightLocator) + self.bannerVictory.loop('idle') + self.bannerVictory.reparentTo(self) + elif self.name == 'CannonVictory': + self.cannonVictory = Actor.Actor('phase_13/models/parties/tt_m_ara_pty_cannonVictory_model', {'idle': 'phase_13/models/parties/tt_m_ara_pty_cannonVictory'}) + confettiLocator = self.cannonVictory.findAllMatches('**/uvj_confetties')[1] + confettiMesh = self.cannonVictory.find('**/confettis') + confettiMesh.setTexProjector(confettiMesh.findTextureStage('default'), self.cannonVictory, confettiLocator) + self.cannonVictory.flattenStrong() + self.cannonVictory.loop('idle') + self.cannonVictory.reparentTo(self) + elif self.name == 'CogStatueVictory': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogDoodleVictory') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'CogIceCreamVictory': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogIceCreamVictory') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'cogIceCreamWinter': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogIceCreamWinter') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'CogStatueWinter': + self.decorationModel = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_cogDoodleWinter') + self.decorationModel.reparentTo(self) + self.decorationShadow = self.setupAnimSeq() + elif self.name == 'snowman': + self.decorationModel = loader.loadModel('phase_13/models/estate/tt_m_prp_ext_snowman') + self.decorationModel.reparentTo(self) + self.decorationModel.find('**/growthStage_1').hide() + self.decorationModel.find('**/growthStage_2').hide() + elif self.name == 'snowDoodle': + self.decorationModel = loader.loadModel('phase_5.5/models/estate/tt_m_prp_ext_snowDoodle') + self.decorationModel.reparentTo(self) + self.decorationModel.find('**/growthStage_1').hide() + self.decorationModel.find('**/growthStage_2').hide() + else: + self.decorationModels = loader.loadModel('phase_4/models/parties/partyDecorations') + self.decorationModels.copyTo(self) + decors = self.findAllMatches('**/partyDecoration_*') + for i in xrange(decors.getNumPaths()): + decPiece = decors.getPath(i) + n = decPiece.getName() + if n.endswith('shadow') or n.endswith('base') or n.endswith('collision') or n.endswith(name): + pass + else: + decPiece.reparentTo(hidden) + + self.reparentTo(base.cr.playGame.hood.loader.geom) + + def setupAnimSeq(self): + self.startAnim = 1 + self.animSeq = None + shadow = self.find('**/*shadow*;+i') + shadow.wrtReparentTo(base.cr.playGame.hood.loader.geom) + self.startAnimSeq() + return shadow + + def startAnimSeq(self): + if self.animSeq: + self.animSeq.finish() + if self.startAnim == 1: + self.animSeq = Sequence(LerpHprInterval(self.decorationModel, 3.0, Vec3(random.randint(0, 5), random.randint(0, 5), random.randint(0, 5))), Wait(0.05), Func(self.startAnimSeq)) + self.animSeq.start() + + def cleanUpAnimSequences(self): + self.startAnim = 0 + if hasattr(self, 'animSeq'): + self.animSeq.pause() + self.animSeq.finish() + if self.animSeq: + del self.animSeq + + def unload(self): + self.notify.debug('Unloading') + if self.name == 'GagGlobe': + self.globeSphereNodePath.removeNode() + del self.globeSphereNodePath + del self.globeSphereNode + del self.globeSphere + self.partyGlobe.removeNode() + del self.partyGlobe + elif self.name == 'Hydra' or self.name == 'StageWinter': + self.cleanUpAnimSequences() + self.hydra.removeNode() + del self.hydra + if hasattr(self, 'decSfx'): + del self.decSfx + elif self.name == 'TubeCogVictory': + self.cleanUpAnimSequences() + self.tubeCog.removeNode() + del self.tubeCog + if hasattr(self, 'decSfx'): + del self.decSfx + elif self.name == 'BannerJellyBean': + self.partyBanner.removeNode() + elif self.name == 'CakeTower': + self.partyCake.removeNode() + elif self.name == 'FlyingHeart': + self.globeSphereNodePath.removeNode() + del self.globeSphereNodePath + del self.globeSphereNode + del self.globeSphere + self.flyingHeart.removeNode() + elif self.name == 'HeartBanner': + self.heartBanner.removeNode() + elif self.name == 'CannonVictory': + self.cannonVictory.removeNode() + del self.cannonVictory + elif self.name == 'CogIceCreamVictory' or self.name == 'CogStatueVictory' or self.name == 'cogIceCreamWinter' or self.name == 'CogStatueWinter': + self.cleanUpAnimSequences() + self.decorationModel.removeNode() + self.decorationShadow.removeNode() + del self.decorationShadow + elif self.name == 'snowman' or self.name == 'snowDoodle': + self.decorationModel.removeNode() + elif self.name == 'BannerVictory': + self.bannerVictory.removeNode() + del self.bannerVictory + elif self.name == 'CannonVictory': + self.decorationModel.removeNode() + del self.decorationModel + else: + self.decorationModels.removeNode() diff --git a/toontown/parties/DistributedParty.py b/toontown/parties/DistributedParty.py new file mode 100644 index 0000000..161cbdf --- /dev/null +++ b/toontown/parties/DistributedParty.py @@ -0,0 +1,584 @@ +import random +import time +import datetime +from pandac.PandaModules import Vec4, TextNode, CardMaker, NodePath +from direct.distributed import DistributedObject +from direct.task.Task import Task +from direct.gui.DirectGui import DirectLabel +from direct.gui import OnscreenText +from toontown.toonbase import ToontownGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +from toontown.parties import PartyGlobals +from toontown.parties.Decoration import Decoration +import PartyUtils + +class DistributedParty(DistributedObject.DistributedObject): + notify = directNotify.newCategory('DistributedParty') + generatedEvent = 'distributedPartyGenerated' + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.partyDoneEvent = 'partyDone' + self.load() + self.avIdsAtParty = [] + base.distributedParty = self + self.titleText = '' + self.isPartyEnding = False + + def setPartyState(self, partyState): + self.isPartyEnding = partyState + messenger.send('partyStateChanged', [partyState]) + + def getPartyState(self): + return self.isPartyEnding + + def setPartyClockInfo(self, x, y, h): + x = PartyUtils.convertDistanceFromPartyGrid(x, 0) + y = PartyUtils.convertDistanceFromPartyGrid(y, 1) + h = PartyUtils.convertDegreesFromPartyGrid(h) + self.partyClockInfo = (x, y, h) + self.loadPartyCountdownTimer() + + def setInviteeIds(self, inviteeIds): + self.inviteeIds = inviteeIds + + def setPartyInfoTuple(self, partyInfoTuple): + self.partyInfo = PartyInfo(*partyInfoTuple) + self.loadDecorations() + allActIds = [ x.activityId for x in self.partyInfo.activityList ] + base.partyHasJukebox = PartyGlobals.ActivityIds.PartyJukebox in allActIds or PartyGlobals.ActivityIds.PartyJukebox40 in allActIds + self.grid = [[False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False], + [False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False], + [False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False], + [False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False], + [False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False], + [False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False, + False], + [False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False], + [False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False, + False], + [False, + False, + False, + False, + False, + False, + True, + True, + True, + True, + True, + False, + False, + False, + False, + False, + False, + False]] + + def fillGrid(x, y, size): + for i in range(-size[1] / 2 + 1, size[1] / 2 + 1): + for j in range(-size[0] / 2 + 1, size[0] / 2 + 1): + self.grid[i + y][j + x] = False + + for activityBase in self.partyInfo.activityList: + fillGrid(activityBase.x, activityBase.y, PartyGlobals.ActivityInformationDict[activityBase.activityId]['gridsize']) + + for decorBase in self.partyInfo.decors: + fillGrid(decorBase.x, decorBase.y, PartyGlobals.DecorationInformationDict[decorBase.decorId]['gridsize']) + + self.loadGrass() + + def setPartyStartedTime(self, startedTime): + stime = time.strptime(startedTime, '%Y-%m-%d %H:%M:%S') + self.partyStartedTime = datetime.datetime(year=stime.tm_year, month=stime.tm_mon, day=stime.tm_mday, hour=stime.tm_hour, minute=stime.tm_min, second=stime.tm_sec, tzinfo=base.cr.toontownTimeManager.getCurServerDateTime().tzinfo) + + def disable(self): + self.notify.debug('disable') + DistributedObject.DistributedObject.disable(self) + base.localAvatar.chatMgr.chatInputSpeedChat.removeInsidePartiesMenu() + + def delete(self): + self.notify.debug('delete') + self.unload() + if hasattr(base, 'distributedParty'): + del base.distributedParty + DistributedObject.DistributedObject.delete(self) + + def load(self): + Toon.loadMinigameAnims() + self.defaultSignModel = loader.loadModel('phase_13/models/parties/eventSign') + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + model = loader.loadModel('phase_4/models/parties/partyStickerbook') + self.partyHat = model.find('**/Stickerbook_PartyIcon') + self.partyHat.setPos(0.0, 0.1, 2.5) + self.partyHat.setHpr(0.0, 0.0, -50.0) + self.partyHat.setScale(4.0) + self.partyHat.setBillboardAxis() + self.partyHat.reparentTo(hidden) + model.removeNode() + self.defaultLeverModel = loader.loadModel('phase_13/models/parties/partyLeverBase') + self.defaultStickModel = loader.loadModel('phase_13/models/parties/partyLeverStick') + + def loadGrass(self): + self.grassRoot = NodePath('GrassRoot') + self.grassRoot.reparentTo(base.cr.playGame.hood.loader.geom) + grass = loader.loadModel('phase_13/models/parties/grass') + clearPositions = self.getClearSquarePositions() + numTufts = min(len(clearPositions) * 3, PartyGlobals.TuftsOfGrass) + for i in range(numTufts): + g = grass.copyTo(self.grassRoot) + pos = random.choice(clearPositions) + g.setPos(pos[0] + random.randint(-8, 8), pos[1] + random.randint(-8, 8), 0.0) + + def loadDecorations(self): + self.decorationsList = [] + for decorBase in self.partyInfo.decors: + self.decorationsList.append(Decoration(PartyGlobals.DecorationIds.getString(decorBase.decorId), PartyUtils.convertDistanceFromPartyGrid(decorBase.x, 0), PartyUtils.convertDistanceFromPartyGrid(decorBase.y, 1), PartyUtils.convertDegreesFromPartyGrid(decorBase.h))) + + def unload(self): + if hasattr(self, 'decorationsList') and self.decorationsList: + for decor in self.decorationsList: + decor.unload() + + del self.decorationsList + self.stopPartyClock() + self.grassRoot.removeNode() + del self.grassRoot + if hasattr(self, 'testGrid'): + self.testGrid.removeNode() + del self.testGrid + self.ignoreAll() + Toon.unloadMinigameAnims() + self.partyHat.removeNode() + del self.partyHat + if hasattr(base, 'partyHasJukebox'): + del base.partyHasJukebox + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.sendUpdate('avIdEnteredParty', [base.localAvatar.doId]) + globalClock.syncFrameTime() + self.startPartyClock() + base.localAvatar.chatMgr.chatInputSpeedChat.addInsidePartiesMenu() + self.spawnTitleText() + messenger.send(self.generatedEvent) + if config.GetBool('show-debug-party-grid', 0): + self.testGrid = NodePath('test_grid') + self.testGrid.reparentTo(base.cr.playGame.hood.loader.geom) + for i in range(len(self.grid)): + for j in range(len(self.grid[i])): + cm = CardMaker('gridsquare') + np = NodePath(cm.generate()) + np.setScale(12) + np.setP(-90.0) + np.setPos(PartyUtils.convertDistanceFromPartyGrid(j, 0) - 6.0, PartyUtils.convertDistanceFromPartyGrid(i, 1) - 6.0, 0.1) + np.reparentTo(self.testGrid) + if self.grid[i][j]: + np.setColorScale(0.0, 1.0, 0.0, 1.0) + else: + np.setColorScale(1.0, 0.0, 0.0, 1.0) + + def getClearSquarePos(self): + clearPositions = self.getClearSquarePositions() + if len(clearPositions) == 0: + raise StandardError, 'Party %s has no empty grid squares.' % self.doId + return random.choice(clearPositions) + + def getClearSquarePositions(self): + clearPositions = [] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x]: + pos = (PartyUtils.convertDistanceFromPartyGrid(x, 0), PartyUtils.convertDistanceFromPartyGrid(y, 1), 0.1) + clearPositions.append(pos) + + return clearPositions + + def startPartyClock(self): + self.partyClockModel.reparentTo(base.cr.playGame.hood.loader.geom) + curServerTime = base.cr.toontownTimeManager.getCurServerDateTime() + timePartyWillEnd = self.partyStartedTime + datetime.timedelta(hours=PartyGlobals.DefaultPartyDuration) + timeLeftInParty = timePartyWillEnd - curServerTime + if curServerTime < timePartyWillEnd: + self.secondsLeftInParty = timeLeftInParty.seconds + else: + self.secondsLeftInParty = 0 + taskMgr.doMethodLater(0.5, self.partyClockTask, 'UpdatePartyClock') + self.partyClockSignFront = self.partyClockModel.find('**/signFrontText_locator') + self.partyClockSignBack = self.partyClockModel.find('**/signBackText_locator') + self.attachHostNameToSign(self.partyClockSignFront) + self.attachHostNameToSign(self.partyClockSignBack) + + def attachHostNameToSign(self, locator): + if self.hostName == '': + return + nameText = TextNode('nameText') + nameText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + nameText.setCardDecal(True) + nameText.setCardColor(1.0, 1.0, 1.0, 0.0) + r = 232.0 / 255.0 + g = 169.0 / 255.0 + b = 23.0 / 255.0 + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setFont(ToontownGlobals.getBuildingNametagFont()) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + nameText.setShadow(*TTLocalizer.BuildingNametagShadow) + nameWordWrap = 11.0 + nameText.setWordwrap(nameWordWrap) + scaleMult = 0.48 + houseName = self.hostName + nameText.setText(houseName) + textWidth = nameText.getWidth() + xScale = 1.0 * scaleMult + if textWidth > nameWordWrap: + xScale = nameWordWrap / textWidth * scaleMult + sign_origin = locator + namePlate = sign_origin.attachNewNode(nameText) + namePlate.setDepthWrite(0) + namePlate.setPos(0, 0, 0) + namePlate.setScale(xScale) + + def stopPartyClock(self): + self.partyClockModel.removeNode() + taskMgr.remove('UpdatePartyClock') + + def partyClockTask(self, task): + self.secondsLeftInParty -= 0.5 + if self.secondsLeftInParty < 0: + self.frontTimer['minute']['text'] = '--' + self.backTimer['minute']['text'] = '--' + self.frontTimer['second']['text'] = '--' + self.backTimer['second']['text'] = '--' + return + if self.frontTimer['colon'].isStashed(): + self.frontTimer['colon'].unstash() + self.backTimer['colon'].unstash() + else: + self.frontTimer['colon'].stash() + self.backTimer['colon'].stash() + minutesLeft = int(int(self.secondsLeftInParty / 60) % 60) + if minutesLeft < 10: + minutesLeft = '0%d' % minutesLeft + else: + minutesLeft = '%d' % minutesLeft + secondsLeft = int(self.secondsLeftInParty % 60) + if secondsLeft < 10: + secondsLeft = '0%d' % secondsLeft + else: + secondsLeft = '%d' % secondsLeft + self.frontTimer['minute']['text'] = minutesLeft + self.backTimer['minute']['text'] = minutesLeft + self.frontTimer['second']['text'] = secondsLeft + self.backTimer['second']['text'] = secondsLeft + taskMgr.doMethodLater(0.5, self.partyClockTask, 'UpdatePartyClock') + if self.secondsLeftInParty != int(self.secondsLeftInParty): + self.partyClockModel.find('**/middleRotateFront_grp').setR(-6.0 * (self.secondsLeftInParty % 60)) + self.partyClockModel.find('**/middleRotateBack_grp').setR(6.0 * (self.secondsLeftInParty % 60)) + + def getAvIdsAtParty(self): + return self.avIdsAtParty + + def setAvIdsAtParty(self, avIdsAtParty): + self.avIdsAtParty = avIdsAtParty + + def loadPartyCountdownTimer(self): + self.partyClockModel = loader.loadModel('phase_13/models/parties/partyClock') + self.partyClockModel.setPos(self.partyClockInfo[0], self.partyClockInfo[1], 0.0) + self.partyClockModel.setH(self.partyClockInfo[2]) + self.partyClockModel.reparentTo(base.cr.playGame.hood.loader.geom) + self.partyClockModel.find('**/frontText_locator').setY(-1.1) + self.partyClockModel.find('**/backText_locator').setY(0.633) + self.frontTimer = self.getTimer(self.partyClockModel.find('**/frontText_locator')) + base.frontTimerLoc = self.partyClockModel.find('**/frontText_locator') + base.backTimerLoc = self.partyClockModel.find('**/backText_locator') + self.backTimer = self.getTimer(self.partyClockModel.find('**/backText_locator')) + self.partyClockModel.stash() + + def getTimer(self, parent): + timeFont = ToontownGlobals.getMinnieFont() + timer = {} + timer['minute'] = DirectLabel(parent=parent, pos=TTLocalizer.DPtimerMinutePos, relief=None, text='59', text_align=TextNode.ACenter, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerMinute) + timer['colon'] = DirectLabel(parent=parent, pos=TTLocalizer.DPtimerColonPos, relief=None, text=':', text_align=TextNode.ACenter, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerColon) + timer['second'] = DirectLabel(parent=parent, relief=None, pos=TTLocalizer.DPtimerSecondPos, text='14', text_align=TextNode.ACenter, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerSecond) + timer['textLabel'] = DirectLabel(parent=parent, relief=None, pos=(0.0, 0.0, 1.15), text=TTLocalizer.PartyCountdownClockText, text_font=timeFont, text_fg=(0.7, 0.3, 0.3, 1.0), scale=TTLocalizer.DPtimerTextLabel) + return timer + + def setHostName(self, hostName): + self.hostName = hostName + if hasattr(self, 'partyClockSignFront'): + self.attachHostNameToSign(self.partyClockSignFront) + if hasattr(self, 'partyClockSignBack'): + self.attachHostNameToSign(self.partyClockSignBack) + + def spawnTitleText(self): + if not self.hostName: + return + partyText = TTLocalizer.PartyTitleText % TTLocalizer.GetPossesive(self.hostName) + self.doSpawnTitleText(partyText) + + def doSpawnTitleText(self, text): + self.titleColor = (1.0, 0.5, 0.4, 1.0) + self.titleText = OnscreenText.OnscreenText(text, fg=self.titleColor, font=ToontownGlobals.getSignFont(), pos=(0, -0.5), scale=0.16, drawOrder=0, mayChange=1, wordwrap=16) + self.titleText.setText(text) + self.titleText.show() + self.titleText.setColor(Vec4(*self.titleColor)) + self.titleText.clearColorScale() + self.titleText.setFg(self.titleColor) + seq = Task.sequence(Task.pause(0.1), Task.pause(6.0), self.titleText.lerpColorScale(Vec4(1.0, 1.0, 1.0, 1.0), Vec4(1.0, 1.0, 1.0, 0.0), 0.5), Task(self.hideTitleTextTask)) + taskMgr.add(seq, 'titleText') + + def hideTitleTextTask(self, task): + self.titleText.hide() + return Task.done + + def hideTitleText(self): + if self.titleText: + self.titleText.hide() diff --git a/toontown/parties/DistributedPartyActivity.py b/toontown/parties/DistributedPartyActivity.py new file mode 100644 index 0000000..96accd0 --- /dev/null +++ b/toontown/parties/DistributedPartyActivity.py @@ -0,0 +1,527 @@ +from pandac.PandaModules import CollisionSphere, CollisionNode, CollisionTube +from pandac.PandaModules import TextNode, NodePath, Vec3, Point3 +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed import DistributedObject +from direct.showbase import RandomNumGen +from direct.showbase import PythonUtil +from direct.interval.IntervalGlobal import Sequence, Parallel, ActorInterval +from direct.interval.FunctionInterval import Wait +from otp.avatar import Emote +from otp.otpbase import OTPGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.minigame.MinigameRulesPanel import MinigameRulesPanel +from toontown.toontowngui import TTDialog +from toontown.parties.JellybeanRewardGui import JellybeanRewardGui +from toontown.parties.PartyUtils import getPartyActivityIcon, getCenterPosFromGridSize + +class DistributedPartyActivity(DistributedObject.DistributedObject): + + def __init__(self, cr, activityId, activityType, wantLever = False, wantRewardGui = False): + DistributedObject.DistributedObject.__init__(self, cr) + self.activityId = activityId + self.activityName = PartyGlobals.ActivityIds.getString(self.activityId) + self.activityType = activityType + self.wantLever = wantLever + self.wantRewardGui = wantRewardGui + self.messageGui = None + self.rewardGui = None + self.toonIds = [] + self._toonId2ror = {} + childName = '%s' % self + childName = childName[childName.rfind('.DistributedParty') + len('.DistributedParty'):childName.rfind('Activity instance')] + if not hasattr(base, 'partyActivityDict'): + base.partyActivityDict = {} + base.partyActivityDict[childName] = self + self.root = NodePath('root') + self.rulesDoneEvent = 'rulesDone' + self.modelCount = 500 + self.cleanupActions = [] + self.usesSmoothing = 0 + self.usesLookAround = 0 + self.difficultyOverride = None + self.trolleyZoneOverride = None + self._localToonRequestStatus = None + return + + def localToonExiting(self): + self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Exiting + + def localToonJoining(self): + self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Joining + + def d_toonJoinRequest(self): + if self._localToonRequestStatus is None: + self.localToonJoining() + self.sendUpdate('toonJoinRequest') + return + + def d_toonExitRequest(self): + if self._localToonRequestStatus is None: + self.localToonExiting() + self.sendUpdate('toonExitRequest') + return + + def d_toonExitDemand(self): + self.localToonExiting() + self.sendUpdate('toonExitDemand') + + def joinRequestDenied(self, reason): + self._localToonRequestStatus = None + return + + def exitRequestDenied(self, reason): + self._localToonRequestStatus = None + return + + def handleToonJoined(self, toonId): + self.notify.error('BASE: handleToonJoined should be overridden %s' % self.activityName) + + def handleToonExited(self, toonId): + self.notify.error('BASE: handleToonExited should be overridden %s' % self.activityName) + + def handleToonDisabled(self, toonId): + self.notify.error('BASE: handleToonDisabled should be overridden %s' % self.activityName) + + def setToonsPlaying(self, toonIds): + exitedToons, joinedToons = self.getToonsPlayingChanges(self.toonIds, toonIds) + self.setToonIds(toonIds) + self._processExitedToons(exitedToons) + self._processJoinedToons(joinedToons) + + def _processExitedToons(self, exitedToons): + for toonId in exitedToons: + if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Exiting): + toon = self.getAvatar(toonId) + if toon is not None: + self.ignore(toon.uniqueName('disable')) + self.handleToonExited(toonId) + if toonId == base.localAvatar.doId: + self._localToonRequestStatus = None + if toonId in self._toonId2ror: + self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) + del self._toonId2ror[toonId] + + return + + def _processJoinedToons(self, joinedToons): + for toonId in joinedToons: + if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Joining): + if toonId not in self._toonId2ror: + request = self.cr.relatedObjectMgr.requestObjects([toonId], allCallback=self._handlePlayerPresent) + if toonId in self._toonId2ror: + del self._toonId2ror[toonId] + else: + self._toonId2ror[toonId] = request + + def _handlePlayerPresent(self, toons): + toon = toons[0] + toonId = toon.doId + if toonId in self._toonId2ror: + del self._toonId2ror[toonId] + else: + self._toonId2ror[toonId] = None + self._enableHandleToonDisabled(toonId) + self.handleToonJoined(toonId) + if toonId == base.localAvatar.doId: + self._localToonRequestStatus = None + return + + def _enableHandleToonDisabled(self, toonId): + toon = self.getAvatar(toonId) + if toon is not None: + self.acceptOnce(toon.uniqueName('disable'), self.handleToonDisabled, [toonId]) + else: + self.notify.warning('BASE: unable to get handle to toon with toonId:%d. Hook for handleToonDisabled not set.' % toonId) + return + + def isLocalToonRequestStatus(self, requestStatus): + return self._localToonRequestStatus == requestStatus + + def setToonIds(self, toonIds): + self.toonIds = toonIds + + def getToonsPlayingChanges(self, oldToonIds, newToonIds): + oldToons = set(oldToonIds) + newToons = set(newToonIds) + exitedToons = oldToons.difference(newToons) + joinedToons = newToons.difference(oldToons) + return (list(exitedToons), list(joinedToons)) + + def setUsesSmoothing(self): + self.usesSmoothing = True + + def setUsesLookAround(self): + self.usesLookAround = True + + def getInstructions(self): + return TTLocalizer.DefaultPartyActivityInstructions + + def getParentNodePath(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return base.cr.playGame.hood.loader.geom + else: + self.notify.warning('Hood or loader not created, defaulting to render') + return render + + def __createRandomNumGen(self): + self.notify.debug('BASE: self.doId=0x%08X' % self.doId) + self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) + + def destroy(self = self): + self.notify.debug('BASE: destroying random num gen') + del self.randomNumGen + + self.cleanupActions.append(destroy) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.notify.debug('BASE: generate, %s' % self.getTitle()) + self.__createRandomNumGen() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.notify.debug('BASE: announceGenerate %s' % self.activityName) + self.root.setName(self.activityName + 'Root') + centeredX, centeredY = getCenterPosFromGridSize(self.x, self.y, PartyGlobals.ActivityInformationDict[self.activityId]['gridsize']) + self.root.setPos(centeredX, centeredY, 0.0) + self.root.setH(self.h) + self.normalExit = True + if self.wantLever: + self.leverTriggerEvent = self.uniqueName('leverTriggerEvent') + self.load() + + def cleanup(self = self): + self.notify.debug('BASE: cleanup: normalExit=%s' % self.normalExit) + base.cr.renderFrame() + if self.normalExit: + self.sendUpdate('toonExitRequest') + + self.cleanupActions.append(cleanup) + + def disable(self): + self.notify.debug('BASE: disable') + DistributedObject.DistributedObject.disable(self) + rorToonIds = self._toonId2ror.keys() + for toonId in rorToonIds: + self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) + del self._toonId2ror[toonId] + + self.ignore(self.messageDoneEvent) + if self.messageGui is not None and not self.messageGui.isEmpty(): + self.messageGui.cleanup() + self.messageGui = None + return + + def delete(self): + self.notify.debug('BASE: delete') + self.unload() + self.ignoreAll() + DistributedObject.DistributedObject.delete(self) + + def load(self): + self.notify.debug('BASE: load') + self.loadSign() + if self.wantLever: + self.loadLever() + if self.wantRewardGui: + self.showRewardDoneEvent = self.uniqueName('showRewardDoneEvent') + self.rewardGui = JellybeanRewardGui(self.showRewardDoneEvent) + self.messageDoneEvent = self.uniqueName('messageDoneEvent') + self.root.reparentTo(self.getParentNodePath()) + self._enableCollisions() + + def loadSign(self): + actNameForSign = self.activityName + if self.activityId == PartyGlobals.ActivityIds.PartyJukebox40: + actNameForSign = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyJukebox) + elif self.activityId == PartyGlobals.ActivityIds.PartyDance20: + actNameForSign = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyDance) + self.sign = self.root.attachNewNode('%sSign' % self.activityName) + self.signModel = self.party.defaultSignModel.copyTo(self.sign) + self.signFlat = self.signModel.find('**/sign_flat') + self.signFlatWithNote = self.signModel.find('**/sign_withNote') + self.signTextLocator = self.signModel.find('**/signText_locator') + textureNodePath = getPartyActivityIcon(self.party.activityIconsModel, actNameForSign) + textureNodePath.setPos(0.0, -0.02, 2.2) + textureNodePath.setScale(2.35) + textureNodePath.copyTo(self.signFlat) + textureNodePath.copyTo(self.signFlatWithNote) + text = TextNode('noteText') + text.setTextColor(0.2, 0.1, 0.7, 1.0) + text.setAlign(TextNode.ACenter) + text.setFont(OTPGlobals.getInterfaceFont()) + text.setWordwrap(10.0) + text.setText('') + self.noteText = self.signFlatWithNote.attachNewNode(text) + self.noteText.setPosHpr(self.signTextLocator, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0) + self.noteText.setScale(0.2) + self.signFlatWithNote.stash() + self.signTextLocator.stash() + + def loadLever(self): + self.lever = self.root.attachNewNode('%sLever' % self.activityName) + self.leverModel = self.party.defaultLeverModel.copyTo(self.lever) + self.controlColumn = NodePath('cc') + column = self.leverModel.find('**/column') + column.getChildren().reparentTo(self.controlColumn) + self.controlColumn.reparentTo(column) + self.stickHinge = self.controlColumn.attachNewNode('stickHinge') + self.stick = self.party.defaultStickModel.copyTo(self.stickHinge) + self.stickHinge.setHpr(0.0, 90.0, 0.0) + self.stick.setHpr(0, -90.0, 0) + self.stick.flattenLight() + self.bottom = self.leverModel.find('**/bottom') + self.bottom.wrtReparentTo(self.controlColumn) + self.bottomPos = self.bottom.getPos() + cs = CollisionSphere(0.0, 1.35, 2.0, 1.0) + cs.setTangible(False) + cn = CollisionNode(self.leverTriggerEvent) + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.leverTrigger = self.root.attachNewNode(cn) + self.leverTrigger.reparentTo(self.lever) + self.leverTrigger.stash() + cs = CollisionTube(0.0, 2.7, 0.0, 0.0, 2.7, 3.0, 1.2) + cn = CollisionNode('levertube') + cn.addSolid(cs) + cn.setIntoCollideMask(OTPGlobals.WallBitmask) + self.leverTube = self.leverModel.attachNewNode(cn) + host = base.cr.doId2do.get(self.party.partyInfo.hostId) + if host is None: + self.notify.debug('%s loadLever : Host has left the game before lever could be created.' % self.activityName) + return + scale = host.getGeomNode().getChild(0).getSz(render) + self.leverModel.setScale(scale) + self.controlColumn.setPos(0, 0, 0) + host.setPosHpr(self.lever, 0, 0, 0, 0, 0, 0) + host.pose('leverNeutral', 0) + host.update() + pos = host.rightHand.getPos(self.controlColumn) + self.controlColumn.setPos(pos[0], pos[1], pos[2] - 1) + self.bottom.setZ(host, 0.0) + self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) + lookAtPoint = Point3(0.3, 0, 0.1) + lookAtUp = Vec3(0, -1, 0) + self.stickHinge.lookAt(host.rightHand, lookAtPoint, lookAtUp) + host.play('walk') + host.update() + return + + def unloadLever(self): + self.lever.removeNode() + self.leverModel.removeNode() + self.controlColumn.removeNode() + self.stickHinge.removeNode() + self.stick.removeNode() + self.bottom.removeNode() + self.leverTrigger.removeNode() + self.leverTube.removeNode() + del self.bottomPos + del self.lever + del self.leverModel + del self.controlColumn + del self.stickHinge + del self.stick + del self.bottom + del self.leverTrigger + del self.leverTube + + def _enableCollisions(self): + if self.wantLever: + self.leverTrigger.unstash() + self.accept('enter%s' % self.leverTriggerEvent, self._leverPulled) + + def _disableCollisions(self): + if self.wantLever: + self.leverTrigger.stash() + self.ignore('enter%s' % self.leverTriggerEvent) + + def _leverPulled(self, collEntry): + self.notify.debug('_leverPulled : Someone pulled the lever!!! ') + if self.activityType == PartyGlobals.ActivityTypes.HostInitiated and base.localAvatar.doId != self.party.partyInfo.hostId: + return False + return True + + def getToonPullingLeverInterval(self, toon): + walkTime = 0.2 + reach = ActorInterval(toon, 'leverReach', playRate=2.0) + pull = ActorInterval(toon, 'leverPull', startFrame=6) + origPos = toon.getPos(render) + origHpr = toon.getHpr(render) + newPos = self.lever.getPos(render) + newHpr = self.lever.getHpr(render) + origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) + toon.setPosHpr(origPos, origHpr) + reachAndPull = Sequence(ActorInterval(toon, 'walk', loop=True, duration=walkTime - reach.getDuration()), reach, pull) + leverSeq = Sequence(Wait(walkTime + reach.getDuration() - 0.1), self.stick.hprInterval(0.55, Point3(0.0, 25.0, 0.0), Point3(0.0, 0.0, 0.0)), Wait(0.3), self.stick.hprInterval(0.4, Point3(0.0, 0.0, 0.0), Point3(0.0, 25.0, 0.0))) + returnSeq = Sequence(Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), leverSeq, reachAndPull)) + return returnSeq + + def showMessage(self, message, endState = 'walk'): + base.cr.playGame.getPlace().fsm.request('activity') + self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) + self.messageGui = TTDialog.TTGlobalDialog(doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge) + self.messageGui.endState = endState + + def __handleMessageDone(self): + self.ignore(self.messageDoneEvent) + if hasattr(base.cr.playGame.getPlace(), 'fsm'): + if self.messageGui and hasattr(self.messageGui, 'endState'): + self.notify.info('__handleMessageDone (endState=%s)' % self.messageGui.endState) + base.cr.playGame.getPlace().fsm.request(self.messageGui.endState) + else: + self.notify.warning("messageGui has no endState, defaulting to 'walk'") + base.cr.playGame.getPlace().fsm.request('walk') + if self.messageGui is not None and not self.messageGui.isEmpty(): + self.messageGui.cleanup() + self.messageGui = None + return + + def showJellybeanReward(self, earnedAmount, jarAmount, message): + if not self.isLocalToonInActivity() or base.localAvatar.doId in self.getToonIdsAsList(): + messenger.send('DistributedPartyActivity-showJellybeanReward') + base.cr.playGame.getPlace().fsm.request('activity') + self.acceptOnce(self.showRewardDoneEvent, self.__handleJellybeanRewardDone) + self.rewardGui.showReward(earnedAmount, jarAmount, message) + + def __handleJellybeanRewardDone(self): + self.ignore(self.showRewardDoneEvent) + self.handleRewardDone() + + def handleRewardDone(self): + if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'): + base.cr.playGame.getPlace().fsm.request('walk') + + def setSignNote(self, note): + self.noteText.node().setText(note) + if len(note.strip()) > 0: + self.signFlat.stash() + self.signFlatWithNote.unstash() + self.signTextLocator.unstash() + else: + self.signFlat.unstash() + self.signFlatWithNote.stash() + self.signTextLocator.stash() + + def unload(self): + self.notify.debug('BASE: unload') + self.finishRules() + self._disableCollisions() + self.signModel.removeNode() + del self.signModel + self.sign.removeNode() + del self.sign + self.ignoreAll() + if self.wantLever: + self.unloadLever() + self.root.removeNode() + del self.root + del self.activityId + del self.activityName + del self.activityType + del self.wantLever + del self.messageGui + if self.rewardGui is not None: + self.rewardGui.destroy() + del self.rewardGui + if hasattr(self, 'toonIds'): + del self.toonIds + del self.rulesDoneEvent + del self.modelCount + del self.cleanupActions + del self.usesSmoothing + del self.usesLookAround + del self.difficultyOverride + del self.trolleyZoneOverride + if hasattr(base, 'partyActivityDict'): + del base.partyActivityDict + return + + def setPartyDoId(self, partyDoId): + self.party = base.cr.doId2do[partyDoId] + + def setX(self, x): + self.x = x + + def setY(self, y): + self.y = y + + def setH(self, h): + self.h = h + + def setState(self, newState, timestamp): + if newState == 'Active': + self.activityStartTime = globalClockDelta.networkToLocalTime(timestamp) + + def turnOffSmoothingOnGuests(self): + for toonId in self.toonIds: + avatar = self.getAvatar(toonId) + if avatar: + if not self.usesSmoothing: + avatar.stopSmooth() + if not self.usesLookAround: + avatar.stopLookAround() + + def getAvatar(self, toonId): + if self.cr.doId2do.has_key(toonId): + return self.cr.doId2do[toonId] + else: + self.notify.warning('BASE: getAvatar: No avatar in doId2do with id: ' + str(toonId)) + return None + return None + + def getAvatarName(self, toonId): + avatar = self.getAvatar(toonId) + if avatar: + return avatar.getName() + else: + return 'Unknown' + + def isLocalToonInActivity(self): + result = False + place = base.cr.playGame.getPlace() + if place and place.__class__.__name__ == 'Party' and hasattr(place, 'fsm') and place.fsm: + result = place.fsm.getCurrentState().getName() == 'activity' + return result + + def getToonIdsAsList(self): + return self.toonIds + + def startRules(self, timeout = PartyGlobals.DefaultRulesTimeout): + self.notify.debug('BASE: startRules') + self.accept(self.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel('PartyRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, timeout) + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], False) + self.rulesPanel.load() + self.rulesPanel.enter() + + def finishRules(self): + self.notify.debug('BASE: finishRules') + self.ignore(self.rulesDoneEvent) + if hasattr(self, 'rulesPanel'): + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], True) + + def handleRulesDone(self): + self.notify.error('BASE: handleRulesDone should be overridden') + + def getTitle(self): + return TTLocalizer.PartyActivityNameDict[self.activityId]['generic'] + + def local2ActivityTime(self, timestamp): + return timestamp - self.activityStartTime + + def activity2LocalTime(self, timestamp): + return timestamp + self.activityStartTime + + def getCurrentActivityTime(self): + return self.local2ActivityTime(globalClock.getFrameTime()) + + def disableEmotes(self): + Emote.globalEmote.disableAll(base.localAvatar) + + def enableEmotes(self): + Emote.globalEmote.releaseAll(base.localAvatar) diff --git a/toontown/parties/DistributedPartyCannon.py b/toontown/parties/DistributedPartyCannon.py new file mode 100644 index 0000000..2212402 --- /dev/null +++ b/toontown/parties/DistributedPartyCannon.py @@ -0,0 +1,449 @@ +from pandac.PandaModules import * +from direct.distributed.DistributedObject import DistributedObject +from direct.task.Task import Task +from toontown.minigame import CannonGameGlobals +from toontown.minigame.CannonGameGlobals import * +from toontown.parties.Cannon import Cannon +from toontown.parties.CannonGui import CannonGui +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyCannonActivity import DistributedPartyCannonActivity +LAND_TIME = 2 +WORLD_SCALE = 2.0 +GROUND_SCALE = 1.4 * WORLD_SCALE +CANNON_SCALE = 1.0 +FAR_PLANE_DIST = 600 * WORLD_SCALE +GROUND_PLANE_MIN = -15 +CANNON_Y = -int(CannonGameGlobals.TowerYRange / 2 * 1.3) +CANNON_X_SPACING = 12 +CANNON_Z = 20 +CANNON_ROTATION_MIN = -55 +CANNON_ROTATION_MAX = 50 +CANNON_ROTATION_VEL = 15.0 +ROTATIONCANNON_ANGLE_MIN = 15 +CANNON_ANGLE_MAX = 85 +CANNON_ANGLE_VEL = 15.0 +CANNON_MOVE_UPDATE_FREQ = 0.5 +CAMERA_PULLBACK_MIN = 20 +CAMERA_PULLBACK_MAX = 40 +MAX_LOOKAT_OFFSET = 80 +TOON_TOWER_THRESHOLD = 150 +SHADOW_Z_OFFSET = 0.5 +TOWER_HEIGHT = 43.85 +TOWER_RADIUS = 10.5 +BUCKET_HEIGHT = 36 +TOWER_Y_RANGE = CannonGameGlobals.TowerYRange +TOWER_X_RANGE = int(TOWER_Y_RANGE / 2.0) +INITIAL_VELOCITY = 80.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.35 + +class DistributedPartyCannon(DistributedObject, Cannon): + notify = directNotify.newCategory('DistributedPartyCannon') + LOCAL_CANNON_MOVE_TASK = 'localCannonMoveTask' + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + Cannon.__init__(self, parent=self.getParentNodePath()) + self.localCannonMoving = False + self.active = False + self.activityDoId = 0 + self.activity = None + self.gui = None + self.toonInsideAvId = 0 + self.sign = None + self.controllingToonAvId = None + return + + def generateInit(self): + self.load() + self.activate() + + def load(self): + self.notify.debug('load') + Cannon.load(self, self.uniqueName('Cannon')) + if base.cr and base.cr.partyManager and base.cr.partyManager.getShowDoid(): + nameText = TextNode('nameText') + nameText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + nameText.setCardDecal(True) + nameText.setCardColor(1.0, 1.0, 1.0, 0.0) + r = 232.0 / 255.0 + g = 169.0 / 255.0 + b = 23.0 / 255.0 + nameText.setTextColor(r, g, b, 1) + nameText.setAlign(nameText.ACenter) + nameText.setShadowColor(0, 0, 0, 1) + nameText.setText(str(self.doId)) + namePlate = self.parentNode.attachNewNode(nameText) + namePlate.setDepthWrite(0) + namePlate.setPos(0, 0, 8) + namePlate.setScale(3) + + def announceGenerate(self): + self.sign = self.activity.sign.instanceUnderNode(self.activity.getParentNodePath(), self.uniqueName('sign')) + self.sign.reparentTo(self.activity.getParentNodePath()) + self.sign.setPos(self.parentNode, self.sign.getPos()) + + def unload(self): + self.notify.debug('unload') + if self.gui is not None: + self.gui.unload() + del self.gui + Cannon.unload(self) + if self.sign is not None: + self.sign.removeNode() + self.sign = None + self.ignoreAll() + return + + def getParentNodePath(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return base.cr.playGame.hood.loader.geom + else: + self.notify.warning('Hood or loader not created, defaulting to render') + return render + + def disable(self): + self.notify.debug('disable') + self.ignoreAll() + self.__disableCannonControl() + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) + + def delete(self): + self.deactivate() + self.unload() + DistributedObject.delete(self) + + def destroy(self): + self.notify.debug('destroy') + DistributedObject.destroy(self) + + def setPosHpr(self, x, y, z, h, p, r): + self.parentNode.setPosHpr(x, y, z, h, p, r) + + def setActivityDoId(self, doId): + self.activityDoId = doId + self.activity = base.cr.doId2do[doId] + + def activate(self): + self.accept(self.getEnterCollisionName(), self.__handleToonCollisionWithCannon) + Cannon.show(self) + self.active = True + + def deactivate(self): + self.ignore(self.getEnterCollisionName()) + Cannon.hide(self) + self.active = False + + def setMovie(self, mode, avId): + self.notify.debug('%s setMovie(%s, %s)' % (self.doId, avId, mode)) + if mode == PartyGlobals.CANNON_MOVIE_CLEAR: + self.setClear() + elif mode == PartyGlobals.CANNON_MOVIE_FORCE_EXIT: + self.exitCannon(avId) + self.setClear() + elif mode == PartyGlobals.CANNON_MOVIE_LOAD: + self.enterCannon(avId) + elif mode == PartyGlobals.CANNON_MOVIE_LANDED: + self.setLanded(avId) + else: + self.notify.error('setMovie Unhandled case mode=%d avId=%d' % (mode, avId)) + + def __handleToonCollisionWithCannon(self, collEntry): + self.notify.debug('collEntry: %s' % collEntry) + if base.cr.playGame.getPlace().getState() == 'walk' and self.toonInsideAvId == 0: + base.cr.playGame.getPlace().setState('activity') + self.d_requestEnter() + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def requestExit(self): + self.notify.debug('requestExit') + base.localAvatar.reparentTo(render) + base.cr.playGame.getPlace().setState('walk') + + def __avatarGone(self, avId): + if self.toonInsideAvId == avId: + self.notify.debug('__avatarGone in if') + if self.toonInside and not self.toonInside.isEmpty(): + self.removeToonDidNotFire() + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) + else: + self.notify.debug('__avatarGone in else, self.toonInsideAvId=%s avId=%s' % (self.toonInsideAvId, avId)) + + def enterCannon(self, avId): + if avId == base.localAvatar.doId: + base.localAvatar.pose('lose', 110) + base.localAvatar.pose('slip-forward', 25) + base.cr.playGame.getPlace().setState('activity') + base.localAvatar.collisionsOff() + camera.reparentTo(self.barrelNode) + camera.setPos(0, -2, 5) + camera.setP(-20) + if not self.activity.hasPlayedBefore(): + self.activity.displayRules() + self.acceptOnce(DistributedPartyCannonActivity.RULES_DONE_EVENT, self.__enableCannonControl) + else: + self.__enableCannonControl() + self.controllingToonAvId = avId + if self.cr.doId2do.has_key(avId): + self.toonInsideAvId = avId + self.notify.debug('enterCannon self.toonInsideAvId=%d' % self.toonInsideAvId) + toon = base.cr.doId2do[avId] + if toon: + self.acceptOnce(toon.uniqueName('disable'), self.__avatarGone, extraArgs=[avId]) + toon.stopSmooth() + toon.dropShadow.hide() + self.placeToonInside(toon) + else: + self.__avatarGone(avId) + else: + self.notify.warning('Unknown avatar %d in cannon %d' % (avId, self.doId)) + + def exitCannon(self, avId): + if avId == base.localAvatar.doId: + self.activity.finishRules() + self.ignore(DistributedPartyCannonActivity.RULES_DONE_EVENT) + self.ignoreDisableForAvId(avId) + if self.gui and avId == base.localAvatar.doId: + self.gui.unload() + toon = base.cr.doId2do.get(avId) + if toon and self.getToonInside() == toon: + self.resetToon() + else: + self.notify.debug('not resetting toon, toon=%s, self.getToonInside()=%s' % (toon, self.getToonInside())) + + def resetToon(self, pos = None): + self.notify.debug('resetToon') + toon = self.getToonInside() + toonInsideAvId = self.toonInsideAvId + self.notify.debug('%d resetToon self.toonInsideAvId=%d' % (self.doId, self.toonInsideAvId)) + self.removeToonDidNotFire() + self.__setToonUpright(toon, pos) + if toonInsideAvId == base.localAvatar.doId: + self.notify.debug('%d resetToon toonInsideAvId ==localAvatar.doId' % self.doId) + if pos: + self.notify.debug('toon setting position to %s' % pos) + base.localAvatar.setPos(pos) + camera.reparentTo(base.localAvatar) + base.localAvatar.collisionsOn() + base.localAvatar.startPosHprBroadcast() + base.localAvatar.enableAvatarControls() + self.notify.debug('currentState=%s, requesting walk' % base.cr.playGame.getPlace().getState()) + base.cr.playGame.getPlace().setState('walk') + self.notify.debug('after request walk currentState=%s,' % base.cr.playGame.getPlace().getState()) + toon.dropShadow.show() + self.d_setLanded() + + def __setToonUpright(self, toon, pos = None): + if not pos: + pos = toon.getPos(render) + toon.setPos(render, pos) + toon.loop('neutral') + toon.lookAt(self.parentNode) + toon.setP(0) + toon.setR(0) + toon.setScale(1, 1, 1) + + def d_setLanded(self): + self.notify.debugStateCall(self) + if self.toonInsideAvId == base.localAvatar.doId: + self.sendUpdate('setLanded', [base.localAvatar.doId]) + + def setLanded(self, avId): + self.removeAvFromCannon(avId) + self.ignoreDisableForAvId(avId) + + def removeAvFromCannon(self, avId): + place = base.cr.playGame.getPlace() + av = base.cr.doId2do.get(avId) + print 'removeAvFromCannon' + if place: + if not hasattr(place, 'fsm'): + return + placeState = place.fsm.getCurrentState().getName() + print placeState + if placeState != 'fishing': + if av != None: + av.startSmooth() + self.__destroyToonModels(avId) + return + self.notify.debug('%s removeAvFromCannon' % self.doId) + if av != None: + self.notify.debug('%d removeAvFromCannon: destroying toon models' % self.doId) + av.resetLOD() + if av == base.localAvatar: + if place: + place.fsm.request('walk') + av.setPlayRate(1.0, 'run') + if av.nametag and self.toonHead: + av.nametag.removeNametag(self.toonHead.tag) + if av.getParent().getName() == 'toonOriginChange': + av.wrtReparentTo(render) + self.__setToonUpright(av) + if av == base.localAvatar: + av.startPosHprBroadcast() + av.startSmooth() + av.setScale(1, 1, 1) + self.ignore(av.uniqueName('disable')) + self.__destroyToonModels(avId) + return + + def __destroyToonModels(self, avId): + av = base.cr.doId2do.get(avId) + if not av: + return + if av != None: + av.dropShadow.show() + self.hitBumper = 0 + self.hitTarget = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + av = None + self.lastWakeTime = 0 + self.localToonShooting = 0 + if self.toonHead != None: + self.toonHead.reparentTo(hidden) + self.toonHead.stopBlink() + self.toonHead.stopLookAroundNow() + self.toonHead.delete() + self.toonHead = None + self.model_Created = 0 + return + + def setClear(self): + toon = base.cr.doId2do.get(self.toonInsideAvId) + toonName = 'None' + self.ignoreDisableForAvId(self.toonInsideAvId) + if toon and self.isToonInside(): + toonName = toon.getName() + toon.resetLOD() + toon.setPlayRate(1.0, 'run') + if toon.getParent().getName() == 'toonOriginChange': + toon.wrtReparentTo(render) + self.__setToonUpright(toon) + toon.startSmooth() + toon.setScale(1, 1, 1) + self.ignore(toon.uniqueName('disable')) + if self.toonInsideAvId == base.localAvatar.doId: + toon.startPosHprBroadcast() + try: + base.localAvatar.enableAvatarControls() + except: + self.notify.warning("couldn't enable avatar controls") + + base.cr.playGame.getPlace().setState('walk') + else: + self.notify.debug('setClear in else toon=%s, self.isToonInsde()=%s' % (toonName, self.isToonInside())) + self.toonInsideAvId = 0 + self.notify.debug('setClear self.toonInsideAvId=%d' % self.toonInsideAvId) + if self.controllingToonAvId == base.localAvatar.doId: + self.notify.debug('set_clear turning off cannon control') + self.__disableCannonControl() + self.controllingToonAvId = 0 + + def __enableCannonControl(self): + if not self.gui: + self.gui = self.activity.gui + self.gui.load() + self.gui.enable(timer=PartyGlobals.CANNON_TIMEOUT) + self.d_setTimeout() + self.accept(CannonGui.FIRE_PRESSED, self.__handleFirePressed) + self.__startLocalCannonMoveTask() + + def d_setTimeout(self): + self.sendUpdate('setTimeout') + + def __disableCannonControl(self): + if self.gui: + self.gui.unload() + self.ignore(CannonGui.FIRE_PRESSED) + self.__stopLocalCannonMoveTask() + + def __startLocalCannonMoveTask(self): + self.localCannonMoving = False + task = Task(self.__localCannonMoveTask) + task.lastPositionBroadcastTime = 0.0 + taskMgr.add(task, self.LOCAL_CANNON_MOVE_TASK) + + def __stopLocalCannonMoveTask(self): + taskMgr.remove(self.LOCAL_CANNON_MOVE_TASK) + if self.localCannonMoving: + self.localCannonMoving = False + self.stopMovingSound() + + def __localCannonMoveTask(self, task): + rotVel = 0 + if self.gui.leftPressed: + rotVel += CANNON_ROTATION_VEL + if self.gui.rightPressed: + rotVel -= CANNON_ROTATION_VEL + self.setRotation(self.getRotation() + rotVel * globalClock.getDt()) + angVel = 0 + if self.gui.upPressed: + angVel += CANNON_ANGLE_VEL + if self.gui.downPressed: + angVel -= CANNON_ANGLE_VEL + self.setAngle(self.getAngle() + angVel * globalClock.getDt()) + if self.hasMoved(): + if not self.localCannonMoving: + self.localCannonMoving = True + self.loopMovingSound() + self.updateModel() + if task.time - task.lastPositionBroadcastTime > CANNON_MOVE_UPDATE_FREQ: + self.notify.debug('Broadcast local cannon %s position' % self.doId) + task.lastPositionBroadcastTime = task.time + self.__broadcastLocalCannonPosition() + elif self.localCannonMoving: + self.localCannonMoving = False + self.stopMovingSound() + self.__broadcastLocalCannonPosition() + self.notify.debug('Cannon Rot = %s, Angle = %s' % (self._rotation, self._angle)) + return Task.cont + + def __broadcastLocalCannonPosition(self): + self.d_setCannonPosition(self._rotation, self._angle) + + def d_setCannonPosition(self, zRot, angle): + self.sendUpdate('setCannonPosition', [zRot, angle]) + + def updateCannonPosition(self, avId, zRot, angle): + if avId and avId == self.toonInsideAvId and avId != base.localAvatar.doId: + self.notify.debug('update cannon %s position zRot = %d, angle = %d' % (self.doId, zRot, angle)) + self.setRotation(zRot) + self.setAngle(angle) + self.updateModel() + + def __handleFirePressed(self): + self.notify.debug('fire pressed') + self.__disableCannonControl() + self.__broadcastLocalCannonPosition() + self.d_setCannonLit(self._rotation, self._angle) + + def d_setCannonLit(self, zRot, angle): + self.sendUpdate('setCannonLit', [zRot, angle]) + + def fire(self): + if base.localAvatar.doId == self.controllingToonAvId: + self.__disableCannonControl() + self.d_setFired() + self.playFireSequence() + self.controllingToonAvId = None + return + + def d_setFired(self): + self.sendUpdate('setFired', []) + + def ignoreDisableForAvId(self, avId): + toon = base.cr.doId2do.get(avId) + if toon: + self.notify.debug('ignoring %s' % toon.uniqueName('disable')) + self.ignore(toon.uniqueName('disable')) + else: + self.notify.debug('ignoring disable-%s' % self.toonInsideAvId) + self.ignore('disable-%s' % self.toonInsideAvId) diff --git a/toontown/parties/DistributedPartyCannonActivity.py b/toontown/parties/DistributedPartyCannonActivity.py new file mode 100644 index 0000000..80b1b0e --- /dev/null +++ b/toontown/parties/DistributedPartyCannonActivity.py @@ -0,0 +1,955 @@ +import math +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import quantizeVec +from direct.task.Task import Task +from toontown.toontowngui import TTDialog +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.effects import Splash, DustCloud, Wake +from toontown.minigame import Trajectory +from toontown.minigame import CannonGameGlobals +from toontown.parties import PartyGlobals +from toontown.parties.PartyGlobals import ActivityIds +from toontown.parties.PartyGlobals import ActivityTypes +from toontown.parties.PartyGlobals import FireworksStartedEvent +from toontown.parties.PartyGlobals import FireworksFinishedEvent +from toontown.parties.PartyGlobals import PartyCannonCollisions +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.CannonGui import CannonGui +from toontown.parties.PartyUtils import toRadians, toDegrees +CANNON_ROTATION_VEL = 15.0 +CANNON_ANGLE_VEL = 15.0 +GROUND_PLANE_MIN = -15 +SHADOW_Z_OFFSET = 0.5 +INITIAL_VELOCITY = 90.0 +WHISTLE_SPEED = INITIAL_VELOCITY * 0.35 + +class DistributedPartyCannonActivity(DistributedPartyActivity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCannonActivity') + HIT_GROUND = 0 + HIT_TOWER = 1 + HIT_WATER = 2 + REACTIVATE_CLOUD_TASK = 'PartyActivity_ReactivateLastCloud' + RULES_DONE_EVENT = 'DistributedPartyCannonActivity_RULES_DONE_EVENT' + LOCAL_TOON_LANDED_EVENT = 'DistributedPartyCannonActivity_LOCAL_TOON_LANDED_EVENT' + NetDivisor = 100 + TimeFactor = 0.75 + BroadcastPeriod = 0.2 + + def __init__(self, cr): + DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyCannon, ActivityTypes.Continuous, wantRewardGui=True) + self.gui = None + self.firingCannon = None + self.shadowNode = None + self.partyDoId = None + self.splash = None + self.dustCloud = None + self.lastWakeTime = 0 + self.localFlyingDropShadow = None + self.localFlyingToon = None + self.localFlyingToonId = 0 + self._lastBroadcastTime = -self.BroadcastPeriod + self._dirtyNewVel = None + self.hitBumper = 0 + self.hitCloud = 0 + self.lastPos = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.vel = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self._lastCloudHit = None + self.cameraPos = Vec3(0, -15.0, -25.0) + self.cameraSpeed = 5.0 + self.camNode = None + self.flyingToonOffsetRotation = 0 + self.flyingToonOffsetAngle = 0 + self.flyingToonOffsetX = 0 + self.flyingToonOffsetY = 0 + self.flyingToonCloudsHit = 0 + self.initialFlyVel = 0 + self._localPlayedBefore = False + self.hitTrack = None + self.cTrav = None + self.flyColNode = None + self.flyColNodePath = None + self._flyingCollisionTaskName = None + return + + def generateInit(self): + DistributedPartyActivity.generateInit(self) + self.taskNameFireCannon = self.taskName('fireCannon') + self.taskNameShoot = self.taskName('shootTask') + self.taskNameFly = self.taskName('flyTask') + self.gui = CannonGui() + + def load(self): + self.notify.debug('load') + DistributedPartyActivity.load(self) + base.cr.playGame.hood.loader.loadClouds() + base.cr.playGame.hood.loader.setCloudSwitch(1) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadowNode = hidden.attachNewNode('dropShadow') + self.shadow.copyTo(self.shadowNode) + self.shadowNode.setColor(0, 0, 0, 0.5) + self.shadowNode.setBin('fixed', 0, 1) + self.splash = Splash.Splash(render) + self.dustCloud = DustCloud.DustCloud(render) + self.dustCloud.setBillboardPointEye() + self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.mp3') + self.sndHitHouse = base.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.mp3') + self.sndBounce1 = base.loadSfx('phase_13/audio/sfx/bounce1.mp3') + self.sndBounce2 = base.loadSfx('phase_13/audio/sfx/bounce2.mp3') + self.sndBounce3 = base.loadSfx('phase_13/audio/sfx/bounce3.mp3') + self.onstage() + self.sign.reparentTo(hidden) + self.sign.setPos(-6.0, 10.0, 0.0) + self.accept(FireworksStartedEvent, self.__handleFireworksStarted) + self.accept(FireworksFinishedEvent, self.__handleFireworksFinished) + + def generate(self): + DistributedPartyActivity.generate(self) + self._doneCannons = False + self._avId2trajectoryInfo = {} + self._remoteToonFlyTaskName = 'remoteToonFlyTask-%s' % self.doId + taskMgr.add(self._remoteToonFlyTask, self._remoteToonFlyTaskName, priority=45) + self.d_cloudsColorRequest() + + def unload(self): + self.notify.debug('unload') + DistributedPartyActivity.unload(self) + if self.shadowNode is not None: + self.shadowNode.removeNode() + del self.shadowNode + if self.splash is not None: + self.splash.destroy() + del self.splash + if self.dustCloud is not None: + self.dustCloud.destroy() + del self.dustCloud + del self.sndHitHouse + del self.sndHitGround + del self.sndHitWater + del self.sndBounce1 + del self.sndBounce2 + del self.sndBounce3 + if self.localFlyingToon: + self.__resetToon(self.localFlyingToon) + self.localFlyingToon.loop('neutral') + self.localFlyingToon.setPlayRate(1.0, 'run') + self.localFlyingToon = None + self.ignoreAll() + return + + def onstage(self): + self.notify.debug('onstage') + self.splash.reparentTo(render) + self.dustCloud.reparentTo(render) + + def offstage(self): + self.notify.debug('offstage') + if self.splash is not None: + self.splash.reparentTo(hidden) + self.splash.stop() + if self.dustCloud is not None: + self.dustCloud.reparentTo(hidden) + self.dustCloud.stop() + return + + def disable(self): + taskMgr.remove(self._remoteToonFlyTaskName) + if self._flyingCollisionTaskName: + taskMgr.remove(self._flyingCollisionTaskName) + taskMgr.remove(self.taskNameFireCannon) + taskMgr.remove(self.taskNameShoot) + taskMgr.remove(self.taskNameFly) + taskMgr.remove(DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) + self.ignoreAll() + if self.localFlyingToonId: + self.__stopCollisionHandler(self.localFlyingToon) + self.__stopLocalFlyTask(self.localFlyingToonId) + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, self.localFlyingToonId) + if self.hitTrack is not None: + self.hitTrack.finish() + del self.hitTrack + self.hitTrack = None + DistributedPartyActivity.disable(self) + return + + def delete(self): + self.offstage() + DistributedPartyActivity.delete(self) + + def setMovie(self, mode, toonId): + self.notify.debug('%s setMovie(%s, %s)' % (self.doId, toonId, mode)) + if toonId != base.localAvatar.doId: + return + if mode == PartyGlobals.CANNON_MOVIE_CLEAR: + self.landToon(toonId) + elif mode == PartyGlobals.CANNON_MOVIE_LANDED: + self.landToon(toonId) + elif mode == PartyGlobals.CANNON_MOVIE_FORCE_EXIT: + self.landToon(toonId) + + def __handleAvatarGone(self): + self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) + + def handleToonDisabled(self, toonId): + self.notify.warning('handleToonDisabled no implementation yet') + + def handleToonJoined(self, toonId): + self.notify.warning('handleToonJoined no implementation yet') + + def isLocalToon(self, av): + return base.localAvatar == av + + def isLocalToonId(self, toonId): + return base.localAvatar.doId == toonId + + def getTitle(self): + return TTLocalizer.PartyCannonActivityTitle + + def getInstructions(self): + return TTLocalizer.PartyCannonActivityInstructions + + def hasPlayedBefore(self): + return self._localPlayedBefore + + def displayRules(self): + self.startRules() + + def handleRulesDone(self): + self.finishRules() + self._localPlayedBefore = True + messenger.send(DistributedPartyCannonActivity.RULES_DONE_EVENT) + + def setCannonWillFire(self, cannonId, zRot, angle): + self.notify.debug('setCannonWillFire: %d %d %d' % (cannonId, zRot, angle)) + cannon = base.cr.doId2do.get(cannonId) + if cannon is None: + self.notify.warning("Cannon has not been created, but we got this message. Don't show firing.") + return + if not cannon.getToonInside(): + self.notify.warning("setCannonWillFire, but no toon insde. Don't show firing") + return + if self.isLocalToon(cannon.getToonInside()): + self.localFlyingToon = base.localAvatar + self.localFlyingToonId = base.localAvatar.doId + self.localFiringCannon = cannon + self.flyingToonCloudsHit = 0 + cannon.updateModel(zRot, angle) + toonId = cannon.getToonInside().doId + task = Task(self.__fireCannonTask) + task.toonId = toonId + task.cannon = cannon + taskMgr.add(task, self.taskNameFireCannon) + self.toonIds.append(toonId) + return + + def __fireCannonTask(self, task): + launchTime = 0.0 + toonId = task.toonId + cannon = task.cannon + toon = cannon.getToonInside() + self.notify.debug(str(self.doId) + ' FIRING CANNON FOR TOON ' + str(toonId)) + if not cannon.isToonInside(): + return Task.done + if self.isLocalToonId(toonId): + self.inWater = 0 + flightResults = self.__calcFlightResults(cannon, toonId, launchTime) + if not isClient(): + print 'EXECWARNING DistributedPartyCannonActivity: %s' % flightResults + printStack() + for key in flightResults: + exec "%s = flightResults['%s']" % (key, key) + + self.notify.debug('start position: ' + str(startPos)) + self.notify.debug('start velocity: ' + str(startVel)) + self.notify.debug('time of launch: ' + str(launchTime)) + cannon.removeToonReadyToFire() + shootTask = Task(self.__shootTask, self.taskNameShoot) + shootTask.info = {'toonId': toonId, + 'cannon': cannon} + if self.isLocalToonId(toonId): + self.flyingToonOffsetRotation = 0 + self.flyingToonOffsetAngle = 0 + self.flyingToonOffsetX = 0 + self.flyingToonOffsetY = 0 + self.hitCloud = 0 + self.initialFlyVel = INITIAL_VELOCITY + self.camNode = NodePath(self.uniqueName('flyingCamera')) + self.camNode.setScale(0.5) + self.camNode.setPos(self.localFlyingToon.getPos()) + self.camNode.setHpr(self.localFlyingToon.getHpr()) + self.camNode.reparentTo(render) + self.lastStartVel = startVel + place = base.cr.playGame.getPlace() + place.fsm.request('activity') + toon.dropShadow.hide() + self.localFlyingDropShadow = self.shadowNode.copyTo(hidden) + vel = startVel + toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) + toon.setP(localAvatar, -90) + hpr = toon.getHpr() + toon.d_setPosHpr(startPos[0], startPos[1], startPos[2], hpr[0], hpr[1], hpr[2]) + self.localFlyingToon.wrtReparentTo(render) + info = {} + info['toonId'] = toonId + info['trajectory'] = trajectory + info['launchTime'] = launchTime + info['toon'] = self.localFlyingToon + info['hRot'] = cannon.getRotation() + camera.wrtReparentTo(self.localFlyingToon) + flyTask = Task(self.__localFlyTask, self.taskNameFly) + flyTask.info = info + seqTask = Task.sequence(shootTask, flyTask) + self.__startCollisionHandler() + self.notify.debug('Disable standard local toon controls.') + base.localAvatar.disableAvatarControls() + frameTime = globalClock.getFrameTime() + netLaunchTime = globalClockDelta.localToNetworkTime(launchTime + frameTime, bits=31) + self.sendUpdate('setToonTrajectoryAi', [netLaunchTime, + startPos[0], + startPos[1], + startPos[2], + startHpr[0], + startHpr[1], + startHpr[2], + startVel[0], + startVel[1], + startVel[2]]) + else: + seqTask = shootTask + taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(toonId)) + toon.startSmooth() + return Task.done + + def setToonTrajectory(self, avId, launchTime, x, y, z, h, p, r, vx, vy, vz): + if avId == localAvatar.doId: + return + startPos = Vec3(x, y, z) + startHpr = Vec3(h, p, r) + startVel = Vec3(vx, vy, vz) + startT = globalClockDelta.networkToLocalTime(launchTime, bits=31) + 0.2 + trajectory = Trajectory.Trajectory(0.0, startPos, startVel) + self._avId2trajectoryInfo[avId] = ScratchPad(startPos=startPos, startHpr=startHpr, startVel=startVel, startT=startT, trajectory=trajectory) + + def _remoteToonFlyTask(self, task = None): + ids2del = [] + frameTime = globalClock.getFrameTime() + for avId, trajInfo in self._avId2trajectoryInfo.iteritems(): + trajectory = trajInfo.trajectory + startTime = trajInfo.startT + groundTime = trajectory.calcTimeOfImpactOnPlane(0.0) / self.TimeFactor + startTime + now = frameTime + if now < startTime: + now = startTime + if now > groundTime: + now = groundTime + t = max(0.0, now - startTime) + t *= self.TimeFactor + toon = self.cr.getDo(avId) + if toon is None: + ids2del.append(avId) + else: + toon.setFluidPos(trajectory.getPos(t)) + vel = trajectory.getVel(t) + toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) + toon.setP(toon, -90) + + for avId in ids2del: + del self._avId2trajectoryInfo[avId] + + return Task.cont + + def __calcFlightResults(self, cannon, toonId, launchTime): + startPos = cannon.getToonFirePos() + startHpr = cannon.getToonFireHpr() + startVel = cannon.getToonFireVel() + quantizeVec(startPos, self.NetDivisor) + quantizeVec(startHpr, self.NetDivisor) + quantizeVec(startVel, self.NetDivisor) + trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) + self.trajectory = trajectory + return {'startPos': startPos, + 'startHpr': startHpr, + 'startVel': startVel, + 'trajectory': trajectory} + + def __shootTask(self, task): + task.info['cannon'].fire() + toonId = task.info['toonId'] + toon = base.cr.doId2do.get(toonId) + if toon: + toon.loop('swim') + else: + self.notify.debug('__shootTask avoided a crash, toon %d not found' % toonId) + if self.isLocalToonId(task.info['toonId']): + self.localFlyingDropShadow.reparentTo(render) + self.gui.enableAimKeys() + return Task.done + + def d_setLanded(self, toonId): + printStack() + self.notify.debug('d_setLanded %s' % toonId) + if self.isLocalToonId(toonId): + if self.cr: + self.sendUpdate('setLanded', [toonId]) + else: + self.notify.debug('we avoided crash 2') + + def setLanded(self, toonId): + if toonId in self._avId2trajectoryInfo: + del self._avId2trajectoryInfo[toonId] + + def landToon(self, toonId): + self.notify.debug('%s landToon' % self.doId) + toon = base.cr.doId2do.get(toonId) + if toon is not None: + toon.resetLOD() + if toon == base.localAvatar: + self.__stopCollisionHandler(base.localAvatar) + toon.wrtReparentTo(render) + self.__setToonUpright(toon) + toon.setPlayRate(1.0, 'run') + toon.startSmooth() + toon.setScale(1.0) + self.ignore(toon.uniqueName('disable')) + self.__cleanupFlyingToonData(toon) + toon.dropShadow.show() + place = base.cr.playGame.getPlace() + if place is not None: + if not hasattr(place, 'fsm'): + return + if toon is not None and toon == base.localAvatar: + self.__localDisplayLandedResults() + return + + def __localDisplayLandedResults(self): + if self.flyingToonCloudsHit > 0: + self._doneCannons = True + else: + self.__localToonDoneLanding() + + def handleRewardDone(self): + DistributedPartyActivity.handleRewardDone(self) + if self._doneCannons: + self.__localToonDoneLanding() + + def __localToonDoneLanding(self): + base.cr.playGame.getPlace().fsm.request('walk') + self.notify.debug('__localToonDoneLanding') + base.localAvatar.collisionsOn() + base.localAvatar.startPosHprBroadcast() + base.localAvatar.enableAvatarControls() + messenger.send(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT) + + def __setToonUpright(self, toon, pos = None): + if toon: + if self.inWater: + toon.setP(0) + toon.setR(0) + return + if not pos: + pos = toon.getPos(render) + toon.setPos(render, pos) + toon.loop('neutral') + if self.localFiringCannon and hasattr(self.localFiringCannon, 'cannonNode'): + if self.localFiringCannon.cannonNode: + toon.lookAt(self.localFiringCannon.cannonNode) + else: + self.notify.debug('we avoided crash 1.') + toon.setP(0) + toon.setR(0) + toon.setScale(1, 1, 1) + + def __resetToonToCannon(self, avatar): + self.notify.debug('__resetToonToCannon') + if not avatar and self.localFlyingToonId: + avatar = base.cr.doId2do.get(self.localFlyingToonId, None) + if avatar: + self.__resetToon(avatar) + return + + def __resetToon(self, avatar, pos = None): + self.notify.debug('__resetToon') + if avatar: + self.__stopCollisionHandler(avatar) + self.__setToonUpright(avatar, pos) + if self.isLocalToonId(avatar.doId): + self.notify.debug('toon setting position to %s' % pos) + if pos: + base.localAvatar.setPos(pos) + camera.reparentTo(avatar) + self.d_setLanded(avatar.doId) + + def __updateFlightVelocity(self, trajectory): + hpr = LRotationf(self.flyingToonOffsetRotation, 0, 0) + newVel = hpr.xform(self.lastStartVel) + hpr = LRotationf(0, self.flyingToonOffsetAngle, 0) + zVel = hpr.xform(self.lastStartVel).getZ() + if zVel < newVel.getZ(): + newVel.setZ(zVel) + trajectory.setStartVel(newVel) + now = globalClock.getFrameTime() + if now - self._lastBroadcastTime >= self.BroadcastPeriod: + self._dirtyNewVel = newVel + if self._dirtyNewVel: + self.sendUpdate('updateToonTrajectoryStartVelAi', [self._dirtyNewVel[0], self._dirtyNewVel[1], self._dirtyNewVel[2]]) + self._lastBroadcastTime = now + self._dirtyNewVel = None + return + + def updateToonTrajectoryStartVel(self, avId, vx, vy, vz): + if avId == localAvatar.doId: + return + if avId in self._avId2trajectoryInfo: + self._avId2trajectoryInfo[avId].trajectory.setStartVel(Vec3(vx, vy, vz)) + + def __isFlightKeyPressed(self): + return self.gui.leftPressed or self.gui.rightPressed or self.gui.upPressed or self.gui.downPressed + + def __moveFlyingToon(self, toon): + toonP = toon.getP(render) + isToonFlyingHorizontal = toonP > -150 and toonP < -30 + OFFSET = 0.25 + rotVel = 0 + if self.gui.leftPressed: + if isToonFlyingHorizontal: + rotVel += CANNON_ROTATION_VEL + else: + self.flyingToonOffsetX -= OFFSET + if self.gui.rightPressed: + if isToonFlyingHorizontal: + rotVel -= CANNON_ROTATION_VEL + else: + self.flyingToonOffsetX += OFFSET + self.flyingToonOffsetRotation += rotVel * globalClock.getDt() + angVel = 0 + if self.gui.upPressed: + if not isToonFlyingHorizontal: + self.flyingToonOffsetY -= OFFSET + if self.gui.downPressed: + if isToonFlyingHorizontal: + angVel += CANNON_ANGLE_VEL + else: + self.flyingToonOffsetY += OFFSET + self.flyingToonOffsetAngle += angVel * globalClock.getDt() + + def __stopLocalFlyTask(self, toonId): + taskMgr.remove(self.taskName('flyingToon') + '-' + str(toonId)) + self.gui.disableAimKeys() + + def __localFlyTask(self, task): + toon = task.info['toon'] + if toon.isEmpty(): + self.__resetToonToCannon(self.localFlyingToon) + return Task.done + curTime = task.time + task.info['launchTime'] + t = curTime + t *= self.TimeFactor + self.lastT = self.t + self.t = t + deltaT = self.t - self.lastT + self.deltaT = deltaT + if self.hitBumper: + pos = self.lastPos + self.lastVel * deltaT + vel = self.lastVel + self.lastVel += Vec3(0, 0, -32.0) * deltaT + self.lastPos = pos + toon.setFluidPos(pos) + lastR = toon.getR() + toon.setR(lastR - deltaT * self.angularVel * 2.0) + cameraView = 0 + else: + if not self.hitCloud and self.__isFlightKeyPressed(): + self.__moveFlyingToon(toon) + self.__updateFlightVelocity(task.info['trajectory']) + if self.hitCloud == 1: + vel = task.info['trajectory'].getVel(t) + startPos = toon.getPos(render) + task.info['trajectory'].setStartTime(t) + task.info['trajectory'].setStartPos(startPos) + task.info['trajectory'].setStartVel(self.lastVel) + toon.lookAt(toon.getPos() + vel) + toon.setH(-toon.getH()) + now = globalClock.getFrameTime() + netLaunchTime = globalClockDelta.localToNetworkTime(now, bits=31) + hpr = toon.getHpr() + self.sendUpdate('setToonTrajectoryAi', [netLaunchTime, + startPos[0], + startPos[1], + startPos[2], + hpr[0], + hpr[1], + hpr[2], + self.lastVel[0], + self.lastVel[1], + self.lastVel[2]]) + self._lastBroadcastTime = now + self._dirtyNewVel = None + self.flyingToonOffsetRotation = 0 + self.flyingToonOffsetAngle = 0 + self.flyingToonOffsetX = 0 + self.flyingToonOffsetY = 0 + self.hitCloud = 2 + pos = task.info['trajectory'].getPos(t) + toon.setFluidPos(pos) + toon.setFluidPos(toon, self.flyingToonOffsetX, self.flyingToonOffsetY, 0) + vel = task.info['trajectory'].getVel(t) + toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) + toon.setP(toon.getP() - 90) + cameraView = 2 + if self.hitCloud == 2: + self.lastStartVel = vel + self.hitCloud = 0 + shadowPos = toon.getPos() + shadowPos.setZ(SHADOW_Z_OFFSET) + self.localFlyingDropShadow.setPos(shadowPos) + if pos.getZ() < -20 or pos.getZ() > 1000: + self.notify.debug('stopping fly task toon.getZ()=%.2f' % pos.getZ()) + self.__resetToonToCannon(self.localFlyingToon) + return Task.done + self.__setFlyingCameraView(task.info['toon'], cameraView, deltaT) + return Task.cont + + def __setFlyingCameraView(self, toon, view, deltaT): + if toon != base.localAvatar: + return + lookAt = toon.getPos(render) + hpr = toon.getHpr(render) + if view == 0: + camera.wrtReparentTo(render) + camera.lookAt(lookAt) + elif view == 1: + camera.reparentTo(render) + camera.setPos(render, 100, 100, 35.25) + camera.lookAt(render, lookAt) + elif view == 2: + if camera.getParent() != self.camNode: + camera.wrtReparentTo(self.camNode) + camera.setPos(self.cameraPos) + camera.lookAt(toon) + self.camNode.setPos(toon.getPos(render)) + camHpr = self.camNode.getHpr(toon) + vec = -Point3(0, 0, 0) - camHpr + relativeSpeed = math.pow(vec.length() / 60.0, 2) + 0.1 + newHpr = camHpr + vec * deltaT * self.cameraSpeed * relativeSpeed + self.camNode.setHpr(toon, newHpr) + camera.lookAt(self.camNode) + camera.setR(render, 0) + + def __cleanupFlyingToonData(self, toon): + self.notify.debug('__cleanupFlyingToonData') + if toon: + toon.dropShadow.show() + self.toonIds.remove(toon.doId) + if self.isLocalToon(toon): + if self.localFlyingDropShadow != None: + self.localFlyingDropShadow.removeNode() + self.localFlyingDropShadow = None + self.hitBumper = 0 + self.angularVel = 0 + self.vel = Vec3(0, 0, 0) + self.lastVel = Vec3(0, 0, 0) + self.lastPos = Vec3(0, 0, 0) + self.landingPos = Vec3(0, 0, 0) + self.t = 0 + self.lastT = 0 + self.deltaT = 0 + self.lastWakeTime = 0 + self.localFlyingToon = None + self.localFlyingToonId = 0 + self.localFiringCannon = None + if hasattr(self, 'camNode') and self.camNode: + self.camNode.removeNode() + self.camNode = None + return + + def __startCollisionHandler(self): + self.flyColSphere = CollisionSphere(0, 0, self.localFlyingToon.getHeight() / 2.0, 1.0) + self.flyColNode = CollisionNode(self.uniqueName('flySphere')) + self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) + self.flyColNode.addSolid(self.flyColSphere) + self.flyColNodePath = self.localFlyingToon.attachNewNode(self.flyColNode) + self.flyColNodePath.setColor(1, 0, 0, 1) + self._activeCollisions = set() + self.handler = CollisionHandlerQueue() + self._flyingCollisionTaskName = 'checkFlyingToonCollision-%s' % self.doId + taskMgr.add(self._checkFlyingToonCollision, self._flyingCollisionTaskName) + base.cTrav.addCollider(self.flyColNodePath, self.handler) + + def __stopCollisionHandler(self, avatar): + self.notify.debug('%s __stopCollisionHandler' % self.doId) + if self._flyingCollisionTaskName: + taskMgr.remove(self._flyingCollisionTaskName) + self._flyingCollisionTaskName = None + self._activeCollisions = set() + if avatar: + avatar.loop('neutral') + if self.flyColNode: + self.flyColNode = None + self.flyColSphere = None + if self.flyColNodePath: + base.cTrav.removeCollider(self.flyColNodePath) + self.flyColNodePath.removeNode() + self.flyColNodePath = None + self.handler = None + return + + def _checkFlyingToonCollision(self, task = None): + curCollisions = set() + if self.handler.getNumEntries(): + self.handler.sortEntries() + i = self.handler.getNumEntries() + activeEntry = None + while i > 0: + entry = self.handler.getEntry(i - 1) + k = (str(entry.getFromNodePath()), str(entry.getIntoNodePath())) + curCollisions.add(k) + if activeEntry is None and k not in self._activeCollisions: + activeEntry = entry + self._activeCollisions.add(k) + i -= 1 + + if activeEntry is not None: + self.__handleFlyingToonCollision(activeEntry) + if self.handler: + self.handler.clearEntries() + for k in list(self._activeCollisions): + if k not in curCollisions: + self._activeCollisions.remove(k) + + return Task.cont + + def __handleFlyingToonCollision(self, collisionEntry): + self.notify.debug('%s __handleToonCollision' % self.doId) + if self.localFlyingToon == None or self.flyColNode == None: + return + hitNode = collisionEntry.getIntoNode().getName() + self.notify.debug('hitNode = %s' % hitNode) + self.notify.debug('hitNodePath.getParent = %s' % collisionEntry.getIntoNodePath().getParent()) + self.vel = self.trajectory.getVel(self.t) + vel = self.trajectory.getVel(self.t) + vel.normalize() + if self.hitBumper: + vel = self.lastVel * 1 + vel.normalize() + self.notify.debug('normalized vel=%s' % vel) + solid = collisionEntry.getInto() + intoNormal = collisionEntry.getSurfaceNormal(collisionEntry.getIntoNodePath()) + self.notify.debug('old intoNormal = %s' % intoNormal) + intoNormal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('new intoNormal = %s' % intoNormal) + hitPylonAboveWater = False + hitPylonBelowWater = False + hitNormal = intoNormal + if hitNode.find('cSphere') == 0 or hitNode.find('treasureSphere') == 0 or hitNode.find('prop') == 0 or hitNode.find('distAvatarCollNode') == 0 or hitNode.find('CannonSphere') == 0 or hitNode.find('plotSphere') == 0 or hitNode.find('flySphere') == 0 or hitNode.find('FishingSpotSphere') == 0 or hitNode.find('TrampolineTrigger') == 0 or hitNode == 'gagtree_collision' or hitNode == 'sign_collision' or hitNode == 'FlowerSellBox' or hitPylonBelowWater: + self.notify.debug('--------------hit and ignoring %s' % hitNode) + return + if vel.dot(hitNormal) > 0 and not hitNode == 'collision_roof' and not hitNode == 'collision_fence': + self.notify.debug('--------------hit and ignoring backfacing %s, dot=%s' % (hitNode, vel.dot(hitNormal))) + return + intoNode = collisionEntry.getIntoNodePath() + bumperNodes = ['sky_collision'] + PartyCannonCollisions['bounce'] + PartyCannonCollisions['fence'] + cloudBumpers = PartyCannonCollisions['clouds'] + bumperNodes += cloudBumpers + if hitNode in bumperNodes or hitNode.find('cogPie') == 0 or PartyCannonCollisions['trampoline_bounce'] in hitNode: + if hitNode == 'sky_collision' or hitNode in PartyCannonCollisions['fence'] or hitNode.find('cogPie') == 0: + self.__hitFence(self.localFlyingToon, collisionEntry) + elif PartyCannonCollisions['trampoline_bounce'] in hitNode or hitNode in PartyCannonCollisions['bounce']: + if hitNode == 'wall_collision': + hitSound = self.sndBounce2 + else: + hitSound = self.sndBounce3 + self.hitCloud = 1 + self.__hitBumper(self.localFlyingToon, collisionEntry, hitSound, kr=0.09, angVel=5) + self.hitBumper = 0 + elif hitNode in cloudBumpers: + self.__hitCloudPlatform(self.localFlyingToon, collisionEntry) + elif hitNode == 'statuaryCol': + self.__hitStatuary(self.localFlyingToon, collisionEntry) + else: + self.notify.debug('*************** hit something else ************') + return + else: + self.__stopCollisionHandler(self.localFlyingToon) + self.__stopLocalFlyTask(self.localFlyingToonId) + self.notify.debug('stopping flying since we hit %s' % hitNode) + if self.isLocalToonId(self.localFlyingToon.doId): + camera.wrtReparentTo(render) + if self.localFlyingDropShadow: + self.localFlyingDropShadow.reparentTo(hidden) + pos = collisionEntry.getSurfacePoint(render) + hpr = self.localFlyingToon.getHpr() + hitPos = collisionEntry.getSurfacePoint(render) + pos = hitPos + self.landingPos = pos + self.notify.debug('hitNode, Normal = %s,%s' % (hitNode, intoNormal)) + track = Sequence() + track.append(Func(self.localFlyingToon.wrtReparentTo, render)) + if self.isLocalToonId(self.localFlyingToon.doId): + track.append(Func(self.localFlyingToon.collisionsOff)) + if hitNode in PartyCannonCollisions['ground']: + track.append(Func(self.__hitGround, self.localFlyingToon, pos)) + track.append(Wait(1.0)) + track.append(Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) + elif hitNode in PartyCannonCollisions['fence']: + track.append(Func(self.__hitFence, self.localFlyingToon, collisionEntry)) + elif hitNode == 'collision3': + track.append(Func(self.__hitWater, self.localFlyingToon, pos, collisionEntry)) + track.append(Wait(2.0)) + track.append(Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) + elif hitNode.find('cloudSphere') == 0: + track.append(Func(self.__hitCloudPlatform, self.localFlyingToon, collisionEntry)) + else: + self.notify.warning('************* unhandled hitNode=%s parent =%s' % (hitNode, collisionEntry.getIntoNodePath().getParent())) + track.append(Func(self.d_setLanded, self.localFlyingToonId)) + if self.isLocalToonId(self.localFlyingToonId): + track.append(Func(self.localFlyingToon.collisionsOn)) + if self.hitTrack: + self.hitTrack.finish() + self.hitTrack = track + self.hitTrack.start() + return + + def __hitBumper(self, avatar, collisionEntry, sound, kr = 0.6, angVel = 1): + self.hitBumper = 1 + base.playSfx(sound) + hitP = avatar.getPos(render) + self.lastPos = hitP + normal = collisionEntry.getSurfaceNormal(render) + self.notify.debug('normal = %s' % normal) + vel = self.vel * 1 + speed = vel.length() + vel.normalize() + self.notify.debug('old vel = %s' % vel) + if self.hitCloud: + centerVec = Vec3(-avatar.getPos(self.getParentNodePath())) + centerVec.setZ(0) + d = centerVec.length() / 15.0 + centerVec.setZ(abs(centerVec.length() * math.sin(70.0))) + centerVec.normalize() + newVel = centerVec * d + normal * 0.2 + newVel = newVel * (kr * speed) + self.initialFlyVel = kr * speed + else: + newVel = (normal * 2.0 + vel) * (kr * speed) + self.lastVel = newVel + self.notify.debug('new vel = %s' % newVel) + self.angularVel = angVel * 360 + if self.hitCloud: + return + t = Sequence(Func(avatar.pose, 'lose', 110)) + t.start() + + def __hitGround(self, avatar, pos, extraArgs = []): + self.notify.debug('__hitGround') + hitP = avatar.getPos(render) + self.notify.debug('hitGround pos = %s, hitP = %s' % (pos, hitP)) + self.notify.debug('avatar hpr = %s' % avatar.getHpr()) + avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) + avatar.setHpr(avatar.getH(), -135, 0) + self.notify.debug('parent = %s' % avatar.getParent()) + self.notify.debug('pos = %s, hpr = %s' % (avatar.getPos(render), avatar.getHpr(render))) + self.__playDustCloud(avatar, pos) + base.playSfx(self.sndHitGround) + avatar.setPlayRate(2.0, 'run') + avatar.loop('run') + + def __playDustCloud(self, toon, pos): + self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + toon.getHeight() / 3.0) + self.dustCloud.setScale(0.35) + self.dustCloud.play() + + def __hitFence(self, avatar, collisionEntry, extraArgs = []): + self.notify.debug('__hitFence') + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) + + def __hitWater(self, avatar, pos, collisionEntry, extraArgs = []): + hitP = avatar.getPos(render) + if hitP[2] > ToontownGlobals.EstateWakeWaterHeight: + self.notify.debug('we hit the ground before we hit water') + self.__hitGround(avatar, pos, extraArgs) + return + self.notify.debug('hit water') + hitP = avatar.getPos(render) + avatar.loop('neutral') + self.splash.setPos(hitP) + self.splash.setZ(ToontownGlobals.EstateWakeWaterHeight) + self.splash.setScale(2) + self.splash.play() + base.playSfx(self.sndHitWater) + place = base.cr.playGame.getPlace() + + def __hitStatuary(self, avatar, collisionEntry, extraArgs = []): + self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) + + def d_cloudsColorRequest(self): + self.notify.debug('cloudsColorRequest') + self.sendUpdate('cloudsColorRequest') + + def cloudsColorResponse(self, cloudColorList): + self.notify.debug('cloudsColorResponse: %s' % cloudColorList) + for cloudColor in cloudColorList: + self.setCloudHit(*cloudColor) + + def d_requestCloudHit(self, cloudNumber, color): + self.sendUpdate('requestCloudHit', [cloudNumber, + color.getX(), + color.getY(), + color.getZ()]) + + def setCloudHit(self, cloudNumber, r, g, b): + cloud = render.find('**/cloud-%d' % cloudNumber) + if not cloud.isEmpty(): + cloud.setColor(r, g, b, 1.0) + else: + self.notify.debug('Could not find cloud-%d' % cloudNumber) + + def __hitCloudPlatform(self, avatar, collisionEntry, extraArgs = []): + if not self.hitBumper and not self.hitCloud: + self.hitCloud = 1 + self.__hitBumper(avatar, collisionEntry, self.sndBounce1, kr=0.35, angVel=5) + self.hitBumper = 0 + if self._lastCloudHit is None: + cloud = collisionEntry.getIntoNodePath().getParent() + self._lastCloudHit = cloud + cloud.setColor(base.localAvatar.style.getHeadColor()) + cloudNumber = int(cloud.getNetTag('number')) + self.d_requestCloudHit(cloudNumber, base.localAvatar.style.getHeadColor()) + self.__playDustCloud(avatar, collisionEntry.getSurfacePoint(render)) + self.flyingToonCloudsHit += 1 + taskMgr.doMethodLater(0.25, self.__reactivateLastCloudHit, DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) + return + + def __reactivateLastCloudHit(self, task): + self._lastCloudHit = None + return Task.done + + def __handleFireworksStarted(self): + self.notify.debug('__handleFireworksStarted') + base.cr.playGame.hood.loader.fadeClouds() + + def __handleFireworksFinished(self): + self.notify.debug('__handleFireworksFinished') + if self.__checkHoodValidity(): + base.cr.playGame.hood.loader.fadeClouds() + else: + self.notify.debug('Toon has left the party') + + def __checkHoodValidity(self): + if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: + return True + else: + return False + + def handleToonExited(self, toonId): + self.notify.debug('DistributedPartyCannonActivity handleToonExited( toonId=%s ) ' % toonId) + if self.cr.doId2do.has_key(toonId): + self.notify.warning('handleToonExited is not defined') diff --git a/toontown/parties/DistributedPartyCatchActivity.py b/toontown/parties/DistributedPartyCatchActivity.py new file mode 100644 index 0000000..004e03b --- /dev/null +++ b/toontown/parties/DistributedPartyCatchActivity.py @@ -0,0 +1,871 @@ +from pandac.PandaModules import Vec3, Point3, Point4, TextNode, NodePath +from pandac.PandaModules import CollisionHandlerEvent, CollisionNode, CollisionSphere +from direct.distributed.ClockDelta import globalClockDelta +from direct.interval.IntervalGlobal import Sequence, Parallel +from direct.interval.IntervalGlobal import LerpScaleInterval, LerpFunctionInterval, LerpColorScaleInterval, LerpPosInterval +from direct.interval.IntervalGlobal import SoundInterval, WaitInterval +from direct.showbase.PythonUtil import Functor, bound, lerp, SerialNumGen +from direct.showbase.RandomNumGen import RandomNumGen +from direct.task.Task import Task +from direct.distributed import DistributedSmoothNode +from direct.directnotify import DirectNotifyGlobal +from direct.interval.FunctionInterval import Wait, Func +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +from toontown.toonbase import ToontownGlobals +from toontown.minigame.Trajectory import Trajectory +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.minigame.DropPlacer import PartyRegionDropPlacer +from toontown.parties import PartyGlobals +from toontown.parties.PartyCatchActivityToonSD import PartyCatchActivityToonSD +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.DistributedPartyCatchActivityBase import DistributedPartyCatchActivityBase +from toontown.parties.DistributedPartyCannonActivity import DistributedPartyCannonActivity +from toontown.parties.activityFSMs import CatchActivityFSM + +class DistributedPartyCatchActivity(DistributedPartyActivity, DistributedPartyCatchActivityBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCatchActivity') + DropTaskName = 'dropSomething' + DropObjectPlurals = {'apple': TTLocalizer.PartyCatchActivityApples, + 'orange': TTLocalizer.PartyCatchActivityOranges, + 'pear': TTLocalizer.PartyCatchActivityPears, + 'coconut': TTLocalizer.PartyCatchActivityCoconuts, + 'watermelon': TTLocalizer.PartyCatchActivityWatermelons, + 'pineapple': TTLocalizer.PartyCatchActivityPineapples, + 'anvil': TTLocalizer.PartyCatchActivityAnvils} + + class Generation: + + def __init__(self, generation, startTime, startNetworkTime, numPlayers): + self.generation = generation + self.startTime = startTime + self.startNetworkTime = startNetworkTime + self.numPlayers = numPlayers + self.hasBeenScheduled = False + self.droppedObjNames = [] + self.dropSchedule = [] + self.numItemsDropped = 0 + self.droppedObjCaught = {} + + def __init__(self, cr): + DistributedPartyActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyCatch, PartyGlobals.ActivityTypes.HostInitiated, wantRewardGui=True) + self.setUsesSmoothing() + self.setUsesLookAround() + self._sNumGen = SerialNumGen() + + def getTitle(self): + return TTLocalizer.PartyCatchActivityTitle + + def getInstructions(self): + return TTLocalizer.PartyCatchActivityInstructions % {'badThing': self.DropObjectPlurals['anvil']} + + def generate(self): + DistributedPartyActivity.generate(self) + self.notify.info('localAvatar doId: %s' % base.localAvatar.doId) + self.notify.info('generate()') + self._generateFrame = globalClock.getFrameCount() + self._id2gen = {} + self._orderedGenerations = [] + self._orderedGenerationIndex = None + rng = RandomNumGen(self.doId) + self._generationSeedBase = rng.randrange(1000) + self._lastDropTime = 0.0 + return + + def getCurGeneration(self): + if self._orderedGenerationIndex is None: + return + return self._orderedGenerations[self._orderedGenerationIndex] + + def _addGeneration(self, generation, startTime, startNetworkTime, numPlayers): + self._id2gen[generation] = self.Generation(generation, startTime, startNetworkTime, numPlayers) + i = 0 + while 1: + if i >= len(self._orderedGenerations): + break + gen = self._orderedGenerations[i] + startNetT = self._id2gen[gen].startTime + genId = self._id2gen[gen].generation + if startNetT > startNetworkTime: + break + if startNetT == startNetworkTime and genId > generation: + break + i += 1 + self._orderedGenerations = self._orderedGenerations[:i] + [generation] + self._orderedGenerations[i:] + if self._orderedGenerationIndex is not None: + if self._orderedGenerationIndex >= i: + self._orderedGenerationIndex += 1 + + def _removeGeneration(self, generation): + del self._id2gen[generation] + i = self._orderedGenerations.index(generation) + self._orderedGenerations = self._orderedGenerations[:i] + self._orderedGenerations[i + 1:] + if self._orderedGenerationIndex is not None: + if len(self._orderedGenerations): + if self._orderedGenerationIndex >= i: + self._orderedGenerationIndex -= 1 + else: + self._orderedGenerationIndex = None + return + + def announceGenerate(self): + self.notify.info('announceGenerate()') + self.catchTreeZoneEvent = 'fence_floor' + DistributedPartyActivity.announceGenerate(self) + + def load(self, loadModels = 1, arenaModel = 'partyCatchTree'): + self.notify.info('load()') + DistributedPartyCatchActivity.notify.debug('PartyCatch: load') + self.activityFSM = CatchActivityFSM(self) + if __dev__: + for o in xrange(3): + print {0: 'SPOTS PER PLAYER', + 1: 'DROPS PER MINUTE PER SPOT DURING NORMAL DROP PERIOD', + 2: 'DROPS PER MINUTE PER PLAYER DURING NORMAL DROP PERIOD'}[o] + for i in xrange(1, self.FallRateCap_Players + 10): + self.defineConstants(forceNumPlayers=i) + numDropLocations = self.DropRows * self.DropColumns + numDropsPerMin = 60.0 / self.DropPeriod + if o == 0: + spotsPerPlayer = numDropLocations / float(i) + print '%2d PLAYERS: %s' % (i, spotsPerPlayer) + elif o == 1: + numDropsPerMinPerSpot = numDropsPerMin / numDropLocations + print '%2d PLAYERS: %s' % (i, numDropsPerMinPerSpot) + elif i > 0: + numDropsPerMinPerPlayer = numDropsPerMin / i + print '%2d PLAYERS: %s' % (i, numDropsPerMinPerPlayer) + + self.defineConstants() + self.treesAndFence = loader.loadModel('phase_13/models/parties/%s' % arenaModel) + self.treesAndFence.setScale(0.9) + self.treesAndFence.find('**/fence_floor').setPos(0.0, 0.0, 0.1) + self.treesAndFence.reparentTo(self.root) + ground = self.treesAndFence.find('**/groundPlane') + ground.setBin('ground', 1) + DistributedPartyActivity.load(self) + exitText = TextNode('PartyCatchExitText') + exitText.setCardAsMargin(0.1, 0.1, 0.1, 0.1) + exitText.setCardDecal(True) + exitText.setCardColor(1.0, 1.0, 1.0, 0.0) + exitText.setText(TTLocalizer.PartyCatchActivityExit) + exitText.setTextColor(0.0, 8.0, 0.0, 0.9) + exitText.setAlign(exitText.ACenter) + exitText.setFont(ToontownGlobals.getBuildingNametagFont()) + exitText.setShadowColor(0, 0, 0, 1) + exitText.setBin('fixed') + if TTLocalizer.BuildingNametagShadow: + exitText.setShadow(*TTLocalizer.BuildingNametagShadow) + exitTextLoc = self.treesAndFence.find('**/loc_exitSignText') + exitTextNp = exitTextLoc.attachNewNode(exitText) + exitTextNp.setDepthWrite(0) + exitTextNp.setScale(4) + exitTextNp.setZ(-.5) + self.sign.reparentTo(self.treesAndFence.find('**/loc_eventSign')) + self.sign.wrtReparentTo(self.root) + self.avatarNodePath = NodePath('PartyCatchAvatarNodePath') + self.avatarNodePath.reparentTo(self.root) + self._avatarNodePathParentToken = 3 + base.cr.parentMgr.registerParent(self._avatarNodePathParentToken, self.avatarNodePath) + self.toonSDs = {} + self.dropShadow = loader.loadModelOnce('phase_3/models/props/drop_shadow') + self.dropObjModels = {} + if loadModels: + self.__loadDropModels() + self.sndGoodCatch = base.loadSfx('phase_4/audio/sfx/SZ_DD_treasure.mp3') + self.sndOof = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + self.sndAnvilLand = base.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.mp3') + self.sndPerfect = base.loadSfx('phase_4/audio/sfx/ring_perfect.mp3') + self.__textGen = TextNode('partyCatchActivity') + self.__textGen.setFont(ToontownGlobals.getSignFont()) + self.__textGen.setAlign(TextNode.ACenter) + self.activityFSM.request('Idle') + + def __loadDropModels(self): + for objType in PartyGlobals.DropObjectTypes: + model = loader.loadModel(objType.modelPath) + self.dropObjModels[objType.name] = model + modelScales = {'apple': 0.7, + 'orange': 0.7, + 'pear': 0.5, + 'coconut': 0.7, + 'watermelon': 0.6, + 'pineapple': 0.45} + if modelScales.has_key(objType.name): + model.setScale(modelScales[objType.name]) + if objType == PartyGlobals.Name2DropObjectType['pear']: + model.setZ(-.6) + if objType == PartyGlobals.Name2DropObjectType['coconut']: + model.setP(180) + if objType == PartyGlobals.Name2DropObjectType['watermelon']: + model.setH(135) + model.setZ(-.5) + if objType == PartyGlobals.Name2DropObjectType['pineapple']: + model.setZ(-1.7) + if objType == PartyGlobals.Name2DropObjectType['anvil']: + model.setZ(-self.ObjRadius) + model.flattenStrong() + + def unload(self): + DistributedPartyCatchActivity.notify.debug('unload') + self.finishAllDropIntervals() + self.destroyOrthoWalk() + DistributedPartyActivity.unload(self) + self.stopDropTask() + del self.activityFSM + del self.__textGen + for avId in self.toonSDs.keys(): + if self.toonSDs.has_key(avId): + toonSD = self.toonSDs[avId] + toonSD.unload() + + del self.toonSDs + self.treesAndFence.removeNode() + del self.treesAndFence + self.dropShadow.removeNode() + del self.dropShadow + base.cr.parentMgr.unregisterParent(self._avatarNodePathParentToken) + for model in self.dropObjModels.values(): + model.removeNode() + + del self.dropObjModels + del self.sndGoodCatch + del self.sndOof + del self.sndAnvilLand + del self.sndPerfect + + def setStartTimestamp(self, timestamp32): + self.notify.info('setStartTimestamp(%s)' % (timestamp32,)) + self._startTimestamp = globalClockDelta.networkToLocalTime(timestamp32, bits=32) + + def getCurrentCatchActivityTime(self): + return globalClock.getFrameTime() - self._startTimestamp + + def getObjModel(self, objName): + return self.dropObjModels[objName].copyTo(hidden) + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + base.cr.playGame.getPlace().fsm.request('walk') + + def handleToonJoined(self, toonId): + if not self.toonSDs.has_key(toonId): + toonSD = PartyCatchActivityToonSD(toonId, self) + self.toonSDs[toonId] = toonSD + toonSD.load() + self.notify.debug('handleToonJoined : currentState = %s' % self.activityFSM.state) + self.cr.doId2do[toonId].useLOD(500) + if self.activityFSM.state == 'Active': + if self.toonSDs.has_key(toonId): + self.toonSDs[toonId].enter() + if base.localAvatar.doId == toonId: + base.localAvatar.b_setParent(self._avatarNodePathParentToken) + self.putLocalAvatarInActivity() + if self.toonSDs.has_key(toonId): + self.toonSDs[toonId].fsm.request('rules') + + def handleToonExited(self, toonId): + self.notify.debug('handleToonExited( toonId=%s )' % toonId) + if self.cr.doId2do.has_key(toonId): + self.cr.doId2do[toonId].resetLOD() + if self.toonSDs.has_key(toonId): + self.toonSDs[toonId].fsm.request('notPlaying') + self.toonSDs[toonId].exit() + self.toonSDs[toonId].unload() + del self.toonSDs[toonId] + if base.localAvatar.doId == toonId: + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def takeLocalAvatarOutOfActivity(self): + self.notify.debug('localToon has left the circle') + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.enableSmartCameraViews() + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + DistributedSmoothNode.activateSmoothing(1, 0) + + def _enableCollisions(self): + DistributedPartyActivity._enableCollisions(self) + self._enteredTree = False + self.accept('enter' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree) + self.accept('again' + self.catchTreeZoneEvent, self._toonMayHaveEnteredTree) + self.accept('exit' + self.catchTreeZoneEvent, self._toonExitedTree) + self.accept(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT, self._handleCannonLanded) + + def _disableCollisions(self): + self.ignore(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT) + self.ignore('enter' + self.catchTreeZoneEvent) + self.ignore('again' + self.catchTreeZoneEvent) + self.ignore('exit' + self.catchTreeZoneEvent) + DistributedPartyActivity._disableCollisions(self) + + def _handleCannonLanded(self): + x = base.localAvatar.getX() + y = base.localAvatar.getY() + if x > self.x - self.StageHalfWidth and x < self.x + self.StageHalfWidth and y > self.y - self.StageHalfHeight and y < self.y + self.StageHalfHeight: + self._toonEnteredTree(None) + return + + def _toonMayHaveEnteredTree(self, collEntry): + if self._enteredTree: + return + if base.localAvatar.controlManager.currentControls.getIsAirborne(): + return + self._toonEnteredTree(collEntry) + + def _toonEnteredTree(self, collEntry): + self.notify.debug('_toonEnteredTree : avid = %s' % base.localAvatar.doId) + self.notify.debug('_toonEnteredTree : currentState = %s' % self.activityFSM.state) + if self.isLocalToonInActivity(): + return + if self.activityFSM.state == 'Active': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + elif self.activityFSM.state == 'Idle': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + self._enteredTree = True + + def _toonExitedTree(self, collEntry): + self.notify.debug('_toonExitedTree : avid = %s' % base.localAvatar.doId) + self._enteredTree = False + if hasattr(base.cr.playGame.getPlace(), 'fsm') and self.activityFSM.state == 'Active' and self.isLocalToonInActivity(): + if self.toonSDs.has_key(base.localAvatar.doId): + self.takeLocalAvatarOutOfActivity() + self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying') + self.d_toonExitDemand() + + def setToonsPlaying(self, toonIds): + self.notify.info('setToonsPlaying(%s)' % (toonIds,)) + DistributedPartyActivity.setToonsPlaying(self, toonIds) + if self.isLocalToonInActivity() and base.localAvatar.doId not in toonIds: + if self.toonSDs.has_key(base.localAvatar.doId): + self.takeLocalAvatarOutOfActivity() + self.toonSDs[base.localAvatar.doId].fsm.request('notPlaying') + + def __genText(self, text): + self.__textGen.setText(text) + return self.__textGen.generate() + + def getNumPlayers(self): + return len(self.toonIds) + + def defineConstants(self, forceNumPlayers = None): + DistributedPartyCatchActivity.notify.debug('defineConstants') + self.ShowObjSpheres = 0 + self.ShowToonSpheres = 0 + self.useGravity = True + self.trickShadows = True + if forceNumPlayers is None: + numPlayers = self.getNumPlayers() + else: + numPlayers = forceNumPlayers + self.calcDifficultyConstants(numPlayers) + DistributedPartyCatchActivity.notify.debug('ToonSpeed: %s' % self.ToonSpeed) + DistributedPartyCatchActivity.notify.debug('total drops: %s' % self.totalDrops) + DistributedPartyCatchActivity.notify.debug('numFruits: %s' % self.numFruits) + DistributedPartyCatchActivity.notify.debug('numAnvils: %s' % self.numAnvils) + self.ObjRadius = 1.0 + dropRegionTable = PartyRegionDropPlacer.getDropRegionTable(numPlayers) + self.DropRows, self.DropColumns = len(dropRegionTable), len(dropRegionTable[0]) + for objType in PartyGlobals.DropObjectTypes: + DistributedPartyCatchActivity.notify.debug('*** Object Type: %s' % objType.name) + objType.onscreenDuration = objType.onscreenDurMult * self.BaselineOnscreenDropDuration + DistributedPartyCatchActivity.notify.debug('onscreenDuration=%s' % objType.onscreenDuration) + v_0 = 0.0 + t = objType.onscreenDuration + x_0 = self.MinOffscreenHeight + x = 0.0 + g = 2.0 * (x - x_0 - v_0 * t) / (t * t) + DistributedPartyCatchActivity.notify.debug('gravity=%s' % g) + objType.trajectory = Trajectory(0, Vec3(0, 0, x_0), Vec3(0, 0, v_0), gravMult=abs(g / Trajectory.gravity)) + objType.fallDuration = objType.onscreenDuration + self.OffscreenTime + + return + + def grid2world(self, column, row): + x = column / float(self.DropColumns - 1) + y = row / float(self.DropRows - 1) + x = x * 2.0 - 1.0 + y = y * 2.0 - 1.0 + x *= self.StageHalfWidth + y *= self.StageHalfHeight + return (x, y) + + def showPosts(self): + self.hidePosts() + self.posts = [Toon.Toon(), + Toon.Toon(), + Toon.Toon(), + Toon.Toon()] + for i in range(len(self.posts)): + tree = self.posts[i] + tree.reparentTo(render) + x = self.StageHalfWidth + y = self.StageHalfHeight + if i > 1: + x = -x + if i % 2: + y = -y + tree.setPos(x + self.x, y + self.y, 0) + + def hidePosts(self): + if hasattr(self, 'posts'): + for tree in self.posts: + tree.removeNode() + + del self.posts + + def showDropGrid(self): + self.hideDropGrid() + self.dropMarkers = [] + for row in range(self.DropRows): + self.dropMarkers.append([]) + rowList = self.dropMarkers[row] + for column in range(self.DropColumns): + toon = Toon.Toon() + toon.setDNA(base.localAvatar.getStyle()) + toon.reparentTo(self.root) + toon.setScale(1.0 / 3) + x, y = self.grid2world(column, row) + toon.setPos(x, y, 0) + rowList.append(toon) + + def hideDropGrid(self): + if hasattr(self, 'dropMarkers'): + for row in self.dropMarkers: + for marker in row: + marker.removeNode() + + del self.dropMarkers + + def handleToonDisabled(self, avId): + DistributedPartyCatchActivity.notify.debug('handleToonDisabled') + DistributedPartyCatchActivity.notify.debug('avatar ' + str(avId) + ' disabled') + if self.toonSDs.has_key(avId): + self.toonSDs[avId].exit(unexpectedExit=True) + del self.toonSDs[avId] + + def turnOffSmoothingOnGuests(self): + pass + + def setState(self, newState, timestamp): + self.notify.info('setState(%s, %s)' % (newState, timestamp)) + DistributedPartyCatchActivity.notify.debug('setState( newState=%s, ... )' % newState) + DistributedPartyActivity.setState(self, newState, timestamp) + self.activityFSM.request(newState) + if newState == 'Active': + if base.localAvatar.doId != self.party.partyInfo.hostId: + if globalClock.getFrameCount() > self._generateFrame: + if base.localAvatar.getX() > self.x - self.StageHalfWidth and base.localAvatar.getX() < self.x + self.StageHalfWidth and base.localAvatar.getY() > self.y - self.StageHalfHeight and base.localAvatar.getY() < self.y + self.StageHalfHeight: + self._toonEnteredTree(None) + return + + def putLocalAvatarInActivity(self): + if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'): + base.cr.playGame.getPlace().fsm.request('activity', [False]) + else: + self.notify.info("Avoided crash: toontown.parties.DistributedPartyCatchActivity:632, toontown.parties.DistributedPartyCatchActivity:1198, toontown.parties.activityFSMMixins:49, direct.fsm.FSM:423, AttributeError: 'NoneType' object has no attribute 'fsm'") + base.localAvatar.stopUpdateSmartCamera() + camera.reparentTo(self.treesAndFence) + camera.setPosHpr(0.0, -63.0, 30.0, 0.0, -20.0, 0.0) + if not hasattr(self, 'ltLegsCollNode'): + self.createCatchCollisions() + + def createCatchCollisions(self): + radius = 0.7 + handler = CollisionHandlerEvent() + handler.setInPattern('ltCatch%in') + self.ltLegsCollNode = CollisionNode('catchLegsCollNode') + self.ltLegsCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + self.ltHeadCollNode = CollisionNode('catchHeadCollNode') + self.ltHeadCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + self.ltLHandCollNode = CollisionNode('catchLHandCollNode') + self.ltLHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + self.ltRHandCollNode = CollisionNode('catchRHandCollNode') + self.ltRHandCollNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + legsCollNodepath = base.localAvatar.attachNewNode(self.ltLegsCollNode) + legsCollNodepath.hide() + head = base.localAvatar.getHeadParts().getPath(2) + headCollNodepath = head.attachNewNode(self.ltHeadCollNode) + headCollNodepath.hide() + lHand = base.localAvatar.getLeftHands()[0] + lHandCollNodepath = lHand.attachNewNode(self.ltLHandCollNode) + lHandCollNodepath.hide() + rHand = base.localAvatar.getRightHands()[0] + rHandCollNodepath = rHand.attachNewNode(self.ltRHandCollNode) + rHandCollNodepath.hide() + base.localAvatar.cTrav.addCollider(legsCollNodepath, handler) + base.localAvatar.cTrav.addCollider(headCollNodepath, handler) + base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler) + base.localAvatar.cTrav.addCollider(lHandCollNodepath, handler) + if self.ShowToonSpheres: + legsCollNodepath.show() + headCollNodepath.show() + lHandCollNodepath.show() + rHandCollNodepath.show() + self.ltLegsCollNode.addSolid(CollisionSphere(0, 0, radius, radius)) + self.ltHeadCollNode.addSolid(CollisionSphere(0, 0, 0, radius)) + self.ltLHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.ltRHandCollNode.addSolid(CollisionSphere(0, 0, 0, 2 * radius / 3.0)) + self.toonCollNodes = [legsCollNodepath, + headCollNodepath, + lHandCollNodepath, + rHandCollNodepath] + + def destroyCatchCollisions(self): + if not hasattr(self, 'ltLegsCollNode'): + return + for collNode in self.toonCollNodes: + while collNode.node().getNumSolids(): + collNode.node().removeSolid(0) + + base.localAvatar.cTrav.removeCollider(collNode) + + del self.toonCollNodes + del self.ltLegsCollNode + del self.ltHeadCollNode + del self.ltLHandCollNode + del self.ltRHandCollNode + + def timerExpired(self): + pass + + def __handleCatch(self, generation, objNum): + DistributedPartyCatchActivity.notify.debug('catch: %s' % [generation, objNum]) + if base.localAvatar.doId not in self.toonIds: + return + self.showCatch(base.localAvatar.doId, generation, objNum) + objName = self._id2gen[generation].droppedObjNames[objNum] + objTypeId = PartyGlobals.Name2DOTypeId[objName] + self.sendUpdate('claimCatch', [generation, objNum, objTypeId]) + self.finishDropInterval(generation, objNum) + + def showCatch(self, avId, generation, objNum): + if not self.toonSDs.has_key(avId): + return + isLocal = avId == base.localAvatar.doId + if generation not in self._id2gen: + return + if not self._id2gen[generation].hasBeenScheduled: + return + objName = self._id2gen[generation].droppedObjNames[objNum] + objType = PartyGlobals.Name2DropObjectType[objName] + if objType.good: + if not self._id2gen[generation].droppedObjCaught.has_key(objNum): + if isLocal: + base.playSfx(self.sndGoodCatch) + fruit = self.getObjModel(objName) + toon = self.getAvatar(avId) + rHand = toon.getRightHands()[1] + self.toonSDs[avId].eatFruit(fruit, rHand) + else: + self.toonSDs[avId].fsm.request('fallForward') + self._id2gen[generation].droppedObjCaught[objNum] = 1 + + def setObjectCaught(self, avId, generation, objNum): + self.notify.info('setObjectCaught(%s, %s, %s)' % (avId, generation, objNum)) + if self.activityFSM.state != 'Active': + DistributedPartyCatchActivity.notify.warning('ignoring msg: object %s caught by %s' % (objNum, avId)) + return + isLocal = avId == base.localAvatar.doId + if not isLocal: + DistributedPartyCatchActivity.notify.debug('AI: avatar %s caught %s' % (avId, objNum)) + self.finishDropInterval(generation, objNum) + self.showCatch(avId, generation, objNum) + self._scheduleGenerations() + gen = self._id2gen[generation] + if gen.hasBeenScheduled: + objName = gen.droppedObjNames[objNum] + if PartyGlobals.Name2DropObjectType[objName].good: + if hasattr(self, 'fruitsCaught'): + self.fruitsCaught += 1 + + def finishDropInterval(self, generation, objNum): + if hasattr(self, 'dropIntervals'): + if self.dropIntervals.has_key((generation, objNum)): + self.dropIntervals[generation, objNum].finish() + + def finishAllDropIntervals(self): + if hasattr(self, 'dropIntervals'): + for dropInterval in self.dropIntervals.values(): + dropInterval.finish() + + def setGenerations(self, generations): + self.notify.info('setGenerations(%s)' % (generations,)) + gen2t = {} + gen2nt = {} + gen2np = {} + for id, timestamp32, numPlayers in generations: + gen2t[id] = globalClockDelta.networkToLocalTime(timestamp32, bits=32) - self._startTimestamp + gen2nt[id] = timestamp32 + gen2np[id] = numPlayers + + ids = self._id2gen.keys() + for id in ids: + if id not in gen2t: + self._removeGeneration(id) + + for id in gen2t: + if id not in self._id2gen: + self._addGeneration(id, gen2t[id], gen2nt[id], gen2np[id]) + + def scheduleDrops(self, genId = None): + if genId is None: + genId = self.getCurGeneration() + gen = self._id2gen[genId] + if gen.hasBeenScheduled: + return + fruitIndex = int((gen.startTime + 0.5 * self.DropPeriod) / PartyGlobals.CatchActivityDuration) + fruitNames = ['apple', + 'orange', + 'pear', + 'coconut', + 'watermelon', + 'pineapple'] + fruitName = fruitNames[fruitIndex % len(fruitNames)] + rng = RandomNumGen(genId + self._generationSeedBase) + gen.droppedObjNames = [fruitName] * self.numFruits + ['anvil'] * self.numAnvils + rng.shuffle(gen.droppedObjNames) + dropPlacer = PartyRegionDropPlacer(self, gen.numPlayers, genId, gen.droppedObjNames, startTime=gen.startTime) + gen.numItemsDropped = 0 + tIndex = gen.startTime % PartyGlobals.CatchActivityDuration + tPercent = float(tIndex) / PartyGlobals.CatchActivityDuration + gen.numItemsDropped += dropPlacer.skipPercent(tPercent) + while not dropPlacer.doneDropping(continuous=True): + nextDrop = dropPlacer.getNextDrop() + gen.dropSchedule.append(nextDrop) + + gen.hasBeenScheduled = True + return + + def startDropTask(self): + taskMgr.add(self.dropTask, self.DropTaskName) + + def stopDropTask(self): + taskMgr.remove(self.DropTaskName) + + def _scheduleGenerations(self): + curT = self.getCurrentCatchActivityTime() + genIndex = self._orderedGenerationIndex + newGenIndex = genIndex + while genIndex is None or genIndex < len(self._orderedGenerations) - 1: + if genIndex is None: + nextGenIndex = 0 + else: + nextGenIndex = genIndex + 1 + nextGenId = self._orderedGenerations[nextGenIndex] + nextGen = self._id2gen[nextGenId] + startT = nextGen.startTime + if curT >= startT: + newGenIndex = nextGenIndex + if not nextGen.hasBeenScheduled: + self.defineConstants(forceNumPlayers=nextGen.numPlayers) + self.scheduleDrops(genId=self._orderedGenerations[nextGenIndex]) + genIndex = nextGenIndex + + self._orderedGenerationIndex = newGenIndex + return + + def dropTask(self, task): + self._scheduleGenerations() + curT = self.getCurrentCatchActivityTime() + if self._orderedGenerationIndex is not None: + i = self._orderedGenerationIndex + genIndex = self._orderedGenerations[i] + gen = self._id2gen[genIndex] + while len(gen.dropSchedule) > 0 and gen.dropSchedule[0][0] < curT: + drop = gen.dropSchedule[0] + gen.dropSchedule = gen.dropSchedule[1:] + dropTime, objName, dropCoords = drop + objNum = gen.numItemsDropped + x, y = self.grid2world(*dropCoords) + dropIval = self.getDropIval(x, y, objName, genIndex, objNum) + + def cleanup(generation, objNum, self = self): + del self.dropIntervals[generation, objNum] + + dropIval.append(Func(Functor(cleanup, genIndex, objNum))) + self.dropIntervals[genIndex, objNum] = dropIval + gen.numItemsDropped += 1 + dropIval.start(curT - dropTime) + self._lastDropTime = dropTime + + return Task.cont + + def getDropIval(self, x, y, dropObjName, generation, num): + objType = PartyGlobals.Name2DropObjectType[dropObjName] + id = (generation, num) + dropNode = hidden.attachNewNode('catchDropNode%s' % (id,)) + dropNode.setPos(x, y, 0) + shadow = self.dropShadow.copyTo(dropNode) + shadow.setZ(PartyGlobals.CatchDropShadowHeight) + shadow.setColor(1, 1, 1, 1) + object = self.getObjModel(dropObjName) + object.reparentTo(hidden) + if dropObjName in ['watermelon', 'anvil']: + objH = object.getH() + absDelta = {'watermelon': 12, + 'anvil': 15}[dropObjName] + delta = (self.randomNumGen.random() * 2.0 - 1.0) * absDelta + newH = objH + delta + else: + newH = self.randomNumGen.random() * 360.0 + object.setH(newH) + sphereName = 'FallObj%s' % (id,) + radius = self.ObjRadius + if objType.good: + radius *= lerp(1.0, 1.3, 0.5) + collSphere = CollisionSphere(0, 0, 0, radius) + collSphere.setTangible(0) + collNode = CollisionNode(sphereName) + collNode.setCollideMask(PartyGlobals.CatchActivityBitmask) + collNode.addSolid(collSphere) + collNodePath = object.attachNewNode(collNode) + collNodePath.hide() + if self.ShowObjSpheres: + collNodePath.show() + catchEventName = 'ltCatch' + sphereName + + def eatCollEntry(forward, collEntry): + forward() + + self.accept(catchEventName, Functor(eatCollEntry, Functor(self.__handleCatch, id[0], id[1]))) + + def cleanup(self = self, dropNode = dropNode, id = id, event = catchEventName): + self.ignore(event) + dropNode.removeNode() + + duration = objType.fallDuration + onscreenDuration = objType.onscreenDuration + targetShadowScale = 0.3 + if self.trickShadows: + intermedScale = targetShadowScale * (self.OffscreenTime / self.BaselineDropDuration) + shadowScaleIval = Sequence(LerpScaleInterval(shadow, self.OffscreenTime, intermedScale, startScale=0)) + shadowScaleIval.append(LerpScaleInterval(shadow, duration - self.OffscreenTime, targetShadowScale, startScale=intermedScale)) + else: + shadowScaleIval = LerpScaleInterval(shadow, duration, targetShadowScale, startScale=0) + targetShadowAlpha = 0.4 + shadowAlphaIval = LerpColorScaleInterval(shadow, self.OffscreenTime, Point4(1, 1, 1, targetShadowAlpha), startColorScale=Point4(1, 1, 1, 0)) + shadowIval = Parallel(shadowScaleIval, shadowAlphaIval) + if self.useGravity: + + def setObjPos(t, objType = objType, object = object): + z = objType.trajectory.calcZ(t) + object.setZ(z) + + setObjPos(0) + dropIval = LerpFunctionInterval(setObjPos, fromData=0, toData=onscreenDuration, duration=onscreenDuration) + else: + startPos = Point3(0, 0, self.MinOffscreenHeight) + object.setPos(startPos) + dropIval = LerpPosInterval(object, onscreenDuration, Point3(0, 0, 0), startPos=startPos, blendType='easeIn') + ival = Sequence(Func(Functor(dropNode.reparentTo, self.root)), Parallel(Sequence(WaitInterval(self.OffscreenTime), Func(Functor(object.reparentTo, dropNode)), dropIval), shadowIval), Func(cleanup), name='drop%s' % (id,)) + if objType == PartyGlobals.Name2DropObjectType['anvil']: + ival.append(Func(self.playAnvil)) + return ival + + def playAnvil(self): + if base.localAvatar.doId in self.toonIds: + base.playSfx(self.sndAnvilLand) + + def initOrthoWalk(self): + DistributedPartyCatchActivity.notify.debug('startOrthoWalk') + + def doCollisions(oldPos, newPos, self = self): + x = bound(newPos[0], self.StageHalfWidth, -self.StageHalfWidth) + y = bound(newPos[1], self.StageHalfHeight, -self.StageHalfHeight) + newPos.setX(x) + newPos.setY(y) + return newPos + + orthoDrive = OrthoDrive(self.ToonSpeed, instantTurn=True) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True) + + def destroyOrthoWalk(self): + DistributedPartyCatchActivity.notify.debug('destroyOrthoWalk') + if hasattr(self, 'orthoWalk'): + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + + def startIdle(self): + DistributedPartyCatchActivity.notify.debug('startIdle') + + def finishIdle(self): + DistributedPartyCatchActivity.notify.debug('finishIdle') + + def startActive(self): + DistributedPartyCatchActivity.notify.debug('startActive') + for avId in self.toonIds: + if self.toonSDs.has_key(avId): + toonSD = self.toonSDs[avId] + toonSD.enter() + toonSD.fsm.request('normal') + + self.fruitsCaught = 0 + self.dropIntervals = {} + self.startDropTask() + if base.localAvatar.doId in self.toonIds: + self.putLocalAvatarInActivity() + + def finishActive(self): + DistributedPartyCatchActivity.notify.debug('finishActive') + self.stopDropTask() + if hasattr(self, 'finishIval'): + self.finishIval.pause() + del self.finishIval + if base.localAvatar.doId in self.toonIds: + self.takeLocalAvatarOutOfActivity() + for ival in self.dropIntervals.values(): + ival.finish() + + del self.dropIntervals + + def startConclusion(self): + DistributedPartyCatchActivity.notify.debug('startConclusion') + for avId in self.toonIds: + if self.toonSDs.has_key(avId): + toonSD = self.toonSDs[avId] + toonSD.fsm.request('notPlaying') + + self.destroyCatchCollisions() + if base.localAvatar.doId not in self.toonIds: + return + else: + self.localToonExiting() + if self.fruitsCaught >= self.numFruits: + finishText = TTLocalizer.PartyCatchActivityFinishPerfect + else: + finishText = TTLocalizer.PartyCatchActivityFinish + perfectTextSubnode = hidden.attachNewNode(self.__genText(finishText)) + perfectText = hidden.attachNewNode('perfectText') + perfectTextSubnode.reparentTo(perfectText) + frame = self.__textGen.getCardActual() + offsetY = -abs(frame[2] + frame[3]) / 2.0 + perfectTextSubnode.setPos(0, 0, offsetY) + perfectText.setColor(1, 0.1, 0.1, 1) + + def fadeFunc(t, text = perfectText): + text.setColorScale(1, 1, 1, t) + + def destroyText(text = perfectText): + text.removeNode() + + textTrack = Sequence(Func(perfectText.reparentTo, aspect2d), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=0.3, startScale=0.0), LerpFunctionInterval(fadeFunc, fromData=0.0, toData=1.0, duration=0.5)), Wait(2.0), Parallel(LerpScaleInterval(perfectText, duration=0.5, scale=1.0), LerpFunctionInterval(fadeFunc, fromData=1.0, toData=0.0, duration=0.5, blendType='easeIn')), Func(destroyText), WaitInterval(0.5)) + soundTrack = SoundInterval(self.sndPerfect) + self.finishIval = Parallel(textTrack, soundTrack) + self.finishIval.start() + + def finishConclusion(self): + DistributedPartyCatchActivity.notify.debug('finishConclusion') + if base.localAvatar.doId in self.toonIds: + self.takeLocalAvatarOutOfActivity() + base.cr.playGame.getPlace().fsm.request('walk') + + def showJellybeanReward(self, earnedAmount, jarAmount, message): + if earnedAmount > 0: + DistributedPartyActivity.showJellybeanReward(self, earnedAmount, jarAmount, message) + else: + base.cr.playGame.getPlace().fsm.request('walk') diff --git a/toontown/parties/DistributedPartyCatchActivityBase.py b/toontown/parties/DistributedPartyCatchActivityBase.py new file mode 100644 index 0000000..66730f0 --- /dev/null +++ b/toontown/parties/DistributedPartyCatchActivityBase.py @@ -0,0 +1,60 @@ +import math +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame.DropScheduler import ThreePhaseDropScheduler +from toontown.parties import PartyGlobals + +class DistributedPartyCatchActivityBase: + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCatchActivityBase') + FallRateCap_Players = 20 + + def calcDifficultyConstants(self, numPlayers): + self.FirstDropDelay = 0.0 + self.NormalDropDelay = int(1.0 / 12 * PartyGlobals.CatchActivityDuration) + self.FasterDropDelay = int(9.0 / 12 * PartyGlobals.CatchActivityDuration) + DistributedPartyCatchActivityBase.notify.debug('will start dropping fast after %s seconds' % self.FasterDropDelay) + self.SlowerDropPeriodMult = 1.5 + self.FasterDropPeriodMult = 1.0 / 3 + self.ToonSpeed = 16.0 + + def scaledDimensions(widthHeight, scale): + w, h = widthHeight + return [math.sqrt(scale * w * w), math.sqrt(scale * h * h)] + + BaseStageDimensions = [36, 36] + self.StageAreaScale = 1.0 + self.StageLinearScale = math.sqrt(self.StageAreaScale) + DistributedPartyCatchActivityBase.notify.debug('StageLinearScale: %s' % self.StageLinearScale) + self.StageDimensions = scaledDimensions(BaseStageDimensions, self.StageAreaScale) + DistributedPartyCatchActivityBase.notify.debug('StageDimensions: %s' % self.StageDimensions) + self.StageHalfWidth = self.StageDimensions[0] / 2.0 + self.StageHalfHeight = self.StageDimensions[1] / 2.0 + self.MinOffscreenHeight = 30 + distance = math.sqrt(self.StageDimensions[0] * self.StageDimensions[0] + self.StageDimensions[1] * self.StageDimensions[1]) + distance /= self.StageLinearScale + ToonRunDuration = distance / self.ToonSpeed + offScreenOnScreenRatio = 1.0 + fraction = 1.0 / 3 * 0.85 + self.BaselineOnscreenDropDuration = ToonRunDuration / (fraction * (1.0 + offScreenOnScreenRatio)) + DistributedPartyCatchActivityBase.notify.debug('BaselineOnscreenDropDuration=%s' % self.BaselineOnscreenDropDuration) + self.OffscreenTime = offScreenOnScreenRatio * self.BaselineOnscreenDropDuration + DistributedPartyCatchActivityBase.notify.debug('OffscreenTime=%s' % self.OffscreenTime) + self.BaselineDropDuration = self.BaselineOnscreenDropDuration + self.OffscreenTime + self.MaxDropDuration = self.BaselineDropDuration + self.DropPeriod = self.BaselineDropDuration / 3.0 + scaledNumPlayers = (min(numPlayers, self.FallRateCap_Players) - 1.0) * 0.85 + 1.0 + self.DropPeriod /= scaledNumPlayers + typeProbs = {'fruit': 3, + 'anvil': 1} + probSum = reduce(lambda x, y: x + y, typeProbs.values()) + for key in typeProbs.keys(): + typeProbs[key] = float(typeProbs[key]) / probSum + + scheduler = ThreePhaseDropScheduler(PartyGlobals.CatchActivityDuration, self.FirstDropDelay, self.DropPeriod, self.MaxDropDuration, self.SlowerDropPeriodMult, self.NormalDropDelay, self.FasterDropDelay, self.FasterDropPeriodMult) + self.totalDrops = 0 + while not scheduler.doneDropping(continuous=True): + scheduler.stepT() + self.totalDrops += 1 + + self.numFruits = int(self.totalDrops * typeProbs['fruit']) + self.numAnvils = int(self.totalDrops - self.numFruits) + self.generationDuration = scheduler.getDuration() diff --git a/toontown/parties/DistributedPartyCogActivity.py b/toontown/parties/DistributedPartyCogActivity.py new file mode 100644 index 0000000..3937983 --- /dev/null +++ b/toontown/parties/DistributedPartyCogActivity.py @@ -0,0 +1,199 @@ +from direct.distributed.ClockDelta import globalClockDelta +from pandac.PandaModules import Point3 +from toontown.toonbase import TTLocalizer +import PartyGlobals +from DistributedPartyTeamActivity import DistributedPartyTeamActivity +from PartyCogActivity import PartyCogActivity + +class DistributedPartyCogActivity(DistributedPartyTeamActivity): + notify = directNotify.newCategory('DistributedPartyCogActivity') + players = {} + localPlayer = None + view = None + + def __init__(self, cr, arenaModel = 'phase_13/models/parties/cogPieArena_model', texture = None): + DistributedPartyTeamActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyCog, startDelay=PartyGlobals.CogActivityStartDelay, balanceTeams=PartyGlobals.CogActivityBalanceTeams) + self.arenaModel = arenaModel + self.texture = texture + + def load(self): + DistributedPartyTeamActivity.load(self) + self.view = PartyCogActivity(self, self.arenaModel, self.texture) + self.view.load() + + def announceGenerate(self): + DistributedPartyTeamActivity.announceGenerate(self) + for i in range(len(self.toonIds)): + for toonId in self.toonIds[i]: + toon = base.cr.doId2do.get(toonId, None) + if toon: + self.view.handleToonJoined(toon, i, lateEntry=True) + + return + + def unload(self): + if hasattr(self, 'view') and self.view is not None: + self.view.unload() + del self.view + DistributedPartyTeamActivity.unload(self) + return + + def enable(self): + DistributedPartyTeamActivity.enable(self) + + def disable(self): + DistributedPartyTeamActivity.disable(self) + + def getTitle(self): + return TTLocalizer.PartyCogTitle + + def getInstructions(self): + return TTLocalizer.PartyCogInstructions + + def pieThrow(self, toonId, timestamp, h, x, y, z, power): + if toonId not in self.toonIds: + return + if toonId != base.localAvatar.doId: + self.view.pieThrow(toonId, timestamp, h, Point3(x, y, z), power) + + def b_pieThrow(self, toon, power): + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + pos = toon.getPos() + h = toon.getH() + toonId = toon.doId + self.view.pieThrow(toonId, timestamp, h, pos, power) + self.d_broadcastPieThrow(toonId, timestamp, h, pos[0], pos[1], pos[2], power) + + def d_broadcastPieThrow(self, toonId, timestamp, h, x, y, z, power): + self.sendUpdate('pieThrow', [toonId, + timestamp, + h, + x, + y, + z, + power]) + + def pieHitsToon(self, toonId, timestamp, x, y, z): + if toonId not in self.toonIds: + return + self.view.pieHitsToon(toonId, timestamp, Point3(x, y, z)) + + def d_broadcastPieHitsToon(self, toonId, timestamp, pos): + self.sendUpdate('pieHitsToon', [toonId, + timestamp, + pos[0], + pos[1], + pos[2]]) + + def b_pieHitsToon(self, toonId, timestamp, pos): + self.view.pieHitsToon(toonId, timestamp, pos) + self.d_broadcastPieHitsToon(toonId, timestamp, pos) + + def pieHitsCog(self, toonId, timestamp, hitCogNum, x, y, z, direction, part): + if toonId not in self.toonIds: + return + if toonId != base.localAvatar.doId: + self.view.pieHitsCog(timestamp, hitCogNum, Point3(x, y, z), direction, part) + + def b_pieHitsCog(self, timestamp, hitCogNum, pos, direction, part): + self.view.pieHitsCog(timestamp, hitCogNum, pos, direction, part) + self.d_broadcastSendPieHitsCog(timestamp, hitCogNum, pos, direction, part) + + def d_broadcastSendPieHitsCog(self, timestamp, hitCogNum, pos, direction, part): + self.sendUpdate('pieHitsCog', [base.localAvatar.doId, + timestamp, + hitCogNum, + pos[0], + pos[1], + pos[2], + direction, + part]) + + def setCogDistances(self, distances): + self.view.setCogDistances(distances) + + def setHighScore(self, toonName, score): + self.setSignNote(TTLocalizer.PartyCogSignNote % (toonName, score)) + + def handleToonJoined(self, toonId): + DistributedPartyTeamActivity.handleToonJoined(self, toonId) + toon = base.cr.doId2do.get(toonId, None) + team = self.getTeam(toonId) + if toon is not None and self.view is not None: + self.view.handleToonJoined(toon, team) + return + + def handleToonExited(self, toonId): + toon = base.cr.doId2do.get(toonId, None) + if toon is None: + return + if self.view is not None: + self.view.handleToonExited(toon) + DistributedPartyTeamActivity.handleToonExited(self, toonId) + return + + def handleToonShifted(self, toonId): + toon = base.cr.doId2do.get(toonId, None) + if toon is None: + return + if self.view is not None: + self.view.handleToonShifted(toon) + return + + def handleToonSwitchedTeams(self, toonId): + DistributedPartyTeamActivity.handleToonSwitchedTeams(self, toonId) + toon = base.cr.doId2do.get(toonId, None) + if toon is None: + return + if self.view is not None: + self.view.handleToonSwitchedTeams(toon) + return + + def handleToonDisabled(self, toonId): + if self.view is not None: + self.view.handleToonDisabled(toonId) + return + + def startWaitForEnough(self): + DistributedPartyTeamActivity.startWaitForEnough(self) + self.view.openArenaDoors() + self.view.hideCogs() + + def startRules(self): + DistributedPartyTeamActivity.startRules(self) + self.view.closeArenaDoors() + self.view.showCogs() + + def startActive(self): + DistributedPartyTeamActivity.startActive(self) + self.view.startActivity(self.getCurrentActivityTime()) + self.view.closeArenaDoors() + if not self.isLocalToonPlaying: + self.view.showArenaDoorTimers(self._duration + PartyGlobals.CogActivityConclusionDuration + 1.0 - self.getCurrentActivityTime()) + + def finishActive(self): + DistributedPartyTeamActivity.finishActive(self) + self.view.stopActivity() + + def startConclusion(self, data): + DistributedPartyTeamActivity.startConclusion(self, data) + if self.isLocalToonPlaying: + score = (int(data / 10000), data % 10000) + winner = 2 + if score[PartyGlobals.TeamActivityTeams.LeftTeam] > score[PartyGlobals.TeamActivityTeams.RightTeam]: + winner = PartyGlobals.TeamActivityTeams.LeftTeam + elif score[PartyGlobals.TeamActivityTeams.LeftTeam] < score[PartyGlobals.TeamActivityTeams.RightTeam]: + winner = PartyGlobals.TeamActivityTeams.RightTeam + if winner < 2: + if self.getTeam(base.localAvatar.doId) == winner: + resultsText = TTLocalizer.PartyTeamActivityLocalAvatarTeamWins + else: + resultsText = TTLocalizer.PartyTeamActivityWins % TTLocalizer.PartyCogTeams[winner] + else: + resultsText = TTLocalizer.PartyTeamActivityGameTie + self.view.showResults(resultsText, winner, score) + + def finishConclusion(self): + self.view.hideResults() + DistributedPartyTeamActivity.finishConclusion(self) + self.view.hideArenaDoorTimers() diff --git a/toontown/parties/DistributedPartyDance20Activity.py b/toontown/parties/DistributedPartyDance20Activity.py new file mode 100644 index 0000000..5721f6e --- /dev/null +++ b/toontown/parties/DistributedPartyDance20Activity.py @@ -0,0 +1,26 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyDance20Activity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyDanceActivity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance20, PartyGlobals.DancePatternToAnims20) + + def getInstructions(self): + return TTLocalizer.PartyDanceActivity20Instructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivity20Title + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_20') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyDanceActivity.py b/toontown/parties/DistributedPartyDanceActivity.py new file mode 100644 index 0000000..dfb243d --- /dev/null +++ b/toontown/parties/DistributedPartyDanceActivity.py @@ -0,0 +1,27 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyDanceActivity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyDanceActivity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance, PartyGlobals.DancePatternToAnims) + + def getInstructions(self): + return TTLocalizer.PartyDanceActivityInstructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivityTitle + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_10') + origBall = self.danceFloor.find('**/discoBall_mesh_orig') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyDanceActivityBase.py b/toontown/parties/DistributedPartyDanceActivityBase.py new file mode 100644 index 0000000..fa8242f --- /dev/null +++ b/toontown/parties/DistributedPartyDanceActivityBase.py @@ -0,0 +1,369 @@ +import random +from pandac.PandaModules import * +from direct.interval.FunctionInterval import Wait, Func +from direct.interval.MetaInterval import Sequence, Parallel +from direct.showbase.PythonUtil import lerp, Enum +from direct.fsm import FSM +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.parties.activityFSMs import DanceActivityFSM +from toontown.parties.PartyGlobals import ActivityIds, ActivityTypes +from toontown.parties.PartyGlobals import DancePatternToAnims, DanceAnimToName +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.PartyDanceActivityToonFSM import PartyDanceActivityToonFSM +from toontown.parties.PartyDanceActivityToonFSM import ToonDancingStates +from toontown.parties.KeyCodes import KeyCodes +from toontown.parties.KeyCodesGui import KeyCodesGui +from toontown.parties import PartyGlobals +DANCE_FLOOR_COLLISION = 'danceFloor_collision' +DanceViews = Enum(('Normal', 'Dancing', 'Isometric')) + +class DistributedPartyDanceActivityBase(DistributedPartyActivity): + notify = directNotify.newCategory('DistributedPartyDanceActivity') + + def __init__(self, cr, actId, dancePatternToAnims, model = 'phase_13/models/parties/danceFloor'): + DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous) + self.model = model + self.danceFloor = None + self.localToonDancing = False + self.keyCodes = None + self.gui = None + self.currentCameraMode = None + self.orthoWalk = None + self.cameraParallel = None + self.localToonDanceSequence = None + self.localPatternsMatched = [] + self.dancePatternToAnims = dancePatternToAnims + self.dancingToonFSMs = {} + return + + def generateInit(self): + self.notify.debug('generateInit') + DistributedPartyActivity.generateInit(self) + self.keyCodes = KeyCodes(patterns=self.dancePatternToAnims.keys()) + self.gui = KeyCodesGui(self.keyCodes) + self.__initOrthoWalk() + self.activityFSM = DanceActivityFSM(self) + + def announceGenerate(self): + DistributedPartyActivity.announceGenerate(self) + self.activityFSM.request('Active') + + def load(self): + DistributedPartyActivity.load(self) + self.danceFloor = loader.loadModel(self.model) + self.danceFloor.reparentTo(self.getParentNodePath()) + self.danceFloor.setPos(self.x, self.y, 0.0) + self.danceFloor.setH(self.h) + self.danceFloor.wrtReparentTo(render) + self.sign.setPos(22, -22, 0) + floor = self.danceFloor.find('**/danceFloor_mesh') + self.danceFloorSequence = Sequence(Wait(0.3), Func(floor.setH, floor, 36)) + discoBall = self.danceFloor.find('**/discoBall_mesh') + self.discoBallSequence = Parallel(discoBall.hprInterval(6.0, Vec3(360, 0, 0)), Sequence(discoBall.posInterval(3, Point3(0, 0, 1), blendType='easeInOut'), discoBall.posInterval(3, Point3(0, 0, 0), blendType='easeInOut'))) + + def unload(self): + DistributedPartyActivity.unload(self) + self.activityFSM.request('Disabled') + if self.localToonDanceSequence is not None: + self.localToonDanceSequence.finish() + if self.localToonDancing: + self.__localStopDancing() + self.ignoreAll() + if self.discoBallSequence is not None: + self.discoBallSequence.finish() + if self.danceFloorSequence is not None: + self.danceFloorSequence.finish() + del self.danceFloorSequence + del self.discoBallSequence + del self.localToonDanceSequence + if self.danceFloor is not None: + self.danceFloor.removeNode() + self.danceFloor = None + self.__destroyOrthoWalk() + for toonId in self.dancingToonFSMs.keys(): + self.dancingToonFSMs[toonId].destroy() + del self.dancingToonFSMs[toonId] + + del self.dancingToonFSMs + del self.cameraParallel + del self.currentCameraMode + if self.keyCodes is not None: + self.keyCodes.destroy() + del self.keyCodes + del self.activityFSM + del self.gui + del self.localPatternsMatched + return + + def handleToonDisabled(self, toonId): + self.notify.debug('handleToonDisabled avatar ' + str(toonId) + ' disabled') + if self.dancingToonFSMs.has_key(toonId): + self.dancingToonFSMs[toonId].request('cleanup') + self.dancingToonFSMs[toonId].destroy() + del self.dancingToonFSMs[toonId] + + def getTitle(self): + self.notify.warning('define title for this dance activity') + return TTLocalizer.PartyDanceActivityTitle + + def getInstructions(self): + self.notify.warning('define instructions for this dance activity') + return TTLocalizer.PartyDanceActivityInstructions + + def startActive(self): + self.accept('enter' + DANCE_FLOOR_COLLISION, self.__handleEnterDanceFloor) + self.accept('exit' + DANCE_FLOOR_COLLISION, self.__handleExitDanceFloor) + self.danceFloorSequence.loop() + self.discoBallSequence.loop() + + def finishActive(self): + pass + + def startDisabled(self): + self.ignore('enter' + DANCE_FLOOR_COLLISION) + self.ignore('exit' + DANCE_FLOOR_COLLISION) + self.discoBallSequence.pause() + self.danceFloorSequence.pause() + + def finishDisabled(self): + pass + + def __initOrthoWalk(self): + self.notify.debug('Initialize Ortho Walk') + orthoDrive = OrthoDrive(9.778) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True) + + def __destroyOrthoWalk(self): + self.notify.debug('Destroy Ortho Walk') + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + + def __disableLocalControl(self): + self.orthoWalk.stop() + self.keyCodes.disable() + self.keyCodesGui.disable() + + def __enableLocalControl(self): + self.orthWalk.start() + self.keyCodes.enable() + self.keyCodesGui.enable() + self.keyCodesGui.hideAll() + + def __handleEnterDanceFloor(self, collEntry): + if not self.isLocalToonInActivity() and not self.localToonDancing: + self.notify.debug('Toon enters dance floor collision area.') + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('activity') + self.d_toonJoinRequest() + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('activity') + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny) + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.fsm.request('walk') + + def setToonsPlaying(self, toonIds, toonHeadings): + self.notify.debug('setToonsPlaying') + self.notify.debug('\ttoonIds: %s' % toonIds) + self.notify.debug('\ttoonHeadings: %s' % toonHeadings) + exitedToons, joinedToons = self.getToonsPlayingChanges(self.toonIds, toonIds) + self.notify.debug('\texitedToons: %s' % exitedToons) + self.notify.debug('\tjoinedToons: %s' % joinedToons) + self.setToonIds(toonIds) + self._processExitedToons(exitedToons) + for toonId in joinedToons: + if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Joining): + self._enableHandleToonDisabled(toonId) + self.handleToonJoined(toonId, toonHeadings[toonIds.index(toonId)]) + if toonId == base.localAvatar.doId: + self._localToonRequestStatus = None + + return + + def handleToonJoined(self, toonId, h): + self.notify.debug('handleToonJoined( toonId=%d, h=%.2f )' % (toonId, h)) + if base.cr.doId2do.has_key(toonId): + toonFSM = PartyDanceActivityToonFSM(toonId, self, h) + toonFSM.request('Init') + self.dancingToonFSMs[toonId] = toonFSM + if toonId == base.localAvatar.doId: + self.__localStartDancing(h) + + def __localStartDancing(self, h): + if not self.localToonDancing: + place = base.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + self.localToonDancing = True + place.fsm.request('activity') + self.__updateLocalToonState(ToonDancingStates.Run) + self.__setViewMode(DanceViews.Dancing) + self.gui.load() + self.startRules() + self.__localEnableControls() + else: + self.notify.warning('__localStartDancing, failed in playGame.getPlace()') + + def handleRulesDone(self): + self.finishRules() + + def __localEnableControls(self): + if not self.dancingToonFSMs.has_key(base.localAvatar.doId): + self.notify.debug('no dancing FSM for local avatar, not enabling controls') + return + self.accept(KeyCodes.PATTERN_MATCH_EVENT, self.__doDanceMove) + self.accept(KeyCodes.PATTERN_NO_MATCH_EVENT, self.__noDanceMoveMatch) + self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown) + self.accept(KeyCodes.KEY_UP_EVENT, self._handleKeyUp) + self.keyCodes.enable() + self.orthoWalk.start() + self.gui.enable() + self.gui.hideAll() + + def __localDisableControls(self): + self.orthoWalk.stop() + self.keyCodes.disable() + self.gui.disable() + self.ignore(KeyCodes.PATTERN_MATCH_EVENT) + self.ignore(KeyCodes.PATTERN_NO_MATCH_EVENT) + self.ignore(KeyCodes.KEY_DOWN_EVENT) + self.ignore(KeyCodes.KEY_UP_EVENT) + + def __handleExitDanceFloor(self, collEntry): + if self.localToonDanceSequence is not None: + self.notify.debug('finishing %s' % self.localToonDanceSequence) + self.localToonDanceSequence.finish() + self.localToonDanceSequence = None + self.finishRules() + self.notify.debug('Toon exits dance floor collision area.') + self.d_toonExitRequest() + return + + def exitRequestDenied(self, reason): + DistributedPartyActivity.exitRequestDenied(self, reason) + if reason != PartyGlobals.DenialReasons.SilentFail: + self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny) + + def handleToonExited(self, toonId): + self.notify.debug('exitDanceFloor %s' % toonId) + if toonId == base.localAvatar.doId: + self.__localStopDancing() + + def __localStopDancing(self): + if self.localToonDancing: + self.__localDisableControls() + self.gui.unload() + self.__setViewMode(DanceViews.Normal) + self.__updateLocalToonState(ToonDancingStates.Cleanup) + if base.cr.playGame.getPlace(): + if hasattr(base.cr.playGame.getPlace(), 'fsm'): + base.cr.playGame.getPlace().fsm.request('walk') + self.localToonDancing = False + + def __doDanceMove(self, pattern): + self.notify.debug('Dance move! %s' % pattern) + anim = self.dancePatternToAnims.get(pattern) + if anim: + self.__updateLocalToonState(ToonDancingStates.DanceMove, anim) + self.gui.setColor(0, 1, 0) + self.gui.showText(DanceAnimToName.get(anim, anim)) + self.finishRules() + if pattern not in self.localPatternsMatched: + camNode = NodePath(self.uniqueName('danceCamNode')) + camNode.reparentTo(base.localAvatar) + camNode.lookAt(camera) + camNode.setHpr(camNode.getH(), 0, 0) + node2 = NodePath('tempCamNode') + node2.reparentTo(camNode) + node2.setPos(Point3(0, 15, 10)) + node2.lookAt(camNode) + h = node2.getH() * (camera.getH(camNode) / abs(camera.getH(camNode))) + node2.removeNode + del node2 + hpr = camera.getHpr() + pos = camera.getPos() + camParent = camera.getParent() + camera.wrtReparentTo(camNode) + self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Parallel(camera.posInterval(0.5, Point3(0, 15, 10), blendType='easeIn'), camera.hprInterval(0.5, Point3(h, -20, 0), blendType='easeIn')), camNode.hprInterval(4.0, Point3(camNode.getH() - 360, 0, 0)), Func(camera.wrtReparentTo, camParent), Func(camNode.removeNode), Parallel(camera.posInterval(0.5, pos, blendType='easeOut'), camera.hprInterval(0.5, hpr, blendType='easeOut')), Func(self.__localEnableControls)) + else: + self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Wait(2.0), Func(self.__localEnableControls)) + self.localToonDanceSequence.start() + self.localPatternsMatched.append(pattern) + + def __noDanceMoveMatch(self): + self.gui.setColor(1, 0, 0) + self.gui.showText('No Match!') + self.__updateLocalToonState(ToonDancingStates.DanceMove) + self.localToonDanceSequence = Sequence(Func(self.__localDisableControls), Wait(1.0), Func(self.__localEnableControls)) + self.localToonDanceSequence.start() + + def _handleKeyDown(self, key, index): + self.__updateLocalToonState(ToonDancingStates.Run) + + def _handleKeyUp(self, key): + if not self.keyCodes.isAnyKeyPressed(): + self.__updateLocalToonState(ToonDancingStates.DanceMove) + self.acceptOnce(KeyCodes.KEY_DOWN_EVENT, self._handleKeyDown) + + def __updateLocalToonState(self, state, anim = ''): + self._requestToonState(base.localAvatar.doId, state, anim) + self.d_updateDancingToon(state, anim) + + def d_updateDancingToon(self, state, anim): + self.sendUpdate('updateDancingToon', [state, anim]) + + def setDancingToonState(self, toonId, state, anim): + if toonId != base.localAvatar.doId and self.dancingToonFSMs.has_key(toonId): + self._requestToonState(toonId, state, anim) + + def _requestToonState(self, toonId, state, anim): + if self.dancingToonFSMs.has_key(toonId): + state = ToonDancingStates.getString(state) + curState = self.dancingToonFSMs[toonId].getCurrentOrNextState() + try: + self.dancingToonFSMs[toonId].request(state, anim) + except FSM.RequestDenied: + self.notify.warning('could not go from state=%s to state %s' % (curState, state)) + + if state == ToonDancingStates.getString(ToonDancingStates.Cleanup): + self.notify.debug('deleting this fsm %s' % self.dancingToonFSMs[toonId]) + del self.dancingToonFSMs[toonId] + if self.localToonDanceSequence: + self.notify.debug('forcing a finish of localToonDanceSequence') + self.localToonDanceSequence.finish() + self.localToonDanceSequence = None + return + + def __setViewMode(self, mode): + toon = base.localAvatar + if mode == DanceViews.Normal: + if self.cameraParallel is not None: + self.cameraParallel.pause() + self.cameraParallel = None + camera.reparentTo(toon) + base.localAvatar.startUpdateSmartCamera() + elif mode == DanceViews.Dancing: + base.localAvatar.stopUpdateSmartCamera() + camera.wrtReparentTo(self.danceFloor) + node = NodePath('temp') + node.reparentTo(toon.getParent()) + node.setPos(Point3(0, -40, 20)) + node2 = NodePath('temp2') + node2.reparentTo(self.danceFloor) + node.reparentTo(node2) + node2.setH(render, toon.getParent().getH()) + pos = node.getPos(self.danceFloor) + node2.removeNode() + node.removeNode() + self.cameraParallel = Parallel(camera.posInterval(0.5, pos, blendType='easeIn'), camera.hprInterval(0.5, Point3(0, -27, 0), other=toon.getParent(), blendType='easeIn')) + self.cameraParallel.start() + self.currentCameraMode = mode + return diff --git a/toontown/parties/DistributedPartyFireworksActivity.py b/toontown/parties/DistributedPartyFireworksActivity.py new file mode 100644 index 0000000..277d509 --- /dev/null +++ b/toontown/parties/DistributedPartyFireworksActivity.py @@ -0,0 +1,153 @@ +from pandac.PandaModules import Vec3 +from pandac.PandaModules import OmniBoundingVolume +from pandac.PandaModules import AlphaTestAttrib +from pandac.PandaModules import RenderAttrib +from direct.actor.Actor import Actor +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import globalClockDelta +from toontown.effects.FireworkShowMixin import FireworkShowMixin +from toontown.effects.RocketExplosion import RocketExplosion +from toontown.toonbase import TTLocalizer +from PartyGlobals import FireworkShows +from PartyGlobals import ActivityIds +from PartyGlobals import ActivityTypes +from PartyGlobals import FireworksStartedEvent +from PartyGlobals import FireworksFinishedEvent +from PartyGlobals import FireworksPostLaunchDelay +from PartyGlobals import RocketSoundDelay +from PartyGlobals import RocketDirectionDelay +from DistributedPartyActivity import DistributedPartyActivity +from activityFSMs import FireworksActivityFSM +import PartyGlobals + +class DistributedPartyFireworksActivity(DistributedPartyActivity, FireworkShowMixin): + notify = directNotify.newCategory('DistributedPartyFireworksActivity') + + def __init__(self, cr): + DistributedPartyFireworksActivity.notify.debug('__init__') + DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyFireworks, ActivityTypes.HostInitiated, wantLever=True) + FireworkShowMixin.__init__(self, restorePlaygroundMusic=True, startDelay=FireworksPostLaunchDelay) + + def setEventId(self, eventId): + DistributedPartyFireworksActivity.notify.debug('setEventId( %s )' % FireworkShows.getString(eventId)) + self.eventId = eventId + + def setShowStyle(self, showStyle): + DistributedPartyFireworksActivity.notify.debug('setShowStyle( %d )' % showStyle) + self.showStyle = showStyle + + def load(self): + DistributedPartyFireworksActivity.notify.debug('load') + DistributedPartyActivity.load(self) + self.eventId = PartyGlobals.FireworkShows.Summer + self.launchPadModel = loader.loadModel('phase_13/models/parties/launchPad') + self.launchPadModel.setH(90.0) + self.launchPadModel.setPos(0.0, -18.0, 0.0) + self.launchPadModel.reparentTo(self.root) + railingsCollection = self.launchPadModel.findAllMatches('**/launchPad_mesh/*railing*') + for i in range(railingsCollection.getNumPaths()): + railingsCollection[i].setAttrib(AlphaTestAttrib.make(RenderAttrib.MGreater, 0.75)) + + leverLocator = self.launchPadModel.find('**/RocketLever_locator') + self.lever.setPosHpr(Vec3.zero(), Vec3.zero()) + self.lever.reparentTo(leverLocator) + self.toonPullingLeverInterval = None + self.sign.reparentTo(self.launchPadModel.find('**/launchPad_sign_locator')) + self.rocketActor = Actor('phase_13/models/parties/rocket_model', {'launch': 'phase_13/models/parties/rocket_launch'}) + rocketLocator = self.launchPadModel.find('**/rocket_locator') + self.rocketActor.reparentTo(rocketLocator) + self.rocketActor.node().setBound(OmniBoundingVolume()) + self.rocketActor.node().setFinal(True) + effectsLocator = self.rocketActor.find('**/joint1') + self.rocketExplosionEffect = RocketExplosion(effectsLocator, rocketLocator) + self.rocketParticleSeq = None + self.launchSound = base.loadSfx('phase_13/audio/sfx/rocket_launch.mp3') + self.activityFSM = FireworksActivityFSM(self) + self.activityFSM.request('Idle') + return + + def unload(self): + DistributedPartyFireworksActivity.notify.debug('unload') + taskMgr.remove(self.taskName('delayedStartShow')) + if self.rocketParticleSeq: + self.rocketParticleSeq.pause() + self.rocketParticleSeq = None + self.launchPadModel.removeNode() + del self.launchPadModel + del self.toonPullingLeverInterval + self.rocketActor.delete() + self.rocketExplosionEffect.destroy() + self.activityFSM.request('Disabled') + del self.rocketActor + del self.launchSound + del self.activityFSM + del self.eventId + del self.showStyle + DistributedPartyActivity.unload(self) + return + + def _leverPulled(self, collEntry): + DistributedPartyFireworksActivity.notify.debug('_leverPulled') + hostPulledLever = DistributedPartyActivity._leverPulled(self, collEntry) + if self.activityFSM.getCurrentOrNextState() == 'Active': + self.showMessage(TTLocalizer.PartyFireworksAlreadyActive) + elif self.activityFSM.getCurrentOrNextState() == 'Disabled': + self.showMessage(TTLocalizer.PartyFireworksAlreadyDone) + elif self.activityFSM.getCurrentOrNextState() == 'Idle': + if hostPulledLever: + base.cr.playGame.getPlace().fsm.request('activity') + self.toonPullingLeverInterval = self.getToonPullingLeverInterval(base.localAvatar) + self.toonPullingLeverInterval.append(Func(self.d_toonJoinRequest)) + self.toonPullingLeverInterval.append(Func(base.cr.playGame.getPlace().fsm.request, 'walk')) + self.toonPullingLeverInterval.start() + else: + self.showMessage(TTLocalizer.PartyOnlyHostLeverPull) + + def setState(self, newState, timestamp): + DistributedPartyFireworksActivity.notify.debug('setState( newState=%s, ... )' % newState) + DistributedPartyActivity.setState(self, newState, timestamp) + if newState == 'Active': + self.activityFSM.request(newState, timestamp) + else: + self.activityFSM.request(newState) + + def startIdle(self): + DistributedPartyFireworksActivity.notify.debug('startIdle') + + def finishIdle(self): + DistributedPartyFireworksActivity.notify.debug('finishIdle') + + def startActive(self, showStartTimestamp): + DistributedPartyFireworksActivity.notify.debug('startActive') + messenger.send(FireworksStartedEvent) + timeSinceStart = globalClockDelta.localElapsedTime(showStartTimestamp) + if timeSinceStart > self.rocketActor.getDuration('launch'): + self.rocketActor.hide() + self.startShow(self.eventId, self.showStyle, showStartTimestamp) + else: + self.rocketActor.play('launch') + self.rocketParticleSeq = Sequence(Wait(RocketSoundDelay), Func(base.playSfx, self.launchSound), Func(self.rocketExplosionEffect.start), Wait(RocketDirectionDelay), LerpHprInterval(self.rocketActor, 4.0, Vec3(0, 0, -60)), Func(self.rocketExplosionEffect.end), Func(self.rocketActor.hide)) + self.rocketParticleSeq.start() + taskMgr.doMethodLater(FireworksPostLaunchDelay, self.startShow, self.taskName('delayedStartShow'), extraArgs=[self.eventId, + self.showStyle, + showStartTimestamp, + self.root]) + + def finishActive(self): + self.rocketParticleSeq = None + DistributedPartyFireworksActivity.notify.debug('finishActive') + messenger.send(FireworksFinishedEvent) + taskMgr.remove(self.taskName('delayedStartShow')) + FireworkShowMixin.disable(self) + return + + def startDisabled(self): + DistributedPartyFireworksActivity.notify.debug('startDisabled') + if not self.rocketActor.isEmpty(): + self.rocketActor.hide() + + def finishDisabled(self): + DistributedPartyFireworksActivity.notify.debug('finishDisabled') + + def handleToonDisabled(self, toonId): + self.notify.warning('handleToonDisabled no implementation yet') diff --git a/toontown/parties/DistributedPartyJukebox40Activity.py b/toontown/parties/DistributedPartyJukebox40Activity.py new file mode 100644 index 0000000..6358bed --- /dev/null +++ b/toontown/parties/DistributedPartyJukebox40Activity.py @@ -0,0 +1,18 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyJukebox40Activity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyJukeboxActivity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyJukebox40, PartyGlobals.PhaseToMusicData40) + + def load(self): + DistributedPartyJukeboxActivityBase.load(self) + newTexture = loader.loadTexture('phase_13/maps/tt_t_ara_pty_jukeboxBlue.jpg', 'phase_13/maps/tt_t_ara_pty_jukeboxBlue_a.rgb') + case = self.jukebox.find('**/jukeboxGlass') + if not case.isEmpty(): + case.setTexture(newTexture, 1) + body = self.jukebox.find('**/jukeboxBody') + if not body.isEmpty(): + body.setTexture(newTexture, 1) diff --git a/toontown/parties/DistributedPartyJukeboxActivity.py b/toontown/parties/DistributedPartyJukeboxActivity.py new file mode 100644 index 0000000..bb1f2ca --- /dev/null +++ b/toontown/parties/DistributedPartyJukeboxActivity.py @@ -0,0 +1,8 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyJukeboxActivity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyJukeboxActivity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyJukebox, PartyGlobals.PhaseToMusicData) diff --git a/toontown/parties/DistributedPartyJukeboxActivityBase.py b/toontown/parties/DistributedPartyJukeboxActivityBase.py new file mode 100644 index 0000000..9e3a8c0 --- /dev/null +++ b/toontown/parties/DistributedPartyJukeboxActivityBase.py @@ -0,0 +1,234 @@ +from direct.actor.Actor import Actor +from direct.task.Task import Task +from pandac.PandaModules import * +from otp.otpbase.OTPBase import OTPBase +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.PartyGlobals import ActivityIds, ActivityTypes, JUKEBOX_TIMEOUT +from toontown.parties.PartyGlobals import getMusicRepeatTimes, MUSIC_PATH, sanitizePhase +from toontown.parties.JukeboxGui import JukeboxGui + +class DistributedPartyJukeboxActivityBase(DistributedPartyActivity): + notify = directNotify.newCategory('DistributedPartyJukeboxActivityBase') + + def __init__(self, cr, actId, phaseToMusicData): + DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous) + self.phaseToMusicData = phaseToMusicData + self.jukebox = None + self.gui = None + self.tunes = [] + self.music = None + self.currentSongData = None + self.localQueuedSongInfo = None + self.localQueuedSongListItem = None + return + + def generateInit(self): + self.gui = JukeboxGui(self.phaseToMusicData) + + def load(self): + DistributedPartyActivity.load(self) + self.jukebox = Actor('phase_13/models/parties/jukebox_model', {'dance': 'phase_13/models/parties/jukebox_dance'}) + self.jukebox.reparentTo(self.root) + self.collNode = CollisionNode(self.getCollisionName()) + self.collNode.setCollideMask(ToontownGlobals.CameraBitmask | ToontownGlobals.WallBitmask) + collTube = CollisionTube(0, 0, 0, 0.0, 0.0, 4.25, 2.25) + collTube.setTangible(1) + self.collNode.addSolid(collTube) + self.collNodePath = self.jukebox.attachNewNode(self.collNode) + self.sign.setPos(-5.0, 0, 0) + self.activate() + + def unload(self): + DistributedPartyActivity.unload(self) + self.gui.unload() + if self.music is not None: + self.music.stop() + self.jukebox.stop() + self.jukebox.delete() + self.jukebox = None + self.ignoreAll() + return + + def getCollisionName(self): + return self.uniqueName('jukeboxCollision') + + def activate(self): + self.accept('enter' + self.getCollisionName(), self.__handleEnterCollision) + + def __handleEnterCollision(self, collisionEntry): + if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'walk': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyJukeboxOccupied) + + def handleToonJoined(self, toonId): + toon = base.cr.doId2do.get(toonId) + if toon: + self.jukebox.lookAt(base.cr.doId2do[toonId]) + self.jukebox.setHpr(self.jukebox.getH() + 180.0, 0, 0) + if toonId == base.localAvatar.doId: + self.__localUseJukebox() + + def handleToonExited(self, toonId): + if toonId == base.localAvatar.doId and self.gui.isLoaded(): + self.__deactivateGui() + + def handleToonDisabled(self, toonId): + self.notify.warning('handleToonDisabled no implementation yet') + + def __localUseJukebox(self): + base.localAvatar.disableAvatarControls() + base.localAvatar.stopPosHprBroadcast() + self.__activateGui() + self.accept(JukeboxGui.CLOSE_EVENT, self.__handleGuiClose) + taskMgr.doMethodLater(0.5, self.__localToonWillExitTask, self.uniqueName('toonWillExitJukeboxOnTimeout'), extraArgs=None) + self.accept(JukeboxGui.ADD_SONG_CLICK_EVENT, self.__handleQueueSong) + if self.isUserHost(): + self.accept(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT, self.__handleMoveSongToTop) + return + + def __localToonWillExitTask(self, task): + self.localToonExiting() + return Task.done + + def __activateGui(self): + self.gui.enable(timer=JUKEBOX_TIMEOUT) + self.gui.disableAddSongButton() + if self.currentSongData is not None: + self.gui.setSongCurrentlyPlaying(self.currentSongData[0], self.currentSongData[1]) + self.d_queuedSongsRequest() + return + + def __deactivateGui(self): + self.ignore(JukeboxGui.CLOSE_EVENT) + self.ignore(JukeboxGui.SONG_SELECT_EVENT) + self.ignore(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) + base.cr.playGame.getPlace().setState('walk') + base.localAvatar.startPosHprBroadcast() + base.localAvatar.enableAvatarControls() + self.gui.unload() + self.__localClearQueuedSong() + + def isUserHost(self): + return self.party.partyInfo.hostId == base.localAvatar.doId + + def d_queuedSongsRequest(self): + self.sendUpdate('queuedSongsRequest') + + def queuedSongsResponse(self, songInfoList, index): + if self.gui.isLoaded(): + for i in range(len(songInfoList)): + songInfo = songInfoList[i] + self.__addSongToQueue(songInfo, isLocalQueue=index >= 0 and i == index) + + self.gui.enableAddSongButton() + + def __handleGuiClose(self): + self.__deactivateGui() + self.d_toonExitDemand() + + def __handleQueueSong(self, name, values): + self.d_setNextSong(values[0], values[1]) + + def d_setNextSong(self, phase, filename): + self.sendUpdate('setNextSong', [(phase, filename)]) + + def setSongInQueue(self, songInfo): + if self.gui.isLoaded(): + phase = sanitizePhase(songInfo[0]) + filename = songInfo[1] + data = self.getMusicData(phase, filename) + if data: + if self.localQueuedSongListItem is not None: + self.localQueuedSongListItem['text'] = data[0] + else: + self.__addSongToQueue(songInfo, isLocalQueue=True) + return + + def __addSongToQueue(self, songInfo, isLocalQueue = False): + isHost = isLocalQueue and self.isUserHost() + data = self.getMusicData(sanitizePhase(songInfo[0]), songInfo[1]) + if data: + listItem = self.gui.addSongToQueue(data[0], highlight=isLocalQueue, moveToTopButton=isHost) + if isLocalQueue: + self.localQueuedSongInfo = songInfo + self.localQueuedSongListItem = listItem + + def __localClearQueuedSong(self): + self.localQueuedSongInfo = None + self.localQueuedSongListItem = None + return + + def __play(self, phase, filename, length): + self.music = base.loadMusic((MUSIC_PATH + '%s') % (phase, filename)) + if self.music: + if self.__checkPartyValidity() and hasattr(base.cr.playGame.getPlace().loader, 'music') and base.cr.playGame.getPlace().loader.music: + base.cr.playGame.getPlace().loader.music.stop() + base.resetMusic.play() + self.music.setTime(0.0) + self.music.setLoopCount(getMusicRepeatTimes(length)) + self.music.play() + jukeboxAnimControl = self.jukebox.getAnimControl('dance') + if not jukeboxAnimControl.isPlaying(): + self.jukebox.loop('dance') + self.currentSongData = (phase, filename) + + def __stop(self): + self.jukebox.stop() + self.currentSongData = None + if self.music: + self.music.stop() + if self.gui.isLoaded(): + self.gui.clearSongCurrentlyPlaying() + return + + def setSongPlaying(self, songInfo, toonId): + phase = sanitizePhase(songInfo[0]) + filename = songInfo[1] + if not filename: + self.__stop() + return + data = self.getMusicData(phase, filename) + if data: + self.__play(phase, filename, data[1]) + self.setSignNote(data[0]) + if self.gui.isLoaded(): + item = self.gui.popSongFromQueue() + self.gui.setSongCurrentlyPlaying(phase, filename) + if item == self.localQueuedSongListItem: + self.__localClearQueuedSong() + if toonId == localAvatar.doId: + localAvatar.setSystemMessage(0, TTLocalizer.PartyJukeboxNowPlaying) + + def __handleMoveSongToTop(self): + if self.isUserHost() and self.localQueuedSongListItem is not None: + self.d_moveHostSongToTopRequest() + return + + def d_moveHostSongToTopRequest(self): + self.notify.debug('d_moveHostSongToTopRequest') + self.sendUpdate('moveHostSongToTopRequest') + + def moveHostSongToTop(self): + self.notify.debug('moveHostSongToTop') + if self.gui.isLoaded(): + self.gui.pushQueuedItemToTop(self.localQueuedSongListItem) + + def getMusicData(self, phase, filename): + data = [] + phase = sanitizePhase(phase) + phase = self.phaseToMusicData.get(phase) + if phase: + data = phase.get(filename, []) + return data + + def __checkPartyValidity(self): + if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'loader') and base.cr.playGame.getPlace().loader: + return True + else: + return False diff --git a/toontown/parties/DistributedPartyTeamActivity.py b/toontown/parties/DistributedPartyTeamActivity.py new file mode 100644 index 0000000..38fa5e8 --- /dev/null +++ b/toontown/parties/DistributedPartyTeamActivity.py @@ -0,0 +1,378 @@ +from direct.distributed.ClockDelta import globalClockDelta +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.activityFSMs import TeamActivityFSM +from toontown.parties.TeamActivityGui import TeamActivityGui + +class DistributedPartyTeamActivity(DistributedPartyActivity): + notify = directNotify.newCategory('DistributedPartyTeamActivity') + + def __init__(self, cr, activityId, startDelay = PartyGlobals.TeamActivityStartDelay, balanceTeams = False): + DistributedPartyActivity.__init__(self, cr, activityId, PartyGlobals.ActivityTypes.GuestInitiated, wantRewardGui=True) + self.notify.debug('__init__') + self.toonIds = ([], []) + self.isLocalToonPlaying = False + self.localToonTeam = None + self.advantage = 1.0 + self.waitToStartTimestamp = None + self._maxPlayersPerTeam = 0 + self._minPlayersPerTeam = 0 + self._duration = 0 + self._startDelay = base.config.GetFloat('party-team-activity-start-delay', startDelay) + self._willBalanceTeams = balanceTeams + self._currentStatus = '' + return + + def load(self): + DistributedPartyActivity.load(self) + self.teamActivityGui = TeamActivityGui(self) + self.activityFSM = TeamActivityFSM(self) + + def unload(self): + DistributedPartyActivity.unload(self) + del self.activityFSM + self.teamActivityGui.unload() + del self.teamActivityGui + if hasattr(self, 'toonIds'): + del self.toonIds + del self.isLocalToonPlaying + del self.localToonTeam + del self.advantage + del self.waitToStartTimestamp + + def handleToonShifted(self, toonId): + self.notify.error('handleToonShifted( toonId=%d ) must be overriden by child class.' % toonId) + + def handleToonSwitchedTeams(self, toonId): + if toonId == base.localAvatar.doId and self._canSwitchTeams: + if self.isState('WaitForEnough') or self.isState('WaitToStart'): + self.teamActivityGui.enableExitButton() + self.teamActivityGui.enableSwitchButton() + + def handleToonDisabled(self, toonId): + self.notify.error('handleToonDisabled( toonId=%d ) must be overriden by child class.' % toonId) + + def setPlayersPerTeam(self, min, max): + self._minPlayersPerTeam = min + self._maxPlayersPerTeam = max + + def setDuration(self, duration): + self._duration = duration + + def setCanSwitchTeams(self, canSwitchTeams): + self._canSwitchTeams = canSwitchTeams + + def d_toonJoinRequest(self, team): + if self.isLocalToonInActivity(): + return + if self.activityFSM.state in ['WaitForEnough', 'WaitToStart'] and self._localToonRequestStatus is None: + base.cr.playGame.getPlace().fsm.request('activity') + self.localToonJoining() + self.sendUpdate('toonJoinRequest', [team]) + return + + def d_toonExitRequest(self): + toonId = base.localAvatar.doId + team = self.getTeam(toonId) + if team is not None: + if self._localToonRequestStatus is None: + self.localToonExiting() + self.sendUpdate('toonExitRequest', [team]) + else: + self.notify.warning('Not sending exitRequest as localToon has no team.') + return + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.notify.debug('joinRequestDenied') + if reason == PartyGlobals.DenialReasons.Full: + self.showMessage(TTLocalizer.PartyTeamActivityTeamFull) + elif reason == PartyGlobals.DenialReasons.Default: + self.showMessage(TTLocalizer.PartyTeamActivityJoinDenied % self.getTitle()) + + def exitRequestDenied(self, reason): + DistributedPartyActivity.exitRequestDenied(self, reason) + if reason == PartyGlobals.DenialReasons.Default: + self.showMessage(TTLocalizer.PartyTeamActivityExitDenied % self.getTitle()) + if self.isLocalToonPlaying and (self.isState('WaitToStart') or self.isState('WaitForEnough')): + self.teamActivityGui.enableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + + def d_toonSwitchTeamRequest(self): + if not self._canSwitchTeams: + return + self.sendUpdate('toonSwitchTeamRequest') + + def switchTeamRequestDenied(self, reason): + self.notify.debug('switchTeamRequestDenied') + if reason == PartyGlobals.DenialReasons.Full: + self.showMessage(TTLocalizer.PartyTeamActivityTeamFull, endState='activity') + elif reason == PartyGlobals.DenialReasons.Default: + self.showMessage(TTLocalizer.PartyTeamActivitySwitchDenied, endState='activity') + if self.isLocalToonPlaying and (self.isState('WaitToStart') or self.isState('WaitForEnough')) and self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + + def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds): + newToonIds = [leftTeamToonIds, rightTeamToonIds] + exitedToons, joinedToons, shifters, switchers = self.getToonsPlayingChanges(self.toonIds, newToonIds) + if base.localAvatar.doId in exitedToons: + self.localToonExiting() + self._processExitedToons(exitedToons) + self.setToonIds([leftTeamToonIds, rightTeamToonIds]) + self._processJoinedToons(joinedToons) + for toonId in shifters: + self.handleToonShifted(toonId) + + if self._canSwitchTeams: + for toonId in switchers: + self.handleToonSwitchedTeams(toonId) + + if self.isState('WaitForEnough'): + self._updateWaitForEnoughStatus() + + def _updateWaitForEnoughStatus(self): + amount = self.getNumToonsNeededToStart() + if self._willBalanceTeams: + text = TTLocalizer.PartyTeamActivityForMoreWithBalance + else: + text = TTLocalizer.PartyTeamActivityForMore + if amount > 1: + plural = TTLocalizer.PartyTeamActivityForMorePlural + else: + plural = '' + self.setStatus(text % (amount, plural)) + + def setState(self, newState, timestamp, data): + DistributedPartyActivity.setState(self, newState, timestamp) + if newState == 'WaitToStart': + self.activityFSM.request(newState, timestamp) + elif newState == 'Conclusion': + self.activityFSM.request(newState, data) + else: + self.activityFSM.request(newState) + + def d_toonReady(self): + self.sendUpdate('toonReady') + + def setAdvantage(self, advantage): + self.advantage = advantage + + def handleToonJoined(self, toonId): + if toonId == base.localAvatar.doId: + self.isLocalToonPlaying = True + self.localToonTeam = self.getTeam(base.localAvatar.doId) + self.teamActivityGui.load() + self.teamActivityGui.enableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + if self.activityFSM.state == 'WaitToStart': + self.showWaitToStartCountdown() + else: + self.showStatus() + + def handleToonExited(self, toonId): + if toonId == base.localAvatar.doId: + self.hideStatus() + self.teamActivityGui.disableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.disableSwitchButton() + if self.activityFSM.state == 'WaitToStart': + self.hideWaitToStartCountdown() + self.teamActivityGui.unload() + self.isLocalToonPlaying = False + self.localToonTeam = None + return + + def handleRulesDone(self): + self.notify.debug('handleRulesDone') + self.d_toonReady() + self.activityFSM.request('WaitForServer') + + def handleGameTimerExpired(self): + pass + + def getToonsPlayingChanges(self, oldToonIds, newToonIds): + oldLeftTeam = oldToonIds[0] + oldRightTeam = oldToonIds[1] + newLeftTeam = newToonIds[0] + newRightTeam = newToonIds[1] + oldToons = set(oldLeftTeam + oldRightTeam) + newToons = set(newLeftTeam + newRightTeam) + exitedToons = oldToons.difference(newToons) + joinedToons = newToons.difference(oldToons) + shifters = [] + if self._canSwitchTeams: + switchers = list(set(oldLeftTeam) & set(newRightTeam)) + list(set(oldRightTeam) & set(newLeftTeam)) + else: + switchers = [] + for i in range(len(PartyGlobals.TeamActivityTeams)): + persistentToons = set(oldToonIds[i]) & set(newToonIds[i]) + for toonId in persistentToons: + if oldToonIds[i].index(toonId) != newToonIds[i].index(toonId): + shifters.append(toonId) + + return (list(exitedToons), + list(joinedToons), + shifters, + switchers) + + def getMaxPlayersPerTeam(self): + return self._maxPlayersPerTeam + + def getMinPlayersPerTeam(self): + return self._minPlayersPerTeam + + def getNumToonsNeededToStart(self): + if self._willBalanceTeams: + return abs(self._minPlayersPerTeam * 2 - self.getNumToonsPlaying()) + else: + return self._minPlayersPerTeam + + def getToonIdsAsList(self): + return self.toonIds[0] + self.toonIds[1] + + def getNumToonsPlaying(self): + return len(self.toonIds[0]) + len(self.toonIds[1]) + + def getNumToonsInTeam(self, team): + return len(self.toonIds[team]) + + def getTeam(self, toonId): + for i in range(len(PartyGlobals.TeamActivityTeams)): + if self.toonIds[i].count(toonId) > 0: + return i + else: + return None + + return None + + def getIndex(self, toonId, team): + if self.toonIds[team].count(toonId) > 0: + return self.toonIds[team].index(toonId) + else: + return None + return None + + def _joinLeftTeam(self, collEntry): + if self.isLocalToonInActivity(): + return + self.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.LeftTeam) + + def _joinRightTeam(self, collEntry): + if self.isLocalToonInActivity(): + return + self.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.RightTeam) + + def showWaitToStartCountdown(self): + if self.waitToStartTimestamp is None: + self.notify.warning('showWaitToStartCountdown was called when self.waitToStartTimestamp was None') + return + self.teamActivityGui.showWaitToStartCountdown(self._startDelay, self.waitToStartTimestamp, almostDoneCallback=self._onCountdownAlmostDone) + self.showStatus() + self.teamActivityGui.enableExitButton() + return + + def _onCountdownAlmostDone(self): + if self._canSwitchTeams: + self.teamActivityGui.disableSwitchButton() + + def hideWaitToStartCountdown(self): + self.teamActivityGui.hideWaitToStartCountdown() + self.teamActivityGui.disableExitButton() + self.hideStatus() + + def setStatus(self, text): + self._currentStatus = text + if self.isLocalToonPlaying: + self.showStatus() + + def showStatus(self): + if self.teamActivityGui is not None: + self.teamActivityGui.showStatus(self._currentStatus) + return + + def hideStatus(self): + if self.teamActivityGui is not None: + self.teamActivityGui.hideStatus() + return + + def toonsCanSwitchTeams(self): + return self._canSwitchTeams + + def isState(self, state): + return hasattr(self, 'activityFSM') and self.activityFSM.getCurrentOrNextState() == state + + def startWaitForEnough(self): + self.notify.debug('startWaitForEnough') + self.advantage = 1.0 + self._updateWaitForEnoughStatus() + if self.isLocalToonPlaying: + self.teamActivityGui.enableExitButton() + if self._canSwitchTeams: + self.teamActivityGui.enableSwitchButton() + + def finishWaitForEnough(self): + self.notify.debug('finishWaitForEnough') + + def startWaitToStart(self, waitStartTimestamp): + self.notify.debug('startWaitToStart') + self.waitToStartTimestamp = globalClockDelta.networkToLocalTime(waitStartTimestamp) + self._updateWaitForEnoughStatus() + self.setStatus(TTLocalizer.PartyTeamActivityWaitingToStart) + if self.isLocalToonPlaying: + self.showWaitToStartCountdown() + + def finishWaitToStart(self): + self.notify.debug('finishWaitToStart') + if self.isLocalToonPlaying: + self.hideWaitToStartCountdown() + if self._canSwitchTeams: + self.teamActivityGui.disableSwitchButton() + self.waitToStartTimestamp = None + return + + def startRules(self): + self.notify.debug('startRules') + if self.isLocalToonPlaying: + DistributedPartyActivity.startRules(self) + + def finishRules(self): + self.notify.debug('finishRules') + if self.isLocalToonPlaying: + DistributedPartyActivity.finishRules(self) + if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough': + DistributedPartyActivity.finishRules(self) + + def startWaitForServer(self): + self.notify.debug('startWaitForServer') + self.setStatus(TTLocalizer.PartyTeamActivityWaitingForOtherPlayers) + + def finishWaitForServer(self): + self.notify.debug('finishWaitForServer') + if self.isLocalToonPlaying: + self.hideStatus() + + def startActive(self): + self.notify.debug('startActive') + if self.isLocalToonPlaying: + self.hideStatus() + self.teamActivityGui.showTimer(self._duration) + + def finishActive(self): + self.notify.debug('finishActive') + if self.isLocalToonPlaying: + self.teamActivityGui.hideTimer() + self.hideStatus() + + def startConclusion(self, data): + self.notify.debug('startConclusion') + self.setStatus('') + if self.isLocalToonPlaying: + self.localToonExiting() + + def finishConclusion(self): + self.notify.debug('finishConclusion') + if self.isLocalToonPlaying: + self.hideStatus() diff --git a/toontown/parties/DistributedPartyTrampolineActivity.py b/toontown/parties/DistributedPartyTrampolineActivity.py new file mode 100644 index 0000000..3cc881e --- /dev/null +++ b/toontown/parties/DistributedPartyTrampolineActivity.py @@ -0,0 +1,676 @@ +import math +import time +import random +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.actor.Actor import Actor +from direct.fsm.FSM import FSM +from direct.gui.DirectGui import DirectButton +from direct.gui.OnscreenText import OnscreenText +from direct.interval.FunctionInterval import Func +from direct.interval.FunctionInterval import Wait +from direct.interval.LerpInterval import LerpFunc +from direct.interval.MetaInterval import Parallel +from direct.interval.MetaInterval import Sequence +from pandac.PandaModules import CardMaker +from pandac.PandaModules import NodePath +from pandac.PandaModules import TextNode +from pandac.PandaModules import Point3 +from pandac.PandaModules import Vec3 +from pandac.PandaModules import VBase3 +from pandac.PandaModules import VBase4 +from pandac.PandaModules import CollisionSphere +from pandac.PandaModules import CollisionTube +from pandac.PandaModules import CollisionNode +from pandac.PandaModules import BitMask32 +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyActivity import DistributedPartyActivity +from toontown.parties.activityFSMs import TrampolineActivityFSM +from toontown.parties import PartyUtils + +class DistributedPartyTrampolineActivity(DistributedPartyActivity): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyTrampolineActivity') + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.notify.debug('__init__') + DistributedPartyActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTrampoline, PartyGlobals.ActivityTypes.GuestInitiated, wantLever=False, wantRewardGui=True) + self.doJellyBeans = doJellyBeans + self.doTricks = doTricks + self.texture = texture + self.toon = None + self.trampHeight = 3.6 + self.trampK = 400.0 + self.normalTrampB = 2.5 + self.leavingTrampB = 8.0 + self.trampB = self.normalTrampB + self.g = -32.0 + self.jumpBoost = 330.0 + self.beginningBoost = 500.0 + self.beginningBoostThreshold = self.trampHeight + 1.5 + self.earlyJumpThreshold = 75.0 + self.boingThreshold = 300.0 + self.turnFactor = 120.0 + self.stepDT = 0.001 + self.targetCameraPos = Point3(0.0, 40.0, 10.0) + self.cameraSpeed = 2.0 + self.hopOffPos = Point3(16.0, 0.0, 0.0) + self.indicatorFactor = 0.0095 + self.dropShadowCutoff = 15.0 + self.minHeightForText = 15.0 + self.heightTextOffset = -0.065 + self.beanOffset = 0.5 + self.guiBeanOffset = -0.02 + self.jumpTextShown = False + self.toonJumped = False + self.turnLeft = False + self.turnRight = False + self.leavingTrampoline = False + self.toonVelocity = 0.0 + self.topHeight = 0.0 + self.lastPeak = 0.0 + self.beginRoundInterval = None + self.hopOnAnim = None + self.hopOffAnim = None + self.flashTextInterval = None + self.numJellyBeans = PartyGlobals.TrampolineNumJellyBeans + self.jellyBeanBonus = PartyGlobals.TrampolineJellyBeanBonus + self.jellyBeanStartHeight = 20.0 + self.jellyBeanStopHeight = 90.0 + self.jellyBeanColors = [VBase4(1.0, 0.5, 0.5, 1.0), + VBase4(0.5, 1.0, 0.5, 1.0), + VBase4(0.5, 1.0, 1.0, 1.0), + VBase4(1.0, 1.0, 0.4, 1.0), + VBase4(0.4, 0.4, 1.0, 1.0), + VBase4(1.0, 0.5, 1.0, 1.0)] + delta = (self.jellyBeanStopHeight - self.jellyBeanStartHeight) / (self.numJellyBeans - 1) + self.jellyBeanPositions = [ self.jellyBeanStartHeight + n * delta for n in range(self.numJellyBeans) ] + self.doSimulateStep = False + return + + def load(self): + DistributedPartyTrampolineActivity.notify.debug('load') + DistributedPartyActivity.load(self) + self.loadModels() + self.loadCollision() + self.loadGUI() + self.loadSounds() + self.loadIntervals() + self.activityFSM = TrampolineActivityFSM(self) + self.activityFSM.request('Idle') + self.animFSM = TrampolineAnimFSM(self) + self.setBestHeightInfo('', 0) + + def loadModels(self): + self.tramp = self.root.attachNewNode(self.uniqueName('tramp')) + self.screenPlaneElements = NodePath(self.uniqueName('screenPlane')) + self.trampActor = Actor('phase_13/models/parties/trampoline_model', {'emptyAnim': 'phase_13/models/parties/trampoline_anim'}) + self.trampActor.reparentTo(self.tramp) + if self.texture: + reskinNode = self.tramp.find('**/trampoline/__Actor_modelRoot/-GeomNode') + reskinNode.setTexture(loader.loadTexture(self.texture), 100) + self.surface = NodePath(self.uniqueName('trampSurface')) + self.surface.reparentTo(self.tramp) + self.surface.setZ(self.trampHeight) + self.trampActor.controlJoint(self.surface, 'modelRoot', 'trampoline_joint1') + self.sign.setPos(PartyGlobals.TrampolineSignOffset) + self.beans = [ loader.loadModelCopy('phase_4/models/props/jellybean4') for i in range(self.numJellyBeans) ] + for bean in self.beans: + bean.find('**/jellybean').setP(-35.0) + bean.setScale(3.0) + bean.setTransparency(True) + bean.reparentTo(self.tramp) + bean.stash() + + self.beans[-1].setScale(8.0) + + def loadCollision(self): + collTube = CollisionTube(0.0, 0.0, 0.0, 0.0, 0.0, 6.0, 5.4) + collTube.setTangible(True) + self.trampolineCollision = CollisionNode(self.uniqueName('TrampolineCollision')) + self.trampolineCollision.addSolid(collTube) + self.trampolineCollision.setCollideMask(OTPGlobals.CameraBitmask | OTPGlobals.WallBitmask) + self.trampolineCollisionNP = self.tramp.attachNewNode(self.trampolineCollision) + collSphere = CollisionSphere(0.0, 0.0, 0.0, 7.0) + collSphere.setTangible(False) + self.trampolineTrigger = CollisionNode(self.uniqueName('TrampolineTrigger')) + self.trampolineTrigger.addSolid(collSphere) + self.trampolineTrigger.setIntoCollideMask(OTPGlobals.WallBitmask) + self.trampolineTriggerNP = self.tramp.attachNewNode(self.trampolineTrigger) + self.accept('enter%s' % self.uniqueName('TrampolineTrigger'), self.onTrampolineTrigger) + + def loadGUI(self): + self.gui = loader.loadModel('phase_13/models/parties/trampolineGUI') + self.gui.setX(-1.15) + self.gui.reparentTo(self.screenPlaneElements) + self.toonIndicator = self.gui.find('**/trampolineGUI_MovingBar') + jumpLineLocator = self.gui.find('**/jumpLine_locator') + guiBean = self.gui.find('**/trampolineGUI_GreenJellyBean') + self.gui.find('**/trampolineGUI_GreenJellyBean').stash() + self.guiBeans = [ guiBean.instanceUnderNode(jumpLineLocator, self.uniqueName('guiBean%d' % i)) for i in range(self.numJellyBeans) ] + self.guiBeans[-1].setScale(1.5) + heightTextNode = TextNode(self.uniqueName('TrampolineActivity.heightTextNode')) + heightTextNode.setFont(ToontownGlobals.getSignFont()) + heightTextNode.setAlign(TextNode.ALeft) + heightTextNode.setText('0.0') + heightTextNode.setShadow(0.05, 0.05) + heightTextNode.setShadowColor(0.0, 0.0, 0.0, 1.0) + heightTextNode.setTextColor(1.0, 1.0, 1.0, 1.0) + self.heightText = jumpLineLocator.attachNewNode(heightTextNode) + self.heightText.setX(0.15) + self.heightText.setScale(0.1) + self.heightText.setAlphaScale(0.0) + self.quitEarlyButtonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + quitEarlyUp = self.quitEarlyButtonModels.find('**//InventoryButtonUp') + quitEarlyDown = self.quitEarlyButtonModels.find('**/InventoryButtonDown') + quitEarlyRollover = self.quitEarlyButtonModels.find('**/InventoryButtonRollover') + self.quitEarlyButton = DirectButton(parent=self.screenPlaneElements, relief=None, text=TTLocalizer.PartyTrampolineQuitEarlyButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.7, image=(quitEarlyUp, quitEarlyDown, quitEarlyRollover), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(1.15, 0, 0.6), scale=0.09, command=self.leaveTrampoline) + self.quitEarlyButton.stash() + self.flashText = OnscreenText(text='', pos=(0.0, -0.45), scale=0.2, fg=(1.0, 1.0, 0.65, 1.0), align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), mayChange=True) + self.timer = PartyUtils.getNewToontownTimer() + self.timer.reparentTo(self.screenPlaneElements) + return + + def loadSounds(self): + self.jellyBeanSound = base.loadSfx('phase_4/audio/sfx/sparkly.mp3') + self.boingSound = base.loadSfx('phase_4/audio/sfx/target_trampoline_2.mp3') + self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.mp3') + + def loadIntervals(self): + + def prepareHeightText(): + self.heightText.node().setText(TTLocalizer.PartyTrampolineGetHeight % int(self.toon.getZ())) + self.heightText.setZ(self.indicatorFactor * self.toon.getZ() + self.heightTextOffset) + + self.heightTextInterval = Sequence(Func(prepareHeightText), LerpFunc(self.heightText.setAlphaScale, fromData=1.0, toData=0.0, duration=1.0)) + + def unload(self): + DistributedPartyTrampolineActivity.notify.debug('unload') + if self.hopOnAnim and self.hopOnAnim.isPlaying(): + self.hopOnAnim.finish() + if self.hopOffAnim and self.hopOffAnim.isPlaying(): + self.hopOffAnim.finish() + if self.beginRoundInterval and self.beginRoundInterval.isPlaying(): + self.beginRoundInterval.finish() + if self.flashTextInterval and self.flashTextInterval.isPlaying(): + self.flashTextInterval.finish() + if self.heightTextInterval and self.heightTextInterval.isPlaying(): + self.heightTextInterval.finish() + self.timer.stop() + DistributedPartyActivity.unload(self) + taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask')) + taskMgr.remove(self.uniqueName('TrampolineActivity.remoteUpdateTask')) + self.ignoreAll() + del self.heightTextInterval + del self.beginRoundInterval + del self.hopOnAnim + del self.hopOffAnim + del self.flashTextInterval + if hasattr(self, 'beanAnims'): + self.cleanupJellyBeans() + self.quitEarlyButton.destroy() + del self.quitEarlyButton + if self.screenPlaneElements: + self.screenPlaneElements.removeNode() + self.screenPlaneElements = None + del self.activityFSM + del self.animFSM + return + + def setBestHeightInfo(self, toonName, height): + self.bestHeightInfo = (toonName, height) + DistributedPartyTrampolineActivity.notify.debug('%s has the best height of %d' % (toonName, height)) + if height > 0: + self.setSignNote(TTLocalizer.PartyTrampolineBestHeight % self.bestHeightInfo) + else: + self.setSignNote(TTLocalizer.PartyTrampolineNoHeightYet) + + def leaveTrampoline(self): + if self.toon != None and self.toon.doId == base.localAvatar.doId: + self._showFlashMessage(TTLocalizer.PartyTrampolineTimesUp) + self.leavingTrampoline = True + self.timer.reset() + self.trampB = self.leavingTrampB + self.ignore('control') + self.quitEarlyButton.stash() + return + + def requestAnim(self, request): + self.animFSM.request(request) + + def b_requestAnim(self, request): + self.requestAnim(request) + self.sendUpdate('requestAnim', [request]) + + def requestAnimEcho(self, request): + if self.toon != None and self.toon.doId != base.localAvatar.doId: + self.requestAnim(request) + return + + def removeBeans(self, beansToRemove): + for i in beansToRemove: + height, bean, guiBean, beanAnim = self.beanDetails[i] + guiBean.stash() + if i in self.beansToCollect: + self.beansToCollect.remove(i) + else: + self.notify.warning('removeBeans avoided a crash, %d not in self.beansToCollect' % i) + self.poofBean(bean, beanAnim) + + def b_removeBeans(self, beansToRemove): + self.removeBeans(beansToRemove) + self.sendUpdate('removeBeans', [beansToRemove]) + + def removeBeansEcho(self, beansToRemove): + if self.toon != None and self.toon.doId != base.localAvatar.doId: + self.removeBeans(beansToRemove) + return + + def joinRequestDenied(self, reason): + DistributedPartyActivity.joinRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyActivityDefaultJoinDeny) + base.cr.playGame.getPlace().fsm.request('walk') + + def exitRequestDenied(self, reason): + DistributedPartyActivity.exitRequestDenied(self, reason) + self.showMessage(TTLocalizer.PartyActivityDefaultExitDeny) + + def setState(self, newState, timestamp): + DistributedPartyTrampolineActivity.notify.debug('setState( newState=%s, ... )' % newState) + DistributedPartyActivity.setState(self, newState, timestamp) + self.activityFSM.request(newState) + + def startIdle(self): + DistributedPartyTrampolineActivity.notify.debug('startIdle') + + def finishIdle(self): + DistributedPartyTrampolineActivity.notify.debug('finishIdle') + + def startRules(self): + DistributedPartyTrampolineActivity.notify.debug('startRules') + if self.doJellyBeans: + self.setupJellyBeans() + if self.toon != None and self.toon.doId == base.localAvatar.doId: + self.acquireToon() + return + + def startActive(self): + DistributedPartyTrampolineActivity.notify.debug('startActive') + if self.toon != None and self.toon.doId == base.localAvatar.doId: + base.setCellsAvailable(base.bottomCells, True) + self.accept('arrow_left', self.onLeft) + self.accept('arrow_left-up', self.onLeftUp) + self.accept('arrow_right', self.onRight) + self.accept('arrow_right-up', self.onRightUp) + self.beginRoundInterval = Sequence(Func(self._showFlashMessage, TTLocalizer.PartyTrampolineReady), Wait(1.2), Func(self.flashMessage, TTLocalizer.PartyTrampolineGo), Func(self.beginRound)) + self.beginRoundInterval.start() + return + + def finishActive(self): + DistributedPartyTrampolineActivity.notify.debug('finishActive') + if self.doJellyBeans: + self.cleanupJellyBeans() + + def setupJellyBeans(self): + self.beanAnims = [] + self.beansToCollect = [] + self.beanDetails = [] + self.numBeansCollected = 0 + for i in range(self.numJellyBeans): + bean = self.beans[i] + guiBean = self.guiBeans[i] + height = self.jellyBeanPositions[i] + color = random.choice(self.jellyBeanColors) + bean.find('**/jellybean').setColor(color) + if self.toon.doId == base.localAvatar.doId: + bean.setAlphaScale(1.0) + else: + bean.setAlphaScale(0.5) + guiBean.setColor(color) + bean.setZ(height + self.toon.getHeight() + self.beanOffset) + guiBean.setZ(height * self.indicatorFactor + self.guiBeanOffset) + bean.setH(0.0) + bean.unstash() + guiBean.unstash() + beanAnim = bean.hprInterval(1.5, VBase3((i % 2 * 2 - 1) * 360.0, 0.0, 0.0)) + beanAnim.loop() + self.beanAnims.append(beanAnim) + self.beanDetails.append((height, + bean, + guiBean, + beanAnim)) + + self.beansToCollect = range(self.numJellyBeans) + + def cleanupJellyBeans(self): + for bean in self.beans: + bean.stash() + + for guiBean in self.guiBeans: + guiBean.stash() + + if hasattr(self, 'beanAnims'): + for beanAnim in self.beanAnims: + beanAnim.finish() + + del self.beanAnims + del self.beansToCollect + + def beginRound(self): + base.playSfx(self.whistleSound) + self.timer.setTime(PartyGlobals.TrampolineDuration) + self.timer.countdown(PartyGlobals.TrampolineDuration) + self.timer.show() + self.quitEarlyButton.unstash() + self.notify.debug('Accepting contorl') + self.accept('control', self.onJump) + self.notify.debug('setting simulate step to true') + self.doSimulateStep = True + + def acquireToon(self): + self.toon.disableSmartCameraViews() + self.toon.stopUpdateSmartCamera() + camera.wrtReparentTo(render) + self.toon.dropShadow.reparentTo(hidden) + self.toon.startPosHprBroadcast(period=0.2) + self.toonAcceleration = 0.0 + self.toonVelocity = 0.0 + self.topHeight = 0.0 + self.trampB = self.normalTrampB + self.leavingTrampoline = False + self.hopOnAnim = Sequence(Func(self.toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, self.toon, Point3(0.0, 0.0, self.trampHeight), 5.0, self.tramp), Func(self.postHopOn)) + self.hopOnAnim.start() + + def postHopOn(self): + self.toon.setH(self.toon.getH() + 90.0) + self.toon.dropShadow.reparentTo(self.surface) + self.screenPlaneElements.reparentTo(aspect2d) + self.timeLeftToSimulate = 0.0 + self.doSimulateStep = False + taskMgr.add(self.updateTask, self.uniqueName('TrampolineActivity.updateTask')) + base.setCellsAvailable(base.leftCells, False) + base.setCellsAvailable(base.bottomCells, False) + DistributedPartyActivity.startRules(self) + + def releaseToon(self): + self._hideFlashMessage() + self.ignore('arrow_left') + self.ignore('arrow_left-up') + self.ignore('arrow_right') + self.ignore('arrow_right-up') + taskMgr.remove(self.uniqueName('TrampolineActivity.updateTask')) + self.hopOffAnim = Sequence(self.toon.hprInterval(0.5, VBase3(-90.0, 0.0, 0.0), other=self.tramp), Func(self.toon.b_setAnimState, 'jump', 1.0), Func(self.toon.dropShadow.reparentTo, hidden), Wait(0.4), PartyUtils.arcPosInterval(0.75, self.toon, self.hopOffPos, 5.0, self.tramp), Func(self.postHopOff)) + self.hopOffAnim.start() + + def postHopOff(self): + self.screenPlaneElements.reparentTo(hidden) + base.setCellsAvailable(base.leftCells, True) + self.timer.stop() + self.timer.hide() + self.toon.dropShadow.reparentTo(self.toon.getShadowJoint()) + self.toon.dropShadow.setAlphaScale(1.0) + self.toon.dropShadow.setScale(1.0) + self.b_requestAnim('Off') + camera.reparentTo(base.localAvatar) + base.localAvatar.startUpdateSmartCamera() + base.localAvatar.enableSmartCameraViews() + base.localAvatar.setCameraPositionByIndex(base.localAvatar.cameraIndex) + place = base.cr.playGame.getPlace() + if self.doJellyBeans: + self.sendUpdate('awardBeans', [self.numBeansCollected, int(self.topHeight)]) + if int(self.topHeight) > self.bestHeightInfo[1]: + self.sendUpdate('reportHeightInformation', [int(self.topHeight)]) + self.d_toonExitDemand() + + def onTrampolineTrigger(self, collEntry): + if self.activityFSM.state == 'Idle' and self.toon == None and base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'walk': + base.cr.playGame.getPlace().fsm.request('activity') + self.d_toonJoinRequest() + else: + self.flashMessage(TTLocalizer.PartyTrampolineActivityOccupied, duration=2.0) + return + + def onJump(self): + self.notify.debug('got onJump') + if self.toon != None and self.toon.getZ() < self.trampHeight: + self.toonJumped = True + self.b_requestAnim('Jump') + else: + self.notify.debug('z is less than tramp height') + return + + def onLeft(self): + self.turnLeft = True + + def onLeftUp(self): + self.turnLeft = False + + def onRight(self): + self.turnRight = True + + def onRightUp(self): + self.turnRight = False + + def handleToonJoined(self, toonId): + DistributedPartyTrampolineActivity.notify.debug('handleToonJoined') + self.toon = self.getAvatar(toonId) + if self.toon != None and not self.toon.isEmpty(): + self.oldJumpSquatPlayRate = self.toon.getPlayRate('jump-squat') + self.oldJumpLandPlayRate = self.toon.getPlayRate('jump-land') + self.toon.setPlayRate(2.5, 'jump-squat') + self.toon.setPlayRate(2.0, 'jump-land') + self.turnLeft = False + self.turnRight = False + self.activityFSM.request('Rules') + if self.toon.doId != base.localAvatar.doId: + taskMgr.add(self.remoteUpdateTask, self.uniqueName('TrampolineActivity.remoteUpdateTask')) + else: + self.notify.warning('handleToonJoined could not get toon %d' % toonId) + return + + def handleToonExited(self, toonId): + DistributedPartyTrampolineActivity.notify.debug('handleToonExited') + if self.toon != None: + if self.toon.doId != base.localAvatar.doId: + taskMgr.remove(self.uniqueName('TrampolineActivity.remoteUpdateTask')) + self.surface.setZ(self.trampHeight) + self.toon.setPlayRate(self.oldJumpSquatPlayRate, 'jump-squat') + self.toon.setPlayRate(self.oldJumpLandPlayRate, 'jump-land') + self.toon = None + return + + def handleToonDisabled(self, toonId): + DistributedPartyTrampolineActivity.notify.debug('handleToonDisabled') + DistributedPartyTrampolineActivity.notify.debug('avatar ' + str(toonId) + ' disabled') + if base.localAvatar.doId == toonId: + self.releaseToon() + + def handleRulesDone(self): + self.sendUpdate('toonReady') + self.finishRules() + + def getTitle(self): + if self.doJellyBeans: + return TTLocalizer.PartyTrampolineJellyBeanTitle + elif self.doTricks: + return TTLocalizer.PartyTrampolineTricksTitle + else: + return DistributedPartyActivity.getTitle(self) + + def getInstructions(self): + return TTLocalizer.PartyTrampolineActivityInstructions + + def updateTask(self, task): + z = self.toon.getZ() + dt = globalClock.getDt() + if self.doSimulateStep: + self.timeLeftToSimulate += dt + while self.timeLeftToSimulate >= self.stepDT: + z, a = self.simulateStep(z) + self.timeLeftToSimulate -= self.stepDT + + self.toon.setZ(z) + if z <= self.trampHeight: + self.surface.setZ(z) + else: + self.surface.setZ(self.trampHeight) + self.toonIndicator.setZ((z - self.trampHeight) * self.indicatorFactor) + if self.turnLeft: + self.toon.setH(self.toon.getH() + self.turnFactor * dt) + if self.turnRight: + self.toon.setH(self.toon.getH() - self.turnFactor * dt) + currentPos = base.camera.getPos(self.toon) + vec = self.targetCameraPos - currentPos + newPos = currentPos + vec * (dt * self.cameraSpeed) + base.camera.setPos(self.toon, newPos) + base.camera.lookAt(self.toon) + if z > self.trampHeight: + heightFactor = 1.0 - min(1.0, (z - self.trampHeight) / self.dropShadowCutoff) + self.toon.dropShadow.setAlphaScale(heightFactor) + self.toon.dropShadow.setScale(max(0.1, heightFactor)) + else: + self.toon.dropShadow.setAlphaScale(1.0) + self.toon.dropShadow.setScale(1.0) + if self.leavingTrampoline and z < self.trampHeight and abs(a) < 0.1: + self.releaseToon() + return Task.cont + + def simulateStep(self, z): + if z >= self.trampHeight: + a = self.g + self.toonJumped = False + else: + a = self.g + self.trampK * (self.trampHeight - z) - self.trampB * self.toonVelocity + if self.toonJumped: + if self.lastPeak > self.earlyJumpThreshold or self.toonVelocity >= -300000.0: + a += self.jumpBoost + if self.lastPeak < self.beginningBoostThreshold: + a += self.beginningBoost + lastVelocity = self.toonVelocity + self.toonVelocity += a * self.stepDT + if lastVelocity > 0.0 and self.toonVelocity <= 0.0: + topOfJump = True + bottomOfJump = False + elif lastVelocity < 0.0 and self.toonVelocity >= 0.0: + topOfJump = False + bottomOfJump = True + else: + topOfJump = False + bottomOfJump = False + newZ = z + self.toonVelocity * self.stepDT + if newZ > self.topHeight: + self.topHeight = newZ + if self.doJellyBeans: + self.collectJellyBeans(newZ) + if topOfJump: + self.lastPeak = newZ + if newZ >= self.minHeightForText: + self.heightTextInterval.start() + if topOfJump: + if newZ > self.trampHeight + 20.0: + self.b_requestAnim('Falling') + elif self.animFSM.state == 'Jump': + self.b_requestAnim('Falling') + if newZ <= self.trampHeight and z > self.trampHeight: + if self.animFSM.state == 'Falling': + self.b_requestAnim('Land') + elif self.animFSM.state != 'Neutral': + self.b_requestAnim('Neutral') + if bottomOfJump and a > self.boingThreshold: + base.playSfx(self.boingSound) + return (newZ, a) + + def collectJellyBeans(self, z): + beansToRemove = [] + for i in self.beansToCollect: + height = self.beanDetails[i][0] + if height <= z: + beansToRemove.append(i) + + if len(beansToRemove) > 0: + base.playSfx(self.jellyBeanSound) + self.numBeansCollected += len(beansToRemove) + self.b_removeBeans(beansToRemove) + + def remoteUpdateTask(self, task): + if self.toon != None and not self.toon.isEmpty(): + z = self.toon.getZ() + if z <= self.trampHeight: + self.surface.setZ(z) + else: + self.surface.setZ(self.trampHeight) + return Task.cont + + def poofBean(self, bean, beanAnim): + if bean == None: + self.notify.warning('poofBean, returning immediately as bean is None') + return + if bean.isEmpty(): + self.notify.warning('poofBean, returning immediately as bean is empty') + return + currentAlpha = bean.getColorScale()[3] + currentScale = bean.getScale() + poofAnim = Sequence(Parallel(LerpFunc(bean.setAlphaScale, fromData=currentAlpha, toData=0.0, duration=0.25), LerpFunc(bean.setScale, fromData=currentScale, toData=currentScale * 5.0, duration=0.25)), Func(bean.stash), Func(beanAnim.finish), Func(bean.setAlphaScale, currentAlpha), Func(bean.setScale, currentScale)) + poofAnim.start() + return + + def _showFlashMessage(self, message): + if self.isDisabled(): + return + if self.flashTextInterval is not None and self.flashTextInterval.isPlaying(): + self.flashTextInterval.finish() + self.flashText.setText(message) + self.flashText.setAlphaScale(1.0) + self.flashText.unstash() + return + + def _hideFlashMessage(self, duration = 0.0): + if self.isDisabled(): + pass + self.flashTextInterval = Sequence(Wait(duration), LerpFunc(self.flashText.setAlphaScale, fromData=1.0, toData=0.0, duration=1.0), Func(self.flashText.stash)) + self.flashTextInterval.start() + + def flashMessage(self, message, duration = 0.5): + self._showFlashMessage(message) + self._hideFlashMessage(duration) + + +class TrampolineAnimFSM(FSM): + + def __init__(self, activity): + FSM.__init__(self, self.__class__.__name__) + self.activity = activity + + def defaultFilter(self, request, args): + if request == self.state: + return None + else: + return FSM.defaultFilter(self, request, args) + return None + + def enterNeutral(self): + self.activity.toon.play('neutral') + + def exitNeutral(self): + pass + + def enterJump(self): + self.activity.toon.play('jump-squat') + + def exitJump(self): + pass + + def enterFalling(self): + self.activity.toon.loop('jump-idle') + + def exitFalling(self): + self.activity.toon.stop('jump-idle') + + def enterLand(self): + self.activity.toon.play('jump-land') + + def exitLand(self): + pass diff --git a/toontown/parties/DistributedPartyTugOfWarActivity.py b/toontown/parties/DistributedPartyTugOfWarActivity.py new file mode 100644 index 0000000..4415d95 --- /dev/null +++ b/toontown/parties/DistributedPartyTugOfWarActivity.py @@ -0,0 +1,697 @@ +import math +from pandac.PandaModules import CollisionTube +from pandac.PandaModules import CollisionNode +from pandac.PandaModules import Point3 +from pandac.PandaModules import VBase3 +from pandac.PandaModules import RopeNode +from direct.interval.IntervalGlobal import LerpPosHprInterval +from direct.interval.IntervalGlobal import LerpPosInterval +from direct.interval.IntervalGlobal import Wait +from direct.interval.IntervalGlobal import ActorInterval +from direct.interval.MetaInterval import Sequence +from direct.interval.MetaInterval import Parallel +from direct.interval.FunctionInterval import Func +from direct.showutil.Rope import Rope +from direct.showbase.PythonUtil import fitDestAngle2Src +from direct.fsm.StatePush import StateVar, FunctionCall +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.effects import Splash +from toontown.minigame.MinigamePowerMeter import MinigamePowerMeter +from toontown.minigame.ArrowKeys import ArrowKeys +import PartyGlobals +import PartyUtils +from DistributedPartyTeamActivity import DistributedPartyTeamActivity + +class DistributedPartyTugOfWarActivity(DistributedPartyTeamActivity): + notify = directNotify.newCategory('DistributedPartyTugOfWarActivity') + + def __init__(self, cr): + DistributedPartyTeamActivity.__init__(self, cr, PartyGlobals.ActivityIds.PartyTugOfWar, startDelay=PartyGlobals.TugOfWarStartDelay) + self.buttons = [0, 1] + self.arrowKeys = None + self.keyTTL = [] + self.idealRate = 0.0 + self.keyRate = 0 + self.allOutMode = False + self.rateMatchAward = 0.0 + self.toonIdsToStartPositions = {} + self.toonIdsToIsPullingFlags = {} + self.toonIdsToRightHands = {} + self.fallenToons = [] + self.fallenPositions = [] + self.unusedFallenPositionsIndices = [0, + 1, + 2, + 3] + self.toonIdsToAnimIntervals = {} + self.tugRopes = [] + return + + def generate(self): + DistributedPartyTeamActivity.generate(self) + self._hopOffFinishedSV = StateVar(True) + self._rewardFinishedSV = StateVar(True) + self._isWalkStateReadyFC = FunctionCall(self._testWalkStateReady, self._hopOffFinishedSV, self._rewardFinishedSV) + + def delete(self): + self._isWalkStateReadyFC.destroy() + self._hopOffFinishedSV.destroy() + self._rewardFinishedSV.destroy() + DistributedPartyTeamActivity.delete(self) + + def handleToonJoined(self, toonId): + DistributedPartyTeamActivity.handleToonJoined(self, toonId) + self.toonIdsToAnimIntervals[toonId] = None + if toonId == base.localAvatar.doId: + base.cr.playGame.getPlace().fsm.request('activity') + camera.wrtReparentTo(self.root) + self.cameraMoveIval = LerpPosHprInterval(camera, 1.5, PartyGlobals.TugOfWarCameraPos, PartyGlobals.TugOfWarCameraInitialHpr, other=self.root) + self.cameraMoveIval.start() + self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam) + self.notify.debug('posIndex: %d' % self.localToonPosIndex) + toon = self.getAvatar(toonId) + targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex] + if toon.getZ(self.root) < PartyGlobals.TugOfWarToonPositionZ: + toon.setZ(self.root, PartyGlobals.TugOfWarToonPositionZ) + targetH = fitDestAngle2Src(toon.getH(self.root), PartyGlobals.TugOfWarHeadings[self.localToonTeam]) + travelVector = targetPos - toon.getPos(self.root) + duration = travelVector.length() / 5.0 + if self.toonIdsToAnimIntervals[toonId] is not None: + self.toonIdsToAnimIntervals[toonId].finish() + self.toonIdsToAnimIntervals[toonId] = Sequence(Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), LerpPosHprInterval(toon, duration, targetPos, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral')) + self.toonIdsToAnimIntervals[toonId].start() + return + + def handleToonExited(self, toonId): + DistributedPartyTeamActivity.handleToonExited(self, toonId) + if toonId == base.localAvatar.doId: + self.cameraMoveIval.pause() + if toonId not in self.fallenToons: + if toonId in self.toonIdsToAnimIntervals and self.toonIdsToAnimIntervals[toonId] is not None: + self.toonIdsToAnimIntervals[toonId].finish() + toon = self.getAvatar(toonId) + targetH = fitDestAngle2Src(toon.getH(self.root), 180.0) + targetPos = self.hopOffPositions[self.getTeam(toonId)][self.getIndex(toonId, self.getTeam(toonId))] + hopOffAnim = Sequence(Func(toon.startPosHprBroadcast, 0.1), toon.hprInterval(0.2, VBase3(targetH, 0.0, 0.0), other=self.root), Func(toon.b_setAnimState, 'jump', 1.0), Wait(0.4), PartyUtils.arcPosInterval(0.75, toon, targetPos, 5.0, self.root), Func(toon.stopPosHprBroadcast), Func(toon.sendCurrentPosition), Func(self.hopOffFinished, toonId)) + self.toonIdsToAnimIntervals[toonId] = hopOffAnim + self._hopOffFinishedSV.set(False) + self.toonIdsToAnimIntervals[toonId].start() + else: + self._hopOffFinishedSV.set(True) + del self.toonIdsToAnimIntervals[toonId] + return + + def handleRewardDone(self): + self._rewardFinishedSV.set(True) + + def _testWalkStateReady(self, hoppedOff, rewardFinished): + if hoppedOff and rewardFinished: + DistributedPartyTeamActivity.handleRewardDone(self) + + def hopOffFinished(self, toonId): + if hasattr(self, 'toonIdsToAnimIntervals') and toonId in self.toonIdsToAnimIntervals: + del self.toonIdsToAnimIntervals[toonId] + if toonId == base.localAvatar.doId: + if hasattr(self._hopOffFinishedSV, '_value'): + self._hopOffFinishedSV.set(True) + + def handleToonShifted(self, toonId): + if toonId == base.localAvatar.doId: + self.localToonPosIndex = self.getIndex(base.localAvatar.doId, self.localToonTeam) + if self.toonIdsToAnimIntervals[toonId] is not None: + self.toonIdsToAnimIntervals[toonId].finish() + toon = self.getAvatar(toonId) + targetPos = self.dockPositions[self.localToonTeam][self.localToonPosIndex] + self.toonIdsToAnimIntervals[toonId] = Sequence(Wait(0.6), Func(toon.startPosHprBroadcast, 0.1), Func(toon.b_setAnimState, 'run'), toon.posInterval(0.5, targetPos, other=self.root), Func(toon.stopPosHprBroadcast), Func(toon.b_setAnimState, 'neutral')) + self.toonIdsToAnimIntervals[toonId].start() + return + + def handleToonDisabled(self, toonId): + if self.toonIdsToAnimIntervals.has_key(toonId): + if self.toonIdsToAnimIntervals[toonId]: + if self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + else: + self.notify.debug('self.toonIdsToAnimIntervals[%d] is none' % toonId) + + def setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds): + DistributedPartyTeamActivity.setToonsPlaying(self, leftTeamToonIds, rightTeamToonIds) + self.toonIdsToRightHands.clear() + for toonId in self.getToonIdsAsList(): + toon = self.getAvatar(toonId) + if toon: + self.toonIdsToRightHands[toonId] = toon.getRightHands()[0] + + def load(self): + DistributedPartyTeamActivity.load(self) + self.loadModels() + self.loadGuiElements() + self.loadSounds() + self.loadIntervals() + self.arrowKeys = ArrowKeys() + + def loadModels(self): + self.playArea = loader.loadModel('phase_13/models/parties/partyTugOfWar') + self.playArea.reparentTo(self.root) + self.sign.reparentTo(self.playArea.find('**/TugOfWar_sign_locator')) + self.dockPositions = [[], []] + for i in range(4): + self.dockPositions[0].append(Point3(-PartyGlobals.TugOfWarInitialToonPositionsXOffset - PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ)) + + for i in range(4): + self.dockPositions[1].append(Point3(PartyGlobals.TugOfWarInitialToonPositionsXOffset + PartyGlobals.TugOfWarToonPositionXSeparation * i, 0.0, PartyGlobals.TugOfWarToonPositionZ)) + + self.hopOffPositions = [[], []] + for i in range(1, 5): + self.hopOffPositions[PartyGlobals.TeamActivityTeams.LeftTeam].append(self.playArea.find('**/leftTeamHopOff%d_locator' % i).getPos()) + self.hopOffPositions[PartyGlobals.TeamActivityTeams.RightTeam].append(self.playArea.find('**/rightTeamHopOff%d_locator' % i).getPos()) + + for i in range(1, 5): + pos = self.playArea.find('**/fallenToon%d_locator' % i).getPos() + self.fallenPositions.append(pos) + + self.joinCollision = [] + self.joinCollisionNodePaths = [] + for i in range(len(PartyGlobals.TeamActivityTeams)): + collShape = CollisionTube(PartyGlobals.TugOfWarJoinCollisionEndPoints[0], PartyGlobals.TugOfWarJoinCollisionEndPoints[1], PartyGlobals.TugOfWarJoinCollisionRadius) + collShape.setTangible(True) + self.joinCollision.append(CollisionNode('TugOfWarJoinCollision%d' % i)) + self.joinCollision[i].addSolid(collShape) + tubeNp = self.playArea.attachNewNode(self.joinCollision[i]) + tubeNp.node().setCollideMask(ToontownGlobals.WallBitmask) + self.joinCollisionNodePaths.append(tubeNp) + self.joinCollisionNodePaths[i].setPos(PartyGlobals.TugOfWarJoinCollisionPositions[i]) + + self.__enableCollisions() + ropeModel = loader.loadModel('phase_4/models/minigames/tug_of_war_rope') + self.ropeTexture = ropeModel.findTexture('*') + ropeModel.removeNode() + for i in range(PartyGlobals.TugOfWarMaximumPlayersPerTeam * 2 - 1): + rope = Rope(self.uniqueName('TugRope%d' % i)) + if rope.showRope: + rope.ropeNode.setRenderMode(RopeNode.RMBillboard) + rope.ropeNode.setThickness(0.2) + rope.setTexture(self.ropeTexture) + rope.ropeNode.setUvMode(RopeNode.UVDistance) + rope.ropeNode.setUvDirection(1) + rope.setTransparency(1) + rope.setColor(0.89, 0.89, 0.6, 1.0) + rope.reparentTo(self.root) + rope.stash() + self.tugRopes.append(rope) + + self.splash = Splash.Splash(self.root) + self.splash.setScale(2.0, 4.0, 1.0) + pos = self.fallenPositions[0] + self.splash.setPos(pos[0], pos[1], PartyGlobals.TugOfWarSplashZOffset) + self.splash.hide() + + def loadGuiElements(self): + self.powerMeter = MinigamePowerMeter(PartyGlobals.TugOfWarPowerMeterSize) + self.powerMeter.reparentTo(aspect2d) + self.powerMeter.setPos(0.0, 0.0, 0.6) + self.powerMeter.hide() + self.arrows = [None] * 2 + for x in range(len(self.arrows)): + self.arrows[x] = loader.loadModel('phase_3/models/props/arrow') + self.arrows[x].reparentTo(self.powerMeter) + self.arrows[x].setScale(0.2 - 0.4 * x, 0.2, 0.2) + self.arrows[x].setPos(0.12 - 0.24 * x, 0, -.26) + + return + + def loadSounds(self): + self.splashSound = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.mp3') + self.whistleSound = base.loadSfx('phase_4/audio/sfx/AA_sound_whistle.mp3') + + def loadIntervals(self): + self.updateIdealRateInterval = Sequence() + self.updateIdealRateInterval.append(Wait(PartyGlobals.TugOfWarTargetRateList[0][0])) + for i in range(1, len(PartyGlobals.TugOfWarTargetRateList)): + duration = PartyGlobals.TugOfWarTargetRateList[i][0] + idealRate = PartyGlobals.TugOfWarTargetRateList[i][1] + self.updateIdealRateInterval.append(Func(self.setIdealRate, idealRate)) + if i == len(PartyGlobals.TugOfWarTargetRateList) - 1: + self.updateIdealRateInterval.append(Func(setattr, self, 'allOutMode', True)) + else: + self.updateIdealRateInterval.append(Wait(duration)) + + self.updateKeyPressRateInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressUpdateRate), Func(self.updateKeyPressRate)) + self.reportToServerInterval = Sequence(Wait(PartyGlobals.TugOfWarKeyPressReportRate), Func(self.reportToServer)) + self.setupInterval = Parallel() + self.globalSetupInterval = Sequence(Wait(PartyGlobals.TugOfWarReadyDuration + PartyGlobals.TugOfWarGoDuration), Func(self.tightenRopes)) + self.localSetupInterval = Sequence(Func(self.setStatus, TTLocalizer.PartyTugOfWarReady), Func(self.showStatus), Wait(PartyGlobals.TugOfWarReadyDuration), Func(base.playSfx, self.whistleSound), Func(self.setStatus, TTLocalizer.PartyTugOfWarGo), Wait(PartyGlobals.TugOfWarGoDuration), Func(self.enableKeys), Func(self.hideStatus), Func(self.updateIdealRateInterval.start), Func(self.updateKeyPressRateInterval.loop), Func(self.reportToServerInterval.loop)) + self.splashInterval = Sequence(Func(base.playSfx, self.splashSound), Func(self.splash.play)) + + def unload(self): + DistributedPartyTeamActivity.unload(self) + self.arrowKeys.destroy() + self.unloadIntervals() + self.unloadModels() + self.unloadGuiElements() + self.unloadSounds() + if hasattr(self, 'toonIds'): + del self.toonIds + del self.buttons + del self.arrowKeys + del self.keyTTL + del self.idealRate + del self.keyRate + del self.allOutMode + del self.rateMatchAward + del self.toonIdsToStartPositions + del self.toonIdsToIsPullingFlags + del self.toonIdsToRightHands + del self.fallenToons + del self.fallenPositions + del self.unusedFallenPositionsIndices + self.toonIdsToAnimIntervals.clear() + del self.toonIdsToAnimIntervals + + def unloadModels(self): + self.playArea.removeNode() + del self.playArea + del self.dockPositions + del self.hopOffPositions + self.__disableCollisions() + while len(self.joinCollision) > 0: + collNode = self.joinCollision.pop() + del collNode + + while len(self.joinCollisionNodePaths) > 0: + collNodePath = self.joinCollisionNodePaths.pop() + collNodePath.removeNode() + del collNodePath + + while len(self.tugRopes) > 0: + rope = self.tugRopes.pop() + if rope is not None: + rope.removeNode() + del rope + + del self.tugRopes + self.splash.destroy() + del self.splash + return + + def unloadGuiElements(self): + for arrow in self.arrows: + if arrow is not None: + arrow.removeNode() + del arrow + + del self.arrows + if self.powerMeter is not None: + self.powerMeter.cleanup() + del self.powerMeter + return + + def unloadSounds(self): + del self.splashSound + del self.whistleSound + + def unloadIntervals(self): + self.updateIdealRateInterval.pause() + del self.updateIdealRateInterval + self.updateKeyPressRateInterval.pause() + del self.updateKeyPressRateInterval + self.reportToServerInterval.pause() + del self.reportToServerInterval + self.setupInterval.pause() + del self.setupInterval + self.globalSetupInterval.pause() + del self.globalSetupInterval + self.localSetupInterval.pause() + del self.localSetupInterval + self.splashInterval.pause() + del self.splashInterval + + def __enableCollisions(self): + for i in range(len(PartyGlobals.TeamActivityTeams)): + self.accept('enterTugOfWarJoinCollision%d' % i, getattr(self, '_join%s' % PartyGlobals.TeamActivityTeams.getString(i))) + + def __disableCollisions(self): + for i in range(len(PartyGlobals.TeamActivityTeams)): + self.ignore('enterTugOfWarJoinCollision%d' % i) + + def startWaitForEnough(self): + DistributedPartyTeamActivity.startWaitForEnough(self) + self.__enableCollisions() + + def finishWaitForEnough(self): + DistributedPartyTeamActivity.finishWaitForEnough(self) + self.__disableCollisions() + + def startWaitToStart(self, waitStartTimestamp): + DistributedPartyTeamActivity.startWaitToStart(self, waitStartTimestamp) + self.__enableCollisions() + + def finishWaitToStart(self): + DistributedPartyTeamActivity.finishWaitToStart(self) + self.__disableCollisions() + + def startRules(self): + DistributedPartyTeamActivity.startRules(self) + self.setUpRopes() + if self.isLocalToonPlaying: + self.showControls() + + def finishRules(self): + DistributedPartyTeamActivity.finishRules(self) + if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough': + self.hideRopes() + self.hideControls() + + def finishWaitForServer(self): + DistributedPartyTeamActivity.finishWaitForServer(self) + if self.activityFSM.getCurrentOrNextState() == 'WaitForEnough': + self.hideRopes() + self.hideControls() + + def startActive(self): + DistributedPartyTeamActivity.startActive(self) + self.toonIdsToStartPositions.clear() + self.toonIdsToIsPullingFlags.clear() + for toonId in self.getToonIdsAsList(): + self.toonIdsToIsPullingFlags[toonId] = False + toon = self.getAvatar(toonId) + if toon: + self.toonIdsToStartPositions[toonId] = toon.getPos(self.root) + else: + self.notify.warning("couldn't find toon %d assigning 0,0,0 to startPos" % toonId) + self.toonIdsToStartPositions[toonId] = Point3(0, 0, 0) + + self.unusedFallenPositionsIndices = [0, + 1, + 2, + 3] + self.setupInterval = Parallel(self.globalSetupInterval) + if self.isLocalToonPlaying: + self.keyTTL = [] + self.idealForce = 0.0 + self.keyRate = 0 + self.rateMatchAward = 0.0 + self.allOutMode = False + self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1]) + self.setupInterval.append(self.localSetupInterval) + self.setupInterval.start() + + def finishActive(self): + DistributedPartyTeamActivity.finishActive(self) + self.hideControls() + self.disableKeys() + self.setupInterval.pause() + self.reportToServerInterval.pause() + self.updateKeyPressRateInterval.pause() + self.updateIdealRateInterval.pause() + self.hideRopes() + + def startConclusion(self, losingTeam): + DistributedPartyTeamActivity.startConclusion(self, losingTeam) + if self.isLocalToonPlaying: + self._rewardFinishedSV.set(False) + if losingTeam == PartyGlobals.TeamActivityNeitherTeam: + self.setStatus(TTLocalizer.PartyTeamActivityGameTie) + else: + self.setStatus(TTLocalizer.PartyTugOfWarGameEnd) + self.showStatus() + if losingTeam == PartyGlobals.TeamActivityNeitherTeam: + for toonId in self.getToonIdsAsList(): + if self.getAvatar(toonId): + self.getAvatar(toonId).loop('neutral') + + else: + for toonId in self.toonIds[losingTeam]: + if self.getAvatar(toonId): + self.getAvatar(toonId).loop('neutral') + + for toonId in self.toonIds[1 - losingTeam]: + if self.getAvatar(toonId): + self.getAvatar(toonId).loop('victory') + + for ival in self.toonIdsToAnimIntervals.values(): + if ival is not None: + ival.finish() + + return + + def finishConclusion(self): + DistributedPartyTeamActivity.finishConclusion(self) + self.fallenToons = [] + + def getTitle(self): + return TTLocalizer.PartyTugOfWarTitle + + def getInstructions(self): + return TTLocalizer.TugOfWarInstructions + + def showControls(self): + for arrow in self.arrows: + arrow.setColor(PartyGlobals.TugOfWarDisabledArrowColor) + + self.powerMeter.setTarget(PartyGlobals.TugOfWarTargetRateList[0][1]) + self.powerMeter.setPower(PartyGlobals.TugOfWarTargetRateList[0][1]) + self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5)) + self.powerMeter.clearTooSlowTooFast() + self.powerMeter.show() + + def hideControls(self): + self.powerMeter.hide() + + def setUpRopes(self): + self.notify.debug('setUpRopes') + ropeIndex = 0 + leftToonId = -1 + if self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam]: + leftToonId = self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0] + rightToonId = -1 + if self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam]: + rightToonId = self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0] + if leftToonId in self.toonIdsToRightHands and rightToonId in self.toonIdsToRightHands: + self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[leftToonId], (0, 0, 0)), (self.root, (0.0, 0.0, 2.5)), (self.toonIdsToRightHands[rightToonId], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[ropeIndex].unstash() + ropeIndex += 1 + teams = [PartyGlobals.TeamActivityTeams.LeftTeam, PartyGlobals.TeamActivityTeams.RightTeam] + for currTeam in teams: + numToons = len(self.toonIds[currTeam]) + if numToons > 1: + for i in range(numToons - 1, 0, -1): + toon1 = self.toonIds[currTeam][i] + toon2 = self.toonIds[currTeam][i - 1] + if not self.toonIdsToRightHands.has_key(toon1): + self.notify.warning('Toon in tug of war activity but not properly setup: %s' % toon1) + elif not self.toonIdsToRightHands.has_key(toon2): + self.notify.warning('Toon in tug of war activity but not properly setup: %s' % toon2) + else: + self.notify.debug('Connecting rope between toon %d and toon %d of team %d.' % (i, i - 1, currTeam)) + self.tugRopes[ropeIndex].setup(3, ((self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon1], (0, 0, 0)), (self.toonIdsToRightHands[toon2], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + self.tugRopes[ropeIndex].unstash() + ropeIndex += 1 + + def tightenRopes(self): + self.notify.debug('tightenRopes') + self.tugRopes[0].setup(3, ((self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.LeftTeam][0]], (0, 0, 0)), (self.toonIdsToRightHands[self.toonIds[PartyGlobals.TeamActivityTeams.RightTeam][0]], (0, 0, 0))), [0, + 0, + 0, + 1, + 1, + 1]) + + def hideRopes(self): + self.notify.debug('hideRopes') + for rope in self.tugRopes: + rope.stash() + + def handleGameTimerExpired(self): + self.disableKeys() + + def setIdealRate(self, idealRate): + self.notify.debug('setIdealRate( %d )' % idealRate) + self.idealRate = idealRate + self.idealForce = self.advantage * (4 + 0.4 * self.idealRate) + + def updateKeyPressRate(self): + for i in range(len(self.keyTTL)): + self.keyTTL[i] -= PartyGlobals.TugOfWarKeyPressUpdateRate + + for i in range(len(self.keyTTL)): + if self.keyTTL[i] <= 0.0: + a = self.keyTTL[0:i] + del self.keyTTL + self.keyTTL = a + break + + self.keyRate = len(self.keyTTL) + if self.keyRate == self.idealRate or self.keyRate == self.idealRate + 1: + self.rateMatchAward += 0.3 + else: + self.rateMatchAward = 0.0 + + def reportToServer(self): + self.currentForce = self.computeForce(self.keyRate) + self.sendUpdate('reportKeyRateForce', [self.keyRate, self.currentForce]) + self.setSpeedGauge() + self.setAnimState(base.localAvatar.doId, self.keyRate) + + def computeForce(self, keyRate): + F = 0 + if self.allOutMode: + F = 0.75 * keyRate + else: + stdDev = 0.25 * self.idealRate + F = self.advantage * (self.rateMatchAward + 4 + 0.4 * self.idealRate) * math.pow(math.e, -math.pow(keyRate - self.idealRate, 2) / (2.0 * math.pow(stdDev, 2))) + return F + + def setSpeedGauge(self): + self.powerMeter.setPower(self.keyRate) + self.powerMeter.setTarget(self.idealRate) + if not self.allOutMode: + self.powerMeter.updateTooSlowTooFast() + index = float(self.currentForce) / self.idealForce + bonus = 0.0 + if index > 1.0: + bonus = max(1.0, index - 1.0) + index = 1.0 + color = (0, + 0.75 * index + 0.25 * bonus, + 0.75 * (1 - index), + 0.5) + self.powerMeter.setBarColor(color) + else: + self.powerMeter.setBarColor((0.0, 1.0, 0.0, 0.5)) + + def updateToonKeyRate(self, toonId, keyRate): + if toonId != base.localAvatar.doId: + self.setAnimState(toonId, keyRate) + + def setAnimState(self, toonId, keyRate): + if self.activityFSM.state != 'Active': + return + toon = self.getAvatar(toonId) + if not self.toonIdsToIsPullingFlags.has_key(toonId): + if self.getTeam(toonId) == None: + self.notify.warning("setAnimState called with toonId (%d) that wasn't in self.toonIds" % toonId) + return + else: + self.notify.warning('setAnimState called with toonId (%d) that was in self.toonIds but not in self.toonIdsToIsPullingFlags. Adding it.' % toonId) + self.toonIdsToIsPullingFlags[toonId] = False + if keyRate > 0 and not self.toonIdsToIsPullingFlags[toonId]: + if toon: + toon.loop('tug-o-war') + else: + self.notify.warning('toon %d is None, skipping toon.loop(tugowar)' % toonId) + self.toonIdsToIsPullingFlags[toonId] = True + if keyRate <= 0 and self.toonIdsToIsPullingFlags[toonId]: + if toon: + toon.pose('tug-o-war', 3) + toon.startLookAround() + else: + self.notify.warning('toon %d is None, skipping toon.startLookAround' % toonId) + self.toonIdsToIsPullingFlags[toonId] = False + return + + def enableKeys(self): + self.notify.debug('enableKeys') + self.arrowKeys.setPressHandlers([lambda : self.__pressHandler(2), + lambda : self.__pressHandler(3), + lambda : self.__pressHandler(1), + lambda : self.__pressHandler(0)]) + self.arrowKeys.setReleaseHandlers([lambda : self.__releaseHandler(2), + lambda : self.__releaseHandler(3), + lambda : self.__releaseHandler(1), + lambda : self.__releaseHandler(0)]) + for arrow in self.arrows: + arrow.setColor(PartyGlobals.TugOfWarEnabledArrowColor) + + def disableKeys(self): + self.arrowKeys.setPressHandlers(self.arrowKeys.NULL_HANDLERS) + self.arrowKeys.setReleaseHandlers(self.arrowKeys.NULL_HANDLERS) + + def __pressHandler(self, index): + if index == self.buttons[0]: + self.arrows[index].setColor(PartyGlobals.TugOfWarHilightedArrowColor) + self.keyTTL.insert(0, PartyGlobals.TugOfWarKeyPressTimeToLive) + self.buttons.reverse() + + def __releaseHandler(self, index): + if index in self.buttons: + self.arrows[index].setColor(PartyGlobals.TugOfWarEnabledArrowColor) + + def updateToonPositions(self, offset): + if self.activityFSM.state != 'Active': + return + if self.isLocalToonPlaying: + camera.lookAt(self.root, offset, 0.0, PartyGlobals.TugOfWarCameraLookAtHeightOffset) + for toonId in self.getToonIdsAsList(): + if hasattr(self, 'fallenToons') and toonId not in self.fallenToons: + toon = self.getAvatar(toonId) + if toon is not None: + origPos = self.toonIdsToStartPositions[toonId] + curPos = toon.getPos(self.root) + newPos = Point3(origPos[0] + offset, curPos[1], curPos[2]) + if self.toonIdsToAnimIntervals[toonId] != None: + if self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + self.checkIfFallen(toonId) + if toonId not in self.fallenToons: + self.toonIdsToAnimIntervals[toonId] = Sequence(LerpPosInterval(toon, duration=PartyGlobals.TugOfWarKeyPressReportRate, pos=newPos, other=self.root), Func(self.checkIfFallen, toonId)) + self.toonIdsToAnimIntervals[toonId].start() + + return + + def checkIfFallen(self, toonId): + if hasattr(self, 'fallenToons') and toonId not in self.fallenToons: + toon = self.getAvatar(toonId) + if toon: + curPos = toon.getPos(self.root) + team = self.getTeam(toonId) + if team == PartyGlobals.TeamActivityTeams.LeftTeam and curPos[0] > -2.0 or team == PartyGlobals.TeamActivityTeams.RightTeam and curPos[0] < 2.0: + losingTeam = self.getTeam(toonId) + self.throwTeamInWater(losingTeam) + self.sendUpdate('reportFallIn', [losingTeam]) + + def throwTeamInWater(self, losingTeam): + self.notify.debug('throwTeamInWater( %s )' % PartyGlobals.TeamActivityTeams.getString(losingTeam)) + splashSet = False + for toonId in self.toonIds[losingTeam]: + self.fallenToons.append(toonId) + toon = self.getAvatar(toonId) + fallenPosIndex = self.toonIds[losingTeam].index(toonId) + if fallenPosIndex < 0 or fallenPosIndex >= 4: + fallenPosIndex = 0 + newPos = self.fallenPositions[fallenPosIndex] + if self.toonIdsToAnimIntervals.has_key(toonId) and self.toonIdsToAnimIntervals[toonId] is not None: + if self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + if toon: + parallel = Parallel(ActorInterval(actor=toon, animName='slip-forward', duration=2.0), LerpPosInterval(toon, duration=2.0, pos=newPos, other=self.root)) + else: + self.notify.warning('toon %d is none, skipping slip-forward' % toonId) + parallel = Parallel() + if not splashSet: + splashSet = True + parallel.append(self.splashInterval) + if toon: + self.toonIdsToAnimIntervals[toonId] = Sequence(parallel, Func(toon.loop, 'neutral')) + else: + self.notify.warning('toon %d is none, skipping toon.loop(neutral)' % toonId) + self.toonIdsToAnimIntervals[toonId] = parallel + self.toonIdsToAnimIntervals[toonId].start() + + return + + def setAdvantage(self, advantage): + DistributedPartyTeamActivity.setAdvantage(self, advantage) + if self.isLocalToonPlaying: + self.setIdealRate(PartyGlobals.TugOfWarTargetRateList[0][1]) diff --git a/toontown/parties/DistributedPartyValentineDance20Activity.py b/toontown/parties/DistributedPartyValentineDance20Activity.py new file mode 100644 index 0000000..47399e1 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineDance20Activity.py @@ -0,0 +1,26 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyValentineDance20Activity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyValentineDance20Activity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance20, PartyGlobals.DancePatternToAnims20, model='phase_13/models/parties/tt_m_ara_pty_danceFloorValentine') + + def getInstructions(self): + return TTLocalizer.PartyDanceActivity20Instructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivity20Title + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_20') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyValentineDanceActivity.py b/toontown/parties/DistributedPartyValentineDanceActivity.py new file mode 100644 index 0000000..d61d5e4 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineDanceActivity.py @@ -0,0 +1,27 @@ +from toontown.parties import PartyGlobals +from toontown.parties.DistributedPartyDanceActivityBase import DistributedPartyDanceActivityBase +from toontown.toonbase import TTLocalizer + +class DistributedPartyValentineDanceActivity(DistributedPartyDanceActivityBase): + notify = directNotify.newCategory('DistributedPartyValentineDanceActivity') + + def __init__(self, cr): + DistributedPartyDanceActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyDance, PartyGlobals.DancePatternToAnims, model='phase_13/models/parties/tt_m_ara_pty_danceFloorValentine') + + def getInstructions(self): + return TTLocalizer.PartyDanceActivityInstructions + + def getTitle(self): + return TTLocalizer.PartyDanceActivityTitle + + def load(self): + DistributedPartyDanceActivityBase.load(self) + parentGroup = self.danceFloor.find('**/discoBall_mesh') + correctBall = self.danceFloor.find('**/discoBall_10') + origBall = self.danceFloor.find('**/discoBall_mesh_orig') + if not correctBall.isEmpty(): + numChildren = parentGroup.getNumChildren() + for i in xrange(numChildren): + child = parentGroup.getChild(i) + if child != correctBall: + child.hide() diff --git a/toontown/parties/DistributedPartyValentineJukebox40Activity.py b/toontown/parties/DistributedPartyValentineJukebox40Activity.py new file mode 100644 index 0000000..7fc3f28 --- /dev/null +++ b/toontown/parties/DistributedPartyValentineJukebox40Activity.py @@ -0,0 +1,18 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyValentineJukebox40Activity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyValentineJukebox40Activity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyValentineJukebox40, PartyGlobals.PhaseToMusicData40) + + def load(self): + DistributedPartyJukeboxActivityBase.load(self) + newTexture = loader.loadTexture('phase_13/maps/tt_t_ara_pty_jukeboxValentineB.jpg', 'phase_13/maps/tt_t_ara_pty_jukeboxValentineB_a.rgb') + case = self.jukebox.find('**/jukeboxGlass') + if not case.isEmpty(): + case.setTexture(newTexture, 1) + body = self.jukebox.find('**/jukeboxBody') + if not body.isEmpty(): + body.setTexture(newTexture, 1) diff --git a/toontown/parties/DistributedPartyValentineJukeboxActivity.py b/toontown/parties/DistributedPartyValentineJukeboxActivity.py new file mode 100644 index 0000000..f67cb5a --- /dev/null +++ b/toontown/parties/DistributedPartyValentineJukeboxActivity.py @@ -0,0 +1,18 @@ +from toontown.parties.DistributedPartyJukeboxActivityBase import DistributedPartyJukeboxActivityBase +from toontown.parties import PartyGlobals + +class DistributedPartyValentineJukeboxActivity(DistributedPartyJukeboxActivityBase): + notify = directNotify.newCategory('DistributedPartyJukeboxActivity') + + def __init__(self, cr): + DistributedPartyJukeboxActivityBase.__init__(self, cr, PartyGlobals.ActivityIds.PartyValentineJukebox, PartyGlobals.PhaseToMusicData) + + def load(self): + DistributedPartyJukeboxActivityBase.load(self) + newTexture = loader.loadTexture('phase_13/maps/tt_t_ara_pty_jukeboxValentineA.jpg', 'phase_13/maps/tt_t_ara_pty_jukeboxValentineA_a.rgb') + case = self.jukebox.find('**/jukeboxGlass') + if not case.isEmpty(): + case.setTexture(newTexture, 1) + body = self.jukebox.find('**/jukeboxBody') + if not body.isEmpty(): + body.setTexture(newTexture, 1) diff --git a/toontown/parties/DistributedPartyValentineTrampolineActivity.py b/toontown/parties/DistributedPartyValentineTrampolineActivity.py new file mode 100644 index 0000000..a362d3c --- /dev/null +++ b/toontown/parties/DistributedPartyValentineTrampolineActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyTrampolineActivity import DistributedPartyTrampolineActivity + +class DistributedPartyValentineTrampolineActivity(DistributedPartyTrampolineActivity): + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.__init__(self, cr, doJellyBeans, doTricks, 'phase_13/maps/tt_t_ara_pty_trampolineValentine.jpg') diff --git a/toontown/parties/DistributedPartyVictoryTrampolineActivity.py b/toontown/parties/DistributedPartyVictoryTrampolineActivity.py new file mode 100644 index 0000000..99d156b --- /dev/null +++ b/toontown/parties/DistributedPartyVictoryTrampolineActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyTrampolineActivity import DistributedPartyTrampolineActivity + +class DistributedPartyVictoryTrampolineActivity(DistributedPartyTrampolineActivity): + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.__init__(self, cr, doJellyBeans, doTricks, 'phase_13/maps/tt_t_ara_pty_trampolineVictory.jpg') diff --git a/toontown/parties/DistributedPartyWinterCatchActivity.py b/toontown/parties/DistributedPartyWinterCatchActivity.py new file mode 100644 index 0000000..5ba2ab0 --- /dev/null +++ b/toontown/parties/DistributedPartyWinterCatchActivity.py @@ -0,0 +1,46 @@ +from pandac.PandaModules import NodePath +from toontown.toonbase import TTLocalizer +from toontown.parties.DistributedPartyCatchActivity import DistributedPartyCatchActivity +from toontown.parties import PartyGlobals +from toontown.parties import WinterPartyCatchActivityToonSD + +class DistributedPartyWinterCatchActivity(DistributedPartyCatchActivity): + + def __init__(self, cr): + DistributedPartyCatchActivity.__init__(self, cr) + + def getInstructions(self): + return TTLocalizer.WinterPartyCatchActivityInstructions % {'badThing': self.DropObjectPlurals['anvil']} + + def load(self): + DistributedPartyCatchActivity.load(self, loadModels=0, arenaModel='tt_m_ara_pty_partyCatchTreeWinter') + self.__loadDropModels() + + def __loadDropModels(self): + for objType in PartyGlobals.DropObjectTypes: + model = None + if not objType == PartyGlobals.Name2DropObjectType['anvil']: + model = loader.loadModel('phase_13/models/parties/tt_m_ara_pty_winterPresent') + model.setScale(0.5) + else: + model = loader.loadModel(objType.modelPath) + self.dropObjModels[objType.name] = model + model.flattenStrong() + + return + + def handleToonJoined(self, toonId): + if not self.toonSDs.has_key(toonId): + toonSD = WinterPartyCatchActivityToonSD.WinterPartyCatchActivityToonSD(toonId, self) + self.toonSDs[toonId] = toonSD + toonSD.load() + self.notify.debug('handleToonJoined : currentState = %s' % self.activityFSM.state) + self.cr.doId2do[toonId].useLOD(500) + if self.activityFSM.state == 'Active': + if self.toonSDs.has_key(toonId): + self.toonSDs[toonId].enter() + if base.localAvatar.doId == toonId: + base.localAvatar.b_setParent(self._avatarNodePathParentToken) + self.putLocalAvatarInActivity() + if self.toonSDs.has_key(toonId): + self.toonSDs[toonId].fsm.request('rules') diff --git a/toontown/parties/DistributedPartyWinterCogActivity.py b/toontown/parties/DistributedPartyWinterCogActivity.py new file mode 100644 index 0000000..6c5c109 --- /dev/null +++ b/toontown/parties/DistributedPartyWinterCogActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyCogActivity import DistributedPartyCogActivity + +class DistributedPartyWinterCogActivity(DistributedPartyCogActivity): + + def __init__(self, cr): + DistributedPartyCogActivity.__init__(self, cr, 'phase_13/models/parties/tt_m_ara_pty_cogPieArenaWinter') diff --git a/toontown/parties/DistributedPartyWinterTrampolineActivity.py b/toontown/parties/DistributedPartyWinterTrampolineActivity.py new file mode 100644 index 0000000..a0dcbfd --- /dev/null +++ b/toontown/parties/DistributedPartyWinterTrampolineActivity.py @@ -0,0 +1,6 @@ +from toontown.parties.DistributedPartyTrampolineActivity import DistributedPartyTrampolineActivity + +class DistributedPartyWinterTrampolineActivity(DistributedPartyTrampolineActivity): + + def __init__(self, cr, doJellyBeans = True, doTricks = False, texture = None): + DistributedPartyTrampolineActivity.__init__(self, cr, doJellyBeans, doTricks, 'phase_13/maps/tt_t_ara_pty_trampolineWinter.jpg') diff --git a/toontown/parties/InviteInfo.py b/toontown/parties/InviteInfo.py new file mode 100644 index 0000000..f242bdf --- /dev/null +++ b/toontown/parties/InviteInfo.py @@ -0,0 +1,56 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.parties.PartyGlobals import InviteStatus +from toontown.toonbase import TTLocalizer + +class InviteInfoBase: + + def __init__(self, inviteKey, partyId, status): + self.inviteKey = inviteKey + self.partyId = partyId + self.status = status + + def __str__(self): + string = 'inviteKey=%d ' % self.inviteKey + string += 'partyId=%d ' % self.partyId + string += 'status=%s' % InviteStatus.getString(self.status) + return string + + def __repr__(self): + return self.__str__() + + +class InviteInfo(InviteInfoBase): + notify = DirectNotifyGlobal.directNotify.newCategory('InviteInfo') + + def __init__(self, inviteKey, partyId, status): + InviteInfoBase.__init__(self, inviteKey, partyId, status) + + def acceptItem(self, mailbox, acceptingIndex, callback): + InviteInfo.notify.debug('acceptItem') + mailbox.acceptInvite(self, acceptingIndex, callback) + + def discardItem(self, mailbox, acceptingIndex, callback): + InviteInfo.notify.debug('discardItem') + mailbox.rejectInvite(self, acceptingIndex, callback) + + def getAcceptItemErrorText(self, retcode): + InviteInfo.notify.debug('getAcceptItemErrorText') + if retcode == ToontownGlobals.P_InvalidIndex: + return TTLocalizer.InviteAcceptInvalidError + elif retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.InviteAcceptAllOk + else: + return TTLocalizer.CatalogAcceptGeneralError % retcode + + def getDiscardItemErrorText(self, retcode): + InviteInfo.notify.debug('getDiscardItemErrorText') + if retcode == ToontownGlobals.P_InvalidIndex: + return TTLocalizer.InviteAcceptInvalidError + elif retcode == ToontownGlobals.P_ItemAvailable: + return TTLocalizer.InviteRejectAllOk + else: + return TTLocalizer.CatalogAcceptGeneralError % retcode + + def output(self, store = -1): + return 'InviteInfo %s' % str(self) diff --git a/toontown/parties/InviteVisual.py b/toontown/parties/InviteVisual.py new file mode 100644 index 0000000..710e878 --- /dev/null +++ b/toontown/parties/InviteVisual.py @@ -0,0 +1,112 @@ +from datetime import datetime +import calendar +from direct.gui.DirectGui import DirectFrame, DirectLabel +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from direct.fsm.FSM import FSM +from toontown.parties import PartyGlobals +from toontown.parties import PartyUtils +from toontown.toonbase.ToontownGlobals import VALENTINES_DAY + +class InviteVisual(DirectFrame): + notify = directNotify.newCategory('InviteVisual') + + def __init__(self, parent): + DirectFrame.__init__(self, parent=parent) + self.gui = loader.loadModel('phase_5.5/models/parties/partyInviteGUI') + self.inviteThemesIdToInfo = {PartyGlobals.InviteTheme.Birthday: (self.gui.find('**/birthdayPage'), TTLocalizer.PartyPlannerBirthdayTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.GenericMale: (self.gui.find('**/genericMalePage'), TTLocalizer.PartyPlannerGenericMaleTheme, (0.7, + 0.7, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.GenericFemale: (self.gui.find('**/genericFemalePage'), TTLocalizer.PartyPlannerGenericFemaleTheme, (0.0, + 1.0, + 0.5, + 1.0)), + PartyGlobals.InviteTheme.Racing: (self.gui.find('**/racingPage'), TTLocalizer.PartyPlannerRacingTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.Valentoons: (self.gui.find('**/valentinePage1'), TTLocalizer.PartyPlannerValentoonsTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.VictoryParty: (self.gui.find('**/victoryPartyPage'), TTLocalizer.PartyPlannerVictoryPartyTheme, (0.0, + 0.0, + 0.0, + 1.0)), + PartyGlobals.InviteTheme.Winter: (self.gui.find('**/winterPartyPage1'), TTLocalizer.PartyPlannerWinterPartyTheme, (1.0, + 1.0, + 1.0, + 1.0))} + self.inviteThemeBackground = DirectFrame(parent=self, image=self.inviteThemesIdToInfo[0][0], relief=None) + self.whosePartyLabel = DirectLabel(parent=self, relief=None, pos=self.gui.find('**/who_locator').getPos(), text='.', text_scale=0.067, textMayChange=True) + self.activityTextLabel = DirectLabel(parent=self, relief=None, text='.\n.\n.\n.', pos=self.gui.find('**/what_locator').getPos(), text_scale=TTLocalizer.IVactivityTextLabel, textMayChange=True) + self.whenTextLabel = DirectLabel(parent=self, relief=None, text='.\n.\n.', pos=self.gui.find('**/when_locator').getPos(), text_scale=TTLocalizer.IVwhenTextLabel, textMayChange=True) + self.noFriends = False + return None + + def setNoFriends(self, noFriends): + self.noFriends = noFriends + self.inviteThemeBackground.show() + + def updateInvitation(self, hostsName, partyInfo): + self.partyInfo = partyInfo + hostsName = TTLocalizer.GetPossesive(hostsName) + self.whosePartyLabel['text'] = TTLocalizer.PartyPlannerInvitationWhoseSentence % hostsName + if self.partyInfo.isPrivate: + publicPrivateText = TTLocalizer.PartyPlannerPrivate.lower() + else: + publicPrivateText = TTLocalizer.PartyPlannerPublic.lower() + activities = self.getActivitiesFormattedCorrectly() + if self.noFriends: + self.activityTextLabel['text'] = TTLocalizer.PartyPlannerInvitationThemeWhatSentenceNoFriends % (publicPrivateText, activities) + else: + self.activityTextLabel['text'] = TTLocalizer.PartyPlannerInvitationThemeWhatSentence % (publicPrivateText, activities) + if self.noFriends: + self.whenTextLabel['text'] = TTLocalizer.PartyPlannerInvitationWhenSentenceNoFriends % (PartyUtils.formatDate(self.partyInfo.startTime.year, self.partyInfo.startTime.month, self.partyInfo.startTime.day), PartyUtils.formatTime(self.partyInfo.startTime.hour, self.partyInfo.startTime.minute)) + else: + self.whenTextLabel['text'] = TTLocalizer.PartyPlannerInvitationWhenSentence % (PartyUtils.formatDate(self.partyInfo.startTime.year, self.partyInfo.startTime.month, self.partyInfo.startTime.day), PartyUtils.formatTime(self.partyInfo.startTime.hour, self.partyInfo.startTime.minute)) + self.changeTheme(partyInfo.inviteTheme) + + def getActivitiesFormattedCorrectly(self): + activitiesString = '' + activityList = [] + for activity in self.partyInfo.activityList: + text = TTLocalizer.PartyActivityNameDict[activity.activityId]['invite'] + if text not in activityList: + activityList.append(text) + + if len(activityList) == 1: + return '\n' + TTLocalizer.PartyPlannerInvitationThemeWhatActivitiesBeginning + activityList[0] + conjunction = TTLocalizer.PartyActivityConjunction + for activity in activityList: + activitiesString = '%s, %s' % (activitiesString, activity) + + activitiesString = activitiesString[2:] + activitiesString = activitiesString[:activitiesString.rfind(',')] + conjunction + activitiesString[activitiesString.rfind(',') + 1:] + activitiesString = TTLocalizer.PartyPlannerInvitationThemeWhatActivitiesBeginning + activitiesString + return self.insertCarriageReturn(activitiesString) + + def insertCarriageReturn(self, stringLeft, stringDone = ''): + desiredNumberOfCharactersInLine = 42 + if len(stringLeft) < desiredNumberOfCharactersInLine: + return stringDone + '\n' + stringLeft + for i in range(desiredNumberOfCharactersInLine - 6, len(stringLeft)): + if stringLeft[i] == ' ': + return self.insertCarriageReturn(stringLeft[i:], stringDone + '\n' + stringLeft[:i]) + + return stringDone + '\n' + stringLeft + + def changeTheme(self, newTheme): + self.inviteThemeBackground['image'] = self.inviteThemesIdToInfo[newTheme][0] + self.whosePartyLabel['text_fg'] = self.inviteThemesIdToInfo[newTheme][2] + self.activityTextLabel['text_fg'] = self.inviteThemesIdToInfo[newTheme][2] + self.whenTextLabel['text_fg'] = self.inviteThemesIdToInfo[newTheme][2] + + def close(self): + self.destroy() + del self diff --git a/toontown/parties/JellybeanRewardGui.py b/toontown/parties/JellybeanRewardGui.py new file mode 100644 index 0000000..1a6e18f --- /dev/null +++ b/toontown/parties/JellybeanRewardGui.py @@ -0,0 +1,91 @@ +from pandac.PandaModules import TextNode +from direct.gui.DirectGui import DirectFrame +from direct.gui.DirectGui import DirectButton +from direct.gui.DirectGui import DirectLabel +from direct.gui import DirectGuiGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class JellybeanRewardGui(DirectFrame): + notify = directNotify.newCategory('JellybeanRewardGui') + PreCountdownDelay = 1.0 + CountDownRate = 0.2 + JarLabelTextColor = (0.95, 0.95, 0.0, 1.0) + JarLabelMaxedTextColor = (1.0, 0.0, 0.0, 1.0) + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + DirectFrame.__init__(self) + self.reparentTo(aspect2d) + self.setPos(0.0, 0.0, 0.16) + self.stash() + publicPartyGui = loader.loadModel('phase_4/models/parties/publicPartyGUI') + self.frame = DirectFrame(parent=self, geom=publicPartyGui.find('**/activities_background'), geom_pos=(-0.8, 0.0, 0.2), geom_scale=2.0, relief=None) + self.earnedLabel = DirectLabel(parent=self, relief=None, text=str(0), text_align=TextNode.ACenter, text_pos=(0.0, -0.07), text_scale=0.2, text_fg=(0.95, 0.95, 0.0, 1.0), text_font=ToontownGlobals.getSignFont(), textMayChange=True, image=DirectGuiGlobals.getDefaultDialogGeom(), image_scale=(0.33, 1.0, 0.33), pos=(-0.3, 0.0, 0.2), scale=0.9) + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + jarImage = purchaseModels.find('**/Jar') + self.jarLabel = DirectLabel(parent=self, relief=None, text=str(0), text_align=TextNode.ACenter, text_pos=(0.0, -0.07), text_scale=0.2, text_fg=JellybeanRewardGui.JarLabelTextColor, text_font=ToontownGlobals.getSignFont(), textMayChange=True, image=jarImage, scale=0.7, pos=(0.3, 0.0, 0.17)) + purchaseModels.removeNode() + del purchaseModels + jarImage.removeNode() + del jarImage + self.messageLabel = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_wordwrap=16.0, text_scale=0.07, pos=(-0.52, 0.0, -0.1), textMayChange=True) + self.doubledJellybeanLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.PartyRewardDoubledJellybean, text_align=TextNode.ACenter, text_wordwrap=12.0, text_scale=0.09, text_fg=(1.0, 0.125, 0.125, 1.0), pos=(0.0, 0.0, -0.465), textMayChange=False) + self.doubledJellybeanLabel.hide() + self.closeButton = DirectButton(parent=self, relief=None, text=TTLocalizer.PartyJellybeanRewardOK, text_align=TextNode.ACenter, text_scale=0.065, text_pos=(0.0, -0.625), geom=(publicPartyGui.find('**/startButton_up'), + publicPartyGui.find('**/startButton_down'), + publicPartyGui.find('**/startButton_rollover'), + publicPartyGui.find('**/startButton_inactive')), geom_pos=(-0.39, 0.0, 0.125), command=self._close) + publicPartyGui.removeNode() + del publicPartyGui + self.countSound = base.loadSfx('phase_13/audio/sfx/tick_counter_short.mp3') + self.overMaxSound = base.loadSfx('phase_13/audio/sfx/tick_counter_overflow.mp3') + return + + def showReward(self, earnedAmount, jarAmount, message): + JellybeanRewardGui.notify.debug('showReward( earnedAmount=%d, jarAmount=%d, ...)' % (earnedAmount, jarAmount)) + self.earnedCount = earnedAmount + self.earnedLabel['text'] = str(self.earnedCount) + self.jarCount = jarAmount + self.jarMax = base.localAvatar.getMaxMoney() + self.jarLabel['text'] = str(self.jarCount) + self.jarLabel['text_fg'] = JellybeanRewardGui.JarLabelTextColor + self.messageLabel['text'] = message + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_DAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_PARTIES_HOLIDAY_MONTH): + self.doubledJellybeanLabel.show() + else: + self.doubledJellybeanLabel.hide() + self.unstash() + taskMgr.doMethodLater(JellybeanRewardGui.PreCountdownDelay, self.transferOneJellybean, 'JellybeanRewardGuiTransferOneJellybean', extraArgs=[]) + + def transferOneJellybean(self): + if self.earnedCount == 0: + return + self.earnedCount -= 1 + self.earnedLabel['text'] = str(self.earnedCount) + self.jarCount += 1 + if self.jarCount <= self.jarMax: + self.jarLabel['text'] = str(self.jarCount) + elif self.jarCount > self.jarMax: + self.jarLabel['text_fg'] = JellybeanRewardGui.JarLabelMaxedTextColor + if self.jarCount <= self.jarMax: + base.playSfx(self.countSound) + else: + base.playSfx(self.overMaxSound) + taskMgr.doMethodLater(JellybeanRewardGui.CountDownRate, self.transferOneJellybean, 'JellybeanRewardGuiTransferOneJellybean', extraArgs=[]) + + def _close(self): + taskMgr.remove('JellybeanRewardGuiTransferOneJellybean') + self.stash() + messenger.send(self.doneEvent) + + def destroy(self): + taskMgr.remove('JellybeanRewardGuiTransferOneJellybean') + del self.countSound + del self.overMaxSound + self.frame.destroy() + self.earnedLabel.destroy() + self.jarLabel.destroy() + self.messageLabel.destroy() + self.closeButton.destroy() + DirectFrame.destroy(self) diff --git a/toontown/parties/JukeboxGui.py b/toontown/parties/JukeboxGui.py new file mode 100644 index 0000000..028720a --- /dev/null +++ b/toontown/parties/JukeboxGui.py @@ -0,0 +1,168 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel +from direct.gui.DirectGui import DirectScrolledListItem, DirectScrolledList +from direct.gui import DirectGuiGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyUtils + +class JukeboxGui(DirectObject): + notify = directNotify.newCategory('JukeboxGui') + CLOSE_EVENT = 'JukeboxGui_CLOSE_EVENT' + SONG_SELECT_EVENT = 'JukeboxGui_SONG_SELECT_EVENT' + QUEUE_SELECT_EVENT = 'JukeboxGui_QUEUE_SELECT_EVENT' + ADD_SONG_CLICK_EVENT = 'JukeboxGui_ADD_SONG_CLICK_EVENT' + MOVE_TO_TOP_CLICK_EVENT = 'JukeboxGUI_MOVE_TO_TOP_EVENT' + + def __init__(self, phaseToMusicData): + self._loaded = False + self._timerGui = None + self._windowFrame = None + self.phaseToMusicData = phaseToMusicData + return + + def load(self): + if self.isLoaded(): + return + guiNode = loader.loadModel('phase_13/models/parties/jukeboxGUI') + self._timerGui = PartyUtils.getNewToontownTimer() + self._windowFrame = DirectFrame(image=guiNode.find('**/background'), relief=None, pos=(0, 0, 0), scale=0.7) + self._songFrame = DirectFrame(image=guiNode.find('**/songTitle_background'), parent=self._windowFrame, relief=None) + self._currentlyPlayingLabel = self.__createLabel(guiNode, 'currentlyPlaying', parent=self._windowFrame, text=TTLocalizer.JukeboxCurrentlyPlayingNothing, scale=TTLocalizer.JGcurrentlyPlayingLabel) + self._songNameLabel = self.__createLabel(guiNode, 'songName', parent=self._windowFrame, text=TTLocalizer.JukeboxCurrentSongNothing, scale=TTLocalizer.JGsongNameLabel) + self._queueList, self._queueLabel = self.__createLabeledScrolledList(guiNode, 'queue', label=TTLocalizer.JukeboxQueueLabel, parent=self._windowFrame) + self._songsList, self._songsLabel = self.__createLabeledScrolledList(guiNode, 'songs', label=TTLocalizer.JukeboxSongsLabel, parent=self._windowFrame) + pos = guiNode.find('**/addButton_text_locator').getPos() + self._addSongButton = self.__createButton(guiNode, 'addSongButton', parent=self._windowFrame, command=self.__handleAddSongButtonClick, image3_color=Vec4(0.6, 0.6, 0.6, 0.6), text=TTLocalizer.JukeboxAddSong, text_align=TextNode.ACenter, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.JGaddSongButton) + self._closeButton = self.__createButton(guiNode, 'can_cancelButton', parent=self._windowFrame, command=self.__handleCloseButtonClick) + pos = guiNode.find('**/close_text_locator').getPos() + self._closeButton = self.__createButton(guiNode, 'close', parent=self._windowFrame, command=self.__handleCloseButtonClick, text=TTLocalizer.JukeboxClose, text_align=TextNode.ACenter, text_pos=(pos[0], pos[2]), text_scale=0.08) + self._moveToTopButton = self.__createButton(guiNode, 'moveToTop', command=self.__handleMoveToTopButtonClick) + guiNode.removeNode() + self._loaded = True + return + + def __createButton(self, guiNode, imagePrefix, parent = hidden, **kwargs): + return DirectButton(parent=parent, relief=None, image=(guiNode.find('**/%s_up' % imagePrefix), guiNode.find('**/%s_down' % imagePrefix), guiNode.find('**/%s_rollover' % imagePrefix)), **kwargs) + + def __createLabel(self, guiNode, locatorPrefix, parent = hidden, **kwargs): + return DirectLabel(parent=parent, relief=None, pos=guiNode.find('**/%s_text_locator' % locatorPrefix).getPos(), **kwargs) + + def __createLabeledScrolledList(self, guiNode, namePrefix, label, parent = hidden, **kwargs): + return (DirectScrolledList(parent=parent, relief=None, incButton_image=(guiNode.find('**/%sButtonDown_up' % namePrefix), guiNode.find('**/%sButtonDown_down' % namePrefix), guiNode.find('**/%sButtonDown_rollover' % namePrefix)), incButton_relief=None, incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), decButton_image=(guiNode.find('**/%sButtonUp_up' % namePrefix), guiNode.find('**/%sButtonUp_down' % namePrefix), guiNode.find('**/%sButtonUp_rollover' % namePrefix)), decButton_relief=None, decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), image=guiNode.find('**/%s_background' % namePrefix), itemFrame_relief=None, itemFrame_pos=guiNode.find('**/%sList_locator' % namePrefix).getPos(), itemFrame_scale=0.07, numItemsVisible=TTLocalizer.JGnumItemsVisible, items=[], **kwargs), self.__createLabel(guiNode, namePrefix, parent=parent, text=label, text_fg=(0.5, 1.0, 1.0, 1.0), text_shadow=(0.0, 0.0, 0.0, 1.0), scale=0.12)) + + def enable(self, timer = 0): + if not self.isLoaded(): + self.load() + phase = 13 + tunes = self.phaseToMusicData[13] + for filename, info in tunes.items(): + self.addToSongList(info[0], phase, filename, info[1]) + + for phase, tunes in self.phaseToMusicData.items(): + if phase == 13: + continue + for filename, info in tunes.items(): + self.addToSongList(info[0], phase, filename, info[1]) + + self._windowFrame.show() + if timer > 0: + self._timerGui.setTime(timer) + self._timerGui.countdown(timer) + self._timerGui.show() + + def disable(self): + self._windowFrame.hide() + self._timerGui.hide() + + def unload(self): + self.ignoreAll() + if not self.isLoaded(): + return + if self._windowFrame is not None: + self._windowFrame.destroy() + self._windowFrame = None + self._moveToTopButton.destroy() + del self._moveToTopButton + if self._timerGui is not None: + self._timerGui.destroy() + self._timerGui = None + self._loaded = False + return + + def isLoaded(self): + return self._loaded + + def addToSongList(self, text, phase, filename, length): + listItem = DirectScrolledListItem(relief=None, parent=self._songsList, text=text, text_align=TextNode.ALeft, text_pos=(0.0, 0.0, 0.0), text_scale=TTLocalizer.JGlistItem, text_fg=(0.0, 0.0, 0.0, 1.0), text1_fg=(1.0, 1.0, 1.0, 1.0), text1_bg=(0.0, 0.0, 1.0, 1.0), text2_fg=(0.0, 0.0, 1.0, 1.0), text3_bg=(0.0, 0.8, 0.0, 1.0), command=self.__handleSongListItemSelect, extraArgs=[]) + listItem.setPythonTag('value', (phase, filename, length)) + self._songsList.addItem(listItem) + return listItem + + def __handleCloseButtonClick(self): + self.disable() + messenger.send(JukeboxGui.CLOSE_EVENT) + + def __handleMoveToTopButtonClick(self): + messenger.send(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) + + def __handleSongListItemSelect(self): + pass + + def __handleAddSongButtonClick(self): + if hasattr(self._songsList, 'currentSelected'): + song = self._songsList.currentSelected + messenger.send(JukeboxGui.ADD_SONG_CLICK_EVENT, [song['text'], song.getPythonTag('value')]) + + def disableAddSongButton(self): + self._addSongButton['state'] = DirectGuiGlobals.DISABLED + + def enableAddSongButton(self): + self._addSongButton['state'] = DirectGuiGlobals.NORMAL + + def addSongToQueue(self, text, highlight = False, moveToTopButton = False): + listItem = DirectLabel(relief=None, parent=self._queueList, text=text, text_align=TextNode.ALeft, text_pos=(0.0, 0.0, 0.0), text_scale=TTLocalizer.JGlistItem) + self._queueList.addItem(listItem) + if highlight: + listItem['text_fg'] = (0.0, 0.5, 0.0, 1.0) + self._addSongButton['text'] = TTLocalizer.JukeboxReplaceSong + listItem.setPythonTag('highlighted', True) + if moveToTopButton and len(self._queueList['items']) > 1: + self._moveToTopButton.reparentTo(listItem) + self._moveToTopButton.setScale(self._windowFrame, 1.0) + self._moveToTopButton.setPos(10.0, 0.0, 0.25) + self._queueList.scrollTo(len(self._queueList['items']) - 1) + return listItem + + def popSongFromQueue(self): + if len(self._queueList['items']) > 0: + item = self._queueList['items'][0] + self._queueList.removeItem(item) + if self._moveToTopButton.getParent() == item: + self._moveToTopButton.reparentTo(hidden) + if self._moveToTopButton.getParent() == item: + self._moveToTopButton.reparentTo(hidden) + if item.getPythonTag('highlighted') == True: + self._addSongButton['text'] = TTLocalizer.JukeboxAddSong + item.removeNode() + return item + return None + + def setSongCurrentlyPlaying(self, phase, filename): + songs = self.phaseToMusicData.get(phase / 1) + if songs: + songName = songs.get(filename) + if songName: + self._songNameLabel['text'] = songName + self._currentlyPlayingLabel['text'] = TTLocalizer.JukeboxCurrentlyPlaying + + def clearSongCurrentlyPlaying(self): + self._currentlyPlayingLabel['text'] = TTLocalizer.JukeboxCurrentlyPlayingNothing + self._songNameLabel['text'] = TTLocalizer.JukeboxCurrentSongNothing + + def pushQueuedItemToTop(self, item): + self._queueList['items'].remove(item) + self._queueList['items'].insert(0, item) + if self._moveToTopButton.getParent() == item: + self._moveToTopButton.reparentTo(hidden) + self._queueList.refresh() diff --git a/toontown/parties/KeyCodes.py b/toontown/parties/KeyCodes.py new file mode 100644 index 0000000..c18ffba --- /dev/null +++ b/toontown/parties/KeyCodes.py @@ -0,0 +1,133 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +ARROW_KEYCODE_MAP = {'arrow_up': 'u', + 'arrow_down': 'd', + 'arrow_left': 'l', + 'arrow_right': 'r'} +KEYCODE_TIMEOUT_SECONDS = 1.5 + +class KeyCodes(DirectObject): + notify = directNotify.newCategory('KeyCodes') + PATTERN_MATCH_EVENT = 'KeyCodes-PATTERN_MATCH' + PATTERN_NO_MATCH_EVENT = 'KeyCodes-PATTERN_NO_MATCH' + KEY_DOWN_EVENT = 'KeyCodes-KEY_DOWN_EVENT' + KEY_UP_EVENT = 'KeyCodes-KEY_UP_EVENT' + CLEAR_CODE_EVENT = 'KeyCodes-CLEAR_CODE_EVENT' + + def __init__(self, keyMap = ARROW_KEYCODE_MAP, patterns = None, timeout = KEYCODE_TIMEOUT_SECONDS): + self._keyMap = keyMap + self._timeout = timeout + self._keyCode = '' + self._keyCodeCount = 0 + self._keyCodeTime = 0.0 + self._patterns = [] + self._patternLimit = 0 + self._enabled = False + self._keyDown = None + self._keysPressed = 0 + self.listenForPatterns(patterns) + return + + def destroy(self): + self.disable() + self.ignoreAll() + self._patterns = [] + del self._patterns + del self._keyMap + del self._timeout + + def listenForPatterns(self, patterns): + self._patterns = patterns + for pattern in self._patterns: + if len(pattern) > self._patternLimit: + self._patternLimit = len(pattern) + + if self._enabled: + self.disable() + self.enable() + + def isAnyKeyPressed(self): + return self._keysPressed > 0 + + def getCurrentInputLength(self): + return self._keyCodeCount + 1 + + def getLargestPatternLength(self): + return self._patternLimit + + def getPossibleMatchesList(self): + return [ p for p in self._patterns if p.startswith(self._keyCode) ] + + def reset(self): + self._keyCode = '' + self._keyCodeCount = 0 + self._keyCodeTime = 0.0 + + def enable(self): + if not self._enabled: + self.notify.debug('Key codes enabled') + self._enabled = True + self.__enableControls() + + def disable(self): + if self._enabled: + self.notify.debug('Key codes disabled') + self.__disableControls() + self.reset() + self._enabled = False + self._keyDown = None + self._keysPressed = 0 + return + + def __enableControls(self): + for key in self._keyMap.keys(): + self.__acceptKeyDown(key) + self.__acceptKeyUp(key) + + def __disableControls(self): + self.ignoreAll() + + def __acceptKeyDown(self, key): + self.accept(key, self.__handleKeyDown, [key]) + + def __acceptKeyUp(self, key): + self.accept(key + '-up', self.__handleKeyUp, [key]) + + def __handleKeyDown(self, key): + self._keysPressed += 1 + if self._keyDown is None and self._keysPressed == 1: + self.__updateElapsedTime() + messenger.send(KeyCodes.KEY_DOWN_EVENT, [self._keyMap[key], self._keyCodeCount]) + self._keyCode += self._keyMap[key] + self._keyCodeCount += 1 + self._keyDown = key + self.__checkForPattern() + else: + messenger.send(KeyCodes.KEY_DOWN_EVENT, [-1, -1]) + return + + def __handleKeyUp(self, key): + arg = -1 + if self._keysPressed > 0: + self._keysPressed -= 1 + if self._keyDown == key and self._keysPressed == 0: + arg = self._keyMap[key] + if self._keysPressed == 0: + self._keyDown = None + messenger.send(KeyCodes.KEY_UP_EVENT, [arg]) + return + + def __updateElapsedTime(self): + if self._keyCodeTime != 0.0 and globalClock.getFrameTime() - self._keyCodeTime >= self._timeout: + self.notify.debug('Key code timed out. Resetting...') + self.reset() + messenger.send(KeyCodes.CLEAR_CODE_EVENT) + self._keyCodeTime = globalClock.getFrameTime() + + def __checkForPattern(self): + if self._keyCode in self._patterns: + messenger.send(KeyCodes.PATTERN_MATCH_EVENT, [self._keyCode]) + self.reset() + elif self._keyCodeCount == self._patternLimit or len(self.getPossibleMatchesList()) == 0: + messenger.send(KeyCodes.PATTERN_NO_MATCH_EVENT) + self.reset() diff --git a/toontown/parties/KeyCodesGui.py b/toontown/parties/KeyCodesGui.py new file mode 100644 index 0000000..cca4169 --- /dev/null +++ b/toontown/parties/KeyCodesGui.py @@ -0,0 +1,117 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from direct.gui.DirectGui import DirectButton +from direct.gui.OnscreenText import OnscreenText +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.parties.KeyCodes import KeyCodes, KEYCODE_TIMEOUT_SECONDS +KEY_TO_INDEX = {'u': 0, + 'r': 1, + 'd': 2, + 'l': 3} + +class KeyCodesGui(DirectObject): + notify = directNotify.newCategory('KeyCodesGui') + TIMEOUT_TASK = 'KeyCodeGui_TIMEOUT_TASK' + + def __init__(self, keyCodes, yOffset = 0.55, keyToIndex = KEY_TO_INDEX): + self._keyCodes = keyCodes + self._keyToIndex = keyToIndex + self._arrowWidth = 0.18 + self._arrowSpaceInBetween = 0.05 + self._yOffset = yOffset + self._danceMoveLabel = None + self._arrowNodes = [] + self.timeoutTask = None + return + + def load(self): + matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + minnieArrow = matchingGameGui.find('**/minnieArrow') + minnieArrow.setScale(0.6) + minnieArrow.setZ(self._yOffset + 0.2) + maxLength = self._keyCodes.getLargestPatternLength() + for i in range(maxLength): + arrow = minnieArrow.copyTo(hidden) + self._arrowNodes.append(arrow) + + matchingGameGui.removeNode() + self._danceMoveLabel = OnscreenText(parent=aspect2d, text='', pos=(0, self._yOffset), scale=0.15, align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), fg=Vec4(1, 1, 1, 1), mayChange=True) + self._danceMoveLabel.hide() + self.enable() + + def unload(self): + self.disable() + for arrow in self._arrowNodes: + arrow.removeNode() + arrow = None + + self._arrowNodes = [] + if self._danceMoveLabel is not None: + self._danceMoveLabel.removeNode() + self._danceMoveLabel = None + return + + def enable(self): + self.notify.debug('KeyCodeGui enabled.') + self.accept(KeyCodes.KEY_DOWN_EVENT, self.__handleKeyDown) + self.accept(KeyCodes.CLEAR_CODE_EVENT, self.hideAll) + + def disable(self): + self.notify.debug('KeyCodeGui disabled.') + self.__stopTimeout() + self.ignoreAll() + + def hideArrows(self, startIndex = 0): + length = len(self._arrowNodes) + if startIndex < length: + for i in range(startIndex, length): + self._arrowNodes[i].reparentTo(hidden) + + def hideAll(self, startIndex = 0): + self.hideArrows(startIndex) + if self._danceMoveLabel: + self._danceMoveLabel.hide() + + def showArrow(self, index, key): + self._arrowNodes[index].setR(-(90 - 90 * self._keyToIndex[key])) + self._arrowNodes[index].setColor(1, 1, 1, 1) + self.__centerArrows() + self._arrowNodes[index].reparentTo(aspect2d) + self.hideAll(index + 1) + self.__startTimeout() + + def showText(self, text = ''): + self.notify.debug('"Showing text "%s"' % text) + self._danceMoveLabel['text'] = text + self._danceMoveLabel.show() + + def setColor(self, r, g, b): + for arrow in self._arrowNodes: + arrow.setColor(r, g, b) + + self._danceMoveLabel.setColorScale(r, g, b, 1) + + def __startTimeout(self): + self.__stopTimeout() + self.timeoutTask = taskMgr.doMethodLater(KEYCODE_TIMEOUT_SECONDS, self.__handleTimeoutTask, KeyCodesGui.TIMEOUT_TASK) + + def __stopTimeout(self): + if self.timeoutTask is not None: + taskMgr.remove(self.timeoutTask) + self.timeoutTask = None + return + + def __handleTimeoutTask(self, task): + self.hideAll() + return Task.done + + def __centerArrows(self): + length = self._keyCodes.getCurrentInputLength() + for i in range(length): + x = -(length * self._arrowWidth * 0.5) + self._arrowWidth * (i + 0.5) + self._arrowNodes[i].setX(x) + + def __handleKeyDown(self, key, index): + if index >= 0: + self.showArrow(index, key) diff --git a/toontown/parties/Party.py b/toontown/parties/Party.py new file mode 100644 index 0000000..edef0fc --- /dev/null +++ b/toontown/parties/Party.py @@ -0,0 +1,284 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from direct.distributed.ClockDelta import * +from toontown.hood import Place +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +import random +from direct.showbase import PythonUtil +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs, TLNull +from toontown.hood import Place +from toontown.hood import SkyUtil +from toontown.parties import PartyPlanner +from toontown.parties.DistributedParty import DistributedParty + +class Party(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('Party') + + def __init__(self, loader, avId, zoneId, parentFSMState, doneEvent): + Place.Place.__init__(self, None, doneEvent) + self.id = PartyHood + self.avId = avId + self.zoneId = zoneId + self.loader = loader + self.musicShouldPlay = False + self.partyPlannerDoneEvent = 'partyPlannerGuiDone' + self.fsm = ClassicFSM.ClassicFSM('Party', [State.State('init', self.enterInit, self.exitInit, ['final', 'teleportIn', 'walk']), + State.State('walk', self.enterWalk, self.exitWalk, ['final', + 'sit', + 'stickerBook', + 'options', + 'quest', + 'fishing', + 'stopped', + 'DFA', + 'trialerFA', + 'push', + 'activity']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk', 'teleportOut']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('push', self.enterPush, self.exitPush, ['walk']), + State.State('partyPlanning', self.enterPartyPlanning, self.exitPartyPlanning, ['DFA', 'teleportOut']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'sit', + 'quest', + 'fishing', + 'stopped', + 'activity', + 'push', + 'DFA', + 'trialerFA']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', 'partyPlanning']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'walk', 'final']), + State.State('died', self.enterDied, self.exitDied, ['walk', 'final']), + State.State('final', self.enterFinal, self.exitFinal, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk']), + State.State('fishing', self.enterFishing, self.exitFishing, ['walk', 'stopped']), + State.State('activity', self.enterActivity, self.exitActivity, ['walk', 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + 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'])], 'init', 'final') + self.fsm.enterInitialState() + self.doneEvent = doneEvent + self.parentFSMState = parentFSMState + self.isPartyEnding = False + self.accept('partyStateChanged', self.setPartyState) + return + + def delete(self): + self.unload() + + def load(self): + self.fog = Fog('PartyFog') + Place.Place.load(self) + if hasattr(base.localAvatar, 'aboutToPlanParty') and base.localAvatar.aboutToPlanParty: + if not hasattr(self, 'partyPlanner') or self.partyPlanner is None: + self.partyPlanner = PartyPlanner.PartyPlanner(self.partyPlannerDoneEvent) + self.parentFSMState.addChild(self.fsm) + return + + def unload(self): + if hasattr(self, 'partyPlanner'): + self.ignore(self.partyPlannerDoneEvent) + self.partyPlanner.close() + del self.partyPlanner + self.__removePartyHat() + self.fog = None + self.ignoreAll() + self.parentFSMState.removeChild(self.fsm) + del self.fsm + Place.Place.unload(self) + return + + def enter(self, requestStatus): + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + if config.GetBool('want-party-telemetry-limiter', 1): + limiter = TLGatherAllAvs('Party', RotationLimitToH) + else: + limiter = TLNull() + self._telemLimiter = limiter + self.loader.hood.startSky() + for i in self.loader.nodeList: + self.loader.enterAnimatedProps(i) + + self.loader.geom.reparentTo(render) + self.fsm.request(requestStatus['how'], [requestStatus]) + self.playMusic() + + def playMusic(self): + if not hasattr(base, 'partyHasJukebox') or not base.partyHasJukebox: + base.playMusic(self.loader.music, looping=1, volume=1) + + def exit(self): + base.localAvatar.stopChat() + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + self.loader.geom.reparentTo(hidden) + for i in self.loader.nodeList: + self.loader.exitAnimatedProps(i) + + self._telemLimiter.destroy() + del self._telemLimiter + self.loader.hood.stopSky() + render.setFogOff() + base.cr.cache.flush() + self.loader.music.stop() + self.notify.debug('exit') + self.ignoreAll() + + def __setZoneId(self, zoneId): + self.zoneId = zoneId + + def doRequestLeave(self, requestStatus): + self.fsm.request('trialerFA', [requestStatus]) + + def enterInit(self): + pass + + def exitInit(self): + pass + + def enterPartyPlanning(self, requestStatus): + base.localAvatar.aboutToPlanParty = False + self.accept(self.partyPlannerDoneEvent, self.handlePartyPlanningDone) + + def handlePartyPlanningDone(self): + self.ignore(self.partyPlannerDoneEvent) + self.partyPlanner.close() + del self.partyPlanner + messenger.send('deallocateZoneIdFromPlannedParty', [base.localAvatar.zoneId]) + hoodId = base.localAvatar.lastHood + self.fsm.request('teleportOut', [{'avId': -1, + 'zoneId': hoodId, + 'shardId': None, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'loader': 'safeZoneLoader', + 'where': 'playground'}]) + return + + def exitPartyPlanning(self): + pass + + def enterTeleportIn(self, requestStatus): + self._partyTiToken = None + if hasattr(base, 'distributedParty'): + self.__updateLocalAvatarTeleportIn(requestStatus) + elif hasattr(base.localAvatar, 'aboutToPlanParty') and base.localAvatar.aboutToPlanParty: + self.__updateLocalAvatarTeleportIn(requestStatus) + else: + self.acceptOnce(DistributedParty.generatedEvent, self.__updateLocalAvatarTeleportIn, [requestStatus]) + return + + def exitTeleportIn(self): + Place.Place.exitTeleportIn(self) + self.removeSetZoneCompleteCallback(self._partyTiToken) + + def __updateLocalAvatarTeleportIn(self, requestStatus): + self.ignore(DistributedParty.generatedEvent) + if hasattr(base, 'distributedParty'): + x, y, z = base.distributedParty.getClearSquarePos() + self.accept('generate-' + str(base.distributedParty.partyInfo.hostId), self.__setPartyHat) + self.__setPartyHat() + else: + x, y, z = (0.0, 0.0, 0.1) + base.localAvatar.detachNode() + base.localAvatar.setPos(render, x, y, z) + base.localAvatar.lookAt(0.0, 0.0, 0.1) + base.localAvatar.setScale(1, 1, 1) + Place.Place.enterTeleportIn(self, requestStatus) + if hasattr(base, 'distributedParty') and base.distributedParty: + self.setPartyState(base.distributedParty.getPartyState()) + if hasattr(base.localAvatar, 'aboutToPlanParty') and base.localAvatar.aboutToPlanParty: + self._partyTiToken = self.addSetZoneCompleteCallback(Functor(self._partyTeleportInPostZoneComplete, requestStatus), 150) + + def _partyTeleportInPostZoneComplete(self, requestStatus): + self.nextState = 'partyPlanning' + + def __setPartyHat(self, doId = None): + if hasattr(base, 'distributedParty'): + if base.cr.doId2do.has_key(base.distributedParty.partyInfo.hostId): + host = base.cr.doId2do[base.distributedParty.partyInfo.hostId] + if hasattr(host, 'gmIcon') and host.gmIcon: + host.removeGMIcon() + host.setGMPartyIcon() + else: + base.distributedParty.partyHat.reparentTo(host.nametag.getNameIcon()) + + def __removePartyHat(self): + if hasattr(base, 'distributedParty'): + if base.cr.doId2do.has_key(base.distributedParty.partyInfo.hostId): + host = base.cr.doId2do[base.distributedParty.partyInfo.hostId] + if hasattr(host, 'gmIcon') and host.gmIcon: + host.removeGMIcon() + host.setGMIcon() + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == ToontownGlobals.PartyHood and zoneId == self.getZoneId() and shardId == None: + self.fsm.request('teleportIn', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + self.doneStatus = requestStatus + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent, [self.doneStatus]) + return + + def goHomeFailed(self, task): + self.notifyUserGoHomeFailed() + 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) + + def getZoneId(self): + if self.zoneId: + return self.zoneId + else: + self.notify.warning('no zone id available') + + def enterActivity(self, setAnimState = True): + if setAnimState: + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(False) + base.localAvatar.laffMeter.start() + + def exitActivity(self): + base.localAvatar.setTeleportAvailable(True) + self.ignore('teleportQuery') + base.localAvatar.laffMeter.stop() + + def setPartyState(self, partyState): + self.isPartyEnding = partyState + + def handleTeleportQuery(self, fromAvatar, toAvatar): + if self.isPartyEnding: + teleportNotify.debug('party ending, sending teleportResponse') + fromAvatar.d_teleportResponse(toAvatar.doId, 0, toAvatar.defaultShard, base.cr.playGame.getPlaceId(), self.getZoneId()) + elif base.config.GetBool('want-tptrack', False): + if toAvatar == localAvatar: + localAvatar.doTeleportResponse(fromAvatar, toAvatar, toAvatar.doId, 1, toAvatar.defaultShard, base.cr.playGame.getPlaceId(), self.getZoneId(), fromAvatar.doId) + else: + self.notify.warning('handleTeleportQuery toAvatar.doId != localAvatar.doId' % (toAvatar.doId, localAvatar.doId)) + else: + fromAvatar.d_teleportResponse(toAvatar.doId, 1, toAvatar.defaultShard, base.cr.playGame.getPlaceId(), self.getZoneId()) diff --git a/toontown/parties/PartyCatchActivityToonSD.py b/toontown/parties/PartyCatchActivityToonSD.py new file mode 100644 index 0000000..16bd2de --- /dev/null +++ b/toontown/parties/PartyCatchActivityToonSD.py @@ -0,0 +1,234 @@ +from pandac.PandaModules import Vec3 +from direct.interval.IntervalGlobal import Sequence, Parallel, Wait, Func +from direct.interval.IntervalGlobal import LerpScaleInterval +from direct.interval.IntervalGlobal import WaitInterval, ActorInterval, FunctionInterval +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.minigame.MinigameRulesPanel import MinigameRulesPanel +from toontown.parties import PartyGlobals +from direct.fsm import ClassicFSM, State + +class PartyCatchActivityToonSD(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyCatchActivityToonSD') + FallBackAnim = 'slip-backward' + FallFwdAnim = 'slip-forward' + CatchNeutralAnim = 'catch-neutral' + CatchRunAnim = 'catch-run' + EatNeutralAnim = 'catch-eatneutral' + EatNRunAnim = 'catch-eatnrun' + animList = [FallBackAnim, + FallFwdAnim, + CatchNeutralAnim, + CatchRunAnim, + EatNeutralAnim, + EatNRunAnim] + + def __init__(self, avId, activity): + PartyCatchActivityToonSD.notify.debug('init : avId = %s, activity = %s ' % (avId, activity)) + self.avId = avId + self.activity = activity + self.isLocal = avId == base.localAvatar.doId + self.toon = self.activity.getAvatar(self.avId) + self.unexpectedExit = False + self.fsm = ClassicFSM.ClassicFSM('CatchActivityAnimFSM-%s' % self.avId, [State.State('init', self.enterInit, self.exitInit, ['notPlaying', 'normal', 'rules']), + State.State('notPlaying', self.enterNotPlaying, self.exitNotPlaying, ['normal', 'rules', 'cleanup']), + State.State('rules', self.enterRules, self.exitRules, ['normal', 'cleanup']), + State.State('normal', self.enterNormal, self.exitNormal, ['eatFruit', + 'fallBack', + 'fallForward', + 'notPlaying']), + State.State('eatFruit', self.enterEatFruit, self.exitEatFruit, ['normal', + 'fallBack', + 'fallForward', + 'eatFruit', + 'notPlaying']), + State.State('fallBack', self.enterFallBack, self.exitFallBack, ['normal', 'notPlaying']), + State.State('fallForward', self.enterFallForward, self.exitFallForward, ['normal', 'notPlaying']), + State.State('cleanup', self.enterCleanup, self.exitCleanup, [])], 'init', 'cleanup') + self.enteredAlready = False + + def load(self): + self.setAnimState('off', 1.0) + for anim in self.animList: + self.toon.pose(anim, 0) + + def unload(self): + del self.fsm + + def enter(self): + if not self.enteredAlready: + self.enteredAlready = True + self.fsm.enterInitialState() + self._exiting = False + + def exit(self, unexpectedExit = False): + if self._exiting: + return + self._exiting = True + self.unexpectedExit = unexpectedExit + if not self.unexpectedExit: + self.fsm.requestFinalState() + del self._exiting + + def enterInit(self): + self.notify.debug('enterInit') + self.toon.startBlink() + self.toon.stopLookAround() + if self.isLocal: + self.activity.initOrthoWalk() + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def exitInit(self): + pass + + def enterNotPlaying(self): + self.toon.stopBlink() + self.toon.startLookAround() + self.setAnimState('neutral', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitNotPlaying(self): + self.dropShadow = self.toon.dropShadow + self.origDropShadowColor = self.dropShadow.getColor() + c = self.origDropShadowColor + alpha = 0.35 + self.dropShadow.setColor(c[0], c[1], c[2], alpha) + + def enterRules(self): + if self.isLocal: + self.notify.debug('enterNormal') + self.setAnimState('Catching', 1.0) + self.activity.orthoWalk.stop() + self.accept(self.activity.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel('PartyRulesPanel', self.activity.getTitle(), self.activity.getInstructions(), self.activity.rulesDoneEvent, PartyGlobals.DefaultRulesTimeout) + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], False) + self.rulesPanel.load() + self.rulesPanel.enter() + else: + self.fsm.request('normal') + + def handleRulesDone(self): + self.fsm.request('normal') + + def exitRules(self): + self.setAnimState('off', 1.0) + self.ignore(self.activity.rulesDoneEvent) + if hasattr(self, 'rulesPanel'): + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], True) + + def enterNormal(self): + self.notify.debug('enterNormal') + self.setAnimState('Catching', 1.0) + if self.isLocal: + self.activity.orthoWalk.start() + self.toon.lerpLookAt(Vec3.forward() + Vec3.up(), time=0.2, blink=0) + + def exitNormal(self): + self.setAnimState('off', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() + self.toon.lerpLookAt(Vec3.forward(), time=0.2, blink=0) + + def eatFruit(self, fruitModel, handNode): + if self.fsm.getCurrentState().getName() == 'eatFruit': + self.fsm.request('normal') + self.fsm.request('eatFruit', [fruitModel, handNode]) + + def enterEatFruit(self, fruitModel, handNode): + self.notify.debug('enterEatFruit') + self.setAnimState('CatchEating', 1.0) + if self.isLocal: + self.activity.orthoWalk.start() + self.fruitModel = fruitModel + renderScale = fruitModel.getScale(render) + fruitModel.reparentTo(handNode) + fruitModel.setScale(render, renderScale) + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpScaleInterval(fruitModel, duration / 2.0, fruitModel.getScale() * 0.5, blendType='easeInOut'), Func(fruitModel.hide))), Func(self.fsm.request, 'normal'), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitEatFruit(self): + self.eatIval.pause() + del self.eatIval + self.fruitModel.reparentTo(hidden) + self.fruitModel.removeNode() + del self.fruitModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() + + def enterFallBack(self): + self.notify.debug('enterFallBack') + if self.isLocal: + base.playSfx(self.activity.sndOof) + duration = 1.0 + animName = self.FallBackAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + frameRate = self.toon.getFrameRate(animName) + newRate = frames / duration + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallBackIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallBackIval.start() + + def exitFallBack(self): + self.fallBackIval.pause() + del self.fallBackIval + + def enterFallForward(self): + self.notify.debug('enterFallForward') + if self.isLocal: + base.playSfx(self.activity.sndOof) + duration = 2.0 + animName = self.FallFwdAnim + startFrame = 12 + totalFrames = self.toon.getNumFrames(animName) + frames = totalFrames - 1 - startFrame + pauseFrame = 19 + frameRate = self.toon.getFrameRate(animName) + newRate = frames / (duration * 0.5) + playRate = newRate / frameRate + + def resume(self = self): + self.fsm.request('normal') + + self.fallFwdIval = Sequence(ActorInterval(self.toon, animName, startTime=startFrame / newRate, endTime=pauseFrame / newRate, playRate=playRate), WaitInterval(duration / 2.0), ActorInterval(self.toon, animName, startTime=pauseFrame / newRate, endTime=totalFrames / newRate, playRate=playRate), FunctionInterval(resume)) + self.fallFwdIval.start() + + def exitFallForward(self): + self.fallFwdIval.pause() + del self.fallFwdIval + + def enterCleanup(self): + self.notify.debug('enterCleanup') + self.toon.stopBlink() + self.toon.startLookAround() + if self.isLocal: + self.activity.orthoWalk.stop() + self.activity.destroyOrthoWalk() + self.dropShadow.setColor(self.origDropShadowColor) + + def exitCleanup(self): + pass + + def setAnimState(self, newState, playRate): + if not self.unexpectedExit: + self.toon.setAnimState(newState, playRate) + else: + self.notify.debug('setAnimState(): Toon unexpectedExit flag is set.') diff --git a/toontown/parties/PartyCog.py b/toontown/parties/PartyCog.py new file mode 100644 index 0000000..97da98a --- /dev/null +++ b/toontown/parties/PartyCog.py @@ -0,0 +1,343 @@ +import math +from direct.actor.Actor import Actor +from direct.interval.ActorInterval import ActorInterval +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from direct.interval.SoundInterval import SoundInterval +from direct.interval.LerpInterval import LerpScaleInterval, LerpFunc +from direct.showbase.PythonUtil import bound as clamp +from direct.task import Task +from direct.fsm.FSM import FSM +from pandac.PandaModules import CollisionTube, CollisionNode, CollisionSphere +from pandac.PandaModules import Point3, Vec4, NodePath, TextNode, Mat4 +from toontown.toonbase import ToontownGlobals +from toontown.battle.BattleProps import globalPropPool +from toontown.battle.BattleSounds import globalBattleSoundCache +import PartyGlobals + +class PartyCogManager: + + def __init__(self): + self.cogs = [] + + def generateCog(self, parentNode, bounceSpeed = 3, bounceHeight = 1, rotateSpeed = 1, heightShift = 1, xMoveSpeed = 0, xMoveDistance = 0, bounceOffset = 0): + cog = PartyCog(parentNode, len(self.cogs), bounceSpeed, bounceHeight, rotateSpeed, heightShift, xMoveSpeed, xMoveDistance, bounceOffset) + self.cogs.append(cog) + return cog + + def unload(self): + for cog in self.cogs: + cog.unload() + + def updateDistances(self, distances): + for i in range(len(distances)): + self.cogs[i].updateDistance(distances[i]) + + +class PartyCog(FSM): + notify = directNotify.newCategory('PartyCog') + HpTextGenerator = TextNode('HpTextGenerator') + hpText = None + height = 7 + + def __init__(self, parentNode, id, bounceSpeed = 3, bounceHeight = 1, rotateSpeed = 1, heightShift = 1, xMoveSpeed = 0, xMoveDistance = 0, bounceOffset = 0): + self.id = id + FSM.__init__(self, 'PartyCogFSM-%d' % self.id) + self.showFacingStatus = False + self.xMoveSpeed = xMoveSpeed + self.xMoveDistance = xMoveDistance + self.heightShift = heightShift + self.bounceSpeed = bounceSpeed + self.bounceHeight = bounceHeight + self.rotateSpeed = rotateSpeed + self.parentNode = parentNode + self.bounceOffset = bounceOffset + self.hitInterval = None + self.kaboomTrack = None + self.resetRollIval = None + self.netTimeSentToStartByHit = 0 + self.load() + self.request('Down') + return + + def load(self): + self.root = NodePath('PartyCog-%d' % self.id) + self.root.reparentTo(self.parentNode) + path = 'phase_13/models/parties/cogPinata_' + self.actor = Actor(path + 'actor', {'idle': path + 'idle_anim', + 'down': path + 'down_anim', + 'up': path + 'up_anim', + 'bodyHitBack': path + 'bodyHitBack_anim', + 'bodyHitFront': path + 'bodyHitFront_anim', + 'headHitBack': path + 'headHitBack_anim', + 'headHitFront': path + 'headHitFront_anim'}) + self.actor.reparentTo(self.root) + self.temp_transform = Mat4() + self.head_locator = self.actor.attachNewNode('temphead') + self.bodyColl = CollisionTube(0, 0, 1, 0, 0, 5.75, 0.75) + self.bodyColl.setTangible(1) + self.bodyCollNode = CollisionNode('PartyCog-%d-Body-Collision' % self.id) + self.bodyCollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.bodyCollNode.addSolid(self.bodyColl) + self.bodyCollNodePath = self.root.attachNewNode(self.bodyCollNode) + self.headColl = CollisionTube(0, 0, 3, 0, 0, 3.0, 1.5) + self.headColl.setTangible(1) + self.headCollNode = CollisionNode('PartyCog-%d-Head-Collision' % self.id) + self.headCollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.headCollNode.addSolid(self.headColl) + self.headCollNodePath = self.root.attachNewNode(self.headCollNode) + self.arm1Coll = CollisionSphere(1.65, 0, 3.95, 1.0) + self.arm1Coll.setTangible(1) + self.arm1CollNode = CollisionNode('PartyCog-%d-Arm1-Collision' % self.id) + self.arm1CollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.arm1CollNode.addSolid(self.arm1Coll) + self.arm1CollNodePath = self.root.attachNewNode(self.arm1CollNode) + self.arm2Coll = CollisionSphere(-1.65, 0, 3.45, 1.0) + self.arm2Coll.setTangible(1) + self.arm2CollNode = CollisionNode('PartyCog-%d-Arm2-Collision' % self.id) + self.arm2CollNode.setCollideMask(ToontownGlobals.PieBitmask) + self.arm2CollNode.addSolid(self.arm2Coll) + self.arm2CollNodePath = self.root.attachNewNode(self.arm2CollNode) + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splatType = globalPropPool.getPropType(splatName) + self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.mp3') + self.upSound = globalBattleSoundCache.getSound('AV_jump_to_side.mp3') + self.hole = loader.loadModel('phase_13/models/parties/cogPinataHole') + self.hole.setTransparency(True) + self.hole.setP(-90.0) + self.hole.setScale(3) + self.hole.setBin('ground', 3) + self.hole.reparentTo(self.parentNode) + + def unload(self): + self.request('Off') + self.clearHitInterval() + if self.hole is not None: + self.hole.removeNode() + self.hole = None + if self.actor is not None: + self.actor.cleanup() + self.actor.removeNode() + self.actor = None + if self.root is not None: + self.root.removeNode() + self.root = None + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.kaboomTrack = None + if self.resetRollIval is not None and self.resetRollIval.isPlaying(): + self.resetRollIval.finish() + self.resetRollIval = None + if self.hitInterval is not None and self.hitInterval.isPlaying(): + self.hitInterval.finish() + self.hitInterval = None + del self.upSound + del self.pieHitSound + return + + def enterStatic(self): + pass + + def exitStatic(self): + pass + + def enterActive(self, startTime): + self.root.setR(0.0) + updateTask = Task.Task(self.updateTask) + updateTask.startTime = startTime + taskMgr.add(updateTask, 'PartyCog.update-%d' % self.id) + + def exitActive(self): + taskMgr.remove('PartyCog.update-%d' % self.id) + taskMgr.remove('PartyCog.bounceTask-%d' % self.id) + self.clearHitInterval() + self.resetRollIval = self.root.hprInterval(0.5, Point3(self.root.getH(), 0.0, 0.0), blendType='easeInOut') + self.resetRollIval.start() + self.actor.stop() + + def enterDown(self): + if self.oldState == 'Off': + downAnimControl = self.actor.getAnimControl('down') + self.actor.pose('down', downAnimControl.getNumFrames() - 1) + return + self.clearHitInterval() + startScale = self.hole.getScale() + endScale = Point3(5, 5, 5) + self.hitInterval = Sequence(LerpFunc(self.setAlongSpline, duration=1.0, fromData=self.currentT, toData=0.0), LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType='easeIn'), Parallel(SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, 'down', loop=0)), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType='easeOut')) + self.hitInterval.start() + + def exitDown(self): + self.root.setR(0.0) + self.root.setH(0.0) + self.targetDistance = 0.0 + self.targetFacing = 0.0 + self.currentT = 0.0 + self.setAlongSpline(0.0) + self.clearHitInterval() + startScale = self.hole.getScale() + endScale = Point3(5, 5, 5) + self.hitInterval = Sequence(LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType='easeIn'), Parallel(SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, 'up', loop=0)), Func(self.actor.loop, 'idle'), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType='easeOut')) + self.hitInterval.start() + + def filterDown(self, request, args): + if request == 'Down': + return None + else: + return self.defaultFilter(request, args) + return None + + def setEndPoints(self, start, end, amplitude = 1.7): + self.sinAmplitude = amplitude + self.sinPeriod = (end.getX() - start.getX()) / 2 + self.sinDisplacement = start.getY() + self.startPoint = start + self.endPoint = end + self.currentT = 0.0 + self.targetDistance = 0.0 + self.currentFacing = 0.0 + self.targetFacing = 0.0 + self.setAlongSpline(self.currentT) + self.hole.setPos(self.root.getPos()) + self.hole.setZ(0.02) + + def rockBackAndForth(self, task): + t = task.startTime + task.time + angle = math.sin(t) * 20.0 + self.root.setR(angle) + return task.cont + + def updateDistance(self, distance): + self.targetDistance = clamp(distance, -1.0, 1.0) + + def updateTask(self, task): + self.rockBackAndForth(task) + if self.targetDistance > self.currentT: + self.currentT += min(0.01, self.targetDistance - self.currentT) + self.setAlongSpline(self.currentT) + elif self.targetDistance < self.currentT: + self.currentT += max(-0.01, self.targetDistance - self.currentT) + self.setAlongSpline(self.currentT) + if self.currentT < 0.0: + self.targetFacing = -90.0 + elif self.currentT > 0.0: + self.targetFacing = 90.0 + else: + self.targetFacing = 0.0 + if self.targetFacing > self.currentFacing: + self.currentFacing += min(10, self.targetFacing - self.currentFacing) + elif self.targetFacing < self.currentFacing: + self.currentFacing += max(-10, self.targetFacing - self.currentFacing) + self.root.setH(self.currentFacing) + return task.cont + + def setAlongSpline(self, t): + t = t + 1.0 + dist = (self.endPoint.getX() - self.startPoint.getX()) / 2.0 + x = self.startPoint.getX() + t * dist + y = self.startPoint.getY() - math.sin(t * 2 * math.pi) * self.sinAmplitude + self.root.setPos(x, y, 0) + + def startBounce(self): + taskMgr.add(self.bounce, 'PartyCog.bounceTask-%d' % self.id) + + def bounce(self, task): + self.root.setZ(math.sin((self.bounceOffset + task.time) * self.bounceSpeed) * self.bounceHeight + self.heightShift) + return task.cont + + def setPos(self, position): + self.root.setPos(position) + + def respondToPieHit(self, timestamp, position, hot = False, direction = 1.0): + if self.netTimeSentToStartByHit < timestamp: + self.__showSplat(position, direction, hot) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.notify.debug('respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def clearHitInterval(self): + if self.hitInterval is not None and self.hitInterval.isPlaying(): + self.hitInterval.clearToInitial() + return + + def __showSplat(self, position, direction, hot = False): + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.clearHitInterval() + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splat.reparentTo(render) + self.splat.setPos(self.root, position) + self.splat.setAlphaScale(1.0) + if not direction == 1.0: + self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[0]) + if self.currentFacing > 0.0: + facing = 'HitFront' + else: + facing = 'HitBack' + else: + self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1]) + if self.currentFacing > 0.0: + facing = 'HitBack' + else: + facing = 'HitFront' + if hot: + targetscale = 0.75 + part = 'head' + else: + targetscale = 0.5 + part = 'body' + + def setSplatAlpha(amount): + self.splat.setAlphaScale(amount) + + self.hitInterval = Sequence(ActorInterval(self.actor, part + facing, loop=0), Func(self.actor.loop, 'idle')) + self.hitInterval.start() + self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, volume=1.0, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode))) + self.kaboomTrack.start() + return + + def showHitScore(self, number, scale = 1): + if number <= 0: + return + if self.hpText: + self.hideHitScore() + self.HpTextGenerator.setFont(ToontownGlobals.getSignFont()) + if number < 0: + self.HpTextGenerator.setText(str(number)) + else: + self.HpTextGenerator.setText('+' + str(number)) + self.HpTextGenerator.clearShadow() + self.HpTextGenerator.setAlign(TextNode.ACenter) + r = 1 + g = 1 + b = 0 + a = 1 + self.HpTextGenerator.setTextColor(r, g, b, a) + self.hpTextNode = self.HpTextGenerator.generate() + self.hpText = render.attachNewNode(self.hpTextNode) + self.hpText.setScale(scale) + self.hpText.setBillboardPointEye() + self.hpText.setBin('fixed', 100) + self.hpText.setPos(self.root, 0, 0, self.height / 2) + seq = Task.sequence(self.hpText.lerpPos(Point3(self.root.getX(render), self.root.getY(render), self.root.getZ(render) + self.height + 1.0), 0.25, blendType='easeOut'), Task.pause(0.25), self.hpText.lerpColor(Vec4(r, g, b, a), Vec4(r, g, b, 0), 0.1), Task.Task(self.__hideHitScoreTask)) + taskMgr.add(seq, 'PartyCogHpText' + str(self.id)) + + def __hideHitScoreTask(self, task): + self.hideHitScore() + return Task.done + + def hideHitScore(self): + if self.hpText: + taskMgr.remove('PartyCogHpText' + str(self.id)) + self.hpText.removeNode() + self.hpText = None + return + + def getHeadLocation(self): + self.actor.getJoints(jointName='head')[0].getNetTransform(self.temp_transform) + self.head_locator.setMat(self.temp_transform) + return self.head_locator.getZ(self.root) diff --git a/toontown/parties/PartyCogActivity.py b/toontown/parties/PartyCogActivity.py new file mode 100644 index 0000000..65375fc --- /dev/null +++ b/toontown/parties/PartyCogActivity.py @@ -0,0 +1,679 @@ +from direct.interval.MetaInterval import Sequence, Parallel, Track +from direct.interval.FunctionInterval import Func, Wait +from direct.interval.SoundInterval import SoundInterval +from direct.interval.ActorInterval import ActorInterval +from direct.interval.ProjectileInterval import ProjectileInterval +from direct.distributed.ClockDelta import globalClockDelta +from direct.showbase.PythonUtil import bound, lerp +from direct.showbase.DirectObject import DirectObject +from pandac.PandaModules import NodePath, Point3, TextNode +from pandac.PandaModules import CollisionSphere, CollisionNode, CollisionHandlerEvent +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToontownTimer import ToontownTimer +import PartyGlobals +import PartyCogUtils +from PartyCog import PartyCogManager +from PartyCogActivityPlayer import PartyCogActivityPlayer +from PartyCogActivityPlayer import PartyCogActivityLocalPlayer +from StretchingArrow import StretchingArrow + +class PartyCogActivity(DirectObject): + notify = directNotify.newCategory('PartyCogActivity') + cog = None + arena = None + player = None + players = {} + + def __init__(self, activity, arenaModel = None, texture = None): + self.activity = activity + self.root = self.activity.root + self.toonPieTracks = {} + self.toonPieEventNames = {} + self.toonIdsToAnimIntervals = {} + self.pieIvals = [] + self.resultsIval = None + self.arenaModel = arenaModel + self.texture = texture + return + + def load(self): + self.arena = loader.loadModel(self.arenaModel) + self.arena.reparentTo(self.root) + ground = self.arena.find('**/ground') + ground.setBin('ground', 1) + entranceArrows = self.arena.findAllMatches('**/arrowFlat*') + for arrow in entranceArrows: + arrow.setBin('ground', 5) + + self.leftEntranceLocator = self.arena.find('**/leftEntrance_locator') + self.rightEntranceLocator = self.arena.find('**/rightEntrance_locator') + self.leftExitLocator = self.arena.find('**/leftExit_locator') + self.rightExitLocator = self.arena.find('**/rightExit_locator') + self.teamCamPosLocators = (self.arena.find('**/team0CamPos_locator'), self.arena.find('**/team1CamPos_locator')) + self.teamCamAimLocators = (self.arena.find('**/team0CamAim_locator'), self.arena.find('**/team1CamAim_locator')) + leftTeamLocator = NodePath('TeamLocator-%d' % PartyGlobals.TeamActivityTeams.LeftTeam) + leftTeamLocator.reparentTo(self.root) + leftTeamLocator.setH(90) + rightTeamLocator = NodePath('TeamLocator-%d' % PartyGlobals.TeamActivityTeams.RightTeam) + rightTeamLocator.reparentTo(self.root) + rightTeamLocator.setH(-90) + self.teamLocators = (leftTeamLocator, rightTeamLocator) + self._lengthBetweenEntrances = self.leftEntranceLocator.getY() - self.rightExitLocator.getY() + self._skyCollisionsCollection = self.arena.findAllMatches('**/cogPieArena_sky*_collision') + if len(self._skyCollisionsCollection) > 0: + self._skyCollisionParent = self._skyCollisionsCollection[0].getParent() + else: + self._skyCollisionParent = self.arena + self._wallCollisionsCollection = self.arena.findAllMatches('**/cogPieArena_wall*_collision') + self._arenaFlagGroups = (self.arena.find('**/flagsL_grp'), self.arena.find('**/flagsR_grp')) + self._initArenaDoors() + self.cogManager = PartyCogManager() + self.arrows = [] + self.distanceLabels = [] + self.teamColors = list(PartyGlobals.CogActivityColors) + [PartyGlobals.TeamActivityStatusColor] + for i in range(3): + start = self.arena.find('**/cog%d_start_locator' % (i + 1)) + end = self.arena.find('**/cog%d_end_locator' % (i + 1)) + cog = self.cogManager.generateCog(self.arena) + cog.setEndPoints(start.getPos(), end.getPos()) + arrow1 = StretchingArrow(self.arena, useColor='orange') + arrow2 = StretchingArrow(self.arena, useColor='blue') + arrow1.setZ(0.1) + arrow2.setZ(0.1) + self.arrows.append([arrow1, arrow2]) + distanceLabel = self.createDistanceLabel(0, self.teamColors[1]) + distanceLabel[0].stash() + distanceLabel2 = self.createDistanceLabel(0, self.teamColors[0]) + distanceLabel2[0].stash() + self.distanceLabels.append([distanceLabel, distanceLabel2]) + + self.winText = [] + text1 = self.createText(0, Point3(-0.5, 0.0, -0.5), self.teamColors[1]) + text2 = self.createText(1, Point3(0.5, 0.0, -0.5), self.teamColors[0]) + self.winText.append(text1) + self.winText.append(text2) + self.winStatus = self.createText(2, Point3(0.0, 0.0, -0.8), self.teamColors[0]) + signLocator = self.arena.find('**/eventSign_locator') + self.activity.sign.setPos(signLocator.getPos(self.root)) + if self.texture: + textureAlpha = self.texture[:-4] + '_a.rgb' + reskinTexture = loader.loadTexture(self.texture, textureAlpha) + self.arena.find('**/center_grp').setTexture(reskinTexture, 100) + self.arena.find('**/leftSide_grp').setTexture(reskinTexture, 100) + self.arena.find('**/rightSide_grp').setTexture(reskinTexture, 100) + self.enable() + + def _initArenaDoors(self): + self._arenaDoors = (self.arena.find('**/doorL'), self.arena.find('**/doorR')) + arenaDoorLocators = (self.arena.find('**/doorL_locator'), self.arena.find('**/doorR_locator')) + for i in range(len(arenaDoorLocators)): + arenaDoorLocators[i].wrtReparentTo(self._arenaDoors[i]) + + self._arenaDoorTimers = (self.createDoorTimer(PartyGlobals.TeamActivityTeams.LeftTeam), self.createDoorTimer(PartyGlobals.TeamActivityTeams.RightTeam)) + self._arenaDoorIvals = [None, None] + self._doorStartPos = [] + for i in range(len(self._arenaDoors)): + door = self._arenaDoors[i] + timer = self._arenaDoorTimers[i] + timer.reparentTo(arenaDoorLocators[i]) + timer.hide() + self._doorStartPos.append(door.getPos()) + door.setPos(door, 0, 0, -7.0) + + return + + def _destroyArenaDoors(self): + for ival in self._arenaDoorIvals: + ival.finish() + + self._arenaDoorIvals = None + self._arenaDoors = None + for timer in self._arenaDoorTimers: + timer.stop() + timer.removeNode() + + self._arenaDoorTimers = None + return + + def createDoorTimer(self, team): + timer = ToontownTimer(useImage=False, highlightNearEnd=False) + timer['text_font'] = ToontownGlobals.getMinnieFont() + timer.setFontColor(PartyGlobals.CogActivityColors[team]) + timer.setScale(7.0) + timer.setPos(0.2, -0.03, 0.0) + return timer + + def createText(self, number, position, color): + text = TextNode('winText%d' % number) + text.setAlign(TextNode.ACenter) + text.setTextColor(color) + text.setFont(ToontownGlobals.getSignFont()) + text.setText('') + noteText = aspect2d.attachNewNode(text) + noteText.setScale(0.2) + noteText.setPos(position) + noteText.stash() + return (text, noteText) + + def createDistanceLabel(self, number, color): + text = TextNode('distanceText-%d' % number) + text.setAlign(TextNode.ACenter) + text.setTextColor(color) + text.setFont(ToontownGlobals.getSignFont()) + text.setText('10 ft') + node = self.root.attachNewNode(text) + node.setBillboardPointEye() + node.setScale(2.5) + node.setZ(5.0) + return (node, text) + + def unload(self): + self.disable() + self._cleanupResultsIval() + if self.winText is not None: + for pair in self.winText: + pair[1].reparentTo(hidden) + pair[1].removeNode() + + self.winText = None + if self.winStatus is not None: + self.winStatus[1].reparentTo(hidden) + self.winStatus[1].removeNode() + self.winStatus = None + if self.cogManager is not None: + self.cogManager.unload() + self.cogManager = None + if self.arrows is not None: + for pair in self.arrows: + for arrow in pair: + arrow.destroy() + arrow = None + + pair = None + + self.arrows = None + if self.distanceLabels is not None: + for pair in self.distanceLabels: + for node, text in pair: + node.removeNode() + + pair = None + + self.distanceLabels = None + if len(self.players): + for player in self.players.values(): + player.disable() + player.destroy() + + self.players.clear() + self.player = None + if self.arena is not None: + self.leftEntranceLocator = None + self.rightEntranceLocator = None + self.leftExitLocator = None + self.rightExitLocator = None + self._skyCollisions = None + self._skyCollisionParent = None + self._arenaFlagGroups = None + self._destroyArenaDoors() + self.arena.removeNode() + self.arena = None + for ival in self.toonPieTracks.values(): + if ival is not None and ival.isPlaying(): + try: + ival.finish() + except Exception, theException: + self.notify.warning('Ival could not finish:\n %s \nException %s ' % (str(ival), str(theException))) + + self.toonPieTracks = {} + for ival in self.pieIvals: + if ival is not None and ival.isPlaying(): + try: + ival.finish() + except Exception, theException: + self.notify.warning('Ival could not finish:\n %s \nException %s ' % (str(ival), str(theException))) + + self.pieIvals = [] + self.toonIdsToAnimIntervals = {} + for eventName in self.toonPieEventNames.values(): + self.ignore(eventName) + + self.toonPieEventNames = {} + return + + def enable(self): + self.enableEnterGateCollision() + + def disable(self): + self.disableEnterGateCollision() + self.ignoreAll() + + def hideTeamFlags(self, team): + self._arenaFlagGroups[team].stash() + + def showTeamFlags(self, team): + self._arenaFlagGroups[team].unstash() + + def _playArenaDoorIval(self, team, opening = True): + ival = self._arenaDoorIvals[team] + if ival is not None and ival.isPlaying(): + ival.pause() + if not opening: + pos = self._doorStartPos[team] + else: + pos = (self._doorStartPos[team] + Point3(0, 0, -7.0),) + ival = self._arenaDoors[team].posInterval(0.75, pos, blendType='easeIn') + self._arenaDoorIvals[team] = ival + ival.start() + return + + def openArenaDoorForTeam(self, team): + self._playArenaDoorIval(team, opening=True) + + def closeArenaDoorForTeam(self, team): + self._playArenaDoorIval(team, opening=False) + + def openArenaDoors(self): + self.enableEnterGateCollision() + for i in range(len(self._arenaDoors)): + self.openArenaDoorForTeam(i) + + def closeArenaDoors(self): + self.disableEnterGateCollision() + for i in range(len(self._arenaDoors)): + self.closeArenaDoorForTeam(i) + + def showArenaDoorTimers(self, duration): + for timer in self._arenaDoorTimers: + timer.setTime(duration) + timer.countdown(duration) + timer.show() + + def hideArenaDoorTimers(self): + for timer in self._arenaDoorTimers: + timer.hide() + + def enableEnterGateCollision(self): + self.acceptOnce('entercogPieArena_entranceLeft_collision', self.handleEnterLeftEntranceTrigger) + self.acceptOnce('entercogPieArena_entranceRight_collision', self.handleEnterRightEntranceTrigger) + + def disableEnterGateCollision(self): + self.ignore('entercogPieArena_entranceLeft_collision') + self.ignore('entercogPieArena_entranceRight_collision') + + def enableWallCollisions(self): + self._wallCollisionsCollection.unstash() + + def disableWallCollisions(self): + self._wallCollisionsCollection.stash() + + def enableSkyCollisions(self): + self._skyCollisionsCollection.unstash() + + def disableSkyCollisions(self): + self._skyCollisionsCollection.stash() + + def handleEnterLeftEntranceTrigger(self, collEntry): + self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.LeftTeam) + + def handleEnterRightEntranceTrigger(self, collEntry): + self.activity.d_toonJoinRequest(PartyGlobals.TeamActivityTeams.RightTeam) + + def checkOrthoDriveCollision(self, oldPos, newPos): + x = bound(newPos[0], -16.8, 16.8) + y = bound(newPos[1], -17.25, -24.1) + newPos.setX(x) + newPos.setY(y) + return newPos + + def getPlayerStartPos(self, team, spot): + if team == PartyGlobals.TeamActivityTeams.LeftTeam: + node = self.leftExitLocator + else: + node = self.rightExitLocator + d = self._lengthBetweenEntrances / (self.activity.getMaxPlayersPerTeam() + 1) + yOffset = node.getY(self.root) + d * (spot + 1) + pos = node.getPos(self.root) + pos.setY(yOffset) + return pos + + def handleToonJoined(self, toon, team, lateEntry = False): + pos = self.getPlayerStartPos(team, self.activity.getIndex(toon.doId, team)) + if toon == base.localAvatar: + player = PartyCogActivityLocalPlayer(self.activity, pos, team, self.handleToonExited) + player.entersActivity() + self.player = player + self.disableSkyCollisions() + self.playPlayerEnterIval() + else: + player = PartyCogActivityPlayer(self.activity, toon, pos, team) + player.entersActivity() + if lateEntry: + player.updateToonPosition() + self.players[toon.doId] = player + + def handleToonSwitchedTeams(self, toon): + toonId = toon.doId + player = self.players.get(toonId) + if player is None: + self.notify.warning('handleToonSwitchedTeams: toonId %s not found' % toonId) + return + team = self.activity.getTeam(toonId) + spot = self.activity.getIndex(toonId, team) + pos = self.getPlayerStartPos(team, spot) + self.finishToonIval(toonId) + player.setTeam(team) + player.setToonStartPosition(pos) + player.updateToonPosition() + return + + def handleToonShifted(self, toon): + toonId = toon.doId + if self.players.has_key(toonId): + player = self.players[toonId] + spot = self.activity.getIndex(toonId, player.team) + pos = self.getPlayerStartPos(player.team, spot) + player.setToonStartPosition(pos) + if self.player is not None and toon == self.player.toon: + self.playToonIval(base.localAvatar.doId, self.player.getRunToStartPositionIval()) + return + + def handleToonDisabled(self, toonId): + self.finishToonIval(toonId) + self.finishPieIvals(toonId) + player = self.players.get(toonId) + if player is not None: + player.disable() + if player == self.player: + self.player = None + del self.players[toonId] + return + + def finishPieIvals(self, toonId): + for ival in self.pieIvals: + if ival.isPlaying(): + if ival.getName().find(str(toonId)) != -1: + ival.finish() + + def playPlayerEnterIval(self): + + def conditionallyShowSwitchButton(self = self, enable = True): + if enable and self.activity.activityFSM.state in ['WaitForEnough', 'WaitToStart']: + self.activity.teamActivityGui.enableSwitchButton() + else: + self.activity.teamActivityGui.disableSwitchButton() + + ival = Sequence(Func(self.disableWallCollisions), Func(conditionallyShowSwitchButton, self, False), self.player.getRunToStartPositionIval(), Func(conditionallyShowSwitchButton, self, True), Func(self.enableWallCollisions)) + self.playToonIval(base.localAvatar.doId, ival) + + def finishToonIval(self, toonId): + if self.toonIdsToAnimIntervals.get(toonId) is not None and self.toonIdsToAnimIntervals[toonId].isPlaying(): + self.toonIdsToAnimIntervals[toonId].finish() + return + + def playToonIval(self, toonId, ival): + self.finishToonIval(toonId) + self.toonIdsToAnimIntervals[toonId] = ival + ival.start() + + def startActivity(self, timestamp): + self.pieHandler = CollisionHandlerEvent() + self.pieHandler.setInPattern('pieHit-%fn') + if self.player is not None: + self.player.resetScore() + self.hideTeamFlags(self.player.team) + for player in self.players.values(): + self.finishToonIval(player.toon.doId) + player.enable() + + for cog in self.cogManager.cogs: + cog.request('Active', timestamp) + + for ival in self.pieIvals: + if ival.isPlaying(): + ival.finish() + + self.pieIvals = [] + return + + def stopActivity(self): + for player in self.players.values(): + player.disable() + + for eventName in self.toonPieEventNames.values(): + self.ignore(eventName) + + self.toonPieEventNames.clear() + for cog in self.cogManager.cogs: + cog.request('Static') + + def handleToonExited(self, toon): + self.finishToonIval(toon.doId) + player = self.players[toon.doId] + player.disable() + player.exitsActivity() + player.destroy() + if player == self.player: + self.showTeamFlags(self.activity.getTeam(toon.doId)) + self.player = None + self.enableEnterGateCollision() + self.enableSkyCollisions() + del self.players[toon.doId] + return + + def pieThrow(self, avId, timestamp, heading, pos, power): + toon = self.activity.getAvatar(avId) + if toon is None: + return + tossTrack, pieTrack, flyPie = self.getTossPieInterval(toon, pos[0], pos[1], pos[2], heading, 0, 0, power) + if avId == base.localAvatar.doId: + flyPie.setTag('throwerId', str(avId)) + collSphere = CollisionSphere(0, 0, 0, 0.5) + collSphere.setTangible(0) + name = 'PieSphere-%d' % avId + collSphereName = self.activity.uniqueName(name) + collNode = CollisionNode(collSphereName) + collNode.setFromCollideMask(ToontownGlobals.PieBitmask) + collNode.addSolid(collSphere) + collNP = flyPie.attachNewNode(collNode) + base.cTrav.addCollider(collNP, self.pieHandler) + self.toonPieEventNames[collNP] = 'pieHit-' + collSphereName + self.accept(self.toonPieEventNames[collNP], self.handlePieCollision) + else: + player = self.players.get(avId) + if player is not None: + player.faceForward() + + def matchRunningAnim(toon = toon): + toon.playingAnim = None + toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed) + return + + newTossTrack = Sequence(tossTrack, Func(matchRunningAnim)) + pieTrack = Parallel(newTossTrack, pieTrack, name='PartyCogActivity.pieTrack-%d-%s' % (avId, timestamp)) + elapsedTime = globalClockDelta.localElapsedTime(timestamp) + if elapsedTime < 16.0 / 24.0: + elapsedTime = 16.0 / 24.0 + pieTrack.start(elapsedTime) + self.pieIvals.append(pieTrack) + self.toonPieTracks[avId] = pieTrack + return + + def getTossPieInterval(self, toon, x, y, z, h, p, r, power, beginFlyIval = Sequence()): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pie = toon.getPieModel() + pie.setScale(0.5) + flyPie = pie.copyTo(NodePath('a')) + pieName = ToontownBattleGlobals.pieNames[toon.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=48) + sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.mp3') + t = power / 100.0 + dist = lerp(PartyGlobals.CogActivityPieMinDist, PartyGlobals.CogActivityPieMaxDist, t) + time = lerp(1.0, 1.5, t) + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + + def getVelocity(toon = toon, relVel = relVel): + return render.getRelativeVector(toon, relVel) * 0.6 + + def __safeSetAnimState(toon = toon, state = 'Happy'): + if toon and hasattr(toon, 'animFSM'): + toon.setAnimState('Happy') + else: + self.notify.warning('The toon is being destroyed. No attribute animState.') + + toss = Track((0, Sequence(Func(toon.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, toon.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), animPie, Parallel(ActorInterval(toon, 'throw', startFrame=48, playRate=1.5, partName='torso'), animPie), Func(__safeSetAnimState, toon, 'Happy'))), (16.0 / 24.0, Func(pie.detachNode))) + fly = Track((14.0 / 24.0, SoundInterval(sound, node=toon, cutOff=PartyGlobals.PARTY_COG_CUTOFF)), (16.0 / 24.0, Sequence(Func(flyPie.reparentTo, render), Func(flyPie.setPosHpr, toon, 0.52, 0.97, 2.24, 0, -45, 0), beginFlyIval, ProjectileInterval(flyPie, startVel=getVelocity, duration=6), Func(flyPie.detachNode)))) + return (toss, fly, flyPie) + + def handlePieCollision(self, colEntry): + if not self.activity.isState('Active') or self.player is None: + return + handled = False + into = colEntry.getIntoNodePath() + intoName = into.getName() + timestamp = globalClockDelta.localToNetworkTime(globalClock.getFrameTime(), bits=32) + if 'PartyCog' in intoName: + if self.toonPieTracks.get(base.localAvatar.doId) is not None: + self.toonPieTracks[base.localAvatar.doId].finish() + self.toonPieTracks[base.localAvatar.doId] = None + parts = intoName.split('-') + cogID = int(parts[1]) + point = colEntry.getSurfacePoint(self.cogManager.cogs[cogID].root) + cog = self.cogManager.cogs[cogID] + hitHead = point.getZ() > cog.getHeadLocation() and not parts[2].startswith('Arm') + if self.activity.getTeam(base.localAvatar.doId) == PartyGlobals.TeamActivityTeams.LeftTeam: + direction = -1.0 + else: + direction = 1.0 + self.activity.b_pieHitsCog(timestamp, cogID, point, direction, hitHead) + if hitHead: + hitPoints = self.player.hitHead() + else: + hitPoints = self.player.hitBody() + self.player.updateScore() + if hitPoints > 0: + cog.showHitScore(hitPoints) + handled = True + elif 'distAvatarCollNode' in intoName: + parts = intoName.split('-') + hitToonId = int(parts[1]) + toon = base.cr.doId2do.get(hitToonId) + if toon is not None and self.activity.getTeam(hitToonId) != self.player.team: + point = colEntry.getSurfacePoint(toon) + self.activity.b_pieHitsToon(hitToonId, timestamp, point) + handled = True + if handled: + eventName = self.toonPieEventNames.get(colEntry.getFromNodePath()) + if eventName is not None: + self.ignore(eventName) + del self.toonPieEventNames[colEntry.getFromNodePath()] + + def pieHitsCog(self, timestamp, cogNum, pos, direction, part): + cog = self.cogManager.cogs[cogNum] + cog.respondToPieHit(timestamp, pos, part, direction) + + def pieHitsToon(self, toonId, timestamp, pos): + player = self.players.get(toonId) + if player is not None: + player.respondToPieHit(timestamp, pos) + return + + def setCogDistances(self, distances): + self.cogManager.updateDistances(distances) + + def showCogs(self): + for cog in self.cogManager.cogs: + cog.request('Static') + + def hideCogs(self): + for cog in self.cogManager.cogs: + cog.request('Down') + + def showResults(self, resultsText, winner, totals): + if self.player is None: + return + base.localAvatar.showName() + self.resultsIval = Sequence(Wait(0.1), Func(self.activity.setStatus, TTLocalizer.PartyCogTimeUp), Func(self.activity.showStatus), Wait(2.0), Func(self.activity.hideStatus), Wait(0.5), Func(self.player.lookAtArena), Func(self.showTeamFlags, self.activity.getTeam(base.localAvatar.doId)), Wait(1.0), Func(self.showArrow, 0), Wait(1.3), Func(self.showArrow, 1), Wait(1.3), Func(self.showArrow, 2), Wait(1.3), Func(self.showTotals, totals), Wait(1.0), Func(self.showWinner, resultsText, winner), Func(self._cleanupResultsIval), name='PartyCog-conclusionSequence') + self.accept('DistributedPartyActivity-showJellybeanReward', self._cleanupResultsIval) + self.resultsIval.start() + return + + def _cleanupResultsIval(self): + if self.resultsIval: + if self.resultsIval.isPlaying(): + self.resultsIval.pause() + self.resultsIval = None + self.ignore('DistributedPartyActivity-showJellybeanReward') + return + + def showTotals(self, totals): + newtotals = (totals[1] - totals[0] + PartyGlobals.CogActivityArenaLength / 2.0 * 3, totals[0] - totals[1] + PartyGlobals.CogActivityArenaLength / 2.0 * 3) + self.winText[0][0].setText(TTLocalizer.PartyCogDistance % newtotals[0]) + self.winText[1][0].setText(TTLocalizer.PartyCogDistance % newtotals[1]) + for textPair in self.winText: + textPair[1].unstash() + + def hideTotals(self): + for textPair in self.winText: + textPair[0].setText('') + textPair[1].stash() + + def showWinner(self, text, winner): + self.winStatus[0].setText(text) + self.winStatus[0].setTextColor(self.teamColors[winner]) + self.winStatus[1].unstash() + + def hideWinner(self): + self.winStatus[0].setText('') + self.winStatus[1].stash() + + def showArrow(self, arrowNum): + arrows = self.arrows[arrowNum] + cog = self.cogManager.cogs[arrowNum] + points = [self.arena.find('**/cog%d_start_locator' % (arrowNum + 1)), self.arena.find('**/cog%d_end_locator' % (arrowNum + 1))] + Y = cog.root.getY() + for point in points: + point.setY(Y) + + for i in range(len(arrows)): + arrow = arrows[i] + arrow.draw(points[i].getPos(), cog.root.getPos(), animate=False) + arrow.unstash() + + i = -1 + length = PartyGlobals.CogActivityArenaLength + for node, text in self.distanceLabels[arrowNum]: + current = bound(i, 0, 1) + node.setPos(cog.root.getPos(self.root) + Point3(i * 4, 2, 4)) + dist = PartyCogUtils.getCogDistanceUnitsFromCenter(cog.currentT) + dist = abs(dist - i * length / 2) + if dist > length - dist: + node.setScale(2.8) + else: + node.setScale(2.2) + text.setText(TTLocalizer.PartyCogDistance % dist) + if dist > 0: + node.unstash() + else: + arrows[current].stash() + i += 2 + + def hideArrows(self): + for pair in self.arrows: + for arrow in pair: + arrow.stash() + + for pair in self.distanceLabels: + for node, text in pair: + node.stash() + + def hideResults(self): + self.hideArrows() + self.hideTotals() + self.hideWinner() diff --git a/toontown/parties/PartyCogActivityGui.py b/toontown/parties/PartyCogActivityGui.py new file mode 100644 index 0000000..bf719f0 --- /dev/null +++ b/toontown/parties/PartyCogActivityGui.py @@ -0,0 +1,329 @@ +from direct.gui.DirectGui import DirectWaitBar, DGG +from direct.gui.OnscreenText import OnscreenText +from direct.showbase.DirectObject import DirectObject +from direct.interval.LerpInterval import LerpScaleInterval +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Wait, Func +from pandac.PandaModules import Point3, VBase4 +from pandac.PandaModules import TextNode +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownIntervals +from toontown.toonbase import TTLocalizer +import PartyGlobals + +class PartyCogTrackerGui: + + def __init__(self): + self.cogTracker = loader.loadModel('phase_13/models/parties/cogTrackerGUI') + self.cogTracker.reparentTo(aspect2d) + self.cogTracker.setScale(1.25) + self.cogTracker.setX(1.0) + self.cogTracker.setZ(-0.75) + self.frame = self.cogTracker.find('**/tracker') + self.cogs = [] + self.cogLayers = [] + self.blinkIntervals = [] + i = 0 + self.cogTracker.find('**/shadow').setBin('fixed', 0) + self.cogTracker.find('**/plane').setBin('fixed', 1) + for i in range(3): + layers = [self.cogTracker.find('**/cog%d_blue' % i), self.cogTracker.find('**/cog%d_orange' % i), self.cogTracker.find('**/cog%d_white' % i)] + self.cogs.append(self.cogTracker.find('**/cog%d' % i)) + self.cogLayers.append(layers) + self.cogTracker.find('**/cog%d' % i).setBin('fixed', 2) + big = Point3(1.5, 1.5, 1.5) + seq = Sequence(LerpScaleInterval(self.cogs[i], duration=0.1, scale=big, startScale=Point3(1.0, 1.0, 1.0), blendType='easeOut'), LerpScaleInterval(self.cogs[i], duration=0.25, scale=Point3(1.0, 1.0, 1.0), startScale=big, blendType='easeOut'), Wait(0.4)) + self.blinkIntervals.append(seq) + + self.top = self.cogTracker.find('**/cog0_top').getZ() + self.bottom = self.cogTracker.find('**/cog0_bottom').getZ() + self.whiteTextureNp = self.cogTracker.find('**/cog0_white') + self.whiteTexture = self.whiteTextureNp.findTexture('*') + for cog in self.cogs: + cog.setTexture(self.whiteTexture) + + def updateCog(self, cogNumber, cog, team): + theCog = self.cogs[cogNumber] + t = (cog.currentT + 1.0) / 2.0 + Z = self.bottom + (self.top - self.bottom) * t + theCog.setZ(Z) + for layer in self.cogLayers[cogNumber]: + layer.stash() + + if cog.currentT < 0.0: + self.cogLayers[cogNumber][0].unstash() + if team == 1 and cog.currentT < -0.5 and not self.blinkIntervals[cogNumber].isPlaying(): + self.blinkIntervals[cogNumber].start() + elif cog.currentT > 0.0: + self.cogLayers[cogNumber][1].unstash() + if team == 0 and cog.currentT > 0.5 and not self.blinkIntervals[cogNumber].isPlaying(): + self.blinkIntervals[cogNumber].start() + else: + self.cogLayers[cogNumber][2].unstash() + + def destory(self): + if self.blinkIntervals is not None: + for interval in self.blinkIntervals: + if interval is not None: + interval.clearToInitial() + interval = None + + if self.cogTracker is not None: + self.cogTracker.removeNode() + self.cogTracker = None + return + + +class PartyCogActivityGui(DirectObject): + notify = directNotify.newCategory('PartyCogActivityGui') + + def __init__(self): + DirectObject.__init__(self) + self._piePowerMeter = None + self._victoryBalanceBar = None + self._scoreLabel = None + self._cogTracker = None + self._piePowerTitle = None + self._victoryBalanceTitle = None + self._scoreTitle = None + self._spamWarning = None + self._spamWarningIvalName = 'PartyCogActivityGui-SpamWarning' + return + + def load(self): + self._initPiePowerMeter() + self._initScore() + self._initCogTracker() + self._initSpamWarning() + self._initControlGui() + self._initVictoryBalanceBar() + + def unload(self): + if self._cogTracker is not None: + self._cogTracker.destory() + self._cogTracker = None + if self._piePowerMeter is not None: + self._piePowerMeter.destroy() + self._piePowerMeter = None + if self._piePowerTitle is not None: + self._piePowerTitle.destroy() + self._piePowerTitle = None + if self._scoreLabel is not None: + self._scoreLabel.destroy() + self._scoreLabel = None + if self._scoreTitle is not None: + self._scoreTitle.destroy() + self._scoreTitle = None + taskMgr.remove(self._spamWarningIvalName) + if self._spamWarning: + self._spamWarning.destroy() + self._spamWarning = None + if hasattr(self, '_attackKeys'): + self._attackKeys.detachNode() + del self._attackKeys + if hasattr(self, '_moveKeys'): + self._moveKeys.detachNode() + del self._moveKeys + if self._victoryBalanceBar: + self._victoryBalanceBar.detachNode() + self._victoryBalanceBar = None + if self._victoryBalanceBarOrange: + self._victoryBalanceBarOrange.detachNode() + self._victoryBalanceBarOrange = None + if self._victoryBalanceBarPie: + self._victoryBalanceBarPie.detachNode() + self._victoryBalanceBarPie = None + if self._victoryBalanceBarArrow: + self._victoryBalanceBarArrow.detachNode() + self._victoryBalanceBarArrow = None + return + + def _initVictoryBalanceBar(self): + h = PartyGlobals.CogActivityPowerMeterHeight / 2.0 + w = PartyGlobals.CogActivityPowerMeterWidth / 2.0 + victoryBalanceBar = loader.loadModel('phase_13/models/parties/tt_m_gui_pty_pieToss_balanceBar') + self._victoryBalanceBar = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceBarBG') + self._victoryBalanceBar.reparentTo(aspect2d) + self._victoryBalanceBar.setBin('fixed', 0) + self._victoryBalanceBar.setPos(PartyGlobals.CogActivityVictoryBarPos) + self._victoryBalanceBar.setScale(1) + self._victoryBalanceBarOrange = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceBarOrange') + self._victoryBalanceBarOrange.reparentTo(self._victoryBalanceBar) + self._victoryBalanceBarOrange.setBin('fixed', 1) + self._victoryBalanceBarOrange.setPos(PartyGlobals.CogActivityVictoryBarOrangePos) + self._victoryBalanceBarOrange.setScale(PartyGlobals.CogActivityBarStartScale, 1.0, 1.0) + self._victoryBalanceBarPie = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceBarPie') + self._victoryBalanceBarPie.reparentTo(self._victoryBalanceBar) + self._victoryBalanceBarPie.setBin('fixed', 2) + self._victoryBalanceBarPie.setX(PartyGlobals.CogActivityVictoryBarPiePos[0]) + self._victoryBalanceBarPie.setY(PartyGlobals.CogActivityVictoryBarPiePos[1]) + self._victoryBalanceBarPie.setZ(PartyGlobals.CogActivityVictoryBarPiePos[2]) + self._victoryBalanceBarPie.setScale(PartyGlobals.CogActivityBarPieScale) + self._victoryBalanceBarArrow = victoryBalanceBar.find('**/*tt_t_gui_pty_pieToss_balanceArrow') + self._victoryBalanceBarArrow.reparentTo(self._victoryBalanceBarPie) + self._victoryBalanceBarArrow.setBin('fixed', 2) + self._victoryBalanceBarArrow.setPos(PartyGlobals.CogActivityVictoryBarArrow) + self._victoryBalanceBarArrow.setScale(1 / PartyGlobals.CogActivityBarPieScale) + + def _initControlGui(self): + self._attackIvalName = 'PartyCogActivityGui-attackKeys' + self._moveIvalName = 'PartyCogActivityGui-moveKeys' + pieTossControls = loader.loadModel('phase_13/models/parties/tt_m_gui_pty_pieToss_controls') + self._attackKeys = pieTossControls.find('**/*control*') + self._moveKeys = pieTossControls.find('**/*arrow*') + self._moveKeys.reparentTo(aspect2d) + self._moveKeys.setPos(1.0, 0.0, -0.435) + self._moveKeys.setScale(0.15) + self._attackKeys.reparentTo(aspect2d) + self._attackKeys.setPos(0.85, 0.0, -0.45) + self._attackKeys.setScale(0.15) + self._moveKeys.hide() + self._attackKeys.hide() + + def _initPiePowerMeter(self): + h = PartyGlobals.CogActivityPowerMeterHeight / 2.0 + w = PartyGlobals.CogActivityPowerMeterWidth / 2.0 + self._piePowerMeter = DirectWaitBar(frameSize=(-h, + h, + -w, + w), relief=DGG.GROOVE, frameColor=(0.9, 0.9, 0.9, 1.0), borderWidth=(0.01, 0.01), barColor=PartyGlobals.CogActivityColors[0], pos=PartyGlobals.CogActivityPowerMeterPos, hpr=(0.0, 0.0, -90.0)) + self._piePowerMeter.setBin('fixed', 0) + self._piePowerTitle = OnscreenText(text=TTLocalizer.PartyCogGuiPowerLabel, pos=PartyGlobals.CogActivityPowerMeterTextPos, scale=0.05, fg=(1.0, 1.0, 1.0, 1.0), align=TextNode.ACenter) + self._piePowerTitle.setBin('fixed', 0) + self._piePowerMeter.hide() + self._piePowerTitle.hide() + + def _initScore(self): + self._scoreLabel = OnscreenText(text='0', pos=PartyGlobals.CogActivityScorePos, scale=PartyGlobals.TugOfWarTextWordScale, fg=(1.0, 1.0, 0.0, 1.0), align=TextNode.ARight, font=ToontownGlobals.getSignFont(), mayChange=True) + self._scoreTitle = OnscreenText(text=TTLocalizer.PartyCogGuiScoreLabel, pos=PartyGlobals.CogActivityScoreTitle, scale=0.05, fg=(1.0, 1.0, 1.0, 1.0), align=TextNode.ARight) + self._scoreLabel.hide() + self._scoreTitle.hide() + + def _initCogTracker(self): + self._cogTracker = PartyCogTrackerGui() + + def _initSpamWarning(self): + self._spamWarning = OnscreenText(text=TTLocalizer.PartyCogGuiSpamWarning, scale=0.15, fg=(1.0, 1.0, 0, 1.0), shadow=(0, 0, 0, 0.62), mayChange=False, pos=(0, 0.33)) + self._spamWarning.hide() + + def showScore(self): + self._scoreLabel.show() + self._scoreTitle.show() + + def hideScore(self): + self._scoreLabel.hide() + self._scoreTitle.hide() + + def setScore(self, score = 0): + self._scoreLabel['text'] = str(score) + + def resetPiePowerMeter(self): + self._piePowerMeter['value'] = 0 + + def showPiePowerMeter(self): + self._piePowerMeter.show() + self._piePowerTitle.show() + + def hidePiePowerMeter(self): + self._piePowerMeter.hide() + self._piePowerTitle.hide() + + def updatePiePowerMeter(self, value): + self._piePowerMeter['value'] = value + + def getPiePowerMeterValue(self): + return self._piePowerMeter['value'] + + def hideSpamWarning(self): + taskMgr.remove(self._spamWarningIvalName) + if self._spamWarning: + self._spamWarning.hide() + + def showSpamWarning(self): + if self._spamWarning.isHidden(): + self._spamWarning.show() + taskMgr.remove(self._spamWarningIvalName) + Sequence(ToontownIntervals.getPulseLargerIval(self._spamWarning, ''), Wait(PartyGlobals.CogActivitySpamWarningShowTime), Func(self.hideSpamWarning), name=self._spamWarningIvalName, autoFinish=1).start() + + def hide(self): + self.hidePiePowerMeter() + self.hideScore() + self.hideSpamWarning() + self.hideControls() + + def disableToontownHUD(self): + base.localAvatar.hideName() + base.localAvatar.laffMeter.hide() + base.setCellsAvailable(base.bottomCells + [base.rightCells[1]], False) + + def enableToontownHUD(self): + base.localAvatar.showName() + base.localAvatar.laffMeter.show() + base.setCellsAvailable(base.bottomCells + [base.rightCells[1]], True) + + def setTeam(self, team): + self.team = team + if team == 0: + self._cogTracker.frame.setR(180) + self._piePowerMeter['barColor'] = PartyGlobals.CogActivityColors[team] + + def startTrackingCogs(self, cogs): + self.cogs = cogs + taskMgr.add(self.trackCogs, 'trackCogs') + + def trackCogs(self, task): + if self.cogs is None: + return + self._updateVictoryBar() + for i, cog in enumerate(self.cogs): + self._cogTracker.updateCog(i, cog, self.team) + + return task.cont + + def _updateVictoryBar(self): + if not (hasattr(self, '_victoryBalanceBar') and self._victoryBalanceBar): + return + netDistance = 0 + for cog in self.cogs: + netDistance = netDistance + cog.targetDistance + + teamDistance = netDistance / 6.0 + self._victoryBalanceBarOrange.setScale(PartyGlobals.CogActivityBarStartScale + teamDistance * 10 * PartyGlobals.CogActivityBarUnitScale, 1.0, 1.0) + self._victoryBalanceBarPie.setX(PartyGlobals.CogActivityVictoryBarPiePos[0] + teamDistance * 10 * PartyGlobals.CogActivityBarPieUnitMove) + self._victoryBalanceBarPie.setY(PartyGlobals.CogActivityVictoryBarPiePos[1]) + self._victoryBalanceBarPie.setZ(PartyGlobals.CogActivityVictoryBarPiePos[2]) + if teamDistance > 0.0: + self._victoryBalanceBarArrow.setColor(PartyGlobals.CogActivityColors[1]) + elif teamDistance < 0.0: + self._victoryBalanceBarArrow.setColor(PartyGlobals.CogActivityColors[0]) + else: + self._victoryBalanceBarArrow.setColor(VBase4(1.0, 1.0, 1.0, 1.0)) + + def stopTrackingCogs(self): + taskMgr.remove('trackCogs') + + def showAttackControls(self): + if self._attackKeys.isHidden(): + self._attackKeys.show() + taskMgr.remove(self._attackIvalName) + Sequence(ToontownIntervals.getPulseLargerIval(self._attackKeys, '', scale=0.15), Wait(PartyGlobals.CogActivityControlsShowTime), Func(self.hideAttackControls), name=self._attackIvalName, autoFinish=1).start() + + def showMoveControls(self): + if self._moveKeys.isHidden() and not self._attackKeys.isHidden(): + self._moveKeys.show() + taskMgr.remove(self._moveIvalName) + Sequence(ToontownIntervals.getPulseLargerIval(self._moveKeys, '', scale=0.15), Wait(PartyGlobals.CogActivityControlsShowTime), Func(self.hideMoveControls), name=self._moveIvalName, autoFinish=1).start() + + def hideAttackControls(self): + taskMgr.remove(self._attackIvalName) + if hasattr(self, '_attackKeys') and self._attackKeys: + self._attackKeys.hide() + + def hideMoveControls(self): + taskMgr.remove(self._moveIvalName) + if hasattr(self, '_moveKeys') and self._moveKeys: + self._moveKeys.hide() + + def hideControls(self): + self.hideMoveControls() + self.hideAttackControls() diff --git a/toontown/parties/PartyCogActivityInput.py b/toontown/parties/PartyCogActivityInput.py new file mode 100644 index 0000000..b7a010f --- /dev/null +++ b/toontown/parties/PartyCogActivityInput.py @@ -0,0 +1,158 @@ +from direct.showbase.DirectObject import DirectObject +from pandac.PandaModules import ModifierButtons +ROTATE_LEFT_KEY = 'arrow_left' +ROTATE_RIGHT_KEY = 'arrow_right' +FORWARD_KEY = 'arrow_up' +BACKWARDS_KEY = 'arrow_down' +THROW_PIE_KEYS = ['control', 'delete', 'insert'] + +class PartyCogActivityInput(DirectObject): + notify = directNotify.newCategory('PartyCogActivityInput') + leftPressed = 0 + rightPressed = 0 + upPressed = 0 + downPressed = 0 + throwPiePressed = False + throwPieWasReleased = False + throwPiePressedStartTime = 0 + + def __init__(self, exitActivityCallback): + DirectObject.__init__(self) + self.exitActivityCallback = exitActivityCallback + self._prevModifierButtons = base.mouseWatcherNode.getModifierButtons() + + def enable(self): + self.enableAimKeys() + self.enableThrowPieKeys() + + def disable(self): + self.disableAimKeys() + self.disableThrowPieKeys() + + def enableExitActivityKeys(self): + self.accept('escape', self.exitActivityCallback) + + def disableExitActivityKeys(self): + self.ignore('escape') + + def enableThrowPieKeys(self): + for key in THROW_PIE_KEYS: + self.accept(key, self.handleThrowPieKeyPressed, [key]) + + self.throwPiePressed = False + self.readyToThrowPie = False + + def disableThrowPieKeys(self): + for key in THROW_PIE_KEYS: + self.ignore(key) + self.ignore(key + '-up') + + def handleThrowPieKeyPressed(self, key): + if self.throwPiePressed: + return + self.throwPiePressed = True + self.accept(key + '-up', self.handleThrowPieKeyReleased, [key]) + self.throwPiePressedStartTime = globalClock.getFrameTime() + + def handleThrowPieKeyReleased(self, key): + if not self.throwPiePressed: + return + self.ignore(key + '-up') + self.throwPieWasReleased = True + self.throwPiePressed = False + + def enableAimKeys(self): + self.leftPressed = 0 + self.rightPressed = 0 + base.mouseWatcherNode.setModifierButtons(ModifierButtons()) + base.buttonThrowers[0].node().setModifierButtons(ModifierButtons()) + self.accept(ROTATE_LEFT_KEY, self.__handleLeftKeyPressed) + self.accept(ROTATE_RIGHT_KEY, self.__handleRightKeyPressed) + self.accept(FORWARD_KEY, self.__handleUpKeyPressed) + self.accept(BACKWARDS_KEY, self.__handleDownKeyPressed) + + def disableAimKeys(self): + self.ignore(ROTATE_LEFT_KEY) + self.ignore(ROTATE_RIGHT_KEY) + self.ignore(FORWARD_KEY) + self.ignore(BACKWARDS_KEY) + self.leftPressed = 0 + self.rightPressed = 0 + self.upPressed = 0 + self.downPressed = 0 + self.ignore(ROTATE_LEFT_KEY + '-up') + self.ignore(ROTATE_RIGHT_KEY + '-up') + self.ignore(FORWARD_KEY + '-up') + self.ignore(BACKWARDS_KEY + '-up') + base.mouseWatcherNode.setModifierButtons(self._prevModifierButtons) + base.buttonThrowers[0].node().setModifierButtons(self._prevModifierButtons) + + def __handleLeftKeyPressed(self): + self.ignore(ROTATE_LEFT_KEY) + self.accept(ROTATE_LEFT_KEY + '-up', self.__handleLeftKeyReleased) + self.__leftPressed() + + def __handleRightKeyPressed(self): + self.ignore(ROTATE_RIGHT_KEY) + self.accept(ROTATE_RIGHT_KEY + '-up', self.__handleRightKeyReleased) + self.__rightPressed() + + def __handleLeftKeyReleased(self): + self.ignore(ROTATE_LEFT_KEY + '-up') + self.accept(ROTATE_LEFT_KEY, self.__handleLeftKeyPressed) + self.__leftReleased() + + def __handleRightKeyReleased(self): + self.ignore(ROTATE_RIGHT_KEY + '-up') + self.accept(ROTATE_RIGHT_KEY, self.__handleRightKeyPressed) + self.__rightReleased() + + def __handleUpKeyPressed(self): + self.ignore(FORWARD_KEY) + self.accept(FORWARD_KEY + '-up', self.__handleUpKeyReleased) + self.__upPressed() + + def __handleUpKeyReleased(self): + self.ignore(FORWARD_KEY + '-up') + self.accept(FORWARD_KEY, self.__handleUpKeyPressed) + self.__upReleased() + + def __handleDownKeyPressed(self): + self.ignore(BACKWARDS_KEY) + self.accept(BACKWARDS_KEY + '-up', self.__handleDownKeyReleased) + self.__downPressed() + + def __handleDownKeyReleased(self): + self.ignore(BACKWARDS_KEY + '-up') + self.accept(BACKWARDS_KEY, self.__handleDownKeyPressed) + self.__downReleased() + + def __leftPressed(self): + self.leftPressed = self.__enterControlActive(self.leftPressed) + + def __rightPressed(self): + self.rightPressed = self.__enterControlActive(self.rightPressed) + + def __upPressed(self): + self.upPressed = self.__enterControlActive(self.upPressed) + + def __downPressed(self): + self.downPressed = self.__enterControlActive(self.downPressed) + + def __leftReleased(self): + self.leftPressed = self.__exitControlActive(self.leftPressed) + + def __rightReleased(self): + self.rightPressed = self.__exitControlActive(self.rightPressed) + + def __upReleased(self): + self.upPressed = self.__exitControlActive(self.upPressed) + + def __downReleased(self): + self.downPressed = self.__exitControlActive(self.downPressed) + + def __enterControlActive(self, input): + return input + 1 + + def __exitControlActive(self, input): + return max(0, input - 1) diff --git a/toontown/parties/PartyCogActivityPlayer.py b/toontown/parties/PartyCogActivityPlayer.py new file mode 100644 index 0000000..483b587 --- /dev/null +++ b/toontown/parties/PartyCogActivityPlayer.py @@ -0,0 +1,344 @@ +import math +from direct.showbase.PythonUtil import bound, lerp +from direct.task.Task import Task +from direct.interval.MetaInterval import Sequence, Parallel +from direct.interval.FunctionInterval import Func, Wait +from direct.interval.SoundInterval import SoundInterval +from direct.interval.LerpInterval import LerpScaleInterval, LerpFunc +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import NodePath, Point3, VBase3 +from toontown.minigame.OrthoDrive import OrthoDrive +from toontown.minigame.OrthoWalk import OrthoWalk +from toontown.battle.BattleProps import globalPropPool +from toontown.battle.BattleSounds import globalBattleSoundCache +import PartyGlobals +from PartyCogActivityInput import PartyCogActivityInput +from PartyCogActivityGui import PartyCogActivityGui +from PartyCogUtils import CameraManager +from PartyCogUtils import StrafingControl +UPDATE_TASK_NAME = 'PartyCogActivityLocalPlayer_UpdateTask' +THROW_PIE_LIMIT_TIME = 0.2 + +class PartyCogActivityPlayer: + toon = None + position = None + team = None + score = 0 + enabled = False + notify = DirectNotifyGlobal.directNotify.newCategory('PartyCogActivityPlayer') + + def __init__(self, activity, toon, position, team): + self.activity = activity + self.position = position + self.toon = toon + self.team = team + self.netTimeSentToStartByHit = 0 + self.kaboomTrack = None + self.locator = None + self.teamSpot = self.activity.getIndex(self.toon.doId, self.team) + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splatType = globalPropPool.getPropType(splatName) + self.pieHitSound = globalBattleSoundCache.getSound('AA_wholepie_only.mp3') + return + + def destroy(self): + self.cleanUpIvals() + self.toon = None + self.locator = None + self.position = None + self.pieHitSound = None + self.splat = None + return + + def cleanUpIvals(self): + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + self.kaboomTrack = None + return + + def faceForward(self): + self.toon.setH(0) + + def setToonStartPosition(self, position): + self.position = position + + def updateToonPosition(self): + self.toon.setPos(self.activity.root, self.position) + self.toon.setH(self.locator, 0.0) + + def setTeam(self, team): + self.team = team + self.locator = self.activity.view.teamLocators[self.team] + self.teamSpot = self.activity.getIndex(self.toon.doId, self.team) + + def entersActivity(self): + self.locator = self.activity.view.teamLocators[self.team] + + def exitsActivity(self): + offset = 0 + if self.teamSpot: + offset = 4.0 * self.teamSpot + self.toon.wrtReparentTo(render) + self.toon.setPos(self.locator, offset, -35.0, 0.0) + self.toon.setH(self.activity.root, 0.0) + self.locator = None + return + + def enable(self): + if self.enabled: + return + self.toon.wrtReparentTo(self.locator) + self.enabled = True + + def disable(self): + if not self.enabled: + return + self.toon.wrtReparentTo(render) + self.enabled = False + self.cleanUpIvals() + + def hitBody(self): + points = PartyGlobals.CogActivityHitPoints + self.score += points + return points + + def hitHead(self): + points = PartyGlobals.CogActivityHitPointsForHead + self.score += points + return points + + def resetScore(self): + self.score = 0 + + def respondToPieHit(self, timestamp, pos): + if self.netTimeSentToStartByHit < timestamp: + self.__showSplat(pos) + if self.netTimeSentToStartByHit < timestamp: + self.netTimeSentToStartByHit = timestamp + else: + self.activity.notify.debug('PartyCogPlayer respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) + + def __showSplat(self, position): + if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): + self.kaboomTrack.finish() + if not self.pieHitSound: + self.notify.warning('Trying to play hit sound on destroyed player') + return + splatName = 'splat-creampie' + self.splat = globalPropPool.getProp(splatName) + self.splat.setBillboardPointEye() + self.splat.reparentTo(render) + self.splat.setPos(self.toon, position) + self.splat.setY(self.toon, bound(self.splat.getY(), self.toon.getHeight() / 2.0, position.getY())) + self.splat.setAlphaScale(1.0) + targetscale = 0.75 + + def setSplatAlpha(amount): + self.splat.setAlphaScale(amount) + + self.kaboomTrack = Parallel(SoundInterval(self.pieHitSound, node=self.toon, volume=1.0, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence(Func(self.splat.showThrough), Parallel(Sequence(LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Wait(0.175)), Sequence(Wait(0.1), LerpFunc(setSplatAlpha, duration=1.0, fromData=1.0, toData=0.0, blendType='easeOut'))), Func(self.splat.cleanup), Func(self.splat.removeNode))) + self.kaboomTrack.start() + return + + +class PartyCogActivityLocalPlayer(PartyCogActivityPlayer): + + def __init__(self, activity, position, team, exitActivityCallback = None): + PartyCogActivityPlayer.__init__(self, activity, base.localAvatar, position, team) + self.input = PartyCogActivityInput(exitActivityCallback) + self.gui = PartyCogActivityGui() + self.throwPiePrevTime = 0 + self.lastMoved = 0 + if base.localAvatar: + self.prevPos = base.localAvatar.getPos() + self.cameraManager = None + self.control = None + self.consecutiveShortThrows = 0 + return + + def destroy(self): + if self.enabled: + self.disable() + if self.cameraManager is not None: + self.cameraManager.setEnabled(False) + self.cameraManager.destroy() + del self.cameraManager + del self.gui + del self.input + if self.control is not None: + self.control.destroy() + del self.control + PartyCogActivityPlayer.destroy(self) + return + + def _initOrthoWalk(self): + orthoDrive = OrthoDrive(9.778, customCollisionCallback=self.activity.view.checkOrthoDriveCollision) + self.orthoWalk = OrthoWalk(orthoDrive, broadcast=True) + + def _destroyOrthoWalk(self): + self.orthoWalk.stop() + self.orthoWalk.destroy() + del self.orthoWalk + + def getPieThrowingPower(self, time): + elapsed = max(time - self.input.throwPiePressedStartTime, 0.0) + w = 1.0 / PartyGlobals.CogActivityPowerMeterTime * 2.0 * math.pi + power = int(round(-math.cos(w * elapsed) * 50.0 + 50.0)) + return power + + def isShortThrow(self, time): + elapsed = max(time - self.input.throwPiePressedStartTime, 0.0) + return elapsed <= PartyGlobals.CogActivityShortThrowTime + + def checkForThrowSpam(self, time): + if self.isShortThrow(time): + self.consecutiveShortThrows += 1 + else: + self.consecutiveShortThrows = 0 + return self.consecutiveShortThrows >= PartyGlobals.CogActivityShortThrowSpam + + def _startUpdateTask(self): + task = Task(self._updateTask) + task.lastPositionBroadcastTime = 0.0 + self.throwPiePrevTime = 0 + taskMgr.add(task, UPDATE_TASK_NAME) + + def _stopUpdateTask(self): + taskMgr.remove(UPDATE_TASK_NAME) + + def _updateTask(self, task): + self._update() + if base.localAvatar.getPos() != self.prevPos: + self.prevPos = base.localAvatar.getPos() + self.lastMoved = self.activity.getCurrentActivityTime() + if max(self.activity.getCurrentActivityTime() - self.lastMoved, 0) > PartyGlobals.ToonMoveIdleThreshold: + self.gui.showMoveControls() + if max(self.activity.getCurrentActivityTime() - self.throwPiePrevTime, 0) > PartyGlobals.ToonAttackIdleThreshold: + self.gui.showAttackControls() + if self.input.throwPieWasReleased: + if self.checkForThrowSpam(globalClock.getFrameTime()): + self.gui.showSpamWarning() + self.input.throwPieWasReleased = False + self.throwPie(self.getPieThrowingPower(globalClock.getFrameTime())) + return Task.cont + + def throwPie(self, piePower): + if not self.activity.isState('Active'): + return + if self.activity.getCurrentActivityTime() - self.throwPiePrevTime > THROW_PIE_LIMIT_TIME: + self.throwPiePrevTime = self.activity.getCurrentActivityTime() + self.activity.b_pieThrow(self.toon, piePower) + + def _update(self): + self.control.update() + + def getLookat(self, whosLooking, refNode = None): + if refNode is None: + refNode = render + dist = 5.0 + oldParent = self.tempNP.getParent() + self.tempNP.reparentTo(whosLooking) + self.tempNP.setPos(0.0, dist, 0.0) + pos = self.tempNP.getPos(refNode) + self.tempNP.reparentTo(oldParent) + return pos + + def entersActivity(self): + base.cr.playGame.getPlace().setState('activity') + PartyCogActivityPlayer.entersActivity(self) + self.gui.disableToontownHUD() + self.cameraManager = CameraManager(camera) + self.tempNP = NodePath('temp') + self.lookAtMyTeam() + self.control = StrafingControl(self) + + def exitsActivity(self): + PartyCogActivityPlayer.exitsActivity(self) + self.gui.enableToontownHUD() + self.cameraManager.setEnabled(False) + self.tempNP.removeNode() + self.tempNP = None + if not aspect2d.find('**/JellybeanRewardGui*'): + base.cr.playGame.getPlace().setState('walk') + else: + self.toon.startPosHprBroadcast() + return + + def getRunToStartPositionIval(self): + targetH = self.locator.getH() + travelVec = self.position - self.toon.getPos(self.activity.root) + duration = travelVec.length() / 9.778 + startH = 0.0 + if travelVec.getY() < 0.0: + startH = 180.0 + return Sequence(Func(self.toon.startPosHprBroadcast, 0.1), Func(self.toon.b_setAnimState, 'run'), Parallel(self.toon.hprInterval(0.5, VBase3(startH, 0.0, 0.0), other=self.activity.root), self.toon.posInterval(duration, self.position, other=self.activity.root)), Func(self.toon.b_setAnimState, 'neutral'), self.toon.hprInterval(0.25, VBase3(targetH, 0.0, 0.0), other=self.activity.root), Func(self.toon.stopPosHprBroadcast)) + + def enable(self): + if self.enabled: + return + PartyCogActivityPlayer.enable(self) + self.toon.b_setAnimState('Happy') + self._initOrthoWalk() + self.orthoWalk.start() + self.orthoWalking = True + self.input.enable() + self.gui.disableToontownHUD() + self.gui.load() + self.gui.setScore(0) + self.gui.showScore() + self.gui.setTeam(self.team) + self.gui.startTrackingCogs(self.activity.view.cogManager.cogs) + self.control.enable() + self._startUpdateTask() + + def disable(self): + if not self.enabled: + return + self._stopUpdateTask() + self.toon.b_setAnimState('neutral') + PartyCogActivityPlayer.disable(self) + self.orthoWalking = False + self.orthoWalk.stop() + self._destroyOrthoWalk() + self.input.disable() + self._aimMode = False + self.cameraManager.setEnabled(False) + self.gui.hide() + self.gui.stopTrackingCogs() + self.gui.unload() + + def updateScore(self): + self.gui.setScore(self.score) + + def b_updateToonPosition(self): + self.updateToonPosition() + self.d_updateToonPosition() + + def d_updateToonPosition(self): + self.toon.d_setPos(self.toon.getX(), self.toon.getY(), self.toon.getZ()) + self.toon.d_setH(self.toon.getH()) + + def lookAtArena(self): + self.cameraManager.setEnabled(True) + self.cameraManager.setTargetPos(self.activity.view.arena.find('**/conclusionCamPos_locator').getPos(render)) + self.cameraManager.setTargetLookAtPos(self.activity.view.arena.find('**/conclusionCamAim_locator').getPos(render)) + + def lookAtMyTeam(self): + activityView = self.activity.view + arena = activityView.arena + pos = activityView.teamCamPosLocators[self.team].getPos() + aim = activityView.teamCamAimLocators[self.team].getPos() + camera.wrtReparentTo(arena) + self.cameraManager.setPos(camera.getPos(render)) + self.tempNP.reparentTo(arena) + self.tempNP.setPos(arena, pos) + self.cameraManager.setTargetPos(self.tempNP.getPos(render)) + self.cameraManager.setLookAtPos(self.getLookat(camera)) + self.tempNP.reparentTo(arena) + self.tempNP.setPos(arena, aim) + self.cameraManager.setTargetLookAtPos(self.tempNP.getPos(render)) + self.cameraManager.setEnabled(True) + camera.setP(0.0) + camera.setR(0.0) diff --git a/toontown/parties/PartyCogUtils.py b/toontown/parties/PartyCogUtils.py new file mode 100644 index 0000000..f847601 --- /dev/null +++ b/toontown/parties/PartyCogUtils.py @@ -0,0 +1,132 @@ +import math +from pandac.PandaModules import NodePath, Point3 +import PartyGlobals +inverse_e = 1.0 / math.e + +def getCogDistanceUnitsFromCenter(distance): + return int(round(distance * (PartyGlobals.CogActivityArenaLength / 2.0))) + + +class CameraManager: + nextID = 0 + + def __init__(self, cameraNP): + self.cameraNP = cameraNP + self.id = CameraManager.nextID + CameraManager.nextID += 1 + self.otherNP = render + self.lookAtNP = NodePath('CameraManager%d.lookAtNP' % self.id) + self.lookAtEnabled = False + self.targetPos = Point3(0.0, 0.0, 0.0) + self.targetLookAtPos = Point3(0.0, 1.0, 0.0) + self.enabled = False + self.rate = 10.0 + + def destroy(self): + if self.enabled: + self.setEnabled(False) + self.lookAtNP.removeNode() + del self.lookAtNP + del self.targetPos + del self.targetLookAtPos + del self.otherNP + + def setEnabled(self, enabled): + if enabled != self.enabled: + if enabled: + taskMgr.add(self.updateTask, 'CameraManager%d.update' % self.id) + else: + taskMgr.remove('CameraManager%d.update' % self.id) + self.enabled = enabled + + def setTargetPos(self, p): + self.targetPos = p + + def setPos(self, p): + self.targetPos = p + self.cameraNP.setPos(self.otherNP, p) + + def setTargetLookAtPos(self, p): + self.lookAtEnabled = True + self.targetLookAtPos = p + + def setLookAtPos(self, p): + self.lookAtEnabled = True + self.targetLookAtPos = p + self.lookAtNP.setPos(p) + + def setHpr(self, hpr): + self.lookAtEnabled = False + self.cameraNP.setHpr(self.otherNP, hpr) + + def updateTask(self, task): + newCameraPos = self.rateInterpolate(self.cameraNP.getPos(self.otherNP), self.targetPos) + self.cameraNP.setPos(self.otherNP, newCameraPos) + if self.lookAtEnabled: + newLookAtPos = self.rateInterpolate(self.lookAtNP.getPos(self.otherNP), self.targetLookAtPos) + self.lookAtNP.setPos(self.otherNP, newLookAtPos) + self.cameraNP.lookAt(self.lookAtNP) + return task.cont + + def rateInterpolate(self, currentPos, targetPos): + dt = globalClock.getDt() + vec = currentPos - targetPos + return targetPos + vec * inverse_e ** (dt * self.rate) + + +class StrafingControl: + + def __init__(self, player): + self.player = player + self.defaultOffset = Point3(1.0, -7.5, self.player.toon.getHeight() + 1.0) + + def destroy(self): + self.player = None + del self.player + self.defaultOffset = None + del self.defaultOffset + return + + def update(self): + self.player.tempNP.setPos(self.player.locator, self.player.toon.getPos() + self.defaultOffset) + self.player.cameraManager.setTargetPos(self.player.tempNP.getPos(render)) + self.player.tempNP.setPos(self.player.locator, self.player.toon.getPos() + self.defaultOffset + Point3(0, 20, 0)) + self.player.cameraManager.setTargetLookAtPos(self.player.tempNP.getPos(render)) + if not self.player._aimMode and self.player.input.throwPiePressed: + self.toggleAim() + if self.player._aimMode and not self.player.input.throwPiePressed and (self.player.input.upPressed or self.player.input.downPressed or self.player.input.leftPressed or self.player.input.rightPressed): + self.toggleAim() + if not self.player._aimMode: + if not (self.player.input.upPressed or self.player.input.downPressed or self.player.input.leftPressed or self.player.input.rightPressed): + self.player.faceForward() + return + if self.player.input.throwPiePressed: + self.player.gui.updatePiePowerMeter(self.player.getPieThrowingPower(globalClock.getFrameTime())) + + def toggleAim(self): + self.player._aimMode = not self.player._aimMode + if not self.player._aimMode: + self.player.orthoWalking = True + self.player.orthoWalk.start() + self.player._rotation = 0.0 + self.player._prevRotation = 0.0 + self.player.gui.hidePiePowerMeter() + self.player.toon.setH(0.0) + else: + self.player.orthoWalk.stop() + self.player.orthoWalking = False + self.player.toon.setH(0.0) + self.player.gui.showPiePowerMeter() + + def enable(self): + self.player._aimMode = False + camera.wrtReparentTo(self.player.locator) + self.player.cameraManager.setEnabled(True) + activityView = self.player.activity.view + pos = activityView.teamCamPosLocators[self.player.team].getPos(render) + aim = activityView.teamCamAimLocators[self.player.team].getPos(render) + self.player.cameraManager.setPos(pos) + self.player.cameraManager.setLookAtPos(aim) + self.player.tempNP.reparentTo(self.player.locator) + self.player.tempNP.setPos(self.player.locator, self.player.toon.getPos() + self.defaultOffset) + self.player.cameraManager.setTargetPos(self.player.tempNP.getPos(render)) diff --git a/toontown/parties/PartyDanceActivityToonFSM.py b/toontown/parties/PartyDanceActivityToonFSM.py new file mode 100644 index 0000000..d9e106f --- /dev/null +++ b/toontown/parties/PartyDanceActivityToonFSM.py @@ -0,0 +1,92 @@ +from pandac.PandaModules import * +from direct.fsm.FSM import FSM +from direct.showbase import PythonUtil +from direct.interval.MetaInterval import Sequence +from toontown.parties.PartyGlobals import DanceReverseLoopAnims, ToonDancingStates + +class PartyDanceActivityToonFSM(FSM): + notify = directNotify.newCategory('PartyDanceActivityToonFSM') + + def __init__(self, avId, activity, h): + FSM.__init__(self, self.__class__.__name__) + self.notify.debug('init : avId = %s, activity = %s ' % (avId, activity)) + self.avId = avId + self.activity = activity + self.isLocal = avId == base.localAvatar.doId + self.toon = self.activity.getAvatar(self.avId) + self.toonH = h + self.danceNode = None + self.danceMoveSequence = None + self.lastAnim = None + self.defaultTransitions = {'Init': ['Run', 'DanceMove', 'Cleanup'], + 'DanceMove': ['Run', 'DanceMove', 'Cleanup'], + 'Run': ['Run', 'DanceMove', 'Cleanup'], + 'Cleanup': []} + self.enteredAlready = False + return + + def destroy(self): + self.toon = None + if self.danceNode is not None: + self.danceNode.removeNode() + self.danceNode = None + self.activity = None + self.avId = None + return + + def enterInit(self, *args): + if not self.enteredAlready: + self.danceNode = NodePath('danceNode-%s' % self.avId) + self.danceNode.reparentTo(render) + self.danceNode.setPos(0, 0, 0) + self.danceNode.setH(self.toonH) + pos = self.toon.getPos(self.danceNode) + self.toon.reparentTo(self.danceNode) + self.toon.setPos(pos) + self.enteredAlready = True + + def exitInit(self): + pass + + def enterCleanup(self, *args): + if hasattr(base.cr.playGame.hood, 'loader'): + pos = self.toon.getPos(self.activity.getParentNodePath()) + hpr = self.toon.getHpr(self.activity.getParentNodePath()) + self.toon.reparentTo(self.activity.getParentNodePath()) + self.toon.setPos(pos) + self.toon.setHpr(hpr) + if self.danceNode is not None: + self.danceNode.removeNode() + self.danceNode = None + self.enteredAlready = False + return + + def exitCleanup(self): + pass + + def enterDanceMove(self, anim = ''): + if self.lastAnim is None and anim == '': + self.toon.loop('victory', fromFrame=98, toFrame=122) + else: + if anim == '': + anim = self.lastAnim + if anim in DanceReverseLoopAnims: + self.danceMoveSequence = Sequence(self.toon.actorInterval(anim, loop=0), self.toon.actorInterval(anim, loop=0, playRate=-1.0)) + self.danceMoveSequence.loop() + else: + self.toon.loop(anim) + self.lastAnim = anim + return + + def exitDanceMove(self): + if self.danceMoveSequence and self.danceMoveSequence.isPlaying(): + self.danceMoveSequence.pause() + self.danceMoveSequence = None + return + + def enterRun(self, *args): + if self.toon.getCurrentAnim() != 'run': + self.toon.loop('run') + + def exitRun(self): + pass diff --git a/toontown/parties/PartyEditor.py b/toontown/parties/PartyEditor.py new file mode 100644 index 0000000..911d64e --- /dev/null +++ b/toontown/parties/PartyEditor.py @@ -0,0 +1,230 @@ +import time +from sets import Set +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase.DirectObject import DirectObject +from direct.showbase import PythonUtil +from direct.fsm.FSM import FSM +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils +from toontown.parties.PartyEditorGrid import PartyEditorGrid +from toontown.parties.PartyEditorListElement import PartyEditorListElement + +class PartyEditor(DirectObject, FSM): + notify = directNotify.newCategory('PartyEditor') + + def __init__(self, partyPlanner, parent): + FSM.__init__(self, self.__class__.__name__) + self.partyPlanner = partyPlanner + self.parent = parent + self.partyEditorGrid = PartyEditorGrid(self) + self.currentElement = None + self.defaultTransitions = {'Hidden': ['Idle', 'Cleanup'], + 'Idle': ['DraggingElement', 'Hidden', 'Cleanup'], + 'DraggingElement': ['Idle', + 'DraggingElement', + 'Hidden', + 'Cleanup'], + 'Cleanup': []} + self.initElementList() + self.initPartyClock() + self.initTrashCan() + return + + def initElementList(self): + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + self.decorationModels = loader.loadModel('phase_4/models/parties/partyDecorations') + pos = self.partyPlanner.gui.find('**/step_05_activitiesIcon_locator').getPos() + self.elementList = DirectScrolledList(parent=self.parent, relief=None, decButton_image=(self.partyPlanner.gui.find('**/activitiesButtonUp_up'), + self.partyPlanner.gui.find('**/activitiesButtonUp_down'), + self.partyPlanner.gui.find('**/activitiesButtonUp_rollover'), + self.partyPlanner.gui.find('**/activitiesButtonUp_inactive')), decButton_relief=None, decButton_pos=(-0.05, 0.0, -0.38), incButton_image=(self.partyPlanner.gui.find('**/activitiesButtonDown_up'), + self.partyPlanner.gui.find('**/activitiesButtonDown_down'), + self.partyPlanner.gui.find('**/activitiesButtonDown_rollover'), + self.partyPlanner.gui.find('**/activitiesButtonDown_inactive')), incButton_relief=None, incButton_pos=(-0.05, 0.0, -0.94), itemFrame_pos=(pos[0], pos[1], pos[2] + 0.04), itemFrame_relief=None, numItemsVisible=1, items=[]) + holidayIds = base.cr.newsManager.getHolidayIdList() + isWinter = ToontownGlobals.WINTER_DECORATIONS in holidayIds or ToontownGlobals.WACKY_WINTER_DECORATIONS in holidayIds + isVictory = ToontownGlobals.VICTORY_PARTY_HOLIDAY in holidayIds + isValentine = ToontownGlobals.VALENTINES_DAY in holidayIds + for activityId in PartyGlobals.PartyEditorActivityOrder: + if not isVictory and activityId in PartyGlobals.VictoryPartyActivityIds or not isWinter and activityId in PartyGlobals.WinterPartyActivityIds or not isValentine and activityId in PartyGlobals.ValentinePartyActivityIds: + pass + elif isVictory and activityId in PartyGlobals.VictoryPartyReplacementActivityIds or isWinter and activityId in PartyGlobals.WinterPartyReplacementActivityIds or isValentine and activityId in PartyGlobals.ValentinePartyReplacementActivityIds: + pass + else: + pele = PartyEditorListElement(self, activityId) + self.elementList.addItem(pele) + if activityId == PartyGlobals.ActivityIds.PartyClock: + self.partyClockElement = pele + + for decorationId in PartyGlobals.DecorationIds: + if not isVictory and decorationId in PartyGlobals.VictoryPartyDecorationIds or not isWinter and decorationId in PartyGlobals.WinterPartyDecorationIds or not isValentine and decorationId in PartyGlobals.ValentinePartyDecorationIds: + pass + elif isVictory and decorationId in PartyGlobals.VictoryPartyReplacementDecorationIds or isValentine and decorationId in PartyGlobals.ValentinePartyReplacementDecorationIds: + pass + else: + pele = PartyEditorListElement(self, decorationId, isDecoration=True) + self.elementList.addItem(pele) + + self.elementList.refresh() + self.elementList['command'] = self.scrollItemChanged + return + + def initPartyClock(self): + self.partyClockElement.buyButtonClicked((8, 7)) + + def initTrashCan(self): + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.trashCanButton = DirectButton(parent=self.parent, relief=None, pos=Point3(*PartyGlobals.TrashCanPosition), scale=PartyGlobals.TrashCanScale, geom=(trashcanGui.find('**/TrashCan_CLSD'), + trashcanGui.find('**/TrashCan_OPEN'), + trashcanGui.find('**/TrashCan_RLVR'), + trashcanGui.find('**/TrashCan_RLVR')), command=self.trashCanClicked) + self.trashCanButton.bind(DirectGuiGlobals.ENTER, self.mouseEnterTrash) + self.trashCanButton.bind(DirectGuiGlobals.EXIT, self.mouseExitTrash) + self.mouseOverTrash = False + self.oldInstructionText = '' + self.trashCanLastClickedTime = 0 + return + + def scrollItemChanged(self): + if not self.elementList['items']: + return + self.currentElement = self.elementList['items'][self.elementList.getSelectedIndex()] + self.elementList['items'][self.elementList.getSelectedIndex()].elementSelectedFromList() + if self.elementList['items'][self.elementList.getSelectedIndex()].isDecoration: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsClickedElementDecoration + else: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsClickedElementActivity + + def listElementClicked(self): + self.request('DraggingElement') + + def listElementReleased(self): + self.request('Idle', True) + + def trashCanClicked(self): + currentTime = time.time() + if currentTime - self.trashCanLastClickedTime < 0.2: + self.clearPartyGrounds() + self.trashCanLastClickedTime = time.time() + + def clearPartyGrounds(self): + for item in self.elementList['items']: + item.clearPartyGrounds() + + self.initPartyClock() + if self.currentElement: + self.currentElement.checkSoldOutAndPaidStatusAndAffordability() + + def buyCurrentElement(self): + if self.currentElement: + purchaseSuccessful = self.currentElement.buyButtonClicked() + if purchaseSuccessful: + self.handleMutuallyExclusiveActivities() + else: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsNoRoom + + def mouseEnterTrash(self, mouseEvent): + self.mouseOverTrash = True + self.oldInstructionText = self.partyPlanner.instructionLabel['text'] + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsTrash + + def mouseExitTrash(self, mouseEvent): + self.mouseOverTrash = False + self.partyPlanner.instructionLabel['text'] = self.oldInstructionText + + def enterHidden(self): + PartyEditor.notify.debug('Enter Hidden') + + def exitHidden(self): + PartyEditor.notify.debug('Exit Hidden') + + def enterIdle(self, fromDragging = False): + PartyEditor.notify.debug('Enter Idle') + if not fromDragging: + self.elementList.scrollTo(0) + self.elementList['items'][0].elementSelectedFromList() + self.currentElement = self.elementList['items'][self.elementList.getSelectedIndex()] + self.currentElement.checkSoldOutAndPaidStatusAndAffordability() + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsIdle + self.updateCostsAndBank() + self.handleMutuallyExclusiveActivities() + + def handleMutuallyExclusiveActivities(self): + mutSet = self.getMutuallyExclusiveActivities() + if not mutSet: + return + currentActivities = self.partyEditorGrid.getActivitiesElementsOnGrid() + lastActivity = self.partyEditorGrid.lastActivityIdPlaced + for act in currentActivities: + if act.id in mutSet and not lastActivity == act.id: + act.removeFromGrid() + removedName = TTLocalizer.PartyActivityNameDict[act.id]['editor'] + addedName = TTLocalizer.PartyActivityNameDict[lastActivity]['editor'] + instr = TTLocalizer.PartyPlannerEditorInstructionsRemoved % {'removed': removedName, + 'added': addedName} + self.partyPlanner.instructionLabel['text'] = instr + self.updateCostsAndBank() + + def getMutuallyExclusiveActivities(self): + currentActivities = self.partyEditorGrid.getActivitiesOnGrid() + actSet = Set([]) + for act in currentActivities: + actSet.add(act[0]) + + result = None + for mutuallyExclusiveTuples in PartyGlobals.MutuallyExclusiveActivities: + mutSet = Set(mutuallyExclusiveTuples) + inter = mutSet.intersection(actSet) + if len(inter) > 1: + result = inter + break + + return result + + def updateCostsAndBank(self): + currentActivities = self.partyEditorGrid.getActivitiesOnGrid() + currentDecorations = self.partyEditorGrid.getDecorationsOnGrid() + newCost = 0 + for elementTuple in currentActivities: + newCost += PartyGlobals.ActivityInformationDict[elementTuple[0]]['cost'] + + for elementTuple in currentDecorations: + newCost += PartyGlobals.DecorationInformationDict[elementTuple[0]]['cost'] + + self.partyPlanner.costLabel['text'] = TTLocalizer.PartyPlannerTotalCost % newCost + if len(currentActivities) > 0 or len(currentDecorations) > 0: + self.partyPlanner.setNextButtonState(enabled=True) + else: + self.partyPlanner.setNextButtonState(enabled=False) + self.partyPlanner.totalCost = newCost + self.partyPlanner.beanBank['text'] = str(int(self.partyPlanner.totalMoney - self.partyPlanner.totalCost)) + + def exitIdle(self): + PartyEditor.notify.debug('Exit Idle') + + def enterDraggingElement(self): + PartyEditor.notify.debug('Enter DraggingElement') + if self.currentElement.isDecoration: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsDraggingDecoration + else: + self.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsDraggingActivity + + def exitDraggingElement(self): + PartyEditor.notify.debug('Exit DraggingElement') + + def enterCleanup(self): + PartyEditor.notify.debug('Enter Cleanup') + self.partyEditorGrid.destroy() + self.elementList.removeAndDestroyAllItems() + self.elementList.destroy() + self.trashCanButton.unbind(DirectGuiGlobals.ENTER) + self.trashCanButton.unbind(DirectGuiGlobals.EXIT) + self.trashCanButton.destroy() + + def exitCleanup(self): + PartyEditor.notify.debug('Exit Cleanup') diff --git a/toontown/parties/PartyEditorGrid.py b/toontown/parties/PartyEditorGrid.py new file mode 100644 index 0000000..f11463d --- /dev/null +++ b/toontown/parties/PartyEditorGrid.py @@ -0,0 +1,423 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils +from toontown.parties.PartyEditorGridSquare import PartyEditorGridSquare + +class PartyEditorGrid: + notify = directNotify.newCategory('PartyEditorGrid') + + def __init__(self, partyEditor): + self.partyEditor = partyEditor + self.initGrid() + self.lastActivityIdPlaced = None + return + + def initGrid(self): + self.grid = [[None, + None, + None, + None, + None, + None, + True, + True, + True, + True, + True, + None, + None, + None, + None, + None, + None, + None], + [None, + None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None, + None, + None, + None], + [None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None, + None, + None], + [None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None, + None], + [None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True], + [None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None], + [None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None], + [None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None], + [None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None], + [None, + None, + None, + None, + None, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + None, + None, + None]] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x]: + self.grid[y][x] = PartyEditorGridSquare(self.partyEditor, x, y) + + return None + + def getActivitiesOnGrid(self): + activities = [] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x] and self.grid[y][x].gridElement: + if not self.grid[y][x].gridElement.isDecoration: + activityTuple = self.grid[y][x].gridElement.getActivityTuple(x, y) + if activityTuple not in activities: + activities.append(activityTuple) + + return activities + + def getActivitiesElementsOnGrid(self): + activities = [] + activityElems = [] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x] and self.grid[y][x].gridElement: + if not self.grid[y][x].gridElement.isDecoration: + activityTuple = self.grid[y][x].gridElement.getActivityTuple(x, y) + if activityTuple not in activities: + activities.append(activityTuple) + activityElems.append(self.grid[y][x].gridElement) + + return activityElems + + def getDecorationsOnGrid(self): + decorations = [] + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x] and self.grid[y][x].gridElement: + if self.grid[y][x].gridElement.isDecoration: + decorationTuple = self.grid[y][x].gridElement.getDecorationTuple(x, y) + if decorationTuple not in decorations: + decorations.append(decorationTuple) + + return decorations + + def getGridSquare(self, x, y): + if y < 0 or y >= PartyGlobals.PartyEditorGridSize[1]: + return None + if x < 0 or x >= PartyGlobals.PartyEditorGridSize[0]: + return None + return self.grid[y][x] + + def checkGridSquareForAvailability(self, gridSquare, size): + xOffsetLow, xOffsetHigh, yOffset = self.getXYOffsets(size) + for y in range(int(gridSquare.y - size[1] / 2), int(gridSquare.y + size[1] / 2) + yOffset): + for x in range(int(gridSquare.x - size[0] / 2) + xOffsetLow, int(gridSquare.x + size[0] / 2) + xOffsetHigh): + testGridSquare = self.getGridSquare(x, y) + if testGridSquare is None: + return False + if testGridSquare.gridElement is not None: + return False + + return True + + def getClearGridSquare(self, size, desiredXY = None): + if desiredXY is not None: + x = desiredXY[0] + y = desiredXY[1] + if self.grid[y][x] is not None: + if self.checkGridSquareForAvailability(self.grid[y][x], size): + return self.grid[y][x] + for y in range(PartyGlobals.PartyEditorGridSize[1]): + for x in range(PartyGlobals.PartyEditorGridSize[0]): + if self.grid[y][x] is not None: + if self.checkGridSquareForAvailability(self.grid[y][x], size): + return self.grid[y][x] + + return + + def getXYOffsets(self, size): + if size[0] % 2 == 0: + xOffsetLow = 1 + xOffsetHigh = 1 + else: + xOffsetLow = 0 + xOffsetHigh = 1 + if size[1] % 2 == 0: + yOffset = 0 + else: + yOffset = 1 + return (xOffsetLow, xOffsetHigh, yOffset) + + def registerNewElement(self, gridElement, centerGridSquare, size): + xOffsetLow, xOffsetHigh, yOffset = self.getXYOffsets(size) + for y in range(int(centerGridSquare.y - size[1] / 2), int(centerGridSquare.y + size[1] / 2) + yOffset): + for x in range(int(centerGridSquare.x - size[0] / 2) + xOffsetLow, int(centerGridSquare.x + size[0] / 2) + xOffsetHigh): + testGridSquare = self.getGridSquare(x, y) + if testGridSquare is None: + return False + if testGridSquare.gridElement is not None: + return False + else: + testGridSquare.gridElement = gridElement + if not gridElement.isDecoration: + self.lastActivityIdPlaced = gridElement.id + + return + + def removeElement(self, centerGridSquare, size): + xOffsetLow, xOffsetHigh, yOffset = self.getXYOffsets(size) + for y in range(int(centerGridSquare.y - size[1] / 2), int(centerGridSquare.y + size[1] / 2) + yOffset): + for x in range(int(centerGridSquare.x - size[0] / 2) + xOffsetLow, int(centerGridSquare.x + size[0] / 2) + xOffsetHigh): + testGridSquare = self.getGridSquare(x, y) + if testGridSquare is None: + return False + if testGridSquare.gridElement is None: + return False + else: + testGridSquare.gridElement = None + + return + + def destroy(self): + self.partyEditor = None + for y in range(len(self.grid)): + for x in range(len(self.grid[0])): + if self.grid[y][x]: + self.grid[y][x].destroy() + + del self.grid + return diff --git a/toontown/parties/PartyEditorGridElement.py b/toontown/parties/PartyEditorGridElement.py new file mode 100644 index 0000000..1f2c9ea --- /dev/null +++ b/toontown/parties/PartyEditorGridElement.py @@ -0,0 +1,263 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4, NodePath +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils + +class PartyEditorGridElement(DirectButton): + notify = directNotify.newCategory('PartyEditorGridElement') + + def __init__(self, partyEditor, id, isDecoration, checkSoldOutAndPaidStatusAndAffordability, **kw): + self.partyEditor = partyEditor + self.id = id + self.isDecoration = isDecoration + self.checkSoldOutAndPaidStatusAndAffordability = checkSoldOutAndPaidStatusAndAffordability + if self.isDecoration: + self.name = TTLocalizer.PartyDecorationNameDict[self.id]['editor'] + colorList = ((1.0, 1.0, 1.0, 1.0), + (0.0, 0.0, 1.0, 1.0), + (0.0, 1.0, 1.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + self.geom = self.partyEditor.partyPlanner.gui.find('**/%s' % PartyGlobals.DecorationInformationDict[self.id]['gridAsset']) + else: + self.name = TTLocalizer.PartyActivityNameDict[self.id]['editor'] + colorList = ((1.0, 1.0, 1.0, 1.0), + (0.0, 1.0, 0.0, 1.0), + (1.0, 1.0, 0.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + self.geom = self.partyEditor.partyPlanner.gui.find('**/%s' % PartyGlobals.ActivityInformationDict[self.id]['gridAsset']) + optiondefs = (('geom', self.geom, None), + ('geom_scale', 1.0, None), + ('geom_color', colorList[0], None), + ('geom1_color', colorList[0], None), + ('geom2_color', colorList[0], None), + ('geom3_color', colorList[0], None), + ('relief', None, None)) + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, self.partyEditor.parent) + self.initialiseoptions(PartyEditorGridElement) + self.setName('%sGridElement' % self.name) + self.bind(DirectGuiGlobals.B1PRESS, self.clicked) + self.bind(DirectGuiGlobals.B1RELEASE, self.released) + self.bind(DirectGuiGlobals.ENTER, self.mouseEnter) + self.bind(DirectGuiGlobals.EXIT, self.mouseExit) + self.uprightNodePath = NodePath('%sUpright' % self.name) + self.uprightNodePath.reparentTo(self) + rollOverZOffset = self.getGridSize()[1] / 30.0 + self.rolloverTitle = DirectLabel(relief=None, parent=self.uprightNodePath, pos=Point3(0.0, 0.0, rollOverZOffset), text=self.name, text_fg=(1.0, 1.0, 1.0, 1.0), text_shadow=(0.0, 0.0, 0.0, 1.0), text_scale=0.075) + self.rolloverTitle.stash() + self.stash() + self.overValidSquare = False + self.lastValidPosition = None + self.setColorScale(0.9, 0.9, 0.9, 0.7) + self.setTransparency(True) + self.mouseOverTrash = False + self.centerGridSquare = None + return + + def getCorrectRotation(self): + r = self.getR() + if r == 90.0: + r = 270.0 + elif r == 270.0: + r = 90.0 + if self.id == PartyGlobals.ActivityIds.PartyCannon: + return PartyUtils.convertDegreesToPartyGrid(r + 180.0) + return PartyUtils.convertDegreesToPartyGrid(r) + + def getDecorationTuple(self, x, y): + return (self.id, + self.centerGridSquare.x, + PartyGlobals.PartyEditorGridSize[1] - 1 - self.centerGridSquare.y, + self.getCorrectRotation()) + + def getActivityTuple(self, x, y): + return (self.id, + self.centerGridSquare.x, + PartyGlobals.PartyEditorGridSize[1] - 1 - self.centerGridSquare.y, + self.getCorrectRotation()) + + def attach(self, mouseEvent): + PartyEditorGridElement.notify.debug('attached grid element %s' % self.name) + taskMgr.remove('gridElementDragTask%s' % self.name) + vWidget2render2d = self.getPos(render2d) + vMouse2render2d = Point3(mouseEvent.getMouse()[0], 0, mouseEvent.getMouse()[1]) + taskMgr.add(self.elementDragTask, 'gridElementDragTask%s' % self.name) + self.unstash() + self.rolloverTitle.unstash() + self.uprightNodePath.reparentTo(self) + self.setPosHprToDefault() + + def elementDragTask(self, state): + mwn = base.mouseWatcherNode + if mwn.hasMouse(): + vMouse2render2d = Point3(mwn.getMouse()[0], 0, mwn.getMouse()[1]) + newPos = vMouse2render2d + if newPos[0] > PartyGlobals.PartyEditorGridBounds[0][0] and newPos[0] < PartyGlobals.PartyEditorGridBounds[1][0] and newPos[2] < PartyGlobals.PartyEditorGridBounds[0][1] and newPos[2] > PartyGlobals.PartyEditorGridBounds[1][1]: + centerGridSquare = self.snapToGrid(newPos) + if centerGridSquare is not None: + self.centerGridSquare = centerGridSquare + if not self.overValidSquare: + self.setOverValidSquare(True) + if self.mouseOverTrash: + self.setOverTrash(False) + return Task.cont + if self.id != PartyGlobals.ActivityIds.PartyClock and newPos[0] > PartyGlobals.PartyEditorTrashBounds[0][0] and newPos[0] < PartyGlobals.PartyEditorTrashBounds[1][0] and newPos[2] < PartyGlobals.PartyEditorTrashBounds[0][1] and newPos[2] > PartyGlobals.PartyEditorTrashBounds[1][1]: + if not self.mouseOverTrash: + self.setOverTrash(True) + elif self.mouseOverTrash: + self.setOverTrash(False) + self.setPos(render2d, newPos) + if self.overValidSquare: + self.setOverValidSquare(False) + return Task.cont + + def setOverTrash(self, value): + self.mouseOverTrash = value + if value: + self.partyEditor.trashCanButton['state'] = DirectGuiGlobals.DISABLED + self.setColorScale(1.0, 0.0, 0.0, 1.0) + else: + self.partyEditor.trashCanButton['state'] = DirectGuiGlobals.NORMAL + self.setColorScale(0.9, 0.9, 0.9, 0.7) + + def setOverValidSquare(self, value): + self.overValidSquare = value + if value: + self.setColorScale(1.0, 1.0, 1.0, 1.0) + else: + self.setColorScale(0.9, 0.9, 0.9, 0.7) + + def removeFromGrid(self): + if self.centerGridSquare is not None: + self.partyEditor.partyEditorGrid.removeElement(self.centerGridSquare, self.getGridSize()) + self.setOverValidSquare(False) + self.lastValidPosition = None + self.stash() + return + + def snapToGrid(self, newPos): + gridSquare = self.getGridSquareFromPosition(newPos) + if gridSquare == None: + self.setPosHprToDefault() + self.setPos(render2d, newPos) + return + elif not self.partyEditor.partyEditorGrid.checkGridSquareForAvailability(gridSquare, self.getGridSize()): + self.setPos(render2d, newPos) + return + self.setPosHprBasedOnGridSquare(gridSquare) + return gridSquare + + def getGridSize(self): + if self.isDecoration: + return PartyGlobals.DecorationInformationDict[self.id]['gridsize'] + else: + return PartyGlobals.ActivityInformationDict[self.id]['gridsize'] + + def setPosHprToDefault(self): + self.setR(0.0) + self.uprightNodePath.setR(0.0) + + def setPosHprBasedOnGridSquare(self, gridSquare): + gridPos = gridSquare.getPos() + if self.getGridSize()[0] % 2 == 0: + gridPos.setX(gridPos[0] + PartyGlobals.PartyEditorGridSquareSize[0] / 2.0) + if self.getGridSize()[1] % 2 == 0: + gridPos.setZ(gridPos[2] + PartyGlobals.PartyEditorGridSquareSize[1] / 2.0) + if self.id != PartyGlobals.ActivityIds.PartyFireworks: + if gridPos[0] > PartyGlobals.PartyEditorGridCenter[0] + PartyGlobals.PartyEditorGridRotateThreshold: + self.setR(90.0) + self.uprightNodePath.setR(-90.0) + elif gridPos[0] < PartyGlobals.PartyEditorGridCenter[0] - PartyGlobals.PartyEditorGridRotateThreshold: + self.setR(270.0) + self.uprightNodePath.setR(-270.0) + elif gridPos[2] < PartyGlobals.PartyEditorGridCenter[1] - PartyGlobals.PartyEditorGridRotateThreshold: + self.setR(180.0) + self.uprightNodePath.setR(-180.0) + else: + self.setR(0.0) + self.uprightNodePath.setR(0.0) + else: + self.setR(270.0) + self.uprightNodePath.setR(-270.0) + self.setPos(render2d, gridPos) + self.lastValidPosition = gridPos + + def getGridSquareFromPosition(self, newPos): + localX = newPos[0] - PartyGlobals.PartyEditorGridBounds[0][0] + localY = newPos[2] - PartyGlobals.PartyEditorGridBounds[1][1] + x = int(localX / PartyGlobals.PartyEditorGridSquareSize[0]) + y = int(localY / PartyGlobals.PartyEditorGridSquareSize[1]) + y = PartyGlobals.PartyEditorGridSize[1] - 1 - y + return self.partyEditor.partyEditorGrid.getGridSquare(x, y) + + def detach(self, mouseEvent): + taskMgr.remove('gridElementDragTask%s' % self.name) + self.rolloverTitle.stash() + if self.overValidSquare: + self.partyEditor.partyEditorGrid.registerNewElement(self, self.centerGridSquare, self.getGridSize()) + self.partyEditor.updateCostsAndBank() + self.partyEditor.handleMutuallyExclusiveActivities() + elif self.lastValidPosition is not None: + if self.mouseOverTrash: + self.partyEditor.trashCanButton['state'] = DirectGuiGlobals.NORMAL + self.lastValidPosition = None + self.partyEditor.updateCostsAndBank() + self.stash() + else: + self.setPos(render2d, self.lastValidPosition) + self.setOverValidSquare(True) + self.partyEditor.partyEditorGrid.registerNewElement(self, self.centerGridSquare, self.getGridSize()) + self.partyEditor.updateCostsAndBank() + self.partyEditor.handleMutuallyExclusiveActivities() + else: + self.stash() + self.checkSoldOutAndPaidStatusAndAffordability() + return + + def placeInPartyGrounds(self, desiredXY = None): + self.centerGridSquare = self.partyEditor.partyEditorGrid.getClearGridSquare(self.getGridSize(), desiredXY) + if self.centerGridSquare is not None: + self.setOverValidSquare(True) + self.unstash() + self.setPosHprBasedOnGridSquare(self.centerGridSquare) + self.partyEditor.partyEditorGrid.registerNewElement(self, self.centerGridSquare, self.getGridSize()) + self.partyEditor.updateCostsAndBank() + self.partyEditor.partyPlanner.instructionLabel['text'] = TTLocalizer.PartyPlannerEditorInstructionsPartyGrounds + self.checkSoldOutAndPaidStatusAndAffordability() + return True + else: + return False + return + + def clicked(self, mouseEvent): + PartyEditorGridElement.notify.debug('clicked grid element %s' % self.name) + if self.centerGridSquare is not None: + self.attach(mouseEvent) + self.partyEditor.partyEditorGrid.removeElement(self.centerGridSquare, self.getGridSize()) + return + + def released(self, mouseEvent): + PartyEditorGridElement.notify.debug('released grid element %s' % self.name) + self.detach(mouseEvent) + + def mouseEnter(self, mouseEvent): + parent = self.getParent() + self.reparentTo(parent) + self.rolloverTitle.unstash() + + def mouseExit(self, mouseEvent): + self.rolloverTitle.stash() + + def destroy(self): + self.unbind(DirectGuiGlobals.B1PRESS) + self.unbind(DirectGuiGlobals.B1RELEASE) + self.unbind(DirectGuiGlobals.ENTER) + self.unbind(DirectGuiGlobals.EXIT) + DirectButton.destroy(self) diff --git a/toontown/parties/PartyEditorGridSquare.py b/toontown/parties/PartyEditorGridSquare.py new file mode 100644 index 0000000..66c074d --- /dev/null +++ b/toontown/parties/PartyEditorGridSquare.py @@ -0,0 +1,26 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase.DirectObject import DirectObject +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils + +class PartyEditorGridSquare(DirectObject): + notify = directNotify.newCategory('PartyEditorGridSquare') + + def __init__(self, partyEditor, x, y): + self.partyEditor = partyEditor + self.x = x + self.y = y + self.gridElement = None + return + + def getPos(self): + return Point3(PartyGlobals.PartyEditorGridBounds[0][0] + self.x * PartyGlobals.PartyEditorGridSquareSize[0] + PartyGlobals.PartyEditorGridSquareSize[0] / 2.0, 0.0, PartyGlobals.PartyEditorGridBounds[1][1] + (PartyGlobals.PartyEditorGridSize[1] - 1 - self.y) * PartyGlobals.PartyEditorGridSquareSize[1] + PartyGlobals.PartyEditorGridSquareSize[1] / 2.0) + + def destroy(self): + del self.gridElement diff --git a/toontown/parties/PartyEditorListElement.py b/toontown/parties/PartyEditorListElement.py new file mode 100644 index 0000000..df99cbb --- /dev/null +++ b/toontown/parties/PartyEditorListElement.py @@ -0,0 +1,208 @@ +from pandac.PandaModules import Vec3 +from direct.gui.DirectGui import DirectButton, DirectLabel +from direct.gui import DirectGuiGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyGlobals +from toontown.parties.PartyEditorGridElement import PartyEditorGridElement +from toontown.parties.PartyUtils import getPartyActivityIcon + +class PartyEditorListElement(DirectButton): + notify = directNotify.newCategory('PartyEditorListElement') + + def __init__(self, partyEditor, id, isDecoration = False, **kw): + self.partyEditor = partyEditor + self.id = id + self.isDecoration = isDecoration + self.unreleased = self.calcUnreleased(id) + self.comingSoonTextScale = 1.0 + if self.isDecoration: + self.name = TTLocalizer.PartyDecorationNameDict[self.id]['editor'] + colorList = ((1.0, 0.0, 1.0, 1.0), + (0.0, 0.0, 1.0, 1.0), + (0.0, 1.0, 1.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + assetName = PartyGlobals.DecorationIds.getString(self.id) + if assetName == 'Hydra': + assetName = 'StageSummer' + geom = self.partyEditor.decorationModels.find('**/partyDecoration_%s' % assetName) + if geom.isEmpty() or self.unreleased: + helpGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_help') + helpImageList = (helpGui.find('**/tt_t_gui_brd_helpUp'), + helpGui.find('**/tt_t_gui_brd_helpDown'), + helpGui.find('**/tt_t_gui_brd_helpHover'), + helpGui.find('**/tt_t_gui_brd_helpDown')) + geom = helpImageList[2] + geom3_color = (0.5, 0.5, 0.5, 1.0) + scale = Vec3(2.5, 2.5, 2.5) + geom_pos = (0.0, 0.0, 0.0) + self.comingSoonTextScale = 0.035 + else: + geom_pos = (0.0, 0.0, -3.0) + geom3_color = (0.5, 0.5, 0.5, 1.0) + scale = Vec3(0.06, 0.0001, 0.06) + if self.id in [PartyGlobals.DecorationIds.CogStatueVictory, PartyGlobals.DecorationIds.TubeCogVictory, PartyGlobals.DecorationIds.CogIceCreamVictory]: + geom_pos = (0.0, 0.0, -3.9) + scale = Vec3(0.05, 0.0001, 0.05) + else: + self.name = TTLocalizer.PartyActivityNameDict[self.id]['editor'] + colorList = ((0.0, 0.0, 0.0, 1.0), + (0.0, 1.0, 0.0, 1.0), + (1.0, 1.0, 0.0, 1.0), + (0.5, 0.5, 0.5, 1.0)) + iconString = PartyGlobals.ActivityIds.getString(self.id) + if self.id == PartyGlobals.ActivityIds.PartyJukebox40: + iconString = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyJukebox) + elif self.id == PartyGlobals.ActivityIds.PartyDance20: + iconString = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyDance) + geom = getPartyActivityIcon(self.partyEditor.activityIconsModel, iconString) + scale = 0.35 + geom3_color = (0.5, 0.5, 0.5, 1.0) + geom_pos = (0.0, 0.0, 0.0) + self.comingSoonTextScale = 0.25 + optiondefs = (('geom', geom, None), + ('geom3_color', geom3_color, None), + ('geom_pos', geom_pos, None), + ('relief', None, None)) + self.defineoptions(kw, optiondefs) + DirectButton.__init__(self, self.partyEditor.elementList) + self.initialiseoptions(PartyEditorListElement) + self.setName('%sListElement' % self.name) + self.setScale(scale) + self.bind(DirectGuiGlobals.B1PRESS, self.clicked) + self.bind(DirectGuiGlobals.B1RELEASE, self.released) + self.partyEditorGridElements = [] + if self.isDecoration: + for i in range(PartyGlobals.DecorationInformationDict[self.id]['limitPerParty']): + self.partyEditorGridElements.append(PartyEditorGridElement(self.partyEditor, self.id, self.isDecoration, self.checkSoldOutAndPaidStatusAndAffordability)) + + else: + for i in range(PartyGlobals.ActivityInformationDict[self.id]['limitPerParty']): + self.partyEditorGridElements.append(PartyEditorGridElement(self.partyEditor, self.id, self.isDecoration, self.checkSoldOutAndPaidStatusAndAffordability)) + + self.activeGridElementIndex = -1 + self.adjustForUnreleased() + return + + def calcUnreleased(self, id): + if base.cr.partyManager.allowUnreleasedClient(): + self.unreleased = False + elif self.isDecoration: + self.unreleased = id in PartyGlobals.UnreleasedDecorationIds + else: + self.unreleased = id in PartyGlobals.UnreleasedActivityIds + return self.unreleased + + def adjustForUnreleased(self): + if self.unreleased: + textScale = self.comingSoonTextScale + comingSoon = DirectLabel(parent=self, text=TTLocalizer.PartyPlannerComingSoon, text_scale=textScale, text_fg=(1.0, 0, 0, 1.0), text_shadow=(0, 0, 0, 1), relief=None) + self['state'] = DirectGuiGlobals.DISABLED + return + + def clearPartyGrounds(self): + for gridElement in self.partyEditorGridElements: + gridElement.removeFromGrid() + + def elementSelectedFromList(self): + PartyEditorListElement.notify.debug('Element %s clicked' % self.name) + if self.isDecoration: + self.partyEditor.partyPlanner.elementDescriptionNode.setText(TTLocalizer.PartyDecorationNameDict[self.id]['description']) + self.partyEditor.partyPlanner.elementPriceNode.setText('%d %s' % (PartyGlobals.DecorationInformationDict[self.id]['cost'], TTLocalizer.PartyPlannerBeans)) + self.partyEditor.partyPlanner.elementTitleLabel['text'] = self.name + else: + self.partyEditor.partyPlanner.elementDescriptionNode.setText(TTLocalizer.PartyActivityNameDict[self.id]['description']) + self.partyEditor.partyPlanner.elementPriceNode.setText('%d %s' % (PartyGlobals.ActivityInformationDict[self.id]['cost'], TTLocalizer.PartyPlannerBeans)) + self.partyEditor.partyPlanner.elementTitleLabel['text'] = self.name + self.checkSoldOutAndPaidStatusAndAffordability() + + def checkSoldOutAndPaidStatusAndAffordability(self): + if self.partyEditor.currentElement != self: + if self.partyEditor.currentElement is not None: + self.partyEditor.currentElement.checkSoldOutAndPaidStatusAndAffordability() + return + if self.isDecoration: + infoDict = PartyGlobals.DecorationInformationDict + else: + infoDict = PartyGlobals.ActivityInformationDict + if not base.cr.isPaid() and infoDict[self.id]['paidOnly']: + self.setOffLimits() + return + if infoDict[self.id]['cost'] > self.partyEditor.partyPlanner.totalMoney - self.partyEditor.partyPlanner.totalCost: + self.setTooExpensive(True) + tooExpensive = True + else: + self.setTooExpensive(False) + tooExpensive = False + for i in range(len(self.partyEditorGridElements)): + if not self.partyEditorGridElements[i].overValidSquare: + if not tooExpensive: + self.setSoldOut(False) + return + + self.setSoldOut(True) + return + + def setOffLimits(self): + self['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerPaidOnly + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['text_scale'] = 0.04 + + def setTooExpensive(self, value): + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerBuy + if value: + self['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.DISABLED + else: + self['state'] = DirectGuiGlobals.NORMAL + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.NORMAL + + def setSoldOut(self, value): + if value: + self['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerSoldOut + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.DISABLED + else: + self['state'] = DirectGuiGlobals.NORMAL + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerBuy + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.NORMAL + if self.unreleased: + self['state'] = DirectGuiGlobals.DISABLED + self.partyEditor.partyPlanner.elementBuyButton['text'] = TTLocalizer.PartyPlannerCantBuy + self.partyEditor.partyPlanner.elementBuyButton['state'] = DirectGuiGlobals.DISABLED + + def clicked(self, mouseEvent): + PartyEditorListElement.notify.debug("Element %s's icon was clicked" % self.name) + self.partyEditor.listElementClicked() + for i in range(len(self.partyEditorGridElements)): + if not self.partyEditorGridElements[i].overValidSquare: + self.partyEditorGridElements[i].attach(mouseEvent) + self.activeGridElementIndex = i + return + + def buyButtonClicked(self, desiredXY = None): + for i in range(len(self.partyEditorGridElements)): + if not self.partyEditorGridElements[i].overValidSquare: + if self.partyEditorGridElements[i].placeInPartyGrounds(desiredXY): + self.activeGridElementIndex = i + return True + else: + self.checkSoldOutAndPaidStatusAndAffordability() + return False + + def released(self, mouseEvent): + PartyEditorListElement.notify.debug("Element %s's icon was released" % self.name) + self.partyEditor.listElementReleased() + if self.activeGridElementIndex != -1: + self.partyEditorGridElements[self.activeGridElementIndex].detach(mouseEvent) + + def destroy(self): + self.unbind(DirectGuiGlobals.B1PRESS) + self.unbind(DirectGuiGlobals.B1RELEASE) + for partyEditorGridElement in self.partyEditorGridElements: + partyEditorGridElement.destroy() + + del self.partyEditorGridElements + self.partyEditor = None + DirectButton.destroy(self) + return diff --git a/toontown/parties/PartyGlobals.py b/toontown/parties/PartyGlobals.py new file mode 100644 index 0000000..57b1b78 --- /dev/null +++ b/toontown/parties/PartyGlobals.py @@ -0,0 +1,882 @@ +from pandac.PandaModules import BitMask32 +from pandac.PandaModules import Point3, VBase4 +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer +KICK_TO_PLAYGROUND_EVENT = 'parties_kickToPlayground' +MaxSetInvites = 1000 +MaxSetPartiesInvitedTo = 100 +MaxSetHostedParties = 50 +MaxPlannedYear = 2030 +MinPlannedYear = 1975 +JellybeanMultiplier = 1.5 +JellyBeanDayMultiplier = 2 +PARTY_DURATION = 1800.0 +EventsPageGuestNameMaxWidth = 0.42 +EventsPageGuestNameMaxLetters = 18 +EventsPageHostNameMaxWidth = 0.37 +PartyRefundPercentage = 0.95 +PartyPlannerAsapMinuteRounding = 5 +UberdogCheckPartyStartFrequency = 5.0 +UberdogPurgePartyPeriod = 24.0 +UberdogPartiesSanityCheckFrequency = 60 +JarLabelTextColor = (0.95, + 0.95, + 0.0, + 1.0) +JarLabelMaxedTextColor = (1.0, + 0.0, + 0.0, + 1.0) +TuftsOfGrass = 75 +MaxToonsAtAParty = 20 +DefaultPartyDuration = 0.5 +DelayBeforeAutoKick = 30.0 +MaxHostedPartiesPerToon = 1 +PartyEditorGridBounds = ((-0.11, 0.289), (0.55, -0.447)) +PartyEditorGridCenter = (PartyEditorGridBounds[0][0] + (PartyEditorGridBounds[1][0] - PartyEditorGridBounds[0][0]) / 2.0, PartyEditorGridBounds[1][1] + (PartyEditorGridBounds[0][1] - PartyEditorGridBounds[1][1]) / 2.0) +PartyEditorGridSize = (18, 15) +PartyEditorGridSquareSize = ((PartyEditorGridBounds[1][0] - PartyEditorGridBounds[0][0]) / float(PartyEditorGridSize[0]), (PartyEditorGridBounds[0][1] - PartyEditorGridBounds[1][1]) / float(PartyEditorGridSize[1])) +PartyEditorGridRotateThreshold = 0.08 +AvailableGridSquares = 202 +TrashCanPosition = (-0.24, 0.0, -0.65) +TrashCanScale = 0.7 +PartyEditorTrashBounds = ((-0.16, -0.38), (-0.05, -0.56)) +ActivityRequestStatus = PythonUtil.Enum(('Joining', 'Exiting')) +InviteStatus = PythonUtil.Enum(('NotRead', + 'ReadButNotReplied', + 'Accepted', + 'Rejected')) +InviteTheme = PythonUtil.Enum(('Birthday', + 'GenericMale', + 'GenericFemale', + 'Racing', + 'Valentoons', + 'VictoryParty', + 'Winter')) +PartyStatus = PythonUtil.Enum(('Pending', + 'Cancelled', + 'Finished', + 'CanStart', + 'Started', + 'NeverStarted')) +AddPartyErrorCode = PythonUtil.Enum(('AllOk', + 'ValidationError', + 'DatabaseError', + 'TooManyHostedParties')) +ChangePartyFieldErrorCode = PythonUtil.Enum(('AllOk', + 'ValidationError', + 'DatabaseError', + 'AlreadyStarted', + 'AlreadyRefunded')) +ActivityTypes = PythonUtil.Enum(('HostInitiated', 'GuestInitiated', 'Continuous')) +PartyGateDenialReasons = PythonUtil.Enum(('Unavailable', 'Full')) +ActivityIds = PythonUtil.Enum(('PartyJukebox', + 'PartyCannon', + 'PartyTrampoline', + 'PartyCatch', + 'PartyDance', + 'PartyTugOfWar', + 'PartyFireworks', + 'PartyClock', + 'PartyJukebox40', + 'PartyDance20', + 'PartyCog', + 'PartyVictoryTrampoline', + 'PartyWinterCatch', + 'PartyWinterTrampoline', + 'PartyWinterCog', + 'PartyValentineDance', + 'PartyValentineDance20', + 'PartyValentineJukebox', + 'PartyValentineJukebox40', + 'PartyValentineTrampoline')) +PartyEditorActivityOrder = [ActivityIds.PartyCog, + ActivityIds.PartyWinterCog, + ActivityIds.PartyJukebox, + ActivityIds.PartyJukebox40, + ActivityIds.PartyValentineJukebox, + ActivityIds.PartyValentineJukebox40, + ActivityIds.PartyCannon, + ActivityIds.PartyTrampoline, + ActivityIds.PartyValentineTrampoline, + ActivityIds.PartyVictoryTrampoline, + ActivityIds.PartyWinterTrampoline, + ActivityIds.PartyCatch, + ActivityIds.PartyWinterCatch, + ActivityIds.PartyDance, + ActivityIds.PartyDance20, + ActivityIds.PartyValentineDance, + ActivityIds.PartyValentineDance20, + ActivityIds.PartyTugOfWar, + ActivityIds.PartyFireworks, + ActivityIds.PartyClock] +UnreleasedActivityIds = () +MutuallyExclusiveActivities = ((ActivityIds.PartyJukebox, ActivityIds.PartyJukebox40), + (ActivityIds.PartyValentineJukebox, ActivityIds.PartyValentineJukebox40), + (ActivityIds.PartyDance, ActivityIds.PartyDance20), + (ActivityIds.PartyValentineDance, ActivityIds.PartyValentineDance20)) +VictoryPartyActivityIds = frozenset([ActivityIds.PartyVictoryTrampoline]) +VictoryPartyReplacementActivityIds = frozenset([ActivityIds.PartyTrampoline]) +WinterPartyActivityIds = frozenset([ActivityIds.PartyWinterCatch, ActivityIds.PartyWinterTrampoline, ActivityIds.PartyWinterCog]) +WinterPartyReplacementActivityIds = frozenset([ActivityIds.PartyCatch, ActivityIds.PartyTrampoline, ActivityIds.PartyCog]) +ValentinePartyActivityIds = frozenset([ActivityIds.PartyValentineDance, + ActivityIds.PartyValentineDance20, + ActivityIds.PartyValentineJukebox, + ActivityIds.PartyValentineJukebox40, + ActivityIds.PartyValentineTrampoline]) +ValentinePartyReplacementActivityIds = frozenset([ActivityIds.PartyDance, + ActivityIds.PartyDance20, + ActivityIds.PartyJukebox, + ActivityIds.PartyJukebox40, + ActivityIds.PartyTrampoline]) +DecorationIds = PythonUtil.Enum(('BalloonAnvil', + 'BalloonStage', + 'Bow', + 'Cake', + 'Castle', + 'GiftPile', + 'Horn', + 'MardiGras', + 'NoiseMakers', + 'Pinwheel', + 'GagGlobe', + 'BannerJellyBean', + 'CakeTower', + 'HeartTarget', + 'HeartBanner', + 'FlyingHeart', + 'Hydra', + 'BannerVictory', + 'CannonVictory', + 'CogStatueVictory', + 'TubeCogVictory', + 'CogIceCreamVictory', + 'cogIceCreamWinter', + 'StageWinter', + 'CogStatueWinter', + 'snowman', + 'snowDoodle', + 'BalloonAnvilValentine')) +DECORATION_VOLUME = 1.0 +DECORATION_CUTOFF = 45 +VictoryPartyDecorationIds = frozenset([DecorationIds.Hydra, + DecorationIds.BannerVictory, + DecorationIds.CannonVictory, + DecorationIds.CogStatueVictory, + DecorationIds.TubeCogVictory, + DecorationIds.CogIceCreamVictory]) +WinterPartyDecorationIds = frozenset([DecorationIds.cogIceCreamWinter, + DecorationIds.StageWinter, + DecorationIds.CogStatueWinter, + DecorationIds.snowman, + DecorationIds.snowDoodle]) +VictoryPartyReplacementDecorationIds = frozenset([DecorationIds.BannerJellyBean]) +ValentinePartyDecorationIds = frozenset([DecorationIds.BalloonAnvilValentine, + DecorationIds.HeartBanner, + DecorationIds.HeartTarget, + DecorationIds.FlyingHeart]) +ValentinePartyReplacementDecorationIds = frozenset([DecorationIds.BalloonAnvil, DecorationIds.BannerJellyBean]) +UnreleasedDecorationIds = () +GoToPartyStatus = PythonUtil.Enum(('AllowedToGo', + 'PartyFull', + 'PrivateParty', + 'PartyOver', + 'PartyNotActive')) +PlayGroundToPartyClockColors = {'the_burrrgh': (53.0 / 255.0, + 116.0 / 255.0, + 148.0 / 255.0, + 1.0), + 'daisys_garden': (52.0 / 255.0, + 153.0 / 255.0, + 95.0 / 255.0, + 1.0), + 'donalds_dock': (60.0 / 255.0, + 98.0 / 255.0, + 142.0 / 255.0, + 1.0), + 'donalds_dreamland': (79.0 / 255.0, + 92.0 / 255.0, + 120.0 / 255.0, + 1.0), + 'minnies_melody_land': (128.0 / 255.0, + 62.0 / 255.0, + 142.0 / 255.0, + 1.0), + 'toontown_central': (77.0 / 255.0, + 137.0 / 255.0, + 52.0 / 255.0, + 1.0)} +PartyGridUnitLength = [14.4, 14.6] +PartyGridHeadingConverter = 15.0 +PartyGridToPandaOffset = (-PartyGridUnitLength[0] * PartyEditorGridSize[0] / 2.0, -PartyGridUnitLength[1] * PartyEditorGridSize[1] / 2.0) +PartyCostMultiplier = 1 +MinimumPartyCost = 100 * PartyCostMultiplier +ActivityInformationDict = {ActivityIds.PartyJukebox: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': False, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyJukebox40: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': False, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyValentineJukebox: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': False, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyValentineJukebox40: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': False, + 'gridAsset': 'PartyJukebox_activity_1x1'}, + ActivityIds.PartyCannon: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 5, + 'limitPerParty': 10, + 'paidOnly': False, + 'gridAsset': 'PartyCannon_activity_1x1'}, + ActivityIds.PartyTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'paidOnly': False, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyValentineTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'paidOnly': False, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyVictoryTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'paidOnly': False, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyWinterTrampoline: {'cost': int(50 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 8, + 'paidOnly': False, + 'gridAsset': 'PartyTrampoline_activity_2x2'}, + ActivityIds.PartyCatch: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyCatch_activity_5x5'}, + ActivityIds.PartyWinterCatch: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyCatch_activity_5x5'}, + ActivityIds.PartyCog: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyCog_activity_5x5'}, + ActivityIds.PartyWinterCog: {'cost': int(300 * PartyCostMultiplier), + 'gridsize': (5, 5), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyCog_activity_5x5'}, + ActivityIds.PartyDance: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyDance20: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyValentineDance: {'cost': int(100 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyValentineDance20: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (3, 3), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': True, + 'gridAsset': 'PartyDance_activity_3x3'}, + ActivityIds.PartyTugOfWar: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (4, 4), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': False, + 'gridAsset': 'PartyTufOfWar_activity_4x4'}, + ActivityIds.PartyFireworks: {'cost': int(200 * PartyCostMultiplier), + 'gridsize': (4, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': False, + 'gridAsset': 'PartyFireworks_activity_2x4'}, + ActivityIds.PartyClock: {'cost': MinimumPartyCost, + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 1, + 'paidOnly': False, + 'gridAsset': 'PartyClock_activity_1x1'}} +DecorationInformationDict = {DecorationIds.BalloonAnvil: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.BalloonAnvilValentine: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.BalloonStage: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Bow: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Cake: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Castle: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.GiftPile: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Horn: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.MardiGras: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.NoiseMakers: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Pinwheel: {'cost': int(10 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.GagGlobe: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.BannerJellyBean: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CakeTower: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.HeartTarget: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.HeartBanner: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.FlyingHeart: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.Hydra: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_propStage_2x2'}, + DecorationIds.BannerVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CannonVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CogStatueVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.TubeCogVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.CogIceCreamVictory: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.cogIceCreamWinter: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.StageWinter: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (2, 2), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_propStage_2x2'}, + DecorationIds.CogStatueWinter: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.snowman: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}, + DecorationIds.snowDoodle: {'cost': int(25 * PartyCostMultiplier), + 'gridsize': (1, 1), + 'numberPerPurchase': 1, + 'limitPerParty': 5, + 'paidOnly': False, + 'gridAsset': 'decoration_1x1'}} +DefaultRulesTimeout = 10.0 +DenialReasons = PythonUtil.Enum(('Default', 'Full', 'SilentFail'), start=0) +FireworkShows = PythonUtil.Enum(('Summer',), start=200) +FireworksGlobalXOffset = 160.0 +FireworksGlobalYOffset = -20.0 +FireworksPostLaunchDelay = 5.0 +RocketSoundDelay = 2.0 +RocketDirectionDelay = 2.0 +FireworksStartedEvent = 'PartyFireworksStarted' +FireworksFinishedEvent = 'PartyFireworksFinished' +FireworksTransitionToDisabledDelay = 3.0 +TeamActivityTeams = PythonUtil.Enum(('LeftTeam', 'RightTeam'), start=0) +TeamActivityNeitherTeam = 3 +TeamActivityTextScale = 0.135 +TeamActivityStartDelay = 8.0 +TeamActivityClientWaitDelay = 30.0 +TeamActivityDefaultMinPlayersPerTeam = 1 +TeamActivityDefaultMaxPlayersPerTeam = 4 +TeamActivityDefaultDuration = 60.0 +TeamActivityDefaultConclusionDuration = 4.0 +TeamActivityStatusColor = VBase4(1.0, 1.0, 0.65, 1.0) +CogActivityBalanceTeams = True +CogActivityStartDelay = 15.0 +CogActivityConclusionDuration = 12 +CogActivityDuration = 90 +CogActivityMinPlayersPerTeam = 1 +CogActivityMaxPlayersPerTeam = 4 +CogActivityColors = (VBase4(0.22, 0.4, 0.98, 1.0), VBase4(1.0, 0.43, 0.04, 1.0)) +CogActivitySplatColorBase = VBase4(0.98, 0.9, 0.094, 1.0) +CogActivitySplatColors = (VBase4(CogActivityColors[0][0] / CogActivitySplatColorBase[0], CogActivityColors[0][1] / CogActivitySplatColorBase[1], CogActivityColors[0][2] / CogActivitySplatColorBase[2], 1.0), VBase4(CogActivityColors[1][0] / CogActivitySplatColorBase[0], CogActivityColors[1][1] / CogActivitySplatColorBase[1], CogActivityColors[1][2] / CogActivitySplatColorBase[2], 1.0)) +CogPinataHeadZ = 4.7 +CogActivityHitPoints = 1 +CogActivityHitPointsForHead = 3 +CogPinataPushBodyFactor = 0.05 +CogPinataPushHeadFactor = CogPinataPushBodyFactor * abs(CogActivityHitPointsForHead - CogActivityHitPoints) +CogActivityAvgBeansPerSecond = 0.15 +CogActivityBeansToAward = round(CogActivityAvgBeansPerSecond * CogActivityDuration * 2.0) +CogActivityWinBeans = int(round(CogActivityBeansToAward * 0.6)) +CogActivityLossBeans = int(round(CogActivityBeansToAward * 0.4)) +CogActivityTieBeans = int(round(CogActivityBeansToAward * 0.4)) +CogActivityPerfectWinBeans = int(round(CogActivityBeansToAward * 0.75)) +CogActivityPerfectLossBeans = int(round(CogActivityBeansToAward * 0.25)) +CogActivityArenaLength = 50.0 +CogActivityPieMinDist = 0.0 +CogActivityPieMaxDist = 110.0 +CogActivityPowerMeterHeight = 0.4 +CogActivityPowerMeterWidth = 0.1 +CogActivityPowerMeterPos = (0.33, 0.0, 0.0) +CogActivityPowerMeterTextPos = (0.33, -0.26) +CogActivityVictoryBarPos = (-0.55, 0.0, 0.825) +CogActivityVictoryBarOrangePos = (0.1725, 0.0, -0.0325) +CogActivityVictoryBarPiePos = (0.47, 0.0, -0.015) +CogActivityVictoryBarArrow = (0.0, 0.0, 0.1) +CogActivityBarUnitScale = 1.1 +CogActivityBarStartScale = CogActivityBarUnitScale * 5 +CogActivityBarPieUnitMove = 0.07 +CogActivityBarPieScale = 1.5 +CogActivityScorePos = (1.25, -0.45) +CogActivityScoreTitle = (1.24, -0.5) +CogActivityPowerMeterTime = 1.0 +CogActivityShortThrowTime = 0.1 +ToonAttackIdleThreshold = 5.0 +ToonMoveIdleThreshold = 5.0 +CogActivityShortThrowSpam = 3 +CogActivitySpamWarningShowTime = 5.0 +CogActivityControlsShowTime = 2.0 +PARTY_COG_CUTOFF = 60 +TugOfWarStartDelay = 8.0 +TugOfWarReadyDuration = 1.5 +TugOfWarGoDuration = 0.75 +TugOfWarDuration = 40.0 +TugOfWarMinimumPlayersPerTeam = 1 +TugOfWarMaximumPlayersPerTeam = 4 +TugOfWarStartGameTimeout = 8 +TugOfWarJoinCollisionEndPoints = [Point3(6.0, 0.0, 0.0), Point3(-6.0, 0.0, 0.0)] +TugOfWarJoinCollisionRadius = 1.75 +TugOfWarJoinCollisionPositions = [Point3(-10.5, 0.25, 4.5), Point3(10.5, -0.25, 4.5)] +TugOfWarInitialToonPositionsXOffset = 8.0 +TugOfWarToonPositionXSeparation = 2.0 +TugOfWarToonPositionZ = 2.55 +TugOfWarTextWordScale = 0.135 +TugOfWarTextCountdownScale = 4.0 +TugOfWarCameraPos = Point3(0.0, -33.0, 10.0) +TugOfWarCameraInitialHpr = Point3(0.0, -6.91123, 0.0) +TugOfWarCameraLookAtHeightOffset = 6.0 +TugOfWarPowerMeterSize = 17 +TugOfWarPowerMeterRulesTarget = 8 +TugOfWarDisabledArrowColor = VBase4(1.0, 0.0, 0.0, 0.3) +TugOfWarEnabledArrowColor = VBase4(1.0, 0.0, 0.0, 1.0) +TugOfWarHilightedArrowColor = VBase4(1.0, 0.7, 0.0, 1.0) +TugOfWarTargetRateList = [(8.0, 6), + (5.0, 7), + (6.0, 8), + (6.0, 10), + (7.0, 11), + (8.0, 12)] +TugOfWarKeyPressTimeToLive = 1.0 +TugOfWarKeyPressUpdateRate = 0.1 +TugOfWarKeyPressReportRate = 0.2 +TugOfWarMovementFactor = 0.03 +TugOfWarSplashZOffset = 1.0 +TugOfWarHeadings = [240.0, 120.0] +TugOfWarConclusionDuration = 4.0 +TugOfWarFallInWinReward = 15 +TugOfWarFallInLossReward = 4 +TugOfWarWinReward = 12 +TugOfWarLossReward = 8 +TugOfWarTieReward = 5 +TugOfWarTieThreshold = 0.75 +TrampolineDuration = 60.0 +TrampolineSignOffset = Point3(-6.0, -6.0, 0.0) +TrampolineLeverOffset = Point3(-5.0, -9.0, 0.0) +TrampolineNumJellyBeans = 12 +TrampolineJellyBeanBonus = 10 +CatchActivityDuration = 80 +CatchActivityBitmask = BitMask32(16) +CatchLeverOffset = Point3(-3.0, -2.0, 0.0) +CatchDropShadowHeight = 0.5 +CatchConclusionDuration = 3.0 + +class DropObject: + + def __init__(self, name, good, onscreenDurMult, modelPath): + self.name = name + self.good = good + self.onscreenDurMult = onscreenDurMult + self.modelPath = modelPath + + def isBaseline(self): + return self.onscreenDurMult == 1.0 + + +DropObjectTypes = [DropObject('apple', 1, 1.0, 'phase_4/models/minigames/apple'), + DropObject('orange', 1, 1.0, 'phase_4/models/minigames/orange'), + DropObject('pear', 1, 1.0, 'phase_4/models/minigames/pear'), + DropObject('coconut', 1, 1.0, 'phase_4/models/minigames/coconut'), + DropObject('watermelon', 1, 1.0, 'phase_4/models/minigames/watermelon'), + DropObject('pineapple', 1, 1.0, 'phase_4/models/minigames/pineapple'), + DropObject('anvil', 0, 0.4, 'phase_4/models/props/anvil-mod')] +Name2DropObjectType = {} +for type in DropObjectTypes: + Name2DropObjectType[type.name] = type + +Name2DOTypeId = {} +names = Name2DropObjectType.keys() +names.sort() +for i in range(len(names)): + Name2DOTypeId[names[i]] = i + +DOTypeId2Name = names +NumFruits = [{2000: 18, + 1000: 19, + 5000: 22, + 4000: 24, + 3000: 27, + 9000: 28}, + {2000: 30, + 1000: 33, + 5000: 38, + 4000: 42, + 3000: 46, + 9000: 50}, + {2000: 42, + 1000: 48, + 5000: 54, + 4000: 60, + 3000: 66, + 9000: 71}, + {2000: 56, + 1000: 63, + 5000: 70, + 4000: 78, + 3000: 85, + 9000: 92}] +DancePatternToAnims = {'dduu': 'slip-backward', + 'ldddud': 'happy-dance', + 'lll': 'left', + 'rdu': 'struggle', + 'rrr': 'right', + 'rulu': 'running-jump', + 'udlr': 'good-putt', + 'udllrr': 'victory', + 'ulu': 'jump', + 'uudd': 'slip-forward'} +DancePatternToAnims20 = {'ddd': 'down', + 'dduu': 'slip-backward', + 'drul': 'sad-walk', + 'ldr': 'push', + 'ldddud': 'happy-dance', + 'ldu': 'sprinkle-dust', + 'lll': 'left', + 'llrr': 'firehose', + 'lrlr': 'wave', + 'rdu': 'struggle', + 'rlrl': 'confused', + 'rrr': 'right', + 'rulu': 'running-jump', + 'uddd': 'reel-neutral', + 'udlr': 'good-putt', + 'udud': 'angry', + 'udllrr': 'victory', + 'ulu': 'jump', + 'uudd': 'slip-forward', + 'uuu': 'up'} +DanceAnimToName = {'right': TTLocalizer.DanceAnimRight, + 'reel-neutral': TTLocalizer.DanceAnimReelNeutral, + 'conked': TTLocalizer.DanceAnimConked, + 'happy-dance': TTLocalizer.DanceAnimHappyDance, + 'confused': TTLocalizer.DanceAnimConfused, + 'walk': TTLocalizer.DanceAnimWalk, + 'jump': TTLocalizer.DanceAnimJump, + 'firehose': TTLocalizer.DanceAnimFirehose, + 'shrug': TTLocalizer.DanceAnimShrug, + 'slip-forward': TTLocalizer.DanceAnimSlipForward, + 'sad-walk': TTLocalizer.DanceAnimSadWalk, + 'wave': TTLocalizer.DanceAnimWave, + 'struggle': TTLocalizer.DanceAnimStruggle, + 'running-jump': TTLocalizer.DanceAnimRunningJump, + 'slip-backward': TTLocalizer.DanceAnimSlipBackward, + 'down': TTLocalizer.DanceAnimDown, + 'up': TTLocalizer.DanceAnimUp, + 'good-putt': TTLocalizer.DanceAnimGoodPutt, + 'victory': TTLocalizer.DanceAnimVictory, + 'push': TTLocalizer.DanceAnimPush, + 'angry': TTLocalizer.DanceAnimAngry, + 'left': TTLocalizer.DanceAnimLeft} +DanceReverseLoopAnims = ['left', + 'right', + 'up', + 'down', + 'good-putt'] +ToonDancingStates = PythonUtil.Enum(('Init', + 'DanceMove', + 'Run', + 'Cleanup')) +JUKEBOX_TIMEOUT = 30.0 +MUSIC_PATH = 'phase_%s/audio/bgm/' +MUSIC_MIN_LENGTH_SECONDS = 50.0 +MUSIC_GAP = 2.5 +PhaseToMusicData = {3.5: {'TC_SZ.mid': [TTLocalizer.MusicTcSz, 57]}, + 3: {'create_a_toon.mid': [TTLocalizer.MusicCreateAToon, 175], + 'tt_theme.mid': [TTLocalizer.MusicTtTheme, 51]}, + 4: {'TC_nbrhood.mid': [TTLocalizer.MusicTcNbrhood, 59], + 'MG_TwoDGame.mid': [TTLocalizer.MusicMgTwodgame, 60], + 'MG_Vine.mid': [TTLocalizer.MusicMgVine, 32], + 'FF_safezone.mid': [TTLocalizer.MusicFfSafezone, 47]}, + 6: {'DD_SZ.mid': [TTLocalizer.MusicDdSz, 33], + 'GS_SZ.mid': [TTLocalizer.MusicGsSz, 60], + 'OZ_SZ.mid': [TTLocalizer.MusicOzSz, 31], + 'GZ_SZ.mid': [TTLocalizer.MusicGzSz, 59], + 'MM_SZ.mid': [TTLocalizer.MusicMmSz, 76]}, + 8: {'DG_SZ.mid': [TTLocalizer.MusicDgSz, 48], + 'DL_SZ.mid': [TTLocalizer.MusicDlSz, 33], + 'TB_SZ.mid': [TTLocalizer.MusicTbSz, 54]}, + 9: {'encntr_hall_of_fame.mid': [TTLocalizer.MusicEncntrHallOfFame, 51], + 'encntr_head_suit_theme.mid': [TTLocalizer.MusicEncntrHeadSuitTheme, 29]}, + 11: {'LB_juryBG.mid': [TTLocalizer.MusicLbJurybg, 30]}, + 13: {'party_original_theme.mid': [TTLocalizer.MusicPartyOriginalTheme, 56], + 'party_generic_theme_jazzy.mid': [TTLocalizer.MusicPartyGenericThemeJazzy, 64]}} +PhaseToMusicData40 = {3.5: {'encntr_general_bg.mid': [TTLocalizer.MusicEncntrGeneralBg, 30], + 'TC_SZ.mid': [TTLocalizer.MusicTcSz, 57]}, + 3: {'create_a_toon.mid': [TTLocalizer.MusicCreateAToon, 175], + 'tt_theme.mid': [TTLocalizer.MusicTtTheme, 51]}, + 4: {'minigame_race.mid': [TTLocalizer.MusicMinigameRace, 77], + 'TC_nbrhood.mid': [TTLocalizer.MusicTcNbrhood, 59], + 'MG_TwoDGame.mid': [TTLocalizer.MusicMgTwodgame, 60], + 'MG_CogThief.mid': [TTLocalizer.MusicMgCogthief, 61], + 'MG_Vine.mid': [TTLocalizer.MusicMgVine, 32], + 'MG_IceGame.mid': [TTLocalizer.MusicMgIcegame, 56], + 'FF_safezone.mid': [TTLocalizer.MusicFfSafezone, 47]}, + 6: {'DD_SZ.mid': [TTLocalizer.MusicDdSz, 33], + 'GZ_PlayGolf.mid': [TTLocalizer.MusicGzPlaygolf, 61], + 'GS_SZ.mid': [TTLocalizer.MusicGsSz, 60], + 'OZ_SZ.mid': [TTLocalizer.MusicOzSz, 31], + 'GS_Race_CC.mid': [TTLocalizer.MusicGsRaceCc, 58], + 'GS_Race_SS.mid': [TTLocalizer.MusicGsRaceSs, 61], + 'GS_Race_RR.mid': [TTLocalizer.MusicGsRaceRr, 60], + 'GZ_SZ.mid': [TTLocalizer.MusicGzSz, 59], + 'MM_SZ.mid': [TTLocalizer.MusicMmSz, 76], + 'DD_nbrhood.mid': [TTLocalizer.MusicDdNbrhood, 67], + 'GS_KartShop.mid': [TTLocalizer.MusicGsKartshop, 32]}, + 7: {'encntr_general_bg_indoor.mid': [TTLocalizer.MusicEncntrGeneralBgIndoor, 31], + 'encntr_suit_winning_indoor.mid': [TTLocalizer.MusicEncntrGeneralSuitWinningIndoor, 36]}, + 8: {'DL_nbrhood.mid': [TTLocalizer.MusicDlNbrhood, 30], + 'DG_SZ.mid': [TTLocalizer.MusicDgSz, 48], + 'DL_SZ.mid': [TTLocalizer.MusicDlSz, 33], + 'TB_SZ.mid': [TTLocalizer.MusicTbSz, 54]}, + 9: {'encntr_hall_of_fame.mid': [TTLocalizer.MusicEncntrHallOfFame, 51], + 'CHQ_FACT_bg.mid': [TTLocalizer.MusicChqFactBg, 50], + 'encntr_suit_winning.mid': [TTLocalizer.MusicEncntrSuitWinning, 31], + 'encntr_head_suit_theme.mid': [TTLocalizer.MusicEncntrHeadSuitTheme, 29]}, + 11: {'LB_juryBG.mid': [TTLocalizer.MusicLbJurybg, 30], + 'LB_courtyard.mid': [TTLocalizer.MusicLbCourtyard, 32]}, + 12: {'Bossbot_Factory_v1.mid': [TTLocalizer.MusicBossbotFactoryV1, 30], + 'BossBot_CEO_v1.mid': [TTLocalizer.MusicBossbotCeoV1, 31]}, + 13: {'party_original_theme.mid': [TTLocalizer.MusicPartyOriginalTheme, 56], + 'party_polka_dance.mid': [TTLocalizer.MusicPartyPolkaDance, 63], + 'party_waltz_dance.mid': [TTLocalizer.MusicPartyWaltzDance, 63], + 'party_generic_theme_jazzy.mid': [TTLocalizer.MusicPartyGenericThemeJazzy, 64]}} + +def countMusic(): + numMusic = 0 + for key in PhaseToMusicData: + numMusic += len(PhaseToMusicData[key]) + + print 'PhaseToMusicData %d' % numMusic + numMusic = 0 + for key in PhaseToMusicData40: + numMusic += len(PhaseToMusicData40[key]) + + print 'PhaseToMusicData40 %d' % numMusic + + +def getMusicRepeatTimes(length, minLength = MUSIC_MIN_LENGTH_SECONDS): + times = round(float(minLength) / length) + if minLength <= 0 or times < 1.0: + times = 1.0 + return times + + +def sanitizePhase(phase): + if phase == int(phase): + phase = int(phase) + return phase + + +CANNON_TIMEOUT = 30 +CANNON_MOVIE_LOAD = 1 +CANNON_MOVIE_CLEAR = 2 +CANNON_MOVIE_FORCE_EXIT = 3 +CANNON_MOVIE_LANDED = 4 +CannonJellyBeanReward = 2 +CannonMaxTotalReward = 200 +CatchMaxTotalReward = 1000 +PartyCannonCollisions = {'clouds': ['cloudSphere-0'], + 'bounce': ['wall_collision', + 'discoBall_collision', + 'platform_left_collision', + 'platform_right_collision'], + 'trampoline_bounce': 'TrampolineCollision', + 'ground': ['floor_collision', + 'danceFloor_collision', + 'danceFloorRamp_collision', + 'hill_collision', + 'fence_floor'], + 'fence': ['dockTube1_collision', + 'dockTube2_collision', + 'dockTube2_collision', + 'dockTube2_collision', + 'palm_collision_01', + 'palm_collision_02', + 'palm_collision_03', + 'wall_1_collision', + 'wall_2_collision', + 'wall_3_collision', + 'wall_4_collision', + 'wall_5_collision', + 'wall_6_collision', + 'tree_collision', + 'partyDecoration_collision', + 'launchPad_railing_collision', + 'launchPad_floor_collision', + 'launchPad_collision', + 'launchPad_railing2_collision', + 'launchPad__rocket_collision', + 'launchPad_lever_collision', + 'launchPad_bridge_collision', + 'launchPad_sphere2_collision', + 'launchPad_sphere1_collision', + 'partyClock_collision', + 'sign_collision']} + +def getCostOfParty(partyInfo): + newCost = 0 + for activityBase in partyInfo.activityList: + newCost += ActivityInformationDict[activityBase.activityId]['cost'] + + for decorBase in partyInfo.decors: + newCost += DecorationInformationDict[decorBase.decorId]['cost'] + + return newCost diff --git a/toontown/parties/PartyInfo.py b/toontown/parties/PartyInfo.py new file mode 100644 index 0000000..816f7cf --- /dev/null +++ b/toontown/parties/PartyInfo.py @@ -0,0 +1,70 @@ +from datetime import datetime +from direct.directnotify import DirectNotifyGlobal +from toontown.parties.PartyGlobals import InviteTheme +from toontown.parties.DecorBase import DecorBase +from toontown.parties.ActivityBase import ActivityBase + +class PartyInfoBase: + notify = DirectNotifyGlobal.directNotify.newCategory('PartyInfoBase') + + def __init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status): + self.partyId = partyId + self.hostId = hostId + self.startTime = datetime(startYear, startMonth, startDay, startHour, startMinute) + self.endTime = datetime(endYear, endMonth, endDay, endHour, endMinute) + self.isPrivate = isPrivate + self.inviteTheme = inviteTheme + self.activityList = [] + for oneItem in activityList: + newActivity = ActivityBase(oneItem[0], oneItem[1], oneItem[2], oneItem[3]) + self.activityList.append(newActivity) + + self.decors = [] + for oneItem in decors: + newDecor = DecorBase(oneItem[0], oneItem[1], oneItem[2], oneItem[3]) + self.decors.append(newDecor) + + self.status = status + + def getActivityIds(self): + activities = [] + for activityBase in self.activityList: + activities.append(activityBase.activityId) + + return activities + + def __str__(self): + string = 'partyId=%d ' % self.partyId + string += 'hostId=%d ' % self.hostId + string += 'start=%s ' % self.startTime + string += 'end=%s ' % self.endTime + string += 'isPrivate=%s ' % self.isPrivate + string += 'inviteTheme=%s ' % InviteTheme.getString(self.inviteTheme) + string += 'activityList=%s ' % self.activityList + string += 'decors=%s ' % self.decors + string += 'status=%s' % self.status + string += '\n' + return string + + def __repr__(self): + return self.__str__() + + +class PartyInfo(PartyInfoBase): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyInfo') + + def __init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status): + PartyInfoBase.__init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status) + serverTzInfo = base.cr.toontownTimeManager.serverTimeZone + self.startTime = self.startTime.replace(tzinfo=serverTzInfo) + self.endTime = self.endTime.replace(tzinfo=serverTzInfo) + + +class PartyInfoAI(PartyInfoBase): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyInfo') + + def __init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status): + PartyInfoBase.__init__(self, partyId, hostId, startYear, startMonth, startDay, startHour, startMinute, endYear, endMonth, endDay, endHour, endMinute, isPrivate, inviteTheme, activityList, decors, status) + serverTzInfo = simbase.air.toontownTimeManager.serverTimeZone + self.startTime = self.startTime.replace(tzinfo=serverTzInfo) + self.endTime = self.endTime.replace(tzinfo=serverTzInfo) diff --git a/toontown/parties/PartyLoader.py b/toontown/parties/PartyLoader.py new file mode 100644 index 0000000..fa801c1 --- /dev/null +++ b/toontown/parties/PartyLoader.py @@ -0,0 +1,301 @@ +import math +import random +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from pandac.PandaModules import * +from pandac.PandaModules import NodePath +from toontown.toonbase.ToontownGlobals import * +from toontown.safezone import SafeZoneLoader +from toontown.parties import Party +from toontown.parties.PartyGlobals import FireworksStartedEvent, FireworksFinishedEvent + +class PartyLoader(SafeZoneLoader.SafeZoneLoader): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyLoader') + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('PartyLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'party', 'planning']), + State.State('party', self.enterParty, self.exitParty, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['planning', 'party']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.musicFile = 'phase_13/audio/bgm/party_original_theme.mid' + self.activityMusicFile = 'phase_13/audio/bgm/party_waltz_dance.mid' + self.dnaFile = 'phase_13/dna/party_sz.dna' + self.safeZoneStorageDNAFile = None + self.cloudSwitch = 0 + self.id = PartyHood + self.partyOwnerId = None + self.branchZone = None + self.partyDoneEvent = 'partyDone' + self.barrel = None + self.clouds = [] + self.cloudTrack = None + self.sunMoonNode = None + self.fsm.enterInitialState() + return + + def load(self): + self.oldClear = base.win.getClearColor() + base.win.setClearColor(Vec4(0.47, 0.69, 0.3, 1.0)) + SafeZoneLoader.SafeZoneLoader.load(self) + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.mp3') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.mp3') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.mp3') + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + self.cricketSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + + def unload(self): + self.ignoreAll() + base.win.setClearColor(self.oldClear) + if base.cr.partyManager: + base.cr.partyManager.leaveParty() + self.partyOwnerId = None + self.partyZoneId = None + if self.place: + self.place.exit() + self.place.unload() + del self.place + del self.underwaterSound + del self.swimSound + del self.submergeSound + del self.birdSound + del self.cricketSound + self.__cleanupCloudFadeInterval() + if self.sunMoonNode: + self.sunMoonNode.removeNode() + del self.sunMoonNode + self.sunMoonNode = None + if self.clouds: + for cloud in self.clouds: + cloud[0].removeNode() + del cloud[1] + + del self.clouds + if self.barrel: + self.barrel.removeNode() + SafeZoneLoader.SafeZoneLoader.unload(self) + return + + def loadClouds(self): + self.loadCloudPlatforms() + if base.cloudPlatformsEnabled and 0: + self.setCloudSwitch(1) + if self.cloudSwitch: + self.setCloudSwitch(self.cloudSwitch) + + def enter(self, requestStatus): + self.partyOwnerId = requestStatus.get('ownerId', base.localAvatar.doId) + base.localAvatar.inParty = 1 + self.accept(FireworksStartedEvent, self.__handleFireworksStarted) + self.accept(FireworksFinishedEvent, self.__handleFireworksFinished) + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + + def exit(self): + self.ignoreAll() + base.cr.cache.flush() + base.localAvatar.stopChat() + base.localAvatar.inParty = 0 + self.ignore(FireworksStartedEvent) + self.ignore(FireworksFinishedEvent) + SafeZoneLoader.SafeZoneLoader.exit(self) + + def createSafeZone(self, dnaFile): + SafeZoneLoader.SafeZoneLoader.createSafeZone(self, dnaFile) + parent = self.geom.getParent() + geom = self.geom + n = NodePath('PartyGroundRoot') + n.reparentTo(parent) + geom.reparentTo(n) + geom.setPos(-10.0, 0.0, 0.0) + self.geom = n + self.loadSunMoon() + + def loadSunMoon(self): + self.sun = loader.loadModel('phase_4/models/props/sun.bam') + self.moon = loader.loadModel('phase_5.5/models/props/moon.bam') + self.sunMoonNode = self.geom.attachNewNode('sunMoon') + self.sunMoonNode.setPosHpr(0, 0, 0, 0, 0, 0) + if self.sun: + self.sun.reparentTo(self.sunMoonNode) + self.sun.setY(270) + self.sun.setScale(2) + self.sun.setBillboardPointEye() + if self.moon: + self.moon.reparentTo(self.sunMoonNode) + self.moon.setY(-270) + self.moon.setScale(15) + self.moon.setBillboardPointEye() + self.sunMoonNode.setP(30) + + def enterParty(self, requestStatus): + self.notify.debug('enterParty: requestStatus = %s' % requestStatus) + ownerId = requestStatus.get('ownerId') + if ownerId: + self.partyOwnerId = ownerId + zoneId = requestStatus['zoneId'] + self.notify.debug('enterParty, ownerId = %s, zoneId = %s' % (self.partyOwnerId, zoneId)) + self.accept(self.partyDoneEvent, self.handlePartyDone) + self.place = Party.Party(self, self.partyOwnerId, zoneId, self.fsm.getStateNamed('party'), self.partyDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + self.partyZoneId = zoneId + + def exitParty(self): + self.notify.debug('exitParty') + self.ignore(self.partyDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + base.cr.cache.flush() + return + + def handlePartyDone(self, doneStatus = None): + PartyLoader.notify.debug('handlePartyDone doneStatus = %s' % doneStatus) + if not doneStatus: + doneStatus = self.place.getDoneStatus() + how = doneStatus['how'] + shardId = doneStatus['shardId'] + hoodId = doneStatus['hoodId'] + zoneId = doneStatus['zoneId'] + avId = doneStatus.get('avId', -1) + ownerId = doneStatus.get('ownerId', -1) + self.notify.debug('hoodId = %s, avId = %s' % (hoodId, avId)) + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def atMyParty(self): + if self.partyOwnerId != None: + if self.partyOwnerId == base.localAvatar.getDoId(): + return 1 + else: + return 0 + else: + self.notify.warning("We aren't in an party") + return + + def startCloudPlatforms(self): + return + if len(self.clouds): + self.cloudTrack = self.__cloudTrack() + self.cloudTrack.loop() + + def stopCloudPlatforms(self): + if self.cloudTrack: + self.cloudTrack.pause() + del self.cloudTrack + self.cloudTrack = None + return + + def __cloudTrack(self): + track = Parallel() + for cloud in self.clouds: + axis = cloud[1] + pos = cloud[0].getPos(render) + newPos = pos + axis * 30 + reversePos = pos - axis * 30 + track.append(Sequence(LerpPosInterval(cloud[0], 10, newPos), LerpPosInterval(cloud[0], 20, reversePos), LerpPosInterval(cloud[0], 10, pos))) + + return track + + def debugGeom(self, decomposed): + print 'numPrimitives = %d' % decomposed.getNumPrimitives() + for primIndex in range(decomposed.getNumPrimitives()): + prim = decomposed.getPrimitive(primIndex) + print 'prim = %s' % prim + print 'isIndexed = %d' % prim.isIndexed() + print 'prim.getNumPrimitives = %d' % prim.getNumPrimitives() + for basicPrim in range(prim.getNumPrimitives()): + print '%d start=%d' % (basicPrim, prim.getPrimitiveStart(basicPrim)) + print '%d end=%d' % (basicPrim, prim.getPrimitiveEnd(basicPrim)) + + def loadCloud(self, version, radius, zOffset): + self.notify.debug('loadOnePlatform version=%d' % version) + cloud = NodePath('cloud-%d%d' % (radius, version)) + cloudModel = loader.loadModel('phase_5.5/models/estate/bumper_cloud') + cc = cloudModel.copyTo(cloud) + colCube = cc.find('**/collision') + colCube.setName('cloudSphere-0') + dTheta = 2.0 * math.pi / self.numClouds + cloud.reparentTo(self.cloudOrigin) + axes = [Vec3(1, 0, 0), Vec3(0, 1, 0), Vec3(0, 0, 1)] + cloud.setPos(radius * math.cos(version * dTheta), radius * math.sin(version * dTheta), 4 * random.random() + zOffset) + cloud.setScale(4.0) + cloud.setTag('number', '%d%d' % (radius, version)) + self.clouds.append([cloud, random.choice(axes)]) + + def loadSkyCollision(self): + plane = CollisionPlane(Plane(Vec3(0, 0, -1), Point3(0, 0, 200))) + plane.setTangible(0) + planeNode = CollisionNode('sky_collision') + planeNode.addSolid(plane) + self.cloudOrigin.attachNewNode(planeNode) + + def loadCloudPlatforms(self): + self.cloudOrigin = self.geom.attachNewNode('cloudOrigin') + self.cloudOrigin.setZ(30) + self.loadSkyCollision() + self.numClouds = 12 + for i in range(self.numClouds): + self.loadCloud(i, 50, 0) + + for i in range(self.numClouds): + self.loadCloud(i, 70, 30) + + for i in range(self.numClouds): + self.loadCloud(i, 30, 60) + + self.cloudOrigin.stash() + + def __cleanupCloudFadeInterval(self): + if hasattr(self, 'cloudFadeInterval'): + self.cloudFadeInterval.pause() + self.cloudFadeInterval = None + return + + def fadeClouds(self): + self.__cleanupCloudFadeInterval() + self.cloudOrigin.setTransparency(1) + self.cloudFadeInterval = self.cloudOrigin.colorInterval(0.5, Vec4(1, 1, 1, int(self.cloudOrigin.isStashed())), blendType='easeIn') + if self.cloudOrigin.isStashed(): + self.cloudOrigin.setColor(Vec4(1, 1, 1, 0)) + self.setCloudSwitch(1) + else: + self.cloudFadeInterval = Sequence(self.cloudFadeInterval, Func(self.setCloudSwitch, 0), Func(self.cloudOrigin.setTransparency, 0)) + self.cloudFadeInterval.start() + + def setCloudSwitch(self, on): + self.cloudSwitch = on + if hasattr(self, 'cloudOrigin'): + if on: + self.cloudOrigin.unstash() + else: + self.cloudOrigin.stash() + + def _clearDayChangeInterval(self): + if hasattr(self, 'dayChangeInterval'): + self.dayChangeInterval.pause() + self.dayChangeInterval = None + return + + def switchToNight(self): + self._clearDayChangeInterval() + self.dayChangeInterval = Sequence(self.sunMoonNode.hprInterval(5.0, Point3(0, -30, 0), blendType='easeInOut'), Func(base.win.setClearColor, Vec4(0.15, 0.22, 0.14, 1.0))) + self.dayChangeInterval.start() + + def switchToDay(self): + self.dayChangeInterval = Sequence(Func(base.win.setClearColor, Vec4(0.47, 0.69, 0.3, 1.0)), self.sunMoonNode.hprInterval(5.0, Point3(0, 30, 0), blendType='easeInOut')) + self.dayChangeInterval.start() + + def __handleFireworksStarted(self): + self.sunMoonNode.hide() + + def __handleFireworksFinished(self): + self.sunMoonNode.show() diff --git a/toontown/parties/PartyPlanner.py b/toontown/parties/PartyPlanner.py new file mode 100644 index 0000000..d943fab --- /dev/null +++ b/toontown/parties/PartyPlanner.py @@ -0,0 +1,735 @@ +import calendar +from datetime import datetime +from datetime import timedelta +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from otp.otpbase import OTPLocalizer +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from direct.fsm.FSM import FSM +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toontowngui.TeaserPanel import TeaserPanel +from toontown.toon import ToonHead +from toontown.parties import PartyGlobals +from toontown.friends.FriendsListPanel import determineFriendName +from toontown.parties.ScrolledFriendList import ScrolledFriendList +from toontown.parties.CalendarGuiMonth import CalendarGuiMonth +from toontown.parties.InviteVisual import InviteVisual +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties import PartyUtils +from toontown.parties.PartyEditor import PartyEditor +from otp.otpbase import OTPGlobals +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal + +class PartyPlanner(DirectFrame, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyPlanner') + + def __init__(self, doneEvent = None): + FSM.__init__(self, 'PartyPlannerFSM') + DirectFrame.__init__(self) + self.doneEvent = doneEvent + self.stateArray = ['Off', + 'Welcome', + 'PartyEditor', + 'Guests', + 'Date', + 'Time', + 'Invitation', + 'Farewell'] + self.partyTime = base.cr.toontownTimeManager.getCurServerDateTime() + self.partyNowTime = base.cr.toontownTimeManager.getCurServerDateTime() + minutesToNextFifteen = 15 - self.partyTime.minute % 15 + self.cleanPartyTime = self.partyTime + timedelta(minutes=minutesToNextFifteen, seconds=-self.partyTime.second) + self.partyTime = self.cleanPartyTime + self.guests = [] + self.isPrivate = False + self.selectedCalendarGuiDay = None + self.gui = loader.loadModel('phase_4/models/parties/partyPlannerGUI') + self.partyDuration = timedelta(hours=PartyGlobals.DefaultPartyDuration) + self.timeTypeToMaxValue = {'hour': 23, + 'minute': 59} + self.timeTypeToChangeAmount = {'hour': (1, -1), + 'minute': (15, -15), + 'ampm': (1, -1)} + self.partyInfo = None + self.asapMinuteRounding = base.config.GetInt('party-asap-minute-rounding', PartyGlobals.PartyPlannerAsapMinuteRounding) + self.load() + self.request('Welcome') + return + + def enterWelcome(self, *args): + self.prevButton['state'] = DirectGuiGlobals.DISABLED + self.prevButton.hide() + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.welcomePage.show() + self.partyPlannerHead.reparentTo(self.welcomePage) + self.partyPlannerHead.startBlink() + self.partyPlannerHead.startLookAround() + self.nametagNP.reparentTo(self.welcomePage) + self.chatNP.reparentTo(self.welcomePage) + + def exitWelcome(self): + self.welcomePage.hide() + self.prevButton.show() + self.partyPlannerHead.stopBlink() + self.partyPlannerHead.stopLookAround() + + def enterPartyEditor(self, *args): + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton['state'] = DirectGuiGlobals.DISABLED + self.nextButton.hide() + self.partyEditorPage.show() + self.okWithGroundsGui.doneStatus = '' + self.partyEditor.request('Idle') + + def exitPartyEditor(self): + self.partyEditor.request('Hidden') + self.partyEditorPage.hide() + + def enterGuests(self, *args): + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.show() + self.guestPage.show() + + def exitGuests(self): + self.guests = [] + for friendCheckBox in self.friendList['items']: + if friendCheckBox['indicatorValue']: + self.guests.append(friendCheckBox.getPythonTag('id')) + + self.guestPage.hide() + + def enterDate(self, *args): + self.prevButton.show() + self.prevButton['state'] = DirectGuiGlobals.NORMAL + if self.selectedCalendarGuiDay is None: + self.nextButton['state'] = DirectGuiGlobals.DISABLED + self.nextButton.hide() + self.makePartyNowButton.show() + self.datePage.show() + return + + def exitDate(self): + self.datePage.hide() + self.nextButton.show() + if self.selectedCalendarGuiDay is not None: + self.partyTime = self.cleanPartyTime + self.alterPartyTime(year=self.selectedCalendarGuiDay.myDate.year, month=self.selectedCalendarGuiDay.myDate.month, day=self.selectedCalendarGuiDay.myDate.day) + else: + self.partyNowTime = self.calcAsapTime() + self.partyTime = self.partyNowTime + return + + def calcAsapTime(self): + curServerTime = base.cr.toontownTimeManager.getCurServerDateTime() + baseTime = curServerTime + baseTime = baseTime.replace(baseTime.year, baseTime.month, baseTime.day, baseTime.hour, baseTime.minute, second=0, microsecond=0) + minute = curServerTime.minute + remainder = minute % self.asapMinuteRounding + if remainder: + baseTime += timedelta(minutes=self.asapMinuteRounding - remainder) + else: + baseTime += timedelta(minutes=self.asapMinuteRounding) + return baseTime + + def enterTime(self, *args): + self.prevButton.show() + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.show() + self.timePage.show() + self.timePageRecapToontownTimeLabel2['text'] = '%s' % PartyUtils.formatDateTime(self.partyTime) + self.timePageRecapLocalTimeLabel['text'] = '%s%s' % (TTLocalizer.PartyPlannerTimeLocalTime, PartyUtils.formatDateTime(self.partyTime, inLocalTime=True)) + + def exitTime(self): + self.timePage.hide() + self.nextButton.show() + + def enterInvitation(self, *args): + self.prevButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.hide() + defaultInviteTheme = PartyGlobals.InviteTheme.GenericMale + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + if ToontownGlobals.VICTORY_PARTY_HOLIDAY in base.cr.newsManager.getHolidayIdList(): + defaultInviteTheme = PartyGlobals.InviteTheme.VictoryParty + elif ToontownGlobals.KARTING_TICKETS_HOLIDAY in base.cr.newsManager.getHolidayIdList() or ToontownGlobals.CIRCUIT_RACING_EVENT in base.cr.newsManager.getHolidayIdList(): + defaultInviteTheme = PartyGlobals.InviteTheme.Racing + elif ToontownGlobals.VALENTINES_DAY in base.cr.newsManager.getHolidayIdList(): + defaultInviteTheme = PartyGlobals.InviteTheme.Valentoons + if self.partyInfo is not None: + del self.partyInfo + activityList = self.partyEditor.partyEditorGrid.getActivitiesOnGrid() + decorationList = self.partyEditor.partyEditorGrid.getDecorationsOnGrid() + endTime = self.partyTime + self.partyDuration + self.partyInfo = PartyInfo(0, 0, self.partyTime.year, self.partyTime.month, self.partyTime.day, self.partyTime.hour, self.partyTime.minute, endTime.year, endTime.month, endTime.day, endTime.hour, endTime.minute, self.isPrivate, defaultInviteTheme, activityList, decorationList, 0) + if self.noFriends or len(self.getInvitees()) == 0: + self.inviteVisual.setNoFriends(True) + self.invitationTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmTitleNoFriends + self.inviteButton['text'] = TTLocalizer.PartyPlannerInviteButtonNoFriends + self.selectedInviteThemeLabel.stash() + self.nextThemeButton.stash() + self.prevThemeButton.stash() + self.setInviteTheme(defaultInviteTheme) + else: + self.inviteVisual.setNoFriends(False) + self.invitationTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmTitle + self.inviteButton['text'] = TTLocalizer.PartyPlannerInviteButton + self.selectedInviteThemeLabel.unstash() + self.nextThemeButton.unstash() + self.prevThemeButton.unstash() + self.setInviteTheme(defaultInviteTheme) + self.inviteVisual.updateInvitation(base.localAvatar.getName(), self.partyInfo) + self.invitationPage.show() + return + + def __prevTheme(self): + self.nextThemeButton.show() + prevTheme = self.currentInvitationTheme - 1 + while prevTheme not in self.inviteThemes: + prevTheme -= 1 + if prevTheme == self.currentInvitationTheme: + self.notify.warning('No previous invite theme found.') + break + elif prevTheme < 0: + prevTheme = len(self.inviteVisual.inviteThemesIdToInfo) - 1 + + self.setInviteTheme(prevTheme) + + def __nextTheme(self): + self.prevThemeButton.show() + nextTheme = self.currentInvitationTheme + 1 + while nextTheme not in self.inviteThemes: + nextTheme += 1 + if nextTheme == self.currentInvitationTheme: + self.notify.warning('No next invite theme found.') + break + elif nextTheme >= len(self.inviteVisual.inviteThemesIdToInfo): + nextTheme = 0 + + self.setInviteTheme(nextTheme) + + def setInviteTheme(self, themeNumber): + self.currentInvitationTheme = themeNumber + self.selectedInviteThemeLabel['text'] = '%s %s (%d/%d)' % (self.inviteVisual.inviteThemesIdToInfo[self.currentInvitationTheme][1], + TTLocalizer.PartyPlannerInvitationTheme, + self.inviteThemes.index(self.currentInvitationTheme) + 1, + len(self.inviteThemes)) + self.partyInfo.inviteTheme = self.currentInvitationTheme + self.inviteVisual.updateInvitation(base.localAvatar.getName(), self.partyInfo) + + def exitInvitation(self): + self.invitationPage.hide() + self.nextButton.show() + + def enterFarewell(self, goingBackAllowed): + self.farewellPage.show() + if goingBackAllowed: + self.prevButton.show() + else: + self.prevButton.hide() + self.nextButton.hide() + self.partyPlannerHead.reparentTo(self.farewellPage) + self.partyPlannerHead.startBlink() + self.partyPlannerHead.startLookAround() + self.nametagNP.reparentTo(self.farewellPage) + self.chatNP.reparentTo(self.farewellPage) + + def exitFarewell(self): + self.farewellPage.hide() + self.nextButton.show() + self.prevButton.show() + self.partyPlannerHead.stopBlink() + self.partyPlannerHead.stopLookAround() + + def load(self): + self.frame = DirectFrame(parent=aspect2d, geom=self.gui.find('**/background'), relief=None, scale=0.85, pos=(0.05, 0.0, 0.1)) + self.titleScale = TTLocalizer.PPtitleScale + self._createNavButtons() + self.welcomePage = self._createWelcomePage() + self.welcomePage.hide() + self.datePage = self._createDatePage() + self.datePage.hide() + self.timePage = self._createTimePage() + self.timePage.hide() + self.guestPage = self._createGuestPage() + self.guestPage.hide() + self.partyEditorPage = self._createPartyEditorPage() + self.partyEditorPage.hide() + self.invitationPage = self._createInvitationPage() + self.invitationPage.hide() + self.farewellPage = self._createFarewellPage() + self.farewellPage.hide() + return + + def _createNavButtons(self): + self.quitButton = DirectButton(parent=self.frame, relief=None, geom=(self.gui.find('**/cancelButton_up'), self.gui.find('**/cancelButton_down'), self.gui.find('**/cancelButton_rollover')), command=self.__acceptExit) + self.nextButton = DirectButton(parent=self.frame, relief=None, geom=(self.gui.find('**/bottomNext_button/nextButton_up'), self.gui.find('**/bottomNext_button/nextButton_down'), self.gui.find('**/bottomNext_button/nextButton_rollover')), command=self.__nextItem, state=DirectGuiGlobals.DISABLED) + self.prevButton = DirectButton(parent=self.frame, relief=None, geom=(self.gui.find('**/bottomPrevious_button/previousButton_up'), self.gui.find('**/bottomPrevious_button/previousButton_down'), self.gui.find('**/bottomPrevious_button/previousButton_rollover')), command=self.__prevItem, state=DirectGuiGlobals.DISABLED) + self.currentItem = None + return + + def __createNametag(self, parent): + if self.nametagGroup == None: + self.nametagGroup = NametagGroup() + self.nametagGroup.setFont(OTPGlobals.getInterfaceFont()) + self.nametagGroup.setActive(0) + self.nametagGroup.setAvatar(self.partyPlannerHead) + self.nametagGroup.manage(base.marginManager) + self.nametagGroup.setColorCode(self.nametagGroup.CCNonPlayer) + self.nametagGroup.getNametag2d().setContents(0) + self.nametagNode = NametagFloat2d() + self.nametagNode.setContents(Nametag.CName) + self.nametagGroup.addNametag(self.nametagNode) + self.nametagGroup.setName(base.cr.partyManager.getPartyPlannerName()) + self.nametagNP = parent.attachNewNode(self.nametagNode.upcastToPandaNode()) + nametagPos = self.gui.find('**/step_01_partymanPeteNametag_locator').getPos() + self.nametagNP.setPosHprScale(nametagPos[0], 0, nametagPos[2], 0, 0, 0, 0.1, 1, 0.1) + self.chatNode = NametagFloat2d() + self.chatNode.setContents(Nametag.CSpeech | Nametag.CThought) + self.nametagGroup.addNametag(self.chatNode) + self.nametagGroup.setChat(TTLocalizer.PartyPlannerInstructions, CFSpeech) + self.chatNP = parent.attachNewNode(self.chatNode.upcastToPandaNode()) + chatPos = self.gui.find('**/step_01_partymanPeteText_locator').getPos() + self.chatNP.setPosHprScale(chatPos[0], 0, chatPos[2], 0, 0, 0, 0.08, 1, 0.08) + return + + def clearNametag(self): + if self.nametagGroup != None: + self.nametagGroup.unmanage(base.marginManager) + self.nametagGroup.removeNametag(self.nametagNode) + self.nametagGroup.removeNametag(self.chatNode) + self.nametagNP.removeNode() + self.chatNP.removeNode() + del self.nametagNP + del self.chatNP + del self.nametagNode + del self.chatNode + self.nametagGroup.setAvatar(NodePath()) + self.nametagGroup = None + return + + def _createWelcomePage(self): + self.nametagGroup = None + page = DirectFrame(self.frame) + page.setName('PartyPlannerWelcomePage') + self.welcomeTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerWelcomeTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.partyPlannerHead = ToonHead.ToonHead() + partyPlannerStyle = base.cr.partyManager.getPartyPlannerStyle() + self.partyPlannerHead.setupHead(partyPlannerStyle, forGui=True) + self.partyPlannerHead.setPos(self.gui.find('**/step_01_partymanPete_locator').getPos()) + animal = partyPlannerStyle.getAnimal() + if animal == 'cat' or animal == 'pig': + headScale = 0.4 + elif animal == 'dog' or animal == 'bear': + headScale = 0.45 + elif animal == 'rabbit': + headScale = 0.35 + else: + headScale = 0.3 + self.partyPlannerHead.setScale(headScale) + self.partyPlannerHead.setH(180.0) + self.partyPlannerHead.reparentTo(page) + self.__createNametag(page) + return page + + def _createDatePage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerDatePage') + self.createDateTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerDateTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + pos = self.gui.find('**/step_06_sendInvitation_locator').getPos() + self.makePartyNowButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/send_up'), self.gui.find('**/send_down'), self.gui.find('**/send_rollover')), text=TTLocalizer.PartyPlannerPartyNow, text_pos=(pos[0], pos[2]), text_scale=0.05, command=self.__doMakePartyNow) + curServerDate = base.cr.toontownTimeManager.getCurServerDateTime() + self.calendarGuiMonth = CalendarGuiMonth(page, curServerDate, scale=0.95, pos=(-0.05, 0.0, -0.33), dayClickCallback=self._dayClickCallback, onlyFutureDaysClickable=True) + return page + + def __doMakePartyNow(self): + self.request('Invitation') + + def _dayClickCallback(self, calendarGuiDay): + self.selectedCalendarGuiDay = calendarGuiDay + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.makePartyNowButton.hide() + self.nextButton.show() + + def alterPartyTime(self, year = None, month = None, day = None, hour = None, minute = None): + self.partyTime = datetime(year=self.positiveTime('year', year), month=self.positiveTime('month', month), day=self.positiveTime('day', day), hour=self.positiveTime('hour', hour), minute=self.positiveTime('minute', minute), tzinfo=self.partyTime.tzinfo) + + def positiveTime(self, type, amount): + if amount is None: + return getattr(self.partyTime, type) + if type == 'hour' or type == 'minute': + if amount < 0: + return self.timeTypeToMaxValue[type] + 1 + self.timeTypeToChangeAmount[type][1] + elif amount > self.timeTypeToMaxValue[type]: + return 0 + return amount + + def _createTimePage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerTimePage') + self.createTimeTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.clockImage = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/toontownTime_background')) + self.timePageToontownLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeToontown, pos=self.gui.find('**/step_03_toontown_locator').getPos(), scale=0.15, text_fg=(1.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getSignFont()) + self.timePageTimeLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeTime, pos=self.gui.find('**/step_03_time_locator').getPos(), scale=0.15, text_fg=(1.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getSignFont()) + self.timePageRecapLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeRecap, pos=self.gui.find('**/step_03_partyDateAndTime_locator').getPos(), scale=0.09) + self.timePageRecapToontownTimeLabel1 = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerTimeToontownTime, pos=self.gui.find('**/step_03_toontownTime_locator').getPos(), scale=0.06) + self.timePageRecapToontownTimeLabel2 = DirectLabel(parent=page, relief=None, text='%s' % PartyUtils.formatDateTime(self.partyTime), pos=self.gui.find('**/step_03_toontownDateAndTime_loactor').getPos(), textMayChange=True, scale=0.06) + self.timePageRecapLocalTimeLabel = DirectLabel(parent=page, relief=None, text='%s%s' % (TTLocalizer.PartyPlannerTimeLocalTime, PartyUtils.formatDateTime(self.partyTime, inLocalTime=True)), pos=self.gui.find('**/step_03_localDateAndTime_loactor').getPos(), textMayChange=True, scale=0.06, text_fg=(1.0, 0.0, 0.0, 1.0)) + self.timeInputHourLabel, self.timeInputHourUpButton, self.timeInputHourDownButton = self.getTimeWidgets(page, 'hour') + self.timeInputMinuteLabel, self.timeInputMinuteUpButton, self.timeInputMinuteDownButton = self.getTimeWidgets(page, 'minute') + self.timeInputAmPmLabel, self.timeInputAmPmUpButton, self.timeInputAmPmDownButton = self.getTimeWidgets(page, 'ampm') + self.timePagecolonLabel = DirectLabel(parent=page, relief=None, text=':', pos=self.gui.find('**/step_03_colon_locator').getPos(), scale=0.15) + return page + + def getTimeWidgets(self, page, type): + if type == 'ampm': + data = self.getCurrentAmPm() + else: + data = getattr(self.partyTime, type) + if data == 0 and type == 'minute': + data = '00' + else: + if type == 'hour': + data = data % 12 + if data == 0: + data = 12 + data = '%d' % data + label = DirectLabel(parent=page, relief=None, text='%s' % data, textMayChange=True, pos=self.gui.find('**/step_03_%s_locator' % type).getPos(), scale=0.12) + + def changeValue(self, amount): + if type == 'ampm': + self.alterPartyTime(hour=(self.partyTime.hour + 12) % 24) + newAmount = self.getCurrentAmPm() + label['text'] = newAmount + else: + if type == 'hour': + newAmount = getattr(self.partyTime, type) + amount + newAmount = newAmount % 12 + if self.timeInputAmPmLabel['text'] == TTLocalizer.PartyTimeFormatMeridiemPM: + newAmount = newAmount % 12 + 12 + self.alterPartyTime(hour=newAmount) + elif type == 'minute': + newAmount = getattr(self.partyTime, type) + amount + self.alterPartyTime(minute=newAmount) + else: + PartyPlanner.notify.error('Invalid type for changeValue in PartyPlanner: %s' % type) + newAmount = getattr(self.partyTime, type) + if newAmount < 10 and type == 'minute': + label['text'] = '0%d' % newAmount + else: + if type == 'hour': + newAmount = newAmount % 12 + if newAmount == 0: + newAmount = 12 + label['text'] = '%d' % newAmount + self.timePageRecapToontownTimeLabel2['text'] = '%s' % PartyUtils.formatDateTime(self.partyTime) + self.timePageRecapLocalTimeLabel['text'] = '%s%s' % (TTLocalizer.PartyPlannerTimeLocalTime, PartyUtils.formatDateTime(self.partyTime, inLocalTime=True)) + + upButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/%sButtonUp_up' % type), self.gui.find('**/%sButtonUp_down' % type), self.gui.find('**/%sButtonUp_rollover' % type)), command=changeValue, extraArgs=[self, self.timeTypeToChangeAmount[type][0]]) + downButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/%sButtonDown_up' % type), self.gui.find('**/%sButtonDown_down' % type), self.gui.find('**/%sButtonDown_rollover' % type)), command=changeValue, extraArgs=[self, self.timeTypeToChangeAmount[type][1]]) + return (label, upButton, downButton) + + def getCurrentAmPm(self): + if self.partyTime.hour < 12: + return TTLocalizer.PartyTimeFormatMeridiemAM + else: + return TTLocalizer.PartyTimeFormatMeridiemPM + + def _createGuestPage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerGuestPage') + self.guestTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerGuestTitle, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.guestBackgroundLabel = DirectLabel(parent=page, relief=None, image=self.gui.find('**/guestListBackground_flat'), scale=(1.2, 1.0, 1.0)) + self.friendList = ScrolledFriendList(page, self.gui, makeItemsCheckBoxes=True) + if len(base.localAvatar.friendsList) == 0: + self.noFriends = True + else: + self.noFriends = False + for friendPair in base.localAvatar.friendsList: + self.friendList.addFriend(determineFriendName(friendPair), friendPair[0]) + + self.friendList.scrollTo(0) + pos = self.gui.find('**/step_04_partyWillBe_locator').getPos() + self.publicPrivateLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPublicPrivateLabel, text_align=TextNode.ACenter, text_scale=0.065, pos=pos) + self.publicDescriptionLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPublicDescription, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPpbulicDescriptionLabel, pos=(pos[0] - 0.52, pos[1], pos[2])) + self.publicDescriptionLabel.stash() + self.privateDescriptionLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPrivateDescription, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPprivateDescriptionLabel, pos=(pos[0] + 0.55, pos[1], pos[2])) + self.privateDescriptionLabel.stash() + pos = self.gui.find('**/step_04_public_locator').getPos() + self.publicButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/publicButton_up'), + self.gui.find('**/publicButton_down'), + self.gui.find('**/publicButton_rollover'), + self.gui.find('**/publicButton_inactive')), text=TTLocalizer.PartyPlannerPublic, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPpublicButton, command=self.__doTogglePublicPrivate) + self.publicButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton.bind(DirectGuiGlobals.ENTER, self.__enterPublic) + self.publicButton.bind(DirectGuiGlobals.EXIT, self.__exitPublic) + pos = self.gui.find('**/step_04_private_locator').getPos() + self.privateButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/privateButton_up'), + self.gui.find('**/privateButton_down'), + self.gui.find('**/privateButton_rollover'), + self.gui.find('**/privateButton_inactive')), text=TTLocalizer.PartyPlannerPrivate, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPprivateButton, command=self.__doTogglePublicPrivate) + self.privateButton.bind(DirectGuiGlobals.ENTER, self.__enterPrivate) + self.privateButton.bind(DirectGuiGlobals.EXIT, self.__exitPrivate) + self.checkAllButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/checkAllButton_up'), self.gui.find('**/checkAllButton_down'), self.gui.find('**/checkAllButton_rollover')), command=self.__doCheckAll) + self.uncheckAllButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/uncheckAllButton_up'), self.gui.find('**/uncheckAllButton_down'), self.gui.find('**/uncheckAllButton_rollover')), command=self.__doUncheckAll) + return page + + def __doCheckAll(self): + for friendBox in self.friendList['items']: + friendBox['indicatorValue'] = True + + def __doUncheckAll(self): + for friendBox in self.friendList['items']: + friendBox['indicatorValue'] = False + + def __enterPrivate(self, mouseEvent): + self.privateDescriptionLabel.unstash() + + def __exitPrivate(self, mouseEvent): + self.privateDescriptionLabel.stash() + + def __enterPublic(self, mouseEvent): + self.publicDescriptionLabel.unstash() + + def __exitPublic(self, mouseEvent): + self.publicDescriptionLabel.stash() + + def __doTogglePublicPrivate(self): + if self.isPrivate: + self.isPrivate = False + self.privateButton['state'] = DirectGuiGlobals.NORMAL + self.publicButton['state'] = DirectGuiGlobals.DISABLED + else: + self.isPrivate = True + self.privateButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton['state'] = DirectGuiGlobals.NORMAL + + def _createPartyEditorPage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerEditorPage') + self.LayoutTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerEditorTitle, pos=self.gui.find('**/title_locator').getPos() + Point3(0.0, 0.0, 0.075), scale=self.titleScale) + self.costLabel = DirectLabel(parent=page, pos=(-0.74, 0.0, 0.17), relief=None, text=TTLocalizer.PartyPlannerTotalCost % 0, text_align=TextNode.ACenter, scale=TTLocalizer.PPcostLabel, textMayChange=True) + self.partyGridBackground = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/partyGrid_flat')) + self.partyGroundsLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerPartyGrounds, text_font=ToontownGlobals.getSignFont(), text_fg=VBase4(1.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.PPpartyGroundsLabel, pos=self.gui.find('**/step_05_partyGrounds_text_locator').getPos(), scale=0.1) + self.activityBackground = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/activitiesDecorations_flat1'), pos=(0.0, 0.0, 0.04)) + pos = self.gui.find('**/step_05_instructions_locator').getPos() + self.instructionLabel = DirectLabel(parent=page, relief=None, text=' ', text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPinstructionLabel, textMayChange=True, geom=self.gui.find('**/instructions_flat')) + self.elementTitleLabel = DirectLabel(parent=page, relief=None, text=' ', pos=self.gui.find('**/step_05_activitiesName_text_locator').getPos() + Point3(0.0, 0.0, 0.04), text_scale=TTLocalizer.PPelementTitleLabel, textMayChange=True) + self.elementPriceNode = TextNode('ElementPrice') + self.elementPriceNode.setAlign(TextNode.ALeft) + self.elementPriceNode.setTextColor(0.0, 0.0, 0.0, 1.0) + self.elementPriceNode.setFont(ToontownGlobals.getToonFont()) + self.elementPrice = page.attachNewNode(self.elementPriceNode) + self.elementPrice.setScale(TTLocalizer.PPelementPriceNode) + self.elementPrice.setPos(self.gui.find('**/step_05_activityPrice_text_locator').getPos() + Point3(-0.02, 0.0, 0.04)) + self.elementDescriptionNode = TextNode('ElementDescription') + self.elementDescriptionNode.setAlign(TextNode.ACenter) + self.elementDescriptionNode.setWordwrap(8) + self.elementDescriptionNode.setFont(ToontownGlobals.getToonFont()) + self.elementDescriptionNode.setTextColor(0.0, 0.0, 0.0, 1.0) + self.elementDescription = page.attachNewNode(self.elementDescriptionNode) + self.elementDescription.setScale(TTLocalizer.PPelementDescription) + self.elementDescription.setPos(self.gui.find('**/step_05_activityDescription_text_locator').getPos() + Point3(0.0, 0.0, 0.04)) + self.totalMoney = base.localAvatar.getTotalMoney() + catalogGui = loader.loadModel('phase_5.5/models/gui/catalog_gui') + self.beanBank = DirectLabel(parent=page, relief=None, text=str(self.totalMoney), text_align=TextNode.ARight, text_scale=0.075, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.495, -0.53), text_font=ToontownGlobals.getSignFont(), textMayChange=True, image=catalogGui.find('**/bean_bank'), image_scale=(0.65, 0.65, 0.65), scale=0.9, pos=(-0.75, 0.0, 0.6)) + catalogGui.removeNode() + del catalogGui + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__moneyChange) + self.partyEditor = PartyEditor(self, page) + self.partyEditor.request('Hidden') + pos = self.gui.find('**/step_05_add_text_locator').getPos() + self.elementBuyButton = DirectButton(parent=page, relief=None, text=TTLocalizer.PartyPlannerBuy, text_pos=(pos[0], pos[2]), text_scale=TTLocalizer.PPelementBuyButton, geom=(self.gui.find('**/add_up'), self.gui.find('**/add_down'), self.gui.find('**/add_rollover')), geom3_color=VBase4(0.5, 0.5, 0.5, 1.0), textMayChange=True, pos=(0.0, 0.0, 0.04), command=self.partyEditor.buyCurrentElement) + self.okWithPartyGroundsLayoutEvent = 'okWithPartyGroundsLayoutEvent' + self.accept(self.okWithPartyGroundsLayoutEvent, self.okWithPartyGroundsLayout) + self.okWithGroundsGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('PartyEditorOkGui'), doneEvent=self.okWithPartyGroundsLayoutEvent, message=TTLocalizer.PartyPlannerOkWithGroundsLayout, style=TTDialog.YesNo, okButtonText=OTPLocalizer.DialogYes, cancelButtonText=OTPLocalizer.DialogNo) + self.okWithGroundsGui.doneStatus = '' + self.okWithGroundsGui.hide() + return page + + def okWithPartyGroundsLayout(self): + self.okWithGroundsGui.hide() + if self.okWithGroundsGui.doneStatus == 'ok': + self.__nextItem() + + def setNextButtonState(self, enabled): + if enabled: + self.nextButton['state'] = DirectGuiGlobals.NORMAL + self.nextButton.show() + else: + self.nextButton['state'] = DirectGuiGlobals.DISABLED + self.nextButton.hide() + + def _createInvitationPage(self): + self.__handleHolidays() + page = DirectFrame(self.frame) + page.setName('PartyPlannerInvitationPage') + self.invitationTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerConfirmTitle, textMayChange=True, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + self.invitationBackground = DirectFrame(parent=page, relief=None, geom=self.gui.find('**/invitationBackground')) + self.inviteVisual = InviteVisual(page) + self.selectedInviteThemeLabel = DirectLabel(parent=page, relief=None, pos=self.gui.find('**/step_06_theme_locator').getPos(), text='', text_scale=0.06, textMayChange=True) + self.nextThemeButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/topNext_button/nextButton_up'), self.gui.find('**/topNext_button/nextButton_down'), self.gui.find('**/topNext_button/nextButton_rollover')), command=self.__nextTheme) + self.prevThemeButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/topPrevious_button/previousButton_up'), self.gui.find('**/topPrevious_button/previousButton_down'), self.gui.find('**/topPrevious_button/previousButton_rollover')), command=self.__prevTheme) + pos = self.gui.find('**/step_06_sendInvitation_locator').getPos() + self.inviteButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/send_up'), self.gui.find('**/send_down'), self.gui.find('**/send_rollover')), text=TTLocalizer.PartyPlannerInviteButton, textMayChange=True, text_scale=0.05, text_pos=(pos[0], pos[2]), command=self.__handleComplete) + return page + + def __handleHolidays(self): + self.inviteThemes = range(len(PartyGlobals.InviteTheme)) + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.VALENTINES_DAY not in holidayIds: + self.inviteThemes.remove(PartyGlobals.InviteTheme.Valentoons) + if ToontownGlobals.VICTORY_PARTY_HOLIDAY not in holidayIds: + self.inviteThemes.remove(PartyGlobals.InviteTheme.VictoryParty) + if ToontownGlobals.WINTER_DECORATIONS not in holidayIds and ToontownGlobals.WACKY_WINTER_DECORATIONS not in holidayIds: + self.inviteThemes.remove(PartyGlobals.InviteTheme.Winter) + + def _createFarewellPage(self): + page = DirectFrame(self.frame) + page.setName('PartyPlannerFarewellPage') + self.confirmTitleLabel = DirectLabel(parent=page, relief=None, text=TTLocalizer.PartyPlannerConfirmationAllOkTitle, textMayChange=True, pos=self.gui.find('**/title_locator').getPos(), scale=self.titleScale) + pos = self.gui.find('**/step_07_close_text_locator').getPos() + self.closePlannerButton = DirectButton(parent=page, relief=None, geom=(self.gui.find('**/close_up'), self.gui.find('**/close_down'), self.gui.find('**/close_rollover')), text=TTLocalizer.PartyPlannerClosePlanner, text_scale=0.055, text_pos=(pos[0], pos[2]), command=self.__acceptExit) + return page + + def close(self): + self.ignore('addPartyResponseReceived') + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.timeInputHourUpButton.destroy() + self.timeInputHourDownButton.destroy() + self.timeInputMinuteUpButton.destroy() + self.timeInputMinuteDownButton.destroy() + self.timeInputAmPmUpButton.destroy() + self.timeInputAmPmDownButton.destroy() + self.privateButton.destroy() + self.publicButton.destroy() + self.makePartyNowButton.destroy() + self.checkAllButton.destroy() + self.uncheckAllButton.destroy() + self.elementBuyButton.destroy() + self.nextThemeButton.destroy() + self.prevThemeButton.destroy() + self.inviteButton.destroy() + self.closePlannerButton.destroy() + self.ignore(self.okWithPartyGroundsLayoutEvent) + if hasattr(self, 'okWithGroundsGui'): + self.okWithGroundsGui.cleanup() + del self.okWithGroundsGui + if hasattr(self, 'frame') and not self.frame.isEmpty(): + messenger.send(self.doneEvent) + self.hide() + self.cleanup() + self.friendList.removeAndDestroyAllItems() + self.friendList.destroy() + self.calendarGuiMonth.destroy() + self.frame.destroy() + self.partyPlannerHead.delete() + self.partyPlannerHead.removeNode() + self.clearNametag() + self.partyEditor.request('Cleanup') + self.partyEditor = None + self.destroy() + del self + return + + def __handleComplete(self): + self.inviteButton['state'] = DirectGuiGlobals.DISABLED + self.prevButton['state'] = DirectGuiGlobals.DISABLED + endTime = self.partyTime + self.partyDuration + hostId = base.localAvatar.doId + self.partyActivities = self.partyEditor.partyEditorGrid.getActivitiesOnGrid() + decorations = self.partyEditor.partyEditorGrid.getDecorationsOnGrid() + invitees = self.getInvitees() + self.accept('addPartyResponseReceived', self.processAddPartyResponse) + base.cr.partyManager.sendAddParty(hostId, self.partyTime.strftime('%Y-%m-%d %H:%M:%S'), endTime.strftime('%Y-%m-%d %H:%M:%S'), self.isPrivate, self.currentInvitationTheme, self.partyActivities, decorations, invitees) + + def getInvitees(self): + invitees = [] + for friendBox in self.friendList['items']: + if friendBox['indicatorValue']: + invitees.append(friendBox.getPythonTag('id')) + + return invitees + + def processAddPartyResponse(self, hostId, errorCode): + PartyPlanner.notify.debug('processAddPartyResponse : hostId=%d errorCode=%s' % (hostId, PartyGlobals.AddPartyErrorCode.getString(errorCode))) + goingBackAllowed = False + if errorCode == PartyGlobals.AddPartyErrorCode.AllOk: + goingBackAllowed = False + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationAllOkTitle + if self.noFriends or len(self.getInvitees()) == 0: + confirmRecapText = TTLocalizer.PartyPlannerConfirmationAllOkTextNoFriends + else: + confirmRecapText = TTLocalizer.PartyPlannerConfirmationAllOkText + elif errorCode == PartyGlobals.AddPartyErrorCode.ValidationError: + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationErrorTitle + confirmRecapText = TTLocalizer.PartyPlannerConfirmationValidationErrorText + elif errorCode == PartyGlobals.AddPartyErrorCode.DatabaseError: + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationErrorTitle + confirmRecapText = TTLocalizer.PartyPlannerConfirmationDatabaseErrorText + elif errorCode == PartyGlobals.AddPartyErrorCode.TooManyHostedParties: + goingBackAllowed = False + self.confirmTitleLabel['text'] = TTLocalizer.PartyPlannerConfirmationErrorTitle + confirmRecapText = TTLocalizer.PartyPlannerConfirmationTooManyText + self.nametagGroup.setChat(confirmRecapText, CFSpeech) + self.request('Farewell', goingBackAllowed) + + def __acceptExit(self): + PartyPlanner.notify.debug('__acceptExit') + if hasattr(self, 'frame'): + self.hide() + messenger.send(self.doneEvent) + + def __nextItem(self): + messenger.send('wakeup') + if self.state == 'PartyEditor' and self.okWithGroundsGui.doneStatus != 'ok': + self.okWithGroundsGui.show() + return + if self.state == 'PartyEditor' and self.noFriends: + self.request('Date') + self.selectedCalendarGuiDay = None + self.calendarGuiMonth.clearSelectedDay() + return + if self.state == 'Guests': + self.selectedCalendarGuiDay = None + self.calendarGuiMonth.clearSelectedDay() + if self.state == 'Time': + if self.partyTime < base.cr.toontownTimeManager.getCurServerDateTime(): + self.okChooseFutureTimeEvent = 'okChooseFutureTimeEvent' + self.acceptOnce(self.okChooseFutureTimeEvent, self.okChooseFutureTime) + self.chooseFutureTimeDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('chooseFutureTimeDialog'), doneEvent=self.okChooseFutureTimeEvent, message=TTLocalizer.PartyPlannerChooseFutureTime, style=TTDialog.Acknowledge) + self.chooseFutureTimeDialog.show() + return + self.requestNext() + return + + def okChooseFutureTime(self): + if hasattr(self, 'chooseFutureTimeDialog'): + self.chooseFutureTimeDialog.cleanup() + del self.chooseFutureTimeDialog + if hasattr(self, 'okChooseFutureTimeEvent'): + self.ignore(self.okChooseFutureTimeEvent) + + def __prevItem(self): + messenger.send('wakeup') + if self.state == 'Date' and self.noFriends: + self.request('PartyEditor') + return + if self.state == 'Invitation' and self.selectedCalendarGuiDay is None: + self.request('Guests') + return + self.requestPrev() + return + + def __moneyChange(self, newMoney): + if hasattr(self, 'totalMoney'): + self.totalMoney = base.localAvatar.getTotalMoney() + if hasattr(self, 'beanBank'): + self.beanBank['text'] = str(int(self.totalMoney)) diff --git a/toontown/parties/PartyReplyInfo.py b/toontown/parties/PartyReplyInfo.py new file mode 100644 index 0000000..c30355d --- /dev/null +++ b/toontown/parties/PartyReplyInfo.py @@ -0,0 +1,23 @@ + + +class SingleReply: + + def __init__(self, inviteeId, status): + self.inviteeId = inviteeId + self.status = status + + +class PartyReplyInfoBase: + + def __init__(self, partyId, partyReplies): + self.partyId = partyId + self.replies = [] + for oneReply in partyReplies: + self.replies.append(SingleReply(*oneReply)) + + def __str__(self): + string = 'partyId=%d ' % self.partyId + for reply in self.replies: + string += '(%d:%d) ' % (reply.inviteeId, reply.status) + + return string diff --git a/toontown/parties/PartyUtils.py b/toontown/parties/PartyUtils.py new file mode 100644 index 0000000..ad9331a --- /dev/null +++ b/toontown/parties/PartyUtils.py @@ -0,0 +1,207 @@ +import math +import time +import datetime +from direct.directnotify import DirectNotifyGlobal +from direct.interval.LerpInterval import LerpFunc +from pandac.PandaModules import Vec3 +from toontown.toonbase import TTLocalizer +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.parties import PartyGlobals +notify = DirectNotifyGlobal.directNotify.newCategory('PartyUtils') + +def getNewToontownTimer(): + timer = ToontownTimer() + timer.hide() + timer.posInTopRightCorner() + timer.setColor(1, 1, 1, 0.75) + return timer + + +def getPartyActivityIcon(activityIconsModel, activityName): + activityIconsDict = {'PartyValentineDance': 'tt_t_ara_pty_iconDanceFloorValentine', + 'PartyValentineDance20': 'tt_t_ara_pty_iconDanceFloorValentine', + 'PartyValentineJukebox': 'tt_t_ara_pty_iconJukeboxValentine', + 'PartyValentineJukebox40': 'tt_t_ara_pty_iconJukeboxValentine', + 'PartyValentineTrampoline': 'tt_t_ara_pty_iconTrampolineValentine'} + iconName = activityIconsDict.get(activityName) + if iconName: + icon = activityIconsModel.find('**/%s' % iconName) + else: + icon = activityIconsModel.find('**/%sIcon' % activityName) + if icon.isEmpty(): + icon = activityIconsModel.find('**/PartyClockIcon') + notify.warning("Couldn't find %sIcon in %s, using PartyClockIcon" % (activityName, activityIconsModel.getName())) + return icon + + +def arcPosInterval(duration, object, pos, arcHeight, other): + startPos = object.getPos() + endPos = object.getParent().getRelativePoint(other, pos) + startX = startPos.getX() + startY = startPos.getY() + startZ = startPos.getZ() + dx = endPos.getX() - startPos.getX() + dy = endPos.getY() - startPos.getY() + dz = endPos.getZ() - startPos.getZ() + + def setArcPos(t): + newX = startX + dx * t + newY = startY + dy * t + newZ = startZ + dz * t + arcHeight * (-(2.0 * t - 1.0) ** 2 + 1.0) + object.setPos(newX, newY, newZ) + + return LerpFunc(setArcPos, duration=duration) + + +def formatDate(year, month, day): + monthString = TTLocalizer.DateOfBirthEntryMonths[month - 1] + return TTLocalizer.PartyDateFormat % {'mm': monthString, + 'dd': day, + 'yyyy': year} + + +def truncateTextOfLabelBasedOnWidth(directGuiObject, textToTruncate, maxWidth): + text0 = directGuiObject.component('text0') + tempNode = text0.textNode + currentText = textToTruncate[:] + scale = text0.getScale()[0] + width = tempNode.calcWidth(currentText) * scale + while width > maxWidth: + currentText = currentText[:-1] + width = tempNode.calcWidth(currentText) * scale + + directGuiObject['text'] = currentText + if directGuiObject['text'] != textToTruncate: + directGuiObject['text'] = '%s...' % directGuiObject['text'] + + +def truncateTextOfLabelBasedOnMaxLetters(directGuiObject, textToTruncate, maxLetters): + curStr = directGuiObject['text'] + if maxLetters < len(curStr): + curStr = curStr[:maxLetters] + curStr += '...' + directGuiObject['text'] = curStr + + +def scaleTextOfGuiObjectBasedOnWidth(directGuiObject, textToScale, maxWidth): + width = directGuiObject.getWidth() + scale = 0.01 + while width > maxWidth: + directGuiObject['text_scale'] = scale + directGuiObject.resetFrameSize() + width = directGuiObject.getWidth() + scale += 0.005 + + +def formatTime(hour, minute): + meridiemString = TTLocalizer.PartyTimeFormatMeridiemAM + if hour == 0: + hour = 12 + elif hour > 11: + meridiemString = TTLocalizer.PartyTimeFormatMeridiemPM + if hour > 12: + hour -= 12 + return TTLocalizer.PartyTimeFormat % (hour, minute, meridiemString) + + +SecondsInOneDay = 60 * 60 * 24 + +def getTimeDeltaInSeconds(td): + result = td.days * SecondsInOneDay + td.seconds + td.microseconds / 1000000.0 + return result + + +def formatDateTime(dateTimeToShow, inLocalTime = False): + if inLocalTime: + curServerTime = base.cr.toontownTimeManager.getCurServerDateTime() + ltime = time.localtime() + localTime = datetime.datetime(year=ltime.tm_year, month=ltime.tm_mon, day=ltime.tm_mday, hour=ltime.tm_hour, minute=ltime.tm_min, second=ltime.tm_sec) + naiveServerTime = curServerTime.replace(tzinfo=None) + newTimeDelta = localTime - naiveServerTime + localDifference = getTimeDeltaInSeconds(newTimeDelta) + dateTimeToShow = dateTimeToShow + datetime.timedelta(seconds=localDifference) + return '%s %s' % (formatDate(dateTimeToShow.year, dateTimeToShow.month, dateTimeToShow.day), formatTime(dateTimeToShow.hour, dateTimeToShow.minute)) + else: + return '%s %s' % (formatDate(dateTimeToShow.year, dateTimeToShow.month, dateTimeToShow.day), formatTime(dateTimeToShow.hour, dateTimeToShow.minute)) + return + + +def convertDistanceToPartyGrid(d, index): + return int((d - PartyGlobals.PartyGridToPandaOffset[index]) / PartyGlobals.PartyGridUnitLength[index]) + + +def convertDistanceFromPartyGrid(d, index): + return d * PartyGlobals.PartyGridUnitLength[index] + PartyGlobals.PartyGridToPandaOffset[index] + PartyGlobals.PartyGridUnitLength[index] / 2.0 + + +def convertDegreesToPartyGrid(h): + while h < 0.0: + h = h + 360.0 + + h = h % 360.0 + return int(h / PartyGlobals.PartyGridHeadingConverter) + + +def convertDegreesFromPartyGrid(h): + return h * PartyGlobals.PartyGridHeadingConverter + + +def getCenterPosFromGridSize(x, y, gridsize): + if gridsize[0] % 2 == 0: + xMod = PartyGlobals.PartyGridUnitLength[0] / 2.0 + else: + xMod = 0 + if gridsize[1] % 2 == 0: + yMod = PartyGlobals.PartyGridUnitLength[1] / 2.0 + else: + yMod = 0 + return (x + xMod, y + yMod) + + +def toRadians(angle): + return angle * math.pi / 180.0 + + +def toDegrees(angle): + return angle * 180.0 / math.pi + + +def calcVelocity(rotation, angle, initialVelocity = 1.0): + horizVel = initialVelocity * math.cos(angle) + xVel = horizVel * -math.sin(rotation) + yVel = horizVel * math.cos(rotation) + zVel = initialVelocity * math.sin(angle) + return Vec3(xVel, yVel, zVel) + + +class LineSegment: + + def __init__(self, pt1, pt2): + self.pt1 = pt1 + self.pt2 = pt2 + + def isIntersecting(self, line, compare = None): + x1 = self.pt1.getX() + x2 = self.pt2.getX() + x3 = line.pt1.getX() + x4 = line.pt2.getX() + y1 = self.pt1.getY() + y2 = self.pt2.getY() + y3 = line.pt1.getY() + y4 = line.pt2.getY() + top1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) + top2 = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) + bot = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) + if bot == 0.0: + return False + u1 = top1 / bot + u2 = top2 / bot + if compare is None: + return 0 <= u1 and u1 <= 1 and 0 <= u2 and u2 <= 1 + elif compare == 'segment-ray': + return 0 <= u1 and u1 <= 1 and 0 <= u2 + elif compare == 'ray-ray': + return 0 <= u1 and 0 <= u2 + elif compare == 'ray-segment': + return 0 <= u1 and 0 <= u2 and u2 <= 1 + return diff --git a/toontown/parties/PublicPartyGui.py b/toontown/parties/PublicPartyGui.py new file mode 100644 index 0000000..f5ba382 --- /dev/null +++ b/toontown/parties/PublicPartyGui.py @@ -0,0 +1,251 @@ +from pandac.PandaModules import Vec3, Vec4, Point3, TextNode, VBase4 +from direct.gui.DirectGui import DGG, DirectFrame, DirectButton, DirectLabel, DirectScrolledList, DirectCheckButton +from direct.gui import DirectGuiGlobals +from direct.showbase import DirectObject +from direct.showbase import PythonUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.parties import PartyGlobals +from toontown.parties import PartyUtils + +class PublicPartyGui(DirectFrame): + notify = directNotify.newCategory('PublicPartyGui') + + def __init__(self, doneEvent): + DirectFrame.__init__(self) + self.doneEvent = doneEvent + self.gui = loader.loadModel('phase_4/models/parties/publicPartyGUI') + self.setPos(0.1, 0.0, 0.1) + self.doneStatus = None + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + self.normalFrameColor = Vec4(130 / 255.0, 174 / 255.0, 249 / 255.0, 1.0) + self.selectedFrameColor = Vec4(1.0, 1.0, 0.0, 1.0) + self.load() + self.gui.removeNode() + self.accept('stoppedAsleep', self._close) + return + + def load(self): + for backgroundName in ['background', 'parties_background', 'activities_background']: + background = DirectFrame(parent=self, geom=self.gui.find('**/%s' % backgroundName), relief=None) + + self.titleLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.PartyGateTitle, pos=self.gui.find('**/title_locator').getPos(), scale=0.1) + self.partyList, self.partyListLabel = self.createPartyListAndLabel('parties', 14) + self.activityList, self.activityListLabel = self.createListAndLabel('activities', 1) + pos = self.gui.find('**/startText_locator').getPos() + self.partyStartButton = DirectButton(parent=self, relief=None, text=TTLocalizer.PartyGateGoToParty, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPGpartyStartButton, text_pos=(pos[0], pos[2]), geom=(self.gui.find('**/startButton_up'), + self.gui.find('**/startButton_down'), + self.gui.find('**/startButton_rollover'), + self.gui.find('**/startButton_inactive')), command=self._startParty) + self.closeButton = DirectButton(parent=self, relief=None, geom=(self.gui.find('**/cancelButton_up'), self.gui.find('**/cancelButton_down'), self.gui.find('**/cancelButton_rollover')), command=self._close) + instructionPos = (0, -0.9) + if not self.gui.find('**/helpText_locator').isEmpty(): + tempPos = self.gui.find('**/helpText_locator').getPos() + instructionPos = (tempPos.getX(), tempPos.getZ()) + self.instructionsLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.PartyGateInstructions, text_align=TextNode.ACenter, text_scale=TTLocalizer.PPGinstructionsLabel, text_pos=instructionPos) + return + + def createListAndLabel(self, typeString, numItems): + list = DirectScrolledList(parent=self, relief=None, incButton_image=(self.gui.find('**/%sButtonDown_up' % typeString), + self.gui.find('**/%sButtonDown_down' % typeString), + self.gui.find('**/%sButtonDown_rollover' % typeString), + self.gui.find('**/%sButtonDown_inactive' % typeString)), incButton_relief=None, decButton_image=(self.gui.find('**/%sButtonUp_up' % typeString), + self.gui.find('**/%sButtonUp_down' % typeString), + self.gui.find('**/%sButtonUp_rollover' % typeString), + self.gui.find('**/%sButtonUp_inactive' % typeString)), decButton_relief=None, itemFrame_pos=self.gui.find('**/%s_locator' % typeString).getPos(), itemFrame_relief=None, numItemsVisible=numItems, forceHeight=0.055) + strings = {'activities': TTLocalizer.EventsPageHostingTabActivityListTitle, + 'parties': TTLocalizer.PartyGatePartiesListTitle} + label = DirectLabel(parent=self, relief=None, text=strings[typeString], text_scale=0.06, pos=self.gui.find('**/%sText_locator' % typeString).getPos()) + return (list, label) + + def refresh(self, partyInfoTupleList): + PublicPartyGui.notify.debug('refresh : partyInfoTupleList = %s' % partyInfoTupleList) + self.selectedItem = None + self.partyList.removeAndDestroyAllItems() + self.activityList.removeAndDestroyAllItems() + self.partyStartButton['state'] = DirectGuiGlobals.DISABLED + sortedList = partyInfoTupleList[:] + + def cmp(left, right): + if left[2] < right[2]: + return -1 + elif left[2] == right[2]: + if len(left[4]) < len(right[4]): + return -1 + elif len(left[4]) == len(right[4]): + return 0 + else: + return 1 + else: + return 1 + + sortedList.sort(cmp, reverse=True) + indexToCut = -1 + for index, partyTuple in enumerate(sortedList): + numberOfGuests = partyTuple[2] + if numberOfGuests < PartyGlobals.MaxToonsAtAParty: + indexToCut = index + break + + if indexToCut > 0: + sortedList = sortedList[indexToCut:] + sortedList[:indexToCut] + for index, partyTuple in enumerate(sortedList): + shardId = partyTuple[0] + zoneId = partyTuple[1] + numberOfGuests = partyTuple[2] + hostName = partyTuple[3] + activityIds = partyTuple[4] + minLeft = partyTuple[5] + item = DirectButton(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(-0.01, + 0.45, + -0.015, + 0.04), frameColor=self.normalFrameColor, text=hostName, text_align=TextNode.ALeft, text_bg=Vec4(0.0, 0.0, 0.0, 0.0), text_scale=0.045, command=self.partyClicked) + otherInfoWidth = 0.08 + numActivities = len(activityIds) + PartyUtils.truncateTextOfLabelBasedOnWidth(item, hostName, PartyGlobals.EventsPageGuestNameMaxWidth) + num = DirectLabel(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(0.0, + otherInfoWidth, + -0.015, + 0.04), frameColor=self.normalFrameColor, text='%d' % numberOfGuests, text_align=TextNode.ALeft, text_scale=0.045, text_pos=(0.01, 0, 0), pos=(0.45, 0.0, 0.0)) + num.reparentTo(item) + item.numLabel = num + actLabelPos = num.getPos() + actLabelPos.setX(actLabelPos.getX() + otherInfoWidth) + actLabel = DirectLabel(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(0.0, + otherInfoWidth, + -0.015, + 0.04), frameColor=self.normalFrameColor, text='%d' % numActivities, text_align=TextNode.ALeft, text_scale=0.045, text_pos=(0.01, 0, 0), pos=actLabelPos) + actLabel.reparentTo(item) + item.actLabel = actLabel + minLabelPos = actLabel.getPos() + minLabelPos.setX(minLabelPos.getX() + otherInfoWidth) + minLabel = DirectLabel(relief=DGG.RIDGE, borderWidth=(0.01, 0.01), frameSize=(0.0, + otherInfoWidth, + -0.015, + 0.04), frameColor=self.normalFrameColor, text='%d' % minLeft, text_align=TextNode.ALeft, text_scale=0.045, text_pos=(0.01, 0, 0), pos=minLabelPos) + minLabel.reparentTo(item) + item.minLabel = minLabel + item['extraArgs'] = [item] + item.setPythonTag('shardId', shardId) + item.setPythonTag('zoneId', zoneId) + item.setPythonTag('activityIds', activityIds) + self.partyList.addItem(item) + + return + + def partyClicked(self, item): + self.partyStartButton['state'] = DirectGuiGlobals.NORMAL + if self.selectedItem is not None: + self.selectedItem['state'] = DirectGuiGlobals.NORMAL + self.selectedItem['frameColor'] = self.normalFrameColor + numLabel = self.selectedItem.numLabel + if not numLabel.isEmpty(): + numLabel['frameColor'] = self.normalFrameColor + actLabel = self.selectedItem.actLabel + if not actLabel.isEmpty(): + actLabel['frameColor'] = self.normalFrameColor + minLabel = self.selectedItem.minLabel + if not minLabel.isEmpty(): + minLabel['frameColor'] = self.normalFrameColor + self.selectedItem = item + self.selectedItem['state'] = DirectGuiGlobals.DISABLED + self.selectedItem['frameColor'] = self.selectedFrameColor + numLabel = self.selectedItem.numLabel + if not numLabel.isEmpty(): + numLabel['frameColor'] = self.selectedFrameColor + actLabel = self.selectedItem.actLabel + if not actLabel.isEmpty(): + actLabel['frameColor'] = self.selectedFrameColor + minLabel = self.selectedItem.minLabel + if not minLabel.isEmpty(): + minLabel['frameColor'] = self.selectedFrameColor + self.fillActivityList(item.getPythonTag('activityIds')) + return + + def fillActivityList(self, activityIds): + self.activityList.removeAndDestroyAllItems() + sortedList = activityIds[:] + sortedList.sort() + lastActivityId = -1 + for activityId in sortedList: + if activityId == lastActivityId: + continue + lastActivityId = activityId + number = sortedList.count(activityId) + text = TTLocalizer.PartyActivityNameDict[activityId]['generic'] + if number > 1: + text += ' X %d' % number + item = DirectLabel(relief=None, text=text, text_align=TextNode.ACenter, text_scale=0.05, text_pos=(0.0, -0.15), geom_scale=0.3, geom_pos=Vec3(0.0, 0.0, 0.07), geom=PartyUtils.getPartyActivityIcon(self.activityIconsModel, PartyGlobals.ActivityIds.getString(activityId))) + self.activityList.addItem(item) + + return + + def _startParty(self): + if self.selectedItem is None: + self.partyStartButton['state'] = DirectGuiGlobals.DISABLED + return + self.doneStatus = (self.selectedItem.getPythonTag('shardId'), self.selectedItem.getPythonTag('zoneId')) + messenger.send(self.doneEvent) + return + + def _close(self): + self.doneStatus = None + messenger.send(self.doneEvent) + return + + def destroy(self): + self.activityIconsModel.removeNode() + del self.activityIconsModel + self.partyList.removeAndDestroyAllItems() + try: + for item in self.partyList['items']: + item.actLabel = None + item.numLabel = None + item.minLabel = None + + except: + pass + + self.activityList.removeAndDestroyAllItems() + del self.partyList + del self.activityList + self.ignoreAll() + DirectFrame.destroy(self) + return + + def createPartyListAndLabel(self, typeString, numItems): + list = DirectScrolledList(parent=self, relief=None, incButton_image=(self.gui.find('**/%sButtonDown_up' % typeString), + self.gui.find('**/%sButtonDown_down' % typeString), + self.gui.find('**/%sButtonDown_rollover' % typeString), + self.gui.find('**/%sButtonDown_inactive' % typeString)), incButton_relief=None, decButton_image=(self.gui.find('**/%sButtonUp_up' % typeString), + self.gui.find('**/%sButtonUp_down' % typeString), + self.gui.find('**/%sButtonUp_rollover' % typeString), + self.gui.find('**/%sButtonUp_inactive' % typeString)), decButton_relief=None, itemFrame_pos=self.gui.find('**/%s_locator' % typeString).getPos(), itemFrame_relief=None, numItemsVisible=numItems, forceHeight=0.055) + strings = {'activities': TTLocalizer.EventsPageHostingTabActivityListTitle, + 'parties': TTLocalizer.PartyGatePartiesListTitle} + hostPos = self.gui.find('**/%sText_locator' % typeString).getPos() + label = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=strings[typeString], text_scale=0.06, pos=hostPos) + curPos = label.getPos() + curPos.setX(curPos.getX() + 0.5) + if not self.gui.find('**/partiesText_locator1').isEmpty(): + curPos = self.gui.find('**/partiesText_locator1').getPos() + hpr = Point3(0, 0, -40) + toonsLabel = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=TTLocalizer.PartyGatesPartiesListToons, text_scale=TTLocalizer.PPGtoonsLabel, pos=curPos, hpr=hpr) + curPos.setX(curPos.getX() + 0.1) + if not self.gui.find('**/partiesText_locator2').isEmpty(): + curPos = self.gui.find('**/partiesText_locator2').getPos() + activitiesLabel = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=TTLocalizer.PartyGatesPartiesListActivities, text_scale=TTLocalizer.PPGactivitiesLabel, pos=curPos, hpr=hpr) + curPos.setX(curPos.getX() + 0.1) + if not self.gui.find('**/partiesText_locator3').isEmpty(): + curPos = self.gui.find('**/partiesText_locator3').getPos() + minLeftLabel = DirectLabel(parent=self, text_align=TextNode.ALeft, relief=None, text=TTLocalizer.PartyGatesPartiesListMinLeft, text_scale=TTLocalizer.PPGminLeftLabel, pos=curPos, hpr=hpr) + return (list, label) + + def stash(self): + base.setCellsAvailable(base.bottomCells, 1) + DirectFrame.stash(self) + + def unstash(self): + base.setCellsAvailable(base.bottomCells, 0) + DirectFrame.unstash(self) diff --git a/toontown/parties/ScrolledFriendList.py b/toontown/parties/ScrolledFriendList.py new file mode 100644 index 0000000..fd3f9e0 --- /dev/null +++ b/toontown/parties/ScrolledFriendList.py @@ -0,0 +1,35 @@ +from direct.gui.DirectGui import DirectFrame, DirectButton, DirectLabel +from direct.gui.DirectGui import DirectScrolledList, DirectCheckButton +from direct.gui.DirectCheckBox import DirectCheckBox +from direct.gui import DirectGuiGlobals +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import Vec3, Vec4, PlaneNode, Plane, Point3, TextNode, VBase4, NodePath + +class ScrolledFriendList(DirectScrolledList): + + def __init__(self, parent, gui, clickCallback = None, makeItemsCheckBoxes = False): + self.makeItemsCheckBoxes = makeItemsCheckBoxes + self.clickCallback = clickCallback + self.parent = parent + self.gui = gui + self.scrollSpeed = 1 + DirectScrolledList.__init__(self, parent=parent, relief=None, incButton_image=(self.gui.find('**/inviteButtonDown_up'), self.gui.find('**/inviteButtonDown_down'), self.gui.find('**/inviteButtonDown_rollover')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.03), incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), decButton_image=(self.gui.find('**/inviteButtonUp_up'), self.gui.find('**/inviteButtonUp_down'), self.gui.find('**/inviteButtonUp_rollover')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.02), decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_relief=None, forceHeight=0.084, numItemsVisible=8, items=[], incButtonCallback=self.scrollButtonPressed, decButtonCallback=self.scrollButtonPressed, itemFrame_pos=(0.0, 0.0, -0.01)) + self.incButtonCallback = None + self.decButtonCallback = None + self.setForceHeight() + return + + def scrollButtonPressed(self): + pass + + def addFriend(self, name, id): + if self.makeItemsCheckBoxes: + checkedImage = self.gui.find('**/inviteButtonChecked') + uncheckedImage = self.gui.find('**/inviteButtonUnchecked') + widget = DirectCheckButton(relief=None, scale=0.1, boxBorder=0.08, boxImage=(uncheckedImage, checkedImage, None), boxImageScale=10.0, boxRelief=None, text=name, text_align=TextNode.ALeft, text_scale=0.7, text_pos=(-3.7, -0.25), command=self.clickCallback, indicator_pos=(-4.8, 0.0, 0.0)) + widget['extraArgs'] = [widget] + else: + widget = DirectLabel(relief=None, text=name, text_align=TextNode.ALeft, text_pos=(-0.6, 0.0, 0.0), scale=0.055) + widget.setPythonTag('id', id) + self.addItem(widget) + return diff --git a/toontown/parties/ServerTimeGui.py b/toontown/parties/ServerTimeGui.py new file mode 100644 index 0000000..c657157 --- /dev/null +++ b/toontown/parties/ServerTimeGui.py @@ -0,0 +1,45 @@ +from pandac.PandaModules import TextNode +from direct.gui.DirectGui import DirectFrame, DirectLabel +from direct.interval.IntervalGlobal import Func, Sequence, Wait +from toontown.toonbase import ToontownGlobals + +class ServerTimeGui(DirectFrame): + + def __init__(self, parent, pos = (0, 0, 0), hourCallback = None): + DirectFrame.__init__(self, parent=parent, pos=pos) + self.createGuiObjects() + self.hourCallback = hourCallback + self.lastHour = -1 + self.lastMinute = -1 + + def createGuiObjects(self): + textScale = 0.075 + timeFont = ToontownGlobals.getMinnieFont() + self.hourLabel = DirectLabel(parent=self, pos=(-0.015, 0, 0), relief=None, text='', text_scale=textScale, text_align=TextNode.ARight, text_font=timeFont) + self.colonLabel = DirectLabel(parent=self, relief=None, text=':', text_scale=textScale, text_align=TextNode.ACenter, text_font=timeFont) + self.minutesLabel = DirectLabel(relief=None, parent=self, pos=(0.015, 0, 0), text='', text_scale=textScale, text_align=TextNode.ALeft, text_font=timeFont) + self.amLabel = DirectLabel(relief=None, parent=self, pos=(0.14, 0, 0), text='', text_scale=textScale, text_align=TextNode.ALeft, text_font=timeFont) + self.ival = Sequence(Func(self.colonLabel.show), Wait(0.75), Func(self.colonLabel.hide), Wait(0.25), Func(self.updateTime)) + self.ival.loop() + return + + def destroy(self): + self.ival.finish() + self.ival = None + DirectFrame.destroy(self) + return + + def updateTime(self): + curServerDate = base.cr.toontownTimeManager.getCurServerDateTime() + if self.hourCallback is not None: + if curServerDate.hour != self.lastHour and self.lastHour != -1: + self.lastHour = curServerDate.hour + self.hourCallback(curServerDate.hour) + if not curServerDate.minute == self.lastMinute: + self.hourLabel['text'] = curServerDate.strftime('%I') + self.lastHour = curServerDate.hour + if self.hourLabel['text'][0] == '0': + self.hourLabel['text'] = self.hourLabel['text'][1:] + self.minutesLabel['text'] = curServerDate.strftime('%M') + self.amLabel['text'] = curServerDate.strftime('%p') + return diff --git a/toontown/parties/SimpleMailBase.py b/toontown/parties/SimpleMailBase.py new file mode 100644 index 0000000..1b19281 --- /dev/null +++ b/toontown/parties/SimpleMailBase.py @@ -0,0 +1,18 @@ + + +class SimpleMailBase: + + def __init__(self, msgId, senderId, year, month, day, body): + self.msgId = msgId + self.senderId = senderId + self.year = year + self.month = month + self.day = day + self.body = body + + def __str__(self): + string = 'msgId=%d ' % self.msgId + string += 'senderId=%d ' % self.senderId + string += 'sent=%s-%s-%s ' % (self.year, self.month, self.day) + string += 'body=%s' % self.body + return string diff --git a/toontown/parties/StretchingArrow.py b/toontown/parties/StretchingArrow.py new file mode 100644 index 0000000..172da01 --- /dev/null +++ b/toontown/parties/StretchingArrow.py @@ -0,0 +1,94 @@ +import math +from direct.gui.DirectGui import DirectFrame +from pandac.PandaModules import Point3 + +class StretchingArrow(DirectFrame): + notify = directNotify.newCategory('StretchingArrow') + arrowMoving = 0 + arrowBegin = 1 + arrowComplete = 2 + body = None + head = None + + def __init__(self, parent, useColor = 'blue', autoload = True): + DirectFrame.__init__(self, parent) + self.useColor = useColor + self.endOffset = 1.5 + self.startOffset = 0.0 + self.shrinkRange = 7.0 + self.ratioDrawn = 0.0 + if autoload: + self.load() + self.stash() + + def load(self): + model = loader.loadModel('phase_13/models/parties/stretchingArrow') + model.setP(-90) + self.body = model.find('**/arrowBody_' + self.useColor) + self.body.wrtReparentTo(self) + self.head = model.find('**/arrowHead_' + self.useColor) + self.head.wrtReparentTo(self) + model.removeNode() + + def unload(self): + if self.body is not None: + self.body.removeNode() + self.body = None + if self.head is not None: + self.body.removeNode() + self.body = None + return + + def reset(self): + self.ratioDrawn = 0.0 + + def draw(self, fromPoint, toPoint, rotation = 0, animate = True): + arrowlength = 2.72 + if self.body is None or self.head is None: + return + actualDifference = fromPoint - toPoint + actualLength = actualDifference.length() + oldRatio = self.ratioDrawn + drawSpeed = 1.6 + drawSpeedMin = 0.6 + downTime = 1.0 + fadeOutTime = 0.5 + drawRate = max(drawSpeedMin, drawSpeed * actualLength / arrowlength) + self.ratioDrawn += globalClock.getDt() / drawRate + result = StretchingArrow.arrowMoving + if self.ratioDrawn >= 1.0: + result = StretchingArrow.arrowComplete + self.ratioDrawn = -downTime + if cmp(oldRatio, 0) != cmp(self.ratioDrawn, 0) and result != StretchingArrow.arrowComplete: + result = StretchingArrow.arrowBegin + if not animate: + self.ratioDrawn = 1.0 + normal = Point3(actualDifference.getX(), actualDifference.getY(), actualDifference.getZ()) + normal.normalize() + rotation = math.degrees(math.atan2(actualDifference.getY(), actualDifference.getX())) + endPoint = toPoint + normal * self.endOffset + startPoint = fromPoint - normal * self.startOffset + newlength = (endPoint - startPoint).length() / arrowlength + newScale = min(actualLength / self.shrinkRange, 1.0) + self.head.setScale(newScale) + ratio = max(0.0, self.ratioDrawn) + if ratio == 0.0: + ratio = 1.0 + newlength *= ratio + if actualLength < self.endOffset: + self.stash() + else: + self.unstash() + self.body.setPos(startPoint) + self.body.setH(rotation) + self.head.setH(rotation - 90) + self.body.setScale(newlength - 0.013 * newScale, newScale, newScale) + vec = startPoint - endPoint + vec *= ratio + self.head.setPos(startPoint - vec) + self.head.setZ(render, self.body.getZ(render) + 0.001) + if self.ratioDrawn < 0.0: + self.setAlphaScale(abs(self.ratioDrawn) - (downTime - fadeOutTime)) + else: + self.setAlphaScale(1.0) + return diff --git a/toontown/parties/TeamActivityGui.py b/toontown/parties/TeamActivityGui.py new file mode 100644 index 0000000..0e31bc3 --- /dev/null +++ b/toontown/parties/TeamActivityGui.py @@ -0,0 +1,137 @@ +from pandac.PandaModules import TextNode +from direct.gui.DirectButton import DirectButton +from direct.gui.OnscreenText import OnscreenText +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.parties import PartyUtils +from toontown.parties import PartyGlobals + +class TeamActivityGui: + COUNTDOWN_TASK_NAME = 'updateCountdownTask' + timer = None + statusText = None + countdownText = None + exitButton = None + switchButton = None + + def __init__(self, activity): + self.activity = activity + + def load(self): + buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + upButton = buttonModels.find('**//InventoryButtonUp') + downButton = buttonModels.find('**/InventoryButtonDown') + rolloverButton = buttonModels.find('**/InventoryButtonRollover') + self.exitButton = DirectButton(relief=None, text=TTLocalizer.PartyTeamActivityExitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.15), text_scale=0.5, image=(upButton, downButton, rolloverButton), image_color=(1, 0, 0, 1), image_scale=(14.5, 1, 9), pos=(0, 0, 0.8), scale=0.15, command=self.handleExitButtonClick) + self.exitButton.hide() + if self.activity.toonsCanSwitchTeams(): + self.switchButton = DirectButton(relief=None, text=TTLocalizer.PartyTeamActivitySwitchTeamsButton, text_fg=(1, 1, 1, 1), text_pos=(0, 0.1), text_scale=0.5, image=(upButton, downButton, rolloverButton), image_color=(0, 1, 0, 1), image_scale=(15, 1, 15), pos=(0, 0, 0.5), scale=0.15, command=self.handleSwitchButtonClick) + self.switchButton.hide() + else: + self.switchButton = None + buttonModels.removeNode() + self.countdownText = OnscreenText(text='', pos=(0.0, -0.2), scale=PartyGlobals.TeamActivityTextScale * 1.2, fg=(1.0, 1.0, 0.65, 1.0), align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), mayChange=True) + self.countdownText.hide() + self.statusText = OnscreenText(text='', pos=(0.0, 0.0), scale=PartyGlobals.TeamActivityTextScale, fg=PartyGlobals.TeamActivityStatusColor, align=TextNode.ACenter, font=ToontownGlobals.getSignFont(), mayChange=True) + self.statusText.hide() + self.timer = PartyUtils.getNewToontownTimer() + self.timer.hide() + return + + def unload(self): + self.hideWaitToStartCountdown() + if self.exitButton is not None: + self.exitButton.destroy() + self.exitButton = None + if self.switchButton is not None: + self.switchButton.destroy() + self.switchButton = None + if self.countdownText is not None: + self.countdownText.destroy() + self.countdownText.removeNode() + self.countdownText = None + if self.statusText is not None: + self.statusText.destroy() + self.statusText.removeNode() + self.statusText = None + if self.timer is not None: + self.timer.destroy() + del self.timer + return + + def showStatus(self, text): + self.statusText.setText(text) + self.statusText.show() + + def hideStatus(self): + self.statusText.hide() + + def enableExitButton(self): + self.exitButton.show() + + def disableExitButton(self): + self.exitButton.hide() + + def handleExitButtonClick(self): + self.disableExitButton() + self.disableSwitchButton() + self.activity.d_toonExitRequest() + + def enableSwitchButton(self): + self.switchButton.show() + + def disableSwitchButton(self): + if self.switchButton is not None: + self.switchButton.hide() + return + + def handleSwitchButtonClick(self): + self.disableSwitchButton() + self.disableExitButton() + self.activity.d_toonSwitchTeamRequest() + + def showWaitToStartCountdown(self, duration, waitToStartTimestamp, almostDoneCallback = None): + self._countdownAlmostDoneCallback = almostDoneCallback + currentTime = globalClock.getRealTime() + waitTimeElapsed = currentTime - waitToStartTimestamp + if duration - waitTimeElapsed > 1.0: + countdownTask = Task(self._updateCountdownTask) + countdownTask.duration = duration - waitTimeElapsed + self.countdownText.setText(str(int(countdownTask.duration))) + self.countdownText.show() + taskMgr.remove(TeamActivityGui.COUNTDOWN_TASK_NAME) + taskMgr.add(countdownTask, TeamActivityGui.COUNTDOWN_TASK_NAME) + + def hideWaitToStartCountdown(self): + taskMgr.remove(TeamActivityGui.COUNTDOWN_TASK_NAME) + self._countdownAlmostDoneCallback = None + if self.countdownText is not None: + self.countdownText.hide() + return + + def _updateCountdownTask(self, task): + countdownTime = int(task.duration - task.time) + seconds = str(countdownTime) + if self.countdownText['text'] != seconds: + self.countdownText['text'] = seconds + if countdownTime == 3 and self._countdownAlmostDoneCallback is not None: + self._countdownAlmostDoneCallback() + self._countdownAlmostDoneCallback = None + if task.time >= task.duration: + return Task.done + else: + return Task.cont + return + + def showTimer(self, duration): + self.timer.setTime(duration) + self.timer.countdown(duration, self._handleTimerExpired) + self.timer.show() + + def hideTimer(self): + self.timer.hide() + self.timer.stop() + + def _handleTimerExpired(self): + self.activity.handleGameTimerExpired() diff --git a/toontown/parties/ToontownTimeManager.py b/toontown/parties/ToontownTimeManager.py new file mode 100644 index 0000000..55cecc1 --- /dev/null +++ b/toontown/parties/ToontownTimeManager.py @@ -0,0 +1,101 @@ +import time +from datetime import datetime, timedelta +import pytz +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer + +class ToontownTimeManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownTimeManager') + ClockFormat = '%I:%M:%S %p' + formatStr = '%Y-%m-%d %H:%M:%S' + + def __init__(self, serverTimeUponLogin = 0, clientTimeUponLogin = 0, globalClockRealTimeUponLogin = 0): + try: + self.serverTimeZoneString = base.config.GetString('server-timezone', TTLocalizer.TimeZone) + except: + try: + self.serverTimeZoneString = simbase.config.GetString('server-timezone', TTLocalizer.TimeZone) + except: + notify.error('ToontownTimeManager does not have access to base or simbase.') + + self.serverTimeZone = pytz.timezone(self.serverTimeZoneString) + self.updateLoginTimes(serverTimeUponLogin, clientTimeUponLogin, globalClockRealTimeUponLogin) + self.debugSecondsAdded = 0 + + def updateLoginTimes(self, serverTimeUponLogin, clientTimeUponLogin, globalClockRealTimeUponLogin): + self.serverTimeUponLogin = serverTimeUponLogin + self.clientTimeUponLogin = clientTimeUponLogin + self.globalClockRealTimeUponLogin = globalClockRealTimeUponLogin + naiveTime = datetime.utcfromtimestamp(self.serverTimeUponLogin) + self.utcServerDateTime = naiveTime.replace(tzinfo=pytz.utc) + self.serverDateTime = datetime.fromtimestamp(self.serverTimeUponLogin, self.serverTimeZone) + + def getCurServerDateTime(self): + secondsPassed = globalClock.getRealTime() - self.globalClockRealTimeUponLogin + self.debugSecondsAdded + curDateTime = self.serverTimeZone.normalize(self.serverDateTime + timedelta(seconds=secondsPassed)) + return curDateTime + + def getRelativeServerDateTime(self, timeOffset): + secondsPassed = globalClock.getRealTime() - self.globalClockRealTimeUponLogin + self.debugSecondsAdded + secondsPassed += timeOffset + curDateTime = self.serverTimeZone.normalize(self.serverDateTime + timedelta(seconds=secondsPassed)) + return curDateTime + + def getCurServerDateTimeForComparison(self): + secondsPassed = globalClock.getRealTime() - self.globalClockRealTimeUponLogin + self.debugSecondsAdded + curDateTime = self.serverDateTime + timedelta(seconds=secondsPassed) + curDateTime = curDateTime.replace(tzinfo=self.serverTimeZone) + return curDateTime + + def getCurServerTimeStr(self): + curDateTime = self.getCurServerDateTime() + result = curDateTime.strftime(self.ClockFormat) + if result[0] == '0': + result = result[1:] + return result + + def setDebugSecondsAdded(self, moreSeconds): + self.debugSecondsAdded = moreSeconds + + def debugTest(self): + startTime = datetime.today() + serverTzInfo = self.serverTimeZone + startTime = startTime.replace(tzinfo=serverTzInfo) + self.notify.info('startTime = %s' % startTime) + serverTime = self.getCurServerDateTime() + self.notify.info('serverTime = %s' % serverTime) + result = startTime <= serverTime + self.notify.info('start < serverTime %s' % result) + startTime1MinAgo = startTime + timedelta(minutes=-1) + self.notify.info('startTime1MinAgo = %s' % startTime1MinAgo) + result2 = startTime1MinAgo <= serverTime + self.notify.info('startTime1MinAgo < serverTime %s' % result2) + serverTimeForComparison = self.getCurServerDateTimeForComparison() + self.notify.info('serverTimeForComparison = %s' % serverTimeForComparison) + result3 = startTime1MinAgo <= serverTimeForComparison + self.notify.info('startTime1MinAgo < serverTimeForComparison %s' % result3) + + def convertStrToToontownTime(self, dateStr): + curDateTime = self.getCurServerDateTime() + try: + curDateTime = datetime.fromtimestamp(time.mktime(time.strptime(dateStr, self.formatStr)), self.serverTimeZone) + curDateTime = self.serverTimeZone.normalize(curDateTime) + except: + self.notify.warning('error parsing date string=%s' % dateStr) + + result = curDateTime + return result + + def convertUtcStrToToontownTime(self, dateStr): + curDateTime = self.getCurServerDateTime() + try: + timeTuple = time.strptime(dateStr, self.formatStr) + utcDateTime = datetime(timeTuple[0], timeTuple[1], timeTuple[2], timeTuple[3], timeTuple[4], timeTuple[5], timeTuple[6], pytz.utc) + curDateTime = utcDateTime.astimezone(self.serverTimeZone) + curDateTime = self.serverTimeZone.normalize(curDateTime) + except: + self.notify.warning('error parsing date string=%s' % dateStr) + + result = curDateTime + return result diff --git a/toontown/parties/WinterPartyCatchActivityToonSD.py b/toontown/parties/WinterPartyCatchActivityToonSD.py new file mode 100644 index 0000000..b50f085 --- /dev/null +++ b/toontown/parties/WinterPartyCatchActivityToonSD.py @@ -0,0 +1,38 @@ +import PartyCatchActivityToonSD +from pandac.PandaModules import Vec4 +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import Sequence, Parallel, Wait, Func +from direct.interval.IntervalGlobal import LerpColorScaleInterval +from direct.interval.IntervalGlobal import WaitInterval, ActorInterval, FunctionInterval +from direct.fsm import ClassicFSM, State + +class WinterPartyCatchActivityToonSD(PartyCatchActivityToonSD.PartyCatchActivityToonSD): + notify = DirectNotifyGlobal.directNotify.newCategory('PartyCatchActivityToonSD') + + def __init__(self, avId, activity): + WinterPartyCatchActivityToonSD.notify.debug('init : avId = %s, activity = %s ' % (avId, activity)) + PartyCatchActivityToonSD.PartyCatchActivityToonSD.__init__(self, avId, activity) + + def enterEatFruit(self, fruitModel, handNode): + self.notify.debug('enterEatFruit') + if self.isLocal: + self.activity.orthoWalk.start() + self.setAnimState('Catching', 1.0) + self.fruitModel = fruitModel + renderScale = fruitModel.getScale(render) + fruitModel.reparentTo(handNode) + fruitModel.setScale(render, renderScale) + fruitModel.setTransparency(1) + duration = self.toon.getDuration('catch-eatneutral') + self.eatIval = Sequence(Parallel(WaitInterval(duration), Sequence(LerpColorScaleInterval(fruitModel, duration / 2.0, Vec4(1.0, 1.0, 1.0, 0.0)))), Func(self.fsm.request, 'normal'), name=self.toon.uniqueName('eatingIval')) + self.eatIval.start() + + def exitEatFruit(self): + self.eatIval.pause() + del self.eatIval + self.fruitModel.reparentTo(hidden) + self.fruitModel.removeNode() + del self.fruitModel + self.setAnimState('off', 1.0) + if self.isLocal: + self.activity.orthoWalk.stop() diff --git a/toontown/parties/__init__.py b/toontown/parties/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/parties/activityFSMMixins.py b/toontown/parties/activityFSMMixins.py new file mode 100644 index 0000000..f9ce286 --- /dev/null +++ b/toontown/parties/activityFSMMixins.py @@ -0,0 +1,198 @@ +from BaseActivityFSM import BaseActivityFSM + +class IdleMixin: + + def enterIdle(self, *args): + BaseActivityFSM.notify.info("enterIdle: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startIdle(*args) + else: + self.activity.startIdle() + + def filterIdle(self, request, args): + BaseActivityFSM.notify.debug("filterIdle( '%s', '%s' )" % (request, args)) + if request == 'Idle': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitIdle(self): + BaseActivityFSM.notify.debug("exitIdle: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishIdle() + + +class ActiveMixin: + + def enterActive(self, *args): + BaseActivityFSM.notify.info("enterActive: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startActive(*args) + else: + self.activity.startActive() + + def filterActive(self, request, args): + BaseActivityFSM.notify.debug("filterActive( '%s', '%s' )" % (request, args)) + if request == 'Active': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitActive(self): + BaseActivityFSM.notify.debug("exitActive: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishActive() + + +class DisabledMixin: + + def enterDisabled(self, *args): + BaseActivityFSM.notify.info("enterDisabled: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startDisabled(*args) + else: + self.activity.startDisabled() + + def filterDisabled(self, request, args): + BaseActivityFSM.notify.debug("filterDisabled( '%s', '%s' )" % (request, args)) + if request == 'Disabled': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitDisabled(self): + BaseActivityFSM.notify.debug("exitDisabled: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishDisabled() + + +class RulesMixin: + + def enterRules(self, *args): + BaseActivityFSM.notify.info("enterRules: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startRules(*args) + else: + self.activity.startRules() + + def filterRules(self, request, args): + BaseActivityFSM.notify.debug("filterRules( '%s', '%s' )" % (request, args)) + if request == 'Rules': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitRules(self): + BaseActivityFSM.notify.debug("exitRules: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishRules() + + +class WaitForEnoughMixin: + + def enterWaitForEnough(self, *args): + BaseActivityFSM.notify.info("enterWaitForEnough: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitForEnough(*args) + else: + self.activity.startWaitForEnough() + + def filterWaitForEnough(self, request, args): + BaseActivityFSM.notify.debug("filterWaitForEnough( '%s', '%s' )" % (request, args)) + if request == 'WaitForEnough': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitForEnough(self): + BaseActivityFSM.notify.debug("exitWaitForEnough: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitForEnough() + + +class WaitToStartMixin: + + def enterWaitToStart(self, *args): + BaseActivityFSM.notify.info("enterWaitToStart: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitToStart(*args) + else: + self.activity.startWaitToStart() + + def filterWaitToStart(self, request, args): + BaseActivityFSM.notify.debug("filterWaitToStart( '%s', '%s' )" % (request, args)) + if request == 'WaitToStart': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitToStart(self): + BaseActivityFSM.notify.debug("exitWaitToStart: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitToStart() + + +class WaitClientsReadyMixin: + + def enterWaitClientsReady(self, *args): + BaseActivityFSM.notify.info("enterWaitClientsReady: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitClientsReady(*args) + else: + self.activity.startWaitClientsReady() + + def filterWaitClientsReady(self, request, args): + BaseActivityFSM.notify.debug("filterWaitClientsReady( '%s', '%s' )" % (request, args)) + if request == 'WaitClientsReady': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitClientsReady(self): + BaseActivityFSM.notify.debug("exitWaitClientsReady: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitClientsReady() + + +class WaitForServerMixin: + + def enterWaitForServer(self, *args): + BaseActivityFSM.notify.info("enterWaitForServer: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startWaitForServer(*args) + else: + self.activity.startWaitForServer() + + def filterWaitForServer(self, request, args): + BaseActivityFSM.notify.debug("filterWaitForServer( '%s', '%s' )" % (request, args)) + if request == 'WaitForServer': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitWaitForServer(self): + BaseActivityFSM.notify.debug("exitWaitForServer: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishWaitForServer() + + +class ConclusionMixin: + + def enterConclusion(self, *args): + BaseActivityFSM.notify.info("enterConclusion: '%s' -> '%s'" % (self.oldState, self.newState)) + if len(args) > 0: + self.activity.startConclusion(*args) + else: + self.activity.startConclusion() + + def filterConclusion(self, request, args): + BaseActivityFSM.notify.debug("filterConclusion( '%s', '%s' )" % (request, args)) + if request == 'Conclusion': + return None + else: + return self.defaultFilter(request, args) + return None + + def exitConclusion(self): + BaseActivityFSM.notify.debug("exitConclusion: '%s' -> '%s'" % (self.oldState, self.newState)) + self.activity.finishConclusion() diff --git a/toontown/parties/activityFSMs.py b/toontown/parties/activityFSMs.py new file mode 100644 index 0000000..a90e953 --- /dev/null +++ b/toontown/parties/activityFSMs.py @@ -0,0 +1,79 @@ +from direct.directnotify import DirectNotifyGlobal +from BaseActivityFSM import BaseActivityFSM +from activityFSMMixins import IdleMixin +from activityFSMMixins import RulesMixin +from activityFSMMixins import ActiveMixin +from activityFSMMixins import DisabledMixin +from activityFSMMixins import ConclusionMixin +from activityFSMMixins import WaitForEnoughMixin +from activityFSMMixins import WaitToStartMixin +from activityFSMMixins import WaitClientsReadyMixin +from activityFSMMixins import WaitForServerMixin + +class FireworksActivityFSM(BaseActivityFSM, IdleMixin, ActiveMixin, DisabledMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('FireworksActivityFSM') + + def __init__(self, activity): + FireworksActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Idle': ['Active', 'Disabled'], + 'Active': ['Disabled'], + 'Disabled': []} + + +class CatchActivityFSM(BaseActivityFSM, IdleMixin, ActiveMixin, ConclusionMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('CatchActivityFSM') + + def __init__(self, activity): + CatchActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Idle': ['Active', 'Conclusion'], + 'Active': ['Conclusion'], + 'Conclusion': ['Idle']} + + +class TrampolineActivityFSM(BaseActivityFSM, IdleMixin, RulesMixin, ActiveMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('TrampolineActivityFSM') + + def __init__(self, activity): + TrampolineActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Idle': ['Rules', 'Active'], + 'Rules': ['Active', 'Idle'], + 'Active': ['Idle']} + + +class DanceActivityFSM(BaseActivityFSM, IdleMixin, ActiveMixin, DisabledMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('DanceActivityFSM') + + def __init__(self, activity): + DanceActivityFSM.notify.debug('__init__') + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'Active': ['Disabled'], + 'Disabled': ['Active']} + + +class TeamActivityAIFSM(BaseActivityFSM, WaitForEnoughMixin, WaitToStartMixin, WaitClientsReadyMixin, ActiveMixin, ConclusionMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('TeamActivityAIFSM') + + def __init__(self, activity): + BaseActivityFSM.__init__(self, activity) + self.notify.debug('__init__') + self.defaultTransitions = {'WaitForEnough': ['WaitToStart'], + 'WaitToStart': ['WaitForEnough', 'WaitClientsReady'], + 'WaitClientsReady': ['WaitForEnough', 'Active'], + 'Active': ['WaitForEnough', 'Conclusion'], + 'Conclusion': ['WaitForEnough']} + + +class TeamActivityFSM(BaseActivityFSM, WaitForEnoughMixin, WaitToStartMixin, RulesMixin, WaitForServerMixin, ActiveMixin, ConclusionMixin): + notify = DirectNotifyGlobal.directNotify.newCategory('TeamActivityFSM') + + def __init__(self, activity): + BaseActivityFSM.__init__(self, activity) + self.defaultTransitions = {'WaitForEnough': ['WaitToStart'], + 'WaitToStart': ['WaitForEnough', 'Rules'], + 'Rules': ['WaitForServer', 'Active', 'WaitForEnough'], + 'WaitForServer': ['Active', 'WaitForEnough'], + 'Active': ['Conclusion', 'WaitForEnough'], + 'Conclusion': ['WaitForEnough']} diff --git a/toontown/pets/DistributedPet.py b/toontown/pets/DistributedPet.py new file mode 100644 index 0000000..138a52a --- /dev/null +++ b/toontown/pets/DistributedPet.py @@ -0,0 +1,490 @@ +from pandac.PandaModules import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNode +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed.MsgTypes import * +from direct.task import Task +from otp.otpbase import OTPGlobals +from toontown.pets import Pet, PetBase, PetTraits, PetConstants, PetManager, PetAvatarPanel +from toontown.pets import PetMood, PetTricks +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.distributed import DelayDelete +from toontown.distributed.DelayDeletable import DelayDeletable +import random +if __dev__: + import pdb +BeanColors = (VBase4(1.0, 0.2, 0.2, 1.0), + VBase4(0.2, 1.0, 0.2, 1.0), + VBase4(0.2, 0.2, 1.0, 1.0), + VBase4(0.0, 1.0, 1.0, 1.0), + VBase4(1.0, 1.0, 0.0, 1.0), + VBase4(1.0, 0.6, 1.0, 1.0), + VBase4(0.6, 0.0, 0.6, 1.0)) + +class DistributedPet(DistributedSmoothNode.DistributedSmoothNode, Pet.Pet, PetBase.PetBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPet') + swallowSfx = None + callSfx = None + petSfx = None + + def __init__(self, cr, bFake = False): + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + Pet.Pet.__init__(self) + self.bFake = bFake + self.isLocalToon = 0 + self.inWater = 0 + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + self.trickAptitudes = [] + self.avDelayDelete = None + return + + def generate(self): + DistributedPet.notify.debug('generate(), fake=%s' % self.bFake) + if not self.bFake: + PetManager.acquirePetManager() + DistributedSmoothNode.DistributedSmoothNode.generate(self) + self.trickIval = None + self.movieTrack = None + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.requiredMoodComponents = {} + return + + def b_setLocation(self, parentId, zoneId): + if not self.bFake: + DistributedSmoothNode.DistributedSmoothNode.b_setLocation(self, parentId, zoneId) + + def d_setLocation(self, parentId, zoneId): + if not self.bFake: + DistributedSmoothNode.DistributedSmoothNode.d_setLocation(self, parentId, zoneId) + + def setLocation(self, parentId, zoneId): + if not self.bFake: + DistributedSmoothNode.DistributedSmoothNode.setLocation(self, parentId, zoneId) + + def getDisplayPrefix(self): + return 'pet%s' % self.doId + + def display(self, key, value, category = ''): + if self.bFake: + return 1 + if len(category) > 0: + category = '-' + category + onScreenDebug.add('%s%s-%s' % (self.getDisplayPrefix(), category, key), value) + return 1 + + def clearDisplay(self): + onScreenDebug.removeAllWithPrefix(self.getDisplayPrefix()) + return 1 + + def moodComponentChanged(self, components = []): + if len(components) == 0: + components = PetMood.PetMood.Components + for comp in components: + self.display(comp, self.mood.getComponent(comp), 'mood') + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getOwnerId(self): + return self.ownerId + + def setPetName(self, petName): + self.petName = petName + DistributedSmoothNode.DistributedSmoothNode.setName(self, self.petName) + if self.isGenerated(): + Pet.Pet.setName(self, self.petName) + messenger.send('petNameChanged', [self]) + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + setterName = self.getSetterName(traitName) + + def traitSetter(value, self = self, i = i): + self.traitList[i] = value + + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(setterName) + + def setHead(self, head): + DistributedPet.notify.debug('setHead: %s' % head) + self.head = head + + def setEars(self, ears): + DistributedPet.notify.debug('setEars: %s' % ears) + self.ears = ears + + def setNose(self, nose): + DistributedPet.notify.debug('setNose: %s' % nose) + self.nose = nose + + def setTail(self, tail): + DistributedPet.notify.debug('setTail: %s' % tail) + self.tail = tail + + def setBodyTexture(self, bodyTexture): + DistributedPet.notify.debug('setBodyTexture: %s' % bodyTexture) + self.bodyTexture = bodyTexture + + def setColor(self, color): + DistributedPet.notify.debug('setColor: %s' % color) + self.color = color + + def setColorScale(self, colorScale): + DistributedPet.notify.debug('setColorScale: %s' % colorScale) + self.colorScale = colorScale + + def setEyeColor(self, eyeColor): + DistributedPet.notify.debug('setEyeColor: %s' % eyeColor) + self.eyeColor = eyeColor + + def setGender(self, gender): + DistributedPet.notify.debug('setGender: %s' % gender) + self.gender = gender + + def setLastSeenTimestamp(self, timestamp): + DistributedPet.notify.debug('setLastSeenTimestamp: %s' % timestamp) + self.lastSeenTimestamp = timestamp + + def getTimeSinceLastSeen(self): + t = self.cr.getServerTimeOfDay() - self.lastSeenTimestamp + return max(0.0, t) + + def updateOfflineMood(self): + self.mood.driftMood(dt=self.getTimeSinceLastSeen(), curMood=self.lastKnownMood) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + setterName = self.getSetterName(compName) + + def moodSetter(value, self = self, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(setterName) + + def setMood(self, *componentValues): + for value, name in zip(componentValues, PetMood.PetMood.Components): + setterName = self.getSetterName(name) + self.__dict__[setterName](value) + + def doTrick(self, trickId, timestamp): + if not self.isLockedDown(): + if self.trickIval is not None and self.trickIval.isPlaying(): + self.trickIval.finish() + self.trickIval = PetTricks.getTrickIval(self, trickId) + if trickId == PetTricks.Tricks.BALK: + mood = self.getDominantMood() + self.trickIval = Parallel(self.trickIval, Sequence(Func(self.handleMoodChange, 'confusion'), Wait(1.0), Func(self.handleMoodChange, mood))) + self.trickIval.start(globalClockDelta.localElapsedTime(timestamp)) + return + + def getName(self): + return Pet.Pet.getName(self) + + def announceGenerate(self): + DistributedPet.notify.debug('announceGenerate(), fake=%s' % self.bFake) + DistributedSmoothNode.DistributedSmoothNode.announceGenerate(self) + if hasattr(self, 'petName'): + Pet.Pet.setName(self, self.petName) + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + self.mood = PetMood.PetMood(self) + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + DistributedPet.notify.debug('time since last seen: %s' % self.getTimeSinceLastSeen()) + self.setDNA([self.head, + self.ears, + self.nose, + self.tail, + self.bodyTexture, + self.color, + self.colorScale, + self.eyeColor, + self.gender]) + av = self.cr.doId2do.get(self.ownerId) + if av: + av.petDNA = self.style + if self.bFake: + self.lastKnownMood = self.mood.makeCopy() + self.updateOfflineMood() + else: + self.__initCollisions() + self.startSmooth() + self.setActiveShadow(1) + self.setPetName(self.petName) + if not self.bFake: + self.addActive() + self.startBlink() + if not self.swallowSfx: + self.swallowSfx = loader.loadSfx('phase_5.5/audio/sfx/beg_eat_swallow.mp3') + if not self.callSfx: + self.callSfx = loader.loadSfx('phase_5.5/audio/sfx/call_pet.mp3') + if not self.petSfx: + self.petSfx = loader.loadSfx('phase_5.5/audio/sfx/pet_the_pet.mp3') + self.handleMoodChange() + self.accept(self.mood.getDominantMoodChangeEvent(), self.handleMoodChange) + self.accept(self.mood.getMoodChangeEvent(), self.moodComponentChanged) + + def disable(self): + DistributedPet.notify.debug('disable(), fake=%s' % self.bFake) + if self.isLocalToon: + base.localAvatar.enableSmartCameraViews() + self.freeAvatar() + self.ignore(self.mood.getDominantMoodChangeEvent()) + self.ignore(self.mood.getMoodChangeEvent()) + if hasattr(self, 'lastKnownMood'): + self.lastKnownMood.destroy() + del self.lastKnownMood + self.mood.destroy() + del self.mood + del self.traits + self.removeActive() + if not self.bFake: + self.stopSmooth() + self.__cleanupCollisions() + self.stopAnimations() + if self.doId == localAvatar.getPetId(): + bboard.post(PetConstants.OurPetsMoodChangedKey, True) + taskMgr.remove(self.uniqueName('lerpCamera')) + self.clearDisplay() + DistributedSmoothNode.DistributedSmoothNode.disable(self) + + def delete(self): + DistributedPet.notify.debug('delete(), fake=%s' % self.bFake) + if self.trickIval is not None: + self.trickIval.finish() + del self.trickIval + if self.movieTrack is not None: + self.movieTrack.finish() + del self.movieTrack + taskMgr.remove(self.uniqueName('Pet-Movie-%s' % self.getDoId())) + self.clearMovie() + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + Pet.Pet.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + if not self.bFake: + PetManager.releasePetManager() + return + + def __initCollisions(self): + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode('pet-cRayNode-%s' % self.doId) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.cRayNodePath = self.attachNewNode(cRayNode) + self.lifter = CollisionHandlerFloor() + self.lifter.setInPattern('enter%in') + self.lifter.setOutPattern('exit%in') + self.lifter.setOffset(OTPGlobals.FloorOffset) + self.lifter.setReach(4.0) + self.lifter.addCollider(self.cRayNodePath, self) + self.cTrav = base.petManager.cTrav + self.cTrav.addCollider(self.cRayNodePath, self.lifter) + taskMgr.add(self._detectWater, self.getDetectWaterTaskName(), priority=32) + self.initializeBodyCollisions('pet-%s' % self.doId) + + def __cleanupCollisions(self): + self.disableBodyCollisions() + taskMgr.remove(self.getDetectWaterTaskName()) + self.cTrav.removeCollider(self.cRayNodePath) + del self.cTrav + self.cRayNodePath.removeNode() + del self.cRayNodePath + del self.lifter + + def lockPet(self): + if not self.lockedDown: + self.prevAnimState = self.animFSM.getCurrentState().getName() + self.animFSM.request('neutral') + self.lockedDown += 1 + + def isLockedDown(self): + return self.lockedDown != 0 + + def unlockPet(self): + if self.lockedDown <= 0: + DistributedPet.notify.warning('%s: unlockPet called on unlockedPet' % self.doId) + else: + self.lockedDown -= 1 + if not self.lockedDown: + self.animFSM.request(self.prevAnimState) + self.prevAnimState = None + return + + def smoothPosition(self): + DistributedSmoothNode.DistributedSmoothNode.smoothPosition(self) + if not self.lockedDown: + self.trackAnimToSpeed(self.smoother.getSmoothForwardVelocity(), self.smoother.getSmoothRotationalVelocity()) + + def getDetectWaterTaskName(self): + return self.uniqueName('detectWater') + + def _detectWater(self, task): + showWake, wakeWaterHeight = ZoneUtil.getWakeInfo() + self.inWater = 0 + if showWake: + if self.getZ() <= wakeWaterHeight: + self.setZ(wakeWaterHeight - PetConstants.SubmergeDistance) + self.inWater = 1 + return Task.cont + + def isInWater(self): + return self.inWater + + def isExcited(self): + return PetBase.PetBase.isExcited(self) + + def isSad(self): + return PetBase.PetBase.isSad(self) + + def handleMoodChange(self, mood = None): + if mood is None: + mood = self.mood.getDominantMood() + if mood == PetMood.PetMood.Neutral: + self.clearChat() + self.clearMood() + else: + self.showMood(mood) + messenger.send('petStateUpdated', [self]) + return + + def getDominantMood(self): + if not hasattr(self, 'mood'): + return PetMood.PetMood.Neutral + return self.mood.getDominantMood() + + def getRequestID(self): + return CLIENT_GET_PET_DETAILS + + def teleportIn(self, timestamp): + self.lockPet() + self.animFSM.request('teleportIn', [timestamp]) + self.unlockPet() + + def teleportOut(self, timestamp): + self.lockPet() + self.animFSM.request('teleportOut', [timestamp]) + self.unlockPet() + + def avatarInteract(self, avId): + place = base.cr.playGame.getPlace() + place.setState('pet') + base.localAvatar.disableSmartCameraViews() + + def freeAvatar(self): + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + base.localAvatar.unlock() + messenger.send('pet-interaction-done') + + def setUpMovieAvatar(self, av): + self.avDelayDelete = DelayDelete.DelayDelete(av, 'Pet.setUpMovieAvatar') + av.headsUp(self, 0, 0, 0) + av.stopLookAround() + + def holdPetDownForMovie(self): + self.lockPet() + self.stopSmooth() + + def releasePetFromHoldDown(self): + self.unlockPet() + self.startSmooth() + + def clearMovieAvatar(self): + if self.avDelayDelete: + self.avDelayDelete.destroy() + self.avDelayDelete = None + return + + def clearMovie(self): + self.clearMovieAvatar() + return Task.done + + def resetAvatarAndPet(self, task = None): + if self.isLocalToon: + base.localAvatar.enableSmartCameraViews() + base.localAvatar.setH(base.localAvatar, 30) + self.freeAvatar() + self.isLocalToon = 0 + return Task.done + + def _petMovieStart(self, av): + if not self.isLocalToon: + av.stopSmooth() + self.setUpMovieAvatar(av) + if self.isLocalToon: + base.localAvatar.setCameraPosForPetInteraction() + base.localAvatar.lock() + + def _getPetMovieCompleteIval(self, av): + + def _petMovieComplete(self = self): + if self.isLocalToon: + base.localAvatar.unsetCameraPosForPetInteraction() + else: + av.startSmooth() + + return Sequence(Func(_petMovieComplete), Wait(0.8), Func(self.resetAvatarAndPet)) + + def setMovie(self, mode, avId, timestamp): + timeStamp = globalClockDelta.localElapsedTime(timestamp) + if mode in (PetConstants.PET_MOVIE_CALL, PetConstants.PET_MOVIE_SCRATCH, PetConstants.PET_MOVIE_FEED): + if self.movieTrack is not None and self.movieTrack.isPlaying(): + self.movieTrack.finish() + if avId != 0: + self.isLocalToon = avId == base.localAvatar.doId + av = base.cr.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + if mode == PetConstants.PET_MOVIE_CLEAR: + self.clearMovie() + return + if mode == PetConstants.PET_MOVIE_CALL: + try: + self.movieTrack = Sequence(Func(self._petMovieStart, av), Parallel(av.getCallPetIval(), Sequence(Wait(0.54), SoundInterval(self.callSfx))), self._getPetMovieCompleteIval(av)) + self.movieTrack.start() + except StandardError, error: + print str(error) + + if mode == PetConstants.PET_MOVIE_SCRATCH: + try: + self.movieTrack = Sequence(Func(self._petMovieStart, av), Func(self.holdPetDownForMovie), Parallel(self.getInteractIval(self.Interactions.SCRATCH), av.getScratchPetIval(), SoundInterval(self.petSfx)), Func(self.releasePetFromHoldDown), self._getPetMovieCompleteIval(av)) + self.movieTrack.start() + except StandardError, error: + print str(error) + + if mode == PetConstants.PET_MOVIE_FEED: + self.bean = loader.loadModel('phase_4/models/props/jellybean4') + bean = self.bean.find('**/jellybean') + bean.setColor(random.choice(BeanColors)) + self.movieTrack = Sequence(Func(self._petMovieStart, av), Func(self.holdPetDownForMovie), Parallel(Func(base.playSfx, self.swallowSfx, 0, 1, 1, 2.5, self.bean), Sequence(ActorInterval(self, 'toBeg'), ActorInterval(self, 'beg'), ActorInterval(self, 'fromBeg'), ActorInterval(self, 'eat'), ActorInterval(self, 'swallow'), Func(self.loop, 'neutral')), Sequence(Wait(0.3), ActorInterval(av, 'feedPet'), Func(av.animFSM.request, 'neutral')), Sequence(Wait(0.3), Func(self.bean.reparentTo, av.rightHand), Func(self.bean.setPos, 0.1, 0.0, 0.2), Wait(2.1), Func(av.update, 0), Func(av.update, 1), Func(av.update, 2), Func(self.bean.wrtReparentTo, render), Parallel(LerpHprInterval(self.bean, hpr=Point3(random.random() * 360.0 * 2, random.random() * 360.0 * 2, random.random() * 360.0 * 2), duration=1.2), ProjectileInterval(self.bean, endPos=self.find('**/joint_tongueBase').getPos(render), duration=1.2, gravityMult=0.45)), Func(self.bean.removeNode))), Func(self.releasePetFromHoldDown), self._getPetMovieCompleteIval(av)) + self.movieTrack.start() + return + + def setTrickAptitudes(self, aptitudes): + self.trickAptitudes = aptitudes diff --git a/toontown/pets/DistributedPetAI.py b/toontown/pets/DistributedPetAI.py new file mode 100644 index 0000000..14b7361 --- /dev/null +++ b/toontown/pets/DistributedPetAI.py @@ -0,0 +1,1093 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import weightedChoice, randFloat, lerp +from direct.showbase.PythonUtil import contains, list2dict, clampScalar +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedSmoothNodeAI +from direct.distributed import DistributedSmoothNodeBase +from direct.distributed import ClockDelta +from direct.fsm import ClassicFSM, State +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +from direct.task import Task +from otp.movement import Mover +from toontown.pets import PetChase, PetFlee, PetWander, PetLeash +from toontown.pets import PetCollider, PetSphere, PetLookerAI +from toontown.pets import PetConstants, PetDNA, PetTraits +from toontown.pets import PetObserve, PetBrain, PetMood +from toontown.pets import PetActionFSM, PetBase, PetGoal, PetTricks +from direct.fsm import FSM +from toontown.toon import DistributedToonAI +from toontown.ai import ServerEventBuffer +import random +import time +import string +import copy +from direct.showbase.PythonUtil import StackTrace + +class DistributedPetAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, PetLookerAI.PetLookerAI, PetBase.PetBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPetAI') + movieTimeSwitch = {PetConstants.PET_MOVIE_FEED: PetConstants.FEED_TIME, + PetConstants.PET_MOVIE_SCRATCH: PetConstants.SCRATCH_TIME, + PetConstants.PET_MOVIE_CALL: PetConstants.CALL_TIME} + movieDistSwitch = {PetConstants.PET_MOVIE_FEED: PetConstants.FEED_DIST.get, + PetConstants.PET_MOVIE_SCRATCH: PetConstants.SCRATCH_DIST.get} + + def __init__(self, air, dna = None): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + PetLookerAI.PetLookerAI.__init__(self) + self.ownerId = 0 + self.petName = 'unnamed' + self.traitSeed = 0 + self.safeZone = ToontownGlobals.ToontownCentral + self.initialDNA = dna + self.active = 1 + self.activated = 0 + self._outOfBounds = False + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.head = -1 + self.ears = -1 + self.nose = -1 + self.tail = -1 + self.bodyTexture = 0 + self.color = 0 + self.colorScale = 0 + self.eyeColor = 0 + self.gender = 0 + self.movieMode = None + self.lockMoverEnabled = 0 + self.trickAptitudes = [] + self.inEstate = 0 + self.estateOwnerId = None + self.estateZones = [] + self.lastSeenTimestamp = self.getCurEpochTimestamp() + self.requiredMoodComponents = {} + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + self.busy = 0 + self.gaitFSM = ClassicFSM.ClassicFSM('petGaitFSM', [State.State('off', self.gaitEnterOff, self.gaitExitOff), + State.State('neutral', self.gaitEnterNeutral, self.gaitExitNeutral), + State.State('happy', self.gaitEnterHappy, self.gaitExitHappy), + State.State('sad', self.gaitEnterSad, self.gaitExitSad)], 'off', 'off') + self.gaitFSM.enterInitialState() + self.unstickFSM = ClassicFSM.ClassicFSM('unstickFSM', [State.State('off', self.unstickEnterOff, self.unstickExitOff), State.State('on', self.unstickEnterOn, self.unstickExitOn)], 'off', 'off') + self.unstickFSM.enterInitialState() + if __dev__: + self.pscMoveResc = PStatCollector('App:Show code:petMove:Reschedule') + return + + def setInactive(self): + self.active = 0 + + def _initDBVals(self, ownerId, name = None, traitSeed = 0, dna = None, safeZone = ToontownGlobals.ToontownCentral): + self.b_setOwnerId(ownerId) + if name is None: + name = 'pet%s' % self.doId + self.b_setPetName(name) + self.b_setTraitSeed(traitSeed) + self.b_setSafeZone(safeZone) + traits = PetTraits.PetTraits(traitSeed, safeZone) + for traitName in PetTraits.getTraitNames(): + setter = self.getSetterName(traitName, 'b_set') + self.__dict__[setter](traits.getTraitValue(traitName)) + + self.traits = traits + for component in PetMood.PetMood.Components: + setterName = self.getSetterName(component, 'b_set') + self.__dict__[setterName](0.0) + + if not dna: + dna = PetDNA.getRandomPetDNA() + self.setDNA(dna) + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + for component in PetMood.PetMood.Components: + self.setMoodComponent(component, 0.0) + + self.b_setTrickAptitudes([]) + return + + def setDNA(self, dna): + head, ears, nose, tail, body, color, colorScale, eyes, gender = dna + self.b_setHead(head) + self.b_setEars(ears) + self.b_setNose(nose) + self.b_setTail(tail) + self.b_setBodyTexture(body) + self.b_setColor(color) + self.b_setColorScale(colorScale) + self.b_setEyeColor(eyes) + self.b_setGender(gender) + + def handleZoneChange(self, newZoneId, oldZoneId): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.handleZoneChange(self, newZoneId, oldZoneId) + self.ignore(PetObserve.getEventName(oldZoneId)) + self.accept(PetObserve.getEventName(newZoneId), self.brain.observe) + + def handleLogicalZoneChange(self, newZoneId, oldZoneId): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.handleLogicalZoneChange(self, newZoneId, oldZoneId) + self.announceZoneChange(newZoneId, oldZoneId) + self.destroySphereImpulse() + self.createSphereImpulse() + + def announceZoneChange(self, newZoneId, oldZoneId): + DistributedPetAI.notify.debug('%s.announceZoneChange: %s->%s' % (self.doId, oldZoneId, newZoneId)) + broadcastZones = list2dict([newZoneId, oldZoneId]) + self.estateOwnerId = simbase.air.estateMgr.getOwnerFromZone(newZoneId) + if self.estateOwnerId: + if __dev__: + pass + self.inEstate = 1 + self.estateZones = simbase.air.estateMgr.getEstateZones(self.estateOwnerId) + else: + self.inEstate = 0 + self.estateZones = [] + PetObserve.send(broadcastZones.keys(), PetObserve.PetActionObserve(PetObserve.Actions.CHANGE_ZONE, self.doId, (oldZoneId, newZoneId))) + + def getOwnerId(self): + return self.ownerId + + def b_setOwnerId(self, ownerId): + self.d_setOwnerId(ownerId) + self.setOwnerId(ownerId) + + def d_setOwnerId(self, ownerId): + self.sendUpdate('setOwnerId', [ownerId]) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getPetName(self): + return self.petName + + def b_setPetName(self, petName): + self.d_setPetName(petName) + self.setPetName(petName) + + def d_setPetName(self, petName): + self.sendUpdate('setPetName', [petName]) + + def setPetName(self, petName): + self.petName = petName + DistributedSmoothNodeAI.DistributedSmoothNodeAI.setName(self, self.petName) + + def getTraitSeed(self): + return self.traitSeed + + def b_setTraitSeed(self, traitSeed): + self.d_setTraitSeed(traitSeed) + self.setTraitSeed(traitSeed) + + def d_setTraitSeed(self, traitSeed): + self.sendUpdate('setTraitSeed', [traitSeed]) + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def getSafeZone(self): + return self.safeZone + + def b_setSafeZone(self, safeZone): + self.d_setSafeZone(safeZone) + self.setSafeZone(safeZone) + + def d_setSafeZone(self, safeZone): + self.sendUpdate('setSafeZone', [safeZone]) + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def getPetName(self): + return self.petName + + def b_setPetName(self, petName): + self.d_setPetName(petName) + self.setPetName(petName) + + def d_setPetName(self, petName): + self.sendUpdate('setPetName', [petName]) + + def setPetName(self, petName): + self.petName = petName + DistributedSmoothNodeAI.DistributedSmoothNodeAI.setName(self, self.petName) + + def setTraits(self, traitList): + self.traitList = traitList + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + getterName = self.getSetterName(traitName, 'get') + b_setterName = self.getSetterName(traitName, 'b_set') + d_setterName = self.getSetterName(traitName, 'd_set') + setterName = self.getSetterName(traitName) + + def traitGetter(i = i): + return self.traitList[i] + + def b_traitSetter(value, setterName = setterName, d_setterName = d_setterName): + self.__dict__[d_setterName](value) + self.__dict__[setterName](value) + + def d_traitSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def traitSetter(value, i = i): + self.traitList[i] = value + + self.__dict__[getterName] = traitGetter + self.__dict__[b_setterName] = b_traitSetter + self.__dict__[d_setterName] = d_traitSetter + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append(b_setterName) + self.__funcsToDelete.append(d_setterName) + self.__funcsToDelete.append(setterName) + + def getHead(self): + return self.head + + def b_setHead(self, head): + self.d_setHead(head) + self.setHead(head) + + def d_setHead(self, head): + self.sendUpdate('setHead', [head]) + + def setHead(self, head): + self.head = head + + def getEars(self): + return self.ears + + def b_setEars(self, ears): + self.d_setEars(ears) + self.setEars(ears) + + def d_setEars(self, ears): + self.sendUpdate('setEars', [ears]) + + def setEars(self, ears): + self.ears = ears + + def getNose(self): + return self.nose + + def b_setNose(self, nose): + self.d_setNose(nose) + self.setNose(nose) + + def d_setNose(self, nose): + self.sendUpdate('setNose', [nose]) + + def setNose(self, nose): + self.nose = nose + + def getTail(self): + return self.tail + + def b_setTail(self, tail): + self.d_setTail(tail) + self.setTail(tail) + + def d_setTail(self, tail): + self.sendUpdate('setTail', [tail]) + + def setTail(self, tail): + self.tail = tail + + def getBodyTexture(self): + return self.bodyTexture + + def b_setBodyTexture(self, bodyTexture): + self.d_setBodyTexture(bodyTexture) + self.setBodyTexture(bodyTexture) + + def d_setBodyTexture(self, bodyTexture): + self.sendUpdate('setBodyTexture', [bodyTexture]) + + def setBodyTexture(self, bodyTexture): + self.bodyTexture = bodyTexture + + def getColor(self): + return self.color + + def b_setColor(self, color): + self.d_setColor(color) + self.setColor(color) + + def d_setColor(self, color): + self.sendUpdate('setColor', [color]) + + def setColor(self, color): + self.color = color + + def getColorScale(self): + return self.colorScale + + def b_setColorScale(self, colorScale): + self.d_setColorScale(colorScale) + self.setColorScale(colorScale) + + def d_setColorScale(self, colorScale): + self.sendUpdate('setColorScale', [colorScale]) + + def setColorScale(self, colorScale): + self.colorScale = colorScale + + def getEyeColor(self): + return self.eyeColor + + def b_setEyeColor(self, eyeColor): + self.d_setEyeColor(eyeColor) + self.setEyeColor(eyeColor) + + def d_setEyeColor(self, eyeColor): + self.sendUpdate('setEyeColor', [eyeColor]) + + def setEyeColor(self, eyeColor): + self.eyeColor = eyeColor + + def getGender(self): + return self.gender + + def b_setGender(self, gender): + self.d_setGender(gender) + self.setGender(gender) + + def d_setGender(self, gender): + self.sendUpdate('setGender', [gender]) + + def setGender(self, gender): + self.gender = gender + + def teleportIn(self, timestamp = None): + self.notify.debug('DPAI: teleportIn') + timestamp = ClockDelta.globalClockDelta.getRealNetworkTime() + self.notify.debug('DPAI: sending update @ ts = %s' % timestamp) + self.sendUpdate('teleportIn', [timestamp]) + return None + + def teleportOut(self, timestamp = None): + self.notify.debug('DPAI: teleportOut') + timestamp = ClockDelta.globalClockDelta.getRealNetworkTime() + self.notify.debug('DPAI: sending update @ ts = %s' % timestamp) + self.sendUpdate('teleportOut', [timestamp]) + return None + + def getLastSeenTimestamp(self): + return self.lastSeenTimestamp + + def b_setLastSeenTimestamp(self, timestamp): + self.d_setLastSeenTimestamp(timestamp) + self.setLastSeenTimestamp(timestamp) + + def d_setLastSeenTimestamp(self, timestamp): + self.sendUpdate('setLastSeenTimestamp', [timestamp]) + + def setLastSeenTimestamp(self, timestamp): + self.lastSeenTimestamp = timestamp + + def getCurEpochTimestamp(self): + return int(time.time()) + + def getTimeSinceLastSeen(self): + t = time.time() - self.lastSeenTimestamp + return max(0.0, t) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __handleMoodGet(self, component): + if self.isGenerated(): + return self.mood.getComponent(component) + else: + return 0.0 + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + getterName = self.getSetterName(compName, 'get') + setterName = self.getSetterName(compName) + + def moodGetter(compName = compName): + return self.__handleMoodGet(compName) + + def b_moodSetter(value, setterName = setterName): + self.__dict__[setterName](value) + + def d_moodSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def moodSetter(value, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[getterName] = moodGetter + self.__dict__['b_%s' % setterName] = b_moodSetter + self.__dict__['d_%s' % setterName] = d_moodSetter + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append('b_%s' % setterName) + self.__funcsToDelete.append('d_%s' % setterName) + self.__funcsToDelete.append(setterName) + + def getTrickAptitudes(self): + return self.trickAptitudes + + def b_setTrickAptitudes(self, aptitudes): + self.setTrickAptitudes(aptitudes, local=1) + self.d_setTrickAptitudes(aptitudes) + + def d_setTrickAptitudes(self, aptitudes): + if __dev__: + for aptitude in aptitudes: + pass + + while len(aptitudes) < len(PetTricks.Tricks) - 1: + aptitudes.append(0.0) + + self.sendUpdate('setTrickAptitudes', [aptitudes]) + + def setTrickAptitudes(self, aptitudes, local = 0): + if not local: + DistributedPetAI.notify.debug('setTrickAptitudes: %s' % aptitudes) + while len(self.trickAptitudes) < len(PetTricks.Tricks) - 1: + self.trickAptitudes.append(0.0) + + self.trickAptitudes = aptitudes + + def getTrickAptitude(self, trickId): + if trickId > len(self.trickAptitudes) - 1: + return 0.0 + return self.trickAptitudes[trickId] + + def setTrickAptitude(self, trickId, aptitude, send = 1): + aptitude = clampScalar(aptitude, 0.0, 1.0) + aptitudes = self.trickAptitudes + while len(aptitudes) - 1 < trickId: + aptitudes.append(0.0) + + if aptitudes[trickId] != aptitude: + aptitudes[trickId] = aptitude + if send: + self.b_setTrickAptitudes(aptitudes) + else: + self.setTrickAptitudes(aptitudes, local=1) + + def generate(self): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self) + self._hasCleanedUp = False + self.setHasRequestedDelete(False) + self.b_setParent(ToontownGlobals.SPHidden) + self.lockedDown = 0 + self.leashMode = 0 + self.leashAvId = None + self.leashGoal = None + self.trickLogger = ServerEventBuffer.ServerEventMultiAccumulator(self.air, 'petTricksPerformed', self.doId) + self.trickFailLogger = ServerEventBuffer.ServerEventMultiAccumulator(self.air, 'petTricksFailed', self.doId) + self.feedLogger = ServerEventBuffer.ServerEventAccumulator(self.air, 'petFeedings', self.doId) + self.scratchLogger = ServerEventBuffer.ServerEventAccumulator(self.air, 'petScratchings', self.doId) + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + if not hasattr(self, '_beingCreatedInDB'): + for i in xrange(len(self.traitList)): + value = self.traitList[i] + if value == 0.0: + traitName = PetTraits.getTraitNames()[i] + traitValue = self.traits.getTraitValue(traitName) + DistributedPetAI.notify.info("%s: initializing new trait '%s' to %s, seed=%s" % (self.doId, + traitName, + traitValue, + self.traitSeed)) + setterName = self.getSetterName(traitName, 'b_set') + self.__dict__[setterName](traitValue) + + self.mood = PetMood.PetMood(self) + if not self.active: + return + self.activated = 1 + self.announceZoneChange(self.zoneId, ToontownGlobals.QuietZone) + self.b_setParent(ToontownGlobals.SPRender) + self.setPos(randFloat(-20, 20), randFloat(-20, 20), 0) + self.setH(randFloat(360)) + if self.initialDNA: + self.setDNA(self.initialDNA) + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + self.brain = PetBrain.PetBrain(self) + self.mover = Mover.Mover(self) + self.lockMover = Mover.Mover(self) + self.createImpulses() + self.enterPetLook() + self.actionFSM = PetActionFSM.PetActionFSM(self) + self.teleportIn() + self.handleMoodChange(distribute=0) + taskMgr.doMethodLater(simbase.petMovePeriod * random.random(), self.move, self.getMoveTaskName()) + self.startPosHprBroadcast() + self.accept(PetObserve.getEventName(self.zoneId), self.brain.observe) + self.accept(self.mood.getMoodChangeEvent(), self.handleMoodChange) + self.mood.start() + self.brain.start() + return + + def _isPet(self): + return 1 + + def setHasRequestedDelete(self, flag): + self._requestedDeleteFlag = flag + + def hasRequestedDelete(self): + return self._requestedDeleteFlag + + def requestDelete(self, task = None): + DistributedPetAI.notify.info('PetAI.requestDelete: %s, owner=%s' % (self.doId, self.ownerId)) + if self.hasRequestedDelete(): + DistributedPetAI.notify.info('PetAI.requestDelete: %s, owner=%s returning immediately' % (self.doId, self.ownerId)) + return + self.setHasRequestedDelete(True) + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.requestDelete(self) + + def _doDeleteCleanup(self): + self.trickLogger.destroy() + self.trickFailLogger.destroy() + self.feedLogger.destroy() + self.scratchLogger.destroy() + del self.trickLogger + del self.trickFailLogger + del self.feedLogger + del self.scratchLogger + taskMgr.remove(self.uniqueName('clearMovie')) + taskMgr.remove(self.uniqueName('PetMovieWait')) + taskMgr.remove(self.uniqueName('PetMovieClear')) + taskMgr.remove(self.uniqueName('PetMovieComplete')) + taskMgr.remove(self.getLockMoveTaskName()) + taskMgr.remove(self.getMoveTaskName()) + if hasattr(self, 'zoneId'): + self.announceZoneChange(ToontownGlobals.QuietZone, self.zoneId) + else: + myDoId = 'No doId' + myTaskName = 'No task name' + myStackTrace = StackTrace().trace + myOldStackTrace = 'No Trace' + if hasattr(self, 'doId'): + myDoId = self.doId + if task: + myTaskName = task.name + if hasattr(self, 'destroyDoStackTrace'): + myOldStackTrace = self.destroyDoStackTrace.trace + simbase.air.writeServerEvent('Pet RequestDelete duplicate', myDoId, 'from task %s' % myTaskName) + simbase.air.writeServerEvent('Pet RequestDelete duplicate StackTrace', myDoId, '%s' % myStackTrace) + simbase.air.writeServerEvent('Pet RequestDelete duplicate OldStackTrace', myDoId, '%s' % myOldStackTrace) + DistributedPetAI.notify.warning('double requestDelete from task %s' % myTaskName) + self.setParent(ToontownGlobals.SPHidden) + if hasattr(self, 'activated'): + if self.activated: + self.activated = 0 + self.brain.destroy() + del self.brain + self.actionFSM.destroy() + del self.actionFSM + self.exitPetLook() + self.destroyImpulses() + self.mover.destroy() + del self.mover + self.lockMover.destroy() + del self.lockMover + self.stopPosHprBroadcast() + if hasattr(self, 'mood'): + self.mood.destroy() + del self.mood + if hasattr(self, 'traits'): + del self.traits + try: + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + except: + pass + + if hasattr(self, 'gaitFSM'): + if self.gaitFSM: + self.gaitFSM.requestFinalState() + del self.gaitFSM + if hasattr(self, 'unstickFSM'): + if self.unstickFSM: + self.unstickFSM.requestFinalState() + del self.unstickFSM + if __dev__: + del self.pscMoveResc + PetLookerAI.PetLookerAI.destroy(self) + self.ignoreAll() + self._hasCleanedUp = True + + def delete(self): + DistributedPetAI.notify.info('PetAI.delete: %s, owner=%s' % (self.doId, self.ownerId)) + if not self._hasCleanedUp: + self._doDeleteCleanup() + self.setHasRequestedDelete(False) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + + def patchDelete(self): + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + del self.gaitFSM + del self.unstickFSM + if __dev__: + del self.pscMoveResc + PetLookerAI.PetLookerAI.destroy(self) + self.doNotDeallocateChannel = True + self.zoneId = None + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + self.ignoreAll() + return + + def createImpulses(self): + self.createSphereImpulse() + self.chaseImpulse = CPetChase() + self.fleeImpulse = CPetFlee() + self.wanderImpulse = PetWander.PetWander() + self.lockChaseImpulse = CPetChase() + + def destroyImpulses(self): + self.wanderImpulse.destroy() + del self.chaseImpulse + del self.fleeImpulse + del self.wanderImpulse + self.destroySphereImpulse() + del self.lockChaseImpulse + + def createSphereImpulse(self): + petRadius = 1.0 + collTrav = self.getCollTrav() + if collTrav is None: + DistributedPetAI.notify.warning('no collision traverser for zone %s' % self.zoneId) + else: + self.sphereImpulse = PetSphere.PetSphere(petRadius, collTrav) + self.mover.addImpulse('sphere', self.sphereImpulse) + return + + def destroySphereImpulse(self): + self.mover.removeImpulse('sphere') + if hasattr(self, 'sphereImpulse'): + self.sphereImpulse.destroy() + del self.sphereImpulse + + def getMoveTaskName(self): + return 'petMove-%s' % self.doId + + def getLockMoveTaskName(self): + return 'petLockMove-%s' % self.doId + + def move(self, task = None): + if self.isEmpty(): + try: + self.air.writeServerEvent('Late Pet Move Call', self.doId, ' ') + except: + pass + + taskMgr.remove(task.name) + return Task.done + if not self.isLockMoverEnabled(): + self.mover.move() + numNearby = len(self.brain.nearbyAvs) - 1 + minNearby = 5 + if numNearby > minNearby: + delay = 0.08 * (numNearby - minNearby) + self.setPosHprBroadcastPeriod(PetConstants.PosBroadcastPeriod + lerp(delay * 0.75, delay, random.random())) + maxDist = 1000 + if abs(self.getX()) > maxDist or abs(self.getY()) > maxDist: + DistributedPetAI.notify.warning('deleting pet %s before he wanders off too far' % self.doId) + self._outOfBounds = True + self.stopPosHprBroadcast() + self.requestDelete() + return Task.done + if __dev__: + self.pscMoveResc.start() + taskMgr.doMethodLater(simbase.petMovePeriod, self.move, self.getMoveTaskName()) + if __dev__: + self.pscMoveResc.stop() + return Task.done + + def startPosHprBroadcast(self): + if self._outOfBounds: + return + DistributedSmoothNodeAI.DistributedSmoothNodeAI.startPosHprBroadcast(self, period=simbase.petPosBroadcastPeriod, type=DistributedSmoothNodeBase.DistributedSmoothNodeBase.BroadcastTypes.XYH) + + def setMoodComponent(self, component, value): + setter = self.getSetterName(component, 'b_set') + self.__dict__[setter](value) + + def addToMood(self, component, delta): + value = self.mood.getComponent(component) + value += delta + self.setMoodComponent(component, clampScalar(value, 0.0, 1.0)) + + def lerpMood(self, component, factor): + curVal = self.mood.getComponent(component) + if factor < 0: + self.setMoodComponent(component, lerp(curVal, 0.0, -factor)) + else: + self.setMoodComponent(component, lerp(curVal, 1.0, factor)) + + def addToMoods(self, mood2delta): + for mood, delta in mood2delta.items(): + self.addToMood(mood, delta) + + def lerpMoods(self, mood2factor): + for mood, factor in mood2factor.items(): + self.lerpMood(mood, factor) + + def handleMoodChange(self, components = [], distribute = 1): + if len(components) == 0: + components = PetMood.PetMood.Components + if distribute: + if len(components) == len(PetMood.PetMood.Components): + values = [] + for comp in PetMood.PetMood.Components: + values.append(self.mood.getComponent(comp)) + + self.sendUpdate('setMood', values) + else: + for comp in components: + setter = self.getSetterName(comp, 'd_set') + self.__dict__[setter](self.mood.getComponent(comp)) + + if self.isExcited(): + self.gaitFSM.request('happy') + elif self.isSad(): + self.gaitFSM.request('sad') + else: + self.gaitFSM.request('neutral') + + def isContented(self): + return self.mood.getDominantMood() in PetMood.PetMood.ContentedMoods + + def call(self, avatar): + self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.COME, avatar.doId)) + self.__petMovieStart(avatar.doId) + + def feed(self, avatar): + if avatar.takeMoney(PetConstants.FEED_AMOUNT): + self.startLockPetMove(avatar.doId) + self.brain.observe(PetObserve.PetActionObserve(PetObserve.Actions.FEED, avatar.doId)) + self.feedLogger.addEvent() + + def scratch(self, avatar): + self.startLockPetMove(avatar.doId) + self.brain.observe(PetObserve.PetActionObserve(PetObserve.Actions.SCRATCH, avatar.doId)) + self.scratchLogger.addEvent() + + def lockPet(self): + DistributedPetAI.notify.debug('%s: lockPet' % self.doId) + if not self.lockedDown: + self.stopPosHprBroadcast() + self.lockedDown += 1 + + def isLockedDown(self): + return self.lockedDown != 0 + + def unlockPet(self): + DistributedPetAI.notify.debug('%s: unlockPet' % self.doId) + if self.lockedDown <= 0: + DistributedPetAI.notify.warning('%s: unlockPet called on unlocked pet' % self.doId) + else: + self.lockedDown -= 1 + if not self.lockedDown and not self.isDeleted(): + self.startPosHprBroadcast() + + def handleStay(self, avatar): + self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.STAY, avatar.doId)) + + def handleShoo(self, avatar): + self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.GO_AWAY, avatar.doId)) + + def gaitEnterOff(self): + pass + + def gaitExitOff(self): + pass + + def gaitEnterNeutral(self): + self.mover.setFwdSpeed(PetConstants.FwdSpeed) + self.mover.setRotSpeed(PetConstants.RotSpeed) + + def gaitExitNeutral(self): + pass + + def gaitEnterHappy(self): + self.mover.setFwdSpeed(PetConstants.HappyFwdSpeed) + self.mover.setRotSpeed(PetConstants.HappyRotSpeed) + + def gaitExitHappy(self): + pass + + def gaitEnterSad(self): + self.mover.setFwdSpeed(PetConstants.SadFwdSpeed) + self.mover.setRotSpeed(PetConstants.SadRotSpeed) + + def gaitExitSad(self): + pass + + def unstickEnterOff(self): + pass + + def unstickExitOff(self): + pass + + def unstickEnterOn(self): + self._collisionTimestamps = [] + self.accept(self.mover.getCollisionEventName(), self._handleCollided) + + def _handleCollided(self, collEntry): + now = globalClock.getFrameTime() + self._collisionTimestamps.append(now) + while now - self._collisionTimestamps[0] > PetConstants.UnstickSampleWindow: + del self._collisionTimestamps[0:1] + + if len(self._collisionTimestamps) > PetConstants.UnstickCollisionThreshold: + self._collisionTimestamps = [] + DistributedPetAI.notify.debug('unsticking pet %s' % self.doId) + self.brain._unstick() + + def unstickExitOn(self): + pass + + def ownerIsOnline(self): + return self.ownerId in simbase.air.doId2do + + def ownerIsInSameZone(self): + if not self.ownerIsOnline(): + return 0 + return self.zoneId == simbase.air.doId2do[self.ownerId].zoneId + + def _getOwnerDict(self): + if self.owner is not None: + if self.ownerIsInSameZone(): + return {self.ownerId: self.owner} + return {} + + def _getFullNearbyToonDict(self): + toons = self.air.getObjectsOfClassInZone(self.air.districtId, self.zoneId, DistributedToonAI.DistributedToonAI) + return toons + + def _getNearbyToonDict(self): + toons = self._getFullNearbyToonDict() + if self.ownerId in toons: + del toons[self.ownerId] + return toons + + def _getNearbyPetDict(self): + pets = self.air.getObjectsOfClassInZone(self.air.districtId, self.zoneId, DistributedPetAI) + if self.doId in pets: + del pets[self.doId] + return pets + + def _getNearbyAvatarDict(self): + avs = self._getFullNearbyToonDict() + avs.update(self._getNearbyPetDict()) + return avs + + def _getOwner(self): + return self.air.doId2do.get(self.ownerId) + + def _getNearbyToon(self): + nearbyToonDict = self._getFullNearbyToonDict() + if not len(nearbyToonDict): + return None + return nearbyToonDict[random.choice(nearbyToonDict.keys())] + + def _getNearbyToonNonOwner(self): + nearbyToonDict = self._getNearbyToonDict() + if not len(nearbyToonDict): + return None + return nearbyToonDict[random.choice(nearbyToonDict.keys())] + + def _getNearbyPet(self): + nearbyPetDict = self._getNearbyPetDict() + if not len(nearbyPetDict): + return None + return nearbyPetDict[random.choice(nearbyPetDict.keys())] + + def _getNearbyAvatar(self): + nearbyAvDict = self._getNearbyAvatarDict() + if not len(nearbyAvDict): + return None + return nearbyAvDict[random.choice(nearbyAvDict.keys())] + + def isBusy(self): + return self.busy > 0 + + def freeAvatar(self, avId): + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + + def avatarInteract(self, avId): + av = self.air.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar: %s not found' % avId) + return 0 + if self.isBusy(): + self.notify.debug('freeing avatar!') + self.freeAvatar(avId) + return 0 + self.busy = avId + self.notify.debug('sending update') + self.sendUpdateToAvatarId(avId, 'avatarInteract', [avId]) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + return 1 + + def rejectAvatar(self, avId): + self.notify.error('rejectAvatar: should not be called by a pet!') + + def d_setMovie(self, avId, flag): + self.sendUpdate('setMovie', [flag, avId, ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendClearMovie(self, task = None): + if self.air != None: + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, PetConstants.PET_MOVIE_CLEAR) + return Task.done + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie() + + def handleAvPetInteraction(self, mode, avId): + if mode not in (PetConstants.PET_MOVIE_SCRATCH, PetConstants.PET_MOVIE_FEED, PetConstants.PET_MOVIE_CALL): + self.air.writeServerEvent('suspicious', avId, 'DistributedPetAI: unknown mode: %s' % mode) + return + if self.avatarInteract(avId): + self.notify.debug('handleAvPetInteraction() avatarInteract calling callback') + self.movieMode = mode + callback = {PetConstants.PET_MOVIE_SCRATCH: self.scratch, + PetConstants.PET_MOVIE_FEED: self.feed, + PetConstants.PET_MOVIE_CALL: self.call}.get(mode) + callback(self.air.doId2do.get(avId)) + else: + self.notify.debug('handleAvPetInteraction() avatarInteract was busy or unhappy') + + def __petMovieStart(self, avId): + self.d_setMovie(avId, self.movieMode) + time = self.movieTimeSwitch.get(self.movieMode) + taskMgr.doMethodLater(time, self.__petMovieComplete, self.uniqueName('PetMovieComplete')) + + def __petMovieComplete(self, task = None): + self.disableLockMover() + self.unlockPet() + self.sendClearMovie() + self.movieMode = None + return Task.done + + def startLockPetMove(self, avId): + self.enableLockMover() + self.lockChaseImpulse.setTarget(self.air.doId2do.get(avId)) + self.lockMover.addImpulse('LockTarget', self.lockChaseImpulse) + self.lockMover.setFwdSpeed(self.mover.getFwdSpeed()) + self.lockMover.setRotSpeed(self.mover.getRotSpeed()) + dist_Callable = self.movieDistSwitch.get(self.movieMode) + dist = dist_Callable(self.air.doId2do.get(avId).getStyle().getLegSize()) + self.lockChaseImpulse.setMinDist(dist) + self.distList = [0, 0, 0] + self.__lockPetMoveTask(avId) + + def getAverageDist(self): + sum = 0 + for i in self.distList: + sum += i + + return sum / 3.0 + + def __lockPetMoveTask(self, avId): + if not hasattr(self, 'air') or not self.air.doId2do.has_key(avId): + self.notify.warning('avId: %s gone or self deleted!' % avId) + return Task.done + av = self.air.doId2do.get(avId) + dist = av.getDistance(self) + self.distList.append(dist) + if len(self.distList) > 3: + self.distList.pop(0) + if self.movieDistSwitch.has_key(self.movieMode): + dist_Callable = self.movieDistSwitch.get(self.movieMode) + movieDist = dist_Callable(av.getStyle().getLegSize()) + else: + self.notify.warning('movieMode: %s not in movieSwitchDist map!' % self.movieMode) + return Task.done + avgDist = self.getAverageDist() + if dist - movieDist > 0.25 and abs(avgDist - dist) > 0.1: + self.lockMover.move() + taskMgr.doMethodLater(simbase.petMovePeriod, self.__lockPetMoveTask, self.getLockMoveTaskName(), [avId]) + else: + self.endLockPetMove(avId) + return Task.done + + def endLockPetMove(self, avId): + del self.distList + taskMgr.remove(self.getLockMoveTaskName()) + self.lockPet() + self.lockMover.removeImpulse('LockTarget') + self.__petMovieStart(avId) + + def enableLockMover(self): + if self.lockMoverEnabled == 0: + self.brain._startMovie() + self.lockMoverEnabled += 1 + + def isLockMoverEnabled(self): + return self.lockMoverEnabled > 0 + + def disableLockMover(self): + if self.lockMoverEnabled > 0: + self.lockMoverEnabled -= 1 + if self.lockMoverEnabled == 0: + self.brain._endMovie() + + def _willDoTrick(self, trickId): + if self.isContented(): + minApt = PetTricks.MinActualTrickAptitude + maxApt = PetTricks.MaxActualTrickAptitude + else: + minApt = PetTricks.NonHappyMinActualTrickAptitude + maxApt = PetTricks.NonHappyMaxActualTrickAptitude + randVal = random.random() + cutoff = lerp(minApt, maxApt, self.getTrickAptitude(trickId)) + if self.mood.isComponentActive('fatigue'): + cutoff *= 0.5 + cutoff *= PetTricks.TrickAccuracies[trickId] + DistributedPetAI.notify.debug('_willDoTrick: %s / %s' % (randVal, cutoff)) + return randVal < cutoff + + def _handleDidTrick(self, trickId): + DistributedPetAI.notify.debug('_handleDidTrick: %s' % trickId) + if trickId == PetTricks.Tricks.BALK: + return + aptitude = self.getTrickAptitude(trickId) + self.setTrickAptitude(trickId, aptitude + PetTricks.AptitudeIncrementDidTrick) + self.addToMood('fatigue', lerp(PetTricks.MaxTrickFatigue, PetTricks.MinTrickFatigue, aptitude)) + self.trickLogger.addEvent(trickId) + + def _handleGotPositiveTrickFeedback(self, trickId, magnitude): + if trickId == PetTricks.Tricks.BALK: + return + self.setTrickAptitude(trickId, self.getTrickAptitude(trickId) + PetTricks.MaxAptitudeIncrementGotPraise * magnitude) + + def toggleLeash(self, avId): + if self.leashMode: + self.leashMode = 0 + self.leashAvId = None + self.brain.goalMgr.removeGoal(self.leashGoal) + del self.leashGoal + response = 'leash OFF' + else: + self.leashMode = 1 + self.leashAvId = avId + self.leashGoal = PetGoal.ChaseAvatarLeash(avId) + self.brain.goalMgr.addGoal(self.leashGoal) + response = 'leash ON' + return response diff --git a/toontown/pets/DistributedPetProxy.py b/toontown/pets/DistributedPetProxy.py new file mode 100644 index 0000000..0e5a693 --- /dev/null +++ b/toontown/pets/DistributedPetProxy.py @@ -0,0 +1,189 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.pets import PetTraits +from toontown.pets import PetMood, PetTricks +from toontown.toonbase import ToontownGlobals +import string + +class DistributedPetProxy(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPetProxy') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + self.dominantMood = 'neutral' + self.sendGenerateMessage = 0 + self.trickAptitudes = [] + self.ghostMode = False + self.bFake = False + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.requiredMoodComponents = {} + + def getSetterName(self, valueName, prefix = 'set'): + return '%s%s%s' % (prefix, string.upper(valueName[0]), valueName[1:]) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getOwnerId(self): + return self.ownerId + + def setPetName(self, petName): + self.petName = petName + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + setterName = self.getSetterName(traitName) + + def traitSetter(value, self = self, i = i): + self.traitList[i] = value + + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(setterName) + + def setHead(self, head): + DistributedPetProxy.notify.debug('setHead: %s' % head) + self.head = head + + def setEars(self, ears): + DistributedPetProxy.notify.debug('setEars: %s' % ears) + self.ears = ears + + def setNose(self, nose): + DistributedPetProxy.notify.debug('setNose: %s' % nose) + self.nose = nose + + def setTail(self, tail): + DistributedPetProxy.notify.debug('setTail: %s' % tail) + self.tail = tail + + def setBodyTexture(self, bodyTexture): + DistributedPetProxy.notify.debug('setBodyTexture: %s' % bodyTexture) + self.bodyTexture = bodyTexture + + def setColor(self, color): + DistributedPetProxy.notify.debug('setColor: %s' % color) + self.color = color + + def setColorScale(self, colorScale): + DistributedPetProxy.notify.debug('setColorScale: %s' % colorScale) + self.colorScale = colorScale + + def setEyeColor(self, eyeColor): + DistributedPetProxy.notify.debug('setEyeColor: %s' % eyeColor) + self.eyeColor = eyeColor + + def setGender(self, gender): + DistributedPetProxy.notify.debug('setGender: %s' % gender) + self.gender = gender + + def getDNA(self): + return self.style + + def getName(self): + return self.petName + + def getFont(self): + return ToontownGlobals.getToonFont() + + def setLastSeenTimestamp(self, timestamp): + DistributedPetProxy.notify.debug('setLastSeenTimestamp: %s' % timestamp) + self.lastSeenTimestamp = timestamp + + def getTimeSinceLastSeen(self): + t = self.cr.getServerTimeOfDay() - self.lastSeenTimestamp + return max(0.0, t) + + def updateOfflineMood(self): + self.mood.driftMood(dt=self.getTimeSinceLastSeen(), curMood=self.lastKnownMood) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + setterName = self.getSetterName(compName) + + def moodSetter(value, self = self, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(setterName) + + def setMood(self, *componentValues): + for value, name in zip(componentValues, PetMood.PetMood.Components): + setterName = self.getSetterName(name) + self.__dict__[setterName](value) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + print self.traits.traits + self.mood = PetMood.PetMood(self) + self.lastKnownMood = self.mood.makeCopy() + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + DistributedPetProxy.notify.debug('time since last seen: %s' % self.getTimeSinceLastSeen()) + self.style = [self.head, + self.ears, + self.nose, + self.tail, + self.bodyTexture, + self.color, + self.colorScale, + self.eyeColor, + self.gender] + self.setLastSeenTimestamp(self.lastSeenTimestamp) + self.updateOfflineMood() + self.sendGenerateMessage = 1 + + def disable(self): + if hasattr(self, 'lastKnownMood'): + self.lastKnownMood.destroy() + del self.lastKnownMood + self.mood.destroy() + del self.mood + del self.traits + DistributedObject.DistributedObject.disable(self) + + def delete(self): + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + DistributedObject.DistributedObject.delete(self) + + def setDominantMood(self, dominantMood): + self.dominantMood = dominantMood + if self.sendGenerateMessage == 1: + proxyGenerateMessage = 'petProxy-%d-generated' % self.doId + messenger.send(proxyGenerateMessage) + self.sendGenerateMessage = 0 + + def getDominantMood(self): + return self.dominantMood + + def setTrickAptitudes(self, aptitudes): + self.trickAptitudes = aptitudes + + def isPet(self): + return True + + def isProxy(self): + return True diff --git a/toontown/pets/DistributedPetProxyAI.py b/toontown/pets/DistributedPetProxyAI.py new file mode 100644 index 0000000..0e9f998 --- /dev/null +++ b/toontown/pets/DistributedPetProxyAI.py @@ -0,0 +1,473 @@ +from direct.showbase.PythonUtil import contains, lerp, clampScalar +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.pets import PetTraits, PetTricks +from toontown.pets import PetMood +from toontown.toonbase import ToontownGlobals +import random +import time +import string +import copy +BATTLE_TRICK_HP_MULTIPLIER = 10.0 + +class DistributedPetProxyAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPetProxyAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.ownerId = 0 + self.petName = 'unnamed' + self.traitSeed = 0 + self.safeZone = ToontownGlobals.ToontownCentral + self.traitList = [0] * PetTraits.PetTraits.NumTraits + self.head = -1 + self.ears = -1 + self.nose = -1 + self.tail = -1 + self.bodyTexture = 0 + self.color = 0 + self.colorScale = 0 + self.eyeColor = 0 + self.gender = 0 + self.trickAptitudes = [] + self.lastSeenTimestamp = self.getCurEpochTimestamp() + self.requiredMoodComponents = {} + self.__funcsToDelete = [] + self.__generateDistTraitFuncs() + self.__generateDistMoodFuncs() + + def getSetterName(self, valueName, prefix = 'set'): + return '%s%s%s' % (prefix, string.upper(valueName[0]), valueName[1:]) + + def setDNA(self, dna): + head, ears, nose, tail, body, color, colorScale, eyes, gender = dna + self.b_setHead(head) + self.b_setEars(ears) + self.b_setNose(nose) + self.b_setTail(tail) + self.b_setBodyTexture(body) + self.b_setColor(color) + self.b_setColorScale(colorScale) + self.b_setEyeColor(eyes) + self.b_setGender(gender) + + def getOwnerId(self): + return self.ownerId + + def b_setOwnerId(self, ownerId): + self.d_setOwnerId(ownerId) + self.setOwnerId(ownerId) + + def d_setOwnerId(self, ownerId): + self.sendUpdate('setOwnerId', [ownerId]) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def getPetName(self): + return self.petName + + def b_setPetName(self, petName): + self.d_setPetName(petName) + self.setPetName(petName) + + def d_setPetName(self, petName): + self.sendUpdate('setPetName', [petName]) + + def setPetName(self, petName): + self.petName = petName + + def getTraitSeed(self): + return self.traitSeed + + def b_setTraitSeed(self, traitSeed): + self.d_setTraitSeed(traitSeed) + self.setTraitSeed(traitSeed) + + def d_setTraitSeed(self, traitSeed): + self.sendUpdate('setTraitSeed', [traitSeed]) + + def setTraitSeed(self, traitSeed): + self.traitSeed = traitSeed + + def getSafeZone(self): + return self.safeZone + + def b_setSafeZone(self, safeZone): + self.d_setSafeZone(safeZone) + self.setSafeZone(safeZone) + + def d_setSafeZone(self, safeZone): + self.sendUpdate('setSafeZone', [safeZone]) + + def setSafeZone(self, safeZone): + self.safeZone = safeZone + + def setTraits(self, traitList): + self.traitList = traitList + + def __generateDistTraitFuncs(self): + for i in xrange(PetTraits.PetTraits.NumTraits): + traitName = PetTraits.getTraitNames()[i] + getterName = self.getSetterName(traitName, 'get') + b_setterName = self.getSetterName(traitName, 'b_set') + d_setterName = self.getSetterName(traitName, 'd_set') + setterName = self.getSetterName(traitName) + + def traitGetter(i = i): + return self.traitList[i] + + def b_traitSetter(value, setterName = setterName, d_setterName = d_setterName): + self.__dict__[d_setterName](value) + self.__dict__[setterName](value) + + def d_traitSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def traitSetter(value, i = i): + self.traitList[i] = value + + self.__dict__[getterName] = traitGetter + self.__dict__[b_setterName] = b_traitSetter + self.__dict__[d_setterName] = d_traitSetter + self.__dict__[setterName] = traitSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append(b_setterName) + self.__funcsToDelete.append(d_setterName) + self.__funcsToDelete.append(setterName) + + def getHead(self): + return self.head + + def b_setHead(self, head): + self.d_setHead(head) + self.setHead(head) + + def d_setHead(self, head): + self.sendUpdate('setHead', [head]) + + def setHead(self, head): + self.head = head + + def getEars(self): + return self.ears + + def b_setEars(self, ears): + self.d_setEars(ears) + self.setEars(ears) + + def d_setEars(self, ears): + self.sendUpdate('setEars', [ears]) + + def setEars(self, ears): + self.ears = ears + + def getNose(self): + return self.nose + + def b_setNose(self, nose): + self.d_setNose(nose) + self.setNose(nose) + + def d_setNose(self, nose): + self.sendUpdate('setNose', [nose]) + + def setNose(self, nose): + self.nose = nose + + def getTail(self): + return self.tail + + def b_setTail(self, tail): + self.d_setTail(tail) + self.setTail(tail) + + def d_setTail(self, tail): + self.sendUpdate('setTail', [tail]) + + def setTail(self, tail): + self.tail = tail + + def getBodyTexture(self): + return self.bodyTexture + + def b_setBodyTexture(self, bodyTexture): + self.d_setBodyTexture(bodyTexture) + self.setBodyTexture(bodyTexture) + + def d_setBodyTexture(self, bodyTexture): + self.sendUpdate('setBodyTexture', [bodyTexture]) + + def setBodyTexture(self, bodyTexture): + self.bodyTexture = bodyTexture + + def getColor(self): + return self.color + + def b_setColor(self, color): + self.d_setColor(color) + self.setColor(color) + + def d_setColor(self, color): + self.sendUpdate('setColor', [color]) + + def setColor(self, color): + self.color = color + + def getColorScale(self): + return self.colorScale + + def b_setColorScale(self, colorScale): + self.d_setColorScale(colorScale) + self.setColorScale(colorScale) + + def d_setColorScale(self, colorScale): + self.sendUpdate('setColorScale', [colorScale]) + + def setColorScale(self, colorScale): + self.colorScale = colorScale + + def getEyeColor(self): + return self.eyeColor + + def b_setEyeColor(self, eyeColor): + self.d_setEyeColor(eyeColor) + self.setEyeColor(eyeColor) + + def d_setEyeColor(self, eyeColor): + self.sendUpdate('setEyeColor', [eyeColor]) + + def setEyeColor(self, eyeColor): + self.eyeColor = eyeColor + + def getGender(self): + return self.gender + + def b_setGender(self, gender): + self.d_setGender(gender) + self.setGender(gender) + + def d_setGender(self, gender): + self.sendUpdate('setGender', [gender]) + + def setGender(self, gender): + self.gender = gender + + def getLastSeenTimestamp(self): + return self.lastSeenTimestamp + + def b_setLastSeenTimestamp(self, timestamp): + self.d_setLastSeenTimestamp(timestamp) + self.setLastSeenTimestamp(timestamp) + + def d_setLastSeenTimestamp(self, timestamp): + self.sendUpdate('setLastSeenTimestamp', [timestamp]) + + def setLastSeenTimestamp(self, timestamp): + self.lastSeenTimestamp = timestamp + + def getCurEpochTimestamp(self): + return int(time.time()) + + def getTimeSinceLastSeen(self): + t = time.time() - self.lastSeenTimestamp + return max(0.0, t) + + def __handleMoodSet(self, component, value): + if self.isGenerated(): + self.mood.setComponent(component, value) + else: + self.requiredMoodComponents[component] = value + + def __handleMoodGet(self, component): + if self.isGenerated(): + return self.mood.getComponent(component) + else: + return 0.0 + + def __generateDistMoodFuncs(self): + for compName in PetMood.PetMood.Components: + getterName = self.getSetterName(compName, 'get') + setterName = self.getSetterName(compName) + + def moodGetter(compName = compName): + return self.__handleMoodGet(compName) + + def b_moodSetter(value, setterName = setterName): + self.__dict__[setterName](value) + + def d_moodSetter(value, setterName = setterName): + self.sendUpdate(setterName, [value]) + + def moodSetter(value, compName = compName): + self.__handleMoodSet(compName, value) + + self.__dict__[getterName] = moodGetter + self.__dict__['b_%s' % setterName] = b_moodSetter + self.__dict__['d_%s' % setterName] = d_moodSetter + self.__dict__[setterName] = moodSetter + self.__funcsToDelete.append(getterName) + self.__funcsToDelete.append('b_%s' % setterName) + self.__funcsToDelete.append('d_%s' % setterName) + self.__funcsToDelete.append(setterName) + + def getTrickAptitudes(self): + return self.trickAptitudes + + def b_setTrickAptitudes(self, aptitudes): + self.setTrickAptitudes(aptitudes, local=1) + self.d_setTrickAptitudes(aptitudes) + + def d_setTrickAptitudes(self, aptitudes): + if __dev__: + for aptitude in aptitudes: + pass + + while len(aptitudes) < len(PetTricks.Tricks) - 1: + aptitudes.append(0.0) + + self.sendUpdate('setTrickAptitudes', [aptitudes]) + + def setTrickAptitudes(self, aptitudes, local = 0): + if not local: + DistributedPetProxyAI.notify.debug('setTrickAptitudes: %s' % aptitudes) + while len(self.trickAptitudes) < len(PetTricks.Tricks) - 1: + self.trickAptitudes.append(0.0) + + self.trickAptitudes = aptitudes + + def getTrickAptitude(self, trickId): + if trickId > len(self.trickAptitudes) - 1: + return 0.0 + return self.trickAptitudes[trickId] + + def setTrickAptitude(self, trickId, aptitude, send = 1): + aptitude = clampScalar(aptitude, 0.0, 1.0) + aptitudes = self.trickAptitudes + while len(aptitudes) - 1 < trickId: + aptitudes.append(0.0) + + if aptitudes[trickId] != aptitude: + aptitudes[trickId] = aptitude + if send: + self.b_setTrickAptitudes(aptitudes) + else: + self.setTrickAptitudes(aptitudes, local=1) + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone) + print self.traits.traits + for i in xrange(len(self.traitList)): + value = self.traitList[i] + if value == 0.0: + traitName = PetTraits.getTraitNames()[i] + traitValue = self.traits.getTraitValue(traitName) + DistributedPetProxyAI.notify.info("%s: initializing new trait '%s' to %s, seed=%s" % (self.doId, + traitName, + traitValue, + self.traitSeed)) + setterName = self.getSetterName(traitName, 'b_set') + self.__dict__[setterName](traitValue) + + self.mood = PetMood.PetMood(self) + for mood, value in self.requiredMoodComponents.items(): + self.mood.setComponent(mood, value, announce=0) + + self.requiredMoodComponents = {} + self.accept(self.mood.getMoodChangeEvent(), self.handleMoodChange) + self.mood.start() + + def broadcastDominantMood(self): + self.d_setDominantMood(self.mood.getDominantMood()) + + def delete(self): + self.ignore(self.mood.getMoodChangeEvent()) + self.mood.destroy() + del self.mood + del self.traits + for funcName in self.__funcsToDelete: + del self.__dict__[funcName] + + DistributedObjectAI.DistributedObjectAI.delete(self) + + def setMoodComponent(self, component, value): + setter = self.getSetterName(component, 'b_set') + self.__dict__[setter](value) + + def addToMood(self, component, delta): + value = self.mood.getComponent(component) + value += delta + self.setMoodComponent(component, clampScalar(value, 0.0, 1.0)) + + def lerpMood(self, component, factor): + curVal = self.mood.getComponent(component) + if factor < 0: + self.setMoodComponent(component, lerp(curVal, 0.0, -factor)) + else: + self.setMoodComponent(component, lerp(curVal, 1.0, factor)) + + def addToMoods(self, mood2delta): + for mood, delta in mood2delta.items(): + self.addToMood(mood, delta) + + def lerpMoods(self, mood2factor): + for mood, factor in mood2factor.items(): + self.lerpMood(mood, factor) + + def isContented(self): + return self.mood.getDominantMood() in PetMood.PetMood.ContentedMoods + + def _willDoTrick(self, trickId): + if self.isContented(): + minApt = PetTricks.MinActualTrickAptitude + maxApt = PetTricks.MaxActualTrickAptitude + else: + minApt = PetTricks.NonHappyMinActualTrickAptitude + maxApt = PetTricks.NonHappyMaxActualTrickAptitude + randVal = random.random() + cutoff = lerp(minApt, maxApt, self.getTrickAptitude(trickId)) + if self.mood.isComponentActive('fatigue'): + cutoff *= 0.5 + cutoff *= PetTricks.TrickAccuracies[trickId] + DistributedPetProxyAI.notify.debug('_willDoTrick: %s / %s' % (randVal, cutoff)) + return randVal < cutoff + + def _handleDidTrick(self, trickId): + DistributedPetProxyAI.notify.debug('_handleDidTrick: %s' % trickId) + if trickId == PetTricks.Tricks.BALK: + return + aptitude = self.getTrickAptitude(trickId) + self.setTrickAptitude(trickId, aptitude + PetTricks.AptitudeIncrementDidTrick) + self.addToMood('fatigue', lerp(PetTricks.MaxTrickFatigue, PetTricks.MinTrickFatigue, aptitude)) + self.d_setDominantMood(self.mood.getDominantMood()) + + def attemptBattleTrick(self, trickId): + self.lerpMoods({'boredom': -.1, + 'excitement': 0.05, + 'loneliness': -.05}) + if self._willDoTrick(trickId): + self._handleDidTrick(trickId) + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + return 0 + else: + self.b_setLastSeenTimestamp(self.getCurEpochTimestamp()) + return 1 + + def handleMoodChange(self, components = [], distribute = 1): + if len(components) == 0: + components = PetMood.PetMood.Components + if distribute: + if len(components) == len(PetMood.PetMood.Components): + values = [] + for comp in PetMood.PetMood.Components: + values.append(self.mood.getComponent(comp)) + + self.sendUpdate('setMood', values) + else: + for comp in components: + setter = self.getSetterName(comp, 'd_set') + self.__dict__[setter](self.mood.getComponent(comp)) + + def d_setDominantMood(self, dominantMood): + self.sendUpdate('setDominantMood', [dominantMood]) diff --git a/toontown/pets/Pet.py b/toontown/pets/Pet.py new file mode 100644 index 0000000..1c0acc3 --- /dev/null +++ b/toontown/pets/Pet.py @@ -0,0 +1,673 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from direct.fsm.ClassicFSM import * +from direct.fsm.State import * +from direct.distributed.ClockDelta import globalClockDelta +from otp.avatar import Avatar +from direct.actor import Actor +from direct.task import Task +from toontown.pets import PetDNA +from PetDNA import HeadParts, EarParts, NoseParts, TailParts, BodyTypes, BodyTextures, AllPetColors, getColors, ColorScales, PetEyeColors, EarTextures, TailTextures, getFootTexture, getEarTexture, GiraffeTail, LeopardTail, PetGenders +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from direct.showbase import PythonUtil +import random +import types +Component2IconDict = {'boredom': 'Bored', + 'restlessness': None, + 'playfulness': 'Play', + 'loneliness': 'Lonely', + 'sadness': 'Sad', + 'fatigue': 'Sleepy', + 'hunger': 'Hungry', + 'confusion': 'Confused', + 'excitement': 'Surprised', + 'anger': 'Angry', + 'surprise': 'Surprised', + 'affection': 'Love'} + +class Pet(Avatar.Avatar): + notify = DirectNotifyGlobal.directNotify.newCategory('Pet') + SerialNum = 0 + Interactions = PythonUtil.Enum('SCRATCH, BEG, EAT, NEUTRAL') + InteractAnims = {Interactions.SCRATCH: ('toPet', 'pet', 'fromPet'), + Interactions.BEG: ('toBeg', 'beg', 'fromBeg'), + Interactions.EAT: ('eat', 'swallow', 'neutral'), + Interactions.NEUTRAL: 'neutral'} + + def __init__(self, forGui = 0): + Avatar.Avatar.__init__(self) + self.serialNum = Pet.SerialNum + Pet.SerialNum += 1 + self.lockedDown = 0 + self.setPickable(1) + self.setPlayerType(NametagGroup.CCNonPlayer) + self.animFSM = ClassicFSM('petAnimFSM', [State('off', self.enterOff, self.exitOff), + State('neutral', self.enterNeutral, self.exitNeutral), + State('neutralHappy', self.enterNeutralHappy, self.exitNeutralHappy), + State('neutralSad', self.enterNeutralSad, self.exitNeutralSad), + State('run', self.enterRun, self.exitRun), + State('swim', self.enterSwim, self.exitSwim), + State('teleportIn', self.enterTeleportIn, self.exitTeleportOut), + State('teleportOut', self.enterTeleportOut, self.exitTeleportOut), + State('walk', self.enterWalk, self.exitWalk), + State('walkHappy', self.enterWalkHappy, self.exitWalkHappy), + State('walkSad', self.enterWalkSad, self.exitWalkSad)], 'off', 'off') + self.animFSM.enterInitialState() + self.forGui = forGui + self.moodModel = None + self.__blinkName = 'petblink-' + str(self.this) + self.track = None + self.soundBackflip = None + self.soundRollover = None + self.soundPlaydead = None + self.soundTeleportIn = None + self.soundTeleportOut = None + self.teleportHole = None + return + + def isPet(self): + return True + + def stopAnimations(self): + if self.track: + self.track.pause() + self.stopBlink() + self.animFSM.request('off') + + def delete(self): + self.stopAnimations() + self.track = None + self.soundBackflip = None + self.soundRollover = None + self.soundPlaydead = None + self.soundTeleportIn = None + self.soundTeleportOut = None + if self.teleportHole: + self.teleportHole.cleanup() + self.teleportHole = None + self.eyesOpenTexture = None + self.eyesClosedTexture = None + self.animFSM = None + self.eyes = None + self.rightPupil = None + self.rightHighlight = None + self.rightBrow = None + self.leftPupil = None + self.leftHighlight = None + self.leftBrow = None + self.color = None + Avatar.Avatar.delete(self) + return + + def getDNA(self): + return self.style + + def setDNA(self, dna): + if self.style: + pass + else: + self.style = dna + self.generatePet() + self.generateMoods() + self.initializeDropShadow() + self.initializeNametag3d() + self.dropShadow.setScale(0.75) + + def generatePet(self): + self.loadModel('phase_4/models/char/TT_pets-mod') + self.loadAnims({'toBeg': 'phase_5/models/char/TT_pets-intoBeg', + 'beg': 'phase_5/models/char/TT_pets-beg', + 'fromBeg': 'phase_5/models/char/TT_pets-begOut', + 'backflip': 'phase_5/models/char/TT_pets-backflip', + 'dance': 'phase_5/models/char/TT_pets-heal', + 'toDig': 'phase_5/models/char/TT_pets-intoDig', + 'dig': 'phase_5/models/char/TT_pets-dig', + 'fromDig': 'phase_5/models/char/TT_pets-digToNeutral', + 'disappear': 'phase_5/models/char/TT_pets-disappear', + 'eat': 'phase_5.5/models/char/TT_pets-eat', + 'jump': 'phase_5/models/char/TT_pets-jump', + 'neutral': 'phase_4/models/char/TT_pets-neutral', + 'neutralHappy': 'phase_4/models/char/TT_pets-neutralHappy', + 'neutralSad': 'phase_4/models/char/TT_pets-neutral_sad', + 'toPet': 'phase_5.5/models/char/TT_pets-petin', + 'pet': 'phase_5.5/models/char/TT_pets-petloop', + 'fromPet': 'phase_5.5/models/char/TT_pets-petend', + 'playDead': 'phase_5/models/char/TT_pets-playdead', + 'fromPlayDead': 'phase_5/models/char/TT_pets-deadend', + 'reappear': 'phase_5/models/char/TT_pets-reappear', + 'run': 'phase_5.5/models/char/TT_pets-run', + 'rollover': 'phase_5/models/char/TT_pets-rollover', + 'walkSad': 'phase_5.5/models/char/TT_pets-sadwalk', + 'speak': 'phase_5/models/char/TT_pets-speak', + 'swallow': 'phase_5.5/models/char/TT_pets-swallow', + 'swim': 'phase_5.5/models/char/TT_pets-swim', + 'toBall': 'phase_5.5/models/char/TT_pets-toBall', + 'walk': 'phase_5.5/models/char/TT_pets-walk', + 'walkHappy': 'phase_5.5/models/char/TT_pets-walkHappy'}) + self.setHeight(2) + color = None + colorIndex = self.style[5] + color = AllPetColors[colorIndex] + self.color = color + bodyType = self.style[4] + body = self.find('**/body') + tex = loader.loadTexture(BodyTextures[BodyTypes[bodyType]]) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + body.setTexture(tex, 1) + body.setColor(color) + leftFoot = self.find('**/leftFoot') + rightFoot = self.find('**/rightFoot') + texName = getFootTexture(bodyType) + tex = loader.loadTexture(texName) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + leftFoot.setTexture(tex, 1) + rightFoot.setTexture(tex, 1) + leftFoot.setColor(color) + rightFoot.setColor(color) + for part in HeadParts + EarParts + NoseParts + TailParts: + self.find('**/' + part).stash() + + colorScale = ColorScales[self.style[6]] + partColor = self.amplifyColor(color, colorScale) + headIndex = self.style[0] + if headIndex != -1: + head = self.find('**/@@' + HeadParts[headIndex]) + head.setColor(partColor) + head.unstash() + earsIndex = self.style[1] + if earsIndex != -1: + ears = self.find('**/@@' + EarParts[earsIndex]) + ears.setColor(partColor) + texName = getEarTexture(bodyType, EarParts[earsIndex]) + if texName: + tex = loader.loadTexture(texName) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + ears.setTexture(tex, 1) + ears.unstash() + noseIndex = self.style[2] + if noseIndex != -1: + nose = self.find('**/@@' + NoseParts[noseIndex]) + nose.setColor(partColor) + nose.unstash() + tailIndex = self.style[3] + if tailIndex != -1: + tail = self.find('**/@@' + TailParts[tailIndex]) + tail.setColor(partColor) + texName = TailTextures[TailParts[tailIndex]] + if texName: + if BodyTypes[bodyType] == 'giraffe': + texName = GiraffeTail + elif BodyTypes[bodyType] == 'leopard': + texName = LeopardTail + tex = loader.loadTexture(texName) + tex.setMinfilter(Texture.FTLinear) + tex.setMagfilter(Texture.FTLinear) + tail.setTexture(tex, 1) + tail.unstash() + if not self.forGui: + self.drawInFront('eyeWhites', 'body', 1) + self.drawInFront('rightPupil', 'eyeWhites', 2) + self.drawInFront('leftPupil', 'eyeWhites', 2) + self.drawInFront('rightHighlight', 'rightPupil', 3) + self.drawInFront('leftHighlight', 'leftPupil', 3) + else: + self.drawInFront('eyeWhites', 'body', -2) + self.drawInFront('rightPupil', 'eyeWhites', -2) + self.drawInFront('leftPupil', 'eyeWhites', -2) + self.find('**/rightPupil').adjustAllPriorities(1) + self.find('**/leftPupil').adjustAllPriorities(1) + eyes = self.style[7] + eyeColor = PetEyeColors[eyes] + self.eyes = self.find('**/eyeWhites') + self.rightPupil = self.find('**/rightPupil') + self.leftPupil = self.find('**/leftPupil') + self.rightHighlight = self.find('**/rightHighlight') + self.leftHighlight = self.find('**/leftHighlight') + self.rightBrow = self.find('**/rightBrow') + self.leftBrow = self.find('**/leftBrow') + self.eyes.setColor(1, 1, 1, 1) + self.rightPupil.setColor(eyeColor, 2) + self.leftPupil.setColor(eyeColor, 2) + self.rightHighlight.setColor(1, 1, 1, 1) + self.leftHighlight.setColor(1, 1, 1, 1) + self.rightBrow.setColor(0, 0, 0, 1) + self.leftBrow.setColor(0, 0, 0, 1) + self.eyes.setTwoSided(1, 1) + self.rightPupil.setTwoSided(1, 1) + self.leftPupil.setTwoSided(1, 1) + self.rightHighlight.setTwoSided(1, 1) + self.leftHighlight.setTwoSided(1, 1) + self.rightBrow.setTwoSided(1, 1) + self.leftBrow.setTwoSided(1, 1) + if self.forGui: + self.rightHighlight.hide() + self.leftHighlight.hide() + if self.style[8]: + self.eyesOpenTexture = loader.loadTexture('phase_4/maps/BeanEyeBoys2.jpg', 'phase_4/maps/BeanEyeBoys2_a.rgb') + self.eyesClosedTexture = loader.loadTexture('phase_4/maps/BeanEyeBoysBlink.jpg', 'phase_4/maps/BeanEyeBoysBlink_a.rgb') + else: + self.eyesOpenTexture = loader.loadTexture('phase_4/maps/BeanEyeGirlsNew.jpg', 'phase_4/maps/BeanEyeGirlsNew_a.rgb') + self.eyesClosedTexture = loader.loadTexture('phase_4/maps/BeanEyeGirlsBlinkNew.jpg', 'phase_4/maps/BeanEyeGirlsBlinkNew_a.rgb') + self.eyesOpenTexture.setMinfilter(Texture.FTLinear) + self.eyesOpenTexture.setMagfilter(Texture.FTLinear) + self.eyesClosedTexture.setMinfilter(Texture.FTLinear) + self.eyesClosedTexture.setMagfilter(Texture.FTLinear) + self.eyesOpen() + return None + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def amplifyColor(self, color, scale): + color = color * scale + for i in (0, 1, 2): + if color[i] > 1.0: + color.setCell(i, 1.0) + + return color + + def generateMoods(self): + moodIcons = loader.loadModel('phase_4/models/char/petEmotes') + self.moodIcons = self.attachNewNode('moodIcons') + self.moodIcons.setScale(2.0) + self.moodIcons.setZ(3.65) + moods = moodIcons.findAllMatches('**/+GeomNode') + for moodNum in range(0, moods.getNumPaths()): + mood = moods.getPath(moodNum) + mood.reparentTo(self.moodIcons) + mood.setBillboardPointEye() + mood.hide() + + def clearMood(self): + if self.moodModel: + self.moodModel.hide() + self.moodModel = None + return + + def showMood(self, mood): + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + holidayIds = base.cr.newsManager.getHolidayIdList() + if (ToontownGlobals.APRIL_FOOLS_COSTUMES in holidayIds or ToontownGlobals.SILLYMETER_EXT_HOLIDAY in holidayIds) and not mood == 'confusion': + self.speakMood(mood) + return + else: + self.clearChat() + else: + self.clearChat() + mood = Component2IconDict[mood] + if mood is None: + moodModel = None + else: + moodModel = self.moodIcons.find('**/*' + mood + '*') + if moodModel.isEmpty(): + self.notify.warning('No such mood!: %s' % mood) + return + if self.moodModel == moodModel: + return + if self.moodModel: + self.moodModel.hide() + self.moodModel = moodModel + if self.moodModel: + self.moodModel.show() + return + + def speakMood(self, mood): + if self.moodModel: + self.moodModel.hide() + if base.config.GetBool('want-speech-bubble', 1): + self.nametag.setChat(random.choice(TTLocalizer.SpokenMoods[mood]), CFSpeech) + else: + self.nametag.setChat(random.choice(TTLocalizer.SpokenMoods[mood]), CFThought) + + def getGenderString(self): + if self.style: + if self.style[8]: + return TTLocalizer.GenderShopBoyButtonText + else: + return TTLocalizer.GenderShopGirlButtonText + + def getShadowJoint(self): + if hasattr(self, 'shadowJoint'): + return self.shadowJoint + shadowJoint = self.find('**/attachShadow') + if shadowJoint.isEmpty(): + self.shadowJoint = self + else: + self.shadowJoint = shadowJoint + return self.shadowJoint + + def getNametagJoints(self): + joints = [] + bundle = self.getPartBundle('modelRoot') + joint = bundle.findChild('attachNametag') + if joint: + joints.append(joint) + return joints + + def fitAndCenterHead(self, maxDim, forGui = 0): + p1 = Point3() + p2 = Point3() + self.calcTightBounds(p1, p2) + if forGui: + h = 180 + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + self.getGeomNode().setDepthWrite(1) + self.getGeomNode().setDepthTest(1) + else: + h = 0 + d = p2 - p1 + biggest = max(d[0], d[2]) + s = (maxDim + 0.0) / biggest + mid = (p1 + d / 2.0) * s + self.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], h, 0, 0, s, s, s) + + def makeRandomPet(self): + dna = PetDNA.getRandomPetDNA() + self.setDNA(dna) + + def enterOff(self): + self.stop() + + def exitOff(self): + pass + + def enterBall(self): + self.setPlayRate(1, 'toBall') + self.play('toBall') + + def exitBall(self): + self.setPlayRate(-1, 'toBall') + self.play('toBall') + + def enterBackflip(self): + self.play('backflip') + + def exitBackflip(self): + self.stop('backflip') + + def enterBeg(self): + delay = self.getDuration('toBeg') + self.track = Sequence(Func(self.play, 'toBeg'), Wait(delay), Func(self.loop, 'beg')) + self.track.start() + + def exitBeg(self): + self.track.pause() + self.play('fromBeg') + + def enterEat(self): + self.loop('eat') + + def exitEat(self): + self.stop('swallow') + + def enterDance(self): + self.loop('dance') + + def exitDance(self): + self.stop('dance') + + def enterNeutral(self): + anim = 'neutral' + self.pose(anim, random.choice(range(0, self.getNumFrames(anim)))) + self.loop(anim, restart=0) + + def exitNeutral(self): + self.stop('neutral') + + def enterNeutralHappy(self): + anim = 'neutralHappy' + self.pose(anim, random.choice(range(0, self.getNumFrames(anim)))) + self.loop(anim, restart=0) + + def exitNeutralHappy(self): + self.stop('neutralHappy') + + def enterNeutralSad(self): + anim = 'neutralSad' + self.pose(anim, random.choice(range(0, self.getNumFrames(anim)))) + self.loop(anim, restart=0) + + def exitNeutralSad(self): + self.stop('neutralSad') + + def enterRun(self): + self.loop('run') + + def exitRun(self): + self.stop('run') + + def enterSwim(self): + self.loop('swim') + + def exitSwim(self): + self.stop('swim') + + def getTeleportInTrack(self): + if not self.teleportHole: + self.teleportHole = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + track = Sequence(Wait(1.0), Parallel(self.getTeleportInSoundInterval(), Sequence(Func(self.showHole), ActorInterval(self.teleportHole, 'hole', startFrame=81, endFrame=71), ActorInterval(self, 'reappear'), ActorInterval(self.teleportHole, 'hole', startFrame=71, endFrame=81), Func(self.cleanupHole), Func(self.loop, 'neutral')), Sequence(Func(self.dropShadow.hide), Wait(1.0), Func(self.dropShadow.show)))) + return track + + def enterTeleportIn(self, timestamp): + self.track = self.getTeleportInTrack() + self.track.start(globalClockDelta.localElapsedTime(timestamp)) + + def exitTeleportIn(self): + self.track.pause() + + def getTeleportOutTrack(self): + if not self.teleportHole: + self.teleportHole = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + track = Sequence(Wait(1.0), Parallel(self.getTeleportOutSoundInterval(), Sequence(ActorInterval(self, 'toDig'), Parallel(ActorInterval(self, 'dig'), Func(self.showHole), ActorInterval(self.teleportHole, 'hole', startFrame=81, endFrame=71)), ActorInterval(self, 'disappear'), ActorInterval(self.teleportHole, 'hole', startFrame=71, endFrame=81), Func(self.cleanupHole)), Sequence(Wait(1.0), Func(self.dropShadow.hide)))) + return track + + def enterTeleportOut(self, timestamp): + self.track = self.getTeleportOutTrack() + self.track.start(globalClockDelta.localElapsedTime(timestamp)) + + def exitTeleportOut(self): + self.track.pause() + + def showHole(self): + if self.teleportHole: + self.teleportHole.setBin('shadow', 0) + self.teleportHole.setDepthTest(0) + self.teleportHole.setDepthWrite(0) + self.teleportHole.reparentTo(self) + self.teleportHole.setScale(0.75) + self.teleportHole.setPos(0, -1, 0) + + def cleanupHole(self): + if self.teleportHole: + self.teleportHole.reparentTo(hidden) + self.teleportHole.clearBin() + self.teleportHole.clearDepthTest() + self.teleportHole.clearDepthWrite() + + def getTeleportInSoundInterval(self): + if not self.soundTeleportIn: + self.soundTeleportIn = loader.loadSfx('phase_5/audio/sfx/teleport_reappear.mp3') + return SoundInterval(self.soundTeleportIn) + + def getTeleportOutSoundInterval(self): + if not self.soundTeleportOut: + self.soundTeleportOut = loader.loadSfx('phase_5/audio/sfx/teleport_disappear.mp3') + return SoundInterval(self.soundTeleportOut) + + def enterWalk(self): + self.loop('walk') + + def exitWalk(self): + self.stop('walk') + + def enterWalkHappy(self): + self.loop('walkHappy') + + def exitWalkHappy(self): + self.stop('walkHappy') + + def enterWalkSad(self): + self.loop('walkSad') + + def exitWalkSad(self): + self.stop('walkSad') + + def trackAnimToSpeed(self, forwardVel, rotVel, inWater = 0): + action = 'neutral' + if self.isInWater(): + action = 'swim' + elif forwardVel > 0.1 or abs(rotVel) > 0.1: + action = 'walk' + self.setAnimWithMood(action) + + def setAnimWithMood(self, action): + how = '' + if self.isExcited(): + how = 'Happy' + elif self.isSad(): + how = 'Sad' + if action == 'swim': + anim = action + else: + anim = '%s%s' % (action, how) + if anim != self.animFSM.getCurrentState().getName(): + self.animFSM.request(anim) + + def isInWater(self): + return 0 + + def isExcited(self): + return 0 + + def isSad(self): + return 0 + + def startTrackAnimToSpeed(self): + self.lastPos = self.getPos(render) + self.lastH = self.getH(render) + taskMgr.add(self._trackAnimTask, self.getTrackAnimTaskName()) + + def stopTrackAnimToSpeed(self): + taskMgr.remove(self.getTrackAnimTaskName()) + del self.lastPos + del self.lastH + + def getTrackAnimTaskName(self): + return 'trackPetAnim-%s' % self.serialNum + + def _trackAnimTask(self, task): + curPos = self.getPos(render) + curH = self.getH(render) + self.trackAnimToSpeed(curPos - self.lastPos, curH - self.lastH) + self.lastPos = curPos + self.lastH = curH + return Task.cont + + def __blinkOpen(self, task): + self.eyesOpen() + r = random.random() + if r < 0.1: + t = 0.2 + else: + t = r * 4.0 + 1 + taskMgr.doMethodLater(t, self.__blinkClosed, self.__blinkName) + return Task.done + + def __blinkClosed(self, task): + self.eyesClose() + taskMgr.doMethodLater(0.125, self.__blinkOpen, self.__blinkName) + return Task.done + + def startBlink(self): + taskMgr.remove(self.__blinkName) + self.eyesOpen() + t = random.random() * 4.0 + 1 + taskMgr.doMethodLater(t, self.__blinkClosed, self.__blinkName) + + def stopBlink(self): + taskMgr.remove(self.__blinkName) + self.eyesOpen() + + def eyesOpen(self): + self.eyes.setColor(1, 1, 1, 1) + self.eyes.setTexture(self.eyesOpenTexture, 1) + self.rightPupil.show() + self.leftPupil.show() + if not self.forGui: + self.rightHighlight.show() + self.leftHighlight.show() + + def eyesClose(self): + self.eyes.setColor(self.color) + self.eyes.setTexture(self.eyesClosedTexture, 1) + self.rightPupil.hide() + self.leftPupil.hide() + if not self.forGui: + self.rightHighlight.hide() + self.leftHighlight.hide() + + def lockPet(self): + if not self.lockedDown: + self.prevAnimState = self.animFSM.getCurrentState().getName() + self.animFSM.request('neutral') + self.lockedDown += 1 + + def isLockedDown(self): + return self.lockedDown != 0 + + def unlockPet(self): + self.lockedDown -= 1 + if not self.lockedDown: + self.animFSM.request(self.prevAnimState) + self.prevAnimState = None + return + + def getInteractIval(self, interactId): + anims = self.InteractAnims[interactId] + if type(anims) == types.StringType: + animIval = ActorInterval(self, anims) + else: + animIval = Sequence() + for anim in anims: + animIval.append(ActorInterval(self, anim)) + + return animIval + + +def gridPets(): + pets = [] + offsetX = 0 + offsetY = 0 + startPos = base.localAvatar.getPos() + for body in range(0, len(BodyTypes)): + colors = getColors(body) + for color in colors: + p = Pet() + p.setDNA([random.choice(range(-1, len(HeadParts))), + random.choice(range(-1, len(EarParts))), + random.choice(range(-1, len(NoseParts))), + random.choice(range(-1, len(TailParts))), + body, + color, + random.choice(range(-1, len(ColorScales))), + random.choice(range(0, len(PetEyeColors))), + random.choice(range(0, len(PetGenders)))]) + p.setPos(startPos[0] + offsetX, startPos[1] + offsetY, startPos[2]) + p.animFSM.request('neutral') + p.reparentTo(render) + pets.append(p) + offsetX += 3 + + offsetY += 3 + offsetX = 0 + + return pets diff --git a/toontown/pets/PetActionFSM.py b/toontown/pets/PetActionFSM.py new file mode 100644 index 0000000..2f11cf0 --- /dev/null +++ b/toontown/pets/PetActionFSM.py @@ -0,0 +1,132 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import FSM +from direct.interval.IntervalGlobal import * +from direct.task import Task +from direct.distributed.ClockDelta import globalClockDelta +from direct.showbase.PythonUtil import lerp +from toontown.pets import PetTricks +from toontown.toon import DistributedToonAI + +class PetActionFSM(FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PetActionFSM') + + def __init__(self, pet): + FSM.FSM.__init__(self, PetActionFSM.__name__) + self.pet = pet + self.trickSerialNum = 0 + + def destroy(self): + self.cleanup() + + def enterNeutral(self): + PetActionFSM.notify.debug('enterNeutral') + + def exitNeutral(self): + pass + + def enterChase(self, target): + PetActionFSM.notify.debug('enterChase: %s' % target) + self.pet.chaseImpulse.setTarget(target) + self.pet.mover.addImpulse('chase', self.pet.chaseImpulse) + self.pet.unstickFSM.request('on') + + def exitChase(self): + self.pet.unstickFSM.request('off') + self.pet.mover.removeImpulse('chase') + + def enterFlee(self, chaser): + PetActionFSM.notify.debug('enterFlee: %s' % chaser) + self.pet.fleeImpulse.setChaser(chaser) + self.pet.mover.addImpulse('flee', self.pet.fleeImpulse) + self.pet.unstickFSM.request('on') + + def exitFlee(self): + self.pet.unstickFSM.request('off') + self.pet.mover.removeImpulse('flee') + + def enterWander(self): + PetActionFSM.notify.debug('enterWander') + self.pet.mover.addImpulse('wander', self.pet.wanderImpulse) + + def exitWander(self): + self.pet.mover.removeImpulse('wander') + + def enterUnstick(self): + PetActionFSM.notify.debug('enterUnstick') + self.pet.mover.addImpulse('unstick', self.pet.wanderImpulse) + + def exitUnstick(self): + self.pet.mover.removeImpulse('unstick') + + def enterInspectSpot(self, spot): + PetActionFSM.notify.debug('enterInspectSpot') + self.pet.chaseImpulse.setTarget(spot) + self.pet.mover.addImpulse('inspect', self.pet.chaseImpulse) + self.pet.unstickFSM.request('on') + + def exitInspectSpot(self): + self.pet.unstickFSM.request('off') + self.pet.mover.removeImpulse('inspect') + + def enterStay(self, avatar): + PetActionFSM.notify.debug('enterStay') + + def exitStay(self): + pass + + def enterHeal(self, avatar): + PetActionFSM.notify.debug('enterHeal') + avatar.toonUp(3) + self.pet.chaseImpulse.setTarget(avatar) + self.pet.mover.addImpulse('chase', self.pet.chaseImpulse) + + def exitHeal(self): + self.pet.mover.removeImpulse('chase') + + def enterTrick(self, avatar, trickId): + PetActionFSM.notify.debug('enterTrick') + if not self.pet.isLockedDown(): + self.pet.lockPet() + self.pet.sendUpdate('doTrick', [trickId, globalClockDelta.getRealNetworkTime()]) + + def finish(avatar = avatar, trickId = trickId, self = self): + if hasattr(self.pet, 'brain'): + healRange = PetTricks.TrickHeals[trickId] + aptitude = self.pet.getTrickAptitude(trickId) + healAmt = int(lerp(healRange[0], healRange[1], aptitude)) + if healAmt: + for avId in self.pet.brain.getAvIdsLookingAtUs(): + av = self.pet.air.doId2do.get(avId) + if av: + if isinstance(av, DistributedToonAI.DistributedToonAI): + av.toonUp(healAmt) + + self.pet._handleDidTrick(trickId) + if not self.pet.isLockedDown(): + self.pet.unlockPet() + messenger.send(self.getTrickDoneEvent()) + + self.trickDoneEvent = 'trickDone-%s-%s' % (self.pet.doId, self.trickSerialNum) + self.trickSerialNum += 1 + self.trickFinishIval = Sequence(WaitInterval(PetTricks.TrickLengths[trickId]), Func(finish), name='petTrickFinish-%s' % self.pet.doId) + self.trickFinishIval.start() + + def getTrickDoLaterName(self): + return 'petTrickDoLater-%s' % self.pet.doId + + def getTrickDoneEvent(self): + return self.trickDoneEvent + + def exitTrick(self): + if self.trickFinishIval.isPlaying(): + self.trickFinishIval.finish() + del self.trickFinishIval + if self.pet.isLockedDown(): + self.pet.unlockPet() + del self.trickDoneEvent + + def enterMovie(self): + PetActionFSM.notify.debug('enterMovie') + + def exitMovie(self): + pass diff --git a/toontown/pets/PetAvatarPanel.py b/toontown/pets/PetAvatarPanel.py new file mode 100644 index 0000000..0792e89 --- /dev/null +++ b/toontown/pets/PetAvatarPanel.py @@ -0,0 +1,301 @@ +from pandac.PandaModules import * +from direct.directnotify.DirectNotifyGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.showbase.PythonUtil import Functor +from direct.task.Task import Task +from direct.distributed import DistributedObject +from otp.avatar import Avatar, AvatarPanel +from toontown.toon import ToonHead +from toontown.toon import LaffMeter +from toontown.toon import ToonAvatarDetailPanel +from toontown.friends import FriendHandle +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.pets import Pet, PetConstants, PetDetailPanel + +class PetAvatarPanel(AvatarPanel.AvatarPanel): + notify = directNotify.newCategory('PetAvatarPanel') + + def __init__(self, avatar): + self.notify.debug('Init(): doId=%s' % avatar.doId) + distPet = base.cr.doId2do.get(avatar.doId) + if distPet and not distPet.isProxy(): + self.avatar = distPet + self.petIsLocal = True + else: + self.avatar = avatar + self.petIsLocal = False + from toontown.friends import FriendsListPanel + AvatarPanel.AvatarPanel.__init__(self, self.avatar, FriendsListPanel=FriendsListPanel) + base.localAvatar.obscureFriendsListButton(1) + base.panel = self + gui = loader.loadModel('phase_3.5/models/gui/PetControlPannel') + guiScale = 0.116 + guiPos = (1.12, 0, 0.3) + self.frame = DirectFrame(parent=aspect2dp, image=gui, scale=guiScale, pos=guiPos, relief=None) + disabledImageColor = Vec4(0.6, 0.6, 0.6, 1) + text0Color = Vec4(1, 1, 1, 1) + text1Color = Vec4(0.5, 1, 0.5, 1) + text2Color = Vec4(1, 1, 0.5, 1) + text3Color = Vec4(0.6, 0.6, 0.6, 1) + self.closeButton = DirectButton(parent=self.frame, image=(gui.find('**/CancelButtonUp'), gui.find('**/CancelButtonDown'), gui.find('**/CancelButtonRollover')), relief=None, command=self.__handleClose) + self.feedButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonFeedUp'), + gui.find('**/ButtonFeedDown'), + gui.find('**/ButtonFeedRollover'), + gui.find('**/ButtonFeedUp')), geom=gui.find('**/PetControlFeedIcon'), geom3_color=disabledImageColor, image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelFeed, text_scale=TTLocalizer.PAPfeedButton, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_pos=(-0.5, 2.8), text_align=TextNode.ALeft, command=self.__handleFeed) + if not self.petIsLocal: + self.feedButton['state'] = DGG.DISABLED + else: + self.feedButton['state'] = self.feedButtonState() + self.callButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonGoToUp'), + gui.find('**/ButtonGoToDown'), + gui.find('**/ButtonGoToRollover'), + gui.find('**/ButtonGoToUp')), geom=gui.find('**/PetControlGoToIcon'), geom3_color=disabledImageColor, image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelCall, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=TTLocalizer.PAPcallButton, text_pos=(-0.5, 1.3), text_align=TextNode.ALeft, command=self.__handleCall) + if not self.petIsLocal: + self.callButton['state'] = DGG.DISABLED + self.scratchButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonScratchUp'), + gui.find('**/ButtonScratchDown'), + gui.find('**/ButtonScratchRollover'), + gui.find('**/ButtonScratchUp')), geom=gui.find('**/PetControlScratchIcon'), geom3_color=disabledImageColor, image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelScratch, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=TTLocalizer.PAPscratchButton, text_pos=(-0.5, 2.05), text_align=TextNode.ALeft, command=self.__handleScratch) + if not self.petIsLocal: + self.scratchButton['state'] = DGG.DISABLED + self.ownerButton = DirectButton(parent=self.frame, image=(gui.find('**/PetControlToonButtonUp'), gui.find('**/PetControlToonButtonDown'), gui.find('**/PetControlToonButtonRollover')), geom=gui.find('**/PetControlToonIcon'), geom3_color=disabledImageColor, relief=None, image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelOwner, + TTLocalizer.PetPanelOwner, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=TTLocalizer.PAPownerButton, text_pos=(0.3, 1.05), text_align=TextNode.ACenter, command=self.__handleToOwner) + if self.avatar.getOwnerId() == base.localAvatar.doId: + self.ownerButton['state'] = DGG.DISABLED + toonGui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.detailButton = DirectButton(parent=self.frame, image=(toonGui.find('**/ChtBx_BackBtn_UP'), + toonGui.find('**/ChtBx_BackBtn_DN'), + toonGui.find('**/ChtBx_BackBtn_Rllvr'), + toonGui.find('**/ChtBx_BackBtn_UP')), relief=None, pos=(-1.3, 0, 0.67), image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelDetail, + TTLocalizer.PetPanelDetail, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.05, text_pos=(0.05, 0.05), text_align=TextNode.ACenter, command=self.__handleDetail) + self.detailButton.setScale(7.5) + if not self.petIsLocal: + self.detailButton['state'] = DGG.DISABLED + gui.removeNode() + toonGui.removeNode() + self.petDetailPanel = None + self.__fillPetInfo(self.avatar) + self.accept('petNameChanged', self.__refreshPetInfo) + self.accept('petStateUpdated', self.__refreshPetInfo) + self.frame.show() + if self.petIsLocal: + proxTask = Task.loop(Task(self.__checkPetProximity), Task.pause(0.5)) + taskMgr.add(proxTask, 'petpanel-proximity-check', priority=ToontownGlobals.PetPanelProximityPriority) + if base.localAvatar.isLockedDown(): + self.disableInteractionButtons() + if self.petIsLocal: + self.listenForInteractionDone() + messenger.send('petPanelDone') + if not self.petIsLocal and hasattr(self.avatar, 'updateMoodFromServer'): + if self.avatar.doId != localAvatar.getPetId() or bboard.get(PetConstants.OurPetsMoodChangedKey, False): + self.stateLabel['text'] = '' + + def refresh(self = self, av = self.avatar): + bboard.remove(PetConstants.OurPetsMoodChangedKey) + self.__refreshPetInfo(av) + + self.avatar.updateMoodFromServer(refresh) + return + + def __checkPetProximity(self, task = None): + if self.petIsLocal: + if base.localAvatar.isInWater(): + self.scratchButton['state'] = DGG.DISABLED + self.feedButton['state'] = DGG.DISABLED + self.callButton['state'] = DGG.DISABLED + self.notify.debug('local avatar is in water') + else: + petPos = self.avatar.getPos() + toonPos = base.localAvatar.getPos() + diff = Vec3(petPos - toonPos) + distance = diff.length() + self.notify.debug('pet distance is %s' % distance) + if distance > 20.0: + self.scratchButton['state'] = DGG.DISABLED + self.feedButton['state'] = DGG.DISABLED + else: + tooVert = abs(diff[2]) > 5.0 + if tooVert: + self.scratchButton['state'] = DGG.DISABLED + else: + self.scratchButton['state'] = DGG.NORMAL + self.feedButton['state'] = self.feedButtonState() + self.callButton['state'] = DGG.NORMAL + return Task.done + + def enableInteractionButtons(self): + self.notify.debug('enable buttons') + proxTask = Task.loop(Task(self.__checkPetProximity), Task.pause(0.5)) + taskMgr.add(proxTask, 'petpanel-proximity-check', priority=ToontownGlobals.PetPanelProximityPriority) + self.__checkPetProximity() + + def disableInteractionButtons(self): + self.notify.debug('disable buttons') + taskMgr.remove('petpanel-proximity-check') + self.scratchButton['state'] = DGG.DISABLED + self.feedButton['state'] = DGG.DISABLED + self.callButton['state'] = DGG.DISABLED + + def listenForInteractionDone(self): + self.accept('pet-interaction-done', self.enableInteractionButtons) + + def cancelListenForInteractionDone(self): + self.ignore('pet-interaction-done') + + def feedButtonState(self): + if base.localAvatar.getMoney() >= PetConstants.FEED_AMOUNT: + return DGG.NORMAL + else: + return DGG.DISABLED + + def cleanup(self): + self.notify.debug('cleanup(): doId=%s' % self.avatar.doId) + if self.frame == None: + return + self.cancelListenForInteractionDone() + taskMgr.remove('petpanel-proximity-check') + if hasattr(self, 'toonDetail'): + del self.toonDetail + self.frame.destroy() + del self.frame + self.frame = None + self.petView.removeNode() + del self.petView + self.petModel.delete() + del self.petModel + base.localAvatar.obscureFriendsListButton(-1) + self.ignore('petStateUpdated') + self.ignore('petNameChanged') + if self.avatar.bFake: + self.avatar.disable() + self.avatar.delete() + AvatarPanel.AvatarPanel.cleanup(self) + base.panel = None + return + + def disableAll(self): + self.disableInteractionButtons() + self.ownerButton['state'] = DGG.DISABLED + self.closeButton['state'] = DGG.DISABLED + self.detailButton['state'] = DGG.DISABLED + + def __handleDetailDone(self): + if self.petDetailPanel != None: + self.petDetailPanel.cleanup() + self.petDetailPanel = None + self.detailButton['state'] = DGG.NORMAL + return + + def __handleDetail(self): + self.petDetailPanel = PetDetailPanel.PetDetailPanel(pet=self.avatar, closeCallback=self.__handleDetailDone, parent=self.frame) + self.detailButton['state'] = DGG.DISABLED + + def __handleToOwner(self): + self.notify.debug('__handleToOwner(): doId=%s' % self.avatar.doId) + handle = base.cr.identifyFriend(self.avatar.ownerId) + if handle != None: + self.cleanup() + messenger.send('clickedNametag', [handle]) + else: + self.disableAll() + from toontown.toon import ToonDetail + self.toonDetail = ToonDetail.ToonDetail(self.avatar.ownerId, self.__ownerDetailsLoaded) + return + + def __ownerDetailsLoaded(self, avatar): + self.notify.debug('__ownerDetailsLoaded(): doId=%s' % self.avatar.doId) + self.cleanup() + if avatar is not None: + messenger.send('clickedNametag', [avatar]) + return + + def __handleCall(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: PET: Call') + self.notify.debug('__handleCall(): doId=%s' % self.avatar.doId) + base.localAvatar.b_setPetMovie(self.avId, PetConstants.PET_MOVIE_CALL) + base.panel.disableInteractionButtons() + if self.avatar.trickIval is not None and self.avatar.trickIval.isPlaying(): + self.avatar.trickIval.finish() + base.cr.playGame.getPlace().setState('pet') + base.localAvatar.lock() + return + + def __handleFeed(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: PET: Feed') + self.notify.debug('__handleFeed(): doId=%s' % self.avatar.doId) + base.localAvatar.b_setPetMovie(self.avId, PetConstants.PET_MOVIE_FEED) + base.panel.disableInteractionButtons() + if self.avatar.trickIval is not None and self.avatar.trickIval.isPlaying(): + self.avatar.trickIval.finish() + base.cr.playGame.getPlace().setState('pet') + base.localAvatar.lock() + return + + def __handleScratch(self): + if base.config.GetBool('want-qa-regression', 1): + self.notify.info('QA-REGRESSION: PET: Scratch') + self.notify.debug('__handleScratch(): doId=%s' % self.avatar.doId) + base.localAvatar.b_setPetMovie(self.avId, PetConstants.PET_MOVIE_SCRATCH) + base.panel.disableInteractionButtons() + if self.avatar.trickIval is not None and self.avatar.trickIval.isPlaying(): + self.avatar.trickIval.finish() + base.cr.playGame.getPlace().setState('pet') + base.localAvatar.lock() + return + + def __handleDisableAvatar(self): + self.notify.debug('__handleDisableAvatar(): doId=%s' % self.avatar.doId) + self.cleanup() + AvatarPanel.currentAvatarPanel = None + return + + def __handleGenerateAvatar(self, avatar): + pass + + def __handleClose(self): + self.notify.debug('__handleClose(): doId=%s' % self.avatar.doId) + self.cleanup() + AvatarPanel.currentAvatarPanel = None + if self.friendsListShown: + self.FriendsListPanel.showFriendsList() + return + + def __fillPetInfo(self, avatar): + self.notify.debug('__fillPetInfo(): doId=%s' % avatar.doId) + self.petView = self.frame.attachNewNode('petView') + self.petView.setPos(0, 0, 5.4) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(avatar.getDNA()) + self.petModel.fitAndCenterHead(3.575, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.enterNeutralHappy() + self.petModel.startBlink() + self.nameLabel = DirectLabel(parent=self.frame, pos=(0, 0, 5.2), relief=None, text=avatar.getName(), text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.4, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.stateLabel = DirectLabel(parent=self.frame, pos=TTLocalizer.PAPstateLabelPos, relief=None, text='', text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_scale=TTLocalizer.PAPstateLabel, text_wordwrap=TTLocalizer.PAPstateLabelWordwrap, text_shadow=(1, 1, 1, 1)) + self.__refreshPetInfo(avatar) + return + + def __refreshPetInfo(self, avatar): + self.notify.debug('__refreshPetInfo(): doId=%s' % avatar.doId) + if avatar.doId != self.avatar.doId: + self.notify.warning('avatar not self!') + return + if self.frame == None: + return + if not self.petIsLocal: + self.avatar.updateOfflineMood() + mood = self.avatar.getDominantMood() + self.stateLabel['text'] = TTLocalizer.PetMoodAdjectives[mood] + self.nameLabel['text'] = avatar.getName() + if self.petDetailPanel != None: + self.petDetailPanel.update(avatar) + return diff --git a/toontown/pets/PetBase.py b/toontown/pets/PetBase.py new file mode 100644 index 0000000..e558233 --- /dev/null +++ b/toontown/pets/PetBase.py @@ -0,0 +1,22 @@ +from toontown.pets.PetConstants import AnimMoods +from toontown.pets import PetMood +import string + +class PetBase: + + def getSetterName(self, valueName, prefix = 'set'): + return '%s%s%s' % (prefix, string.upper(valueName[0]), valueName[1:]) + + def getAnimMood(self): + if self.mood.getDominantMood() in PetMood.PetMood.ExcitedMoods: + return AnimMoods.EXCITED + elif self.mood.getDominantMood() in PetMood.PetMood.UnhappyMoods: + return AnimMoods.SAD + else: + return AnimMoods.NEUTRAL + + def isExcited(self): + return self.getAnimMood() == AnimMoods.EXCITED + + def isSad(self): + return self.getAnimMood() == AnimMoods.SAD diff --git a/toontown/pets/PetBrain.py b/toontown/pets/PetBrain.py new file mode 100644 index 0000000..42ae174 --- /dev/null +++ b/toontown/pets/PetBrain.py @@ -0,0 +1,561 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import weightedChoice, randFloat, Functor +from direct.showbase.PythonUtil import list2dict +from direct.showbase import DirectObject +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from direct.fsm import FSM +from toontown.toon import DistributedToonAI +from toontown.pets import PetConstants, PetObserve, PetGoal, PetGoalMgr +from toontown.pets import PetTricks, PetLookerAI +import random, types + +class PetBrain(DirectObject.DirectObject, CPetBrain): + notify = DirectNotifyGlobal.directNotify.newCategory('PetBrain') + + def __init__(self, pet): + CPetBrain.__init__(self) + self.pet = pet + self.focus = None + self.started = 0 + self.inMovie = 0 + self.chaseNode = self.pet.getRender().attachNewNode('PetChaseNode') + self.goalMgr = PetGoalMgr.PetGoalMgr(self.pet) + self.doId2goals = {} + self.nearbyAvs = {} + self.avAwareness = {} + self.lastInteractTime = {} + self.nextAwarenessIndex = 0 + if __dev__: + self.pscPrior = PStatCollector('App:Show code:petThink:UpdatePriorities') + self.pscAware = PStatCollector('App:Show code:petThink:ShuffleAwareness') + self.pscResc = PStatCollector('App:Show code:petThink:Reschedule') + return + + def destroy(self): + taskMgr.remove(self.getTeleportTaskName()) + if __dev__: + del self.pscPrior + del self.pscAware + del self.pscResc + self.stop() + self.goalMgr.destroy() + self.chaseNode.removeNode() + del self.chaseNode + del self.focus + del self.pet + if self.doId2goals: + self.notify.warning('destroy(): self.doId2goals is not empty: %s' % self.doId2goals.keys()) + for goalList in self.doId2goals.values(): + for goal in goalList: + goal.destroy() + + del self.doId2goals + del self.avAwareness + + def getThinkTaskName(self): + return 'petThink-%s' % self.pet.doId + + def getTeleportTaskName(self): + return 'petTeleport-%s' % self.pet.doId + + def getObserveEventAttendedByAvStart(self, otherDoId): + return 'petObserveAttendedByAvStart-%s-%s' % (self.pet.doId, otherDoId) + + def getObserveEventAttendedByAvStop(self, otherDoId): + return 'petObserveAttendedByAvStop-%s-%s' % (self.pet.doId, otherDoId) + + def getObserveEventAttendingAvStart(self, otherDoId): + return 'petObserveAttendingAvStart-%s-%s' % (self.pet.doId, otherDoId) + + def getObserveEventAttendingAvStop(self, otherDoId): + return 'petObserveAttendingAvStop-%s-%s' % (self.pet.doId, otherDoId) + + def start(self): + PetBrain.notify.debug('start: %s' % self.pet.doId) + self.lookers = {} + self.lookees = {} + self.accept(PetLookerAI.getStartLookedAtByOtherEvent(self.pet.doId), self._handleLookedAtByOtherStart) + self.accept(PetLookerAI.getStopLookedAtByOtherEvent(self.pet.doId), self._handleLookedAtByOtherStop) + self.accept(PetLookerAI.getStartLookingAtOtherEvent(self.pet.doId), self._handleLookingAtOtherStart) + self.accept(PetLookerAI.getStopLookingAtOtherEvent(self.pet.doId), self._handleLookingAtOtherStop) + self.globalGoals = [PetGoal.Wander()] + for goal in self.globalGoals: + self.goalMgr.addGoal(goal) + + for doId in self.pet._getNearbyAvatarDict(): + self._handleAvatarArrive(doId) + + self.tLastLonelinessUpdate = globalClock.getFrameTime() + taskMgr.doMethodLater(simbase.petThinkPeriod * random.random(), self._think, self.getThinkTaskName()) + self.started = 1 + + def stop(self): + PetBrain.notify.debug('stop: %s' % self.pet.doId) + if not self.started: + return + self.started = 0 + del self.lookers + del self.lookees + for doId in self.pet._getNearbyAvatarDict(): + self._handleAvatarLeave(doId) + + for goal in self.globalGoals: + self.goalMgr.removeGoal(goal) + goal.destroy() + + del self.globalGoals + self.clearFocus() + taskMgr.remove(self.getThinkTaskName()) + self.ignore(PetLookerAI.getStartLookedAtByOtherEvent(self.pet.doId)) + self.ignore(PetLookerAI.getStopLookedAtByOtherEvent(self.pet.doId)) + self.ignore(PetLookerAI.getStartLookingAtOtherEvent(self.pet.doId)) + self.ignore(PetLookerAI.getStopLookingAtOtherEvent(self.pet.doId)) + + def observe(self, petObserve): + if petObserve.isForgettable(): + if random.random() < 0.05 * self.pet.traits.forgetfulness: + return + petObserve._influence(self) + + def updateLastInteractTime(self, avId): + if avId in self.lastInteractTime: + self.lastInteractTime[avId] = globalClock.getFrameTime() + + def _think(self, task = None): + if not self.inMovie: + if __dev__: + self.pscPrior.start() + self._updatePriorities() + if __dev__: + self.pscPrior.stop() + if __dev__: + self.pscAware.start() + if len(self.nearbyAvs) > PetConstants.MaxAvatarAwareness: + self.nextAwarenessIndex %= len(self.nearbyAvs) + self._considerBecomeAwareOf(self.nearbyAvs.keys()[self.nextAwarenessIndex]) + self.nextAwarenessIndex += 1 + if __dev__: + self.pscAware.stop() + curT = globalClock.getFrameTime() + tSinceLastLonelinessUpdate = curT - self.tLastLonelinessUpdate + if tSinceLastLonelinessUpdate >= PetConstants.LonelinessUpdatePeriod: + self.tLastLonelinessUpdate = curT + numLookers = len(self.lookers) + if numLookers: + dt = tSinceLastLonelinessUpdate + self.pet.lerpMood('loneliness', max(-1.0, dt * -.003 * numLookers)) + if numLookers > 5: + self.pet.lerpMood('excitement', min(1.0, dt * 0.001 * numLookers)) + if __dev__: + self.pscResc.start() + taskMgr.doMethodLater(simbase.petThinkPeriod, self._think, self.getThinkTaskName()) + if __dev__: + self.pscResc.stop() + return Task.done + + def _updatePriorities(self): + self.goalMgr.updatePriorities() + + def _handleLookingAtOtherStart(self, avId): + if avId in self.lookees: + PetBrain.notify.warning('%s: already looking at av %s' % (self.pet.doId, avId)) + return + self.lookees[avId] = avId + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDING_START, avId)) + + def _handleLookingAtOtherStop(self, avId): + if avId not in self.lookees: + PetBrain.notify.warning('%s: not looking at av %s' % (self.pet.doId, avId)) + return + del self.lookees[avId] + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDING_STOP, avId)) + + def _handleLookedAtByOtherStart(self, avId): + if avId in self.lookers: + PetBrain.notify.warning('%s: av %s already looking at me' % (self.pet.doId, avId)) + return + self.lookers[avId] = avId + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDED_START, avId)) + + def _handleLookedAtByOtherStop(self, avId): + if avId not in self.lookers: + PetBrain.notify.warning('%s: av %s not looking at me' % (self.pet.doId, avId)) + return + del self.lookers[avId] + self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDED_STOP, avId)) + + def lookedAtBy(self, avId): + return avId in self.lookers + + def lookingAt(self, avId): + return avId in self.lookees + + def getAvIdsLookingAtUs(self): + return self.lookers + + def getAvIdsWeAreLookingAt(self): + return self.lookees + + def setFocus(self, object): + if isinstance(self.focus, DistributedObjectAI.DistributedObjectAI): + self.ignore(self.focus.getDeleteEvent()) + self.lastInteractTime.setdefault(self.focus.doId, 0) + PetBrain.notify.debug('setFocus: %s' % object) + self.focus = object + if isinstance(self.focus, DistributedObjectAI.DistributedObjectAI): + self.accept(self.focus.getDeleteEvent(), self._handleFocusHasLeft) + + def getFocus(self): + return self.focus + + def clearFocus(self): + self.setFocus(None) + return + + def _handleFocusHasLeft(self): + if self.focus.isEmpty(): + self.chaseNode.setPos(self.pet, 0, 0, 0) + else: + self.chaseNode.setPos(self.focus, 0, 0, 0) + self._inspectSpot(self.chaseNode) + + def _chase(self, target): + if callable(target): + target = target() + if target is None: + return 0 + self.setFocus(target) + self.pet.actionFSM.request('Chase', target) + return 1 + + def _wander(self): + self.clearFocus() + self.pet.actionFSM.request('Wander') + return 1 + + def _unstick(self): + self.clearFocus() + self.pet.actionFSM.request('Unstick') + return 1 + + def _flee(self, chaser): + if callable(chaser): + chaser = chaser() + if chaser is None: + return 0 + self.setFocus(chaser) + self.pet.actionFSM.request('Flee', chaser) + return 1 + + def _inspectSpot(self, spot = None): + if spot is None: + spot = NodePath('randomSpot') + spot.setPos(randFloat(-20, 20), randFloat(-20, 20), 0) + self.setFocus(spot) + self.pet.actionFSM.request('InspectSpot', spot) + return 1 + + def _stay(self, avatar): + self.setFocus(avatar) + self.pet.actionFSM.request('Stay', avatar) + return 1 + + def _doTrick(self, trickId, avatar): + self.setFocus(avatar) + self.pet.actionFSM.request('Trick', avatar, trickId) + return 1 + + def _heal(self, avatar): + if callable(avatar): + avatar = avatar() + if avatar is None: + return 0 + self.setFocus(avatar) + self.pet.actionFSM.request('Heal', avatar) + return 1 + + def _startMovie(self): + self.setFocus(None) + self.pet.actionFSM.request('Movie') + self.inMovie = 1 + return + + def _endMovie(self): + self.inMovie = 0 + + def _handleGenericObserve(self, observe): + pass + + def _handleActionObserve(self, observe): + action = observe.getAction() + avId = observe.getAvId() + OA = PetObserve.Actions + dbg = PetBrain.notify.debug + if action == OA.ATTENDED_START: + dbg('avatar %s is looking at me' % avId) + self.pet.lerpMoods({'boredom': -.1, + 'excitement': 0.05, + 'loneliness': -.05}) + messenger.send(self.getObserveEventAttendedByAvStart(avId)) + elif action == OA.ATTENDED_STOP: + dbg('avatar %s is no longer looking at me' % avId) + messenger.send(self.getObserveEventAttendedByAvStop(avId)) + elif action == OA.ATTENDING_START: + dbg('I am looking at avatar %s' % avId) + messenger.send(self.getObserveEventAttendingAvStart(avId)) + elif action == OA.ATTENDING_STOP: + dbg('I am no longer looking at avatar %s' % avId) + messenger.send(self.getObserveEventAttendingAvStop(avId)) + elif action == OA.CHANGE_ZONE: + if avId != self.pet.doId: + oldZoneId, newZoneId = observe.getData() + PetBrain.notify.debug('%s.CHANGE_ZONE: %s, %s->%s' % (self.pet.doId, + avId, + oldZoneId, + newZoneId)) + myZoneId = self.pet.zoneId + if newZoneId != oldZoneId: + if newZoneId == myZoneId: + self._handleAvatarArrive(avId) + elif oldZoneId == myZoneId: + self._handleAvatarLeave(avId) + if self.pet.inEstate: + if avId in (self.pet.ownerId, self.pet.estateOwnerId): + if oldZoneId in self.pet.estateZones and newZoneId not in self.pet.estateZones: + if avId == self.pet.ownerId: + self._handleOwnerLeave() + else: + self._handleEstateOwnerLeave() + elif action == OA.LOGOUT: + if avId == self.pet.ownerId: + self._handleOwnerLeave() + elif avId == self.pet.estateOwnerId: + self._handleEstateOwnerLeave() + elif action == OA.FEED: + dbg('avatar %s is feeding me' % avId) + self.pet.lerpMoods({'affection': 0.35, + 'anger': -.07, + 'boredom': -.5, + 'excitement': 0.5, + 'fatigue': -.2, + 'hunger': -.5, + 'loneliness': -.08, + 'playfulness': 0.1, + 'restlessness': -.05, + 'sadness': -.2}) + self.updateLastInteractTime(avId) + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + avatar.setHatePets(0) + elif action == OA.SCRATCH: + dbg('avatar %s is scratching me' % avId) + self.pet.lerpMoods({'affection': 0.45, + 'anger': -.1, + 'boredom': -.8, + 'excitement': 0.5, + 'fatigue': -.25, + 'loneliness': -.2, + 'playfulness': 0.1, + 'restlessness': -.2, + 'sadness': -.2}) + self.updateLastInteractTime(avId) + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + avatar.setHatePets(0) + elif action == OA.GARDEN: + dbg('avatar %s is gardening' % avId) + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + if self.getFocus() == avatar: + self._wander() + return + + def _handlePhraseObserve(self, observe): + + def _handleGettingFriendlyAttention(avId, self = self): + self.pet.lerpMoods({'boredom': -.85, + 'restlessness': -.1, + 'playfulness': 0.2, + 'loneliness': -.4, + 'sadness': -.1, + 'fatigue': -.05, + 'excitement': 0.05, + 'anger': -.05}) + self.updateLastInteractTime(avId) + + def _handleComeHere(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar: + self._chase(avatar) + avatar.setHatePets(0) + + def _handleFollowMe(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar: + self._chase(avatar) + avatar.setHatePets(0) + + def _handleStay(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar: + self._stay(avatar) + + def _handleCriticism(avId, self = self): + ownerFactor = 0.5 + if avId == self.pet.ownerId: + ownerFactor = 1.0 + self.pet.lerpMoods({'affection': -.4, + 'anger': 0.4, + 'boredom': -.3, + 'confusion': 0.05, + 'fatigue': 0.2, + 'playfulness': -.1, + 'sadness': 0.5 * ownerFactor}) + + def _handleGoAway(avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar is not None: + if self.getFocus() == avatar: + self._wander() + return + + def _handleDoTrick(trickId, avId, self = self): + avatar = simbase.air.doId2do.get(avId) + if avatar: + if self.lookedAtBy(avatar.doId): + if not self.goalMgr.hasTrickGoal(): + if not self.pet._willDoTrick(trickId): + self.pet.trickFailLogger.addEvent(trickId) + trickId = PetTricks.Tricks.BALK + trickGoal = PetGoal.DoTrick(avatar, trickId) + self.goalMgr.addGoal(trickGoal) + + phrase = observe.getPetPhrase() + avId = observe.getAvId() + OP = PetObserve.Phrases + if phrase in list2dict([OP.COME, + OP.FOLLOW_ME, + OP.STAY, + OP.NEED_LAFF, + OP.NEED_GAGS, + OP.NEED_JB, + OP.HI, + OP.SOOTHE, + OP.PRAISE, + OP.HAPPY, + OP.QUESTION, + OP.FRIENDLY, + OP.LETS_PLAY, + OP.DO_TRICK]): + _handleGettingFriendlyAttention(avId) + if phrase == OP.COME: + _handleComeHere(avId) + if phrase == OP.FOLLOW_ME: + _handleFollowMe(avId) + if phrase == OP.STAY: + _handleStay(avId) + if phrase == OP.CRITICISM: + _handleCriticism(avId) + if phrase == OP.GO_AWAY: + _handleGoAway(avId) + if phrase == OP.DO_TRICK: + _handleDoTrick(observe.getTrickId(), avId) + + def _addGoalsReAvatar(self, avId): + av = self.pet.air.doId2do.get(avId) + if av is None: + PetBrain.notify.warning('%s._addGoalsReAvatar: %s not in doId2do' % (self.pet.doId, avId)) + return + if avId not in self.doId2goals: + goals = [PetGoal.ChaseAvatar(av), PetGoal.FleeFromAvatar(av)] + self.doId2goals[avId] = goals + self.lastInteractTime.setdefault(avId, 0) + for goal in self.doId2goals[avId]: + self.goalMgr.addGoal(goal) + + return + + def _removeGoalsReAvatar(self, avId): + if avId not in self.doId2goals: + PetBrain.notify.warning('no goals re av %s to remove' % avId) + return + for goal in self.doId2goals[avId]: + self.goalMgr.removeGoal(goal) + goal.destroy() + + del self.doId2goals[avId] + + def _considerBecomeAwareOf(self, avId): + av = simbase.air.doId2do.get(avId) + if av is None: + PetBrain.notify.warning('_considerBecomeAwareOf: av %s does not exist' % avId) + return + if avId in self.avAwareness: + return + + def becomeAwareOf(avId, self = self): + self.avAwareness[avId] = None + self._addGoalsReAvatar(avId) + return + + if len(self.avAwareness) < PetConstants.MaxAvatarAwareness: + becomeAwareOf(avId) + return + + def calcInterest(avId, self = self): + if avId == self.pet.ownerId: + return 100.0 + return random.random() + + avInterest = calcInterest(avId) + minInterest = avInterest + minInterestAvId = avId + for awAvId in self.avAwareness: + i = calcInterest(awAvId) + if i < minInterest: + minInterest = i + minInterestAvId = awAvId + break + + if minInterestAvId != avId: + self._removeAwarenessOf(minInterestAvId) + becomeAwareOf(avId) + return + + def _removeAwarenessOf(self, avId): + if avId in self.avAwareness: + self._removeGoalsReAvatar(avId) + del self.avAwareness[avId] + + def _handleAvatarArrive(self, avId): + PetBrain.notify.debug('%s._handleAvatarArrive: %s' % (self.pet.doId, avId)) + if avId in self.nearbyAvs: + PetBrain.notify.warning('%s already in self.nearbyAvs' % avId) + return + self.nearbyAvs[avId] = None + excitement = 0.3 + if avId == self.pet.ownerId: + excitement = 0.7 + self.pet.lerpMoods({'excitement': 0.7, + 'loneliness': -.4}) + self._considerBecomeAwareOf(avId) + return + + def _handleAvatarLeave(self, avId): + PetBrain.notify.debug('%s._handleAvatarLeave: %s' % (self.pet.doId, avId)) + if avId not in self.nearbyAvs: + PetBrain.notify.warning('av %s not in self.nearbyAvs' % avId) + return + del self.nearbyAvs[avId] + self.pet.lerpMoods({'loneliness': 0.1}) + self._removeAwarenessOf(avId) + + def _handleOwnerLeave(self): + self.pet.teleportOut() + taskMgr.doMethodLater(PetConstants.TELEPORT_OUT_DURATION, self.pet.requestDelete, self.getTeleportTaskName()) + + def _handleEstateOwnerLeave(self): + self.pet.teleportOut() + taskMgr.doMethodLater(PetConstants.TELEPORT_OUT_DURATION, self.pet.requestDelete, self.getTeleportTaskName()) diff --git a/toontown/pets/PetChase.py b/toontown/pets/PetChase.py new file mode 100644 index 0000000..599770a --- /dev/null +++ b/toontown/pets/PetChase.py @@ -0,0 +1,71 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import reduceAngle +from otp.movement import Impulse +import math + +class PetChase(Impulse.Impulse): + + def __init__(self, target = None, minDist = None, moveAngle = None): + Impulse.Impulse.__init__(self) + self.target = target + if minDist is None: + minDist = 5.0 + self.minDist = minDist + if moveAngle is None: + moveAngle = 20.0 + self.moveAngle = moveAngle + self.lookAtNode = NodePath('lookatNode') + self.lookAtNode.hide() + self.vel = None + self.rotVel = None + return + + def setTarget(self, target): + self.target = target + + def destroy(self): + self.lookAtNode.removeNode() + del self.lookAtNode + del self.target + del self.vel + del self.rotVel + + def _setMover(self, mover): + Impulse.Impulse._setMover(self, mover) + self.lookAtNode.reparentTo(self.nodePath) + self.vel = self.VecType(0) + self.rotVel = self.VecType(0) + + def _process(self, dt): + Impulse.Impulse._process(self, dt) + me = self.nodePath + target = self.target + targetPos = target.getPos(me) + x = targetPos[0] + y = targetPos[1] + distance = math.sqrt(x * x + y * y) + self.lookAtNode.lookAt(target) + relH = reduceAngle(self.lookAtNode.getH(me)) + epsilon = 0.005 + rotSpeed = self.mover.getRotSpeed() + if relH < -epsilon: + vH = -rotSpeed + elif relH > epsilon: + vH = rotSpeed + else: + vH = 0 + if abs(vH * dt) > abs(relH): + vH = relH / dt + if distance > self.minDist and abs(relH) < self.moveAngle: + vForward = self.mover.getFwdSpeed() + else: + vForward = 0 + distanceLeft = distance - self.minDist + if distance > self.minDist and vForward * dt > distanceLeft: + vForward = distanceLeft / dt + if vForward: + self.vel.setY(vForward) + self.mover.addShove(self.vel) + if vH: + self.rotVel.setX(vH) + self.mover.addRotShove(self.rotVel) diff --git a/toontown/pets/PetCollider.py b/toontown/pets/PetCollider.py new file mode 100644 index 0000000..61e950f --- /dev/null +++ b/toontown/pets/PetCollider.py @@ -0,0 +1,67 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import reduceAngle +from otp.movement import Impulse +from otp.otpbase import OTPGlobals + +class PetCollider(Impulse.Impulse): + SerialNum = 0 + + def __init__(self, petRadius, collTrav): + Impulse.Impulse.__init__(self) + self.petRadius = petRadius + self.collTrav = collTrav + self.vel = None + self.rotVel = None + self.vH = 0 + self.fwdCLine = CollisionSegment(0.0, 0.0, 0.0, 0.0, 1.0, 0.0) + self.leftCLine = CollisionSegment(0.0, 0.0, 0.0, -1.0, 1.0, 0.0) + self.rightCLine = CollisionSegment(0.0, 0.0, 0.0, 1.0, 1.0, 0.0) + self.calcCollLines() + cLineNode = CollisionNode('cLineNode') + cLineNode.addSolid(self.fwdCLine) + cLineNode.addSolid(self.leftCLine) + cLineNode.addSolid(self.rightCLine) + cLineNode.setFromCollideMask(OTPGlobals.WallBitmask) + cLineNode.setIntoCollideMask(BitMask32.allOff()) + self.cLineNodePath = hidden.attachNewNode(cLineNode) + self.cHandler = CollisionHandlerEvent() + self.cHandler.addInPattern(self._getCollisionEvent()) + self.cHandler.addAgainPattern(self._getCollisionEvent()) + self.collTrav.addCollider(self.cLineNodePath, self.cHandler) + self.accept(self._getCollisionEvent(), self.handleCollision) + return + + def _setMover(self, mover): + Impulse.Impulse._setMover(self, mover) + self.cLineNodePath.reparentTo(self.nodePath) + self.vel = self.VecType(0) + self.rotVel = self.VecType(0) + + def destroy(self): + self.ignore(self._getCollisionEvent()) + self.collTrav.removeCollider(self.cLineNodePath) + del self.cHandler + del self.collTrav + self.cLineNodePath.removeNode() + del self.cLineNodePath + del self.vel + + def calcCollLines(self): + self.fwdCLine.setPointB(Point3(0, self.mover.getFwdSpeed(), 0)) + self.leftCLine.setPointB(Point3(-self.petRadius, self.petRadius, 0)) + self.rightCLine.setPointB(Point3(self.petRadius, self.petRadius, 0)) + + def _getSerialNum(self): + if not hasattr(self, 'serialNum'): + self.serialNum = PetCollider.SerialNum + PetCollider.SerialNum += 1 + return self.serialNum + + def _getCollisionEvent(self): + return 'petFeeler-%s' % self._getSerialNum() + + def handleCollision(self, collEntry): + print 'collision!' + cPoint = collEntry.getSurfacePoint(self.cLineNodePath) + cNormal = collEntry.getSurfaceNormal(self.cLineNodePath) + messenger.send(self.mover.getCollisionEventName(), [cPoint, cNormal]) diff --git a/toontown/pets/PetConstants.py b/toontown/pets/PetConstants.py new file mode 100644 index 0000000..72c4c9e --- /dev/null +++ b/toontown/pets/PetConstants.py @@ -0,0 +1,61 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import Enum, invertDictLossless +import math +from toontown.toonbase import ToontownGlobals +OurPetsMoodChangedKey = 'OurPetsMoodChanged' +ThinkPeriod = 1.5 +MoodDriftPeriod = 300.0 +MovePeriod = 1.0 / 4 +PosBroadcastPeriod = 1.0 / 5 +LonelinessUpdatePeriod = 100.0 +SubmergeDistance = 0.7 +MaxAvatarAwareness = 3 +NonPetSphereRadius = 5.0 +PetSphereRadius = 3.0 +UnstickSampleWindow = 20 +UnstickCollisionThreshold = int(0.5 * UnstickSampleWindow) +PriorityFleeFromAvatar = 0.6 +PriorityDefault = 1.0 +PriorityChaseAv = 1.0 +PriorityDebugLeash = 50.0 +PriorityDoTrick = 100.0 +PrimaryGoalDecayDur = 60.0 +PrimaryGoalScale = 1.3 +HungerChaseToonScale = 1.2 +FleeFromOwnerScale = 0.5 +GettingAttentionGoalScale = 1.2 +GettingAttentionGoalScaleDur = 7.0 +AnimMoods = Enum('EXCITED, SAD, NEUTRAL') +FwdSpeed = 12.0 +RotSpeed = 360.0 +_HappyMult = 1.0 +HappyFwdSpeed = FwdSpeed * _HappyMult +HappyRotSpeed = RotSpeed * _HappyMult +_SadMult = 0.3 +SadFwdSpeed = FwdSpeed * _SadMult +SadRotSpeed = RotSpeed * _SadMult +PETCLERK_TIMER = 180 +PET_MOVIE_CLEAR = 0 +PET_MOVIE_START = 1 +PET_MOVIE_COMPLETE = 2 +PET_MOVIE_FEED = 3 +PET_MOVIE_SCRATCH = 4 +PET_MOVIE_CALL = 5 +FEED_TIME = 10.0 +SCRATCH_TIME = 8.042 +CALL_TIME = 8.0 / 3 +FEED_DIST = {'long': 4.0, + 'medium': 4.0, + 'short': 4.0} +FEED_AMOUNT = 1 +SCRATCH_DIST = {'long': 2.0, + 'medium': 1.5, + 'short': 1.0} +TELEPORT_IN_DURATION = 2.34 +TELEPORT_OUT_DURATION = 4.5 +ZoneToCostRange = {ToontownGlobals.ToontownCentral: (100, 500), + ToontownGlobals.DonaldsDock: (600, 1700), + ToontownGlobals.DaisyGardens: (1000, 2500), + ToontownGlobals.MinniesMelodyland: (1500, 3000), + ToontownGlobals.TheBrrrgh: (2500, 4000), + ToontownGlobals.DonaldsDreamland: (3000, 5000)} diff --git a/toontown/pets/PetDCImports.py b/toontown/pets/PetDCImports.py new file mode 100644 index 0000000..09d5b9e --- /dev/null +++ b/toontown/pets/PetDCImports.py @@ -0,0 +1,2 @@ +if hasattr(base, 'wantPets') and base.wantPets: + import DistributedPet diff --git a/toontown/pets/PetDNA.py b/toontown/pets/PetDNA.py new file mode 100644 index 0000000..3395464 --- /dev/null +++ b/toontown/pets/PetDNA.py @@ -0,0 +1,274 @@ +from toontown.toon import ToonDNA +from pandac.PandaModules import VBase4 +from toontown.toonbase import TTLocalizer, ToontownGlobals +from direct.showbase import PythonUtil +NumFields = 9 +Fields = {'head': 0, + 'ears': 1, + 'nose': 2, + 'tail': 3, + 'body': 4, + 'color': 5, + 'colorScale': 6, + 'eyes': 7, + 'gender': 8} +HeadParts = ['feathers'] +EarParts = ['horns', + 'antennae', + 'dogEars', + 'catEars', + 'rabbitEars'] +EarTextures = {'horns': None, + 'antennae': None, + 'dogEars': None, + 'catEars': 'phase_4/maps/BeanCatEar6.jpg', + 'rabbitEars': 'phase_4/maps/BeanBunnyEar6.jpg'} +ExoticEarTextures = {'horns': None, + 'antennae': None, + 'dogEars': None, + 'catEars': 'phase_4/maps/BeanCatEar3Yellow.jpg', + 'rabbitEars': 'phase_4/maps/BeanBunnyEar6.jpg'} +NoseParts = ['clownNose', + 'dogNose', + 'ovalNose', + 'pigNose'] +TailParts = ['catTail', + 'longTail', + 'birdTail', + 'bunnyTail'] +TailTextures = {'catTail': 'phase_4/maps/beanCatTail6.jpg', + 'longTail': 'phase_4/maps/BeanLongTail6.jpg', + 'birdTail': None, + 'bunnyTail': None} +GiraffeTail = 'phase_4/maps/BeanLongTailGiraffe.jpg' +LeopardTail = 'phase_4/maps/BeanLongTailLepord.jpg' +GenericBodies = ['dots', + 'threeStripe', + 'tigerStripe', + 'tummy'] +SpecificBodies = ['turtle', 'giraffe', 'leopard'] +BodyTypes = GenericBodies + SpecificBodies +PetRarities2 = (('leopard', 0.005), + ('giraffe', 0.015), + ('turtle', 0.045), + ('tigerStripe', 0.115), + ('dots', 0.265), + ('tummy', 0.525), + ('threeStripe', 1.0)) +PetRarities = {'body': {ToontownGlobals.ToontownCentral: {'threeStripe': 50, + 'tummy': 30, + 'dots': 20}, + ToontownGlobals.DonaldsDock: {'threeStripe': 35, + 'tummy': 30, + 'dots': 20, + 'tigerStripe': 15}, + ToontownGlobals.DaisyGardens: {'threeStripe': 15, + 'tummy': 20, + 'dots': 20, + 'tigerStripe': 20, + 'turtle': 15}, + ToontownGlobals.MinniesMelodyland: {'threeStripe': 10, + 'tummy': 15, + 'dots': 30, + 'tigerStripe': 25, + 'turtle': 20}, + ToontownGlobals.TheBrrrgh: {'threeStripe': 5, + 'tummy': 10, + 'dots': 20, + 'tigerStripe': 25, + 'turtle': 25, + 'giraffe': 15}, + ToontownGlobals.DonaldsDreamland: {'threeStripe': 5, + 'tummy': 5, + 'dots': 15, + 'tigerStripe': 20, + 'turtle': 25, + 'giraffe': 20, + 'leopard': 10}}} +BodyTextures = {'dots': 'phase_4/maps/BeanbodyDots6.jpg', + 'threeStripe': 'phase_4/maps/Beanbody3stripes6.jpg', + 'tigerStripe': 'phase_4/maps/BeanbodyZebraStripes6.jpg', + 'turtle': 'phase_4/maps/BeanbodyTurtle.jpg', + 'giraffe': 'phase_4/maps/BeanbodyGiraffe1.jpg', + 'leopard': 'phase_4/maps/BeanbodyLepord2.jpg', + 'tummy': 'phase_4/maps/BeanbodyTummy6.jpg'} +FeetTextures = {'normal': 'phase_4/maps/BeanFoot6.jpg', + 'turtle': 'phase_4/maps/BeanFootTurttle.jpg', + 'giraffe': 'phase_4/maps/BeanFootYellow3.jpg', + 'leopard': 'phase_4/maps/BeanFootYellow3.jpg'} +AllPetColors = (VBase4(1.0, 1.0, 1.0, 1.0), + VBase4(0.96875, 0.691406, 0.699219, 1.0), + VBase4(0.933594, 0.265625, 0.28125, 1.0), + VBase4(0.863281, 0.40625, 0.417969, 1.0), + VBase4(0.710938, 0.234375, 0.4375, 1.0), + VBase4(0.570312, 0.449219, 0.164062, 1.0), + VBase4(0.640625, 0.355469, 0.269531, 1.0), + VBase4(0.996094, 0.695312, 0.511719, 1.0), + VBase4(0.832031, 0.5, 0.296875, 1.0), + VBase4(0.992188, 0.480469, 0.167969, 1.0), + VBase4(0.996094, 0.898438, 0.320312, 1.0), + VBase4(0.996094, 0.957031, 0.597656, 1.0), + VBase4(0.855469, 0.933594, 0.492188, 1.0), + VBase4(0.550781, 0.824219, 0.324219, 1.0), + VBase4(0.242188, 0.742188, 0.515625, 1.0), + VBase4(0.304688, 0.96875, 0.402344, 1.0), + VBase4(0.433594, 0.90625, 0.835938, 1.0), + VBase4(0.347656, 0.820312, 0.953125, 1.0), + VBase4(0.191406, 0.5625, 0.773438, 1.0), + VBase4(0.558594, 0.589844, 0.875, 1.0), + VBase4(0.285156, 0.328125, 0.726562, 1.0), + VBase4(0.460938, 0.378906, 0.824219, 1.0), + VBase4(0.546875, 0.28125, 0.75, 1.0), + VBase4(0.726562, 0.472656, 0.859375, 1.0), + VBase4(0.898438, 0.617188, 0.90625, 1.0), + VBase4(0.7, 0.7, 0.8, 1.0)) +GenericPetColors = [0, + 1, + 3, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 21, + 22, + 23, + 24, + 25] +SpecificPetColors = [0, + 1, + 3, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 16, + 17, + 18, + 23, + 24, + 25] +ColorScales = [0.8, + 0.85, + 0.9, + 0.95, + 1.0, + 1.05, + 1.1, + 1.15, + 1.2] +PetEyeColors = (VBase4(0.29, 0.29, 0.69, 1.0), + VBase4(0.49, 0.49, 0.99, 1.0), + VBase4(0.69, 0.49, 0.29, 1.0), + VBase4(0.99, 0.69, 0.49, 1.0), + VBase4(0.29, 0.69, 0.29, 1.0), + VBase4(0.49, 0.99, 0.49, 1.0)) +PetGenders = [0, 1] + +def getRandomPetDNA(zoneId = ToontownGlobals.DonaldsDreamland): + from random import choice + head = choice(range(-1, len(HeadParts))) + ears = choice(range(-1, len(EarParts))) + nose = choice(range(-1, len(NoseParts))) + tail = choice(range(-1, len(TailParts))) + body = getSpecies(zoneId) + color = choice(range(0, len(getColors(body)))) + colorScale = choice(range(0, len(ColorScales))) + eyes = choice(range(0, len(PetEyeColors))) + gender = choice(range(0, len(PetGenders))) + return [head, + ears, + nose, + tail, + body, + color, + colorScale, + eyes, + gender] + + +def getSpecies(zoneId): + body = PythonUtil.weightedRand(PetRarities['body'][zoneId]) + return BodyTypes.index(body) + + +def getColors(bodyType): + if BodyTypes[bodyType] in GenericBodies: + return GenericPetColors + else: + return SpecificPetColors + + +def getFootTexture(bodyType): + if BodyTypes[bodyType] == 'turtle': + texName = FeetTextures['turtle'] + elif BodyTypes[bodyType] == 'giraffe': + texName = FeetTextures['giraffe'] + elif BodyTypes[bodyType] == 'leopard': + texName = FeetTextures['leopard'] + else: + texName = FeetTextures['normal'] + return texName + + +def getEarTexture(bodyType, earType): + if BodyTypes[bodyType] == 'giraffe' or BodyTypes[bodyType] == 'leopard': + dict = ExoticEarTextures + else: + dict = EarTextures + return dict[earType] + + +def getBodyRarity(bodyIndex): + bodyName = BodyTypes[bodyIndex] + totalWeight = 0.0 + weight = {} + for zoneId in PetRarities['body']: + for body in PetRarities['body'][zoneId]: + totalWeight += PetRarities['body'][zoneId][body] + if weight.has_key(body): + weight[body] += PetRarities['body'][zoneId][body] + else: + weight[body] = PetRarities['body'][zoneId][body] + + minWeight = min(weight.values()) + rarity = (weight[bodyName] - minWeight) / (totalWeight - minWeight) + return rarity + + +def getRarity(dna): + body = dna[Fields['body']] + rarity = getBodyRarity(body) + return rarity + + +def getGender(dna): + return dna[Fields['gender']] + + +def setGender(dna, gender): + dna[Fields['gender']] = gender + + +def getGenderString(dna = None, gender = -1): + if dna != None: + gender = getGender(dna) + if gender: + return TTLocalizer.GenderShopBoyButtonText + else: + return TTLocalizer.GenderShopGirlButtonText + return diff --git a/toontown/pets/PetDetail.py b/toontown/pets/PetDetail.py new file mode 100644 index 0000000..37786c3 --- /dev/null +++ b/toontown/pets/PetDetail.py @@ -0,0 +1,16 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import AvatarDetail +from toontown.pets import DistributedPet + +class PetDetail(AvatarDetail.AvatarDetail): + notify = DirectNotifyGlobal.directNotify.newCategory('PetDetail') + + def getDClass(self): + return 'DistributedPet' + + def createHolder(self): + pet = DistributedPet.DistributedPet(base.cr, bFake=True) + pet.forceAllowDelayDelete() + pet.generateInit() + pet.generate() + return pet diff --git a/toontown/pets/PetDetailPanel.py b/toontown/pets/PetDetailPanel.py new file mode 100644 index 0000000..5e1b3c4 --- /dev/null +++ b/toontown/pets/PetDetailPanel.py @@ -0,0 +1,78 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.pets import PetTricks +from otp.otpbase import OTPLocalizer +from direct.showbase.PythonUtil import lerp +FUDGE_FACTOR = 0.01 + +class PetDetailPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetDetailPanel') + + def __init__(self, pet, closeCallback, parent = aspect2d, **kw): + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + detailPanel = gui.find('**/PetBattlePannel2') + optiondefs = (('pos', (-4.52, 0.0, 3.05), None), + ('scale', 3.58, None), + ('relief', None, None), + ('image', detailPanel, None), + ('image_color', GlobalDialogColor, None), + ('text', TTLocalizer.PetDetailPanelTitle, None), + ('text_wordwrap', 10.4, None), + ('text_scale', 0.132, None), + ('text_pos', (-0.2, 0.6125), None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.dataText = DirectLabel(self, text='', text_scale=0.09, text_align=TextNode.ALeft, text_wordwrap=15, relief=None, pos=(-0.7, 0.0, 0.55)) + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.AvatarDetailPanelCancel, text_scale=0.05, text_pos=(0.12, -0.01), pos=(-0.88, 0.0, -0.68), scale=2.0, command=closeCallback) + self.initialiseoptions(PetDetailPanel) + self.labels = {} + self.bars = {} + self.update(pet) + buttons.removeNode() + gui.removeNode() + return + + def cleanup(self): + del self.labels + del self.bars + self.destroy() + + def update(self, pet): + if not pet: + return + for trickId in PetTricks.TrickId2scIds.keys(): + trickText = TTLocalizer.PetTrickStrings[trickId] + if trickId < len(pet.trickAptitudes): + aptitude = pet.trickAptitudes[trickId] + bar = self.bars.get(trickId) + label = self.bars.get(trickId) + if aptitude != 0: + healRange = PetTricks.TrickHeals[trickId] + hp = lerp(healRange[0], healRange[1], aptitude) + if hp == healRange[1]: + hp = healRange[1] + length = 1 + barColor = (0.7, 0.8, 0.5, 1) + else: + hp, length = divmod(hp, 1) + barColor = (0.9, 1, 0.7, 1) + if not label: + self.labels[trickId] = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.43 - trickId * 0.155), scale=0.7, text=trickText, text_scale=TTLocalizer.PDPtrickText, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ALeft, text_pos=(-1.4, -0.05)) + else: + label['text'] = trickText + if not bar: + self.bars[trickId] = DirectWaitBar(parent=self, pos=(0, 0, 0.43 - trickId * 0.155), relief=DGG.SUNKEN, frameSize=(-0.5, + 0.9, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=0.7, frameColor=(0.4, 0.6, 0.4, 1), barColor=barColor, range=1.0 + FUDGE_FACTOR, value=length + FUDGE_FACTOR, text=str(int(hp)) + ' ' + TTLocalizer.Laff, text_scale=TTLocalizer.PDPlaff, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ALeft, text_pos=TTLocalizer.PDPlaffPos) + else: + bar['value'] = length + FUDGE_FACTOR + bar['text'] = (str(int(hp)) + ' ' + TTLocalizer.Laff,) + + return diff --git a/toontown/pets/PetFlee.py b/toontown/pets/PetFlee.py new file mode 100644 index 0000000..38ac3a3 --- /dev/null +++ b/toontown/pets/PetFlee.py @@ -0,0 +1,63 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import reduceAngle +from otp.movement import Impulse + +class PetFlee(Impulse.Impulse): + + def __init__(self, chaser = None, maxDist = 50.0, moveAngle = 20.0): + Impulse.Impulse.__init__(self) + self.chaser = chaser + self.maxDist = maxDist + self.moveAngle = moveAngle + self.lookAtNode = NodePath('lookatNode') + self.lookAtNode.hide() + self.vel = None + self.rotVel = None + return + + def destroy(self): + self.lookAtNode.removeNode() + del self.lookAtNode + del self.chaser + del self.vel + del self.rotVel + + def setChaser(self, chaser): + self.chaser = chaser + + def _setMover(self, mover): + Impulse.Impulse._setMover(self, mover) + self.lookAtNode.reparentTo(self.nodePath) + self.vel = self.VecType(0) + self.rotVel = self.VecType(0) + + def _process(self, dt): + Impulse.Impulse._process(self, dt) + me = self.nodePath + chaser = self.chaser + chaserPos = chaser.getPos(me) + chaserPos.setZ(0) + distance = self.VecType(chaserPos).length() + self.lookAtNode.lookAt(chaser) + relH = reduceAngle(self.lookAtNode.getH(me) + 180.0) + epsilon = 0.005 + rotSpeed = self.mover.getRotSpeed() + if relH < -epsilon: + vH = -rotSpeed + elif relH > epsilon: + vH = rotSpeed + else: + vH = 0 + if abs(vH * dt) > abs(relH): + vH = relH / dt + if distance < self.maxDist and abs(relH) < self.moveAngle: + vForward = self.mover.getFwdSpeed() + else: + vForward = 0 + distanceLeft = self.maxDist - distance + if distanceLeft > 0.0 and vForward * dt > distanceLeft: + vForward = distanceLeft / dt + self.vel.setY(vForward) + self.rotVel.setX(vH) + self.mover.addShove(self.vel) + self.mover.addRotShove(self.rotVel) diff --git a/toontown/pets/PetGoal.py b/toontown/pets/PetGoal.py new file mode 100644 index 0000000..108ad01 --- /dev/null +++ b/toontown/pets/PetGoal.py @@ -0,0 +1,264 @@ +from direct.task import Task +from direct.fsm import FSM, ClassicFSM, State +from direct.showbase.PythonUtil import randFloat, Functor +from direct.directnotify import DirectNotifyGlobal +from toontown.pets import PetConstants +from toontown.toon import DistributedToonAI + +class PetGoal(FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PetGoal') + SerialNum = 0 + + def __init__(self): + FSM.FSM.__init__(self, self.__class__.__name__) + self.goalMgr = None + self.pet = None + self.brain = None + self.removeOnDone = 0 + self.serialNum = PetGoal.SerialNum + PetGoal.SerialNum += 1 + self.fsm = ClassicFSM.ClassicFSM('PetGoalFSM', [State.State('off', self.enterOff, self.exitOff, ['background']), State.State('background', self.enterBackground, self.exitBackground, ['foreground']), State.State('foreground', self.enterForeground, self.exitForeground, ['background'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def destroy(self): + if hasattr(self, 'fsm'): + self.fsm.requestFinalState() + del self.fsm + self.cleanup() + + def _removeSelf(self): + self.goalMgr.removeGoal(self) + + def getDoneEvent(self): + return 'PetGoalDone-%s' % self.serialNum + + def announceDone(self): + if self.removeOnDone: + self._removeSelf() + messenger.send(self.getDoneEvent()) + if self.removeOnDone: + self.destroy() + + def setGoalMgr(self, goalMgr): + self.goalMgr = goalMgr + self.pet = goalMgr.pet + self.brain = self.pet.brain + self.fsm.request('background') + + def clearGoalMgr(self): + self.goalMgr = None + self.pet = None + self.brain = None + self.fsm.requestFinalState() + return + + def getPriority(self): + return PetConstants.PriorityDefault + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterBackground(self): + pass + + def exitBackground(self): + pass + + def enterForeground(self): + pass + + def exitForeground(self): + pass + + def __repr__(self): + return self.__str__() + + def __str__(self): + return '%s: %s' % (self.__class__.__name__, self.getPriority()) + + +class InteractWithAvatar(PetGoal): + SerialNum = 0 + + def __init__(self, avatar): + PetGoal.__init__(self) + self.avatar = avatar + self.serialNum = InteractWithAvatar.SerialNum + InteractWithAvatar.SerialNum += 1 + self.transitionDoLaterName = '%s-doLater-%s' % (InteractWithAvatar.__name__, self.serialNum) + + def destroy(self): + PetGoal.destroy(self) + if hasattr(self, 'avatar'): + del self.avatar + + def enterForeground(self): + self.request('Chase') + + def exitForeground(self): + self.request('Off') + + def enterChase(self): + PetGoal.notify.debug('enterChase') + if self.brain.lookingAt(self.avatar.doId): + + def goToInteract(task = None, self = self): + self.request('Interact') + return Task.done + + taskMgr.doMethodLater(0.0001, goToInteract, self.transitionDoLaterName) + else: + self.accept(self.brain.getObserveEventAttendingAvStart(self.avatar.doId), Functor(self.request, 'Interact')) + self.brain._chase(self.avatar) + return + + def exitChase(self): + self.ignore(self.brain.getObserveEventAttendingAvStart(self.avatar.doId)) + taskMgr.remove(self.transitionDoLaterName) + + def enterInteract(self): + PetGoal.notify.debug('enterInteract') + if self._chaseAvInInteractMode(): + self.accept(self.brain.getObserveEventAttendingAvStop(self.avatar.doId), Functor(self.request, 'Chase')) + self.startInteract() + + def exitInteract(self): + self.stopInteract() + self.ignore(self.brain.getObserveEventAttendingAvStop(self.avatar.doId)) + + def startInteract(self): + pass + + def stopInteract(self): + pass + + def _chaseAvInInteractMode(self): + return True + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class Wander(PetGoal): + + def enterForeground(self): + self.brain._wander() + + +class ChaseAvatar(PetGoal): + + def __init__(self, avatar): + PetGoal.__init__(self) + self.avatar = avatar + self.isToon = isinstance(self.avatar, DistributedToonAI.DistributedToonAI) + + def destroy(self): + PetGoal.destroy(self) + if hasattr(self, 'avatar'): + del self.avatar + + def setGoalMgr(self, goalMgr): + PetGoal.setGoalMgr(self, goalMgr) + self.basePriority = PetConstants.PriorityChaseAv + + def getPriority(self): + priority = self.basePriority + if self.isToon and self.pet.mood.getDominantMood() == 'hunger': + priority *= PetConstants.HungerChaseToonScale + lastInteractTime = self.brain.lastInteractTime.get(self.avatar.doId) + if lastInteractTime is not None: + elapsed = globalClock.getFrameTime() - lastInteractTime + if elapsed < PetConstants.GettingAttentionGoalScaleDur: + priority *= PetConstants.GettingAttentionGoalScale + return priority + + def enterForeground(self): + self.brain._chase(self.avatar) + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class ChaseAvatarLeash(PetGoal): + + def __init__(self, avId): + PetGoal.__init__(self) + self.avId = avId + + def getPriority(self): + return PetConstants.PriorityDebugLeash + + def enterForeground(self): + av = simbase.air.doId2do.get(self.avId) + if av: + self.brain._chase(av) + else: + self._removeSelf() + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class FleeFromAvatar(PetGoal): + + def __init__(self, avatar): + PetGoal.__init__(self) + self.avatar = avatar + + def destroy(self): + PetGoal.destroy(self) + if hasattr(self, 'avatar'): + del self.avatar + + def getPriority(self): + priority = PetConstants.PriorityFleeFromAvatar + if self.avatar.doId == self.goalMgr.pet.ownerId: + priority *= PetConstants.FleeFromOwnerScale + return priority + + def enterForeground(self): + self.brain._chase(self.avatar) + + def __str__(self): + return '%s-%s: %s' % (self.__class__.__name__, self.avatar.doId, self.getPriority()) + + +class DoTrick(InteractWithAvatar): + + def __init__(self, avatar, trickId): + InteractWithAvatar.__init__(self, avatar) + self.trickId = trickId + self.removeOnDone = 1 + + def getPriority(self): + return PetConstants.PriorityDoTrick + + def setGoalMgr(self, goalMgr): + goalMgr._setHasTrickGoal(True) + InteractWithAvatar.setGoalMgr(self, goalMgr) + + def clearGoalMgr(self): + self.goalMgr._setHasTrickGoal(False) + InteractWithAvatar.clearGoalMgr(self) + + def _chaseAvInInteractMode(self): + return False + + def startInteract(self): + self.brain._doTrick(self.trickId, self.avatar) + self.trickDoneEvent = self.pet.actionFSM.getTrickDoneEvent() + self.accept(self.trickDoneEvent, self.announceDone) + + def stopInteract(self): + self.ignore(self.trickDoneEvent) + del self.trickDoneEvent + + def __str__(self): + return '%s-%s-%s: %s' % (self.__class__.__name__, + self.avatar.doId, + self.trickId, + self.getPriority()) diff --git a/toontown/pets/PetGoalMgr.py b/toontown/pets/PetGoalMgr.py new file mode 100644 index 0000000..cf1f275 --- /dev/null +++ b/toontown/pets/PetGoalMgr.py @@ -0,0 +1,120 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from direct.showbase.PythonUtil import randFloat, lerp +from toontown.pets import PetConstants +import random + +class PetGoalMgr(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('PetGoalMgr') + + def __init__(self, pet): + self.pet = pet + self.goals = {} + self._hasTrickGoal = False + self.primaryGoal = None + self.primaryStartT = 0 + if __dev__: + self.pscSetup = PStatCollector('App:Show code:petThink:UpdatePriorities:Setup') + self.pscFindPrimary = PStatCollector('App:Show code:petThink:UpdatePriorities:FindPrimary') + self.pscSetPrimary = PStatCollector('App:Show code:petThink:UpdatePriorities:SetPrimary') + return + + def destroy(self): + if __dev__: + del self.pscSetup + del self.pscFindPrimary + del self.pscSetPrimary + goals = self.goals.keys() + for goal in goals: + self.removeGoal(goal) + goal.destroy() + + del self.goals + + def hasTrickGoal(self): + return self._hasTrickGoal + + def _setHasTrickGoal(self, hasTrickGoal): + self._hasTrickGoal = hasTrickGoal + + def addGoal(self, goal): + self.goals[goal] = None + goal.setGoalMgr(self) + return + + def removeGoal(self, goal): + if self.primaryGoal == goal: + self._setPrimaryGoal(None) + goal.clearGoalMgr() + del self.goals[goal] + return + + def updatePriorities(self): + if len(self.goals) == 0: + return + if __dev__: + self.pscSetup.start() + if self.primaryGoal is None: + highestPriority = -99999.0 + candidates = [] + else: + highestPriority = self.primaryGoal.getPriority() + candidates = [self.primaryGoal] + decayDur = PetConstants.PrimaryGoalDecayDur + priFactor = PetConstants.PrimaryGoalScale + elapsed = min(decayDur, globalClock.getFrameTime() - self.primaryStartT) + highestPriority *= lerp(priFactor, 1.0, elapsed / decayDur) + if __dev__: + self.pscSetup.stop() + if __dev__: + self.pscFindPrimary.start() + for goal in self.goals: + thisPri = goal.getPriority() + if thisPri >= highestPriority: + if thisPri > highestPriority: + highestPriority = thisPri + candidates = [goal] + else: + candidates.append(goal) + + if __dev__: + self.pscFindPrimary.stop() + if __dev__: + self.pscSetPrimary.start() + newPrimary = random.choice(candidates) + if self.primaryGoal != newPrimary: + self.pet.notify.debug('new goal: %s, priority=%s' % (newPrimary.__class__.__name__, highestPriority)) + self._setPrimaryGoal(newPrimary) + if __dev__: + self.pscSetPrimary.stop() + return + + def _setPrimaryGoal(self, goal): + if self.primaryGoal == goal: + return + if self.primaryGoal is not None: + self.primaryGoal.fsm.request('background') + self.primaryGoal = goal + self.primaryStartT = globalClock.getFrameTime() + if goal is not None: + goal.fsm.request('foreground') + return + + def _handlePrimaryGoalDone(self): + self._setPrimaryGoal(None) + return + + def __repr__(self): + string = '%s' % self.__class__.__name__ + string += '\n Primary: %s' % self.primaryGoal + goalPairs = [] + for goal in self.goals: + goalPairs.append((goal.getPriority(), goal)) + + goalPairs.sort() + goalPairs.reverse() + for goalPair in goalPairs: + string += '\n %s' % goalPair[1] + + return string diff --git a/toontown/pets/PetHandle.py b/toontown/pets/PetHandle.py new file mode 100644 index 0000000..1eecdf1 --- /dev/null +++ b/toontown/pets/PetHandle.py @@ -0,0 +1,66 @@ +from toontown.toonbase import ToontownGlobals +from toontown.pets import PetMood, PetTraits, PetDetail + +class PetHandle: + + def __init__(self, avatar): + self.doId = avatar.doId + self.name = avatar.name + self.style = avatar.style + self.ownerId = avatar.ownerId + self.bFake = False + self.cr = avatar.cr + self.traits = PetTraits.PetTraits(avatar.traitSeed, avatar.safeZone, traitValueList=avatar.traitList) + self._grabMood(avatar) + + def _grabMood(self, avatar): + self.mood = avatar.lastKnownMood.makeCopy() + self.mood.setPet(self) + self.lastKnownMood = self.mood.makeCopy() + self.setLastSeenTimestamp(avatar.lastSeenTimestamp) + self.updateOfflineMood() + + def getDoId(self): + return self.doId + + def getOwnerId(self): + return self.ownerId + + def isPet(self): + return True + + def getName(self): + return self.name + + def getDNA(self): + return self.style + + def getFont(self): + return ToontownGlobals.getToonFont() + + def setLastSeenTimestamp(self, timestamp): + self.lastSeenTimestamp = timestamp + + def getTimeSinceLastSeen(self): + t = self.cr.getServerTimeOfDay() - self.lastSeenTimestamp + return max(0.0, t) + + def updateOfflineMood(self): + self.mood.driftMood(dt=self.getTimeSinceLastSeen(), curMood=self.lastKnownMood) + + def getDominantMood(self): + if not hasattr(self, 'mood'): + return PetMood.PetMood.Neutral + return self.mood.getDominantMood() + + def uniqueName(self, idString): + return idString + '-' + str(self.getDoId()) + + def updateMoodFromServer(self, callWhenDone = None): + + def handleGotDetails(avatar, callWhenDone = callWhenDone): + self._grabMood(avatar) + if callWhenDone: + callWhenDone() + + PetDetail.PetDetail(self.doId, handleGotDetails) diff --git a/toontown/pets/PetLeash.py b/toontown/pets/PetLeash.py new file mode 100644 index 0000000..94de2f4 --- /dev/null +++ b/toontown/pets/PetLeash.py @@ -0,0 +1,20 @@ +from pandac.PandaModules import * +from otp.movement import Impulse + +class PetLeash(Impulse.Impulse): + + def __init__(self, origin, length): + Impulse.Impulse.__init__(self) + self.origin = origin + self.length = length + + def _process(self, dt): + Impulse.Impulse._process(self, dt) + myPos = self.nodePath.getPos() + myDist = self.VecType(myPos - self.origin.getPos()).length() + if myDist > self.length: + excess = myDist - self.length + shove = self.VecType(myPos) + shove.normalize() + shove *= -excess + self.mover.addShove(shove) diff --git a/toontown/pets/PetLookerAI.py b/toontown/pets/PetLookerAI.py new file mode 100644 index 0000000..6cbdd42 --- /dev/null +++ b/toontown/pets/PetLookerAI.py @@ -0,0 +1,184 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import DirectObject +from otp.ai.AIZoneData import AIZoneData +from toontown.toonbase import ToontownGlobals +from toontown.pets import PetConstants + +def getStartLookingAtOtherEvent(lookingAvId): + return 'PetLookerAI-%s-startLookingAtOther' % lookingAvId + + +def getStopLookingAtOtherEvent(lookingAvId): + return 'PetLookerAI-%s-stopLookingAtOther' % lookingAvId + + +def getStartLookedAtByOtherEvent(lookedAtAvId): + return 'PetLookerAI-%s-startLookedAtByOther' % lookedAtAvId + + +def getStopLookedAtByOtherEvent(lookedAtAvId): + return 'PetLookerAI-%s-stopLookedAtByOther' % lookedAtAvId + + +class PetLookerAI: + notify = DirectNotifyGlobal.directNotify.newCategory('PetLookerAI') + + def __init__(self): + self.__active = 0 + self.others = {} + + def destroy(self): + if self.__active: + self.exitPetLook() + if len(self.others): + PetLookerAI.notify.warning('%s: self.others not empty: %s' % (self.doId, self.others.keys())) + self.others = {} + + def _getPetLookerBodyNode(self): + return self + + def _isPet(self): + return 0 + + def enterPetLook(self): + PetLookerAI.notify.debug('enterPetLook: %s' % self.doId) + if self.__active: + PetLookerAI.notify.warning('enterPetLook: %s already active!' % self.doId) + return + if len(self.others): + PetLookerAI.notify.warning('%s: len(self.others) != 0: %s' % (self.doId, self.others.keys())) + self.others = {} + self.__active = 1 + self.__collNode = self._getPetLookerBodyNode().attachNewNode('PetLookerCollNode') + self._createPetLookSphere() + + def exitPetLook(self): + PetLookerAI.notify.debug('exitPetLook: %s' % self.doId) + if not self.__active: + PetLookerAI.notify.warning('exitPetLook: %s not active!' % self.doId) + return + if len(self.others): + otherIds = self.others.keys() + PetLookerAI.notify.warning('%s: still in otherIds: %s' % (self.doId, otherIds)) + for otherId in otherIds: + self._handleLookingAtOtherStop(otherId) + + if len(self.others): + PetLookerAI.notify.warning('%s: self.others still not empty: %s' % (self.doId, self.others.keys())) + self.others = {} + self._destroyPetLookSphere() + self.__collNode.removeNode() + del self.__collNode + self.__active = 0 + + def _createPetLookSphere(self): + isPet = self._isPet() + if isPet: + radius = PetConstants.PetSphereRadius + else: + radius = PetConstants.NonPetSphereRadius + lookSphere = CollisionSphere(0, 0, 0, radius) + lookSphereNode = CollisionNode('petLookSphere-%s' % self.doId) + lookSphereNode.addSolid(lookSphere) + lookSphereNode.setFromCollideMask(BitMask32.allOff()) + if isPet: + intoCollideMask = ToontownGlobals.PetLookatPetBitmask + fromCollideMask = ToontownGlobals.PetLookatPetBitmask | ToontownGlobals.PetLookatNonPetBitmask + else: + intoCollideMask = ToontownGlobals.PetLookatNonPetBitmask + fromCollideMask = ToontownGlobals.PetLookatPetBitmask + lookSphereNode.setIntoCollideMask(intoCollideMask) + lookSphereNode.setFromCollideMask(fromCollideMask) + self.lookSphereNodePath = self.__collNode.attachNewNode(lookSphereNode) + self.lookSphereNodePath.setTag('petLooker', '%s' % self.doId) + self._cHandler = CollisionHandlerEvent() + self._cHandler.addInPattern(self._getLookingStartEvent()) + self._cHandler.addOutPattern(self._getLookingStopEvent()) + collTrav = self.getCollTrav() + if collTrav: + collTrav.addCollider(self.lookSphereNodePath, self._cHandler) + self.accept(self._getLookingStartEvent(), self._handleLookingAtOtherStart) + self.accept(self._getLookingStopEvent(), self._handleLookingAtOtherStop) + if hasattr(self, 'eventProxy'): + PetLookerAI.notify.warning('%s: already have an eventProxy!' % self.doId) + else: + self.eventProxy = DirectObject.DirectObject() + self.eventProxy.accept(self.getZoneChangeEvent(), self._handleZoneChange) + + def _destroyPetLookSphere(self): + collTrav = self.getCollTrav() + if collTrav: + collTrav.removeCollider(self.lookSphereNodePath) + del self._cHandler + self.lookSphereNodePath.removeNode() + del self.lookSphereNodePath + self.ignore(self._getLookingStartEvent()) + self.ignore(self._getLookingStopEvent()) + self.eventProxy.ignoreAll() + del self.eventProxy + + def _handleZoneChange(self, newZoneId, oldZoneId): + PetLookerAI.notify.debug('_handleZoneChange: %s' % self.doId) + if not self.__active: + PetLookerAI.notify.warning('%s: _handleZoneChange: not active!' % self.doId) + return + oldZoneData = AIZoneData(self.air, self.parentId, oldZoneId) + if oldZoneData.hasCollTrav(): + oldZoneData.getCollTrav().removeCollider(self.lookSphereNodePath) + oldZoneData.destroy() + newZoneData = AIZoneData(self.air, self.parentId, newZoneId) + if newZoneData.hasCollTrav(): + newZoneData.getCollTrav().addCollider(self.lookSphereNodePath, self._cHandler) + newZoneData.destroy() + + def _getLookingStartEvent(self): + return 'PetLookerAI-lookingStart-%s' % self.doId + + def _getLookingStopEvent(self): + return 'PetLookerAI-lookingStop-%s' % self.doId + + def __getOtherLookerDoIdFromCollEntry(self, collEntry): + into = collEntry.getIntoNodePath() + if not into.hasTag('petLooker'): + return 0 + return int(into.getTag('petLooker')) + + def _handleLookingAtOtherStart(self, other): + if not self.__active: + PetLookerAI.notify.warning('%s: _handleLookingAtOtherStart: not active!' % self.doId) + return + if isinstance(other, CollisionEntry): + other = self.__getOtherLookerDoIdFromCollEntry(other) + if other == 0: + PetLookerAI.notify.warning('%s: looking at unknown other avatar' % self.doId) + return + PetLookerAI.notify.debug('_handleLookingAtOtherStart: %s looking at %s' % (self.doId, other)) + if other in self.others: + PetLookerAI.notify.warning('%s: other (%s) is already in self.others!' % (self.doId, other)) + if not hasattr(self, '_cHandler'): + PetLookerAI.notify.warning('-->The looker sphere has already been destroyed') + else: + self.others[other] = None + messenger.send(getStartLookingAtOtherEvent(self.doId), [other]) + messenger.send(getStartLookedAtByOtherEvent(other), [self.doId]) + return + + def _handleLookingAtOtherStop(self, other): + if not self.__active: + PetLookerAI.notify.warning('%s: _handleLookingAtOtherStop: not active!' % self.doId) + return + if isinstance(other, CollisionEntry): + other = self.__getOtherLookerDoIdFromCollEntry(other) + if other == 0: + PetLookerAI.notify.warning('%s: stopped looking at unknown other avatar' % self.doId) + return + PetLookerAI.notify.debug('_handleLookingAtOtherStop: %s no longer looking at %s' % (self.doId, other)) + if other not in self.others: + PetLookerAI.notify.warning('%s: other (%s) is not in self.others!' % (self.doId, other)) + if not hasattr(self, '_cHandler'): + PetLookerAI.notify.warning('-->The looker sphere has already been destroyed') + else: + del self.others[other] + messenger.send(getStopLookingAtOtherEvent(self.doId), [other]) + messenger.send(getStopLookedAtByOtherEvent(other), [self.doId]) diff --git a/toontown/pets/PetManager.py b/toontown/pets/PetManager.py new file mode 100644 index 0000000..2544aec --- /dev/null +++ b/toontown/pets/PetManager.py @@ -0,0 +1,39 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from direct.task import Task + +def acquirePetManager(): + if not hasattr(base, 'petManager'): + PetManager() + base.petManager.incRefCount() + + +def releasePetManager(): + base.petManager.decRefCount() + + +class PetManager: + CollTaskName = 'petFloorCollisions' + + def __init__(self): + base.petManager = self + self.refCount = 0 + self.cTrav = CollisionTraverser('petFloorCollisions') + taskMgr.add(self._doCollisions, PetManager.CollTaskName, priority=ToontownGlobals.PetFloorCollPriority) + + def _destroy(self): + taskMgr.remove(PetManager.CollTaskName) + del self.cTrav + + def _doCollisions(self, task): + self.cTrav.traverse(render) + return Task.cont + + def incRefCount(self): + self.refCount += 1 + + def decRefCount(self): + self.refCount -= 1 + if self.refCount == 0: + self._destroy() + del base.petManager diff --git a/toontown/pets/PetMood.py b/toontown/pets/PetMood.py new file mode 100644 index 0000000..0e53d57 --- /dev/null +++ b/toontown/pets/PetMood.py @@ -0,0 +1,214 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +from direct.showbase.PythonUtil import lerp, average, clampScalar +from toontown.toonbase import TTLocalizer +import random, time, weakref + +class PetMood: + notify = DirectNotifyGlobal.directNotify.newCategory('PetMood') + Neutral = 'neutral' + Components = ('boredom', 'restlessness', 'playfulness', 'loneliness', 'sadness', 'affection', 'hunger', 'confusion', 'excitement', 'fatigue', 'anger', 'surprise') + SerialNum = 0 + ContentedMoods = ('neutral', 'excitement', 'playfulness', 'affection') + ExcitedMoods = ('excitement', 'playfulness') + UnhappyMoods = ('boredom', 'restlessness', 'loneliness', 'sadness', 'fatigue', 'hunger', 'anger') + DisabledDominants = ('restlessness', 'playfulness') + AssertiveDominants = ('fatigue',) + HOUR = 1.0 + MINUTE = HOUR / 60.0 + DAY = 24.0 * HOUR + WEEK = 7 * DAY + LONGTIME = 5000 * WEEK + TBoredom = 12 * HOUR + TRestlessness = 18 * HOUR + TPlayfulness = -1 * HOUR + TLoneliness = 24 * HOUR + TSadness = -1 * HOUR + TFatigue = -15 * MINUTE + THunger = 24 * HOUR + TConfusion = -5 * MINUTE + TExcitement = -5 * MINUTE + TSurprise = -5 * MINUTE + TAffection = -10 * MINUTE + TAngerDec = -20 * MINUTE + TAngerInc = 2 * WEEK + + def __init__(self, pet = None): + self.setPet(pet) + self.started = 0 + self.serialNum = PetMood.SerialNum + PetMood.SerialNum += 1 + for comp in PetMood.Components: + self.__dict__[comp] = 0.0 + + def calcDrift(baseT, trait, fasterDriftIsBetter = False): + value = trait.percentile + if not trait.higherIsBetter: + value = 1.0 - value + if fasterDriftIsBetter: + if value < 0.5: + factor = lerp(2.0, 1.0, value * 2.0) + else: + rebased = (value - 0.5) * 2.0 + factor = lerp(1.0, 0.1, rebased * rebased) + elif value < 0.5: + factor = lerp(0.75, 1.0, value * 2.0) + else: + rebased = (value - 0.5) * 2.0 + factor = lerp(1.0, 28.0, rebased * rebased) + return baseT * factor + + pet = self.getPet() + self.tBoredom = calcDrift(PetMood.TBoredom, pet.traits.traits['boredomThreshold']) + self.tRestlessness = calcDrift(PetMood.TRestlessness, pet.traits.traits['restlessnessThreshold']) + self.tPlayfulness = calcDrift(PetMood.TPlayfulness, pet.traits.traits['playfulnessThreshold']) + self.tLoneliness = calcDrift(PetMood.TLoneliness, pet.traits.traits['lonelinessThreshold']) + self.tSadness = calcDrift(PetMood.TSadness, pet.traits.traits['sadnessThreshold'], True) + self.tFatigue = calcDrift(PetMood.TFatigue, pet.traits.traits['fatigueThreshold'], True) + self.tHunger = calcDrift(PetMood.THunger, pet.traits.traits['hungerThreshold']) + self.tConfusion = calcDrift(PetMood.TConfusion, pet.traits.traits['confusionThreshold'], True) + self.tExcitement = calcDrift(PetMood.TExcitement, pet.traits.traits['excitementThreshold']) + self.tSurprise = calcDrift(PetMood.TSurprise, pet.traits.traits['surpriseThreshold'], True) + self.tAffection = calcDrift(PetMood.TAffection, pet.traits.traits['affectionThreshold']) + self.tAngerDec = calcDrift(PetMood.TAngerDec, pet.traits.traits['angerThreshold'], True) + self.tAngerInc = calcDrift(PetMood.TAngerInc, pet.traits.traits['angerThreshold']) + self.dominantMood = PetMood.Neutral + + def destroy(self): + self.stop() + del self.petRef + + def setPet(self, pet): + self.petRef = weakref.ref(pet) + + def getPet(self): + pet = self.petRef() + if pet is None: + self.notify.error('pet has been deleted') + return pet + + def getMoodDriftTaskName(self): + return 'petMoodDrift-%s' % self.serialNum + + def getMoodChangeEvent(self): + return 'petMoodChange-%s' % self.serialNum + + def getDominantMoodChangeEvent(self): + return 'petDominantMoodChange-%s' % self.serialNum + + def announceChange(self, components = []): + oldMood = self.dominantMood + if hasattr(self, 'dominantMood'): + del self.dominantMood + newMood = self.getDominantMood() + messenger.send(self.getMoodChangeEvent(), [components]) + if newMood != oldMood: + messenger.send(self.getDominantMoodChangeEvent(), [newMood]) + + def getComponent(self, compName): + return self.__dict__[compName] + + def setComponent(self, compName, value, announce = 1): + different = self.__dict__[compName] != value + self.__dict__[compName] = value + if announce and different: + self.announceChange([compName]) + + def _getComponentThreshold(self, compName): + threshName = compName + 'Threshold' + pet = self.getPet() + return pet.traits.__dict__[threshName] + + def isComponentActive(self, compName): + return self.getComponent(compName) >= self._getComponentThreshold(compName) + + def anyActive(self, compNames): + for comp in compNames: + if self.isComponentActive(comp): + return 1 + + return 0 + + def getDominantMood(self): + if hasattr(self, 'dominantMood'): + return self.dominantMood + dominantMood = PetMood.Neutral + priority = 1.0 + for comp in PetMood.Components: + if comp in PetMood.DisabledDominants: + continue + value = self.getComponent(comp) + pri = value / max(self._getComponentThreshold(comp), 0.01) + if pri >= priority: + dominantMood = comp + priority = pri + elif comp in PetMood.AssertiveDominants and pri >= 1.0: + dominantMood = comp + + self.dominantMood = dominantMood + return dominantMood + + def makeCopy(self): + other = PetMood(self.getPet()) + for comp in PetMood.Components: + other.__dict__[comp] = self.__dict__[comp] + + return other + + def start(self): + pet = self.getPet() + taskMgr.doMethodLater(simbase.petMoodDriftPeriod / simbase.petMoodTimescale * random.random(), self._driftMoodTask, self.getMoodDriftTaskName()) + self.started = 1 + + def stop(self): + if not self.started: + return + self.started = 0 + taskMgr.remove(self.getMoodDriftTaskName()) + + def driftMood(self, dt = None, curMood = None): + now = globalClock.getFrameTime() + if not hasattr(self, 'lastDriftTime'): + self.lastDriftTime = now + if dt is None: + dt = now - self.lastDriftTime + self.lastDriftTime = now + if dt <= 0.0: + return + if curMood is None: + curMood = self + + def doDrift(curValue, timeToMedian, dt = float(dt)): + newValue = curValue + dt / (timeToMedian * 7200) + return clampScalar(newValue, 0.0, 1.0) + + self.boredom = doDrift(curMood.boredom, self.tBoredom) + self.loneliness = doDrift(curMood.loneliness, self.tLoneliness) + self.sadness = doDrift(curMood.sadness, self.tSadness) + self.fatigue = doDrift(curMood.fatigue, self.tFatigue) + self.hunger = doDrift(curMood.hunger, self.tHunger) + self.confusion = doDrift(curMood.confusion, self.tConfusion) + self.excitement = doDrift(curMood.excitement, self.tExcitement) + self.surprise = doDrift(curMood.surprise, self.tSurprise) + self.affection = doDrift(curMood.affection, self.tAffection) + abuse = average(curMood.hunger, curMood.hunger, curMood.hunger, curMood.boredom, curMood.loneliness) + tipPoint = 0.6 + if abuse < tipPoint: + tAnger = lerp(self.tAngerDec, -PetMood.LONGTIME, abuse / tipPoint) + else: + tAnger = lerp(PetMood.LONGTIME, self.tAngerInc, (abuse - tipPoint) / (1.0 - tipPoint)) + self.anger = doDrift(curMood.anger, tAnger) + self.announceChange() + return + + def _driftMoodTask(self, task = None): + self.driftMood() + taskMgr.doMethodLater(simbase.petMoodDriftPeriod / simbase.petMoodTimescale, self._driftMoodTask, self.getMoodDriftTaskName()) + return Task.done + + def __repr__(self): + s = '%s' % self.__class__.__name__ + for comp in PetMood.Components: + s += '\n %s: %s' % (comp, self.__dict__[comp]) + + return s diff --git a/toontown/pets/PetNameGenerator.py b/toontown/pets/PetNameGenerator.py new file mode 100644 index 0000000..f496764 --- /dev/null +++ b/toontown/pets/PetNameGenerator.py @@ -0,0 +1,86 @@ +import random +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import os +from direct.showbase import AppRunnerGlobal +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * + +class PetNameGenerator: + notify = DirectNotifyGlobal.directNotify.newCategory('PetNameGenerator') + boyFirsts = [] + girlFirsts = [] + neutralFirsts = [] + + def __init__(self): + self.generateLists() + + def generateLists(self): + self.boyFirsts = [] + self.girlFirsts = [] + self.neutralFirsts = [] + self.nameDictionary = {} + searchPath = DSearchPath() + if AppRunnerGlobal.appRunner: + searchPath.appendDirectory(Filename.expandFrom('$TT_3_ROOT/phase_3/etc')) + else: + searchPath.appendDirectory(Filename('phase_3/etc')) + if os.path.expandvars('$TOONTOWN') != '': + searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$TOONTOWN/src/configfiles'))) + else: + searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('toontown/src/configfiles'))) + searchPath.appendDirectory(Filename('.')) + filename = Filename(TTLocalizer.PetNameMaster) + found = vfs.resolveFilename(filename, searchPath) + if not found: + self.notify.error('PetNameGenerator: Error opening name list text file.') + input = StreamReader(vfs.openReadFile(filename, 1), 1) + currentLine = input.readline() + while currentLine: + if currentLine.lstrip()[0:1] != '#': + a1 = currentLine.find('*') + a2 = currentLine.find('*', a1 + 1) + self.nameDictionary[int(currentLine[0:a1])] = (int(currentLine[a1 + 1:a2]), currentLine[a2 + 1:len(currentLine) - 1].strip()) + currentLine = input.readline() + + masterList = [self.boyFirsts, self.girlFirsts, self.neutralFirsts] + for tu in self.nameDictionary.values(): + masterList[tu[0]].append(tu[1]) + + return 1 + + def getName(self, uniqueID): + try: + return self.nameDictionary[uniqueID][1] + except: + return self.nameDictionary[0][1] + + def returnUniqueID(self, name): + newtu = [(), (), ()] + newtu[0] = (0, name) + newtu[1] = (1, name) + newtu[2] = (2, name) + for tu in self.nameDictionary.items(): + for g in newtu: + if tu[1] == g: + return tu[0] + + return -1 + + def randomName(self, gender = None, seed = None): + S = random.getstate() + if seed is not None: + random.seed(seed) + if gender is None: + gender = random.choice([0, 1]) + retString = '' + firstList = self.neutralFirsts[:] + if gender == 0: + firstList += self.boyFirsts + elif gender == 1: + firstList += self.girlFirsts + else: + self.error('Must be boy or girl.') + retString += random.choice(firstList) + random.setstate(S) + return retString diff --git a/toontown/pets/PetObserve.py b/toontown/pets/PetObserve.py new file mode 100644 index 0000000..e502118 --- /dev/null +++ b/toontown/pets/PetObserve.py @@ -0,0 +1,203 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.PythonUtil import list2dict, Enum +from toontown.pets import PetTricks +import types +notify = DirectNotifyGlobal.directNotify.newCategory('PetObserve') + +def getEventName(zoneId): + return 'PetObserve-%s' % zoneId + + +def send(zoneIds, petObserve): + if petObserve.isValid(): + if type(zoneIds) not in (types.ListType, types.TupleType): + zoneIds = [zoneIds] + for zoneId in zoneIds: + messenger.send(getEventName(zoneId), [petObserve]) + + +Phrases = Enum('HI, BYE, YES, NO, SOOTHE, PRAISE, CRITICISM, HAPPY,SAD, ANGRY, HURRY, QUESTION, FRIENDLY, LETS_PLAY,COME, FOLLOW_ME, STAY, NEED_LAFF, NEED_GAGS, NEED_JB,GO_AWAY, DO_TRICK,') +Actions = Enum('FEED, SCRATCH,ATTENDED_START, ATTENDED_STOP,ATTENDING_START, ATTENDING_STOP,CHANGE_ZONE, LOGOUT,GARDEN') + +class PetObserve: + + def isValid(self): + return 1 + + def isForgettable(self): + return 0 + + def _influence(self, petBrain): + petBrain._handleGenericObserve(self) + + def __repr__(self): + return '%s()' % self.__class__.__name__ + + +class PetActionObserve(PetObserve): + + def __init__(self, action, avId, data = None): + self.action = action + self.avId = avId + self.data = data + + def getAction(self): + return self.action + + def getAvId(self): + return self.avId + + def getData(self): + return self.data + + def _influence(self, petBrain): + petBrain._handleActionObserve(self) + + def __repr__(self): + return '%s(%s,%s)' % (self.__class__.__name__, Actions.getString(self.action), self.avId) + + +class PetPhraseObserve(PetObserve): + + def __init__(self, petPhrase, avId): + self.petPhrase = petPhrase + self.avId = avId + + def getPetPhrase(self): + return self.petPhrase + + def getAvId(self): + return self.avId + + def isForgettable(self): + return 1 + + def _influence(self, petBrain): + petBrain._handlePhraseObserve(self) + + def __repr__(self): + return '%s(%s,%s)' % (self.__class__.__name__, Phrases.getString(self.petPhrase), self.avId) + + +class SCObserve(PetPhraseObserve): + + def __init__(self, msgId, petPhrase, avId): + self.msgId = msgId + PetPhraseObserve.__init__(self, petPhrase, avId) + + def isValid(self): + return self.petPhrase is not None + + +class TrickRequestObserve(PetPhraseObserve): + + def __init__(self, trickId, avId): + self.trickId = trickId + PetPhraseObserve.__init__(self, Phrases.DO_TRICK, avId) + + def isForgettable(self): + return 0 + + def getTrickId(self): + return self.trickId + + +OP = Phrases +_scPhrase2petPhrase = {1: OP.YES, + 2: OP.NO, + 3: OP.SOOTHE, + 100: OP.HI, + 101: OP.HI, + 102: OP.HI, + 103: OP.HI, + 104: OP.HI, + 105: OP.HI, + 107: OP.HI, + 108: OP.HI, + 200: OP.BYE, + 201: OP.BYE, + 202: OP.BYE, + 203: OP.BYE, + 204: OP.BYE, + 205: OP.BYE, + 206: OP.BYE, + 207: OP.BYE, + 300: OP.HAPPY, + 301: OP.HAPPY, + 302: OP.HAPPY, + 303: OP.HAPPY, + 304: OP.HAPPY, + 305: OP.HAPPY, + 306: OP.HAPPY, + 307: OP.HAPPY, + 308: OP.HAPPY, + 309: OP.HAPPY, + 310: OP.HAPPY, + 311: OP.HAPPY, + 312: OP.HAPPY, + 313: OP.HAPPY, + 314: OP.HAPPY, + 315: OP.HAPPY, + 400: OP.SAD, + 401: OP.SAD, + 402: OP.SAD, + 403: OP.SAD, + 404: OP.SAD, + 405: OP.SAD, + 406: OP.SAD, + 407: OP.NO, + 410: OP.NEED_LAFF, + 500: OP.FRIENDLY, + 505: OP.PRAISE, + 506: OP.HAPPY, + 507: OP.FRIENDLY, + 508: OP.FRIENDLY, + 509: OP.FRIENDLY, + 510: OP.QUESTION, + 511: OP.QUESTION, + 513: OP.QUESTION, + 514: OP.NEED_LAFF, + 600: OP.PRAISE, + 601: OP.PRAISE, + 602: OP.PRAISE, + 603: OP.PRAISE, + 700: OP.PRAISE, + 701: OP.PRAISE, + 900: OP.CRITICISM, + 901: OP.CRITICISM, + 902: OP.CRITICISM, + 903: OP.CRITICISM, + 904: OP.CRITICISM, + 905: OP.CRITICISM, + 1006: OP.FOLLOW_ME, + 1007: OP.STAY, + 1010: OP.STAY, + 1015: OP.STAY, + 1201: OP.CRITICISM, + 1300: OP.NEED_LAFF, + 1400: OP.HURRY, + 1404: OP.PRAISE, + 1405: OP.PRAISE, + 1413: OP.NEED_GAGS, + 1414: OP.NEED_LAFF, + 1601: OP.NEED_JB, + 1603: OP.HURRY, + 1605: OP.LETS_PLAY, + 1606: OP.LETS_PLAY, + 21000: OP.COME, + 21001: OP.COME, + 21002: OP.STAY, + 21003: OP.PRAISE, + 21004: OP.PRAISE, + 21005: OP.PRAISE} +for scId in PetTricks.ScId2trickId: + _scPhrase2petPhrase[scId] = OP.DO_TRICK + +del OP + +def getSCObserve(msgId, speakerDoId): + phrase = _scPhrase2petPhrase.get(msgId) + if phrase == Phrases.DO_TRICK: + trickId = PetTricks.ScId2trickId[msgId] + return TrickRequestObserve(trickId, speakerDoId) + return SCObserve(msgId, phrase, speakerDoId) diff --git a/toontown/pets/PetSphere.py b/toontown/pets/PetSphere.py new file mode 100644 index 0000000..e4677b5 --- /dev/null +++ b/toontown/pets/PetSphere.py @@ -0,0 +1,47 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import reduceAngle +from otp.movement import Impulse +from otp.otpbase import OTPGlobals + +class PetSphere(Impulse.Impulse): + SerialNum = 0 + + def __init__(self, petRadius, collTrav): + Impulse.Impulse.__init__(self) + self.serialNum = PetSphere.SerialNum + PetSphere.SerialNum += 1 + self.petRadius = petRadius + self.collTrav = collTrav + + def _setMover(self, mover): + Impulse.Impulse._setMover(self, mover) + self.cSphere = CollisionSphere(0.0, 0.0, 0.0, self.petRadius) + cSphereNode = CollisionNode('PetSphere') + cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = hidden.attachNewNode(cSphereNode) + self.cSphereNodePath.reparentTo(self.nodePath) + cSphereNode.setFromCollideMask(OTPGlobals.WallBitmask) + cSphereNode.setIntoCollideMask(OTPGlobals.WallBitmask) + self.pusher = CollisionHandlerPusher() + self.pusher.setHorizontal(1) + self.pusher.setInPattern('enter%in') + self.pusher.setOutPattern('exit%in') + self.pusher.addCollider(self.cSphereNodePath, self.nodePath) + self.pusher.addInPattern(self._getCollisionEvent()) + self.collTrav.addCollider(self.cSphereNodePath, self.pusher) + self.accept(self._getCollisionEvent(), self._handleCollision) + + def _clearMover(self, mover): + self.ignore(self._getCollisionEvent()) + self.collTrav.removeCollider(self.cSphereNodePath) + del self.cSphere + del self.pusher + del self.collTrav + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + + def _getCollisionEvent(self): + return 'petSphereColl-%s' % self.serialNum + + def _handleCollision(self, collEntry): + messenger.send(self.mover.getCollisionEventName(), [collEntry]) diff --git a/toontown/pets/PetTraits.py b/toontown/pets/PetTraits.py new file mode 100644 index 0000000..538b1ec --- /dev/null +++ b/toontown/pets/PetTraits.py @@ -0,0 +1,248 @@ +from direct.showbase.PythonUtil import randFloat, normalDistrib, Enum +from direct.showbase.PythonUtil import clampScalar +from toontown.toonbase import TTLocalizer, ToontownGlobals +import random, copy +TraitDivisor = 10000 + +def getTraitNames(): + if not hasattr(PetTraits, 'TraitNames'): + traitNames = [] + for desc in PetTraits.TraitDescs: + traitNames.append(desc[0]) + PetTraits.TraitNames = traitNames + + return PetTraits.TraitNames + + +def uniform(min, max, rng): + return randFloat(min, max, rng.random) + + +def gaussian(min, max, rng): + return normalDistrib(min, max, rng.gauss) + + +class TraitDistribution: + TraitQuality = Enum('VERY_BAD, BAD, AVERAGE, GOOD, VERY_GOOD') + TraitTypes = Enum('INCREASING, DECREASING') + Sz2MinMax = None + TraitType = None + TraitCutoffs = {TraitTypes.INCREASING: {TraitQuality.VERY_BAD: 0.1, + TraitQuality.BAD: 0.25, + TraitQuality.GOOD: 0.75, + TraitQuality.VERY_GOOD: 0.9}, + TraitTypes.DECREASING: {TraitQuality.VERY_BAD: 0.9, + TraitQuality.BAD: 0.75, + TraitQuality.GOOD: 0.25, + TraitQuality.VERY_GOOD: 0.1}} + + def __init__(self, rndFunc = gaussian): + self.rndFunc = rndFunc + if not hasattr(self.__class__, 'GlobalMinMax'): + _min = 1.0 + _max = 0.0 + minMax = self.Sz2MinMax + for sz in minMax: + thisMin, thisMax = minMax[sz] + _min = min(_min, thisMin) + _max = max(_max, thisMax) + + self.__class__.GlobalMinMax = [_min, _max] + + def getRandValue(self, szId, rng = random): + min, max = self.getMinMax(szId) + return self.rndFunc(min, max, rng) + + def getHigherIsBetter(self): + return self.TraitType == TraitDistribution.TraitTypes.INCREASING + + def getMinMax(self, szId): + return (self.Sz2MinMax[szId][0], self.Sz2MinMax[szId][1]) + + def getGlobalMinMax(self): + return (self.GlobalMinMax[0], self.GlobalMinMax[1]) + + def _getTraitPercent(self, traitValue): + gMin, gMax = self.getGlobalMinMax() + if traitValue < gMin: + gMin = traitValue + elif traitValue > gMax: + gMax = traitValue + return (traitValue - gMin) / (gMax - gMin) + + def getPercentile(self, traitValue): + if self.TraitType is TraitDistribution.TraitTypes.INCREASING: + return self._getTraitPercent(traitValue) + else: + return 1.0 - self._getTraitPercent(traitValue) + + def getQuality(self, traitValue): + TraitQuality = TraitDistribution.TraitQuality + TraitCutoffs = self.TraitCutoffs[self.TraitType] + percent = self._getTraitPercent(traitValue) + if self.TraitType is TraitDistribution.TraitTypes.INCREASING: + if percent <= TraitCutoffs[TraitQuality.VERY_BAD]: + return TraitQuality.VERY_BAD + elif percent <= TraitCutoffs[TraitQuality.BAD]: + return TraitQuality.BAD + elif percent >= TraitCutoffs[TraitQuality.VERY_GOOD]: + return TraitQuality.VERY_GOOD + elif percent >= TraitCutoffs[TraitQuality.GOOD]: + return TraitQuality.GOOD + else: + return TraitQuality.AVERAGE + elif percent <= TraitCutoffs[TraitQuality.VERY_GOOD]: + return TraitQuality.VERY_GOOD + elif percent <= TraitCutoffs[TraitQuality.GOOD]: + return TraitQuality.GOOD + elif percent >= TraitCutoffs[TraitQuality.VERY_BAD]: + return TraitQuality.VERY_BAD + elif percent >= TraitCutoffs[TraitQuality.BAD]: + return TraitQuality.BAD + else: + return TraitQuality.AVERAGE + + def getExtremeness(self, traitValue): + percent = self._getTraitPercent(traitValue) + if percent < 0.5: + howExtreme = (0.5 - percent) * 2.0 + else: + howExtreme = (percent - 0.5) * 2.0 + return clampScalar(howExtreme, 0.0, 1.0) + + +class PetTraits: + + class StdIncDistrib(TraitDistribution): + TraitType = TraitDistribution.TraitTypes.INCREASING + Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.2, 0.65), + ToontownGlobals.DonaldsDock: (0.3, 0.7), + ToontownGlobals.DaisyGardens: (0.4, 0.75), + ToontownGlobals.MinniesMelodyland: (0.5, 0.8), + ToontownGlobals.TheBrrrgh: (0.6, 0.85), + ToontownGlobals.DonaldsDreamland: (0.7, 0.9)} + + class StdDecDistrib(TraitDistribution): + TraitType = TraitDistribution.TraitTypes.DECREASING + Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.35, 0.8), + ToontownGlobals.DonaldsDock: (0.3, 0.7), + ToontownGlobals.DaisyGardens: (0.25, 0.6), + ToontownGlobals.MinniesMelodyland: (0.2, 0.5), + ToontownGlobals.TheBrrrgh: (0.15, 0.4), + ToontownGlobals.DonaldsDreamland: (0.1, 0.3)} + + class ForgetfulnessDistrib(TraitDistribution): + TraitType = TraitDistribution.TraitTypes.DECREASING + Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.0, 1.0), + ToontownGlobals.DonaldsDock: (0.0, 0.9), + ToontownGlobals.DaisyGardens: (0.0, 0.8), + ToontownGlobals.MinniesMelodyland: (0.0, 0.7), + ToontownGlobals.TheBrrrgh: (0.0, 0.6), + ToontownGlobals.DonaldsDreamland: (0.0, 0.5)} + + TraitDescs = (('forgetfulness', ForgetfulnessDistrib(), True), + ('boredomThreshold', StdIncDistrib(), True), + ('restlessnessThreshold', StdIncDistrib(), True), + ('playfulnessThreshold', StdDecDistrib(), True), + ('lonelinessThreshold', StdIncDistrib(), True), + ('sadnessThreshold', StdIncDistrib(), True), + ('fatigueThreshold', StdIncDistrib(), True), + ('hungerThreshold', StdIncDistrib(), True), + ('confusionThreshold', StdIncDistrib(), True), + ('excitementThreshold', StdDecDistrib(), True), + ('angerThreshold', StdIncDistrib(), True), + ('surpriseThreshold', StdIncDistrib(), False), + ('affectionThreshold', StdDecDistrib(), True)) + NumTraits = len(TraitDescs) + + class Trait: + + def __init__(self, index, traitsObj, value = None): + self.name, distrib, self.hasWorth = PetTraits.TraitDescs[index] + if value is not None: + self.value = value + else: + szId = traitsObj.safeZoneId + self.value = distrib.getRandValue(szId, traitsObj.rng) + self.value = int(self.value * TraitDivisor) / float(TraitDivisor) + self.higherIsBetter = distrib.getHigherIsBetter() + self.percentile = distrib.getPercentile(self.value) + self.quality = distrib.getQuality(self.value) + self.howExtreme = distrib.getExtremeness(self.value) + return + + def __repr__(self): + return 'Trait: %s, %s, %s, %s' % (self.name, + self.value, + TraitDistribution.TraitQuality.getString(self.quality), + self.howExtreme) + + def __init__(self, traitSeed, safeZoneId, traitValueList = []): + self.traitSeed = traitSeed + self.safeZoneId = safeZoneId + self.rng = random.Random(self.traitSeed) + self.traits = {} + for i in xrange(len(PetTraits.TraitDescs)): + if i < len(traitValueList) and traitValueList[i] > 0.0: + trait = PetTraits.Trait(i, self, traitValueList[i]) + else: + trait = PetTraits.Trait(i, self) + self.traits[trait.name] = trait + self.__dict__[trait.name] = trait.value + + extremeTraits = [] + for trait in self.traits.values(): + if not trait.hasWorth: + continue + if trait.quality == TraitDistribution.TraitQuality.AVERAGE: + continue + i = 0 + while i < len(extremeTraits) and extremeTraits[i].howExtreme > trait.howExtreme: + i += 1 + + extremeTraits.insert(i, trait) + + self.extremeTraits = [] + for trait in extremeTraits: + self.extremeTraits.append((trait.name, trait.quality)) + + def getValueList(self): + traitValues = [] + for desc in PetTraits.TraitDescs: + traitName = desc[0] + traitValues.append(self.traits[traitName].value) + + return traitValues + + def getTraitValue(self, traitName): + return self.traits[traitName].value + + def getExtremeTraits(self): + return copy.copy(self.extremeTraits) + + def getOverallValue(self): + total = 0 + numUsed = 0 + for trait in self.traits.values(): + if trait.hasWorth: + if trait.higherIsBetter: + value = trait.value + else: + value = 1.0 - trait.value + total += value + numUsed += 1 + + value = total / len(self.traits.values()) + return value + + def getExtremeTraitDescriptions(self): + descs = [] + TraitQuality = TraitDistribution.TraitQuality + Quality2index = {TraitQuality.VERY_BAD: 0, + TraitQuality.BAD: 1, + TraitQuality.GOOD: 2, + TraitQuality.VERY_GOOD: 3} + for name, quality in self.extremeTraits: + descs.append(TTLocalizer.PetTrait2descriptions[name][Quality2index[quality]]) + + return descs diff --git a/toontown/pets/PetTricks.py b/toontown/pets/PetTricks.py new file mode 100644 index 0000000..c1ae1fa --- /dev/null +++ b/toontown/pets/PetTricks.py @@ -0,0 +1,104 @@ +from direct.showbase.PythonUtil import Enum, invertDictLossless +from direct.interval.IntervalGlobal import * +import types +import random +Tricks = Enum('JUMP, BEG, PLAYDEAD, ROLLOVER, BACKFLIP, DANCE, SPEAK, BALK,') +NonHappyMinActualTrickAptitude = 0.1 +NonHappyMaxActualTrickAptitude = 0.6 +MinActualTrickAptitude = 0.5 +MaxActualTrickAptitude = 0.97 +AptitudeIncrementDidTrick = 0.0005 +MaxAptitudeIncrementGotPraise = 0.0003 +MaxTrickFatigue = 0.65 +MinTrickFatigue = 0.1 +ScId2trickId = {21200: Tricks.JUMP, + 21201: Tricks.BEG, + 21202: Tricks.PLAYDEAD, + 21203: Tricks.ROLLOVER, + 21204: Tricks.BACKFLIP, + 21205: Tricks.DANCE, + 21206: Tricks.SPEAK} +TrickId2scIds = invertDictLossless(ScId2trickId) +TrickAnims = {Tricks.JUMP: 'jump', + Tricks.BEG: ('toBeg', 'beg', 'fromBeg'), + Tricks.PLAYDEAD: ('playDead', 'fromPlayDead'), + Tricks.ROLLOVER: 'rollover', + Tricks.BACKFLIP: 'backflip', + Tricks.DANCE: 'dance', + Tricks.SPEAK: 'speak', + Tricks.BALK: 'neutral'} +TrickLengths = {Tricks.JUMP: 2.0, + Tricks.BEG: 5.167, + Tricks.PLAYDEAD: 15.21, + Tricks.ROLLOVER: 8.0, + Tricks.BACKFLIP: 4.88, + Tricks.DANCE: 7.42, + Tricks.SPEAK: 0.75, + Tricks.BALK: 1.0} +TrickAccuracies = {Tricks.JUMP: 1.0, + Tricks.BEG: 0.9, + Tricks.PLAYDEAD: 0.8, + Tricks.ROLLOVER: 0.7, + Tricks.BACKFLIP: 0.6, + Tricks.DANCE: 0.5, + Tricks.SPEAK: 0.4, + Tricks.BALK: 1.0} +TrickHeals = {Tricks.JUMP: (5, 10), + Tricks.BEG: (6, 12), + Tricks.PLAYDEAD: (7, 14), + Tricks.ROLLOVER: (8, 16), + Tricks.BACKFLIP: (9, 18), + Tricks.DANCE: (10, 20), + Tricks.SPEAK: (11, 22), + Tricks.BALK: (0, 0)} +TrickSounds = {Tricks.BACKFLIP: 'phase_5/audio/sfx/backflip.mp3', + Tricks.ROLLOVER: 'phase_5/audio/sfx/rollover.mp3', + Tricks.PLAYDEAD: 'phase_5/audio/sfx/play_dead.mp3', + Tricks.BEG: 'phase_5/audio/sfx/beg.mp3', + Tricks.DANCE: 'phase_5/audio/sfx/heal_dance.mp3', + Tricks.JUMP: 'phase_5/audio/sfx/jump.mp3', + Tricks.SPEAK: 'phase_5/audio/sfx/speak_v1.mp3'} + +def getSoundIval(trickId): + sounds = TrickSounds.get(trickId, None) + if sounds: + if type(sounds) == types.StringType: + sound = loader.loadSfx(sounds) + return SoundInterval(sound) + else: + soundIval = Sequence() + for s in sounds: + sound = loader.loadSfx(s) + soundIval.append(SoundInterval(sound)) + + return soundIval + return + + +def getTrickIval(pet, trickId): + anims = TrickAnims[trickId] + animRate = random.uniform(0.9, 1.1) + waitTime = random.uniform(0.0, 1.0) + if type(anims) == types.StringType: + if trickId == Tricks.JUMP: + animIval = Parallel() + animIval.append(ActorInterval(pet, anims, playRate=animRate)) + animIval.append(Sequence(Wait(0.36), ProjectileInterval(pet, startPos=pet.getPos(), endPos=pet.getPos(), duration=1.0, gravityMult=0.5))) + elif trickId == Tricks.ROLLOVER: + animIval = Sequence() + animIval.append(ActorInterval(pet, anims, playRate=animRate)) + animIval.append(ActorInterval(pet, anims, playRate=-1.0 * animRate)) + elif trickId == Tricks.SPEAK: + animIval = ActorInterval(pet, anims, startFrame=10, playRate=animRate) + else: + animIval = ActorInterval(pet, anims, playRate=animRate) + else: + animIval = Sequence() + for anim in anims: + animIval.append(ActorInterval(pet, anim, playRate=animRate)) + + trickIval = Parallel(animIval) + soundIval = getSoundIval(trickId) + if soundIval: + trickIval.append(soundIval) + return Sequence(Func(pet.lockPet), Wait(waitTime), trickIval, Func(pet.unlockPet)) diff --git a/toontown/pets/PetTutorial.py b/toontown/pets/PetTutorial.py new file mode 100644 index 0000000..fa2c305 --- /dev/null +++ b/toontown/pets/PetTutorial.py @@ -0,0 +1,80 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class PetTutorial(DirectFrame, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('PetTutorial') + + def __init__(self, doneEvent): + FSM.FSM.__init__(self, 'PetTutorial') + self.doneEvent = doneEvent + self.setStateArray(['Page1', 'Page2', 'Page3']) + DirectFrame.__init__(self, pos=(0.0, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.5, 1.5, 0.9), text='', text_scale=0.06) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.32), text_fg=(1, 0, 0, 1), text_scale=TTLocalizer.PTtitle, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_5.5/models/gui/PetTutorial') + self.iPage1 = DirectFrame(self, image=images.find('**/PetTutorialPanel'), scale=0.75, pos=(-0.55, -0.1, 0)) + self.iPage1.hide() + self.iPage2 = DirectFrame(self, image=images.find('**/PetTutorialSpeedChat'), scale=0.75, pos=(0.43, -0.1, 0.05)) + self.iPage2.hide() + self.iPage3 = DirectFrame(self, image=images.find('**/PetTutorialCattlelog'), scale=0.75, pos=(-0.55, -0.1, 0)) + self.iPage3.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.PetTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.2, -0.3, -0.25), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.PetTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.2, -0.3, -0.25), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.PetTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.55, -0.3, -0.25), command=self.__handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.request('Page1') + return + + def enterPage1(self, *args): + self.title['text'] = (TTLocalizer.PetTutorialTitle1,) + self['text'] = TTLocalizer.PetTutorialPage1 + self['text_pos'] = TTLocalizer.PTenterPage1Pos + self['text_wordwrap'] = 16.5 + self.bPrev['state'] = DGG.DISABLED + self.iPage1.show() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + + def enterPage2(self, *args): + self.title['text'] = (TTLocalizer.PetTutorialTitle2,) + self['text'] = TTLocalizer.PetTutorialPage2 + self['text_pos'] = TTLocalizer.PTenterPage2Pos + self['text_wordwrap'] = 13.5 + self.iPage2.show() + + def exitPage2(self, *args): + self.iPage2.hide() + + def enterPage3(self, *args): + self.title['text'] = (TTLocalizer.PetTutorialTitle3,) + self['text'] = TTLocalizer.PetTutorialPage3 + self['text_pos'] = TTLocalizer.PTenterPage3Pos + self['text_wordwrap'] = 16.5 + self.bQuit.show() + self.bNext['state'] = DGG.DISABLED + self.iPage3.show() + + def exitPage3(self, *args): + self.bNext['state'] = DGG.NORMAL + self.iPage3.hide() + self.bQuit.hide() + + def __handleQuit(self): + base.localAvatar.b_setPetTutorialDone(True) + messenger.send(self.doneEvent) diff --git a/toontown/pets/PetUtil.py b/toontown/pets/PetUtil.py new file mode 100644 index 0000000..c7afbcf --- /dev/null +++ b/toontown/pets/PetUtil.py @@ -0,0 +1,29 @@ +from toontown.pets import PetDNA, PetTraits, PetConstants +from toontown.pets import PetNameGenerator +from direct.showbase import PythonUtil +import random + +def getPetInfoFromSeed(seed, safezoneId): + S = random.getstate() + random.seed(seed) + dnaArray = PetDNA.getRandomPetDNA(safezoneId) + gender = PetDNA.getGender(dnaArray) + nameString = PetNameGenerator.PetNameGenerator().randomName(gender=gender, seed=seed + safezoneId) + traitSeed = PythonUtil.randUint31() + random.setstate(S) + return (nameString, dnaArray, traitSeed) + + +def getPetCostFromSeed(seed, safezoneId): + name, dna, traitSeed = getPetInfoFromSeed(seed, safezoneId) + traits = PetTraits.PetTraits(traitSeed, safezoneId) + traitValue = traits.getOverallValue() + traitValue -= 0.3 + traitValue = max(0, traitValue) + rarity = PetDNA.getRarity(dna) + rarity *= 1.0 - traitValue + rarity = pow(0.001, rarity) - 0.001 + minCost, maxCost = PetConstants.ZoneToCostRange[safezoneId] + cost = rarity * (maxCost - minCost) + minCost + cost = int(cost) + return cost diff --git a/toontown/pets/PetWander.py b/toontown/pets/PetWander.py new file mode 100644 index 0000000..fa293be --- /dev/null +++ b/toontown/pets/PetWander.py @@ -0,0 +1,61 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import reduceAngle, randFloat, normalDistrib +from direct.showbase import DirectObject +from toontown.pets import PetChase +from toontown.pets import PetConstants + +class PetWander(CPetChase, DirectObject.DirectObject): + + def __init__(self, minDist = 5.0, moveAngle = 20.0): + self.movingTarget = hidden.attachNewNode('petWanderTarget') + CPetChase.__init__(self, self.movingTarget, minDist, moveAngle) + self.targetMoveCountdown = 0 + self.collEvent = None + self.gotCollision = False + return + + def isCpp(self): + return 0 + + def __ignoreCollisions(self): + if self.collEvent is not None: + self.ignore(self.collEvent) + self.collEvent = None + return + + def _setMover(self, mover): + CPetChase.setMover(self, mover) + self.mover = mover + self.__ignoreCollisions() + self.collEvent = mover.getCollisionEventName() + self.accept(self.collEvent, self._handleCollision) + + def _clearMover(self, mover): + CPetChase.clearMover(self, mover) + self.__ignoreCollisions() + + def _handleCollision(self, collEntry): + self.gotCollision = True + self.movingTarget.setPos(self.getNodePath().getPos()) + self.targetMoveCountdown *= 0.5 + + def destroy(self): + self.__ignoreCollisions() + self.movingTarget.removeNode() + del self.movingTarget + + def _process(self, dt): + self.targetMoveCountdown -= dt + if self.targetMoveCountdown <= 0.0: + distance = normalDistrib(3.0, 30.0) + heading = normalDistrib(-(90 + 45), 90 + 45) + if self.gotCollision: + self.gotCollision = False + heading = heading + 180 + target = self.getTarget() + target.setPos(self.getNodePath().getPos()) + target.setH(target, heading) + target.setY(target, distance) + duration = distance / self.mover.getFwdSpeed() + self.targetMoveCountdown = duration * randFloat(1.2, 3.0) + CPetChase.process(self, dt) diff --git a/toontown/pets/PetshopGUI.py b/toontown/pets/PetshopGUI.py new file mode 100644 index 0000000..f27abaa --- /dev/null +++ b/toontown/pets/PetshopGUI.py @@ -0,0 +1,527 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase.DirectObject import DirectObject +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from direct.task import Task +from otp.namepanel import NameTumbler +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from toontown.fishing import FishSellGUI +from toontown.pets import Pet, PetConstants +from toontown.pets import PetDNA +from toontown.pets import PetUtil +from toontown.pets import PetDetail +from toontown.pets import PetTraits +from toontown.pets import PetNameGenerator +from toontown.hood import ZoneUtil +import string +import random +Dialog_MainMenu = 0 +Dialog_AdoptPet = 1 +Dialog_ChoosePet = 2 +Dialog_ReturnPet = 3 +Dialog_SellFish = 4 +Dialog_NamePicker = 5 +Dialog_GoHome = 6 +disabledImageColor = Vec4(0.6, 0.6, 0.6, 1) +text0Color = Vec4(0.65, 0, 0.87, 1) +text1Color = Vec4(0.65, 0, 0.87, 1) +text2Color = Vec4(1, 1, 0.5, 1) +text3Color = Vec4(0.4, 0.4, 0.4, 1) + +class PetshopGUI(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGui') + + class GoHomeDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GoHomeDlg') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, pos=(0.0, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_wordwrap=13.5, text_scale=0.06, text_pos=(0.0, 0.13)) + self['image'] = DGG.getDefaultDialogGeom() + self['text'] = TTLocalizer.PetshopGoHomeText + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.bYes = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.TutorialYes, text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.15, 0.0, -0.1), command=lambda : messenger.send(doneEvent, [1])) + self.bNo = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.TutorialNo, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.15, 0.0, -0.1), command=lambda : messenger.send(doneEvent, [0])) + buttons.removeNode() + gui.removeNode() + return + + class NamePicker(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.NamePicker') + + def __init__(self, doneEvent, petSeed, gender): + zoneId = ZoneUtil.getCanonicalSafeZoneId(base.localAvatar.getZoneId()) + name, dna, traitSeed = PetUtil.getPetInfoFromSeed(petSeed, zoneId) + self.gui = loader.loadModel('phase_4/models/gui/PetNamePanel') + self.guiScale = 0.09 + DirectFrame.__init__(self, relief=None, geom=self.gui, geom_scale=self.guiScale, state='normal', frameSize=(-1, 1, -1, 1)) + self.initialiseoptions(PetshopGUI.NamePicker) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.21, 0, -0.04) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(dna) + self.petModel.fitAndCenterHead(0.435, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(225) + self.petModel.enterNeutralHappy() + self.ng = PetNameGenerator.PetNameGenerator() + if gender == 1: + self.allNames = self.ng.boyFirsts + else: + self.allNames = self.ng.girlFirsts + self.allNames += self.ng.neutralFirsts + self.allNames.sort() + self.checkNames() + self.letters = [] + for name in self.allNames: + if name[0:TTLocalizer.PGUIcharLength] not in self.letters: + self.letters.append(name[0:TTLocalizer.PGUIcharLength]) + + self.curLetter = self.letters[0] + self.curNames = [] + self.curName = '' + self.alphabetList = self.makeScrollList(self.gui, (-0.012, 0, -0.075), (1, 0.8, 0.8, 1), self.letters, self.makeLabel, [TextNode.ACenter, 'alphabet'], 6) + self.nameList = None + self.rebuildNameList() + self.randomButton = DirectButton(parent=self, relief=None, image=(self.gui.find('**/RandomUpButton'), self.gui.find('**/RandomDownButton'), self.gui.find('**/RandomRolloverButton')), scale=self.guiScale, text=TTLocalizer.RandomButton, text_pos=(-0.8, -5.7), text_scale=0.8, text_fg=text2Color, pressEffect=False, command=self.randomName) + self.nameResult = DirectLabel(parent=self, relief=None, scale=self.guiScale, text='', text_align=TextNode.ACenter, text_pos=(-1.85, 2.6), text_fg=text0Color, text_scale=0.6, text_wordwrap=8) + self.submitButton = DirectButton(parent=self, relief=None, image=(self.gui.find('**/SubmitUpButton'), self.gui.find('**/SubmitDownButton'), self.gui.find('**/SubmitRolloverButton')), scale=self.guiScale, text=TTLocalizer.PetshopAdopt, text_pos=(3.3, -5.7), text_scale=TTLocalizer.PGUIsubmitButton, text_fg=text0Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [self.ng.returnUniqueID(self.curName)])) + model = loader.loadModel('phase_4/models/gui/PetShopInterface') + modelScale = 0.1 + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')) + cancelIcon = model.find('**/CancelIcon') + self.cancelButton = DirectButton(parent=self, relief=None, pos=(-0.04, 0, -0.47), image=cancelImageList, geom=cancelIcon, scale=modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [-1])) + self.randomName() + return + + def checkNames(self): + if __dev__: + for name in self.allNames: + if not name.replace(' ', '').isalpha(): + self.notify.warning('Bad name:%s' % name) + + def destroy(self): + self.petModel.delete() + DirectFrame.destroy(self) + + def rebuildNameList(self): + self.curNames = [] + for name in self.allNames: + if name[0:TTLocalizer.PGUIcharLength] == self.curLetter: + self.curNames += [name] + + if self.nameList: + self.nameList.destroy() + self.nameList = self.makeScrollList(self.gui, (0.277, 0, -0.075), (1, 0.8, 0.8, 1), self.curNames, self.makeLabel, [TextNode.ACenter, 'name'], 5) + + def updateNameText(self): + self.nameResult['text'] = self.curName + + def nameClickedOn(self, listType, index): + if listType == 'alphabet': + self.curLetter = self.letters[index] + self.rebuildNameList() + elif listType == 'name': + self.curName = self.curNames[index] + self.updateNameText() + + def makeLabel(self, te, index, others): + alig = others[0] + listName = others[1] + if alig == TextNode.ARight: + newpos = (0.44, 0, 0) + elif alig == TextNode.ALeft: + newpos = (0, 0, 0) + else: + newpos = (0.2, 0, 0) + df = DirectButton(parent=self, state='normal', relief=None, text=te, text_scale=0.1, text_pos=(0.2, 0, 0), text_align=alig, textMayChange=0, command=lambda : self.nameClickedOn(listName, index)) + return df + + def makeScrollList(self, gui, ipos, mcolor, nitems, nitemMakeFunction, nitemMakeExtraArgs, nVisibleItems): + decScale = self.guiScale / 0.44 + incScale = (decScale, decScale, -decScale) + it = nitems[:] + listType = nitemMakeExtraArgs[1] + if listType == 'alphabet': + arrowList = (gui.find('**/ArrowSmUpButton'), + gui.find('**/ArrowSmUpRollover'), + gui.find('**/ArrowSmUpRollover'), + gui.find('**/ArrowSmUpButton')) + fHeight = 0.09 + elif listType == 'name': + arrowList = (gui.find('**/ArrowUpBigButton'), + gui.find('**/ArrowUpBigRollover'), + gui.find('**/ArrowUpBigRollover'), + gui.find('**/ArrowUpBigButton')) + fHeight = 0.119 + ds = DirectScrolledList(parent=self, items=it, itemMakeFunction=nitemMakeFunction, itemMakeExtraArgs=nitemMakeExtraArgs, relief=None, command=None, pos=ipos, scale=0.44, incButton_image=arrowList, incButton_image_pos=(1.015, 0, 3.32), incButton_relief=None, incButton_scale=incScale, incButton_image3_color=Vec4(0.4, 0.4, 0.4, 1), decButton_image=arrowList, decButton_image_pos=(1.015, 0, 1.11), decButton_relief=None, decButton_scale=decScale, decButton_image3_color=Vec4(0.4, 0.4, 0.4, 1), numItemsVisible=nVisibleItems, forceHeight=fHeight) + return ds + + def randomName(self): + numNames = len(self.allNames) + self.curName = self.allNames[random.randrange(numNames)] + self.curLetter = self.curName[0:TTLocalizer.PGUIcharLength] + self.rebuildNameList() + self.updateNameText() + self.alphabetList.scrollTo(self.letters.index(self.curLetter)) + self.nameList.scrollTo(self.curNames.index(self.curName)) + + class MainMenuDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.MainMenuDlg') + + def __init__(self, doneEvent): + model = loader.loadModel('phase_4/models/gui/AdoptReturnSell') + modelPos = (0, 0, -0.3) + modelScale = 0.055 + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=(modelScale, modelScale, modelScale), pos=modelPos, frameSize=(-1, 1, -1, 1)) + self.initialiseoptions(PetshopGUI.MainMenuDlg) + textScale = TTLocalizer.PGUItextScale + sellFishImageList = (model.find('**/SellButtonUp'), + model.find('**/SellButtonDown'), + model.find('**/SellButtonRollover'), + model.find('**/SellButtonDown')) + fishLogoImageList = model.find('**/Fish') + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/cancelButtonDown'), model.find('**/CancelButtonRollover')) + XImageList = model.find('**/CancelIcon') + adoptImageList = (model.find('**/AdoptButtonUp'), model.find('**/AdoptButtonDown'), model.find('**/AdoptButtonRollover')) + pawLogoAdoptImageList = model.find('**/PawPink') + returnImageList = (model.find('**/ReturnButtonUp'), + model.find('**/ReturnButtonDown'), + model.find('**/ReturnButtonRollover'), + model.find('**/ReturnButtonDown')) + pawLogoReturnImageList = model.find('**/PawYellow') + self.cancelButton = DirectButton(parent=self, relief=None, scale=(modelScale, modelScale, modelScale), geom=XImageList, image=cancelImageList, text=('', TTLocalizer.PetshopCancel), text_pos=TTLocalizer.PGUIcancelButtonPos, text_scale=0.8, pressEffect=False, command=lambda : messenger.send(doneEvent, [0])) + self.sellFishButton = DirectButton(parent=self, relief=None, image=sellFishImageList, image3_color=disabledImageColor, geom=fishLogoImageList, scale=(modelScale, modelScale, modelScale), text=TTLocalizer.PetshopSell, text_scale=textScale, text_pos=(0, 6), text0_fg=text2Color, text1_fg=text2Color, text2_fg=text0Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [1])) + fishValue = base.localAvatar.fishTank.getTotalValue() + if fishValue == 0: + self.sellFishButton['state'] = DGG.DISABLED + self.adoptPetButton = DirectButton(parent=self, relief=None, image=adoptImageList, geom=pawLogoAdoptImageList, scale=(modelScale, modelScale, modelScale), text=TTLocalizer.PetshopAdoptAPet, text_scale=textScale, text_pos=(0, 12.5), text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [2])) + self.returnPetButton = DirectButton(parent=self, relief=None, image=returnImageList, geom=pawLogoReturnImageList, image3_color=disabledImageColor, scale=(modelScale, modelScale, modelScale), text=TTLocalizer.PetshopReturnPet, text_scale=textScale, text_pos=(-0.6, 9.2), text0_fg=text2Color, text1_fg=text2Color, text2_fg=text0Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [3])) + if not base.localAvatar.hasPet(): + self.returnPetButton['state'] = DGG.DISABLED + model.removeNode() + return + + class AdoptPetDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.AdoptPetDlg') + + def __init__(self, doneEvent, petSeed, petNameIndex): + zoneId = ZoneUtil.getCanonicalSafeZoneId(base.localAvatar.getZoneId()) + name, dna, traitSeed = PetUtil.getPetInfoFromSeed(petSeed, zoneId) + name = PetNameGenerator.PetNameGenerator().getName(petNameIndex) + cost = PetUtil.getPetCostFromSeed(petSeed, zoneId) + model = loader.loadModel('phase_4/models/gui/AdoptPet') + modelPos = (0, 0, -0.3) + modelScale = 0.055 + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=modelScale, frameSize=(-1, 1, -1, 1), pos=modelPos, text=TTLocalizer.PetshopAdoptConfirm % (name, cost), text_wordwrap=12, text_scale=0.05, text_pos=(0, 0.55), text_fg=text0Color) + self.initialiseoptions(PetshopGUI.AdoptPetDlg) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.13, 0, 0.8) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(dna) + self.petModel.fitAndCenterHead(0.395, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(130) + self.petModel.enterNeutralHappy() + self.moneyDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTotalMoney()), text_scale=0.075, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.225, 0.33), text_font=ToontownGlobals.getSignFont()) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__moneyChange) + okImageList = (model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')) + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelRollover')) + cancelIcon = model.find('**/CancelIcon') + checkIcon = model.find('**/CheckIcon') + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, geom=cancelIcon, scale=modelScale, text=('', TTLocalizer.PetshopGoBack), text_pos=(-5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [0])) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, geom=checkIcon, scale=modelScale, text=('', TTLocalizer.PetshopAdopt), text_pos=(5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [1])) + model.removeNode() + return + + def destroy(self): + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.petModel.delete() + DirectFrame.destroy(self) + + def __moneyChange(self, money): + self.moneyDisplay['text'] = str(base.localAvatar.getTotalMoney()) + + class ReturnPetDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.ReturnPetDlg') + + def __init__(self, doneEvent): + + def showDialog(avatar): + model = loader.loadModel('phase_4/models/gui/ReturnPet') + modelPos = (0, 0, -0.3) + modelScale = (0.055, 0.055, 0.055) + base.r = self + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=modelScale, frameSize=(-1, 1, -1, 1), pos=modelPos, text=TTLocalizer.PetshopReturnConfirm % avatar.getName(), text_wordwrap=12, text_scale=TTLocalizer.PGUIreturnConfirm, text_pos=(0, 0.45), text_fg=text2Color) + self.initialiseoptions(PetshopGUI.ReturnPetDlg) + okImageList = (model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckRollover')) + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelRollover')) + cancelIcon = model.find('**/CancelIcon') + checkIcon = model.find('**/CheckIcon') + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, geom=cancelIcon, scale=modelScale, text=('', TTLocalizer.PetshopGoBack), text_pos=(-5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [0])) + self.okButton = DirectButton(parent=self, relief=None, image=okImageList, geom=checkIcon, scale=modelScale, text=('', TTLocalizer.PetshopReturn), text_pos=(5.8, 4.4), text_scale=0.7, pressEffect=False, command=lambda : messenger.send(doneEvent, [1])) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.15, 0, 0.8) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(avatar.getDNA()) + self.petModel.fitAndCenterHead(0.395, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(130) + self.petModel.enterNeutralSad() + model.removeNode() + self.initialized = True + return + + self.initialized = False + self.petPanel = PetDetail.PetDetail(base.localAvatar.getPetId(), showDialog) + + def destroy(self): + if self.initialized: + self.petPanel.avatar.disable() + self.petPanel.avatar.delete() + self.petPanel.avatar = None + self.PetPanel = None + self.petModel.delete() + DirectFrame.destroy(self) + return + + class ChoosePetDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('PetshopGUI.ChoosePetDlg') + + def __init__(self, doneEvent, petSeeds): + model = loader.loadModel('phase_4/models/gui/PetShopInterface') + modelPos = (0, 0, -0.9) + modelScale = (0.185, 0.185, 0.185) + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=modelScale, frameSize=(-1, 1, -1, 1), pos=modelPos, text=TTLocalizer.PetshopChooserTitle, text_wordwrap=26, text_scale=TTLocalizer.PGUIchooserTitle, text_fg=Vec4(0.36, 0.94, 0.93, 1), text_pos=(0, 1.58)) + self.initialiseoptions(PetshopGUI.ChoosePetDlg) + adoptImageList = (model.find('**/AdoptButtonUp'), + model.find('**/AdoptButtonDown'), + model.find('**/AdoptButtonRollover'), + model.find('**/AdoptButtonRollover')) + cancelImageList = (model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')) + cancelIcon = model.find('**/CancelIcon') + pawLImageList = (model.find('**/Paw1Up'), model.find('**/Paw1Down'), model.find('**/Paw1Rollover')) + pawLArrowImageList = model.find('**/Arrow1') + pawRImageList = (model.find('**/Paw2Up'), model.find('**/Paw2Down'), model.find('**/Paw2Rollover')) + pawRArrowImageList = model.find('**/Arrow2') + self.cancelButton = DirectButton(parent=self, relief=None, image=cancelImageList, geom=cancelIcon, scale=modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [-1])) + self.pawLButton = DirectButton(parent=self, relief=None, image=pawLImageList, geom=pawLArrowImageList, scale=modelScale, pressEffect=False, command=lambda : self.__handlePetChange(-1)) + self.pawRButton = DirectButton(parent=self, relief=None, image=pawRImageList, geom=pawRArrowImageList, scale=modelScale, pressEffect=False, command=lambda : self.__handlePetChange(1)) + self.okButton = DirectButton(parent=self, relief=None, image=adoptImageList, image3_color=disabledImageColor, scale=modelScale, text=TTLocalizer.PetshopAdopt, text_scale=TTLocalizer.PGUIokButton, text_pos=TTLocalizer.PGUIokButtonPos, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, pressEffect=False, command=lambda : messenger.send(doneEvent, [self.curPet])) + self.moneyDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTotalMoney()), text_scale=0.1, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0.34, 0.12), text_font=ToontownGlobals.getSignFont()) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('bankMoneyChange'), self.__moneyChange) + self.petView = self.attachNewNode('petView') + self.petView.setPos(-0.05, 0, 1.15) + model.removeNode() + self.petSeeds = petSeeds + self.makePetList() + self.showPet() + return + + def makePetList(self): + self.numPets = len(self.petSeeds) + self.curPet = 0 + self.petDNA = [] + self.petName = [] + self.petDesc = [] + self.petCost = [] + for i in range(self.numPets): + random.seed(self.petSeeds[i]) + zoneId = ZoneUtil.getCanonicalSafeZoneId(base.localAvatar.getZoneId()) + name, dna, traitSeed = PetUtil.getPetInfoFromSeed(self.petSeeds[i], zoneId) + cost = PetUtil.getPetCostFromSeed(self.petSeeds[i], zoneId) + traits = PetTraits.PetTraits(traitSeed, zoneId) + traitList = traits.getExtremeTraitDescriptions() + numGenders = len(PetDNA.PetGenders) + gender = i % numGenders + PetDNA.setGender(dna, gender) + self.petDNA.append(dna) + self.petName.append(TTLocalizer.PetshopUnknownName) + descList = [] + descList.append(TTLocalizer.PetshopDescGender % PetDNA.getGenderString(gender=gender)) + if traitList: + descList.append(TTLocalizer.PetshopDescTrait % traitList[0]) + else: + descList.append(TTLocalizer.PetshopDescTrait % TTLocalizer.PetshopDescStandard) + traitList.extend(['', + '', + '', + '']) + for trait in traitList[1:4]: + descList.append('\t%s' % trait) + + descList.append(TTLocalizer.PetshopDescCost % cost) + self.petDesc.append(string.join(descList, '\n')) + self.petCost.append(cost) + + def destroy(self): + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('bankMoneyChange')) + self.petModel.delete() + DirectFrame.destroy(self) + + def __handlePetChange(self, nDir): + self.curPet = (self.curPet + nDir) % self.numPets + self.nameLabel.destroy() + self.petModel.delete() + self.descLabel.destroy() + self.showPet() + + def showPet(self): + self.nameLabel = DirectLabel(parent=self, pos=(0, 0, 1.35), relief=None, text=self.petName[self.curPet], text_fg=Vec4(0.45, 0, 0.61, 1), text_pos=(0, 0), text_scale=0.08, text_shadow=(1, 1, 1, 1)) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(self.petDNA[self.curPet]) + self.petModel.fitAndCenterHead(0.57, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.setH(130) + self.petModel.enterNeutralHappy() + self.descLabel = DirectLabel(parent=self, pos=(-0.4, 0, 0.72), relief=None, scale=0.05, text=self.petDesc[self.curPet], text_align=TextNode.ALeft, text_wordwrap=TTLocalizer.PGUIwordwrap, text_scale=TTLocalizer.PGUIdescLabel) + if self.petCost[self.curPet] > base.localAvatar.getTotalMoney(): + self.okButton['state'] = DGG.DISABLED + else: + self.okButton['state'] = DGG.NORMAL + return + + def __moneyChange(self, money): + self.moneyDisplay['text'] = str(base.localAvatar.getTotalMoney()) + + def __init__(self, eventDict, petSeeds): + self.eventDict = eventDict + self.mainMenuDoneEvent = 'MainMenuGuiDone' + self.adoptPetDoneEvent = 'AdoptPetGuiDone' + self.returnPetDoneEvent = 'ReturnPetGuiDone' + self.petChooserDoneEvent = 'PetChooserGuiDone' + self.fishGuiDoneEvent = 'MyFishGuiDone' + self.namePickerDoneEvent = 'NamePickerGuiDone' + self.goHomeDlgDoneEvent = 'GoHomeDlgDone' + self.dialog = None + self.dialogStack = [] + self.petSeeds = petSeeds + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(aspect2d) + self.timer.posInTopRightCorner() + self.timer.countdown(PetConstants.PETCLERK_TIMER, self.__timerExpired) + self.doDialog(Dialog_MainMenu) + return + + def __timerExpired(self): + messenger.send(self.eventDict['guiDone'], [True]) + + def destroy(self): + self.destroyDialog() + self.timer.destroy() + del self.timer + self.ignore(self.mainMenuDoneEvent) + self.ignore(self.adoptPetDoneEvent) + self.ignore(self.returnPetDoneEvent) + self.ignore(self.petChooserDoneEvent) + self.ignore(self.fishGuiDoneEvent) + self.ignore(self.namePickerDoneEvent) + self.ignore(self.goHomeDlgDoneEvent) + + def destroyDialog(self): + if self.dialog != None: + self.dialog.destroy() + self.dialog = None + return + + def popDialog(self): + self.dialogStack.pop() + self.doDialog(self.dialogStack.pop()) + + def doDialog(self, nDialog): + self.destroyDialog() + self.dialogStack.append(nDialog) + if nDialog == Dialog_MainMenu: + self.acceptOnce(self.mainMenuDoneEvent, self.__handleMainMenuDlg) + self.dialog = self.MainMenuDlg(self.mainMenuDoneEvent) + elif nDialog == Dialog_AdoptPet: + self.acceptOnce(self.adoptPetDoneEvent, self.__handleAdoptPetDlg) + self.dialog = self.AdoptPetDlg(self.adoptPetDoneEvent, self.petSeeds[self.adoptPetNum], self.adoptPetNameIndex) + elif nDialog == Dialog_ChoosePet: + self.acceptOnce(self.petChooserDoneEvent, self.__handleChoosePetDlg) + self.dialog = self.ChoosePetDlg(self.petChooserDoneEvent, self.petSeeds) + elif nDialog == Dialog_ReturnPet: + self.acceptOnce(self.returnPetDoneEvent, self.__handleReturnPetDlg) + self.dialog = self.ReturnPetDlg(self.returnPetDoneEvent) + elif nDialog == Dialog_SellFish: + self.acceptOnce(self.fishGuiDoneEvent, self.__handleFishSellDlg) + self.dialog = FishSellGUI.FishSellGUI(self.fishGuiDoneEvent) + elif nDialog == Dialog_NamePicker: + self.acceptOnce(self.namePickerDoneEvent, self.__handleNamePickerDlg) + self.dialog = self.NamePicker(self.namePickerDoneEvent, self.petSeeds[self.adoptPetNum], gender=self.adoptPetNum % 2) + elif nDialog == Dialog_GoHome: + self.acceptOnce(self.goHomeDlgDoneEvent, self.__handleGoHomeDlg) + self.dialog = self.GoHomeDlg(self.goHomeDlgDoneEvent) + + def __handleMainMenuDlg(self, exitVal): + if exitVal == 0: + messenger.send(self.eventDict['guiDone']) + elif exitVal == 1: + self.doDialog(Dialog_SellFish) + elif exitVal == 2: + self.doDialog(Dialog_ChoosePet) + elif exitVal == 3: + self.doDialog(Dialog_ReturnPet) + + def __handleFishSellDlg(self, exitVal): + if exitVal == 0: + self.popDialog() + elif exitVal == 1: + self.destroyDialog() + messenger.send(self.eventDict['fishSold']) + + def __handleChoosePetDlg(self, exitVal): + if exitVal == -1: + self.popDialog() + else: + self.adoptPetNum = exitVal + self.doDialog(Dialog_NamePicker) + + def __handleNamePickerDlg(self, exitVal): + if exitVal == -1: + self.popDialog() + else: + self.adoptPetNameIndex = exitVal + if base.localAvatar.hasPet(): + self.doDialog(Dialog_ReturnPet) + else: + self.doDialog(Dialog_AdoptPet) + + def __handleAdoptPetDlg(self, exitVal): + if exitVal == 0: + self.popDialog() + elif exitVal == 1: + self.destroyDialog() + messenger.send(self.eventDict['petAdopted'], [self.adoptPetNum, self.adoptPetNameIndex]) + messenger.send(self.eventDict['guiDone']) + + def __handleGoHomeDlg(self, exitVal): + if exitVal == 0: + messenger.send(self.eventDict['guiDone']) + elif exitVal == 1: + messenger.send(self.eventDict['guiDone']) + place = base.cr.playGame.getPlace() + if place == None: + self.notify.warning('Tried to go home, but place is None.') + return + place.goHomeNow(base.localAvatar.lastHood) + return + + def __handleReturnPetDlg(self, exitVal): + if exitVal == 0: + self.popDialog() + elif exitVal == 1: + if self.dialogStack[len(self.dialogStack) - 2] == Dialog_NamePicker: + self.doDialog(Dialog_AdoptPet) + else: + self.destroyDialog() + messenger.send(self.eventDict['petReturned']) + messenger.send(self.eventDict['guiDone']) diff --git a/toontown/pets/__init__.py b/toontown/pets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/quest/BlinkingArrows.py b/toontown/quest/BlinkingArrows.py new file mode 100644 index 0000000..662b1e9 --- /dev/null +++ b/toontown/quest/BlinkingArrows.py @@ -0,0 +1,62 @@ +from direct.interval.IntervalGlobal import * +from pandac.PandaModules import * + +class BlinkingArrows: + + def __init__(self, parent = aspect2d, otherNode = None): + self.arrow1 = loader.loadModel('phase_3/models/props/arrow') + self.arrow2 = loader.loadModel('phase_3/models/props/arrow') + self.arrowTrack = None + self.parent = parent + self.otherNode = otherNode + return + + def delete(self): + self.arrowsOff() + self.arrow1.removeNode() + self.arrow2.removeNode() + del self.arrow1 + del self.arrow2 + + def arrowsOn(self, x1, y1, h1, x2, y2, h2, onTime = 0.75, offTime = 0.75): + self.stopArrowsFlashing() + self.arrow1.setBin('gui-popup', 0) + self.arrow2.setBin('gui-popup', 0) + self.arrow1.reparentTo(self.parent) + self.arrow2.reparentTo(self.parent) + self.arrow1.setScale(0.2) + self.arrow2.setScale(0.2) + self.arrow1.setPos(x1, 0, y1) + self.arrow2.setPos(x2, 0, y2) + self.arrow1.setR(h1) + self.arrow2.setR(h2) + self.onTime = onTime + self.offTime = offTime + self.startArrowsFlashing() + + def arrowsOff(self): + self.stopArrowsFlashing() + self.arrow1.reparentTo(hidden) + self.arrow2.reparentTo(hidden) + + def startArrowsFlashing(self): + onColor = Vec4(1, 1, 1, 1) + offColor = Vec4(1, 1, 1, 0.25) + self.arrow1.show() + self.arrow2.show() + if self.otherNode: + self.otherNode.show() + self.arrowTrack = Sequence(Parallel(self.arrow1.colorScaleInterval(self.onTime, onColor, offColor), self.arrow2.colorScaleInterval(self.onTime, onColor, offColor), self.otherNode.colorScaleInterval(self.onTime, onColor, offColor)), Parallel(self.arrow1.colorScaleInterval(self.offTime, offColor, onColor), self.arrow2.colorScaleInterval(self.offTime, offColor, onColor), self.otherNode.colorScaleInterval(self.offTime, offColor, onColor))) + else: + self.arrowTrack = Sequence(Parallel(self.arrow1.colorScaleInterval(self.onTime, onColor, offColor), self.arrow2.colorScaleInterval(self.onTime, onColor, offColor)), Parallel(self.arrow1.colorScaleInterval(self.offTime, offColor, onColor), self.arrow2.colorScaleInterval(self.offTime, offColor, onColor))) + self.arrowTrack.loop() + + def stopArrowsFlashing(self): + if self.arrowTrack: + self.arrowTrack.finish() + self.arrowTrack = None + self.arrow1.hide() + self.arrow2.hide() + if self.otherNode: + self.otherNode.hide() + return diff --git a/toontown/quest/QuestBookPoster.py b/toontown/quest/QuestBookPoster.py new file mode 100644 index 0000000..ea9bea1 --- /dev/null +++ b/toontown/quest/QuestBookPoster.py @@ -0,0 +1,61 @@ +from QuestPoster import * +IMAGE_SCALE_LARGE = 0.15 +IMAGE_SCALE_SMALL = 0.1 +POSTER_WIDTH = 0.7 +TEXT_SCALE = TTLocalizer.QPtextScale * 0.7 +TEXT_WORDWRAP = TTLocalizer.QPtextWordwrap * 0.8 + +class QuestBookPoster(QuestPoster): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestPoster') + colors = {'white': (1, 1, 1, 1), + 'blue': (0.45, 0.45, 0.8, 1), + 'lightBlue': (0.42, 0.671, 1.0, 1.0), + 'green': (0.45, 0.8, 0.45, 1), + 'lightGreen': (0.784, 1, 0.863, 1), + 'red': (0.8, 0.45, 0.45, 1), + 'rewardRed': (0.8, 0.3, 0.3, 1), + 'brightRed': (1.0, 0.16, 0.16, 1.0), + 'brown': (0.52, 0.42, 0.22, 1)} + normalTextColor = (0.3, 0.25, 0.2, 1) + confirmDeleteButtonEvent = 'confirmDeleteButtonEvent' + + def __init__(self, parent = aspect2d, **kw): + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + questCard = bookModel.find('**/questCard') + optiondefs = (('relief', None, None), + ('reverse', 0, None), + ('mapIndex', 0, None), + ('image', questCard, None), + ('image_scale', (0.8, 1.0, 0.58), None), + ('state', DGG.NORMAL, None)) + self.defineoptions(kw, optiondefs) + QuestPoster.__init__(self, relief=None) + self.initialiseoptions(QuestBookPoster) + self._deleteCallback = None + self.questFrame = DirectFrame(parent=self, relief=None) + gui = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI') + icon = gui.find('**/startPartyButton_inactive') + iconNP = aspect2d.attachNewNode('iconNP') + icon.reparentTo(iconNP) + icon.setX((-12.0792 + 0.2) / 30.48) + icon.setZ((-9.7404 + 1) / 30.48) + self.mapIndex = DirectLabel(parent=self.questFrame, relief=None, text='%s' % self['mapIndex'], text_fg=(1, 1, 1, 1), text_scale=0.035, text_align=TextNode.ACenter, image=iconNP, image_scale=0.3, image_color=(1, 0, 0, 1), pos=(-0.3, 0, 0.15)) + self.mapIndex.hide() + iconNP.removeNode() + gui.removeNode() + bookModel.removeNode() + self.reverseBG(self['reverse']) + self.laffMeter = None + return + + def reverseBG(self, reverse = 0): + try: + self.initImageScale + except AttributeError: + self.initImageScale = self['image_scale'] + if reverse: + self.initImageScale.setX(-abs(self.initImageScale[0])) + self.questFrame.setX(0.015) + else: + self.initImageScale.setX(abs(self.initImageScale[0])) + self['image_scale'] = self.initImageScale diff --git a/toontown/quest/QuestChoiceGui.py b/toontown/quest/QuestChoiceGui.py new file mode 100644 index 0000000..635d1f7 --- /dev/null +++ b/toontown/quest/QuestChoiceGui.py @@ -0,0 +1,65 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import QuestPoster +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class QuestChoiceGui(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestChoiceGui') + + def __init__(self): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=Vec4(0.8, 0.6, 0.4, 1), geom_scale=(1.85, 1, 0.9), geom_hpr=(0, 0, -90), pos=(-0.85, 0, 0)) + self.initialiseoptions(QuestChoiceGui) + self.questChoicePosters = [] + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.cancelButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.QuestChoiceGuiCancel, text_scale=0.06, text_pos=(0, -0.02), command=self.chooseQuest, extraArgs=[0]) + guiButton.removeNode() + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self) + self.timer.setScale(0.3) + base.setCellsAvailable(base.leftCells, 0) + base.setCellsAvailable([base.bottomCells[0], base.bottomCells[1]], 0) + return + + def setQuests(self, quests, fromNpcId, timeout): + for i in range(0, len(quests), 3): + questId, rewardId, toNpcId = quests[i:i + 3] + qp = QuestPoster.QuestPoster() + qp.reparentTo(self) + qp.showChoicePoster(questId, fromNpcId, toNpcId, rewardId, self.chooseQuest) + self.questChoicePosters.append(qp) + + if len(quests) == 1 * 3: + self['geom_scale'] = (1, 1, 0.9) + self.questChoicePosters[0].setPos(0, 0, 0.1) + self.cancelButton.setPos(0.15, 0, -0.375) + self.timer.setPos(-0.2, 0, -0.35) + elif len(quests) == 2 * 3: + self['geom_scale'] = (1.5, 1, 0.9) + self.questChoicePosters[0].setPos(0, 0, -0.2) + self.questChoicePosters[1].setPos(0, 0, 0.4) + self.cancelButton.setPos(0.15, 0, -0.625) + self.timer.setPos(-0.2, 0, -0.6) + elif len(quests) == 3 * 3: + self['geom_scale'] = (1.85, 1, 0.9) + map(lambda x: x.setScale(0.95), self.questChoicePosters) + self.questChoicePosters[0].setPos(0, 0, -0.4) + self.questChoicePosters[1].setPos(0, 0, 0.125) + self.questChoicePosters[2].setPos(0, 0, 0.65) + self.cancelButton.setPos(0.15, 0, -0.8) + self.timer.setPos(-0.2, 0, -0.775) + self.timer.countdown(timeout, self.timeout) + + def chooseQuest(self, questId): + if questId != 0: + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: CREATEATASK: Create A Task.') + base.setCellsAvailable(base.leftCells, 1) + base.setCellsAvailable([base.bottomCells[0], base.bottomCells[1]], 1) + self.timer.stop() + messenger.send('chooseQuest', [questId]) + + def timeout(self): + messenger.send('chooseQuest', [0]) diff --git a/toontown/quest/QuestMap.py b/toontown/quest/QuestMap.py new file mode 100644 index 0000000..2ed98ee --- /dev/null +++ b/toontown/quest/QuestMap.py @@ -0,0 +1,291 @@ +import math +from pandac.PandaModules import CardMaker, TextNode +from direct.gui.DirectGui import DirectFrame, DirectLabel, DirectButton +from direct.task import Task +from toontown.toon import NPCToons +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.quest import Quests +from toontown.suit import SuitPlannerBase +import QuestMapGlobals + +class QuestMap(DirectFrame): + + def __init__(self, av, **kw): + DirectFrame.__init__(self, relief=None, sortOrder=50) + self.initialiseoptions(QuestMap) + self.container = DirectFrame(parent=self, relief=None) + self.marker = DirectFrame(parent=self.container, relief=None) + self.cogInfoFrame = DirectFrame(parent=self.container, relief=None) + cm = CardMaker('bg') + cm.setFrame(-0.5, 0.5, -0.5, 0.5) + bg = self.cogInfoFrame.attachNewNode(cm.generate()) + bg.setTransparency(1) + bg.setColor(0.5, 0.5, 0.5, 0.5) + bg.setBin('fixed', 0) + self.cogInfoFrame['geom'] = bg + self.cogInfoFrame['geom_pos'] = (0, 0, 0) + self.cogInfoFrame['geom_scale'] = (6, 1, 2) + self.cogInfoFrame.setScale(0.05) + self.cogInfoFrame.setPos(0, 0, 0.6) + self.buildingMarkers = [] + self.av = av + self.wantToggle = False + if base.config.GetBool('want-toggle-quest-map', True): + self.wantToggle = True + self.updateMarker = True + self.cornerPosInfo = None + self.hqPosInfo = None + self.fishingSpotInfo = None + self.load() + self.setScale(1.5) + bg.removeNode() + self.hoodId = None + self.zoneId = None + self.suitPercentage = {} + for currHoodInfo in SuitPlannerBase.SuitPlannerBase.SuitHoodInfo: + tracks = currHoodInfo[SuitPlannerBase.SuitPlannerBase.SUIT_HOOD_INFO_TRACK] + self.suitPercentage[currHoodInfo[SuitPlannerBase.SuitPlannerBase.SUIT_HOOD_INFO_ZONE]] = tracks + + return + + def load(self): + gui = loader.loadModel('phase_4/models/questmap/questmap_gui') + icon = gui.find('**/tt_t_gui_qst_arrow') + iconNP = aspect2d.attachNewNode('iconNP') + icon.reparentTo(iconNP) + icon.setR(90) + self.marker['geom'] = iconNP + self.marker['image'] = iconNP + self.marker.setScale(0.05) + iconNP.removeNode() + self.mapOpenButton = DirectButton(image=(gui.find('**/tt_t_gui_qst_mapClose'), gui.find('**/tt_t_gui_qst_mapClose'), gui.find('**/tt_t_gui_qst_mapTryToOpen')), relief=None, pos=(1.25, 0, -0.63), scale=0.205, command=self.show) + self.mapCloseButton = DirectButton(image=(gui.find('**/tt_t_gui_qst_mapOpen'), gui.find('**/tt_t_gui_qst_mapOpen'), gui.find('**/tt_t_gui_qst_mapTryToClose')), relief=None, pos=(1.25, 0, -0.63), scale=0.205, command=self.hide) + self.mapOpenButton.hide() + self.mapCloseButton.hide() + gui.removeNode() + icons = loader.loadModel('phase_3/models/gui/cog_icons') + cIcon = icons.find('**/CorpIcon') + lIcon = icons.find('**/LegalIcon') + mIcon = icons.find('**/MoneyIcon') + sIcon = icons.find('**/SalesIcon') + cogInfoTextColor = (0.2, 0.2, 0.2, 1) + textPos = (1.2, -0.2) + textScale = 0.8 + self.cInfo = DirectLabel(parent=self.cogInfoFrame, text='', text_fg=cogInfoTextColor, text_pos=textPos, text_scale=textScale, geom=cIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.cInfo.setPos(-2.2, 0, 0.5) + self.lInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=lIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.lInfo.setPos(-2.2, 0, -0.5) + self.mInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=mIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.mInfo.setPos(0.8, 0, 0.5) + self.sInfo = DirectLabel(parent=self.cogInfoFrame, text_fg=cogInfoTextColor, text='', text_pos=textPos, text_scale=textScale, geom=sIcon, geom_pos=(-0.2, 0, 0), geom_scale=0.8, relief=None) + self.sInfo.setPos(0.8, 0, -0.5) + icons.removeNode() + return + + def updateCogInfo(self): + currPercentage = self.suitPercentage.get(self.zoneId) + if currPercentage is None: + return + self.cInfo['text'] = '%s%%' % currPercentage[0] + self.lInfo['text'] = '%s%%' % currPercentage[1] + self.mInfo['text'] = '%s%%' % currPercentage[2] + self.sInfo['text'] = '%s%%' % currPercentage[3] + return + + def destroy(self): + self.ignore('questPageUpdated') + self.mapOpenButton.destroy() + self.mapCloseButton.destroy() + del self.mapOpenButton + del self.mapCloseButton + DirectFrame.destroy(self) + + def putBuildingMarker(self, pos, hpr = (0, 0, 0), mapIndex = None): + marker = DirectLabel(parent=self.container, text='', text_pos=(-0.05, -0.15), text_fg=(1, 1, 1, 1), relief=None) + gui = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI') + icon = gui.find('**/startPartyButton_inactive') + iconNP = aspect2d.attachNewNode('iconNP') + icon.reparentTo(iconNP) + icon.setX(-12.0792 / 30.48) + icon.setZ(-9.7404 / 30.48) + marker['text'] = '%s' % mapIndex + marker['text_scale'] = 0.7 + marker['image'] = iconNP + marker['image_color'] = (1, 0, 0, 1) + marker['image_scale'] = 6 + marker.setScale(0.05) + relX, relY = self.transformAvPos(pos) + marker.setPos(relX, 0, relY) + self.buildingMarkers.append(marker) + iconNP.removeNode() + gui.removeNode() + return + + def updateQuestInfo(self): + for marker in self.buildingMarkers: + marker.destroy() + + self.buildingMarkers = [] + dnaStore = base.cr.playGame.dnaStore + for questIndex in self.av.questPage.quests.keys(): + questDesc = self.av.questPage.quests.get(questIndex) + if questDesc is None: + continue + mapIndex = questIndex + 1 + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + quest = Quests.getQuest(questId) + fComplete = quest.getCompletionStatus(self.av, questDesc) == Quests.COMPLETE + if not fComplete: + if quest.getType() == Quests.RecoverItemQuest: + if quest.getHolder() == Quests.AnyFish: + self.putBuildingMarker(self.fishingSpotInfo, mapIndex=mapIndex) + continue + elif quest.getType() != Quests.DeliverGagQuest and quest.getType() != Quests.DeliverItemQuest and quest.getType() != Quests.VisitQuest and quest.getType() != Quests.TrackChoiceQuest: + continue + if toNpcId == Quests.ToonHQ: + self.putBuildingMarker(self.hqPosInfo, mapIndex=mapIndex) + else: + npcZone = NPCToons.getNPCZone(toNpcId) + hoodId = ZoneUtil.getCanonicalHoodId(npcZone) + branchId = ZoneUtil.getCanonicalBranchZone(npcZone) + if self.hoodId == hoodId and self.zoneId == branchId: + for blockIndex in range(dnaStore.getNumBlockTitles()): + blockNumber = dnaStore.getTitleBlockAt(blockIndex) + zone = dnaStore.getZoneFromBlockNumber(blockNumber) + branchZone = zone - zone % 100 + finalZone = branchZone + 500 + blockNumber + buildingType = dnaStore.getBlockBuildingType(blockNumber) + if npcZone == finalZone: + self.putBuildingMarker(dnaStore.getDoorPosHprFromBlockNumber(blockNumber).getPos(), dnaStore.getDoorPosHprFromBlockNumber(blockNumber).getHpr(), mapIndex=mapIndex) + + return + + def transformAvPos(self, pos): + if self.cornerPosInfo is None: + return (0, 0) + topRight = self.cornerPosInfo[0] + bottomLeft = self.cornerPosInfo[1] + relativeX = (pos.getX() - bottomLeft.getX()) / (topRight.getX() - bottomLeft.getX()) - 0.5 + relativeY = (pos.getY() - bottomLeft.getY()) / (topRight.getY() - bottomLeft.getY()) - 0.5 + return (relativeX, relativeY) + + def update(self, task): + if self.av: + if self.updateMarker: + relX, relY = self.transformAvPos(self.av.getPos()) + self.marker.setPos(relX, 0, relY) + self.marker.setHpr(0, 0, -180 - self.av.getH()) + i = 0 + for buildingMarker in self.buildingMarkers: + buildingMarker.setScale((math.sin(task.time * 16.0 + i * math.pi / 3.0) + 1) * 0.005 + 0.04) + i = i + 1 + + return Task.cont + + def updateMap(self): + if self.av: + try: + hoodId = ZoneUtil.getCanonicalHoodId(self.av.getLocation()[1]) + zoneId = ZoneUtil.getCanonicalBranchZone(self.av.getLocation()[1]) + mapsGeom = loader.loadModel('phase_4/models/questmap/%s_maps' % ToontownGlobals.dnaMap[hoodId]) + mapImage = mapsGeom.find('**/%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + if not mapImage.isEmpty(): + self.container['image'] = mapImage + self.resetFrameSize() + self.cornerPosInfo = QuestMapGlobals.CornerPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.hqPosInfo = QuestMapGlobals.HQPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.fishingSpotInfo = QuestMapGlobals.FishingSpotPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.cogInfoPos = QuestMapGlobals.CogInfoPosTable.get('%s_%s_english' % (ToontownGlobals.dnaMap[hoodId], zoneId)) + self.cogInfoFrame.setPos(self.cogInfoPos) + self.hide() + self.hoodId = hoodId + self.zoneId = zoneId + self.updateQuestInfo() + self.updateCogInfo() + taskMgr.add(self.update, 'questMapUpdate') + else: + self.stop() + mapsGeom.removeNode() + except: + self.stop() + + def start(self): + self.container.show() + self.accept('questPageUpdated', self.updateMap) + self.handleMarker() + self.updateMap() + + def initMarker(self, task): + if self.av: + if not hasattr(base.cr.playGame.getPlace(), 'isInterior') or not base.cr.playGame.getPlace().isInterior: + relX, relY = self.transformAvPos(self.av.getPos()) + self.marker.setPos(relX, 0, relY) + self.marker.setHpr(0, 0, -180 - self.av.getH()) + self.marker['geom_scale'] = 1.4 * task.time % 0.5 * 10 + 1 + self.marker['geom_color'] = (1, + 1, + 1, + 0.8 - 1.4 * task.time % 0.5 * 2 / 0.8 + 0.2) + if task.time < 1: + return Task.cont + else: + self.marker['geom_color'] = (1, 1, 1, 0) + return Task.done + + def show(self): + taskMgr.add(self.initMarker, 'questMapInit') + DirectFrame.show(self) + self.mapOpenButton.hide() + if self.container['image']: + self.mapCloseButton.show() + + def hide(self): + taskMgr.remove('questMapInit') + DirectFrame.hide(self) + if self.container['image']: + self.mapOpenButton.show() + self.mapCloseButton.hide() + + def toggle(self): + if self.isHidden(): + self.show() + else: + self.hide() + + def obscureButton(self): + self.mapOpenButton.hide() + self.mapCloseButton.hide() + + def stop(self): + self.container['image'] = None + for marker in self.buildingMarkers: + marker.destroy() + + self.buildingMarkers = [] + self.container.hide() + self.hide() + self.obscureButton() + self.ignore('questPageUpdated') + taskMgr.remove('questMapUpdate') + return + + def handleMarker(self): + if hasattr(base.cr.playGame.getPlace(), 'isInterior') and base.cr.playGame.getPlace().isInterior: + self.updateMarker = False + else: + self.updateMarker = True + + def acceptOnscreenHooks(self): + if self.wantToggle: + self.accept(ToontownGlobals.MapHotkey, self.toggle) + else: + self.accept(ToontownGlobals.MapHotkeyOn, self.show) + self.accept(ToontownGlobals.MapHotkeyOff, self.hide) + self.updateMap() + + def ignoreOnscreenHooks(self): + self.ignore(ToontownGlobals.MapHotkey) + self.ignore(ToontownGlobals.MapHotkeyOn) + self.ignore(ToontownGlobals.MapHotkeyOff) + self.obscureButton() diff --git a/toontown/quest/QuestMapGlobals.py b/toontown/quest/QuestMapGlobals.py new file mode 100644 index 0000000..f1f042d --- /dev/null +++ b/toontown/quest/QuestMapGlobals.py @@ -0,0 +1,70 @@ +from pandac.PandaModules import Point3 +CogInfoPosTable = {'toontown_central_2100_english': Point3(-0.3, 0, -0.2), + 'toontown_central_2200_english': Point3(0.2, 0, -0.2), + 'toontown_central_2300_english': Point3(0.35, 0, -0.2), + 'minnies_melody_land_4100_english': Point3(0.45, 0, -0.2), + 'minnies_melody_land_4200_english': Point3(0.3, 0, -0.3), + 'minnies_melody_land_4300_english': Point3(0.4, 0, -0.2), + 'donalds_dock_1100_english': Point3(0.35, 0, -0.2), + 'donalds_dock_1200_english': Point3(0.3, 0, -0.35), + 'donalds_dock_1300_english': Point3(0.3, 0, -0.25), + 'daisys_garden_5100_english': Point3(0.3, 0, -0.2), + 'daisys_garden_5200_english': Point3(0.3, 0, -0.2), + 'daisys_garden_5300_english': Point3(-0.3, 0, -0.3), + 'the_burrrgh_3100_english': Point3(0.37, 0, -0.2), + 'the_burrrgh_3200_english': Point3(0.35, 0, -0.35), + 'the_burrrgh_3300_english': Point3(0.3, 0, -0.2), + 'donalds_dreamland_9100_english': Point3(0.2, 0, -0.2), + 'donalds_dreamland_9200_english': Point3(-0.25, 0, -0.2)} +HQPosTable = {'toontown_central_2100_english': Point3(-55, -350, 0), + 'toontown_central_2200_english': Point3(-385, 70, 0), + 'toontown_central_2300_english': Point3(490, 140, 0), + 'minnies_melody_land_4100_english': Point3(-330, -40, 0), + 'minnies_melody_land_4200_english': Point3(0, 555, 0), + 'minnies_melody_land_4300_english': Point3(326, 172, 5), + 'donalds_dock_1100_english': Point3(365, 100, 0), + 'donalds_dock_1200_english': Point3(-25, -410, 0), + 'donalds_dock_1300_english': Point3(210, -200, 0), + 'daisys_garden_5100_english': Point3(357.935, 28.45, 0), + 'daisys_garden_5200_english': Point3(345, 150, 0), + 'daisys_garden_5300_english': Point3(-138.597, 77.1236, -0.5), + 'the_burrrgh_3100_english': Point3(129.737, 51.9612, 0), + 'the_burrrgh_3200_english': Point3(230, 200, 0), + 'the_burrrgh_3300_english': Point3(268.052, 228.052, 0), + 'donalds_dreamland_9100_english': Point3(-250, -160, 0), + 'donalds_dreamland_9200_english': Point3(322.91, -67.1147, 0)} +FishingSpotPosTable = {'toontown_central_2100_english': Point3(4.45088, -658.174, 0.431656), + 'toontown_central_2200_english': Point3(-223.506, 161.474, 0.477789), + 'toontown_central_2300_english': Point3(512.4, -69.9997, 0.485466), + 'minnies_melody_land_4100_english': Point3(-570.662, -101.811, 1.9065), + 'minnies_melody_land_4200_english': Point3(-205.722, 238.179, 1.92272), + 'minnies_melody_land_4300_english': Point3(705.052, -26.4757, 1.93412), + 'donalds_dock_1100_english': Point3(368.148, -339.916, 0.419537), + 'donalds_dock_1200_english': Point3(-394.913, -243.085, 0.462966), + 'donalds_dock_1300_english': Point3(346.219, 88.2058, 0.643978), + 'daisys_garden_5100_english': Point3(162.961, 57.4756, 0.360527), + 'daisys_garden_5200_english': Point3(162.961, 113.59, 0.404519), + 'daisys_garden_5300_english': Point3(120.848, -55.6079, 0.388995), + 'the_burrrgh_3100_english': Point3(455.445, 15.6579, 0.376913), + 'the_burrrgh_3200_english': Point3(340.009, 462.856, 0.415845), + 'the_burrrgh_3300_english': Point3(54.17, 75.405, 0.141113), + 'donalds_dreamland_9100_english': Point3(106.796, -197.427, 0.354809), + 'donalds_dreamland_9200_english': Point3(239.646, -330.58, 0.347881)} +CornerPosTable = {'daisys_garden_5200_english': [Point3(781.219727, 476.963623, 0), Point3(-123.380096, -427.636261, 0)], + 'minnies_melody_land_4100_english': [Point3(-98.880981, 349.36908, 0), Point3(-658.680908, -210.430862, 0)], + 'donalds_dock_1300_english': [Point3(713.403076, 361.576324, 0), Point3(-83.596741, -435.423615, 0)], + 'toontown_central_sz_english': [Point3(225.733368, 209.740448, 0), Point3(-207.066559, -223.059525, 0)], + 'donalds_dreamland_9200_english': [Point3(420.817871, 204.719116, 0), Point3(-220.182007, -436.280853, 0)], + 'the_burrrgh_3100_english': [Point3(535.648132, 267.004578, 0), Point3(59.248276, -209.395325, 0)], + 'the_burrrgh_3300_english': [Point3(461.503418, 467.348511, 0), Point3(-27.896454, -22.051422, 0)], + 'toontown_central_2200_english': [Point3(1.816254, 335.417358, 0), Point3(-641.983643, -308.382629, 0)], + 'minnies_melody_land_4200_english': [Point3(283.292847, 658.077393, 0), Point3(-289.50708, 85.277466, 0)], + 'donalds_dock_1200_english': [Point3(34.281967, -47.717041, 0), Point3(-464.517944, -546.516968, 0)], + 'donalds_dock_1100_english': [Point3(596.439575, 146.409653, 0), Point3(28.039612, -421.990356, 0)], + 'minnies_melody_land_4300_english': [Point3(801.294067, 430.992218, 0), Point3(-5.505768, -375.807678, 0)], + 'daisys_garden_5300_english': [Point3(203.715881, 429.168274, 0), Point3(-390.284027, -164.83165, 0)], + 'donalds_dreamland_9100_english': [Point3(279.354584, 111.658554, 0), Point3(-423.445221, -591.141357, 0)], + 'daisys_garden_5100_english': [Point3(760.089722, 526.236206, 0), Point3(-139.510132, -373.3638, 0)], + 'toontown_central_2300_english': [Point3(870.224243, 536.165771, 0), Point3(-89.175751, -423.234344, 0)], + 'toontown_central_2100_english': [Point3(161.735336, -59.985107, 0), Point3(-512.664612, -734.385193, 0)], + 'the_burrrgh_3200_english': [Point3(429.647949, 561.224304, 0), Point3(-31.951935, 99.624283, 0)]} diff --git a/toontown/quest/QuestParser.py b/toontown/quest/QuestParser.py new file mode 100644 index 0000000..44e58a6 --- /dev/null +++ b/toontown/quest/QuestParser.py @@ -0,0 +1,1081 @@ +import sys +import os +import tokenize +import copy +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import AppRunnerGlobal +from pandac.PandaModules import * +from direct.showbase import DirectObject +import BlinkingArrows +from toontown.toon import ToonHeadFrame +from toontown.char import CharDNA +from toontown.suit import SuitDNA +from toontown.char import Char +from toontown.suit import Suit +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from otp.speedchat import SpeedChatGlobals +from toontown.ai import DistributedBlackCatMgr +from direct.showbase import PythonUtil +from direct.interval.IntervalGlobal import * +notify = DirectNotifyGlobal.directNotify.newCategory('QuestParser') +lineDict = {} +globalVarDict = {} +curId = None + +def init(): + globalVarDict.update({'render': render, + 'camera': camera, + 'hidden': hidden, + 'aspect2d': aspect2d, + 'localToon': base.localAvatar, + 'laffMeter': base.localAvatar.laffMeter, + 'inventory': base.localAvatar.inventory, + 'bFriendsList': base.localAvatar.bFriendsList, + 'book': base.localAvatar.book, + 'bookPrevArrow': base.localAvatar.book.prevArrow, + 'bookNextArrow': base.localAvatar.book.nextArrow, + 'bookOpenButton': base.localAvatar.book.bookOpenButton, + 'bookCloseButton': base.localAvatar.book.bookCloseButton, + 'chatNormalButton': base.localAvatar.chatMgr.normalButton, + 'chatScButton': base.localAvatar.chatMgr.scButton, + 'arrows': BlinkingArrows.BlinkingArrows()}) + + +def clear(): + globalVarDict.clear() + + +def readFile(filename): + global curId + scriptFile = StreamReader(vfs.openReadFile(filename, 1), 1) + gen = tokenize.generate_tokens(scriptFile.readline) + line = getLineOfTokens(gen) + while line is not None: + if line == []: + line = getLineOfTokens(gen) + continue + if line[0] == 'ID': + parseId(line) + elif curId is None: + notify.error('Every script must begin with an ID') + else: + lineDict[curId].append(line) + line = getLineOfTokens(gen) + + return + + +def getLineOfTokens(gen): + tokens = [] + nextNeg = 0 + token = gen.next() + if token[0] == tokenize.ENDMARKER: + return None + while token[0] != tokenize.NEWLINE and token[0] != tokenize.NL: + if token[0] == tokenize.COMMENT: + pass + elif token[0] == tokenize.OP and token[1] == '-': + nextNeg = 1 + elif token[0] == tokenize.NUMBER: + if nextNeg: + tokens.append(-eval(token[1])) + nextNeg = 0 + else: + tokens.append(eval(token[1])) + elif token[0] == tokenize.STRING: + tokens.append(eval(token[1])) + elif token[0] == tokenize.NAME: + tokens.append(token[1]) + else: + notify.warning('Ignored token type: %s on line: %s' % (tokenize.tok_name[token[0]], token[2][0])) + token = gen.next() + + return tokens + + +def parseId(line): + global curId + curId = line[1] + notify.debug('Setting current scriptId to: %s' % curId) + if questDefined(curId): + notify.error('Already defined scriptId: %s' % curId) + else: + lineDict[curId] = [] + + +def questDefined(scriptId): + return lineDict.has_key(scriptId) + + +class NPCMoviePlayer(DirectObject.DirectObject): + + def __init__(self, scriptId, toon, npc): + DirectObject.DirectObject.__init__(self) + self.scriptId = scriptId + self.toon = toon + self.isLocalToon = self.toon == base.localAvatar + self.npc = npc + self.privateVarDict = {} + self.toonHeads = {} + self.chars = [] + self.uniqueId = 'scriptMovie_' + str(self.scriptId) + '_' + str(toon.getDoId()) + '_' + str(npc.getDoId()) + self.setVar('toon', self.toon) + self.setVar('npc', self.npc) + self.chapterDict = {} + self.timeoutTrack = None + self.currentTrack = None + return + + def getVar(self, varName): + if self.privateVarDict.has_key(varName): + return self.privateVarDict[varName] + elif globalVarDict.has_key(varName): + return globalVarDict[varName] + elif varName.find('tomDialogue') > -1 or varName.find('harryDialogue') > -1: + notify.warning('%s getting referenced. Tutorial Ack: %d Place: %s' % (varName, base.localAvatar.tutorialAck, base.cr.playGame.hood)) + return None + else: + notify.error('Variable not defined: %s' % varName) + return None + + def delVar(self, varName): + if self.privateVarDict.has_key(varName): + del self.privateVarDict[varName] + elif globalVarDict.has_key(varName): + del globalVarDict[varName] + else: + notify.warning('Variable not defined: %s' % varName) + + def setVar(self, varName, var): + self.privateVarDict[varName] = var + + def cleanup(self): + if self.currentTrack: + self.currentTrack.pause() + self.currentTrack = None + self.ignoreAll() + taskMgr.remove(self.uniqueId) + for toonHeadFrame in self.toonHeads.values(): + toonHeadFrame.destroy() + + while self.chars: + self.__unloadChar(self.chars[0]) + + del self.toonHeads + del self.privateVarDict + del self.chapterDict + del self.toon + del self.npc + del self.timeoutTrack + return + + def __unloadChar(self, char): + char.removeActive() + if char.style.name == 'mk' or char.style.name == 'mn': + char.stopEarTask() + char.delete() + self.chars.remove(char) + + def timeout(self, fFinish = 0): + if self.timeoutTrack: + if fFinish: + self.timeoutTrack.finish() + else: + self.timeoutTrack.start() + + def finishMovie(self): + self.npc.finishMovie(self.toon, self.isLocalToon, 0.0) + + def playNextChapter(self, eventName, timeStamp = 0.0): + trackList = self.chapterDict[eventName] + if trackList: + self.currentTrack = trackList.pop(0) + self.currentTrack.start() + else: + notify.debug('Movie ended waiting for an event (%s)' % eventName) + + def play(self): + lineNum = 0 + self.currentEvent = 'start' + lines = lineDict.get(self.scriptId) + if lines is None: + notify.error('No movie defined for scriptId: %s' % self.scriptId) + chapterList = [] + timeoutList = [] + for line in lines: + lineNum += 1 + command = line[0] + if command == 'UPON_TIMEOUT': + uponTimeout = 1 + iList = timeoutList + line = line[1:] + command = line[0] + else: + uponTimeout = 0 + iList = chapterList + if command == 'CALL': + if uponTimeout: + self.notify.error('CALL not allowed in an UPON_TIMEOUT') + iList.append(self.parseCall(line)) + continue + elif command == 'DEBUG': + iList.append(self.parseDebug(line)) + continue + elif command == 'WAIT': + if uponTimeout: + self.notify.error('WAIT not allowed in an UPON_TIMEOUT') + iList.append(self.parseWait(line)) + continue + elif command == 'CHAT': + iList.append(self.parseChat(line)) + continue + elif command == 'CLEAR_CHAT': + iList.append(self.parseClearChat(line)) + continue + elif command == 'FINISH_QUEST_MOVIE': + chapterList.append(Func(self.finishMovie)) + continue + elif command == 'CHAT_CONFIRM': + if uponTimeout: + self.notify.error('CHAT_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseChatConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + elif command == 'LOCAL_CHAT_CONFIRM': + if uponTimeout: + self.notify.error('LOCAL_CHAT_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseLocalChatConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + elif command == 'LOCAL_CHAT_PERSIST': + iList.append(self.parseLocalChatPersist(line)) + continue + elif command == 'LOCAL_CHAT_TO_CONFIRM': + if uponTimeout: + self.notify.error('LOCAL_CHAT_TO_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseLocalChatToConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + elif command == 'CC_CHAT_CONFIRM': + if uponTimeout: + self.notify.error('CC_CHAT_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseCCChatConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + elif command == 'CC_CHAT_TO_CONFIRM': + if uponTimeout: + self.notify.error('CC_CHAT_TO_CONFIRM not allowed in an UPON_TIMEOUT') + avatarName = line[1] + avatar = self.getVar(avatarName) + nextEvent = avatar.uniqueName('doneChatPage') + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseCCChatToConfirm(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + continue + if self.isLocalToon: + if command == 'LOAD': + self.parseLoad(line) + elif command == 'LOAD_SFX': + self.parseLoadSfx(line) + elif command == 'LOAD_DIALOGUE': + self.parseLoadDialogue(line) + elif command == 'LOAD_CC_DIALOGUE': + self.parseLoadCCDialogue(line) + elif command == 'LOAD_CHAR': + self.parseLoadChar(line) + elif command == 'LOAD_CLASSIC_CHAR': + self.parseLoadClassicChar(line) + elif command == 'UNLOAD_CHAR': + iList.append(self.parseUnloadChar(line)) + elif command == 'LOAD_SUIT': + self.parseLoadSuit(line) + elif command == 'SET': + self.parseSet(line) + elif command == 'LOCK_LOCALTOON': + iList.append(self.parseLockLocalToon(line)) + elif command == 'FREE_LOCALTOON': + iList.append(self.parseFreeLocalToon(line)) + elif command == 'REPARENTTO': + iList.append(self.parseReparent(line)) + elif command == 'WRTREPARENTTO': + iList.append(self.parseWrtReparent(line)) + elif command == 'SHOW': + iList.append(self.parseShow(line)) + elif command == 'HIDE': + iList.append(self.parseHide(line)) + elif command == 'POS': + iList.append(self.parsePos(line)) + elif command == 'HPR': + iList.append(self.parseHpr(line)) + elif command == 'SCALE': + iList.append(self.parseScale(line)) + elif command == 'POSHPRSCALE': + iList.append(self.parsePosHprScale(line)) + elif command == 'COLOR': + iList.append(self.parseColor(line)) + elif command == 'COLOR_SCALE': + iList.append(self.parseColorScale(line)) + elif command == 'ADD_LAFFMETER': + iList.append(self.parseAddLaffMeter(line)) + elif command == 'LAFFMETER': + iList.append(self.parseLaffMeter(line)) + elif command == 'OBSCURE_LAFFMETER': + iList.append(self.parseObscureLaffMeter(line)) + elif command == 'ARROWS_ON': + iList.append(self.parseArrowsOn(line)) + elif command == 'ARROWS_OFF': + iList.append(self.parseArrowsOff(line)) + elif command == 'START_THROB': + iList.append(self.parseStartThrob(line)) + elif command == 'STOP_THROB': + iList.append(self.parseStopThrob(line)) + elif command == 'SHOW_FRIENDS_LIST': + iList.append(self.parseShowFriendsList(line)) + elif command == 'HIDE_FRIENDS_LIST': + iList.append(self.parseHideFriendsList(line)) + elif command == 'SHOW_BOOK': + iList.append(self.parseShowBook(line)) + elif command == 'HIDE_BOOK': + iList.append(self.parseHideBook(line)) + elif command == 'ENABLE_CLOSE_BOOK': + iList.append(self.parseEnableCloseBook(line)) + elif command == 'OBSCURE_BOOK': + iList.append(self.parseObscureBook(line)) + elif command == 'OBSCURE_CHAT': + iList.append(self.parseObscureChat(line)) + elif command == 'ADD_INVENTORY': + iList.append(self.parseAddInventory(line)) + elif command == 'SET_INVENTORY': + iList.append(self.parseSetInventory(line)) + elif command == 'SET_INVENTORY_YPOS': + iList.append(self.parseSetInventoryYPos(line)) + elif command == 'SET_INVENTORY_DETAIL': + iList.append(self.parseSetInventoryDetail(line)) + elif command == 'PLAY_SFX': + iList.append(self.parsePlaySfx(line)) + elif command == 'STOP_SFX': + iList.append(self.parseStopSfx(line)) + elif command == 'PLAY_ANIM': + iList.append(self.parsePlayAnim(line)) + elif command == 'LOOP_ANIM': + iList.append(self.parseLoopAnim(line)) + elif command == 'LERP_POS': + iList.append(self.parseLerpPos(line)) + elif command == 'LERP_HPR': + iList.append(self.parseLerpHpr(line)) + elif command == 'LERP_SCALE': + iList.append(self.parseLerpScale(line)) + elif command == 'LERP_POSHPRSCALE': + iList.append(self.parseLerpPosHprScale(line)) + elif command == 'LERP_COLOR': + iList.append(self.parseLerpColor(line)) + elif command == 'LERP_COLOR_SCALE': + iList.append(self.parseLerpColorScale(line)) + elif command == 'DEPTH_WRITE_ON': + iList.append(self.parseDepthWriteOn(line)) + elif command == 'DEPTH_WRITE_OFF': + iList.append(self.parseDepthWriteOff(line)) + elif command == 'DEPTH_TEST_ON': + iList.append(self.parseDepthTestOn(line)) + elif command == 'DEPTH_TEST_OFF': + iList.append(self.parseDepthTestOff(line)) + elif command == 'SET_BIN': + iList.append(self.parseSetBin(line)) + elif command == 'CLEAR_BIN': + iList.append(self.parseClearBin(line)) + elif command == 'TOON_HEAD': + iList.append(self.parseToonHead(line)) + elif command == 'SEND_EVENT': + iList.append(self.parseSendEvent(line)) + elif command == 'FUNCTION': + iList.append(self.parseFunction(line)) + elif command == 'BLACK_CAT_LISTEN': + iList.append(self.parseBlackCatListen(line)) + elif command == 'SHOW_THROW_SQUIRT_PREVIEW': + if uponTimeout: + self.notify.error('SHOW_THROW_SQUIRT_PREVIEW not allowed in an UPON_TIMEOUT') + nextEvent = 'doneThrowSquirtPreview' + iList.append(Func(self.acceptOnce, nextEvent, self.playNextChapter, [nextEvent])) + iList.append(self.parseThrowSquirtPreview(line)) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + elif command == 'WAIT_EVENT': + if uponTimeout: + self.notify.error('WAIT_EVENT not allowed in an UPON_TIMEOUT') + nextEvent = self.parseWaitEvent(line) + + def proceed(self = self, nextEvent = nextEvent): + self.playNextChapter(nextEvent) + + def handleEvent(*args): + proceed = args[0] + proceed() + + iList.append(Func(self.acceptOnce, nextEvent, handleEvent, [proceed])) + self.closePreviousChapter(iList) + chapterList = [] + self.currentEvent = nextEvent + elif command == 'SET_MUSIC_VOLUME': + iList.append(self.parseSetMusicVolume(line)) + else: + notify.warning('Unknown command token: %s for scriptId: %s on line: %s' % (command, self.scriptId, lineNum)) + + self.closePreviousChapter(chapterList) + if timeoutList: + self.timeoutTrack = Sequence(*timeoutList) + self.playNextChapter('start') + return + + def closePreviousChapter(self, iList): + trackList = self.chapterDict.setdefault(self.currentEvent, []) + trackList.append(Sequence(*iList)) + + def parseLoad(self, line): + if len(line) == 3: + token, varName, modelPath = line + node = loader.loadModel(modelPath) + elif len(line) == 4: + token, varName, modelPath, subNodeName = line + node = loader.loadModel(modelPath).find('**/' + subNodeName) + else: + notify.error('invalid parseLoad command') + self.setVar(varName, node) + + def parseLoadSfx(self, line): + token, varName, fileName = line + sfx = base.loadSfx(fileName) + self.setVar(varName, sfx) + + def parseLoadDialogue(self, line): + token, varName, fileName = line + if varName == 'tomDialogue_01': + notify.debug('VarName tomDialogue getting added. Tutorial Ack: %d' % base.localAvatar.tutorialAck) + if base.config.GetString('language', 'english') == 'japanese': + dialogue = base.loadSfx(fileName) + else: + dialogue = None + self.setVar(varName, dialogue) + return + + def parseLoadCCDialogue(self, line): + token, varName, filenameTemplate = line + if self.toon.getStyle().gender == 'm': + classicChar = 'mickey' + else: + classicChar = 'minnie' + filename = filenameTemplate % classicChar + if base.config.GetString('language', 'english') == 'japanese': + dialogue = base.loadSfx(filename) + else: + dialogue = None + self.setVar(varName, dialogue) + return + + def parseLoadChar(self, line): + token, name, charType = line + char = Char.Char() + dna = CharDNA.CharDNA() + dna.newChar(charType) + char.setDNA(dna) + if charType == 'mk' or charType == 'mn': + char.startEarTask() + char.nametag.manage(base.marginManager) + char.addActive() + char.hideName() + self.setVar(name, char) + + def parseLoadClassicChar(self, line): + token, name = line + char = Char.Char() + dna = CharDNA.CharDNA() + if self.toon.getStyle().gender == 'm': + charType = 'mk' + else: + charType = 'mn' + dna.newChar(charType) + char.setDNA(dna) + char.startEarTask() + char.nametag.manage(base.marginManager) + char.addActive() + char.hideName() + self.setVar(name, char) + self.chars.append(char) + + def parseUnloadChar(self, line): + token, name = line + char = self.getVar(name) + track = Sequence() + track.append(Func(self.__unloadChar, char)) + track.append(Func(self.delVar, name)) + return track + + def parseLoadSuit(self, line): + token, name, suitType = line + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + dna.newSuit(suitType) + suit.setDNA(dna) + self.setVar(name, suit) + + def parseSet(self, line): + token, varName, value = line + self.setVar(varName, value) + + def parseCall(self, line): + token, scriptId = line + nmp = NPCMoviePlayer(scriptId, self.toon, self.npc) + return Func(nmp.play) + + def parseLockLocalToon(self, line): + return Sequence(Func(self.toon.detachCamera), Func(self.toon.collisionsOff), Func(self.toon.disableAvatarControls), Func(self.toon.stopTrackAnimToSpeed), Func(self.toon.stopUpdateSmartCamera)) + + def parseFreeLocalToon(self, line): + return Sequence(Func(self.toon.attachCamera), Func(self.toon.startTrackAnimToSpeed), Func(self.toon.collisionsOn), Func(self.toon.enableAvatarControls), Func(self.toon.startUpdateSmartCamera)) + + def parseDebug(self, line): + token, str = line + return Func(notify.debug, str) + + def parseReparent(self, line): + if len(line) == 3: + token, childNodeName, parentNodeName = line + subNodeName = None + elif len(line) == 4: + token, childNodeName, parentNodeName, subNodeName = line + childNode = self.getVar(childNodeName) + if subNodeName: + parentNode = self.getVar(parentNodeName).find(subNodeName) + else: + parentNode = self.getVar(parentNodeName) + return ParentInterval(childNode, parentNode) + + def parseWrtReparent(self, line): + if len(line) == 3: + token, childNodeName, parentNodeName = line + subNodeName = None + elif len(line) == 4: + token, childNodeName, parentNodeName, subNodeName = line + childNode = self.getVar(childNodeName) + if subNodeName: + parentNode = self.getVar(parentNodeName).find(subNodeName) + else: + parentNode = self.getVar(parentNodeName) + return WrtParentInterval(childNode, parentNode) + + def parseShow(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Func(node.show) + + def parseHide(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Func(node.hide) + + def parsePos(self, line): + token, nodeName, x, y, z = line + node = self.getVar(nodeName) + return Func(node.setPos, x, y, z) + + def parseHpr(self, line): + token, nodeName, h, p, r = line + node = self.getVar(nodeName) + return Func(node.setHpr, h, p, r) + + def parseScale(self, line): + token, nodeName, x, y, z = line + node = self.getVar(nodeName) + return Func(node.setScale, x, y, z) + + def parsePosHprScale(self, line): + token, nodeName, x, y, z, h, p, r, sx, sy, sz = line + node = self.getVar(nodeName) + return Func(node.setPosHprScale, x, y, z, h, p, r, sx, sy, sz) + + def parseColor(self, line): + token, nodeName, r, g, b, a = line + node = self.getVar(nodeName) + return Func(node.setColor, r, g, b, a) + + def parseColorScale(self, line): + token, nodeName, r, g, b, a = line + node = self.getVar(nodeName) + return Func(node.setColorScale, r, g, b, a) + + def parseWait(self, line): + token, waitTime = line + return Wait(waitTime) + + def parseChat(self, line): + toonId = self.toon.getDoId() + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + chatFlags = CFSpeech | CFTimeout + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + if extraChatFlags: + chatFlags |= extraChatFlags + if len(dialogueList) > 0: + dialogue = dialogueList[0] + else: + dialogue = None + return Func(avatar.setChatAbsolute, chatString, chatFlags, dialogue) + + def parseClearChat(self, line): + toonId = self.toon.getDoId() + avatarName = line[1] + avatar = self.getVar(avatarName) + chatFlags = CFSpeech | CFTimeout + return Func(avatar.setChatAbsolute, '', chatFlags) + + def parseExtraChatArgs(self, args): + quitButton = 0 + extraChatFlags = None + dialogueList = [] + for arg in args: + if type(arg) == type(0): + quitButton = arg + elif type(arg) == type(''): + if len(arg) > 2 and arg[:2] == 'CF': + extraChatFlags = eval(arg) + else: + dialogueList.append(self.getVar(arg)) + else: + notify.error('invalid argument type') + + return (quitButton, extraChatFlags, dialogueList) + + def parseChatConfirm(self, line): + lineLength = len(line) + toonId = self.toon.getDoId() + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + return Func(avatar.setPageChat, toonId, 0, chatString, quitButton, extraChatFlags, dialogueList) + + def parseLocalChatConfirm(self, line): + lineLength = len(line) + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) + + def parseLocalChatPersist(self, line): + lineLength = len(line) + avatarName = line[1] + avatar = self.getVar(avatarName) + chatString = eval('TTLocalizer.' + line[2]) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + if len(dialogueList) > 0: + dialogue = dialogueList[0] + else: + dialogue = None + return Func(avatar.setChatAbsolute, chatString, CFSpeech, dialogue) + + def parseLocalChatToConfirm(self, line): + lineLength = len(line) + avatarKey = line[1] + avatar = self.getVar(avatarKey) + toAvatarKey = line[2] + toAvatar = self.getVar(toAvatarKey) + localizerAvatarName = toAvatar.getName().capitalize() + toAvatarName = eval('TTLocalizer.' + localizerAvatarName) + chatString = eval('TTLocalizer.' + line[3]) + chatString = chatString.replace('%s', toAvatarName) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[4:]) + return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) + + def parseCCChatConfirm(self, line): + lineLength = len(line) + avatarName = line[1] + avatar = self.getVar(avatarName) + if self.toon.getStyle().gender == 'm': + chatString = eval('TTLocalizer.' + line[2] % 'Mickey') + else: + chatString = eval('TTLocalizer.' + line[2] % 'Minnie') + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[3:]) + return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) + + def parseCCChatToConfirm(self, line): + lineLength = len(line) + avatarKey = line[1] + avatar = self.getVar(avatarKey) + toAvatarKey = line[2] + toAvatar = self.getVar(toAvatarKey) + localizerAvatarName = toAvatar.getName().capitalize() + toAvatarName = eval('TTLocalizer.' + localizerAvatarName) + if self.toon.getStyle().gender == 'm': + chatString = eval('TTLocalizer.' + line[3] % 'Mickey') + else: + chatString = eval('TTLocalizer.' + line[3] % 'Minnie') + chatString = chatString.replace('%s', toAvatarName) + quitButton, extraChatFlags, dialogueList = self.parseExtraChatArgs(line[4:]) + return Func(avatar.setLocalPageChat, chatString, quitButton, extraChatFlags, dialogueList) + + def parsePlaySfx(self, line): + if len(line) == 2: + token, sfxName = line + looping = 0 + elif len(line) == 3: + token, sfxName, looping = line + else: + notify.error('invalid number of arguments') + sfx = self.getVar(sfxName) + return Func(base.playSfx, sfx, looping) + + def parseStopSfx(self, line): + token, sfxName = line + sfx = self.getVar(sfxName) + return Func(sfx.stop) + + def parsePlayAnim(self, line): + if len(line) == 3: + token, actorName, animName = line + playRate = 1.0 + elif len(line) == 4: + token, actorName, animName, playRate = line + else: + notify.error('invalid number of arguments') + actor = self.getVar(actorName) + return Sequence(Func(actor.setPlayRate, playRate, animName), Func(actor.play, animName)) + + def parseLoopAnim(self, line): + if len(line) == 3: + token, actorName, animName = line + playRate = 1.0 + elif len(line) == 4: + token, actorName, animName, playRate = line + else: + notify.error('invalid number of arguments') + actor = self.getVar(actorName) + return Sequence(Func(actor.setPlayRate, playRate, animName), Func(actor.loop, animName)) + + def parseLerpPos(self, line): + token, nodeName, x, y, z, t = line + node = self.getVar(nodeName) + return Sequence(LerpPosInterval(node, t, Point3(x, y, z), blendType='easeInOut'), duration=0.0) + + def parseLerpHpr(self, line): + token, nodeName, h, p, r, t = line + node = self.getVar(nodeName) + return Sequence(LerpHprInterval(node, t, VBase3(h, p, r), blendType='easeInOut'), duration=0.0) + + def parseLerpScale(self, line): + token, nodeName, x, y, z, t = line + node = self.getVar(nodeName) + return Sequence(LerpScaleInterval(node, t, VBase3(x, y, z), blendType='easeInOut'), duration=0.0) + + def parseLerpPosHprScale(self, line): + token, nodeName, x, y, z, h, p, r, sx, sy, sz, t = line + node = self.getVar(nodeName) + return Sequence(LerpPosHprScaleInterval(node, t, VBase3(x, y, z), VBase3(h, p, r), VBase3(sx, sy, sz), blendType='easeInOut'), duration=0.0) + + def parseLerpColor(self, line): + token, nodeName, sr, sg, sb, sa, er, eg, eb, ea, t = line + node = self.getVar(nodeName) + return Sequence(LerpColorInterval(node, t, VBase4(er, eg, eb, ea), startColorScale=VBase4(sr, sg, sb, sa), blendType='easeInOut'), duration=0.0) + + def parseLerpColorScale(self, line): + token, nodeName, sr, sg, sb, sa, er, eg, eb, ea, t = line + node = self.getVar(nodeName) + return Sequence(LerpColorScaleInterval(node, t, VBase4(er, eg, eb, ea), startColorScale=VBase4(sr, sg, sb, sa), blendType='easeInOut'), duration=0.0) + + def parseDepthWriteOn(self, line): + token, nodeName, depthWrite = line + node = self.getVar(nodeName) + return Sequence(Func(node.setDepthWrite, depthWrite)) + + def parseDepthWriteOff(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Sequence(Func(node.clearDepthWrite)) + + def parseDepthTestOn(self, line): + token, nodeName, depthTest = line + node = self.getVar(nodeName) + return Sequence(Func(node.setDepthTest, depthTest)) + + def parseDepthTestOff(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Sequence(Func(node.clearDepthTest)) + + def parseSetBin(self, line): + if len(line) == 3: + token, nodeName, binName = line + sortOrder = 0 + else: + token, nodeName, binName, sortOrder = line + node = self.getVar(nodeName) + return Sequence(Func(node.setBin, binName, sortOrder)) + + def parseClearBin(self, line): + token, nodeName = line + node = self.getVar(nodeName) + return Sequence(Func(node.clearBin)) + + def parseWaitEvent(self, line): + token, eventName = line + return eventName + + def parseSendEvent(self, line): + token, eventName = line + return Func(messenger.send, eventName) + + def parseFunction(self, line): + token, objectName, functionName = line + object = self.getVar(objectName) + cfunc = compile('object' + '.' + functionName, '', 'eval') + return Func(eval(cfunc)) + + def parseAddLaffMeter(self, line): + token, maxHpDelta = line + newMaxHp = maxHpDelta + self.toon.getMaxHp() + newHp = newMaxHp + laffMeter = self.getVar('laffMeter') + return Func(laffMeter.adjustFace, newHp, newMaxHp) + + def parseLaffMeter(self, line): + token, newHp, newMaxHp = line + laffMeter = self.getVar('laffMeter') + return Func(laffMeter.adjustFace, newHp, newMaxHp) + + def parseObscureLaffMeter(self, line): + token, val = line + return Func(self.toon.laffMeter.obscure, val) + + def parseAddInventory(self, line): + token, track, level, number = line + inventory = self.getVar('inventory') + countSound = base.loadSfx('phase_3.5/audio/sfx/tick_counter.mp3') + return Sequence(Func(base.playSfx, countSound), Func(inventory.buttonBoing, track, level), Func(inventory.addItems, track, level, number), Func(inventory.updateGUI, track, level)) + + def parseSetInventory(self, line): + token, track, level, number = line + inventory = self.getVar('inventory') + return Sequence(Func(inventory.setItem, track, level, number), Func(inventory.updateGUI, track, level)) + + def parseSetInventoryYPos(self, line): + token, track, level, yPos = line + inventory = self.getVar('inventory') + button = inventory.buttons[track][level].stateNodePath[0] + text = button.find('**/+TextNode') + return Sequence(Func(text.setY, yPos)) + + def parseSetInventoryDetail(self, line): + if len(line) == 2: + token, val = line + elif len(line) == 4: + token, val, track, level = line + else: + notify.error('invalid line for parseSetInventoryDetail: %s' % line) + inventory = self.getVar('inventory') + if val == -1: + return Func(inventory.noDetail) + elif val == 0: + return Func(inventory.hideDetail) + elif val == 1: + return Func(inventory.showDetail, track, level) + else: + notify.error('invalid inventory detail level: %s' % val) + + def parseShowFriendsList(self, line): + from toontown.friends import FriendsListPanel + return Func(FriendsListPanel.showFriendsListTutorial) + + def parseHideFriendsList(self, line): + from toontown.friends import FriendsListPanel + return Func(FriendsListPanel.hideFriendsListTutorial) + + def parseShowBook(self, line): + return Sequence(Func(self.toon.book.setPage, self.toon.mapPage), Func(self.toon.book.enter), Func(self.toon.book.disableBookCloseButton)) + + def parseEnableCloseBook(self, line): + return Sequence(Func(self.toon.book.enableBookCloseButton)) + + def parseHideBook(self, line): + return Func(self.toon.book.exit) + + def parseObscureBook(self, line): + token, val = line + return Func(self.toon.book.obscureButton, val) + + def parseObscureChat(self, line): + token, val0, val1 = line + return Func(self.toon.chatMgr.obscure, val0, val1) + + def parseArrowsOn(self, line): + arrows = self.getVar('arrows') + token, x1, y1, h1, x2, y2, h2 = line + return Func(arrows.arrowsOn, x1, y1, h1, x2, y2, h2) + + def parseArrowsOff(self, line): + arrows = self.getVar('arrows') + return Func(arrows.arrowsOff) + + def parseStartThrob(self, line): + token, nodeName, r, g, b, a, r2, g2, b2, a2, t = line + node = self.getVar(nodeName) + startCScale = Point4(r, g, b, a) + destCScale = Point4(r2, g2, b2, a2) + self.throbIval = Sequence(LerpColorScaleInterval(node, t / 2.0, destCScale, startColorScale=startCScale, blendType='easeInOut'), LerpColorScaleInterval(node, t / 2.0, startCScale, startColorScale=destCScale, blendType='easeInOut')) + return Func(self.throbIval.loop) + + def parseStopThrob(self, line): + return Func(self.throbIval.finish) + + def parseToonHead(self, line): + if len(line) == 5: + token, toonName, x, z, toggle = line + scale = 1.0 + else: + token, toonName, x, z, toggle, scale = line + toon = self.getVar(toonName) + toonId = toon.getDoId() + toonHeadFrame = self.toonHeads.get(toonId) + if not toonHeadFrame: + toonHeadFrame = ToonHeadFrame.ToonHeadFrame(toon) + toonHeadFrame.tag1Node.setActive(1) + toonHeadFrame.hide() + self.toonHeads[toonId] = toonHeadFrame + self.setVar('%sToonHead' % toonName, toonHeadFrame) + if toggle: + return Sequence(Func(toonHeadFrame.setPos, x, 0, z), Func(toonHeadFrame.setScale, scale), Func(toonHeadFrame.show)) + else: + return Func(toonHeadFrame.hide) + + def parseToonHeadScale(self, line): + token, toonName, scale = line + toon = self.getVar(toonName) + toonId = toon.getDoId() + toonHeadFrame = self.toonHeads.get(toonId) + return Func(toonHeadFrame.setScale, scale) + + def parseBlackCatListen(self, line): + token, enable = line + if enable: + + def phraseSaid(phraseId): + toontastic = 315 + if phraseId == toontastic: + messenger.send(DistributedBlackCatMgr.DistributedBlackCatMgr.ActivateEvent) + + def enableBlackCatListen(): + self.acceptOnce(SpeedChatGlobals.SCStaticTextMsgEvent, phraseSaid) + + return Func(enableBlackCatListen) + else: + + def disableBlackCatListen(): + self.ignore(SpeedChatGlobals.SCStaticTextMsgEvent) + + return Func(disableBlackCatListen) + + def parseThrowSquirtPreview(self, line): + oldTrackAccess = [None] + + def grabCurTrackAccess(oldTrackAccess = oldTrackAccess): + oldTrackAccess[0] = copy.deepcopy(base.localAvatar.getTrackAccess()) + + def restoreTrackAccess(oldTrackAccess = oldTrackAccess): + base.localAvatar.setTrackAccess(oldTrackAccess[0]) + + minGagLevel = ToontownBattleGlobals.MIN_LEVEL_INDEX + 1 + maxGagLevel = ToontownBattleGlobals.MAX_LEVEL_INDEX + 1 + curGagLevel = minGagLevel + + def updateGagLevel(t, curGagLevel = curGagLevel): + newGagLevel = int(round(t)) + if newGagLevel == curGagLevel: + return + curGagLevel = newGagLevel + base.localAvatar.setTrackAccess([0, + 0, + 0, + 0, + curGagLevel, + curGagLevel, + 0]) + + return Sequence(Func(grabCurTrackAccess), LerpFunctionInterval(updateGagLevel, fromData=1, toData=7, duration=0.3), WaitInterval(3.5), LerpFunctionInterval(updateGagLevel, fromData=7, toData=1, duration=0.3), Func(restoreTrackAccess), Func(messenger.send, 'doneThrowSquirtPreview')) + + def parseSetMusicVolume(self, line): + if base.config.GetString('language', 'english') == 'japanese': + try: + loader = base.cr.playGame.place.loader + type = 'music' + duration = 0 + fromLevel = 1.0 + if len(line) == 2: + token, level = line + elif len(line) == 3: + token, level, type = line + elif len(line) == 4: + token, level, type, duration = line + elif len(line) == 5: + token, level, type, duration, fromLevel = line + if type == 'battleMusic': + music = loader.battleMusic + elif type == 'activityMusic': + music = loader.activityMusic + else: + music = loader.music + if duration == 0: + return Func(music.setVolume, level) + else: + + def setVolume(level): + music.setVolume(level) + + return LerpFunctionInterval(setVolume, fromData=fromLevel, toData=level, duration=duration) + except AttributeError: + pass + + else: + return Wait(0.0) + + +searchPath = DSearchPath() +if AppRunnerGlobal.appRunner: + searchPath.appendDirectory(Filename.expandFrom('$TT_3_ROOT/phase_3/etc')) +else: + searchPath.appendDirectory(Filename('phase_3/etc')) + searchPath.appendDirectory(Filename.fromOsSpecific(os.path.expandvars('$TOONTOWN/src/quest'))) + searchPath.appendDirectory(Filename.fromOsSpecific('toontown/src/quest')) + searchPath.appendDirectory(Filename('.')) +scriptFile = Filename('QuestScripts.txt') +found = vfs.resolveFilename(scriptFile, searchPath) +if not found: + notify.error('Could not find QuestScripts.txt file') +readFile(scriptFile) diff --git a/toontown/quest/QuestPoster.py b/toontown/quest/QuestPoster.py new file mode 100644 index 0000000..784192f --- /dev/null +++ b/toontown/quest/QuestPoster.py @@ -0,0 +1,989 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import Quests +from toontown.toon import NPCToons +from toontown.toon import ToonHead +from toontown.toon import ToonDNA +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import string, types +from toontown.toon import LaffMeter +from toontown.toonbase.ToontownBattleGlobals import AvPropsNew +from toontown.toontowngui.TeaserPanel import TeaserPanel +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +IMAGE_SCALE_LARGE = 0.2 +IMAGE_SCALE_SMALL = 0.15 +POSTER_WIDTH = 0.7 +TEXT_SCALE = TTLocalizer.QPtextScale +TEXT_WORDWRAP = TTLocalizer.QPtextWordwrap + +class QuestPoster(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestPoster') + colors = {'white': (1, + 1, + 1, + 1), + 'blue': (0.45, + 0.45, + 0.8, + 1), + 'lightBlue': (0.42, + 0.671, + 1.0, + 1.0), + 'green': (0.45, + 0.8, + 0.45, + 1), + 'lightGreen': (0.784, + 1, + 0.863, + 1), + 'red': (0.8, + 0.45, + 0.45, + 1), + 'rewardRed': (0.8, + 0.3, + 0.3, + 1), + 'brightRed': (1.0, + 0.16, + 0.16, + 1.0), + 'brown': (0.52, + 0.42, + 0.22, + 1)} + normalTextColor = (0.3, + 0.25, + 0.2, + 1) + confirmDeleteButtonEvent = 'confirmDeleteButtonEvent' + + def __init__(self, parent = aspect2d, **kw): + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + questCard = bookModel.find('**/questCard') + optiondefs = (('relief', None, None), + ('image', questCard, None), + ('image_scale', (0.8, 1.0, 0.58), None), + ('state', DGG.NORMAL, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(QuestPoster) + self._deleteCallback = None + self.questFrame = DirectFrame(parent=self, relief=None) + self.headline = DirectLabel(parent=self.questFrame, relief=None, text='', text_font=ToontownGlobals.getMinnieFont(), text_fg=self.normalTextColor, text_scale=0.05, text_align=TextNode.ACenter, text_wordwrap=12.0, textMayChange=1, pos=(0, 0, 0.23)) + self.questInfo = DirectLabel(parent=self.questFrame, relief=None, text='', text_fg=self.normalTextColor, text_scale=TEXT_SCALE, text_align=TextNode.ACenter, text_wordwrap=TEXT_WORDWRAP, textMayChange=1, pos=(0, 0, -0.0625)) + self.rewardText = DirectLabel(parent=self.questFrame, relief=None, text='', text_fg=self.colors['rewardRed'], text_scale=0.0425, text_align=TextNode.ALeft, text_wordwrap=17.0, textMayChange=1, pos=(-0.36, 0, -0.26)) + self.rewardText.hide() + self.lPictureFrame = DirectFrame(parent=self.questFrame, relief=None, image=bookModel.find('**/questPictureFrame'), image_scale=IMAGE_SCALE_SMALL, text='', text_pos=(0, -0.11), text_fg=self.normalTextColor, text_scale=TEXT_SCALE, text_align=TextNode.ACenter, text_wordwrap=11.0, textMayChange=1) + self.lPictureFrame.hide() + self.rPictureFrame = DirectFrame(parent=self.questFrame, relief=None, image=bookModel.find('**/questPictureFrame'), image_scale=IMAGE_SCALE_SMALL, text='', text_pos=(0, -0.11), text_fg=self.normalTextColor, text_scale=TEXT_SCALE, text_align=TextNode.ACenter, text_wordwrap=11.0, textMayChange=1, pos=(0.18, 0, 0.13)) + self.rPictureFrame.hide() + self.lQuestIcon = DirectFrame(parent=self.lPictureFrame, relief=None, text=' ', text_font=ToontownGlobals.getSuitFont(), text_pos=(0, -0.03), text_fg=self.normalTextColor, text_scale=0.13, text_align=TextNode.ACenter, text_wordwrap=13.0, textMayChange=1) + self.lQuestIcon.setColorOff(-1) + self.rQuestIcon = DirectFrame(parent=self.rPictureFrame, relief=None, text=' ', text_font=ToontownGlobals.getSuitFont(), text_pos=(0, -0.03), text_fg=self.normalTextColor, text_scale=0.13, text_align=TextNode.ACenter, text_wordwrap=13.0, textMayChange=1) + self.rQuestIcon.setColorOff(-1) + self.auxText = DirectLabel(parent=self.questFrame, relief=None, text='', text_scale=TTLocalizer.QPauxText, text_fg=self.normalTextColor, text_align=TextNode.ACenter, textMayChange=1) + self.auxText.hide() + self.questProgress = DirectWaitBar(parent=self.questFrame, relief=DGG.SUNKEN, frameSize=(-0.95, + 0.95, + -0.1, + 0.12), borderWidth=(0.025, 0.025), scale=0.2, frameColor=(0.945, 0.875, 0.706, 1.0), barColor=(0.5, 0.7, 0.5, 1), text='0/0', text_scale=0.19, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ACenter, text_pos=(0, -0.04), pos=(0, 0, -0.195)) + self.questProgress.hide() + self.funQuest = DirectLabel(parent=self.questFrame, relief=None, text=TTLocalizer.QuestPosterFun, text_fg=(0.0, 0.439, 1.0, 1.0), text_shadow=(0, 0, 0, 1), pos=(-0.2825, 0, 0.2), scale=0.03) + self.funQuest.setR(-30) + self.funQuest.hide() + bookModel.removeNode() + self.laffMeter = None + return + + def destroy(self): + self._deleteGeoms() + DirectFrame.destroy(self) + + def _deleteGeoms(self): + for icon in (self.lQuestIcon, self.rQuestIcon): + geom = icon['geom'] + if geom: + if hasattr(geom, 'delete'): + geom.delete() + + def mouseEnterPoster(self, event): + self.reparentTo(self.getParent()) + sc = Vec3(self.initImageScale) + sc.setZ(sc[2] + 0.07) + self['image_scale'] = sc + self.questFrame.setZ(0.03) + self.rewardText.show() + + def mouseExitPoster(self, event): + self['image_scale'] = self.initImageScale + self.questFrame.setZ(0) + self.rewardText.hide() + + def createNpcToonHead(self, toNpcId): + npcInfo = NPCToons.NPCToonDict[toNpcId] + dnaList = npcInfo[2] + gender = npcInfo[3] + if dnaList == 'r': + dnaList = NPCToons.getRandomDNA(toNpcId, gender) + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(*dnaList) + head = ToonHead.ToonHead() + head.setupHead(dna, forGui=1) + self.fitGeometry(head, fFlip=1) + return head + + def createLaffMeter(self, hp): + lm = LaffMeter.LaffMeter(base.localAvatar.style, hp, hp) + lm.adjustText() + return lm + + def createSuitHead(self, suitName): + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit(suitName) + suit = Suit.Suit() + suit.setDNA(suitDNA) + headParts = suit.getHeadParts() + head = hidden.attachNewNode('head') + for part in headParts: + copyPart = part.copyTo(head) + copyPart.setDepthTest(1) + copyPart.setDepthWrite(1) + + self.fitGeometry(head, fFlip=1) + suit.delete() + suit = None + return head + + def loadElevator(self, building, numFloors): + elevatorNodePath = hidden.attachNewNode('elevatorNodePath') + elevatorModel = loader.loadModel('phase_4/models/modules/elevator') + floorIndicator = [None, + None, + None, + None, + None] + npc = elevatorModel.findAllMatches('**/floor_light_?;+s') + for i in range(npc.getNumPaths()): + np = npc.getPath(i) + floor = int(np.getName()[-1:]) - 1 + floorIndicator[floor] = np + if floor < numFloors: + np.setColor(Vec4(0.5, 0.5, 0.5, 1.0)) + else: + np.hide() + + elevatorModel.reparentTo(elevatorNodePath) + suitDoorOrigin = building.find('**/*_door_origin') + elevatorNodePath.reparentTo(suitDoorOrigin) + elevatorNodePath.setPosHpr(0, 0, 0, 0, 0, 0) + return + + def fitGeometry(self, geom, fFlip = 0, dimension = 0.8): + p1 = Point3() + p2 = Point3() + geom.calcTightBounds(p1, p2) + if fFlip: + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + d = p2 - p1 + biggest = max(d[0], d[2]) + s = dimension / biggest + mid = (p1 + d / 2.0) * s + geomXform = hidden.attachNewNode('geomXform') + for child in geom.getChildren(): + child.reparentTo(geomXform) + + geomXform.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], 180, 0, 0, s, s, s) + geomXform.reparentTo(geom) + + def clear(self): + self['image_color'] = Vec4(*self.colors['white']) + self.headline['text'] = '' + self.headline['text_fg'] = self.normalTextColor + self.questInfo['text'] = '' + self.questInfo['text_fg'] = self.normalTextColor + self.rewardText['text'] = '' + self.auxText['text'] = '' + self.auxText['text_fg'] = self.normalTextColor + self.funQuest.hide() + self.lPictureFrame.hide() + self.rPictureFrame.hide() + self.questProgress.hide() + if hasattr(self, 'chooseButton'): + self.chooseButton.destroy() + del self.chooseButton + if hasattr(self, 'deleteButton'): + self.deleteButton.destroy() + del self.deleteButton + self.ignore(self.confirmDeleteButtonEvent) + if hasattr(self, 'confirmDeleteButton'): + self.confirmDeleteButton.cleanup() + del self.confirmDeleteButton + if self.laffMeter != None: + self.laffMeter.reparentTo(hidden) + self.laffMeter.destroy() + self.laffMeter = None + return + + def showChoicePoster(self, questId, fromNpcId, toNpcId, rewardId, callback): + self.update((questId, + fromNpcId, + toNpcId, + rewardId, + 0)) + quest = Quests.getQuest(questId) + self.rewardText.show() + self.rewardText.setZ(-0.205) + self.questProgress.hide() + if not hasattr(self, 'chooseButton'): + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.chooseButton = DirectButton(parent=self.questFrame, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.QuestPageChoose, text_scale=0.06, text_pos=(0, -0.02), pos=(0.285, 0, 0.245), scale=0.65) + guiButton.removeNode() + npcZone = NPCToons.getNPCZone(toNpcId) + hoodId = ZoneUtil.getCanonicalHoodId(npcZone) + if not base.cr.isPaid() and (questId == 401 or hasattr(quest, 'getLocation') and quest.getLocation() == 1000 or hoodId == 1000): + + def showTeaserPanel(): + TeaserPanel(pageName='getGags') + + self.chooseButton['command'] = showTeaserPanel + else: + self.chooseButton['command'] = callback + self.chooseButton['extraArgs'] = [questId] + self.unbind(DGG.WITHIN) + self.unbind(DGG.WITHOUT) + if not quest.getType() == Quests.TrackChoiceQuest: + self.questInfo.setZ(-0.0625) + return + + def update(self, questDesc): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + quest = Quests.getQuest(questId) + if quest == None: + self.notify.warning('Tried to display poster for unknown quest %s' % questId) + return + if rewardId == Quests.NA: + finalReward = Quests.getFinalRewardId(questId, fAll=1) + transformedReward = Quests.transformReward(finalReward, base.localAvatar) + reward = Quests.getReward(transformedReward) + else: + reward = Quests.getReward(rewardId) + if reward and questId not in Quests.NoRewardTierZeroQuests: + rewardString = reward.getPosterString() + else: + rewardString = '' + self.rewardText['text'] = rewardString + self.fitLabel(self.rewardText) + if Quests.isQuestJustForFun(questId, rewardId): + self.funQuest.show() + else: + self.funQuest.hide() + if self._deleteCallback: + self.showDeleteButton(questDesc) + else: + self.hideDeleteButton() + fComplete = quest.getCompletionStatus(base.localAvatar, questDesc) == Quests.COMPLETE + if toNpcId == Quests.ToonHQ: + toNpcName = TTLocalizer.QuestPosterHQOfficer + toNpcBuildingName = TTLocalizer.QuestPosterHQBuildingName + toNpcStreetName = TTLocalizer.QuestPosterHQStreetName + toNpcLocationName = TTLocalizer.QuestPosterHQLocationName + elif toNpcId == Quests.ToonTailor: + toNpcName = TTLocalizer.QuestPosterTailor + toNpcBuildingName = TTLocalizer.QuestPosterTailorBuildingName + toNpcStreetName = TTLocalizer.QuestPosterTailorStreetName + toNpcLocationName = TTLocalizer.QuestPosterTailorLocationName + else: + toNpcName = NPCToons.getNPCName(toNpcId) + toNpcZone = NPCToons.getNPCZone(toNpcId) + toNpcHoodId = ZoneUtil.getCanonicalHoodId(toNpcZone) + toNpcLocationName = base.cr.hoodMgr.getFullnameFromId(toNpcHoodId) + toNpcBuildingName = NPCToons.getBuildingTitle(toNpcZone) + toNpcBranchId = ZoneUtil.getBranchZone(toNpcZone) + toNpcStreetName = ZoneUtil.getStreetName(toNpcBranchId) + lPos = Vec3(0, 0, 0.13) + lIconGeom = None + lIconGeomScale = 1 + rIconGeom = None + rIconGeomScale = 1 + infoText = '' + infoZ = TTLocalizer.QPinfoZ + auxText = None + auxTextPos = Vec3(0, 0, 0.12) + headlineString = quest.getHeadlineString() + objectiveStrings = quest.getObjectiveStrings() + captions = map(string.capwords, quest.getObjectiveStrings()) + imageColor = Vec4(*self.colors['white']) + if quest.getType() == Quests.DeliverGagQuest or quest.getType() == Quests.DeliverItemQuest: + frameBgColor = 'red' + if quest.getType() == Quests.DeliverGagQuest: + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + track, item = quest.getGagType() + lIconGeom = invModel.find('**/' + AvPropsNew[track][item]) + invModel.removeNode() + else: + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/package') + lIconGeomScale = 0.12 + bookModel.removeNode() + if not fComplete: + captions.append(toNpcName) + auxText = TTLocalizer.QuestPosterAuxTo + auxTextPos.setZ(0.12) + lPos.setX(-0.18) + infoText = TTLocalizer.QuestPageDestination % (toNpcBuildingName, toNpcStreetName, toNpcLocationName) + rIconGeom = self.createNpcToonHead(toNpcId) + rIconGeomScale = IMAGE_SCALE_SMALL + elif quest.getType() == Quests.RecoverItemQuest: + frameBgColor = 'green' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/package') + lIconGeomScale = 0.12 + bookModel.removeNode() + if not fComplete: + rIconGeomScale = IMAGE_SCALE_SMALL + holder = quest.getHolder() + holderType = quest.getHolderType() + if holder == Quests.Any: + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + rIconGeom = cogIcons.find('**/cog') + cogIcons.removeNode() + lPos.setX(-0.18) + auxText = TTLocalizer.QuestPosterAuxFrom + elif holder == Quests.AnyFish: + headlineString = TTLocalizer.QuestPosterFishing + auxText = TTLocalizer.QuestPosterAuxFor + auxTextPos.setX(-0.18) + captions = captions[:1] + else: + if holderType == 'track': + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + if holder == 'c': + icon = cogIcons.find('**/CorpIcon') + elif holder == 's': + icon = cogIcons.find('**/SalesIcon') + elif holder == 'l': + icon = cogIcons.find('**/LegalIcon') + elif holder == 'm': + icon = cogIcons.find('**/MoneyIcon') + rIconGeom = icon.copyTo(hidden) + rIconGeom.setColor(Suit.Suit.medallionColors[holder]) + rIconGeomScale = 0.12 + cogIcons.removeNode() + elif holderType == 'level': + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + rIconGeom = cogIcons.find('**/cog') + rIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + else: + rIconGeom = self.createSuitHead(holder) + lPos.setX(-0.18) + auxText = TTLocalizer.QuestPosterAuxFrom + infoText = string.capwords(quest.getLocationName()) + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.VisitQuest: + frameBgColor = 'brown' + captions[0] = '%s' % toNpcName + lIconGeom = self.createNpcToonHead(toNpcId) + lIconGeomScale = IMAGE_SCALE_SMALL + if not fComplete: + infoText = TTLocalizer.QuestPageDestination % (toNpcBuildingName, toNpcStreetName, toNpcLocationName) + elif quest.getType() == Quests.TrackChoiceQuest: + frameBgColor = 'green' + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + track1, track2 = quest.getChoices() + lIconGeom = invModel.find('**/' + AvPropsNew[track1][1]) + if not fComplete: + auxText = TTLocalizer.QuestPosterAuxOr + lPos.setX(-0.18) + rIconGeom = invModel.find('**/' + AvPropsNew[track2][1]) + infoText = TTLocalizer.QuestPageNameAndDestination % (toNpcName, + toNpcBuildingName, + toNpcStreetName, + toNpcLocationName) + infoZ = -0.02 + invModel.removeNode() + elif quest.getType() == Quests.BuildingQuest: + frameBgColor = 'blue' + track = quest.getBuildingTrack() + numFloors = quest.getNumFloors() + if track == 'c': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_corp') + elif track == 'l': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_legal') + elif track == 'm': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_money') + elif track == 's': + lIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_sales') + else: + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/COG_building') + bookModel.removeNode() + if lIconGeom and track != Quests.Any: + self.loadElevator(lIconGeom, numFloors) + lIconGeom.setH(180) + self.fitGeometry(lIconGeom, fFlip=0) + lIconGeomScale = IMAGE_SCALE_SMALL + else: + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.BuildingNewbieQuest: + frameBgColor = 'blue' + track = quest.getBuildingTrack() + numFloors = quest.getNumFloors() + if track == 'c': + rIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_corp') + elif track == 'l': + rIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_legal') + elif track == 'm': + rIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_money') + elif track == 's': + rIconGeom = loader.loadModel('phase_4/models/modules/suit_landmark_sales') + else: + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = bookModel.find('**/COG_building') + bookModel.removeNode() + if rIconGeom and track != Quests.Any: + self.loadElevator(rIconGeom, numFloors) + rIconGeom.setH(180) + self.fitGeometry(rIconGeom, fFlip=0) + rIconGeomScale = IMAGE_SCALE_SMALL + else: + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.FactoryQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/factoryIcon2') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.FactoryNewbieQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = bookModel.find('**/factoryIcon2') + bookModel.removeNode() + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.MintQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/CashBotMint') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.MintNewbieQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = bookModel.find('**/CashBotMint') + bookModel.removeNode() + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.CogPartQuest: + frameBgColor = 'green' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/CogArmIcon2') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.CogPartNewbieQuest: + frameBgColor = 'green' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = bookModel.find('**/CogArmIcon2') + bookModel.removeNode() + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogPartQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.ForemanQuest or quest.getType() == Quests.SupervisorQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/skelecog5') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.ForemanNewbieQuest or quest.getType() == Quests.SupervisorNewbieQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = bookModel.find('**/skelecog5') + bookModel.removeNode() + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.VPQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/BossHead3Icon') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.VPNewbieQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = bookModel.find('**/BossHead3Icon') + bookModel.removeNode() + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.CFOQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/CashBotBossHeadIcon') + bookModel.removeNode() + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.CFONewbieQuest: + frameBgColor = 'blue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = bookModel.find('**/CashBotBossHeadIcon') + bookModel.removeNode() + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.RescueQuest: + frameBgColor = 'blue' + lIconGeom = self.createNpcToonHead(2001) + lIconGeomScale = 0.13 + if not fComplete: + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.RescueNewbieQuest: + frameBgColor = 'blue' + rIconGeom = self.createNpcToonHead(2001) + rIconGeomScale = 0.13 + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsRescueQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + infoText = quest.getLocationName() + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.FriendQuest: + frameBgColor = 'brown' + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + lIconGeom = gui.find('**/FriendsBox_Closed') + lIconGeomScale = 0.45 + gui.removeNode() + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.FriendNewbieQuest: + frameBgColor = 'brown' + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + lIconGeom = gui.find('**/FriendsBox_Closed') + lIconGeomScale = 0.45 + gui.removeNode() + infoText = TTLocalizer.QuestPosterAnywhere + elif quest.getType() == Quests.TrolleyQuest: + frameBgColor = 'lightBlue' + gui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = gui.find('**/trolley') + lIconGeomScale = 0.13 + gui.removeNode() + infoText = TTLocalizer.QuestPosterPlayground + elif quest.getType() == Quests.MailboxQuest: + frameBgColor = 'lightBlue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/package') + lIconGeomScale = 0.12 + bookModel.removeNode() + infoText = TTLocalizer.QuestPosterAtHome + elif quest.getType() == Quests.PhoneQuest: + frameBgColor = 'lightBlue' + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = bookModel.find('**/clarabelleCow') + lIconGeomScale = 0.12 + bookModel.removeNode() + infoText = TTLocalizer.QuestPosterOnPhone + elif quest.getType() == Quests.MinigameNewbieQuest: + frameBgColor = 'lightBlue' + gui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = gui.find('**/trolley') + rIconGeomScale = 0.13 + gui.removeNode() + infoText = TTLocalizer.QuestPosterPlayground + if not fComplete: + captions = [TTLocalizer.QuestsMinigameNewbieQuestCaption % quest.getNewbieLevel()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsMinigameNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + else: + frameBgColor = 'blue' + if quest.getType() == Quests.CogTrackQuest: + dept = quest.getCogTrack() + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeomScale = 0.13 + if dept == 'c': + icon = cogIcons.find('**/CorpIcon') + elif dept == 's': + icon = cogIcons.find('**/SalesIcon') + elif dept == 'l': + icon = cogIcons.find('**/LegalIcon') + elif dept == 'm': + icon = cogIcons.find('**/MoneyIcon') + lIconGeom = icon.copyTo(hidden) + lIconGeom.setColor(Suit.Suit.medallionColors[dept]) + cogIcons.removeNode() + elif quest.getType() == Quests.CogQuest: + if quest.getCogType() != Quests.Any: + lIconGeom = self.createSuitHead(quest.getCogType()) + lIconGeomScale = IMAGE_SCALE_SMALL + else: + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeom = cogIcons.find('**/cog') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.CogLevelQuest: + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeom = cogIcons.find('**/cog') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.CogNewbieQuest: + if quest.getCogType() != Quests.Any: + rIconGeom = self.createSuitHead(quest.getCogType()) + rIconGeomScale = IMAGE_SCALE_SMALL + else: + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + rIconGeom = cogIcons.find('**/cog') + rIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.SkelecogTrackQuest: + dept = quest.getCogTrack() + cogIcons = loader.loadModel('phase_3/models/gui/cog_icons') + lIconGeomScale = 0.13 + if dept == 'c': + icon = cogIcons.find('**/CorpIcon') + elif dept == 's': + icon = cogIcons.find('**/SalesIcon') + elif dept == 'l': + icon = cogIcons.find('**/LegalIcon') + elif dept == 'm': + icon = cogIcons.find('**/MoneyIcon') + lIconGeom = icon.copyTo(hidden) + lIconGeom.setColor(Suit.Suit.medallionColors[dept]) + cogIcons.removeNode() + elif quest.getType() == Quests.SkelecogQuest: + cogIcons = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = cogIcons.find('**/skelecog5') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.SkelecogLevelQuest: + cogIcons = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = cogIcons.find('**/skelecog5') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + elif quest.getType() == Quests.SkelecogNewbieQuest: + cogIcons = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + rIconGeom = cogIcons.find('**/skelecog5') + rIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + if not fComplete: + headlineString = TTLocalizer.QuestsNewbieQuestHeadline + captions = [quest.getCaption()] + captions.append(map(string.capwords, quest.getObjectiveStrings())) + auxText = TTLocalizer.QuestsCogNewbieQuestAux + lPos.setX(-0.18) + self.laffMeter = self.createLaffMeter(quest.getNewbieLevel()) + self.laffMeter.setScale(0.04) + lIconGeom = None + else: + lIconGeom = rIconGeom + rIconGeom = None + lIconGeomScale = rIconGeomScale + rIconGeomScale = 1 + elif quest.getType() == Quests.SkeleReviveQuest: + cogIcons = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + lIconGeom = cogIcons.find('**/skelecog5') + lIconGeomScale = IMAGE_SCALE_SMALL + cogIcons.removeNode() + if not fComplete: + infoText = string.capwords(quest.getLocationName()) + if infoText == '': + infoText = TTLocalizer.QuestPosterAnywhere + if fComplete: + textColor = (0, 0.3, 0, 1) + imageColor = Vec4(*self.colors['lightGreen']) + lPos.setX(-0.18) + rIconGeom = self.createNpcToonHead(toNpcId) + rIconGeomScale = IMAGE_SCALE_SMALL + captions = captions[:1] + captions.append(toNpcName) + auxText = TTLocalizer.QuestPosterAuxReturnTo + headlineString = TTLocalizer.QuestPosterComplete + infoText = TTLocalizer.QuestPageDestination % (toNpcBuildingName, toNpcStreetName, toNpcLocationName) + if self.laffMeter != None: + self.laffMeter.reparentTo(hidden) + self.laffMeter.destroy() + self.laffMeter = None + else: + textColor = self.normalTextColor + self.show() + self['image_color'] = imageColor + self.headline['text_fg'] = textColor + self.headline['text'] = headlineString + self.lPictureFrame.show() + self.lPictureFrame.setPos(lPos) + self.lPictureFrame['text_scale'] = TEXT_SCALE + if lPos[0] != 0: + self.lPictureFrame['text_scale'] = 0.0325 + self.lPictureFrame['text'] = captions[0] + self.lPictureFrame['image_color'] = Vec4(*self.colors[frameBgColor]) + if len(captions) > 1: + self.rPictureFrame.show() + self.rPictureFrame['text'] = captions[1] + self.rPictureFrame['text_scale'] = 0.0325 + self.rPictureFrame['image_color'] = Vec4(*self.colors[frameBgColor]) + else: + self.rPictureFrame.hide() + self._deleteGeoms() + self.lQuestIcon['geom'] = lIconGeom + self.lQuestIcon['geom_pos'] = (0, 10, 0) + if lIconGeom: + self.lQuestIcon['geom_scale'] = lIconGeomScale + if self.laffMeter != None: + self.laffMeter.reparentTo(self.lQuestIcon) + self.rQuestIcon['geom'] = rIconGeom + self.rQuestIcon['geom_pos'] = (0, 10, 0) + if rIconGeom: + self.rQuestIcon['geom_scale'] = rIconGeomScale + if auxText: + self.auxText.show() + self.auxText['text'] = auxText + self.auxText.setPos(auxTextPos) + else: + self.auxText.hide() + self.bind(DGG.WITHIN, self.mouseEnterPoster) + self.bind(DGG.WITHOUT, self.mouseExitPoster) + numQuestItems = quest.getNumQuestItems() + if fComplete or numQuestItems <= 1: + self.questProgress.hide() + if not quest.getType() == Quests.TrackChoiceQuest: + infoZ = -0.075 + else: + self.questProgress.show() + self.questProgress['value'] = toonProgress & pow(2, 16) - 1 + self.questProgress['range'] = numQuestItems + self.questProgress['text'] = quest.getProgressString(base.localAvatar, questDesc) + self.questInfo['text'] = infoText + self.questInfo.setZ(infoZ) + self.fitLabel(self.questInfo) + return + + def unbindMouseEnter(self): + self.unbind(DGG.WITHIN) + + def showDeleteButton(self, questDesc): + self.hideDeleteButton() + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.deleteButton = DirectButton(parent=self.questFrame, image=(trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.QuestPosterDeleteBtn, TTLocalizer.QuestPosterDeleteBtn), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.18, text_pos=(0, -0.12), relief=None, pos=(0.3, 0, 0.145), scale=0.3, command=self.onPressedDeleteButton, extraArgs=[questDesc]) + trashcanGui.removeNode() + return + + def hideDeleteButton(self): + if hasattr(self, 'deleteButton'): + self.deleteButton.destroy() + del self.deleteButton + + def setDeleteCallback(self, callback): + self._deleteCallback = callback + + def onPressedDeleteButton(self, questDesc): + self.deleteButton['state'] = DGG.DISABLED + self.accept(self.confirmDeleteButtonEvent, self.confirmedDeleteButton) + self.confirmDeleteButton = TTDialog.TTGlobalDialog(doneEvent=self.confirmDeleteButtonEvent, message=TTLocalizer.QuestPosterConfirmDelete, style=TTDialog.YesNo, okButtonText=TTLocalizer.QuestPosterDialogYes, cancelButtonText=TTLocalizer.QuestPosterDialogNo) + self.confirmDeleteButton.quest = questDesc + self.confirmDeleteButton.doneStatus = '' + self.confirmDeleteButton.show() + + def confirmedDeleteButton(self): + questDesc = self.confirmDeleteButton.quest + self.ignore(self.confirmDeleteButtonEvent) + if self.confirmDeleteButton.doneStatus == 'ok': + if self._deleteCallback: + self._deleteCallback(questDesc) + else: + self.deleteButton['state'] = DGG.NORMAL + self.confirmDeleteButton.cleanup() + del self.confirmDeleteButton + + def fitLabel(self, label, lineNo = 0): + text = label['text'] + label['text_scale'] = TEXT_SCALE + label['text_wordwrap'] = TEXT_WORDWRAP + if len(text) > 0: + lines = text.split('\n') + lineWidth = label.component('text0').textNode.calcWidth(lines[lineNo]) + if lineWidth > 0: + textScale = POSTER_WIDTH / lineWidth + label['text_scale'] = min(TEXT_SCALE, textScale) + label['text_wordwrap'] = max(TEXT_WORDWRAP, lineWidth + 0.05) diff --git a/toontown/quest/QuestRewardCounter.py b/toontown/quest/QuestRewardCounter.py new file mode 100644 index 0000000..ce6fd47 --- /dev/null +++ b/toontown/quest/QuestRewardCounter.py @@ -0,0 +1,150 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +import Quests +from toontown.toonbase import ToontownGlobals +from toontown.fishing import FishGlobals +from toontown.suit import SuitDNA +from toontown.racing import RaceGlobals +from toontown.estate import GardenGlobals +from toontown.golf import GolfGlobals + +class QuestRewardCounter: + notify = directNotify.newCategory('QuestRewardCounter') + + def __init__(self): + self.reset() + + def reset(self): + self.maxHp = 15 + self.maxCarry = 20 + self.maxMoney = 40 + self.questCarryLimit = 1 + self.teleportAccess = [] + self.trackAccess = [0, + 0, + 0, + 0, + 1, + 1, + 0] + self.trackProgressId = -1 + self.trackProgress = 0 + + def addTeleportAccess(self, zoneId): + if zoneId not in self.teleportAccess: + self.teleportAccess.append(zoneId) + + def addTrackAccess(self, track): + self.trackAccess[track] = 1 + + def addTrackProgress(self, trackId, progressIndex): + if self.trackProgressId != trackId: + self.notify.warning('tried to update progress on a track toon is not training') + self.trackProgress = self.trackProgress | 1 << progressIndex + + def getTrackProgress(self): + return (self.trackProgressId, self.trackProgress) + + def clearTrackProgress(self): + self.trackProgressId = -1 + self.trackProgress = 0 + + def setFromAvatar(self, av): + rewardIds = [] + for q in av.quests: + questId, fromNpcId, toNpcId, rewardId, toonProgress = q + if rewardId == Quests.NA: + rewardId = Quests.getFinalRewardId(questId, fAll=1) + rewardIds.append(rewardId) + + self.notify.debug('Ignoring rewards: %s' % rewardIds) + self.setRewardIndex(av.rewardTier, rewardIds, av.rewardHistory) + fishHp = int(len(av.fishCollection) / FishGlobals.FISH_PER_BONUS) + self.notify.debug('Adding %s hp for fish collection' % fishHp) + self.maxHp += fishHp + flowerHp = int(len(av.flowerCollection) / GardenGlobals.FLOWERS_PER_BONUS) + self.notify.debug('Adding %s hp for fish collection' % flowerHp) + self.maxHp += flowerHp + HQdepts = (ToontownGlobals.cogHQZoneId2deptIndex(ToontownGlobals.SellbotHQ), ToontownGlobals.cogHQZoneId2deptIndex(ToontownGlobals.LawbotHQ), ToontownGlobals.cogHQZoneId2deptIndex(ToontownGlobals.CashbotHQ)) + levels = av.getCogLevels() + cogTypes = av.getCogTypes() + suitHp = 0 + for dept in HQdepts: + level = levels[dept] + type = cogTypes[dept] + if type >= SuitDNA.suitsPerDept - 1: + for milestoneLevel in ToontownGlobals.CogSuitHPLevels: + if level >= milestoneLevel: + suitHp += 1 + else: + break + + self.notify.debug('Adding %s hp for cog suits' % suitHp) + self.maxHp += suitHp + kartingHp = int(av.kartingTrophies.count(1) / RaceGlobals.TrophiesPerCup) + self.notify.debug('Adding %s hp for karting trophies' % kartingHp) + self.maxHp += kartingHp + golfHp = int(av.golfTrophies.count(True) / GolfGlobals.TrophiesPerCup) + self.notify.debug('Adding %s hp for golf trophies' % golfHp) + self.maxHp += golfHp + + def setRewardIndex(self, tier, rewardIds, rewardHistory): + self.reset() + for tierNum in range(tier): + for rewardId in Quests.getRewardsInTier(tierNum): + reward = Quests.getReward(rewardId) + reward.countReward(self) + self.notify.debug('Assigning reward %d' % rewardId) + + for rewardId in rewardHistory: + if rewardId in Quests.getRewardsInTier(tier): + if rewardIds.count(rewardId) != 0: + rewardIds.remove(rewardId) + self.notify.debug('Ignoring reward %d' % rewardId) + else: + reward = Quests.getReward(rewardId) + reward.countReward(self) + self.notify.debug('Assigning reward %d' % rewardId) + + self.notify.debug('Remaining rewardIds %s' % rewardIds) + self.maxHp = min(ToontownGlobals.MaxHpLimit, self.maxHp) + + def assignReward(self, rewardId, rewardIds): + if rewardIds.count(rewardId) != 0: + rewardIds.remove(rewardId) + self.notify.debug('Ignoring reward %d' % rewardId) + else: + reward = Quests.getReward(rewardId) + reward.countReward(self) + self.notify.debug('Assigning reward %d' % rewardId) + + def fixAvatar(self, av): + self.setFromAvatar(av) + anyChanged = 0 + if self.maxHp != av.maxHp: + self.notify.info('Changed avatar %d to have maxHp %d instead of %d' % (av.doId, self.maxHp, av.maxHp)) + av.b_setMaxHp(self.maxHp) + anyChanged = 1 + if self.maxCarry != av.maxCarry: + self.notify.info('Changed avatar %d to have maxCarry %d instead of %d' % (av.doId, self.maxCarry, av.maxCarry)) + av.b_setMaxCarry(self.maxCarry) + anyChanged = 1 + if self.maxMoney != av.maxMoney: + self.notify.info('Changed avatar %d to have maxMoney %d instead of %d' % (av.doId, self.maxMoney, av.maxMoney)) + av.b_setMaxMoney(self.maxMoney) + anyChanged = 1 + if self.questCarryLimit != av.questCarryLimit: + self.notify.info('Changed avatar %d to have questCarryLimit %d instead of %d' % (av.doId, self.questCarryLimit, av.questCarryLimit)) + av.b_setQuestCarryLimit(self.questCarryLimit) + anyChanged = 1 + if self.teleportAccess != av.teleportZoneArray: + self.notify.info('Changed avatar %d to have teleportAccess %s instead of %s' % (av.doId, self.teleportAccess, av.teleportZoneArray)) + av.b_setTeleportAccess(self.teleportAccess) + anyChanged = 1 + if self.trackAccess != av.trackArray: + self.notify.info('Changed avatar %d to have trackAccess %s instead of %s' % (av.doId, self.trackAccess, av.trackArray)) + av.b_setTrackAccess(self.trackAccess) + anyChanged = 1 + if av.fixTrackAccess(): + anyChanged = 1 + return anyChanged diff --git a/toontown/quest/Quests.py b/toontown/quest/Quests.py new file mode 100644 index 0000000..fa5fbea --- /dev/null +++ b/toontown/quest/Quests.py @@ -0,0 +1,19855 @@ +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals +from toontown.battle import SuitBattleGlobals +from toontown.coghq import CogDisguiseGlobals +import random +from toontown.toon import NPCToons +import copy, string +from toontown.hood import ZoneUtil +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +import time, types, random +notify = DirectNotifyGlobal.directNotify.newCategory('Quests') +ItemDict = TTLocalizer.QuestsItemDict +CompleteString = TTLocalizer.QuestsCompleteString +NotChosenString = TTLocalizer.QuestsNotChosenString +DefaultGreeting = TTLocalizer.QuestsDefaultGreeting +DefaultIncomplete = TTLocalizer.QuestsDefaultIncomplete +DefaultIncompleteProgress = TTLocalizer.QuestsDefaultIncompleteProgress +DefaultIncompleteWrongNPC = TTLocalizer.QuestsDefaultIncompleteWrongNPC +DefaultComplete = TTLocalizer.QuestsDefaultComplete +DefaultLeaving = TTLocalizer.QuestsDefaultLeaving +DefaultReject = TTLocalizer.QuestsDefaultReject +DefaultTierNotDone = TTLocalizer.QuestsDefaultTierNotDone +DefaultQuest = TTLocalizer.QuestsDefaultQuest +DefaultVisitQuestDialog = TTLocalizer.QuestsDefaultVisitQuestDialog +GREETING = 0 +QUEST = 1 +INCOMPLETE = 2 +INCOMPLETE_PROGRESS = 3 +INCOMPLETE_WRONG_NPC = 4 +COMPLETE = 5 +LEAVING = 6 +Any = 1 +OBSOLETE = 'OBSOLETE' +Start = 1 +Cont = 0 +Anywhere = 1 +NA = 2 +Same = 3 +AnyFish = 4 +AnyCashbotSuitPart = 5 +AnyLawbotSuitPart = 6 +AnyBossbotSuitPart = 7 +ToonTailor = 999 +ToonHQ = 1000 +QuestDictTierIndex = 0 +QuestDictStartIndex = 1 +QuestDictDescIndex = 2 +QuestDictFromNpcIndex = 3 +QuestDictToNpcIndex = 4 +QuestDictRewardIndex = 5 +QuestDictNextQuestIndex = 6 +QuestDictDialogIndex = 7 +VeryEasy = 100 +Easy = 75 +Medium = 50 +Hard = 25 +VeryHard = 20 +TT_TIER = 0 +DD_TIER = 4 +DG_TIER = 7 +MM_TIER = 8 +BR_TIER = 11 +DL_TIER = 14 +LAWBOT_HQ_TIER = 18 +BOSSBOT_HQ_TIER = 32 +ELDER_TIER = 49 +LOOPING_FINAL_TIER = ELDER_TIER +VISIT_QUEST_ID = 1000 +TROLLEY_QUEST_ID = 110 +FIRST_COG_QUEST_ID = 145 +FRIEND_QUEST_ID = 150 +PHONE_QUEST_ID = 175 +NEWBIE_HP = 25 +SELLBOT_HQ_NEWBIE_HP = 50 +CASHBOT_HQ_NEWBIE_HP = 85 +from toontown.toonbase.ToontownGlobals import FT_FullSuit, FT_Leg, FT_Arm, FT_Torso +QuestRandGen = random.Random() + +def seedRandomGen(npcId, avId, tier, rewardHistory): + QuestRandGen.seed(npcId * 100 + avId + tier + len(rewardHistory)) + + +def seededRandomChoice(seq): + return QuestRandGen.choice(seq) + + +def getCompleteStatusWithNpc(questComplete, toNpcId, npc): + if questComplete: + if npc: + if npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + else: + return COMPLETE + elif npc: + if npcMatches(toNpcId, npc): + return INCOMPLETE_PROGRESS + else: + return INCOMPLETE + else: + return INCOMPLETE + + +def npcMatches(toNpcId, npc): + return toNpcId == npc.getNpcId() or toNpcId == Any or toNpcId == ToonHQ and npc.getHq() or toNpcId == ToonTailor and npc.getTailor() + + +def calcRecoverChance(numberNotDone, baseChance, cap = 1): + chance = baseChance + avgNum2Kill = 1.0 / (chance / 100.0) + if numberNotDone >= avgNum2Kill * 1.5 and cap: + chance = 1000 + elif numberNotDone > avgNum2Kill * 0.5: + diff = float(numberNotDone - avgNum2Kill * 0.5) + luck = 1.0 + abs(diff / (avgNum2Kill * 0.5)) + chance *= luck + return chance + + +def simulateRecoveryVar(numNeeded, baseChance, list = 0, cap = 1): + numHave = 0 + numTries = 0 + greatestFailChain = 0 + currentFail = 0 + capHits = 0 + attemptList = {} + while numHave < numNeeded: + numTries += 1 + chance = calcRecoverChance(currentFail, baseChance, cap) + test = random.random() * 100 + if chance == 1000: + capHits += 1 + if test < chance: + numHave += 1 + if currentFail > greatestFailChain: + greatestFailChain = currentFail + if attemptList.get(currentFail): + attemptList[currentFail] += 1 + else: + attemptList[currentFail] = 1 + currentFail = 0 + else: + currentFail += 1 + + print 'Test results: %s tries, %s longest failure chain, %s cap hits' % (numTries, greatestFailChain, capHits) + if list: + print 'failures for each succes %s' % attemptList + + +def simulateRecoveryFix(numNeeded, baseChance, list = 0): + numHave = 0 + numTries = 0 + greatestFailChain = 0 + currentFail = 0 + attemptList = {} + while numHave < numNeeded: + numTries += 1 + chance = baseChance + test = random.random() * 100 + if test < chance: + numHave += 1 + if currentFail > greatestFailChain: + greatestFailChain = currentFail + if attemptList.get(currentFail): + attemptList[currentFail] += 1 + else: + attemptList[currentFail] = 1 + currentFail = 0 + else: + currentFail += 1 + + print 'Test results: %s tries, %s longest failure chain' % (numTries, greatestFailChain) + if list: + print 'failures for each succes %s' % attemptList + + +class Quest: + _cogTracks = [Any, + 'c', + 'l', + 'm', + 's'] + _factoryTypes = [Any, + FT_FullSuit, + FT_Leg, + FT_Arm, + FT_Torso] + + def check(self, cond, msg): + pass + + def checkLocation(self, location): + locations = [Anywhere] + TTLocalizer.GlobalStreetNames.keys() + self.check(location in locations, 'invalid location: %s' % location) + + def checkNumCogs(self, num): + self.check(1, 'invalid number of cogs: %s' % num) + + def checkNewbieLevel(self, level): + self.check(1, 'invalid newbie level: %s' % level) + + def checkCogType(self, type): + types = [Any] + SuitBattleGlobals.SuitAttributes.keys() + self.check(type in types, 'invalid cog type: %s' % type) + + def checkCogTrack(self, track): + self.check(track in self._cogTracks, 'invalid cog track: %s' % track) + + def checkCogLevel(self, level): + self.check(level >= 1 and level <= 12, 'invalid cog level: %s' % level) + + def checkNumSkelecogs(self, num): + self.check(1, 'invalid number of cogs: %s' % num) + + def checkSkelecogTrack(self, track): + self.check(track in self._cogTracks, 'invalid cog track: %s' % track) + + def checkSkelecogLevel(self, level): + self.check(level >= 1 and level <= 12, 'invalid cog level: %s' % level) + + def checkNumSkeleRevives(self, num): + self.check(1, 'invalid number of cogs: %s' % num) + + def checkNumForemen(self, num): + self.check(num > 0, 'invalid number of foremen: %s' % num) + + def checkNumVPs(self, num): + self.check(num > 0, 'invalid number of VPs: %s' % num) + + def checkNumSupervisors(self, num): + self.check(num > 0, 'invalid number of supervisors: %s' % num) + + def checkNumCFOs(self, num): + self.check(num > 0, 'invalid number of CFOs: %s' % num) + + def checkNumBuildings(self, num): + self.check(1, 'invalid num buildings: %s' % num) + + def checkBuildingTrack(self, track): + self.check(track in self._cogTracks, 'invalid building track: %s' % track) + + def checkBuildingFloors(self, floors): + self.check(floors >= 1 and floors <= 5, 'invalid num floors: %s' % floors) + + def checkNumFactories(self, num): + self.check(1, 'invalid num factories: %s' % num) + + def checkFactoryType(self, type): + self.check(type in self._factoryTypes, 'invalid factory type: %s' % type) + + def checkNumMints(self, num): + self.check(1, 'invalid num mints: %s' % num) + + def checkNumCogParts(self, num): + self.check(1, 'invalid num cog parts: %s' % num) + + def checkNumGags(self, num): + self.check(1, 'invalid num gags: %s' % num) + + def checkGagTrack(self, track): + self.check(track >= ToontownBattleGlobals.MIN_TRACK_INDEX and track <= ToontownBattleGlobals.MAX_TRACK_INDEX, 'invalid gag track: %s' % track) + + def checkGagItem(self, item): + self.check(item >= ToontownBattleGlobals.MIN_LEVEL_INDEX and item <= ToontownBattleGlobals.MAX_LEVEL_INDEX, 'invalid gag item: %s' % item) + + def checkDeliveryItem(self, item): + self.check(ItemDict.has_key(item), 'invalid delivery item: %s' % item) + + def checkNumItems(self, num): + self.check(1, 'invalid num items: %s' % num) + + def checkRecoveryItem(self, item): + self.check(ItemDict.has_key(item), 'invalid recovery item: %s' % item) + + def checkPercentChance(self, chance): + self.check(chance > 0 and chance <= 100, 'invalid percent chance: %s' % chance) + + def checkRecoveryItemHolderAndType(self, holder, holderType = 'type'): + holderTypes = ['type', 'level', 'track'] + self.check(holderType in holderTypes, 'invalid recovery item holderType: %s' % holderType) + if holderType == 'type': + holders = [Any, AnyFish] + SuitBattleGlobals.SuitAttributes.keys() + self.check(holder in holders, 'invalid recovery item holder: %s for holderType: %s' % (holder, holderType)) + elif holderType == 'level': + pass + elif holderType == 'track': + self.check(holder in self._cogTracks, 'invalid recovery item holder: %s for holderType: %s' % (holder, holderType)) + + def checkTrackChoice(self, option): + self.check(option >= ToontownBattleGlobals.MIN_TRACK_INDEX and option <= ToontownBattleGlobals.MAX_TRACK_INDEX, 'invalid track option: %s' % option) + + def checkNumFriends(self, num): + self.check(1, 'invalid number of friends: %s' % num) + + def checkNumMinigames(self, num): + self.check(1, 'invalid number of minigames: %s' % num) + + def filterFunc(avatar): + return 1 + + filterFunc = staticmethod(filterFunc) + + def __init__(self, id, quest): + self.id = id + self.quest = quest + + def getId(self): + return self.id + + def getType(self): + return self.__class__ + + def getObjectiveStrings(self): + return [''] + + def getString(self): + return self.getObjectiveStrings()[0] + + def getRewardString(self, progressString): + return self.getString() + ' : ' + progressString + + def getChooseString(self): + return self.getString() + + def getPosterString(self): + return self.getString() + + def getHeadlineString(self): + return self.getString() + + def getDefaultQuestDialog(self): + return self.getString() + TTLocalizer.Period + + def getNumQuestItems(self): + return -1 + + def addArticle(self, num, oString): + if len(oString) == 0: + return oString + if num == 1: + return oString + else: + return '%d %s' % (num, oString) + + def __repr__(self): + return 'Quest type: %s id: %s params: %s' % (self.__class__.__name__, self.id, self.quest[0:]) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return 0 + + def doesVPCount(self, avId, cogDict, zoneId, avList): + return 0 + + def doesCFOCount(self, avId, cogDict, zoneId, avList): + return 0 + + def doesFactoryCount(self, avId, location, avList): + return 0 + + def doesMintCount(self, avId, location, avList): + return 0 + + def doesCogPartCount(self, avId, location, avList): + return 0 + + def getCompletionStatus(self, av, questDesc, npc = None): + notify.error('Pure virtual - please override me') + return None + + +class LocationBasedQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkLocation(self.quest[0]) + + def getLocation(self): + return self.quest[0] + + def getLocationName(self): + loc = self.getLocation() + if loc == Anywhere: + locName = '' + elif loc in ToontownGlobals.hoodNameMap: + locName = TTLocalizer.QuestInLocationString % {'inPhrase': ToontownGlobals.hoodNameMap[loc][1], + 'location': ToontownGlobals.hoodNameMap[loc][-1] + TTLocalizer.QuestsLocationArticle} + elif loc in ToontownGlobals.StreetBranchZones: + locName = TTLocalizer.QuestInLocationString % {'inPhrase': ToontownGlobals.StreetNames[loc][1], + 'location': ToontownGlobals.StreetNames[loc][-1] + TTLocalizer.QuestsLocationArticle} + return locName + + def isLocationMatch(self, zoneId): + loc = self.getLocation() + if loc is Anywhere: + return 1 + if ZoneUtil.isPlayground(loc): + if loc == ZoneUtil.getCanonicalHoodId(zoneId): + return 1 + else: + return 0 + elif loc == ZoneUtil.getCanonicalBranchZone(zoneId): + return 1 + elif loc == zoneId: + return 1 + else: + return 0 + + def getChooseString(self): + return TTLocalizer.QuestsLocationString % {'string': self.getString(), + 'location': self.getLocationName()} + + def getPosterString(self): + return TTLocalizer.QuestsLocationString % {'string': self.getString(), + 'location': self.getLocationName()} + + def getDefaultQuestDialog(self): + return (TTLocalizer.QuestsLocationString + TTLocalizer.Period) % {'string': self.getString(), + 'location': self.getLocationName()} + + +class NewbieQuest: + def getNewbieLevel(self): + notify.error('Pure virtual - please override me') + + def getString(self, newStr = TTLocalizer.QuestsCogNewNewbieQuestObjective, oldStr = TTLocalizer.QuestsCogOldNewbieQuestObjective): + laff = self.getNewbieLevel() + if laff <= NEWBIE_HP: + return newStr % self.getObjectiveStrings()[0] + else: + return oldStr % {'laffPoints': laff, + 'objective': self.getObjectiveStrings()[0]} + + def getCaption(self): + laff = self.getNewbieLevel() + if laff <= NEWBIE_HP: + return TTLocalizer.QuestsCogNewNewbieQuestCaption % laff + else: + return TTLocalizer.QuestsCogOldNewbieQuestCaption % laff + + def getNumNewbies(self, avId, avList): + newbieHp = self.getNewbieLevel() + num = 0 + for av in avList: + if av.getDoId() != avId and av.getMaxHp() <= newbieHp: + num += 1 + + return num + + +class CogQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + if self.__class__ == CogQuest: + self.checkNumCogs(self.quest[1]) + self.checkCogType(self.quest[2]) + + def getCogType(self): + return self.quest[2] + + def getNumQuestItems(self): + return self.getNumCogs() + + def getNumCogs(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumCogs() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumCogs() == 1: + return '' + else: + return TTLocalizer.QuestsCogQuestProgress % {'progress': questDesc[4], + 'numCogs': self.getNumCogs()} + + def getCogNameString(self): + numCogs = self.getNumCogs() + cogType = self.getCogType() + if numCogs == 1: + if cogType == Any: + return TTLocalizer.Cog + else: + return SuitBattleGlobals.SuitAttributes[cogType]['singularname'] + elif cogType == Any: + return TTLocalizer.Cogs + else: + return SuitBattleGlobals.SuitAttributes[cogType]['pluralname'] + + def getObjectiveStrings(self): + cogName = self.getCogNameString() + numCogs = self.getNumCogs() + if numCogs == 1: + text = cogName + else: + text = TTLocalizer.QuestsCogQuestDefeatDesc % {'numCogs': numCogs, + 'cogName': cogName} + return (text,) + + def getString(self): + return TTLocalizer.QuestsCogQuestDefeat % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumCogs(): + return getFinishToonTaskSCStrings(toNpcId) + cogName = self.getCogNameString() + numCogs = self.getNumCogs() + if numCogs == 1: + text = TTLocalizer.QuestsCogQuestSCStringS + else: + text = TTLocalizer.QuestsCogQuestSCStringP + cogLoc = self.getLocationName() + return text % {'cogName': cogName, + 'cogLoc': cogLoc} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId, avList): + questCogType = self.getCogType() + return (questCogType is Any or questCogType is cogDict['type']) and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class CogNewbieQuest(CogQuest, NewbieQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + if self.__class__ == CogNewbieQuest: + self.checkNumCogs(self.quest[1]) + self.checkCogType(self.quest[2]) + self.checkNewbieLevel(self.quest[3]) + + def getNewbieLevel(self): + return self.quest[3] + + def getString(self): + return NewbieQuest.getString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + if CogQuest.doesCogCount(self, avId, cogDict, zoneId, avList): + return self.getNumNewbies(avId, avList) + else: + return 0 + + +class CogTrackQuest(CogQuest): + trackCodes = ['c', + 'l', + 'm', + 's'] + trackNamesS = [TTLocalizer.BossbotS, + TTLocalizer.LawbotS, + TTLocalizer.CashbotS, + TTLocalizer.SellbotS] + trackNamesP = [TTLocalizer.BossbotP, + TTLocalizer.LawbotP, + TTLocalizer.CashbotP, + TTLocalizer.SellbotP] + + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + if self.__class__ == CogTrackQuest: + self.checkNumCogs(self.quest[1]) + self.checkCogTrack(self.quest[2]) + + def getCogTrack(self): + return self.quest[2] + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumCogs() == 1: + return '' + else: + return TTLocalizer.QuestsCogTrackQuestProgress % {'progress': questDesc[4], + 'numCogs': self.getNumCogs()} + + def getObjectiveStrings(self): + numCogs = self.getNumCogs() + track = self.trackCodes.index(self.getCogTrack()) + if numCogs == 1: + text = self.trackNamesS[track] + else: + text = TTLocalizer.QuestsCogTrackDefeatDesc % {'numCogs': numCogs, + 'trackName': self.trackNamesP[track]} + return (text,) + + def getString(self): + return TTLocalizer.QuestsCogTrackQuestDefeat % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumCogs(): + return getFinishToonTaskSCStrings(toNpcId) + numCogs = self.getNumCogs() + track = self.trackCodes.index(self.getCogTrack()) + if numCogs == 1: + cogText = self.trackNamesS[track] + text = TTLocalizer.QuestsCogTrackQuestSCStringS + else: + cogText = self.trackNamesP[track] + text = TTLocalizer.QuestsCogTrackQuestSCStringP + cogLocName = self.getLocationName() + return text % {'cogText': cogText, + 'cogLoc': cogLocName} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogTrackQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId, avList): + questCogTrack = self.getCogTrack() + return questCogTrack == cogDict['track'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class CogLevelQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumCogs(self.quest[1]) + self.checkCogLevel(self.quest[2]) + + def getCogType(self): + return Any + + def getCogLevel(self): + return self.quest[2] + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumCogs() == 1: + return '' + else: + return TTLocalizer.QuestsCogLevelQuestProgress % {'progress': questDesc[4], + 'numCogs': self.getNumCogs()} + + def getObjectiveStrings(self): + count = self.getNumCogs() + level = self.getCogLevel() + name = self.getCogNameString() + if count == 1: + text = TTLocalizer.QuestsCogLevelQuestDesc + else: + text = TTLocalizer.QuestsCogLevelQuestDescC + return (text % {'count': count, + 'level': level, + 'name': name},) + + def getString(self): + return TTLocalizer.QuestsCogLevelQuestDefeat % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumCogs(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumCogs() + level = self.getCogLevel() + name = self.getCogNameString() + if count == 1: + text = TTLocalizer.QuestsCogLevelQuestDesc + else: + text = TTLocalizer.QuestsCogLevelQuestDescI + objective = text % {'level': level, + 'name': name} + location = self.getLocationName() + return TTLocalizer.QuestsCogLevelQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogLevelQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId, avList): + questCogLevel = self.getCogLevel() + return questCogLevel <= cogDict['level'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class SkelecogQBase: + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.ASkeleton + else: + return TTLocalizer.SkeletonP + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return cogDict['isSkelecog'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class SkelecogQuest(CogQuest, SkelecogQBase): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumSkelecogs(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + return SkelecogQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return SkelecogQBase.doesCogCount(self, avId, cogDict, zoneId, avList) + + +class SkelecogNewbieQuest(SkelecogQuest, NewbieQuest): + def __init__(self, id, quest): + SkelecogQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + if SkelecogQuest.doesCogCount(self, avId, cogDict, zoneId, avList): + return self.getNumNewbies(avId, avList) + else: + return 0 + + +class SkelecogTrackQuest(CogTrackQuest, SkelecogQBase): + trackNamesS = [TTLocalizer.BossbotSkelS, + TTLocalizer.LawbotSkelS, + TTLocalizer.CashbotSkelS, + TTLocalizer.SellbotSkelS] + trackNamesP = [TTLocalizer.BossbotSkelP, + TTLocalizer.LawbotSkelP, + TTLocalizer.CashbotSkelP, + TTLocalizer.SellbotSkelP] + + def __init__(self, id, quest): + CogTrackQuest.__init__(self, id, quest) + self.checkNumSkelecogs(self.quest[1]) + self.checkSkelecogTrack(self.quest[2]) + + def getCogNameString(self): + return SkelecogQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return SkelecogQBase.doesCogCount(self, avId, cogDict, zoneId, avList) and self.getCogTrack() == cogDict['track'] + + +class SkelecogLevelQuest(CogLevelQuest, SkelecogQBase): + def __init__(self, id, quest): + CogLevelQuest.__init__(self, id, quest) + self.checkNumSkelecogs(self.quest[1]) + self.checkSkelecogLevel(self.quest[2]) + + def getCogType(self): + return Any + + def getCogNameString(self): + return SkelecogQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return SkelecogQBase.doesCogCount(self, avId, cogDict, zoneId, avList) and self.getCogLevel() <= cogDict['level'] + + +class SkeleReviveQBase: + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.Av2Cog + else: + return TTLocalizer.v2CogP + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return cogDict['hasRevives'] and avId in cogDict['activeToons'] and self.isLocationMatch(zoneId) + + +class SkeleReviveQuest(CogQuest, SkeleReviveQBase): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumSkeleRevives(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + return SkeleReviveQBase.getCogNameString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return SkeleReviveQBase.doesCogCount(self, avId, cogDict, zoneId, avList) + + +class ForemanQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumForemen(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.AForeman + else: + return TTLocalizer.ForemanP + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return bool(CogQuest.doesCogCount(self, avId, cogDict, zoneId, avList) and cogDict['isForeman']) + + +class ForemanNewbieQuest(ForemanQuest, NewbieQuest): + def __init__(self, id, quest): + ForemanQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + if ForemanQuest.doesCogCount(self, avId, cogDict, zoneId, avList): + return self.getNumNewbies(avId, avList) + else: + return 0 + + +class VPQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumVPs(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.ACogVP + else: + return TTLocalizer.CogVPs + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return 0 + + def doesVPCount(self, avId, cogDict, zoneId, avList): + return self.isLocationMatch(zoneId) + + +class VPNewbieQuest(VPQuest, NewbieQuest): + def __init__(self, id, quest): + VPQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self) + + def doesVPCount(self, avId, cogDict, zoneId, avList): + if VPQuest.doesVPCount(self, avId, cogDict, zoneId, avList): + return self.getNumNewbies(avId, avList) + else: + return 0 + + +class SupervisorQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumSupervisors(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.ASupervisor + else: + return TTLocalizer.SupervisorP + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return bool(CogQuest.doesCogCount(self, avId, cogDict, zoneId, avList) and cogDict['isSupervisor']) + + +class SupervisorNewbieQuest(SupervisorQuest, NewbieQuest): + def __init__(self, id, quest): + SupervisorQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self) + + def doesCogCount(self, avId, cogDict, zoneId, avList): + if SupervisorQuest.doesCogCount(self, avId, cogDict, zoneId, avList): + return self.getNumNewbies(avId, avList) + else: + return 0 + + +class CFOQuest(CogQuest): + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumCFOs(self.quest[1]) + + def getCogType(self): + return Any + + def getCogNameString(self): + numCogs = self.getNumCogs() + if numCogs == 1: + return TTLocalizer.ACogCFO + else: + return TTLocalizer.CogCFOs + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return 0 + + def doesCFOCount(self, avId, cogDict, zoneId, avList): + return self.isLocationMatch(zoneId) + + +class CFONewbieQuest(CFOQuest, NewbieQuest): + def __init__(self, id, quest): + CFOQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self) + + def doesCFOCount(self, avId, cogDict, zoneId, avList): + if CFOQuest.doesCFOCount(self, avId, cogDict, zoneId, avList): + return self.getNumNewbies(avId, avList) + else: + return 0 + + +class RescueQuest(VPQuest): + def __init__(self, id, quest): + VPQuest.__init__(self, id, quest) + + def getNumToons(self): + return self.getNumCogs() + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumToons() == 1: + return '' + else: + return TTLocalizer.QuestsRescueQuestProgress % {'progress': questDesc[4], + 'numToons': self.getNumToons()} + + def getObjectiveStrings(self): + numToons = self.getNumCogs() + if numToons == 1: + text = TTLocalizer.QuestsRescueQuestToonS + else: + text = TTLocalizer.QuestsRescueQuestRescueDesc % {'numToons': numToons} + return (text,) + + def getString(self): + return TTLocalizer.QuestsRescueQuestRescue % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumToons(): + return getFinishToonTaskSCStrings(toNpcId) + numToons = self.getNumToons() + if numToons == 1: + text = TTLocalizer.QuestsRescueQuestSCStringS + else: + text = TTLocalizer.QuestsRescueQuestSCStringP + toonLoc = self.getLocationName() + return text % {'toonLoc': toonLoc} + + def getHeadlineString(self): + return TTLocalizer.QuestsRescueQuestHeadline + + +class RescueNewbieQuest(RescueQuest, NewbieQuest): + def __init__(self, id, quest): + RescueQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self, newStr=TTLocalizer.QuestsRescueNewNewbieQuestObjective, oldStr=TTLocalizer.QuestsRescueOldNewbieQuestObjective) + + def doesVPCount(self, avId, cogDict, zoneId, avList): + if RescueQuest.doesVPCount(self, avId, cogDict, zoneId, avList): + return self.getNumNewbies(avId, avList) + else: + return 0 + + +class BuildingQuest(CogQuest): + trackCodes = ['c', + 'l', + 'm', + 's'] + trackNames = [TTLocalizer.Bossbot, + TTLocalizer.Lawbot, + TTLocalizer.Cashbot, + TTLocalizer.Sellbot] + + def __init__(self, id, quest): + CogQuest.__init__(self, id, quest) + self.checkNumBuildings(self.quest[1]) + self.checkBuildingTrack(self.quest[2]) + self.checkBuildingFloors(self.quest[3]) + + def getNumFloors(self): + return self.quest[3] + + def getBuildingTrack(self): + return self.quest[2] + + def getNumQuestItems(self): + return self.getNumBuildings() + + def getNumBuildings(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumBuildings() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumBuildings() == 1: + return '' + else: + return TTLocalizer.QuestsBuildingQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumBuildings()} + + def getObjectiveStrings(self): + count = self.getNumBuildings() + floors = TTLocalizer.QuestsBuildingQuestFloorNumbers[self.getNumFloors() - 1] + buildingTrack = self.getBuildingTrack() + if buildingTrack == Any: + type = TTLocalizer.Cog + else: + type = self.trackNames[self.trackCodes.index(buildingTrack)] + if count == 1: + if floors == '': + text = TTLocalizer.QuestsBuildingQuestDesc + else: + text = TTLocalizer.QuestsBuildingQuestDescF + elif floors == '': + text = TTLocalizer.QuestsBuildingQuestDescC + else: + text = TTLocalizer.QuestsBuildingQuestDescCF + return (text % {'count': count, + 'floors': floors, + 'type': type},) + + def getString(self): + return TTLocalizer.QuestsBuildingQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumBuildings(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumBuildings() + floors = TTLocalizer.QuestsBuildingQuestFloorNumbers[self.getNumFloors() - 1] + buildingTrack = self.getBuildingTrack() + if buildingTrack == Any: + type = TTLocalizer.Cog + else: + type = self.trackNames[self.trackCodes.index(buildingTrack)] + if count == 1: + if floors == '': + text = TTLocalizer.QuestsBuildingQuestDesc + else: + text = TTLocalizer.QuestsBuildingQuestDescF + elif floors == '': + text = TTLocalizer.QuestsBuildingQuestDescI + else: + text = TTLocalizer.QuestsBuildingQuestDescIF + objective = text % {'floors': floors, + 'type': type} + location = self.getLocationName() + return TTLocalizer.QuestsBuildingQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsBuildingQuestHeadline + + def doesCogCount(self, avId, cogDict, zoneId, avList): + return 0 + + def doesBuildingCount(self, avId, avList): + return 1 + + +class BuildingNewbieQuest(BuildingQuest, NewbieQuest): + def __init__(self, id, quest): + BuildingQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[4]) + + def getNewbieLevel(self): + return self.quest[4] + + def getString(self): + return NewbieQuest.getString(self) + + def getHeadlineString(self): + return TTLocalizer.QuestsNewbieQuestHeadline + + def doesBuildingCount(self, avId, avList): + return self.getNumNewbies(avId, avList) + + +class FactoryQuest(LocationBasedQuest): + factoryTypeNames = {FT_FullSuit: TTLocalizer.Cog, + FT_Leg: TTLocalizer.FactoryTypeLeg, + FT_Arm: TTLocalizer.FactoryTypeArm, + FT_Torso: TTLocalizer.FactoryTypeTorso} + + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumFactories(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumFactories() + + def getNumFactories(self): + return self.quest[1] + + def getFactoryType(self): + loc = self.getLocation() + type = Any + if loc in ToontownGlobals.factoryId2factoryType: + type = ToontownGlobals.factoryId2factoryType[loc] + return type + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumFactories() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumFactories() == 1: + return '' + else: + return TTLocalizer.QuestsFactoryQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumFactories()} + + def getObjectiveStrings(self): + count = self.getNumFactories() + factoryType = self.getFactoryType() + if factoryType == Any: + type = TTLocalizer.Cog + else: + type = FactoryQuest.factoryTypeNames[factoryType] + if count == 1: + text = TTLocalizer.QuestsFactoryQuestDesc + else: + text = TTLocalizer.QuestsFactoryQuestDescC + return (text % {'count': count, + 'type': type},) + + def getString(self): + return TTLocalizer.QuestsFactoryQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumFactories(): + return getFinishToonTaskSCStrings(toNpcId) + factoryType = self.getFactoryType() + if factoryType == Any: + type = TTLocalizer.Cog + else: + type = FactoryQuest.factoryTypeNames[factoryType] + count = self.getNumFactories() + if count == 1: + text = TTLocalizer.QuestsFactoryQuestDesc + else: + text = TTLocalizer.QuestsFactoryQuestDescI + objective = text % {'type': type} + location = self.getLocationName() + return TTLocalizer.QuestsFactoryQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsFactoryQuestHeadline + + def doesFactoryCount(self, avId, location, avList): + return self.isLocationMatch(location) + + +class FactoryNewbieQuest(FactoryQuest, NewbieQuest): + def __init__(self, id, quest): + FactoryQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self) + + def getHeadlineString(self): + return TTLocalizer.QuestsNewbieQuestHeadline + + def doesFactoryCount(self, avId, location, avList): + if FactoryQuest.doesFactoryCount(self, avId, location, avList): + return self.getNumNewbies(avId, avList) + else: + return num + + +class MintQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumMints(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumMints() + + def getNumMints(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumMints() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumMints() == 1: + return '' + else: + return TTLocalizer.QuestsMintQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumMints()} + + def getObjectiveStrings(self): + count = self.getNumMints() + if count == 1: + text = TTLocalizer.QuestsMintQuestDesc + else: + text = TTLocalizer.QuestsMintQuestDescC % {'count': count} + return (text,) + + def getString(self): + return TTLocalizer.QuestsMintQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumMints(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumMints() + if count == 1: + objective = TTLocalizer.QuestsMintQuestDesc + else: + objective = TTLocalizer.QuestsMintQuestDescI + location = self.getLocationName() + return TTLocalizer.QuestsMintQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsMintQuestHeadline + + def doesMintCount(self, avId, location, avList): + return self.isLocationMatch(location) + + +class MintNewbieQuest(MintQuest, NewbieQuest): + def __init__(self, id, quest): + MintQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self) + + def getHeadlineString(self): + return TTLocalizer.QuestsNewbieQuestHeadline + + def doesMintCount(self, avId, location, avList): + if MintQuest.doesMintCount(self, avId, location, avList): + return self.getNumNewbies(avId, avList) + else: + return num + + +class CogPartQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumCogParts(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumParts() + + def getNumParts(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumParts() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumParts() == 1: + return '' + else: + return TTLocalizer.QuestsCogPartQuestProgressString % {'progress': questDesc[4], + 'num': self.getNumParts()} + + def getObjectiveStrings(self): + count = self.getNumParts() + if count == 1: + text = TTLocalizer.QuestsCogPartQuestDesc + else: + text = TTLocalizer.QuestsCogPartQuestDescC + return (text % {'count': count},) + + def getString(self): + return TTLocalizer.QuestsCogPartQuestString % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumParts(): + return getFinishToonTaskSCStrings(toNpcId) + count = self.getNumParts() + if count == 1: + text = TTLocalizer.QuestsCogPartQuestDesc + else: + text = TTLocalizer.QuestsCogPartQuestDescI + objective = text + location = self.getLocationName() + return TTLocalizer.QuestsCogPartQuestSCString % {'objective': objective, + 'location': location} + + def getHeadlineString(self): + return TTLocalizer.QuestsCogPartQuestHeadline + + def doesCogPartCount(self, avId, location, avList): + return self.isLocationMatch(location) + + +class CogPartNewbieQuest(CogPartQuest, NewbieQuest): + def __init__(self, id, quest): + CogPartQuest.__init__(self, id, quest) + self.checkNewbieLevel(self.quest[2]) + + def getNewbieLevel(self): + return self.quest[2] + + def getString(self): + return NewbieQuest.getString(self, newStr=TTLocalizer.QuestsCogPartNewNewbieQuestObjective, oldStr=TTLocalizer.QuestsCogPartOldNewbieQuestObjective) + + def getHeadlineString(self): + return TTLocalizer.QuestsNewbieQuestHeadline + + def doesCogPartCount(self, avId, location, avList): + if CogPartQuest.doesCogPartCount(self, avId, location, avList): + return self.getNumNewbies(avId, avList) + else: + return num + + +class DeliverGagQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkNumGags(self.quest[0]) + self.checkGagTrack(self.quest[1]) + self.checkGagItem(self.quest[2]) + + def getGagType(self): + return (self.quest[1], self.quest[2]) + + def getNumQuestItems(self): + return self.getNumGags() + + def getNumGags(self): + return self.quest[0] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + gag = self.getGagType() + num = self.getNumGags() + track = gag[0] + level = gag[1] + questComplete = npc and av.inventory and av.inventory.numItem(track, level) >= num + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumGags() == 1: + return '' + else: + return TTLocalizer.QuestsDeliverGagQuestProgress % {'progress': questDesc[4], + 'numGags': self.getNumGags()} + + def getObjectiveStrings(self): + track, item = self.getGagType() + num = self.getNumGags() + if num == 1: + text = ToontownBattleGlobals.AvPropStringsSingular[track][item] + else: + gagName = ToontownBattleGlobals.AvPropStringsPlural[track][item] + text = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': gagName} + return (text,) + + def getString(self): + return TTLocalizer.QuestsDeliverGagQuestString % self.getObjectiveStrings()[0] + + def getRewardString(self, progress): + return TTLocalizer.QuestsDeliverGagQuestStringLong % self.getObjectiveStrings()[0] + + def getDefaultQuestDialog(self): + return TTLocalizer.QuestsDeliverGagQuestStringLong % self.getObjectiveStrings()[0] + '\x07' + TTLocalizer.QuestsDeliverGagQuestInstructions + + def getSCStrings(self, toNpcId, progress): + if progress >= self.getNumGags(): + return getFinishToonTaskSCStrings(toNpcId) + track, item = self.getGagType() + num = self.getNumGags() + if num == 1: + text = TTLocalizer.QuestsDeliverGagQuestToSCStringS + gagName = ToontownBattleGlobals.AvPropStringsSingular[track][item] + else: + text = TTLocalizer.QuestsDeliverGagQuestToSCStringP + gagName = ToontownBattleGlobals.AvPropStringsPlural[track][item] + return [text % {'gagName': gagName}, TTLocalizer.QuestsDeliverGagQuestSCString] + getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsDeliverGagQuestHeadline + + +class DeliverItemQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkDeliveryItem(self.quest[0]) + + def getItem(self): + return self.quest[0] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + if npc and npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + + def getProgressString(self, avatar, questDesc): + return TTLocalizer.QuestsDeliverItemQuestProgress + + def getObjectiveStrings(self): + iDict = ItemDict[self.getItem()] + article = iDict[2] + itemName = iDict[0] + return [article + itemName] + + def getString(self): + return TTLocalizer.QuestsDeliverItemQuestString % self.getObjectiveStrings()[0] + + def getRewardString(self, progress): + return TTLocalizer.QuestsDeliverItemQuestStringLong % self.getObjectiveStrings()[0] + + def getDefaultQuestDialog(self): + return TTLocalizer.QuestsDeliverItemQuestStringLong % self.getObjectiveStrings()[0] + + def getSCStrings(self, toNpcId, progress): + iDict = ItemDict[self.getItem()] + article = iDict[2] + itemName = iDict[0] + return [TTLocalizer.QuestsDeliverItemQuestSCString % {'article': article, + 'itemName': itemName}] + getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsDeliverItemQuestHeadline + + +class VisitQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + if npc and npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + + def getProgressString(self, avatar, questDesc): + return TTLocalizer.QuestsVisitQuestProgress + + def getObjectiveStrings(self): + return [''] + + def getString(self): + return TTLocalizer.QuestsVisitQuestStringShort + + def getChooseString(self): + return TTLocalizer.QuestsVisitQuestStringLong + + def getRewardString(self, progress): + return TTLocalizer.QuestsVisitQuestStringLong + + def getDefaultQuestDialog(self): + return random.choice(DefaultVisitQuestDialog) + + def getSCStrings(self, toNpcId, progress): + return getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsVisitQuestHeadline + + +class RecoverItemQuest(LocationBasedQuest): + def __init__(self, id, quest): + LocationBasedQuest.__init__(self, id, quest) + self.checkNumItems(self.quest[1]) + self.checkRecoveryItem(self.quest[2]) + self.checkPercentChance(self.quest[3]) + if len(self.quest) > 5: + self.checkRecoveryItemHolderAndType(self.quest[4], self.quest[5]) + else: + self.checkRecoveryItemHolderAndType(self.quest[4]) + + def testRecover(self, progress): + test = random.random() * 100 + chance = self.getPercentChance() + numberDone = progress & pow(2, 16) - 1 + numberNotDone = progress >> 16 + returnTest = None + avgNum2Kill = 1.0 / (chance / 100.0) + if numberNotDone >= avgNum2Kill * 1.5: + chance = 100 + elif numberNotDone > avgNum2Kill * 0.5: + diff = float(numberNotDone - avgNum2Kill * 0.5) + luck = 1.0 + abs(diff / (avgNum2Kill * 0.5)) + chance *= luck + if test <= chance: + returnTest = 1 + numberNotDone = 0 + numberDone += 1 + else: + returnTest = 0 + numberNotDone += 1 + numberDone += 0 + returnCount = numberNotDone << 16 + returnCount += numberDone + return (returnTest, returnCount) + + def testDone(self, progress): + numberDone = progress & pow(2, 16) - 1 + print 'Quest number done %s' % numberDone + if numberDone >= self.getNumItems(): + return 1 + else: + return 0 + + def getNumQuestItems(self): + return self.getNumItems() + + def getNumItems(self): + return self.quest[1] + + def getItem(self): + return self.quest[2] + + def getPercentChance(self): + return self.quest[3] + + def getHolder(self): + return self.quest[4] + + def getHolderType(self): + if len(self.quest) == 5: + return 'type' + else: + return self.quest[5] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + forwardProgress = toonProgress & pow(2, 16) - 1 + questComplete = forwardProgress >= self.getNumItems() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumItems() == 1: + return '' + else: + progress = questDesc[4] & pow(2, 16) - 1 + return TTLocalizer.QuestsRecoverItemQuestProgress % {'progress': progress, + 'numItems': self.getNumItems()} + + def getObjectiveStrings(self): + holder = self.getHolder() + holderType = self.getHolderType() + if holder == Any: + holderName = TTLocalizer.TheCogs + elif holder == AnyFish: + holderName = TTLocalizer.AFish + elif holderType == 'type': + holderName = SuitBattleGlobals.SuitAttributes[holder]['pluralname'] + elif holderType == 'level': + holderName = TTLocalizer.QuestsRecoverItemQuestHolderString % {'level': TTLocalizer.Level, + 'holder': holder, + 'cogs': TTLocalizer.Cogs} + elif holderType == 'track': + if holder == 'c': + holderName = TTLocalizer.BossbotP + elif holder == 's': + holderName = TTLocalizer.SellbotP + elif holder == 'm': + holderName = TTLocalizer.CashbotP + elif holder == 'l': + holderName = TTLocalizer.LawbotP + item = self.getItem() + num = self.getNumItems() + if num == 1: + itemName = ItemDict[item][2] + ItemDict[item][0] + else: + itemName = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': ItemDict[item][1]} + return [itemName, holderName] + + def getString(self): + return TTLocalizer.QuestsRecoverItemQuestString % {'item': self.getObjectiveStrings()[0], + 'holder': self.getObjectiveStrings()[1]} + + def getSCStrings(self, toNpcId, progress): + item = self.getItem() + num = self.getNumItems() + forwardProgress = progress & pow(2, 16) - 1 + if forwardProgress >= self.getNumItems(): + if num == 1: + itemName = ItemDict[item][2] + ItemDict[item][0] + else: + itemName = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': ItemDict[item][1]} + if toNpcId == ToonHQ: + strings = [TTLocalizer.QuestsRecoverItemQuestReturnToHQSCString % itemName, TTLocalizer.QuestsRecoverItemQuestGoToHQSCString] + elif toNpcId: + npcName, hoodName, buildingArticle, buildingName, toStreet, streetName, isInPlayground = getNpcInfo(toNpcId) + strings = [TTLocalizer.QuestsRecoverItemQuestReturnToSCString % {'item': itemName, + 'npcName': npcName}] + if isInPlayground: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToPlaygroundSCString % hoodName) + else: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToStreetSCString % {'to': toStreet, + 'street': streetName, + 'hood': hoodName}) + strings.extend([TTLocalizer.QuestsRecoverItemQuestVisitBuildingSCString % (buildingArticle, buildingName), TTLocalizer.QuestsRecoverItemQuestWhereIsBuildingSCString % (buildingArticle, buildingName)]) + return strings + holder = self.getHolder() + holderType = self.getHolderType() + locName = self.getLocationName() + if holder == Any: + holderName = TTLocalizer.TheCogs + elif holder == AnyFish: + holderName = TTLocalizer.TheFish + elif holderType == 'type': + holderName = SuitBattleGlobals.SuitAttributes[holder]['pluralname'] + elif holderType == 'level': + holderName = TTLocalizer.QuestsRecoverItemQuestHolderString % {'level': TTLocalizer.Level, + 'holder': holder, + 'cogs': TTLocalizer.Cogs} + elif holderType == 'track': + if holder == 'c': + holderName = TTLocalizer.BossbotP + elif holder == 's': + holderName = TTLocalizer.SellbotP + elif holder == 'm': + holderName = TTLocalizer.CashbotP + elif holder == 'l': + holderName = TTLocalizer.LawbotP + if num == 1: + itemName = ItemDict[item][2] + ItemDict[item][0] + else: + itemName = TTLocalizer.QuestsItemNameAndNum % {'num': TTLocalizer.getLocalNum(num), + 'name': ItemDict[item][1]} + return TTLocalizer.QuestsRecoverItemQuestRecoverFromSCString % {'item': itemName, + 'holder': holderName, + 'loc': locName} + + def getHeadlineString(self): + return TTLocalizer.QuestsRecoverItemQuestHeadline + + +class TrackChoiceQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkTrackChoice(self.quest[0]) + self.checkTrackChoice(self.quest[1]) + + def getChoices(self): + return (self.quest[0], self.quest[1]) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + if npc and npcMatches(toNpcId, npc): + return COMPLETE + else: + return INCOMPLETE_WRONG_NPC + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return NotChosenString + + def getObjectiveStrings(self): + trackA, trackB = self.getChoices() + trackAName = ToontownBattleGlobals.Tracks[trackA].capitalize() + trackBName = ToontownBattleGlobals.Tracks[trackB].capitalize() + return [trackAName, trackBName] + + def getString(self): + return TTLocalizer.QuestsTrackChoiceQuestString % {'trackA': self.getObjectiveStrings()[0], + 'trackB': self.getObjectiveStrings()[1]} + + def getSCStrings(self, toNpcId, progress): + trackA, trackB = self.getChoices() + trackAName = ToontownBattleGlobals.Tracks[trackA].capitalize() + trackBName = ToontownBattleGlobals.Tracks[trackB].capitalize() + return [TTLocalizer.QuestsTrackChoiceQuestSCString % {'trackA': trackAName, + 'trackB': trackBName}, TTLocalizer.QuestsTrackChoiceQuestMaybeSCString % trackAName, TTLocalizer.QuestsTrackChoiceQuestMaybeSCString % trackBName] + getVisitSCStrings(toNpcId) + + def getHeadlineString(self): + return TTLocalizer.QuestsTrackChoiceQuestHeadline + + +class FriendQuest(Quest): + def filterFunc(avatar): + if len(avatar.getFriendsList()) == 0: + return 1 + else: + return 0 + + filterFunc = staticmethod(filterFunc) + + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 or len(av.getFriendsList()) > 0 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsFriendQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsFriendQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsFriendQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsFriendQuestString] + + def doesFriendCount(self, av, otherAv): + return 1 + + +class FriendNewbieQuest(FriendQuest, NewbieQuest): + def filterFunc(avatar): + return 1 + + filterFunc = staticmethod(filterFunc) + + def __init__(self, id, quest): + FriendQuest.__init__(self, id, quest) + self.checkNumFriends(self.quest[0]) + self.checkNewbieLevel(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumFriends() + + def getNumFriends(self): + return self.quest[0] + + def getNewbieLevel(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumFriends() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumFriends() == 1: + return '' + else: + return TTLocalizer.QuestsFriendNewbieQuestProgress % {'progress': questDesc[4], + 'numFriends': self.getNumFriends()} + + def getString(self): + return TTLocalizer.QuestsFriendNewbieQuestObjective % self.getNumFriends() + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsFriendNewbieQuestString % (self.getNumFriends(), self.getNewbieLevel())] + + def doesFriendCount(self, av, otherAv): + if otherAv != None and otherAv.getMaxHp() <= self.getNewbieLevel(): + return 1 + return 0 + + +class TrolleyQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsFriendQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsTrolleyQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsTrolleyQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsTrolleyQuestString] + + +class MailboxQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsMailboxQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsMailboxQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsMailboxQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsMailboxQuestString] + + +class PhoneQuest(Quest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= 1 + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + else: + return '' + + def getString(self): + return TTLocalizer.QuestsPhoneQuestString + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsPhoneQuestSCString + + def getHeadlineString(self): + return TTLocalizer.QuestsPhoneQuestHeadline + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsPhoneQuestString] + + +class MinigameNewbieQuest(Quest, NewbieQuest): + def __init__(self, id, quest): + Quest.__init__(self, id, quest) + self.checkNumMinigames(self.quest[0]) + self.checkNewbieLevel(self.quest[1]) + + def getNumQuestItems(self): + return self.getNumMinigames() + + def getNumMinigames(self): + return self.quest[0] + + def getNewbieLevel(self): + return self.quest[1] + + def getCompletionStatus(self, av, questDesc, npc = None): + questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc + questComplete = toonProgress >= self.getNumMinigames() + return getCompleteStatusWithNpc(questComplete, toNpcId, npc) + + def getProgressString(self, avatar, questDesc): + if self.getCompletionStatus(avatar, questDesc) == COMPLETE: + return CompleteString + elif self.getNumMinigames() == 1: + return '' + else: + return TTLocalizer.QuestsMinigameNewbieQuestProgress % {'progress': questDesc[4], + 'numMinigames': self.getNumMinigames()} + + def getString(self): + return TTLocalizer.QuestsMinigameNewbieQuestObjective % self.getNumMinigames() + + def getObjectiveStrings(self): + return [TTLocalizer.QuestsMinigameNewbieQuestString % self.getNumMinigames()] + + def getHeadlineString(self): + return TTLocalizer.QuestsNewbieQuestHeadline + + def getSCStrings(self, toNpcId, progress): + if progress: + return getFinishToonTaskSCStrings(toNpcId) + return TTLocalizer.QuestsTrolleyQuestSCString + + def doesMinigameCount(self, av, avList): + newbieHp = self.getNewbieLevel() + points = 0 + for toon in avList: + if toon != av and toon.getMaxHp() <= newbieHp: + points += 1 + + return points + + +DefaultDialog = {GREETING: DefaultGreeting, + QUEST: DefaultQuest, + INCOMPLETE: DefaultIncomplete, + INCOMPLETE_PROGRESS: DefaultIncompleteProgress, + INCOMPLETE_WRONG_NPC: DefaultIncompleteWrongNPC, + COMPLETE: DefaultComplete, + LEAVING: DefaultLeaving} + +def getQuestFromNpcId(id): + return QuestDict.get(id)[QuestDictFromNpcIndex] + + +def getQuestToNpcId(id): + return QuestDict.get(id)[QuestDictToNpcIndex] + + +def getQuestDialog(id): + return QuestDict.get(id)[QuestDictDialogIndex] + + +def getQuestReward(id, av): + baseRewardId = QuestDict.get(id)[QuestDictRewardIndex] + return transformReward(baseRewardId, av) + + +def isQuestJustForFun(questId, rewardId): + questEntry = QuestDict.get(questId) + if questEntry: + tier = questEntry[QuestDictTierIndex] + return isRewardOptional(tier, rewardId) + else: + return False + + +NoRewardTierZeroQuests = (101, + 110, + 121, + 131, + 141, + 145, + 150, + 160, + 161, + 162, + 163) +RewardTierZeroQuests = () +PreClarabelleQuestIds = NoRewardTierZeroQuests + RewardTierZeroQuests +QuestDict = { + 101: (TT_TIER, + Start, + (CogQuest, + Anywhere, + 1, + 'f'), + Any, + ToonHQ, + NA, + 110, + DefaultDialog), + 110: (TT_TIER, + Cont, + (TrolleyQuest,), + Any, + ToonHQ, + NA, + 145, + DefaultDialog), + 120: (TT_TIER, + OBSOLETE, + (DeliverItemQuest, 5), + ToonHQ, + 2002, + NA, + 121, + DefaultDialog), + 121: (TT_TIER, + OBSOLETE, + (RecoverItemQuest, + ToontownGlobals.ToontownCentral, + 1, + 2, + VeryEasy, + Any, + 'type'), + 2002, + 2002, + NA, + 150, + DefaultDialog), + 130: (TT_TIER, + OBSOLETE, + (DeliverItemQuest, 6), + ToonHQ, + 2003, + NA, + 131, + DefaultDialog), + 131: (TT_TIER, + OBSOLETE, + (RecoverItemQuest, + ToontownGlobals.ToontownCentral, + 1, + 3, + VeryEasy, + Any, + 'type'), + 2003, + 2003, + NA, + 150, + DefaultDialog), + 140: (TT_TIER, + OBSOLETE, + (DeliverItemQuest, 4), + ToonHQ, + 2005, + NA, + 141, + DefaultDialog), + 141: (TT_TIER, + OBSOLETE, + (RecoverItemQuest, + ToontownGlobals.ToontownCentral, + 1, + 1, + VeryEasy, + Any, + 'type'), + 2005, + 2005, + NA, + 150, + DefaultDialog), + 145: (TT_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.ToontownCentral, + 1, + 20, + VeryEasy, + Any, + 'type'), + ToonHQ, + ToonHQ, + NA, + 150, + DefaultDialog), + 150: (TT_TIER, + Cont, + (FriendQuest,), + Same, + Same, + NA, + 175, + DefaultDialog), + 160: (TT_TIER, + OBSOLETE, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 'c'), + Same, + ToonHQ, + NA, + 175, + TTLocalizer.QuestDialogDict[160]), + 161: (TT_TIER, + OBSOLETE, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 'l'), + Same, + ToonHQ, + NA, + 175, + TTLocalizer.QuestDialogDict[161]), + 162: (TT_TIER, + OBSOLETE, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 's'), + Same, + ToonHQ, + NA, + 175, + TTLocalizer.QuestDialogDict[162]), + 163: (TT_TIER, + OBSOLETE, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 'm'), + Same, + ToonHQ, + NA, + 175, + TTLocalizer.QuestDialogDict[163]), + 175: (TT_TIER, + Cont, + (PhoneQuest,), + Same, + ToonHQ, + 100, + NA, + TTLocalizer.QuestDialogDict[175]), + 164: (TT_TIER + 1, + Start, + (VisitQuest,), + Any, + 2001, + NA, + 165, + TTLocalizer.QuestDialogDict[164]), + 165: (TT_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + Any), + 2001, + Same, + NA, + (166, + 167, + 168, + 169), + TTLocalizer.QuestDialogDict[165]), + 166: (TT_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 4, + 'c'), + Same, + Same, + NA, + (170, 171, 172), + TTLocalizer.QuestDialogDict[166]), + 167: (TT_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 4, + 'l'), + Same, + Same, + NA, + (170, 171, 172), + TTLocalizer.QuestDialogDict[167]), + 168: (TT_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 4, + 's'), + Same, + Same, + NA, + (170, 171, 172), + TTLocalizer.QuestDialogDict[168]), + 169: (TT_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 4, + 'm'), + Same, + Same, + NA, + (170, 171, 172), + TTLocalizer.QuestDialogDict[169]), + 170: (TT_TIER + 1, + Cont, + (VisitQuest,), + Same, + 2005, + NA, + 400, + TTLocalizer.QuestDialogDict[170]), + 171: (TT_TIER + 1, + Cont, + (VisitQuest,), + Same, + 2311, + NA, + 400, + TTLocalizer.QuestDialogDict[171]), + 172: (TT_TIER + 1, + Cont, + (VisitQuest,), + Same, + 2119, + NA, + 400, + TTLocalizer.QuestDialogDict[172]), + 400: (TT_TIER + 1, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.SOUND_TRACK, ToontownBattleGlobals.HEAL_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.QuestDialogDict[400]), + 1001: (TT_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.ToontownCentral, + 3, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1002: (TT_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.ToontownCentral, + 4, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1003: (TT_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.ToontownCentral, + 5, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1004: (TT_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.ToontownCentral, + 6, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1005: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'f'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1006: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'p'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1007: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'bf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1008: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1009: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'sc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1010: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'pp'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1011: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'cc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1012: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'tm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1013: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 4, + 'f'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1014: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 4, + 'p'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1015: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 4, + 'bf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1016: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 4, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1017: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 1, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1018: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 1, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1019: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 1, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1020: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 1, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1021: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 2, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1022: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 6, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1023: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 3, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1024: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 4, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1025: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 4, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1026: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 6, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1027: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 2, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1028: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 2, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1029: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 2, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1030: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 2, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1031: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1032: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1033: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1034: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 3, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1035: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 5, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1036: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 5, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1037: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 5, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1038: (TT_TIER + 2, + Start, + (CogTrackQuest, + ToontownGlobals.ToontownCentral, + 5, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1039: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2135, + NA, + (1041, 1042, 1043), + TTLocalizer.QuestDialogDict[1039]), + 1040: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2207, + NA, + (1041, 1042, 1043), + TTLocalizer.QuestDialogDict[1040]), + 1041: (TT_TIER + 2, + Cont, + (VisitQuest,), + Same, + 2211, + NA, + 1044, + TTLocalizer.QuestDialogDict[1041]), + 1042: (TT_TIER + 2, + Cont, + (VisitQuest,), + Same, + 2209, + NA, + 1044, + TTLocalizer.QuestDialogDict[1042]), + 1043: (TT_TIER + 2, + Cont, + (VisitQuest,), + Same, + 2210, + NA, + 1044, + TTLocalizer.QuestDialogDict[1043]), + 1044: (TT_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 4, + 7, + VeryEasy, + Any, + 'type'), + Same, + Same, + NA, + 1045, + TTLocalizer.QuestDialogDict[1044]), + 1045: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 8), + Same, + ToonHQ, + 300, + NA, + TTLocalizer.QuestDialogDict[1045]), + 1046: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2127, + NA, + 1047, + TTLocalizer.QuestDialogDict[1046]), + 1047: (TT_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 5, + 9, + VeryEasy, + 'm', + 'track'), + 2127, + Same, + NA, + 1048, + TTLocalizer.QuestDialogDict[1047]), + 1048: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 9), + Same, + 2131, + NA, + 1049, + TTLocalizer.QuestDialogDict[1048]), + 1049: (TT_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 10, + 2007, + VeryEasy, + 3, + 'level'), + Same, + Same, + NA, + 1053, + TTLocalizer.QuestDialogDict[1049]), + 1053: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 9), + Same, + 2127, + 700, + NA, + TTLocalizer.QuestDialogDict[1053]), + 1054: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2128, + NA, + 1055, + TTLocalizer.QuestDialogDict[1054]), + 1055: (TT_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 4, + 10, + Easy, + AnyFish), + 2128, + Same, + NA, + 1056, + TTLocalizer.QuestDialogDict[1055]), + 1056: (TT_TIER + 2, + Cont, + (VisitQuest,), + Same, + 2213, + NA, + 1057, + TTLocalizer.QuestDialogDict[1056]), + 1057: (TT_TIER + 2, + Cont, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 6, + 3), + Same, + Same, + NA, + 1058, + TTLocalizer.QuestDialogDict[1057]), + 1058: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 11), + Same, + 2128, + 200, + NA, + TTLocalizer.QuestDialogDict[1058]), + 1059: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2302, + NA, + 1060, + TTLocalizer.QuestDialogDict[1059]), + 1060: (TT_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 12, + Medium, + AnyFish), + 2302, + Same, + NA, + 1062, + TTLocalizer.QuestDialogDict[1060]), + 1061: (TT_TIER + 2, + Cont, + (CogQuest, + ToontownGlobals.ToontownCentral, + 6, + 'p'), + Same, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[1061]), + 1062: (TT_TIER + 2, + Cont, + (CogQuest, + ToontownGlobals.ToontownCentral, + 6, + 'b'), + Same, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[1062]), + 900: (TT_TIER + 3, + Start, + (VisitQuest,), + Any, + 2201, + NA, + 1063, + TTLocalizer.QuestDialogDict[900]), + 1063: (TT_TIER + 3, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 13, + Medium, + 3, + 'level'), + 2201, + Same, + NA, + 1067, + TTLocalizer.QuestDialogDict[1063]), + 1067: (TT_TIER + 3, + Cont, + (DeliverItemQuest, 13), + Same, + 2112, + NA, + 1068, + TTLocalizer.QuestDialogDict[1067]), + 1068: (TT_TIER + 3, + Cont, + (CogQuest, + ToontownGlobals.ToontownCentral, + 10, + Any), + Same, + Same, + NA, + (1069, 1070, 1071), + TTLocalizer.QuestDialogDict[1068]), + 1069: (TT_TIER + 3, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 13, + Medium, + 'm', + 'track'), + Same, + Same, + NA, + 1072, + TTLocalizer.QuestDialogDict[1069]), + 1070: (TT_TIER + 3, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 13, + Medium, + 's', + 'track'), + Same, + Same, + NA, + 1072, + TTLocalizer.QuestDialogDict[1070]), + 1071: (TT_TIER + 3, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 13, + Medium, + 'c', + 'track'), + Same, + Same, + NA, + 1072, + TTLocalizer.QuestDialogDict[1071]), + 1072: (TT_TIER + 3, + Cont, + (DeliverItemQuest, 13), + Same, + 2301, + NA, + 1073, + TTLocalizer.QuestDialogDict[1072]), + 1073: (TT_TIER + 3, + Cont, + (VisitQuest,), + Any, + 2201, + NA, + 1074, + TTLocalizer.QuestDialogDict[1073]), + 1074: (TT_TIER + 3, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 13, + Hard, + Any), + Same, + Same, + NA, + 1075, + TTLocalizer.QuestDialogDict[1074]), + 1075: (TT_TIER + 3, + Cont, + (DeliverItemQuest, 13), + Same, + 2301, + 900, + NA, + TTLocalizer.QuestDialogDict[1075]), + 1076: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2217, + NA, + 1077, + TTLocalizer.QuestDialogDict[1076]), + 1077: (TT_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 14, + Medium, + Any), + 2217, + Same, + NA, + 1078, + TTLocalizer.QuestDialogDict[1077]), + 1078: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 14), + Same, + 2302, + NA, + 1079, + TTLocalizer.QuestDialogDict[1078]), + 1079: (TT_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 15, + Easy, + 'f'), + Same, + 2217, + NA, + 1080, + TTLocalizer.QuestDialogDict[1079]), + 1092: (TT_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 15, + Easy, + 'sc'), + Same, + 2217, + NA, + 1080, + TTLocalizer.QuestDialogDict[1092]), + 1080: (TT_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 4, + 15, + Easy, + AnyFish), + Same, + Same, + 500, + NA, + TTLocalizer.QuestDialogDict[1080]), + 1081: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2208, + NA, + 1082, + TTLocalizer.QuestDialogDict[1081]), + 1082: (TT_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 16, + Medium, + 's', + 'track'), + 2208, + Same, + NA, + 1083, + TTLocalizer.QuestDialogDict[1082]), + 1083: (TT_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 17, + Medium, + 'l', + 'track'), + Same, + Same, + NA, + 1084, + TTLocalizer.QuestDialogDict[1083]), + 1084: (TT_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 18, + Medium, + 'm', + 'track'), + Same, + Same, + 102, + NA, + TTLocalizer.QuestDialogDict[1084]), + 1085: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2003, + NA, + 1086, + TTLocalizer.QuestDialogDict[1085]), + 1086: (TT_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 5, + 2007, + Easy, + 2, + 'level'), + 2003, + Same, + NA, + 1089, + TTLocalizer.QuestDialogDict[1086]), + 1089: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 19), + Same, + ToonHQ, + 100, + NA, + TTLocalizer.QuestDialogDict[1089]), + 1090: (TT_TIER + 2, + Start, + (VisitQuest,), + Any, + 2119, + NA, + 1091, + TTLocalizer.QuestDialogDict[1090]), + 1091: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 8, + 2), + 2119, + ToonHQ, + 101, + NA, + TTLocalizer.QuestDialogDict[1091]), + 1100: (TT_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.ToontownCentral, + 10, + Any), + Any, + ToonHQ, + NA, + 1101, + DefaultDialog), + 1101: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 1000), + Any, + 2004, + 1000, + NA, + DefaultDialog), + 1102: (TT_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.ToontownCentral, + 8, + 3), + Any, + ToonHQ, + NA, + 1103, + DefaultDialog), + 1103: (TT_TIER + 2, + Cont, + (DeliverItemQuest, 1000), + Any, + 2004, + 1000, + NA, + DefaultDialog), + 1105: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'f'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1106: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'p'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1107: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'bf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1108: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1109: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'sc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1110: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'pp'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1111: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'cc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1112: (TT_TIER + 2, + Start, + (CogQuest, + Anywhere, + 2, + 'tm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1205: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'f'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1206: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'p'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1207: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'bf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1208: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1209: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'sc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1210: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'pp'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1211: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'cc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 1212: (TT_TIER + 3, + Start, + (CogQuest, + Anywhere, + 4, + 'tm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 401: (DD_TIER, + Start, + (TrackChoiceQuest, ToontownBattleGlobals.DROP_TRACK, ToontownBattleGlobals.LURE_TRACK), + Any, + ToonHQ, + 400, + NA, + TTLocalizer.QuestDialogDict[401]), + 2001: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 3, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2002: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 4, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2003: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 5, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2004: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 6, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2005: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 7, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2006: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 8, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2007: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 9, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2008: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 10, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2009: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 12, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2010: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 2, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2011: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 3, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2012: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 2, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2013: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 4, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2014: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 4, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2015: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 5, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2816: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 4, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2817: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 5, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2818: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 6, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2819: (DD_TIER, + Start, + (CogLevelQuest, + Anywhere, + 7, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2020: (DD_TIER, + Start, + (CogQuest, + Anywhere, + 10, + Any), + Any, + ToonHQ, + NA, + 2021, + DefaultDialog), + 2021: (DD_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + 1007, + 1000, + NA, + DefaultDialog), + 2101: (DD_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.DonaldsDock, + 3, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2102: (DD_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.DonaldsDock, + 4, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2103: (DD_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.DonaldsDock, + 5, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2104: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 6, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2105: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 7, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2106: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 8, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2107: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 6, + 'f'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2108: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'p'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2109: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2110: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 3, + 'mm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2111: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 2, + 'ds'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2112: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 1, + 'hh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2113: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 6, + 'cc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2114: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'tm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2115: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2116: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 3, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2117: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 2, + 'ms'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2118: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 1, + 'tf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2119: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 6, + 'sc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2120: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'pp'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2121: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2122: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 3, + 'bc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2123: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 2, + 'nc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2124: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 1, + 'mb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2125: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 6, + 'bf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2126: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2127: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 4, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2128: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 3, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2129: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 2, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2130: (DD_TIER + 1, + Start, + (CogQuest, + Anywhere, + 1, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2131: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 2, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2132: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 3, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2133: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 2, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2134: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 4, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2135: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 4, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2136: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 5, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2137: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 4, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2138: (DD_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.DonaldsDock, + 6, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2139: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 3, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2140: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 3, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2141: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 3, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2142: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 3, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2143: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 5, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2144: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 5, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2145: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 5, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2146: (DD_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 5, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2147: (DD_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 7, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2148: (DD_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 7, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2149: (DD_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 7, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2150: (DD_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 7, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2151: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 1, + Any, + 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2152: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 1, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2153: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + Any, + 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2154: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2155: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 1, + 'm', + 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2156: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 1, + 's', + 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2157: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 1, + 'c', + 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2158: (DD_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 1, + 'l', + 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2159: (DD_TIER + 1, + Start, + (DeliverGagQuest, + 2, + ToontownBattleGlobals.THROW_TRACK, + 1), + Any, + Any, + Any, + NA, + DefaultDialog), + 2160: (DD_TIER + 1, + Start, + (DeliverGagQuest, + 1, + ToontownBattleGlobals.SQUIRT_TRACK, + 1), + Any, + Any, + Any, + NA, + DefaultDialog), + 2161: (DD_TIER + 1, + Start, + (DeliverGagQuest, + 1, + ToontownBattleGlobals.SQUIRT_TRACK, + 2), + Any, + Any, + Any, + NA, + DefaultDialog), + 2162: (DD_TIER + 1, + Start, + (DeliverGagQuest, + 2, + ToontownBattleGlobals.THROW_TRACK, + 2), + Any, + Any, + Any, + NA, + DefaultDialog), + 2201: (DD_TIER + 1, + Start, + (VisitQuest,), + Any, + 1101, + NA, + 2202, + TTLocalizer.QuestDialogDict[2201]), + 2202: (DD_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 2001, + Medium, + 'pp'), + 1101, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[2202]), + 2203: (DD_TIER + 1, + Start, + (VisitQuest,), + Any, + 1102, + NA, + 2204, + TTLocalizer.QuestDialogDict[2203]), + 2204: (DD_TIER + 1, + Start, + (DeliverItemQuest, 2002), + 1102, + 1104, + NA, + 2205, + TTLocalizer.QuestDialogDict[2204]), + 2205: (DD_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 2003, + Medium, + 'f'), + Same, + Same, + NA, + 2206, + TTLocalizer.QuestDialogDict[2205]), + 2206: (DD_TIER + 1, + Cont, + (DeliverItemQuest, 2004), + Same, + 1102, + 201, + NA, + TTLocalizer.QuestDialogDict[2206]), + 2207: (DD_TIER + 1, + Start, + (VisitQuest,), + Any, + 1201, + NA, + 2208, + TTLocalizer.QuestDialogDict[2207]), + 2208: (DD_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 2005, + Easy, + 'bs'), + 1201, + Same, + 701, + NA, + TTLocalizer.QuestDialogDict[2208]), + 2209: (DD_TIER + 1, + Start, + (VisitQuest,), + Any, + 1302, + NA, + 2210, + TTLocalizer.QuestDialogDict[2209]), + 2210: (DD_TIER + 1, + Start, + (VisitQuest,), + 1302, + 1301, + NA, + 2211, + TTLocalizer.QuestDialogDict[2210]), + 2211: (DD_TIER + 1, + Cont, + (CogQuest, + ToontownGlobals.DonaldsDock, + 5, + 'mm'), + Same, + Same, + NA, + 2212, + TTLocalizer.QuestDialogDict[2211]), + 2212: (DD_TIER + 1, + Cont, + (DeliverItemQuest, 2006), + Same, + 1302, + NA, + 2213, + TTLocalizer.QuestDialogDict[2212]), + 2213: (DD_TIER + 1, + Cont, + (VisitQuest,), + Same, + 1202, + NA, + 2214, + TTLocalizer.QuestDialogDict[2213]), + 2214: (DD_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.DonaldsDock, + 3, + 2007, + Hard, + Any), + Same, + Same, + NA, + 2215, + TTLocalizer.QuestDialogDict[2214]), + 2215: (DD_TIER + 1, + Cont, + (DeliverItemQuest, 2008), + Same, + 1302, + 301, + NA, + TTLocalizer.QuestDialogDict[2215]), + 2500: (DD_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.DonaldsDock, + 15, + Any), + Any, + ToonHQ, + NA, + 2501, + DefaultDialog), + 2501: (DD_TIER + 1, + Cont, + (DeliverItemQuest, 1000), + Any, + 1007, + 1000, + NA, + DefaultDialog), + 2801: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 3, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2802: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 4, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2803: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 5, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2804: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 6, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2805: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 7, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2806: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 8, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2807: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 9, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2808: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 10, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2809: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 12, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2810: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 2, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2811: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 3, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2812: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 2, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2813: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 4, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2814: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 4, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2815: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 5, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2816: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 4, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2817: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 5, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2818: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 6, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2819: (DD_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 7, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 2820: (DD_TIER + 2, + Start, + (CogQuest, + Anywhere, + 20, + Any), + Any, + ToonHQ, + NA, + 2821, + DefaultDialog), + 2821: (DD_TIER + 2, + Cont, + (DeliverItemQuest, 1000), + Any, + 1007, + 1000, + NA, + DefaultDialog), + 901: (DD_TIER + 2, + Start, + (VisitQuest,), + Any, + 1203, + NA, + 2902, + TTLocalizer.QuestDialogDict[901]), + 2902: (DD_TIER + 2, + Start, + (VisitQuest,), + 1203, + 1303, + NA, + 2903, + TTLocalizer.QuestDialogDict[2902]), + 2903: (DD_TIER + 2, + Cont, + (DeliverItemQuest, 2009), + Same, + 1106, + NA, + 2904, + TTLocalizer.QuestDialogDict[2903]), + 2904: (DD_TIER + 2, + Cont, + (DeliverItemQuest, 2010), + Same, + 1203, + NA, + 2905, + TTLocalizer.QuestDialogDict[2904]), + 2905: (DD_TIER + 2, + Cont, + (VisitQuest, 2009), + Same, + 1105, + NA, + 2906, + TTLocalizer.QuestDialogDict[2905]), + 2906: (DD_TIER + 2, + Cont, + (DeliverGagQuest, + 3, + ToontownBattleGlobals.SQUIRT_TRACK, + 2), + Same, + Same, + NA, + 2907, + TTLocalizer.QuestDialogDict[2906]), + 2907: (DD_TIER + 2, + Cont, + (DeliverItemQuest, 2011), + Same, + 1203, + NA, + (2910, 2915, 2920), + TTLocalizer.QuestDialogDict[2907]), + 2910: (DD_TIER + 2, + Cont, + (VisitQuest,), + Same, + 1107, + NA, + 2911, + TTLocalizer.QuestDialog_2910), + 2911: (DD_TIER + 2, + Cont, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 4, + 'm'), + Same, + Same, + NA, + 2925, + TTLocalizer.QuestDialogDict[2911]), + 2915: (DD_TIER + 2, + Cont, + (VisitQuest,), + Same, + 1204, + NA, + 2916, + TTLocalizer.QuestDialog_2910), + 2916: (DD_TIER + 2, + Cont, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 2, + 's'), + Same, + Same, + NA, + 2925, + TTLocalizer.QuestDialogDict[2916]), + 2920: (DD_TIER + 2, + Cont, + (VisitQuest,), + Same, + 1204, + NA, + 2921, + TTLocalizer.QuestDialog_2910), + 2921: (DD_TIER + 2, + Cont, + (CogTrackQuest, + ToontownGlobals.DonaldsDock, + 6, + 'c'), + Same, + Same, + NA, + 2925, + TTLocalizer.QuestDialogDict[2921]), + 2925: (DD_TIER + 2, + Cont, + (DeliverItemQuest, 2012), + Same, + 1203, + NA, + 2926, + TTLocalizer.QuestDialogDict[2925]), + 2926: (DD_TIER + 2, + Cont, + (BuildingQuest, + ToontownGlobals.DonaldsDock, + 1, + Any, + 2), + Same, + Same, + 900, + NA, + TTLocalizer.QuestDialogDict[2926]), + 3101: (DG_TIER, + Start, + (CogQuest, + ToontownGlobals.DaisyGardens, + 8, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3102: (DG_TIER, + Start, + (CogQuest, + ToontownGlobals.DaisyGardens, + 10, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3103: (DG_TIER, + Start, + (CogQuest, + ToontownGlobals.DaisyGardens, + 12, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3104: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 14, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3105: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 16, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3106: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 18, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3107: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 10, + 'f'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3108: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'p'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3109: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'ym'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3110: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'mm'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3111: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'ds'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3112: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'hh'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3113: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 10, + 'cc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3114: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'tm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3115: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3116: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3117: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'ms'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3118: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'tf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3119: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 10, + 'sc'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3120: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'pp'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3121: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'tw'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3122: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'bc'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3123: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'nc'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3124: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'mb'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3125: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 10, + 'bf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3126: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3127: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3128: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3129: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3130: (DG_TIER, + Start, + (CogQuest, + Anywhere, + 6, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3131: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 10, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3132: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 15, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3133: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 8, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3134: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 12, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3135: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 4, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3136: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 6, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3137: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 8, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3138: (DG_TIER, + Start, + (CogLevelQuest, + Anywhere, + 12, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3139: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 6, + 'm'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3140: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 6, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3141: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 6, + 'c'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3142: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 6, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3143: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 10, + 'm'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3144: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 10, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3145: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 10, + 'c'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3146: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DaisyGardens, + 10, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3147: (DG_TIER, + Start, + (CogTrackQuest, + Anywhere, + 14, + 'm'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3148: (DG_TIER, + Start, + (CogTrackQuest, + Anywhere, + 14, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3149: (DG_TIER, + Start, + (CogTrackQuest, + Anywhere, + 14, + 'c'), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3150: (DG_TIER, + Start, + (CogTrackQuest, + Anywhere, + 14, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3151: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 1, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3152: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3153: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 3, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3154: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 4, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3155: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 'm', + 2), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3156: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 's', + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3157: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 'c', + 2), + Any, + ToonHQ, + OBSOLETE, + NA, + DefaultDialog), + 3158: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 'l', + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 3200: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5101, + NA, + 3201, + TTLocalizer.QuestDialogDict[3200]), + 3201: (DG_TIER, + Start, + (DeliverItemQuest, 5001), + 5101, + 5206, + NA, + 3203, + TTLocalizer.QuestDialogDict[3201]), + 3203: (DG_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.DaisyGardens, + 1, + 5002, + VeryHard, + Any), + Same, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[3203]), + 3204: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5106, + NA, + 3205, + TTLocalizer.QuestDialogDict[3204]), + 3205: (DG_TIER, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 5003, + Medium, + 'b'), + 5106, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[3205]), + 3206: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5107, + NA, + 3207, + TTLocalizer.QuestDialogDict[3206]), + 3207: (DG_TIER, + Start, + (RecoverItemQuest, + ToontownGlobals.DaisyGardens, + 10, + 5004, + VeryEasy, + 'dt'), + 5107, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[3207]), + 3208: (DG_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.DaisyGardens, + 10, + 'cc'), + Any, + ToonHQ, + NA, + 3209, + TTLocalizer.QuestDialogDict[3208]), + 3209: (DG_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.DaisyGardens, + 10, + 'tm'), + Same, + Same, + 202, + NA, + TTLocalizer.QuestDialogDict[3209]), + 3247: (DG_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.DaisyGardens, + 20, + 'b'), + Any, + ToonHQ, + 202, + NA, + TTLocalizer.QuestDialogDict[3247]), + 3210: (DG_TIER, + Start, + (DeliverGagQuest, + 10, + ToontownBattleGlobals.SQUIRT_TRACK, + 0), + Any, + 5207, + NA, + 3211, + TTLocalizer.QuestDialogDict[3210]), + 3211: (DG_TIER, + Cont, + (CogQuest, + 5200, + 20, + Any), + Same, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[3211]), + 3212: (DG_TIER, + OBSOLETE, + (VisitQuest,), + Any, + 5208, + NA, + 3213, + TTLocalizer.QuestDialogDict[3212]), + 3213: (DG_TIER, + OBSOLETE, + (RecoverItemQuest, + ToontownGlobals.DaisyGardens, + 1, + 5005, + VeryHard, + Any), + 5208, + Same, + NA, + 3214, + TTLocalizer.QuestDialogDict[3213]), + 3214: (DG_TIER, + OBSOLETE, + (RecoverItemQuest, + ToontownGlobals.DaisyGardens, + 1, + 5006, + VeryHard, + Any), + Same, + Same, + NA, + 3215, + TTLocalizer.QuestDialogDict[3214]), + 3215: (DG_TIER, + OBSOLETE, + (RecoverItemQuest, + ToontownGlobals.DaisyGardens, + 1, + 5007, + VeryHard, + Any), + Same, + Same, + NA, + 3216, + TTLocalizer.QuestDialogDict[3215]), + 3216: (DG_TIER, + OBSOLETE, + (RecoverItemQuest, + ToontownGlobals.DaisyGardens, + 1, + 5008, + VeryHard, + Any), + Same, + Same, + 202, + NA, + TTLocalizer.QuestDialogDict[3216]), + 3217: (DG_TIER, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 5010, + VeryEasy, + 'nd'), + ToonHQ, + ToonHQ, + NA, + 3218, + TTLocalizer.QuestDialogDict[3217]), + 3218: (DG_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 5010, + VeryHard, + 'gh'), + Same, + Same, + NA, + 3219, + TTLocalizer.QuestDialogDict[3218]), + 3219: (DG_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 5010, + Easy, + 'ms'), + Same, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[3219]), + 3244: (DG_TIER, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 5010, + VeryEasy, + 'ac'), + ToonHQ, + ToonHQ, + NA, + 3245, + TTLocalizer.QuestDialogDict[3244]), + 3245: (DG_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 5010, + VeryHard, + 'bs'), + Same, + Same, + NA, + 3246, + TTLocalizer.QuestDialogDict[3245]), + 3246: (DG_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 5010, + VeryHard, + 'sd'), + Same, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[3246]), + 3220: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5207, + NA, + 3221, + TTLocalizer.QuestDialogDict[3220]), + 3221: (DG_TIER, + Start, + (CogQuest, + ToontownGlobals.DaisyGardens, + 20, + Any), + 5207, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[3221]), + 3222: (DG_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + Any, + 1), + ToonHQ, + ToonHQ, + NA, + 3223, + TTLocalizer.QuestDialogDict[3222]), + 3223: (DG_TIER, + Cont, + (BuildingQuest, + Anywhere, + 2, + Any, + 2), + Same, + Same, + NA, + 3224, + TTLocalizer.QuestDialogDict[3223]), + 3224: (DG_TIER, + Cont, + (BuildingQuest, + Anywhere, + 2, + Any, + 3), + Same, + Same, + 501, + NA, + TTLocalizer.QuestDialogDict[3224]), + 3225: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5108, + NA, + (3226, + 3227, + 3228, + 3229, + 3230, + 3231, + 3232, + 3233, + 3234), + TTLocalizer.QuestDialogDict[3225]), + 3226: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5201, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3227: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5203, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3228: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5204, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3229: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5205, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3230: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5102, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3231: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5103, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3232: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5104, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3233: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5105, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3234: (DG_TIER, + Start, + (DeliverItemQuest, 5011), + 5108, + 5207, + NA, + 3235, + TTLocalizer.QuestDialog_3225), + 3235: (DG_TIER, + Cont, + (CogQuest, + ToontownGlobals.DaisyGardens, + 10, + Any), + Same, + 5108, + 100, + NA, + TTLocalizer.QuestDialogDict[3235]), + 3236: (DG_TIER, + OBSOLETE, + (BuildingQuest, + Anywhere, + 3, + 'l', + 2), + Any, + ToonHQ, + NA, + 3237, + TTLocalizer.QuestDialogDict[3236]), + 3237: (DG_TIER, + OBSOLETE, + (BuildingQuest, + Anywhere, + 3, + 's', + 2), + Same, + Same, + 702, + NA, + TTLocalizer.QuestDialogDict[3237]), + 3238: (DG_TIER, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 2, + VeryEasy, + 'm'), + Any, + ToonHQ, + NA, + 3239, + TTLocalizer.QuestDialogDict[3238]), + 3239: (DG_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 5012, + Hard, + 'm'), + Same, + Same, + 302, + NA, + TTLocalizer.QuestDialogDict[3239]), + 3242: (DG_TIER, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 2, + VeryEasy, + 'le'), + Any, + ToonHQ, + NA, + 3243, + TTLocalizer.QuestDialogDict[3242]), + 3243: (DG_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 5012, + Hard, + 'le'), + Same, + Same, + 302, + NA, + TTLocalizer.QuestDialogDict[3243]), + 3240: (DG_TIER, + OBSOLETE, + (RecoverItemQuest, + Anywhere, + 1, + 5009, + Hard, + 'le'), + Any, + 5103, + 102, + NA, + TTLocalizer.QuestDialogDict[3240]), + 3241: (DG_TIER, + OBSOLETE, + (BuildingQuest, + Anywhere, + 5, + Any, + 3), + Any, + ToonHQ, + 102, + NA, + TTLocalizer.QuestDialogDict[3241]), + 3250: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5317, + NA, + 3251, + TTLocalizer.QuestDialogDict[3250]), + 3251: (DG_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.SellbotHQ, + 5, + 's'), + 5317, + Same, + NA, + 3252, + TTLocalizer.QuestDialogDict[3251]), + 3252: (DG_TIER, + Cont, + (VisitQuest,), + Same, + 5311, + NA, + 3253, + TTLocalizer.QuestDialogDict[3252]), + 3253: (DG_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.SellbotHQ, + 1, + 5013, + Medium, + 's', + 'track'), + Same, + Same, + NA, + 3254, + TTLocalizer.QuestDialogDict[3253]), + 3254: (DG_TIER, + Cont, + (DeliverItemQuest, 5013), + Same, + 5317, + 202, + NA, + TTLocalizer.QuestDialogDict[3254]), + 3255: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5314, + NA, + 3258, + TTLocalizer.QuestDialogDict[3255]), + 3256: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5315, + NA, + 3258, + TTLocalizer.QuestDialogDict[3256]), + 3257: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5316, + NA, + 3258, + TTLocalizer.QuestDialogDict[3257]), + 3258: (DG_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.SellbotHQ, + 1, + 5014, + VeryEasy, + 's', + 'track'), + Same, + Same, + NA, + 3259, + TTLocalizer.QuestDialogDict[3258]), + 3259: (DG_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.SellbotHQ, + 1, + 5015, + Easy, + 's', + 'track'), + Same, + Same, + NA, + 3260, + TTLocalizer.QuestDialogDict[3259]), + 3260: (DG_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.SellbotHQ, + 1, + 5016, + Easy, + 's', + 'track'), + Same, + Same, + NA, + 3261, + TTLocalizer.QuestDialogDict[3260]), + 3261: (DG_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.SellbotHQ, + 1, + 5017, + Medium, + 's', + 'track'), + Same, + Same, + 102, + NA, + TTLocalizer.QuestDialogDict[3261]), + 3262: (DG_TIER, + Start, + (VisitQuest,), + Any, + 5313, + NA, + 3263, + TTLocalizer.QuestDialogDict[3262]), + 3263: (DG_TIER, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + 5313, + 5313, + 702, + NA, + TTLocalizer.QuestDialogDict[3263]), + 3500: (DG_TIER, + Start, + (CogQuest, + ToontownGlobals.DaisyGardens, + 25, + Any), + Any, + ToonHQ, + NA, + 3501, + DefaultDialog), + 3501: (DG_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + 5007, + 1000, + NA, + DefaultDialog), + 4001: (MM_TIER, + Start, + (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.HEAL_TRACK), + Any, + ToonHQ, + 400, + NA, + TTLocalizer.QuestDialogDict[4001]), + 4002: (MM_TIER, + Start, + (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.SOUND_TRACK), + Any, + ToonHQ, + 400, + NA, + TTLocalizer.QuestDialogDict[4002]), + 4010: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 16, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4011: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 18, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4012: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 20, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4013: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 22, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4014: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 24, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4015: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 26, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4016: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 28, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4017: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 30, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4018: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 32, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4019: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 34, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4020: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 20, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4021: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 25, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4022: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 16, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4023: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 20, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4024: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 10, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4025: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 20, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4026: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 16, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4027: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 18, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4028: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 20, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4029: (MM_TIER, + Start, + (CogLevelQuest, + Anywhere, + 24, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4030: (MM_TIER, + Start, + (CogQuest, + Anywhere, + 45, + Any), + Any, + ToonHQ, + NA, + 4031, + DefaultDialog), + 4031: (MM_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + 4008, + 1000, + NA, + DefaultDialog), + 4040: (MM_TIER, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 6, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4041: (MM_TIER, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 6, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4042: (MM_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 3, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4043: (MM_TIER, + Start, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4101: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.MinniesMelodyland, + 16, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4102: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.MinniesMelodyland, + 18, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4103: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.MinniesMelodyland, + 20, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4104: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.MinniesMelodyland, + 24, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4105: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 28, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4106: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 32, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4107: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'f'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4108: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'p'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4109: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4110: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'mm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4111: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'ds'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4112: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'hh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4113: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'cc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4114: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'tm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4115: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4116: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4117: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'ms'), + None, + ToonHQ, + Any, + NA, + DefaultDialog), + 4118: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'tf'), + None, + ToonHQ, + Any, + NA, + DefaultDialog), + 4119: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'sc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4120: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'pp'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4121: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4122: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'bc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4123: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'nc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4124: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'mb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4125: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'bf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4126: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4127: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 16, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4128: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4129: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4130: (MM_TIER + 1, + Start, + (CogQuest, + Anywhere, + 12, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4131: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 20, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4132: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 25, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4133: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 16, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4134: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 20, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4135: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 10, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4136: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 20, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4137: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 16, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4138: (MM_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 24, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4139: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 15, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4140: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 15, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4141: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 15, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4142: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 15, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4143: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 24, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4144: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 24, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4145: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 24, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4146: (MM_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 24, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4147: (MM_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 30, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4148: (MM_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 30, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4149: (MM_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 30, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4150: (MM_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 30, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4151: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 1, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4152: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4153: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 3, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4154: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 4, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4155: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 3, + 'm', + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4156: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 3, + 's', + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4157: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 3, + 'c', + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4158: (MM_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 3, + 'l', + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4160: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 10, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4161: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 12, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4162: (MM_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 6, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4163: (MM_TIER + 1, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4164: (MM_TIER + 1, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4165: (MM_TIER + 1, + Start, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4166: (MM_TIER + 1, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4200: (MM_TIER + 1, + Start, + (VisitQuest,), + Any, + 4101, + NA, + 4201, + TTLocalizer.QuestDialogDict[4200]), + 4201: (MM_TIER + 1, + Start, + (VisitQuest,), + 4101, + 4201, + NA, + 4202, + TTLocalizer.QuestDialogDict[4201]), + 4202: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 4001), + Same, + 4101, + NA, + 4203, + TTLocalizer.QuestDialogDict[4202]), + 4203: (MM_TIER + 1, + Cont, + (VisitQuest,), + Same, + 4301, + NA, + 4204, + TTLocalizer.QuestDialogDict[4203]), + 4204: (MM_TIER + 1, + Cont, + (CogQuest, + ToontownGlobals.MinniesMelodyland, + 10, + Any), + Same, + Same, + NA, + 4205, + TTLocalizer.QuestDialogDict[4204]), + 4205: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 4002), + Same, + 4101, + NA, + 4206, + TTLocalizer.QuestDialogDict[4205]), + 4206: (MM_TIER + 1, + Cont, + (VisitQuest,), + Same, + 4102, + NA, + 4207, + TTLocalizer.QuestDialogDict[4206]), + 4207: (MM_TIER + 1, + Cont, + (VisitQuest,), + Same, + 4108, + NA, + 4208, + TTLocalizer.QuestDialogDict[4207]), + 4208: (MM_TIER + 1, + Cont, + (DeliverGagQuest, + 1, + ToontownBattleGlobals.THROW_TRACK, + 4), + Same, + Same, + NA, + 4209, + TTLocalizer.QuestDialogDict[4208]), + 4209: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 4003), + Same, + 4102, + NA, + 4210, + TTLocalizer.QuestDialogDict[4209]), + 4210: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 4004), + Same, + 4101, + 203, + NA, + TTLocalizer.QuestDialogDict[4210]), + 4211: (MM_TIER + 1, + Start, + (VisitQuest,), + ToonHQ, + 4103, + NA, + 4212, + TTLocalizer.QuestDialogDict[4211]), + 4212: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.MinniesMelodyland, + 10, + 'nc'), + 4103, + Same, + NA, + 4213, + TTLocalizer.QuestDialogDict[4212]), + 4213: (MM_TIER + 1, + Cont, + (CogTrackQuest, + ToontownGlobals.MinniesMelodyland, + 20, + 'm'), + Same, + Same, + NA, + 4214, + TTLocalizer.QuestDialogDict[4213]), + 4214: (MM_TIER + 1, + Cont, + (BuildingQuest, + Anywhere, + 1, + 'm', + Any), + Same, + Same, + 303, + NA, + TTLocalizer.QuestDialogDict[4214]), + 4215: (MM_TIER + 1, + Start, + (VisitQuest,), + Any, + 4302, + NA, + 4216, + TTLocalizer.QuestDialogDict[4215]), + 4216: (MM_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 4005, + VeryHard, + 'gh'), + 4302, + Same, + NA, + 4217, + TTLocalizer.QuestDialogDict[4216]), + 4217: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 4005), + Same, + 4203, + NA, + 4218, + TTLocalizer.QuestDialogDict[4217]), + 4218: (MM_TIER + 1, + Cont, + (VisitQuest,), + Any, + 4302, + NA, + 4219, + TTLocalizer.QuestDialogDict[4218]), + 4219: (MM_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 4006, + VeryHard, + 'gh'), + Same, + Same, + NA, + 4220, + TTLocalizer.QuestDialogDict[4219]), + 4220: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 4006), + Same, + 4308, + NA, + 4221, + TTLocalizer.QuestDialogDict[4220]), + 4221: (MM_TIER + 1, + Cont, + (VisitQuest,), + Any, + 4302, + NA, + 4222, + TTLocalizer.QuestDialogDict[4221]), + 4222: (MM_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 4007, + VeryHard, + 'gh'), + Same, + Same, + NA, + 4223, + TTLocalizer.QuestDialogDict[4222]), + 4223: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 4007), + Same, + 4202, + NA, + 4224, + TTLocalizer.QuestDialogDict[4223]), + 4224: (MM_TIER + 1, + Cont, + (VisitQuest,), + Any, + 4302, + 703, + NA, + TTLocalizer.QuestDialogDict[4224]), + 4500: (MM_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.MinniesMelodyland, + 40, + Any), + Any, + ToonHQ, + NA, + 4501, + DefaultDialog), + 4501: (MM_TIER + 1, + Cont, + (DeliverItemQuest, 1000), + Any, + 4008, + 1000, + NA, + DefaultDialog), + 902: (MM_TIER + 2, + Start, + (VisitQuest,), + Any, + 4303, + NA, + 4903, + TTLocalizer.QuestDialogDict[902]), + 4903: (MM_TIER + 2, + Start, + (DeliverItemQuest, 4008), + 4303, + 4109, + NA, + 4904, + TTLocalizer.QuestDialogDict[4903]), + 4904: (MM_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 4009, + VeryHard, + AnyFish), + Same, + Same, + NA, + 4905, + TTLocalizer.QuestDialogDict[4904]), + 4905: (MM_TIER + 2, + Cont, + (BuildingQuest, + Anywhere, + 1, + Any, + 1), + Same, + Same, + NA, + 4906, + TTLocalizer.QuestDialogDict[4905]), + 4906: (MM_TIER + 2, + Cont, + (DeliverItemQuest, 4010), + Same, + 4303, + NA, + 4907, + TTLocalizer.QuestDialogDict[4906]), + 4907: (MM_TIER + 2, + Cont, + (VisitQuest,), + Same, + 4208, + NA, + 4908, + TTLocalizer.QuestDialogDict[4907]), + 4908: (MM_TIER + 2, + Cont, + (BuildingQuest, + Anywhere, + 1, + Any, + 2), + Same, + Same, + NA, + 4909, + TTLocalizer.QuestDialogDict[4908]), + 4909: (MM_TIER + 2, + Cont, + (BuildingQuest, + Anywhere, + 1, + Any, + 3), + Same, + Same, + NA, + 4910, + TTLocalizer.QuestDialogDict[4909]), + 4910: (MM_TIER + 2, + Cont, + (DeliverItemQuest, 4011), + Same, + 4303, + 900, + NA, + TTLocalizer.QuestDialogDict[4910]), + 4810: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 16, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4811: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 18, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4812: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 20, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4813: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 22, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4814: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 24, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4815: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 26, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4816: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 28, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4817: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 30, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4818: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 32, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4819: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 34, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4820: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 20, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4821: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 25, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4822: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 16, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4823: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 20, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4824: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 10, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4825: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 20, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4826: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 16, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4827: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 18, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4828: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 20, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4829: (MM_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 24, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4830: (MM_TIER + 2, + Start, + (CogQuest, + Anywhere, + 45, + Any), + Any, + ToonHQ, + NA, + 4831, + DefaultDialog), + 4831: (MM_TIER + 2, + Cont, + (DeliverItemQuest, 1000), + Any, + 4008, + 1000, + NA, + DefaultDialog), + 4840: (MM_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 12, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4841: (MM_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 15, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4842: (MM_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 12, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4843: (MM_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 10, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4844: (MM_TIER + 2, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4845: (MM_TIER + 2, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4846: (MM_TIER + 2, + Start, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4847: (MM_TIER + 2, + Start, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 3, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 4848: (MM_TIER + 2, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5247: (BR_TIER, + Start, + (VisitQuest,), + Any, + 3112, + NA, + 5248, + TTLocalizer.QuestDialogDict[5247]), + 5248: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 10, + 8), + 3112, + Same, + NA, + 5249, + TTLocalizer.QuestDialogDict[5248]), + 5249: (BR_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 3, + 3018, + VeryHard, + AnyFish), + Same, + Same, + NA, + (5250, + 5258, + 5259, + 5260), + TTLocalizer.QuestDialogDict[5249]), + 5250: (BR_TIER, + Cont, + (BuildingQuest, + Anywhere, + 2, + 'l', + 4), + Same, + Same, + NA, + (5001, + 5002, + 5003, + 5004, + 5005, + 5006, + 5007, + 5008), + TTLocalizer.QuestDialogDict[5250]), + 5258: (BR_TIER, + Cont, + (BuildingQuest, + Anywhere, + 2, + 'c', + 4), + Same, + Same, + NA, + (5001, + 5002, + 5003, + 5004, + 5005, + 5006, + 5007, + 5008), + TTLocalizer.QuestDialogDict[5258]), + 5259: (BR_TIER, + Cont, + (BuildingQuest, + Anywhere, + 2, + 'm', + 4), + Same, + Same, + NA, + (5001, + 5002, + 5003, + 5004, + 5005, + 5006, + 5007, + 5008), + TTLocalizer.QuestDialogDict[5259]), + 5260: (BR_TIER, + Cont, + (BuildingQuest, + Anywhere, + 2, + 's', + 4), + Same, + Same, + NA, + (5001, + 5002, + 5003, + 5004, + 5005, + 5006, + 5007, + 5008), + TTLocalizer.QuestDialogDict[5260]), + 5001: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.SOUND_TRACK, ToontownBattleGlobals.DROP_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5002: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.SOUND_TRACK, ToontownBattleGlobals.LURE_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5003: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.HEAL_TRACK, ToontownBattleGlobals.DROP_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5004: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.HEAL_TRACK, ToontownBattleGlobals.LURE_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5005: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.SOUND_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5006: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.HEAL_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5007: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.DROP_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5008: (BR_TIER, + Cont, + (TrackChoiceQuest, ToontownBattleGlobals.TRAP_TRACK, ToontownBattleGlobals.LURE_TRACK), + Same, + Same, + 400, + NA, + TTLocalizer.TheBrrrghTrackQuestDict), + 5020: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 36, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5021: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 38, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5022: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 40, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5023: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 42, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5024: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 44, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5025: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 46, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5026: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 48, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5027: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 50, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5028: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 52, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5029: (BR_TIER, + Start, + (CogQuest, + Anywhere, + 54, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5030: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 25, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5031: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 30, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5032: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 35, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5033: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 6, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5034: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 10, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5035: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 20, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5036: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 2, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5037: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 8, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5038: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 10, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5039: (BR_TIER, + Start, + (CogLevelQuest, + Anywhere, + 12, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5040: (BR_TIER, + Start, + (CogQuest, + ToontownGlobals.TheBrrrgh, + 75, + Any), + Any, + ToonHQ, + NA, + 5041, + DefaultDialog), + 5041: (BR_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + 3008, + 1000, + NA, + DefaultDialog), + 5060: (BR_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 5, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5061: (BR_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 5, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5062: (BR_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 5, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5063: (BR_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 5, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5064: (BR_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5065: (BR_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5066: (BR_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5067: (BR_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5070: (BR_TIER, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 20, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5071: (BR_TIER, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 22, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5072: (BR_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 15, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5073: (BR_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 10, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5074: (BR_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 12, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5075: (BR_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 8, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5076: (BR_TIER, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5077: (BR_TIER, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5078: (BR_TIER, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5079: (BR_TIER, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5080: (BR_TIER, + Start, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5081: (BR_TIER, + Start, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 5, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5082: (BR_TIER, + Start, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 2, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5083: (BR_TIER, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5084: (BR_TIER, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5101: (BR_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.TheBrrrgh, + 36, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5102: (BR_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.TheBrrrgh, + 40, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5103: (BR_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.TheBrrrgh, + 42, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5104: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 45, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5105: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5106: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 55, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5107: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 25, + 'p'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5108: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5109: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'mm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5110: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'ds'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5111: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'hh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5112: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 8, + 'cr'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5113: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 25, + 'tm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5114: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5115: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5116: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'ms'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5117: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'tf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5118: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 8, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5119: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 25, + 'pp'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5120: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5121: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'bc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5122: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'nc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5123: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'mb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5124: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 8, + 'ls'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5125: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 25, + 'b'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5126: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5127: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5128: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5129: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 15, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5130: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 8, + 'le'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5131: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 25, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5132: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 30, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5133: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 35, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5134: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 6, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5135: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 10, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5136: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 20, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5137: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 2, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5138: (BR_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 8, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5139: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 32, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5140: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 32, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5141: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 32, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5142: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 32, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5143: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 40, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5144: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 40, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5145: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 40, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5146: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 40, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5147: (BR_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 45, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5148: (BR_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 45, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5149: (BR_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 45, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5150: (BR_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 45, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5151: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 8, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5152: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5153: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 5, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5154: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 6, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5155: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + 'm', + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5156: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + 's', + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5157: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + 'c', + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5158: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 2, + 'l', + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5160: (BR_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 22, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5161: (BR_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 25, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5162: (BR_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 16, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5163: (BR_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 12, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5164: (BR_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 14, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5165: (BR_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 10, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5166: (BR_TIER + 1, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5167: (BR_TIER + 1, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5168: (BR_TIER + 1, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5169: (BR_TIER + 1, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5170: (BR_TIER + 1, + Start, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5171: (BR_TIER + 1, + Start, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 6, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5172: (BR_TIER + 1, + Start, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 3, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5173: (BR_TIER + 1, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5174: (BR_TIER + 1, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5200: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3110, + NA, + (5201, + 5261, + 5262, + 5263), + TTLocalizer.QuestDialogDict[5200]), + 5201: (BR_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 3001, + VeryHard, + 'hh'), + 3110, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[5201]), + 5261: (BR_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 3001, + VeryHard, + 'tf'), + 3110, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[5261]), + 5262: (BR_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 3001, + VeryHard, + 'mb'), + 3110, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[5262]), + 5263: (BR_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 3001, + VeryHard, + 'sd'), + 3110, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[5263]), + 5202: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3108, + NA, + 5203, + TTLocalizer.QuestDialogDict[5202]), + 5203: (BR_TIER + 1, + Start, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 1, + 3002, + VeryHard, + Any), + 3108, + Same, + NA, + 5204, + TTLocalizer.QuestDialogDict[5203]), + 5204: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3205, + NA, + 5205, + TTLocalizer.QuestDialogDict[5204]), + 5205: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 3, + 3003, + Hard, + AnyFish), + Same, + Same, + NA, + 5206, + TTLocalizer.QuestDialogDict[5205]), + 5206: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3210, + NA, + 5207, + TTLocalizer.QuestDialogDict[5206]), + 5207: (BR_TIER + 1, + Cont, + (BuildingQuest, + Anywhere, + 5, + Any, + 4), + Same, + Same, + NA, + 5208, + TTLocalizer.QuestDialogDict[5207]), + 5208: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3114, + NA, + 5209, + TTLocalizer.QuestDialogDict[5208]), + 5209: (BR_TIER + 1, + Cont, + (CogLevelQuest, + Anywhere, + 20, + 7), + Same, + Same, + 204, + NA, + TTLocalizer.QuestDialogDict[5209]), + 5210: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3206, + NA, + (5211, + 5264, + 5265, + 5266), + TTLocalizer.QuestDialogDict[5210]), + 5211: (BR_TIER + 1, + Start, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 1, + 3004, + Medium, + 'le'), + 3206, + Same, + NA, + 5212, + TTLocalizer.QuestDialogDict[5211]), + 5264: (BR_TIER + 1, + Start, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 1, + 3004, + Hard, + 'ls'), + 3206, + Same, + NA, + 5212, + TTLocalizer.QuestDialogDict[5264]), + 5265: (BR_TIER + 1, + Start, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 1, + 3004, + Hard, + 'm'), + 3206, + Same, + NA, + 5212, + TTLocalizer.QuestDialogDict[5265]), + 5266: (BR_TIER + 1, + Start, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 1, + 3004, + Hard, + 'cr'), + 3206, + Same, + NA, + 5212, + TTLocalizer.QuestDialogDict[5266]), + 5212: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 3004), + Same, + 3111, + NA, + 5213, + TTLocalizer.QuestDialogDict[5212]), + 5213: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 10, + 3005, + Hard, + Any), + Same, + Same, + NA, + 5214, + TTLocalizer.QuestDialogDict[5213]), + 5214: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3119, + NA, + 5215, + TTLocalizer.QuestDialogDict[5214]), + 5215: (BR_TIER + 1, + Cont, + (CogLevelQuest, + Anywhere, + 10, + 8), + Same, + Same, + NA, + 5216, + TTLocalizer.QuestDialogDict[5215]), + 5216: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 3006), + Same, + 3206, + 704, + NA, + TTLocalizer.QuestDialogDict[5216]), + 5217: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3113, + NA, + 5218, + TTLocalizer.QuestDialogDict[5217]), + 5218: (BR_TIER + 1, + Start, + (CogQuest, + Anywhere, + 10, + 'm'), + 3113, + Same, + NA, + 5219, + TTLocalizer.QuestDialogDict[5218]), + 5219: (BR_TIER + 1, + Cont, + (CogQuest, + Anywhere, + 10, + 'cr'), + Same, + Same, + NA, + 5220, + TTLocalizer.QuestDialogDict[5219]), + 5220: (BR_TIER + 1, + Cont, + (CogQuest, + Anywhere, + 10, + 'ls'), + Same, + Same, + NA, + 5221, + TTLocalizer.QuestDialogDict[5220]), + 5221: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3211, + NA, + 5222, + TTLocalizer.QuestDialogDict[5221]), + 5222: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 2, + 3007, + Hard, + AnyFish), + Same, + Same, + NA, + 5223, + TTLocalizer.QuestDialogDict[5222]), + 5223: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 3008), + Same, + 3113, + NA, + 5224, + TTLocalizer.QuestDialogDict[5223]), + 5224: (BR_TIER + 1, + Cont, + (CogQuest, + Anywhere, + 5, + 'le'), + Same, + Same, + 502, + NA, + TTLocalizer.QuestDialogDict[5224]), + 5225: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3106, + NA, + 5226, + TTLocalizer.QuestDialogDict[5225]), + 5226: (BR_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 3, + 'm', + 4), + 3106, + Same, + NA, + 5227, + TTLocalizer.QuestDialogDict[5226]), + 5227: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3208, + NA, + 5228, + TTLocalizer.QuestDialogDict[5227]), + 5228: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 3009), + Same, + 3207, + NA, + (5229, + 5267, + 5268, + 5269), + TTLocalizer.QuestDialogDict[5228]), + 5229: (BR_TIER + 1, + Cont, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 8, + 'm'), + Same, + Same, + NA, + 5230, + TTLocalizer.QuestDialogDict[5229]), + 5267: (BR_TIER + 1, + Cont, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 8, + 's'), + Same, + Same, + NA, + 5230, + TTLocalizer.QuestDialogDict[5267]), + 5268: (BR_TIER + 1, + Cont, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 8, + 'l'), + Same, + Same, + NA, + 5230, + TTLocalizer.QuestDialogDict[5268]), + 5269: (BR_TIER + 1, + Cont, + (CogTrackQuest, + ToontownGlobals.TheBrrrgh, + 8, + 'c'), + Same, + Same, + NA, + (5230, + 5270, + 5271, + 5272), + TTLocalizer.QuestDialogDict[5269]), + 5230: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3010, + Hard, + 'rb'), + Same, + Same, + NA, + 5231, + TTLocalizer.QuestDialogDict[5230]), + 5270: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3010, + Hard, + 'tbc'), + Same, + Same, + NA, + 5231, + TTLocalizer.QuestDialogDict[5270]), + 5271: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3010, + Hard, + 'mh'), + Same, + Same, + NA, + 5231, + TTLocalizer.QuestDialogDict[5271]), + 5272: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3010, + Medium, + 'bw'), + Same, + Same, + NA, + 5231, + TTLocalizer.QuestDialogDict[5272]), + 5231: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 3010), + Same, + 3208, + NA, + 5232, + TTLocalizer.QuestDialogDict[5231]), + 5232: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3106, + NA, + 5233, + TTLocalizer.QuestDialogDict[5232]), + 5233: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 3011), + Same, + 3208, + 304, + NA, + TTLocalizer.QuestDialogDict[5233]), + 5243: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3217, + NA, + 5244, + TTLocalizer.QuestDialogDict[5243]), + 5244: (BR_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 2007, + VeryHard, + 'mm'), + 3217, + Same, + NA, + 5245, + TTLocalizer.QuestDialogDict[5244]), + 5245: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3017, + Hard, + AnyFish), + Same, + Same, + NA, + 5246, + TTLocalizer.QuestDialogDict[5245]), + 5246: (BR_TIER + 1, + Cont, + (BuildingQuest, + ToontownGlobals.TheBrrrgh, + 5, + Any, + 1), + Same, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[5246]), + 5251: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3134, + NA, + 5252, + TTLocalizer.QuestDialogDict[5251]), + 5252: (BR_TIER + 1, + Start, + (RecoverItemQuest, + Anywhere, + 1, + 3019, + VeryHard, + Any), + 3134, + Same, + NA, + (5253, + 5273, + 5274, + 5275), + TTLocalizer.QuestDialogDict[5252]), + 5253: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3020, + VeryHard, + 'cr'), + Same, + Same, + NA, + (5254, + 5282, + 5283, + 5284), + TTLocalizer.QuestDialogDict[5253]), + 5273: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3020, + VeryHard, + 'm'), + Same, + Same, + NA, + (5254, + 5282, + 5283, + 5284), + TTLocalizer.QuestDialogDict[5273]), + 5274: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3020, + VeryHard, + 'ls'), + Same, + Same, + NA, + (5254, + 5282, + 5283, + 5284), + TTLocalizer.QuestDialogDict[5274]), + 5275: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3020, + Hard, + 'le'), + Same, + Same, + NA, + (5254, + 5282, + 5283, + 5284), + TTLocalizer.QuestDialogDict[5275]), + 5254: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3021, + VeryHard, + 'mh'), + Same, + Same, + 102, + NA, + TTLocalizer.QuestDialogDict[5254]), + 5282: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3021, + VeryHard, + 'tbc'), + Same, + Same, + 102, + NA, + TTLocalizer.QuestDialogDict[5282]), + 5283: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3021, + VeryHard, + 'rb'), + Same, + Same, + 102, + NA, + TTLocalizer.QuestDialogDict[5283]), + 5284: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3021, + Hard, + 'bw'), + Same, + Same, + 102, + NA, + TTLocalizer.QuestDialogDict[5284]), + 5255: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3228, + NA, + (5256, 5276), + TTLocalizer.QuestDialogDict[5255]), + 5256: (BR_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 45, + 'c'), + 3228, + Same, + NA, + (5257, 5277), + TTLocalizer.QuestDialogDict[5256]), + 5276: (BR_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 40, + 'l'), + 3228, + Same, + NA, + (5257, 5277), + TTLocalizer.QuestDialogDict[5276]), + 5257: (BR_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 45, + 's'), + Same, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[5257]), + 5277: (BR_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 45, + 'm'), + Same, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[5277]), + 5301: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3304, + NA, + 5302, + TTLocalizer.QuestDialogDict[5301]), + 5302: (BR_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 90, + 'l'), + Same, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[5302]), + 5303: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3318, + NA, + 5304, + TTLocalizer.QuestDialogDict[5303]), + 5304: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 1, + 3024, + VeryHard, + 'l', + 'track'), + Same, + Same, + NA, + 5305, + TTLocalizer.QuestDialogDict[5304]), + 5305: (BR_TIER + 1, + Cont, + (CogLevelQuest, + Anywhere, + 20, + 7), + Same, + Same, + NA, + 5306, + TTLocalizer.QuestDialogDict[5305]), + 5306: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.TheBrrrgh, + 2, + 3025, + Hard, + AnyFish), + Same, + Same, + NA, + 5307, + TTLocalizer.QuestDialogDict[5306]), + 5307: (BR_TIER + 1, + Cont, + (BuildingQuest, + Anywhere, + 5, + Any, + 4), + Same, + Same, + 204, + NA, + TTLocalizer.QuestDialogDict[5307]), + 5308: (BR_TIER + 1, + Start, + (VisitQuest,), + Any, + 3312, + NA, + 5309, + TTLocalizer.QuestDialogDict[5308]), + 5309: (BR_TIER + 1, + Start, + (CogTrackQuest, + ToontownGlobals.PolarPlace, + 30, + 'l'), + Same, + Same, + NA, + 5310, + TTLocalizer.QuestDialogDict[5309]), + 5310: (BR_TIER + 1, + Cont, + (VisitQuest,), + Same, + 3113, + NA, + 5311, + TTLocalizer.QuestDialogDict[5310]), + 5311: (BR_TIER + 1, + Cont, + (RecoverItemQuest, + Anywhere, + 2, + 3026, + Medium, + 'le'), + Same, + Same, + NA, + 5312, + TTLocalizer.QuestDialogDict[5311]), + 5312: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 3026), + Same, + 3312, + 502, + NA, + TTLocalizer.QuestDialogDict[5312]), + 5290: (BR_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 12, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5291: (BR_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 12, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5292: (BR_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 12, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5293: (BR_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 12, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5294: (BR_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5295: (BR_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5296: (BR_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5297: (BR_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5500: (BR_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.TheBrrrgh, + 75, + Any), + Any, + ToonHQ, + NA, + 5501, + DefaultDialog), + 5501: (BR_TIER + 1, + Cont, + (DeliverItemQuest, 1000), + Any, + 3008, + 1000, + NA, + DefaultDialog), + 903: (BR_TIER + 2, + Start, + (VisitQuest,), + Any, + 3112, + NA, + (5234, 5278), + TTLocalizer.QuestDialogDict[903]), + 5234: (BR_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 6, + 3012, + Medium, + 'tbc'), + 3112, + Same, + NA, + (5235, 5279), + TTLocalizer.QuestDialogDict[5234]), + 5278: (BR_TIER + 2, + Start, + (RecoverItemQuest, + Anywhere, + 6, + 3022, + Medium, + 'mh'), + 3112, + Same, + NA, + (5235, 5279), + TTLocalizer.QuestDialogDict[5278]), + 5235: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3013, + Hard, + 'rb'), + Same, + Same, + NA, + 5236, + TTLocalizer.QuestDialogDict[5235]), + 5279: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3013, + Medium, + 'bw'), + Same, + Same, + NA, + 5236, + TTLocalizer.QuestDialogDict[5279]), + 5236: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3014, + VeryHard, + AnyFish), + Same, + Same, + NA, + 5237, + TTLocalizer.QuestDialogDict[5236]), + 5237: (BR_TIER + 2, + Cont, + (VisitQuest,), + Same, + 3128, + NA, + (5238, 5280), + TTLocalizer.QuestDialogDict[5237]), + 5238: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 10, + 3015, + VeryEasy, + 'mh'), + Same, + Same, + NA, + 5239, + TTLocalizer.QuestDialogDict[5238]), + 5280: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 10, + 3015, + VeryEasy, + 'tbc'), + Same, + Same, + NA, + 5239, + TTLocalizer.QuestDialogDict[5280]), + 5239: (BR_TIER + 2, + Cont, + (DeliverItemQuest, 3015), + Same, + 3112, + NA, + (5240, 5281), + TTLocalizer.QuestDialogDict[5239]), + 5240: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3016, + Hard, + 'bw'), + Same, + Same, + NA, + 5241, + TTLocalizer.QuestDialogDict[5240]), + 5281: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3023, + Hard, + 'mh'), + Same, + Same, + NA, + 5241, + TTLocalizer.QuestDialogDict[5281]), + 5241: (BR_TIER + 2, + Cont, + (BuildingQuest, + Anywhere, + 20, + Any, + 4), + Same, + Same, + NA, + 5242, + TTLocalizer.QuestDialogDict[5241]), + 5242: (BR_TIER + 2, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 3014, + VeryHard, + AnyFish), + Same, + Same, + 900, + NA, + TTLocalizer.QuestDialogDict[5242]), + 5320: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 36, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5321: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 38, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5322: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 40, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5323: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 42, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5324: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 44, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5325: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 46, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5326: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 48, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5327: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 53, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5328: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 52, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5329: (BR_TIER + 2, + Start, + (CogQuest, + Anywhere, + 54, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5330: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 25, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5331: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 30, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5332: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 35, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5333: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 6, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5334: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 10, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5335: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 20, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5336: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 2, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5337: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 8, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5338: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 10, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5339: (BR_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 12, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5340: (BR_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.TheBrrrgh, + 75, + Any), + Any, + ToonHQ, + NA, + 5341, + DefaultDialog), + 5341: (BR_TIER + 2, + Cont, + (DeliverItemQuest, 1000), + Any, + 3008, + 1000, + NA, + DefaultDialog), + 5360: (BR_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 20, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5361: (BR_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 20, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5362: (BR_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 20, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5363: (BR_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 20, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 606, + NA, + DefaultDialog), + 5364: (BR_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5365: (BR_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5366: (BR_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5367: (BR_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 5370: (BR_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 22, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5371: (BR_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.SellbotHQ, + 25, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5372: (BR_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 16, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5373: (BR_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 12, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5374: (BR_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 14, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5375: (BR_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 10, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5376: (BR_TIER + 2, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5377: (BR_TIER + 2, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5378: (BR_TIER + 2, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5379: (BR_TIER + 2, + Start, + (FactoryQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5380: (BR_TIER + 2, + Start, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5381: (BR_TIER + 2, + Start, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 6, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5382: (BR_TIER + 2, + Start, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 3, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5383: (BR_TIER + 2, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 1), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 5384: (BR_TIER + 2, + Start, + (ForemanQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6101: (DL_TIER, + Start, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 60, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6102: (DL_TIER, + Start, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 65, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6103: (DL_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 70, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6104: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 80, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6105: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 90, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6106: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 100, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6107: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6108: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'mm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6109: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'ds'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6110: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'hh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6111: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 15, + 'cr'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6112: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'tbc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6113: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6114: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6115: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'ms'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6116: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'tf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6117: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 15, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6118: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'mh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6119: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6120: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'bc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6121: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'nc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6122: (DL_TIER, + OBSOLETE, + (CogQuest, + Anywhere, + 25, + 'mb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6123: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 15, + 'ls'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6124: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'rb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6125: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6126: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6127: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6128: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 25, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6129: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 15, + 'le'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6130: (DL_TIER, + Start, + (CogQuest, + Anywhere, + 8, + 'bw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6131: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 50, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6132: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 40, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6133: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 35, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6134: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 30, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6135: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 25, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6136: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 20, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6137: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 15, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6138: (DL_TIER, + Start, + (CogLevelQuest, + Anywhere, + 10, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6139: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 50, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6140: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 50, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6141: (DL_TIER, + OBSOLETE, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 50, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6142: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 50, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6143: (DL_TIER, + OBSOLETE, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 55, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6144: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 55, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6145: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 55, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6146: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 55, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6147: (DL_TIER, + OBSOLETE, + (CogTrackQuest, + Anywhere, + 70, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6148: (DL_TIER, + Start, + (CogTrackQuest, + Anywhere, + 70, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6149: (DL_TIER, + Start, + (CogTrackQuest, + Anywhere, + 70, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6150: (DL_TIER, + Start, + (CogTrackQuest, + Anywhere, + 70, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6151: (DL_TIER, + Start, + (BuildingQuest, + Anywhere, + 10, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6152: (DL_TIER, + Start, + (BuildingQuest, + Anywhere, + 6, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6153: (DL_TIER, + OBSOLETE, + (BuildingQuest, + Anywhere, + 8, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6154: (DL_TIER, + Start, + (BuildingQuest, + Anywhere, + 6, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6155: (DL_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 'm', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6156: (DL_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 's', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6157: (DL_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 'c', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6158: (DL_TIER, + Start, + (BuildingQuest, + Anywhere, + 2, + 'l', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6160: (DL_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 25, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 6161: (DL_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 25, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 6162: (DL_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 25, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 6163: (DL_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 25, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 6164: (DL_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 2, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 6165: (DL_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 2, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 6166: (DL_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 2, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 6167: (DL_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 2, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 6170: (DL_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 40, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6171: (DL_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 45, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6172: (DL_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 50, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6173: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 30, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6174: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 20, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6175: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 20, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6176: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 15, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6177: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 10, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6178: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 10, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6179: (DL_TIER, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6180: (DL_TIER, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6181: (DL_TIER, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6182: (DL_TIER, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6183: (DL_TIER, + OBSOLETE, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 20), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6184: (DL_TIER, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 10, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6185: (DL_TIER, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 4, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6186: (DL_TIER, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6187: (DL_TIER, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6190: (DL_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 15, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 6191: (DL_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 15, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 6192: (DL_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 15, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 6193: (DL_TIER, + Start, + (SkelecogNewbieQuest, + ToontownGlobals.SellbotHQ, + 3, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 6194: (DL_TIER, + Start, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 1, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 6195: (DL_TIER, + Start, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 1, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 6196: (DL_TIER, + Start, + (ForemanNewbieQuest, + ToontownGlobals.SellbotFactoryInt, + 1, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 6201: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9111, + NA, + 6202, + TTLocalizer.QuestDialogDict[6201]), + 6202: (DL_TIER, + Start, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 70, + Any), + 9111, + Same, + 100, + NA, + TTLocalizer.QuestDialogDict[6202]), + 6206: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9131, + NA, + 6207, + TTLocalizer.QuestDialogDict[6206]), + 6207: (DL_TIER, + Start, + (BuildingQuest, + ToontownGlobals.DonaldsDreamland, + 8, + Any, + 4), + 9131, + Same, + 205, + NA, + TTLocalizer.QuestDialogDict[6207]), + 6211: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9217, + NA, + 6212, + TTLocalizer.QuestDialogDict[6211]), + 6212: (DL_TIER, + Start, + (RecoverItemQuest, + Anywhere, + 3, + 6002, + Medium, + 'bc'), + 9217, + Same, + NA, + 6213, + TTLocalizer.QuestDialogDict[6212]), + 6213: (DL_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 6003, + Hard, + 'mb'), + Same, + Same, + NA, + 6214, + TTLocalizer.QuestDialogDict[6213]), + 6214: (DL_TIER, + Cont, + (RecoverItemQuest, + Anywhere, + 1, + 6004, + VeryHard, + 'pp'), + Same, + Same, + 101, + NA, + TTLocalizer.QuestDialogDict[6214]), + 6221: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9119, + NA, + 6222, + TTLocalizer.QuestDialogDict[6221]), + 6222: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 50, + 'c'), + 9119, + Same, + 102, + NA, + TTLocalizer.QuestDialogDict[6222]), + 6231: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9203, + NA, + 6232, + TTLocalizer.QuestDialogDict[6231]), + 6232: (DL_TIER, + Start, + (VisitQuest,), + 9203, + 9103, + NA, + 6233, + TTLocalizer.QuestDialogDict[6232]), + 6233: (DL_TIER, + Cont, + (CogTrackQuest, + ToontownGlobals.CashbotHQ, + 10, + 'm'), + Same, + Same, + NA, + 6234, + TTLocalizer.QuestDialogDict[6233]), + 6234: (DL_TIER, + Cont, + (VisitQuest,), + Same, + 9203, + NA, + 6235, + TTLocalizer.QuestDialogDict[6234]), + 6235: (DL_TIER, + Cont, + (RecoverItemQuest, + ToontownGlobals.CashbotHQ, + 1, + 6001, + VeryHard, + 'm', + 'track'), + Same, + Same, + 4000, + NA, + TTLocalizer.QuestDialogDict[6235]), + 6241: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9219, + NA, + 6242, + TTLocalizer.QuestDialogDict[6241]), + 6242: (DL_TIER, + Start, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 25, + 'nc'), + 9219, + Same, + 705, + NA, + TTLocalizer.QuestDialogDict[6242]), + 6251: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9221, + NA, + 6252, + TTLocalizer.QuestDialogDict[6251]), + 6252: (DL_TIER, + Start, + (DeliverItemQuest, 6006), + 9221, + 9222, + NA, + 6253, + TTLocalizer.QuestDialogDict[6252]), + 6253: (DL_TIER, + Cont, + (VisitQuest,), + Same, + 9221, + NA, + 6254, + TTLocalizer.QuestDialogDict[6253]), + 6254: (DL_TIER, + Cont, + (DeliverItemQuest, 6007), + Same, + 9210, + NA, + 6255, + TTLocalizer.QuestDialogDict[6254]), + 6255: (DL_TIER, + Cont, + (CogTrackQuest, + Anywhere, + 70, + 'm'), + Same, + Same, + NA, + 6256, + TTLocalizer.QuestDialogDict[6255]), + 6256: (DL_TIER, + Cont, + (VisitQuest,), + Same, + 9221, + NA, + 6257, + TTLocalizer.QuestDialogDict[6256]), + 6257: (DL_TIER, + Cont, + (DeliverItemQuest, 6008), + Same, + 9205, + NA, + 6258, + TTLocalizer.QuestDialogDict[6257]), + 6258: (DL_TIER, + Cont, + (CogQuest, + Anywhere, + 25, + 'ms'), + Same, + Same, + NA, + 6259, + TTLocalizer.QuestDialogDict[6258]), + 6259: (DL_TIER, + Cont, + (VisitQuest,), + Same, + 9221, + NA, + 6260, + TTLocalizer.QuestDialogDict[6259]), + 6260: (DL_TIER, + Cont, + (DeliverItemQuest, 6009), + Same, + 9229, + NA, + 6261, + TTLocalizer.QuestDialogDict[6260]), + 6261: (DL_TIER, + Cont, + (VisitQuest,), + Same, + 9221, + NA, + 6262, + TTLocalizer.QuestDialogDict[6261]), + 6262: (DL_TIER, + Cont, + (DeliverItemQuest, 6010), + Same, + 9126, + NA, + 6263, + TTLocalizer.QuestDialogDict[6262]), + 6263: (DL_TIER, + Cont, + (DeliverItemQuest, 6010), + Same, + 9112, + NA, + 6264, + TTLocalizer.QuestDialogDict[6263]), + 6264: (DL_TIER, + Cont, + (DeliverItemQuest, 6011), + Same, + 9221, + NA, + 6265, + TTLocalizer.QuestDialogDict[6264]), + 6265: (DL_TIER, + Cont, + (DeliverItemQuest, 6012), + Same, + 9115, + NA, + 6266, + TTLocalizer.QuestDialogDict[6265]), + 6266: (DL_TIER, + Cont, + (VisitQuest,), + Same, + 9221, + 103, + NA, + TTLocalizer.QuestDialogDict[6266]), + 6271: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9208, + NA, + 6272, + TTLocalizer.QuestDialogDict[6271]), + 6272: (DL_TIER, + Start, + (BuildingQuest, + ToontownGlobals.DonaldsDreamland, + 2, + 'm', + 5), + 9208, + Same, + 305, + NA, + TTLocalizer.QuestDialogDict[6272]), + 6281: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9123, + NA, + 6282, + TTLocalizer.QuestDialogDict[6281]), + 6282: (DL_TIER, + Start, + (CogTrackQuest, + ToontownGlobals.DonaldsDreamland, + 55, + 'm'), + 9123, + Same, + 4001, + NA, + TTLocalizer.QuestDialogDict[6282]), + 6291: (DL_TIER, + Start, + (VisitQuest,), + Any, + 9226, + NA, + 6292, + TTLocalizer.QuestDialogDict[6291]), + 6292: (DL_TIER, + Start, + (RecoverItemQuest, + ToontownGlobals.DonaldsDreamland, + 1, + 6005, + VeryHard, + 'm', + 'track'), + 9226, + Same, + 4002, + NA, + TTLocalizer.QuestDialogDict[6292]), + 6301: (DL_TIER, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 40, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6302: (DL_TIER, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 45, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6303: (DL_TIER, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 50, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6304: (DL_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 30, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6305: (DL_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 20, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6306: (DL_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 15, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6307: (DL_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntA, + 12, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6308: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntB, + 10, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6309: (DL_TIER, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntC, + 8, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6310: (DL_TIER, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntA, 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6311: (DL_TIER, + OBSOLETE, + (MintQuest, ToontownGlobals.CashbotMintIntB, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6312: (DL_TIER, + OBSOLETE, + (MintQuest, ToontownGlobals.CashbotMintIntC, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6313: (DL_TIER, + Start, + (SkelecogQuest, ToontownGlobals.CashbotHQ, 20), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6314: (DL_TIER, + Start, + (SkelecogLevelQuest, + ToontownGlobals.CashbotHQ, + 10, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6315: (DL_TIER, + Start, + (SkelecogLevelQuest, + ToontownGlobals.CashbotHQ, + 6, + 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6318: (DL_TIER, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6319: (DL_TIER, + OBSOLETE, + (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6320: (DL_TIER, + OBSOLETE, + (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6321: (DL_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntA, + 10, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6322: (DL_TIER, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntA, + 8, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6323: (DL_TIER, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntA, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6324: (DL_TIER, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntA, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6325: (DL_TIER, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 6326: (DL_TIER, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7101: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 120, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7102: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 130, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7103: (DL_TIER + 1, + OBSOLETE, + (CogQuest, + Anywhere, + 140, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7104: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 160, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7105: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 180, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7106: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 200, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7107: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 70, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7108: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 60, + 'mm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7109: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + 'ds'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7110: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + 'hh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7111: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 30, + 'cr'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7112: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'tbc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7113: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 70, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7114: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 60, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7115: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + 'ms'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7116: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + 'tf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7117: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 30, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7118: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'mh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7119: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 70, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7120: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 60, + 'bc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7121: (DL_TIER + 1, + OBSOLETE, + (CogQuest, + Anywhere, + 50, + 'nc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7122: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + 'mb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7123: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 30, + 'ls'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7124: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'rb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7125: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 70, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7126: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 60, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7127: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7128: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 50, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7129: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 30, + 'le'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7130: (DL_TIER + 1, + Start, + (CogQuest, + Anywhere, + 20, + 'bw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7131: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 100, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7132: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 80, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7133: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 60, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7134: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 70, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7135: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 40, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7136: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 50, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7137: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 20, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7138: (DL_TIER + 1, + Start, + (CogLevelQuest, + Anywhere, + 30, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7139: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 100, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7140: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 100, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7141: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 100, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7142: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 100, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7143: (DL_TIER + 1, + OBSOLETE, + (CogTrackQuest, + Anywhere, + 120, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7144: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 120, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7145: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 120, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7146: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 120, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7147: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 140, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7148: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 140, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7149: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 140, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7150: (DL_TIER + 1, + Start, + (CogTrackQuest, + Anywhere, + 140, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7151: (DL_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 20, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7152: (DL_TIER + 1, + OBSOLETE, + (BuildingQuest, + Anywhere, + 10, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7153: (DL_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 10, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7154: (DL_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 10, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7155: (DL_TIER + 1, + OBSOLETE, + (BuildingQuest, + Anywhere, + 5, + 'm', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7156: (DL_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 5, + 's', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7157: (DL_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 5, + 'c', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7158: (DL_TIER + 1, + Start, + (BuildingQuest, + Anywhere, + 5, + 'l', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7160: (DL_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 35, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 7161: (DL_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 35, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 7162: (DL_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 35, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 7163: (DL_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 35, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 607, + NA, + DefaultDialog), + 7164: (DL_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 2, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 7165: (DL_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 2, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 7166: (DL_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 2, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 7167: (DL_TIER + 1, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 2, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 7170: (DL_TIER + 1, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 80, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7171: (DL_TIER + 1, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 90, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7172: (DL_TIER + 1, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 100, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7173: (DL_TIER + 1, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 50, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7174: (DL_TIER + 1, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 35, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7175: (DL_TIER + 1, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 35, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7176: (DL_TIER + 1, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 30, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7177: (DL_TIER + 1, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 20, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7178: (DL_TIER + 1, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 20, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7179: (DL_TIER + 1, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7180: (DL_TIER + 1, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7181: (DL_TIER + 1, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7182: (DL_TIER + 1, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7183: (DL_TIER + 1, + OBSOLETE, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 40), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7184: (DL_TIER + 1, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 20, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7185: (DL_TIER + 1, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 8, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7186: (DL_TIER + 1, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7187: (DL_TIER + 1, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7190: (DL_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 25, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 7191: (DL_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 25, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 7192: (DL_TIER + 1, + Start, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 25, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 7193: (DL_TIER + 1, + Start, + (SkelecogNewbieQuest, + ToontownGlobals.SellbotHQ, + 6, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 7194: (DL_TIER + 1, + Start, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 2, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 7195: (DL_TIER + 1, + Start, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 2, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 7196: (DL_TIER + 1, + Start, + (ForemanNewbieQuest, + ToontownGlobals.SellbotFactoryInt, + 2, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 7201: (DL_TIER + 1, + Start, + (VisitQuest,), + Any, + 9124, + NA, + 7202, + TTLocalizer.QuestDialogDict[7201]), + 7202: (DL_TIER + 1, + Start, + (VisitQuest,), + 9124, + 9128, + NA, + 7203, + TTLocalizer.QuestDialogDict[7202]), + 7203: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7001), + Same, + 9124, + NA, + 7204, + TTLocalizer.QuestDialogDict[7203]), + 7204: (DL_TIER + 1, + Cont, + (VisitQuest,), + Same, + 9128, + NA, + 7205, + TTLocalizer.QuestDialogDict[7204]), + 7205: (DL_TIER + 1, + Cont, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 140, + Any), + Same, + Same, + NA, + 7206, + TTLocalizer.QuestDialogDict[7205]), + 7206: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7002), + Same, + 9124, + 4003, + NA, + TTLocalizer.QuestDialogDict[7206]), + 7209: (DL_TIER + 1, + Start, + (VisitQuest,), + Any, + 9232, + NA, + 7210, + TTLocalizer.QuestDialogDict[7209]), + 7210: (DL_TIER + 1, + Start, + (VisitQuest,), + 9232, + 9101, + NA, + 7211, + TTLocalizer.QuestDialogDict[7210]), + 7211: (DL_TIER + 1, + Cont, + (CogQuest, + Anywhere, + 30, + 'nc'), + Same, + Same, + NA, + 7212, + TTLocalizer.QuestDialogDict[7211]), + 7212: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7003), + Same, + 9232, + NA, + 7213, + TTLocalizer.QuestDialogDict[7212]), + 7213: (DL_TIER + 1, + Cont, + (VisitQuest,), + Same, + 9101, + NA, + 7214, + TTLocalizer.QuestDialogDict[7213]), + 7214: (DL_TIER + 1, + Cont, + (CogTrackQuest, + Anywhere, + 65, + 'm'), + Same, + Same, + NA, + 7215, + TTLocalizer.QuestDialogDict[7214]), + 7215: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7004), + Same, + 9232, + 4004, + NA, + TTLocalizer.QuestDialogDict[7215]), + 7218: (DL_TIER + 1, + Start, + (VisitQuest,), + Any, + 9109, + NA, + 7219, + TTLocalizer.QuestDialogDict[7218]), + 7219: (DL_TIER + 1, + Start, + (VisitQuest,), + 9109, + 9230, + NA, + 7220, + TTLocalizer.QuestDialogDict[7219]), + 7220: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7005), + Same, + 9109, + NA, + 7221, + TTLocalizer.QuestDialogDict[7220]), + 7221: (DL_TIER + 1, + Cont, + (VisitQuest,), + Same, + 9230, + NA, + 7222, + TTLocalizer.QuestDialogDict[7221]), + 7222: (DL_TIER + 1, + Cont, + (BuildingQuest, + Anywhere, + 10, + Any, + 3), + Same, + Same, + NA, + 7223, + TTLocalizer.QuestDialogDict[7222]), + 7223: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7006), + Same, + 9109, + 4005, + NA, + TTLocalizer.QuestDialogDict[7223]), + 7226: (DL_TIER + 1, + Start, + (VisitQuest,), + Any, + 9224, + NA, + 7227, + TTLocalizer.QuestDialogDict[7226]), + 7227: (DL_TIER + 1, + Start, + (VisitQuest,), + 9224, + 9102, + NA, + 7228, + TTLocalizer.QuestDialogDict[7227]), + 7228: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7007), + Same, + 9224, + NA, + 7229, + TTLocalizer.QuestDialogDict[7228]), + 7229: (DL_TIER + 1, + Cont, + (VisitQuest,), + Same, + 9102, + NA, + 7230, + TTLocalizer.QuestDialogDict[7229]), + 7230: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7008), + Same, + 9224, + NA, + 7231, + TTLocalizer.QuestDialogDict[7230]), + 7231: (DL_TIER + 1, + Cont, + (VisitQuest,), + Same, + 9102, + NA, + 7232, + TTLocalizer.QuestDialogDict[7231]), + 7232: (DL_TIER + 1, + Cont, + (VisitQuest,), + Same, + 9216, + NA, + 7233, + TTLocalizer.QuestDialogDict[7232]), + 7233: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7009), + Same, + 9224, + NA, + 7234, + TTLocalizer.QuestDialogDict[7233]), + 7234: (DL_TIER + 1, + Cont, + (VisitQuest,), + Same, + 9216, + NA, + 7235, + TTLocalizer.QuestDialogDict[7234]), + 7235: (DL_TIER + 1, + Cont, + (BuildingQuest, + Anywhere, + 5, + 'm', + 5), + Same, + Same, + NA, + 7236, + TTLocalizer.QuestDialogDict[7235]), + 7236: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7010), + Same, + 9224, + 4006, + NA, + TTLocalizer.QuestDialogDict[7236]), + 7239: (DL_TIER + 1, + Start, + (VisitQuest,), + Any, + 9114, + NA, + 7240, + TTLocalizer.QuestDialogDict[7239]), + 7240: (DL_TIER + 1, + Start, + (VisitQuest,), + 9114, + 9215, + NA, + 7241, + TTLocalizer.QuestDialogDict[7240]), + 7241: (DL_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.DonaldsDreamland, + 1, + 7011, + Hard, + AnyFish), + Same, + Same, + NA, + 7242, + TTLocalizer.QuestDialogDict[7241]), + 7242: (DL_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.DonaldsDreamland, + 1, + 7012, + VeryHard, + AnyFish), + Same, + Same, + NA, + 7243, + TTLocalizer.QuestDialogDict[7242]), + 7243: (DL_TIER + 1, + Cont, + (RecoverItemQuest, + ToontownGlobals.CashbotHQ, + 1, + 7013, + Hard, + 'ls'), + Same, + Same, + NA, + 7244, + TTLocalizer.QuestDialogDict[7243]), + 7244: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 7014), + Same, + 9114, + 4007, + NA, + TTLocalizer.QuestDialogDict[7244]), + 7250: (DL_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 80, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7251: (DL_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 90, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7252: (DL_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 100, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7253: (DL_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 50, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7254: (DL_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 35, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7255: (DL_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 35, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7256: (DL_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntA, + 30, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7257: (DL_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntB, + 25, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7258: (DL_TIER + 1, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntC, + 20, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7259: (DL_TIER + 1, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntA, 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7260: (DL_TIER + 1, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntB, 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7261: (DL_TIER + 1, + OBSOLETE, + (MintQuest, ToontownGlobals.CashbotMintIntC, 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7262: (DL_TIER + 1, + Start, + (SkelecogQuest, ToontownGlobals.CashbotHQ, 30), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7263: (DL_TIER + 1, + Start, + (SkelecogLevelQuest, + ToontownGlobals.CashbotHQ, + 20, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7264: (DL_TIER + 1, + Start, + (SkelecogLevelQuest, + ToontownGlobals.CashbotHQ, + 10, + 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7265: (DL_TIER + 1, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7266: (DL_TIER + 1, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7267: (DL_TIER + 1, + OBSOLETE, + (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7268: (DL_TIER + 1, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntB, + 20, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7269: (DL_TIER + 1, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntB, 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7270: (DL_TIER + 1, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 7500: (DL_TIER + 1, + Start, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 100, + Any), + Any, + ToonHQ, + NA, + 7501, + DefaultDialog), + 7501: (DL_TIER + 1, + Cont, + (DeliverItemQuest, 1000), + Any, + 9010, + 1000, + NA, + DefaultDialog), + 8101: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 240, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8102: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 260, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8103: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 280, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8104: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 320, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8105: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 360, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8106: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 400, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8107: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 140, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8108: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 120, + 'mm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8109: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'ds'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8110: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'hh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8111: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 60, + 'cr'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8112: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 40, + 'tbc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8113: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 140, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8114: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 120, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8115: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'ms'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8116: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'tf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8117: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 60, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8118: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 40, + 'mh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8119: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 140, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8120: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 120, + 'bc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8121: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'nc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8122: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'mb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8123: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 60, + 'ls'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8124: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 40, + 'rb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8125: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 140, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8126: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 120, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8127: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8128: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 100, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8129: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 60, + 'le'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8130: (DL_TIER + 2, + Start, + (CogQuest, + Anywhere, + 40, + 'bw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8131: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 160, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8132: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 200, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8133: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 120, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8134: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 140, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8135: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 80, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8136: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 100, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8137: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 40, + 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8138: (DL_TIER + 2, + Start, + (CogLevelQuest, + Anywhere, + 60, + 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8139: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 200, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8140: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 200, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8141: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 200, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8142: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 200, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8143: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 250, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8144: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 250, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8145: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 250, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8146: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 250, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8147: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 300, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8148: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 300, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8149: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 300, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8150: (DL_TIER + 2, + Start, + (CogTrackQuest, + Anywhere, + 300, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8151: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 40, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8152: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 20, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8153: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 20, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8154: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 20, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8155: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 10, + 'm', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8156: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 10, + 's', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8157: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 10, + 'c', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8158: (DL_TIER + 2, + Start, + (BuildingQuest, + Anywhere, + 10, + 'l', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8160: (DL_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 40, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 8161: (DL_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 40, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 8162: (DL_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 40, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 8163: (DL_TIER + 2, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 40, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 608, + NA, + DefaultDialog), + 8164: (DL_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 8165: (DL_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 8166: (DL_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 8167: (DL_TIER + 2, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 8170: (DL_TIER + 2, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 160, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8171: (DL_TIER + 2, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 180, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8172: (DL_TIER + 2, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 200, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8173: (DL_TIER + 2, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 100, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8174: (DL_TIER + 2, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 70, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8175: (DL_TIER + 2, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 70, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8176: (DL_TIER + 2, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 60, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8177: (DL_TIER + 2, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 40, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8178: (DL_TIER + 2, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 40, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8179: (DL_TIER + 2, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8180: (DL_TIER + 2, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 16), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8181: (DL_TIER + 2, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8182: (DL_TIER + 2, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 16), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8183: (DL_TIER + 2, + OBSOLETE, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 80), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8184: (DL_TIER + 2, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 40, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8185: (DL_TIER + 2, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 16, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8186: (DL_TIER + 2, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8187: (DL_TIER + 2, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 16), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8188: (DL_TIER + 2, + OBSOLETE, + (VPQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8189: (DL_TIER + 2, + OBSOLETE, + (RescueQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8190: (DL_TIER + 2, + OBSOLETE, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 30, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8191: (DL_TIER + 2, + OBSOLETE, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 30, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8192: (DL_TIER + 2, + OBSOLETE, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 30, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8193: (DL_TIER + 2, + OBSOLETE, + (SkelecogNewbieQuest, + ToontownGlobals.SellbotHQ, + 8, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8194: (DL_TIER + 2, + OBSOLETE, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 3, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8195: (DL_TIER + 2, + OBSOLETE, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 3, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8196: (DL_TIER + 2, + OBSOLETE, + (ForemanNewbieQuest, + ToontownGlobals.SellbotFactoryInt, + 3, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8197: (DL_TIER + 2, + OBSOLETE, + (VPNewbieQuest, + ToontownGlobals.SellbotHQ, + 1, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8198: (DL_TIER + 2, + OBSOLETE, + (RescueNewbieQuest, + ToontownGlobals.SellbotHQ, + 1, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 610, + NA, + DefaultDialog), + 8201: (DL_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 160, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8202: (DL_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 180, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8203: (DL_TIER + 2, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 200, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8204: (DL_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 100, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8205: (DL_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 90, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8206: (DL_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 80, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8207: (DL_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntA, + 60, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8208: (DL_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntB, + 50, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8209: (DL_TIER + 2, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntC, + 40, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8210: (DL_TIER + 2, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntA, 16), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8211: (DL_TIER + 2, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntB, 14), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8212: (DL_TIER + 2, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntC, 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8213: (DL_TIER + 2, + Start, + (SkelecogQuest, ToontownGlobals.CashbotMintIntA, 80), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8214: (DL_TIER + 2, + Start, + (SkelecogQuest, ToontownGlobals.CashbotMintIntB, 60), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8215: (DL_TIER + 2, + Start, + (SkelecogQuest, ToontownGlobals.CashbotMintIntC, 40), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8216: (DL_TIER + 2, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 16), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8217: (DL_TIER + 2, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 14), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8218: (DL_TIER + 2, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 8219: (DL_TIER + 2, + Start, + (CFOQuest, ToontownGlobals.CashbotHQ, 2), + Any, + ToonHQ, + 621, + NA, + DefaultDialog), + 9101: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 500, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9102: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 600, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9103: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 700, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9104: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 800, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9105: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 900, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9106: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 1000, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9107: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 300, + 'ym'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9108: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 250, + 'mm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9109: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'ds'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9110: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'hh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9111: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 120, + 'cr'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9112: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 80, + 'tbc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9113: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 280, + 'nd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9114: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 240, + 'gh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9115: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'ms'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9116: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'tf'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9117: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 120, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9118: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 80, + 'mh'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9119: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 280, + 'tw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9120: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 240, + 'bc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9121: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'nc'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9122: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'mb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9123: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 120, + 'ls'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9124: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 80, + 'rb'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9125: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 280, + 'dt'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9126: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 240, + 'ac'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9127: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'bs'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9128: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 200, + 'sd'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9129: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 120, + 'le'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9130: (DL_TIER + 3, + Start, + (CogQuest, + Anywhere, + 80, + 'bw'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9131: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 320, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9132: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 400, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9133: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 240, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9134: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 280, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9135: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 160, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9136: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 200, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9137: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 80, + 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9138: (DL_TIER + 3, + Start, + (CogLevelQuest, + Anywhere, + 120, + 12), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9139: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 400, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9140: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 400, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9141: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 400, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9142: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 400, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9143: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 500, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9144: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 500, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9145: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 500, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9146: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 500, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9147: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 600, + 'm'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9148: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 600, + 's'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9149: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 600, + 'c'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9150: (DL_TIER + 3, + Start, + (CogTrackQuest, + Anywhere, + 600, + 'l'), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9151: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 400, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9152: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 200, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9153: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 200, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9154: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 200, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9155: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 100, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9156: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 100, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9157: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 100, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9158: (DL_TIER + 3, + Start, + (BuildingQuest, + Anywhere, + 100, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9160: (DL_TIER + 3, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 45, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9161: (DL_TIER + 3, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 45, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9162: (DL_TIER + 3, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 45, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9163: (DL_TIER + 3, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 45, + Any, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9164: (DL_TIER + 3, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9165: (DL_TIER + 3, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9166: (DL_TIER + 3, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9167: (DL_TIER + 3, + Start, + (BuildingNewbieQuest, + Anywhere, + 1, + Any, + 3, + NEWBIE_HP), + Any, + ToonHQ, + 609, + NA, + DefaultDialog), + 9170: (DL_TIER + 3, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 350, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9171: (DL_TIER + 3, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 400, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9172: (DL_TIER + 3, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 500, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9173: (DL_TIER + 3, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 200, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9174: (DL_TIER + 3, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 150, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9175: (DL_TIER + 3, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotHQ, + 150, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9176: (DL_TIER + 3, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 150, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9177: (DL_TIER + 3, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 100, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9178: (DL_TIER + 3, + OBSOLETE, + (CogLevelQuest, + ToontownGlobals.SellbotFactoryInt, + 100, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9179: (DL_TIER + 3, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 25), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9180: (DL_TIER + 3, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 35), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9181: (DL_TIER + 3, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 25), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9182: (DL_TIER + 3, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 35), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9183: (DL_TIER + 3, + OBSOLETE, + (SkelecogQuest, ToontownGlobals.SellbotFactoryInt, 150), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9184: (DL_TIER + 3, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 80, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9185: (DL_TIER + 3, + OBSOLETE, + (SkelecogLevelQuest, + ToontownGlobals.SellbotHQ, + 32, + 6), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9186: (DL_TIER + 3, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 25), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9187: (DL_TIER + 3, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 35), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9188: (DL_TIER + 3, + OBSOLETE, + (VPQuest, ToontownGlobals.SellbotHQ, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9189: (DL_TIER + 3, + OBSOLETE, + (RescueQuest, ToontownGlobals.SellbotHQ, 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9190: (DL_TIER + 3, + OBSOLETE, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 35, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9191: (DL_TIER + 3, + OBSOLETE, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 35, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9192: (DL_TIER + 3, + OBSOLETE, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 35, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9193: (DL_TIER + 3, + OBSOLETE, + (SkelecogNewbieQuest, + ToontownGlobals.SellbotHQ, + 10, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9194: (DL_TIER + 3, + OBSOLETE, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 4, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9195: (DL_TIER + 3, + OBSOLETE, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 4, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9196: (DL_TIER + 3, + OBSOLETE, + (ForemanNewbieQuest, + ToontownGlobals.SellbotFactoryInt, + 4, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9197: (DL_TIER + 3, + OBSOLETE, + (VPNewbieQuest, + ToontownGlobals.SellbotHQ, + 2, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9198: (DL_TIER + 3, + OBSOLETE, + (RescueNewbieQuest, + ToontownGlobals.SellbotHQ, + 2, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9201: (DL_TIER + 3, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 350, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9202: (DL_TIER + 3, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 400, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9203: (DL_TIER + 3, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 450, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9204: (DL_TIER + 3, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 200, + 7), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9205: (DL_TIER + 3, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 150, + 8), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9206: (DL_TIER + 3, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotHQ, + 100, + 9), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9207: (DL_TIER + 3, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntA, + 200, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9208: (DL_TIER + 3, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntB, + 150, + 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9209: (DL_TIER + 3, + Start, + (CogLevelQuest, + ToontownGlobals.CashbotMintIntC, + 100, + 11), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9210: (DL_TIER + 3, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntA, 35), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9211: (DL_TIER + 3, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntB, 30), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9212: (DL_TIER + 3, + Start, + (MintQuest, ToontownGlobals.CashbotMintIntC, 25), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9213: (DL_TIER + 3, + Start, + (SkelecogQuest, ToontownGlobals.CashbotMintIntA, 150), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9214: (DL_TIER + 3, + Start, + (SkelecogQuest, ToontownGlobals.CashbotMintIntB, 100), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9215: (DL_TIER + 3, + Start, + (SkelecogQuest, ToontownGlobals.CashbotMintIntC, 50), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9216: (DL_TIER + 3, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntA, 35), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9217: (DL_TIER + 3, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntB, 30), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9218: (DL_TIER + 3, + Start, + (SupervisorQuest, ToontownGlobals.CashbotMintIntC, 25), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 9219: (DL_TIER + 3, + Start, + (CFOQuest, ToontownGlobals.CashbotHQ, 3), + Any, + ToonHQ, + 622, + NA, + DefaultDialog), + 9220: (DL_TIER + 3, + Start, + (CogNewbieQuest, + ToontownGlobals.CashbotMintIntA, + 35, + Any, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9221: (DL_TIER + 3, + Start, + (CogNewbieQuest, + ToontownGlobals.CashbotMintIntB, + 30, + Any, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9222: (DL_TIER + 3, + Start, + (CogNewbieQuest, + ToontownGlobals.CashbotMintIntC, + 25, + Any, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9223: (DL_TIER + 3, + Start, + (SkelecogNewbieQuest, + ToontownGlobals.CashbotHQ, + 10, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9224: (DL_TIER + 3, + Start, + (MintNewbieQuest, + ToontownGlobals.CashbotMintIntA, + 6, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9225: (DL_TIER + 3, + Start, + (MintNewbieQuest, + ToontownGlobals.CashbotMintIntB, + 4, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9226: (DL_TIER + 3, + Start, + (MintNewbieQuest, + ToontownGlobals.CashbotMintIntC, + 2, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9227: (DL_TIER + 3, + Start, + (SupervisorNewbieQuest, + ToontownGlobals.CashbotMintIntA, + 6, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9228: (DL_TIER + 3, + Start, + (SupervisorNewbieQuest, + ToontownGlobals.CashbotMintIntB, + 4, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9229: (DL_TIER + 3, + Start, + (SupervisorNewbieQuest, + ToontownGlobals.CashbotMintIntC, + 2, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 9500: (DL_TIER + 3, + Start, + (CogQuest, + ToontownGlobals.DonaldsDreamland, + 1000, + Any), + Any, + ToonHQ, + NA, + 9501, + DefaultDialog), + 9501: (DL_TIER + 3, + Cont, + (DeliverItemQuest, 1000), + Any, + 2004, + 1000, + NA, + DefaultDialog), + 10001: (ELDER_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 50, + Any, + NEWBIE_HP), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10002: (ELDER_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 4, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10100: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 80, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10101: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 100, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10102: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 120, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10103: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 200, + Any), + Any, + ToonHQ, + 613, + NA, + DefaultDialog), + 10104: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 250, + Any), + Any, + ToonHQ, + 615, + NA, + DefaultDialog), + 10105: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 300, + Any), + Any, + ToonHQ, + 616, + NA, + DefaultDialog), + 10106: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 400, + Any), + Any, + ToonHQ, + 618, + NA, + DefaultDialog), + 10110: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 40, + Any, + 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10111: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 30, + Any, + 3), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10112: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 25, + Any, + 4), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10113: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 20, + Any, + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10114: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 20, + 'm', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10115: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 20, + 's', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10116: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 20, + 'c', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10117: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 20, + 'l', + 5), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10118: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 50, + Any, + 1), + Any, + ToonHQ, + 620, + NA, + DefaultDialog), + 10120: (ELDER_TIER, + OBSOLETE, + (CogQuest, + ToontownGlobals.SellbotHQ, + 60, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10121: (ELDER_TIER, + OBSOLETE, + (FactoryQuest, ToontownGlobals.SellbotHQ, 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10122: (ELDER_TIER, + OBSOLETE, + (ForemanQuest, ToontownGlobals.SellbotHQ, 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10123: (ELDER_TIER, + OBSOLETE, + (VPQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10124: (ELDER_TIER, + OBSOLETE, + (RescueQuest, ToontownGlobals.SellbotHQ, 2), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10130: (ELDER_TIER, + OBSOLETE, + (CogNewbieQuest, + ToontownGlobals.SellbotHQ, + 40, + Any, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10131: (ELDER_TIER, + OBSOLETE, + (FactoryNewbieQuest, + ToontownGlobals.SellbotHQ, + 3, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10132: (ELDER_TIER, + OBSOLETE, + (VPNewbieQuest, + ToontownGlobals.SellbotHQ, + 1, + SELLBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10140: (ELDER_TIER, + Start, + (CogQuest, + ToontownGlobals.CashbotHQ, + 60, + Any), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10141: (ELDER_TIER, + Start, + (MintQuest, ToontownGlobals.CashbotHQ, 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10142: (ELDER_TIER, + Start, + (SupervisorQuest, ToontownGlobals.CashbotHQ, 10), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10143: (ELDER_TIER, + Start, + (CFOQuest, ToontownGlobals.CashbotHQ, 2), + Any, + ToonHQ, + 623, + NA, + DefaultDialog), + 10145: (ELDER_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.CashbotHQ, + 40, + Any, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10146: (ELDER_TIER, + Start, + (MintNewbieQuest, + ToontownGlobals.CashbotHQ, + 3, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + Any, + NA, + DefaultDialog), + 10147: (ELDER_TIER, + Start, + (SupervisorNewbieQuest, + ToontownGlobals.CashbotHQ, + 3, + CASHBOT_HQ_NEWBIE_HP), + Any, + ToonHQ, + 611, + NA, + DefaultDialog), + 10200: (ELDER_TIER, + Start, + (CogQuest, + Anywhere, + 100, + Any), + Any, + ToonHQ, + NA, + 10201, + DefaultDialog), + 10201: (ELDER_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + ToonTailor, + 1000, + NA, + DefaultDialog), + 10202: (ELDER_TIER, + Start, + (BuildingQuest, + Anywhere, + 25, + Any, + 1), + Any, + ToonHQ, + NA, + 10203, + DefaultDialog), + 10203: (ELDER_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + ToonTailor, + 1000, + NA, + DefaultDialog), + 10204: (ELDER_TIER, + Start, + (CogNewbieQuest, + ToontownGlobals.ToontownCentral, + 60, + Any, + NEWBIE_HP), + Any, + ToonHQ, + NA, + 10205, + DefaultDialog), + 10205: (ELDER_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + ToonTailor, + 1000, + NA, + DefaultDialog), + 10206: (ELDER_TIER, + Start, + (BuildingNewbieQuest, + Anywhere, + 4, + Any, + 1, + NEWBIE_HP), + Any, + ToonHQ, + NA, + 10207, + DefaultDialog), + 10207: (ELDER_TIER, + Cont, + (DeliverItemQuest, 1000), + Any, + ToonTailor, + 1000, + NA, + DefaultDialog), + 11000: (LAWBOT_HQ_TIER, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11001, + TTLocalizer.QuestDialogDict[11000]), + 11001: (LAWBOT_HQ_TIER, + Start, + (RecoverItemQuest, + ToontownGlobals.SillyStreet, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4100, + NA, + TTLocalizer.QuestDialogDict[11001]), + 11002: (LAWBOT_HQ_TIER + 1, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11003, + TTLocalizer.QuestDialogDict[11002]), + 11003: (LAWBOT_HQ_TIER + 1, + Start, + (RecoverItemQuest, + ToontownGlobals.LoopyLane, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4101, + NA, + TTLocalizer.QuestDialogDict[11003]), + 11004: (LAWBOT_HQ_TIER + 2, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11005, + TTLocalizer.QuestDialogDict[11004]), + 11005: (LAWBOT_HQ_TIER + 2, + Start, + (RecoverItemQuest, + ToontownGlobals.PunchlinePlace, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4102, + NA, + TTLocalizer.QuestDialogDict[11005]), + 11006: (LAWBOT_HQ_TIER + 3, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11007, + TTLocalizer.QuestDialogDict[11006]), + 11007: (LAWBOT_HQ_TIER + 3, + Start, + (RecoverItemQuest, + ToontownGlobals.BarnacleBoulevard, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4103, + NA, + TTLocalizer.QuestDialogDict[11007]), + 11008: (LAWBOT_HQ_TIER + 4, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11009, + TTLocalizer.QuestDialogDict[11008]), + 11009: (LAWBOT_HQ_TIER + 4, + Start, + (RecoverItemQuest, + ToontownGlobals.SeaweedStreet, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4104, + NA, + TTLocalizer.QuestDialogDict[11009]), + 11010: (LAWBOT_HQ_TIER + 5, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11011, + TTLocalizer.QuestDialogDict[11010]), + 11011: (LAWBOT_HQ_TIER + 5, + Start, + (RecoverItemQuest, + ToontownGlobals.LighthouseLane, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4105, + NA, + TTLocalizer.QuestDialogDict[11011]), + 11012: (LAWBOT_HQ_TIER + 6, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11013, + TTLocalizer.QuestDialogDict[11012]), + 11013: (LAWBOT_HQ_TIER + 6, + Start, + (RecoverItemQuest, + ToontownGlobals.ElmStreet, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4106, + NA, + TTLocalizer.QuestDialogDict[11013]), + 11014: (LAWBOT_HQ_TIER + 7, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11015, + TTLocalizer.QuestDialogDict[11014]), + 11015: (LAWBOT_HQ_TIER + 7, + Start, + (RecoverItemQuest, + ToontownGlobals.MapleStreet, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4107, + NA, + TTLocalizer.QuestDialogDict[11015]), + 11016: (LAWBOT_HQ_TIER + 8, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11017, + TTLocalizer.QuestDialogDict[11016]), + 11017: (LAWBOT_HQ_TIER + 8, + Start, + (RecoverItemQuest, + ToontownGlobals.OakStreet, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4108, + NA, + TTLocalizer.QuestDialogDict[11017]), + 11018: (LAWBOT_HQ_TIER + 9, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11019, + TTLocalizer.QuestDialogDict[11018]), + 11019: (LAWBOT_HQ_TIER + 9, + Start, + (RecoverItemQuest, + ToontownGlobals.AltoAvenue, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4109, + NA, + TTLocalizer.QuestDialogDict[11019]), + 11020: (LAWBOT_HQ_TIER + 10, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11021, + TTLocalizer.QuestDialogDict[11020]), + 11021: (LAWBOT_HQ_TIER + 10, + Start, + (RecoverItemQuest, + ToontownGlobals.BaritoneBoulevard, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4110, + NA, + TTLocalizer.QuestDialogDict[11021]), + 11022: (LAWBOT_HQ_TIER + 11, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11023, + TTLocalizer.QuestDialogDict[11022]), + 11023: (LAWBOT_HQ_TIER + 11, + Start, + (RecoverItemQuest, + ToontownGlobals.TenorTerrace, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4111, + NA, + TTLocalizer.QuestDialogDict[11023]), + 11024: (LAWBOT_HQ_TIER + 12, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11025, + TTLocalizer.QuestDialogDict[11024]), + 11025: (LAWBOT_HQ_TIER + 12, + Start, + (RecoverItemQuest, + ToontownGlobals.LullabyLane, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4112, + NA, + TTLocalizer.QuestDialogDict[11025]), + 11026: (LAWBOT_HQ_TIER + 13, + Start, + (VisitQuest,), + Any, + 3310, + NA, + 11027, + TTLocalizer.QuestDialogDict[11026]), + 11027: (LAWBOT_HQ_TIER + 13, + Start, + (RecoverItemQuest, + ToontownGlobals.PajamaPlace, + 1, + 3027, + Hard, + Any), + Same, + Same, + 4113, + NA, + TTLocalizer.QuestDialogDict[11027]), + 12000: (BOSSBOT_HQ_TIER, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12001, + TTLocalizer.QuestDialogDict[12000]), + 12001: (BOSSBOT_HQ_TIER, + Start, + (CogQuest, + Anywhere, + 1, + 'f'), + Same, + Same, + 4200, + NA, + TTLocalizer.QuestDialogDict[12001]), + 12002: (BOSSBOT_HQ_TIER + 1, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12003, + TTLocalizer.QuestDialogDict[12002]), + 12003: (BOSSBOT_HQ_TIER + 1, + Start, + (CogQuest, + Anywhere, + 1, + 'p'), + Same, + Same, + 4201, + NA, + TTLocalizer.QuestDialogDict[12003]), + 12004: (BOSSBOT_HQ_TIER + 2, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12005, + TTLocalizer.QuestDialogDict[12004]), + 12005: (BOSSBOT_HQ_TIER + 2, + Start, + (CogQuest, + Anywhere, + 1, + 'ym'), + Same, + Same, + 4202, + NA, + TTLocalizer.QuestDialogDict[12005]), + 12006: (BOSSBOT_HQ_TIER + 3, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12007, + TTLocalizer.QuestDialogDict[12006]), + 12007: (BOSSBOT_HQ_TIER + 3, + Start, + (CogQuest, + Anywhere, + 1, + 'mm'), + Same, + Same, + 4203, + NA, + TTLocalizer.QuestDialogDict[12007]), + 12008: (BOSSBOT_HQ_TIER + 4, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12009, + TTLocalizer.QuestDialogDict[12008]), + 12009: (BOSSBOT_HQ_TIER + 4, + Start, + (CogQuest, + Anywhere, + 1, + 'ds'), + Same, + Same, + 4204, + NA, + TTLocalizer.QuestDialogDict[12009]), + 12010: (BOSSBOT_HQ_TIER + 5, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12011, + TTLocalizer.QuestDialogDict[12010]), + 12011: (BOSSBOT_HQ_TIER + 5, + Start, + (CogQuest, + Anywhere, + 1, + 'hh'), + Same, + Same, + 4205, + NA, + TTLocalizer.QuestDialogDict[12011]), + 12012: (BOSSBOT_HQ_TIER + 6, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12013, + TTLocalizer.QuestDialogDict[12012]), + 12013: (BOSSBOT_HQ_TIER + 6, + Start, + (CogQuest, + Anywhere, + 1, + 'cr'), + Same, + Same, + 4206, + NA, + TTLocalizer.QuestDialogDict[12013]), + 12014: (BOSSBOT_HQ_TIER + 7, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12015, + TTLocalizer.QuestDialogDict[12014]), + 12015: (BOSSBOT_HQ_TIER + 7, + Start, + (CogQuest, + Anywhere, + 1, + 'tbc'), + Same, + Same, + 4207, + NA, + TTLocalizer.QuestDialogDict[12015]), + 12016: (BOSSBOT_HQ_TIER + 8, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12017, + TTLocalizer.QuestDialogDict[12016]), + 12017: (BOSSBOT_HQ_TIER + 8, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4208, + NA, + TTLocalizer.QuestDialogDict[12017]), + 12018: (BOSSBOT_HQ_TIER + 9, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12019, + TTLocalizer.QuestDialogDict[12018]), + 12019: (BOSSBOT_HQ_TIER + 9, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4209, + NA, + TTLocalizer.QuestDialogDict[12019]), + 12020: (BOSSBOT_HQ_TIER + 10, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12021, + TTLocalizer.QuestDialogDict[12020]), + 12021: (BOSSBOT_HQ_TIER + 10, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4210, + NA, + TTLocalizer.QuestDialogDict[12021]), + 12022: (BOSSBOT_HQ_TIER + 11, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12023, + TTLocalizer.QuestDialogDict[12022]), + 12023: (BOSSBOT_HQ_TIER + 11, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4211, + NA, + TTLocalizer.QuestDialogDict[12023]), + 12024: (BOSSBOT_HQ_TIER + 12, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12025, + TTLocalizer.QuestDialogDict[12024]), + 12025: (BOSSBOT_HQ_TIER + 12, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4212, + NA, + TTLocalizer.QuestDialogDict[12025]), + 12026: (BOSSBOT_HQ_TIER + 13, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12027, + TTLocalizer.QuestDialogDict[12026]), + 12027: (BOSSBOT_HQ_TIER + 13, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4213, + NA, + TTLocalizer.QuestDialogDict[12027]), + 12028: (BOSSBOT_HQ_TIER + 14, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12029, + TTLocalizer.QuestDialogDict[12028]), + 12029: (BOSSBOT_HQ_TIER + 14, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4214, + NA, + TTLocalizer.QuestDialogDict[12029]), + 12030: (BOSSBOT_HQ_TIER + 15, + Start, + (VisitQuest,), + Any, + 1222, + NA, + 12031, + TTLocalizer.QuestDialogDict[12030]), + 12031: (BOSSBOT_HQ_TIER + 15, + Start, + (SkeleReviveQuest, Anywhere, 1), + Same, + Same, + 4215, + NA, + TTLocalizer.QuestDialogDict[12031]), + 12032: (BOSSBOT_HQ_TIER + 16, + Start, + (VisitQuest,), + Any, + 2001, + 4216, + NA, + TTLocalizer.QuestDialogDict[12032])} + +Tier2QuestsDict = {} +for questId, questDesc in QuestDict.items(): + if questDesc[QuestDictStartIndex] == Start: + tier = questDesc[QuestDictTierIndex] + if Tier2QuestsDict.has_key(tier): + Tier2QuestsDict[tier].append(questId) + else: + Tier2QuestsDict[tier] = [questId] + +Quest2RewardDict = {} +Tier2Reward2QuestsDict = {} +Quest2RemainingStepsDict = {} + +def getAllRewardIdsForReward(rewardId): + if rewardId is AnyCashbotSuitPart: + return range(4000, 4011 + 1) + if rewardId is AnyLawbotSuitPart: + return range(4100, 4113 + 1) + if rewardId is AnyBossbotSuitPart: + return range(4200, 4216 + 1) + return (rewardId,) + + +def findFinalRewardId(questId): + finalRewardId = Quest2RewardDict.get(questId) + if finalRewardId: + remainingSteps = Quest2RemainingStepsDict.get(questId) + else: + try: + questDesc = QuestDict[questId] + except KeyError: + print 'findFinalRewardId: Quest ID: %d not found' % questId + return -1 + + nextQuestId = questDesc[QuestDictNextQuestIndex] + if nextQuestId == NA: + finalRewardId = questDesc[QuestDictRewardIndex] + remainingSteps = 1 + else: + if type(nextQuestId) == type(()): + finalRewardId, remainingSteps = findFinalRewardId(nextQuestId[0]) + for id in nextQuestId[1:]: + findFinalRewardId(id) + + else: + finalRewardId, remainingSteps = findFinalRewardId(nextQuestId) + remainingSteps += 1 + if finalRewardId != OBSOLETE: + if questDesc[QuestDictStartIndex] == Start: + tier = questDesc[QuestDictTierIndex] + tier2RewardDict = Tier2Reward2QuestsDict.setdefault(tier, {}) + rewardIds = getAllRewardIdsForReward(finalRewardId) + for rewardId in rewardIds: + questList = tier2RewardDict.setdefault(rewardId, []) + questList.append(questId) + + else: + finalRewardId = None + Quest2RewardDict[questId] = finalRewardId + Quest2RemainingStepsDict[questId] = remainingSteps + return (finalRewardId, remainingSteps) + + +for questId in QuestDict.keys(): + findFinalRewardId(questId) + +def getStartingQuests(tier = None): + startingQuests = [] + for questId in QuestDict.keys(): + if isStartingQuest(questId): + if tier is None: + startingQuests.append(questId) + elif questId in Tier2QuestsDict[tier]: + startingQuests.append(questId) + + startingQuests.sort() + return startingQuests + + +def getFinalRewardId(questId, fAll = 0): + if fAll or isStartingQuest(questId): + return Quest2RewardDict.get(questId) + else: + return None + return None + + +def isStartingQuest(questId): + try: + return QuestDict[questId][QuestDictStartIndex] == Start + except KeyError: + return None + + return None + + +def getNumChoices(tier): + if tier in (0,): + return 0 + if tier in (1,): + return 2 + else: + return 3 + + +def getAvatarRewardId(av, questId): + for quest in av.quests: + if questId == quest[0]: + return quest[3] + + notify.warning('getAvatarRewardId(): quest not found on avatar') + return None + + +def getNextQuest(id, currentNpc, av): + nextQuest = QuestDict[id][QuestDictNextQuestIndex] + if nextQuest == NA: + return (NA, NA) + elif type(nextQuest) == type(()): + nextReward = QuestDict[nextQuest[0]][QuestDictRewardIndex] + nextNextQuest, nextNextToNpcId = getNextQuest(nextQuest[0], currentNpc, av) + if nextReward == 400 and nextNextQuest == NA: + nextQuest = chooseTrackChoiceQuest(av.getRewardTier(), av) + else: + nextQuest = random.choice(nextQuest) + if not getQuestClass(nextQuest).filterFunc(av): + return getNextQuest(nextQuest, currentNpc, av) + nextToNpcId = getQuestToNpcId(nextQuest) + if nextToNpcId == Any: + nextToNpcId = 2004 + elif nextToNpcId == Same: + if currentNpc.getHq(): + nextToNpcId = ToonHQ + else: + nextToNpcId = currentNpc.getNpcId() + elif nextToNpcId == ToonHQ: + nextToNpcId = ToonHQ + return (nextQuest, nextToNpcId) + + +def filterQuests(entireQuestPool, currentNpc, av): + if notify.getDebug(): + notify.debug('filterQuests: entireQuestPool: %s' % entireQuestPool) + validQuestPool = dict([ (questId, 1) for questId in entireQuestPool ]) + if isLoopingFinalTier(av.getRewardTier()): + history = map(lambda questDesc: questDesc[0], av.quests) + else: + history = av.getQuestHistory() + if notify.getDebug(): + notify.debug('filterQuests: av quest history: %s' % history) + currentQuests = av.quests + for questId in entireQuestPool: + if questId in history: + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because in history' % questId) + validQuestPool[questId] = 0 + continue + potentialFromNpc = getQuestFromNpcId(questId) + if not npcMatches(potentialFromNpc, currentNpc): + if notify.getDebug(): + notify.debug('filterQuests: Removed %s: potentialFromNpc does not match currentNpc' % questId) + validQuestPool[questId] = 0 + continue + potentialToNpc = getQuestToNpcId(questId) + if currentNpc.getNpcId() == potentialToNpc: + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because potentialToNpc is currentNpc' % questId) + validQuestPool[questId] = 0 + continue + if not getQuestClass(questId).filterFunc(av): + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because of filterFunc' % questId) + validQuestPool[questId] = 0 + continue + for quest in currentQuests: + fromNpcId = quest[1] + toNpcId = quest[2] + if potentialToNpc == toNpcId and toNpcId != ToonHQ: + validQuestPool[questId] = 0 + if notify.getDebug(): + notify.debug('filterQuests: Removed %s because npc involved' % questId) + break + + finalQuestPool = filter(lambda key: validQuestPool[key], validQuestPool.keys()) + if notify.getDebug(): + notify.debug('filterQuests: finalQuestPool: %s' % finalQuestPool) + return finalQuestPool + + +def chooseTrackChoiceQuest(tier, av, fixed = 0): + + def fixAndCallAgain(): + if not fixed and av.fixTrackAccess(): + notify.info('av %s trackAccess fixed: %s' % (av.getDoId(), trackAccess)) + return chooseTrackChoiceQuest(tier, av, fixed=1) + else: + return None + return None + + bestQuest = None + trackAccess = av.getTrackAccess() + if tier == MM_TIER: + if trackAccess[ToontownBattleGlobals.HEAL_TRACK] == 1: + bestQuest = 4002 + elif trackAccess[ToontownBattleGlobals.SOUND_TRACK] == 1: + bestQuest = 4001 + else: + notify.warning('av %s has bogus trackAccess: %s' % (av.getDoId(), trackAccess)) + return fixAndCallAgain() + elif tier == BR_TIER: + if trackAccess[ToontownBattleGlobals.TRAP_TRACK] == 1: + if trackAccess[ToontownBattleGlobals.SOUND_TRACK] == 1: + if trackAccess[ToontownBattleGlobals.DROP_TRACK] == 1: + bestQuest = 5004 + elif trackAccess[ToontownBattleGlobals.LURE_TRACK] == 1: + bestQuest = 5003 + else: + notify.warning('av %s has bogus trackAccess: %s' % (av.getDoId(), trackAccess)) + return fixAndCallAgain() + elif trackAccess[ToontownBattleGlobals.HEAL_TRACK] == 1: + if trackAccess[ToontownBattleGlobals.DROP_TRACK] == 1: + bestQuest = 5002 + elif trackAccess[ToontownBattleGlobals.LURE_TRACK] == 1: + bestQuest = 5001 + else: + notify.warning('av %s has bogus trackAccess: %s' % (av.getDoId(), trackAccess)) + return fixAndCallAgain() + elif trackAccess[ToontownBattleGlobals.SOUND_TRACK] == 0: + bestQuest = 5005 + elif trackAccess[ToontownBattleGlobals.HEAL_TRACK] == 0: + bestQuest = 5006 + elif trackAccess[ToontownBattleGlobals.DROP_TRACK] == 0: + bestQuest = 5007 + elif trackAccess[ToontownBattleGlobals.LURE_TRACK] == 0: + bestQuest = 5008 + else: + notify.warning('av %s has bogus trackAccess: %s' % (av.getDoId(), trackAccess)) + return fixAndCallAgain() + else: + if notify.getDebug(): + notify.debug('questPool for reward 400 had no dynamic choice, tier: %s' % tier) + bestQuest = seededRandomChoice(Tier2Reward2QuestsDict[tier][400]) + if notify.getDebug(): + notify.debug('chooseTrackChoiceQuest: avId: %s trackAccess: %s tier: %s bestQuest: %s' % (av.getDoId(), + trackAccess, + tier, + bestQuest)) + return bestQuest + + +def chooseMatchingQuest(tier, validQuestPool, rewardId, npc, av): + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(rewardId, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: %s tier: %s = %s' % (rewardId, tier, questsMatchingReward)) + if rewardId == 400 and QuestDict[questsMatchingReward[0]][QuestDictNextQuestIndex] == NA: + bestQuest = chooseTrackChoiceQuest(tier, av) + if notify.getDebug(): + notify.debug('single part track choice quest: %s tier: %s avId: %s trackAccess: %s bestQuest: %s' % (rewardId, + tier, + av.getDoId(), + av.getTrackAccess(), + bestQuest)) + else: + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: %s tier: %s = %s' % (rewardId, tier, validQuestsMatchingReward)) + if validQuestsMatchingReward: + bestQuest = seededRandomChoice(validQuestsMatchingReward) + else: + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(AnyCashbotSuitPart, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: AnyCashbotSuitPart tier: %s = %s' % (tier, questsMatchingReward)) + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if validQuestsMatchingReward: + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: AnyCashbotSuitPart tier: %s = %s' % (tier, validQuestsMatchingReward)) + bestQuest = seededRandomChoice(validQuestsMatchingReward) + else: + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(AnyLawbotSuitPart, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: AnyLawbotSuitPart tier: %s = %s' % (tier, questsMatchingReward)) + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if validQuestsMatchingReward: + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: AnyLawbotSuitPart tier: %s = %s' % (tier, validQuestsMatchingReward)) + bestQuest = seededRandomChoice(validQuestsMatchingReward) + else: + questsMatchingReward = Tier2Reward2QuestsDict[tier].get(Any, []) + if notify.getDebug(): + notify.debug('questsMatchingReward: Any tier: %s = %s' % (tier, questsMatchingReward)) + if not questsMatchingReward: + notify.warning('chooseMatchingQuests, no questsMatchingReward') + return None + validQuestsMatchingReward = PythonUtil.intersection(questsMatchingReward, validQuestPool) + if not validQuestsMatchingReward: + notify.warning('chooseMatchingQuests, no validQuestsMatchingReward') + return None + if notify.getDebug(): + notify.debug('validQuestsMatchingReward: Any tier: %s = %s' % (tier, validQuestsMatchingReward)) + bestQuest = seededRandomChoice(validQuestsMatchingReward) + return bestQuest + + +def transformReward(baseRewardId, av): + if baseRewardId == 900: + trackId, progress = av.getTrackProgress() + if trackId == -1: + notify.warning('transformReward: asked to transform 900 but av is not training') + actualRewardId = baseRewardId + else: + actualRewardId = 900 + 1 + trackId + return actualRewardId + elif baseRewardId > 800 and baseRewardId < 900: + trackId, progress = av.getTrackProgress() + if trackId < 0: + notify.warning('transformReward: av: %s is training a track with none chosen!' % av.getDoId()) + return 601 + else: + actualRewardId = baseRewardId + 200 + trackId * 100 + return actualRewardId + else: + return baseRewardId + + +def chooseBestQuests(tier, currentNpc, av): + if isLoopingFinalTier(tier): + rewardHistory = map(lambda questDesc: questDesc[3], av.quests) + else: + rewardHistory = av.getRewardHistory()[1] + seedRandomGen(currentNpc.getNpcId(), av.getDoId(), tier, rewardHistory) + numChoices = getNumChoices(tier) + rewards = getNextRewards(numChoices, tier, av) + if not rewards: + return [] + possibleQuests = [] + possibleRewards = list(rewards) + if Any not in possibleRewards: + possibleRewards.append(Any) + for rewardId in possibleRewards: + possibleQuests.extend(Tier2Reward2QuestsDict[tier].get(rewardId, [])) + + validQuestPool = filterQuests(possibleQuests, currentNpc, av) + if not validQuestPool: + return [] + if numChoices == 0: + numChoices = 1 + bestQuests = [] + for i in range(numChoices): + if len(validQuestPool) == 0: + break + if len(rewards) == 0: + break + rewardId = rewards.pop(0) + bestQuestId = chooseMatchingQuest(tier, validQuestPool, rewardId, currentNpc, av) + if bestQuestId is None: + continue + validQuestPool.remove(bestQuestId) + bestQuestToNpcId = getQuestToNpcId(bestQuestId) + if bestQuestToNpcId == Any: + bestQuestToNpcId = 2003 + elif bestQuestToNpcId == Same: + if currentNpc.getHq(): + bestQuestToNpcId = ToonHQ + else: + bestQuestToNpcId = currentNpc.getNpcId() + elif bestQuestToNpcId == ToonHQ: + bestQuestToNpcId = ToonHQ + bestQuests.append([bestQuestId, rewardId, bestQuestToNpcId]) + + for quest in bestQuests: + quest[1] = transformReward(quest[1], av) + + return bestQuests + + +def questExists(id): + return QuestDict.has_key(id) + + +def getQuest(id): + questEntry = QuestDict.get(id) + if questEntry: + questDesc = questEntry[QuestDictDescIndex] + questClass = questDesc[0] + return questClass(id, questDesc[1:]) + else: + return None + return None + + +def getQuestClass(id): + questEntry = QuestDict.get(id) + if questEntry: + return questEntry[QuestDictDescIndex][0] + else: + return None + return None + + +def getVisitSCStrings(npcId): + if npcId == ToonHQ: + strings = [TTLocalizer.QuestsRecoverItemQuestSeeHQSCString, TTLocalizer.QuestsRecoverItemQuestGoToHQSCString] + elif npcId == ToonTailor: + strings = [TTLocalizer.QuestsTailorQuestSCString] + elif npcId: + npcName, hoodName, buildingArticle, buildingName, toStreet, streetName, isInPlayground = getNpcInfo(npcId) + strings = [TTLocalizer.QuestsVisitQuestSeeSCString % npcName] + if isInPlayground: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToPlaygroundSCString % hoodName) + else: + strings.append(TTLocalizer.QuestsRecoverItemQuestGoToStreetSCString % {'to': toStreet, + 'street': streetName, + 'hood': hoodName}) + strings.extend([TTLocalizer.QuestsRecoverItemQuestVisitBuildingSCString % (buildingArticle, buildingName), TTLocalizer.QuestsRecoverItemQuestWhereIsBuildingSCString % (buildingArticle, buildingName)]) + return strings + + +def getFinishToonTaskSCStrings(npcId): + return [TTLocalizer.QuestsGenericFinishSCString] + getVisitSCStrings(npcId) + + +def chooseQuestDialog(id, status): + questDialog = getQuestDialog(id).get(status) + if questDialog == None: + if status == QUEST: + quest = getQuest(id) + questDialog = quest.getDefaultQuestDialog() + else: + questDialog = DefaultDialog[status] + if type(questDialog) == type(()): + return random.choice(questDialog) + else: + return questDialog + return + + +def chooseQuestDialogReject(): + return random.choice(DefaultReject) + + +def chooseQuestDialogTierNotDone(): + return random.choice(DefaultTierNotDone) + + +def getNpcInfo(npcId): + npcName = NPCToons.getNPCName(npcId) + npcZone = NPCToons.getNPCZone(npcId) + hoodId = ZoneUtil.getCanonicalHoodId(npcZone) + hoodName = base.cr.hoodMgr.getFullnameFromId(hoodId) + buildingArticle = NPCToons.getBuildingArticle(npcZone) + buildingName = NPCToons.getBuildingTitle(npcZone) + branchId = ZoneUtil.getCanonicalBranchZone(npcZone) + toStreet = ToontownGlobals.StreetNames[branchId][0] + streetName = ToontownGlobals.StreetNames[branchId][-1] + isInPlayground = ZoneUtil.isPlayground(branchId) + return (npcName, + hoodName, + buildingArticle, + buildingName, + toStreet, + streetName, + isInPlayground) + + +def getNpcLocationDialog(fromNpcId, toNpcId): + if not toNpcId: + return (None, None, None) + fromNpcZone = None + fromBranchId = None + if fromNpcId: + fromNpcZone = NPCToons.getNPCZone(fromNpcId) + fromBranchId = ZoneUtil.getCanonicalBranchZone(fromNpcZone) + toNpcZone = NPCToons.getNPCZone(toNpcId) + toBranchId = ZoneUtil.getCanonicalBranchZone(toNpcZone) + toNpcName, toHoodName, toBuildingArticle, toBuildingName, toStreetTo, toStreetName, isInPlayground = getNpcInfo(toNpcId) + if fromBranchId == toBranchId: + if isInPlayground: + streetDesc = TTLocalizer.QuestsStreetLocationThisPlayground + else: + streetDesc = TTLocalizer.QuestsStreetLocationThisStreet + elif isInPlayground: + streetDesc = TTLocalizer.QuestsStreetLocationNamedPlayground % toHoodName + else: + streetDesc = TTLocalizer.QuestsStreetLocationNamedStreet % {'toStreetName': toStreetName, + 'toHoodName': toHoodName} + paragraph = TTLocalizer.QuestsLocationParagraph % {'building': TTLocalizer.QuestsLocationBuilding % toNpcName, + 'buildingName': toBuildingName, + 'buildingVerb': TTLocalizer.QuestsLocationBuildingVerb, + 'street': streetDesc} + return (paragraph, toBuildingName, streetDesc) + + +def fillInQuestNames(text, avName = None, fromNpcId = None, toNpcId = None): + text = copy.deepcopy(text) + if avName != None: + text = string.replace(text, '_avName_', avName) + if toNpcId: + if toNpcId == ToonHQ: + toNpcName = TTLocalizer.QuestsHQOfficerFillin + where = TTLocalizer.QuestsHQWhereFillin + buildingName = TTLocalizer.QuestsHQBuildingNameFillin + streetDesc = TTLocalizer.QuestsHQLocationNameFillin + elif toNpcId == ToonTailor: + toNpcName = TTLocalizer.QuestsTailorFillin + where = TTLocalizer.QuestsTailorWhereFillin + buildingName = TTLocalizer.QuestsTailorBuildingNameFillin + streetDesc = TTLocalizer.QuestsTailorLocationNameFillin + else: + toNpcName = str(NPCToons.getNPCName(toNpcId)) + where, buildingName, streetDesc = getNpcLocationDialog(fromNpcId, toNpcId) + text = string.replace(text, '_toNpcName_', toNpcName) + text = string.replace(text, '_where_', where) + text = string.replace(text, '_buildingName_', buildingName) + text = string.replace(text, '_streetDesc_', streetDesc) + return text + + +def getVisitingQuest(): + return VisitQuest(VISIT_QUEST_ID) + + +class Reward: + def __init__(self, id, reward): + self.id = id + self.reward = reward + + def getId(self): + return self.id + + def getType(self): + return self.__class__ + + def getAmount(self): + return None + + def sendRewardAI(self, av): + raise 'not implemented' + + def countReward(self, qrc): + raise 'not implemented' + + def getString(self): + return 'undefined' + + def getPosterString(self): + return 'base class' + + +class MaxHpReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + maxHp = av.getMaxHp() + maxHp = min(ToontownGlobals.MaxHpLimit, maxHp + self.getAmount()) + av.b_setMaxHp(maxHp) + av.toonUp(maxHp) + + def countReward(self, qrc): + qrc.maxHp += self.getAmount() + + def getString(self): + return TTLocalizer.QuestsMaxHpReward % self.getAmount() + + def getPosterString(self): + return TTLocalizer.QuestsMaxHpRewardPoster % self.getAmount() + + +class MoneyReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + money = av.getMoney() + maxMoney = av.getMaxMoney() + av.addMoney(self.getAmount()) + + def countReward(self, qrc): + qrc.money += self.getAmount() + + def getString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMoneyRewardSingular + else: + return TTLocalizer.QuestsMoneyRewardPlural % amt + + def getPosterString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMoneyRewardPosterSingular + else: + return TTLocalizer.QuestsMoneyRewardPosterPlural % amt + + +class MaxMoneyReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + av.b_setMaxMoney(self.getAmount()) + + def countReward(self, qrc): + qrc.maxMoney = self.getAmount() + + def getString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMaxMoneyRewardSingular + else: + return TTLocalizer.QuestsMaxMoneyRewardPlural % amt + + def getPosterString(self): + amt = self.getAmount() + if amt == 1: + return TTLocalizer.QuestsMaxMoneyRewardPosterSingular + else: + return TTLocalizer.QuestsMaxMoneyRewardPosterPlural % amt + + +class MaxGagCarryReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def getName(self): + return self.reward[1] + + def sendRewardAI(self, av): + av.b_setMaxCarry(self.getAmount()) + + def countReward(self, qrc): + qrc.maxCarry = self.getAmount() + + def getString(self): + name = self.getName() + amt = self.getAmount() + return TTLocalizer.QuestsMaxGagCarryReward % {'name': name, + 'num': amt} + + def getPosterString(self): + name = self.getName() + amt = self.getAmount() + return TTLocalizer.QuestsMaxGagCarryRewardPoster % {'name': name, + 'num': amt} + + +class MaxQuestCarryReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getAmount(self): + return self.reward[0] + + def sendRewardAI(self, av): + av.b_setQuestCarryLimit(self.getAmount()) + + def countReward(self, qrc): + qrc.questCarryLimit = self.getAmount() + + def getString(self): + amt = self.getAmount() + return TTLocalizer.QuestsMaxQuestCarryReward % amt + + def getPosterString(self): + amt = self.getAmount() + return TTLocalizer.QuestsMaxQuestCarryRewardPoster % amt + + +class TeleportReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getZone(self): + return self.reward[0] + + def sendRewardAI(self, av): + av.addTeleportAccess(self.getZone()) + + def countReward(self, qrc): + qrc.addTeleportAccess(self.getZone()) + + def getString(self): + hoodName = ToontownGlobals.hoodNameMap[self.getZone()][-1] + return TTLocalizer.QuestsTeleportReward % hoodName + + def getPosterString(self): + hoodName = ToontownGlobals.hoodNameMap[self.getZone()][-1] + return TTLocalizer.QuestsTeleportRewardPoster % hoodName + + +TrackTrainingQuotas = {ToontownBattleGlobals.HEAL_TRACK: 15, + ToontownBattleGlobals.TRAP_TRACK: 15, + ToontownBattleGlobals.LURE_TRACK: 15, + ToontownBattleGlobals.SOUND_TRACK: 15, + ToontownBattleGlobals.THROW_TRACK: 15, + ToontownBattleGlobals.SQUIRT_TRACK: 15, + ToontownBattleGlobals.DROP_TRACK: 15} + +class TrackTrainingReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getTrack(self): + track = self.reward[0] + if track == None: + track = 0 + return track + + def sendRewardAI(self, av): + av.b_setTrackProgress(self.getTrack(), 0) + + def countReward(self, qrc): + qrc.trackProgressId = self.getTrack() + qrc.trackProgress = 0 + + def getString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackTrainingReward % trackName + + def getPosterString(self): + return TTLocalizer.QuestsTrackTrainingRewardPoster + + +class TrackProgressReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getTrack(self): + track = self.reward[0] + if track == None: + track = 0 + return track + + def getProgressIndex(self): + return self.reward[1] + + def sendRewardAI(self, av): + av.addTrackProgress(self.getTrack(), self.getProgressIndex()) + + def countReward(self, qrc): + qrc.addTrackProgress(self.getTrack(), self.getProgressIndex()) + + def getString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackProgressReward % {'frameNum': self.getProgressIndex(), + 'trackName': trackName} + + def getPosterString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackProgressRewardPoster % {'trackName': trackName, + 'frameNum': self.getProgressIndex()} + + +class TrackCompleteReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getTrack(self): + track = self.reward[0] + if track == None: + track = 0 + return track + + def sendRewardAI(self, av): + av.addTrackAccess(self.getTrack()) + av.clearTrackProgress() + + def countReward(self, qrc): + qrc.addTrackAccess(self.getTrack()) + qrc.clearTrackProgress() + + def getString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackCompleteReward % trackName + + def getPosterString(self): + trackName = ToontownBattleGlobals.Tracks[self.getTrack()].capitalize() + return TTLocalizer.QuestsTrackCompleteRewardPoster % trackName + + +class ClothingTicketReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def sendRewardAI(self, av): + pass + + def countReward(self, qrc): + pass + + def getString(self): + return TTLocalizer.QuestsClothingTicketReward + + def getPosterString(self): + return TTLocalizer.QuestsClothingTicketRewardPoster + + +class TIPClothingTicketReward(ClothingTicketReward): + def __init__(self, id, reward): + ClothingTicketReward.__init__(self, id, reward) + + def getString(self): + return TTLocalizer.TIPQuestsClothingTicketReward + + def getPosterString(self): + return TTLocalizer.TIPQuestsClothingTicketRewardPoster + + +class CheesyEffectReward(Reward): + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getEffect(self): + return self.reward[0] + + def getHoodId(self): + return self.reward[1] + + def getDurationMinutes(self): + return self.reward[2] + + def sendRewardAI(self, av): + expireTime = int(time.time() / 60 + 0.5) + self.getDurationMinutes() + av.b_setCheesyEffect(self.getEffect(), self.getHoodId(), expireTime) + + def countReward(self, qrc): + pass + + def getString(self): + effect = self.getEffect() + hoodId = self.getHoodId() + duration = self.getDurationMinutes() + string = TTLocalizer.CheesyEffectMinutes + if duration > 90: + duration = int((duration + 30) / 60) + string = TTLocalizer.CheesyEffectHours + if duration > 36: + duration = int((duration + 12) / 24) + string = TTLocalizer.CheesyEffectDays + desc = TTLocalizer.CheesyEffectDescriptions[effect][1] + if hoodId == 0: + whileStr = '' + elif hoodId == 1: + whileStr = TTLocalizer.CheesyEffectExceptIn % TTLocalizer.ToontownCentral[-1] + else: + hoodName = base.cr.hoodMgr.getFullnameFromId(hoodId) + whileStr = TTLocalizer.CheesyEffectWhileYouAreIn % hoodName + if duration: + return string % {'time': duration, + 'effectName': desc, + 'whileIn': whileStr} + else: + return TTLocalizer.CheesyEffectIndefinite % {'effectName': desc, + 'whileIn': whileStr} + + def getPosterString(self): + effect = self.getEffect() + desc = TTLocalizer.CheesyEffectDescriptions[effect][0] + return TTLocalizer.QuestsCheesyEffectRewardPoster % desc + + +class CogSuitPartReward(Reward): + trackNames = [TTLocalizer.Bossbot, + TTLocalizer.Lawbot, + TTLocalizer.Cashbot, + TTLocalizer.Sellbot] + + def __init__(self, id, reward): + Reward.__init__(self, id, reward) + + def getCogTrack(self): + return self.reward[0] + + def getCogPart(self): + return self.reward[1] + + def sendRewardAI(self, av): + dept = self.getCogTrack() + part = self.getCogPart() + av.giveCogPart(part, dept) + + def countReward(self, qrc): + pass + + def getCogTrackName(self): + index = ToontownGlobals.cogDept2index[self.getCogTrack()] + return CogSuitPartReward.trackNames[index] + + def getCogPartName(self): + index = ToontownGlobals.cogDept2index[self.getCogTrack()] + return CogDisguiseGlobals.PartsQueryNames[index][self.getCogPart()] + + def getString(self): + return TTLocalizer.QuestsCogSuitPartReward % {'cogTrack': self.getCogTrackName(), + 'part': self.getCogPartName()} + + def getPosterString(self): + return TTLocalizer.QuestsCogSuitPartRewardPoster % {'cogTrack': self.getCogTrackName(), + 'part': self.getCogPartName()} + + +def getRewardClass(id): + reward = RewardDict.get(id) + if reward: + return reward[0] + else: + return None + return None + + +def getReward(id): + reward = RewardDict.get(id) + if reward: + rewardClass = reward[0] + return rewardClass(id, reward[1:]) + else: + notify.warning('getReward(): id %s not found.' % id) + return None + return None + + +def getNextRewards(numChoices, tier, av): + rewardTier = list(getRewardsInTier(tier)) + optRewards = list(getOptionalRewardsInTier(tier)) + if av.getGameAccess() == OTPGlobals.AccessFull and tier == TT_TIER + 3: + optRewards = [] + if isLoopingFinalTier(tier): + rewardHistory = map(lambda questDesc: questDesc[3], av.quests) + if notify.getDebug(): + notify.debug('getNextRewards: current rewards (history): %s' % rewardHistory) + else: + rewardHistory = av.getRewardHistory()[1] + if notify.getDebug(): + notify.debug('getNextRewards: rewardHistory: %s' % rewardHistory) + if notify.getDebug(): + notify.debug('getNextRewards: rewardTier: %s' % rewardTier) + notify.debug('getNextRewards: numChoices: %s' % numChoices) + for rewardId in getRewardsInTier(tier): + if getRewardClass(rewardId) == CogSuitPartReward: + deptStr = RewardDict.get(rewardId)[1] + cogPart = RewardDict.get(rewardId)[2] + dept = ToontownGlobals.cogDept2index[deptStr] + if av.hasCogPart(cogPart, dept): + notify.debug('getNextRewards: already has cog part: %s dept: %s' % (cogPart, dept)) + rewardTier.remove(rewardId) + else: + notify.debug('getNextRewards: keeping quest for cog part: %s dept: %s' % (cogPart, dept)) + + for rewardId in rewardHistory: + if rewardId in rewardTier: + rewardTier.remove(rewardId) + elif rewardId in optRewards: + optRewards.remove(rewardId) + elif rewardId in (901, 902, 903, 904, 905, 906, 907): + genericRewardId = 900 + if genericRewardId in rewardTier: + rewardTier.remove(genericRewardId) + elif rewardId > 1000 and rewardId < 1699: + index = rewardId % 100 + genericRewardId = 800 + index + if genericRewardId in rewardTier: + rewardTier.remove(genericRewardId) + + if numChoices == 0: + if len(rewardTier) == 0: + return [] + else: + return [rewardTier[0]] + rewardPool = rewardTier[:numChoices] + for i in range(len(rewardPool), numChoices * 2): + if optRewards: + optionalReward = seededRandomChoice(optRewards) + optRewards.remove(optionalReward) + rewardPool.append(optionalReward) + else: + break + + if notify.getDebug(): + notify.debug('getNextRewards: starting reward pool: %s' % rewardPool) + if len(rewardPool) == 0: + if notify.getDebug(): + notify.debug('getNextRewards: no rewards left at all') + return [] + finalRewardPool = [rewardPool.pop(0)] + for i in range(numChoices - 1): + if len(rewardPool) == 0: + break + selectedReward = seededRandomChoice(rewardPool) + rewardPool.remove(selectedReward) + finalRewardPool.append(selectedReward) + + if notify.getDebug(): + notify.debug('getNextRewards: final reward pool: %s' % finalRewardPool) + return finalRewardPool + + +RewardDict = {100: (MaxHpReward, 1), + 101: (MaxHpReward, 2), + 102: (MaxHpReward, 3), + 103: (MaxHpReward, 4), + 104: (MaxHpReward, 5), + 105: (MaxHpReward, 6), + 106: (MaxHpReward, 7), + 107: (MaxHpReward, 8), + 108: (MaxHpReward, 9), + 109: (MaxHpReward, 10), + 200: (MaxGagCarryReward, 25, TTLocalizer.QuestsMediumPouch), + 201: (MaxGagCarryReward, 30, TTLocalizer.QuestsLargePouch), + 202: (MaxGagCarryReward, 35, TTLocalizer.QuestsSmallBag), + 203: (MaxGagCarryReward, 40, TTLocalizer.QuestsMediumBag), + 204: (MaxGagCarryReward, 50, TTLocalizer.QuestsLargeBag), + 205: (MaxGagCarryReward, 60, TTLocalizer.QuestsSmallBackpack), + 206: (MaxGagCarryReward, 70, TTLocalizer.QuestsMediumBackpack), + 207: (MaxGagCarryReward, 80, TTLocalizer.QuestsLargeBackpack), + 300: (TeleportReward, ToontownGlobals.ToontownCentral), + 301: (TeleportReward, ToontownGlobals.DonaldsDock), + 302: (TeleportReward, ToontownGlobals.DaisyGardens), + 303: (TeleportReward, ToontownGlobals.MinniesMelodyland), + 304: (TeleportReward, ToontownGlobals.TheBrrrgh), + 305: (TeleportReward, ToontownGlobals.DonaldsDreamland), + 400: (TrackTrainingReward, None), + 401: (TrackTrainingReward, ToontownBattleGlobals.HEAL_TRACK), + 402: (TrackTrainingReward, ToontownBattleGlobals.TRAP_TRACK), + 403: (TrackTrainingReward, ToontownBattleGlobals.LURE_TRACK), + 404: (TrackTrainingReward, ToontownBattleGlobals.SOUND_TRACK), + 405: (TrackTrainingReward, ToontownBattleGlobals.THROW_TRACK), + 406: (TrackTrainingReward, ToontownBattleGlobals.SQUIRT_TRACK), + 407: (TrackTrainingReward, ToontownBattleGlobals.DROP_TRACK), + 500: (MaxQuestCarryReward, 2), + 501: (MaxQuestCarryReward, 3), + 502: (MaxQuestCarryReward, 4), + 600: (MoneyReward, 10), + 601: (MoneyReward, 20), + 602: (MoneyReward, 40), + 603: (MoneyReward, 60), + 604: (MoneyReward, 100), + 605: (MoneyReward, 150), + 606: (MoneyReward, 200), + 607: (MoneyReward, 250), + 608: (MoneyReward, 300), + 609: (MoneyReward, 400), + 610: (MoneyReward, 500), + 611: (MoneyReward, 600), + 612: (MoneyReward, 700), + 613: (MoneyReward, 800), + 614: (MoneyReward, 900), + 615: (MoneyReward, 1000), + 616: (MoneyReward, 1100), + 617: (MoneyReward, 1200), + 618: (MoneyReward, 1300), + 619: (MoneyReward, 1400), + 620: (MoneyReward, 1500), + 621: (MoneyReward, 1750), + 622: (MoneyReward, 2000), + 623: (MoneyReward, 2500), + 700: (MaxMoneyReward, 50), + 701: (MaxMoneyReward, 60), + 702: (MaxMoneyReward, 80), + 703: (MaxMoneyReward, 100), + 704: (MaxMoneyReward, 120), + 705: (MaxMoneyReward, 150), + 706: (MaxMoneyReward, 200), + 707: (MaxMoneyReward, 250), + 801: (TrackProgressReward, None, 1), + 802: (TrackProgressReward, None, 2), + 803: (TrackProgressReward, None, 3), + 804: (TrackProgressReward, None, 4), + 805: (TrackProgressReward, None, 5), + 806: (TrackProgressReward, None, 6), + 807: (TrackProgressReward, None, 7), + 808: (TrackProgressReward, None, 8), + 809: (TrackProgressReward, None, 9), + 810: (TrackProgressReward, None, 10), + 811: (TrackProgressReward, None, 11), + 812: (TrackProgressReward, None, 12), + 813: (TrackProgressReward, None, 13), + 814: (TrackProgressReward, None, 14), + 815: (TrackProgressReward, None, 15), + 110: (TIPClothingTicketReward,), + 1000: (ClothingTicketReward,), + 1001: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 1), + 1002: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 2), + 1003: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 3), + 1004: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 4), + 1005: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 5), + 1006: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 6), + 1007: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 7), + 1008: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 8), + 1009: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 9), + 1010: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 10), + 1011: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 11), + 1012: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 12), + 1013: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 13), + 1014: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 14), + 1015: (TrackProgressReward, ToontownBattleGlobals.HEAL_TRACK, 15), + 1101: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 1), + 1102: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 2), + 1103: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 3), + 1104: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 4), + 1105: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 5), + 1106: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 6), + 1107: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 7), + 1108: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 8), + 1109: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 9), + 1110: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 10), + 1111: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 11), + 1112: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 12), + 1113: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 13), + 1114: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 14), + 1115: (TrackProgressReward, ToontownBattleGlobals.TRAP_TRACK, 15), + 1201: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 1), + 1202: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 2), + 1203: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 3), + 1204: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 4), + 1205: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 5), + 1206: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 6), + 1207: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 7), + 1208: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 8), + 1209: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 9), + 1210: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 10), + 1211: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 11), + 1212: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 12), + 1213: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 13), + 1214: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 14), + 1215: (TrackProgressReward, ToontownBattleGlobals.LURE_TRACK, 15), + 1301: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 1), + 1302: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 2), + 1303: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 3), + 1304: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 4), + 1305: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 5), + 1306: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 6), + 1307: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 7), + 1308: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 8), + 1309: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 9), + 1310: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 10), + 1311: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 11), + 1312: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 12), + 1313: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 13), + 1314: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 14), + 1315: (TrackProgressReward, ToontownBattleGlobals.SOUND_TRACK, 15), + 1601: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 1), + 1602: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 2), + 1603: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 3), + 1604: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 4), + 1605: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 5), + 1606: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 6), + 1607: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 7), + 1608: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 8), + 1609: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 9), + 1610: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 10), + 1611: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 11), + 1612: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 12), + 1613: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 13), + 1614: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 14), + 1615: (TrackProgressReward, ToontownBattleGlobals.DROP_TRACK, 15), + 900: (TrackCompleteReward, None), + 901: (TrackCompleteReward, ToontownBattleGlobals.HEAL_TRACK), + 902: (TrackCompleteReward, ToontownBattleGlobals.TRAP_TRACK), + 903: (TrackCompleteReward, ToontownBattleGlobals.LURE_TRACK), + 904: (TrackCompleteReward, ToontownBattleGlobals.SOUND_TRACK), + 905: (TrackCompleteReward, ToontownBattleGlobals.THROW_TRACK), + 906: (TrackCompleteReward, ToontownBattleGlobals.SQUIRT_TRACK), + 907: (TrackCompleteReward, ToontownBattleGlobals.DROP_TRACK), + 2205: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 2000, + 10), + 2206: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 2000, + 10), + 2101: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 1000, + 10), + 2102: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 1000, + 10), + 2105: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 20), + 2106: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 20), + 2501: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 5000, + 60), + 2502: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 5000, + 60), + 2503: (CheesyEffectReward, + ToontownGlobals.CEBigLegs, + 5000, + 20), + 2504: (CheesyEffectReward, + ToontownGlobals.CESmallLegs, + 5000, + 20), + 2505: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 60), + 2506: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 60), + 2401: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 1, + 120), + 2402: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 1, + 120), + 2403: (CheesyEffectReward, + ToontownGlobals.CEBigLegs, + 4000, + 60), + 2404: (CheesyEffectReward, + ToontownGlobals.CESmallLegs, + 4000, + 60), + 2405: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 120), + 2406: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 120), + 2407: (CheesyEffectReward, + ToontownGlobals.CEFlatPortrait, + 4000, + 30), + 2408: (CheesyEffectReward, + ToontownGlobals.CEFlatProfile, + 4000, + 30), + 2409: (CheesyEffectReward, + ToontownGlobals.CETransparent, + 4000, + 30), + 2410: (CheesyEffectReward, + ToontownGlobals.CENoColor, + 4000, + 30), + 2301: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 1, + 360), + 2302: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 1, + 360), + 2303: (CheesyEffectReward, + ToontownGlobals.CEBigLegs, + 1, + 360), + 2304: (CheesyEffectReward, + ToontownGlobals.CESmallLegs, + 1, + 360), + 2305: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 1440), + 2306: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 1440), + 2307: (CheesyEffectReward, + ToontownGlobals.CEFlatPortrait, + 3000, + 240), + 2308: (CheesyEffectReward, + ToontownGlobals.CEFlatProfile, + 3000, + 240), + 2309: (CheesyEffectReward, + ToontownGlobals.CETransparent, + 1, + 120), + 2310: (CheesyEffectReward, + ToontownGlobals.CENoColor, + 1, + 120), + 2311: (CheesyEffectReward, + ToontownGlobals.CEInvisible, + 3000, + 120), + 2900: (CheesyEffectReward, + ToontownGlobals.CENormal, + 0, + 0), + 2901: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 1, + 1440), + 2902: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 1, + 1440), + 2903: (CheesyEffectReward, + ToontownGlobals.CEBigLegs, + 1, + 1440), + 2904: (CheesyEffectReward, + ToontownGlobals.CESmallLegs, + 1, + 1440), + 2905: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 1440), + 2906: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 1440), + 2907: (CheesyEffectReward, + ToontownGlobals.CEFlatPortrait, + 1, + 1440), + 2908: (CheesyEffectReward, + ToontownGlobals.CEFlatProfile, + 1, + 1440), + 2909: (CheesyEffectReward, + ToontownGlobals.CETransparent, + 1, + 1440), + 2910: (CheesyEffectReward, + ToontownGlobals.CENoColor, + 1, + 1440), + 2911: (CheesyEffectReward, + ToontownGlobals.CEInvisible, + 1, + 1440), + 2920: (CheesyEffectReward, + ToontownGlobals.CENormal, + 0, + 0), + 2921: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 1, + 2880), + 2922: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 1, + 2880), + 2923: (CheesyEffectReward, + ToontownGlobals.CEBigLegs, + 1, + 2880), + 2924: (CheesyEffectReward, + ToontownGlobals.CESmallLegs, + 1, + 2880), + 2925: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 2880), + 2926: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 2880), + 2927: (CheesyEffectReward, + ToontownGlobals.CEFlatPortrait, + 1, + 2880), + 2928: (CheesyEffectReward, + ToontownGlobals.CEFlatProfile, + 1, + 2880), + 2929: (CheesyEffectReward, + ToontownGlobals.CETransparent, + 1, + 2880), + 2930: (CheesyEffectReward, + ToontownGlobals.CENoColor, + 1, + 2880), + 2931: (CheesyEffectReward, + ToontownGlobals.CEInvisible, + 1, + 2880), + 2940: (CheesyEffectReward, + ToontownGlobals.CENormal, + 0, + 0), + 2941: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 1, + 10080), + 2942: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 1, + 10080), + 2943: (CheesyEffectReward, + ToontownGlobals.CEBigLegs, + 1, + 10080), + 2944: (CheesyEffectReward, + ToontownGlobals.CESmallLegs, + 1, + 10080), + 2945: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 10080), + 2946: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 10080), + 2947: (CheesyEffectReward, + ToontownGlobals.CEFlatPortrait, + 1, + 10080), + 2948: (CheesyEffectReward, + ToontownGlobals.CEFlatProfile, + 1, + 10080), + 2949: (CheesyEffectReward, + ToontownGlobals.CETransparent, + 1, + 10080), + 2950: (CheesyEffectReward, + ToontownGlobals.CENoColor, + 1, + 10080), + 2951: (CheesyEffectReward, + ToontownGlobals.CEInvisible, + 1, + 10080), + 2960: (CheesyEffectReward, + ToontownGlobals.CENormal, + 0, + 0), + 2961: (CheesyEffectReward, + ToontownGlobals.CEBigHead, + 1, + 43200), + 2962: (CheesyEffectReward, + ToontownGlobals.CESmallHead, + 1, + 43200), + 2963: (CheesyEffectReward, + ToontownGlobals.CEBigLegs, + 1, + 43200), + 2964: (CheesyEffectReward, + ToontownGlobals.CESmallLegs, + 1, + 43200), + 2965: (CheesyEffectReward, + ToontownGlobals.CEBigToon, + 0, + 43200), + 2966: (CheesyEffectReward, + ToontownGlobals.CESmallToon, + 0, + 43200), + 2967: (CheesyEffectReward, + ToontownGlobals.CEFlatPortrait, + 1, + 43200), + 2968: (CheesyEffectReward, + ToontownGlobals.CEFlatProfile, + 1, + 43200), + 2969: (CheesyEffectReward, + ToontownGlobals.CETransparent, + 1, + 43200), + 2970: (CheesyEffectReward, + ToontownGlobals.CENoColor, + 1, + 43200), + 2971: (CheesyEffectReward, + ToontownGlobals.CEInvisible, + 1, + 43200), + 4000: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftLegUpper), + 4001: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftLegLower), + 4002: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftLegFoot), + 4003: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightLegUpper), + 4004: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightLegLower), + 4005: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightLegFoot), + 4006: (CogSuitPartReward, 'm', CogDisguiseGlobals.upperTorso), + 4007: (CogSuitPartReward, 'm', CogDisguiseGlobals.torsoPelvis), + 4008: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftArmUpper), + 4009: (CogSuitPartReward, 'm', CogDisguiseGlobals.leftArmLower), + 4010: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightArmUpper), + 4011: (CogSuitPartReward, 'm', CogDisguiseGlobals.rightArmLower), + 4100: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftLegUpper), + 4101: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftLegLower), + 4102: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftLegFoot), + 4103: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightLegUpper), + 4104: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightLegLower), + 4105: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightLegFoot), + 4106: (CogSuitPartReward, 'l', CogDisguiseGlobals.upperTorso), + 4107: (CogSuitPartReward, 'l', CogDisguiseGlobals.torsoPelvis), + 4108: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftArmUpper), + 4109: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftArmLower), + 4110: (CogSuitPartReward, 'l', CogDisguiseGlobals.leftArmHand), + 4111: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightArmUpper), + 4112: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightArmLower), + 4113: (CogSuitPartReward, 'l', CogDisguiseGlobals.rightArmHand), + 4200: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftLegUpper), + 4201: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftLegLower), + 4202: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftLegFoot), + 4203: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightLegUpper), + 4204: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightLegLower), + 4205: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightLegFoot), + 4206: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoLeftShoulder), + 4207: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoRightShoulder), + 4208: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoChest), + 4209: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoHealthMeter), + 4210: (CogSuitPartReward, 'c', CogDisguiseGlobals.torsoPelvis), + 4211: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftArmUpper), + 4212: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftArmLower), + 4213: (CogSuitPartReward, 'c', CogDisguiseGlobals.leftArmHand), + 4214: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightArmUpper), + 4215: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightArmLower), + 4216: (CogSuitPartReward, 'c', CogDisguiseGlobals.rightArmHand)} + +def getNumTiers(): + return len(RequiredRewardTrackDict) - 1 + + +def isLoopingFinalTier(tier): + return tier == LOOPING_FINAL_TIER + + +def getRewardsInTier(tier): + return RequiredRewardTrackDict.get(tier, []) + + +def getNumRewardsInTier(tier): + return len(RequiredRewardTrackDict.get(tier, [])) + + +def rewardTierExists(tier): + return RequiredRewardTrackDict.has_key(tier) + + +def getOptionalRewardsInTier(tier): + return OptionalRewardTrackDict.get(tier, []) + + +def getRewardIdFromTrackId(trackId): + return 401 + trackId + + +RequiredRewardTrackDict = {TT_TIER: (100,), + TT_TIER + 1: (400,), + TT_TIER + 2: (100, + 801, + 200, + 802, + 803, + 101, + 804, + 805, + 102, + 806, + 807, + 100, + 808, + 809, + 101, + 810, + 811, + 500, + 812, + 813, + 700, + 814, + 815, + 300), + TT_TIER + 3: (900,), + DD_TIER: (400,), + DD_TIER + 1: (100, + 801, + 802, + 201, + 803, + 804, + 101, + 805, + 806, + 102, + 807, + 808, + 100, + 809, + 810, + 101, + 811, + 812, + 701, + 813, + 814, + 815, + 301), + DD_TIER + 2: (900,), + DG_TIER: (100, + 202, + 101, + 102, + 100, + 101, + 501, + 702, + 302), + MM_TIER: (400,), + MM_TIER + 1: (100, + 801, + 802, + 203, + 803, + 804, + 101, + 805, + 806, + 102, + 807, + 808, + 100, + 809, + 810, + 101, + 811, + 812, + 703, + 813, + 814, + 815, + 303), + MM_TIER + 2: (900,), + BR_TIER: (400,), + BR_TIER + 1: (100, + 801, + 802, + 704, + 803, + 804, + 101, + 805, + 806, + 502, + 807, + 808, + 102, + 809, + 810, + 204, + 811, + 812, + 100, + 813, + 814, + 101, + 815, + 304), + BR_TIER + 2: (900,), + DL_TIER: (4000, + 100, + 205, + 101, + 102, + 705, + 103, + 305, + 4001, + 4002), + DL_TIER + 1: (100, + 206, + 101, + 4003, + 4004, + 4005, + 102, + 4006, + 4007, + 4008, + 706, + 103, + 4009, + 4010, + 4011, + 4000, + 4001, + 4002), + DL_TIER + 2: (4006, + 4007, + 4008, + 100, + 4000, + 4001, + 4002, + 4003, + 101, + 4004, + 4005, + 4009, + 102, + 103, + 4010, + 4011), + DL_TIER + 3: (4009, + 4010, + 4011, + 100, + 4000, + 4001, + 101, + 4002, + 4003, + 102, + 4004, + 4005, + 102, + 4006, + 4007, + 707, + 207, + 4008), + LAWBOT_HQ_TIER: (4100,), + LAWBOT_HQ_TIER + 1: (4101,), + LAWBOT_HQ_TIER + 2: (4102,), + LAWBOT_HQ_TIER + 3: (4103,), + LAWBOT_HQ_TIER + 4: (4104,), + LAWBOT_HQ_TIER + 5: (4105,), + LAWBOT_HQ_TIER + 6: (4106,), + LAWBOT_HQ_TIER + 7: (4107,), + LAWBOT_HQ_TIER + 8: (4108,), + LAWBOT_HQ_TIER + 9: (4109,), + LAWBOT_HQ_TIER + 10: (4110,), + LAWBOT_HQ_TIER + 11: (4111,), + LAWBOT_HQ_TIER + 12: (4112,), + LAWBOT_HQ_TIER + 13: (4113,), + BOSSBOT_HQ_TIER: (4200,), + BOSSBOT_HQ_TIER + 1: (4201,), + BOSSBOT_HQ_TIER + 2: (4202,), + BOSSBOT_HQ_TIER + 3: (4203,), + BOSSBOT_HQ_TIER + 4: (4204,), + BOSSBOT_HQ_TIER + 5: (4205,), + BOSSBOT_HQ_TIER + 6: (4206,), + BOSSBOT_HQ_TIER + 7: (4207,), + BOSSBOT_HQ_TIER + 8: (4208,), + BOSSBOT_HQ_TIER + 9: (4209,), + BOSSBOT_HQ_TIER + 10: (4210,), + BOSSBOT_HQ_TIER + 11: (4211,), + BOSSBOT_HQ_TIER + 12: (4212,), + BOSSBOT_HQ_TIER + 13: (4213,), + BOSSBOT_HQ_TIER + 14: (4214,), + BOSSBOT_HQ_TIER + 15: (4215,), + BOSSBOT_HQ_TIER + 16: (4216,), + ELDER_TIER: (4000, + 4001, + 4002, + 4003, + 4004, + 4005, + 4006, + 4007, + 4008, + 4009, + 4010, + 4011)} +OptionalRewardTrackDict = {TT_TIER: (), + TT_TIER + 1: (), + TT_TIER + 2: (1000, + 601, + 601, + 602, + 602, + 2205, + 2206, + 2205, + 2206), + TT_TIER + 3: (601, + 601, + 602, + 602, + 2205, + 2206, + 2205, + 2206), + DD_TIER: (1000, + 602, + 602, + 603, + 603, + 2101, + 2102, + 2105, + 2106), + DD_TIER + 1: (1000, + 602, + 602, + 603, + 603, + 2101, + 2102, + 2105, + 2106), + DD_TIER + 2: (1000, + 602, + 602, + 603, + 603, + 2101, + 2102, + 2105, + 2106), + DG_TIER: (1000, + 603, + 603, + 604, + 604, + 2501, + 2502, + 2503, + 2504, + 2505, + 2506), + MM_TIER: (1000, + 604, + 604, + 605, + 605, + 2403, + 2404, + 2405, + 2406, + 2407, + 2408, + 2409), + MM_TIER + 1: (1000, + 604, + 604, + 605, + 605, + 2403, + 2404, + 2405, + 2406, + 2407, + 2408, + 2409), + MM_TIER + 2: (1000, + 604, + 604, + 605, + 605, + 2403, + 2404, + 2405, + 2406, + 2407, + 2408, + 2409), + BR_TIER: (1000, + 606, + 606, + 606, + 606, + 606, + 607, + 607, + 607, + 607, + 607, + 2305, + 2306, + 2307, + 2308, + 2309, + 2310, + 2311), + BR_TIER + 1: (1000, + 606, + 606, + 606, + 606, + 606, + 607, + 607, + 607, + 607, + 607, + 2305, + 2306, + 2307, + 2308, + 2309, + 2310, + 2311), + BR_TIER + 2: (1000, + 606, + 606, + 606, + 606, + 606, + 607, + 607, + 607, + 607, + 607, + 2305, + 2306, + 2307, + 2308, + 2309, + 2310, + 2311), + DL_TIER: (607, + 607, + 607, + 607, + 608, + 608, + 608, + 608, + 2901, + 2902, + 2907, + 2908, + 2909, + 2910, + 2911), + DL_TIER + 1: (1000, + 607, + 607, + 607, + 607, + 608, + 608, + 608, + 608, + 2923, + 2924, + 2927, + 2928, + 2929, + 2930, + 2931), + DL_TIER + 2: (608, + 608, + 608, + 608, + 609, + 609, + 609, + 609, + 2941, + 2942, + 2943, + 2944, + 2947, + 2948, + 2949, + 2950, + 2951), + DL_TIER + 3: (1000, + 609, + 609, + 609, + 609, + 609, + 609, + 2961, + 2962, + 2963, + 2964, + 2965, + 2966, + 2967, + 2968, + 2969, + 2970, + 2971), + ELDER_TIER: (1000, + 1000, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 2961, + 2962, + 2963, + 2964, + 2965, + 2966, + 2967, + 2968, + 2969, + 2970, + 2971)} + +def isRewardOptional(tier, rewardId): + return OptionalRewardTrackDict.has_key(tier) and rewardId in OptionalRewardTrackDict[tier] + + +def getItemName(itemId): + return ItemDict[itemId][0] + + +def getPluralItemName(itemId): + return ItemDict[itemId][1] + + +def avatarHasTrolleyQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == TROLLEY_QUEST_ID + + +def avatarHasCompletedTrolleyQuest(av): + return av.quests[0][4] > 0 + + +def avatarHasFirstCogQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == FIRST_COG_QUEST_ID + + +def avatarHasCompletedFirstCogQuest(av): + return av.quests[0][4] > 0 + + +def avatarHasFriendQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == FRIEND_QUEST_ID + + +def avatarHasCompletedFriendQuest(av): + return av.quests[0][4] > 0 + + +def avatarHasPhoneQuest(av): + return len(av.quests) == 1 and av.quests[0][0] == PHONE_QUEST_ID + + +def avatarHasCompletedPhoneQuest(av): + return av.quests[0][4] > 0 + + +def avatarWorkingOnRequiredRewards(av): + tier = av.getRewardTier() + rewardList = list(getRewardsInTier(tier)) + for i in range(len(rewardList)): + actualRewardId = transformReward(rewardList[i], av) + rewardList[i] = actualRewardId + + for questDesc in av.quests: + questId = questDesc[0] + rewardId = questDesc[3] + if rewardId in rewardList: + return 1 + elif rewardId == NA: + rewardId = transformReward(getFinalRewardId(questId, fAll=1), av) + if rewardId in rewardList: + return 1 + + return 0 + + +def avatarHasAllRequiredRewards(av, tier): + rewardHistory = list(av.getRewardHistory()[1]) + rewardList = getRewardsInTier(tier) + notify.debug('checking avatarHasAllRequiredRewards: history: %s, tier: %s' % (rewardHistory, rewardList)) + for rewardId in rewardList: + if rewardId == 900: + found = 0 + for actualRewardId in (901, 902, 903, 904, 905, 906, 907): + if actualRewardId in rewardHistory: + found = 1 + rewardHistory.remove(actualRewardId) + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId 900 found as: %s' % actualRewardId) + break + + if not found: + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId 900 not found') + return 0 + else: + actualRewardId = transformReward(rewardId, av) + if actualRewardId in rewardHistory: + rewardHistory.remove(actualRewardId) + elif getRewardClass(rewardId) == CogSuitPartReward: + deptStr = RewardDict.get(rewardId)[1] + cogPart = RewardDict.get(rewardId)[2] + dept = ToontownGlobals.cogDept2index[deptStr] + if av.hasCogPart(cogPart, dept): + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId: %s counts, avatar has cog part: %s dept: %s' % (actualRewardId, cogPart, dept)) + else: + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: CogSuitPartReward: %s not found' % actualRewardId) + return 0 + else: + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: rewardId %s not found' % actualRewardId) + return 0 + + if notify.getDebug(): + notify.debug('avatarHasAllRequiredRewards: remaining rewards: %s' % rewardHistory) + for rewardId in rewardHistory: + if not isRewardOptional(tier, rewardId): + notify.warning('required reward found, expected only optional: %s' % rewardId) + + return 1 + + +def nextQuestList(nextQuest): + if nextQuest == NA: + return None + seqTypes = (types.ListType, types.TupleType) + if type(nextQuest) in seqTypes: + return nextQuest + else: + return (nextQuest,) + return None + + +def checkReward(questId, forked = 0): + quest = QuestDict[questId] + reward = quest[5] + nextQuests = nextQuestList(quest[6]) + if nextQuests is None: + validRewards = RewardDict.keys() + [Any, + AnyCashbotSuitPart, + AnyLawbotSuitPart, + OBSOLETE] + if reward is OBSOLETE: + print 'warning: quest %s is obsolete' % questId + return reward + else: + forked = forked or len(nextQuests) > 1 + firstReward = checkReward(nextQuests[0], forked) + for qId in nextQuests[1:]: + thisReward = checkReward(qId, forked) + + return firstReward + return + + +def assertAllQuestsValid(): + print 'checking quests...' + for questId in QuestDict.keys(): + try: + quest = getQuest(questId) + except AssertionError, e: + err = 'invalid quest: %s' % questId + print err + raise + + for questId in QuestDict.keys(): + quest = QuestDict[questId] + tier, start, questDesc, fromNpc, toNpc, reward, nextQuest, dialog = quest + if start: + checkReward(questId) diff --git a/toontown/quest/TrackChoiceGui.py b/toontown/quest/TrackChoiceGui.py new file mode 100644 index 0000000..72a7c95 --- /dev/null +++ b/toontown/quest/TrackChoiceGui.py @@ -0,0 +1,69 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import TTLocalizer + +class TrackPoster(DirectFrame): + normalTextColor = (0.3, 0.25, 0.2, 1) + + def __init__(self, trackId, callback): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(TrackPoster) + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + trackName = ToontownBattleGlobals.Tracks[trackId].capitalize() + self.poster = DirectFrame(parent=self, relief=None, image=bookModel.find('**/questCard'), image_scale=(0.8, 0.58, 0.58)) + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + iconGeom = invModel.find('**/' + ToontownBattleGlobals.AvPropsNew[trackId][1]) + invModel.removeNode() + self.pictureFrame = DirectFrame(parent=self.poster, relief=None, image=bookModel.find('**/questPictureFrame'), image_scale=0.25, image_color=(0.45, 0.8, 0.45, 1), text=trackName, text_font=ToontownGlobals.getInterfaceFont(), text_pos=(0, -0.16), text_fg=self.normalTextColor, text_scale=0.05, text_align=TextNode.ACenter, text_wordwrap=8.0, textMayChange=0, geom=iconGeom, pos=(-0.2, 0, 0.06)) + bookModel.removeNode() + if trackId == ToontownBattleGlobals.HEAL_TRACK: + help = TTLocalizer.TrackChoiceGuiHEAL + elif trackId == ToontownBattleGlobals.TRAP_TRACK: + help = TTLocalizer.TrackChoiceGuiTRAP + elif trackId == ToontownBattleGlobals.LURE_TRACK: + help = TTLocalizer.TrackChoiceGuiLURE + elif trackId == ToontownBattleGlobals.SOUND_TRACK: + help = TTLocalizer.TrackChoiceGuiSOUND + elif trackId == ToontownBattleGlobals.DROP_TRACK: + help = TTLocalizer.TrackChoiceGuiDROP + else: + help = '' + self.helpText = DirectFrame(parent=self.poster, relief=None, text=help, text_font=ToontownGlobals.getInterfaceFont(), text_fg=self.normalTextColor, text_scale=0.05, text_align=TextNode.ALeft, text_wordwrap=8.0, textMayChange=0, pos=(-0.05, 0, 0.14)) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.chooseButton = DirectButton(parent=self.poster, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.TrackChoiceGuiChoose, text_scale=0.06, text_pos=(0, -0.02), command=callback, extraArgs=[trackId], pos=(0, 0, -0.16), scale=0.8) + guiButton.removeNode() + return + + +class TrackChoiceGui(DirectFrame): + + def __init__(self, tracks, timeout): + DirectFrame.__init__(self, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=Vec4(0.8, 0.6, 0.4, 1), geom_scale=(1.5, 1, 0.9), geom_hpr=(0, 0, -90), pos=(-0.85, 0, 0)) + self.initialiseoptions(TrackChoiceGui) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.cancelButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.7, 1, 1), text=TTLocalizer.TrackChoiceGuiCancel, pos=(0.15, 0, -0.625), text_scale=0.06, text_pos=(0, -0.02), command=self.chooseTrack, extraArgs=[-1]) + guiButton.removeNode() + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(self) + self.timer.setScale(0.35) + self.timer.setPos(-0.2, 0, -0.6) + self.timer.countdown(timeout, self.timeout) + self.trackChoicePosters = [] + for trackId in tracks: + tp = TrackPoster(trackId, self.chooseTrack) + tp.reparentTo(self) + self.trackChoicePosters.append(tp) + + self.trackChoicePosters[0].setPos(0, 0, -0.2) + self.trackChoicePosters[1].setPos(0, 0, 0.4) + return + + def chooseTrack(self, trackId): + self.timer.stop() + messenger.send('chooseTrack', [trackId]) + + def timeout(self): + messenger.send('chooseTrack', [-1]) diff --git a/toontown/quest/__init__.py b/toontown/quest/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/racing/DistributedGag.py b/toontown/racing/DistributedGag.py new file mode 100644 index 0000000..1a5faff --- /dev/null +++ b/toontown/racing/DistributedGag.py @@ -0,0 +1,105 @@ +from pandac.PandaModules import * +from direct.distributed import DistributedObject +from direct.interval.ProjectileInterval import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from toontown.racing.DistributedVehicle import DistributedVehicle +from DroppedGag import * + +class DistributedGag(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.nodePath = None + self.billboard = False + self.scale = 1 + self.shadow = True + self.dropShadow = None + self.type = 0 + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.nodePath.delete() + self.ignoreAll() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if not self.nodePath: + self.makeNodePath() + self.delta = -globalClockDelta.networkToLocalTime(self.initTime, globalClock.getFrameTime(), 16, 100) + globalClock.getFrameTime() + if self.type == 0: + self.name = self.uniqueName('banana') + elif self.type == 1: + self.name = self.uniqueName('pie') + self.nodePath.reparentTo(self.race.geom) + if self.ownerId == localAvatar.doId: + base.race.thrownGags[0].remove() + base.race.thrownGags = base.race.thrownGags[1:] + self.nodePath.setPos(self.pos[0], self.pos[1], self.pos[2]) + else: + startPos = base.cr.doId2do[self.ownerId].getPos(render) + endPos = Vec3(self.pos[0], self.pos[1], self.pos[2]) + throwIt = ProjectileInterval(self.nodePath, startPos=startPos, endPos=endPos, duration=1) + throwIt.start() + taskMgr.doMethodLater(0.8 - self.delta, self.addCollider, self.uniqueName('addCollider')) + + def addCollider(self, t): + bs = CollisionSphere(0, 0, 0, 2) + bn = CollisionNode(self.name) + self.bnp = NodePath(bn) + self.bnp.reparentTo(self.nodePath) + self.bnp.node().addSolid(bs) + self.bnp.node().setIntoCollideMask(BitMask32(32768)) + self.bnp.node().setFromCollideMask(BitMask32(32768)) + self.accept('imIn-' + self.name, self.b_imHit) + + def b_imHit(self, cevent): + self.ignoreAll() + self.sendUpdate('hitSomebody', [localAvatar.doId, globalClockDelta.getFrameNetworkTime(16, 100)]) + if self.type == 0: + base.race.localKart.hitBanana() + elif self.type == 1: + base.race.localKart.hitPie() + self.nodePath.hide() + if hasattr(self, 'bnp'): + self.bnp.remove() + + def hitSomebody(self, avId, timeStamp): + if localAvatar.doId != avId: + kart = DistributedVehicle.getKartFromAvId(avId) + if kart: + self.nodePath.hide() + if hasattr(self, 'bnp'): + self.bnp.remove() + kart.playSpin(timeStamp) + + def setActivateTime(self, actTime): + self.activateTime = actTime + + def setInitTime(self, initTime): + self.initTime = initTime + + def setRace(self, doId): + self.race = base.cr.doId2do.get(doId) + + def setPos(self, x, y, z): + self.pos = (x, y, z) + + def makeNodePath(self): + if self.type == 0: + self.nodePath = DroppedGag(self.uniqueName('gag'), base.race.banana) + if self.billboard: + self.nodePath.setBillboardPointEye() + self.nodePath.setScale(0.9 * self.scale) + if self.type == 1: + self.nodePath = DroppedGag(self.uniqueName('gag'), base.race.banana) + if self.billboard: + self.nodePath.setBillboardPointEye() + self.nodePath.setScale(4.0 * self.scale) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def setType(self, type): + self.type = type diff --git a/toontown/racing/DistributedKartPad.py b/toontown/racing/DistributedKartPad.py new file mode 100644 index 0000000..d13c378 --- /dev/null +++ b/toontown/racing/DistributedKartPad.py @@ -0,0 +1,25 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.DistributedObject import DistributedObject +if (__debug__): + import pdb + +class DistributedKartPad(DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedKartPad') + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + self.startingBlocks = [] + + def delete(self): + del self.startingBlocks + DistributedObject.delete(self) + + def setArea(self, area): + self.area = area + + def getArea(self): + return self.area + + def addStartingBlock(self, block): + self.startingBlocks.append(block) + self.notify.debug('KartPad %s has added starting block %s' % (self.doId, block.doId)) diff --git a/toontown/racing/DistributedLeaderBoard.py b/toontown/racing/DistributedLeaderBoard.py new file mode 100644 index 0000000..020df0f --- /dev/null +++ b/toontown/racing/DistributedLeaderBoard.py @@ -0,0 +1,169 @@ +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPTimer +from toontown.toonbase import TTLocalizer +from toontown.racing import KartShopGlobals +from toontown.toonbase.ToonBaseGlobal import * +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +import random +import cPickle + +class DistributedLeaderBoard(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DisributedLeaderBoard') + + def __init__(self, cr): + self.notify.debug('__init__: initialization of local leaderboard') + DistributedObject.DistributedObject.__init__(self, cr) + self.corner = 0 + self.length = 0 + self.width = 0 + self.updateCount = 0 + self.board = None + self.surface = None + return + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.board = NodePath(self.uniqueName('LeaderBoard')) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.buildListParts() + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.board.reparentTo(render) + self.accept('decorator-holiday-%d-ending' % ToontownGlobals.CRASHED_LEADERBOARD, self.showLists) + self.accept('decorator-holiday-%d-starting' % ToontownGlobals.CRASHED_LEADERBOARD, self.hideLists) + newsManager = base.cr.newsManager + if newsManager: + if ToontownGlobals.CRASHED_LEADERBOARD in newsManager.holidayIdList: + self.hideLists() + + def showLists(self): + self.board.show() + + def hideLists(self): + self.board.hide() + + def setPosHpr(self, x, y, z, h, p, r): + self.surface.setPosHpr(x, y, z, h, p, r) + + def setDisplay(self, pData): + self.notify.debug('setDisplay: changing leaderboard text on local side') + trackName, recordTitle, scores = cPickle.loads(pData) + self.display(trackName, recordTitle, scores) + + def buildListParts(self): + self.surface = self.board.attachNewNode('surface') + z = 7.7 + dz = 0.4 + x = -3.7 + row, trackName = self.buildTrackRow() + self.trackNameNode = trackName + row.reparentTo(self.surface) + row.setPos(0, 1.6, z) + z = 7.3 + row, self.titleTextNode = self.buildTitleRow() + row.reparentTo(self.surface) + row.setPos(0, 1.6, z) + zListTop = 6.9 + z = zListTop + self.nameTextNodes = [] + self.timeTextNodes = [] + for i in range(10): + row, nameText, timeText, placeText = self.buildLeaderRow() + self.nameTextNodes.append(nameText) + placeText.setText(str(len(self.nameTextNodes)) + '.') + self.timeTextNodes.append(timeText) + row.reparentTo(self.surface) + if len(self.nameTextNodes) == 6: + z = zListTop + x = 0.35 + row.setX(x) + row.setZ(z) + row.setY(1.6) + z -= dz + + self.surface.flattenLight() + + def display(self, pTrackTitle = 'Track Title', pPeriodTitle = 'Period Title', pLeaderList = []): + self.titleTextNode.setText(pPeriodTitle) + self.trackNameNode.setText(pTrackTitle) + self.updateCount += 1 + for i in range(10): + if i > len(pLeaderList): + self.nameTextNodes[i].setText('-') + self.timeTextNodes[i].setText('-') + else: + name = pLeaderList[i][1] + time = pLeaderList[i][0] + secs, hundredths = divmod(time, 1) + min, sec = divmod(secs, 60) + self.nameTextNodes[i].setText(name[:22]) + self.timeTextNodes[i].setText('%02d:%02d:%02d' % (min, sec, hundredths * 100)) + + def buildTitleRow(self): + row = hidden.attachNewNode('TitleRow') + nameText = TextNode('titleRow') + nameText.setFont(ToontownGlobals.getSignFont()) + nameText.setAlign(TextNode.ACenter) + nameText.setTextColor(0.3, 0.75, 0.6, 1) + nameText.setText('Score Title') + namePath = row.attachNewNode(nameText) + namePath.setScale(TTLocalizer.DLBbuildTitleRow) + namePath.setDepthWrite(0) + return (row, nameText) + + def buildTrackRow(self): + row = hidden.attachNewNode('trackRow') + nameText = TextNode('trackRow') + nameText.setFont(ToontownGlobals.getSignFont()) + nameText.setAlign(TextNode.ACenter) + nameText.setTextColor(0.5, 0.75, 0.7, 1) + nameText.setText('Track Title') + namePath = row.attachNewNode(nameText) + namePath.setScale(0.55) + namePath.setDepthWrite(0) + return (row, nameText) + + def buildLeaderRow(self): + row = hidden.attachNewNode('leaderRow') + nameText = TextNode('nameText') + nameText.setFont(ToontownGlobals.getToonFont()) + nameText.setAlign(TextNode.ALeft) + nameText.setTextColor(0.125, 0, 0.5, 1) + nameText.setText('-') + namePath = row.attachNewNode(nameText) + namePath.setPos(1.1, 0, 0) + namePath.setScale(0.23) + namePath.setDepthWrite(0) + timeText = TextNode('timeText') + timeText.setFont(ToontownGlobals.getToonFont()) + timeText.setAlign(TextNode.ARight) + timeText.setTextColor(0, 0, 0, 1) + timeText.setText('-') + timePath = row.attachNewNode(timeText) + timePath.setPos(1.0, 0, 0) + timePath.setScale(0.23) + timePath.setDepthWrite(0) + placeText = TextNode('placeText') + placeText.setFont(ToontownGlobals.getSignFont()) + placeText.setAlign(TextNode.ARight) + placeText.setTextColor(1, 1, 0.1, 1) + placeText.setText('-') + placePath = row.attachNewNode(placeText) + placePath.setPos(-0.1, 0, -0.05) + placePath.setScale(0.3) + placePath.setDepthWrite(0) + return (row, + nameText, + timeText, + placeText) + + def delete(self): + self.notify.debug('delete: deleting local leaderboard') + self.ignoreAll() + self.board.removeNode() + DistributedObject.DistributedObject.delete(self) diff --git a/toontown/racing/DistributedProjectile.py b/toontown/racing/DistributedProjectile.py new file mode 100644 index 0000000..c2c9ced --- /dev/null +++ b/toontown/racing/DistributedProjectile.py @@ -0,0 +1,41 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import FSM +from direct.distributed.DistributedSmoothNode import DistributedSmoothNode +from otp.avatar.ShadowCaster import ShadowCaster + +class DistributedProjectile(DistributedSmoothNode, ShadowCaster, NodePath): + + def __init__(self, cr): + ShadowCaster.__init__(self) + DistributedSmoothNode.__init__(self, cr) + NodePath.__init__(self, 'Projectile') + + def announceGenerate(self): + DistributedSmoothNode.announceGenerate(self) + self.name = self.uniqueName('projectile') + self.posHprBroadcastName = self.uniqueName('projectileBroadcast') + geom = loader.loadModel('models/smiley') + self.geom = geom + self.geom.reparentTo(self) + self.startSmooth() + self.reparentTo(render) + + def generate(self): + DistributedSmoothNode.generate(self) + self.name = self.uniqueName('projectile') + self.posHprBroadcastName = self.uniqueName('projectileBroadcast') + geom = loader.loadModel('models/smiley') + self.geom = geom + self.geom.reparentTo(self) + self.startSmooth() + self.reparentTo(render) + + def setAvId(self, avId): + self.avId = avId + + def delete(self): + DistributedSmoothNode.delete(self) diff --git a/toontown/racing/DistributedRace.py b/toontown/racing/DistributedRace.py new file mode 100644 index 0000000..1e3059d --- /dev/null +++ b/toontown/racing/DistributedRace.py @@ -0,0 +1,1235 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectLabel import * +from direct.gui.DirectButton import * +from direct.showbase import BulletinBoardWatcher +from direct.interval.IntervalGlobal import * +from otp.otpbase import OTPGlobals +from direct.interval.IntervalGlobal import * +from RaceGag import RaceGag +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toon import ToonHeadFrame +from toontown.racing.KartDNA import InvalidEntry, getAccessory, getDefaultColor +from pandac.PandaModules import CardMaker, OrthographicLens, LineSegs +from direct.distributed import DistributedSmoothNode +from math import fmod +from math import sqrt +from RaceGUI import RaceGUI +import RaceGlobals +from direct.task.Task import Task +from toontown.hood import SkyUtil +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.battle.BattleProps import * +from toontown.minigame import MinigameRulesPanel +from toontown.racing import Piejectile +from toontown.racing import EffectManager +from toontown.racing import PiejectileManager + +class DistributedRace(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRace') + ReadyPost = 'RaceReady' + WinEvent = 'RaceWinEvent' + BGM_BaseDir = 'phase_6/audio/bgm/' + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_StartBoop = SFX_BaseDir + 'KART_raceStart1.mp3' + SFX_StartBoop2 = SFX_BaseDir + 'KART_raceStart2.mp3' + SFX_Applause = SFX_BaseDir + 'KART_Applause_%d.mp3' + + def __init__(self, cr): + self.qbox = loader.loadModel('phase_6/models/karting/qbox') + self.boostArrowTexture = loader.loadTexture('phase_6/maps/boost_arrow.jpg', 'phase_6/maps/boost_arrow_a.rgb') + self.boostArrowTexture.setMinfilter(Texture.FTLinear) + DistributedObject.DistributedObject.__init__(self, cr) + self.kartMap = {} + self.fsm = ClassicFSM.ClassicFSM('Race', [State.State('join', self.enterJoin, self.exitJoin, ['prep', 'leave']), + State.State('prep', self.enterPrep, self.exitPrep, ['tutorial', 'leave']), + State.State('tutorial', self.enterTutorial, self.exitTutorial, ['start', 'waiting', 'leave']), + State.State('waiting', self.enterWaiting, self.exitWaiting, ['start', 'leave']), + State.State('start', self.enterStart, self.exitStart, ['racing', 'leave']), + State.State('racing', self.enterRacing, self.exitRacing, ['finished', 'leave']), + State.State('finished', self.enterFinished, self.exitFinished, ['leave']), + State.State('leave', self.enterLeave, self.exitLeave, [])], 'join', 'leave') + self.gui = RaceGUI(self) + base.race = self + self.currT = 0 + self.currLapT = 0 + self.currGag = 0 + self.tdelay = 0 + self.finished = False + self.thrownGags = [] + self.effectManager = EffectManager.EffectManager() + self.piejectileManager = PiejectileManager.PiejectileManager() + self.lastTimeUpdate = globalClock.getFrameTime() + self.initGags() + self.canShoot = True + self.isUrbanTrack = False + self.hasFog = False + self.dummyNode = None + self.fog = None + self.bananaSound = base.loadSfx('phase_6/audio/sfx/KART_tossBanana.mp3') + self.anvilFall = base.loadSfx('phase_6/audio/sfx/KART_Gag_Hit_Anvil.mp3') + self.accept('leaveRace', self.leaveRace) + self.toonsToLink = [] + self.curveTs = [] + self.curvePoints = [] + self.localKart = None + self.musicTrack = None + self.victory = None + self.miscTaskNames = [] + self.boostDir = {} + self.knownPlace = {} + self.placeFixup = [] + self.curve = None + self.barricadeSegments = 100.0 + self.outerBarricadeDict = {} + self.innerBarricadeDict = {} + self.maxLap = 0 + self.oldT = 0 + self.debugIt = 0 + self.startPos = None + return + + def generate(self): + self.notify.debug('generate: %s' % self.doId) + DistributedObject.DistributedObject.generate(self) + bboard.post('race', self) + self.roomWatcher = None + self.cutoff = 0.01 + self.startBoopSfx = base.loadSfx(self.SFX_StartBoop) + self.startBoop2Sfx = base.loadSfx(self.SFX_StartBoop2) + return + + def announceGenerate(self): + self.notify.debug('announceGenerate: %s' % self.doId) + DistributedObject.DistributedObject.announceGenerate(self) + musicFile = self.BGM_BaseDir + RaceGlobals.TrackDict[self.trackId][7] + self.raceMusic = base.loadMusic(musicFile) + base.playMusic(self.raceMusic, looping=1, volume=0.8) + camera.reparentTo(render) + if self.trackId in (RaceGlobals.RT_Urban_1, + RaceGlobals.RT_Urban_1_rev, + RaceGlobals.RT_Urban_2, + RaceGlobals.RT_Urban_2_rev): + self.isUrbanTrack = True + self.oldFarPlane = base.camLens.getFar() + base.camLens.setFar(12000) + localAvatar.startPosHprBroadcast() + localAvatar.d_broadcastPositionNow() + DistributedSmoothNode.activateSmoothing(1, 1) + self.reversed = self.trackId / 2.0 > int(self.trackId / 2.0) + for i in range(3): + base.loader.tick() + + self.sky = loader.loadModel('phase_3.5/models/props/TT_sky') + self.sky.setPos(0, 0, 0) + self.sky.setScale(20.0) + self.sky.setFogOff() + if self.trackId in (RaceGlobals.RT_Urban_1, + RaceGlobals.RT_Urban_1_rev, + RaceGlobals.RT_Urban_2, + RaceGlobals.RT_Urban_2_rev): + self.loadFog() + self.setupGeom() + self.startSky() + for i in range(5): + base.loader.tick() + + def disable(self): + self.notify.debug('disable %s' % self.doId) + if self.musicTrack: + self.musicTrack.finish() + self.raceMusic.stop() + self.stopSky() + if self.sky is not None: + self.sky.removeNode() + if self.dummyNode: + self.dummyNode.removeNode() + self.dummyNode = None + for taskName in self.miscTaskNames: + taskMgr.remove(taskName) + + taskMgr.remove('raceWatcher') + self.ignoreAll() + DistributedSmoothNode.activateSmoothing(1, 0) + if self.isUrbanTrack: + self.unloadUrbanTrack() + if self.fog: + render.setFogOff() + del self.fog + self.fog = None + if self.geom is not None: + self.geom.hide() + base.camLens.setFar(self.oldFarPlane) + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.notify.debug('delete %s' % self.doId) + if self.gui: + self.gui.destroy() + self.gui = None + if self.geom is not None: + self.geom.removeNode() + self.geom = None + for i in self.gags: + i.delete() + del i + + self.piejectileManager.delete() + if self.curveTs: + del self.curveTs + if self.curvePoints: + del self.curvePoints + if self.curve: + del self.curve + if self.victory: + del self.victory + del self.fsm + del self.anvilFall + del self.bananaSound + del self.localKart + DistributedObject.DistributedObject.delete(self) + taskMgr.remove(self.uniqueName('countdownTimerTask')) + taskMgr.remove('raceWatcher') + bboard.remove('race') + self.ignoreAll() + del base.race + return + + def d_requestThrow(self, x, y, z): + self.sendUpdate('requestThrow', [x, y, z]) + + def d_requestKart(self): + self.sendUpdate('requestKart', []) + + def waitingForJoin(self): + self.notify.debug('I got the barrier') + self.fsm.enterInitialState() + + def racerDisconnected(self, avId): + self.notify.debug('lost racer: %s' % avId) + if avId in self.kartMap: + if avId in self.toonsToLink: + self.toonsToLink.remove(avId) + toon = base.cr.doId2do.get(avId, None) + kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None) + self.avIds.remove(avId) + del self.kartMap[avId] + self.gui.racerLeft(avId, unexpected=True) + if kart: + kart.reparentTo(hidden) + if toon: + toon.reparentTo(hidden) + if len(self.toonsToLink) == 0: + self.doneBarrier('waitingForPrep') + return + + def setPlace(self, avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime): + if self.fsm.getCurrentState().getName() == 'leaving': + return + if avId == localAvatar.doId: + cheerToPlay = place + (4 - self.numRacers) + if cheerToPlay > 4: + cheerToPlay = 4 + self.victory = base.loadSfx(self.SFX_Applause % cheerToPlay) + self.victory.play() + self.knownPlace[avId] = place + kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None) + avatar = base.cr.doId2do.get(avId, None) + if avatar: + self.gui.racerFinished(avId, self.trackId, place, totalTime, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime) + taskName = 'hideAv: %s' % avId + taskMgr.doMethodLater(6, avatar.reparentTo, taskName, extraArgs=[hidden]) + self.miscTaskNames.append(taskName) + if kart: + taskName = 'hideKart: %s' % self.localKart.doId + taskMgr.doMethodLater(6, kart.reparentTo, taskName, extraArgs=[hidden]) + self.miscTaskNames.append(taskName) + return + + def setCircuitPlace(self, avId, place, entryFee, winnings, bonus, trophies): + print 'setting cicruit place' + if self.fsm.getCurrentState().getName() == 'leaving': + return + if avId == localAvatar.doId: + cheerToPlay = place + (4 - self.numRacers) + self.victory = base.loadSfx(self.SFX_Applause % cheerToPlay) + self.victory.play() + oldPlace = 0 + if self.knownPlace.get(avId): + oldPlace = self.knownPlace[avId] + self.placeFixup.append([oldPlace - 1, place - 1]) + avatar = base.cr.doId2do.get(avId, None) + if avatar: + print 'circuit trophies %s' % trophies + print 'winnings %s' % winnings + self.gui.racerFinishedCircuit(avId, oldPlace, entryFee, winnings, bonus, trophies) + return + + def endCircuitRace(self): + print self.placeFixup + self.gui.circuitFinished(self.placeFixup) + + def prepForRace(self): + self.fsm.request('prep') + + def startRace(self, startTime = 0): + self.baseTime = globalClockDelta.networkToLocalTime(startTime) + self.fsm.request('start') + + def startTutorial(self): + self.fsm.request('tutorial') + + def genGag(self, slot, number, type): + self.notify.debug('making gag...') + if not self.gags[slot].isActive(): + self.gags[slot].genGag(number, type) + + def dropAnvilOn(self, ownerId, avId, timeStamp): + kart = base.cr.doId2do.get(self.kartMap.get(avId, None), None) + if kart: + if avId != ownerId: + if avId == localAvatar.doId: + self.anvilFall.play() + kart.dropOnMe(timeStamp) + else: + kart.dropOnHim(timeStamp) + return + + def shootPiejectile(self, sourceId, targetId, type = 0): + kart = base.cr.doId2do.get(self.kartMap.get(sourceId, None), None) + if kart: + self.piejectileManager.addPiejectile(sourceId, targetId, type) + return + + def goToSpeedway(self, avIds, reason = RaceGlobals.Exit_UserReq): + self.notify.debug('goToSpeedway %s %s' % (avIds, reason)) + if localAvatar.doId in avIds: + base.loader.endBulkLoad('atRace') + self.kartCleanup() + self.doneBarrier('waitingForExit') + self.sendUpdate('racerLeft', [localAvatar.doId]) + out = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'hoodId': localAvatar.lastHood, + 'zoneId': localAvatar.lastHood, + 'shardId': None, + 'avId': -1, + 'reason': reason} + base.cr.playGame.fsm.request('quietZone', [out]) + return + + def kartCleanup(self): + kart = self.localKart + if kart: + kart.setState('P', 0) + for i in self.avIds: + if i != localAvatar.doId: + toon = base.cr.doId2do.get(i, None) + if toon: + toon.stopSmooth() + toon.setScale(1) + toon.setShear(0, 0, 0) + toon.reparentTo(render) + kart.doHeadScale(toon, None) + + localAvatar.setPos(0, 14, 0) + localAvatar.sendCurrentPosition() + return + + def heresMyT(self, avId, avNumLaps, avTime, timestamp): + self.gui.updateRacerInfo(avId, curvetime=avNumLaps + avTime) + + def setZoneId(self, zoneId): + self.zoneId = zoneId + + def setRaceType(self, raceType): + self.raceType = raceType + + def setCircuitLoop(self, circuitLoop): + self.circuitLoop = circuitLoop + + def setTrackId(self, id): + DistributedRace.notify.debug('setTrackId: %s' % id) + self.trackId = id + + def setAvatars(self, avIds): + ids = '' + for i in avIds: + ids += str(i) + ' ' + + DistributedRace.notify.debug('setAvatars: %s' % ids) + self.avIds = avIds + self.avT = [0] * len(self.avIds) + + def setLapCount(self, lapCount): + self.lapCount = lapCount + + def setStartingPlaces(self, startList): + self.startingPlaces = startList + + def enterJoin(self): + self.doneBarrier('waitingForJoin') + self.notify.debug('entering Join') + + def exitJoin(self): + pass + + def setEnteredRacers(self, avAndKarts): + self.notify.debug('setEnteredRacers %s' % avAndKarts) + avatarsGone = [] + avatarsLeft = [] + self.numRacers = len(avAndKarts) + for i in avAndKarts: + if i[0] in self.avIds: + self.kartMap[i[0]] = i[1] + avatarsLeft.append(i[0]) + + for i in self.avIds: + if i not in avatarsLeft: + avatarsGone.append(i) + + base.loader.tick() + for i in avatarsGone: + self.avIds.remove(i) + + self.toonsToLink = list(self.avIds) + for i in avAndKarts: + self.cr.relatedObjectMgr.requestObjects(i, allCallback=self.__gotKartAvatarLink) + + def __gotKartAvatarLink(self, avAndKart): + self.notify.debug('got a Link') + toon = avAndKart[0] + kart = avAndKart[1] + base.loader.tick() + if toon.doId in self.toonsToLink: + self.toonsToLink.remove(toon.doId) + if toon.doId == localAvatar.doId: + self.localKart = kart + if len(self.toonsToLink) == 0: + self.doneBarrier('waitingForPrep') + + def enterPrep(self): + self.d_requestKart() + self.notify.debug('entering Prep State') + if self.reversed: + self.spin = Vec3(180, 0, 0) + else: + self.spin = Vec3(0, 0, 0) + for i in range(4): + base.loader.tick() + + self.gui.initRaceMode() + self.gui.initResultMode() + self.myPos = self.startingPos[self.startingPlaces[self.avIds.index(localAvatar.doId)]] + self.localKart.setPosHpr(self.myPos[0], self.myPos[1] + self.spin) + self.localKart.setupLapCollisions() + if self.dummyNode: + self.dummyNode.setPosHpr(self.myPos[0], self.myPos[1] + self.spin) + self.currentPole = self.findSegmentStart() + self.rabbitPoint = Vec3(0, 0, 0) + self.doneBarrier('waitingForReady') + + def exitPrep(self): + pass + + def enterTutorial(self): + self.notify.debug('entering Tutorial State') + base.loader.endBulkLoad('atRace') + self.localKart.setPosHpr(self.myPos[0], self.myPos[1] + self.spin) + base.transitions.irisIn() + self.rulesDoneEvent = 'finishedRules' + self.accept(self.rulesDoneEvent, self.handleRulesDone) + self.rulesPanel = MinigameRulesPanel.MinigameRulesPanel('RacingRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, 10) + self.rulesPanel.load() + self.rulesPanel.frame.setPos(0, 0, -0.6667) + self.rulesPanel.enter() + + def exitTutorial(self): + self.ignore(self.rulesDoneEvent) + self.rulesPanel.exit() + self.rulesPanel.unload() + del self.rulesPanel + + def getTitle(self): + return TTLocalizer.KartRace_TitleInfo + + def getInstructions(self): + return TTLocalizer.KartRace_TrackInfo[self.trackId] + + def handleRulesDone(self): + self.doneBarrier('readRules') + self.fsm.request('waiting') + + def enterWaiting(self): + self.waitingLabel = DirectLabel() + self.waitingLabel['text'] = TTLocalizer.BuildingWaitingForVictors + self.waitingLabel.setScale(TTLocalizer.DRenterWaiting) + + def exitWaiting(self): + self.waitingLabel.remove() + + def enterStart(self): + waitTime = self.baseTime - globalClock.getFrameTime() + taskName = 'enableRaceModeLater' + taskMgr.doMethodLater(1, self.gui.enableRaceMode, taskName, extraArgs=[]) + self.miscTaskNames.append(taskName) + for i in self.avIds: + self.gui.racerEntered(i) + + self.startCountdownClock(waitTime, 0) + taskMgr.doMethodLater(waitTime, self.fsm.request, 'goToRacing', extraArgs=['racing']) + + def exitStart(self): + pass + + def enterRacing(self): + self.localKart.setInput(1) + self.gui.setTimerEnabled(True) + self.raceTask = taskMgr.add(self.raceWatcher, 'raceWatcher') + + def exitRacing(self): + pass + + def raceWatcher(self, task): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + if self.localKart.amIClampingPosition(): + self.notify.debug('teleporting kart %d back to main track' % localAvatar.doId) + self.localKart.setPos(self.curvePoints[self.currentPole]) + kartPoint = self.localKart.getPos() + direction = 0 + while True: + currPoint = self.curvePoints[self.currentPole] + nextPole = (self.currentPole + 1) % len(self.curvePoints) + nextPoint = self.curvePoints[nextPole] + segment = nextPoint - currPoint + segment.setZ(0) + segLength2 = segment.lengthSquared() + kartVector = kartPoint - currPoint + kartVector.setZ(0) + project = segment * (segment.dot(kartVector) / segLength2) + projLength2 = project.lengthSquared() + if project.dot(segment) < 0: + if direction == 1: + break + prevPole = (self.currentPole - 1) % len(self.curvePoints) + self.currentPole = prevPole + direction = -1 + elif projLength2 > segLength2: + if direction == -1: + break + self.currentPole = nextPole + direction = 1 + else: + break + + if self.dummyNode: + self.dummyNode.setPos(kartPoint[0], kartPoint[1], 0) + self.dummyNode.setHpr(self.localKart.getH(), 0, 0) + t = projLength2 / segLength2 + if self.debugIt: + self.notify.debug('self.debugIt = %d' % self.debugIt) + import pdb + pdb.set_trace() + if nextPole < self.currentPole: + newT = self.curveTs[self.currentPole] * (1 - t) + self.curve.getMaxT() * t + else: + newT = self.curveTs[self.currentPole] * (1 - t) + self.curveTs[nextPole] * t + kartDirection = self.localKart.forward.getPos(render) - self.localKart.getPos(render) + kartDirection.normalize() + project.normalize() + globalDirection = kartDirection.dot(project) + if globalDirection < 0: + self.wrongWay = True + elif globalDirection > 0.1: + self.wrongWay = False + newLapT = (newT - self.startT) / self.curve.getMaxT() % 1.0 + if newLapT - self.currLapT < -0.5: + self.laps += 1 + self.changeMusicTempo(1 + self.laps * 0.5) + self.notify.debug('crossed the start line: %s, %s, %s, %s' % (self.laps, + self.startT, + self.currT, + newT)) + elif newLapT - self.currLapT > 0.5: + self.laps -= 1 + self.changeMusicTempo(1 + self.laps * 0.5) + self.notify.debug('crossed the start line - wrong way: %s, %s, %s, %s' % (self.laps, + self.startT, + self.currT, + newT)) + self.currT = newT + self.currLapT = newLapT + if self.isUrbanTrack: + self.showBuildings(self.currT) + now = globalClock.getFrameTime() + timestamp = globalClockDelta.localToNetworkTime(now) + if self.laps == self.lapCount: + self.sendUpdate('heresMyT', [localAvatar.doId, + self.laps, + self.currLapT, + timestamp]) + self.fsm.request('finished') + if self.laps > self.maxLap: + self.maxLap = self.laps + self.sendUpdate('heresMyT', [localAvatar.doId, + self.laps, + self.currLapT, + timestamp]) + if now - self.lastTimeUpdate > 0.5: + self.lastTimeUpdate = now + self.sendUpdate('heresMyT', [localAvatar.doId, + self.laps, + self.currLapT, + timestamp]) + self.gui.updateRacerInfo(localAvatar.doId, curvetime=self.currLapT + self.laps) + self.gui.update(now) + return Task.cont + + def enterFinished(self): + taskMgr.remove('raceWatcher') + self.fadeOutMusic() + self.localKart.interruptTurbo() + self.localKart.disableControls() + taskName = 'parkIt' + taskMgr.doMethodLater(2, self.stopDriving, taskName, extraArgs=[]) + self.miscTaskNames.append(taskName) + self.finished = True + camera.reparentTo(render) + camera.setPos(self.localKart.getPos(render) + Vec3(0, 0, 10)) + camera.setH(self.localKart.getH(render) + 180) + self.gui.disableRaceMode() + self.gui.enableResultMode() + localAvatar.reparentTo(hidden) + self.localKart.reparentTo(hidden) + + def exitFinished(self): + pass + + def stopDriving(self): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + cpos = camera.getPos() + chpr = camera.getHpr() + localAvatar.reparentTo(hidden) + self.localKart.reparentTo(hidden) + self.localKart.stopSmooth() + self.localKart.stopPosHprBroadcast() + camera.setPos(cpos) + camera.setHpr(chpr) + return + + def enterLeave(self): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + taskMgr.remove('raceWatcher') + self.gui.disable() + if self.localKart: + self.localKart.disableControls() + base.transitions.irisOut() + if self.raceType == RaceGlobals.Circuit and not len(self.circuitLoop) == 0: + self.sendUpdate('racerLeft', [localAvatar.doId]) + else: + taskMgr.doMethodLater(1, self.goToSpeedway, 'leaveRace', extraArgs=[[localAvatar.doId], RaceGlobals.Exit_UserReq]) + if self.victory: + self.victory.stop() + self.bananaSound.stop() + self.anvilFall.stop() + return + + def exitLeave(self): + pass + + def getCountdownColor(self, countdownTimeInt): + clockNodeColors = [Vec4(0, 1, 0, 1), + Vec4(1, 1, 0, 1), + Vec4(1, 0.5, 0, 1), + Vec4(1, 0, 0, 1)] + i = max(min(countdownTimeInt, len(clockNodeColors) - 1), 0) + return clockNodeColors[i] + + def startCountdownClock(self, countdownTime, ts): + self.clockNode = TextNode('k') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + countdownInt = int(countdownTime) + self.clockNode.setTextColor(self.getCountdownColor(countdownInt)) + self.clockNode.setText(str(countdownInt)) + self.clock = render2d.attachNewNode(self.clockNode) + rs = TTLocalizer.DRrollScale + self.clock.setPosHprScale(0, 0, 0, 0, 0, 0, rs, rs, rs) + self.clock.hide() + if ts < countdownTime: + self.countdown(countdownTime - ts) + + def timerTask(self, task): + countdownTime = int(task.duration - task.time) + timeStr = str(countdownTime + 1) + if self.clock.isHidden(): + if task.duration - task.time <= task.maxCount: + self.clock.show() + if self.clockNode.getText() != timeStr: + self.startBoopSfx.play() + self.clockNode.setText(timeStr) + self.clockNode.setTextColor(self.getCountdownColor(countdownTime + 1)) + if task.time >= task.duration: + self.startBoop2Sfx.play() + self.clockNode.setText(TTLocalizer.KartRace_Go) + self.clockNode.setTextColor(self.getCountdownColor(-1)) + taskMgr.doMethodLater(1, self.endGoSign, 'removeGoSign') + return Task.done + else: + return Task.cont + + def endGoSign(self, t): + self.clock.remove() + + def countdown(self, duration): + countdownTask = Task(self.timerTask) + countdownTask.duration = duration + countdownTask.maxCount = RaceGlobals.RaceCountdown + taskMgr.remove(self.uniqueName('countdownTimerTask')) + return taskMgr.add(countdownTask, self.uniqueName('countdownTimerTask')) + + def initGags(self): + self.banana = globalPropPool.getProp('banana') + self.banana.setScale(2) + self.pie = globalPropPool.getProp('creampie') + self.pie.setScale(1) + + def makeCheckPoint(self, trigger, location, event): + cs = CollisionSphere(0, 0, 0, 140) + cs.setTangible(0) + triggerEvent = 'imIn-' + trigger + cn = CollisionNode(trigger) + cn.addSolid(cs) + cn.setIntoCollideMask(BitMask32(32768)) + cn.setFromCollideMask(BitMask32(32768)) + cnp = NodePath(cn) + cnp.reparentTo(self.geom) + cnp.setPos(location) + self.accept(triggerEvent, event) + + def loadUrbanTrack(self): + self.dnaStore = DNAStorage() + loader.loadDNAFile(self.dnaStore, 'phase_4/dna/storage.dna') + loader.loadDNAFile(self.dnaStore, 'phase_5/dna/storage_town.dna') + loader.loadDNAFile(self.dnaStore, 'phase_4/dna/storage_TT.dna') + loader.loadDNAFile(self.dnaStore, 'phase_5/dna/storage_TT_town.dna') + loader.loadDNAFile(self.dnaStore, 'phase_8/dna/storage_BR.dna') + loader.loadDNAFile(self.dnaStore, 'phase_8/dna/storage_BR_town.dna') + dnaFile = 'phase_6/dna/urban_track_town.dna' + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + dnaFile = 'phase_6/dna/urban_track_town_B.dna' + node = loader.loadDNAFile(self.dnaStore, dnaFile) + self.townGeom = self.geom.attachNewNode(node) + self.townGeom.findAllMatches('**/+CollisionNode').stash() + self.buildingGroups = {} + self.currBldgInd = {} + self.currBldgGroups = {} + bgGeom = self.geom.find('**/polySurface8') + if self.dummyNode: + bgGeom.reparentTo(self.dummyNode) + else: + bgGeom.reparentTo(localAvatar) + bgGeom.setScale(0.1) + ce = CompassEffect.make(NodePath(), CompassEffect.PRot) + bgGeom.node().setEffect(ce) + bgGeom.setDepthTest(0) + bgGeom.setDepthWrite(0) + bgGeom.setBin('background', 102) + bgGeom.setZ(-1) + self.bgGeom = bgGeom + l = self.geom.findAllMatches('**/+ModelNode') + for n in l: + n.node().setPreserveTransform(0) + self.geom.flattenLight() + maxNum = 0 + for side in ['inner', 'outer']: + self.buildingGroups[side] = [] + self.currBldgInd[side] = None + self.currBldgGroups[side] = None + i = 0 + while 1: + bldgGroup = self.townGeom.find('**/Buildings_' + side + '-' + str(i)) + if bldgGroup.isEmpty(): + break + l = bldgGroup.findAllMatches('**/+ModelNode') + for n in l: + n2 = n.getParent().attachNewNode(n.getName()) + n.getChildren().reparentTo(n2) + n.removeNode() + bldgGroup.flattenStrong() + if not bldgGroup.getNode(0).getBounds().isEmpty(): + self.buildingGroups[side].append(bldgGroup) + i += 1 + if i > maxNum: + maxNum = i + for side in ['innersidest', 'outersidest']: + self.buildingGroups[side] = [] + self.currBldgInd[side] = None + self.currBldgGroups[side] = None + for i in range(maxNum): + for barricade in ('innerbarricade', 'outerbarricade'): + bldgGroup = self.townGeom.find('**/Buildings_' + side + '-' + barricade + '_' + str(i)) + if bldgGroup.isEmpty(): + continue + l = bldgGroup.findAllMatches('**/+ModelNode') + for n in l: + n2 = n.getParent().attachNewNode(n.getName()) + n.getChildren().reparentTo(n2) + n.removeNode() + self.buildingGroups[side].append(bldgGroup) + treeNodes = self.townGeom.findAllMatches('**/prop_tree_*') + for tree in treeNodes: + tree.flattenStrong() + snowTreeNodes = self.townGeom.findAllMatches('**/prop_snow_tree_*') + for snowTree in snowTreeNodes: + snowTree.flattenStrong() + for side in ['inner', 'outer', 'innersidest', 'outersidest']: + for grp in self.buildingGroups[side]: + grp.stash() + self.showBuildings(0) + + def unloadUrbanTrack(self): + del self.buildingGroups + self.townGeom.removeNode() + + def loadFog(self): + self.hasFog = True + if self.isUrbanTrack: + base.camLens.setFar(650) + else: + base.camLens.setFar(650) + self.dummyNode = render.attachNewNode('dummyNode') + if base.wantFog: + self.fog = Fog('TrackFog') + self.fog.setColor(Vec4(0.6, 0.7, 0.8, 1.0)) + if self.isUrbanTrack: + self.fog.setLinearRange(200.0, 650.0) + else: + self.fog.setLinearRange(200.0, 800.0) + render.setFog(self.fog) + self.sky.setScale(1.725) + self.sky.reparentTo(self.dummyNode) + + def showBuildings(self, t, forceRecompute = False): + firstTimeCalled = 0 + if self.curve: + t = t / self.curve.getMaxT() + else: + firstTimeCalled = 1 + if self.reversed: + t = 1.0 - t + numGroupsShown = 5 + for side in ['inner', 'outer']: + numBldgGroups = len(self.buildingGroups[side]) + bldgInd = int(t * numBldgGroups) + bldgInd = bldgInd % numBldgGroups + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + oldBldgInd = int(self.oldT * numBldgGroups) + newBldgInd = int(t * numBldgGroups) + kartPoint = self.startPos + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + if kart: + kartPoint = self.localKart.getPos() + if not self.currBldgInd[side]: + self.currBldgInd[side] = 0 + curInd = self.currBldgInd[side] + myCurGroup = self.buildingGroups[side][curInd] + prevGrp = (curInd - 1) % numBldgGroups + myPrevGroup = self.buildingGroups[side][prevGrp] + nextGrp = (curInd + 1) % numBldgGroups + myNextGroup = self.buildingGroups[side][nextGrp] + curVector = myCurGroup.getNode(0).getBounds().getCenter() - kartPoint + curDistance = curVector.lengthSquared() + prevVector = myPrevGroup.getNode(0).getBounds().getCenter() - kartPoint + prevDistance = prevVector.lengthSquared() + nextVector = myNextGroup.getNode(0).getBounds().getCenter() - kartPoint + nextDistance = nextVector.lengthSquared() + if curDistance <= prevDistance and curDistance <= nextDistance: + bldgInd = self.currBldgInd[side] + elif prevDistance <= curDistance and prevDistance <= nextDistance: + bldgInd = prevGrp + elif nextDistance <= curDistance and nextDistance <= prevDistance: + bldgInd = nextGrp + else: + self.notify.warning('unhandled case!!!!') + bldgInd = self.currBldgInd[side] + if bldgInd != self.currBldgInd[side]: + currBldgGroups = self.currBldgGroups[side] + if currBldgGroups: + for i in currBldgGroups: + self.buildingGroups[side][i].stash() + + prevGrp2 = (bldgInd - 2) % numBldgGroups + prevGrp = (bldgInd - 1) % numBldgGroups + currGrp = bldgInd % numBldgGroups + nextGrp = (bldgInd + 1) % numBldgGroups + nextGrp2 = (bldgInd + 2) % numBldgGroups + self.currBldgGroups[side] = [prevGrp2, + prevGrp, + currGrp, + nextGrp, + nextGrp2] + for i in self.currBldgGroups[side]: + self.buildingGroups[side][i].unstash() + + self.currBldgInd[side] = bldgInd + + if self.currBldgGroups['inner'] != self.currBldgGroups['outer']: + pass + if t != self.oldT: + self.oldT = t + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + if self.reversed: + t = 1.0 - t + for side in ['innersidest', 'outersidest']: + segmentInd = int(t * self.barricadeSegments) + seglmentInd = segmentInd % self.barricadeSegments + if segmentInd != self.currBldgInd[side] or forceRecompute: + currBldgGroups = self.currBldgGroups[side] + if currBldgGroups: + for i in currBldgGroups: + self.buildingGroups[side][i].stash() + + self.currBldgGroups[side] = [] + if side == 'innersidest': + dict = self.innerBarricadeDict + elif side == 'outersidest': + dict = self.outerBarricadeDict + if dict.has_key(segmentInd): + self.currBldgGroups[side] = dict[segmentInd] + for i in self.currBldgGroups[side]: + self.buildingGroups[side][i].unstash() + + self.currBldgInd[side] = segmentInd + + return + + def setupGeom(self): + trackFilepath = RaceGlobals.TrackDict[self.trackId][0] + self.geom = loader.loadModel(trackFilepath) + for i in range(10): + base.loader.tick() + + self.geom.reparentTo(render) + if self.reversed: + lapStartPos = self.geom.find('**/lap_start_rev').getPos() + else: + lapStartPos = self.geom.find('**/lap_start').getPos() + self.startPos = lapStartPos + lapMidPos = self.geom.find('**/lap_middle').getPos() + for i in range(5): + base.loader.tick() + + self.startingPos = [] + posLocators = self.geom.findAllMatches('**/start_pos*') + for i in range(posLocators.getNumPaths()): + base.loader.tick() + self.startingPos.append([posLocators[i].getPos(), posLocators[i].getHpr()]) + + self.notify.debug('self.startingPos: %s' % self.startingPos) + self.wrongWay = False + self.laps = 0 + if self.isUrbanTrack: + self.loadUrbanTrack() + self.genArrows() + if self.reversed: + self.curve = self.geom.find('**/curve_reverse').node() + else: + self.curve = self.geom.find('**/curve_forward').node() + for i in range(4000): + self.curvePoints.append(Point3(0, 0, 0)) + self.curve.getPoint(i / 4000.0 * (self.curve.getMaxT() - 1e-11), self.curvePoints[-1]) + self.curveTs.append(i / 4000.0 * (self.curve.getMaxT() - 1e-11)) + + if self.trackId in (RaceGlobals.RT_Urban_2, RaceGlobals.RT_Urban_2_rev): + self.precomputeSideStreets() + for i in range(10): + base.loader.tick() + + self.startT = self.getNearestT(lapStartPos) + self.midT = self.getNearestT(lapMidPos) + self.gags = [] + gagList = RaceGlobals.TrackDict[self.trackId][4] + for i in range(len(gagList)): + self.notify.debug('generating gag: %s' % i) + self.gags.append(RaceGag(self, i, Vec3(*gagList[i]) + Vec3(0, 0, 3))) + + for i in range(5): + base.loader.tick() + + def precomputeSideStreets(self): + farDist = base.camLens.getFar() + 300 + farDistSquared = farDist * farDist + for i in range(self.barricadeSegments): + testPoint = Point3(0, 0, 0) + self.curve.getPoint(i / self.barricadeSegments * (self.curve.getMaxT() - 1e-11), testPoint) + for side in ('innersidest', 'outersidest'): + for bldgGroupIndex in range(len(self.buildingGroups[side])): + bldgGroup = self.buildingGroups[side][bldgGroupIndex] + if not bldgGroup.getNode(0).getBounds().isEmpty(): + bldgPoint = bldgGroup.getNode(0).getBounds().getCenter() + vector = testPoint - bldgPoint + if vector.lengthSquared() < farDistSquared: + if side == 'innersidest': + dict = self.innerBarricadeDict + elif side == 'outersidest': + dict = self.outerBarricadeDict + else: + self.notify.error('unhandled side') + if dict.has_key(i): + if bldgGroupIndex not in dict[i]: + dict[i].append(bldgGroupIndex) + else: + dict[i] = [bldgGroupIndex] + for childIndex in (0,): + if childIndex >= bldgGroup.getNumChildren(): + continue + childNodePath = bldgGroup.getChild(childIndex) + bldgPoint = childNodePath.node().getBounds().getCenter() + vector = testPoint - bldgPoint + if vector.lengthSquared() < farDistSquared: + if side == 'innersidest': + dict = self.innerBarricadeDict + elif side == 'outersidest': + dict = self.outerBarricadeDict + else: + self.notify.error('unhandled side') + if dict.has_key(i): + if bldgGroupIndex not in dict[i]: + dict[i].append(bldgGroupIndex) + else: + dict[i] = [bldgGroupIndex] + + for side in ('innersidest', 'outersidest'): + for bldgGroup in self.buildingGroups[side]: + bldgGroup.flattenStrong() + + if self.isUrbanTrack: + self.showBuildings(0, forceRecompute=True) + + def findSegmentStart(self): + kart = base.cr.doId2do.get(self.kartMap.get(localAvatar.doId, None), None) + minLength2 = 1000000 + minIndex = -1 + currPoint = Point3(0, 0, 0) + kartPoint = self.localKart.getPos() + for i in range(len(self.curvePoints)): + currPoint = self.curvePoints[i] + currLength2 = (kartPoint - currPoint).lengthSquared() + if currLength2 < minLength2: + minLength2 = currLength2 + minIndex = i + + currPoint = self.curvePoints[minIndex] + if minIndex + 1 == len(self.curvePoints): + nextPoint = self.curvePoints[0] + else: + nextPoint = self.curvePoints[minIndex + 1] + if minIndex - 1 < 0: + prevIndex = len(self.curvePoints) - 1 + else: + prevIndex = minIndex - 1 + forwardSegment = nextPoint - currPoint + if (kartPoint - currPoint).dot(forwardSegment) > 0: + return minIndex + else: + return prevIndex + return + + def getNearestT(self, pos): + minLength2 = 1000000 + minIndex = -1 + currPoint = Point3(0, 0, 0) + for i in range(len(self.curvePoints)): + currPoint = self.curvePoints[i] + currLength2 = (pos - currPoint).lengthSquared() + if currLength2 < minLength2: + minLength2 = currLength2 + minIndex = i + + currPoint = self.curvePoints[minIndex] + if minIndex + 1 == len(self.curvePoints): + nextPoint = self.curvePoints[0] + else: + nextPoint = self.curvePoints[minIndex + 1] + if minIndex - 1 < 0: + prevIndex = len(self.curvePoints) - 1 + else: + prevIndex = minIndex - 1 + forwardSegment = nextPoint - currPoint + if (pos - currPoint).dot(forwardSegment) > 0: + pole = minIndex + else: + pole = prevIndex + currPoint = self.curvePoints[pole] + nextPole = (pole + 1) % len(self.curvePoints) + nextPoint = self.curvePoints[nextPole] + segment = nextPoint - currPoint + segment.setZ(0) + segLength2 = segment.lengthSquared() + posVector = pos - currPoint + posVector.setZ(0) + project = segment * (segment.dot(posVector) / segLength2) + percent = project.lengthSquared() / segLength2 + if nextPole < pole: + t = self.curveTs[pole] * (1 - percent) + self.curve.getMaxT() * percent + else: + t = self.curveTs[pole] * (1 - percent) + self.curveTs[nextPole] * percent + return t + + def hasGag(self, slot, type, index): + if self.gags[slot].isActive(): + self.gags[slot].disableGag() + + def leaveRace(self): + self.fsm.request('leave') + + def racerLeft(self, avId): + if avId != localAvatar.doId: + self.gui.racerLeft(avId, unexpected=False) + + def skyTrack(self, task): + return SkyUtil.cloudSkyTrack(task) + + def startSky(self): + if self.hasFog: + SkyUtil.startCloudSky(self, parent=self.dummyNode, effects=CompassEffect.PRot) + else: + SkyUtil.startCloudSky(self, parent=render) + + def stopSky(self): + taskMgr.remove('skyTrack') + + def pickupGag(self, slot, index): + self.canShoot = False + standing = self.gui.racerDict[localAvatar.doId].place - 1 + self.currGag = RaceGlobals.GagFreq[standing][index] + cycleTime = 2 + self.gui.waitingOnGag(cycleTime) + taskMgr.doMethodLater(cycleTime, self.enableShoot, 'enableShoot') + self.sendUpdate('hasGag', [slot, self.currGag, index]) + + def shootGag(self): + if self.canShoot: + if self.currGag == 1: + self.bananaSound.play() + self.shootBanana() + elif self.currGag == 2: + self.d_requestThrow(0, 0, 0) + self.localKart.startTurbo() + elif self.currGag == 3: + self.d_requestThrow(0, 0, 0) + elif self.currGag == 4: + self.bananaSound.play() + self.shootPie() + self.currGag = 0 + self.gui.updateGag(0) + + def enableShoot(self, t): + self.canShoot = True + if self.gui: + self.gui.updateGag(self.currGag) + + def shootBanana(self): + pos = self.localKart.getPos(render) + banana = self.banana.copyTo(self.geom) + banana.setPos(pos) + self.thrownGags.append(banana) + self.d_requestThrow(pos[0], pos[1], pos[2]) + + def shootPie(self): + pos = self.localKart.getPos(render) + self.d_requestThrow(pos[0], pos[1], pos[2]) + + def genArrows(self): + base.arrows = [] + arrowId = 0 + for boost in RaceGlobals.TrackDict[self.trackId][5]: + self.genArrow(boost[0], boost[1], arrowId) + arrowId += 1 + + def genArrow(self, pos, hpr, id): + factory = CardMaker('factory') + factory.setFrame(-.5, 0.5, -.5, 0.5) + arrowNode = factory.generate() + arrowRoot = NodePath('root') + baseArrow = NodePath(arrowNode) + baseArrow.setTransparency(1) + baseArrow.setTexture(self.boostArrowTexture) + baseArrow.reparentTo(arrowRoot) + arrow2 = baseArrow.copyTo(baseArrow) + arrow2.setPos(0, 0, 1) + arrow3 = arrow2.copyTo(arrow2) + arrowRoot.setPos(*pos) + arrowRoot.setHpr(*hpr) + baseArrow.setHpr(0, -90, 0) + baseArrow.setScale(24) + arrowRoot.reparentTo(self.geom) + trigger = 'boostArrow' + str(id) + cs = CollisionTube(Point3(0.6, -6, 0), Point3(0.6, 54, 0), 4.8) + cs.setTangible(0) + triggerEvent = 'imIn-' + trigger + cn = CollisionNode(trigger) + cn.addSolid(cs) + cn.setIntoCollideMask(BitMask32(32768)) + cn.setFromCollideMask(BitMask32(32768)) + cnp = NodePath(cn) + cnp.reparentTo(arrowRoot) + self.accept(triggerEvent, self.hitBoostArrow) + arrowVec = arrow2.getPos(self.geom) - baseArrow.getPos(self.geom) + arrowVec.normalize() + idStr = str(id) + cnp.setTag('boostId', idStr) + self.boostDir[idStr] = arrowVec + base.arrows.append(arrowRoot) + + def hitBoostArrow(self, cevent): + into = cevent.getIntoNodePath() + idStr = into.getTag('boostId') + arrowVec = self.boostDir.get(idStr) + if arrowVec == None: + print 'Unknown boost arrow %s' % idStr + return + fvec = self.localKart.forward.getPos(self.geom) - self.localKart.getPos(self.geom) + fvec.normalize() + dotP = arrowVec.dot(fvec) + if dotP > 0.7: + self.localKart.startTurbo() + return + + def fadeOutMusic(self): + if self.musicTrack: + self.musicTrack.finish() + curVol = self.raceMusic.getVolume() + interval = LerpFunctionInterval(self.raceMusic.setVolume, fromData=curVol, toData=0, duration=3) + self.musicTrack = Sequence(interval) + self.musicTrack.start() + + def changeMusicTempo(self, newPR): + if self.musicTrack: + self.musicTrack.finish() + curPR = self.raceMusic.getPlayRate() + interval = LerpFunctionInterval(self.raceMusic.setPlayRate, fromData=curPR, toData=newPR, duration=3) + self.musicTrack = Sequence(interval) + self.musicTrack.start() + + def setRaceZone(self, zoneId, trackId): + hoodId = self.cr.playGame.hood.hoodId + base.loader.endBulkLoad('atRace') + self.kartCleanup() + self.doneBarrier('waitingForExit') + self.sendUpdate('racerLeft', [localAvatar.doId]) + out = {'loader': 'racetrack', + 'where': 'racetrack', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'trackId': trackId, + 'shardId': None, + 'reason': RaceGlobals.Exit_UserReq} + base.cr.playGame.hood.loader.fsm.request('quietZone', [out]) + return diff --git a/toontown/racing/DistributedRaceAI.py b/toontown/racing/DistributedRaceAI.py new file mode 100644 index 0000000..6ccb24e --- /dev/null +++ b/toontown/racing/DistributedRaceAI.py @@ -0,0 +1,477 @@ +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from direct.showbase.PythonUtil import nonRepeatingRandomList +import DistributedGagAI, DistributedProjectileAI +from direct.task import Task +import random, time, Racer, RaceGlobals +from direct.distributed.ClockDelta import * + +class DistributedRaceAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRaceAI') + + def __init__(self, air, trackId, zoneId, avIds, laps, raceType, racerFinishedFunc, raceDoneFunc, circuitLoop, circuitPoints, circuitTimes, qualTimes=[], circuitTimeList={}, circuitTotalBonusTickets={}): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.trackId = trackId + self.direction = self.trackId % 2 + self.zoneId = zoneId + self.racers = {} + self.avIds = [] + self.kickedAvIds = [] + self.circuitPoints = circuitPoints + self.circuitTimes = circuitTimes + self.finishPending = [] + self.flushPendingTask = None + self.kickSlowRacersTask = None + for avId in avIds: + if avId and avId in self.air.doId2do: + self.avIds.append(avId) + self.racers[avId] = Racer.Racer(self, air, avId, zoneId) + + self.toonCount = len(self.racers) + self.startingPlaces = nonRepeatingRandomList(self.toonCount, 4) + self.thrownGags = [] + self.ready = False + self.setGo = False + self.racerFinishedFunc = racerFinishedFunc + self.raceDoneFunc = raceDoneFunc + self.lapCount = laps + self.raceType = raceType + if raceType == RaceGlobals.Practice: + self.gagList = [] + else: + self.gagList = [ + 0] * len(RaceGlobals.TrackDict[trackId][4]) + self.circuitLoop = circuitLoop + self.qualTimes = qualTimes + self.circuitTimeList = circuitTimeList + self.qualTimes.append(RaceGlobals.TrackDict[trackId][1]) + self.circuitTotalBonusTickets = circuitTotalBonusTickets + return + + def generate(self): + DistributedObjectAI.DistributedObjectAI.generate(self) + self.notify.debug('generate %s, id=%s, ' % (self.doId, self.trackId)) + trackFilepath = RaceGlobals.TrackDict[self.trackId][0] + taskMgr.doMethodLater(0.5, self.enableEntryBarrier, 'enableWaitingBarrier') + + def enableEntryBarrier(self, task): + self.enterRaceBarrier = self.beginBarrier('waitingForJoin', self.avIds, 60, self.b_racersJoined) + self.notify.debug('Waiting for Joins!!!!') + self.sendUpdate('waitingForJoin', []) + + def removeObject(self, object): + if object: + self.notify.debug('deleting object: %s' % object.doId) + object.requestDelete() + + def requestDelete(self, lastRace=True): + self.notify.debug('requestDelete: %s' % self.doId) + self.ignoreBarrier('waitingForExit') + for i in self.thrownGags: + i.requestDelete() + + del self.thrownGags + if lastRace: + for i in self.racers: + racer = self.racers[i] + self.ignore(racer.exitEvent) + if racer.kart: + racer.kart.requestDelete() + racer.kart = None + if racer.avatar: + racer.avatar.kart = None + racer.avatar = None + + self.racers = {} + if self.flushPendingTask: + taskMgr.remove(self.flushPendingTask) + self.flushPendingTask = None + if self.kickSlowRacersTask: + taskMgr.remove(self.kickSlowRacersTask) + self.kickSlowRacersTask = None + DistributedObjectAI.DistributedObjectAI.requestDelete(self) + return + + def delete(self): + self.notify.debug('delete: %s' % self.doId) + DistributedObjectAI.DistributedObjectAI.delete(self) + del self.raceDoneFunc + del self.racerFinishedFunc + + def getTaskZoneId(self): + return self.zoneId + + def allToonsGone(self): + self.notify.debug('allToonsGone') + self.requestDelete() + + def getZoneId(self): + return self.zoneId + + def getTrackId(self): + return self.trackId + + def getRaceType(self): + return self.raceType + + def getCircuitLoop(self): + return self.circuitLoop + + def getAvatars(self): + avIds = [] + for i in self.racers: + avIds.append(i) + + return avIds + + def getStartingPlaces(self): + return self.startingPlaces + + def getLapCount(self): + return self.lapCount + + def requestKart(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.racers: + kart = self.racers[avId].kart + if kart: + kart.request('Controlled', avId) + + def b_racersJoined(self, avIds): + self.ignoreBarrier('waitingForJoin') + racersOut = [] + for i in self.avIds: + if i not in avIds: + racersOut.append(i) + + if len(avIds) == 0: + self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace) + for i in self.avIds: + self.d_kickRacer(i) + + return + for i in racersOut: + self.d_kickRacer(i) + + self.avIds = avIds + self.waitingForPrepBarrier = self.beginBarrier('waitingForPrep', self.avIds, 30, self.b_prepForRace) + avAndKarts = [] + for i in self.racers: + avAndKarts.append([self.racers[i].avId, self.racers[i].kart.doId]) + + self.sendUpdate('setEnteredRacers', [avAndKarts]) + + def b_prepForRace(self, avIds): + self.notify.debug('Prepping!!!') + self.ignoreBarrier('waitingForPrep') + racersOut = [] + for i in self.avIds: + if i not in avIds: + racersOut.append(i) + + if len(avIds) == 0: + self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace) + for i in racersOut: + self.d_kickRacer(i) + + if len(avIds) == 0: + return + self.avIds = avIds + for i in range(len(self.gagList)): + self.d_genGag(i) + + self.waitingForReadyBarrier = self.beginBarrier('waitingForReady', self.avIds, 20, self.b_startTutorial) + self.sendUpdate('prepForRace', []) + + def b_startTutorial(self, avIds): + self.ignoreBarrier('waitingForReady') + racersOut = [] + for i in self.avIds: + if i not in avIds: + racersOut.append(i) + + if len(avIds) == 0: + self.exitBarrier = self.beginBarrier('waitingForExit', self.avIds, 10, self.endRace) + for i in racersOut: + self.d_kickRacer(i) + + if len(avIds) == 0: + return + for avId in avIds: + av = self.air.doId2do.get(avId, None) + if not av: + self.notify.warning('b_racersJoined: Avatar not found with id %s' % avId) + elif not self.raceType == RaceGlobals.Practice: + if self.isCircuit() and not self.isFirstRace(): + continue + raceFee = RaceGlobals.getEntryFee(self.trackId, self.raceType) + avTickets = av.getTickets() + if avTickets < raceFee: + self.notify.warning('b_racersJoined: Avatar %s does not own enough tickets for the race!') + av.b_setTickets(0) + else: + av.b_setTickets(avTickets - raceFee) + + self.avIds = avIds + self.readRulesBarrier = self.beginBarrier('readRules', self.avIds, 10, self.b_startRace) + self.sendUpdate('startTutorial', []) + return + + def b_startRace(self, avIds): + self.ignoreBarrier('readRules') + if self.isDeleted(): + return + self.notify.debug('Going!!!!!!') + self.ignoreBarrier(self.waitingForReadyBarrier) + self.toonCount = len(self.avIds) + self.baseTime = globalClock.getFrameTime() + 0.5 + RaceGlobals.RaceCountdown + for i in self.racers: + self.racers[i].baseTime = self.baseTime + + self.sendUpdate('startRace', [globalClockDelta.localToNetworkTime(self.baseTime)]) + qualTime = RaceGlobals.getQualifyingTime(self.trackId) + timeout = qualTime + 60 + 3 + self.kickSlowRacersTask = taskMgr.doMethodLater(timeout, self.kickSlowRacers, 'kickSlowRacers') + + def kickSlowRacers(self, task): + self.kickSlowRacersTask = None + if self.isDeleted(): + return + for racer in self.racers.values(): + avId = racer.avId + av = simbase.air.doId2do.get(avId, None) + if av and not av.allowRaceTimeout: + continue + if not racer.finished and avId not in self.kickedAvIds: + self.notify.info('Racer %s timed out - kicking.' % racer.avId) + self.d_kickRacer(avId, RaceGlobals.Exit_Slow) + self.ignore(racer.exitEvent) + racer.exited = True + racer.finished = True + taskMgr.doMethodLater(10, self.removeObject, 'removeKart-%s' % racer.kart.doId, extraArgs=[racer.kart]) + taskMgr.remove('make %s invincible' % avId) + self.racers[avId].anvilTarget = True + + self.checkForEndOfRace() + return + + def d_kickRacer(self, avId, reason=RaceGlobals.Exit_Barrier): + if avId not in self.kickedAvIds: + self.kickedAvIds.append(avId) + if self.isCircuit() and not self.isFirstRace() and reason == RaceGlobals.Exit_Barrier: + reason = RaceGlobals.Exit_BarrierNoRefund + self.sendUpdate('goToSpeedway', [self.kickedAvIds, reason]) + + def d_genGag(self, slot): + index = random.randint(0, 5) + self.gagList[slot] = index + pos = slot + self.sendUpdate('genGag', [slot, pos, index]) + + def d_dropAnvil(self, ownerId): + possibleTargets = [] + for i in self.racers: + if not self.racers[i].anvilTarget: + possibleTargets.append(self.racers[i]) + + while len(possibleTargets) > 1: + if possibleTargets[0].lapT <= possibleTargets[1].lapT: + possibleTargets = possibleTargets[1:] + else: + possibleTargets = possibleTargets[1:] + possibleTargets[:1] + + if len(possibleTargets): + id = possibleTargets[0].avId + if id != ownerId: + possibleTargets[0].anvilTarget = True + taskMgr.doMethodLater(4, setattr, 'make %s invincible' % id, extraArgs=[self.racers[id], 'anvilTarget', False]) + self.sendUpdate('dropAnvilOn', [ownerId, id, globalClockDelta.getFrameNetworkTime()]) + + def d_makeBanana(self, avId, x, y, z): + gag = DistributedGagAI.DistributedGagAI(simbase.air, avId, self, 3, x, y, z, 0) + self.thrownGags.append(gag) + gag.generateWithRequired(self.zoneId) + + def d_launchPie(self, avId): + ownerRacer = simbase.air.doId2do.get(avId, None) + targetId = 0 + type = 0 + targetDist = 10000 + for iiId in self.racers: + targetRacer = simbase.air.doId2do.get(iiId, None) + if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart): + continue + if targetRacer.kart.getPos(ownerRacer.kart)[1] < 500 and targetRacer.kart.getPos(ownerRacer.kart)[1] >= 0 and abs(targetRacer.kart.getPos(ownerRacer.kart)[0]) < 50 and avId != iiId and targetDist > targetRacer.kart.getPos(ownerRacer.kart)[1]: + targetId = iiId + targetDist = targetRacer.kart.getPos(ownerRacer.kart)[1] + + if targetId == 0: + for iiId in self.racers: + targetRacer = simbase.air.doId2do.get(iiId, None) + if not (targetRacer and targetRacer.kart and ownerRacer and ownerRacer.kart): + continue + if targetRacer.kart.getPos(ownerRacer.kart)[1] > -80 and targetRacer.kart.getPos(ownerRacer.kart)[1] <= 0 and abs(targetRacer.kart.getPos(ownerRacer.kart)[0]) < 50 and avId != iiId: + targetId = iiId + + self.sendUpdate('shootPiejectile', [avId, targetId, type]) + return + + def d_makePie(self, avId, x, y, z): + gag = DistributedProjectileAI.DistributedProjectileAI(simbase.air, self, avId) + self.thrownGags.append(gag) + gag.generateWithRequired(self.zoneId) + + def endRace(self, avIds): + if hasattr(self, 'raceDoneFunc'): + self.raceDoneFunc(self, False) + + def racerLeft(self, avIdFromClient): + avId = self.air.getAvatarIdFromSender() + if self.racers.has_key(avId) and avId == avIdFromClient: + self.notify.debug('Removing %d from race %d' % (avId, self.doId)) + racer = self.racers[avId] + taskMgr.doMethodLater(10, self.removeObject, racer.kart.uniqueName('removeIt'), extraArgs=[racer.kart]) + if racer.avatar: + racer.avatar.kart = None + self.racers[avId].exited = True + taskMgr.remove('make %s invincible' % id) + self.racers[avId].anvilTarget = True + raceDone = True + for i in self.racers: + if not self.racers[i].exited: + raceDone = False + + if raceDone: + self.notify.debug('race over, sending callback to raceMgr') + self.raceDoneFunc(self) + if avId in self.finishPending: + self.finishPending.remove(avId) + return + + def hasGag(self, slot, type, index): + avId = self.air.getAvatarIdFromSender() + if avId in self.racers: + if self.racers[avId].hasGag: + return + if self.gagList[slot] == index: + self.gagList[slot] = None + taskMgr.doMethodLater(5, self.d_genGag, 'remakeGag-' + str(slot), extraArgs=[slot]) + self.racers[avId].hasGag = True + self.racers[avId].gagType = type + else: + return + return + + def requestThrow(self, x, y, z): + avId = self.air.getAvatarIdFromSender() + if avId in self.racers: + racer = self.racers[avId] + if racer.hasGag: + if racer.gagType == 1: + self.d_makeBanana(avId, x, y, z) + if racer.gagType == 2: + pass + if racer.gagType == 3: + self.d_dropAnvil(avId) + if racer.gagType == 4: + self.d_launchPie(avId) + racer.hasGag = False + racer.gagType = 0 + + def heresMyT(self, inputAvId, numLaps, t, timestamp): + avId = self.air.getAvatarIdFromSender() + if avId in self.racers and avId == inputAvId: + me = self.racers[avId] + me.setLapT(numLaps, t, timestamp) + if me.maxLap == self.lapCount and not me.finished: + me.finished = True + taskMgr.remove('make %s invincible' % id) + me.anvilTarget = True + someoneIsClose = False + for racer in self.racers.values(): + if not racer.exited and not racer.finished: + if me.lapT - racer.lapT < 0.15: + someoneIsClose = True + break + + index = 0 + for racer in self.finishPending: + if me.totalTime < racer.totalTime: + break + index += 1 + + self.finishPending.insert(index, me) + if self.flushPendingTask: + taskMgr.remove(self.flushPendingTask) + self.flushPendingTask = None + if someoneIsClose: + task = taskMgr.doMethodLater(3, self.flushPending, self.uniqueName('flushPending')) + self.flushPendingTask = task + else: + self.flushPending() + return + + def flushPending(self, task=None): + for racer in self.finishPending: + self.racerFinishedFunc(self, racer) + + self.finishPending = [] + self.flushPendingTask = None + return + + def d_setPlace(self, avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime): + self.sendUpdate('setPlace', [avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime]) + + def d_setCircuitPlace(self, avId, place, entryFee, winnings, bonus, trophies): + self.sendUpdate('setCircuitPlace', [avId, place, entryFee, winnings, bonus, trophies]) + + def d_endCircuitRace(self): + self.sendUpdate('endCircuitRace') + + def unexpectedExit(self, avId): + self.notify.debug('racer disconnected: %s' % avId) + racer = self.racers.get(avId, None) + if racer: + self.sendUpdate('racerDisconnected', [avId]) + self.ignore(racer.exitEvent) + racer.exited = True + taskMgr.doMethodLater(10, self.removeObject, 'removeKart-%s' % racer.kart.doId, extraArgs=[racer.kart]) + taskMgr.remove('make %s invincible' % id) + self.racers[avId].anvilTarget = True + self.checkForEndOfRace() + return + + def checkForEndOfRace(self): + if self.isCircuit() and self.everyoneDone(): + simbase.air.raceMgr.endCircuitRace(self) + raceOver = True + for i in self.racers: + if not self.racers[i].exited: + raceOver = False + + if raceOver: + self.raceDoneFunc(self) + + def sendToonsToNextCircuitRace(self, raceZone, trackId): + for avId in self.avIds: + self.notify.debug('Handling Circuit Race transisiton for avatar %s' % avId) + self.sendUpdateToAvatarId(avId, 'setRaceZone', [raceZone, trackId]) + + def isCircuit(self): + return self.raceType == RaceGlobals.Circuit + + def isLastRace(self): + return len(self.circuitLoop) == 0 + + def isFirstRace(self): + return len(self.circuitLoop) == 2 + + def everyoneDone(self): + done = True + for racer in self.racers.values(): + if not racer.exited and racer.avId not in self.playersFinished and racer.avId not in self.kickedAvIds: + done = False + break + + return done diff --git a/toontown/racing/DistributedRacePad.py b/toontown/racing/DistributedRacePad.py new file mode 100644 index 0000000..f90f055 --- /dev/null +++ b/toontown/racing/DistributedRacePad.py @@ -0,0 +1,277 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.task.Task import Task +from direct.distributed.ClockDelta import * +from direct.fsm.FSM import FSM +from direct.interval.IntervalGlobal import * +from toontown.racing.DistributedKartPad import DistributedKartPad +from toontown.racing import RaceGlobals +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.racing.KartShopGlobals import KartGlobals + +class DistributedRacePad(DistributedKartPad, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedRacePad') + defaultTransitions = {'Off': ['WaitEmpty'], + 'WaitEmpty': ['WaitCountdown', 'Off'], + 'WaitCountdown': ['WaitEmpty', + 'WaitBoarding', + 'Off', + 'AllAboard'], + 'WaitBoarding': ['AllAboard', 'WaitEmpty', 'Off'], + 'AllAboard': ['Off', 'WaitEmpty', 'WaitCountdown']} + id = 0 + + def __init__(self, cr): + self.cr = cr + DistributedKartPad.__init__(self, cr) + FSM.__init__(self, 'RacePad_%s_FSM' % self.id) + self.id = DistributedRacePad.id + DistributedRacePad.id += 1 + self.trackId = None + self.trackType = None + self.timeStamp = None + self.clockNodepath = None + self.timerTask = None + self.tunnelSign = None + self.trackNameNode = None + self.tunnelSignInterval = None + return + + def disable(self): + self.notify.debug('Disable') + self.ignore('enterPlayground') + self.request('Off') + if self.tunnelSignInterval: + self.tunnelSignInterval = None + DistributedKartPad.disable(self) + return + + def enableStartingBlocks(self): + self.notify.debug('Enabling starting blocks') + for block in self.startingBlocks: + block.setActive(0) + + def disableStartingBlocks(self): + for block in self.startingBlocks: + self.notify.debug('Disabling kart block: %s' % block.getDoId()) + block.setActive(1) + + def isPractice(self): + return self.trackType == RaceGlobals.Practice + + def setState(self, state, timestamp): + self.request(state, [timestamp]) + + def setRaceZone(self, zoneId): + for block in self.startingBlocks: + if block.avId == base.localAvatar.getDoId(): + hoodId = self.cr.playGame.hood.hoodId + self.cr.playGame.getPlace().doneStatus = {'loader': 'racetrack', + 'where': 'racetrack', + 'zoneId': zoneId, + 'trackId': self.trackId, + 'hoodId': hoodId} + messenger.send(base.cr.playGame.getPlace().doneEvent) + + def setTrackInfo(self, trackInfo): + if self.isDisabled(): + return + self.trackId, self.trackType = trackInfo + self.notify.debugStateCall(self) + self.setTunnelSignText() + self.ignore('enterPlayground') + self.acceptOnce('enterPlayground', self.setTunnelSignText) + + def enterOff(self): + self.notify.debug('enterOff: Entering Off State for RacePad %s' % self.id) + if self.tunnelSignInterval: + self.tunnelSignInterval.finish() + self.cleanupTunnelText() + + def exitOff(self): + self.notify.debug('exitOff: Exiting Off state for RacePad %s' % self.id) + + def enterWaitEmpty(self, args): + self.notify.debug('enterWaitEmpty: Entering WaitEmpty State for RacePad %s' % self.id) + if self.tunnelSignInterval: + self.tunnelSignInterval.finish() + + def exitWaitEmpty(self): + self.notify.debug('exitWaitEmpty: Exiting WaitEmpty State for RacePad %s' % self.id) + + def enterWaitCountdown(self, args): + self.notify.debug('enterWaitCountdown: Entering WaitCountdown State for RacePad %s' % self.id) + self.timeStamp = args[0] + self.startCountdown() + + def exitWaitCountdown(self): + self.notify.debug('exitWaitCountdown: Exiting WaitCountdown State for RacePad %s' % self.id) + self.stopCountdown() + + def enterWaitBoarding(self, args): + self.notify.debug('enterWaitBoarding: Entering WaitBoarding State for RacePad %s' % self.id) + self.timeStamp = args[0] + for block in self.startingBlocks: + block.hideGui() + + def exitWaitBoarding(self): + self.notify.debug('exitWaitBoarding: Exiting WaitBording State for RacePad %s' % self.id) + + def enterAllAboard(self, args): + self.notify.debug('enterAllAboard: Entering AllAboard State for RacePad %s' % self.id) + for block in self.startingBlocks: + block.request('Off') + if block.av and block.kartNode: + self.notify.debug('enterAllAboard: Avatar %s is in the race.' % block.av.doId) + block.doExitToRaceTrack() + + def exitAllAboard(self): + self.notify.debug('enterAllAboard: Exiting AllAboard State for RacePad %s' % self.id) + + def getTimestamp(self, avId = None): + error = 'DistributedRacePad::getTimeStamp - timestamp not yet set!' + return self.timeStamp + + def stopCountdown(self): + if self.timerTask: + taskMgr.remove(self.timerTask) + self.clockNodepath.removeNode() + self.clockNodepath = None + self.clockNode = None + self.timerTask = None + return + + def updateTimerTask(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 startCountdown(self): + if not self.timerTask and self.startingBlocks: + self.makeClockGui() + duration = KartGlobals.COUNTDOWN_TIME - globalClockDelta.localElapsedTime(self.getTimestamp()) + countdownTask = Task(self.updateTimerTask) + countdownTask.duration = duration + self.timerTask = taskMgr.add(countdownTask, self.uniqueName('racePadTimerTask')) + + def addStartingBlock(self, block): + DistributedKartPad.addStartingBlock(self, block) + if self.state == 'WaitCountdown': + self.startCountdown() + + def makeClockGui(self): + self.notify.debugStateCall(self) + if self.clockNodepath is not None: + return + self.clockNode, self.clockNodepath = self.getSignTextNodes('racePadClock') + self.clockNodepath.setPos(0, 0.125, -3.0) + self.clockNodepath.setScale(2.5) + self.clockNodepath.flattenLight() + return + + def getTunnelSign(self): + cPadId = RaceGlobals.RaceInfo2RacePadId(self.trackId, self.trackType) + genreId = RaceGlobals.getTrackGenre(self.trackId) + tunnelName = RaceGlobals.getTunnelSignName(genreId, cPadId) + self.tunnelSign = self.cr.playGame.hood.loader.geom.find('**/' + tunnelName) + + def getSignTextNodes(self, nodeName, font = ToontownGlobals.getSignFont()): + signTextNode = TextNode(nodeName) + signTextNode.setFont(font) + signTextNode.setAlign(TextNode.ACenter) + signTextNode.setTextColor(0.5, 0.5, 0.5, 1) + signTextNodepath = self.tunnelSign.attachNewNode(signTextNode) + signTextNodepath.setPos(0, 0.25, 0) + signTextNodepath.setH(165.0) + signTextNodepath.setDepthWrite(0) + return (signTextNode, signTextNodepath) + + def setTunnelSignText(self): + self.notify.debugStateCall(self) + self.getTunnelSign() + if not self.tunnelSign or self.tunnelSign.isEmpty(): + return + if not self.trackNameNode: + self.makeTextNodes() + if self.tunnelSignInterval: + self.tunnelSignInterval.finish() + self.tunnelSignInterval = Sequence(Func(self.hideTunnelSignText), Wait(0.2), Func(self.showTunnelSignText), Wait(0.2), Func(self.hideTunnelSignText), Wait(0.2), Func(self.showTunnelSignText), Wait(0.2), Func(self.hideTunnelSignText), Wait(0.2), Func(self.updateTunnelSignText), Func(self.showTunnelSignText)) + self.tunnelSignInterval.start() + + def hideTunnelSignText(self): + if self.tunnelSign: + textNodePaths = self.tunnelSign.findAllMatches('**/+TextNode') + numTextNodePaths = textNodePaths.getNumPaths() + for i in range(numTextNodePaths): + textNodePath = textNodePaths.getPath(i) + textNodePath.hide() + + def showTunnelSignText(self): + if self.tunnelSign: + textNodePaths = self.tunnelSign.findAllMatches('**/+TextNode') + numTextNodePaths = textNodePaths.getNumPaths() + for i in range(numTextNodePaths): + textNodePath = textNodePaths.getPath(i) + textNodePath.show() + + def updateTunnelSignText(self): + self.notify.debugStateCall(self) + trackNameString = TTLocalizer.KartRace_TrackNames[self.trackId] + if not self.trackNameNode: + self.notify.warning('invalid trackNameNode, just returning') + return + self.trackNameNode.setText(trackNameString) + trackTypeString = TTLocalizer.KartRace_RaceNames[self.trackType] + self.trackTypeNode.setText(trackTypeString) + deposit = 0 + if self.trackType: + deposit = RaceGlobals.getEntryFee(self.trackId, self.trackType) + depositString = TTLocalizer.KartRace_DepositPhrase + str(deposit) + self.depositNode.setText(depositString) + time = RaceGlobals.TrackDict[self.trackId][1] + secs, hundredths = divmod(time, 1) + min, sec = divmod(secs, 60) + timeText = '%02d:%02d:%02d' % (min, sec, hundredths * 100) + qualifyString = TTLocalizer.KartRace_QualifyPhrase + timeText + self.qualifyNode.setText(qualifyString) + + def makeTextNodes(self): + self.notify.debugStateCall(self) + self.trackNameNode, trackNameNodePath = self.getSignTextNodes('trackNameNode') + trackNameNodePath.setZ(0.7) + trackNameNodePath.setScale(0.875) + trackNameNodePath.flattenLight() + self.trackTypeNode, trackTypeNodePath = self.getSignTextNodes('trackTypeNode') + trackTypeNodePath.setZ(-0.35) + trackTypeNodePath.setScale(0.875) + trackTypeNodePath.flattenLight() + self.depositNode, depositNodePath = self.getSignTextNodes('depositNode', ToontownGlobals.getToonFont()) + self.depositNode.setTextColor(0, 0, 0, 1) + depositNodePath.setPos(4.0, -1.0, -2.0) + depositNodePath.setScale(0.75) + depositNodePath.flattenLight() + self.qualifyNode, qualifyNodePath = self.getSignTextNodes('qualifyNode', ToontownGlobals.getToonFont()) + self.qualifyNode.setTextColor(0, 0, 0, 1) + qualifyNodePath.setPos(-4.0, 1.2, -2.0) + qualifyNodePath.setScale(0.75) + qualifyNodePath.flattenLight() + + def cleanupTunnelText(self): + self.notify.debugStateCall(self) + if self.tunnelSign: + textNodePaths = self.tunnelSign.findAllMatches('**/+TextNode') + numTextNodePaths = textNodePaths.getNumPaths() + for i in range(numTextNodePaths): + textNodePath = textNodePaths.getPath(i) + textNodePath.removeNode() + textNodePath = None + + self.tunnelSign = None + self.trackNameNode = None + return diff --git a/toontown/racing/DistributedStartingBlock.py b/toontown/racing/DistributedStartingBlock.py new file mode 100644 index 0000000..2b8cc98 --- /dev/null +++ b/toontown/racing/DistributedStartingBlock.py @@ -0,0 +1,694 @@ +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from toontown.building.ElevatorConstants import * +from toontown.building.ElevatorUtils import * +from toontown.building import DistributedElevatorExt +from toontown.building import DistributedElevator +from toontown.toonbase import ToontownGlobals +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.gui import DirectGui +from toontown.hood import ZoneUtil +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from direct.distributed import DistributedObject +from direct.distributed import DistributedSmoothNode +from direct.actor import Actor +from direct.fsm.FSM import FSM +from direct.showbase import PythonUtil +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.racing.Kart import Kart +from toontown.racing.KartShopGlobals import KartGlobals +from toontown.racing import RaceGlobals +from toontown.toontowngui.TTDialog import TTGlobalDialog +from toontown.toontowngui.TeaserPanel import TeaserPanel +if (__debug__): + import pdb + +class DistributedStartingBlock(DistributedObject.DistributedObject, FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStartingBlock') + sphereRadius = 1.5 + id = 0 + cameraPos = Point3(0, -23, 10) + cameraHpr = Point3(0, -10, 0) + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_KartAppear = SFX_BaseDir + 'KART_Appear.mp3' + defaultTransitions = {'Off': ['EnterMovie'], + 'EnterMovie': ['Off', 'Waiting', 'ExitMovie'], + 'Waiting': ['ExitMovie', 'Off'], + 'ExitMovie': ['Off', 'ExitMovie']} + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + FSM.__init__(self, 'staringBlock_%s_FSM' % DistributedStartingBlock.id) + self.avId = 0 + self.av = None + self.lastAvId = 0 + self.avatar = None + self.kartPad = None + self.collNode = None + self.movieNode = None + self.movieTrack = None + self.collSphere = None + self.collNodePath = None + self.localToonKarting = 0 + self.kartNode = None + self.kart = None + self.holeActor = None + self.exitRequested = False + if (__debug__): + self.testLOD = False + self.id = DistributedStartingBlock.id + DistributedStartingBlock.id += 1 + return + + def disable(self): + FSM.cleanup(self) + self.ignore(self.uniqueName('enterStartingBlockSphere')) + self.ignore('stoppedAsleep') + self.setOccupied(0) + self.avId = 0 + self.nodePath.detachNode() + self.kartPad = None + if self.holeActor: + self.holeActor.cleanup() + self.holeActor = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + if hasattr(self, 'dialog'): + if not self.dialog.removed(): + self.dialog.ignoreAll() + if not self.dialog.isEmpty(): + self.dialog.cleanup() + del self.dialog + self.finishMovie() + if hasattr(self, 'cancelButton'): + self.cancelButton.destroy() + del self.cancelButton + del self.kartPad + if self.nodePath: + self.nodePath.removeNode() + del self.nodePath + DistributedObject.DistributedObject.delete(self) + + def generateInit(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('StartingBlock')) + self.collSphere = CollisionSphere(0, 0, 0, self.sphereRadius) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('StartingBlockSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + + def announceGenerate(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.announceGenerate(self) + self.nodePath.reparentTo(render) + self.accept(self.uniqueName('enterStartingBlockSphere'), self.__handleEnterSphere) + if (__debug__): + if self.testLOD: + self.__generateKartAppearTrack() + + def setPadDoId(self, padDoId): + self.notify.debugStateCall(self) + self.kartPad = base.cr.doId2do.get(padDoId) + self.kartPad.addStartingBlock(self) + + def setPosHpr(self, x, y, z, h, p, r): + self.notify.debugStateCall(self) + self.nodePath.setPosHpr(x, y, z, h + 180, 0, 0) + + def setPadLocationId(self, padLocationId): + self.notify.debugStateCall(self) + self.movieNode = self.nodePath.attachNewNode(self.uniqueName('MovieNode')) + self.exitMovieNode = self.movieNode + if padLocationId % 2: + self.movieNode.setPosHpr(3.0, 0, 0, 90.0, 0, 0) + else: + self.movieNode.setPosHpr(-3.0, 0, 0, -90.0, 0, 0) + + def setActive(self, isTangible): + self.collSphere.setTangible(isTangible) + + def __handleEnterSphere(self, collEntry): + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount() <= self.lastFrame + 1: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + if base.localAvatar.hp > 0: + + def handleEnterRequest(self = self): + self.ignore('stoppedAsleep') + if hasattr(self.dialog, 'doneStatus') and self.dialog.doneStatus == 'ok': + self.d_requestEnter(base.cr.isPaid()) + elif self.cr and not self.isDisabled(): + self.cr.playGame.getPlace().setState('walk') + else: + self.notify.warning('Warning! Object has already been disabled.') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + + self.cr.playGame.getPlace().fsm.request('stopped') + self.accept('stoppedAsleep', handleEnterRequest) + doneEvent = 'enterRequest|dialog' + if self.kartPad.isPractice(): + msg = TTLocalizer.StartingBlock_EnterPractice + else: + raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType] + numTickets = RaceGlobals.getEntryFee(self.kartPad.trackId, self.kartPad.trackType) + msg = TTLocalizer.StartingBlock_EnterNonPractice % (raceName, numTickets) + self.dialog = TTGlobalDialog(msg, doneEvent, 4) + self.dialog.accept(doneEvent, handleEnterRequest) + + def d_movieFinished(self): + self.notify.debugStateCall(self) + self.sendUpdate('movieFinished', []) + + def d_requestEnter(self, paid): + self.notify.debugStateCall(self) + self.sendUpdate('requestEnter', [paid]) + + def d_requestExit(self): + self.notify.debugStateCall(self) + self.exitRequested = True + self.hideGui() + self.sendUpdate('requestExit', []) + + def rejectEnter(self, errCode): + self.notify.debugStateCall(self) + + def handleTicketError(self = self): + self.ignore('stoppedAsleep') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + self.cr.playGame.getPlace().setState('walk') + + doneEvent = 'errorCode|dialog' + if errCode == KartGlobals.ERROR_CODE.eTickets: + msg = TTLocalizer.StartingBlock_NotEnoughTickets + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eBoardOver: + msg = TTLocalizer.StartingBlock_NoBoard + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eNoKart: + msg = TTLocalizer.StartingBlock_NoKart + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eOccupied: + msg = TTLocalizer.StartingBlock_Occupied + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eTrackClosed: + msg = TTLocalizer.StartingBlock_TrackClosed + self.dialog = TTGlobalDialog(msg, doneEvent, 2) + self.dialog.accept(doneEvent, handleTicketError) + self.accept('stoppedAsleep', handleTicketError) + elif errCode == KartGlobals.ERROR_CODE.eUnpaid: + self.dialog = TeaserPanel(pageName='karting', doneFunc=handleTicketError) + else: + self.cr.playGame.getPlace().setState('walk') + + def finishMovie(self): + if self.movieTrack: + self.movieTrack.finish() + self.movieTrack = None + return + + def setOccupied(self, avId): + self.notify.debug('%d setOccupied: %d' % (self.doId, avId)) + if self.av != None: + self.finishMovie() + if not self.av.isEmpty() and not self.av.isDisabled(): + self.av.loop('neutral') + self.av.setParent(ToontownGlobals.SPRender) + self.av.startSmooth() + self.finishMovie() + if self.kart: + self.kart.delete() + self.kart = None + if self.kartNode: + self.kartNode.removeNode() + self.kartNode = None + self.placedAvatar = 0 + self.ignore(self.av.uniqueName('disable')) + self.av = None + wasLocalToon = self.localToonKarting + self.lastAvId = self.avId + self.lastFrame = globalClock.getFrameCount() + self.avId = avId + self.localToonKarting = 0 + if self.avId == 0: + self.collSphere.setTangible(0) + self.request('Off') + else: + self.collSphere.setTangible(1) + av = self.cr.doId2do.get(self.avId) + self.placedAvatar = 0 + if self.avId == base.localAvatar.doId: + self.localToonKarting = 1 + if av != None: + self.av = av + self.av.stopSmooth() + self.placedAvatar = 0 + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.kartNode = render.attachNewNode(self.av.uniqueName('KartNode')) + self.kartNode.setPosHpr(self.nodePath.getPos(render), self.nodePath.getHpr(render)) + self.kart = Kart() + self.kart.baseScale = 1.6 + self.kart.setDNA(self.av.getKartDNA()) + self.kart.generateKart() + self.kart.resetGeomPos() + self.av.wrtReparentTo(self.nodePath) + self.av.setAnimState('neutral', 1.0) + if not self.localToonKarting: + av.stopSmooth() + self.__placeAvatar() + self.avParent = av.getParent() + else: + self.notify.warning('Unknown avatar %d in kart block %d ' % (self.avId, self.doId)) + self.avId = 0 + if wasLocalToon and not self.localToonKarting: + place = base.cr.playGame.getPlace() + if place: + if self.exitRequested: + place.setState('walk') + else: + + def handleDialogOK(self = self): + self.ignore('stoppedAsleep') + place.setState('walk') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + + doneEvent = 'kickedOutDialog' + msg = TTLocalizer.StartingBlock_KickSoloRacer + self.dialog = TTGlobalDialog(msg, doneEvent, style=1) + self.dialog.accept(doneEvent, handleDialogOK) + self.accept('stoppedAsleep', handleDialogOK) + return + + def __avatarGone(self): + self.notify.debugStateCall(self) + self.setOccupied(0) + + def __placeAvatar(self): + self.notify.debugStateCall(self) + if not self.placedAvatar: + self.placedAvatar = 1 + self.av.setPosHpr(0, 0, 0, 0, 0, 0) + + def setMovie(self, mode): + self.notify.debugStateCall(self) + if self.avId == 0: + return + self.finishMovie() + if mode == 0: + pass + elif mode == KartGlobals.ENTER_MOVIE: + self.request('EnterMovie') + elif mode == KartGlobals.EXIT_MOVIE: + self.request('ExitMovie') + + def makeGui(self): + self.notify.debugStateCall(self) + if hasattr(self, 'cancelButton'): + return + fishGui = loader.loadModel('phase_4/models/gui/fishingGui') + self.cancelButton = DirectGui.DirectButton(relief=None, scale=0.67, pos=(1.16, 0, -0.9), text=('', TTLocalizer.FishingExit, TTLocalizer.FishingExit), text_align=TextNode.ACenter, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0.0, -0.12), textMayChange=0, text_scale=0.1, image=(fishGui.find('**/exit_buttonUp'), fishGui.find('**/exit_buttonDown'), fishGui.find('**/exit_buttonRollover')), text_font=ToontownGlobals.getInterfaceFont(), command=self.d_requestExit) + self.cancelButton.hide() + return + + def showGui(self): + self.notify.debugStateCall(self) + if hasattr(self.kartPad, 'state'): + if not self.kartPad.state == 'WaitCountdown': + return + self.cancelButton.show() + + def hideGui(self): + self.notify.debugStateCall(self) + if not hasattr(self, 'cancelButton'): + return + self.cancelButton.hide() + + def generateToonMoveTrack(self): + hpr = self.movieNode.getHpr(render) + heading = PythonUtil.fitDestAngle2Src(self.av.getH(render), hpr[0]) + hpr.setX(heading) + self.av.setAnimState('run', 1.0) + toonTrack = Sequence(Wait(0.5), Parallel(LerpPosInterval(self.av, 1.0, Point3(self.movieNode.getX(self.avParent), self.movieNode.getY(self.avParent), 0)), LerpHprInterval(self.av, 1.0, hpr=hpr, other=render)), Func(self.av.loop, 'neutral')) + return toonTrack + + def generateKartAppearTrack(self): + if not self.av: + if not self.kartNode: + self.kartNode = render.attachNewNode(str(self) + 'kartNode') + self.kartNode.setPosHpr(self.nodePath.getPos(render), self.nodePath.getHpr(render)) + self.kart.setScale(0.85) + self.kart.reparentTo(self.kartNode) + return Parallel() + self.kart.setScale(0.1) + kartTrack = Parallel( + Sequence( + ActorInterval(self.av, 'feedPet'), + Func(self.av.loop, 'neutral')), + Sequence( + Func(self.kart.setActiveShadow, False), + Func(self.kart.reparentTo, self.av.rightHand), + Wait(2.1), + Func(self.kart.wrtReparentTo, render), + Func(self.kart.setShear, 0, 0, 0), + Parallel( + LerpHprInterval(self.kart, hpr=self.kartNode.getHpr(render), duration=1.2), + ProjectileInterval(self.kart, endPos=self.kartNode.getPos(render), duration=1.2, gravityMult=0.45)), + Wait(0.2), + Func(self.kart.setActiveShadow, True), + Sequence( + LerpScaleInterval(self.kart, scale=Point3(1.1, 1.1, 0.1), duration=0.2), + LerpScaleInterval(self.kart, scale=Point3(0.9, 0.9, 0.1), duration=0.1), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 0.1), duration=0.1), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 1.1), duration=0.2), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 0.9), duration=0.1), + LerpScaleInterval(self.kart, scale=Point3(1.0, 1.0, 1.0), duration=0.1), + Func(self.kart.wrtReparentTo, self.kartNode)))) + return kartTrack + + def generateToonJumpTrack(self): + base.sb = self + + def getToonJumpTrack(av, kart): + + def getJumpDest(av = av, node = kart.toonNode[0]): + dest = node.getPos(av.getParent()) + return dest + + def getJumpHpr(av = av, node = kart.toonNode[0]): + hpr = node.getHpr(av.getParent()) + 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(self.av, self.kart) + toonSitTrack = getToonSitTrack(self.av) + jumpTrack = Sequence( + Parallel( + toonJumpTrack, + Sequence( + Wait(1), + toonSitTrack)), + Func(self.av.setPosHpr, 0, 0.45, -.25, 0, 0, 0), + Func(self.av.reparentTo, self.kart.toonSeat)) + return jumpTrack + + def generateToonReverseJumpTrack(self): + + def getToonJumpTrack(av, destNode): + + def getJumpDest(av = av, node = destNode): + dest = node.getPos(av.getParent()) + return dest + + def getJumpHpr(av = av, node = destNode): + hpr = node.getHpr(av.getParent()) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.1), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=0.9), ProjectileInterval(av, endPos=getJumpDest, duration=0.9)))) + return toonJumpTrack + + toonJumpTrack = getToonJumpTrack(self.av, self.exitMovieNode) + jumpTrack = Sequence(toonJumpTrack, Func(self.av.loop, 'neutral'), Func(self.av.reparentTo, render), Func(self.av.setPosHpr, self.exitMovieNode, 0, 0, 0, 0, 0, 0)) + return jumpTrack + + def generateCameraMoveTrack(self): + self.cPos = camera.getPos(self.av) + self.cHpr = camera.getHpr(self.av) + camera.wrtReparentTo(self.nodePath) + cameraTrack = LerpPosHprInterval(camera, 1.5, self.cameraPos, self.cameraHpr) + return cameraTrack + + def generateCameraReturnMoveTrack(self): + cameraTrack = Sequence(Func(camera.wrtReparentTo, self.av), LerpPosHprInterval(camera, 1.5, self.cPos, self.cHpr)) + return cameraTrack + + def generateKartDisappearTrack(self): + + def getHoleTrack(hole, holeParent): + holeTrack = Sequence( + Wait(0.2), + Func(hole.setBin, 'shadow', 0), + Func(hole.setDepthTest, 0), + Func(hole.setDepthWrite, 0), + Func(hole.reparentTo, holeParent), + Func(hole.setPos, holeParent, Point3(0, 0.0, -.6)), + ActorInterval(hole, 'hole', startTime=3.4, endTime=3.1), + Wait(0.4), + ActorInterval(hole, 'hole', startTime=3.1, endTime=3.4)) + return holeTrack + + def getKartShrinkTrack(kart): + pos = kart.getPos() + pos.addZ(-1.0) + kartTrack = Sequence(LerpScaleInterval(kart, scale=Point3(1.0, 1.0, 0.9), duration=0.1), LerpScaleInterval(kart, scale=Point3(1.0, 1.0, 1.1), duration=0.1), LerpScaleInterval(kart, scale=Point3(1.0, 1.0, 0.1), duration=0.2), LerpScaleInterval(kart, scale=Point3(0.9, 0.9, 0.1), duration=0.1), LerpScaleInterval(kart, scale=Point3(1.1, 1.1, 0.1), duration=0.1), LerpScaleInterval(kart, scale=Point3(0.1, 0.1, 0.1), duration=0.2), Wait(0.2), LerpPosInterval(kart, pos=pos, duration=0.2), Func(kart.hide)) + return kartTrack + + if not self.holeActor: + self.holeActor = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + holeTrack = getHoleTrack(self.holeActor, self.kartNode) + shrinkTrack = getKartShrinkTrack(self.kart) + kartTrack = Parallel(shrinkTrack, holeTrack) + return kartTrack + + def enterOff(self): + self.notify.debug('%d enterOff: Entering the Off State.' % self.doId) + self.hideGui() + + def exitOff(self): + self.notify.debug('%d exitOff: Exiting the Off State.' % self.doId) + + def enterEnterMovie(self): + self.notify.debug('%d enterEnterMovie: Entering the Enter Movie State.' % self.doId) + if base.config.GetBool('want-qa-regression', 0): + raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType] + self.notify.info('QA-REGRESSION: KARTING: %s' % raceName) + toonTrack = self.generateToonMoveTrack() + kartTrack = self.generateKartAppearTrack() + jumpTrack = self.generateToonJumpTrack() + name = self.av.uniqueName('EnterRaceTrack') + if self.av is not None and self.localToonKarting: + kartAppearSfx = base.loadSfx(self.SFX_KartAppear) + cameraTrack = self.generateCameraMoveTrack() + engineStartTrack = self.kart.generateEngineStartTrack() + self.finishMovie() + self.movieTrack = Sequence(Parallel(cameraTrack, toonTrack), Parallel(SoundInterval(kartAppearSfx), Sequence(kartTrack, jumpTrack, engineStartTrack, Func(self.makeGui), Func(self.showGui), Func(self.request, 'Waiting'), Func(self.d_movieFinished))), name=name, autoFinish=1) + self.exitRequested = False + else: + self.finishMovie() + self.movieTrack = Sequence(toonTrack, kartTrack, jumpTrack, name=name, autoFinish=1) + self.movieTrack.start() + return + + def exitEnterMovie(self): + self.notify.debug('%d exitEnterMovie: Exiting the Enter Movie State.' % self.doId) + + def enterWaiting(self): + self.notify.debug('%d enterWaiting: Entering the Waiting State.' % self.doId) + + def exitWaiting(self): + self.notify.debug('%d exitWaiting: Exiting the Waiting State.' % self.doId) + + def enterExitMovie(self): + self.notify.debug('%d enterExitMovie: Entering the Exit Movie State.' % self.doId) + self.hideGui() + jumpTrack = self.generateToonReverseJumpTrack() + kartTrack = self.generateKartDisappearTrack() + self.finishMovie() + self.movieTrack = Sequence(Func(self.kart.kartLoopSfx.stop), jumpTrack, kartTrack, name=self.av.uniqueName('ExitRaceTrack'), autoFinish=1) + if self.av is not None and self.localToonKarting: + cameraTrack = self.generateCameraReturnMoveTrack() + self.movieTrack.append(cameraTrack) + self.movieTrack.append(Func(self.d_movieFinished)) + self.movieTrack.start() + return + + def exitExitMovie(self): + self.notify.debug('%d exitExitMovie: Exiting the Exit Movie State.' % self.doId) + + def doExitToRaceTrack(self): + self.hideGui() + self.finishMovie() + oldBlockPos = self.kartNode.getPos(render) + self.kartNode.setPos(self.kartNode, 0, 40, 0) + newBlockPos = self.kartNode.getPos(render) + oldBlockScale = self.kartNode.getScale() + self.kart.LODnode.setSwitch(0, 60, 0) + self.kartNode.setPos(render, oldBlockPos) + blockLerpIval = LerpPosInterval(self.kartNode, pos=newBlockPos, duration=2.0) + scaleLerpIval = LerpScaleInterval(self.kartNode, scale=oldBlockScale * 0.2, duration=2.0) + engineStopTrack = self.kart.generateEngineStopTrack(2) + self.finishMovie() + self.movieTrack = Parallel() + if self.av == base.localAvatar: + self.movieTrack.insert(0, Func(base.transitions.irisOut, 1.5, 0)) + (self.movieTrack.append(engineStopTrack),) + taskMgr.doMethodLater(1.6, self.bulkLoad, 'loadIt', extraArgs=[]) + self.movieTrack.append(Sequence(Parallel(blockLerpIval, scaleLerpIval), Func(self.kartNode.hide), Func(self.kartNode.setPos, render, oldBlockPos), Func(self.kartNode.setScale, oldBlockScale))) + self.movieTrack.start() + + def bulkLoad(self): + base.loader.beginBulkLoad('atRace', TTLocalizer.StartingBlock_Loading, 60, 1, TTLocalizer.TIP_KARTING) + + +class DistributedViewingBlock(DistributedStartingBlock): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewingBlock') + sphereRadius = 6 + + def __init__(self, cr): + DistributedStartingBlock.__init__(self, cr) + self.timer = None + return + + def delete(self): + if self.timer is not None: + self.timer.destroy() + del self.timer + DistributedStartingBlock.delete(self) + return + + def generateInit(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('StartingBlock')) + self.collSphere = CollisionSphere(-1, 6.75, -1, self.sphereRadius) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('StartingBlockSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + + def announceGenerate(self): + self.notify.debugStateCall(self) + DistributedObject.DistributedObject.announceGenerate(self) + self.nodePath.reparentTo(render) + self.accept(self.uniqueName('enterStartingBlockSphere'), self.__handleEnterSphere) + if (__debug__): + if self.testLOD: + self.__generateKartAppearTrack() + + def setPadLocationId(self, padLocationId): + self.notify.debugStateCall(self) + self.movieNode = self.nodePath.attachNewNode(self.uniqueName('MovieNode')) + self.exitMovieNode = self.nodePath.attachNewNode(self.uniqueName('ExitMovieNode')) + if padLocationId % 2: + self.movieNode.setPosHpr(0, 6.5, 0, 0, 0, 0) + else: + self.movieNode.setPosHpr(0, -6.5, 0, 0, 0, 0) + self.exitMovieNode.setPosHpr(3, 6.5, 0, 270, 0, 0) + self.collNodePath.reparentTo(self.movieNode) + + def __handleEnterSphere(self, collEntry): + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount() <= self.lastFrame + 1: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + if base.localAvatar.hp > 0: + + def handleEnterRequest(self = self): + self.ignore('stoppedAsleep') + if hasattr(self.dialog, 'doneStatus') and self.dialog.doneStatus == 'ok': + self.d_requestEnter(base.cr.isPaid()) + else: + self.cr.playGame.getPlace().setState('walk') + self.dialog.ignoreAll() + self.dialog.cleanup() + del self.dialog + + self.cr.playGame.getPlace().fsm.request('stopped') + self.accept('stoppedAsleep', handleEnterRequest) + doneEvent = 'enterRequest|dialog' + msg = TTLocalizer.StartingBlock_EnterShowPad + self.dialog = TTGlobalDialog(msg, doneEvent, 4) + self.dialog.accept(doneEvent, handleEnterRequest) + + def generateCameraMoveTrack(self): + self.cPos = camera.getPos(self.av) + self.cHpr = camera.getHpr(self.av) + cameraPos = Point3(23, -10, 7) + cameraHpr = Point3(65, -10, 0) + camera.wrtReparentTo(self.nodePath) + cameraTrack = LerpPosHprInterval(camera, 1.5, cameraPos, cameraHpr) + return cameraTrack + + def makeGui(self): + self.notify.debugStateCall(self) + if self.timer is not None: + return + self.timer = ToontownTimer() + self.timer.setScale(0.3) + self.timer.setPos(1.16, 0, -.73) + self.timer.hide() + DistributedStartingBlock.makeGui(self) + return + + def showGui(self): + self.notify.debugStateCall(self) + self.timer.show() + DistributedStartingBlock.showGui(self) + + def hideGui(self): + self.notify.debugStateCall(self) + if not hasattr(self, 'timer') or self.timer is None: + return + self.timer.reset() + self.timer.hide() + DistributedStartingBlock.hideGui(self) + return + + def countdown(self): + countdownTime = KartGlobals.COUNTDOWN_TIME - globalClockDelta.localElapsedTime(self.kartPad.getTimestamp(self.avId)) + self.timer.countdown(countdownTime) + + def enterEnterMovie(self): + self.notify.debug('%d enterEnterMovie: Entering the Enter Movie State.' % self.doId) + if base.config.GetBool('want-qa-regression', 0): + raceName = TTLocalizer.KartRace_RaceNames[self.kartPad.trackType] + self.notify.info('QA-REGRESSION: KARTING: %s' % raceName) + pos = self.nodePath.getPos(render) + hpr = self.nodePath.getHpr(render) + pos.addZ(1.7) + hpr.addX(270) + self.kartNode.setPosHpr(pos, hpr) + toonTrack = self.generateToonMoveTrack() + kartTrack = self.generateKartAppearTrack() + jumpTrack = self.generateToonJumpTrack() + name = self.av.uniqueName('EnterRaceTrack') + if self.av is not None and self.localToonKarting: + cameraTrack = self.generateCameraMoveTrack() + self.finishMovie() + self.movieTrack = Sequence(Parallel(cameraTrack, Sequence()), kartTrack, jumpTrack, Func(self.makeGui), Func(self.showGui), Func(self.countdown), Func(self.request, 'Waiting'), Func(self.d_movieFinished), name=name, autoFinish=1) + else: + self.finishMovie() + self.movieTrack = Sequence(toonTrack, kartTrack, jumpTrack, name=name, autoFinish=1) + self.movieTrack.start() + self.exitRequested = True + return diff --git a/toontown/racing/DistributedStartingBlockAI.py b/toontown/racing/DistributedStartingBlockAI.py new file mode 100644 index 0000000..36b577b --- /dev/null +++ b/toontown/racing/DistributedStartingBlockAI.py @@ -0,0 +1,131 @@ +from otp.ai.AIBase import * +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from toontown.building.ElevatorConstants import * +from toontown.building import DistributedElevatorExtAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.showbase.DirectObject import DirectObject +from toontown.racing.KartShopGlobals import KartGlobals +if __debug__: + import pdb + +class DistributedStartingBlockAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStartingBlockAI') + + def __init__(self, air, kartPad, x, y, z, h, p, r, padLocationId): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.avId = 0 + self.isActive = True + self.kartPad = kartPad + self.unexpectedEvent = None + self.padLocationId = padLocationId + self.posHpr = (x, y, z, h, p, r) + self.currentMovie = None + return + + def delete(self): + self.avId = 0 + self.kartPad = None + DistributedObjectAI.DistributedObjectAI.delete(self) + return + + def getPadDoId(self): + return self.kartPad.getDoId() + + def getPadLocationId(self): + return self.padLocationId + + def getPosHpr(self): + return self.posHpr + + def setActive(self, isActive): + self.isActive = isActive + + def requestEnter(self, paid): + avId = self.air.getAvatarIdFromSender() + if self.isActive and self.avId == 0: + success = self.kartPad.addAvBlock(avId, self, paid) + self.notify.debug('requestEnter: avId %s wants to enter the kart block.' % avId) + if success == KartGlobals.ERROR_CODE.success: + self.avId = avId + self.isActive = False + self.unexpectedEvent = self.air.getAvatarExitEvent(self.avId) + self.acceptOnce(self.unexpectedEvent, self.unexpectedExit) + self.d_setOccupied(self.avId) + self.d_setMovie(KartGlobals.ENTER_MOVIE) + else: + self.sendUpdateToAvatarId(avId, 'rejectEnter', [success]) + else: + if hasattr(self.kartPad, 'state') and self.kartPad.state in ['WaitBoarding', 'AllAboard']: + errorCode = KartGlobals.ERROR_CODE.eBoardOver + else: + errorCode = KartGlobals.ERROR_CODE.eOccupied + self.sendUpdateToAvatarId(avId, 'rejectEnter', [errorCode]) + + def requestExit(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestExit: avId %s wants to exit the Kart Block.' % avId) + success = self.validate(avId, self.avId == avId, 'requestExit: avId is not occupying this kart block.') + if not success: + return + self.normalExit() + + def movieFinished(self): + if self.currentMovie == KartGlobals.EXIT_MOVIE: + self.cleanupAvatar() + self.currentMovie = None + if not self.kartPad: + self.handleUnexpectedCleanup() + return + self.kartPad.kartMovieDone() + return + + def cleanupAvatar(self): + self.ignore(self.unexpectedEvent) + if not self.kartPad: + self.handleUnexpectedCleanup() + return + self.kartPad.removeAvBlock(self.avId, self) + self.avId = 0 + self.isActive = True + self.d_setOccupied(0) + + def handleUnexpectedCleanup(self): + self.notify.warning('KartPad has already been cleaned up') + from toontown.hood import GSHoodDataAI + if hasattr(simbase.air, 'hoods') and simbase.air.hoods: + for hood in simbase.air.hoods: + if isinstance(hood, GSHoodDataAI.GSHoodDataAI): + hood.logPossibleRaceCondition(self) + + def normalExit(self): + self.d_setMovie(KartGlobals.EXIT_MOVIE) + + def raceExit(self): + self.cleanupAvatar() + self.movieFinished() + + def unexpectedExit(self): + self.cleanupAvatar() + self.movieFinished() + self.unexpectedEvent = None + return + + def d_setOccupied(self, avId): + self.sendUpdate('setOccupied', [avId]) + + def d_setMovie(self, mode): + self.currentMovie = mode + self.sendUpdate('setMovie', [mode]) + + +class DistributedViewingBlockAI(DistributedStartingBlockAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewingBlockAI') + + def __init__(self, air, kartPad, x, y, z, h, p, r, padLocationId): + DistributedStartingBlockAI.__init__(self, air, kartPad, x, y, z, h, p, r, padLocationId) + + def delete(self): + DistributedStartingBlockAI.delete(self) diff --git a/toontown/racing/DistributedVehicle.py b/toontown/racing/DistributedVehicle.py new file mode 100644 index 0000000..ab052e8 --- /dev/null +++ b/toontown/racing/DistributedVehicle.py @@ -0,0 +1,1222 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import FSM +from direct.distributed import DistributedSmoothNode +from direct.interval.IntervalGlobal import * +from direct.showbase.PythonUtil import clampScalar +from otp.otpbase import OTPGlobals +from otp.avatar import ShadowCaster +from toontown.racing import Kart +from toontown.racing.KartDNA import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.effects.Drift import Drift +from toontown.effects.Sparks import Sparks +from direct.interval.ProjectileInterval import * +from toontown.battle.BattleProps import * +import random +from direct.showbase.PythonUtil import randFloat +from direct.task.Task import Task +import math +iceTurnFactor = 0.25 +iceAccelFactor = 0.4 + +class DistributedVehicle(DistributedSmoothNode.DistributedSmoothNode, Kart.Kart, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVehicle') + cheatFactor = 1.0 + proRacer = 0 + physicsCalculationsPerSecond = 60 + maxPhysicsDt = 1.0 + physicsDt = 1.0 / float(physicsCalculationsPerSecond) + maxPhysicsFrames = maxPhysicsDt * physicsCalculationsPerSecond + maxSpeed = 200 * cheatFactor + turnRatio = 1.0 / 0.025 * math.sqrt(cheatFactor) + accelerationMult = 35 + accelerationBase = 20 + if proRacer: + accelerationMult = 35 + accelerationBase = 30 + surfaceModifiers = {'asphalt': {'shake': 0.1, + 'driftMin': 65, + 'windResistance': 0.2, + 'particleColor': Vec4(0.7, 0.7, 0.7, 1.0)}, + 'gravel': {'shake': 0.2, + 'driftMin': 65, + 'windResistance': 0.2, + 'particleColor': Vec4(0.53, 0.53, 0.53, 1.0)}, + 'dirt': {'shake': 0.4, + 'driftMin': 35, + 'windResistance': 0.3, + 'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)}, + 'grass': {'shake': 0.8, + 'driftMin': 15, + 'windResistance': 0.4, + 'particleColor': Vec4(0.8, 0.42, 0.8, 1.0)}, + 'ice': {'shake': 0.0, + 'driftMin': 0, + 'windResistance': 0.01, + 'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)}, + '': {'shake': 0, + 'driftMin': 1, + 'windResistance': 0.2, + 'particleColor': Vec4(1.0, 1.0, 1.0, 1.0)}} + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_WallHits = [SFX_BaseDir + 'KART_Hitting_Wood_Fence.mp3', + SFX_BaseDir + 'KART_Hitting_Wood_Fence_1.mp3', + SFX_BaseDir + 'KART_Hitting_Metal_Fence.mp3', + SFX_BaseDir + 'KART_Hitting_Wall.mp3'] + SFX_SkidLoop_Grass = SFX_BaseDir + 'KART_Skidding_On_Grass.wav' + SFX_SkidLoop_Asphalt = SFX_BaseDir + 'KART_Skidding_On_Asphalt.wav' + SFX_TurboStart = SFX_BaseDir + 'KART_turboStart.mp3' + SFX_TurboLoop = SFX_BaseDir + 'KART_turboLoop.wav' + AvId2kart = {} + + @classmethod + def getKartFromAvId(cls, avId): + return cls.AvId2kart.get(avId) + + def __init__(self, cr): + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedVehicle') + Kart.Kart.__init__(self) + if base.config.GetBool('want-racer', 0) == 1: + DistributedVehicle.proRacer = 1 + DistributedVehicle.accelerationMult = 35 + DistributedVehicle.accelerationBase = 30 + self.speedometer = None + self.speedGauge = None + self.leanAmount = 0 + self.leftHeld = 0 + self.stopped = False + self.rightHeld = 0 + self.canRace = False + self.groundType = 'gravel' + self.offGround = 0 + self.turbo = False + self.ownerId = 0 + self.cameraTrack = None + self.curSpeed = 0 + self.acceleration = 0 + self.imHitMult = 1 + self.skidding = False + self.hittingWall = 0 + base.kart = self + self.wipeOut = None + self.gagMovie = None + self.spinAnim = None + self.wrongWay = False + self.wallCollideTrack = None + self.wheelMaxTurn = 1.0 + self.wheelMinTurn = 0.15 + self.speedMaxTurn = 90 + self.speedMinTurn = 300 + self.wheelTurnTime = 0.6 + self.wheelReturnTime = 0.3 + self.wheelFightTime = 0.2 + self.wheelPosition = 0.0 + self.outPutCounter = 0 + self.proCameraHeading = 0 + self.proCameraDummyNode = None + self.cameraArmNode = None + self.armSwingSpeedPerp = 0.2 + self.armSwingSpeedPara = 0.7 + self.cameraTightener = 3.0 + self.cameraArmBase = 0 + self.cameraArmExtend = 20 + self.pieCount = 0 + self.numPieChunks = 6 + self.pieSlideSpeed = [] + for piece in range(self.numPieChunks): + self.pieSlideSpeed.append(randFloat(0.0, 0.2)) + + self.wantSmoke = ConfigVariableBool('want-kart-smoke', 1).getValue() + self.wantSparks = ConfigVariableBool('want-kart-sparks', 1).getValue() + self.__loadTextures() + return + + def __loadTextures(self): + self.pieSplatter = loader.loadModel('phase_6/models/karting/pie_splat_1.bam') + + def announceGenerate(self): + DistributedSmoothNode.DistributedSmoothNode.announceGenerate(self) + self.generateKart() + self.name = self.uniqueName('vehicle') + self.posHprBroadcastName = self.uniqueName('vehicleBroadcast') + self.LODnode.forceSwitch(self.LODnode.getHighestSwitch()) + self.cameraNode = NodePath('cameraNode') + self.cameraNode.reparentTo(self) + if self.proRacer: + self.cameraArmNode = render.attachNewNode('cameraArm') + self.cameraArmNode.reparentTo(self) + self.cameraNode.reparentTo(self.cameraArmNode) + self.proCameraDummyNode = render.attachNewNode('proCameraDummy') + self.proCameraDummyNode.reparentTo(render) + self.localVehicle = self.ownerId == localAvatar.doId + if self.localVehicle: + self.setupPhysics() + self.setupParticles() + self.__enableCollisions() + self.forward = NodePath('forward') + self.forward.setPos(0, 1, 0) + self.wallHitsSfx = [] + for wallHit in self.SFX_WallHits: + self.wallHitsSfx.append(base.loadSfx(wallHit)) + + self.skidLoopAsphaltSfx = base.loadSfx(self.SFX_SkidLoop_Asphalt) + self.skidLoopAsphaltSfx.setLoop() + self.skidLoopGrassSfx = base.loadSfx(self.SFX_SkidLoop_Grass) + self.skidLoopGrassSfx.setLoop() + self.turboStartSfx = base.loadSfx(self.SFX_TurboStart) + self.turboLoopSfx = base.loadSfx(self.SFX_TurboLoop) + self.turboLoopSfx.setLoop() + self.forward.reparentTo(self.geom[0]) + self.anvil = globalPropPool.getProp('anvil') + self.anvil.setScale(5) + self.anvil.reparentTo(hidden) + self.anvil.setColor(1, 1, 1, 1) + self.anvil.setTransparency(1) + self.reparentTo(render) + + def setupPhysics(self): + self.__setupCollisions() + self.physicsMgr = PhysicsManager() + self.physicsEpoch = globalClock.getFrameTime() + self.lastPhysicsFrame = 0 + integrator = LinearEulerIntegrator() + self.physicsMgr.attachLinearIntegrator(integrator) + fn = ForceNode('windResistance') + fnp = NodePath(fn) + fnp.reparentTo(render) + windResistance = LinearFrictionForce(0.2) + fn.addForce(windResistance) + self.physicsMgr.addLinearForce(windResistance) + self.windResistance = windResistance + fn = ForceNode('engine') + fnp = NodePath(fn) + fnp.reparentTo(self) + engine = LinearVectorForce(0, 0, 0) + fn.addForce(engine) + self.physicsMgr.addLinearForce(engine) + self.engine = engine + + def disable(self): + DistributedVehicle.AvId2kart.pop(self.ownerId) + self.finishMovies() + self.request('Off') + self.stopSkid() + self.stopSmooth() + if self.localVehicle: + self.__disableCollisions() + self.__undoCollisions() + self.physicsMgr.clearLinearForces() + self.detachNode() + DistributedSmoothNode.DistributedSmoothNode.disable(self) + taskMgr.remove('slidePies') + + def delete(self): + self.stopSmooth() + Kart.Kart.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + if self.localVehicle: + self.ignoreAll() + if hasattr(self, 'piePieces'): + for piece in self.piePieces: + del piece + + def getVelocity(self): + return self.actorNode.getPhysicsObject().getVelocity() + + def __updateWallCollision(self, entry = None): + vol = self.curSpeed / 160 + for wallHit in self.wallHitsSfx: + wallHit.setVolume(vol) + + def __wallCollisionStart(self, entry): + self.entry = entry + self.__wallCollisionStop() + self.hittingWall = 1 + hitToPlay = random.randrange(0, len(self.wallHitsSfx)) + self.wallCollideTrack = Parallel(Func(self.wallHitsSfx[hitToPlay].play)) + self.__updateWallCollision() + self.wallCollideTrack.start() + curSpeed = self.actorNode.getPhysicsObject().getVelocity().length() + if self.wantSparks and curSpeed > 10: + if entry.getSurfaceNormal(self)[0] < 0: + self.fireSparkParticles('right') + else: + self.fireSparkParticles('left') + + def __wallCollisionStop(self, entry = None): + self.hittingWall = 0 + if self.wallCollideTrack: + self.wallCollideTrack.pause() + + def __setupCollisions(self): + self.cWallTrav = CollisionTraverser('KartWall') + self.cWallTrav.setRespectPrevTransform(True) + self.collisionNode = CollisionNode(self.uniqueName('vehicle')) + self.collisionNode.setFromCollideMask(OTPGlobals.WallBitmask) + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask) + cs = CollisionSphere(0, 0, 4, 4) + self.collisionNode.addSolid(cs) + self.collisionNodePath = NodePath(self.collisionNode) + self.wallHandler = PhysicsCollisionHandler() + self.wallHandler.setStaticFrictionCoef(0.0) + self.wallHandler.setDynamicFrictionCoef(0.1) + self.wallHandler.addCollider(self.collisionNodePath, self) + self.wallHandler.setInPattern('enterWallCollision') + self.wallHandler.setOutPattern('exitWallCollision') + self.cWallTrav.addCollider(self.collisionNodePath, self.wallHandler) + self.accept('enterWallCollision', self.__wallCollisionStart) + self.accept('exitWallCollision', self.__wallCollisionStop) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + cRayNode = CollisionNode(self.uniqueName('vehicle-FloorRay')) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.cRayNodePath = self.attachNewNode(cRayNode) + self.lifter = CollisionHandlerGravity() + self.lifter.setGravity(32.174 * 3.0) + self.lifter.addInPattern('floorCollision') + self.lifter.addAgainPattern('floorCollision') + self.lifter.setOffset(OTPGlobals.FloorOffset) + self.lifter.setReach(40.0) + self.lifter.addCollider(self.cRayNodePath, self) + base.cTrav.addCollider(self.cRayNodePath, self.lifter) + self.accept('floorCollision', self.__groundCollision) + self.accept('imIn-banana', self.hitBanana) + base.localAvatar.collisionsOff() + + def __undoCollisions(self): + base.cTrav.removeCollider(self.cRayNodePath) + base.localAvatar.collisionsOn() + + def __groundCollision(self, entry): + ground = entry.getIntoNodePath() + if ground.getNetTag('surface') == '': + return + self.groundType = ground.getNetTag('surface') + + def __enableCollisions(self): + self.cQueue = [] + self.cRays = NodePath('stickVehicleToFloor') + self.cRays.reparentTo(self.geom[0]) + for wheelNode in self.wheelBases: + rayNode = CollisionNode(wheelNode.getName()) + x = wheelNode.getX() + y = wheelNode.getY() + ray = CollisionRay(x, y, 40000.0, 0.0, 0.0, -1.0) + rayNode.addSolid(ray) + rayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + rayNodePath = self.cRays.attachNewNode(rayNode) + cQueue = CollisionHandlerQueue() + self.cWallTrav.addCollider(rayNodePath, cQueue) + self.cQueue.append(cQueue) + + self.collisionNodePath.reparentTo(self) + + def setupLapCollisions(self): + self.lapBit = BitMask32(32768) + self.lapHandler = CollisionHandlerEvent() + self.lapHandler.addInPattern('imIn-%in') + self.cSphere = CollisionSphere(0, 0, 0, 3) + self.lapNode = CollisionNode('lapChecker') + self.lapNode.addSolid(self.cSphere) + self.lapNode.setIntoCollideMask(BitMask32.allOff()) + self.lapNode.setFromCollideMask(self.lapBit) + self.lapNodePath = NodePath(self.lapNode) + self.lapNodePath.reparentTo(self) + self.cWallTrav.addCollider(self.lapNodePath, self.lapHandler) + + def __disableCollisions(self): + self.ignore('imIn-startLine') + self.ignore('imIn-quarterLine') + self.ignore('imIn-midLine') + + def __handleCollisionSphereEnter(self, collEntry = None): + self.d_requestControl() + + def setupParticles(self): + if self.wantSmoke: + self.setupDriftParticles() + if self.wantSparks: + self.setupSparkParticles() + + def updateParticles(self, leanAmount): + if self.wantSmoke: + self.updateDriftParticles(leanAmount) + + def cleanupParticles(self): + if self.wantSmoke: + self.cleanupDriftParticles() + if self.wantSparks: + self.cleanupSparkParticles() + + def setupDriftParticles(self): + smokeMount = self.geom[0].attachNewNode('Smoke Effect') + backLeft = smokeMount.attachNewNode('Back Left Smokemount') + backLeft.setPos(self.geom[0].find('**/' + self.wheelData[self.LRWHEEL]['node']).getPos() + Vec3(-0.25, -1.0, -0.5)) + backRight = smokeMount.attachNewNode('Back Right Smokemount') + backRight.setPos(self.geom[0].find('**/' + self.wheelData[self.RRWHEEL]['node']).getPos() + Vec3(0.25, -1.0, -0.5)) + driftEffects = (Drift(backLeft, backLeft), Drift(backRight, backRight)) + for x in driftEffects: + x.start() + + self.drifts = driftEffects + self.driftSeq = Sequence(Func(backLeft.show), Func(backRight.show), Wait(1000000), Func(self.drifts[0].effect.getParticlesNamed('particles-1').getRenderer().setColor, Vec4(1.0, 1.0, 1.0, 1.0)), Func(self.drifts[1].effect.getParticlesNamed('particles-1').getRenderer().setColor, Vec4(1.0, 1.0, 1.0, 1.0)), Func(backLeft.hide), Func(backRight.hide)) + self.driftSeqStarted = False + self.driftParticleForces = [ x.effect.getParticlesNamed('particles-1').getLinearForce(0) for x in self.drifts ] + self.smokeMount = smokeMount + backLeft.hide() + backRight.hide() + + def updateDriftParticles(self, leanAmount): + for x in self.driftParticleForces: + x.setAmplitude(leanAmount * 30) + + if self.skidding and not self.driftSeqStarted: + self.driftSeqStarted = True + self.driftSeq.start() + elif not self.skidding and self.driftSeqStarted: + self.driftSeqStarted = False + self.driftSeq.finish() + + def cleanupDriftParticles(self): + self.driftSeq.finish() + for x in self.drifts: + x.destroy() + + self.smokeMount.remove() + del self.driftSeq + del self.driftParticleForces + del self.drifts + del self.smokeMount + + def setupSparkParticles(self): + bodyType = self.kartDNA[KartDNA.bodyType] + endPts = KartDict[bodyType][7] + self.sparkMount = self.geom[0].attachNewNode('Spark Effect') + left = self.sparkMount.attachNewNode('Left Sparkmount') + left.setPos((self.geom[0].find('**/' + self.wheelData[self.LFWHEEL]['node']).getPos() + self.geom[0].find('**/' + self.wheelData[self.LRWHEEL]['node']).getPos()) / 2.0 + Vec3(0.0, -1.0, 1.0)) + left.setScale(0.5) + right = self.sparkMount.attachNewNode('Right Sparkmount') + right.setPos((self.geom[0].find('**/' + self.wheelData[self.RFWHEEL]['node']).getPos() + self.geom[0].find('**/' + self.wheelData[self.RRWHEEL]['node']).getPos()) / 2.0 + Vec3(0.0, -1.0, 1.0)) + right.setScale(0.5) + self.sparks = (Sparks(right, self.sparkMount), Sparks(left, self.sparkMount)) + self.sparks[0].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint1(endPts[0]) + self.sparks[0].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint2(endPts[1]) + self.sparks[0].effect.getParticlesNamed('particles-1').getLinearForce(0).setAmplitude(-50) + self.sparks[1].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint1(Point3(-endPts[0][0], endPts[0][1], endPts[0][2])) + self.sparks[1].effect.getParticlesNamed('particles-1').getEmitter().setEndpoint2(Point3(-endPts[1][0], endPts[1][1], endPts[1][2])) + self.sparks[1].effect.getParticlesNamed('particles-1').getLinearForce(0).setAmplitude(50) + + def fireSparkParticles(self, side): + spark = self.sparks[{'right': 0, + 'left': 1}[side]] + if not spark.effect.isEnabled(): + spark.effect.getParticlesNamed('particles-1').setBirthRate(0.02) + spark.start() + taskMgr.doMethodLater(0.25, self.stopSparkParticles, 'sparkTimer-' + side, extraArgs=[side]) + + def stopSparkParticles(self, side = None): + sides = {0: 'right', + 1: 'left'} + if side == None: + for x in sides.keys(): + self.sparks[x].effect.getParticlesNamed('particles-1').setBirthRate(1000) + taskMgr.doMethodLater(0.75, self.sparks[x].stop, 'stopSparks-' + sides[x], extraArgs=[]) + + else: + spark = self.sparks[{'right': 0, + 'left': 1}[side]] + spark.effect.getParticlesNamed('particles-1').setBirthRate(1000) + taskMgr.doMethodLater(0.75, spark.stop, 'stopSparks-' + side, extraArgs=[]) + return + + def cleanupSparkParticles(self): + taskMgr.remove('sparkTimer-left') + taskMgr.remove('sparkTimer-right') + taskMgr.remove('stopSparks-left') + taskMgr.remove('stopSparks-right') + for x in self.sparks: + x.destroy() + + self.sparkMount.remove() + del self.sparks + del self.sparkMount + + def setState(self, state, avId): + self.notify.debug('SetState received: %s avId: %s' % (state, avId)) + if state == 'C': + self.demand('Controlled', avId) + elif state == 'P': + self.demand('Parked') + else: + self.notify.error('Invalid state from AI: %s' % state) + + def d_requestControl(self): + self.sendUpdate('requestControl') + + def d_requestParked(self): + self.sendUpdate('requestParked') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterParked(self): + pass + + def exitParked(self): + pass + + def enterControlled(self, avId): + self.avId = avId + self.toon = base.cr.doId2do.get(avId, None) + if self.toon: + self.toon.stopSmooth() + self.toon.stopPosHprBroadcast() + for feet in self.toon.getPieces(('legs', 'feet')): + feet.hide() + + self.toon.reparentTo(self.toonSeat) + self.toon.dropShadow.hide() + self.notify.debug('setting pos of toon%s' % self.toon.doId) + self.toon.setPosHpr(0, 0, 0, 0, 0, 0) + self.toon.loop('sit') + if self.toon.doId == localAvatar.doId: + self.notify.debug('calling send currentPosition') + self.toon.sendCurrentPosition() + self.doHeadScale(self.toon, 1.75) + self.toon.setShear(0, 0, 0) + NametagGlobals.setOnscreenChatForced(1) + if self.localVehicle: + camera.reparentTo(self.cameraNode) + camera.setPosHpr(0, -33, 16, 0, -10, 0) + self.physicsMgr.attachPhysicalNode(self.node()) + self.__enableControlInterface() + self.__createPieWindshield() + self.startPosHprBroadcast() + self.engineSfxTrack = self.generateEngineStartTrack() + self.engineSfxTrack.start() + else: + self.startSmooth() + taskName = 'updateNonlocalVehicle-%s' % avId + self.updateNonLocalTask = taskMgr.add(self.__updateNonlocalVehicle, taskName) + return + + def exitControlled(self): + if self.localVehicle: + self.stopPosHprBroadcast() + self.__disableControlInterface() + self.physicsMgr.removePhysicalNode(self.node()) + self.cleanupParticles() + camera.reparentTo(localAvatar) + camera.setPos(localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.engineSfxTrack.finish() + self.engineSfxTrack = self.generateEngineStopTrack() + else: + self.stopSmooth() + taskMgr.remove(self.updateNonLocalTask) + if self.toon and not self.toon.isDisabled() and not self.toon.isEmpty(): + self.toon.dropShadow.show() + self.doHeadScale(self.toon, None) + self.toon.setPosHpr(self.geom[0], 0, 8, 0, 0, 0, 0) + for feet in self.toon.getPieces(('legs', 'feet')): + feet.show() + + self.toon.reparentTo(render) + self.toon.loop('neutral') + self.toon.startSmooth() + NametagGlobals.setOnscreenChatForced(0) + return + + def doHeadScale(self, model, scale): + if scale == None: + scale = ToontownGlobals.toonHeadScales[model.style.getAnimal()] + base.localAvatar.clearCheesyEffect() + for hi in range(model.headParts.getNumPaths()): + head = model.headParts[hi] + head.setScale(scale) + + return + + def __createPieWindshield(self): + self.piePieces = [] + for piece in range(self.numPieChunks): + self.piePieces.append(DirectLabel(relief=None, pos=(0.0, 0.0, 0.0), image=self.pieSplatter, image_scale=(0.5, 0.5, 0.5), text=' ', text_scale=0.18, text_fg=(1, 0, 1, 1), text_pos=(-0.0, 0.0, 0), text_font=ToontownGlobals.getSignFont(), textMayChange=1)) + self.piePieces[piece].hide() + + return + + def showPieces(self): + xRange = 0.3 + for piece in self.piePieces: + piece.setPos(randFloat(-xRange, xRange), randFloat(-0.1, 3.5), randFloat(-0.9, 0.9)) + piece.setScale(randFloat(1.1, 2.0), 1, randFloat(1.1, 2.0)) + piece.show() + xRange += 2.5 / self.numPieChunks + + for piece in range(self.numPieChunks): + self.pieSlideSpeed[piece] = randFloat(0.0, 0.2) + + def splatPie(self): + self.showPieces() + if self.pieCount == 0: + self.pieCount = 15 + taskMgr.doMethodLater(1, self.__countPies, ' ', extraArgs=[]) + taskMgr.add(self.__slidePies, 'slidePies', priority=25) + else: + self.pieCount = 15 + + def __countPies(self): + if self.pieCount > 0: + taskMgr.doMethodLater(1, self.__countPies, ' ', extraArgs=[]) + self.pieCount -= 1 + else: + for piece in self.piePieces: + piece.hide() + + def __slidePies(self, optional = None): + dt = globalClock.getDt() + for piece in range(self.numPieChunks): + self.pieSlideSpeed[piece] += randFloat(0.0, 0.25 * dt) + pieSpeed = self.pieSlideSpeed[piece] * dt + self.piePieces[piece].setPos(self.piePieces[piece].getPos()[0], self.piePieces[piece].getPos()[1] - pieSpeed, self.piePieces[piece].getPos()[2] - pieSpeed) + + if self.pieCount == 0: + return Task.done + else: + return Task.cont + + def __enableControlInterface(self): + if self.canRace: + self.enableControls() + taskMgr.remove('watchVehicleControls') + taskMgr.add(self.__watchControls, 'watchVehicleControls', priority=25) + if not self.speedometer: + cm = CardMaker('speed') + cm.setFrame(-0.5, 0.5, -0.5, 0.5) + self.speedometerImages = aspect2d.attachNewNode('SpeedometerImages') + self.speedometerImages.setTransparency(True) + self.speedometerImages.setPos(1.24, 0.0, -0.98) + self.speedometerImages.setScale(0.75) + m = loader.loadModel('phase_6/models/karting/speedometer') + if self.getBodyColor() == InvalidEntry: + bodyColor = getDefaultColor() + else: + bodyColor = getAccessory(self.getBodyColor()) + if self.getAccessoryColor() == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.getAccessoryColor()) + self.speedometerImages.attachNewNode(m.find('**/*gauge').node()).setColorScale(1, 1, 1, 1) + self.speedGauge = self.speedometerImages.attachNewNode(m.find('**/*spin').node()) + self.speedGauge.setColor(1, 1, 1, 1) + c = (Vec4(1, 1, 1, 1) + accColor) / 2.0 + c.setW(1.0) + self.speedometerImages.attachNewNode(m.find('**/*face').node()).setColorScale(c) + c = (Vec4(2, 2, 2, 2) - accColor) / 2.0 + c = (bodyColor + Vec4(1, 1, 1, 1)) / 2.0 + c.setW(1.0) + self.speedometerImages.attachNewNode(m.find('**/*tics').node()).setColorScale(c) + c = (bodyColor + Vec4(1, 1, 1, 1)) / 2.0 + c.setW(1.0) + self.speedometerImages.attachNewNode(m.find('**/*ring').node()).setColorScale(c) + self.speedometer = DirectLabel(relief=None, pos=(1.24, 0.0, -0.98), text=str(0), text_scale=0.18, text_fg=bodyColor, text_pos=(-0.04, 0.02, 0), text_font=ToontownGlobals.getSignFont()) + else: + self.showSpeedometer() + self.arrowVert = 0 + self.arrowHorz = 0 + return + + def showSpeedometer(self): + if self.speedometer: + self.speedometer.show() + self.speedometerImages.show() + + def hideSpeedometer(self): + if self.speedometer: + self.speedometer.hide() + self.speedometerImages.hide() + + def __disableControlInterface(self): + self.hideSpeedometer() + self.disableControls() + self.stopSmooth() + taskMgr.remove('watchVehicleControls') + + def __deleteControlInterface(self): + if self.speedometer: + self.speedometer.destroy() + self.speedometer = None + self.speedGauge = None + self.speedometerImages.removeNode() + self.speedometerImages = None + return + + def setTurbo(self, setting): + self.turbo = setting + if self.turbo: + self.turboLoopSfx.play() + else: + self.turboLoopSfx.stop() + + def interruptTurbo(self): + self.__stopTurbo() + if self.cameraTrack: + self.cameraTrack.finish() + self.cameraTrack = None + return + + def startTurbo(self): + newCameraPos = Point3(0, -25, 16) + newCameraFov = 90 + turboDuration = 3 + startFov = base.camLens.getFov().getX() + if self.cameraTrack: + self.cameraTrack.pause() + cameraZoomIn = Parallel(LerpPosInterval(camera, 2, newCameraPos), LerpFunc(base.camLens.setFov, fromData=startFov, toData=newCameraFov, duration=2)) + cameraToNormal = Parallel(LerpPosInterval(camera, 1, Point3(0, -33, 16), newCameraPos), LerpFunc(base.camLens.setFov, fromData=newCameraFov, toData=ToontownGlobals.DefaultCameraFov, duration=1)) + self.cameraTrack = Sequence(Func(self.turboStartSfx.play), cameraZoomIn, Func(lambda : self.setTurbo(True)), Wait(turboDuration), Func(self.__stopTurbo), cameraToNormal) + self.cameraTrack.start() + + def __stopTurbo(self, task = None): + self.setTurbo(False) + + def __controlPressed(self): + self.__throwGag() + + def __controlReleased(self): + pass + + def __upArrow(self, pressed): + if pressed: + self.arrowVert = 1 + elif self.arrowVert > 0: + self.arrowVert = 0 + + def __downArrow(self, pressed): + if pressed: + self.arrowVert = -1 + elif self.arrowVert < 0: + self.arrowVert = 0 + + def __rightArrow(self, pressed): + if pressed: + self.arrowHorz = 1 + self.rightHeld += 1 + elif self.arrowHorz > 0: + self.arrowHorz = 0 + self.rightHeld = 0 + + def __leftArrow(self, pressed): + if pressed: + self.arrowHorz = -1 + self.leftHeld += 1 + elif self.arrowHorz < 0: + self.arrowHorz = 0 + self.leftHeld = 0 + + def __updateNonlocalVehicle(self, task = None): + self.curSpeed = self.smoother.getSmoothForwardVelocity() + rotSpeed = -1 * self.smoother.getSmoothRotationalVelocity() + self.leanAmount = self.curSpeed * rotSpeed / 500.0 + self.leanAmount = clampScalar(self.leanAmount, -10, 10) + self.__animate() + return Task.cont + + def __animate(self): + speed = self.curSpeed + self.spinWheels(speed / 10) + enginePitch = clampScalar(speed / 120.0, 0.5, 15) + self.kartLoopSfx.setPlayRate(enginePitch) + if not self.localVehicle: + dist = (self.getPos() - localAvatar.getPos()).length() + if self.localVehicle: + if self.lifter.isOnGround(): + if self.offGround > 10: + kart = self.geom[0].find('**/main*') + bumpDown1 = kart.posInterval(0.1, Vec3(0, 0, -1)) + bumpUp1 = kart.posInterval(0.15, Vec3(0, 0, 0)) + bumpDown2 = kart.posInterval(0.5, Vec3(0, 0, -.4)) + bumpUp2 = kart.posInterval(0.7, Vec3(0, 0, 0)) + bumpSeq = Sequence(bumpDown1, bumpUp1, bumpDown2, bumpUp2) + bumpSeq.start() + self.offGround = 0 + else: + self.offGround += 1 + if self.offGround == 0: + modifier = self.surfaceModifiers[self.groundType]['shake'] * (speed / 50.0) + else: + modifier = 1 + roll = self.leanAmount * 1.5 + roll += (random.random() * 2 - 1) * modifier + pitch = self.acceleration * -.005 + pitch += (random.random() * 2 - 1) * modifier + self.rollSuspension(roll) + self.pitchSuspension(pitch) + return Task.cont + + def __clampPosition(self): + retVal = False + if self.getX() < -3276: + self.notify.debug('clamping X %d' % self.getX()) + self.setX(-3276) + retVal = True + if self.getX() > 3276: + self.notify.debug('clamping X %d' % self.getX()) + self.setX(3276) + retVal = True + if self.getY() < -3276: + self.notify.debug('clamping Y %d' % self.getY()) + self.setY(-3276) + retVal = True + if self.getY() > 3276: + self.notify.debug('clamping Y %d' % self.getY()) + self.setY(3276) + retVal = True + if self.getZ() < -3276: + self.notify.debug('clamping Z %d' % self.getZ()) + self.setZ(-3276) + retVal = True + if self.getZ() > 3276: + self.notify.debug('clamping Z %d' % self.getZ()) + self.setZ(3276) + retVal = True + return retVal + + def amIClampingPosition(self): + return self.getX() == -3276 or self.getX() == 3276 or self.getY() == -3276 or self.getY() == 3276 or self.getZ() == -3276 or self.getZ() == 3276 + + def __computeTurnRatio(self, speed): + effectiveSpeed = speed + if effectiveSpeed > self.speedMinTurn: + effectiveSpeed = self.speedMinTurn + if effectiveSpeed < self.speedMaxTurn: + turnRatio = 1.0 * effectiveSpeed + elif effectiveSpeed >= self.speedMaxTurn: + turnRatio = 1.0 * (effectiveSpeed * (self.speedMaxTurn / speed)) + + def __updateWheelPos(self, dt, curSpeed): + ratioIntoMax = (curSpeed - self.speedMinTurn) / (self.speedMaxTurn - self.speedMinTurn) + if ratioIntoMax > 1.0: + ratioIntoMax = 1.0 + if ratioIntoMax < 0.0: + ratioIntoMax = 0.0 + maxWheelDeflection = self.wheelMaxTurn * ratioIntoMax + self.wheelMinTurn * (1.0 - ratioIntoMax) + if self.wheelPosition * self.arrowHorz > 0: + self.wheelPosition += self.arrowHorz * dt / self.wheelTurnTime + else: + self.wheelPosition += self.arrowHorz * dt / self.wheelFightTime + if not self.arrowHorz: + if self.wheelPosition > 0: + self.wheelPosition -= dt / self.wheelReturnTime + if self.wheelPosition < 0: + self.wheelPosition = 0 + else: + self.wheelPosition += dt / self.wheelReturnTime + if self.wheelPosition > 0: + self.wheelPosition = 0 + if abs(self.wheelPosition) >= maxWheelDeflection: + if self.wheelPosition > 0: + self.wheelPosition = maxWheelDeflection + else: + self.wheelPosition = -maxWheelDeflection + + def __watchControls(self, task): + dt = globalClock.getDt() + curVelocity = self.actorNode.getPhysicsObject().getVelocity() + curSpeed = curVelocity.length() + fvec = self.forward.getPos(render) - self.getPos(render) + fvec.normalize() + dotProduct = curVelocity.dot(fvec) + goingBack = -1 + if dotProduct < 0: + goingBack = 1 + if self.proRacer: + self.__computeTurnRatio(curSpeed) + self.__updateWheelPos(dt, curSpeed) + newHForTurning = self.getH() + if self.proRacer or not self.stopped and self.arrowHorz and curSpeed > 1: + if self.proRacer: + turnHelp = 0 + if self.hittingWall or goingBack == 1: + turnHelp = self.arrowHorz + effectiveSpeed = curSpeed + if effectiveSpeed > self.speedMinTurn: + effectiveSpeed = self.speedMinTurn + rotation = -goingBack * (self.wheelPosition * dt * self.turnRatio * -1.8 * curSpeed / 100 + turnHelp * dt * self.turnRatio * -1.2) + self.outPutCounter += 1 + if self.outPutCounter > 5: + self.outPutCounter = 0 + else: + rotation = self.arrowHorz * dt * self.turnRatio * -1.2 + oldH = self.getH() + newH = (oldH + rotation) % 360 + self.setH(newH) + newHForTurning = newH + if self.groundType == 'ice': + newHForTurning = (oldH + rotation * iceTurnFactor) % 360 + pitch = -self.getP() + 5 + accelBase = self.accelerationBase + pitch += accelBase + pitch = clampScalar(pitch, accelBase - 5, accelBase + 5) + self.accelerationMult = pitch * 2 + if self.groundType == 'ice': + self.accelerationMult *= iceAccelFactor + if self.stopped: + self.acceleration = 0 + else: + self.acceleration = self.arrowVert * self.accelerationMult * self.cheatFactor + if self.proRacer: + if self.skidding: + self.acceleration = self.arrowVert * self.accelerationMult * self.cheatFactor * 0.5 + if self.turbo: + self.acceleration += self.accelerationMult * 1.5 + self.engine.setVector(Vec3(0, self.acceleration, 0)) + if self.groundType == 'ice': + rotMat = Mat3.rotateMatNormaxis(newHForTurning, Vec3.up()) + else: + rotMat = Mat3.rotateMatNormaxis(self.getH(), Vec3.up()) + curHeading = rotMat.xform(Vec3.forward()) + push = (3 - self.getP()) * 0.02 + curHeading.setZ(curHeading.getZ() - min(0.2, max(-.2, push))) + onScreenDebug.append('vehicle curHeading = %s\n' % curHeading.pPrintValues()) + onScreenDebug.append('vehicle H = %s newHForTurning=%f\n' % (self.getH(), newHForTurning)) + windResistance = self.surfaceModifiers[self.groundType]['windResistance'] + self.windResistance.setCoef(windResistance) + physicsFrame = int((globalClock.getFrameTime() - self.physicsEpoch) * self.physicsCalculationsPerSecond) + numFrames = min(physicsFrame - self.lastPhysicsFrame, self.maxPhysicsFrames) + self.lastPhysicsFrame = physicsFrame + leanIncrement = self.arrowHorz * self.physicsDt * self.turnRatio + if self.stopped: + leanIncrement = 0 + driftMin = self.surfaceModifiers[self.groundType]['driftMin'] + if self.proRacer: + driftMin = self.surfaceModifiers[self.groundType]['driftMin'] * 0.2 + if self.skidding: + driftMin = self.surfaceModifiers[self.groundType]['driftMin'] + for i in range(numFrames): + self.physicsMgr.doPhysics(self.physicsDt) + curVelocity = self.actorNode.getPhysicsObject().getVelocity() + idealVelocity = curHeading * curSpeed + curVelocity *= self.imHitMult + driftVal = abs(self.leanAmount) * 16 / self.cheatFactor + 15 / self.cheatFactor + curVelocity = Vec3((curVelocity * driftVal + idealVelocity) / (driftVal + 1)) + curSpeed = curVelocity.length() + curVelocity.normalize() + curVelocity *= min(curSpeed, 600) + self.actorNode.getPhysicsObject().setVelocity(curVelocity) + curSpeed = curVelocity.length() + speedFactor = min(curSpeed, 150) / 162.0 + self.leanAmount = (self.leanAmount + leanIncrement) * speedFactor + self.leanAmount = clampScalar(self.leanAmount, -10, 10) + + self.cWallTrav.traverse(render) + self.curSpeed = curSpeed + if self.proRacer: + self.turnWheels(self.wheelPosition * -45) + else: + self.turnWheels(self.arrowHorz * -10) + self.__animate() + if self.proRacer: + speedProporation = 1.0 + if curSpeed < self.speedMaxTurn: + speedProporation = 0.0 + else: + speedProporation = (curSpeed - self.speedMaxTurn) / self.speedMinTurn + cameraDist = self.cameraArmBase + self.cameraArmExtend * speedProporation + cameraOffset = Point3(0, -cameraDist, 0) + self.cameraNode.setPos(cameraOffset) + behindPos = render.getRelativePoint(self, Point3(0, -30, 0)) + self.proCameraDummyNode.setPos(render, behindPos) + self.proCameraDummyNode.lookAt(self) + self.cameraNode.lookAt(self) + dir1 = self.proCameraDummyNode.getH() + dir2 = self.proCameraHeading + if dir1 > 180: + dir1 -= 360 + elif dir1 < -180: + dir1 += 360 + if dir2 > 180: + dir2 -= 360 + elif dir2 < -180: + dir2 += 360 + self.proCameraHeading = dir2 + dif = dir1 - dir2 + if dif > 180: + dif -= 360 + elif dif < -180: + dif += 360 + speedDif = abs(dif) + if speedDif > 90: + speedDif = 90 + cameraTightener = 1.0 + if curSpeed > self.speedMinTurn: + cameraTightener = self.cameraTightener + else: + cameraTightener = 1.0 + curSpeed / self.speedMinTurn * (self.cameraTightener - 1.0) + swingSpeedRatio = speedDif / 90 + swingSpeed = self.armSwingSpeedPerp * swingSpeedRatio + self.armSwingSpeedPara * (1 - swingSpeedRatio) + self.proCameraHeading += dif * cameraTightener * (dt / swingSpeed) + self.cameraArmNode.setH(self.proCameraHeading - self.getH()) + elif not self.stopped: + self.cameraNode.setH(self.leanAmount) + self.updateParticles(self.leanAmount) + if (self.leanAmount > 8 or self.leanAmount < -8) and self.offGround <= 0: + self.startSkid() + else: + self.stopSkid() + if self.speedometer: + self.speedometer['text'] = str(int(curSpeed / 3)) + self.speedGauge.setR(min(110, max(0, curSpeed / 3 / 120 * 110))) + if not self.stopped: + self.stickCarToGround() + if self.__clampPosition(): + self.notify.debug('did a clampPosition on %d' % self.doId) + return Task.cont + + def enableControls(self): + self.canRace = True + 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) + + def disableControls(self): + self.arrowVert = 0 + self.arrowHorz = 0 + self.ignore('control') + self.ignore('control-up') + self.ignore('tab') + self.ignore('InputState-forward') + self.ignore('InputState-reverse') + self.ignore('InputState-turnLeft') + self.ignore('InputState-turnRight') + + def setInput(self, on): + if localAvatar.doId == self.ownerId: + if on: + self.enableControls() + else: + self.disableControls() + + def shakeWheel(self, nWheel, floorNode): + groundType = floorNode.getNetTag('surface') + modifier = self.surfaceModifiers[groundType]['shake'] + shakeFactor = (random.random() * 2 - 1) * modifier + shakeFactor *= self.curSpeed / 200 + node = self.wheelCenters[nWheel].find('*Wheel') + node.setZ(shakeFactor) + + def stickCarToGround(self): + posList = [] + nWheels = len(self.wheelData) + for nWheel in range(nWheels): + cQueue = self.cQueue[nWheel] + cQueue.sortEntries() + if cQueue.getNumEntries() == 0: + return + entry = cQueue.getEntry(0) + self.shakeWheel(nWheel, entry.getIntoNodePath()) + pos = entry.getSurfacePoint(render) + wheelPos = self.wheelBases[nWheel].getPos(render) + pos = wheelPos - pos + posList.append(pos) + cQueue.clearEntries() + + rf = posList[0].getZ() + lf = posList[1].getZ() + rr = posList[2].getZ() + lr = posList[3].getZ() + right = (rf + rr) / 2 + left = (lf + lr) / 2 + rollVal = right - left + rollVal = clampScalar(rollVal, -1, 1) + curRoll = self.getR() + newRoll = curRoll + rollVal * 2.0 + self.setR(newRoll) + if not self.stopped: + camera.setR(-newRoll) + front = (rf + lf) / 2 + rear = (rr + lr) / 2 + center = (front + rear) / 2 + pitchVal = front - rear + pitchVal = clampScalar(pitchVal, -1, 1) + curPitch = self.getP() + newPitch = curPitch - pitchVal * 2.0 + self.setP((newPitch + curPitch) / 2.0) + if self.proRacer: + self.cameraNode.setP(-newPitch) + elif not self.stopped: + self.cameraNode.setP(-newPitch) + + def setBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def setBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def setAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def setEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def setSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def setFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def setBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def setRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def setDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def setOwner(self, avId): + self.ownerId = avId + DistributedVehicle.AvId2kart[avId] = self + + def stopCar(self, level): + self.imHitMult = level + if hasattr(self, 'cameraTrack') and self.cameraTrack: + self.cameraTrack.pause() + cameraToNormal = Parallel(LerpPosInterval(camera, 0.05, Point3(0, -33, 16), startPos=camera.getPos()), LerpFunc(base.camLens.setFov, fromData=base.camLens.getFov()[0], toData=ToontownGlobals.DefaultCameraFov, duration=0.05)) + cameraToNormal.start() + self.__stopTurbo() + self.stopped = True + + def startCar(self): + self.imHitMult = 1 + self.stopped = False + + def hideAnvil(self): + self.anvil.reparentTo(hidden) + self.anvil.setAlphaScale(1) + self.dropShadow.setScale(self.ShadowScale) + + def spinCar(self, spin): + self.geom[0].setH(2 * spin) + + def playSpin(self, timeStamp): + delta = -globalClockDelta.networkToLocalTime(timeStamp, globalClock.getFrameTime(), 16, 100) + globalClock.getFrameTime() + self.spinAnim = LerpFunc(self.spinCar, fromData=0, toData=360, duration=min(1 - delta, 0)) + self.spinAnim.start() + + def __throwGag(self): + base.race.shootGag() + + def dropOnMe(self, timestamp): + self.anvil.setScale(7, 9, 7) + self.anvil.setPos(0, 0, 75) + self.anvil.reparentTo(self) + anvilDrop = ProjectileInterval(self.anvil, startPos=Point3(0, 0, 100), endPos=Point3(0, 0, 0), duration=1) + shadowScale = self.dropShadow.scaleInterval(1, self.ShadowScale * 2) + fadeOut = LerpFunc(self.anvil.setAlphaScale, fromData=1, toData=0, duration=0.5) + flattenMe = self.geom[0].scaleInterval(0.08, Vec3(1, 1, 0.1)) + unFlatten = Sequence(self.geom[0].scaleInterval(0.2, Vec3(1, 1, 2)), self.geom[0].scaleInterval(0.1, 1)) + anvilSquish = Parallel(Parallel(anvilDrop, shadowScale), Sequence(Wait(0.92), flattenMe)) + anvilStretchFade = Sequence(Parallel(self.anvil.scaleInterval(0.2, Vec3(7, 9, 5)), Func(self.lookUp)), fadeOut) + self.gagMovie = Sequence(anvilSquish, Func(self.stopCar, 0.9), anvilStretchFade, Wait(0.5), Parallel(unFlatten, Func(self.lookNormal)), Func(self.startCar), Func(self.hideAnvil)) + self.gagMovie.start() + + def lookUp(self): + if self.toon and self.toon.headParts: + headParts = self.toon.headParts + for hi in range(headParts.getNumPaths()): + head = headParts[hi] + head.setP(90) + + def lookNormal(self): + if self.toon and self.toon.headParts: + headParts = self.toon.headParts + for hi in range(headParts.getNumPaths()): + head = headParts[hi] + head.setP(0) + + def flattenMe(self): + self.geom[0].setScale(3, 3, 0.1) + + def unFlattenMe(self): + self.geom[0].setScale(2) + + def startSkid(self): + if self.skidding == False: + self.skidding = True + self.skidLoopAsphaltSfx.play() + + def stopSkid(self): + if self.skidding == True: + self.skidding = False + self.skidLoopAsphaltSfx.stop() + self.skidLoopGrassSfx.stop() + + def updateSkid(self): + if self.skidding: + if self.groundType == 'grass': + self.skidLoopGrassSfx.setVolume(1) + self.skidLoopAsphaltSfx.setVolume(0) + else: + self.skidLoopGrassSfx.setVolume(0) + self.skidLoopAsphaltSfx.setVolume(1) + + def dropOnHim(self, timestamp): + self.anvil.setScale(10, 13, 10) + self.anvil.setPos(0, 0, 75) + self.anvil.reparentTo(self) + anvilDrop = ProjectileInterval(self.anvil, startPos=Point3(0, 0, 100), endPos=Point3(0, 0, 0), duration=1) + shadowScale = self.dropShadow.scaleInterval(1, self.ShadowScale * 4) + fadeOut = LerpFunc(self.anvil.setAlphaScale, fromData=1, toData=0, duration=0.5) + flattenMe = self.geom[0].scaleInterval(0.08, Vec3(1, 1, 0.1)) + unFlatten = Sequence(self.geom[0].scaleInterval(0.2, Vec3(1, 1, 2)), self.geom[0].scaleInterval(0.1, 1)) + anvilSquish = Parallel(Parallel(anvilDrop, shadowScale), Sequence(Wait(0.92), flattenMe)) + anvilStretchFade = Sequence(Parallel(self.anvil.scaleInterval(0.2, Vec3(10, 13, 4)), Func(self.lookUp)), fadeOut) + self.gagMovie = Sequence(anvilSquish, anvilStretchFade, Wait(0.5), Parallel(unFlatten, Func(self.lookNormal)), Func(self.hideAnvil)) + self.gagMovie.start() + + def hitBanana(self): + if self.wipeOut: + self.wipeOut.pause() + spinAnim = LerpFunc(self.spinCar, fromData=self.geom[0].getH(), toData=360, duration=1) + else: + spinAnim = LerpFunc(self.spinCar, fromData=0, toData=360, duration=1) + self.wipeOut = Sequence(Func(self.stopCar, 0.99), spinAnim, Func(self.startCar)) + self.wipeOut.start() + + def hitPie(self): + print 'yar, got Me with pi!' + self.splatPie() + if self.wipeOut: + self.wipeOut.pause() + spinAnim = LerpFunc(self.spinCar, fromData=self.geom[0].getH(), toData=1080, duration=0.5) + else: + spinAnim = LerpFunc(self.spinCar, fromData=0, toData=1080, duration=0.5) + self.wipeOut = Sequence(Func(self.stopCar, 0.99), spinAnim, Func(self.startCar)) + self.wipeOut.start() + + def finishMovies(self): + if self.gagMovie: + self.gagMovie.finish() + self.gagMovie = None + if self.wipeOut: + self.wipeOut.finish() + self.wipeOut = None + if self.spinAnim: + self.spinAnim.finish() + self.spinAnim = None + return diff --git a/toontown/racing/DistributedVehicleAI.py b/toontown/racing/DistributedVehicleAI.py new file mode 100644 index 0000000..39d3359 --- /dev/null +++ b/toontown/racing/DistributedVehicleAI.py @@ -0,0 +1,137 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from toontown.racing.KartDNA import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedSmoothNodeAI +from direct.fsm import FSM +from direct.task import Task +if (__debug__): + import pdb + +class DistributedVehicleAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedVehicleAI') + + def __init__(self, air, avId): + self.ownerId = avId + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + FSM.FSM.__init__(self, 'DistributedVehicleAI') + self.driverId = 0 + self.kartDNA = [-1] * getNumFields() + self.__initDNA() + self.request('Off') + + def generate(self): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self) + + def delete(self): + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + + def __initDNA(self): + owner = self.air.doId2do.get(self.ownerId) + if owner: + self.kartDNA[KartDNA.bodyType] = owner.getKartBodyType() + self.kartDNA[KartDNA.bodyColor] = owner.getKartBodyColor() + self.kartDNA[KartDNA.accColor] = owner.getKartAccessoryColor() + self.kartDNA[KartDNA.ebType] = owner.getKartEngineBlockType() + self.kartDNA[KartDNA.spType] = owner.getKartSpoilerType() + self.kartDNA[KartDNA.fwwType] = owner.getKartFrontWheelWellType() + self.kartDNA[KartDNA.bwwType] = owner.getKartBackWheelWellType() + self.kartDNA[KartDNA.rimsType] = owner.getKartRimType() + self.kartDNA[KartDNA.decalType] = owner.getKartDecalType() + else: + self.notify.warning('__initDNA - OWNER %s OF KART NOT FOUND!' % self.ownerId) + + def d_setState(self, state, avId): + self.sendUpdate('setState', [state, avId]) + + def requestControl(self): + avId = self.air.getAvatarIdFromSender() + if self.driverId == 0: + self.request('Controlled', avId) + + def requestParked(self): + avId = self.air.getAvatarIdFromSender() + if avId == self.driverId: + self.request('Parked') + + def start(self): + self.request('Parked') + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterParked(self): + self.driverId = 0 + self.d_setState('P', 0) + return None + + def exitParked(self): + return None + + def enterControlled(self, avId): + self.driverId = avId + fieldList = ['setComponentL', + 'setComponentX', + 'setComponentY', + 'setComponentZ', + 'setComponentH', + 'setComponentP', + 'setComponentR', + 'setComponentT', + 'setSmStop', + 'setSmH', + 'setSmZ', + 'setSmXY', + 'setSmXZ', + 'setSmPos', + 'setSmHpr', + 'setSmXYH', + 'setSmXYZH', + 'setSmPosHpr', + 'setSmPosHprL', + 'clearSmoothing', + 'suggestResync', + 'returnResync'] + self.air.setAllowClientSend(avId, self, fieldNameList=fieldList) + self.d_setState('C', self.driverId) + + def exitControlled(self): + pass + + def __handleUnexpectedExit(self): + self.notify.warning('toon: %d exited unexpectedly, resetting vehicle %d' % (self.driverId, self.doId)) + self.request('Parked') + self.requestDelete() + + def getBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def getBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def getAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def getEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def getSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def getFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def getBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def getRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def getDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def getOwner(self): + return self.ownerId diff --git a/toontown/racing/DistributedViewPad.py b/toontown/racing/DistributedViewPad.py new file mode 100644 index 0000000..e7da249 --- /dev/null +++ b/toontown/racing/DistributedViewPad.py @@ -0,0 +1,28 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import * +from direct.task import Task +from pandac.PandaModules import * +from toontown.racing.DistributedKartPad import DistributedKartPad +from toontown.racing.KartShopGlobals import KartGlobals +if (__debug__): + import pdb + +class DistributedViewPad(DistributedKartPad): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedViewPad') + id = 0 + + def __init__(self, cr): + DistributedKartPad.__init__(self, cr) + self.id = DistributedViewPad.id + DistributedViewPad.id += 1 + + def setLastEntered(self, timeStamp): + self.timeStamp = timeStamp + + def getTimestamp(self, avId): + return self.timeStamp + + def addStartingBlock(self, block): + block.cameraPos = Point3(0, 23, 7) + block.cameraHpr = Point3(180, -10, 0) + DistributedKartPad.addStartingBlock(self, block) diff --git a/toontown/racing/DroppedGag.py b/toontown/racing/DroppedGag.py new file mode 100644 index 0000000..9644645 --- /dev/null +++ b/toontown/racing/DroppedGag.py @@ -0,0 +1,24 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from otp.avatar import ShadowCaster + +class DroppedGag(NodePath, ShadowCaster.ShadowCaster): + + def __init__(self, name, geom): + NodePath.__init__(self, name) + ShadowCaster.ShadowCaster.__init__(self, False) + self.gag = geom.copyTo(self) + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setScale(1) + + def delete(self): + ShadowCaster.ShadowCaster.delete(self) + NodePath.removeNode(self) + self.gag = None + return + + def getGeomNode(self): + return self.gag diff --git a/toontown/racing/EffectManager.py b/toontown/racing/EffectManager.py new file mode 100644 index 0000000..3cc2e49 --- /dev/null +++ b/toontown/racing/EffectManager.py @@ -0,0 +1,43 @@ +from pandac.PandaModules import * +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from toontown.battle import MovieUtil + +class EffectManager(DirectObject): + + def __init__(self): + self.effectList = [] + + def delete(self): + for effect in effectList: + self.__removeEffect(effect) + + def addSplatEffect(self, spawner, splatName = 'splat-creampie', time = 1, size = 6, parent = render): + splat = globalPropPool.getProp(splatName) + splatSeq = Sequence() + splatType = globalPropPool.getPropType(splatName) + splatShow = Func(self.__showProp, splat, size, parent, spawner.getPos(parent)) + splatAnim = ActorInterval(splat, splatName) + splatHide = Func(MovieUtil.removeProp, splat) + splatRemove = Func(self.__removeEffect, splatSeq) + splatSeq.append(splatShow) + splatSeq.append(splatAnim) + splatSeq.append(splatHide) + splatSeq.append(splatRemove) + splatSeq.start() + self.effectList.append(splatSeq) + + def __showProp(self, prop, size, parent, pos): + prop.reparentTo(parent) + prop.setScale(size) + prop.setBillboardPointEye() + prop.setPos(pos + Vec3(0, 0, size / 2)) + + def __removeEffect(self, effect): + if effect.isPlaying(): + effect.finish() + self.effectList.remove(effect) + effect = None + return diff --git a/toontown/racing/FlyingGag.py b/toontown/racing/FlyingGag.py new file mode 100644 index 0000000..0ca3b85 --- /dev/null +++ b/toontown/racing/FlyingGag.py @@ -0,0 +1,35 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from otp.avatar.ShadowCaster import ShadowCaster + +class FlyingGag(NodePath, ShadowCaster): + + def __init__(self, name, geom = None): + an = ActorNode('flyingGagAN') + NodePath.__init__(self, an) + self.actorNode = an + self.gag = None + self.gagNode = None + ShadowCaster.__init__(self, False) + if geom: + self.gagNode = self.attachNewNode('PieNode') + self.gag = geom.copyTo(self.gagNode) + self.gag.setScale(3) + self.gagNode.setHpr(0, -45, 0) + self.gagNode.setPos(0, 0, 2) + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setPos(0, 0, 2) + self.dropShadow.setScale(3) + return + + def delete(self): + ShadowCaster.delete(self) + NodePath.remove(self) + self.gag = None + return + + def getGeomNode(self): + return self.gag diff --git a/toontown/racing/Kart.py b/toontown/racing/Kart.py new file mode 100644 index 0000000..2bf2519 --- /dev/null +++ b/toontown/racing/Kart.py @@ -0,0 +1,537 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from otp.avatar import ShadowCaster +from toontown.racing.KartDNA import * +from toontown.toonbase import TTLocalizer + +class Kart(NodePath, ShadowCaster.ShadowCaster): + notify = DirectNotifyGlobal.directNotify.newCategory('Kart') + index = 0 + baseScale = 2.0 + RFWHEEL = 0 + LFWHEEL = 1 + RRWHEEL = 2 + LRWHEEL = 3 + wheelData = [{'node': 'wheel*Node2'}, + {'node': 'wheel*Node1'}, + {'node': 'wheel*Node3'}, + {'node': 'wheel*Node4'}] + ShadowScale = 2.5 + SFX_BaseDir = 'phase_6/audio/sfx/' + SFX_KartStart = SFX_BaseDir + 'KART_Engine_start_%d.mp3' + SFX_KartLoop = SFX_BaseDir + 'KART_Engine_loop_%d.wav' + + def __init__(self): + NodePath.__init__(self) + an = ActorNode('vehicle-test') + anp = NodePath(an) + NodePath.assign(self, anp) + self.actorNode = an + ShadowCaster.ShadowCaster.__init__(self, False) + Kart.index += 1 + self.updateFields = [] + self.kartDNA = [-1] * getNumFields() + self.kartAccessories = {KartDNA.ebType: None, + KartDNA.spType: None, + KartDNA.fwwType: (None, None), + KartDNA.bwwType: (None, None)} + self.texCount = 1 + return + + def delete(self): + self.__stopWheelSpin() + del self.kartDNA + del self.updateFields + self.kartLoopSfx.stop() + NodePath.removeNode(self) + ShadowCaster.ShadowCaster.delete(self) + + def getKartBounds(self): + return self.geom[0].getTightBounds() + + def generateKart(self, forGui = 0): + self.LODnode = FadeLODNode('lodNode') + self.LODpath = self.attachNewNode(self.LODnode) + self.LODnode.setFadeTime(0.15) + self.geom = {} + self.pitchNode = {} + self.toonNode = {} + self.rotateNode = self.attachNewNode('rotate') + levelIn = [base.config.GetInt('lod1-in', 30), base.config.GetInt('lod2-in', 80), base.config.GetInt('lod2-in', 200)] + levelOut = [base.config.GetInt('lod1-out', 0), base.config.GetInt('lod2-out', 30), base.config.GetInt('lod2-out', 80)] + lodRequired = 3 + if forGui: + lodRequired = 1 + levelIn[0] = base.config.GetInt('lod1-in', 2500) + levelIn[1] = base.config.GetInt('lod1-out', 0) + self.toonSeat = NodePath('toonSeat') + for level in range(lodRequired): + self.__createLODKart(level) + self.LODnode.addSwitch(levelIn[level], levelOut[level]) + + self.setScale(self.baseScale) + self.flattenMedium() + for level in range(lodRequired): + self.toonSeat = self.toonSeat.instanceTo(self.toonNode[level]) + + self.LODpath.reparentTo(self.rotateNode) + tempNode = NodePath('tempNode') + self.accGeomScale = tempNode.getScale(self.pitchNode[0]) * self.baseScale + tempNode.removeNode() + self.__applyBodyColor() + self.__applyEngineBlock() + self.__applySpoiler() + self.__applyFrontWheelWells() + self.__applyBackWheelWells() + self.__applyRims() + self.__applyDecals() + self.__applyAccessoryColor() + self.wheelCenters = [] + self.wheelBases = [] + for wheel in self.wheelData: + center = self.geom[0].find('**/' + wheel['node']) + self.wheelCenters.append(center) + wheelBase = center.getParent().attachNewNode('wheelBase') + wheelBase.setPos(center.getPos()) + wheelBase.setZ(0) + self.wheelBases.append(wheelBase) + + self.wheelBaseH = self.wheelCenters[0].getH() + self.__startWheelSpin() + self.setWheelSpinSpeed(0) + if not forGui: + self.shadowJoint = self.geom[0] + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setScale(self.ShadowScale) + else: + self.shadowJoint = self.LODpath + self.initializeDropShadow() + self.setActiveShadow() + self.dropShadow.setScale(1.3, 3, 1) + kartType = self.kartDNA[KartDNA.bodyType] + self.kartStartSfx = base.loadSfx(self.SFX_KartStart % kartType) + self.kartLoopSfx = base.loadSfx(self.SFX_KartLoop % kartType) + self.kartLoopSfx.setLoop() + + def __createLODKart(self, level): + kartBodyPath = getKartModelPath(self.kartDNA[KartDNA.bodyType], level) + self.geom[level] = loader.loadModel(kartBodyPath) + self.geom[level].reparentTo(self.LODpath) + self.geom[level].setH(180) + self.geom[level].setPos(0.0, 0, 0.025) + self.pitchNode[level] = self.geom[level].find('**/suspensionNode') + self.toonNode[level] = self.geom[level].find('**/toonNode') + scale = 1.0 / self.pitchNode[level].getScale()[0] + scale /= self.baseScale + self.toonNode[level].setScale(scale) + h = (180 + self.pitchNode[level].getH()) % 360 + self.toonNode[level].setH(h) + pos = Point3(0, -1.3, -7) + self.toonNode[level].setPos(pos) + + def resetGeomPos(self): + for level in self.geom.keys(): + self.geom[level].setPos(0, 0, 0.025) + + def __update(self): + for field in self.updateFields: + if field == KartDNA.bodyType: + if hasattr(self, 'geom'): + for kart in self.geom: + self.geom[kart].removeNode() + self.__createLODKart(kart) + self.geom[kart].reparentTo(self.rotateNode) + + self.__applyBodyColor() + self.__applyEngineBlock() + self.__applySpoiler() + self.__applyFrontWheelWells() + self.__applyRims() + self.__applyDecals() + self.__applyAccessoryColor() + else: + raise StandardError, 'Kart::__update - Has this method been called before generateKart?' + elif field == KartDNA.bodyColor: + self.__applyBodyColor() + elif field == KartDNA.accColor: + self.__applyAccessoryColor() + elif field == KartDNA.ebType: + if self.kartAccessories[KartDNA.ebType] != None: + name = self.kartAccessories[KartDNA.ebType].getName() + for key in self.geom.keys(): + self.geom[key].find('**/%s' % name).removeNode() + + self.kartAccessories[KartDNA.ebType].removeNode() + self.kartAccessories[KartDNA.ebType] = None + self.__applyEngineBlock() + elif field == KartDNA.spType: + if self.kartAccessories[KartDNA.spType] != None: + name = self.kartAccessories[KartDNA.spType].getName() + for key in self.geom.keys(): + self.geom[key].find('**/%s' % name).removeNode() + + self.kartAccessories[KartDNA.spType].removeNode() + self.kartAccessories[KartDNA.spType] = None + self.__applySpoiler() + elif field == KartDNA.fwwType: + if self.kartAccessories[KartDNA.fwwType] != (None, None): + left, right = self.kartAccessories[KartDNA.fwwType] + for key in self.geom.keys(): + self.geom[key].find('**/%s' % left.getName()).removeNode() + self.geom[key].find('**/%s' % right.getName()).removeNode() + + left.removeNode() + right.removeNode() + self.kartAccessories[KartDNA.fwwType] = (None, None) + self.__applyFrontWheelWells() + elif field == KartDNA.bwwType: + if self.kartAccessories[KartDNA.bwwType] != (None, None): + left, right = self.kartAccessories[KartDNA.bwwType] + for key in self.geom.keys(): + self.geom[key].find('**/%s' % left.getName()).removeNode() + self.geom[key].find('**/%s' % right.getName()).removeNode() + + left.removeNode() + right.removeNode() + self.kartAccessories[KartDNA.bwwType] = (None, None) + self.__applyBackWheelWells() + else: + if field == KartDNA.rimsType: + self.__applyRims() + elif field == KartDNA.decalType: + self.__applyDecals() + self.__applyAccessoryColor() + + self.updateFields = [] + return + + def updateDNAField(self, field, fieldValue): + if field == KartDNA.bodyType: + self.setBodyType(fieldValue) + elif field == KartDNA.bodyColor: + self.setBodyColor(fieldValue) + elif field == KartDNA.accColor: + self.setAccessoryColor(fieldValue) + elif field == KartDNA.ebType: + self.setEngineBlockType(fieldValue) + elif field == KartDNA.spType: + self.setSpoilerType(fieldValue) + elif field == KartDNA.fwwType: + self.setFrontWheelWellType(fieldValue) + elif field == KartDNA.bwwType: + self.setBackWheelWellType(fieldValue) + elif field == KartDNA.rimsType: + self.setRimType(fieldValue) + elif field == KartDNA.decalType: + self.setDecalType(fieldValue) + self.updateFields.append(field) + self.__update() + + def __applyBodyColor(self): + if self.kartDNA[KartDNA.bodyColor] == InvalidEntry: + bodyColor = getDefaultColor() + else: + bodyColor = getAccessory(self.kartDNA[KartDNA.bodyColor]) + for kart in self.geom: + kartBody = self.geom[kart].find('**/chasse') + kartBody.setColorScale(bodyColor) + + def __applyAccessoryColor(self): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + for kart in self.geom: + hoodDecal = self.geom[kart].find('**/hoodDecal') + rightSideDecal = self.geom[kart].find('**/rightSideDecal') + leftSideDecal = self.geom[kart].find('**/leftSideDecal') + hoodDecal.setColorScale(accColor) + rightSideDecal.setColorScale(accColor) + leftSideDecal.setColorScale(accColor) + + for type in [KartDNA.ebType, KartDNA.spType]: + model = self.kartAccessories.get(type, None) + if model != None and not model.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + model.find('**/vertex').setColorScale(accColor) + + for type in [KartDNA.fwwType, KartDNA.bwwType]: + lModel, rModel = self.kartAccessories.get(type, (None, None)) + if lModel != None and not lModel.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + lModel.find('**/vertex').setColorScale(accColor) + rModel.find('**/vertex').setColorScale(accColor) + + return + + def __applyEngineBlock(self): + ebType = self.kartDNA[KartDNA.ebType] + if ebType == InvalidEntry: + return + ebPath = getAccessory(ebType) + attachNode = getAccessoryAttachNode(ebType) + model = loader.loadModel(ebPath) + self.kartAccessories[KartDNA.ebType] = model + model.setScale(self.accGeomScale) + if not model.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + model.find('**/vertex').setColorScale(accColor) + for kart in self.geom: + engineBlockNode = self.geom[kart].find('**/%s' % attachNode) + model.setPos(engineBlockNode.getPos(self.pitchNode[kart])) + model.setHpr(engineBlockNode.getHpr(self.pitchNode[kart])) + model.instanceTo(self.pitchNode[kart]) + + def __applySpoiler(self): + spType = self.kartDNA[KartDNA.spType] + if spType == InvalidEntry: + return + spPath = getAccessory(spType) + attachNode = getAccessoryAttachNode(spType) + model = loader.loadModel(spPath) + self.kartAccessories[KartDNA.spType] = model + model.setScale(self.accGeomScale) + for kart in self.geom: + spoilerNode = self.geom[kart].find('**/%s' % attachNode) + model.setPos(spoilerNode.getPos(self.pitchNode[kart])) + model.setHpr(spoilerNode.getHpr(self.pitchNode[kart])) + model.instanceTo(self.pitchNode[kart]) + + def __applyRims(self): + if self.kartDNA[KartDNA.rimsType] == InvalidEntry: + rimTexPath = getAccessory(getDefaultRim()) + else: + rimTexPath = getAccessory(self.kartDNA[KartDNA.rimsType]) + rimTex = loader.loadTexture('%s.jpg' % rimTexPath, '%s_a.rgb' % rimTexPath) + for kart in self.geom: + leftFrontWheelRim = self.geom[kart].find('**/leftFrontWheelRim') + rightFrontWheelRim = self.geom[kart].find('**/rightFrontWheelRim') + leftRearWheelRim = self.geom[kart].find('**/leftRearWheelRim') + rightRearWheelRim = self.geom[kart].find('**/rightRearWheelRim') + rimTex.setMinfilter(Texture.FTLinearMipmapLinear) + leftFrontWheelRim.setTexture(rimTex, self.texCount) + rightFrontWheelRim.setTexture(rimTex, self.texCount) + leftRearWheelRim.setTexture(rimTex, self.texCount) + rightRearWheelRim.setTexture(rimTex, self.texCount) + + self.texCount += 1 + + def __applyFrontWheelWells(self): + fwwType = self.kartDNA[KartDNA.fwwType] + if fwwType == InvalidEntry: + return + fwwPath = getAccessory(fwwType) + attachNode = getAccessoryAttachNode(fwwType) + leftAttachNode = attachNode % 'left' + rightAttachNode = attachNode % 'right' + leftModel = loader.loadModel(fwwPath) + rightModel = loader.loadModel(fwwPath) + self.kartAccessories[KartDNA.fwwType] = (leftModel, rightModel) + if not leftModel.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + leftModel.find('**/vertex').setColorScale(accColor) + rightModel.find('**/vertex').setColorScale(accColor) + for kart in self.geom: + leftNode = self.geom[kart].find('**/%s' % leftAttachNode) + rightNode = self.geom[kart].find('**/%s' % rightAttachNode) + leftNodePath = leftModel.instanceTo(self.pitchNode[kart]) + leftNodePath.setPos(rightNode.getPos(self.pitchNode[kart])) + leftNodePath.setHpr(rightNode.getHpr(self.pitchNode[kart])) + leftNodePath.setScale(self.accGeomScale) + leftNodePath.setSx(-1.0 * leftNodePath.getSx()) + leftNodePath.setTwoSided(True) + rightNodePath = rightModel.instanceTo(self.pitchNode[kart]) + rightNodePath.setPos(leftNode.getPos(self.pitchNode[kart])) + rightNodePath.setHpr(leftNode.getHpr(self.pitchNode[kart])) + rightNodePath.setScale(self.accGeomScale) + + def __applyBackWheelWells(self): + bwwType = self.kartDNA[KartDNA.bwwType] + if bwwType == InvalidEntry: + return + bwwPath = getAccessory(bwwType) + attachNode = getAccessoryAttachNode(bwwType) + leftAttachNode = attachNode % 'left' + rightAttachNode = attachNode % 'right' + leftModel = loader.loadModel(bwwPath) + rightModel = loader.loadModel(bwwPath) + self.kartAccessories[KartDNA.bwwType] = (leftModel, rightModel) + if not leftModel.find('**/vertex').isEmpty(): + if self.kartDNA[KartDNA.accColor] == InvalidEntry: + accColor = getDefaultColor() + else: + accColor = getAccessory(self.kartDNA[KartDNA.accColor]) + leftModel.find('**/vertex').setColorScale(accColor) + rightModel.find('**/vertex').setColorScale(accColor) + for kart in self.geom: + leftNode = self.geom[kart].find('**/%s' % leftAttachNode) + rightNode = self.geom[kart].find('**/%s' % rightAttachNode) + leftNodePath = leftModel.instanceTo(self.pitchNode[kart]) + leftNodePath.setPos(rightNode.getPos(self.pitchNode[kart])) + leftNodePath.setHpr(rightNode.getHpr(self.pitchNode[kart])) + leftNodePath.setScale(self.accGeomScale) + leftNodePath.setSx(-1.0 * leftNodePath.getSx()) + leftNodePath.setTwoSided(True) + rightNodePath = rightModel.instanceTo(self.pitchNode[kart]) + rightNodePath.setPos(leftNode.getPos(self.pitchNode[kart])) + rightNodePath.setHpr(leftNode.getHpr(self.pitchNode[kart])) + rightNodePath.setScale(self.accGeomScale) + + def __applyDecals(self): + if self.kartDNA[KartDNA.decalType] != InvalidEntry: + decalId = getAccessory(self.kartDNA[KartDNA.decalType]) + kartDecal = getDecalId(self.kartDNA[KartDNA.bodyType]) + hoodDecalTex = loader.loadTexture('phase_6/maps/%s_HoodDecal_%s.jpg' % (kartDecal, decalId), 'phase_6/maps/%s_HoodDecal_%s_a.rgb' % (kartDecal, decalId)) + sideDecalTex = loader.loadTexture('phase_6/maps/%s_SideDecal_%s.jpg' % (kartDecal, decalId), 'phase_6/maps/%s_SideDecal_%s_a.rgb' % (kartDecal, decalId)) + hoodDecalTex.setMinfilter(Texture.FTLinearMipmapLinear) + sideDecalTex.setMinfilter(Texture.FTLinearMipmapLinear) + for kart in self.geom: + hoodDecal = self.geom[kart].find('**/hoodDecal') + rightSideDecal = self.geom[kart].find('**/rightSideDecal') + leftSideDecal = self.geom[kart].find('**/leftSideDecal') + hoodDecal.setTexture(hoodDecalTex, self.texCount) + rightSideDecal.setTexture(sideDecalTex, self.texCount) + leftSideDecal.setTexture(sideDecalTex, self.texCount) + hoodDecal.show() + rightSideDecal.show() + leftSideDecal.show() + + else: + for kart in self.geom: + hoodDecal = self.geom[kart].find('**/hoodDecal') + rightSideDecal = self.geom[kart].find('**/rightSideDecal') + leftSideDecal = self.geom[kart].find('**/leftSideDecal') + hoodDecal.hide() + rightSideDecal.hide() + leftSideDecal.hide() + + self.texCount += 1 + + def rollSuspension(self, roll): + for kart in self.pitchNode: + self.pitchNode[kart].setR(roll) + + def pitchSuspension(self, pitch): + for kart in self.pitchNode: + self.pitchNode[kart].setP(pitch) + + def getDNA(self): + return self.kartDNA + + def setDNA(self, dna): + if self.kartDNA != [-1] * getNumFields(): + for field in xrange(len(self.kartDNA)): + if dna[field] != self.kartDNA[field]: + self.updateDNAField(field, dna[field]) + + return + self.kartDNA = dna + + def setBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def getBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def setBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def getBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def setAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def getAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def setEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def getEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def setSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def getSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def setFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def getFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def setBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def getBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def setRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def getRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def setDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def getDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def getGeomNode(self): + return self.geom[0] + + def spinWheels(self, amount): + newSpin = (self.oldSpinAmount + amount) % 360 + for wheelNode in self.wheelCenters: + wheelNode.setP(newSpin) + + self.oldSpinAmount = newSpin + + def setWheelSpinSpeed(self, speed): + pass + + def __startWheelSpin(self): + self.oldSpinAmount = 0 + + def __stopWheelSpin(self): + pass + + def turnWheels(self, amount): + amount += self.wheelBaseH + node = self.wheelCenters[self.RFWHEEL] + node.setH(amount) + node = self.wheelCenters[self.LFWHEEL] + node.setH(amount) + + def generateEngineStartTrack(self): + length = self.kartStartSfx.length() + + def printVol(): + print self.kartLoopSfx.getVolume() + + track = Parallel(SoundInterval(self.kartStartSfx), Func(self.kartLoopSfx.play), LerpFunctionInterval(self.kartLoopSfx.setVolume, fromData=0, toData=0.4, duration=length)) + return Sequence(track, Func(printVol)) + + def generateEngineStopTrack(self, duration = 0): + track = Parallel(LerpFunctionInterval(self.kartLoopSfx.setVolume, fromData=0.4, toData=0, duration=duration)) + return track diff --git a/toontown/racing/KartDNA.py b/toontown/racing/KartDNA.py new file mode 100644 index 0000000..50455e5 --- /dev/null +++ b/toontown/racing/KartDNA.py @@ -0,0 +1,621 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import * +from KartShopGlobals import * +import types +if (__debug__): + import pdb +import copy +KartDNA = PythonUtil.Enum('bodyType, bodyColor, accColor, ebType, spType, fwwType, bwwType, rimsType, decalType') +InvalidEntry = -1 +KartInfo = PythonUtil.Enum('name, model, cost, viewDist, decalId, LODmodel1, LODmodel2') +AccInfo = PythonUtil.Enum('name, model, cost, texCard, attach') +kNames = TTLocalizer.KartDNA_KartNames +KartDict = {0: (kNames[0], + 'phase_6/models/karting/Kart1_Final', + 100, + 7.0, + 'kart1', + 'phase_6/models/karting/Kart1_LOD_Final', + 'phase_6/models/karting/Kart1_LOD_Final', + (Point3(1.5, 8.0, -0.5), Point3(1.5, 0.0, 2.0))), + 1: (kNames[1], + 'phase_6/models/karting/Kart2_Final', + 7500, + 7.0, + 'kart2', + 'phase_6/models/karting/Kart2_LOD2_Final', + 'phase_6/models/karting/Kart2_LOD3_Final', + (Point3(0.25, 7, -2), Point3(1.25, -3, 0))), + 2: (kNames[2], + 'phase_6/models/karting/Kart3_Final', + 2500, + 8.5, + 'kart3', + 'phase_6/models/karting/Kart3_Final_LOD2', + 'phase_6/models/karting/Kart3_Final_LOD3', + (Point3(1.25, 4.0, 1.0), Point3(1.25, -3.0, 2.5)))} +aNames = TTLocalizer.KartDNA_AccNames +AccessoryDict = {0: (aNames[1000], + 'phase_6/models/karting/accessory_frontMiddle_0', + 200, + 'accessory_frontMiddle_0_gui', + 'ebNode_0'), + 1: (aNames[1001], + 'phase_6/models/karting/accessory_frontMiddle_1', + 1250, + 'accessory_frontMiddle_1_gui', + 'ebNode_0'), + 2: (aNames[1002], + 'phase_6/models/karting/accessory_frontMiddle_2', + 450, + 'accessory_frontMiddle_2_gui', + 'ebNode_1'), + 3: (aNames[1003], + 'phase_6/models/karting/accessory_frontMiddle_3', + 5000, + 'accessory_frontMiddle_3_gui', + 'ebNode_1'), + 4: (aNames[1004], + 'phase_6/models/karting/accessory_frontMiddle_4', + 800, + 'accessory_frontMiddle_4_gui', + 'ebNode_0'), + 5: (aNames[1005], + 'phase_6/models/karting/accessory_frontMiddle_5', + 200, + 'accessory_frontMiddle_5_gui', + 'ebNode_0'), + 6: (aNames[1006], + 'phase_6/models/karting/accessory_frontMiddle_6', + 450, + 'accessory_frontMiddle_6_gui', + 'ebNode_0'), + 7: (aNames[1007], + 'phase_6/models/karting/accessory_frontMiddle_7', + 800, + 'accessory_frontMiddle_7_gui', + 'ebNode_0'), + 8: (aNames[1008], + 'phase_6/models/karting/accessory_frontMiddle_8', + 200, + 'accessory_frontMiddle_8_gui', + 'ebNode_1'), + 9: (aNames[1009], + 'phase_6/models/karting/accessory_frontMiddle_9', + 800, + 'accessory_frontMiddle_9_gui', + 'ebNode_1'), + 10: (aNames[1010], + 'phase_6/models/karting/accessory_frontMiddle_10', + 1250, + 'accessory_frontMiddle_10_gui', + 'ebNode_0'), + 11: (aNames[2000], + 'phase_6/models/karting/accessory_backMiddle_0', + 450, + 'accessory_backMiddle_0_gui', + 'spNode_1'), + 12: (aNames[2001], + 'phase_6/models/karting/accessory_backMiddle_1', + 200, + 'accessory_backMiddle_1_gui', + 'spNode_0'), + 13: (aNames[2002], + 'phase_6/models/karting/accessory_backMiddle_2', + 800, + 'accessory_backMiddle_2_gui', + 'spNode_1'), + 14: (aNames[2003], + 'phase_6/models/karting/accessory_backMiddle_3', + 1250, + 'accessory_backMiddle_3_gui', + 'spNode_1'), + 15: (aNames[2004], + 'phase_6/models/karting/accessory_backMiddle_4', + 5000, + 'accessory_backMiddle_4_gui', + 'spNode_1'), + 16: (aNames[2005], + 'phase_6/models/karting/accessory_backMiddle_5', + 800, + 'accessory_backMiddle_5_gui', + 'spNode_1'), + 17: (aNames[2006], + 'phase_6/models/karting/accessory_backMiddle_6', + 450, + 'accessory_backMiddle_6_gui', + 'spNode_0'), + 18: (aNames[2007], + 'phase_6/models/karting/accessory_backMiddle_7', + 1250, + 'accessory_backMiddle_7_gui', + 'spNode_1'), + 19: (aNames[3000], + 'phase_6/models/karting/accessory_front_ww_0', + 200, + 'accessory_front_ww_0_gui', + '%sFWWNode_0'), + 20: (aNames[3001], + 'phase_6/models/karting/accessory_front_ww_1', + 200, + 'accessory_front_ww_1_gui', + '%sFWWNode_1'), + 21: (aNames[3002], + 'phase_6/models/karting/accessory_front_ww_2', + 800, + 'accessory_front_ww_2_gui', + '%sFWWNode_2'), + 22: (aNames[3003], + 'phase_6/models/karting/accessory_front_ww_3', + 5000, + 'accessory_front_ww_3_gui', + '%sFWWNode_2'), + 23: (aNames[3004], + 'phase_6/models/karting/accessory_front_ww_4', + 1250, + 'accessory_front_ww_4_gui', + '%sFWWNode_2'), + 24: (aNames[3005], + 'phase_6/models/karting/accessory_front_ww_5', + 800, + 'accessory_front_ww_5_gui', + '%sFWWNode_1'), + 25: (aNames[3006], + 'phase_6/models/karting/accessory_front_ww_6', + 1250, + 'accessory_front_ww_6_gui', + '%sFWWNode_2'), + 26: (aNames[3007], + 'phase_6/models/karting/accessory_front_ww_7', + 450, + 'accessory_front_ww_7_gui', + '%sFWWNode_2'), + 27: (aNames[4000], + 'phase_6/models/karting/accessory_rear_ww_0', + 800, + 'accessory_rear_ww_0_gui', + '%sBWWNode_0'), + 28: (aNames[4001], + 'phase_6/models/karting/accessory_rear_ww_1', + 200, + 'accessory_rear_ww_1_gui', + '%sBWWNode_1'), + 29: (aNames[4002], + 'phase_6/models/karting/accessory_rear_ww_2', + 200, + 'accessory_rear_ww_2_gui', + '%sBWWNode_2'), + 30: (aNames[4003], + 'phase_6/models/karting/accessory_rear_ww_3', + 1250, + 'accessory_rear_ww_3_gui', + '%sBWWNode_0'), + 31: (aNames[4004], + 'phase_6/models/karting/accessory_rear_ww_4', + 200, + 'accessory_rear_ww_4_gui', + '%sBWWNode_2'), + 32: (aNames[4005], + 'phase_6/models/karting/accessory_rear_ww_5', + 800, + 'accessory_rear_ww_5_gui', + '%sBWWNode_2'), + 33: (aNames[4006], + 'phase_6/models/karting/accessory_rear_ww_6', + 450, + 'accessory_rear_ww_6_gui', + '%sBWWNode_2'), + 34: (aNames[4007], + 'phase_6/models/karting/accessory_rear_ww_7', + 5000, + 'accessory_rear_ww_7_gui', + '%sBWWNode_2'), + 35: (aNames[4008], + 'phase_6/models/karting/accessory_rear_ww_8', + 1250, + 'accessory_rear_ww_8_gui', + '%sBWWNode_0'), + 36: (aNames[4009], + 'phase_6/models/karting/accessory_rear_ww_9', + 1250, + 'accessory_rear_ww_9_gui', + '%sBWWNode_0'), + 37: (aNames[4010], + 'phase_6/models/karting/accessory_rear_ww_10', + 450, + 'accessory_rear_ww_10_gui', + '%sBWWNode_2'), + 38: (aNames[4011], + 'phase_6/models/karting/accessory_rear_ww_11', + 800, + 'accessory_rear_ww_11_gui', + '%sBWWNode_2'), + 39: (aNames[4012], + 'phase_6/models/karting/accessory_rear_ww_12', + 1250, + 'accessory_rear_ww_12_gui', + '%sBWWNode_2'), + 40: (aNames[5000], + 'phase_6/maps/kart_Rim_1', + InvalidEntry, + 'kart_Rim_1'), + 41: (aNames[5001], + 'phase_6/maps/kart_Rim_2', + 450, + 'kart_Rim_2'), + 42: (aNames[5002], + 'phase_6/maps/kart_Rim_3', + 100, + 'kart_Rim_3'), + 43: (aNames[5003], + 'phase_6/maps/kart_Rim_4', + 800, + 'kart_Rim_4'), + 44: (aNames[5004], + 'phase_6/maps/kart_Rim_5', + 100, + 'kart_Rim_5'), + 45: (aNames[5005], + 'phase_6/maps/kart_Rim_6', + 200, + 'kart_Rim_6'), + 46: (aNames[5006], + 'phase_6/maps/kart_Rim_7', + 200, + 'kart_Rim_7'), + 47: (aNames[5007], + 'phase_6/maps/kart_Rim_8', + 200, + 'kart_Rim_8'), + 48: (aNames[5008], + 'phase_6/maps/kart_Rim_9', + 200, + 'kart_Rim_9'), + 49: (aNames[5009], + 'phase_6/maps/kart_Rim_10', + 200, + 'kart_Rim_10'), + 50: (aNames[5010], + 'phase_6/maps/kart_Rim_11', + 200, + 'kart_Rim_11'), + 51: (aNames[5011], + 'phase_6/maps/kart_Rim_12', + 800, + 'kart_Rim_12'), + 52: (aNames[5012], + 'phase_6/maps/kart_Rim_13', + 450, + 'kart_Rim_13'), + 53: (aNames[5013], + 'phase_6/maps/kart_Rim_14', + 1250, + 'kart_Rim_14'), + 54: (aNames[5014], + 'phase_6/maps/kart_Rim_15', + 5000, + 'kart_Rim_15'), + 55: (aNames[6000], + 1, + 200, + '%s_SideDecal_1'), + 56: (aNames[6001], + 2, + 450, + '%s_SideDecal_2'), + 57: (aNames[6002], + 3, + 100, + '%s_SideDecal_3'), + 58: (aNames[6003], + 4, + 5000, + '%s_SideDecal_4'), + 59: (aNames[6004], + 5, + 200, + '%s_SideDecal_5'), + 60: (aNames[6005], + 6, + 200, + '%s_SideDecal_6'), + 61: (aNames[6006], + 7, + 450, + '%s_SideDecal_7'), + 62: (aNames[6007], + 8, + 100, + '%s_SideDecal_8'), + 63: (aNames[6008], + 9, + 1250, + '%s_SideDecal_9'), + 64: (aNames[6009], + 10, + 800, + '%s_SideDecal_10'), + 65: (aNames[7000], VBase4(0.8, 1.0, 0.5, 1.0), 100), + 66: (aNames[7001], VBase4(0.96875, 0.691406, 0.699219, 1.0), 100), + 67: (aNames[7002], VBase4(0.933594, 0.265625, 0.28125, 1.0), 2500), + 68: (aNames[7003], VBase4(0.863281, 0.40625, 0.417969, 1.0), 900), + 69: (aNames[7004], VBase4(0.710938, 0.234375, 0.4375, 1.0), 1600), + 70: (aNames[7005], VBase4(0.570312, 0.449219, 0.164062, 1.0), 1600), + 71: (aNames[7006], VBase4(0.640625, 0.355469, 0.269531, 1.0), 1600), + 72: (aNames[7007], VBase4(0.996094, 0.695312, 0.511719, 1.0), 100), + 73: (aNames[7008], VBase4(0.832031, 0.5, 0.296875, 1.0), 1600), + 74: (aNames[7009], VBase4(0.992188, 0.480469, 0.167969, 1.0), 1600), + 75: (aNames[7010], VBase4(0.996094, 0.898438, 0.320312, 1.0), 2500), + 76: (aNames[7011], VBase4(0.996094, 0.957031, 0.597656, 1.0), 900), + 77: (aNames[7012], VBase4(0.855469, 0.933594, 0.492188, 1.0), 900), + 78: (aNames[7013], VBase4(0.550781, 0.824219, 0.324219, 1.0), 900), + 79: (aNames[7014], VBase4(0.242188, 0.742188, 0.515625, 1.0), 1600), + 80: (aNames[7015], VBase4(0.304688, 0.96875, 0.402344, 1.0), 2500), + 81: (aNames[7016], VBase4(0.433594, 0.90625, 0.835938, 1.0), 900), + 82: (aNames[7017], VBase4(0.347656, 0.820312, 0.953125, 1.0), 900), + 83: (aNames[7018], VBase4(0.191406, 0.5625, 0.773438, 1.0), 1600), + 84: (aNames[7019], VBase4(0.558594, 0.589844, 0.875, 1.0), 900), + 85: (aNames[7020], VBase4(0.285156, 0.328125, 0.726562, 1.0), 2500), + 86: (aNames[7021], VBase4(0.460938, 0.378906, 0.824219, 1.0), 900), + 87: (aNames[7022], VBase4(0.546875, 0.28125, 0.75, 1.0), 1600), + 88: (aNames[7023], VBase4(0.726562, 0.472656, 0.859375, 1.0), 1600), + 89: (aNames[7024], VBase4(0.898438, 0.617188, 0.90625, 1.0), 1600), + 90: (aNames[7025], VBase4(0.7, 0.7, 0.8, 1.0), 2500), + 91: (aNames[7026], VBase4(0.3, 0.3, 0.35, 1.0), 10000)} +AccessoryTypeDict = {KartDNA.ebType: [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10], + KartDNA.spType: [11, + 12, + 13, + 14, + 15, + 16, + 17, + 18], + KartDNA.fwwType: [19, + 20, + 21, + 22, + 23, + 24, + 25, + 26], + KartDNA.bwwType: [27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39], + KartDNA.rimsType: [40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54], + KartDNA.decalType: [55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64], + KartDNA.bodyColor: [65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91]} +AccessoryTypeNameDict = [None, + 'KartShtikerBodyColor', + 'KartShtikerAccColor', + 'KartShtikerEngineBlock', + 'KartShtikerSpoiler', + 'KartShtikerFrontWheelWell', + 'KartShtikerBackWheelWell', + 'KartShtikerRim', + 'KartShtikerDecal'] + +def checkNumFieldsValidity(numFields): + return KartDNA.decalType == numFields - 1 + + +def checkKartFieldValidity(field): + if field < KartDNA.bodyType or field > KartDNA.decalType: + return 0 + return 1 + + +def getNumFields(): + return KartDNA.decalType + 1 + + +def getKartModelPath(kartType, lodLevel = 0): + if lodLevel == 1: + return KartDict.get(kartType)[KartInfo.LODmodel1] + if lodLevel == 2: + return KartDict.get(kartType)[KartInfo.LODmodel2] + return KartDict.get(kartType)[KartInfo.model] + + +def getKartViewDist(kartType): + return KartDict.get(kartType)[KartInfo.viewDist] + + +def getDecalId(kartType): + return KartDict.get(kartType)[KartInfo.decalId] + + +def getAccessory(accId): + return AccessoryDict.get(accId)[KartInfo.model] + + +def getAccessoryAttachNode(accId): + accInfo = AccessoryDict.get(accId) + if len(accInfo) == 5: + return accInfo[4] + return None + + +def getTexCardNode(accId): + accInfo = AccessoryDict.get(accId) + if len(accInfo) <= 5: + return accInfo[3] + return None + + +def checkKartDNAValidity(dna): + if not checkNumFieldsValidity(len(dna)): + return 0 + for field in xrange(len(dna)): + if field == KartDNA.bodyType: + if dna[field] not in KartDict.keys(): + return 0 + elif field == KartDNA.bodyColor or field == KartDNA.accColor: + accList = [InvalidEntry] + AccessoryTypeDict.get(KartDNA.bodyColor) + if dna[field] not in accList: + return 0 + else: + accList = [InvalidEntry] + AccessoryTypeDict.get(field) + if dna[field] not in accList: + return 0 + + return 1 + + +def getDefaultColor(): + return VBase4(1, 1, 1, 1) + + +def getDefaultRim(): + return AccessoryTypeDict[KartDNA.rimsType][0] + + +def getDefaultAccessory(category): + if category in [KartDNA.bodyColor, KartDNA.accColor]: + return getDefaultColor() + elif category == KartDNA.rimsType: + return getDefaultRim() + else: + return InvalidEntry + + +def getAccessoryItemList(accessoryType): + return [ AccessoryDict[itemId] for itemId in AccessoryTypeDict[accessoryType] ] + + +def getKartTypeInfo(type): + if type in KartDict.keys(): + return KartDict[type] + return InvalidEntry + + +def getAccessoryInfo(index): + if index in AccessoryDict.keys(): + return AccessoryDict[index] + return InvalidEntry + + +def getAccessoryType(accessoryId): + for key in AccessoryTypeDict.keys(): + if accessoryId in AccessoryTypeDict[key]: + return key + + return InvalidEntry + + +def getAccessoryDictFromOwned(accessoryOwnedList, pType = -1): + accessDict = copy.deepcopy(AccessoryTypeDict) + accessDict[KartDNA.rimsType].remove(getDefaultRim()) + for accOwnedId in accessoryOwnedList: + type = getAccessoryType(accOwnedId) + if type != InvalidEntry and accOwnedId in accessDict[type]: + accessDict[type].remove(accOwnedId) + + if pType != -1: + return accessDict[pType] + else: + return accessDict + + +def getAccessDictByType(accessoryOwnedList): + accessDict = {} + if type(accessoryOwnedList) == types.ListType: + for accOwnedId in accessoryOwnedList: + accType = getAccessoryType(accOwnedId) + if accType != InvalidEntry: + if not accessDict.has_key(accType): + accessDict[accType] = [] + accessDict[accType].append(accOwnedId) + + else: + print 'KartDNA: getAccessDictByType: bad accessory list: ', accessoryOwnedList + return accessDict + + +def getKartCost(kartID): + if KartDict.has_key(kartID): + return KartDict[kartID][KartInfo.cost] + else: + return 'key error' + + +def getAccCost(accID): + return AccessoryDict[accID][AccInfo.cost] + + +def getAccName(accID): + try: + return AccessoryDict[accID][AccInfo.name] + except: + return TTLocalizer.KartShtikerDefault diff --git a/toontown/racing/KartShopGlobals.py b/toontown/racing/KartShopGlobals.py new file mode 100644 index 0000000..e3037d7 --- /dev/null +++ b/toontown/racing/KartShopGlobals.py @@ -0,0 +1,28 @@ +from direct.showbase import PythonUtil + +class KartShopGlobals: + EVENTDICT = {'guiDone': 'guiDone', + 'returnKart': 'returnKart', + 'buyKart': 'buyAKart', + 'buyAccessory': 'buyAccessory'} + KARTCLERK_TIMER = 180 + MAX_KART_ACC = 16 + + +class KartGlobals: + ENTER_MOVIE = 1 + EXIT_MOVIE = 2 + COUNTDOWN_TIME = 30 + BOARDING_TIME = 10.0 + ENTER_RACE_TIME = 6.0 + ERROR_CODE = PythonUtil.Enum('success, eGeneric, eTickets, eBoardOver, eNoKart, eOccupied, eTrackClosed, eTooLate, eUnpaid') + FRONT_LEFT_SPOT = 0 + FRONT_RIGHT_SPOT = 1 + REAR_LEFT_SPOT = 2 + REAR_RIGHT_SPOT = 3 + PAD_GROUP_NUM = 4 + + def getPadLocation(padId): + return padId % KartGlobals.PAD_GROUP_NUM + + getPadLocation = staticmethod(getPadLocation) diff --git a/toontown/racing/KartShopGui.py b/toontown/racing/KartShopGui.py new file mode 100644 index 0000000..be26fe6 --- /dev/null +++ b/toontown/racing/KartShopGui.py @@ -0,0 +1,993 @@ +if __name__ == '__main__': + from direct.directbase import DirectStart +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject, PythonUtil +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownTimer +from KartShopGlobals import * +from toontown.racing.Kart import Kart +from toontown.shtiker.KartPage import KartViewer +from KartDNA import * +from toontown.toontowngui.TeaserPanel import TeaserPanel +if (__debug__): + import pdb +MENUS = PythonUtil.Enum('MainMenu, BuyKart, BuyAccessory, ReturnKart, ConfirmBuyAccessory, ConfirmBuyKart, BoughtKart, BoughtAccessory, TeaserPanel') +MM_OPTIONS = PythonUtil.Enum('Cancel, BuyAccessory, BuyKart', -1) +BK_OPTIONS = PythonUtil.Enum('Cancel, BuyKart', -1) +BA_OPTIONS = PythonUtil.Enum('Cancel, BuyAccessory', -1) +RK_OPTIONS = PythonUtil.Enum('Cancel, ReturnKart', -1) +CBK_OPTIONS = PythonUtil.Enum('Cancel, BuyKart', -1) +CBA_OPTIONS = PythonUtil.Enum('Cancel, BuyAccessory', -1) +BTK_OPTIONS = PythonUtil.Enum('Ok', -1) +BTA_OPTIONS = PythonUtil.Enum('Ok', -1) +KS_TEXT_SIZE_BIG = TTLocalizer.KSGtextSizeBig +KS_TEXT_SIZE_SMALL = TTLocalizer.KSGtextSizeSmall + +class KartShopGuiMgr(object, DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr') + + class MainMenuDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.MainMenuDlg') + + def __init__(self, doneEvent): + model = loader.loadModel('phase_6/models/gui/Kart_MainMenuPanel') + self.modelScale = 0.75 + DirectFrame.__init__(self, relief=None, state='normal', geom=model, text_scale=0.1, geom_scale=self.modelScale, pos=(0, 0, -.01), frameSize=(-1, 1, -1, 1)) + self.initialiseoptions(KartShopGuiMgr.MainMenuDlg) + self.cancelButton = DirectButton( + parent=self, + relief=None, + geom=model.find('**/CancelIcon'), + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [MM_OPTIONS.Cancel])) + self.buyKartButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyKartButtonUp'), model.find('**/BuyKartButtonDown'), model.find('**/BuyKartButtonRollover'), model.find('**/BuyKartButtonDisabled')), + scale=self.modelScale, + geom=model.find('**/BuyKartIcon'), + text=TTLocalizer.KartShop_BuyKart, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(-0.2, 0.34), + pressEffect=False, + command=lambda : messenger.send(doneEvent, [MM_OPTIONS.BuyKart])) + self.buyAccessoryButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyAccessoryButtonUp'), model.find('**/BuyAccessoryButtonDown'), model.find('**/BuyAccessoryButtonRollover'), model.find('**/BuyAccessoryButtonDisabled')), + geom=model.find('**/BuyAccessoryIcon'), + image3_color=Vec4(0.6, 0.6, 0.6, 1), + scale=self.modelScale, + text=TTLocalizer.KartShop_BuyAccessories, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(-0.1, 0.036), + pressEffect=False, + command=lambda : messenger.send(doneEvent, [MM_OPTIONS.BuyAccessory])) + self.updateButtons() + return + + def updateButtons(self): + if not base.localAvatar.hasKart(): + self.buyAccessoryButton['state'] = DGG.DISABLED + else: + self.buyAccessoryButton['state'] = DGG.NORMAL + + def show(self): + self.updateButtons() + DirectFrame.DirectFrame.show(self) + + class BuyKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.BuyKartDlg') + + def __init__(self, doneEvent): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BuyKartPanel') + self.unownedKartList = KartDict.keys() + if base.localAvatar.hasKart(): + k = base.localAvatar.getKartBodyType() + if k in self.unownedKartList: + self.unownedKartList.remove(k) + self.numKarts = len(self.unownedKartList) + self.curKart = 0 + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text_wordwrap=26, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(0, 0)) + self.initialiseoptions(KartShopGuiMgr.BuyKartDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.44, -0.55), + text_font=ToontownGlobals.getSignFont()) + self.buyKartButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyKartButtonUp'), model.find('**/BuyKartButtonDown'), model.find('**/BuyKartButtonRollover'), model.find('**/BuyKartButtonDisabled')), + scale=self.modelScale, + text=TTLocalizer.KartShop_BuyKart, + text_scale=KS_TEXT_SIZE_BIG, + text_pos=(0, -.534), + pressEffect=False, + command=lambda : messenger.send(doneEvent, [self.unownedKartList[self.curKart]])) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [BK_OPTIONS.Cancel])) + self.arrowLeftButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowLeftButtonUp'), model.find('**/ArrowLeftButtonDown'), model.find('**/ArrowLeftButtonRollover'), model.find('**/ArrowLeftButtonInactive')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleKartChange, + extraArgs=[-1]) + self.arrowRightButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowRightButtonUp'), model.find('**/ArrowRightButtonDown'), model.find('**/ArrowRightButtonRollover'), model.find('**/ArrowRightButtonInactive')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleKartChange, + extraArgs=[1]) + self.kartView = KartViewer([self.curKart, -1, -1, -1, -1, -1, -1, -1, -1], parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0.255, -.054)], ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (-.24, -.054)], (0, -.055)) + self.kartView.setBounds(-0.38, 0.38, 0.0035, 0.53) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.showKart() + self.initialize = True + model.removeNode() + return + + def showKart(self): + self.buyKartButton.configure(text=TTLocalizer.KartShop_BuyKart) + self.buyKartButton.configure(text_scale=KS_TEXT_SIZE_BIG) + if self.numKarts > 0: + info = getKartTypeInfo(self.unownedKartList[self.curKart]) + description = info[KartInfo.name] + cost = TTLocalizer.KartShop_Cost % info[KartInfo.cost] + self.kartDescription = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text=description, + text_pos=(0, -.29), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + textMayChange=True) + self.kartCost = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text=cost, + text_pos=(0, -.365), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + textMayChange=True) + self.buyKartButton['state'] = DGG.NORMAL + self.arrowRightButton['state'] = DGG.DISABLED + self.arrowLeftButton['state'] = DGG.DISABLED + if self.numKarts > self.curKart + 1: + self.arrowRightButton['state'] = DGG.NORMAL + if self.curKart > 0: + self.arrowLeftButton['state'] = DGG.NORMAL + if info[KartInfo.cost] > base.localAvatar.getTickets(): + self.buyKartButton['state'] = DGG.DISABLED + self.buyKartButton.configure(text_scale=KS_TEXT_SIZE_SMALL * 0.75) + self.buyKartButton.configure(text=TTLocalizer.KartShop_NotEnoughTickets) + self.kartCost.configure(text_fg=(0.95, 0, 0.0, 1.0)) + self.kartView.refresh([self.unownedKartList[self.curKart], -1, -1, -1, -1, -1, -1, -1, -1]) + self.kartView.show() + return + + def __handleKartChange(self, nDir): + self.curKart = (self.curKart + nDir) % self.numKarts + self.kartDescription.destroy() + self.kartCost.destroy() + self.showKart() + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class ReturnKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.ReturnKartDlg') + + def __init__(self, doneEvent): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/ReturnKartPanel') + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text=TTLocalizer.KartShop_ConfirmReturnKart, + text_wordwrap=11, + text_scale=KS_TEXT_SIZE_SMALL * 0.9, + text_pos=(0, -0.26)) + self.initialiseoptions(KartShopGuiMgr.ReturnKartDlg) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [RK_OPTIONS.Cancel])) + self.okButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), + geom=model.find('**/CheckIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [RK_OPTIONS.ReturnKart])) + oldDNA = list(base.localAvatar.getKartDNA()) + for d in range(len(oldDNA)): + if d == KartDNA.bodyType: + continue + else: + oldDNA[d] = InvalidEntry + + self.kartView = KartViewer(oldDNA, parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', [], [], None) + self.kartView.setBounds(-0.38, 0.38, -.04, 0.49) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.kartView.show() + model.removeNode() + self.initialize = True + return + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class BoughtKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.BoughtKartDlg') + + def __init__(self, doneEvent, kartID): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BoughtKartPanel') + kartInfo = getKartTypeInfo(kartID) + name = kartInfo[KartInfo.name] + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text=TTLocalizer.KartShop_ConfirmBoughtTitle, + text_wordwrap=26, + text_scale=KS_TEXT_SIZE_SMALL, + text_pos=(0, -0.26)) + self.initialiseoptions(KartShopGuiMgr.BoughtKartDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.43, -0.5), + text_font=ToontownGlobals.getSignFont()) + self.okButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), + geom=model.find('**/CheckIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [BTK_OPTIONS.Ok])) + self.kartView = KartViewer([kartID, -1, -1, -1, -1, -1, -1, -1, -1], parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', [], []) + self.kartView.setBounds(-0.38, 0.38, -.0425, 0.49) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.kartView.show() + model.removeNode() + self.initialize = True + return + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class ConfirmBuyKartDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.ConfirmBuyKartDlg') + + def __init__(self, doneEvent, kartNum): + self.kartNum = kartNum + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/ConfirmBuyKartPanel') + kartInfo = getKartTypeInfo(kartNum) + name = kartInfo[KartInfo.name] + cost = kartInfo[KartInfo.cost] + DirectFrame.__init__( + self, + relief=None, + state='normal', + geom=model, + geom_scale=self.modelScale, + frameSize=(-1, 1, -1, 1), + pos=(0, 0, -0.01), + text=TTLocalizer.KartShop_ConfirmBuy % (name, cost), + text_wordwrap=11, + text_scale=KS_TEXT_SIZE_SMALL, + text_pos=(0, -0.26)) + self.initialiseoptions(KartShopGuiMgr.ConfirmBuyKartDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.43, -0.5), + text_font=ToontownGlobals.getSignFont()) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [CBK_OPTIONS.Cancel])) + self.okButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), + geom=model.find('**/CheckIcon'), + scale=self.modelScale, + pressEffect=False, + command=lambda : messenger.send(doneEvent, [CBK_OPTIONS.BuyKart])) + self.kartView = KartViewer([self.kartNum, -1, -1, -1, -1, -1, -1, -1, -1], parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', [], [], None) + self.kartView.setBounds(-0.38, 0.38, -.0425, 0.49) + self.kartView.setBgColor(1.0, 1.0, 0.8, 1.0) + self.initialize = True + self.kartView.show() + model.removeNode() + return + + def destroy(self): + if self.initialize: + self.kartView.destroy() + DirectFrame.destroy(self) + + class BuyAccessoryDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.buyAccessoryDlg') + + def __init__(self, doneEvent): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BuyAccessoryPanel') + self.doneEvent = doneEvent + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=self.modelScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, -0.01), text_wordwrap=26, text_scale=0.1, text_fg=Vec4(0.36, 0.94, 0.93, 1.0), text_pos=(0, 0)) + self.initialiseoptions(KartShopGuiMgr.BuyAccessoryDlg) + self.ticketDisplay = DirectLabel( + parent=self, + relief=None, + text=str(base.localAvatar.getTickets()), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0.95, 0.95, 0.0, 1.0), + text_shadow=(0, 0, 0, 1), + text_pos=(0.42, -0.6), + text_font=ToontownGlobals.getSignFont()) + self.arrowLeftButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowLeftButtonUp'), model.find('**/ArrowLeftButtonDown'), model.find('**/ArrowLeftButtonRollover'), model.find('**/ArrowLeftButtonInactive')), + scale=self.modelScale, + text_pos=(0, 0), + text_scale=0.1, + pressEffect=False, + command=self.__handleAccessoryChange, + extraArgs=[-1]) + self.arrowRightButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/ArrowRightButtonUp'), model.find('**/ArrowRightButtonDown'), model.find('**/ArrowRightButtonRollover'), model.find('**/ArrowRightButtonInactive')), + scale=self.modelScale, + text_pos=(0, 0), + text_scale=0.1, + pressEffect=False, + command=self.__handleAccessoryChange, + extraArgs=[1]) + self.cancelButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), + geom=model.find('**/CancelIcon'), + scale=self.modelScale, + command=lambda : messenger.send(doneEvent, [BA_OPTIONS.Cancel]), + pressEffect=False) + self.decalAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/DecalButtonUp'), model.find('**/DecalButtonDown'), model.find('**/DecalButtonRollover'), model.find('**/DecalButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.decalType]) + self.spoilerAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/SpoilerButtonUp'), model.find('**/SpoilerButtonDown'), model.find('**/SpoilerButtonRollover'), model.find('**/SpoilerButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.spType]) + self.eBlockAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/EBlockButtonUp'), model.find('**/EBlockButtonDown'), model.find('**/EBlockButtonRollover'), model.find('**/EBlockButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.ebType]) + self.rearAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/RearButtonUp'), model.find('**/RearButtonDown'), model.find('**/RearButtonRollover'), model.find('**/RearButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.bwwType]) + self.frontAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/FrontButtonUp'), model.find('**/FrontButtonDown'), model.find('**/FrontButtonRollover'), model.find('**/FrontButtonDown')), + scale=self.modelScale, + text_pos=(0, 0), + text_scale=0.1, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.fwwType]) + self.rimAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/RimButtonUp'), model.find('**/RimButtonDown'), model.find('**/RimButtonRollover'), model.find('**/RimButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.rimsType]) + self.paintAccButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/PaintButtonUp'), model.find('**/PaintButtonDown'), model.find('**/PaintButtonRollover'), model.find('**/PaintButtonDown')), + scale=self.modelScale, + pressEffect=False, + command=self.__handleAccessoryTypeChange, + extraArgs=[KartDNA.bodyColor]) + self.accButtonsDict = { + KartDNA.ebType: self.eBlockAccButton, + KartDNA.spType: self.spoilerAccButton, + KartDNA.fwwType: self.frontAccButton, + KartDNA.bwwType: self.rearAccButton, + KartDNA.rimsType: self.rimAccButton, + KartDNA.decalType: self.decalAccButton, + KartDNA.bodyColor: self.paintAccButton} + self.buyAccessoryButton = DirectButton( + parent=self, + relief=None, + image=(model.find('**/BuyAccessoryButtonUp'), model.find('**/BuyAccessoryButtonDown'), model.find('**/BuyAccessoryButtonRollover'), model.find('**/BuyAccessoryButtonDisabled')), + scale=self.modelScale, + text=TTLocalizer.KartShop_BuyAccessory, + text_pos=(0, -.57), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + command=self.__handleBuyAccessory) + if not base.cr.isPaid(): + + def showTeaserPanel(): + TeaserPanel(pageName='kartingAccessories') + + self.buyAccessoryButton['command'] = showTeaserPanel + self.ownedAccList = base.localAvatar.getKartAccessoriesOwned() + while -1 in self.ownedAccList: + self.ownedAccList.remove(-1) + + self.unownedAccDict = getAccessoryDictFromOwned(self.ownedAccList) + self.curAccType = KartDNA.ebType + self.curAccIndex = {} + for type in self.unownedAccDict: + self.curAccIndex[type] = 0 + + self.kartView = KartViewer(list(base.localAvatar.getKartDNA()), parent=self) + self.kartView.setPos(model.find('**/KartViewerFrame').getPos()) + self.kartView.load(model, 'KartViewerFrame', ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0.255, 0)], ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (-.24, 0)], (0, 0)) + self.kartView.setBounds(-0.38, 0.38, 0.044, 0.58) + self.kartView.setBgColor(1.0, 1.0, 0.87, 1.0) + self.initialize = True + self.showAccessory() + model.removeNode() + return + + def __handleBuyAccessory(self): + accessoryID = self.unownedAccDict[self.curAccType][self.curAccIndex[self.curAccType]] + self.ownedAccList.append(accessoryID) + self.unownedAccDict = getAccessoryDictFromOwned(self.ownedAccList) + self.__handleAccessoryChange(0) + messenger.send(self.doneEvent, [accessoryID]) + + def __handleAccessoryChange(self, nDir): + if len(self.unownedAccDict[self.curAccType]) < 1: + self.curAccIndex[self.curAccType] = -1 + else: + self.curAccIndex[self.curAccType] = (self.curAccIndex[self.curAccType] + nDir) % len(self.unownedAccDict[self.curAccType]) + if hasattr(self, 'accDescription'): + self.accDescription.destroy() + self.accCost.destroy() + self.showAccessory() + + def __handleAccessoryTypeChange(self, type): + self.curAccType = type + try: + self.accDescription.destroy() + self.accCost.destroy() + except: + pass + + for b in self.accButtonsDict: + self.accButtonsDict[b]['state'] = DGG.NORMAL + + self.accButtonsDict[self.curAccType]['state'] = DGG.DISABLED + self.showAccessory() + + def showAccessory(self): + self.arrowRightButton['state'] = DGG.DISABLED + self.arrowLeftButton['state'] = DGG.DISABLED + self.buyAccessoryButton['state'] = DGG.DISABLED + self.accDescription = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text='', + text_pos=(0, -.33), + text_scale=KS_TEXT_SIZE_SMALL, + pressEffect=False, + text_wordwrap=TTLocalizer.KSGaccDescriptionWordwrap, + textMayChange=True) + self.buyAccessoryButton.configure(text_fg=(0, 0, 0.0, 1.0)) + self.buyAccessoryButton.configure(text=TTLocalizer.KartShop_BuyAccessory) + self.buyAccessoryButton.configure(text_scale=KS_TEXT_SIZE_SMALL) + self.buyAccessoryButton['state'] = DGG.NORMAL + if len(self.unownedAccDict[self.curAccType]) < 1: + self.kartView.setDNA(None) + self.kartView.hide() + self.accDescription.configure(text=TTLocalizer.KartShop_NoAvailableAcc) + self.buyAccessoryButton['state'] = DGG.DISABLED + else: + if self.curAccIndex[self.curAccType] + 1 < len(self.unownedAccDict[self.curAccType]): + self.arrowRightButton['state'] = DGG.NORMAL + if self.curAccIndex[self.curAccType] > 0: + self.arrowLeftButton['state'] = DGG.NORMAL + curDNA = None + curDNA = list(base.localAvatar.getKartDNA()) + for d in range(len(curDNA)): + if d == KartDNA.bodyType or d == KartDNA.accColor or d == KartDNA.bodyColor: + continue + else: + curDNA[d] = -1 + + curAcc = self.unownedAccDict[self.curAccType][self.curAccIndex[self.curAccType]] + curDNA[self.curAccType] = curAcc + self.kartView.refresh(curDNA) + self.accDescription.configure(text=AccessoryDict[curAcc][KartInfo.name]) + cost = TTLocalizer.KartShop_Cost % AccessoryDict[curAcc][KartInfo.cost] + self.accCost = DirectButton( + parent=self, + relief=None, + scale=self.modelScale, + text=cost, + text_pos=(0, -.4), + text_scale=KS_TEXT_SIZE_SMALL, + text_fg=(0, 0, 0.0, 1.0), + pressEffect=False, + textMayChange=True) + if AccessoryDict[curAcc][KartInfo.cost] > base.localAvatar.getTickets(): + self.buyAccessoryButton['state'] = DGG.DISABLED + self.buyAccessoryButton.configure(text_scale=KS_TEXT_SIZE_SMALL * 0.75) + self.buyAccessoryButton.configure(text=TTLocalizer.KartShop_NotEnoughTickets) + self.accCost.configure(text_fg=(0.95, 0, 0.0, 1.0)) + if len(base.localAvatar.getKartAccessoriesOwned()) >= KartShopGlobals.MAX_KART_ACC: + self.buyAccessoryButton['state'] = DGG.DISABLED + self.buyAccessoryButton.configure(text_fg=(0.95, 0, 0.0, 1.0)) + self.buyAccessoryButton.configure(text_scale=KS_TEXT_SIZE_SMALL * 0.8) + self.buyAccessoryButton.configure(text=TTLocalizer.KartShop_FullTrunk) + self.kartView.show() + return + + def destroy(self): + if self.initialize: + try: + self.accDescription.destroy() + except: + pass + + try: + self.kartView.destroy() + except: + pass + + DirectFrame.destroy(self) + + class BoughtAccessoryDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.BoughtAccessoryDlg') + + def __init__(self, doneEvent, accID): + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/BoughtAccessoryPanel') + accInfo = getAccessoryInfo(accID) + name = accInfo[AccInfo.name] + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=self.modelScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, -0.01), text=TTLocalizer.KartShop_ConfirmBoughtTitle, text_wordwrap=26, text_scale=KS_TEXT_SIZE_SMALL, text_pos=(0, -0.28)) + self.initialiseoptions(KartShopGuiMgr.BoughtAccessoryDlg) + self.ticketDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTickets()), text_scale=KS_TEXT_SIZE_SMALL, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_pos=(0.43, -0.5), text_font=ToontownGlobals.getSignFont()) + self.okButton = DirectButton(parent=self, relief=None, image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), geom=model.find('**/CheckIcon'), scale=self.modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [BTA_OPTIONS.Ok])) + self.kartView = DirectFrame(parent=self, relief=None, geom=model.find('**/KartViewerFrame'), scale=1.0) + bounds = self.kartView.getBounds() + radius = (bounds[3] - bounds[2]) / 2 + xCenter = self.kartView.getCenter()[0] + cm = CardMaker('accViewer') + cm.setFrame(xCenter - radius, xCenter + radius, bounds[2], bounds[3]) + self.kartView['geom'] = NodePath(cm.generate()) + self.kartView.component('geom0').setColorScale(1.0, 1.0, 0.8, 1.0) + self.kartView.component('geom0').setTransparency(True) + accType = getAccessoryType(accID) + texNodePath = None + tex = None + if accType in [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType]: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType == KartDNA.rimsType: + if accID == InvalidEntry: + texNodePath = getTexCardNode(getDefaultRim()) + else: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType in [KartDNA.bodyColor, KartDNA.accColor]: + tex = loader.loadTexture('phase_6/maps/Kartmenu_paintbucket.jpg', 'phase_6/maps/Kartmenu_paintbucket_a.rgb') + if accID == InvalidEntry: + self.kartView.component('geom0').setColorScale(getDefaultColor()) + else: + self.kartView.component('geom0').setColorScale(getAccessory(accID)) + elif accType == KartDNA.decalType: + kartDecal = getDecalId(base.localAvatar.getKartBodyType()) + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath % kartDecal, 'phase_6/maps/%s_a.rgb' % texNodePath % kartDecal) + else: + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + tex.setMinfilter(Texture.FTLinearMipmapLinear) + self.kartView.component('geom0').setTexture(tex, 1) + self.initialize = True + return + + def destroy(self): + if self.initialize: + DirectFrame.destroy(self) + + class ConfirmBuyAccessoryDlg(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartShopGuiMgr.ConfirmBuyAccessoryDlg') + + def __init__(self, doneEvent, accID): + self.accID = accID + self.modelScale = 1 + model = loader.loadModel('phase_6/models/gui/ConfirmBuyAccessory') + accInfo = getAccessoryInfo(accID) + cost = accInfo[AccInfo.cost] + name = accInfo[AccInfo.name] + DirectFrame.__init__(self, relief=None, state='normal', geom=model, geom_scale=self.modelScale, frameSize=(-1, 1, -1, 1), pos=(0, 0, -0.01), text=TTLocalizer.KartShop_ConfirmBuy % (name, cost), text_wordwrap=14, text_scale=KS_TEXT_SIZE_SMALL, text_pos=(0, -0.25)) + self.initialiseoptions(KartShopGuiMgr.ConfirmBuyAccessoryDlg) + self.ticketDisplay = DirectLabel(parent=self, relief=None, text=str(base.localAvatar.getTickets()), text_scale=KS_TEXT_SIZE_SMALL, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_pos=(0.43, -0.5), text_font=ToontownGlobals.getSignFont()) + self.cancelButton = DirectButton(parent=self, relief=None, image=(model.find('**/CancelButtonUp'), model.find('**/CancelButtonDown'), model.find('**/CancelButtonRollover')), geom=model.find('**/CancelIcon'), scale=self.modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [CBA_OPTIONS.Cancel])) + self.okButton = DirectButton(parent=self, relief=None, image=(model.find('**/CheckButtonUp'), model.find('**/CheckButtonDown'), model.find('**/CheckButtonRollover')), geom=model.find('**/CheckIcon'), scale=self.modelScale, pressEffect=False, command=lambda : messenger.send(doneEvent, [CBA_OPTIONS.BuyAccessory])) + self.kartView = DirectFrame(parent=self, relief=None, geom=model.find('**/KartViewerFrame'), scale=1.0) + bounds = self.kartView.getBounds() + radius = (bounds[3] - bounds[2]) / 3 + xCenter, yCenter = self.kartView.getCenter() + cm = CardMaker('accViewer') + cm.setFrame(xCenter - radius, xCenter + radius, yCenter - radius, yCenter + radius) + self.kartView['geom'] = NodePath(cm.generate()) + self.kartView.component('geom0').setColorScale(1.0, 1.0, 0.8, 1.0) + self.kartView.component('geom0').setTransparency(True) + accType = getAccessoryType(accID) + texNodePath = None + tex = None + if accType in [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType]: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType == KartDNA.rimsType: + if accID == InvalidEntry: + texNodePath = getTexCardNode(getDefaultRim()) + else: + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif accType in [KartDNA.bodyColor, KartDNA.accColor]: + tex = loader.loadTexture('phase_6/maps/Kartmenu_paintbucket.jpg', 'phase_6/maps/Kartmenu_paintbucket_a.rgb') + if accID == InvalidEntry: + self.kartView.component('geom0').setColorScale(getDefaultColor()) + else: + self.kartView.component('geom0').setColorScale(getAccessory(accID)) + elif accType == KartDNA.decalType: + kartDecal = getDecalId(base.localAvatar.getKartBodyType()) + texNodePath = getTexCardNode(accID) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath % kartDecal, 'phase_6/maps/%s_a.rgb' % texNodePath % kartDecal) + else: + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + tex.setMinfilter(Texture.FTLinearMipmapLinear) + self.kartView.component('geom0').setTexture(tex, 1) + self.initialize = True + return + + def destroy(self): + if self.initialize: + DirectFrame.destroy(self) + + def __init__(self, eventDict): + self.dialog = None + self.dialogStack = [] + self.eventDict = eventDict + self.dialogEventDict = {MENUS.MainMenu: ('MainMenuGuiDone', self.__handleMainMenuDlg, self.MainMenuDlg), + MENUS.BuyKart: ('BuyKartGuiDone', self.__handleBuyKartDlg, self.BuyKartDlg), + MENUS.BuyAccessory: ('BuyAccessoryGuiDone', self.__handleBuyAccessoryDlg, self.BuyAccessoryDlg), + MENUS.ReturnKart: ('ReturnKartGuiDone', self.__handleReturnKartDlg, self.ReturnKartDlg), + MENUS.ConfirmBuyKart: ('ConfirmBuyKartGuiDone', self.__handleConfirmBuyKartDlg, self.ConfirmBuyKartDlg), + MENUS.ConfirmBuyAccessory: ('ConfirmBuyAccessoryGuiDone', self.__handleConfirmBuyAccessoryDlg, self.ConfirmBuyAccessoryDlg), + MENUS.BoughtKart: ('BoughtKartGuiDone', self.__handleBoughtKartDlg, self.BoughtKartDlg), + MENUS.BoughtAccessory: ('BoughtAccessoryGuiDone', self.__handleBoughtAccessoryDlg, self.BoughtAccessoryDlg), + MENUS.TeaserPanel: ('UnpaidPurchaseAttempt', self.__handleTeaserPanelDlg, TeaserPanel)} + self.kartID = -1 + self.accID = -1 + self.timer = ToontownTimer.ToontownTimer() + self.timer.reparentTo(aspect2d) + self.timer.posInTopRightCorner() + self.timer.accept('RESET_KARTSHOP_TIMER', self.__resetTimer) + self.timer.countdown(KartShopGlobals.KARTCLERK_TIMER, self.__timerExpired) + self.__doDialog(MENUS.MainMenu) + return + + def __resetTimer(self): + if hasattr(self, 'timer') and self.timer: + self.timer.stop() + self.timer.countdown(KartShopGlobals.KARTCLERK_TIMER, self.__timerExpired) + + def __isActive(self, dlgName): + for d in self.dialogStack: + if d == dlgName: + return True + + return False + + def __timerExpired(self): + messenger.send(self.eventDict['guiDone'], [True]) + + def destroy(self): + self.__destroyDialog() + self.ignoreAll() + self.timer.destroy() + del self.timer + for event in self.dialogEventDict.values(): + self.ignore(event) + + self.dialogEventDict = None + return + + def __destroyDialog(self): + if hasattr(self, 'timer'): + self.ignoreAll() + if self.dialog != None: + self.dialog.destroy() + self.dialog = None + return + + def __removeDialog(self, dlgName = None): + if self.dialog != None: + if dlgName != None: + for d in self.dialogStack: + if d == dlgName: + self.dialogStack.remove(d) + + return + + def __popDialog(self): + if self.dialog != None: + if dlgName != None: + for d in self.dialogStack: + if d == dlgName: + self.dialogStack.remove(d) + + else: + d = self.dialogStack.pop() + event = self.dialogEventDict.get(d) + eventType = event[0] + self.ignore(eventType) + if self.dialogStack: + self.__doDialog(self.dialogStack[-1]) + return + + def __doLastMenu(self): + self.__doDialog(self.lastMenu) + + def __doDialog(self, dialogType): + self.__destroyDialog() + messenger.send('wakeup') + event = self.dialogEventDict.get(dialogType) + eventType = event[0] + eventHandler = event[1] + eventDlg = event[2] + self.acceptOnce(eventType, eventHandler) + if dialogType == MENUS.ConfirmBuyKart: + self.dialog = eventDlg(eventType, self.kartID) + elif dialogType == MENUS.BoughtKart: + self.dialog = eventDlg(eventType, self.kartID) + elif dialogType == MENUS.ConfirmBuyAccessory: + self.dialog = eventDlg(eventType, self.accID) + elif dialogType == MENUS.BoughtAccessory: + self.dialog = eventDlg(eventType, self.accID) + elif dialogType == MENUS.TeaserPanel: + self.dialog = eventDlg(pageName='karting', doneFunc=self.__doLastMenu) + else: + self.dialog = eventDlg(eventType) + if not dialogType == MENUS.TeaserPanel: + self.lastMenu = dialogType + + def __handleMainMenuDlg(self, exitType, args = []): + self.notify.debug('__handleMainMenuDlg: Handling MainMenu Dialog Selection.') + if exitType == MM_OPTIONS.Cancel: + messenger.send(self.eventDict['guiDone']) + elif exitType == MM_OPTIONS.BuyKart: + self.__doDialog(MENUS.BuyKart) + elif exitType == MM_OPTIONS.BuyAccessory: + self.__doDialog(MENUS.BuyAccessory) + + def __handleBoughtKartDlg(self, exitType): + self.notify.debug('__handleBoughtKartDlg: Telling the player their purchase was successful') + if not hasattr(base.localAvatar, 'kartPage'): + base.localAvatar.addKartPage() + self.kartID = -1 + self.__doDialog(MENUS.MainMenu) + + def __handleBoughtAccessoryDlg(self, exitType): + self.notify.debug('__handleBoughtAccessoryDlg: Telling the player their purchase was successful') + self.accID = -1 + self.__doDialog(MENUS.BuyAccessory) + + def __handleTeaserPanelDlg(self): + self.__doDialog(MENUS.TeaserPanel) + + def __handleBuyKartDlg(self, exitType, args = []): + self.notify.debug('__handleBuyKartDlg: Handling BuyKart Dialog Selection.') + if exitType == BK_OPTIONS.Cancel: + self.__doDialog(MENUS.MainMenu) + else: + self.kartID = exitType + if base.localAvatar.hasKart(): + self.__doDialog(MENUS.ReturnKart) + else: + self.__doDialog(MENUS.ConfirmBuyKart) + + def __handleBuyAccessoryDlg(self, exitType, args = []): + self.notify.debug('__handleBuyKartDlg: Handling BuyKart Dialog Selection.') + if exitType == BA_OPTIONS.Cancel: + self.__doDialog(MENUS.MainMenu) + else: + self.accID = exitType + self.__doDialog(MENUS.ConfirmBuyAccessory) + + def __handleReturnKartDlg(self, exitType, args = []): + self.notify.debug('__handleReturnKartDlg: Handling ReturnKart Dialog Selection.') + if exitType == RK_OPTIONS.Cancel: + self.__doDialog(MENUS.BuyKart) + elif exitType == RK_OPTIONS.ReturnKart: + self.__doDialog(MENUS.ConfirmBuyKart) + + def __handleConfirmBuyAccessoryDlg(self, exitType, args = []): + self.notify.debug('__handleConfirmBuyAccessoryDlg: Handling ConfirmBuyAccessory Dialog Selection.') + if exitType == CBA_OPTIONS.Cancel: + self.__doDialog(MENUS.BuyAccessory) + self.accID = -1 + elif exitType == CBA_OPTIONS.BuyAccessory: + if self.accID != -1: + messenger.send(self.eventDict['buyAccessory'], [self.accID]) + oldTickets = base.localAvatar.getTickets() + accInfo = getAccessoryInfo(self.accID) + cost = accInfo[AccInfo.cost] + base.localAvatar.setTickets(oldTickets - cost) + accList = base.localAvatar.getKartAccessoriesOwned() + accList.append(self.accID) + base.localAvatar.setKartAccessoriesOwned(accList) + self.accID = -1 + self.__doDialog(MENUS.BuyAccessory) + + def __handleConfirmBuyKartDlg(self, exitType, args = []): + self.notify.debug('__handleConfirmBuyKartDlg: Handling ConfirmBuyKart Dialog Selection.') + if exitType == CBK_OPTIONS.Cancel: + self.__doDialog(MENUS.BuyKart) + self.kartID = -1 + elif exitType == CBK_OPTIONS.BuyKart: + if self.kartID != -1: + messenger.send(self.eventDict['buyKart'], [self.kartID]) + self.__doDialog(MENUS.BoughtKart) + if __name__ == '__main__': + + class Main(DirectObject.DirectObject): + + def __init__(self): + self.acceptOnce('1', self.__popupKartShopGui) + self.accept(KartShopGlobals.EVENTDICT['buyAccessory'], self.__handleBuyAccessory) + self.accept(KartShopGlobals.EVENTDICT['buyKart'], self.__handleBuyKart) + + def __popupKartShopGui(self): + if not hasattr(self, 'kartShopGui') or self.kartShopGui == None: + self.acceptOnce(KartShopGlobals.EVENTDICT['guiDone'], self.__handleGuiDone) + self.kartShopGui = KartShopGuiMgr(KartShopGlobals.EVENTDICT) + return + + def __handleGuiDone(self, args = []): + if hasattr(self, 'kartShopGui') and self.kartShopGui != None: + self.ignoreAll() + self.kartShopGui.destroy() + del self.kartShopGui + self.acceptOnce('1', self.__popupKartShopGui) + return + + def __handleBuyAccessory(self, accID = -1): + requestAddOwnedAccessory(self, accID) + + def __handleBuyKart(self, kartID = -1): + pass + + m = Main() + run() diff --git a/toontown/racing/Piejectile.py b/toontown/racing/Piejectile.py new file mode 100644 index 0000000..5929a1e --- /dev/null +++ b/toontown/racing/Piejectile.py @@ -0,0 +1,246 @@ +import math +import random +from direct.showbase.PythonUtil import * +from direct.showbase.DirectObject import DirectObject +from direct.task import Task +from pandac.PandaModules import * +from direct.fsm import FSM +from direct.distributed import DistributedSmoothNode +from otp.avatar import ShadowCaster +from otp.otpbase import OTPGlobals +from toontown.racing.FlyingGag import FlyingGag +from toontown.battle import MovieUtil + +class Piejectile(DirectObject, FlyingGag): + physicsCalculationsPerSecond = 60 + maxPhysicsDt = 1.0 + physicsDt = 1.0 / float(physicsCalculationsPerSecond) + maxPhysicsFrames = maxPhysicsDt * physicsCalculationsPerSecond + + def __init__(self, sourceId, targetId, type, name): + FlyingGag.__init__(self, 'flyingGag', base.race.pie) + self.billboard = False + self.race = base.race + self.scale = 1 + self.imHitMult = 1 + self.wallCollideTrack = None + self.curSpeed = 0 + self.acceleration = 0 + self.count = 0 + self.name = name + self.physicsObj = None + self.ownerId = sourceId + self.targetId = targetId + self.ownerAv = None + self.ownerKart = None + self.hasTarget = 0 + self.deleting = 0 + self.d2t = 0 + self.lastD2t = 0 + self.startTime = globalClock.getFrameTime() + self.timeRatio = 0 + self.maxTime = 8 + self.rotH = randFloat(-360, 360) + self.rotP = randFloat(-90, 90) + self.rotR = randFloat(-90, 90) + print 'generating Pie %s' % self.name + self.ownerKart = base.cr.doId2do.get(base.race.kartMap.get(sourceId, None), None) + if targetId != 0: + self.targetKart = base.cr.doId2do.get(base.race.kartMap.get(targetId, None), None) + self.hasTarget = 1 + if self.ownerId == localAvatar.doId: + startPos = self.ownerKart.getPos(render) + else: + startPos = self.ownerKart.getPos(render) + self.setPos(startPos[0], startPos[1], startPos[2]) + self.__setupCollisions() + self.setupPhysics() + self.__enableCollisions() + self.forward = NodePath('forward') + self.forward.setPos(0, 1, 0) + self.splatTaskName = 'splatTask %s' % self.name + if self.hasTarget: + self.splatTask = taskMgr.doMethodLater(self.maxTime, self.splat, self.splatTaskName) + else: + self.splatTask = taskMgr.doMethodLater(self.maxTime / 2.5, self.splat, self.splatTaskName) + self.reparentTo(render) + return + + def delete(self): + print 'removing piejectile' + taskMgr.remove(self.taskName) + self.__undoCollisions() + self.physicsMgr.clearLinearForces() + FlyingGag.delete(self) + self.deleting = 1 + self.ignoreAll() + + def remove(self): + self.delete() + + def setAvId(self, avId): + self.avId = avId + + def setRace(self, doId): + self.race = base.cr.doId2do.get(doId) + + def setOwnerId(self, ownerId): + self.ownerId = ownerId + + def setType(self, type): + self.type = type + + def setPos(self, x, y, z): + DistributedSmoothNode.DistributedSmoothNode.setPos(self, x, y, z) + + def getVelocity(self): + return self.actorNode.getPhysicsObject().getVelocity() + + def setupPhysics(self): + self.physicsMgr = PhysicsManager() + self.physicsEpoch = globalClock.getFrameTime() + self.lastPhysicsFrame = 0 + integrator = LinearEulerIntegrator() + self.physicsMgr.attachLinearIntegrator(integrator) + fn = ForceNode('windResistance') + fnp = NodePath(fn) + fnp.reparentTo(render) + windResistance = LinearFrictionForce(0.2) + fn.addForce(windResistance) + self.physicsMgr.addLinearForce(windResistance) + self.windResistance = windResistance + fn = ForceNode('engine') + fnp = NodePath(fn) + fnp.reparentTo(self) + engine = LinearVectorForce(0, 0, 3) + fn.addForce(engine) + self.physicsMgr.addLinearForce(engine) + self.engine = engine + self.physicsMgr.attachPhysicalNode(self.node()) + self.physicsObj = self.actorNode.getPhysicsObject() + ownerAv = base.cr.doId2do[self.ownerId] + ownerVel = self.ownerKart.getVelocity() + ownerSpeed = ownerVel.length() + rotMat = Mat3.rotateMatNormaxis(self.ownerKart.getH(), Vec3.up()) + ownerHeading = rotMat.xform(Vec3.forward()) + throwSpeed = 50 + throwVel = ownerHeading * throwSpeed + throwVelCast = Vec3(throwVel[0], throwVel[1], throwVel[2] + 50) + self.actorNode.getPhysicsObject().setVelocity(self.ownerKart.getVelocity() + throwVelCast) + lookPoint = render.getRelativePoint(self.ownerKart, Point3(0, 10, 0)) + self.lookAt(lookPoint) + self.taskName = 'updatePhysics%s' % self.name + taskMgr.add(self.__updatePhysics, self.taskName, priority=25) + + def checkTargetDistance(self): + if self.hasTarget: + return self.getDistance(self.targetKart) + else: + return 0 + + def splatTarget(self): + if self.targetId == base.localAvatar.getDoId() and base.race.localKart: + base.race.localKart.splatPie() + self.race.effectManager.addSplatEffect(spawner=self.targetKart, parent=self.targetKart) + taskMgr.remove(self.splatTaskName) + self.remove() + + def splat(self, optional = None): + self.race.effectManager.addSplatEffect(spawner=self) + taskMgr.remove(self.splatTaskName) + self.remove() + + def __updatePhysics(self, task): + if self.deleting: + return Task.done + self.timeRatio = (globalClock.getFrameTime() - self.startTime) / self.maxTime + self.windResistance.setCoef(0.2 + 0.8 * self.timeRatio) + if base.cr.doId2do.get(self.targetId) == None: + self.hasTarget = 0 + self.lastD2t = self.d2t + self.d2t = self.checkTargetDistance() + if self.hasTarget: + targetDistance = self.d2t + distMax = 100 + if self.d2t > distMax: + targetDistance = distMax + targetVel = self.targetKart.getVelocity() + targetPos = self.targetKart.getPos() + targetAim = Point3(targetPos[0] + targetVel[0] * (targetDistance / distMax), targetPos[1] + targetVel[1] * (targetDistance / distMax), targetPos[2] + targetVel[2] * (targetDistance / distMax)) + self.lookAt(targetPos) + if self.d2t < 7 and self.hasTarget: + self.splatTarget() + return Task.done + self.count += 1 + dt = globalClock.getDt() + physicsFrame = int((globalClock.getFrameTime() - self.physicsEpoch) * self.physicsCalculationsPerSecond) + numFrames = min(physicsFrame - self.lastPhysicsFrame, self.maxPhysicsFrames) + self.lastPhysicsFrame = physicsFrame + if self.hasTarget: + targetVel = self.targetKart.getVelocity() + targetSpeed = targetVel.length() + if self.d2t - 10 * self.physicsDt > self.lastD2t: + self.engine.setVector(Vec3(0, 150 + 150 * self.timeRatio + targetSpeed * (1.0 + 1.0 * self.timeRatio) + self.d2t * (1.0 + 1.0 * self.timeRatio), 12)) + else: + self.engine.setVector(Vec3(0, 10 + 10 * self.timeRatio + targetSpeed * (0.5 + 0.5 * self.timeRatio) + self.d2t * (0.5 + 0.5 * self.timeRatio), 12)) + else: + self.engine.setVector(Vec3(0, 100, 3)) + for i in range(numFrames): + pitch = self.gagNode.getP() + self.gagNode.setP(pitch + self.rotH * self.physicsDt) + roll = self.gagNode.getR() + self.gagNode.setR(roll + self.rotP * self.physicsDt) + heading = self.gagNode.getH() + self.gagNode.setH(heading + self.rotR * self.physicsDt) + self.physicsMgr.doPhysics(self.physicsDt) + + if self.count % 60 == 0: + pass + return Task.cont + + def __setupCollisions(self): + self.cWallTrav = CollisionTraverser('ProjectileWall') + self.cWallTrav.setRespectPrevTransform(True) + self.collisionNode = CollisionNode(self.name) + self.collisionNode.setFromCollideMask(OTPGlobals.WallBitmask) + self.collisionNode.setIntoCollideMask(BitMask32.allOff()) + cs = CollisionSphere(0, 0, 0, 7) + self.collisionNode.addSolid(cs) + sC = self.attachNewNode(self.collisionNode) + self.collisionNodePath = NodePath(self.collisionNode) + self.wallHandler = CollisionHandlerPusher() + base.cTrav.addCollider(sC, self.wallHandler) + self.wallHandler.addCollider(self.collisionNodePath, self) + cRay = CollisionRay(0.0, 0.0, 40000.0, 0.0, 0.0, -1.0) + pieFloorRayName = 'pieFloorRay%s' % self.name + cRayNode = CollisionNode(pieFloorRayName) + cRayNode.addSolid(cRay) + cRayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + cRayNode.setIntoCollideMask(BitMask32.allOff()) + self.cRayNodePath = self.attachNewNode(cRayNode) + self.lifter = CollisionHandlerGravity() + self.lifter.setGravity(32.174 * 3.0) + self.lifter.setOffset(OTPGlobals.FloorOffset) + self.lifter.setReach(40.0) + self.lifter.addCollider(self.cRayNodePath, self) + base.cTrav.addCollider(self.cRayNodePath, self.lifter) + + def __undoCollisions(self): + base.cTrav.removeCollider(self.cRayNodePath) + + def __enableCollisions(self): + self.cQueue = [] + self.cRays = NodePath('stickProjectileToFloor') + self.cRays.reparentTo(self.gag) + x = self.gag.getX() + y = self.gag.getY() + rayNode = CollisionNode('floorcast') + ray = CollisionRay(x, y, 40000.0, 0.0, 0.0, -1.0) + rayNode.addSolid(ray) + rayNode.setFromCollideMask(OTPGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + rayNodePath = self.cRays.attachNewNode(rayNode) + cQueue = CollisionHandlerQueue() + self.cWallTrav.addCollider(rayNodePath, cQueue) + self.cQueue.append(cQueue) + self.collisionNodePath.reparentTo(self) diff --git a/toontown/racing/PiejectileManager.py b/toontown/racing/PiejectileManager.py new file mode 100644 index 0000000..93b7ed4 --- /dev/null +++ b/toontown/racing/PiejectileManager.py @@ -0,0 +1,28 @@ +from pandac.PandaModules import * +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from toontown.racing import Piejectile + +class PiejectileManager(DirectObject): + pieCounter = 0 + + def __init__(self): + self.piejectileList = [] + + def delete(self): + for piejectile in self.piejectileList: + self.__removePiejectile(piejectile) + + def addPiejectile(self, sourceId, targetId = 0, type = 0): + name = 'PiejectileManager Pie %s' % PiejectileManager.pieCounter + pie = Piejectile.Piejectile(sourceId, targetId, type, name) + self.piejectileList.append(pie) + PiejectileManager.pieCounter += 1 + + def __removePiejectile(self, piejectile): + self.piejectileList.remove(piejectile) + piejectile.delete() + piejectile = None + return diff --git a/toontown/racing/RaceEndPanels.py b/toontown/racing/RaceEndPanels.py new file mode 100644 index 0000000..0bc06a0 --- /dev/null +++ b/toontown/racing/RaceEndPanels.py @@ -0,0 +1,412 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from toontown.shtiker.KartPage import RacingTrophy +from toontown.racing import RaceGlobals + +class RaceResultsPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RaceEndPanels') + + def __init__(self, numRacers, race, raceEndPanel, *args, **kwargs): + opts = {'relief': None, + 'geom': DGG.getDefaultDialogGeom(), + 'geom_color': ToontownGlobals.GlobalDialogColor[:3] + (0.8,), + 'geom_scale': (1.75, 1, 0.75)} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(RaceResultsPanel) + self.entryList = [] + self.entryListSeqs = [] + self.pointSeqs = [] + self.numRacers = numRacers + base.resultsPanel = self + self.circuitFinishSeq = None + self.tickets = {} + self.race = race + self.raceEndPanel = raceEndPanel + self.pointsLabel = DirectLabel(parent=self, relief=None, pos=(0.7, 0, 0.3), text=TTLocalizer.KartRace_CircuitPoints, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPsmallLabel) + self.pointsLabel.hide() + self.rowFrame = [] + for x in range(self.numRacers): + frame = DirectFrame(parent=self, relief=None, pos=self.getRowPos(x)) + self.rowFrame.append(frame) + pLabel = DirectLabel(parent=frame, relief=None, pos=(0.0, 0.0, -0.01), text=`(x + 1)` + ' -', text_fg=(0.5, 0.5, 0.5, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + fFrame = DirectFrame(parent=frame, relief=None, pos=(0.1, -0.01, 0.01)) + nLabel = DirectLabel(parent=frame, relief=None, pos=(0.46, 0.0, 0.0), text='', text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ACenter, text_font=DGG.getDefaultFont()) + tLabel = DirectLabel(parent=frame, relief=None, pos=(0.9, 0.0, 0.0), text="--'--''--", text_fg=(0.5, 0.5, 0.5, 1.0), text_scale=TTLocalizer.REPsmallLabel, text_font=DGG.getDefaultFont()) + wLabel = DirectLabel(parent=frame, relief=None, pos=(1.14, 0.0, 0.0), text='', text_fg=(0, 0, 0, 1), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + cpLabel = DirectLabel(parent=frame, relief=None, pos=(1.4, 0.0, 0.0), text='', text_fg=(0, 0, 0, 1), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + ncpLabel = DirectLabel(parent=frame, relief=None, pos=(1.44, 0.0, 0.0), text='', text_fg=(1, 0, 0, 1), text_scale=TTLocalizer.REPsmallLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + self.entryList.append((pLabel, + fFrame, + nLabel, + tLabel, + wLabel, + cpLabel, + ncpLabel)) + + return + + def getRowPos(self, place): + return Point3(-0.72, -0.01, 0.25 - place * 0.18) + + def displayRacer(self, place, headFrame, name, time, qualify, tickets, bonus, trophies, circuitPoints, circuitTime): + self.notify.debug('displayRacer: place=%d name=%s tickets=%d bonus=%d' % (place, + name, + tickets, + bonus)) + self.tickets[place] = tickets + self.entryList[place - 1][0].configure(text_fg=(0.0, 0.0, 0.0, 1.0)) + if headFrame: + headFrame.reparentTo(self.entryList[place - 1][1]) + headFrame.setPos(0, -1.0, 0) + headFrame.setScale(0.25) + headFrame.show() + self.entryList[place - 1][2]['text'] = name + if len(name) > 24: + self.entryList[place - 1][2]['text_scale'] = 0.036 + else: + self.entryList[place - 1][2]['text_scale'] = 0.04 + minutes = int(time / 60) + time -= minutes * 60 + seconds = int(time) + padding = (seconds < 10 and ['0'] or [''])[0] + time -= seconds + fraction = str(time)[2:4] + fraction = fraction + '0' * (2 - len(fraction)) + timeStr = "%d'%s%d''%s" % (minutes, + padding, + seconds, + fraction) + self.entryList[place - 1][3].configure(text_fg=(0.0, 0.0, 0.0, 1.0)) + self.entryList[place - 1][3]['text'] = timeStr + + def flipText(flip, label = self.entryList[place - 1][3], timeStr = timeStr, recStr = TTLocalizer.KartRace_Record): + self.entryList[place - 1][3].configure(text_scale=0.06) + self.entryList[place - 1][3].configure(text_fg=(0.95, 0.0, 0.0, 1.0)) + if flip: + self.entryList[place - 1][3].configure(text_font=DGG.getDefaultFont()) + self.entryList[place - 1][3]['text'] = timeStr + else: + self.entryList[place - 1][3].configure(text_font=ToontownGlobals.getSignFont()) + self.entryList[place - 1][3]['text'] = recStr + + bonusSeq = Sequence() + if qualify and bonus: + qText = TTLocalizer.KartRace_Qualified + for i in range(1, 7): + bonusSeq.append(Func(flipText, 0, recStr=qText)) + bonusSeq.append(Wait(0.5)) + bonusSeq.append(Func(flipText, 1)) + bonusSeq.append(Wait(0.5)) + bonusSeq.append(Func(flipText, 0)) + bonusSeq.append(Wait(0.5)) + bonusSeq.append(Func(flipText, 1)) + bonusSeq.append(Wait(0.5)) + + elif qualify: + qText = TTLocalizer.KartRace_Qualified + for i in range(0, 12): + bonusSeq.append(Func(flipText, i % 2, recStr=qText)) + bonusSeq.append(Wait(0.5)) + + elif bonus: + for i in range(0, 12): + bonusSeq.append(Func(flipText, i % 2)) + bonusSeq.append(Wait(0.5)) + + if trophies: + DirectFrame(parent=headFrame, relief=None, image=loader.loadModel('phase_6/models/karting/trophy'), image_pos=(0.25, -1.01, -0.25), image_scale=0.25) + + def ticketTicker(t, label = self.entryList[place - 1][4], tickets = tickets): + label['text'] = TTLocalizer.KartRace_TicketPhrase % int(t * tickets) + + ticketSeq = LerpFunc(ticketTicker, duration=2) + displayPar = Parallel(bonusSeq, ticketSeq) + displayPar.start() + self.entryListSeqs.append(displayPar) + if not circuitPoints == []: + self.pointsLabel.show() + newPoints = circuitPoints[:].pop() + currentPoints = sum(circuitPoints[:-1]) + self.entryList[place - 1][5]['text'] = '%s' % currentPoints + self.entryList[place - 1][6]['text'] = ' + %s' % newPoints + + def totalPointTicker(t, label = self.entryList[place - 1][5], current = currentPoints, new = newPoints): + label['text'] = '%s' % int(currentPoints + t * new) + + def newPointTicker(t, label = self.entryList[place - 1][6], new = newPoints): + label['text'] = '+%s' % int(new - t * new) + + def endTicker(newLabel = self.entryList[place - 1][6]): + newLabel.hide() + + seq = Sequence(Wait(1), Parallel(LerpFunc(totalPointTicker, duration=1), LerpFunc(newPointTicker, duration=1)), Func(endTicker)) + self.pointSeqs.append(seq) + + def updateWinnings(self, place, newTotalTickets): + self.notify.debug('updateWinnings: self.tickets=%s place=%d newTotalTickets=%d' % (self.tickets, place, newTotalTickets)) + winnings = newTotalTickets - self.tickets[place] + if winnings != 0: + self.entryListSeqs[place - 1].finish() + newTickets = self.tickets[place] + winnings + + def ticketTicker(t, label = self.entryList[place - 1][4], tickets = newTickets): + label['text'] = TTLocalizer.KartRace_TicketPhrase % int(t * tickets) + + ticketSeq = LerpFunc(ticketTicker, duration=2) + ticketSeq.start() + self.entryListSeqs[place - 1] = ticketSeq + + def circuitFinished(self, placeFixup): + calcPointsSeq = Parallel() + for seq in self.pointSeqs: + calcPointsSeq.append(seq) + + shiftRacersSeq = Parallel() + for oldPlace, newPlace in placeFixup: + if oldPlace != newPlace: + + def fixPlaceValue(oldPlace = oldPlace, newPlace = newPlace): + self.entryList[oldPlace][0]['text'] = '%s -' % str(newPlace + 1) + + newPos = self.getRowPos(newPlace) + shiftRacersSeq.append(Parallel(Func(fixPlaceValue), LerpPosInterval(self.rowFrame[oldPlace], 1, newPos))) + + self.circuitFinishSeq = Sequence(calcPointsSeq, shiftRacersSeq) + if not len(self.race.circuitLoop) == 0: + self.notify.debug('Not the last race in a circuit, pressing next race in 30 secs') + self.circuitFinishSeq.append(Wait(30)) + self.circuitFinishSeq.append(Func(self.raceEndPanel.closeButtonPressed)) + self.circuitFinishSeq.start() + + def destroy(self): + for seq in self.entryListSeqs: + seq.finish() + del seq + + del self.entryListSeqs + if self.circuitFinishSeq: + self.circuitFinishSeq.finish() + del self.circuitFinishSeq + del self.entryList + del self.pointsLabel + DirectFrame.destroy(self) + + +class RaceWinningsPanel(DirectFrame): + + def __init__(self, race, *args, **kwargs): + opts = {'relief': None, + 'geom': DGG.getDefaultDialogGeom(), + 'geom_color': ToontownGlobals.GlobalDialogColor[:3] + (0.8,), + 'geom_scale': (1.75, 1, 0.75)} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(RaceWinningsPanel) + self.race = race + frame = DirectFrame(parent=self, relief=None, pos=(0, -0.01, 0)) + tFrame = DirectFrame(parent=frame, relief=None, pos=(0, 0, 0)) + tLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.0, 0.0, 0.25), text=TTLocalizer.KartRace_Tickets, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.1, text_font=DGG.getDefaultFont()) + DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, 0.1), text=TTLocalizer.KartRace_Deposit + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + dLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, 0.1), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, 0.0), text=TTLocalizer.KartRace_Winnings + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + wLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, 0.0), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, -0.1), text=TTLocalizer.KartRace_Bonus + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + bLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, -0.1), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + self.raceTotalLabel = DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, -0.2), text=TTLocalizer.KartRace_RaceTotal + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + self.circuitTotalLabel = DirectLabel(parent=tFrame, relief=None, pos=(TTLocalizer.REPtextPosX, 0.0, -0.2), text=TTLocalizer.KartRace_CircuitTotal + TTLocalizer.KartRace_Colon, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ALeft, text_font=DGG.getDefaultFont()) + self.doubleTicketsLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.5, 0.0, -0.26), text=TTLocalizer.KartRace_DoubleTickets, text_fg=(1.0, 0.125, 0.125, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ACenter, text_shadow=(0, 0, 0, 1), text_font=DGG.getDefaultFont()) + fLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.05, 0.0, -0.2), text=TTLocalizer.KartRace_Zero, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=TTLocalizer.REPlargeLabel, text_align=TextNode.ARight, text_font=DGG.getDefaultFont()) + ticketPic = DirectFrame(parent=tFrame, relief=None, image=loader.loadModel('phase_6/models/karting/tickets'), image_pos=(0.5, 0, -0.02), image_scale=0.4) + self.ticketFrame = tFrame + self.ticketComponents = (dLabel, + wLabel, + bLabel, + fLabel) + tFrame = DirectFrame(parent=frame, relief=None, pos=(0, 0, 0)) + tLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.0, 0.0, 0.25), text=TTLocalizer.KartRace_Bonus, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.1, text_font=DGG.getDefaultFont()) + textFrame = DirectFrame(parent=tFrame, relief=None, text='', text_scale=TTLocalizer.REPlargeLabel, text_font=DGG.getDefaultFont(), text_pos=(-0.3, 0.1, 0)) + bonusPic = DirectFrame(parent=tFrame, relief=None, image=loader.loadModel('phase_6/models/karting/tickets'), image_pos=(0.5, 0, -0.02), image_scale=0.4) + self.bonusFrame = tFrame + self.bonusComponents = (textFrame, bonusPic) + tFrame = DirectFrame(parent=frame, relief=None, pos=(0, 0, 0)) + tLabel = DirectLabel(parent=tFrame, relief=None, pos=(0.0, 0.0, 0.25), text=TTLocalizer.KartRace_Trophies, text_fg=(0.0, 0.0, 0.0, 1.0), text_scale=0.1, text_font=DGG.getDefaultFont()) + textFrame = DirectFrame(parent=tFrame, relief=None, text='', text_scale=TTLocalizer.REPlargeLabel, text_font=DGG.getDefaultFont(), text_pos=(-0.3, 0.1, 0)) + trophyPic = DirectFrame(parent=tFrame, relief=None) + self.trophyFrame = tFrame + self.trophyComponents = (textFrame, trophyPic) + self.ticketFrame.hide() + self.bonusFrame.hide() + self.trophyFrame.hide() + return + + def generateDisplaySequences(self, track, ticDeposit, ticWon, ticBonus, trophies, endOfCircuitRace = False): + + def ticketTicker(t, label, startTickets, endTickets): + label['text'] = str(int(t * endTickets + (1 - t) * startTickets)) + + def wrapStr(str = '', maxWidth = 10, font = DGG.getDefaultFont()): + textNode = TextNode('WidthChecker') + textNode.setFont(font) + tokens = str.split() + outStr = '' + lineStr = '' + tempStr = '' + while tokens: + if lineStr: + tempStr = ' '.join([lineStr, tokens[0]]) + else: + tempStr = tokens[0] + if textNode.calcWidth(tempStr) > maxWidth: + if not outStr: + outStr = lineStr + else: + outStr = '\n'.join([outStr, lineStr]) + lineStr = tokens.pop(0) + else: + lineStr = tempStr + tokens.pop(0) + + if lineStr: + if not outStr: + outStr = lineStr + else: + outStr = '\n'.join([outStr, lineStr]) + return outStr + + ticketSeq = Sequence() + origTicBonus = ticBonus + bonusType = None + if ticBonus > 0: + if not endOfCircuitRace: + bonusType = RaceGlobals.PeriodDict.values().index(ticBonus) + else: + ticBonus = 0 + if endOfCircuitRace: + self.circuitTotalLabel.unstash() + self.raceTotalLabel.stash() + else: + self.circuitTotalLabel.stash() + self.raceTotalLabel.unstash() + if ToontownGlobals.KARTING_TICKETS_HOLIDAY not in base.cr.newsManager.getHolidayIdList() or self.race.raceType != RaceGlobals.Practice: + self.doubleTicketsLabel.stash() + if ticBonus: + ticketSeq.append(Sequence(Func(self.ticketFrame.hide), Func(self.bonusFrame.show), Func(self.trophyFrame.hide), Func(self.bonusComponents[0].configure, text=wrapStr(TTLocalizer.KartRace_RecordString % (TTLocalizer.KartRecordStrings[bonusType], TTLocalizer.KartRace_TrackNames[track], str(ticBonus)))), Wait(3))) + ticketSeq.append(Sequence(Func(self.bonusFrame.hide), Func(self.trophyFrame.hide), Func(self.ticketFrame.show), Func(self.ticketComponents[3].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[0].configure, text_color=Vec4(1, 0, 0, 1)), Parallel(LerpFunc(ticketTicker, duration=(ticDeposit and [1] or [0])[0], extraArgs=[self.ticketComponents[0], 0, ticDeposit]), LerpFunc(ticketTicker, duration=(ticDeposit and [1] or [0])[0], extraArgs=[self.ticketComponents[3], 0, ticDeposit])), Func(self.ticketComponents[0].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[1].configure, text_color=Vec4(1, 0, 0, 1)), Parallel(LerpFunc(ticketTicker, duration=(ticWon and [1] or [0])[0], extraArgs=[self.ticketComponents[1], 0, ticWon]), LerpFunc(ticketTicker, duration=(ticWon and [1] or [0])[0], extraArgs=[self.ticketComponents[3], ticDeposit, ticDeposit + ticWon])), Func(self.ticketComponents[1].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[2].configure, text_color=Vec4(1, 0, 0, 1)), Parallel(LerpFunc(ticketTicker, duration=(origTicBonus and [1] or [0])[0], extraArgs=[self.ticketComponents[2], 0, origTicBonus]), LerpFunc(ticketTicker, duration=(origTicBonus and [1] or [0])[0], extraArgs=[self.ticketComponents[3], ticDeposit + ticWon, ticDeposit + ticWon + origTicBonus])), Func(self.ticketComponents[2].configure, text_color=Vec4(0, 0, 0, 1)), Func(self.ticketComponents[3].configure, text_color=Vec4(1, 0, 0, 1)))) + winningsSeq = Sequence(Func(self.ticketFrame.show), Func(self.bonusFrame.hide), Func(self.trophyFrame.hide), Wait(5)) + if ticBonus: + winningsSeq.append(Sequence(Func(self.ticketFrame.hide), Func(self.bonusFrame.show), Func(self.trophyFrame.hide), Wait(5))) + + def showCorrectTrophy(trophyId): + if hasattr(self, 'trophyImage'): + self.trophyImage.destroy() + self.trophyImage = RacingTrophy(level=trophyId, parent=self.trophyComponents[1], pos=(0.5, 0, -0.25)) + if trophyId == RaceGlobals.GrandTouring or trophyId == RaceGlobals.TotalQuals or trophyId == RaceGlobals.TotalWins: + scale = self.trophyImage.getScale() + scale = scale * 0.5 + self.trophyImage.setScale(scale) + + base.trop = self + if trophies: + winningsSeq.append(Sequence(Func(self.ticketFrame.hide), Func(self.bonusFrame.hide), Func(self.trophyFrame.show))) + for x in trophies: + winningsSeq.append(Sequence(Func(self.trophyComponents[0].configure, text=wrapStr(TTLocalizer.KartTrophyDescriptions[x])), Func(showCorrectTrophy, x), Wait(5))) + + return (ticketSeq, winningsSeq) + + +class RaceEndPanel(DirectFrame): + + def __init__(self, numRacers, race, *args, **kwargs): + opts = {'relief': None} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.initialiseoptions(RaceEndPanel) + self.enabled = False + self.race = race + self.results = RaceResultsPanel(numRacers, race, self, parent=self, pos=(0, 0, 0.525)) + self.winnings = RaceWinningsPanel(race, parent=self, pos=(0, 0, -0.525)) + if len(self.race.circuitLoop) == 0: + exitText = TTLocalizer.KartRace_Exit + else: + exitText = TTLocalizer.KartRace_NextRace + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(parent=self, image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=2.0, text=exitText, text_scale=TTLocalizer.REPsmallLabel, text_pos=(0, -0.1), text_fg=VBase4(1, 1, 1, 1), pos=(1.1, 0, -0.5), command=self.closeButtonPressed) + self.closeButton.hide() + self.disable() + return + + def destroy(self): + taskMgr.remove('showExitButton') + try: + if self.seq: + self.seq.pause() + self.seq = None + except AttributeError: + pass + + DirectFrame.destroy(self) + return + + def enable(self): + self.show() + self.enabled = True + + def disable(self): + self.hide() + self.enabled = False + + def closeButtonPressed(self): + messenger.send('leaveRace') + + def displayRacer(self, place, entryFee, qualify, winnings, track, bonus, trophies, headFrame, name, time, circuitPoints, circuitTime): + self.results.displayRacer(place, headFrame, name, time, qualify, entryFee + winnings + bonus, bonus, trophies, circuitPoints, circuitTime) + + def updateWinnings(self, place, winnings): + self.results.updateWinnings(place, winnings) + + def updateWinningsFromCircuit(self, place, entryFee, winnings, bonus, trophies = ()): + print 'updateWinningsFromCircuit' + self.seq.finish() + totalTickets = winnings + entryFee + bonus + self.results.updateWinnings(place, totalTickets) + self.startWinningsPanel(entryFee, winnings, 0, bonus, trophies, True) + + def startWinningsPanel(self, entryFee, winnings, track, bonus = None, trophies = (), endOfCircuitRace = False): + if not self.enabled: + return + taskMgr.remove('showExitButton') + try: + if self.seq: + self.seq.pause() + self.seq = None + except AttributeError: + pass + + tSeq, wSeq = self.winnings.generateDisplaySequences(track, entryFee, winnings, bonus, trophies, endOfCircuitRace) + + def showButton(s = self, w = wSeq): + s.seq.finish() + s.seq = Sequence(Func(self.closeButton.show), wSeq) + s.seq.loop() + + self.seq = Sequence(tSeq, wSeq) + if self.race.raceType != RaceGlobals.Circuit: + if self.seq.getDuration() < 5.0: + taskMgr.doMethodLater(5.0, showButton, 'showExitButton', extraArgs=[]) + else: + taskMgr.doMethodLater(self.seq.getDuration(), showButton, 'showExitButton', extraArgs=[]) + self.seq.start() + return + + def circuitFinished(self, placeFixup): + self.closeButton.show() + self.results.circuitFinished(placeFixup) diff --git a/toontown/racing/RaceGUI.py b/toontown/racing/RaceGUI.py new file mode 100644 index 0000000..0eb136c --- /dev/null +++ b/toontown/racing/RaceGUI.py @@ -0,0 +1,488 @@ +from pandac.PandaModules import * +from direct.showbase.PythonUtil import clampScalar +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectLabel import * +from direct.gui.DirectButton import * +from direct.showbase import BulletinBoardWatcher +from direct.interval.IntervalGlobal import * +from otp.otpbase import OTPGlobals +from direct.interval.IntervalGlobal import * +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.racing.KartDNA import InvalidEntry, getAccessory, getDefaultColor +from toontown.racing.RaceHeadFrame import RaceHeadFrame +from toontown.racing.RaceGag import RaceGag +from toontown.racing.RaceEndPanels import RaceEndPanel +from toontown.racing import RaceGlobals +from pandac.PandaModules import CardMaker, OrthographicLens, LineSegs +from direct.particles.ParticleEffect import * +from math import fmod +from math import sqrt +import time +import operator +from direct.gui.DirectGui import DirectFrame +import random + +class RaceGUI: + GagPie = 0 + gagRoot = 'phase_3.5/maps/inventory_' + + class RacerInfo: + + def __init__(self, face, mapSpot): + self.curvetime = 0 + self.maxlaphit = 0 + self.face = face + self.mapspot = mapSpot + self.place = 1 + self.enabled = True + self.finished = False + self.gag = None + return + + def update(self, curvetime = None, maxlaphit = None, faceX = None, mapspotPt = None, place = None, finished = None): + if self.enabled: + if not curvetime == None: + self.curvetime = curvetime + if not maxlaphit == None: + self.maxlaphit = maxlaphit + if not faceX == None: + self.face.setX(faceX) + if not mapspotPt == None: + self.mapspot.setPos(mapspotPt) + if not place == None: + self.place = place + if not finished == None: + self.finished = finished + return + + def disable(self): + self.enabled = False + if not self.finished: + self.face.hide() + self.mapspot.hide() + + def enable(self): + self.enabled = True + self.face.show() + self.mapspot.show() + + def __init__(self, distRace): + self.race = distRace + self.timerEnabled = False + self.maxLapHit = 0 + self.photoFinish = False + toonInteriorTextures = loader.loadModel('phase_3.5/models/modules/toon_interior_textures') + invTextures = loader.loadModel('phase_3.5/models/gui/inventory_icons') + racingTextures = loader.loadModel('phase_6/models/karting/racing_textures') + self.gagTextures = [toonInteriorTextures.find('**/couch'), + invTextures.find('**/inventory_bannana_peel'), + racingTextures.find('**/boost_arrow'), + invTextures.find('**/inventory_anvil'), + invTextures.find('**/inventory_creampie')] + self.gagTextures[1].setScale(7.5) + self.gagTextures[3].setScale(7.5) + self.gagTextures[4].setScale(7.5) + self.cardMaker = CardMaker('card') + self.racerDict = {} + self.render2dRoot = render2d.attachNewNode('RaceGuiRender2dRoot') + self.render2dRoot.setDepthWrite(1) + self.directObjList = [] + self.aspect2dRoot = aspect2d.attachNewNode('RaceGuiAspect2dRoot') + self.aspect2dRoot.setDepthWrite(1) + self.raceModeRoot = self.aspect2dRoot.attachNewNode('RaceModeRoot') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.closeButton = DirectButton(image=(gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), relief=None, scale=1.05, text=TTLocalizer.KartRace_Leave, text_scale=0.04, text_pos=(0, -0.07), text_fg=VBase4(1, 1, 1, 1), pos=(-0.99, 0, 0.925), command=self.race.leaveRace) + self.closeButton.reparentTo(self.aspect2dRoot) + self.directObjList.append(self.closeButton) + self.raceTimeDelta = 0 + self.raceModeReady = False + self.resultModeReady = False + self.gagCycleSound = base.loadSfx('phase_3.5/audio/sfx/tick_counter.mp3') + if hasattr(self.gagCycleSound, 'setPlayRate'): + self.gagCycleSound.setPlayRate(0.2) + self.gagCycleSound.setLoop(1) + self.gagAcquireSound = base.loadSfx('phase_6/audio/sfx/SZ_MM_gliss.mp3') + self.disable() + return + + def initRaceMode(self): + self.mapScene = self.raceModeRoot.attachNewNode('MapScene') + self.mapScene.setPos(1.1, 0, 0.75) + self.mapScene.setScale(0.25, 0.001, 0.25) + maxT = self.race.curve.getMaxT() + pt = Vec3(0, 0, 0) + ls = LineSegs('MapLines') + ls.setColor(1, 1, 1, 1) + ls.setThickness(2) + for x in range(101): + self.race.curve.getPoint(x / 100.0 * maxT, pt) + if x == 0: + ls.moveTo(pt[0], pt[1], pt[2]) + else: + ls.drawTo(pt[0], pt[1], pt[2]) + + self.mapLines = self.mapScene.attachNewNode(ls.create()) + self.mapLines.setScale(0.00025 * RaceGlobals.TrackDict[self.race.trackId][6]) + self.mapLines.setP(90) + self.faceStartPos = Vec3(-0.8, 0, 0.93) + self.faceEndPos = Vec3(0.8, 0, 0.93) + self.placeLabelNum = DirectLabel(relief=None, pos=TTLocalizer.RGUIplaceLabelNumPos, text='1', text_scale=0.35, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.placeLabelNum.reparentTo(self.raceModeRoot) + self.directObjList.append(self.placeLabelNum) + self.placeLabelStr = DirectLabel(relief=None, pos=TTLocalizer.RGUIplaceLabelStrPos, text=TTLocalizer.KartRace_FirstSuffix, text_scale=0.1, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.placeLabelStr.reparentTo(self.raceModeRoot) + self.directObjList.append(self.placeLabelStr) + self.lapLabel = DirectLabel(relief=None, pos=(1.1, 0, 0.45), text='1/' + str(self.race.lapCount), text_scale=0.1, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.lapLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(self.lapLabel) + self.photoFinishLabel = DirectLabel(relief=None, pos=(0, 0, -0.1), text=TTLocalizer.KartRace_PhotoFinish, text_scale=TTLocalizer.RGUIphotoFinish, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.photoFinishLabel.hide() + self.directObjList.append(self.photoFinishLabel) + self.wrongWayLabel = DirectLabel(relief=None, pos=(1.1, 0, 0.85), text=TTLocalizer.KartRace_WrongWay, text_scale=0.1, text_fg=(0.95, 0, 0, 1), text_font=ToontownGlobals.getSignFont()) + self.wrongWayLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(self.wrongWayLabel) + self.wrongWayLabel.setColorScale(Vec4(1, 1, 1, 0)) + self.wrongWaySeq = Sequence(self.wrongWayLabel.colorScaleInterval(0.25, colorScale=Vec4(1, 1, 1, 1), startColorScale=Vec4(1, 1, 1, 0)), self.wrongWayLabel.colorScaleInterval(0.25, colorScale=Vec4(1, 1, 1, 0), startColorScale=Vec4(1, 1, 1, 1))) + interpolateFacePos = lambda x: self.faceStartPos * (1.0 - x) + self.faceEndPos * x + self.timeLabels = [] + for x in range(self.race.lapCount): + minLabel = DirectLabel(relief=None, pos=(interpolateFacePos((2.0 * x + 1) / (self.race.lapCount * 2))[0] - 0.06, 0, 0.84), text="0'", text_scale=0.06, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ARight) + minLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(minLabel) + secLabel = DirectLabel(relief=None, pos=(interpolateFacePos((2.0 * x + 1) / (self.race.lapCount * 2))[0] + 0.06, 0, 0.84), text="00''", text_scale=0.06, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ARight) + secLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(secLabel) + fractionLabel = DirectLabel(relief=None, pos=(interpolateFacePos((2.0 * x + 1) / (self.race.lapCount * 2))[0] + 0.14, 0, 0.84), text='00', text_scale=0.06, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), text_align=TextNode.ARight) + fractionLabel.reparentTo(self.raceModeRoot) + self.directObjList.append(fractionLabel) + self.timeLabels.append((minLabel, secLabel, fractionLabel)) + + self.cardMaker.reset() + self.cardMaker.setName('GagIndicator') + self.cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5) + self.cardMaker.setColor(1, 1, 1, 1) + self.gagPanel = DirectFrame(parent=self.raceModeRoot, relief=None, image=loader.loadModel('phase_6/models/karting/gag_panel'), image_scale=0.25, pos=(-1.13, 0, -0.5)) + self.directObjList.append(self.gagPanel) + self.gag = self.gagPanel.attachNewNode('gag') + self.gag.setScale(0.2) + for gag in self.gagTextures: + gag.reparentTo(self.gag) + gag.hide() + + self.cardMaker.reset() + self.cardMaker.setName('RaceProgressLine') + self.cardMaker.setFrame(-0.5, 0.5, -0.5, 0.5) + line = self.raceModeRoot.attachNewNode(self.cardMaker.generate()) + line.setScale(self.faceEndPos[0] - self.faceStartPos[0], 1, 0.01) + line.setPos(0, 0, self.faceStartPos[2]) + self.cardMaker.setName('RaceProgressLineHash') + for n in range(self.race.lapCount + 1): + hash = self.raceModeRoot.attachNewNode(self.cardMaker.generate()) + hash.setScale(line.getScale()[2], 1, line.getScale()[2] * 5) + t = float(n) / self.race.lapCount + hash.setPos(self.faceStartPos[0] * (1 - t) + self.faceEndPos[0] * t, self.faceStartPos[1], self.faceStartPos[2]) + + self.raceModeReady = True + self.disable() + return + + def initResultMode(self): + self.endPanel = RaceEndPanel(len(self.race.avIds), self.race) + self.endPanel.reparentTo(self.aspect2dRoot) + self.directObjList.append(self.endPanel) + self.resultModeReady = True + self.disable() + + def showGag(self, gagIndex): + if gagIndex < len(self.gagTextures): + for gag in self.gagTextures: + gag.hide() + + self.gagTextures[gagIndex].show() + + def updateGag(self, gagIndex): + if self.gag: + if hasattr(self, 'gagCycleInterval'): + self.gagCycleInterval.finish() + del self.gagCycleInterval + self.gag.setHpr(0, 0, 0) + self.showGag(gagIndex) + if gagIndex == 0: + self.gag.hide() + else: + self.gag.show() + self.gagAcquireSound.play() + self.gagAcquireInterval = LerpHprInterval(self.gag, duration=0.5, blendType='easeOut', startHpr=Point3(0, -90, 0), hpr=Point3(0, 0, 0)) + self.gagAcquireInterval.start() + + def waitingOnGag(self, cycleTime): + if self.gag: + numTextures = len(self.gagTextures) + startOffset = random.choice(range(0, numTextures)) + self.gag.show() + self.gagCycleInterval = Parallel(LerpFunc(self.showNextGag, fromData=startOffset, toData=numTextures * 2 * cycleTime + startOffset, blendType='easeOut', duration=cycleTime), LerpHprInterval(self.gag, duration=cycleTime, hpr=Point3(0, 180 * numTextures * 2 * cycleTime - 90, 0), blendType='easeOut', startHpr=Point3(0, 0, 0)), SoundInterval(self.gagCycleSound, loop=1, duration=cycleTime, startTime=0), name='gagCycleInterval') + self.gagCycleInterval.start() + + def showNextGag(self, t): + if self.gag: + currGagIndex = int(t % (len(self.gagTextures) - 1)) + 1 + self.showGag(currGagIndex) + + def enableSpeedometer(self): + self.race.localKart.showSpeedometer() + + def disableSpeedometer(self): + self.race.localKart.hideSpeedometer() + + def disableRaceMode(self): + self.disableSpeedometer() + self.render2dRoot.hide() + self.raceModeRoot.hide() + for x in self.timeLabels: + for y in x: + y.hide() + + self.setTimerEnabled(False) + + def disableResultMode(self): + self.endPanel.disable() + + def disable(self): + self.closeButton.hide() + taskMgr.removeTasksMatching('clearRaceEndPanel') + if self.raceModeReady: + self.disableRaceMode() + if self.resultModeReady: + self.disableResultMode() + + def enableRaceMode(self): + self.enableSpeedometer() + self.render2dRoot.show() + self.raceModeRoot.show() + self.maxLapHit = min(self.maxLapHit, self.race.lapCount - 1) + for x in range(self.maxLapHit + 1): + for y in self.timeLabels[x]: + y.configure(text_font=ToontownGlobals.getSignFont()) + y.show() + + for y in self.timeLabels[self.maxLapHit]: + y.configure(text_font=ToontownGlobals.getSignFont()) + + def enableResultMode(self): + self.endPanel.enable() + if not self.race.circuitLoop: + taskMgr.doMethodLater(180, self.endPanel.closeButtonPressed, 'clearRaceEndPanel', extraArgs=[]) + + def destroy(self): + self.disable() + if hasattr(self, 'wrongWaySeq'): + self.wrongWaySeq.finish() + self.wrongWaySeq = None + taskMgr.removeTasksMatching('removeIt') + taskMgr.removeTasksMatching('removeCam*') + taskMgr.removeTasksMatching('clearRaceEndPanel') + for obj in self.directObjList: + obj.destroy() + + if hasattr(self, 'mapScene'): + self.mapScene.removeNode() + self.mapScene = None + self.aspect2dRoot.removeNode() + self.aspect2dRoot = None + self.raceModeRoot.removeNode() + self.raceModeRoot = None + self.render2dRoot.removeNode() + self.render2dRoot = None + self.closeButton = None + self.gag = None + self.lapLabel = None + self.timeLabels = None + self.placeLabelStr = None + self.placeLabelNum = None + self.photoFinishLabel = None + self.mapScene = None + self.race = None + return + + def setSpotAsymptotic(self, diffT, spot): + p = (-1, 1)[diffT > 0] * (1 - 1 / pow(abs(diffT) / self.cutoff + 1, 2)) + spot.setX(p) + + def setSpotRaceLinear(self, t, spot): + spot.setX(-1.0 + 2.0 * (t / self.lapCount)) + + def setSpotLapLinear(self, t, spot): + spot.setX(-1.0 + 2.0 * (t - int(t))) + + def update(self, time): + placeSorter = [] + placeCount = 0 + for key in self.racerDict.keys(): + racer = self.racerDict[key] + curvetime = racer.curvetime + face = racer.face + mapspot = racer.mapspot + maxlaphit = racer.maxlaphit + if not racer.finished and racer.enabled: + placeSorter.append((curvetime, key)) + if racer.finished or racer.enabled: + placeCount += 1 + pt = Vec3(0, 0, 0) + mapT = (curvetime % 1 + self.race.startT / self.race.curve.getMaxT()) % 1 * self.race.curve.getMaxT() + self.race.curve.getPoint(mapT, pt) + self.race.curve.getPoint(mapT % self.race.curve.getMaxT(), pt) + lapT = clampScalar(curvetime / self.race.lapCount, 0.0, 1.0) + faceX = self.faceStartPos[0] * (1 - lapT) + self.faceEndPos[0] * lapT + racer.update(faceX=faceX, mapspotPt=pt) + t = time - self.race.baseTime - self.raceTimeDelta + if key == localAvatar.doId: + if self.race.laps > maxlaphit: + racer.update(maxlaphit=self.race.laps) + self.maxLapHit = racer.maxlaphit + if self.maxLapHit < self.race.lapCount: + for y in self.timeLabels[self.maxLapHit - 1]: + y.configure(text_font=ToontownGlobals.getSignFont()) + + for y in self.timeLabels[self.maxLapHit]: + y.show() + + for y in self.timeLabels[self.maxLapHit]: + y.configure(text_font=ToontownGlobals.getSignFont()) + + self.raceTimeDelta = globalClock.getFrameTime() - self.race.baseTime + lapNotice = DirectLabel() + lapNotice.setScale(0.1) + if self.maxLapHit == self.race.lapCount - 1: + lapNotice['text'] = TTLocalizer.KartRace_FinalLapText + else: + lapNotice['text'] = TTLocalizer.KartRace_LapText % str(self.maxLapHit + 1) + taskMgr.doMethodLater(2, lapNotice.remove, 'removeIt', extraArgs=[]) + self.lapLabel['text'] = str(clampScalar(self.maxLapHit + 1, 1, self.race.lapCount)) + '/' + str(self.race.lapCount) + + suffix = {1: TTLocalizer.KartRace_FirstSuffix, + 2: TTLocalizer.KartRace_SecondSuffix, + 3: TTLocalizer.KartRace_ThirdSuffix, + 4: TTLocalizer.KartRace_FourthSuffix} + placeSorter.sort() + for x, p in zip(placeSorter, xrange(len(placeSorter), 0, -1)): + self.racerDict[x[1]].update(place=p + placeCount - len(placeSorter)) + + localRacer = self.racerDict[localAvatar.doId] + nearDiff, farDiff = RaceGlobals.TrackDict[self.race.trackId][8] + if not localRacer.finished and self.faceEndPos[0] - localRacer.face.getX() < nearDiff: + for racerId in self.racerDict.keys(): + racer = self.racerDict[racerId] + if not racer.enabled or racerId == localAvatar.doId or racer.face.getX() >= self.faceEndPos[0]: + continue + if self.faceEndPos[0] - racer.face.getX() < farDiff: + self.photoFinish = True + + if self.photoFinish: + self.photoFinishLabel.show() + self.placeLabelNum['text'] = '' + self.placeLabelStr['text'] = '' + else: + self.photoFinishLabel.hide() + self.placeLabelNum['text'] = str(self.racerDict[localAvatar.doId].place) + self.placeLabelStr['text'] = suffix[self.racerDict[localAvatar.doId].place] + minutes = int(t / 60) + t -= minutes * 60 + seconds = int(t) + padding = (seconds < 10 and ['0'] or [''])[0] + t -= seconds + fraction = str(t)[2:4] + fraction = fraction + '0' * (2 - len(fraction)) + if self.timerEnabled and self.maxLapHit < self.race.lapCount: + self.timeLabels[self.maxLapHit][0]['text'] = "%d'" % minutes + self.timeLabels[self.maxLapHit][1]['text'] = "%s%d''" % (padding, seconds) + self.timeLabels[self.maxLapHit][2]['text'] = '%s' % fraction + if self.race.wrongWay and not self.wrongWaySeq.isPlaying(): + self.wrongWaySeq.loop() + elif not self.race.wrongWay and self.wrongWaySeq.isPlaying(): + self.wrongWaySeq.finish() + + def updateRacerInfo(self, avId, curvetime = None, maxlaphit = None): + if avId in self.racerDict.keys(): + self.racerDict[avId].update(curvetime=curvetime, maxlaphit=maxlaphit) + + def racerEntered(self, avId): + toon = base.cr.doId2do.get(avId, None) + kart = base.cr.doId2do.get(self.race.kartMap.get(avId, None), None) + if not toon or not kart: + return + if kart.getBodyColor() == InvalidEntry: + bodyColor = getDefaultColor() + else: + bodyColor = getAccessory(kart.getBodyColor()) + headframe = RaceHeadFrame(av=toon, color=bodyColor) + eyes = headframe.head.find('**/eyes*') + eyes.setDepthTest(1) + eyes.setDepthWrite(1) + headframe.configure(geom_scale=(0.5, 0.5, 0.5)) + headframe.setZ(self.faceStartPos[2]) + headframe.setDepthWrite(True) + headframe.setDepthTest(True) + headframe.reparentTo(self.raceModeRoot) + self.directObjList.append(headframe) + mapspot = loader.loadModel('phase_6/models/karting/race_mapspot') + mapspot.setColor(bodyColor) + mapspot.reparentTo(self.mapLines) + mapspot.setHpr(self.mapScene, 0, 0, 0) + self.racerDict[avId] = self.RacerInfo(headframe, mapspot) + for key, i in zip(self.racerDict.keys(), range(len(self.racerDict.keys()))): + face = self.racerDict[key].face + mapspot = self.racerDict[key].mapspot + face.setX(self.faceStartPos[0]) + face.setY(-1 - 5 * (i + 1)) + face.setScale(0.15) + mapspot.getChild(0).setY((-5 - 5 * (i + 1)) * 1000) + mapspot.setScale(self.mapScene, 0.15) + mapspot.setPos(self.race.startingPos[0][0]) + if key == localAvatar.doId: + face.setY(-1) + face.setScale(face.getScale() * 1.25) + mapspot.getChild(0).setY(-5 * 1000) + mapspot.setScale(mapspot.getScale() * 1.25) + self.face = face + self.mapspot = mapspot + + return + + def racerLeft(self, avId, unexpected = False): + racer = self.racerDict.get(avId, None) + if racer: + racer.disable() + return + + def racerFinished(self, avId, trackId, place, totalTime, entryFee, qualify, winnings, bonus, trophies, circuitPoints, circuitTime): + racer = self.racerDict.get(avId, None) + if racer: + racer.update(finished=True) + racer.disable() + self.endPanel.displayRacer(place, entryFee, qualify, winnings, trackId, bonus, trophies, racer.face, base.cr.doId2do[avId].getName(), totalTime, circuitPoints, circuitTime) + self.directObjList.remove(racer.face) + if avId == localAvatar.doId: + self.disableRaceMode() + self.enableResultMode() + self.endPanel.startWinningsPanel(entryFee, winnings, trackId, bonus, trophies) + return + + def racerFinishedCircuit(self, avId, place, entryFee, winnings, bonus, trophies): + racer = self.racerDict.get(avId, None) + if racer: + newTotalTickets = winnings + entryFee + bonus + self.endPanel.updateWinnings(place, newTotalTickets) + if avId == localAvatar.doId: + self.endPanel.updateWinningsFromCircuit(place, entryFee, winnings, bonus, trophies) + return + + def circuitFinished(self, placeFixup): + self.endPanel.circuitFinished(placeFixup) + + def setTimerEnabled(self, enabled): + self.timerEnabled = enabled diff --git a/toontown/racing/RaceGag.py b/toontown/racing/RaceGag.py new file mode 100644 index 0000000..9647719 --- /dev/null +++ b/toontown/racing/RaceGag.py @@ -0,0 +1,76 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.showbase import DirectObject +from DroppedGag import * +types = ['', + 'Pie', + 'Banana', + 'Anvil'] + +class RaceGag(DirectObject.DirectObject): + + def __init__(self, parent, slot, testPos): + DirectObject.DirectObject.__init__(self) + self.parent = parent + self.name = 'gag-' + str(slot) + self.geom = DroppedGag(self.name, base.race.qbox) + self.geom.dropShadow.setScale(0.7) + self.geom.setPos(testPos + Vec3(0, 0, -1)) + qc = CollisionTube(Point3(0, 0, -2.5), Point3(0, 0, 2.5), 1) + self.gagnp = NodePath(CollisionNode(self.name)) + self.gagnp.node().addSolid(qc) + self.gagnp.reparentTo(self.geom) + self.gagnp.node().setIntoCollideMask(BitMask32(32768)) + self.gagnp.node().setFromCollideMask(BitMask32(32768)) + self.slot = slot + self.type = 0 + self.accept('imIn-' + self.name, self.hitGag) + self.pickupSound = base.loadSfx('phase_6/audio/sfx/KART_getGag.mp3') + self.fadeout = None + return + + def delete(self): + if self.fadeout: + self.fadeout.finish() + self.fadeout = None + self.gagnp.removeNode() + self.gagnp = None + self.geom.delete() + self.geom = None + del self.parent + self.ignore('imIn-' + self.name) + return + + def getType(self): + return self.type + + def isActive(self): + if self.type: + return True + else: + return False + + def genGag(self, spot, type): + self.type = type + fadein = self.geom.scaleInterval(1, 4) + self.geom.setScale(0.001) + self.geom.reparentTo(self.parent.geom) + self.gagnp.reparentTo(self.geom) + fadein.start() + + def hideGeom(self): + self.geom.detachNode() + + def disableGag(self): + self.gagnp.reparentTo(hidden) + self.type = 0 + if self.fadeout: + self.fadeout.finish() + self.fadeout = Sequence(self.geom.scaleInterval(0.2, 0), Func(self.hideGeom)) + self.fadeout.start() + + def hitGag(self, cevent): + if not self.parent.currGag: + self.pickupSound.play() + self.parent.pickupGag(self.slot, self.type) + self.disableGag() diff --git a/toontown/racing/RaceGlobals.py b/toontown/racing/RaceGlobals.py new file mode 100644 index 0000000..0865293 --- /dev/null +++ b/toontown/racing/RaceGlobals.py @@ -0,0 +1,556 @@ +TrackSignDuration = 15 +RaceCountdown = 3 +MaxRacers = 4 +MaxTickets = 99999 +Practice = 0 +ToonBattle = 1 +Circuit = 2 +Speedway = 0 +Rural = 1 +Urban = 2 +RT_Speedway_1 = 0 +RT_Speedway_1_rev = 1 +RT_Rural_1 = 20 +RT_Rural_1_rev = 21 +RT_Urban_1 = 40 +RT_Urban_1_rev = 41 +RT_Speedway_2 = 60 +RT_Speedway_2_rev = 61 +RT_Rural_2 = 62 +RT_Rural_2_rev = 63 +RT_Urban_2 = 64 +RT_Urban_2_rev = 65 +KARTING_TICKETS_HOLIDAY_MULTIPLIER = 2 + +def getTrackGenre(trackId): + if trackId in (RT_Speedway_1, + RT_Speedway_1_rev, + RT_Speedway_2, + RT_Speedway_2_rev): + return Speedway + elif trackId in (RT_Rural_1, + RT_Rural_1_rev, + RT_Rural_2, + RT_Rural_2_rev): + return Rural + else: + return Urban + + +RT_Speedway_1_Gags = ((923.052, -1177.431, 0.024), + (926.099, -1187.345, 0.024), + (925.68, -1197.327, 0.024), + (925.169, -1209.502, 0.024), + (394.009, 209.219, 0.025), + (279.109, 279.744, 0.025), + (204.366, 316.238, 0.025), + (118.646, 358.009, 0.025), + (-1462.098, 791.722, 0.025), + (-1459.446, 730.064, 0.025), + (-1450.731, 666.811, 0.025), + (-1438.388, 615.1, 0.025)) +RT_Speedway_2_Gags = ((-355.18, -2430.1, -0.126728), + (-343.456, -2421.43, -0.0116951), + (-329.644, -2411.06, -0.0169053), + (-315.054, -2402.91, -0.0800667), + (243.293, -906.412, 0.021832), + (216.555, -910.885, -0.146125), + (192.16, -915.93, -0.242366), + (165.941, -922.381, -0.247588), + (-840.626, 2405.96, 58.4195), + (-868.154, 2370.54, 56.7396), + (-896.126, 2332.55, 53.8607), + (-921.952, 2291.16, 49.8209)) +RT_Speedway_1_rev_Gags = ((1364.601, -664.452, 0.025), + (1312.491, -588.218, 0.025), + (1251.775, -509.556, 0.025), + (1214.052, -461.743, 0.025), + (-976.044, 995.072, 0.025), + (-1043.917, 1018.78, 0.025), + (-1124.555, 1038.362, 0.025), + (-1187.95, 1047.006, 0.025), + (-1174.542, -208.968, 0.025), + (-1149.34, -270.698, 0.025), + (-1121.2, -334.367, 0.025), + (-1090.627, -392.662, 0.026)) +RT_Rural_1_Gags = ((814.276, -552.928, 2.107), + (847.738, -551.97, 2.106), + (889.265, -549.569, 2.107), + (922.022, -554.813, 2.106), + (1791.42, 2523.91, 2.106), + (1754.14, 2540.25, 2.107), + (1689.66, 2557.28, 2.107), + (1614.01, 2577.16, 2.106), + (-1839.0, 654.477, 86.83), + (-1894.33, 640.125, 80.39), + (-1955.3, 625.09, 73.07), + (-2016.99, 611.746, 65.86)) +RT_Rural_2_Gags = ((2001.53, 560.532, 198.912), + (2002.45, 574.292, 198.912), + (2003.42, 588.612, 198.912), + (2004, 602.849, 198.912), + (-2107.4, 2209.67, 198.913), + (-2086.13, 2224.31, 198.913), + (-2058.11, 2244.31, 198.912), + (-2023.85, 2268.77, 198.912), + (-331.746, -1010.57, 222.332), + (-358.595, -1007.68, 225.129), + (-388.556, -1004.87, 228.239), + (-410.122, -1003.03, 230.482), + (69.763, -2324.5, 198.912), + (63.5314, -2334.02, 198.913), + (57.9662, -2349.14, 198.913), + (51.8838, -2363.87, 198.913)) +RT_Urban_1_Gags = ((51.9952, 2431.62, 55.7053), + (39.5407, 2421.64, 65.7053), + (27.7504, 2411.67, 55.7053), + (15.55, 2401.65, 65.7053), + (-1008.36, 2116.41, 0.0246798), + (-1050.31, 2099.78, 0.025), + (-1092.26, 2083.15, 0.0253202), + (-1134.21, 2066.52, 0.0256404), + (-1966.68, 1139.32, 1.76981), + (-1970.46, 1120.57, 1.76981), + (-1974.18, 1101.82, 1.76981), + (-1977.93, 1084.07, 1.76981), + (1419.05, -2987.18, 0.025), + (1411.09, -3004.09, 0.025), + (1403.13, -3021.01, 0.025), + (1395.17, -3037.92, 0.025), + (948.131, -1216.77, 0.025), + (935.545, -1204.09, 0.025), + (922.959, -1191.41, 0.025), + (909.959, -1177.41, 0.025)) +RT_Urban_2_Gags = ((-2761.49, -3070.97, -0.255122), + (-2730.18, -3084.09, -0.255153), + (-2701.45, -3096.26, -0.255669), + (-2669.81, -3108.9, -0.255252), + (735.479, -423.828, 23.7334), + (759.026, -427.198, 23.0068), + (783.232, -430.659, 22.2569), + (809.914, -434.476, 21.4326), + (3100.09, 240.411, 23.4672), + (3089.09, 242.019, 23.5251), + (3077.68, 243.688, 23.6857), + (3064.82, 245.567, 23.8771), + (-10.7389, 2980.48, -0.255609), + (-41.2644, 2974.53, -0.255122), + (-69.8423, 2989.98, -0.255682), + (-102.331, 2986.1, -0.255637), + (-1978.67, 588.981, -0.255685), + (-1977.07, 560.797, -0.255415), + (-1948.58, 544.782, -0.255122), + (-1943.42, 510.262, -0.255866)) +RT_Urban_1_rev_Gags = ((1034.43, -366.371, 0.025), + (1051.84, -360.473, 0.025), + (1069.25, -354.575, 0.025), + (1086.66, -348.677, 0.025), + (1849.66, -2807.21, 0.0246158), + (1858.55, -2795.99, 0.0246158), + (1867.44, -2784.76, 0.0246158), + (1876.33, -2773.53, 0.0246158), + (316.342, -44.9529, 0.025), + (305.173, -63.4405, 0.025), + (294.004, -81.9281, 0.025), + (282.835, -100.416, 0.025), + (-762.377, 2979.25, 0.025), + (-753.029, 2995.69, 0.025), + (-743.681, 3012.14, 0.025), + (-734.333, 3028.58, 0.025), + (470.628, 1828.32, 55.0), + (481.284, 1836.89, 55.0), + (491.941, 1845.47, 55.0), + (502.597, 1854.04, 55.0)) +Speedway_1_Boosts = (((-320, 685, 1), (415, 0, 0)),) +Speedway_1_Rev_Boosts = (((-320, 685, 0.1), (235, 0, 0)),) +Speedway_2_Boosts = (((-120, 430, 1.0), (-50, 0, 0)),) +Speedway_2_Rev_Boosts = (((176, 625, 1.0), (130, 0, 0)),) +Rural_1_Boosts = (((3132.64, 859.56, 5.0), (384.44, 363.5, 0)), ((-3050.33, -1804.97, 207.7), (229.4, 353.25, 342.9))) +Rural_1_Rev_Boosts = (((3132.64, 859.56, 5.0), (197.1, -2.25, 0)), ((-3151.34, -1569.56, 200.621), (189.46, 182.75, 195.255))) +Rural_2_Boosts = (((873.255, -593.664, 199.5), (87.715, 0, 0)), ((-1747.62, 801.56, 199.5), (-126.516, 0, 0))) +Rural_2_Rev_Boosts = (((-428.004, -243.692, 324.516), (51.428, 6, 1)), ((-384.043, 211.62, 193.5), (-127.859, 1, 0))) +Urban_1_Boosts = (((677.057, 1618.24, 0.025), (35.9995, 0, 0)), ((-2250.35, 1618.1, 0.0241526), (-154.8, 0, 0)), ((400.13, -1090.26, 0.025), (-175.204, 0, 0))) +Urban_1_Rev_Boosts = (((488.739, -2055.07, 0.025), (3.59753, 0, 0)), ((-1737.29, 588.138, 0.025), (26.3975, 0, 0)), ((-212.314, 2638.34, 0.025), (-128.404, 0, 0))) +Urban_2_Boosts = (((358.134, -1655.42, 0.3), (-4.95, 1, 0)), ((2058.77, 2560.03, 0.3), (77.31, 0, 0)), ((-3081.33, -1037.55, 0.25), (177.359, 0, 0))) +Urban_2_Rev_Boosts = (((-2007.38, 484.878, 0.25), (30.9102, 0, 0)), ((2646.51, 1455.15, 0.25), (-120.172, 0, 0)), ((-472.215, -2048.21, 0.25), (136.192, 0, 0))) + +def RaceInfo2RacePadId(trackId, trackType): + rev = trackId % 2 + if not rev: + if trackType == Practice: + padId = 0 + else: + padId = 2 + elif trackType == Practice: + padId = 1 + else: + padId = 3 + return padId + + +def getTrackGenreString(genreId): + genreStrings = ['Speedway', 'Country', 'City'] + return genreStrings[genreId].lower() + + +def getTunnelSignName(genreId, padId): + if genreId == 2 and padId == 0: + return 'tunne1l_citysign' + elif genreId == 1 and padId == 0: + return 'tunnel_countrysign1' + else: + return 'tunnel%s_%ssign' % (padId + 1, getTrackGenreString(genreId)) + + +RacePadId2RaceInfo = {0: (0, Practice, 3), + 1: (1, Practice, 3), + 2: (0, ToonBattle, 3), + 3: (1, ToonBattle, 3)} + +def getGenreFromString(string): + if string == 'town': + return Urban + elif string == 'stadium': + return Speedway + else: + return Rural + + +def getTrackListByType(genre, type): + return Rural + + +def getTrackListByType(genre, type): + genreDict = {Urban: [[RT_Urban_1, RT_Urban_2], [RT_Urban_1_rev, RT_Urban_2_rev]], + Rural: [[RT_Rural_1, RT_Rural_2], [RT_Rural_1_rev, RT_Rural_2_rev]], + Speedway: [[RT_Speedway_1, RT_Speedway_2], [RT_Speedway_1_rev, RT_Speedway_2_rev]]} + trackIdList = genreDict.get(genre) + return trackIdList[type] + + +def getCanonicalPadId(padId): + return padId % 4 + + +def getNextRaceInfo(prevTrackId, genreString, padId): + genre = getGenreFromString(genreString) + cPadId = getCanonicalPadId(padId) + raceInfo = RacePadId2RaceInfo.get(cPadId) + trackList = getTrackListByType(genre, raceInfo[0]) + if trackList.count(prevTrackId) == 0: + trackId = trackList[1] + else: + index = trackList.index(prevTrackId) + index += 1 + index %= len(trackList) + trackId = trackList[index] + return (trackId, raceInfo[1], raceInfo[2]) + + +TrackPath = 'phase_6/models/karting/' +TrackDict = {RT_Speedway_1: (TrackPath + 'RT_SpeedwayA', + 240.0, + 115.0, + (50, 500), + RT_Speedway_1_Gags, + Speedway_1_Boosts, + 1.0, + 'GS_Race_SS.mid', + (0.01, 0.015)), + RT_Speedway_1_rev: (TrackPath + 'RT_SpeedwayA', + 240.0, + 115.0, + (50, 500), + RT_Speedway_1_rev_Gags, + Speedway_1_Rev_Boosts, + 1.0, + 'GS_Race_SS.mid', + (0.01, 0.015)), + RT_Speedway_2: (TrackPath + 'RT_SpeedwayB', + 335.0, + 210.0, + (75, 1000), + RT_Speedway_2_Gags, + Speedway_2_Boosts, + 1.0, + 'GS_Race_SS.mid', + (0.01, 0.015)), + RT_Speedway_2_rev: (TrackPath + 'RT_SpeedwayB', + 335.0, + 210.0, + (75, 1000), + RT_Speedway_2_Gags, + Speedway_2_Rev_Boosts, + 1.0, + 'GS_Race_SS.mid', + (0.01, 0.015)), + RT_Rural_1: (TrackPath + 'RT_RuralB', + 360.0, + 230.0, + (100, 500), + RT_Rural_1_Gags, + Rural_1_Boosts, + 0.75, + 'GS_Race_RR.mid', + (0.003, 0.004)), + RT_Rural_1_rev: (TrackPath + 'RT_RuralB', + 360.0, + 230.0, + (100, 500), + RT_Rural_1_Gags, + Rural_1_Rev_Boosts, + 0.75, + 'GS_Race_RR.mid', + (0.003, 0.004)), + RT_Rural_2: (TrackPath + 'RT_RuralB2', + 480.0, + 360.0, + (150, 1000), + RT_Rural_2_Gags, + Rural_2_Boosts, + 0.75, + 'GS_Race_RR.mid', + (0.003, 0.004)), + RT_Rural_2_rev: (TrackPath + 'RT_RuralB2', + 480.0, + 360.0, + (150, 1000), + RT_Rural_2_Gags, + Rural_2_Rev_Boosts, + 0.75, + 'GS_Race_RR.mid', + (0.003, 0.004)), + RT_Urban_1: (TrackPath + 'RT_UrbanA', + 480.0, + 305.0, + (300, 500), + RT_Urban_1_Gags, + Urban_1_Boosts, + 1.0, + 'GS_Race_CC.mid', + (0.002, 0.003)), + RT_Urban_1_rev: (TrackPath + 'RT_UrbanA', + 480.0, + 305.0, + (300, 500), + RT_Urban_1_rev_Gags, + Urban_1_Rev_Boosts, + 1.0, + 'GS_Race_CC.mid', + (0.002, 0.003)), + RT_Urban_2: (TrackPath + 'RT_UrbanB', + 480.0, + 280.0, + (400, 1000), + RT_Urban_2_Gags, + Urban_2_Boosts, + 1.0, + 'GS_Race_CC.mid', + (0.002, 0.003)), + RT_Urban_2_rev: (TrackPath + 'RT_UrbanB', + 480.0, + 280.0, + (400, 1000), + RT_Urban_2_Gags, + Urban_2_Rev_Boosts, + 1.0, + 'GS_Race_CC.mid', + (0.002, 0.003))} +TrackIds = TrackDict.keys() +TrackIds.sort() + +def getEntryFee(trackId, raceType): + fee = 0 + if raceType == ToonBattle: + fee = TrackDict[trackId][3][0] + elif raceType == Circuit: + fee = TrackDict[trackId][3][1] + return fee + + +def getQualifyingTime(trackId): + return TrackDict[trackId][1] + + +def getDefaultRecordTime(trackId): + return TrackDict[trackId][2] + + +def getDefaultRecord(trackId): + return getDefaultRecordTime(trackId), 0, 1, 'Goofy' + + +Daily = 0 +Weekly = 1 +AllTime = 2 +PeriodDict = {Daily: 10, + Weekly: 100, + AllTime: 1000} +PeriodIds = PeriodDict.keys() +NumRecordPeriods = len(PeriodIds) +NumRecordsPerPeriod = 10 +Winnings = [3.0, + 1.0, + 0.5, + 0.15] +PracticeWinnings = 20 +SpeedwayQuals = 0 +RuralQuals = 1 +UrbanQuals = 2 +SpeedwayWins = 3 +RuralWins = 4 +UrbanWins = 5 +CircuitWins = 6 +TwoPlayerWins = 7 +ThreePlayerWins = 8 +FourPlayerWins = 9 +CircuitSweeps = 10 +CircuitQuals = 11 +QualsList = [SpeedwayQuals, RuralQuals, UrbanQuals] +WinsList = [SpeedwayWins, RuralWins, UrbanWins] +SpeedwayQuals1 = 0 +SpeedwayQuals2 = 1 +SpeedwayQuals3 = 2 +RuralQuals1 = 3 +RuralQuals2 = 4 +RuralQuals3 = 5 +UrbanQuals1 = 6 +UrbanQuals2 = 7 +UrbanQuals3 = 8 +TotalQuals = 9 +SpeedwayWins1 = 10 +SpeedwayWins2 = 11 +SpeedwayWins3 = 12 +RuralWins1 = 13 +RuralWins2 = 14 +RuralWins3 = 15 +UrbanWins1 = 16 +UrbanWins2 = 17 +UrbanWins3 = 18 +TotalWins = 19 +CircuitQuals1 = 20 +CircuitQuals2 = 21 +CircuitQuals3 = 22 +CircuitWins1 = 23 +CircuitWins2 = 24 +CircuitWins3 = 25 +CircuitSweeps1 = 26 +CircuitSweeps2 = 27 +CircuitSweeps3 = 28 +GrandTouring = 29 +NumTrophies = 30 +TenTrophyCup = 30 +TwentyTrophyCup = 31 +ThirtyTrophyCup = 32 +TrophyCups = [TenTrophyCup, TwentyTrophyCup, ThirtyTrophyCup] +NumCups = 3 +SpeedwayQualsList = [SpeedwayQuals1, SpeedwayQuals2, SpeedwayQuals3] +RuralQualsList = [RuralQuals1, RuralQuals2, RuralQuals3] +UrbanQualsList = [UrbanQuals1, UrbanQuals2, UrbanQuals3] +SpeedwayWinsList = [SpeedwayWins1, SpeedwayWins2, SpeedwayWins3] +RuralWinsList = [RuralWins1, RuralWins2, RuralWins3] +UrbanWinsList = [UrbanWins1, UrbanWins2, UrbanWins3] +CircuitWinsList = [CircuitWins1, CircuitWins2, CircuitWins3] +CircuitSweepsList = [CircuitSweeps1, CircuitSweeps2, CircuitSweeps3] +CircuitQualList = [CircuitQuals1, CircuitQuals2, CircuitQuals3] +AllQualsList = [SpeedwayQualsList, RuralQualsList, UrbanQualsList] +AllWinsList = [SpeedwayWinsList, RuralWinsList, UrbanWinsList] +TrophiesPerCup = NumTrophies / NumCups +QualifiedRaces = [1, 10, 100] +TotalQualifiedRaces = 100 +WonRaces = [1, 10, 100] +TotalWonRaces = 100 +WonCircuitRaces = [1, 5, 25] +SweptCircuitRaces = [1, 5, 25] +QualifiedCircuitRaces = [1, 5, 25] +LBSubscription = {'stadium': [(RT_Speedway_1, Daily), + (RT_Speedway_1, Weekly), + (RT_Speedway_1, AllTime), + (RT_Speedway_1_rev, Daily), + (RT_Speedway_1_rev, Weekly), + (RT_Speedway_1_rev, AllTime), + (RT_Speedway_2, Daily), + (RT_Speedway_2, Weekly), + (RT_Speedway_2, AllTime), + (RT_Speedway_2_rev, Daily), + (RT_Speedway_2_rev, Weekly), + (RT_Speedway_2_rev, AllTime)], + 'country': [(RT_Rural_1, Daily), + (RT_Rural_1, Weekly), + (RT_Rural_1, AllTime), + (RT_Rural_1_rev, Daily), + (RT_Rural_1_rev, Weekly), + (RT_Rural_1_rev, AllTime), + (RT_Rural_2, Daily), + (RT_Rural_2, Weekly), + (RT_Rural_2, AllTime), + (RT_Rural_2_rev, Daily), + (RT_Rural_2_rev, Weekly), + (RT_Rural_2_rev, AllTime)], + 'city': [(RT_Urban_1, Daily), + (RT_Urban_1, Weekly), + (RT_Urban_1, AllTime), + (RT_Urban_1_rev, Daily), + (RT_Urban_1_rev, Weekly), + (RT_Urban_1_rev, AllTime), + (RT_Urban_2, Daily), + (RT_Urban_2, Weekly), + (RT_Urban_2, AllTime), + (RT_Urban_2_rev, Daily), + (RT_Urban_2_rev, Weekly), + (RT_Urban_2_rev, AllTime)]} +BANANA = 1 +TURBO = 2 +ANVIL = 3 +PIE = 4 +GagFreq = [[PIE, + BANANA, + BANANA, + BANANA, + TURBO, + PIE], + [PIE, + BANANA, + BANANA, + TURBO, + ANVIL, + PIE], + [PIE, + BANANA, + TURBO, + TURBO, + ANVIL, + PIE], + [BANANA, + TURBO, + TURBO, + TURBO, + ANVIL, + PIE]] +CircuitLoops = [[RT_Speedway_1, RT_Rural_1, RT_Urban_1], + [RT_Speedway_1_rev, RT_Rural_1_rev, RT_Urban_1_rev], + [RT_Speedway_2, RT_Rural_2, RT_Urban_2], + [RT_Speedway_2_rev, RT_Rural_2_rev, RT_Urban_2_rev]] +CircuitPoints = [10, + 8, + 6, + 4] + +def getCircuitLoop(startingTrack): + circuitLoop = [startingTrack] + for loop in CircuitLoops: + if startingTrack in loop: + print loop + numTracks = len(loop) + tempLoop = loop * 2 + startingIndex = tempLoop.index(startingTrack) + circuitLoop = tempLoop[startingIndex:startingIndex + numTracks] + break + + return circuitLoop + + +Exit_UserReq = 0 +Exit_Barrier = 1 +Exit_Slow = 2 +Exit_BarrierNoRefund = 3 diff --git a/toontown/racing/RaceHeadFrame.py b/toontown/racing/RaceHeadFrame.py new file mode 100644 index 0000000..060448e --- /dev/null +++ b/toontown/racing/RaceHeadFrame.py @@ -0,0 +1,34 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toon import ToonHead + +class RaceHeadFrame(DirectFrame): + + def __init__(self, av = None, color = Vec4(1, 1, 1, 1), *args, **kwargs): + self.panelGeom = loader.loadModel('phase_4/models/karting/racing_panel') + self.panelGeom.find('**/*fg').setColor(color) + opts = {'relief': None, + 'geom': self.panelGeom, + 'geom_scale': (1, 1, 0.5), + 'pos': (0, 0, 0)} + opts.update(kwargs) + apply(DirectFrame.__init__, (self,) + args, opts) + self.initialiseoptions(RaceHeadFrame) + if av: + self.setAv(av) + return + + def setAv(self, av): + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(0, -0.5, -0.09, 180.0, 0.0, 0.0, 0.2, 0.2, 0.2) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(av.style, forGui=1) + self.headModel.reparentTo(self.head) + + def destroy(self): + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + DirectFrame.destroy(self) diff --git a/toontown/racing/RaceManagerAI.py b/toontown/racing/RaceManagerAI.py new file mode 100644 index 0000000..f665ee0 --- /dev/null +++ b/toontown/racing/RaceManagerAI.py @@ -0,0 +1,738 @@ +from direct.directnotify import DirectNotifyGlobal +import DistributedRaceAI +from toontown.toonbase import ToontownGlobals, TTLocalizer +from toontown.coghq import MintLayout +from toontown.ai import HolidayBaseAI +from direct.showbase import DirectObject +import RaceGlobals, random, os, cPickle + +class RaceManagerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('RaceManagerAI') + serverDataFolder = simbase.config.GetString('server-data-folder', '') + + def __init__(self, air): + DirectObject.DirectObject.__init__(self) + self.air = air + self.races = [] + self.shard = str(air.districtId) + self.filename = self.getFilename() + self.trackRecords = self.loadRecords() + + def getDoId(self): + return 0 + + def createRace(self, trackId, raceType, laps, players, circuitLoop, circuitPoints, circuitTimes, qualTimes=[], circuitTimeList={}, circuitTotalBonusTickets={}): + raceZone = self.air.allocateZone() + race = DistributedRaceAI.DistributedRaceAI(self.air, trackId, raceZone, players, laps, raceType, self.exitedRace, self.raceOver, circuitLoop, circuitPoints, circuitTimes, qualTimes, circuitTimeList, circuitTotalBonusTickets) + race.generateWithRequired(raceZone) + race.playersFinished = [] + race.lastTotalTime = 0 + self.races.append(race) + return raceZone + + def exitedRace(self, race, playerInfo): + self.notify.debug('exited race: %s' % playerInfo.avId) + totalTime = playerInfo.totalTime + entryFee = 0 + bonus = 0 + placeMultiplier = 0 + qualify = 0 + winnings = 0 + trophies = [] + points = [] + newHistory = None + race.playersFinished.append(playerInfo.avId) + place = len(race.playersFinished) + self.notify.debug('place: %s of %s' % (place, race.toonCount)) + self.notify.debug('pre-tie totalTime: %s' % totalTime) + if totalTime <= race.lastTotalTime: + totalTime = race.lastTotalTime + 0.01 + race.lastTotalTime = totalTime + self.notify.debug('totalTime: %s, qualify: %s' % (totalTime, RaceGlobals.getQualifyingTime(race.trackId))) + circuitTime = totalTime + race.circuitTimes.get(playerInfo.avId, 0) + race.circuitTimes[playerInfo.avId] = circuitTime + if not race.circuitTimeList.get(playerInfo.avId): + race.circuitTimeList[playerInfo.avId] = [] + race.circuitTimeList[playerInfo.avId].append([totalTime, 0]) + self.notify.debug('CircuitTimeList %s' % race.circuitTimeList) + if race.raceType == RaceGlobals.Circuit: + points = race.circuitPoints.get(playerInfo.avId, []) + points.append(RaceGlobals.CircuitPoints[place - 1]) + race.circuitPoints[playerInfo.avId] = points + currentTimeIndex = len(race.circuitTimeList[playerInfo.avId]) - 1 + if totalTime <= RaceGlobals.getQualifyingTime(race.trackId): + race.circuitTimeList[playerInfo.avId][currentTimeIndex][1] = 1 + self.notify.debug('Racer Qualified time: %s required: %s' % (totalTime, RaceGlobals.getQualifyingTime(race.trackId))) + qualify = 1 + self.checkPersonalBest(race.trackId, totalTime, race.raceType, race.toonCount, playerInfo.avId) + if race.raceType == RaceGlobals.Practice: + winnings = RaceGlobals.PracticeWinnings + self.notify.debug('GrandTouring: Checking from branch: practice %s' % playerInfo.avId) + trophies = self.checkForNonRaceTrophies(playerInfo.avId) + if trophies: + self.updateTrophiesFromList(playerInfo.avId, trophies) + else: + self.air.writeServerEvent('kartingPlaced', playerInfo.avId, '%s|%s' % (place, race.toonCount)) + if race.raceType != RaceGlobals.Circuit: + entryFee = RaceGlobals.getEntryFee(race.trackId, race.raceType) + placeMultiplier = RaceGlobals.Winnings[place - 1 + (RaceGlobals.MaxRacers - race.toonCount)] + winnings = int(entryFee * placeMultiplier) + newHistory = self.getNewSingleRaceHistory(race, playerInfo.avId, place) + av = self.air.doId2do.get(playerInfo.avId) + if newHistory: + self.notify.debug('history %s' % newHistory) + av.b_setKartingHistory(newHistory) + trophies = self.checkForRaceTrophies(race, playerInfo.avId) + else: + trophies = self.checkForNonRaceTrophies(playerInfo.avId) + if trophies: + self.updateTrophiesFromList(playerInfo.avId, trophies) + bonus = self.checkTimeRecord(race.trackId, totalTime, race.raceType, race.toonCount, playerInfo.avId) + if race.circuitTotalBonusTickets.has_key(playerInfo.avId): + race.circuitTotalBonusTickets[playerInfo.avId] += bonus + else: + race.circuitTotalBonusTickets[playerInfo.avId] = bonus + av = self.air.doId2do.get(playerInfo.avId) + if av: + oldTickets = av.getTickets() + self.notify.debug('old tickets: %s' % oldTickets) + newTickets = oldTickets + winnings + entryFee + bonus + self.air.writeServerEvent('kartingTicketsWon', playerInfo.avId, '%s' % (newTickets - oldTickets)) + self.notify.debug('entry fee: %s' % entryFee) + self.notify.debug('place mult: %s' % placeMultiplier) + self.notify.debug('winnings: %s' % winnings) + self.notify.debug('bonus: %s' % bonus) + self.notify.debug('new tickets: %s' % newTickets) + self.notify.debug('circuit points: %s' % points) + self.notify.debug('circuit time: %s' % circuitTime) + self.notify.debug('circuitTotalBonusTickets: %s' % race.circuitTotalBonusTickets) + av.b_setTickets(newTickets) + else: + race.circuitTimeList[playerInfo.avId][currentTimeIndex][1] = -1 + self.notify.debug('GrandTouring: Checking from branch: Not Qualified %s' % playerInfo.avId) + trophies = self.checkForNonRaceTrophies(playerInfo.avId) + if trophies: + self.updateTrophiesFromList(playerInfo.avId, trophies) + if race in self.races: + race.d_setPlace(playerInfo.avId, totalTime, place, entryFee, qualify, winnings, bonus, trophies, points, circuitTime) + if race.isCircuit(): + self.notify.debug('isCircuit') + if race.everyoneDone(): + self.notify.debug('everyoneDone') + if race.isLastRace(): + taskMgr.doMethodLater(10, self.endCircuitRace, 'DelayEndCircuitRace=%d' % race.doId, (race, bonus)) + else: + self.endCircuitRace(race, bonus) + else: + self.notify.debug('not isCircuit') + return + + def endCircuitRace(self, race, bonus=0): + self.notify.debug('endCircuitRace') + pointTotals = [] + for avId in race.circuitPoints: + pointTotals.append([avId, sum(race.circuitPoints[avId])]) + + numVals = len(pointTotals) + finalStandings = [] + + def swap(x, y): + t = pointTotals[x] + pointTotals[x] = pointTotals[y] + pointTotals[y] = t + + for i in range(numVals - 1, 0, -1): + for j in range(i): + if pointTotals[j][1] < pointTotals[j + 1][1]: + swap(j, j + 1) + elif pointTotals[j][1] == pointTotals[j + 1][1]: + avId1 = pointTotals[j][0] + avId2 = pointTotals[j + 1][0] + if race.circuitTimes[avId1] > race.circuitTimes[avId2]: + swap(j, j + 1) + + for i in range(numVals): + finalStandings.append(pointTotals[i][0]) + + self.notify.debug('Final standings %s' % finalStandings) + for avId in finalStandings: + self.notify.debug('avId %s' % avId) + place = finalStandings.index(avId) + 1 + places = len(finalStandings) + winnings = 0 + trophies = [] + if race.isLastRace(): + self.notify.debug('isLastRace') + av = self.air.doId2do.get(avId) + if av and avId in race.playersFinished: + self.air.writeServerEvent('kartingCircuitFinished', avId, '%s|%s' % (place, places)) + print 'kartingCircuitFinished', avId, '%s|%s' % (place, places) + entryFee = RaceGlobals.getEntryFee(race.trackId, race.raceType) + placeMultiplier = RaceGlobals.Winnings[place - 1 + (RaceGlobals.MaxRacers - places)] + winnings = int(entryFee * placeMultiplier) + newHistory = self.getNewCircuitHistory(race, avId, place) + if newHistory: + self.notify.debug('history %s' % newHistory) + trophies = self.checkForCircuitTrophies(race, avId, newHistory) + av.b_setKartingHistory(newHistory) + if trophies: + self.updateTrophiesFromList(avId, trophies) + else: + self.notify.debug('no new history') + trophies = self.checkForNonRaceTrophies(avId) + oldTickets = av.getTickets() + self.notify.debug('endCircuitRace: old tickets: %s' % oldTickets) + newTickets = oldTickets + winnings + entryFee + self.air.writeServerEvent('kartingTicketsWonCircuit', avId, '%s' % (newTickets - oldTickets)) + self.notify.debug('entry fee: %s' % entryFee) + self.notify.debug('place mult: %s' % placeMultiplier) + self.notify.debug('winnings: %s' % winnings) + self.notify.debug('new tickets: %s' % newTickets) + self.notify.debug('trophies: %s' % trophies) + self.notify.debug('bonus: %s' % bonus) + av.b_setTickets(newTickets) + finalBonus = 0 + if race.circuitTotalBonusTickets.has_key(avId): + finalBonus = race.circuitTotalBonusTickets[avId] + race.d_setCircuitPlace(avId, place, entryFee, winnings, finalBonus, trophies) + + race.playersFinished = finalStandings + race.d_endCircuitRace() + + def updateTrophiesFromList(self, avId, trophies): + self.air.writeServerEvent('Writing Trophies: Updating trophies from list', avId, '%s' % trophies) + av = self.air.doId2do.get(avId) + if not av: + return + if not trophies: + return + trophyField = av.getKartingTrophies() + for trophy in trophies: + trophyField[trophy] = 1 + + av.b_setKartingTrophies(trophyField) + + def checkForCircuitTrophies(self, race, avId, inHistory=None): + av = self.air.doId2do.get(avId) + if not av: + return [] + trophies = av.getKartingTrophies() + if inHistory: + history = inHistory + else: + history = av.getKartingHistory() + winIndex = RaceGlobals.CircuitWins + winReqList = RaceGlobals.WonCircuitRaces + winIndices = RaceGlobals.CircuitWinsList + sweepIndex = RaceGlobals.CircuitSweeps + sweepReqList = RaceGlobals.SweptCircuitRaces + sweepIndices = RaceGlobals.CircuitSweepsList + qualIndex = RaceGlobals.CircuitQuals + qualReqList = RaceGlobals.QualifiedCircuitRaces + qualIndices = RaceGlobals.CircuitQualList + trophies = av.getKartingTrophies() + newTrophies = [] + newTrophies.extend(self.checkHistoryForTrophy(trophies, history, winIndex, winReqList, winIndices)) + newTrophies.extend(self.checkHistoryForTrophy(trophies, history, sweepIndex, sweepReqList, sweepIndices)) + newTrophies.extend(self.checkHistoryForTrophy(trophies, history, qualIndex, qualReqList, qualIndices)) + self.notify.debug('GrandTouring: Checking from branch: Circuit %s' % avId) + newTrophies.extend(self.checkForNonRaceTrophies(avId, history)) + newTrophies.sort() + return newTrophies + + def checkForRaceTrophies(self, race, avId, inHistory=None): + av = self.air.doId2do.get(avId) + if not av: + return [] + trophies = av.getKartingTrophies() + if inHistory: + history = inHistory + else: + history = av.getKartingHistory() + newTrophies = [] + numTrackGenres = len(RaceGlobals.WinsList) + for genre in range(numTrackGenres): + singleGenreNewTrophies = self.checkHistoryForTrophy(trophies, history, RaceGlobals.WinsList[genre], RaceGlobals.WonRaces, RaceGlobals.AllWinsList[genre]) + newTrophies.extend(singleGenreNewTrophies) + + numTrackGenres = len(RaceGlobals.QualsList) + for genre in range(numTrackGenres): + singleGenreNewTrophies = self.checkHistoryForTrophy(trophies, history, RaceGlobals.QualsList[genre], RaceGlobals.QualifiedRaces, RaceGlobals.AllQualsList[genre]) + newTrophies.extend(singleGenreNewTrophies) + + totalWins = 0 + for genre in range(numTrackGenres): + totalWins += history[RaceGlobals.WinsList[genre]] + + singleGenreNewTrophies = self.checkHistoryForTrophyByValue(trophies, history, totalWins, [ + RaceGlobals.TotalWonRaces], [ + RaceGlobals.TotalWins]) + newTrophies.extend(singleGenreNewTrophies) + totalQuals = 0 + for genre in range(numTrackGenres): + totalQuals += history[RaceGlobals.QualsList[genre]] + + singleGenreNewTrophies = self.checkHistoryForTrophyByValue(trophies, history, totalQuals, [ + RaceGlobals.TotalQualifiedRaces], [ + RaceGlobals.TotalQuals]) + newTrophies.extend(singleGenreNewTrophies) + self.notify.debug('GrandTouring: Checking from branch: Race %s ' % avId) + newTrophies.extend(self.checkForNonRaceTrophies(avId, history)) + newTrophies.sort() + return newTrophies + + def checkForNonRaceTrophies(self, avId, inHistory=None): + self.notify.debug('CHECKING FOR NONRACE TROPHIES') + self.notify.debug('GrandTouring: Checking for non-race trophies %s' % avId) + av = self.air.doId2do.get(avId) + if not av: + self.notify.debug('NO AV %s' % avId) + self.notify.debug("GrandTouring: can't convert avId to Av %s" % avId) + return [] + trophies = av.getKartingTrophies() + if inHistory: + history = inHistory + else: + history = av.getKartingHistory() + newTrophies = [] + self.notify.debug('GrandTouring: history-- %s' % history) + self.notify.debug('GrandTouring: trophies- %s' % trophies) + addTrophyCount = 0 + if not trophies[RaceGlobals.GrandTouring]: + self.notify.debug('checking for grand touring') + self.notify.debug('GrandTouring: checking for grand touring %s' % trophies[RaceGlobals.GrandTouring]) + best = av.getKartingPersonalBestAll() + self.notify.debug('personal best %s' % best) + self.notify.debug('GrandTouring: checking personal best %s' % best) + counter = 0 + for time in best: + if not time == 0: + counter += 1 + + self.notify.debug('counter %s tracks %s' % (counter, len(RaceGlobals.TrackDict))) + self.notify.debug('GrandTouring: bests comparison counter: %s tracks: %s' % (counter, len(RaceGlobals.TrackDict))) + if counter >= len(RaceGlobals.TrackDict): + newTrophies.append(RaceGlobals.GrandTouring) + addTrophyCount += 1 + self.air.writeServerEvent('kartingTrophy', avId, '%s' % RaceGlobals.GrandTouring) + self.notify.debug('trophy: ' + TTLocalizer.KartTrophyDescriptions[RaceGlobals.GrandTouring]) + self.notify.debug('GrandTouring: awarding grand touring new trophies %s' % newTrophies) + else: + self.notify.debug('already has grandtouring') + self.notify.debug('trophies %s' % trophies) + self.notify.debug('GrandTouring: already has grand touring %s' % trophies[RaceGlobals.GrandTouring]) + for i in range(1, RaceGlobals.NumTrophies / RaceGlobals.TrophiesPerCup + 1): + cupNum = (trophies[:RaceGlobals.NumTrophies].count(1) + addTrophyCount) / (i * RaceGlobals.TrophiesPerCup) + self.notify.debug('cupNum: %s' % cupNum) + trophyIndex = RaceGlobals.TrophyCups[i - 1] + if cupNum and not trophies[trophyIndex]: + newTrophies.append(trophyIndex) + oldMaxHp = av.getMaxHp() + newMaxHp = min(ToontownGlobals.MaxHpLimit, oldMaxHp + 1) + self.notify.debug('cup awarded! new max laff : %s' % newMaxHp) + av.b_setMaxHp(newMaxHp) + av.toonUp(newMaxHp) + self.air.writeServerEvent('kartingTrophy', avId, '%s' % trophyIndex) + self.notify.debug('trophy: ' + TTLocalizer.KartTrophyDescriptions[trophyIndex]) + + self.notify.debug('NONRACE TROPHIES %s' % newTrophies) + return newTrophies + + def checkHistoryForTrophyByValue(self, trophies, history, historyIndexValue, trophyReqList, trophyIndices): + newTrophies = [] + self.notify.debug('Checking History for Trophy') + self.notify.debug('Index %s Num %s ReqList %s Indices %s' % (0, historyIndexValue, trophyReqList, trophyIndices)) + for index in range(len(trophyIndices)): + if not trophies[trophyIndices[index]]: + if historyIndexValue >= trophyReqList[index]: + trophies[trophyIndices[index]] = 1 + newTrophies.append(trophyIndices[index]) + + return newTrophies + + def checkHistoryForTrophy(self, trophies, history, historyIndex, trophyReqList, trophyIndices): + newTrophies = [] + self.notify.debug('Checking History for Trophy') + self.notify.debug('Index %s Num %s ReqList %s Indices %s' % (historyIndex, history[historyIndex], trophyReqList, trophyIndices)) + for index in range(len(trophyIndices)): + if not trophies[trophyIndices[index]]: + if history[historyIndex] >= trophyReqList[index]: + trophies[trophyIndices[index]] = 1 + newTrophies.append(trophyIndices[index]) + + return newTrophies + + def mergeHistories(self, historyA, historyB): + newHistorySize = len(historyA) + if len(historyB) > len(historyA): + newHistorySize = historyB + mergedHistory = [] + for index in range(newHistorySize): + mergedHistory[index] = 0 + if len(historyA) > index: + if historyA[index] > mergedHistory[index]: + historyA[index] = mergedHistory[index] + if len(historyB) > index: + if historyB[index] > mergedHistory[index]: + historyB[index] = mergedHistory[index] + + return mergedHistory + + def getNewSingleRaceHistory(self, race, avId, positionFinished): + newHistory = 0 + av = self.air.doId2do.get(avId) + if not av: + return [] + history = av.getKartingHistory() + trackGenre = RaceGlobals.getTrackGenre(race.trackId) + winIndex = RaceGlobals.WinsList[trackGenre] + winReqList = RaceGlobals.WonRaces + qualIndex = RaceGlobals.QualsList[trackGenre] + qualReqList = RaceGlobals.QualifiedRaces + if history[winIndex] < winReqList[-1] and positionFinished == 1: + history[winIndex] += 1 + self.notify.debug('New History Won!') + newHistory = 1 + if history[qualIndex] < qualReqList[-1]: + history[qualIndex] += 1 + self.notify.debug('New History Qualified!') + newHistory = 1 + if newHistory: + return history + + def getNewCircuitHistory(self, race, avId, positionFinished): + newHistory = 0 + av = self.air.doId2do.get(avId) + if not av: + return [] + history = av.getKartingHistory() + trackGenre = RaceGlobals.getTrackGenre(race.trackId) + historyIndex = RaceGlobals.CircuitWins + trophyReqList = RaceGlobals.WonCircuitRaces + sweepIndices = RaceGlobals.CircuitSweepsList + sweepReqList = RaceGlobals.SweptCircuitRaces + self.notify.debug('getNewCircuitHistory: avId=%d positionFinished=%d history =%s' % (avId, positionFinished, history)) + if history[historyIndex] < trophyReqList[-1] and positionFinished == 1: + history[historyIndex] += 1 + self.notify.debug('New History Won!') + newHistory = 1 + swept = 0 + totalPoints = sum(race.circuitPoints[avId]) + if totalPoints == len(race.circuitPoints[avId]) * RaceGlobals.CircuitPoints[0]: + swept = 1 + if swept: + if history[RaceGlobals.CircuitSweeps] < sweepReqList[-1]: + if not history[RaceGlobals.CircuitSweeps]: + history[RaceGlobals.CircuitSweeps] = 0 + history[RaceGlobals.CircuitSweeps] += 1 + self.notify.debug('New History Swept!') + newHistory = 1 + qualified = 0 + self.notify.debug('qual times %s' % race.qualTimes) + self.notify.debug('avatar times %s' % race.circuitTimeList[avId]) + qualified = 1 + self.notify.debug('End Race Circuit Time List %s' % race.circuitTimeList) + self.notify.debug('check for qualify') + for qual in race.circuitTimeList[avId]: + self.notify.debug('qual %s' % qual) + if qual[1] == -1: + qualified = 0 + self.notify.debug('not qualified') + + if qualified: + self.notify.debug('qualified has %s needs %s' % (history[RaceGlobals.CircuitQuals], RaceGlobals.QualifiedCircuitRaces[-1])) + if history[RaceGlobals.CircuitQuals] < RaceGlobals.QualifiedCircuitRaces[-1]: + history[RaceGlobals.CircuitQuals] += 1 + self.notify.debug('New History qualified!') + newHistory = 1 + if newHistory: + return history + + def raceOver(self, race, normalExit=True): + if race in self.races: + if normalExit and race.isCircuit() and not race.isLastRace(): + nextTrackId = race.circuitLoop[0] + continuingAvs = [] + for avId in race.avIds: + if avId not in race.kickedAvIds: + continuingAvs.append(avId) + + lastRace = False + if len(continuingAvs) > 0: + raceZone = self.createRace(nextTrackId, race.raceType, race.lapCount, continuingAvs, race.circuitLoop[1:], race.circuitPoints, race.circuitTimes, race.qualTimes, race.circuitTimeList, race.circuitTotalBonusTickets) + race.sendToonsToNextCircuitRace(raceZone, nextTrackId) + else: + lastRace = True + self.races.remove(race) + race.requestDelete(lastRace) + else: + self.races.remove(race) + race.requestDelete() + + def checkForTrophies(self, place, trackId, raceType, numRacers, avId): + av = self.air.doId2do.get(avId) + outHistory = av.getKartingHistory() + trophies = av.getKartingTrophies() + trophiesWon = [] + trackGenre = RaceGlobals.getTrackGenre(trackId) + if place == 1: + historyIndex = RaceGlobals.WinsList[trackGenre] + trophyIndices = RaceGlobals.AllWinsList[trackGenre] + trophyReqList = RaceGlobals.WonRaces + historyTotalList = RaceGlobals.WinsList + totalTrophyIndex = RaceGlobals.TotalWins + totalReq = RaceGlobals.TotalWonRaces + trophiesWon += self.checkForTrophy(place, trackId, raceType, numRacers, avId, historyIndex, trophyIndices, trophyReqList, historyTotalList, totalTrophyIndex, totalReq) + historyIndex = RaceGlobals.QualsList[trackGenre] + trophyIndices = RaceGlobals.AllQualsList[trackGenre] + trophyReqList = RaceGlobals.QualifiedRaces + historyTotalList = RaceGlobals.QualsList + totalTrophyIndex = RaceGlobals.TotalQuals + totalReq = RaceGlobals.TotalQualifiedRaces + trophiesWon += self.checkForTrophy(place, trackId, raceType, numRacers, avId, historyIndex, trophyIndices, trophyReqList, historyTotalList, totalTrophyIndex, totalReq) + if not trophies[RaceGlobals.GrandTouring]: + self.notify.debug('checking for grand touring') + best = av.getKartingPersonalBestAll() + self.notify.debug('personal best %s' % best) + counter = 0 + for time in best: + if not time == 0: + counter += 1 + + self.notify.debug('counter %s tracks %s' % (counter, len(RaceGlobals.TrackDict))) + if counter >= len(RaceGlobals.TrackDict): + trophiesWon.append(RaceGlobals.GrandTouring) + if outHistory: + av.b_setKartingHistory(outHistory) + if len(trophiesWon): + for trophy in trophiesWon: + trophies[trophy] = 1 + + av.b_setKartingTrophies(trophies) + trophiesWon.sort() + return trophiesWon + + def checkForTrophy(self, place, trackId, raceType, numRacers, avId, historyIndex, trophyIndices, trophyReqList, historyTotalList, totalTrophyIndex, totalReq): + av = self.air.doId2do.get(avId) + if not av: + return [] + history = av.getKartingHistory() + newHistory = 0 + trophies = av.getKartingTrophies() + newTrophies = [] + if history[historyIndex] < trophyReqList[-1]: + history[historyIndex] += 1 + newHistory = 1 + for i in range(0, len(trophyReqList)): + if not trophies[trophyIndices[i]]: + if history[historyIndex] == trophyReqList[i]: + trophies[trophyIndices[i]] = 1 + newTrophies.append(trophyIndices[i]) + self.air.writeServerEvent('kartingTrophy', avId, '%s' % trophyIndices[i]) + self.notify.debug('trophy: ' + TTLocalizer.KartTrophyDescriptions[trophyIndices[i]]) + break + + if not raceType == RaceGlobals.Circuit: + if not trophies[totalTrophyIndex]: + total = 0 + for i in historyTotalList: + total += history[i] + + self.notify.debug('checking for total trophy: %s >= %s' % (total, totalReq)) + if total >= totalReq: + trophies[totalTrophyIndex] = 1 + newTrophies.append(totalTrophyIndex) + self.air.writeServerEvent('kartingTrophy', avId, '%s' % totalTrophyIndex) + self.notify.debug('trophy: ' + TTLocalizer.KartTrophyDescriptions[totalTrophyIndex]) + for i in range(1, RaceGlobals.NumTrophies / RaceGlobals.TrophiesPerCup + 1): + cupNum = trophies[:RaceGlobals.NumTrophies].count(1) / (i * RaceGlobals.TrophiesPerCup) + self.notify.debug('cupNum: %s' % cupNum) + trophyIndex = RaceGlobals.TrophyCups[i - 1] + if cupNum and not trophies[trophyIndex]: + trophies[trophyIndex] = 1 + newTrophies.append(trophyIndex) + oldMaxHp = av.getMaxHp() + newMaxHp = min(ToontownGlobals.MaxHpLimit, oldMaxHp + 1) + self.notify.debug('cup awarded! new max laff : %s' % newMaxHp) + av.b_setMaxHp(newMaxHp) + av.toonUp(newMaxHp) + self.air.writeServerEvent('kartingTrophy', avId, '%s' % trophyIndex) + self.notify.debug('trophy: ' + TTLocalizer.KartTrophyDescriptions[trophyIndex]) + + self.notify.debug('newTrophies: %s' % newTrophies) + self.notify.debug('trophies: %s' % trophies) + self.notify.debug('kartingHistory: %s' % history) + if newHistory: + av.b_setKartingHistory(history) + return newTrophies + + def checkPersonalBest(self, trackId, time, raceType, numRacers, avId): + av = simbase.air.doId2do.get(avId) + if av: + bestTimes = av.getKartingPersonalBestAll() + trackIndex = RaceGlobals.TrackIds.index(trackId) + bestTime = bestTimes[trackIndex] + self.notify.debug('thisTime: %s, bestTime: %s' % (time, bestTime)) + if bestTime == 0.0 or time < bestTime: + bestTimes[trackIndex] = time + self.notify.debug('new personal best!') + av.b_setKartingPersonalBest(bestTimes) + self.notify.debug('personal best: %s' % bestTimes) + + def checkTimeRecord(self, trackId, time, raceType, numRacers, avId): + bonus = 0 + newRecord = 0 + for period in RaceGlobals.PeriodIds: + for record in range(0, RaceGlobals.NumRecordsPerPeriod): + recordTime = self.trackRecords[trackId][period][record][0] + if time < recordTime: + newRecord = 1 + self.notify.debug('new %s record!' % TTLocalizer.RecordPeriodStrings[period]) + av = simbase.air.doId2do.get(avId) + if av: + name = av.name + self.trackRecords[trackId][period].insert(record, (time, raceType, numRacers, name)) + self.trackRecords[trackId][period] = self.trackRecords[trackId][period][:RaceGlobals.NumRecordsPerPeriod] + self.updateLeaderboards(trackId, period) + bonus = RaceGlobals.PeriodDict[period] + self.air.writeServerEvent('kartingRecord', avId, '%s|%s|%s' % (period, trackId, time)) + else: + self.notify.warning('warning: av not logged in!') + break + + if newRecord: + self.updateRecordFile() + return bonus + + def updateRecordFile(self): + try: + backup = self.filename + '.bu' + if os.path.exists(self.filename): + os.rename(self.filename, backup) + file = open(self.filename, 'w') + file.seek(0) + cPickle.dump(self.trackRecords, file) + file.close() + if os.path.exists(backup): + os.remove(backup) + except EnvironmentError: + self.notify.warning(str(sys.exc_info()[1])) + + def getFilename(self): + return '%s%s.trackRecords' % (self.serverDataFolder, self.shard) + + def loadRecords(self): + try: + file = open(self.filename + '.bu', 'r') + if os.path.exists(self.filename): + os.remove(self.filename) + except IOError: + try: + file = open(self.filename, 'r') + except IOError: + return self.getRecordTimes() + + file.seek(0) + records = self.loadFrom(file) + file.close() + for trackId in RaceGlobals.TrackIds: + if not records.has_key(trackId): + records[trackId] = {} + for i in RaceGlobals.PeriodIds: + records[trackId][i] = [] + for j in range(0, RaceGlobals.NumRecordsPerPeriod): + records[trackId][i].append(RaceGlobals.getDefaultRecord(trackId)) + + self.resetLeaderboards() + return records + + def loadFrom(self, file): + records = {} + try: + while 1: + records = cPickle.load(file) + + except EOFError: + pass + + return records + + def getRecordTimes(self): + records = {} + for trackId in RaceGlobals.TrackIds: + records[trackId] = {} + for i in RaceGlobals.PeriodIds: + records[trackId][i] = [] + for j in range(0, RaceGlobals.NumRecordsPerPeriod): + records[trackId][i].append(RaceGlobals.getDefaultRecord(trackId)) + + return records + + def resetRecordPeriod(self, period): + for trackId in RaceGlobals.TrackIds: + for i in range(0, RaceGlobals.NumRecordsPerPeriod): + self.trackRecords[trackId][period][i] = RaceGlobals.getDefaultRecord(trackId) + + self.updateLeaderboards(trackId, period) + + self.updateRecordFile() + + def getRecords(self, trackId, period): + return self.trackRecords[trackId][period] + + def updateLeaderboards(self, trackId, period): + messenger.send('UpdateRaceRecord', [(trackId, period)]) + + def resetLeaderboards(self): + for track in RaceGlobals.TrackIds: + for period in RaceGlobals.PeriodIds: + self.updateLeaderboards(track, period) + + +class KartRecordDailyResetter(HolidayBaseAI.HolidayBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('ResistanceEventMgrAI') + PostName = 'kertRecordDailyReset' + + def __init__(self, air, holidayId): + HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId) + + def start(self): + bboard.post(KartRecordDailyResetter.PostName) + simbase.air.raceMgr.resetRecordPeriod(RaceGlobals.Daily) + + def stop(self): + bboard.remove(KartRecordDailyResetter.PostName) + + +class KartRecordWeeklyResetter(HolidayBaseAI.HolidayBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('ResistanceEventMgrAI') + PostName = 'kartRecordWeeklyReset' + + def __init__(self, air, holidayId): + HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId) + + def start(self): + bboard.post(KartRecordWeeklyResetter.PostName) + simbase.air.raceMgr.resetRecordPeriod(RaceGlobals.Weekly) + + def stop(self): + bboard.remove(KartRecordWeeklyResetter.PostName) + + +class CircuitRaceHolidayMgr(HolidayBaseAI.HolidayBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('CircuitRaceHolidayMgr') + PostName = 'CircuitRaceHoliday' + StartStopMsg = 'CircuitRaceHolidayStartStop' + + def __init__(self, air, holidayId): + HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId) + + def start(self): + bboard.post(CircuitRaceHolidayMgr.PostName, True) + simbase.air.newsManager.circuitRaceStart() + messenger.send(CircuitRaceHolidayMgr.StartStopMsg) + + def stop(self): + bboard.remove(CircuitRaceHolidayMgr.PostName) + simbase.air.newsManager.circuitRaceEnd() + messenger.send(CircuitRaceHolidayMgr.StartStopMsg) diff --git a/toontown/racing/__init__.py b/toontown/racing/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/rpc/AwardManager.py b/toontown/rpc/AwardManager.py new file mode 100644 index 0000000..c6f8ae1 --- /dev/null +++ b/toontown/rpc/AwardManager.py @@ -0,0 +1,4 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal + +class AwardManager(DistributedObjectGlobal): + pass diff --git a/toontown/rpc/AwardManagerConsts.py b/toontown/rpc/AwardManagerConsts.py new file mode 100644 index 0000000..bdf5800 --- /dev/null +++ b/toontown/rpc/AwardManagerConsts.py @@ -0,0 +1,20 @@ +GiveAwardErrors = Enum('Success, WrongGender, NotGiftable, FullMailbox, FullAwardMailbox, AlreadyInMailbox, AlreadyInGiftQueue, AlreadyInOrderedQueue, AlreadyInCloset, AlreadyBeingWorn, AlreadyInAwardMailbox, AlreadyInThirtyMinuteQueue, AlreadyInMyPhrases, AlreadyKnowDoodleTraining, AlreadyRented, GenericAlreadyHaveError, UnknownError, UnknownToon, NonToon,') +GiveAwardErrorStrings = {GiveAwardErrors.Success: 'success', + GiveAwardErrors.WrongGender: 'wrong gender', + GiveAwardErrors.NotGiftable: 'item is not giftable', + GiveAwardErrors.FullMailbox: 'mailbox is full', + GiveAwardErrors.FullAwardMailbox: 'award mailbox is full', + GiveAwardErrors.AlreadyInMailbox: 'award already in mailbox.', + GiveAwardErrors.AlreadyInGiftQueue: 'award already in gift queue.', + GiveAwardErrors.AlreadyInOrderedQueue: 'award already in ordered queue.', + GiveAwardErrors.AlreadyInCloset: 'award already in closet.', + GiveAwardErrors.AlreadyBeingWorn: 'award already being worn.', + GiveAwardErrors.AlreadyInAwardMailbox: 'award already in award mailbox', + GiveAwardErrors.AlreadyInThirtyMinuteQueue: 'award already in 30 minute queue', + GiveAwardErrors.AlreadyInMyPhrases: 'speed chat award already in my phrases', + GiveAwardErrors.AlreadyKnowDoodleTraining: 'doodle training award already known', + GiveAwardErrors.AlreadyRented: 'award is already rented', + GiveAwardErrors.GenericAlreadyHaveError: 'generic-already-have error', + GiveAwardErrors.UnknownError: 'unknown error', + GiveAwardErrors.UnknownToon: 'toon not in database', + GiveAwardErrors.NonToon: 'this is not a toon'} diff --git a/toontown/rpc/RATManager.py b/toontown/rpc/RATManager.py new file mode 100644 index 0000000..37035aa --- /dev/null +++ b/toontown/rpc/RATManager.py @@ -0,0 +1,4 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal + +class RATManager(DistributedObjectGlobal): + pass diff --git a/toontown/rpc/__init__.py b/toontown/rpc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/safezone/BRPlayground.py b/toontown/safezone/BRPlayground.py new file mode 100644 index 0000000..360f0f4 --- /dev/null +++ b/toontown/safezone/BRPlayground.py @@ -0,0 +1,59 @@ +from pandac.PandaModules import * +import Playground +from direct.task.Task import Task +import random +from toontown.hood import Place +from toontown.toonbase import ToontownGlobals + +class BRPlayground(Playground.Playground): + STILL = 1 + RUN = 2 + ROTATE = 3 + stillPos = Point3(0, 20, 8) + runPos = Point3(0, 60, 8) + rotatePos = Point3(0, 0, 8) + timeFromStill = 1.0 + timeFromRotate = 2.0 + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Playground.Playground.load(self) + + def unload(self): + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + self.nextWindTime = 0 + taskMgr.add(self.__windTask, 'br-wind') + self.state = 0 + + def exit(self): + taskMgr.remove('br-wind') + taskMgr.remove('lerp-snow') + Playground.Playground.exit(self) + + def enterTunnelOut(self, requestStatus): + Place.Place.enterTunnelOut(self, requestStatus) + + def __windTask(self, task): + now = globalClock.getFrameTime() + if now < self.nextWindTime: + return Task.cont + randNum = random.random() + wind = int(randNum * 100) % 3 + 1 + if wind == 1: + base.playSfx(self.loader.wind1Sound) + elif wind == 2: + base.playSfx(self.loader.wind2Sound) + elif wind == 3: + base.playSfx(self.loader.wind3Sound) + self.nextWindTime = now + randNum * 8.0 + return Task.cont + + def showPaths(self): + from toontown.classicchars import CCharPaths + from toontown.toonbase import TTLocalizer + self.showPathPoints(CCharPaths.getPaths(TTLocalizer.Pluto)) diff --git a/toontown/safezone/BRSafeZoneLoader.py b/toontown/safezone/BRSafeZoneLoader.py new file mode 100644 index 0000000..e362f0b --- /dev/null +++ b/toontown/safezone/BRSafeZoneLoader.py @@ -0,0 +1,76 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +import SafeZoneLoader +import BRPlayground +from toontown.battle import BattleParticles + +class BRSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + SnowFadeLerpTime = 2.0 + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = BRPlayground.BRPlayground + self.musicFile = 'phase_8/audio/bgm/TB_nbrhood.mid' + self.activityMusicFile = 'phase_8/audio/bgm/TB_SZ_activity.mid' + self.dnaFile = 'phase_8/dna/the_burrrgh_sz.dna' + self.safeZoneStorageDNAFile = 'phase_8/dna/storage_BR_sz.dna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.wind1Sound = base.loadSfx('phase_8/audio/sfx/SZ_TB_wind_1.mp3') + self.wind2Sound = base.loadSfx('phase_8/audio/sfx/SZ_TB_wind_2.mp3') + self.wind3Sound = base.loadSfx('phase_8/audio/sfx/SZ_TB_wind_3.mp3') + self.snow = BattleParticles.loadParticleFile('snowdisk.ptf') + self.snow.setPos(0, 0, 5) + self.snowRender = self.geom.attachNewNode('snowRender') + self.snowRender.setDepthWrite(0) + self.snowRender.setBin('fixed', 1) + self.snowFade = None + return + + def unload(self): + del self.wind1Sound + del self.wind2Sound + del self.wind3Sound + del self.snow + del self.snowRender + SafeZoneLoader.SafeZoneLoader.unload(self) + + def enter(self, requestStatus): + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + self.snow.start(camera, self.snowRender) + self.accept('enterigloo-interior', self.enterIgloo) + self.accept('exitigloo-interior', self.exitIgloo) + + def exit(self): + self.ignore('enterigloo-interior') + self.ignore('exitigloo-interior') + self.resetSnowLerp() + self.snow.cleanup() + SafeZoneLoader.SafeZoneLoader.exit(self) + + def enterIgloo(self, entry): + self.fadeOutSnow() + + def exitIgloo(self, entry): + self.fadeInSnow() + + def resetSnowLerp(self): + if self.snowFade != None: + self.snowFade.stop() + self.snowFade = None + return + + def fadeInSnow(self): + self.resetSnowLerp() + currentScale = self.snowRender.getColorScale()[3] + ivals = [LerpFunctionInterval(self.snowRender.setAlphaScale, fromData=currentScale, toData=1.0, duration=self.SnowFadeLerpTime), FunctionInterval(self.snowRender.clearColorScale)] + self.snowFade = Track(ivals, 'snow-fade') + self.snowFade.play() + + def fadeOutSnow(self): + self.resetSnowLerp() + currentScale = self.snowRender.getColorScale()[3] + ivals = [LerpFunctionInterval(self.snowRender.setAlphaScale, fromData=currentScale, toData=0.0, duration=self.SnowFadeLerpTime)] + self.snowFade = Track(ivals, 'snow-fade') + self.snowFade.play() diff --git a/toontown/safezone/BRTreasurePlannerAI.py b/toontown/safezone/BRTreasurePlannerAI.py new file mode 100644 index 0000000..c2307c0 --- /dev/null +++ b/toontown/safezone/BRTreasurePlannerAI.py @@ -0,0 +1,30 @@ +from toontown.toonbase.ToontownGlobals import * +import RegenTreasurePlannerAI, DistributedBRTreasureAI + +class BRTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + + def __init__(self, zoneId): + self.healAmount = 12 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedBRTreasureAI.DistributedBRTreasureAI, 'BRTreasurePlanner', 20, 2) + return None + + def initSpawnPoints(self): + self.spawnPoints = [(-108, 46, 6.2), + (-111, 74, 6.2), + (-126, 81, 6.2), + (-74, -75, 3.0), + (-136, -51, 3.0), + (-20, 35, 6.2), + (-55, 109, 6.2), + (58, -57, 6.2), + (-42, -134, 6.2), + (-68, -148, 6.2), + (-1, -62, 6.2), + (25, 2, 6.2), + (-133, 53, 6.2), + (-99, 86, 6.2), + (30, 63, 6.2), + (-147, 3, 6.2), + (-135, -102, 6.2), + (35, -98, 6.2)] + return self.spawnPoints diff --git a/toontown/safezone/ButterflyGlobals.py b/toontown/safezone/ButterflyGlobals.py new file mode 100644 index 0000000..d185f86 --- /dev/null +++ b/toontown/safezone/ButterflyGlobals.py @@ -0,0 +1,262 @@ +from pandac.PandaModules import * +import random +OFF = 0 +FLYING = 1 +LANDED = 2 +states = {OFF: 'off', + FLYING: 'Flying', + LANDED: 'Landed'} +NUM_BUTTERFLIES = (6, 36, 5) +NUM_BUTTERFLY_AREAS = (4, 1, 4) +BUTTERFLY_SPEED = 2.0 +BUTTERFLY_HEIGHT = (2.2, 3.2, 2.2) +BUTTERFLY_TAKEOFF = (1.4, 1.8, 1.4) +BUTTERFLY_LANDING = (1.4, 1.8, 1.4) +MAX_LANDED_TIME = 20.0 +TTC = 0 +DG = 1 +ESTATE = 2 +ButterflyPoints = (((Point3(84.0, -116.0, 3.5), + Point3(95.0, -144.0, 2.6), + Point3(94.0, -145.0, 2.6), + Point3(95.0, -149.0, 2.6), + Point3(50.0, -155.0, 2.6), + Point3(51.0, -147.0, 2.6), + Point3(51.0, -145.0, 2.6), + Point3(14.0, -99.0, 3.1), + Point3(17.0, -94.0, 3.1), + Point3(50.0, -79.0, 3.1), + Point3(47.0, -86.0, 3.1), + Point3(54.0, -127.0, 2.6), + Point3(84.0, -113.0, 3.8)), + (Point3(-57.0, -70.0, 0.1), + Point3(-55.0, -68.0, 0.1), + Point3(-90.0, -77.0, 0.6), + Point3(-90.0, -72.0, 0.1), + Point3(-133.0, -50.0, 0.6), + Point3(-129.0, -48.0, 0.6), + Point3(-127.0, -25.0, 0.1), + Point3(-125.0, -22.0, 0.1), + Point3(-123.0, -22.0, 0.1), + Point3(-103.0, -10.0, -3.0), + Point3(-104.0, -13.0, -2.5), + Point3(-100.0, -28.0, -2.7), + Point3(-89.0, -41.0, -4.4), + Point3(-58.0, -34.0, -4.1), + Point3(-69.0, -18.0, -1.9), + Point3(-65.0, -19.0, -1.9), + Point3(-65.0, -16.0, -1.9), + Point3(6.0, -49.0, -0.1), + Point3(2.6, -47.0, 0.1), + Point3(-33.6, -43.0, 0.0)), + (Point3(-53.0, 3.0, -1.8), + Point3(-58.0, 2.0, -1.8), + Point3(-58.0, 2.0, -1.8), + Point3(-76.0, 2.0, -1.8), + Point3(-69.0, 11.0, -1.8), + Point3(-100.0, 14.0, -4.1), + Point3(-104.0, 17.0, -2.6), + Point3(-125.0, 34.0, 0.1), + Point3(-124.0, 30.0, 0.1), + Point3(-113.0, 73.0, 0.6), + Point3(-33.0, 78.0, 0.1), + Point3(-65.0, 48.0, -3.0), + Point3(-51.0, 33.0, -3.0), + Point3(-30.0, 71.0, 0.1), + Point3(-26.0, 71.0, 0.1), + Point3(-23.0, 69.0, 0.1), + Point3(-23.0, 64.0, 0.1), + Point3(-5.0, 42.0, 0.1), + Point3(-22.0, 22.0, 0.1), + Point3(-27.0, 22.0, 0.1)), + (Point3(14.0, 93.0, 3.1), + Point3(17.0, 93.0, 3.1), + Point3(20.0, 122.0, 2.6), + Point3(21.0, 127.0, 2.6), + Point3(23.0, 123.0, 2.6), + Point3(32.0, 130.0, 2.6), + Point3(48.0, 148.0, 2.6), + Point3(64.0, 111.0, 2.6), + Point3(32.0, 82.0, 2.6), + Point3(63.0, 90.0, 3.1), + Point3(68.0, 85.0, 3.1), + Point3(65.0, 85.0, 3.1), + Point3(70.0, 95.0, 3.1))), ((Point3(-7.9, 22.9, 0.05), + Point3(-8.0, 17.0, 2.1), + Point3(-7.5, 18.0, 2.1), + Point3(-27.5, 70.7, 0.05), + Point3(-30.0, 70.0, 1.0), + Point3(-31.0, 69.0, 1.0), + Point3(-1.0, 53.0, 2.2), + Point3(-0.5, 53.0, 2.2), + Point3(35.0, 71.5, 1.0), + Point3(33.0, 69.0, 0.05), + Point3(45.0, 61.0, 0.05), + Point3(55.0, 62.0, 0.05), + Point3(80.0, 74.0, 0.05), + Point3(80.0, 73.0, 0.05), + Point3(76.0, 46.0, 0.05), + Point3(76.0, 45.0, 0.05), + Point3(77.0, 41.0, 0.05), + Point3(62.0, 28.0, 0.05), + Point3(48.0, 24.0, 0.05), + Point3(83.0, 122.0, 0.05), + Point3(82.0, 123.0, 0.05), + Point3(81.0, 81.0, 0.05), + Point3(38.0, 77.0, 0.05), + Point3(-26.0, 69.0, 0.05), + Point3(-26.0, 70.0, 0.05), + Point3(-61.0, 71.0, 0.05), + Point3(-61.0, 70.0, 0.05), + Point3(-78.0, 79.0, 0.05), + Point3(-99.0, 106.0, 0.05), + Point3(-99.0, 108.0, 0.05), + Point3(-80.0, 123.0, 0.05), + Point3(-77.0, 125.0, 0.05), + Point3(-32.0, 162.0, 0.05), + Point3(-3.0, 186.5, 2.2), + Point3(-3.2, 186.8, 2.2), + Point3(-1.0, 185.0, 2.2), + Point3(39.0, 165.0, 0.05), + Point3(42.0, 162.0, 0.05), + Point3(62.0, 145.0, 0.05), + Point3(64.0, 145.0, 0.05), + Point3(59.0, 102.0, 0.05), + Point3(32.7, 93.7, 0.05), + Point3(31.2, 90.8, 0.05), + Point3(29.8, 140.1, 0.05), + Point3(16.5, 146.3, 0.05), + Point3(15.3, 146.9, 0.05), + Point3(-24.3, 128.6, 0.05), + Point3(-67.9, 117.9, 0.05), + Point3(-41.6, 88.4, 0.05), + Point3(-13.6, 120.3, 0.05), + Point3(26.0, 117.8, 0.05), + Point3(22.6, 112.3, 0.05), + Point3(-8.2, 107.9, 0.05), + Point3(-18.1, 97.0, 0.05), + Point3(-21.4, 92.9, 0.05), + Point3(-2.1, 74.0, 0.05), + Point3(19.8, 93.5, 0.05), + Point3(21.4, 95.4, 0.05), + Point3(19.2, 97.5, 0.05), + Point3(-10.7, 143.3, 0.05), + Point3(38.2, 120.7, 0.05), + Point3(34.1, 101.5, 0.05), + Point3(32.4, 96.5, 0.05), + Point3(72.9, 121.8, 0.05)),), ((Point3(-40, -137, 0.025), + Point3(2.35, -167.95, 0.025), + Point3(70.8, -125.3, 0.025), + Point3(63.49, -67.4, 0.025), + Point3(17.5, -59.25, 0.623), + Point3(-51.87, -107.0, 0.723), + Point3(-20.325, -48.716, 4.884), + Point3(51.03, -67.244, 0.244), + Point3(20.02, -34.271, 7.105), + Point3(24.731, -20.905, 9.247)), + (Point3(88, -57.4, 0.025), + Point3(92.347, -7.71, 0.169), + Point3(129.39, 0.85, 0.025), + Point3(121.14, 37, 0.025), + Point3(126, 30.3, 0.025), + Point3(100.3, 21.2, 0.05), + Point3(103.42, 1.544, 0.025), + Point3(82.37, -45, 0.025), + Point3(103.8, 4.306, 0.05), + Point3(119.195, -42.042, 0.025)), + (Point3(10, 98.5, -0.028), + Point3(11.65, 92.52, -0.079), + Point3(-16.25, 86.67, 0.216), + Point3(-65.3, 67.8, 0.025), + Point3(-41.6, 67.0, 0.025), + Point3(-34.8, 68.9, 0.025), + Point3(-32.272, 56.65, 1.192), + Point3(-63.956, 39.678, 0.281), + Point3(-79.65, 36.99, 0.025), + Point3(-14.769, 72.399, 0.244)), + (Point3(-79.6, 36.9, 0.025), + Point3(-57.6, 27.24, 2.355), + Point3(-69.642, -28.137, 3.98), + Point3(-111, -58.1, 0.025), + Point3(-152.223, 25.627, 0.025), + Point3(-104.4, 43.5, 0.278), + Point3(-85.25, 10.513, 0.111), + Point3(-43.6, 1.644, 3.838), + Point3(-48.993, -21.968, 3.98), + Point3(-30.088, -5.987, 7.025)))) +allocatedIndexes = {} + +def generateIndexes(doId, playground): + usedI = [] + unusedI = [] + for area in ButterflyPoints[playground]: + usedI.append(range(0, len(area))) + unusedI.append([]) + + allocatedIndexes[doId] = (usedI, unusedI) + + +def clearIndexes(doId): + if allocatedIndexes.has_key(doId): + del allocatedIndexes[doId] + + +def getFirstRoute(playground, area, doId): + curPos, curIndex = __getCurrentPos(playground, area, doId) + destPos, destIndex, time = getNextPos(curPos, playground, area, doId) + return (curPos, + curIndex, + destPos, + destIndex, + time) + + +def __getCurrentPos(playground, area, doId): + if allocatedIndexes.has_key(doId): + unusedI = allocatedIndexes[doId][0][area] + usedI = allocatedIndexes[doId][1][area] + else: + return (ButterflyPoints[playground][area][0], 0) + if len(unusedI) == 0: + index = random.choice(usedI) + return (ButterflyPoints[playground][area][index], index) + index = random.choice(unusedI) + unusedI.remove(index) + usedI.append(index) + return (ButterflyPoints[playground][area][index], index) + + +def getNextPos(currentPos, playground, area, doId): + if allocatedIndexes.has_key(doId): + unusedI = allocatedIndexes[doId][0][area] + usedI = allocatedIndexes[doId][1][area] + else: + return (ButterflyPoints[playground][area][0], 0, 4.0) + nextPos = currentPos + while nextPos == currentPos: + if len(unusedI) == 0: + index = random.choice(usedI) + nextPos = ButterflyPoints[playground][area][index] + else: + index = random.choice(unusedI) + nextPos = ButterflyPoints[playground][area][index] + if nextPos != currentPos: + unusedI.remove(index) + usedI.append(index) + + dist = Vec3(nextPos - currentPos).length() + time = dist / BUTTERFLY_SPEED + BUTTERFLY_TAKEOFF[playground] + BUTTERFLY_LANDING[playground] + return (nextPos, index, time) + + +def recycleIndex(index, playground, area, doId): + if allocatedIndexes.has_key(doId): + unusedI = allocatedIndexes[doId][0][area] + usedI = allocatedIndexes[doId][1][area] + else: + return None + if usedI.count(index) > 0: + usedI.remove(index) + if unusedI.count(index) == 0: + unusedI.append(index) + return None diff --git a/toontown/safezone/CheckersBoard.py b/toontown/safezone/CheckersBoard.py new file mode 100644 index 0000000..15b1ed8 --- /dev/null +++ b/toontown/safezone/CheckersBoard.py @@ -0,0 +1,336 @@ + + +class CheckersBoard: + + def __init__(self): + self.squareList = [] + for x in range(32): + self.squareList.append(CheckersTile(x)) + + self.squareList[0].setAdjacent([None, + None, + 4, + None]) + self.squareList[1].setAdjacent([None, + 4, + 5, + None]) + self.squareList[2].setAdjacent([None, + 5, + 6, + None]) + self.squareList[3].setAdjacent([None, + 6, + 7, + None]) + self.squareList[4].setAdjacent([0, + 8, + 9, + 1]) + self.squareList[5].setAdjacent([1, + 9, + 10, + 2]) + self.squareList[6].setAdjacent([2, + 10, + 11, + 3]) + self.squareList[7].setAdjacent([3, + 11, + None, + None]) + self.squareList[8].setAdjacent([None, + None, + 12, + 4]) + self.squareList[9].setAdjacent([4, + 12, + 13, + 5]) + self.squareList[10].setAdjacent([5, + 13, + 14, + 6]) + self.squareList[11].setAdjacent([6, + 14, + 15, + 7]) + self.squareList[12].setAdjacent([8, + 16, + 17, + 9]) + self.squareList[13].setAdjacent([9, + 17, + 18, + 10]) + self.squareList[14].setAdjacent([10, + 18, + 19, + 11]) + self.squareList[15].setAdjacent([11, + 19, + None, + None]) + self.squareList[16].setAdjacent([None, + None, + 20, + 12]) + self.squareList[17].setAdjacent([12, + 20, + 21, + 13]) + self.squareList[18].setAdjacent([13, + 21, + 22, + 14]) + self.squareList[19].setAdjacent([14, + 22, + 23, + 15]) + self.squareList[20].setAdjacent([16, + 24, + 25, + 17]) + self.squareList[21].setAdjacent([17, + 25, + 26, + 18]) + self.squareList[22].setAdjacent([18, + 26, + 27, + 19]) + self.squareList[23].setAdjacent([19, + 27, + None, + None]) + self.squareList[24].setAdjacent([None, + None, + 28, + 20]) + self.squareList[25].setAdjacent([20, + 28, + 29, + 21]) + self.squareList[26].setAdjacent([21, + 29, + 30, + 22]) + self.squareList[27].setAdjacent([22, + 30, + 31, + 23]) + self.squareList[28].setAdjacent([24, + None, + None, + 25]) + self.squareList[29].setAdjacent([25, + None, + None, + 26]) + self.squareList[30].setAdjacent([26, + None, + None, + 27]) + self.squareList[31].setAdjacent([27, + None, + None, + None]) + self.squareList[0].setJumps([None, + None, + 9, + None]) + self.squareList[1].setJumps([None, + 8, + 10, + None]) + self.squareList[2].setJumps([None, + 9, + 11, + None]) + self.squareList[3].setJumps([None, + 10, + None, + None]) + self.squareList[4].setJumps([None, + None, + 13, + None]) + self.squareList[5].setJumps([None, + 12, + 14, + None]) + self.squareList[6].setJumps([None, + 13, + 15, + None]) + self.squareList[7].setJumps([None, + 14, + None, + None]) + self.squareList[8].setJumps([None, + None, + 17, + 1]) + self.squareList[9].setJumps([0, + 16, + 18, + 2]) + self.squareList[10].setJumps([1, + 17, + 19, + 3]) + self.squareList[11].setJumps([2, + 18, + None, + None]) + self.squareList[12].setJumps([None, + None, + 21, + 5]) + self.squareList[13].setJumps([4, + 20, + 22, + 6]) + self.squareList[14].setJumps([5, + 21, + 23, + 7]) + self.squareList[15].setJumps([6, + 22, + None, + None]) + self.squareList[16].setJumps([None, + None, + 25, + 9]) + self.squareList[17].setJumps([8, + 24, + 26, + 10]) + self.squareList[18].setJumps([9, + 25, + 27, + 11]) + self.squareList[19].setJumps([10, + 26, + None, + None]) + self.squareList[20].setJumps([None, + None, + 29, + 13]) + self.squareList[21].setJumps([12, + 28, + 30, + 14]) + self.squareList[22].setJumps([13, + 29, + 31, + 15]) + self.squareList[23].setJumps([14, + 30, + None, + None]) + self.squareList[24].setJumps([None, + None, + None, + 17]) + self.squareList[25].setJumps([16, + None, + None, + 18]) + self.squareList[26].setJumps([17, + None, + None, + 19]) + self.squareList[27].setJumps([18, + None, + None, + None]) + self.squareList[28].setJumps([None, + None, + None, + 21]) + self.squareList[29].setJumps([20, + None, + None, + 22]) + self.squareList[30].setJumps([21, + None, + None, + 23]) + self.squareList[31].setJumps([22, + None, + None, + None]) + return + + def delete(self): + for x in self.squareList: + x.delete() + + del self.squareList + + def getSquare(self, arrayLoc): + return self.squareList[arrayLoc] + + def getState(self, squareNum): + return self.squareList[squareNum].getState() + + def setState(self, squareNum, newState): + self.squareList[squareNum].setState(newState) + + def getAdjacent(self, squareNum): + return self.squareList[squareNum].adjacent + + def getStates(self): + retList = [] + for x in range(32): + retList.append(self.squareList[x].getState()) + + return retList + + def setStates(self, squares): + y = 0 + for x in range(32): + self.squareList[x].setState(squares[x]) + + def getJumps(self, squareNum): + return self.squareList[squareNum].jumps + + +class CheckersTile: + + def __init__(self, tileNum): + self.tileNum = tileNum + self.state = 0 + self.adjacent = [] + self.jumps = [] + + def delete(self): + del self.tileNum + del self.state + del self.adjacent + + def setJumps(self, jumpList): + for x in jumpList: + self.jumps.append(x) + + def getJumps(self): + return self.jumps + + def setAdjacent(self, adjList): + for x in adjList: + self.adjacent.append(x) + + def getAdjacent(self): + return self.adjacent + + def setState(self, newState): + self.state = newState + + def getState(self): + return self.state + + def getNum(self): + return self.tileNum diff --git a/toontown/safezone/ChineseCheckersBoard.py b/toontown/safezone/ChineseCheckersBoard.py new file mode 100644 index 0000000..a0a1f5d --- /dev/null +++ b/toontown/safezone/ChineseCheckersBoard.py @@ -0,0 +1,807 @@ + + +class ChineseCheckersBoard: + + def __init__(self): + self.squareList = [] + for x in range(121): + self.squareList.append(CheckersSquare(x)) + + self.squareList[0].setAdjacent([None, + 1, + 2, + None, + None, + None]) + self.squareList[1].setAdjacent([None, + 3, + 4, + 2, + 0, + None]) + self.squareList[2].setAdjacent([1, + 4, + 5, + None, + None, + 0]) + self.squareList[3].setAdjacent([None, + 6, + 7, + 4, + 1, + None]) + self.squareList[4].setAdjacent([3, + 7, + 8, + 5, + 2, + 1]) + self.squareList[5].setAdjacent([4, + 8, + 9, + None, + None, + 2]) + self.squareList[6].setAdjacent([None, + 14, + 15, + 7, + 3, + None]) + self.squareList[7].setAdjacent([6, + 15, + 16, + 8, + 4, + 3]) + self.squareList[8].setAdjacent([7, + 16, + 17, + 9, + 5, + 4]) + self.squareList[9].setAdjacent([8, + 17, + 18, + None, + None, + 5]) + self.squareList[10].setAdjacent([None, + None, + 23, + 11, + None, + None]) + self.squareList[11].setAdjacent([10, + 23, + 24, + 12, + None, + None]) + self.squareList[12].setAdjacent([11, + 24, + 25, + 13, + None, + None]) + self.squareList[13].setAdjacent([12, + 25, + 26, + 14, + None, + None]) + self.squareList[14].setAdjacent([13, + 26, + 27, + 15, + 6, + None]) + self.squareList[15].setAdjacent([14, + 27, + 28, + 16, + 7, + 6]) + self.squareList[16].setAdjacent([15, + 28, + 29, + 17, + 8, + 7]) + self.squareList[17].setAdjacent([16, + 29, + 30, + 18, + 9, + 8]) + self.squareList[18].setAdjacent([17, + 30, + 31, + 19, + None, + 9]) + (self.squareList[19].setAdjacent([18, + 31, + 32, + 20, + None, + None]),) + self.squareList[20].setAdjacent([19, + 32, + 33, + 21, + None, + None]) + self.squareList[21].setAdjacent([20, + 33, + 34, + 22, + None, + None]) + self.squareList[22].setAdjacent([21, + 34, + None, + None, + None, + None]) + self.squareList[23].setAdjacent([None, + None, + 35, + 24, + 11, + 10]) + self.squareList[24].setAdjacent([23, + 35, + 36, + 25, + 12, + 11]) + self.squareList[25].setAdjacent([24, + 36, + 37, + 26, + 13, + 12]) + self.squareList[26].setAdjacent([25, + 37, + 38, + 27, + 14, + 13]) + self.squareList[27].setAdjacent([26, + 38, + 39, + 28, + 15, + 14]) + self.squareList[28].setAdjacent([27, + 39, + 40, + 29, + 16, + 15]) + self.squareList[29].setAdjacent([28, + 40, + 41, + 30, + 17, + 16]) + self.squareList[30].setAdjacent([29, + 41, + 42, + 31, + 18, + 17]) + self.squareList[31].setAdjacent([30, + 42, + 43, + 32, + 19, + 18]) + self.squareList[32].setAdjacent([31, + 43, + 44, + 33, + 20, + 19]) + self.squareList[33].setAdjacent([32, + 44, + 45, + 34, + 21, + 20]) + self.squareList[34].setAdjacent([33, + 45, + None, + None, + 22, + 21]) + self.squareList[35].setAdjacent([None, + None, + 46, + 36, + 24, + 23]) + self.squareList[36].setAdjacent([35, + 46, + 47, + 37, + 25, + 24]) + self.squareList[37].setAdjacent([36, + 47, + 48, + 38, + 26, + 25]) + self.squareList[38].setAdjacent([37, + 48, + 49, + 39, + 27, + 26]) + self.squareList[39].setAdjacent([38, + 49, + 50, + 40, + 28, + 27]) + self.squareList[40].setAdjacent([39, + 50, + 51, + 41, + 29, + 28]) + self.squareList[41].setAdjacent([40, + 51, + 52, + 42, + 30, + 29]) + self.squareList[42].setAdjacent([41, + 52, + 53, + 43, + 31, + 30]) + self.squareList[43].setAdjacent([42, + 53, + 54, + 44, + 32, + 31]) + self.squareList[44].setAdjacent([43, + 54, + 55, + 45, + 33, + 32]) + self.squareList[45].setAdjacent([44, + 55, + None, + None, + 34, + 33]) + self.squareList[46].setAdjacent([None, + None, + 56, + 47, + 36, + 35]) + self.squareList[47].setAdjacent([46, + 56, + 57, + 48, + 37, + 36]) + self.squareList[48].setAdjacent([47, + 57, + 58, + 49, + 38, + 37]) + self.squareList[49].setAdjacent([48, + 58, + 59, + 50, + 39, + 38]) + self.squareList[50].setAdjacent([49, + 59, + 60, + 51, + 40, + 39]) + self.squareList[51].setAdjacent([50, + 60, + 61, + 52, + 41, + 40]) + self.squareList[52].setAdjacent([51, + 61, + 62, + 53, + 42, + 41]) + self.squareList[53].setAdjacent([52, + 62, + 63, + 54, + 43, + 42]) + self.squareList[54].setAdjacent([53, + 63, + 64, + 55, + 44, + 43]) + self.squareList[55].setAdjacent([54, + 64, + None, + None, + 45, + 44]) + self.squareList[56].setAdjacent([None, + 65, + 66, + 57, + 47, + 46]) + self.squareList[57].setAdjacent([56, + 66, + 67, + 58, + 48, + 47]) + self.squareList[58].setAdjacent([57, + 67, + 68, + 59, + 49, + 48]) + self.squareList[59].setAdjacent([58, + 68, + 69, + 60, + 50, + 49]) + self.squareList[60].setAdjacent([59, + 69, + 70, + 61, + 51, + 50]) + self.squareList[61].setAdjacent([60, + 70, + 71, + 62, + 52, + 51]) + self.squareList[62].setAdjacent([61, + 71, + 72, + 63, + 53, + 52]) + self.squareList[63].setAdjacent([62, + 72, + 73, + 64, + 54, + 53]) + self.squareList[64].setAdjacent([63, + 73, + 74, + None, + 55, + 54]) + self.squareList[65].setAdjacent([None, + 75, + 76, + 66, + 56, + None]) + self.squareList[66].setAdjacent([65, + 76, + 77, + 67, + 57, + 56]) + self.squareList[67].setAdjacent([66, + 77, + 78, + 68, + 58, + 57]) + self.squareList[68].setAdjacent([67, + 78, + 79, + 69, + 59, + 58]) + self.squareList[69].setAdjacent([68, + 79, + 80, + 70, + 60, + 61]) + self.squareList[70].setAdjacent([69, + 80, + 81, + 71, + 61, + 60]) + self.squareList[71].setAdjacent([70, + 81, + 82, + 72, + 62, + 61]) + self.squareList[72].setAdjacent([71, + 82, + 83, + 73, + 63, + 62]) + self.squareList[73].setAdjacent([72, + 83, + 84, + 74, + 64, + 63]) + self.squareList[74].setAdjacent([73, + 84, + 85, + None, + None, + 64]) + self.squareList[75].setAdjacent([None, + 86, + 87, + 76, + 65, + None]) + self.squareList[76].setAdjacent([75, + 87, + 88, + 77, + 66, + 65]) + self.squareList[77].setAdjacent([76, + 88, + 89, + 78, + 67, + 66]) + self.squareList[78].setAdjacent([77, + 89, + 90, + 79, + 68, + 67]) + self.squareList[79].setAdjacent([78, + 90, + 91, + 80, + 69, + 68]) + self.squareList[80].setAdjacent([79, + 91, + 92, + 81, + 70, + 69]) + self.squareList[81].setAdjacent([80, + 92, + 93, + 82, + 71, + 70]) + self.squareList[82].setAdjacent([81, + 93, + 94, + 83, + 72, + 71]) + self.squareList[83].setAdjacent([82, + 94, + 95, + 84, + 73, + 72]) + self.squareList[84].setAdjacent([83, + 95, + 96, + 85, + 74, + 73]) + self.squareList[85].setAdjacent([84, + 96, + 97, + None, + None, + 74]) + self.squareList[86].setAdjacent([None, + 98, + 99, + 87, + 75, + None]) + self.squareList[87].setAdjacent([86, + 99, + 100, + 88, + 76, + 75]) + self.squareList[88].setAdjacent([87, + 100, + 101, + 89, + 77, + 76]) + self.squareList[89].setAdjacent([88, + 101, + 102, + 90, + 78, + 77]) + self.squareList[90].setAdjacent([89, + 102, + 103, + 91, + 79, + 78]) + self.squareList[91].setAdjacent([90, + 103, + 104, + 92, + 80, + 79]) + self.squareList[92].setAdjacent([91, + 104, + 105, + 93, + 81, + 80]) + self.squareList[93].setAdjacent([92, + 105, + 106, + 94, + 82, + 81]) + self.squareList[94].setAdjacent([93, + 106, + 107, + 95, + 83, + 82]) + self.squareList[95].setAdjacent([94, + 107, + 108, + 96, + 84, + 83]) + self.squareList[96].setAdjacent([95, + 108, + 109, + 97, + 85, + 84]) + self.squareList[97].setAdjacent([96, + 109, + 110, + None, + None, + 85]) + self.squareList[98].setAdjacent([None, + None, + None, + 99, + 86, + None]) + self.squareList[99].setAdjacent([98, + None, + None, + 100, + 87, + 86]) + self.squareList[100].setAdjacent([99, + None, + None, + 101, + 88, + 87]) + self.squareList[101].setAdjacent([100, + None, + None, + 102, + 89, + 88]) + self.squareList[102].setAdjacent([101, + None, + 111, + 103, + 90, + 89]) + self.squareList[103].setAdjacent([102, + 111, + 112, + 104, + 91, + 90]) + self.squareList[104].setAdjacent([103, + 112, + 113, + 105, + 92, + 91]) + self.squareList[105].setAdjacent([104, + 113, + 114, + 106, + 93, + 92]) + self.squareList[106].setAdjacent([105, + 114, + None, + 107, + 94, + 93]) + self.squareList[107].setAdjacent([106, + None, + None, + 108, + 95, + 94]) + self.squareList[108].setAdjacent([107, + None, + None, + 109, + 96, + 95]) + self.squareList[109].setAdjacent([108, + None, + None, + 110, + 97, + 96]) + self.squareList[110].setAdjacent([109, + None, + None, + None, + None, + 97]) + self.squareList[111].setAdjacent([None, + None, + 115, + 112, + 103, + 102]) + self.squareList[112].setAdjacent([111, + 115, + 116, + 113, + 104, + 103]) + self.squareList[113].setAdjacent([112, + 116, + 117, + 114, + 105, + 104]) + self.squareList[114].setAdjacent([113, + 117, + None, + None, + 106, + 105]) + self.squareList[115].setAdjacent([None, + None, + 118, + 116, + 112, + 111]) + self.squareList[116].setAdjacent([115, + 118, + 119, + 117, + 113, + 112]) + self.squareList[117].setAdjacent([116, + 119, + None, + None, + 114, + 113]) + self.squareList[118].setAdjacent([None, + None, + 120, + 119, + 116, + 115]) + self.squareList[119].setAdjacent([118, + 120, + None, + None, + 117, + 116]) + self.squareList[120].setAdjacent([None, + None, + None, + None, + 119, + 118]) + return + + def delete(self): + for x in self.squareList: + x.delete() + + del self.squareList + + def getSquare(self, arrayLoc): + return self.squareList[arrayLoc] + + def getSquareOffset(self, arrayLoc): + return self.squareList[arrayLoc - 1] + + def getState(self, squareNum): + return self.squareList[squareNum].getState() + + def getStateOffset(self, arrayLoc): + return self.squareList[squareNum - 1].getState() + + def setState(self, squareNum, newState): + self.squareList[squareNum].setState(newState) + + def setStateOffset(self, squareNum, newState): + self.squareList[squareNum - 1].setState(newState) + + def getAdjacent(self, squareNum): + return self.squareList[squareNum].adjacent + + def getAdjacentOffset(self, squareNum): + return self.squareList[squareNum - 1].adjacent + + def getStates(self): + retList = [] + for x in range(121): + retList.append(self.squareList[x].getState()) + + return retList + + def setStates(self, squares): + y = 0 + for x in range(121): + self.squareList[x].setState(squares[x]) + + +class CheckersSquare: + + def __init__(self, tileNu): + self.tileNum = tileNu + self.state = 0 + self.adjacent = [] + + def delete(self): + del self.tileNum + del self.state + del self.adjacent + + def setAdjacent(self, adjList): + for x in adjList: + self.adjacent.append(x) + + def getAdjacent(self): + return self.adjacent + + def setState(self, newState): + self.state = newState + + def getState(self): + return self.state + + def getNum(self): + return self.tileNum diff --git a/toontown/safezone/DDPlayground.py b/toontown/safezone/DDPlayground.py new file mode 100644 index 0000000..dbba771 --- /dev/null +++ b/toontown/safezone/DDPlayground.py @@ -0,0 +1,137 @@ +from pandac.PandaModules import * +import Playground +from direct.task.Task import Task +import random +from direct.fsm import ClassicFSM, State +from direct.actor import Actor +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place + +class DDPlayground(Playground.Playground): + notify = DirectNotifyGlobal.directNotify.newCategory('DDPlayground') + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.cameraSubmerged = -1 + self.toonSubmerged = -1 + self.activityFsm = ClassicFSM.ClassicFSM('Activity', [State.State('off', self.enterOff, self.exitOff, ['OnBoat']), State.State('OnBoat', self.enterOnBoat, self.exitOnBoat, ['off'])], 'off', 'off') + self.activityFsm.enterInitialState() + + def load(self): + Playground.Playground.load(self) + + def unload(self): + del self.activityFsm + Playground.Playground.unload(self) + + def enter(self, requestStatus): + self.nextSeagullTime = 0 + taskMgr.add(self.__seagulls, 'dd-seagulls') + self.loader.hood.setWhiteFog() + Playground.Playground.enter(self, requestStatus) + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('dd-check-toon-underwater') + taskMgr.remove('dd-check-cam-underwater') + taskMgr.remove('dd-seagulls') + self.loader.hood.setNoFog() + + def enterStart(self): + self.cameraSubmerged = 0 + self.toonSubmerged = 0 + taskMgr.add(self.__checkToonUnderwater, 'dd-check-toon-underwater') + taskMgr.add(self.__checkCameraUnderwater, 'dd-check-cam-underwater') + + def enterDoorOut(self): + taskMgr.remove('dd-check-toon-underwater') + + def exitDoorOut(self): + pass + + def enterDoorIn(self, requestStatus): + Playground.Playground.enterDoorIn(self, requestStatus) + taskMgr.add(self.__checkToonUnderwater, 'dd-check-toon-underwater') + + def __checkCameraUnderwater(self, task): + if camera.getZ(render) < 1.0: + self.__submergeCamera() + else: + self.__emergeCamera() + return Task.cont + + def __checkToonUnderwater(self, task): + if base.localAvatar.getZ() < -2.3314585: + self.__submergeToon() + else: + self.__emergeToon() + return Task.cont + + def __submergeCamera(self): + if self.cameraSubmerged == 1: + return + self.loader.hood.setUnderwaterFog() + base.playSfx(self.loader.underwaterSound, looping=1, volume=0.8) + self.loader.seagullSound.stop() + taskMgr.remove('dd-seagulls') + self.cameraSubmerged = 1 + self.walkStateData.setSwimSoundAudible(1) + + def __emergeCamera(self): + if self.cameraSubmerged == 0: + return + self.loader.hood.setWhiteFog() + self.loader.underwaterSound.stop() + self.nextSeagullTime = random.random() * 8.0 + taskMgr.add(self.__seagulls, 'dd-seagulls') + self.cameraSubmerged = 0 + self.walkStateData.setSwimSoundAudible(0) + + def __submergeToon(self): + if self.toonSubmerged == 1: + return + base.playSfx(self.loader.submergeSound) + if base.config.GetBool('disable-flying-glitch') == 0: + self.fsm.request('walk') + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + pos = base.localAvatar.getPos(render) + base.localAvatar.d_playSplashEffect(pos[0], pos[1], 1.675) + self.toonSubmerged = 1 + + def __emergeToon(self): + if self.toonSubmerged == 0: + return + self.walkStateData.fsm.request('walking') + self.toonSubmerged = 0 + + def __seagulls(self, task): + if task.time < self.nextSeagullTime: + return Task.cont + base.playSfx(self.loader.seagullSound) + self.nextSeagullTime = task.time + random.random() * 4.0 + 8.0 + return Task.cont + + def enterTeleportIn(self, requestStatus): + self.toonSubmerged = -1 + taskMgr.remove('dd-check-toon-underwater') + Playground.Playground.enterTeleportIn(self, requestStatus) + + def teleportInDone(self): + self.toonSubmerged = -1 + taskMgr.add(self.__checkToonUnderwater, 'dd-check-toon-underwater') + Playground.Playground.teleportInDone(self) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterOnBoat(self): + base.localAvatar.b_setParent(ToontownGlobals.SPDonaldsBoat) + base.playSfx(self.loader.waterSound, looping=1) + + def exitOnBoat(self): + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + self.loader.waterSound.stop() diff --git a/toontown/safezone/DDSafeZoneLoader.py b/toontown/safezone/DDSafeZoneLoader.py new file mode 100644 index 0000000..f18cdc3 --- /dev/null +++ b/toontown/safezone/DDSafeZoneLoader.py @@ -0,0 +1,59 @@ +from pandac.PandaModules import * +import SafeZoneLoader +import DDPlayground +from direct.fsm import State +from toontown.char import CharDNA +from toontown.char import Char +from toontown.toonbase import ToontownGlobals + +class DDSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = DDPlayground.DDPlayground + self.musicFile = 'phase_6/audio/bgm/DD_nbrhood.mid' + self.activityMusicFile = 'phase_6/audio/bgm/DD_SZ_activity.mid' + self.dnaFile = 'phase_6/dna/donalds_dock_sz.dna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_DD_sz.dna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.seagullSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_Seagull.mp3') + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.mp3') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.mp3') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.mp3') + water = self.geom.find('**/water') + water.setTransparency(1) + water.setColor(1, 1, 1, 0.8) + self.boat = self.geom.find('**/donalds_boat') + if self.boat.isEmpty(): + self.notify.error('Boat not found') + else: + wheel = self.boat.find('**/wheel') + if wheel.isEmpty(): + self.notify.warning('Wheel not found') + else: + wheel.hide() + self.boat.stash() + self.dockSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_dockcreak.mp3') + self.foghornSound = base.loadSfx('phase_5/audio/sfx/SZ_DD_foghorn.mp3') + self.bellSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_shipbell.mp3') + self.waterSound = base.loadSfx('phase_6/audio/sfx/SZ_DD_waterlap.mp3') + + def unload(self): + SafeZoneLoader.SafeZoneLoader.unload(self) + del self.seagullSound + del self.underwaterSound + del self.swimSound + del self.dockSound + del self.foghornSound + del self.bellSound + del self.waterSound + del self.submergeSound + del self.boat + + def enter(self, requestStatus): + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + + def exit(self): + SafeZoneLoader.SafeZoneLoader.exit(self) diff --git a/toontown/safezone/DDTreasurePlannerAI.py b/toontown/safezone/DDTreasurePlannerAI.py new file mode 100644 index 0000000..6367272 --- /dev/null +++ b/toontown/safezone/DDTreasurePlannerAI.py @@ -0,0 +1,38 @@ +from toontown.toonbase.ToontownGlobals import * +import RegenTreasurePlannerAI, DistributedDDTreasureAI + +class DDTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + + def __init__(self, zoneId): + self.healAmount = 10 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedDDTreasureAI.DistributedDDTreasureAI, 'DDTreasurePlanner', 20, 2) + return None + + def initSpawnPoints(self): + self.spawnPoints = [(52.9072, -23.4768, -12.308), + (35.3827, -51.9196, -12.308), + (17.4252, -57.3107, -12.308), + (-0.716054, -68.5, -12.308), + (-29.0169, -66.8887, -12.308), + (-63.492, -64.2191, -12.308), + (-72.2423, -58.3686, -12.308), + (-97.9602, -42.8905, -12.308), + (-102.215, -34.1519, -12.308), + (-102.978, -4.09065, -12.308), + (-101.305, 30.6454, -12.308), + (-45.0621, -21.0088, -12.308), + (-11.4043, -29.0816, -12.308), + (2.33548, -7.71722, -12.308), + (-8.643, 33.9891, -12.308), + (-53.224, 18.1293, -12.308), + (-99.7225, -8.1298, -12.308), + (-100.457, 28.351, -12.308), + (-76.7946, 4.21199, -12.308), + (-64.9137, 37.5765, -12.308), + (-17.6075, 102.135, -12.308), + (-23.4112, 127.777, -12.308), + (-11.3513, 128.991, -12.308), + (-14.1068, 83.2043, -12.308), + (53.2685, 24.3585, -12.308), + (41.4197, 4.35384, -12.308)] + return self.spawnPoints diff --git a/toontown/safezone/DGPlayground.py b/toontown/safezone/DGPlayground.py new file mode 100644 index 0000000..8e859de --- /dev/null +++ b/toontown/safezone/DGPlayground.py @@ -0,0 +1,45 @@ +from pandac.PandaModules import * +import Playground +import random +from direct.task import Task + +class DGPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Playground.Playground.load(self) + + def unload(self): + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + self.nextBirdTime = 0 + taskMgr.add(self.__birds, 'DG-birds') + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('DG-birds') + + def __birds(self, task): + if task.time < self.nextBirdTime: + return Task.cont + randNum = random.random() + bird = int(randNum * 100) % 4 + 1 + if bird == 1: + base.playSfx(self.loader.bird1Sound) + elif bird == 2: + base.playSfx(self.loader.bird2Sound) + elif bird == 3: + base.playSfx(self.loader.bird3Sound) + elif bird == 4: + base.playSfx(self.loader.bird4Sound) + self.nextBirdTime = task.time + randNum * 20.0 + return Task.cont + + def showPaths(self): + from toontown.classicchars import CCharPaths + from toontown.toonbase import TTLocalizer + self.showPathPoints(CCharPaths.getPaths(TTLocalizer.Goofy)) diff --git a/toontown/safezone/DGSafeZoneLoader.py b/toontown/safezone/DGSafeZoneLoader.py new file mode 100644 index 0000000..6ee7a30 --- /dev/null +++ b/toontown/safezone/DGSafeZoneLoader.py @@ -0,0 +1,33 @@ +from pandac.PandaModules import * +import SafeZoneLoader +import DGPlayground + +class DGSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = DGPlayground.DGPlayground + self.musicFile = 'phase_8/audio/bgm/DG_nbrhood.mid' + self.activityMusicFile = 'phase_8/audio/bgm/DG_SZ.mid' + self.dnaFile = 'phase_8/dna/daisys_garden_sz.dna' + self.safeZoneStorageDNAFile = 'phase_8/dna/storage_DG_sz.dna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.bird1Sound = base.loadSfx('phase_8/audio/sfx/SZ_DG_bird_01.mp3') + self.bird2Sound = base.loadSfx('phase_8/audio/sfx/SZ_DG_bird_02.mp3') + self.bird3Sound = base.loadSfx('phase_8/audio/sfx/SZ_DG_bird_03.mp3') + self.bird4Sound = base.loadSfx('phase_8/audio/sfx/SZ_DG_bird_04.mp3') + + def unload(self): + SafeZoneLoader.SafeZoneLoader.unload(self) + del self.bird1Sound + del self.bird2Sound + del self.bird3Sound + del self.bird4Sound + + def enter(self, requestStatus): + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + + def exit(self): + SafeZoneLoader.SafeZoneLoader.exit(self) diff --git a/toontown/safezone/DGTreasurePlannerAI.py b/toontown/safezone/DGTreasurePlannerAI.py new file mode 100644 index 0000000..14eb7eb --- /dev/null +++ b/toontown/safezone/DGTreasurePlannerAI.py @@ -0,0 +1,32 @@ +from toontown.toonbase.ToontownGlobals import * +import RegenTreasurePlannerAI, DistributedDGTreasureAI + +class DGTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + + def __init__(self, zoneId): + self.healAmount = 10 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedDGTreasureAI.DistributedDGTreasureAI, 'DGTreasurePlanner', 15, 2) + return None + + def initSpawnPoints(self): + self.spawnPoints = [(-49, 156, 0.0), + (-59, 50, 0.0), + (19, 16, 0.0), + (76, 38, 1.1), + (102, 121, 0.0), + (69, 123, 0.0), + (49, 105, 0.0), + (24, 156, 0.0), + (-27, 127, 0.0), + (-56, 105, 0.0), + (-40, 113, 0.0), + (25, 114, 0.0), + (-6, 84, 0.0), + (19, 96, 0.0), + (0, 114, 0.0), + (-78, 157, 10.0), + (-33.4, 218.2, 10.0), + (57, 205, 10.0), + (32, 77, 0.0), + (-102, 101, 0.0)] + return self.spawnPoints diff --git a/toontown/safezone/DLPlayground.py b/toontown/safezone/DLPlayground.py new file mode 100644 index 0000000..1948075 --- /dev/null +++ b/toontown/safezone/DLPlayground.py @@ -0,0 +1,13 @@ +from pandac.PandaModules import * +import Playground +import random + +class DLPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + + def showPaths(self): + from toontown.classicchars import CCharPaths + from toontown.toonbase import TTLocalizer + self.showPathPoints(CCharPaths.getPaths(TTLocalizer.Donald)) diff --git a/toontown/safezone/DLSafeZoneLoader.py b/toontown/safezone/DLSafeZoneLoader.py new file mode 100644 index 0000000..943d419 --- /dev/null +++ b/toontown/safezone/DLSafeZoneLoader.py @@ -0,0 +1,13 @@ +from pandac.PandaModules import * +import SafeZoneLoader +import DLPlayground + +class DLSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = DLPlayground.DLPlayground + self.musicFile = 'phase_8/audio/bgm/DL_nbrhood.mid' + self.activityMusicFile = 'phase_8/audio/bgm/DL_SZ_activity.mid' + self.dnaFile = 'phase_8/dna/donalds_dreamland_sz.dna' + self.safeZoneStorageDNAFile = 'phase_8/dna/storage_DL_sz.dna' diff --git a/toontown/safezone/DLTreasurePlannerAI.py b/toontown/safezone/DLTreasurePlannerAI.py new file mode 100644 index 0000000..13710b6 --- /dev/null +++ b/toontown/safezone/DLTreasurePlannerAI.py @@ -0,0 +1,29 @@ +from toontown.toonbase.ToontownGlobals import * +import RegenTreasurePlannerAI, DistributedDLTreasureAI + +class DLTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + + def __init__(self, zoneId): + self.healAmount = 12 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedDLTreasureAI.DistributedDLTreasureAI, 'DLTreasurePlanner', 20, 2) + return None + + def initSpawnPoints(self): + self.spawnPoints = [(86, 69, -17.4), + (34, -48, -16.4), + (87, -70, -17.5), + (-98, 99, 0.0), + (51, 100, 0.0), + (-45, -12, -15.0), + (9, 8, -15.0), + (-24, 64, -17.2), + (-100, -99, 0.0), + (21, -101, 0.0), + (88, -17, -15.0), + (32, 70, -17.4), + (53, 35, -15.8), + (2, -30, -15.5), + (-40, -56, -16.8), + (-28, 18, -15.0), + (-34, -88, 0.0)] + return self.spawnPoints diff --git a/toontown/safezone/DistributedBRTreasure.py b/toontown/safezone/DistributedBRTreasure.py new file mode 100644 index 0000000..ee64e42 --- /dev/null +++ b/toontown/safezone/DistributedBRTreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedBRTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_8/models/props/snowflake_treasure' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedBRTreasureAI.py b/toontown/safezone/DistributedBRTreasureAI.py new file mode 100644 index 0000000..51dcfe9 --- /dev/null +++ b/toontown/safezone/DistributedBRTreasureAI.py @@ -0,0 +1,6 @@ +import DistributedSZTreasureAI + +class DistributedBRTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/safezone/DistributedBoat.py b/toontown/safezone/DistributedBoat.py new file mode 100644 index 0000000..abf92d2 --- /dev/null +++ b/toontown/safezone/DistributedBoat.py @@ -0,0 +1,127 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.distributed import DistributedObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import NodePath +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals + +class DistributedBoat(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.eastWestMopath = Mopath.Mopath() + self.westEastMopath = Mopath.Mopath() + self.eastWestMopathInterval = None + self.westEastMopathInterval = None + self.fsm = ClassicFSM.ClassicFSM('DistributedBoat', [State.State('off', self.enterOff, self.exitOff, ['DockedEast', + 'SailingWest', + 'DockedWest', + 'SailingEast']), + State.State('DockedEast', self.enterDockedEast, self.exitDockedEast, ['SailingWest', 'SailingEast', 'DockedWest']), + State.State('SailingWest', self.enterSailingWest, self.exitSailingWest, ['DockedWest', 'SailingEast', 'DockedEast']), + State.State('DockedWest', self.enterDockedWest, self.exitDockedWest, ['SailingEast', 'SailingWest', 'DockedEast']), + State.State('SailingEast', self.enterSailingEast, self.exitSailingEast, ['DockedEast', 'DockedWest', 'SailingWest'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.boat = base.cr.playGame.hood.loader.boat + base.cr.parentMgr.registerParent(ToontownGlobals.SPDonaldsBoat, self.boat) + self.setupTracks() + self.accept('enterdonalds_boat_floor', self.__handleOnFloor) + self.accept('exitdonalds_boat_floor', self.__handleOffFloor) + + def setupTracks(self): + boat = self.boat + boat.unstash() + dockSound = self.cr.playGame.hood.loader.dockSound + foghornSound = self.cr.playGame.hood.loader.foghornSound + bellSound = self.cr.playGame.hood.loader.bellSound + self.eastWestMopath.loadFile('phase_6/paths/dd-e-w') + self.eastWestMopathInterval = MopathInterval(self.eastWestMopath, boat) + ewBoatTrack = ParallelEndTogether(Parallel(self.eastWestMopathInterval, SoundInterval(bellSound, node=boat)), SoundInterval(foghornSound, node=boat), name='ew-boat') + self.westEastMopath.loadFile('phase_6/paths/dd-w-e') + self.westEastMopathInterval = MopathInterval(self.westEastMopath, boat) + weBoatTrack = ParallelEndTogether(Parallel(self.westEastMopathInterval, SoundInterval(bellSound, node=boat)), SoundInterval(foghornSound, node=boat), name='we-boat') + PIER_TIME = 5.0 + eastPier = self.cr.playGame.hood.loader.geom.find('**/east_pier') + ePierHpr = VBase3(90, -44.2601, 0) + ePierTargetHpr = VBase3(90, 0.25, 0) + westPier = self.cr.playGame.hood.loader.geom.find('**/west_pier') + wPierHpr = VBase3(-90, -44.2601, 0) + wPierTargetHpr = VBase3(-90, 0.25, 0) + ePierDownTrack = Parallel(LerpHprInterval(eastPier, PIER_TIME, ePierHpr, ePierTargetHpr), SoundInterval(dockSound, node=eastPier), name='e-pier-down') + ePierUpTrack = Parallel(LerpHprInterval(eastPier, PIER_TIME, ePierTargetHpr, ePierHpr), SoundInterval(dockSound, node=eastPier), name='e-pier-up') + wPierDownTrack = Parallel(LerpHprInterval(westPier, PIER_TIME, wPierHpr, wPierTargetHpr), SoundInterval(dockSound, node=westPier), name='w-pier-down') + wPierUpTrack = Parallel(LerpHprInterval(westPier, PIER_TIME, wPierTargetHpr, wPierHpr), SoundInterval(dockSound, node=westPier), name='w-pier-up') + self.ewTrack = ParallelEndTogether(Parallel(ewBoatTrack, ePierDownTrack), wPierUpTrack, name='ew-track') + self.weTrack = ParallelEndTogether(Parallel(weBoatTrack, wPierDownTrack), ePierUpTrack, name='we-track') + + def disable(self): + base.cr.parentMgr.unregisterParent(ToontownGlobals.SPDonaldsBoat) + self.ignore('enterdonalds_boat_floor') + self.ignore('exitdonalds_boat_floor') + self.fsm.request('off') + DistributedObject.DistributedObject.disable(self) + self.ewTrack.finish() + self.weTrack.finish() + + def delete(self): + self.eastWestMopath.reset() + self.westEastMopath.reset() + if self.eastWestMopathInterval.mopath: + self.eastWestMopathInterval.destroy() + if self.westEastMopathInterval.mopath: + self.westEastMopathInterval.destroy() + del self.eastWestMopath + del self.westEastMopath + del self.ewTrack + del self.weTrack + del self.fsm + DistributedObject.DistributedObject.delete(self) + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def __handleOnFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('OnBoat') + + def __handleOffFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('off') + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterDockedEast(self, ts): + self.weTrack.finish() + return None + + def exitDockedEast(self): + return None + + def enterSailingWest(self, ts): + self.ewTrack.start(ts) + + def exitSailingWest(self): + self.ewTrack.finish() + + def enterDockedWest(self, ts): + self.ewTrack.finish() + return None + + def exitDockedWest(self): + return None + + def enterSailingEast(self, ts): + self.weTrack.start(ts) + + def exitSailingEast(self): + self.weTrack.finish() + return None diff --git a/toontown/safezone/DistributedBoatAI.py b/toontown/safezone/DistributedBoatAI.py new file mode 100644 index 0000000..1965e56 --- /dev/null +++ b/toontown/safezone/DistributedBoatAI.py @@ -0,0 +1,95 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task + +class DistributedBoatAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.fsm = ClassicFSM.ClassicFSM('DistributedBoatAI', [ + State.State('off', self.enterOff, self.exitOff, [ + 'DockedEast']), + State.State('DockedEast', self.enterDockedEast, self.exitDockedEast, [ + 'SailingWest']), + State.State('SailingWest', self.enterSailingWest, self.exitSailingWest, [ + 'DockedWest']), + State.State('DockedWest', self.enterDockedWest, self.exitDockedWest, [ + 'SailingEast']), + State.State('SailingEast', self.enterSailingEast, self.exitSailingEast, [ + 'DockedEast'])], 'off', 'off') + self.fsm.enterInitialState() + return None + + def delete(self): + self.fsm.request('off') + DistributedObjectAI.DistributedObjectAI.delete(self) + + def b_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + self.fsm.request(state) + + def getState(self): + return [ + self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] + + def start(self): + self.b_setState('DockedEast') + return None + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterDockedEast(self): + taskMgr.doMethodLater(10.0, self.__departEast, 'depart-east') + return None + + def exitDockedEast(self): + taskMgr.remove('depart-east') + return None + + def __departEast(self, task): + self.b_setState('SailingWest') + return Task.done + + def enterSailingWest(self): + taskMgr.doMethodLater(20.0, self.__dockWest, 'dock-west') + return None + + def exitSailingWest(self): + taskMgr.remove('dock-west') + return None + + def __dockWest(self, task): + self.b_setState('DockedWest') + return Task.done + + def enterDockedWest(self): + taskMgr.doMethodLater(10.0, self.__departWest, 'depart-west') + return None + + def exitDockedWest(self): + taskMgr.remove('depart-west') + return None + + def __departWest(self, task): + self.b_setState('SailingEast') + return Task.done + + def enterSailingEast(self): + taskMgr.doMethodLater(20.0, self.__dockEast, 'dock-east') + return None + + def exitSailingEast(self): + taskMgr.remove('dock-east') + return None + + def __dockEast(self, task): + self.b_setState('DockedEast') + return Task.done diff --git a/toontown/safezone/DistributedButterfly.py b/toontown/safezone/DistributedButterfly.py new file mode 100644 index 0000000..55fdeea --- /dev/null +++ b/toontown/safezone/DistributedButterfly.py @@ -0,0 +1,223 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed import DistributedObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import NodePath +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals +from direct.actor import Actor +import ButterflyGlobals +from direct.showbase import RandomNumGen +import random + +class DistributedButterfly(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedButterfly') + id = 0 + wingTypes = ('wings_1', 'wings_2', 'wings_3', 'wings_4', 'wings_5', 'wings_6') + yellowColors = (Vec4(1, 1, 1, 1), Vec4(0.2, 0, 1, 1), Vec4(0.8, 0, 1, 1)) + whiteColors = (Vec4(0.8, 0, 0.8, 1), + Vec4(0, 0.8, 0.8, 1), + Vec4(0.9, 0.4, 0.6, 1), + Vec4(0.9, 0.4, 0.4, 1), + Vec4(0.8, 0.5, 0.9, 1), + Vec4(0.4, 0.1, 0.7, 1)) + paleYellowColors = (Vec4(0.8, 0, 0.8, 1), + Vec4(0.6, 0.6, 0.9, 1), + Vec4(0.7, 0.6, 0.9, 1), + Vec4(0.8, 0.6, 0.9, 1), + Vec4(0.9, 0.6, 0.9, 1), + Vec4(1, 0.6, 0.9, 1)) + shadowScaleBig = Point3(0.07, 0.07, 0.07) + shadowScaleSmall = Point3(0.01, 0.01, 0.01) + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedButterfly', [State.State('off', self.enterOff, self.exitOff, ['Flying', 'Landed']), State.State('Flying', self.enterFlying, self.exitFlying, ['Landed']), State.State('Landed', self.enterLanded, self.exitLanded, ['Flying'])], 'off', 'off') + self.butterfly = None + self.butterflyNode = None + self.curIndex = 0 + self.destIndex = 0 + self.time = 0.0 + self.ival = None + self.fsm.enterInitialState() + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + if self.butterfly: + return + self.butterfly = Actor.Actor() + self.butterfly.loadModel('phase_4/models/props/SZ_butterfly-mod.bam') + self.butterfly.loadAnims({'flutter': 'phase_4/models/props/SZ_butterfly-flutter.bam', + 'glide': 'phase_4/models/props/SZ_butterfly-glide.bam', + 'land': 'phase_4/models/props/SZ_butterfly-land.bam'}) + index = self.doId % len(self.wingTypes) + chosenType = self.wingTypes[index] + node = self.butterfly.getGeomNode() + for type in self.wingTypes: + wing = node.find('**/' + type) + if type != chosenType: + wing.removeNode() + else: + if index == 0 or index == 1: + color = self.yellowColors[self.doId % len(self.yellowColors)] + elif index == 2 or index == 3: + color = self.whiteColors[self.doId % len(self.whiteColors)] + elif index == 4: + color = self.paleYellowColors[self.doId % len(self.paleYellowColors)] + else: + color = Vec4(1, 1, 1, 1) + wing.setColor(color) + + self.butterfly2 = Actor.Actor(other=self.butterfly) + self.butterfly.enableBlend(blendType=PartBundle.BTLinear) + self.butterfly.loop('flutter') + self.butterfly.loop('land') + self.butterfly.loop('glide') + rng = RandomNumGen.RandomNumGen(self.doId) + playRate = 0.6 + 0.8 * rng.random() + self.butterfly.setPlayRate(playRate, 'flutter') + self.butterfly.setPlayRate(playRate, 'land') + self.butterfly.setPlayRate(playRate, 'glide') + self.butterfly2.setPlayRate(playRate, 'flutter') + self.butterfly2.setPlayRate(playRate, 'land') + self.butterfly2.setPlayRate(playRate, 'glide') + self.glideWeight = rng.random() * 2 + lodNode = LODNode('butterfly-node') + lodNode.addSwitch(100, 40) + lodNode.addSwitch(40, 0) + self.butterflyNode = NodePath(lodNode) + self.butterfly2.setH(180.0) + self.butterfly2.reparentTo(self.butterflyNode) + self.butterfly.setH(180.0) + self.butterfly.reparentTo(self.butterflyNode) + self.__initCollisions() + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadow.setColor(0, 0, 0, 0.3) + self.dropShadow.setPos(0, 0.1, -0.05) + self.dropShadow.setScale(self.shadowScaleBig) + self.dropShadow.reparentTo(self.butterfly) + + def disable(self): + self.butterflyNode.reparentTo(hidden) + if self.ival != None: + self.ival.finish() + self.__ignoreAvatars() + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + self.butterfly.cleanup() + self.butterfly = None + self.butterfly2.cleanup() + self.butterfly2 = None + self.butterflyNode.removeNode() + self.__deleteCollisions() + self.ival = None + del self.fsm + DistributedObject.DistributedObject.delete(self) + return + + def uniqueButterflyName(self, name): + DistributedButterfly.id += 1 + return name + '-%d' % DistributedButterfly.id + + def __detectAvatars(self): + self.accept('enter' + self.cSphereNode.getName(), self.__handleCollisionSphereEnter) + + def __ignoreAvatars(self): + self.ignore('enter' + self.cSphereNode.getName()) + + def __initCollisions(self): + self.cSphere = CollisionSphere(0.0, 1.0, 0.0, 3.0) + self.cSphere.setTangible(0) + self.cSphereNode = CollisionNode(self.uniqueButterflyName('cSphereNode')) + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.butterflyNode.attachNewNode(self.cSphereNode) + self.cSphereNodePath.hide() + self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __deleteCollisions(self): + del self.cSphere + del self.cSphereNode + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + + def __handleCollisionSphereEnter(self, collEntry): + self.sendUpdate('avatarEnter', []) + + def setArea(self, playground, area): + self.playground = playground + self.area = area + + def setState(self, stateIndex, curIndex, destIndex, time, timestamp): + self.curIndex = curIndex + self.destIndex = destIndex + self.time = time + self.fsm.request(ButterflyGlobals.states[stateIndex], [globalClockDelta.localElapsedTime(timestamp)]) + + def enterOff(self, ts = 0.0): + if self.butterflyNode != None: + self.butterflyNode.reparentTo(hidden) + return + + def exitOff(self): + if self.butterflyNode != None: + self.butterflyNode.reparentTo(render) + return + + def enterFlying(self, ts): + self.__detectAvatars() + curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.curIndex] + destPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.destIndex] + flyHeight = max(curPos[2], destPos[2]) + ButterflyGlobals.BUTTERFLY_HEIGHT[self.playground] + curPosHigh = Point3(curPos[0], curPos[1], flyHeight) + destPosHigh = Point3(destPos[0], destPos[1], flyHeight) + if ts <= self.time: + flyTime = self.time - (ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] + ButterflyGlobals.BUTTERFLY_LANDING[self.playground]) + self.butterflyNode.setPos(curPos) + self.dropShadow.show() + self.dropShadow.setScale(self.shadowScaleBig) + oldHpr = self.butterflyNode.getHpr() + self.butterflyNode.headsUp(destPos) + newHpr = self.butterflyNode.getHpr() + self.butterflyNode.setHpr(oldHpr) + takeoffShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground] + landShadowT = 0.2 * ButterflyGlobals.BUTTERFLY_LANDING[self.playground] + self.butterfly2.loop('flutter') + self.ival = Sequence(Parallel(LerpPosHprInterval(self.butterflyNode, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], curPosHigh, newHpr), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], 'land', 'flutter'), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_TAKEOFF[self.playground], None, 'glide', startWeight=0, endWeight=self.glideWeight), Sequence(LerpScaleInterval(self.dropShadow, takeoffShadowT, self.shadowScaleSmall, startScale=self.shadowScaleBig), HideInterval(self.dropShadow))), LerpPosInterval(self.butterflyNode, flyTime, destPosHigh), Parallel(LerpPosInterval(self.butterflyNode, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], destPos), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], 'flutter', 'land'), LerpAnimInterval(self.butterfly, ButterflyGlobals.BUTTERFLY_LANDING[self.playground], None, 'glide', startWeight=self.glideWeight, endWeight=0), Sequence(Wait(ButterflyGlobals.BUTTERFLY_LANDING[self.playground] - landShadowT), ShowInterval(self.dropShadow), LerpScaleInterval(self.dropShadow, landShadowT, self.shadowScaleBig, startScale=self.shadowScaleSmall))), name=self.uniqueName('Butterfly')) + self.ival.start(ts) + else: + self.ival = None + self.butterflyNode.setPos(destPos) + self.butterfly.setControlEffect('land', 1.0) + self.butterfly.setControlEffect('flutter', 0.0) + self.butterfly.setControlEffect('glide', 0.0) + self.butterfly2.loop('land') + return + + def exitFlying(self): + self.__ignoreAvatars() + if self.ival != None: + self.ival.finish() + self.ival = None + return + + def enterLanded(self, ts): + self.__detectAvatars() + curPos = ButterflyGlobals.ButterflyPoints[self.playground][self.area][self.curIndex] + self.butterflyNode.setPos(curPos) + self.dropShadow.show() + self.dropShadow.setScale(self.shadowScaleBig) + self.butterfly.setControlEffect('land', 1.0) + self.butterfly.setControlEffect('flutter', 0.0) + self.butterfly.setControlEffect('glide', 0.0) + self.butterfly2.pose('land', random.randrange(self.butterfly2.getNumFrames('land'))) + return None + + def exitLanded(self): + self.__ignoreAvatars() + return None diff --git a/toontown/safezone/DistributedButterflyAI.py b/toontown/safezone/DistributedButterflyAI.py new file mode 100644 index 0000000..73b9b05 --- /dev/null +++ b/toontown/safezone/DistributedButterflyAI.py @@ -0,0 +1,96 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.task import Task +import ButterflyGlobals, random + +class DistributedButterflyAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air, playground, area, ownerId): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.playground = playground + self.area = area + self.ownerId = ownerId + self.fsm = ClassicFSM.ClassicFSM('DistributedButterfliesAI', [ + State.State('off', self.enterOff, self.exitOff, [ + 'Flying', 'Landed']), + State.State('Flying', self.enterFlying, self.exitFlying, [ + 'Landed']), + State.State('Landed', self.enterLanded, self.exitLanded, [ + 'Flying'])], 'off', 'off') + self.fsm.enterInitialState() + self.curPos, self.curIndex, self.destPos, self.destIndex, self.time = ButterflyGlobals.getFirstRoute(self.playground, self.area, self.ownerId) + return None + + def delete(self): + try: + self.butterfly_deleted + except: + self.butterfly_deleted = 1 + ButterflyGlobals.recycleIndex(self.curIndex, self.playground, self.area, self.ownerId) + ButterflyGlobals.recycleIndex(self.destIndex, self.playground, self.area, self.ownerId) + self.fsm.request('off') + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + def d_setState(self, stateIndex, curIndex, destIndex, time): + self.sendUpdate('setState', [stateIndex, curIndex, destIndex, time, globalClockDelta.getRealNetworkTime()]) + + def getArea(self): + return [ + self.playground, self.area] + + def getState(self): + return [ + self.stateIndex, self.curIndex, self.destIndex, self.time, globalClockDelta.getRealNetworkTime()] + + def start(self): + self.fsm.request('Flying') + + def avatarEnter(self): + if self.fsm.getCurrentState().getName() == 'Landed': + self.__ready() + return None + + def enterOff(self): + self.stateIndex = ButterflyGlobals.OFF + return None + + def exitOff(self): + return None + + def enterFlying(self): + self.stateIndex = ButterflyGlobals.FLYING + ButterflyGlobals.recycleIndex(self.curIndex, self.playground, self.area, self.ownerId) + self.d_setState(ButterflyGlobals.FLYING, self.curIndex, self.destIndex, self.time) + taskMgr.doMethodLater(self.time, self.__handleArrival, self.uniqueName('butter-flying')) + return None + + def exitFlying(self): + taskMgr.remove(self.uniqueName('butter-flying')) + return None + + def __handleArrival(self, task): + self.curPos = self.destPos + self.curIndex = self.destIndex + self.fsm.request('Landed') + return Task.done + + def enterLanded(self): + self.stateIndex = ButterflyGlobals.LANDED + self.time = random.random() * ButterflyGlobals.MAX_LANDED_TIME + self.d_setState(ButterflyGlobals.LANDED, self.curIndex, self.destIndex, self.time) + taskMgr.doMethodLater(self.time, self.__ready, self.uniqueName('butter-ready')) + return None + + def exitLanded(self): + taskMgr.remove(self.uniqueName('butter-ready')) + return None + + def __ready(self, task=None): + self.destPos, self.destIndex, self.time = ButterflyGlobals.getNextPos(self.curPos, self.playground, self.area, self.ownerId) + self.fsm.request('Flying') + return Task.done diff --git a/toontown/safezone/DistributedCheckers.py b/toontown/safezone/DistributedCheckers.py new file mode 100644 index 0000000..05e36a7 --- /dev/null +++ b/toontown/safezone/DistributedCheckers.py @@ -0,0 +1,738 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from CheckersBoard import CheckersBoard +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from otp.otpbase import OTPGlobals +from direct.showbase import PythonUtil + +class DistributedCheckers(DistributedNode.DistributedNode): + + def __init__(self, cr): + NodePath.__init__(self, 'DistributedCheckers') + DistributedNode.DistributedNode.__init__(self, cr) + self.cr = cr + self.reparentTo(render) + self.boardNode = loader.loadModel('phase_6/models/golf/regular_checker_game.bam') + self.boardNode.reparentTo(self) + self.board = CheckersBoard() + self.exitButton = None + self.inGame = False + self.waiting = True + self.startButton = None + self.playerNum = None + self.turnText = None + self.isMyTurn = False + self.wantTimer = True + self.leaveButton = None + self.screenText = None + self.turnText = None + self.exitButton = None + self.numRandomMoves = 0 + self.blinker = Sequence() + self.moveList = [] + self.mySquares = [] + self.myKings = [] + self.isRotated = False + self.accept('mouse1', self.mouseClick) + self.traverser = base.cTrav + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.myHandler = CollisionHandlerQueue() + self.traverser.addCollider(self.pickerNP, self.myHandler) + 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') + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + self.playerColors = [Vec4(0, 0, 1, 1), Vec4(0, 1, 0, 1)] + self.tintConstant = Vec4(0.25, 0.25, 0.25, 0.5) + self.ghostConstant = Vec4(0, 0, 0, 0.8) + self.startingPositions = [[0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11], [20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31]] + self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.mp3') + self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.mp3') + self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.mp3') + self.accept('stoppedAsleep', self.handleSleep) + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + x = self.boardNode.find('**/locator*') + self.locatorList = x.getChildren() + tempList = [] + for x in range(0, 32): + self.locatorList[x].setTag('GamePeiceLocator', '%d' % x) + tempList.append(self.locatorList[x].attachNewNode(CollisionNode('picker%d' % x))) + tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.39)) + + for z in self.locatorList: + y = loader.loadModel('phase_6/models/golf/regular_checker_piecewhite.bam') + y.find('**/checker_k*').hide() + zz = loader.loadModel('phase_6/models/golf/regular_checker_pieceblack.bam') + zz.find('**/checker_k*').hide() + y.reparentTo(z) + y.hide() + zz.reparentTo(z) + zz.hide() + + return + + def setName(self, name): + self.name = name + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + if self.table.fsm.getCurrentState().getName() != 'observing': + if base.localAvatar.doId in self.table.tableState: + self.seatPos = self.table.tableState.index(base.localAvatar.doId) + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.exitButtonPushed() + if task != None: + task.done + return + + def setTableDoId(self, doId): + self.tableDoId = doId + self.table = self.cr.doId2do[doId] + self.table.setTimerFunc(self.startButtonPushed) + self.fsm.enterInitialState() + self.table.setGameDoId(self.doId) + + def disable(self): + DistributedNode.DistributedNode.disable(self) + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + return + + def delete(self): + DistributedNode.DistributedNode.delete(self) + self.table.gameDoId = None + self.table.game = None + if self.exitButton: + self.exitButton.destroy() + if self.startButton: + self.startButton.destroy() + self.clockNode.stop() + self.clockNode.hide() + self.table.startButtonPushed = None + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.table = None + return + + def getTimer(self): + self.sendUpdate('requestTimer', []) + + def setTimer(self, timerEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0 and timerEnd != 0: + if timeLeft > 60: + timeLeft = 60 + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.countdown(timeLeft, self.startButtonPushed) + self.clockNode.show() + else: + self.clockNode.stop() + self.clockNode.hide() + return + + def setTurnTimer(self, turnEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(turnEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0: + self.clockNode.setPos(-.74, 0, -0.2) + if self.isMyTurn: + self.clockNode.countdown(timeLeft, self.doNothing) + else: + self.clockNode.countdown(timeLeft, self.doNothing) + self.clockNode.show() + return + + def gameStart(self, playerNum): + if playerNum != 255: + self.playerNum = playerNum + if self.playerNum == 1: + self.playerColorString = 'white' + else: + self.playerColorString = 'black' + self.playerColor = self.playerColors[playerNum - 1] + self.moveCameraForGame() + self.fsm.request('playing') + + def sendTurn(self, playersTurn): + if self.fsm.getCurrentState().getName() == 'playing': + if playersTurn == self.playerNum: + self.isMyTurn = True + self.enableTurnScreenText(playersTurn) + + def illegalMove(self): + self.exitButtonPushed() + + def moveCameraForGame(self): + if self.table.cameraBoardTrack.isPlaying(): + self.table.cameraBoardTrack.finish() + rotation = 0 + if self.seatPos > 2: + if self.playerNum == 1: + rotation = 180 + elif self.playerNum == 2: + rotation = 0 + for x in self.locatorList: + x.setH(180) + + self.isRotated = True + elif self.playerNum == 1: + rotation = 0 + elif self.playerNum == 2: + rotation = 180 + for x in self.locatorList: + x.setH(180) + + self.isRotated = True + int = LerpHprInterval(self.boardNode, 4.2, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + int.start() + + def enterWaitingToBegin(self): + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableExitButton() + self.enableStartButton() + + def exitWaitingToBegin(self): + if self.exitButton: + self.exitButton.destroy() + self.exitButton = None + if self.startButton: + self.startButton.destroy() + self.exitButton = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterPlaying(self): + self.inGame = True + self.enableScreenText() + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableLeaveButton() + + def exitPlaying(self): + self.inGame = False + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + self.playerNum = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterGameOver(self): + pass + + def exitGameOver(self): + pass + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableScreenText(self): + defaultPos = (-.8, -0.4) + if self.playerNum == 1: + message = TTLocalizer.CheckersColorWhite + color = Vec4(1, 1, 1, 1) + elif self.playerNum == 2: + message = TTLocalizer.CheckersColorBlack + color = Vec4(0, 0, 0, 1) + else: + message = TTLocalizer.CheckersObserver + color = Vec4(0, 0, 0, 1) + defaultPos = (-.8, -0.4) + self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1) + + def enableStartButton(self): + self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.1), scale=0.15, command=lambda self = self: self.startButtonPushed()) + return + + def enableLeaveButton(self): + self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableTurnScreenText(self, player): + playerOrder = [1, + 4, + 2, + 5, + 3, + 6] + message1 = TTLocalizer.CheckersIts + if self.turnText != None: + self.turnText.destroy() + if player == self.playerNum: + message2 = TTLocalizer.ChineseCheckersYourTurn + color = (0, 0, 0, 1) + elif player == 1: + message2 = TTLocalizer.CheckersWhiteTurn + color = (1, 1, 1, 1) + elif player == 2: + message2 = TTLocalizer.CheckersBlackTurn + color = (0, 0, 0, 1) + self.turnText = OnscreenText(text=message1 + message2, pos=(-0.8, -0.5), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1) + return + + def startButtonPushed(self): + self.sendUpdate('requestBegin') + self.startButton.hide() + self.clockNode.stop() + self.clockNode.hide() + + def exitButtonPushed(self): + self.fsm.request('gameOver') + self.table.fsm.request('off') + self.clockNode.stop() + self.clockNode.hide() + self.table.sendUpdate('requestExit') + + def mouseClick(self): + messenger.send('wakeup') + if self.isMyTurn == True and self.inGame == True: + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.traverser.traverse(render) + if self.myHandler.getNumEntries() > 0: + self.myHandler.sortEntries() + pickedObj = self.myHandler.getEntry(0).getIntoNodePath() + pickedObj = pickedObj.getNetTag('GamePeiceLocator') + if pickedObj: + self.handleClicked(int(pickedObj)) + + def handleClicked(self, index): + self.sound = Sequence(SoundInterval(self.clickSound)) + if self.moveList == []: + if index not in self.mySquares and index not in self.myKings: + return + self.moveList.append(index) + type = self.board.squareList[index].getState() + if type == 3 or type == 4: + self.moverType = 'king' + else: + self.moverType = 'normal' + self.blinker = Sequence() + col = self.locatorList[index].getColor() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.tintConstant, col)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, col, self.tintConstant)) + self.blinker.loop() + self.sound.start() + elif index in self.mySquares or index in self.myKings: + for x in self.moveList: + self.locatorList[x].setColor(1, 1, 1, 1) + self.locatorList[x].hide() + + self.blinker.finish() + self.blinker = Sequence() + col = self.locatorList[index].getColor() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.tintConstant, col)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, col, self.tintConstant)) + self.blinker.loop() + self.sound.start() + self.locatorList[self.moveList[0]].show() + self.moveList = [] + self.moveList.append(index) + type = self.board.squareList[index].getState() + if type == 3 or type == 4: + self.moverType = 'king' + else: + self.moverType = 'normal' + else: + self.currentMove = index + lastItem = self.board.squareList[self.moveList[len(self.moveList) - 1]] + thisItem = self.board.squareList[index] + if self.mustJump == True: + if lastItem.getNum() == index: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.isMyTurn = False + self.moveList = [] + return + if self.checkLegalJump(lastItem, thisItem, self.moverType) == True: + col = self.locatorList[index].getColor() + self.locatorList[index].show() + self.sound.start() + if self.existsLegalJumpsFrom(index, self.moverType) == False: + self.moveList.append(index) + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + else: + self.moveList.append(index) + if self.playerColorString == 'white': + x = self.locatorList[index].getChildren()[1] + x.show() + else: + x = self.locatorList[index].getChildren()[2] + x.show() + if self.moverType == 'king': + x.find('**/checker_k*').show() + self.locatorList[index].setColor(Vec4(0.5, 0.5, 0.5, 0.5)) + elif self.checkLegalMove(lastItem, thisItem, self.moverType) == True: + self.moveList.append(index) + col = self.locatorList[index].getColor() + self.locatorList[index].show() + self.sound.start() + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + + def existsLegalJumpsFrom(self, index, peice): + if peice == 'king': + for x in range(4): + if self.board.squareList[index].getAdjacent()[x] != None and \ + self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + if index not in self.moveList and jump.getNum() not in self.moveList: + return True + return False + elif peice == 'normal': + if self.playerNum == 1: + moveForward = [1, 2] + elif self.playerNum == 2: + moveForward = [0, 3] + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None and \ + self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + if index not in self.moveList: + return True + return False + + def existsLegalMovesFrom(self, index, peice): + if peice == 'king': + for x in self.board.squareList[index].getAdjacent(): + if x != None: + if self.board.squareList[x].getState() == 0: + return True + + return False + elif peice == 'normal': + if self.playerNum == 1: + moveForward = [1, 2] + elif self.playerNum == 2: + moveForward = [0, 3] + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + if adj.getState() == 0: + return True + + return False + return + + def checkLegalMove(self, firstSquare, secondSquare, peice): + if firstSquare.getNum() not in self.mySquares and firstSquare.getNum() not in self.myKings: + return False + if self.playerNum == 1: + moveForward = [1, 2] + else: + moveForward = [0, 3] + if peice == 'king': + for x in range(4): + if firstSquare.getAdjacent()[x] != None: + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and secondSquare.getNum() in firstSquare.getAdjacent(): + return True + + return False + elif peice == 'normal': + for x in moveForward: + if firstSquare.getAdjacent()[x] != None and secondSquare.getNum() in firstSquare.getAdjacent(): + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0 and firstSquare.getAdjacent().index(secondSquare.getNum()) == x: + return True + + return False + return + + def checkLegalJump(self, firstSquare, secondSquare, peice): + if firstSquare.getNum() not in self.mySquares and firstSquare.getNum() not in self.myKings and len(self.moveList) == 1: + return False + if self.playerNum == 1: + moveForward = [1, 2] + opposingPeices = [2, 4] + else: + moveForward = [0, 3] + opposingPeices = [1, 3] + if peice == 'king': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + elif peice == 'normal': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if index in moveForward: + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + else: + return False + else: + return False + + def d_requestMove(self, moveList): + self.sendUpdate('requestMove', [moveList]) + + def setGameState(self, tableState, moveList): + if moveList != []: + if self.board.squareList[moveList[0]].getState() == 1 or self.board.squareList[moveList[0]].getState() == 3: + playerColor = 'white' + else: + playerColor = 'black' + if self.board.squareList[moveList[0]].getState() <= 2: + self.animatePeice(tableState, moveList, 'normal', playerColor) + else: + self.animatePeice(tableState, moveList, 'king', playerColor) + else: + self.updateGameState(tableState) + + def updateGameState(self, squares): + self.board.setStates(squares) + self.mySquares = [] + self.myKings = [] + messenger.send('wakeup') + isObserve = False + if self.playerNum == None: + self.playerNum = 1 + self.playerColorString = 'white' + isObserve = True + for xx in range(32): + for blah in self.locatorList[xx].getChildren(): + blah.hide() + if self.locatorList[xx].getChildren().index(blah) != 0: + blah1 = blah.find('**/checker_k*') + + owner = self.board.squareList[xx].getState() + if owner == self.playerNum: + if self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').hide() + else: + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').hide() + self.mySquares.append(xx) + elif owner == 0: + self.hideChildren(self.locatorList[xx].getChildren()) + elif owner == self.playerNum + 2: + if self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').show() + else: + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').show() + self.myKings.append(xx) + elif owner <= 2: + if self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').hide() + else: + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').hide() + elif self.playerColorString == 'white': + x = self.locatorList[xx].getChildren()[2] + x.show() + x.find('**/checker_k*').show() + else: + x = self.locatorList[xx].getChildren()[1] + x.show() + x.find('**/checker_k*').show() + + if isObserve == True: + self.playerNum = None + self.playerColorString = None + return + self.mustJump = False + self.hasNormalMoves = False + for x in self.myKings: + if self.existsLegalJumpsFrom(x, 'king') == True: + self.mustJump = True + break + else: + self.mustJump = False + + if self.mustJump == False: + for x in self.mySquares: + if self.existsLegalJumpsFrom(x, 'normal') == True: + self.mustJump = True + break + else: + self.mustJump = False + + if self.mustJump != True: + for x in self.mySquares: + if self.existsLegalMovesFrom(x, 'normal') == True: + self.hasNormalMoves = True + break + else: + self.hasNormalMoves = False + if self.hasNormalMoves == False: + for x in self.myKings: + if self.existsLegalMovesFrom(x, 'king') == True: + self.hasNormalMoves = True + break + else: + self.hasNormalMoves = False + + if self.mustJump == False and self.hasNormalMoves == False: + pass + return + + def hideChildren(self, nodeList): + for x in range(1, 2): + nodeList[x].hide() + + def animatePeice(self, tableState, moveList, type, playerColor): + messenger.send('wakeup') + if playerColor == 'white': + gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/regular_checker_piecewhite.bam') + else: + gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/regular_checker_pieceblack.bam') + if type == 'king': + gamePeiceForAnimation.find('**/checker_k*').show() + else: + gamePeiceForAnimation.find('**/checker_k*').hide() + gamePeiceForAnimation.reparentTo(self.boardNode) + gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos()) + if self.isRotated == True: + gamePeiceForAnimation.setH(180) + for x in self.locatorList[moveList[0]].getChildren(): + x.hide() + + checkersPeiceTrack = Sequence() + length = len(moveList) + for x in range(length - 1): + checkersPeiceTrack.append(Parallel(SoundInterval(self.moveSound), ProjectileInterval(gamePeiceForAnimation, endPos=self.locatorList[moveList[x + 1]].getPos(), duration=0.5))) + + checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode)) + checkersPeiceTrack.append(Func(self.updateGameState, tableState)) + checkersPeiceTrack.append(Func(self.unAlpha, moveList)) + checkersPeiceTrack.start() + + def announceWin(self, avId): + self.fsm.request('gameOver') + + def unAlpha(self, moveList): + for x in moveList: + self.locatorList[x].setColorOff() + + def doRandomMove(self): + import random + move = [] + foundLegal = False + self.blinker.pause() + self.numRandomMoves += 1 + while not foundLegal: + x = random.randint(0, 9) + for y in self.board.getAdjacent(self.mySquares[x]): + if y != None and self.board.getState(y) == 0: + move.append(self.mySquares[x]) + move.append(y) + foundLegal = True + break + + if move == []: + pass + playSound = Sequence(SoundInterval(self.knockSound)) + playSound.start() + self.d_requestMove(move) + self.moveList = [] + self.isMyTurn = False + if self.numRandomMoves >= 5: + self.exitButtonPushed() + return + + def doNothing(self): + pass diff --git a/toontown/safezone/DistributedCheckersAI.py b/toontown/safezone/DistributedCheckersAI.py new file mode 100644 index 0000000..4110e34 --- /dev/null +++ b/toontown/safezone/DistributedCheckersAI.py @@ -0,0 +1,526 @@ +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.distributed.ClockDelta import * +from toontown.safezone import CheckersBoard + +class DistributedCheckersAI(DistributedNodeAI): + + def __init__(self, air, parent, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.air = air + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.myPos = ( + x, y, z) + self.myHpr = (h, p, r) + self.board = CheckersBoard.CheckersBoard() + self.parent = self.air.doId2do[parent] + self.parentDo = parent + self.wantStart = [] + self.playersPlaying = [] + self.playersSitting = 0 + self.playersTurn = 1 + self.movesMade = 0 + self.playerNum = 1 + self.hasWon = False + self.playersGamePos = [ + None, None] + self.wantTimer = True + self.timerEnd = 0 + self.turnEnd = 0 + self.playersObserving = [] + self.winLaffPoints = 20 + self.movesRequiredToWin = 10 + self.zoneId = self.air.allocateZone() + self.generateOtpObject(air.districtId, self.zoneId, optionalFields=['setX', 'setY', 'setZ', 'setH', 'setP', 'setR']) + self.parent.setCheckersZoneId(self.zoneId) + self.startingPositions = [ + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]] + self.kingPositions = [[31, 30, 29, 28], [0, 1, 2, 3]] + self.timerStart = None + self.fsm = ClassicFSM.ClassicFSM('Checkers', [ + State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [ + 'playing']), + State.State('playing', self.enterPlaying, self.exitPlaying, [ + 'gameOver']), + State.State('gameOver', self.enterGameOver, self.exitGameOver, [ + 'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + self.fsm.enterInitialState() + return + + def announceGenerate(self): + self.parent.setGameDoId(self.doId) + + def getTableDoId(self): + return self.parentDo + + def delete(self): + self.fsm.requestFinalState() + self.board.delete() + del self.fsm + DistributedNodeAI.delete(self) + + def informGameOfPlayer(self): + self.playersSitting += 1 + if self.playersSitting < 2: + self.timerEnd = 0 + else: + if self.playersSitting == 2: + self.timerEnd = globalClock.getRealTime() + 20 + self.parent.isAccepting = False + self.parent.sendUpdate('setIsPlaying', [1]) + else: + if self.playersSitting > 2: + pass + self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + + def informGameOfPlayerLeave(self): + self.playersSitting -= 1 + if self.playersSitting < 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.timerEnd = 0 + self.parent.isAccepting = True + self.parent.sendUpdate('setIsPlaying', [0]) + + if self.playersSitting > 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + pass + else: + self.timerEnd = 0 + if self.timerEnd != 0: + self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + else: + self.sendUpdate('setTimer', [0]) + + def setGameCountdownTime(self): + self.timerEnd = globalClock.getRealTime() + 10 + + def setTurnCountdownTime(self): + self.turnEnd = globalClock.getRealTime() + 40 + + def getTimer(self): + if self.timerEnd != 0: + return 0 + else: + return 0 + + def getTurnTimer(self): + return globalClockDelta.localToNetworkTime(self.turnEnd) + + def requestTimer(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + + def handlePlayerExit(self, avId): + if avId in self.wantStart: + self.wantStart.remove(avId) + if self.fsm.getCurrentState().getName() == 'playing': + gamePos = self.playersGamePos.index(avId) + self.playersGamePos[gamePos] = None + self.fsm.request('gameOver') + return + + def handleEmptyGame(self): + self.movesMade = 0 + self.playersTurn = 1 + self.playerNum = 1 + self.fsm.request('waitingToBegin') + self.parent.isAccepting = True + + def requestWin(self): + avId = self.air.getAvatarIdFromSender() + + def distributeLaffPoints(self): + for x in self.parent.seats: + if x != None: + av = self.air.doId2do.get(x) + av.toonUp(self.winLaffPoints) + + return + + def enterWaitingToBegin(self): + self.setGameCountdownTime() + self.parent.isAccepting = True + + def exitWaitingToBegin(self): + self.turnEnd = 0 + + def enterPlaying(self): + self.parent.isAccepting = False + for x in self.playersGamePos: + if x != None: + self.playersTurn = self.playersGamePos.index(x) + self.d_sendTurn(self.playersTurn + 1) + break + + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)]) + return + + def exitPlaying(self): + pass + + def enterGameOver(self): + self.timerEnd = 0 + isAccepting = True + self.parent.handleGameOver() + self.playersObserving = [] + self.playersTurn = 1 + self.playerNum = 1 + self.clearBoard() + self.sendGameState([]) + self.movesMade = 0 + self.playersGamePos = [None, None, None, None, None, None] + self.parent.isAccepting = True + self.fsm.request('waitingToBegin') + return + + def exitGameOver(self): + pass + + def requestBegin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.wantStart: + self.wantStart.append(avId) + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers = numPlayers + 1 + + if len(self.wantStart) == numPlayers and numPlayers >= 2: + self.d_gameStart(avId) + self.parent.sendIsPlaying() + return + + def d_gameStart(self, avId): + for x in self.playersObserving: + self.sendUpdateToAvatarId(x, 'gameStart', [255]) + + zz = 0 + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers += 1 + self.playersPlaying.append(x) + + if numPlayers == 2: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [2]) + self.playersGamePos[1] = player2 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + self.sendGameState([]) + self.wantStart = [] + self.fsm.request('playing') + self.parent.getTableState() + return + + def d_sendTurn(self, playersTurn): + self.sendUpdate('sendTurn', [playersTurn]) + + def advancePlayerTurn(self): + if self.playersTurn == 0: + self.playersTurn = 1 + self.playerNum = 2 + else: + self.playerNum = 1 + self.playersTurn = 0 + + def requestMove(self, moveList): + if self.checkLegalMoves(moveList) == True: + self.makeMove(moveList) + self.advancePlayerTurn() + self.d_sendTurn(self.playersTurn + 1) + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)]) + else: + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'illegalMove', []) + self.air.writeServerEvent('suspicious', avId, 'has requested an illegal move in Regular checkers - not possible') + + def checkLegalMoves(self, moveList): + if self.board.squareList[moveList[0]].getState() >= 3: + moveType = 'king' + else: + moveType = 'normal' + if len(moveList) == 2: + firstSquare = self.board.squareList[moveList[0]] + secondSquare = self.board.squareList[moveList[1]] + if self.checkLegalMove(firstSquare, secondSquare, moveType) == True: + return True + else: + for x in range(len(moveList) - 1): + y = self.checkLegalJump(self.board.getSquare(moveList[x]), self.board.getSquare(moveList[x + 1]), moveType) + if y == False: + return False + else: + return True + return False + + elif len(moveList) > 2: + for x in range(len(moveList) - 1): + y = self.checkLegalJump(self.board.getSquare(moveList[x]), self.board.getSquare(moveList[x + 1]), moveType) + if y == False: + return False + + return True + + def makeMove(self, moveList): + for x in range(len(moveList) - 1): + firstSquare = self.board.squareList[moveList[x]] + secondSquare = self.board.squareList[moveList[x + 1]] + if firstSquare.getNum() in secondSquare.getAdjacent(): + break + index = firstSquare.jumps.index(secondSquare.getNum()) + self.board.squareList[firstSquare.getAdjacent()[index]].setState(0) + + haveMoved = False + squareState = self.board.squareList[moveList[0]].getState() + if squareState <= 2: + piecetype = 'normal' + if squareState == 1: + playerNum = 1 + else: + playerNum = 2 + else: + piecetype = 'king' + if squareState == 3: + playerNum = 1 + else: + playerNum = 2 + if piecetype == 'normal': + lastElement = moveList[len(moveList) - 1] + if playerNum == 1: + if lastElement in self.kingPositions[0]: + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[lastElement].setState(3) + haveMoved = True + self.sendGameState(moveList) + elif lastElement in self.kingPositions[1]: + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[lastElement].setState(4) + haveMoved = True + self.sendGameState(moveList) + if haveMoved == False: + spot1 = self.board.squareList[moveList[0]].getState() + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[moveList[len(moveList) - 1]].setState(spot1) + self.sendGameState(moveList) + temp = self.playerNum + self.playerNum = 1 + if self.hasWon == True: + return + if self.hasPeicesAndMoves(1, 3) == False: + self.parent.announceWinner('Checkers', self.playersPlaying[1]) + self.fsm.request('gameOver') + self.hasWon = True + return + self.playerNum = temp + temp = self.playerNum + self.playerNum = 2 + if self.hasPeicesAndMoves(2, 4) == False: + self.parent.announceWinner('Checkers', self.playersPlaying[0]) + self.fsm.request('gameOver') + self.hasWon = True + return + self.playerNum = temp + + def hasPeicesAndMoves(self, normalNum, kingNum): + for x in self.board.squareList: + if x.getState() == normalNum: + if self.existsLegalMovesFrom(x.getNum(), 'normal') == True: + return True + if self.existsLegalJumpsFrom(x.getNum(), 'normal') == True: + return True + elif x.getState() == kingNum: + if self.existsLegalMovesFrom(x.getNum(), 'king') == True: + return True + if self.existsLegalJumpsFrom(x.getNum(), 'king') == True: + return True + + return False + + def getState(self): + return self.fsm.getCurrentState().getName() + + def getName(self): + return self.name + + def getGameState(self): + return [ + self.board.getStates(), []] + + def sendGameState(self, moveList): + gameState = self.board.getStates() + self.sendUpdate('setGameState', [gameState, moveList]) + + def clearBoard(self): + for x in self.board.squareList: + x.setState(0) + + def getPosHpr(self): + return self.posHpr + + def existsLegalJumpsFrom(self, index, peice): + if peice == 'king': + for x in range(4): + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + return False + else: + if peice == 'normal': + if self.playerNum == 1: + moveForward = [ + 1, 2] + else: + if self.playerNum == 2: + moveForward = [ + 0, 3] + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + return False + return + + def existsLegalMovesFrom(self, index, peice): + if peice == 'king': + for x in self.board.squareList[index].getAdjacent(): + if x != None: + if self.board.squareList[x].getState() == 0: + return True + + return False + else: + if peice == 'normal': + if self.playerNum == 1: + moveForward = [ + 1, 2] + else: + if self.playerNum == 2: + moveForward = [ + 0, 3] + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + if adj.getState() == 0: + return True + + return False + return + + def existsLegalJumpsFrom(self, index, peice): + if peice == 'king': + for x in range(4): + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + return False + else: + if peice == 'normal': + if self.playerNum == 1: + moveForward = [ + 1, 2] + else: + if self.playerNum == 2: + moveForward = [ + 0, 3] + for x in moveForward: + if self.board.squareList[index].getAdjacent()[x] != None and self.board.squareList[index].getJumps()[x] != None: + adj = self.board.squareList[self.board.squareList[index].getAdjacent()[x]] + jump = self.board.squareList[self.board.squareList[index].getJumps()[x]] + if adj.getState() == 0: + pass + elif adj.getState() == self.playerNum or adj.getState() == self.playerNum + 2: + pass + elif jump.getState() == 0: + return True + + return False + return + + def checkLegalMove(self, firstSquare, secondSquare, peice): + if self.playerNum == 1: + moveForward = [ + 1, 2] + else: + moveForward = [ + 0, 3] + if peice == 'king': + for x in range(4): + if firstSquare.getAdjacent()[x] != None: + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0: + return True + + return False + else: + if peice == 'normal': + for x in moveForward: + if firstSquare.getAdjacent()[x] != None: + if self.board.squareList[firstSquare.getAdjacent()[x]].getState() == 0: + return True + + return False + return + + def checkLegalJump(self, firstSquare, secondSquare, peice): + if self.playerNum == 1: + moveForward = [ + 1, 2] + opposingPeices = [2, 4] + else: + moveForward = [ + 0, 3] + opposingPeices = [1, 3] + if peice == 'king': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + else: + if peice == 'normal': + if secondSquare.getNum() in firstSquare.getJumps(): + index = firstSquare.getJumps().index(secondSquare.getNum()) + if index in moveForward: + if self.board.squareList[firstSquare.getAdjacent()[index]].getState() in opposingPeices: + return True + else: + return False + else: + return False + else: + return False diff --git a/toontown/safezone/DistributedChineseCheckers.py b/toontown/safezone/DistributedChineseCheckers.py new file mode 100644 index 0000000..be1dd09 --- /dev/null +++ b/toontown/safezone/DistributedChineseCheckers.py @@ -0,0 +1,746 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from ChineseCheckersBoard import ChineseCheckersBoard +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.distributed import DelayDelete +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from otp.otpbase import OTPGlobals +from direct.showbase import PythonUtil + +class DistributedChineseCheckers(DistributedNode.DistributedNode): + + def __init__(self, cr): + NodePath.__init__(self, 'DistributedChineseCheckers') + DistributedNode.DistributedNode.__init__(self, cr) + self.cr = cr + self.reparentTo(render) + self.boardNode = loader.loadModel('phase_6/models/golf/checker_game.bam') + self.boardNode.reparentTo(self) + self.board = ChineseCheckersBoard() + self.playerTags = render.attachNewNode('playerTags') + self.playerTagList = [] + self.exitButton = None + self.inGame = False + self.waiting = True + self.startButton = None + self.playerNum = None + self.turnText = None + self.isMyTurn = False + self.wantTimer = True + self.leaveButton = None + self.screenText = None + self.turnText = None + self.exitButton = None + self.numRandomMoves = 0 + self.blinker = Sequence() + self.playersTurnBlinker = Sequence() + self.yourTurnBlinker = Sequence() + self.moveList = [] + self.mySquares = [] + self.playerSeats = None + self.accept('mouse1', self.mouseClick) + self.traverser = base.cTrav + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(ToontownGlobals.WallBitmask) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.myHandler = CollisionHandlerQueue() + self.traverser.addCollider(self.pickerNP, self.myHandler) + 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') + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + self.playerColors = [Vec4(0, 0.9, 0, 1), + Vec4(0.9, 0.9, 0, 1), + Vec4(0.45, 0, 0.45, 1), + Vec4(0.2, 0.4, 0.8, 1), + Vec4(1, 0.45, 1, 1), + Vec4(0.8, 0, 0, 1)] + self.tintConstant = Vec4(0.25, 0.25, 0.25, 0) + self.ghostConstant = Vec4(0, 0, 0, 0.5) + self.startingPositions = [[0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9], + [10, + 11, + 12, + 13, + 23, + 24, + 25, + 35, + 36, + 46], + [65, + 75, + 76, + 86, + 87, + 88, + 98, + 99, + 100, + 101], + [111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120], + [74, + 84, + 85, + 95, + 96, + 97, + 107, + 108, + 109, + 110], + [19, + 20, + 21, + 22, + 32, + 33, + 34, + 44, + 45, + 55]] + self.nonOpposingPositions = [] + self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.mp3') + self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.mp3') + self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.mp3') + self.accept('stoppedAsleep', self.handleSleep) + from direct.fsm import ClassicFSM, State + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + x = self.boardNode.find('**/locators') + self.locatorList = x.getChildren() + tempList = [] + for x in range(0, 121): + self.locatorList[x].setTag('GamePeiceLocator', '%d' % x) + tempList.append(self.locatorList[x].attachNewNode(CollisionNode('picker%d' % x))) + tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.115)) + + for z in self.locatorList: + y = loader.loadModel('phase_6/models/golf/checker_marble.bam') + z.setColor(0, 0, 0, 0) + y.reparentTo(z) + + return + + def setName(self, name): + self.name = name + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + if self.table.fsm.getCurrentState().getName() != 'observing': + if base.localAvatar.doId in self.table.tableState: + self.seatPos = self.table.tableState.index(base.localAvatar.doId) + self.playerTags.setPos(self.getPos()) + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.exitButtonPushed() + if task != None: + task.done + return + + def setTableDoId(self, doId): + self.tableDoId = doId + self.table = self.cr.doId2do[doId] + self.table.setTimerFunc(self.startButtonPushed) + self.fsm.enterInitialState() + self.table.setGameDoId(self.doId) + + def disable(self): + DistributedNode.DistributedNode.disable(self) + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.cleanPlayerTags() + return + + def delete(self): + DistributedNode.DistributedNode.delete(self) + self.table.gameDoId = None + self.table.game = None + if self.exitButton: + self.exitButton.destroy() + if self.startButton: + self.startButton.destroy() + self.clockNode.stop() + self.clockNode.hide() + self.table.startButtonPushed = None + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.table = None + self.cleanPlayerTags() + del self.playerTags + del self.playerTagList + self.playerSeats = None + self.yourTurnBlinker.finish() + return + + def getTimer(self): + self.sendUpdate('requestTimer', []) + + def setTimer(self, timerEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0 and timerEnd != 0: + if timeLeft > 60: + timeLeft = 60 + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.countdown(timeLeft, self.startButtonPushed) + self.clockNode.show() + else: + self.clockNode.stop() + self.clockNode.hide() + return + + def setTurnTimer(self, turnEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(turnEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0: + self.clockNode.setPos(-.74, 0, -0.2) + if self.isMyTurn: + self.clockNode.countdown(timeLeft, self.doRandomMove) + else: + self.clockNode.countdown(timeLeft, self.doNothing) + self.clockNode.show() + return + + def gameStart(self, playerNum): + if playerNum != 255: + self.playerNum = playerNum + self.playerColor = self.playerColors[playerNum - 1] + self.moveCameraForGame() + playerPos = playerNum - 1 + import copy + self.nonOpposingPositions = copy.deepcopy(self.startingPositions) + if playerPos == 0: + self.nonOpposingPositions.pop(0) + self.opposingPositions = self.nonOpposingPositions.pop(2) + elif playerPos == 1: + self.nonOpposingPositions.pop(1) + self.opposingPositions = self.nonOpposingPositions.pop(3) + elif playerPos == 2: + self.nonOpposingPositions.pop(2) + self.opposingPositions = self.nonOpposingPositions.pop(4) + elif playerPos == 3: + self.nonOpposingPositions.pop(3) + self.opposingPositions = self.nonOpposingPositions.pop(0) + elif playerPos == 4: + self.nonOpposingPositions.pop(4) + self.opposingPositions = self.nonOpposingPositions.pop(1) + elif playerPos == 5: + self.nonOpposingPositions.pop(5) + self.opposingPositions = self.nonOpposingPositions.pop(2) + self.fsm.request('playing') + + def sendTurn(self, playersTurn): + self.playersTurnBlinker.finish() + if self.fsm.getCurrentState().getName() == 'playing': + if self.playerSeats == None: + self.sendUpdate('requestSeatPositions', []) + else: + if playersTurn == self.playerNum: + self.isMyTurn = True + self.enableTurnScreenText(playersTurn) + self.playersTurnBlinker = Sequence() + origColor = self.playerColors[playersTurn - 1] + self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.4, origColor - self.tintConstant - self.ghostConstant, origColor)) + self.playersTurnBlinker.append(LerpColorInterval(self.playerTagList[self.playerSeats.index(playersTurn)], 0.4, origColor, origColor - self.tintConstant - self.ghostConstant)) + self.playersTurnBlinker.loop() + return + + def announceSeatPositions(self, playerPos): + self.playerSeats = playerPos + for x in range(6): + pos = self.table.seats[x].getPos(render) + renderedPeice = loader.loadModel('phase_6/models/golf/checker_marble.bam') + renderedPeice.reparentTo(self.playerTags) + renderedPeice.setPos(pos) + renderedPeice.setScale(1.5) + if x == 1: + renderedPeice.setZ(renderedPeice.getZ() + 3.3) + renderedPeice.setScale(1.3) + elif x == 4: + renderedPeice.setZ(renderedPeice.getZ() + 3.3) + renderedPeice.setScale(1.45) + else: + renderedPeice.setZ(renderedPeice.getZ() + 3.3) + renderedPeice.hide() + + self.playerTagList = self.playerTags.getChildren() + for x in playerPos: + if x != 0: + self.playerTagList[playerPos.index(x)].setColor(self.playerColors[x - 1]) + self.playerTagList[playerPos.index(x)].show() + + def cleanPlayerTags(self): + for x in self.playerTagList: + x.removeNode() + + self.playerTagList = [] + self.playerTags.removeNode() + + def moveCameraForGame(self): + if self.table.cameraBoardTrack.isPlaying(): + self.table.cameraBoardTrack.finish() + rotation = 0 + if self.seatPos > 2: + if self.playerNum == 1: + rotation = 180 + elif self.playerNum == 2: + rotation = -120 + elif self.playerNum == 3: + rotation = -60 + elif self.playerNum == 4: + rotation = 0 + elif self.playerNum == 5: + rotation = 60 + elif self.playerNum == 6: + rotation = 120 + elif self.playerNum == 1: + rotation = 0 + elif self.playerNum == 2: + rotation = 60 + elif self.playerNum == 3: + rotation = 120 + elif self.playerNum == 4: + rotation = 180 + elif self.playerNum == 5: + rotation = -120 + elif self.playerNum == 6: + rotation = -60 + if rotation == 60 or rotation == -60: + int = LerpHprInterval(self.boardNode, 2.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + elif rotation == 120 or rotation == -120: + int = LerpHprInterval(self.boardNode, 3.5, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + else: + int = LerpHprInterval(self.boardNode, 4.2, Vec3(rotation, self.boardNode.getP(), self.boardNode.getR()), self.boardNode.getHpr()) + int.start() + + def enterWaitingToBegin(self): + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableExitButton() + self.enableStartButton() + + def exitWaitingToBegin(self): + if self.exitButton: + self.exitButton.destroy() + self.exitButton = None + if self.startButton: + self.startButton.destroy() + self.exitButton = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterPlaying(self): + self.inGame = True + self.enableScreenText() + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableLeaveButton() + + def exitPlaying(self): + self.inGame = False + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + self.playerNum = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.cleanPlayerTags() + return + + def enterGameOver(self): + pass + + def exitGameOver(self): + pass + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableScreenText(self): + defaultPos = (-.8, -0.4) + if self.playerNum == 1: + message = TTLocalizer.ChineseCheckersColorG + color = self.playerColors[0] + elif self.playerNum == 2: + message = TTLocalizer.ChineseCheckersColorY + color = self.playerColors[1] + elif self.playerNum == 3: + message = TTLocalizer.ChineseCheckersColorP + color = self.playerColors[2] + elif self.playerNum == 4: + message = TTLocalizer.ChineseCheckersColorB + color = self.playerColors[3] + elif self.playerNum == 5: + message = TTLocalizer.ChineseCheckersColorPink + color = self.playerColors[4] + elif self.playerNum == 6: + message = TTLocalizer.ChineseCheckersColorR + color = self.playerColors[5] + else: + message = TTLocalizer.ChineseCheckersColorO + color = Vec4(0.0, 0.0, 0.0, 1.0) + defaultPos = (-.8, -0.4) + self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1) + + def enableStartButton(self): + self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.1), scale=0.15, command=lambda self = self: self.startButtonPushed()) + return + + def enableLeaveButton(self): + self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.4), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableTurnScreenText(self, player): + self.yourTurnBlinker.finish() + playerOrder = [1, + 4, + 2, + 5, + 3, + 6] + message1 = TTLocalizer.ChineseCheckersIts + if self.turnText != None: + self.turnText.destroy() + if player == self.playerNum: + message2 = TTLocalizer.ChineseCheckersYourTurn + color = (0, 0, 0, 1) + elif player == 1: + message2 = TTLocalizer.ChineseCheckersGreenTurn + color = self.playerColors[0] + elif player == 2: + message2 = TTLocalizer.ChineseCheckersYellowTurn + color = self.playerColors[1] + elif player == 3: + message2 = TTLocalizer.ChineseCheckersPurpleTurn + color = self.playerColors[2] + elif player == 4: + message2 = TTLocalizer.ChineseCheckersBlueTurn + color = self.playerColors[3] + elif player == 5: + message2 = TTLocalizer.ChineseCheckersPinkTurn + color = self.playerColors[4] + elif player == 6: + message2 = TTLocalizer.ChineseCheckersRedTurn + color = self.playerColors[5] + self.turnText = OnscreenText(text=message1 + message2, pos=(-0.8, -0.5), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1) + if player == self.playerNum: + self.yourTurnBlinker = Sequence() + self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.6, 1.045, 1)) + self.yourTurnBlinker.append(LerpScaleInterval(self.turnText, 0.6, 1, 1.045)) + self.yourTurnBlinker.loop() + return + + def startButtonPushed(self): + self.sendUpdate('requestBegin') + self.startButton.hide() + self.clockNode.stop() + self.clockNode.hide() + + def exitButtonPushed(self): + self.fsm.request('gameOver') + self.table.fsm.request('off') + self.clockNode.stop() + self.clockNode.hide() + self.table.sendUpdate('requestExit') + + def mouseClick(self): + messenger.send('wakeup') + if self.isMyTurn == True and self.inGame == True: + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.traverser.traverse(render) + if self.myHandler.getNumEntries() > 0: + self.myHandler.sortEntries() + pickedObj = self.myHandler.getEntry(0).getIntoNodePath() + pickedObj = pickedObj.getNetTag('GamePeiceLocator') + if pickedObj: + self.handleClicked(int(pickedObj)) + + def handleClicked(self, index): + self.sound = Sequence(SoundInterval(self.clickSound)) + if self.moveList == []: + if index not in self.mySquares: + return + self.moveList.append(index) + if index in self.opposingPositions: + self.isOpposing = True + else: + self.isOpposing = False + self.blinker = Sequence() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor - self.tintConstant, self.playerColor)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor, self.playerColor - self.tintConstant)) + self.blinker.loop() + self.sound.start() + elif self.board.squareList[index].getState() == self.playerNum: + for x in self.moveList: + self.locatorList[x].setColor(1, 1, 1, 1) + self.locatorList[x].hide() + + self.blinker.finish() + self.blinker = Sequence() + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor - self.tintConstant, self.playerColor)) + self.blinker.append(LerpColorInterval(self.locatorList[index], 0.7, self.playerColor, self.playerColor - self.tintConstant)) + self.blinker.loop() + self.sound.start() + self.locatorList[self.moveList[0]].setColor(self.playerColor) + self.locatorList[self.moveList[0]].show() + self.moveList = [] + self.moveList.append(index) + if index in self.opposingPositions: + self.isOpposing = True + else: + self.isOpposing = False + elif self.board.squareList[index].getState() != 0: + return + else: + if len(self.moveList) == 1 and self.board.squareList[index].getState() == 0: + if index in self.board.squareList[self.moveList[0]].getAdjacent(): + for x in self.nonOpposingPositions: + if index in x: + return + + self.moveList.append(index) + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + self.sound.start() + if len(self.moveList) >= 1: + if index == self.moveList[len(self.moveList) - 1]: + for x in self.nonOpposingPositions: + if index in x: + return + + if self.existsLegalJumpsFrom(index) == True: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + self.sound.start() + elif self.checkLegalMove(self.board.getSquare(self.moveList[len(self.moveList) - 1]), self.board.getSquare(index)) == True: + if index not in self.board.squareList[self.moveList[len(self.moveList) - 1]].getAdjacent(): + for x in self.nonOpposingPositions: + if self.existsLegalJumpsFrom(index) == False: + if index in x: + return + + self.moveList.append(index) + self.locatorList[index].setColor(self.playerColor - self.tintConstant - self.ghostConstant) + self.locatorList[index].show() + self.sound.start() + if self.existsLegalJumpsFrom(index) == False: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + + def existsLegalJumpsFrom(self, index): + for x in self.board.squareList[index].getAdjacent(): + if x == None: + pass + elif x in self.moveList: + pass + elif self.board.getState(x) == 0: + pass + elif self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] == None: + pass + elif self.board.getState(self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)]) == 0 and self.board.squareList[x].getAdjacent()[self.board.squareList[index].getAdjacent().index(x)] not in self.moveList: + return True + + return False + + def checkLegalMove(self, firstSquare, secondSquare): + if secondSquare.getNum() in firstSquare.getAdjacent(): + return True + else: + for x in firstSquare.getAdjacent(): + if x == None: + pass + elif self.board.squareList[x].getState() == 0: + pass + elif self.board.squareList[x].getAdjacent()[firstSquare.getAdjacent().index(x)] == secondSquare.getNum(): + return True + + return False + return + + def d_requestMove(self, moveList): + self.sendUpdate('requestMove', [moveList]) + + def setGameState(self, tableState, moveList): + if moveList != []: + self.animatePeice(tableState, moveList) + else: + self.updateGameState(tableState) + + def updateGameState(self, squares): + self.board.setStates(squares) + self.mySquares = [] + messenger.send('wakeup') + for x in range(121): + self.locatorList[x].clearColor() + owner = self.board.squareList[x].getState() + if owner == self.playerNum: + self.mySquares.append(x) + if owner == 0: + self.locatorList[x].hide() + else: + self.locatorList[x].show() + if owner == 1: + self.locatorList[x].setColor(self.playerColors[0]) + elif owner == 2: + self.locatorList[x].setColor(self.playerColors[1]) + elif owner == 3: + self.locatorList[x].setColor(self.playerColors[2]) + elif owner == 4: + self.locatorList[x].setColor(self.playerColors[3]) + elif owner == 5: + self.locatorList[x].setColor(self.playerColors[4]) + elif owner == 6: + self.locatorList[x].setColor(self.playerColors[5]) + + self.mySquares.sort() + self.checkForWin() + + def animatePeice(self, tableState, moveList): + messenger.send('wakeup') + gamePeiceForAnimation = loader.loadModel('phase_6/models/golf/checker_marble.bam') + gamePeiceForAnimation.setColor(self.locatorList[moveList[0]].getColor()) + gamePeiceForAnimation.reparentTo(self.boardNode) + gamePeiceForAnimation.setPos(self.locatorList[moveList[0]].getPos()) + self.locatorList[moveList[0]].hide() + checkersPeiceTrack = Sequence() + length = len(moveList) + for x in range(length - 1): + checkersPeiceTrack.append(Parallel(SoundInterval(self.moveSound), ProjectileInterval(gamePeiceForAnimation, endPos=self.locatorList[moveList[x + 1]].getPos(), duration=0.5))) + + checkersPeiceTrack.append(Func(gamePeiceForAnimation.removeNode)) + checkersPeiceTrack.append(Func(self.updateGameState, tableState)) + checkersPeiceTrack.start() + + def checkForWin(self): + if self.playerNum == 1: + if self.mySquares == self.startingPositions[3]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 2: + if self.mySquares == self.startingPositions[4]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 3: + if self.mySquares == self.startingPositions[5]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 4: + if self.mySquares == self.startingPositions[0]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 5: + if self.mySquares == self.startingPositions[1]: + self.sendUpdate('requestWin', []) + elif self.playerNum == 6: + if self.mySquares == self.startingPositions[2]: + self.sendUpdate('requestWin', []) + + def announceWin(self, avId): + self.fsm.request('gameOver') + + def doRandomMove(self): + if len(self.moveList) >= 2: + self.blinker.finish() + self.d_requestMove(self.moveList) + self.moveList = [] + self.isMyTurn = False + self.playSound = Sequence(SoundInterval(self.knockSound)) + self.playSound.start() + else: + import random + move = [] + foundLegal = False + self.blinker.pause() + self.numRandomMoves += 1 + while not foundLegal: + x = random.randint(0, 9) + for y in self.board.getAdjacent(self.mySquares[x]): + if y != None and self.board.getState(y) == 0: + for zz in self.nonOpposingPositions: + if y not in zz: + move.append(self.mySquares[x]) + move.append(y) + foundLegal = True + break + + break + + if move == []: + pass + playSound = Sequence(SoundInterval(self.knockSound)) + playSound.start() + self.d_requestMove(move) + self.moveList = [] + self.isMyTurn = False + if self.numRandomMoves >= 5: + self.exitButtonPushed() + return + + def doNothing(self): + pass diff --git a/toontown/safezone/DistributedChineseCheckersAI.py b/toontown/safezone/DistributedChineseCheckersAI.py new file mode 100644 index 0000000..e367705 --- /dev/null +++ b/toontown/safezone/DistributedChineseCheckersAI.py @@ -0,0 +1,516 @@ +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.distributed.ClockDelta import * +from toontown.safezone import ChineseCheckersBoard + +class DistributedChineseCheckersAI(DistributedNodeAI): + + def __init__(self, air, parent, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.air = air + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.myPos = ( + x, y, z) + self.myHpr = (h, p, r) + self.board = ChineseCheckersBoard.ChineseCheckersBoard() + self.parent = self.air.doId2do[parent] + self.parentDo = parent + self.wantStart = [] + self.playersPlaying = [] + self.playersSitting = 0 + self.playersTurn = 1 + self.movesMade = 0 + self.playersGamePos = [ + None, None, None, None, None, None] + self.wantTimer = True + self.timerEnd = 0 + self.turnEnd = 0 + self.playersObserving = [] + self.winLaffPoints = 20 + self.movesRequiredToWin = 10 + self.zoneId = self.air.allocateZone() + self.generateOtpObject(air.districtId, self.zoneId, optionalFields=['setX', 'setY', 'setZ', 'setH', 'setP', 'setR']) + self.parent.setCheckersZoneId(self.zoneId) + self.startingPositions = [ + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 23, 24, 25, 35, 36, 46], [65, 75, 76, 86, 87, 88, 98, 99, 100, 101], [111, 112, 113, 114, 115, 116, 117, 118, 119, 120], [74, 84, 85, 95, 96, 97, 107, 108, 109, 110], [19, 20, 21, 22, 32, 33, 34, 44, 45, 55]] + self.timerStart = None + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [ + State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [ + 'playing']), + State.State('playing', self.enterPlaying, self.exitPlaying, [ + 'gameOver']), + State.State('gameOver', self.enterGameOver, self.exitGameOver, [ + 'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + self.fsm.enterInitialState() + return + + def announceGenerate(self): + self.parent.setGameDoId(self.doId) + + def getTableDoId(self): + return self.parentDo + + def delete(self): + self.fsm.requestFinalState() + self.board.delete() + self.playerSeatPos = None + del self.fsm + DistributedNodeAI.delete(self) + return + + def requestSeatPositions(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'announceSeatPositions', [self.playerSeatPos]) + self.sendUpdateToAvatarId(avId, 'sendTurn', [self.playersTurn + 1]) + + def informGameOfPlayer(self): + self.playersSitting += 1 + if self.playersSitting < 2: + self.timerEnd = 0 + else: + if self.playersSitting == 2: + self.timerEnd = globalClock.getRealTime() + 60 + else: + if self.playersSitting > 2: + pass + self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + + def informGameOfPlayerLeave(self): + self.playersSitting -= 1 + if self.playersSitting < 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.timerEnd = 0 + if self.playersSitting > 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + pass + else: + self.timerEnd = 0 + if self.timerEnd != 0: + self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + else: + self.sendUpdate('setTimer', [0]) + + def setGameCountdownTime(self): + self.timerEnd = globalClock.getRealTime() + 60 + + def setTurnCountdownTime(self): + self.turnEnd = globalClock.getRealTime() + 60 + + def getTimer(self): + if self.timerEnd != 0: + return 0 + else: + return 0 + + def getTurnTimer(self): + return globalClockDelta.localToNetworkTime(self.turnEnd) + + def requestTimer(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + + def handlePlayerExit(self, avId): + playerOrder = [ + 1, 4, 2, 5, 3, 6] + if avId in self.wantStart: + self.wantStart.remove(avId) + playstate = self.fsm.getStateNamed('playing') + if self.fsm.getCurrentState().getName() == 'playing': + gamePos = self.playersGamePos.index(avId) + self.playersGamePos[gamePos] = None + for x in self.board.squareList: + if x.getState() == gamePos + 1: + x.setState(0) + + self.sendGameState([]) + if self.playersTurn == gamePos: + self.advancePlayerTurn() + self.d_sendTurn(self.playersTurn + 1) + remainingPlayers = 0 + for x in self.playersGamePos: + if x != None: + remainingPlayers += 1 + + if remainingPlayers == 1: + for x in self.playersGamePos: + if x != None: + self.clearBoard() + self.sendGameState([]) + if self.movesMade >= self.movesRequiredToWin: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', x) + else: + self.fsm.request('gameOver') + + return + + def handleEmptyGame(self): + self.movesMade = 0 + self.playersPlaying = [] + self.playersTurn = 1 + self.fsm.request('waitingToBegin') + + def requestWin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.playersGamePos: + self.air.writeServerEvent('suspicious', avId, 'Has requested a Chinese Checkers win and is NOT playing! SeatList of the table - %s - PlayersGamePos - %s' % (self.parent.seats, self.playersGamePos)) + return + requestWinGamePos = self.playersGamePos.index(avId) + 1 + checkSquares = [] + for x in self.board.squareList: + if x.getState() == requestWinGamePos: + checkSquares.append(x.getNum()) + + if requestWinGamePos == 1: + if checkSquares == self.startingPositions[3]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + else: + if requestWinGamePos == 2: + if checkSquares == self.startingPositions[4]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + else: + if requestWinGamePos == 3: + if checkSquares == self.startingPositions[5]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + else: + if requestWinGamePos == 4: + if checkSquares == self.startingPositions[0]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + else: + if requestWinGamePos == 5: + if checkSquares == self.startingPositions[1]: + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + else: + if requestWinGamePos == 6: + if checkSquares == self.startingPositions[2]: + self.distributeLaffPoints() + self.fsm.request('gameOver') + self.parent.announceWinner('Chinese Checkers', avId) + self.parent = None + return + + def distributeLaffPoints(self): + for x in self.parent.seats: + if x != None: + av = self.air.doId2do.get(x) + av.toonUp(self.winLaffPoints) + + return + + def enterWaitingToBegin(self): + self.setGameCountdownTime() + self.parent.isAccepting = True + + def exitWaitingToBegin(self): + self.turnEnd = 0 + + def enterPlaying(self): + self.parent.isAccepting = False + for x in self.playersGamePos: + if x != None: + self.playersTurn = self.playersGamePos.index(x) + self.d_sendTurn(self.playersTurn + 1) + break + + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)]) + return + + def exitPlaying(self): + pass + + def enterGameOver(self): + self.timerEnd = 0 + isAccepting = True + self.playersObserving = [] + self.parent.handleGameOver() + self.playersTurn = 1 + self.playersPlaying = [] + self.clearBoard() + self.sendGameState([]) + self.movesMade = 0 + self.playersGamePos = [None, None, None, None, None, None] + self.fsm.request('waitingToBegin') + return + + def exitGameOver(self): + pass + + def requestBegin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.wantStart: + self.wantStart.append(avId) + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers = numPlayers + 1 + + if len(self.wantStart) == numPlayers and numPlayers >= 2: + self.d_gameStart(avId) + self.parent.sendIsPlaying() + return + + def d_gameStart(self, avId): + for x in self.playersObserving: + self.sendUpdateToAvatarId(x, 'gameStart', [255]) + + playerJoinOrder = [ + 1, 4, 2, 5, 3, 6] + zz = 0 + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers += 1 + self.playersPlaying.append(x) + + if numPlayers == 2: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + else: + if numPlayers == 3: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [2]) + self.playersGamePos[1] = player1 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [6]) + self.playersGamePos[5] = player3 + for x in self.startingPositions[5]: + self.board.setState(x, 6) + + else: + if numPlayers == 4: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [2]) + self.playersGamePos[1] = player3 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player4 = self.playersPlaying[3] + self.sendUpdateToAvatarId(player4, 'gameStart', [5]) + self.playersGamePos[4] = player4 + for x in self.startingPositions[4]: + self.board.setState(x, 5) + + else: + if numPlayers == 5: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [2]) + self.playersGamePos[1] = player3 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player4 = self.playersPlaying[3] + self.sendUpdateToAvatarId(player4, 'gameStart', [5]) + self.playersGamePos[4] = player4 + for x in self.startingPositions[4]: + self.board.setState(x, 5) + + player5 = self.playersPlaying[4] + self.sendUpdateToAvatarId(player5, 'gameStart', [3]) + self.playersGamePos[2] = player5 + for x in self.startingPositions[2]: + self.board.setState(x, 3) + + else: + if numPlayers == 6: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [1]) + self.playersGamePos[0] = player1 + for x in self.startingPositions[0]: + self.board.setState(x, 1) + + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [4]) + self.playersGamePos[3] = player2 + for x in self.startingPositions[3]: + self.board.setState(x, 4) + + player3 = self.playersPlaying[2] + self.sendUpdateToAvatarId(player3, 'gameStart', [2]) + self.playersGamePos[1] = player3 + for x in self.startingPositions[1]: + self.board.setState(x, 2) + + player4 = self.playersPlaying[3] + self.sendUpdateToAvatarId(player4, 'gameStart', [5]) + self.playersGamePos[4] = player4 + for x in self.startingPositions[4]: + self.board.setState(x, 5) + + player5 = self.playersPlaying[4] + self.sendUpdateToAvatarId(player5, 'gameStart', [3]) + self.playersGamePos[2] = player5 + for x in self.startingPositions[2]: + self.board.setState(x, 3) + + player6 = self.playersPlaying[5] + self.sendUpdateToAvatarId(player6, 'gameStart', [6]) + self.playersGamePos[5] = player6 + for x in self.startingPositions[5]: + self.board.setState(x, 6) + + playerSeatPos = [ + 0, 0, 0, 0, 0, 0] + for x in range(6): + id = self.playersGamePos[x] + if id != None: + playerSeatPos[self.parent.seats.index(id)] = x + 1 + + self.sendUpdate('announceSeatPositions', [playerSeatPos]) + self.playerSeatPos = playerSeatPos + self.sendGameState([]) + self.wantStart = [] + self.fsm.request('playing') + self.parent.getTableState() + return + + def d_sendTurn(self, playersTurn): + self.sendUpdate('sendTurn', [playersTurn]) + + def advancePlayerTurn(self): + foundNewPlayer = False + while foundNewPlayer == False: + self.playersTurn += 1 + if self.playersTurn > 5: + self.playersTurn = 0 + if self.playersGamePos[self.playersTurn] != None: + foundNewPlayer = True + + return + + def requestMove(self, moveList): + playerOrder = [ + 1, 4, 2, 5, 3, 6] + if self.checkLegalMoves(moveList) == True: + self.movesMade += 1 + self.makeMove(moveList) + self.advancePlayerTurn() + self.d_sendTurn(self.playersTurn + 1) + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)]) + + def checkLegalMoves(self, moveList): + if not moveList: + return False + else: + if self.board.squareList[moveList[0]].getState() == 0: + return False + for x in range(len(moveList) - 1): + y = self.checkLegalMove(self.board.getSquare(moveList[x]), self.board.getSquare(moveList[x + 1])) + if y == False: + return False + + return True + + def checkLegalMove(self, firstSquare, secondSquare): + if secondSquare.getNum() in firstSquare.getAdjacent(): + return True + else: + for x in firstSquare.getAdjacent(): + if x == None: + pass + elif self.board.squareList[x].getState() == 0: + pass + elif self.board.squareList[x].getAdjacent()[firstSquare.getAdjacent().index(x)] == secondSquare.getNum(): + return True + + return False + return + + def makeMove(self, moveList): + spot1 = self.board.squareList[moveList[0]].getState() + self.board.squareList[moveList[0]].setState(0) + self.board.squareList[moveList[len(moveList) - 1]].setState(spot1) + self.sendGameState(moveList) + + def getState(self): + return self.fsm.getCurrentState().getName() + + def getName(self): + return self.name + + def getGameState(self): + return [ + self.board.getStates(), []] + + def sendGameState(self, moveList): + gameState = self.board.getStates() + self.sendUpdate('setGameState', [gameState, moveList]) + + def clearBoard(self): + for x in self.board.squareList: + x.setState(0) + + def getPosHpr(self): + return self.posHpr + + def testWin(self): + self.clearBoard() + for x in self.startingPositions[0]: + self.board.squareList[x].setState(4) + + self.board.squareList[self.startingPositions[0][len(self.startingPositions[0]) - 1]].setState(0) + self.board.squareList[51].setState(4) + for x in self.startingPositions[3]: + self.board.squareList[x].setState(1) + + self.board.squareList[120].setState(0) + self.board.squareList[104].setState(1) + self.sendGameState([]) diff --git a/toontown/safezone/DistributedDDTreasure.py b/toontown/safezone/DistributedDDTreasure.py new file mode 100644 index 0000000..d6c0c4d --- /dev/null +++ b/toontown/safezone/DistributedDDTreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedDDTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_6/models/props/starfish_treasure' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedDDTreasureAI.py b/toontown/safezone/DistributedDDTreasureAI.py new file mode 100644 index 0000000..e25df11 --- /dev/null +++ b/toontown/safezone/DistributedDDTreasureAI.py @@ -0,0 +1,6 @@ +import DistributedSZTreasureAI + +class DistributedDDTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/safezone/DistributedDGFlower.py b/toontown/safezone/DistributedDGFlower.py new file mode 100644 index 0000000..356a9a9 --- /dev/null +++ b/toontown/safezone/DistributedDGFlower.py @@ -0,0 +1,60 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from toontown.toonbase import ToontownGlobals +from direct.task import Task +SPIN_RATE = 1.25 + +class DistributedDGFlower(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.bigFlower = loader.loadModel('phase_8/models/props/DG_flower-mod.bam') + self.bigFlower.setPos(1.39, 92.91, 2.0) + self.bigFlower.setScale(2.5) + self.bigFlower.reparentTo(render) + self.flowerCollSphere = CollisionSphere(0, 0, 0, 4.5) + self.flowerCollSphereNode = CollisionNode('bigFlowerCollide') + self.flowerCollSphereNode.addSolid(self.flowerCollSphere) + self.flowerCollSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.bigFlower.attachNewNode(self.flowerCollSphereNode) + self.flowerTrigSphere = CollisionSphere(0, 0, 0, 6.0) + self.flowerTrigSphere.setTangible(0) + self.flowerTrigSphereNode = CollisionNode('bigFlowerTrigger') + self.flowerTrigSphereNode.addSolid(self.flowerTrigSphere) + self.flowerTrigSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + self.bigFlower.attachNewNode(self.flowerTrigSphereNode) + taskMgr.add(self.__flowerSpin, self.taskName('DG-flowerSpin')) + self.accept('enterbigFlowerTrigger', self.__flowerEnter) + self.accept('exitbigFlowerTrigger', self.__flowerExit) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + taskMgr.remove(self.taskName('DG-flowerRaise')) + taskMgr.remove(self.taskName('DG-flowerSpin')) + self.ignore('enterbigFlowerTrigger') + self.ignore('exitbigFlowerTrigger') + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.bigFlower.removeNode() + del self.bigFlower + del self.flowerCollSphere + del self.flowerCollSphereNode + + def __flowerSpin(self, task): + self.bigFlower.setH(self.bigFlower.getH() + SPIN_RATE) + return Task.cont + + def __flowerEnter(self, collisionEntry): + self.sendUpdate('avatarEnter', []) + + def __flowerExit(self, collisionEntry): + self.sendUpdate('avatarExit', []) + + def setHeight(self, newHeight): + pos = self.bigFlower.getPos() + self.bigFlower.lerpPos(pos[0], pos[1], newHeight, 0.5, task=self.taskName('DG-flowerRaise')) diff --git a/toontown/safezone/DistributedDGFlowerAI.py b/toontown/safezone/DistributedDGFlowerAI.py new file mode 100644 index 0000000..60e133d --- /dev/null +++ b/toontown/safezone/DistributedDGFlowerAI.py @@ -0,0 +1,38 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from direct.task import Task +HEIGHT_DELTA = 0.5 +MAX_HEIGHT = 10.0 +MIN_HEIGHT = 2.0 + +class DistributedDGFlowerAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.height = MIN_HEIGHT + self.avList = [] + return None + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def start(self): + return None + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.avList: + self.avList.append(avId) + if self.height + HEIGHT_DELTA <= MAX_HEIGHT: + self.height += HEIGHT_DELTA + self.sendUpdate('setHeight', [self.height]) + + def avatarExit(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.avList: + self.avList.remove(avId) + if self.height - HEIGHT_DELTA >= MIN_HEIGHT: + self.height -= HEIGHT_DELTA + self.sendUpdate('setHeight', [self.height]) diff --git a/toontown/safezone/DistributedDGTreasure.py b/toontown/safezone/DistributedDGTreasure.py new file mode 100644 index 0000000..148bbbd --- /dev/null +++ b/toontown/safezone/DistributedDGTreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedDGTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_8/models/props/flower_treasure' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedDGTreasureAI.py b/toontown/safezone/DistributedDGTreasureAI.py new file mode 100644 index 0000000..a16bef6 --- /dev/null +++ b/toontown/safezone/DistributedDGTreasureAI.py @@ -0,0 +1,6 @@ +import DistributedSZTreasureAI + +class DistributedDGTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/safezone/DistributedDLTreasure.py b/toontown/safezone/DistributedDLTreasure.py new file mode 100644 index 0000000..db74763 --- /dev/null +++ b/toontown/safezone/DistributedDLTreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedDLTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_8/models/props/zzz_treasure' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedDLTreasureAI.py b/toontown/safezone/DistributedDLTreasureAI.py new file mode 100644 index 0000000..a60caa7 --- /dev/null +++ b/toontown/safezone/DistributedDLTreasureAI.py @@ -0,0 +1,6 @@ +import DistributedSZTreasureAI + +class DistributedDLTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/safezone/DistributedEFlyingTreasure.py b/toontown/safezone/DistributedEFlyingTreasure.py new file mode 100644 index 0000000..47c53c1 --- /dev/null +++ b/toontown/safezone/DistributedEFlyingTreasure.py @@ -0,0 +1,42 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +import DistributedSZTreasure +from direct.task.Task import Task +import math +import random + +class DistributedEFlyingTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_5.5/models/props/popsicle_treasure' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' + self.scale = 2 + self.delT = math.pi * 2.0 * random.random() + self.shadow = 0 + + def disable(self): + DistributedSZTreasure.DistributedSZTreasure.disable(self) + taskMgr.remove(self.taskName('flying-treasure')) + + def generateInit(self): + DistributedSZTreasure.DistributedSZTreasure.generateInit(self) + + def setPosition(self, x, y, z): + DistributedSZTreasure.DistributedSZTreasure.setPosition(self, x, y, z) + self.initPos = self.nodePath.getPos() + self.pos = self.nodePath.getPos() + + def startAnimation(self): + taskMgr.add(self.animateTask, self.taskName('flying-treasure')) + + def animateTask(self, task): + pos = self.initPos + t = 0.5 * math.pi * globalClock.getFrameTime() + dZ = 5.0 * math.sin(t + self.delT) + dY = 2.0 * math.cos(t + self.delT) + self.nodePath.setPos(pos[0], pos[1], pos[2] + dZ) + if self.pos: + del self.pos + self.pos = self.nodePath.getPos() + return Task.cont diff --git a/toontown/safezone/DistributedETreasure.py b/toontown/safezone/DistributedETreasure.py new file mode 100644 index 0000000..f303465 --- /dev/null +++ b/toontown/safezone/DistributedETreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedETreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_4/models/props/icecream' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedFindFour.py b/toontown/safezone/DistributedFindFour.py new file mode 100644 index 0000000..e782f0e --- /dev/null +++ b/toontown/safezone/DistributedFindFour.py @@ -0,0 +1,892 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from ChineseCheckersBoard import ChineseCheckersBoard +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.distributed.ClockDelta import * +from otp.otpbase import OTPGlobals +from direct.showbase import PythonUtil + +class DistributedFindFour(DistributedNode.DistributedNode): + + def __init__(self, cr): + NodePath.__init__(self, 'DistributedFindFour') + DistributedNode.DistributedNode.__init__(self, cr) + self.cr = cr + self.reparentTo(render) + self.boardNode = loader.loadModel('phase_6/models/golf/findfour_game.bam') + self.boardNode.reparentTo(self) + self.board = [[0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0], + [0, + 0, + 0, + 0, + 0, + 0, + 0]] + self.exitButton = None + self.inGame = False + self.waiting = True + self.startButton = None + self.playerNum = None + self.turnText = None + self.isMyTurn = False + self.wantTimer = True + self.leaveButton = None + self.screenText = None + self.turnText = None + self.exitButton = None + self.numRandomMoves = 0 + self.blinker = Sequence() + self.playersTurnBlinker = Sequence() + self.yourTurnBlinker = Sequence() + self.winningSequence = Sequence() + self.moveSequence = Sequence() + self.moveList = [] + self.mySquares = [] + self.playerSeats = None + self.moveCol = None + self.move = None + self.accept('mouse1', self.mouseClick) + self.traverser = base.cTrav + self.pickerNode = CollisionNode('mouseRay') + self.pickerNP = camera.attachNewNode(self.pickerNode) + self.pickerNode.setFromCollideMask(BitMask32(4096)) + self.pickerRay = CollisionRay() + self.pickerNode.addSolid(self.pickerRay) + self.myHandler = CollisionHandlerQueue() + self.traverser.addCollider(self.pickerNP, self.myHandler) + 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') + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + self.tintConstant = Vec4(0.25, 0.25, 0.25, 0) + self.ghostConstant = Vec4(0, 0, 0, 0.5) + self.knockSound = base.loadSfx('phase_5/audio/sfx/GUI_knock_1.mp3') + self.clickSound = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.mp3') + self.moveSound = base.loadSfx('phase_6/audio/sfx/CC_move.mp3') + self.accept('stoppedAsleep', self.handleSleep) + from direct.fsm import ClassicFSM, State + self.fsm = ClassicFSM.ClassicFSM('ChineseCheckers', [State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, ['playing', 'gameOver']), State.State('playing', self.enterPlaying, self.exitPlaying, ['gameOver']), State.State('gameOver', self.enterGameOver, self.exitGameOver, ['waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + startLoc = self.boardNode.find('**/locators') + self.locatorList = startLoc.getChildren() + self.startingPositions = self.locatorList.pop(0) + self.startingPositions = self.startingPositions.getChildren() + instancePiece = self.boardNode.find('**/pieces') + tempList = [] + for x in range(7): + self.startingPositions[x].setTag('StartLocator', '%d' % x) + collNode = CollisionNode('startpicker%d' % x) + collNode.setIntoCollideMask(BitMask32(4096)) + tempList.append(self.startingPositions[x].attachNewNode(collNode)) + tempList[x].node().addSolid(CollisionTube(0, 0, 0.23, 0, 0, -.23, 0.2)) + + for z in self.startingPositions: + y = instancePiece.copyTo(z) + for val in y.getChildren(): + val.hide() + + tempList = [] + for x in range(42): + self.locatorList[x].setTag('GamePeiceLocator', '%d' % x) + collNode = CollisionNode('startpicker%d' % x) + collNode.setIntoCollideMask(BitMask32(4096)) + tempList.append(self.locatorList[x].attachNewNode(collNode)) + tempList[x].node().addSolid(CollisionSphere(0, 0, 0, 0.2)) + + for z in self.locatorList: + y = instancePiece.copyTo(z) + for val in y.getChildren(): + val.hide() + + dummyHide = instancePiece.getParent().attachNewNode('DummyHider') + instancePiece.reparentTo(dummyHide) + dummyHide.hide() + return + + def setName(self, name): + self.name = name + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + if self.table.fsm.getCurrentState().getName() != 'observing': + if base.localAvatar.doId in self.table.tableState: + self.seatPos = self.table.tableState.index(base.localAvatar.doId) + if self.seatPos <= 2: + for x in self.startingPositions: + x.setH(0) + + for x in self.locatorList: + x.setH(0) + + else: + for x in self.startingPositions: + x.setH(180) + + for x in self.locatorList: + x.setH(180) + + self.moveCameraForGame() + else: + self.seatPos = self.table.seatBumpForObserve + if self.seatPos > 2: + for x in self.startingPositions: + x.setH(180) + + for x in self.locatorList: + x.setH(180) + + self.moveCameraForGame() + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.exitButtonPushed() + if task != None: + task.done + return + + def setTableDoId(self, doId): + self.tableDoId = doId + self.table = self.cr.doId2do[doId] + self.table.setTimerFunc(self.startButtonPushed) + self.fsm.enterInitialState() + self.table.setGameDoId(self.doId) + + def disable(self): + DistributedNode.DistributedNode.disable(self) + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + taskMgr.remove('playerTurnTask') + return + + def delete(self): + DistributedNode.DistributedNode.delete(self) + self.table.gameDoId = None + self.table.game = None + if self.exitButton: + self.exitButton.destroy() + if self.startButton: + self.startButton.destroy() + self.clockNode.stop() + self.clockNode.hide() + self.table.startButtonPushed = None + self.ignore('mouse1') + self.ignore('stoppedAsleep') + self.fsm = None + self.table = None + self.winningSequence.finish() + taskMgr.remove('playerTurnTask') + return + + def getTimer(self): + self.sendUpdate('requestTimer', []) + + def setTimer(self, timerEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'waitingToBegin' and not self.table.fsm.getCurrentState().getName() == 'observing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0 and timerEnd != 0: + if timeLeft > 60: + timeLeft = 60 + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.countdown(timeLeft, self.startButtonPushed) + self.clockNode.show() + else: + self.clockNode.stop() + self.clockNode.hide() + return + + def setTurnTimer(self, turnEnd): + if self.fsm.getCurrentState() != None and self.fsm.getCurrentState().getName() == 'playing': + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(turnEnd) + timeLeft = int(time - globalClock.getRealTime()) + if timeLeft > 0: + self.clockNode.setPos(0.64, 0, -0.27) + self.clockNode.countdown(timeLeft, self.doRandomMove) + self.clockNode.show() + return + + def gameStart(self, playerNum): + if playerNum != 255: + self.playerNum = playerNum + if self.playerNum == 1: + self.playerColorString = 'Red' + else: + self.playerColorString = 'Yellow' + self.moveCameraForGame() + self.fsm.request('playing') + + def sendTurn(self, playersTurn): + if self.fsm.getCurrentState().getName() == 'playing': + if playersTurn == self.playerNum: + self.isMyTurn = True + taskMgr.add(self.turnTask, 'playerTurnTask') + self.enableTurnScreenText(playersTurn) + + def illegalMove(self): + self.exitButtonPushed() + + def moveCameraForGame(self): + if self.table.cameraBoardTrack.isPlaying(): + self.table.cameraBoardTrack.pause() + rotation = 0 + if self.seatPos <= 2: + position = self.table.seats[1].getPos() + position = position + Vec3(0, -8, 12.8) + int = LerpPosHprInterval(camera, 2, position, Vec3(0, -38, 0), camera.getPos(), camera.getHpr()) + else: + position = self.table.seats[4].getPos() + position = position + Vec3(0, -8, 12.8) + if camera.getH() < 0: + int = LerpPosHprInterval(camera, 2, position, Vec3(-180, -20, 0), camera.getPos(), camera.getHpr()) + else: + int = LerpPosHprInterval(camera, 2, position, Vec3(180, -20, 0), camera.getPos(), camera.getHpr()) + int.start() + + def enterWaitingToBegin(self): + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableExitButton() + self.enableStartButton() + + def exitWaitingToBegin(self): + if self.exitButton: + self.exitButton.destroy() + self.exitButton = None + if self.startButton: + self.startButton.destroy() + self.exitButton = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterPlaying(self): + self.inGame = True + self.enableScreenText() + if self.table.fsm.getCurrentState().getName() != 'observing': + self.enableLeaveButton() + + def exitPlaying(self): + self.inGame = False + if self.leaveButton: + self.leaveButton.destroy() + self.leavebutton = None + self.playerNum = None + if self.screenText: + self.screenText.destroy() + self.screenText = None + if self.turnText: + self.turnText.destroy() + self.turnText = None + self.clockNode.stop() + self.clockNode.hide() + return + + def enterGameOver(self): + pass + + def exitGameOver(self): + pass + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersGetUpButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.8), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableScreenText(self): + defaultPos = (-.7, -0.29) + if self.playerNum == 1: + message = 'You are Red' + color = Vec4(1, 0, 0, 1) + elif self.playerNum == 2: + message = 'You are Yellow' + color = Vec4(1, 1, 0, 1) + else: + message = TTLocalizer.CheckersObserver + color = Vec4(0, 0, 0, 1) + self.screenText = OnscreenText(text=message, pos=defaultPos, scale=0.1, fg=color, align=TextNode.ACenter, mayChange=1) + + def enableStartButton(self): + self.startButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersStartButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.23), text_scale=0.6, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.57), scale=0.15, command=lambda self = self: self.startButtonPushed()) + return + + def enableLeaveButton(self): + self.leaveButton = DirectButton(relief=None, text=TTLocalizer.ChineseCheckersQuitButton, text_fg=(1, 1, 0.65, 1), text_pos=(0, -.13), text_scale=0.5, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0.92, 0, 0.8), scale=0.15, command=lambda self = self: self.exitButtonPushed()) + return + + def enableTurnScreenText(self, player): + playerOrder = [1, + 4, + 2, + 5, + 3, + 6] + message1 = TTLocalizer.CheckersIts + if self.turnText != None: + self.turnText.destroy() + if player == self.playerNum: + message2 = TTLocalizer.ChineseCheckersYourTurn + color = (0, 0, 0, 1) + elif player == 1: + message2 = "Red's Turn" + color = (1, 0, 0, 1) + elif player == 2: + message2 = "Yellow's Turn" + color = (1, 1, 0, 1) + self.turnText = OnscreenText(text=message1 + message2, pos=(-0.7, -0.39), scale=0.092, fg=color, align=TextNode.ACenter, mayChange=1) + return + + def startButtonPushed(self): + self.sendUpdate('requestBegin') + self.startButton.hide() + self.clockNode.stop() + self.clockNode.hide() + + def exitButtonPushed(self): + self.fsm.request('gameOver') + self.table.fsm.request('off') + self.clockNode.stop() + self.clockNode.hide() + self.table.sendUpdate('requestExit') + + def mouseClick(self): + messenger.send('wakeup') + if self.isMyTurn == True and self.inGame == True and not self.moveSequence.isPlaying(): + if self.moveCol != None: + self.d_requestMove(self.moveCol) + self.moveCol = None + self.isMyTurn = False + taskMgr.remove('playerTurnTask') + return + + def handleClicked(self, index): + pass + + def turnTask(self, task): + if base.mouseWatcherNode.hasMouse() == False: + return task.cont + if self.isMyTurn == False: + return task.cont + if self.moveSequence.isPlaying(): + return task.cont + mpos = base.mouseWatcherNode.getMouse() + self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY()) + self.traverser.traverse(render) + if self.myHandler.getNumEntries() > 0: + self.myHandler.sortEntries() + pickedObj = self.myHandler.getEntry(0).getIntoNodePath() + pickedObj = pickedObj.getNetTag('StartLocator') + if pickedObj: + colVal = int(pickedObj) + if colVal == self.moveCol: + return task.cont + if self.board[0][colVal] == 0: + if self.moveCol != None: + for x in self.startingPositions[self.moveCol].getChild(1).getChildren(): + x.hide() + + self.moveCol = colVal + if self.playerNum == 1: + self.startingPositions[self.moveCol].getChild(1).getChild(2).show() + elif self.playerNum == 2: + self.startingPositions[self.moveCol].getChild(1).getChild(3).show() + return task.cont + + def d_requestMove(self, moveCol): + self.sendUpdate('requestMove', [moveCol]) + + def setGameState(self, tableState, moveCol, movePos, turn): + messenger.send('wakeup') + if self.table.fsm.getCurrentState().getName() == 'observing': + isBlank = True + for x in range(7): + if self.board[5][x] != 0: + isBlank = False + break + + gameBlank = True + for x in range(7): + if tableState[5][x] != 0: + gameBlank = False + break + + if isBlank == True and gameBlank == False: + for x in range(6): + for y in range(7): + self.board[x][y] = tableState[x][y] + + self.updateGameState() + return + if moveCol == 0 and movePos == 0 and turn == 0: + for x in range(6): + for y in range(7): + self.board[x][y] = tableState[x][y] + + self.updateGameState() + else: + self.animatePeice(tableState, moveCol, movePos, turn) + didIWin = self.checkForWin() + if didIWin != None: + self.sendUpdate('requestWin', [didIWin]) + return + + def updateGameState(self): + for x in range(6): + for y in range(7): + for z in self.locatorList[x * 7 + y].getChild(1).getChildren(): + z.hide() + + for x in range(6): + for y in range(7): + state = self.board[x][y] + if state == 1: + self.locatorList[x * 7 + y].getChild(1).getChild(0).show() + elif state == 2: + self.locatorList[x * 7 + y].getChild(1).getChild(1).show() + + def checkForWin(self): + for x in range(6): + for y in range(7): + if self.board[x][y] == self.playerNum: + if self.checkHorizontal(x, y, self.playerNum) == True: + return [x, y] + elif self.checkVertical(x, y, self.playerNum) == True: + return [x, y] + elif self.checkDiagonal(x, y, self.playerNum) == True: + return [x, y] + + return None + + def announceWinnerPosition(self, x, y, winDirection, playerNum): + self.isMyturn = False + if self.turnText: + self.turnText.hide() + self.clockNode.stop() + self.clockNode.hide() + if winDirection == 0: + blinkList = self.findHorizontal(x, y, playerNum) + elif winDirection == 1: + blinkList = self.findVertical(x, y, playerNum) + elif winDirection == 2: + blinkList = self.findDiagonal(x, y, playerNum) + if blinkList != []: + print blinkList + val0 = x * 7 + y + x = blinkList[0][0] + y = blinkList[0][1] + val1 = x * 7 + y + x = blinkList[1][0] + y = blinkList[1][1] + val2 = x * 7 + y + x = blinkList[2][0] + y = blinkList[2][1] + val3 = x * 7 + y + self.winningSequence = Sequence() + downBlinkerParallel = Parallel(LerpColorInterval(self.locatorList[val0], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val1], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val2], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[val3], 0.3, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1))) + upBlinkerParallel = Parallel(LerpColorInterval(self.locatorList[val0], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val1], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val2], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)), LerpColorInterval(self.locatorList[val3], 0.3, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5))) + self.winningSequence.append(downBlinkerParallel) + self.winningSequence.append(upBlinkerParallel) + self.winningSequence.loop() + + def tie(self): + self.tieSequence = Sequence(autoFinish=1) + self.clockNode.stop() + self.clockNode.hide() + self.isMyTurn = False + self.moveSequence.finish() + if self.turnText: + self.turnText.hide() + for x in range(41): + self.tieSequence.append(Parallel(LerpColorInterval(self.locatorList[x], 0.15, Vec4(0.5, 0.5, 0.5, 0.5), Vec4(1, 1, 1, 1)), LerpColorInterval(self.locatorList[x], 0.15, Vec4(1, 1, 1, 1), Vec4(0.5, 0.5, 0.5, 0.5)))) + + whisper = WhisperPopup('This Find Four game has resulted in a Tie!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + whisper.manage(base.marginManager) + self.tieSequence.start() + + def hideChildren(self, nodeList): + pass + + def animatePeice(self, tableState, moveCol, movePos, turn): + messenger.send('wakeup') + for x in range(6): + for y in range(7): + self.board[x][y] = tableState[x][y] + + pos = self.startingPositions[moveCol].getPos() + if turn == 0: + peice = self.startingPositions[moveCol].getChild(1).getChildren()[2] + peice.show() + elif turn == 1: + peice = self.startingPositions[moveCol].getChild(1).getChildren()[3] + peice.show() + self.moveSequence = Sequence() + startPos = self.startingPositions[moveCol].getPos() + arrayLoc = movePos * 7 + moveCol + self.moveSequence.append(LerpPosInterval(self.startingPositions[moveCol], 1.5, self.locatorList[arrayLoc].getPos(self), startPos)) + self.moveSequence.append(Func(peice.hide)) + self.moveSequence.append(Func(self.startingPositions[moveCol].setPos, startPos)) + self.moveSequence.append(Func(self.updateGameState)) + self.moveSequence.start() + + def announceWin(self, avId): + self.fsm.request('gameOver') + + def doRandomMove(self): + if self.isMyTurn: + if self.moveCol != None: + self.d_requestMove(self.moveCol) + self.moveCol = None + self.isMyTurn = False + taskMgr.remove('playerTurnTask') + else: + hasfound = False + while hasfound == False: + from random import * + x = randint(0, 6) + if self.board[0][x] == 0: + self.d_requestMove(x) + self.moveCol = None + self.isMyTurn = False + taskMgr.remove('playerTurnTask') + hasfound = True + + return + + def doNothing(self): + pass + + def checkHorizontal(self, rVal, cVal, playerNum): + if cVal == 3: + for x in range(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + + for x in range(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal == 2: + for x in range(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal == 4: + for x in range(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + + return False + else: + return False + + def checkVertical(self, rVal, cVal, playerNum): + if rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal] != playerNum: + break + if self.board[rVal + x][cVal] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in range(1, 4): + if self.board[rVal - x][cVal] != playerNum: + break + if self.board[rVal - x][cVal] == playerNum and x == 3: + return True + + return False + else: + return False + + def checkDiagonal(self, rVal, cVal, playerNum): + if cVal <= 2: + if rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in range(1, 4): + if self.board[rVal - x][cVal + x] != playerNum: + break + if self.board[rVal - x][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal >= 4: + if rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in range(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 3 or rVal == 4 or rVal == 5: + for x in range(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + for x in range(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 0 or rVal == 1 or rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + + for x in range(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + + return False + return False + + def findHorizontal(self, rVal, cVal, playerNum): + if cVal == 3: + retList = [] + for x in range(1, 4): + retList.append([rVal, cVal - x]) + if self.board[rVal][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return retList + + for x in range(1, 4): + retList.append([rVal, cVal + x]) + if self.board[rVal][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif cVal == 2: + retList = [] + for x in range(1, 4): + retList.append([rVal, cVal + x]) + if self.board[rVal][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif cVal == 4: + retList = [] + for x in range(1, 4): + retList.append([rVal, cVal - x]) + if self.board[rVal][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return retList + + return [] + else: + return [] + + def findVertical(self, rVal, cVal, playerNum): + if rVal == 2: + retList = [] + for x in range(1, 4): + retList.append([rVal + x, cVal]) + if self.board[rVal + x][cVal] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3: + retList = [] + for x in range(1, 4): + retList.append([rVal - x, cVal]) + if self.board[rVal - x][cVal] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal] == playerNum and x == 3: + return retList + + return [] + else: + return [] + + def findDiagonal(self, rVal, cVal, playerNum): + retList = [] + if cVal <= 2: + if rVal == 2: + for x in range(1, 4): + retList.append([rVal + x, cVal + x]) + if self.board[rVal + x][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3: + for x in range(1, 4): + retList.append([rVal - x, cVal + x]) + if self.board[rVal - x][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal + x] == playerNum and x == 3: + return retList + + return [] + elif cVal >= 4: + if rVal == 2: + for x in range(1, 4): + retList.append([rVal + x, cVal - x]) + if self.board[rVal + x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3: + for x in range(1, 4): + retList.append([rVal - x, cVal - x]) + if self.board[rVal - x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 3 or rVal == 4 or rVal == 5: + for x in range(1, 4): + retList.append([rVal - x, cVal - x]) + if self.board[rVal - x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return retList + + for x in range(1, 4): + retList.append([rVal + x, cVal - x]) + if self.board[rVal + x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return retList + + return [] + elif rVal == 0 or rVal == 1 or rVal == 2: + for x in range(1, 4): + retList.append([rVal + x, cVal - x]) + if self.board[rVal + x][cVal - x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return retList + + for x in range(1, 4): + retList.append([rVal + x, cVal + x]) + if self.board[rVal + x][cVal + x] != playerNum: + retList = [] + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return retList + + return [] + return [] diff --git a/toontown/safezone/DistributedFindFourAI.py b/toontown/safezone/DistributedFindFourAI.py new file mode 100644 index 0000000..f9e9c36 --- /dev/null +++ b/toontown/safezone/DistributedFindFourAI.py @@ -0,0 +1,427 @@ +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * + +class DistributedFindFourAI(DistributedNodeAI): + + def __init__(self, air, parent, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.air = air + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.myPos = ( + x, y, z) + self.myHpr = (h, p, r) + self.board = [[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]] + self.parent = self.air.doId2do[parent] + self.parentDo = parent + self.wantStart = [] + self.playersPlaying = [] + self.playersSitting = 0 + self.playersTurn = 1 + self.movesMade = 0 + self.playerNum = 1 + self.winDirection = None + self.playersGamePos = [ + None, None] + self.wantTimer = True + self.timerEnd = 0 + self.turnEnd = 0 + self.playersObserving = [] + self.winLaffPoints = 20 + self.movesRequiredToWin = 10 + self.zoneId = self.air.allocateZone() + self.generateOtpObject(air.districtId, self.zoneId, optionalFields=['setX', 'setY', 'setZ', 'setH', 'setP', 'setR']) + self.parent.setCheckersZoneId(self.zoneId) + self.timerStart = None + self.fsm = ClassicFSM.ClassicFSM('Checkers', [ + State.State('waitingToBegin', self.enterWaitingToBegin, self.exitWaitingToBegin, [ + 'playing']), + State.State('playing', self.enterPlaying, self.exitPlaying, [ + 'gameOver']), + State.State('gameOver', self.enterGameOver, self.exitGameOver, [ + 'waitingToBegin'])], 'waitingToBegin', 'waitingToBegin') + self.fsm.enterInitialState() + return + + def announceGenerate(self): + self.parent.setGameDoId(self.doId) + + def getTableDoId(self): + return self.parentDo + + def delete(self): + self.fsm.requestFinalState() + self.parent = None + self.parentDo = None + del self.board + del self.fsm + DistributedNodeAI.delete(self) + return + + def informGameOfPlayer(self): + self.playersSitting += 1 + if self.playersSitting < 2: + self.timerEnd = 0 + else: + if self.playersSitting == 2: + self.timerEnd = globalClock.getRealTime() + 20 + self.parent.isAccepting = False + self.parent.sendUpdate('setIsPlaying', [1]) + else: + if self.playersSitting > 2: + pass + self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + + def informGameOfPlayerLeave(self): + self.playersSitting -= 1 + if self.playersSitting < 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + self.timerEnd = 0 + self.parent.isAccepting = True + self.parent.sendUpdate('setIsPlaying', [0]) + if self.playersSitting > 2 and self.fsm.getCurrentState().getName() == 'waitingToBegin': + pass + else: + self.timerEnd = 0 + if self.timerEnd != 0: + self.sendUpdate('setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + else: + self.sendUpdate('setTimer', [0]) + + def setGameCountdownTime(self): + self.timerEnd = globalClock.getRealTime() + 10 + + def setTurnCountdownTime(self): + self.turnEnd = globalClock.getRealTime() + 25 + + def getTimer(self): + if self.timerEnd != 0: + return 0 + else: + return 0 + + def getTurnTimer(self): + return globalClockDelta.localToNetworkTime(self.turnEnd) + + def requestTimer(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setTimer', [globalClockDelta.localToNetworkTime(self.timerEnd)]) + + def handlePlayerExit(self, avId): + if avId in self.wantStart: + self.wantStart.remove(avId) + if self.fsm.getCurrentState().getName() == 'playing': + gamePos = self.playersGamePos.index(avId) + self.playersGamePos[gamePos] = None + self.fsm.request('gameOver') + return + + def handleEmptyGame(self): + self.movesMade = 0 + self.playersPlaying = [] + self.playersTurn = 1 + self.playerNum = 1 + self.fsm.request('waitingToBegin') + self.parent.isAccepting = True + + def requestWin(self, pieceNum): + avId = self.air.getAvatarIdFromSender() + playerNum = self.playersGamePos.index(avId) + 1 + x = pieceNum[0] + y = pieceNum[1] + if self.checkWin(x, y, playerNum) == True: + self.sendUpdate('announceWinnerPosition', [x, y, self.winDirection, playerNum]) + winnersSequence = Sequence(Wait(5.0), Func(self.fsm.request, 'gameOver'), Func(self.parent.announceWinner, 'Find Four', avId)) + winnersSequence.start() + else: + self.sendUpdateToAvatarId(avId, 'illegalMove', []) + + def distributeLaffPoints(self): + for x in self.parent.seats: + if x != None: + av = self.air.doId2do.get(x) + av.toonUp(self.winLaffPoints) + + return + + def enterWaitingToBegin(self): + self.setGameCountdownTime() + self.parent.isAccepting = True + + def exitWaitingToBegin(self): + self.turnEnd = 0 + + def enterPlaying(self): + self.parent.isAccepting = False + for x in self.playersGamePos: + if x != None: + self.playersTurn = self.playersGamePos.index(x) + self.d_sendTurn(self.playersTurn + 1) + break + + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)]) + return + + def exitPlaying(self): + pass + + def enterGameOver(self): + self.timerEnd = 0 + isAccepting = True + self.parent.handleGameOver() + self.playersObserving = [] + self.playersTurn = 1 + self.playerNum = 1 + self.playersPlaying = [] + self.movesMade = 0 + self.playersGamePos = [None, None] + self.parent.isAccepting = True + self.fsm.request('waitingToBegin') + return + + def exitGameOver(self): + pass + + def requestBegin(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.wantStart: + self.wantStart.append(avId) + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers = numPlayers + 1 + + if len(self.wantStart) == numPlayers and numPlayers >= 2: + self.d_gameStart(avId) + self.parent.sendIsPlaying() + return + + def d_gameStart(self, avId): + for x in self.playersObserving: + self.sendUpdateToAvatarId(x, 'gameStart', [255]) + + zz = 0 + numPlayers = 0 + for x in self.parent.seats: + if x != None: + numPlayers += 1 + self.playersPlaying.append(x) + + if numPlayers == 2: + player1 = self.playersPlaying[0] + self.sendUpdateToAvatarId(player1, 'gameStart', [1]) + self.playersGamePos[0] = player1 + player2 = self.playersPlaying[1] + self.sendUpdateToAvatarId(player2, 'gameStart', [2]) + self.playersGamePos[1] = player2 + self.wantStart = [] + self.fsm.request('playing') + self.parent.getTableState() + return + + def d_sendTurn(self, playersTurn): + self.sendUpdate('sendTurn', [playersTurn]) + + def advancePlayerTurn(self): + if self.playersTurn == 0: + self.playersTurn = 1 + self.playerNum = 2 + else: + self.playerNum = 1 + self.playersTurn = 0 + + def requestMove(self, moveColumn): + avId = self.air.getAvatarIdFromSender() + turn = self.playersTurn + if avId in self.playersGamePos: + if self.playersGamePos.index(avId) != self.playersTurn: + pass + if self.board[0][moveColumn] != 0: + self.sendUpdateToAvatarId(avId, 'illegalMove', []) + for x in range(6): + if self.board[x][moveColumn] == 0: + movePos = x + + self.board[movePos][moveColumn] = self.playersTurn + 1 + if self.checkForTie() == True: + self.sendUpdate('setGameState', [self.board, moveColumn, movePos, turn]) + self.sendUpdate('tie', []) + winnersSequence = Sequence(Wait(8.0), Func(self.fsm.request, 'gameOver')) + winnersSequence.start() + return + self.movesMade += 1 + self.advancePlayerTurn() + self.setTurnCountdownTime() + self.sendUpdate('setTurnTimer', [globalClockDelta.localToNetworkTime(self.turnEnd)]) + self.d_sendTurn(self.playersTurn + 1) + self.sendUpdate('setGameState', [self.board, moveColumn, movePos, turn]) + + def checkForTie(self): + for x in range(7): + if self.board[0][x] == 0: + return False + + return True + + def getState(self): + return self.fsm.getCurrentState().getName() + + def getName(self): + return self.name + + def getGameState(self): + return [ + self.board, 0, 0, 0] + + def clearBoard(self): + for x in self.board.squareList: + x.setState(0) + + def getPosHpr(self): + return self.posHpr + + def tempSetBoardState(self): + self.board = [ + [ + 0, 0, 0, 0, 0, 0, 0], [1, 2, 1, 2, 2, 2, 1], [2, 2, 1, 2, 1, 2, 1], [2, 1, 1, 2, 2, 1, 2], [1, 2, 2, 1, 2, 1, 1], [1, 2, 1, 2, 1, 2, 1]] + self.sendUpdate('setGameState', [self.board, 0, 0, 1]) + + def checkWin(self, rVal, cVal, playerNum): + if self.checkHorizontal(rVal, cVal, playerNum) == True: + self.winDirection = 0 + return True + elif self.checkVertical(rVal, cVal, playerNum) == True: + self.winDirection = 1 + return True + elif self.checkDiagonal(rVal, cVal, playerNum) == True: + self.winDirection = 2 + return True + else: + self.winDirection = None + return False + return + + def checkHorizontal(self, rVal, cVal, playerNum): + if cVal == 3: + for x in range(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + + for x in range(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal == 2: + for x in range(1, 4): + if self.board[rVal][cVal + x] != playerNum: + break + if self.board[rVal][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal == 4: + for x in range(1, 4): + if self.board[rVal][cVal - x] != playerNum: + break + if self.board[rVal][cVal - x] == playerNum and x == 3: + return True + + return False + else: + return False + + def checkVertical(self, rVal, cVal, playerNum): + if rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal] != playerNum: + break + if self.board[rVal + x][cVal] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in range(1, 4): + if self.board[rVal - x][cVal] != playerNum: + break + if self.board[rVal - x][cVal] == playerNum and x == 3: + return True + + return False + else: + return False + + def checkDiagonal(self, rVal, cVal, playerNum): + if cVal <= 2: + if rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in range(1, 4): + if self.board[rVal - x][cVal + x] != playerNum: + break + if self.board[rVal - x][cVal + x] == playerNum and x == 3: + return True + + return False + elif cVal >= 4: + if rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 3: + for x in range(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 3 or rVal == 4 or rVal == 5: + for x in range(1, 4): + if self.board[rVal - x][cVal - x] != playerNum: + break + if self.board[rVal - x][cVal - x] == playerNum and x == 3: + return True + + for x in range(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + + return False + elif rVal == 0 or rVal == 1 or rVal == 2: + for x in range(1, 4): + if self.board[rVal + x][cVal - x] != playerNum: + break + if self.board[rVal + x][cVal - x] == playerNum and x == 3: + return True + + for x in range(1, 4): + if self.board[rVal + x][cVal + x] != playerNum: + break + if self.board[rVal + x][cVal + x] == playerNum and x == 3: + return True + + return False + return False diff --git a/toontown/safezone/DistributedFishingSpot.py b/toontown/safezone/DistributedFishingSpot.py new file mode 100644 index 0000000..3522271 --- /dev/null +++ b/toontown/safezone/DistributedFishingSpot.py @@ -0,0 +1,1054 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directtools.DirectGeometry import LineNodePath +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.fishing import FishGlobals +from toontown.shtiker import FishPage +from toontown.toonbase import TTLocalizer +from toontown.quest import Quests +from direct.actor import Actor +from direct.showutil import Rope +import math +from direct.task.Task import Task +import random +import random +from toontown.fishing import FishingTargetGlobals +from toontown.fishing import FishBase +from toontown.fishing import FishPanel +from toontown.effects import Ripples +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownTimer +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.hood import ZoneUtil +from toontown.toontowngui import TeaserPanel + +class DistributedFishingSpot(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFishingSpot') + vZeroMax = 25.0 + angleMax = 30.0 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.lastAvId = 0 + self.lastFrame = 0 + self.avId = 0 + self.av = None + self.placedAvatar = 0 + self.localToonFishing = 0 + self.nodePath = None + self.collSphere = None + self.collNode = None + self.collNodePath = None + self.castTrack = None + self.pond = None + self.guiTrack = None + self.madeGui = 0 + self.castGui = None + self.itemGui = None + self.pole = None + self.line = None + self.poleNode = [] + self.ptop = None + self.bob = None + self.bobBobTask = None + self.splashSounds = None + self.ripples = None + self.line = None + self.lineSphere = None + self.power = 0.0 + self.startAngleNP = 0 + self.firstCast = 1 + self.fishPanel = None + self.fsm = ClassicFSM.ClassicFSM('DistributedFishingSpot', [State.State('off', self.enterOff, self.exitOff, ['waiting', + 'distCasting', + 'fishing', + 'reward', + 'leaving']), + State.State('waiting', self.enterWaiting, self.exitWaiting, ['localAdjusting', + 'distCasting', + 'leaving', + 'sellFish']), + State.State('localAdjusting', self.enterLocalAdjusting, self.exitLocalAdjusting, ['localCasting', 'leaving']), + State.State('localCasting', self.enterLocalCasting, self.exitLocalCasting, ['localAdjusting', 'fishing', 'leaving']), + State.State('distCasting', self.enterDistCasting, self.exitDistCasting, ['fishing', 'leaving', 'reward']), + State.State('fishing', self.enterFishing, self.exitFishing, ['localAdjusting', + 'distCasting', + 'waitForAI', + 'reward', + 'leaving']), + State.State('sellFish', self.enterSellFish, self.exitSellFish, ['waiting', 'leaving']), + State.State('waitForAI', self.enterWaitForAI, self.exitWaitForAI, ['reward', 'leaving']), + State.State('reward', self.enterReward, self.exitReward, ['localAdjusting', + 'distCasting', + 'leaving', + 'sellFish']), + State.State('leaving', self.enterLeaving, self.exitLeaving, [])], 'off', 'off') + self.fsm.enterInitialState() + return + + def disable(self): + self.ignore(self.uniqueName('enterFishingSpotSphere')) + self.setOccupied(0) + self.avId = 0 + if self.castTrack != None: + if self.castTrack.isPlaying(): + self.castTrack.finish() + self.castTrack = None + if self.guiTrack != None: + if self.guiTrack.isPlaying(): + self.guiTrack.finish() + self.guiTrack = None + self.__hideBob() + self.nodePath.detachNode() + self.__unmakeGui() + self.pond.stopCheckingTargets() + self.pond = None + DistributedObject.DistributedObject.disable(self) + return + + def delete(self): + del self.pond + del self.fsm + if self.nodePath: + self.nodePath.removeNode() + del self.nodePath + DistributedObject.DistributedObject.delete(self) + if self.ripples: + self.ripples.destroy() + + def generateInit(self): + DistributedObject.DistributedObject.generateInit(self) + self.nodePath = NodePath(self.uniqueName('FishingSpot')) + self.angleNP = self.nodePath.attachNewNode(self.uniqueName('FishingSpotAngleNP')) + self.collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + self.collSphere.setTangible(0) + self.collNode = CollisionNode(self.uniqueName('FishingSpotSphere')) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.collNode.addSolid(self.collSphere) + self.collNodePath = self.nodePath.attachNewNode(self.collNode) + self.bobStartPos = Point3(0.0, 3.0, 8.5) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.nodePath.reparentTo(self.getParentNodePath()) + self.accept(self.uniqueName('enterFishingSpotSphere'), self.__handleEnterSphere) + + def setPondDoId(self, pondDoId): + self.pond = base.cr.doId2do[pondDoId] + self.area = self.pond.getArea() + self.waterLevel = FishingTargetGlobals.getWaterLevel(self.area) + + def allowedToEnter(self): + if hasattr(base, 'ttAccess') and base.ttAccess and base.ttAccess.canAccess(): + return True + return False + + def handleOkTeaser(self): + self.dialog.destroy() + del self.dialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + + def __handleEnterSphere(self, collEntry): + if self.allowedToEnter(): + if base.localAvatar.doId == self.lastAvId and globalClock.getFrameCount() <= self.lastFrame + 1: + self.notify.debug('Ignoring duplicate entry for avatar.') + return + if base.localAvatar.hp > 0 and base.cr.playGame.getPlace().fsm.getCurrentState().getName() != 'fishing': + self.cr.playGame.getPlace().detectedFishingCollision() + self.d_requestEnter() + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='fishing', doneFunc=self.handleOkTeaser) + + def d_requestEnter(self): + self.sendUpdate('requestEnter', []) + + def rejectEnter(self): + self.cr.playGame.getPlace().setState('walk') + + def d_requestExit(self): + self.sendUpdate('requestExit', []) + + def d_doCast(self, power, heading): + self.sendUpdate('doCast', [power, heading]) + + def getSphereRadius(self): + return 1.5 + + def getParentNodePath(self): + return render + + def setPosHpr(self, x, y, z, h, p, r): + self.nodePath.setPosHpr(x, y, z, h, p, r) + self.angleNP.setH(render, self.nodePath.getH(render)) + + def setOccupied(self, avId): + if self.av != None: + if not self.av.isEmpty(): + self.__dropPole() + self.av.loop('neutral') + self.av.setParent(ToontownGlobals.SPRender) + self.av.startSmooth() + self.ignore(self.av.uniqueName('disable')) + self.__hideBob() + self.fsm.requestFinalState() + self.__removePole() + self.av = None + self.placedAvatar = 0 + self.angleNP.setH(render, self.nodePath.getH(render)) + self.__hideLine() + wasLocalToon = self.localToonFishing + self.lastAvId = self.avId + self.lastFrame = globalClock.getFrameCount() + self.avId = avId + self.localToonFishing = 0 + if self.avId == 0: + self.collSphere.setTangible(0) + else: + self.collSphere.setTangible(1) + if self.avId == base.localAvatar.doId: + base.setCellsAvailable(base.bottomCells, 0) + self.localToonFishing = 1 + if base.wantBingo: + self.pond.setLocalToonSpot(self) + av = self.cr.doId2do.get(self.avId) + if av: + self.av = av + self.__loadStuff() + self.placedAvatar = 0 + self.firstCast = 1 + self.acceptOnce(self.av.uniqueName('disable'), self.__avatarGone) + self.av.stopSmooth() + self.av.wrtReparentTo(self.angleNP) + self.av.setAnimState('neutral', 1.0) + self.createCastTrack() + else: + self.notify.warning('Unknown avatar %d in fishing spot %d' % (self.avId, self.doId)) + if wasLocalToon and not self.localToonFishing: + self.__hideCastGui() + if base.wantBingo: + self.pond.setLocalToonSpot() + base.setCellsAvailable([base.bottomCells[1], base.bottomCells[2]], 1) + base.setCellsAvailable(base.rightCells, 1) + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + return + + def __avatarGone(self): + self.setOccupied(0) + + def setMovie(self, mode, code, itemDesc1, itemDesc2, itemDesc3, power, h): + if self.av == None: + return + if mode == FishGlobals.NoMovie: + pass + elif mode == FishGlobals.EnterMovie: + self.fsm.request('waiting') + elif mode == FishGlobals.ExitMovie: + self.fsm.request('leaving') + elif mode == FishGlobals.CastMovie: + if not self.localToonFishing: + self.fsm.request('distCasting', [power, h]) + elif mode == FishGlobals.PullInMovie: + self.fsm.request('reward', [code, + itemDesc1, + itemDesc2, + itemDesc3]) + return + + def getStareAtNodeAndOffset(self): + return (self.nodePath, Point3()) + + def __loadStuff(self): + rodId = self.av.getFishingRod() + rodPath = FishGlobals.RodFileDict.get(rodId) + if not rodPath: + self.notify.warning('Rod id: %s model not found' % rodId) + rodPath = RodFileDict[0] + self.pole = Actor.Actor() + self.pole.loadModel(rodPath) + self.pole.loadAnims({'cast': 'phase_4/models/props/fishing-pole-chan'}) + self.pole.pose('cast', 0) + self.ptop = self.pole.find('**/joint_attachBill') + if self.line == None: + self.line = Rope.Rope(self.uniqueName('Line')) + self.line.setColor(1, 1, 1, 0.4) + self.line.setTransparency(1) + self.lineSphere = BoundingSphere(Point3(-0.6, -2, -5), 5.5) + if self.bob == None: + self.bob = loader.loadModel('phase_4/models/props/fishing_bob') + self.bob.setScale(1.5) + self.ripples = Ripples.Ripples(self.nodePath) + self.ripples.setScale(0.4) + self.ripples.hide() + if self.splashSounds == None: + self.splashSounds = (base.loadSfx('phase_4/audio/sfx/TT_splash1.mp3'), base.loadSfx('phase_4/audio/sfx/TT_splash2.mp3')) + return + + def __placeAvatar(self): + if not self.placedAvatar: + self.placedAvatar = 1 + self.__holdPole() + self.av.setPosHpr(0, 0, 0, 0, 0, 0) + + def __holdPole(self): + if self.poleNode != []: + self.__dropPole() + np = NodePath('pole-holder') + hands = self.av.getRightHands() + for h in hands: + self.poleNode.append(np.instanceTo(h)) + + self.pole.reparentTo(self.poleNode[0]) + + def __dropPole(self): + self.__hideBob() + self.__hideLine() + if self.pole != None: + self.pole.clearMat() + self.pole.detachNode() + for pn in self.poleNode: + pn.removeNode() + + self.poleNode = [] + return + + def __removePole(self): + self.pole.removeNode() + self.poleNode = [] + self.ptop.removeNode() + self.pole = None + self.ptop = None + return + + def __showLineWaiting(self): + self.line.setup(4, ((None, (0, 0, 0)), + (None, (0, -2, -4)), + (self.bob, (0, -1, 0)), + (self.bob, (0, 0, 0)))) + self.line.ropeNode.setBounds(self.lineSphere) + self.line.reparentTo(self.ptop) + return + + def __showLineCasting(self): + self.line.setup(2, ((None, (0, 0, 0)), (self.bob, (0, 0, 0)))) + self.line.ropeNode.setBounds(self.lineSphere) + self.line.reparentTo(self.ptop) + return + + def __showLineReeling(self): + self.line.setup(2, ((None, (0, 0, 0)), (self.bob, (0, 0, 0)))) + self.line.ropeNode.setBounds(self.lineSphere) + self.line.reparentTo(self.ptop) + return + + def __hideLine(self): + if self.line: + self.line.detachNode() + + def __showBobFloat(self): + self.__hideBob() + self.bob.reparentTo(self.angleNP) + self.ripples.reparentTo(self.angleNP) + self.ripples.setPos(self.bob.getPos()) + self.ripples.setZ(self.waterLevel + 0.025) + self.ripples.play() + splashSound = random.choice(self.splashSounds) + base.playSfx(splashSound, volume=0.8, node=self.bob) + self.bobBobTask = taskMgr.add(self.__doBobBob, self.taskName('bob')) + + def __hideBob(self): + if self.bob: + self.bob.detachNode() + if self.bobBobTask: + taskMgr.remove(self.bobBobTask) + self.bobBobTask = None + if self.ripples: + self.ripples.stop() + self.ripples.detachNode() + return + + def __doBobBob(self, task): + z = math.sin(task.time * 1.8) * 0.08 + self.bob.setZ(self.waterLevel + z) + return Task.cont + + def __userExit(self, event = None): + if self.localToonFishing: + self.fsm.request('leaving') + self.d_requestExit() + + def __sellFish(self, result = None): + if self.localToonFishing: + if result == DGG.DIALOG_OK: + self.sendUpdate('sellFish', []) + for button in self.sellFishDialog.buttonList: + button['state'] = DGG.DISABLED + + else: + self.fsm.request('leaving') + self.d_requestExit() + + def __sellFishConfirm(self, result = None): + if self.localToonFishing: + self.fsm.request('waiting', [False]) + + def __showCastGui(self): + self.__hideCastGui() + self.__makeGui() + self.castButton.show() + self.arrow.hide() + self.exitButton.show() + self.timer.show() + self.__updateFishTankGui() + self.castGui.reparentTo(aspect2d) + self.castButton['state'] = DGG.NORMAL + self.jar['text'] = str(self.av.getMoney()) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.accept(localAvatar.uniqueName('fishTankChange'), self.__updateFishTankGui) + target = base.cr.doFind('DistributedTarget') + if target: + target.hideGui() + if base.wantBingo: + self.__setBingoCastGui() + + def requestLocalAdjusting(mouseEvent): + if self.av.isFishTankFull() and self.__allowSellFish(): + self.fsm.request('sellFish') + else: + self.fsm.request('localAdjusting') + + def requestLocalCasting(mouseEvent): + if not (self.av.isFishTankFull() and self.__allowSellFish()): + self.fsm.request('localCasting') + + self.castButton.bind(DGG.B1PRESS, requestLocalAdjusting) + self.castButton.bind(DGG.B3PRESS, requestLocalAdjusting) + self.castButton.bind(DGG.B1RELEASE, requestLocalCasting) + self.castButton.bind(DGG.B3RELEASE, requestLocalCasting) + if self.firstCast and len(self.av.fishCollection) == 0 and len(self.av.fishTank) == 0: + self.__showHowTo(TTLocalizer.FishingHowToFirstTime) + elif base.wantBingo and self.pond.hasPondBingoManager() and not self.av.bFishBingoTutorialDone: + self.__showHowTo(TTLocalizer.FishBingoHelpMain) + self.av.b_setFishBingoTutorialDone(True) + + def __moneyChange(self, money): + self.jar['text'] = str(money) + + def __initCastGui(self): + self.timer.countdown(FishGlobals.CastTimeout) + + def __showQuestItem(self, itemId): + self.__makeGui() + itemName = Quests.getItemName(itemId) + self.itemLabel['text'] = itemName + self.itemGui.reparentTo(aspect2d) + self.itemPackage.show() + self.itemJellybean.hide() + self.itemBoot.hide() + + def __showBootItem(self): + self.__makeGui() + itemName = TTLocalizer.FishingBootItem + self.itemLabel['text'] = itemName + self.itemGui.reparentTo(aspect2d) + self.itemBoot.show() + self.itemJellybean.hide() + self.itemPackage.hide() + + def __setItemLabel(self): + if self.pond.hasPondBingoManager(): + self.itemLabel['text'] = str(itemName + '\n\n' + 'BINGO WILDCARD') + else: + self.itemLabel['text'] = itemName + + def __showJellybeanItem(self, amount): + self.__makeGui() + itemName = TTLocalizer.FishingJellybeanItem % amount + self.itemLabel['text'] = itemName + self.itemGui.reparentTo(aspect2d) + self.jar['text'] = str(self.av.getMoney()) + self.itemJellybean.show() + self.itemBoot.hide() + self.itemPackage.hide() + + def __showFishItem(self, code, fish): + self.fishPanel = FishPanel.FishPanel(fish) + self.__setFishItemPos() + self.fishPanel.setSwimBounds(-0.3, 0.3, -0.235, 0.25) + self.fishPanel.setSwimColor(1.0, 1.0, 0.74901, 1.0) + self.fishPanel.load() + self.fishPanel.show(code) + self.__updateFishTankGui() + + def __setFishItemPos(self): + if base.wantBingo: + if self.pond.hasPondBingoManager(): + self.fishPanel.setPos(0.65, 0, 0.4) + else: + self.fishPanel.setPos(0, 0, 0.5) + else: + self.fishPanel.setPos(0, 0, 0.5) + + def __updateFishTankGui(self): + fishTank = self.av.getFishTank() + lenFishTank = len(fishTank) + maxFishTank = self.av.getMaxFishTank() + self.bucket['text'] = '%s/%s' % (lenFishTank, maxFishTank) + + def __showFailureReason(self, code): + self.__makeGui() + reason = '' + if code == FishGlobals.OverTankLimit: + reason = TTLocalizer.FishingOverTankLimit + self.failureDialog.setMessage(reason) + self.failureDialog.show() + + def __showSellFishDialog(self): + self.__makeGui() + self.sellFishDialog.show() + + def __hideSellFishDialog(self): + self.__makeGui() + self.sellFishDialog.hide() + + def __showSellFishConfirmDialog(self, numFishCaught): + self.__makeGui() + msg = TTLocalizer.STOREOWNER_TROPHY % (numFishCaught, FishGlobals.getTotalNumFish()) + self.sellFishConfirmDialog.setMessage(msg) + self.sellFishConfirmDialog.show() + + def __hideSellFishConfirmDialog(self): + self.__makeGui() + self.sellFishConfirmDialog.hide() + + def __showBroke(self): + self.__makeGui() + self.brokeDialog.show() + self.castButton['state'] = DGG.DISABLED + + def __showHowTo(self, message): + self.__makeGui() + self.howToDialog.setMessage(message) + self.howToDialog.show() + + def __hideHowTo(self, event = None): + self.__makeGui() + self.howToDialog.hide() + + def __showFishTankFull(self): + self.__makeGui() + self.__showFailureReason(FishGlobals.OverTankLimit) + self.castButton['state'] = DGG.DISABLED + + def __hideCastGui(self): + target = base.cr.doFind('DistributedTarget') + if target: + target.showGui() + if self.madeGui: + self.timer.hide() + self.castGui.detachNode() + self.itemGui.detachNode() + self.failureDialog.hide() + self.sellFishDialog.hide() + self.sellFishConfirmDialog.hide() + self.brokeDialog.hide() + self.howToDialog.hide() + self.castButton.unbind(DGG.B1PRESS) + self.castButton.unbind(DGG.B3PRESS) + self.castButton.unbind(DGG.B1RELEASE) + self.castButton.unbind(DGG.B3RELEASE) + self.ignore(localAvatar.uniqueName('moneyChange')) + self.ignore(localAvatar.uniqueName('fishTankChange')) + + def __itemGuiClose(self): + self.itemGui.detachNode() + + def __makeGui(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: FISHING: ZoneId: %s' % self.pond.getArea()) + if self.madeGui: + return + self.timer = ToontownTimer.ToontownTimer() + self.timer.posInTopRightCorner() + self.timer.hide() + self.castGui = loader.loadModel('phase_4/models/gui/fishingGui') + self.castGui.setScale(0.67) + self.castGui.setPos(0, 1, 0) + for nodeName in ('bucket', 'jar', 'display_bucket', 'display_jar'): + self.castGui.find('**/' + nodeName).reparentTo(self.castGui) + + self.exitButton = DirectButton(parent=self.castGui, relief=None, text=('', TTLocalizer.FishingExit, TTLocalizer.FishingExit), text_align=TextNode.ACenter, text_scale=0.1, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0.0, -0.12), pos=(1.75, 0, -1.33), textMayChange=0, image=(self.castGui.find('**/exit_buttonUp'), self.castGui.find('**/exit_buttonDown'), self.castGui.find('**/exit_buttonRollover')), command=self.__userExit) + self.castGui.find('**/exitButton').removeNode() + self.castButton = DirectButton(parent=self.castGui, relief=None, text=TTLocalizer.FishingCast, text_align=TextNode.ACenter, text_scale=(3, 3 * 0.75, 3 * 0.75), text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -4), image=self.castGui.find('**/castButton'), image0_color=(1, 0, 0, 1), image1_color=(0, 1, 0, 1), image2_color=(1, 1, 0, 1), image3_color=(0.8, 0.5, 0.5, 1), pos=(0, -0.05, -0.666), scale=(0.036, 1, 0.048)) + self.castGui.find('**/castButton').removeNode() + self.arrow = self.castGui.find('**/arrow') + self.arrowTip = self.arrow.find('**/arrowTip') + self.arrowTail = self.arrow.find('**/arrowTail') + self.arrow.reparentTo(self.castGui) + self.arrow.setColorScale(0.9, 0.9, 0.1, 0.7) + self.arrow.hide() + self.jar = DirectLabel(parent=self.castGui, relief=None, text=str(self.av.getMoney()), text_scale=0.16, text_fg=(0.95, 0.95, 0, 1), text_font=ToontownGlobals.getSignFont(), pos=(-1.12, 0, -1.3)) + self.bucket = DirectLabel(parent=self.castGui, relief=None, text='', text_scale=0.09, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), pos=(1.14, 0, -1.33)) + self.__updateFishTankGui() + self.itemGui = NodePath('itemGui') + self.itemFrame = DirectFrame(parent=self.itemGui, relief=None, geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor, geom_scale=(1, 1, 0.6), text=TTLocalizer.FishingItemFound, text_pos=(0, 0.2), text_scale=0.08, pos=(0, 0, 0.587)) + self.itemLabel = DirectLabel(parent=self.itemFrame, text='', text_scale=0.06, pos=(0, 0, -0.25)) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.itemGuiCloseButton = DirectButton(parent=self.itemFrame, pos=(0.44, 0, -0.24), relief=None, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=(0.7, 1, 0.7), command=self.__itemGuiClose) + buttons.removeNode() + jarGui = loader.loadModel('phase_3.5/models/gui/jar_gui') + bootGui = loader.loadModel('phase_4/models/gui/fishing_boot') + packageGui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui').find('**/package') + self.itemJellybean = DirectFrame(parent=self.itemFrame, relief=None, image=jarGui, scale=0.5) + self.itemBoot = DirectFrame(parent=self.itemFrame, relief=None, image=bootGui, scale=0.2) + self.itemPackage = DirectFrame(parent=self.itemFrame, relief=None, image=packageGui, scale=0.25) + self.itemJellybean.hide() + self.itemBoot.hide() + self.itemPackage.hide() + self.failureDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('failureDialog'), doneEvent=self.uniqueName('failureDialog'), command=self.__userExit, message=TTLocalizer.FishingFailure, style=TTDialog.CancelOnly, cancelButtonText=TTLocalizer.FishingExit) + self.failureDialog.hide() + self.sellFishDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('sellFishDialog'), doneEvent=self.uniqueName('sellFishDialog'), command=self.__sellFish, message=TTLocalizer.FishBingoOfferToSellFish, style=TTDialog.YesNo) + self.sellFishDialog.hide() + self.sellFishConfirmDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('sellFishConfirmDialog'), doneEvent=self.uniqueName('sellFishConfirmDialog'), command=self.__sellFishConfirm, message=TTLocalizer.STOREOWNER_TROPHY, style=TTDialog.Acknowledge) + self.sellFishConfirmDialog.hide() + self.brokeDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('brokeDialog'), doneEvent=self.uniqueName('brokeDialog'), command=self.__userExit, message=TTLocalizer.FishingBroke, style=TTDialog.CancelOnly, cancelButtonText=TTLocalizer.FishingExit) + self.brokeDialog.hide() + self.howToDialog = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('howToDialog'), doneEvent=self.uniqueName('howToDialog'), fadeScreen=0, message=TTLocalizer.FishingHowToFailed, style=TTDialog.Acknowledge) + self.howToDialog['command'] = self.__hideHowTo + self.howToDialog.setPos(-0.3, 0, 0.5) + self.howToDialog.hide() + self.madeGui = 1 + return + + def __setBingoCastGui(self): + if self.pond.hasPondBingoManager(): + self.notify.debug('__setBingoCastGui: Has PondBing Manager %s' % self.pond.getPondBingoManager().getDoId()) + bucket = self.castGui.find('**/bucket') + self.castGui.find('**/display_bucket').reparentTo(bucket) + self.bucket.reparentTo(bucket) + jar = self.castGui.find('**/jar') + self.castGui.find('**/display_jar').reparentTo(jar) + self.jar.reparentTo(jar) + base.setCellsAvailable(base.rightCells, 0) + bucket.setScale(0.9) + bucket.setX(-1.9) + bucket.setZ(-.11) + jar.setScale(0.9) + jar.setX(-.375) + jar.setZ(-.135) + else: + self.notify.debug('__setItemFramePos: Has No Pond Bingo Manager') + bucket = self.castGui.find('**/bucket') + bucket.setScale(1) + bucket.setPos(0, 0, 0) + jar = self.castGui.find('**/jar') + jar.setScale(1) + jar.setPos(0, 0, 0) + + def resetCastGui(self): + self.notify.debug('resetCastGui: Bingo Night Ends - resetting Gui') + bucket = self.castGui.find('**/bucket') + jar = self.castGui.find('**/jar') + bucketPosInt = bucket.posInterval(5.0, Point3(0, 0, 0), startPos=bucket.getPos(), blendType='easeInOut') + bucketScaleInt = bucket.scaleInterval(5.0, VBase3(1.0, 1.0, 1.0), startScale=bucket.getScale(), blendType='easeInOut') + bucketTrack = Parallel(bucketPosInt, bucketScaleInt) + jarPosInt = jar.posInterval(5.0, Point3(0, 0, 0), startPos=jar.getPos(), blendType='easeInOut') + jarScaleInt = jar.scaleInterval(5.0, VBase3(1.0, 1.0, 1.0), startScale=jar.getScale(), blendType='easeInOut') + jarTrack = Parallel(jarPosInt, jarScaleInt) + self.guiTrack = Parallel(bucketTrack, jarTrack) + self.guiTrack.start() + + def setCastGui(self): + self.notify.debug('setCastGui: Bingo Night Starts - setting Gui') + bucket = self.castGui.find('**/bucket') + self.castGui.find('**/display_bucket').reparentTo(bucket) + self.bucket.reparentTo(bucket) + jar = self.castGui.find('**/jar') + self.castGui.find('**/display_jar').reparentTo(jar) + self.jar.reparentTo(jar) + bucketPosInt = bucket.posInterval(3.0, Point3(-1.9, 0, -.11), startPos=bucket.getPos(), blendType='easeInOut') + bucketScaleInt = bucket.scaleInterval(3.0, VBase3(0.9, 0.9, 0.9), startScale=bucket.getScale(), blendType='easeInOut') + bucketTrack = Parallel(bucketPosInt, bucketScaleInt) + jarPosInt = jar.posInterval(3.0, Point3(-.375, 0, -.135), startPos=jar.getPos(), blendType='easeInOut') + jarScaleInt = jar.scaleInterval(3.0, VBase3(0.9, 0.9, 0.9), startScale=jar.getScale(), blendType='easeInOut') + jarTrack = Parallel(jarPosInt, jarScaleInt) + self.guiTrack = Parallel(bucketTrack, jarTrack) + self.guiTrack.start() + + def setJarAmount(self, amount): + if self.madeGui: + money = int(self.jar['text']) + amount + pocketMoney = min(money, self.av.getMaxMoney()) + self.jar.setProp('text', str(pocketMoney)) + + def __unmakeGui(self): + if not self.madeGui: + return + self.timer.destroy() + del self.timer + self.exitButton.destroy() + self.castButton.destroy() + self.jar.destroy() + self.bucket.destroy() + self.itemFrame.destroy() + self.itemGui.removeNode() + self.failureDialog.cleanup() + self.sellFishDialog.cleanup() + self.sellFishConfirmDialog.cleanup() + self.brokeDialog.cleanup() + self.howToDialog.cleanup() + self.castGui.removeNode() + self.madeGui = 0 + + def localAdjustingCastTask(self, state): + self.getMouse() + deltaX = self.mouseX - self.initMouseX + deltaY = self.mouseY - self.initMouseY + if deltaY >= 0: + if self.power == 0: + self.arrowTail.setScale(0.075, 0.075, 0) + self.arrow.setR(0) + self.castTrack.pause() + return Task.cont + dist = math.sqrt(deltaX * deltaX + deltaY * deltaY) + delta = dist / 0.5 + self.power = max(min(abs(delta), 1.0), 0.0) + self.castTrack.setT(0.2 + self.power * 0.7) + angle = rad2Deg(math.atan(deltaX / deltaY)) + if self.power < 0.25: + angle = angle * math.pow(self.power * 4, 3) + if delta < 0: + angle += 180 + minAngle = -FishGlobals.FishingAngleMax + maxAngle = FishGlobals.FishingAngleMax + if angle < minAngle: + self.arrow.setColorScale(1, 0, 0, 1) + angle = minAngle + elif angle > maxAngle: + self.arrow.setColorScale(1, 0, 0, 1) + angle = maxAngle + else: + self.arrow.setColorScale(1, 1 - math.pow(self.power, 3), 0.1, 0.7) + self.arrowTail.setScale(0.075, 0.075, self.power * 0.2) + self.arrow.setR(angle) + self.angleNP.setH(-angle) + return Task.cont + + def localAdjustingCastTaskIndAxes(self, state): + self.getMouse() + deltaX = self.mouseX - self.initMouseX + deltaY = self.mouseY - self.initMouseY + self.power = max(min(abs(deltaY) * 1.5, 1.0), 0.0) + self.castTrack.setT(0.4 + self.power * 0.5) + angle = deltaX * -180.0 + self.angleNP.setH(self.startAngleNP - angle) + return Task.cont + + def getMouse(self): + if base.mouseWatcherNode.hasMouse(): + self.mouseX = base.mouseWatcherNode.getMouseX() + self.mouseY = base.mouseWatcherNode.getMouseY() + else: + self.mouseX = 0 + self.mouseY = 0 + + def createCastTrack(self): + self.castTrack = Sequence(ActorInterval(self.av, 'castlong', playRate=4), ActorInterval(self.av, 'cast', startFrame=20), Func(self.av.loop, 'fish-neutral')) + + def startMoveBobTask(self): + self.__showBob() + taskMgr.add(self.moveBobTask, self.taskName('moveBobTask')) + + def moveBobTask(self, task): + g = 32.2 + t = task.time + vZero = self.power * self.vZeroMax + angle = deg2Rad(self.power * self.angleMax) + deltaY = vZero * math.cos(angle) * t + deltaZ = vZero * math.sin(angle) * t - g * t * t / 2.0 + deltaPos = Point3(0, deltaY, deltaZ) + self.bobStartPos = Point3(0.0, 3.0, 8.5) + pos = self.bobStartPos + deltaPos + self.bob.setPos(pos) + if pos[2] < self.waterLevel: + self.fsm.request('fishing') + return Task.done + else: + return Task.cont + + def __showBob(self): + self.__hideBob() + self.bob.reparentTo(self.angleNP) + self.bob.setPos(self.ptop, 0, 0, 0) + self.av.update(0) + + def hitTarget(self): + self.fsm.request('waitForAI') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWaiting(self, doAnimation = True): + self.av.stopLookAround() + self.__hideLine() + self.track = Parallel() + if doAnimation: + toonTrack = Sequence(Func(self.av.setPlayRate, 1.0, 'run'), Func(self.av.loop, 'run'), LerpPosHprInterval(self.av, 1.0, Point3(0, 0, 0), Point3(0, 0, 0)), Func(self.__placeAvatar), Parallel(ActorInterval(self.av, 'pole'), Func(self.pole.pose, 'cast', 0), LerpScaleInterval(self.pole, duration=0.5, scale=1.0, startScale=0.01)), Func(self.av.loop, 'pole-neutral')) + if self.localToonFishing: + camera.wrtReparentTo(render) + self.track.append(LerpPosHprInterval(nodePath=camera, other=self.av, duration=1.5, pos=Point3(0, -12, 15), hpr=VBase3(0, -38, 0), blendType='easeInOut')) + toonTrack.append(Func(self.__showCastGui)) + toonTrack.append(Func(self.__initCastGui)) + if base.wantBingo: + self.__appendBingoMethod(toonTrack, self.pond.showBingoGui) + self.track.append(toonTrack) + else: + self.__showCastGui() + self.track.start() + + def __appendBingoMethod(self, interval, callback): + interval.append(Func(callback)) + + def exitWaiting(self): + self.track.finish() + self.track = None + return + + def enterLocalAdjusting(self, guiEvent = None): + if self.track: + self.track.pause() + if self.castTrack: + self.castTrack.pause() + self.power = 0.0 + self.firstCast = 0 + self.castButton['image0_color'] = Vec4(0, 1, 0, 1) + self.castButton['text'] = '' + self.av.stopLookAround() + self.__hideLine() + self.__hideBob() + self.howToDialog.hide() + castCost = FishGlobals.getCastCost(self.av.getFishingRod()) + if self.av.getMoney() < castCost: + self.__hideCastGui() + self.__showBroke() + self.av.loop('pole-neutral') + return + if self.av.isFishTankFull(): + self.__hideCastGui() + self.__showFishTankFull() + self.av.loop('pole-neutral') + return + self.arrow.show() + self.arrow.setColorScale(1, 1, 0, 0.7) + self.startAngleNP = self.angleNP.getH() + self.getMouse() + self.initMouseX = self.mouseX + self.initMouseY = self.mouseY + self.__hideBob() + if config.GetBool('fishing-independent-axes', 0): + taskMgr.add(self.localAdjustingCastTaskIndAxes, self.taskName('adjustCastTask')) + else: + taskMgr.add(self.localAdjustingCastTask, self.taskName('adjustCastTask')) + if base.wantBingo: + bingoMgr = self.pond.getPondBingoManager() + if bingoMgr: + bingoMgr.castingStarted() + + def exitLocalAdjusting(self): + taskMgr.remove(self.taskName('adjustCastTask')) + self.castButton['image0_color'] = Vec4(1, 0, 0, 1) + self.castButton['text'] = TTLocalizer.FishingCast + self.arrow.hide() + + def enterLocalCasting(self): + if self.power == 0.0 and len(self.av.fishCollection) == 0: + self.__showHowTo(TTLocalizer.FishingHowToFailed) + if self.castTrack: + self.castTrack.pause() + self.av.loop('pole-neutral') + self.track = None + return + castCost = FishGlobals.getCastCost(self.av.getFishingRod()) + self.jar['text'] = str(max(self.av.getMoney() - castCost, 0)) + if not self.castTrack: + self.createCastTrack() + self.castTrack.pause() + startT = 0.7 + (1 - self.power) * 0.3 + self.castTrack.start(startT) + self.track = Sequence(Wait(1.2 - startT), Func(self.startMoveBobTask), Func(self.__showLineCasting)) + self.track.start() + heading = self.angleNP.getH() + self.d_doCast(self.power, heading) + self.timer.countdown(FishGlobals.CastTimeout) + return + + def exitLocalCasting(self): + taskMgr.remove(self.taskName('moveBobTask')) + if self.track: + self.track.pause() + self.track = None + if self.castTrack: + self.castTrack.pause() + self.__hideLine() + self.__hideBob() + return + + def enterDistCasting(self, power, h): + self.av.stopLookAround() + self.__placeAvatar() + self.__hideLine() + self.__hideBob() + self.angleNP.setH(h) + self.power = power + self.track = Parallel(Sequence(ActorInterval(self.av, 'cast'), Func(self.pole.pose, 'cast', 0), Func(self.av.loop, 'fish-neutral')), Sequence(Wait(1.0), Func(self.startMoveBobTask), Func(self.__showLineCasting))) + self.track.start() + + def exitDistCasting(self): + self.track.finish() + self.track = None + taskMgr.remove(self.taskName('moveBobTask')) + self.__hideLine() + self.__hideBob() + return + + def enterFishing(self): + if self.localToonFishing: + self.track = Sequence(ActorInterval(self.av, 'cast'), Func(self.pole.pose, 'cast', 0), Func(self.av.loop, 'fish-neutral')) + self.track.start(self.castTrack.getT()) + else: + self.track = None + self.av.loop('fish-neutral') + self.__showBobFloat() + self.__showLineWaiting() + if self.localToonFishing: + self.pond.startCheckingTargets(self, self.bob.getPos(render)) + return + + def exitFishing(self): + if self.localToonFishing: + self.pond.stopCheckingTargets() + if self.track: + self.track.finish() + self.track = None + return + + def enterWaitForAI(self): + self.castButton['state'] = DGG.DISABLED + + def exitWaitForAI(self): + self.castButton['state'] = DGG.NORMAL + + def enterReward(self, code, itemDesc1, itemDesc2, itemDesc3): + self.__placeAvatar() + self.bob.reparentTo(self.angleNP) + self.bob.setZ(self.waterLevel) + self.__showLineReeling() + self.castTrack.pause() + if self.localToonFishing: + self.__showCastGui() + if code == FishGlobals.QuestItem: + self.__showQuestItem(itemDesc1) + elif code in (FishGlobals.FishItem, FishGlobals.FishItemNewEntry, FishGlobals.FishItemNewRecord): + genus, species, weight = itemDesc1, itemDesc2, itemDesc3 + fish = FishBase.FishBase(genus, species, weight) + self.__showFishItem(code, fish) + if base.wantBingo: + self.pond.handleBingoCatch((genus, species)) + elif code == FishGlobals.BootItem: + self.__showBootItem() + if base.wantBingo: + self.pond.handleBingoCatch(FishGlobals.BingoBoot) + elif code == FishGlobals.JellybeanItem: + amount = itemDesc1 + self.__showJellybeanItem(amount) + elif code == FishGlobals.OverTankLimit: + self.__hideCastGui() + else: + self.__showFailureReason(code) + self.track = Sequence(Parallel(ActorInterval(self.av, 'reel'), ActorInterval(self.pole, 'cast', startFrame=63, endFrame=127)), ActorInterval(self.av, 'reel-neutral'), Func(self.__hideLine), Func(self.__hideBob), ActorInterval(self.av, 'fish-again'), Func(self.av.loop, 'pole-neutral')) + self.track.start() + + def cleanupFishPanel(self): + if self.fishPanel: + self.fishPanel.hide() + self.fishPanel.destroy() + self.fishPanel = None + return + + def hideBootPanel(self): + if self.madeGui and self.itemBoot: + self.__itemGuiClose() + + def exitReward(self): + if self.localToonFishing: + self.itemGui.detachNode() + self.cleanupFishPanel() + self.track.finish() + self.track = None + return + + def enterLeaving(self): + if self.localToonFishing: + self.__hideCastGui() + if base.wantBingo: + self.pond.cleanupBingoMgr() + self.av.stopLookAround() + self.av.startLookAround() + self.__placeAvatar() + self.__hideLine() + self.__hideBob() + self.track = Sequence(Parallel(ActorInterval(self.av, 'fish-end'), Func(self.pole.pose, 'cast', 0), LerpScaleInterval(self.pole, duration=0.5, scale=0.01, startScale=1.0)), Func(self.__dropPole), Func(self.av.loop, 'neutral')) + if self.localToonFishing: + self.track.append(Func(self.fsm.requestFinalState)) + self.track.start() + + def exitLeaving(self): + self.track.pause() + self.track = None + return + + def enterSellFish(self): + self.castButton['state'] = DGG.DISABLED + self.__showSellFishDialog() + self.__hideHowTo() + + def exitSellFish(self): + self.castButton['state'] = DGG.NORMAL + self.__hideSellFishDialog() + self.__hideSellFishConfirmDialog() + + def sellFishComplete(self, trophyResult, numFishCaught): + for button in self.sellFishDialog.buttonList: + button['state'] = DGG.NORMAL + + if self.localToonFishing: + if trophyResult: + self.__hideSellFishDialog() + self.__showSellFishConfirmDialog(numFishCaught) + else: + self.fsm.request('waiting', [False]) + + def __allowSellFish(self): + if base.wantBingo: + if self.pond.hasPondBingoManager(): + hoodId = base.cr.playGame.getPlaceId() + if hoodId == ToontownGlobals.MyEstate: + return True + return False diff --git a/toontown/safezone/DistributedGolfKart.py b/toontown/safezone/DistributedGolfKart.py new file mode 100644 index 0000000..9c09d51 --- /dev/null +++ b/toontown/safezone/DistributedGolfKart.py @@ -0,0 +1,500 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from toontown.golf import GolfGlobals +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.distributed import DelayDelete +from direct.task.Task import Task +from direct.showbase import PythonUtil +from toontown.toontowngui import TeaserPanel +from toontown.toon import ToonDNA +from toontown.hood import ZoneUtil + +class DistributedGolfKart(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfKart') + SeatOffsets = ((0.5, -0.5, 0), + (-0.5, -0.5, 0), + (0.5, 0.5, 0), + (-0.5, 0.5, 0)) + JumpOutOffsets = ((3, 5, 0), + (1.5, 4, 0), + (-1.5, 4, 0), + (-3, 4, 0)) + KART_ENTER_TIME = 400 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.localToonOnBoard = 0 + self.trolleyCountdownTime = base.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [State.State('off', self.enterOff, self.exitOff, ['entering', + 'waitEmpty', + 'waitCountdown', + 'leaving']), + State.State('entering', self.enterEntering, self.exitEntering, ['waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'leaving']), + State.State('leaving', self.enterLeaving, self.exitLeaving, ['entering'])], 'off', 'off') + self.fsm.enterInitialState() + self.trolleyAwaySfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_away.mp3') + self.trolleyBellSfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_bell.mp3') + self.__toonTracks = {} + self.avIds = [0, + 0, + 0, + 0] + self.kartModelPath = 'phase_6/models/golf/golf_cart3.bam' + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.loader = self.cr.playGame.hood.loader + if self.loader: + self.notify.debug('Loader has been loaded') + self.notify.debug(str(self.loader)) + else: + self.notify.debug('Loader has not been loaded') + 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.golfKart.reparentTo(self.loader.geom) + self.wheels = self.kart.findAllMatches('**/wheelNode*') + self.numWheels = self.wheels.getNumPaths() + trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, node=self.golfKart) + trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, node=self.golfKart) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.golfKartSphereNode = self.golfKart.attachNewNode(CollisionNode('golfkart_sphere_%d' % self.getDoId())) + self.golfKartSphereNode.node().addSolid(CollisionSphere(0, 0, 0, 2)) + 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 + 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='KartAnimate') + trolleyExitTrack1 = Parallel(LerpPosInterval(self.golfKart, 5.0, self.endPos), self.kartEnterAnimateInterval, name='KartExitTrack') + self.trolleyExitTrack = Sequence(trolleyExitTrack1, Func(self.hideSittingToons)) + self.trolleyEnterTrack = Sequence(LerpPosInterval(self.golfKart, 5.0, self.startingPos, startPos=self.enteringPos)) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.fsm.request('off') + self.clearToonTracks() + del self.wheels + del self.numWheels + del self.golfKartSphereNode + self.notify.debug('Deleted self loader ' + str(self.getDoId())) + del self.loader + self.golfKart.removeNode() + self.kart.removeNode() + del self.kart + del self.golfKart + self.trolleyEnterTrack.pause() + self.trolleyEnterTrack = None + del self.kartEnterAnimateInterval + del self.trolleyEnterTrack + self.trolleyExitTrack.pause() + self.trolleyExitTrack = None + del self.trolleyExitTrack + return + + def delete(self): + self.notify.debug('Golf kart getting deleted: %s' % self.getDoId()) + del self.trolleyAwaySfx + del self.trolleyBellSfx + DistributedObject.DistributedObject.delete(self) + del self.fsm + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def handleEnterTrolleySphere(self, collEntry): + self.notify.debug('Entering Trolley Sphere....') + self.loader.place.detectedTrolleyCollision() + + def allowedToEnter(self): + if hasattr(base, 'ttAccess') and base.ttAccess and base.ttAccess.canAccess(): + return True + return False + + def handleEnterGolfKartSphere(self, collEntry): + self.notify.debug('Entering Golf Kart Sphere.... %s' % self.getDoId()) + if self.allowedToEnter(): + self.loader.place.detectedGolfKartCollision(self) + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='golf', doneFunc=self.handleOkTeaser) + + def handleOkTeaser(self): + self.dialog.destroy() + del self.dialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + + def handleEnterTrolley(self): + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + def handleEnterGolfKart(self): + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + 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 fillSlot(self, index, avId): + self.avIds[index] = avId + if avId == 0: + pass + else: + if avId == base.localAvatar.getDoId(): + self.loader.place.trolley.fsm.request('boarding', [self.golfKart]) + self.localToonOnBoard = 1 + if avId == base.localAvatar.getDoId(): + self.loader.place.trolley.fsm.request('boarded') + if self.cr.doId2do.has_key(avId): + 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('fillTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'GolfKart.fillSlot') + self.storeToonTrack(avId, track) + track.start() + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot board the trolley!') + + def emptySlot0(self, avId, timestamp): + self.emptySlot(0, avId, timestamp) + + def emptySlot1(self, avId, timestamp): + self.emptySlot(1, avId, timestamp) + + def emptySlot2(self, avId, timestamp): + self.emptySlot(2, avId, timestamp) + + def emptySlot3(self, avId, timestamp): + self.emptySlot(3, avId, timestamp) + + def notifyToonOffTrolley(self, toon): + toon.setAnimState('neutral', 1.0) + if toon == base.localAvatar: + self.loader.place.trolley.handleOffTrolley() + self.localToonOnBoard = 0 + else: + toon.startSmooth() + + def emptySlot(self, index, avId, timestamp): + if avId == 0: + pass + else: + self.avIds[index] = 0 + if self.cr.doId2do.has_key(avId): + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + track = Sequence(jumpOutTrack, Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'GolfKart.emptySlot') + self.storeToonTrack(avId, track) + track.start() + if avId == base.localAvatar.getDoId(): + self.loader.place.trolley.fsm.request('exiting') + else: + self.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the trolley!') + + def rejectBoard(self, avId): + self.loader.place.trolley.handleRejectBoard() + + def setMinigameZone(self, zoneId, minigameId): + self.localToonOnBoard = 0 + messenger.send('playMinigame', [zoneId, minigameId]) + + def setGolfZone(self, zoneId, courseId): + self.localToonOnBoard = 0 + messenger.send('playGolf', [zoneId, courseId]) + + def __enableCollisions(self): + self.accept('entertrolley_sphere', self.handleEnterTrolleySphere) + self.accept('enterTrolleyOK', self.handleEnterTrolley) + self.accept('entergolfkart_sphere_%d' % self.getDoId(), self.handleEnterGolfKartSphere) + self.accept('enterGolfKartOK_%d' % self.getDoId(), self.handleEnterGolfKart) + self.golfKartSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + self.ignore('entertrolley_sphere') + self.ignore('enterTrolleyOK') + self.ignore('entergolfkart_sphere_%d' % self.getDoId()) + self.ignore('enterTrolleyOK_%d' % self.getDoId()) + self.ignore('enterGolfKartOK_%d' % self.getDoId()) + self.golfKartSphereNode.setCollideMask(BitMask32(0)) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterEntering(self, ts): + self.trolleyEnterTrack.start(ts) + + def exitEntering(self): + self.trolleyEnterTrack.finish() + + def enterWaitEmpty(self, ts): + self.__enableCollisions() + + def exitWaitEmpty(self): + self.__disableCollisions() + + def enterWaitCountdown(self, ts): + self.__enableCollisions() + self.accept('trolleyExitButton', self.handleExitButton) + self.clockNode = TextNode('trolleyClock') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + self.clockNode.setTextColor(0.9, 0.1, 0.1, 1) + self.clockNode.setText('10') + self.clock = self.golfKart.attachNewNode(self.clockNode) + self.clock.setBillboardAxis() + self.clock.setPosHprScale(0, -1, 7.0, -0.0, 0.0, 0.0, 2.0, 2.0, 2.0) + if ts < self.trolleyCountdownTime: + self.countdown(self.trolleyCountdownTime - ts) + + 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('golfKartTimerTask')) + return taskMgr.add(countdownTask, self.uniqueName('golfKartTimerTask')) + + def handleExitButton(self): + self.sendUpdate('requestExit') + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + taskMgr.remove(self.uniqueName('golfKartTimerTask')) + self.clock.removeNode() + del self.clock + del self.clockNode + + def enterLeaving(self, ts): + self.trolleyExitTrack.start(ts) + if self.localToonOnBoard: + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('trolleyLeaving') + + def exitLeaving(self): + self.trolleyExitTrack.finish() + + def getStareAtNodeAndOffset(self): + return (self.golfKart, Point3(0, 0, 4)) + + 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 setGolfCourse(self, golfCourse): + self.golfCourse = golfCourse + + 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 setColor(self, r, g, b): + kartBody = self.kart.find('**/main_body') + kartBody.setColor(r / 255.0, g / 255.0, b / 255.0, 1) + cartBase = self.kart.find('**/cart_base*') + red = r / 255.0 + green = g / 255.0 + blue = b / 255.0 + if red >= green and red > blue: + s = (red - blue) / float(red) + v = red + if green > blue: + h = (green - blue) / (red - blue) + else: + h = (green - blue) / (red - green) + elif green >= blue: + s = (green - blue) / float(green) + v = green + if red > blue: + h = 2 + (blue - red) / (green - blue) + else: + h = 2 + (blue - red) / (green - red) + else: + if red > green: + s = (blue - green) / blue + h = 4 + (red - green) / (blue - green) + else: + s = (blue - red) / blue + h = 4 + (red - green) / (blue - red) + v = blue + if h < 0: + h *= 60 + h += 360 + h /= 60 + s /= 3 + if s == 0: + red = green = blue = v + else: + i = int(h) + f = h - i + p = v * (1 - s) + q = v * (1 - s * f) + t = v * (1 - s * (1 - f)) + if i == 0: + red = v + green = t + blue = p + elif i == 1: + red = q + green = v + blue = p + elif i == 2: + red = p + green = v + blue = t + elif i == 3: + red = p + green = q + blue = v + elif i == 4: + red = t + green = p + blue = v + elif i == 5: + red = v + green = p + blue = q + cartBase.setColorScale(red, green, blue, 1) + + 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 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 hideSittingToons(self): + for avId in self.avIds: + if avId: + av = base.cr.doId2do.get(avId) + if av: + av.hide() diff --git a/toontown/safezone/DistributedGolfKartAI.py b/toontown/safezone/DistributedGolfKartAI.py new file mode 100644 index 0000000..47f0498 --- /dev/null +++ b/toontown/safezone/DistributedGolfKartAI.py @@ -0,0 +1,330 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from TrolleyConstants 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.minigame import MinigameCreatorAI +from toontown.quest import Quests +from toontown.minigame import TrolleyHolidayMgrAI +from toontown.golf import GolfManagerAI +from toontown.golf import GolfGlobals +import random + +class DistributedGolfKartAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGolfKartAI') + + def __init__(self, air, golfCourse, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.seats = [ + None, None, None, None] + self.golfCourse = golfCourse + self.posHpr = (x, y, z, h, p, r) + self.color = ( + random.randint(GolfGlobals.KartColors[self.golfCourse][0][0], GolfGlobals.KartColors[self.golfCourse][0][1]), + random.randint(GolfGlobals.KartColors[self.golfCourse][1][0], GolfGlobals.KartColors[self.golfCourse][1][1]), + random.randint(GolfGlobals.KartColors[self.golfCourse][2][0], GolfGlobals.KartColors[self.golfCourse][2][1])) + if self.golfCourse == 1: + if self.color[0] + self.color[1] <= 255: + self.color = ( + self.color[0], self.color[0] + self.color[1], self.color[2]) + else: + self.color = ( + self.color[0], 255, self.color[2]) + self.accepting = 0 + self.trolleyCountdownTime = simbase.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM('DistributedGolfKartAI', [ + State.State('off', self.enterOff, self.exitOff, [ + 'entering']), + State.State('entering', self.enterEntering, self.exitEntering, [ + 'waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, [ + 'waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, [ + 'waitEmpty', 'allAboard']), + State.State('allAboard', self.enterAllAboard, self.exitAllAboard, [ + 'leaving', 'waitEmpty']), + State.State('leaving', self.enterLeaving, self.exitLeaving, [ + 'entering'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + 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 rejectingBoardersHandler(self, avId): + self.rejectBoarder(avId) + + def rejectBoarder(self, avId): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId]) + + def acceptingBoardersHandler(self, avId): + self.notify.debug('acceptingBoardersHandler') + seatIndex = self.findAvailableSeat() + if seatIndex == None: + self.rejectBoarder(avId) + 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.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.timeOfBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [ + avId]) + self.waitCountdown() + return + + 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.waitEmpty() + return + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + else: + self.clearFullNow(seatIndex) + self.sendUpdate('emptySlot' + str(seatIndex), [ + avId, globalClockDelta.getRealNetworkTime()]) + if self.countFullSeats() == 0: + self.waitEmpty() + taskMgr.doMethodLater(TOON_EXIT_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,)) + return + + def clearEmptyNow(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [ + 0, globalClockDelta.getRealNetworkTime()]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == 0: + 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.fsm.getCurrentState().getName() + + 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 + if av.hp > 0 and self.accepting: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board a trolley' % 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 a trolley' % avId) + + def start(self): + self.enter() + + def enterOff(self): + self.accepting = 0 + if hasattr(self, 'doId'): + for seatIndex in range(4): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + def exitOff(self): + self.accepting = 0 + + def enter(self): + self.fsm.request('entering') + + def enterEntering(self): + self.d_setState('entering') + self.accepting = 0 + self.seats = [None, None, None, None] + taskMgr.doMethodLater(TROLLEY_ENTER_TIME, self.waitEmptyTask, self.uniqueName('entering-timer')) + return + + def exitEntering(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('entering-timer')) + + def waitEmptyTask(self, task): + self.waitEmpty() + return Task.done + + def waitEmpty(self): + self.fsm.request('waitEmpty') + + def enterWaitEmpty(self): + self.d_setState('waitEmpty') + self.accepting = 1 + + def exitWaitEmpty(self): + self.accepting = 0 + + def waitCountdown(self): + self.fsm.request('waitCountdown') + + def enterWaitCountdown(self): + self.d_setState('waitCountdown') + self.accepting = 1 + taskMgr.doMethodLater(self.trolleyCountdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + if self.countFullSeats() > 0: + self.allAboard() + else: + self.waitEmpty() + return Task.done + + def exitWaitCountdown(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def allAboard(self): + self.fsm.request('allAboard') + + def enterAllAboard(self): + self.accepting = 0 + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(TOON_BOARD_TIME - elapsedTime, 0) + taskMgr.doMethodLater(waitTime, self.leaveTask, self.uniqueName('waitForAllAboard')) + + def exitAllAboard(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('waitForAllAboard')) + + def leaveTask(self, task): + if self.countFullSeats() > 0: + self.leave() + else: + self.waitEmpty() + return Task.done + + def leave(self): + self.fsm.request('leaving') + + def enterLeaving(self): + self.d_setState('leaving') + self.accepting = 0 + taskMgr.doMethodLater(TROLLEY_EXIT_TIME, self.trolleyLeftTask, self.uniqueName('leaving-timer')) + + def trolleyLeftTask(self, task): + self.trolleyLeft() + return Task.done + + def trolleyLeft(self): + numPlayers = self.countFullSeats() + avIdList = [] + if numPlayers > 0: + for seatIndex in range(len(self.seats)): + avId = self.seats[seatIndex] + avIdList.append(avId) + self.clearFullNow(seatIndex) + + golfZone = GolfManagerAI.GolfManagerAI().readyGolfCourse(avIdList, self.golfCourse) + for avId in avIdList: + if avId: + self.sendUpdateToAvatarId(avId, 'setGolfZone', [golfZone, 0]) + + else: + self.notify.warning('The trolley left, but was empty.') + self.enter() + + def exitLeaving(self): + self.accepting = 0 + self.color = ( + random.randint(GolfGlobals.KartColors[self.golfCourse][0][0], GolfGlobals.KartColors[self.golfCourse][0][1]), + random.randint(GolfGlobals.KartColors[self.golfCourse][1][0], GolfGlobals.KartColors[self.golfCourse][1][1]), + random.randint(GolfGlobals.KartColors[self.golfCourse][2][0], GolfGlobals.KartColors[self.golfCourse][2][1])) + if self.golfCourse == 1: + if self.color[0] + self.color[1] <= 255: + self.color = ( + self.color[0], self.color[0] + self.color[1], self.color[2]) + else: + self.color = ( + self.color[0], 255, self.color[2]) + self.sendUpdate('setColor', [ + self.color[0], self.color[1], self.color[2]]) + taskMgr.remove(self.uniqueName('leaving-timer')) + + def getGolfCourse(self): + return self.golfCourse + + def getPosHpr(self): + return self.posHpr + + def getColor(self): + return self.color diff --git a/toontown/safezone/DistributedMMPiano.py b/toontown/safezone/DistributedMMPiano.py new file mode 100644 index 0000000..37d864b --- /dev/null +++ b/toontown/safezone/DistributedMMPiano.py @@ -0,0 +1,117 @@ +from pandac.PandaModules import * +from direct.task.Task import Task +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from direct.distributed import DistributedObject +from pandac.PandaModules import NodePath +from toontown.toonbase import ToontownGlobals +ChangeDirectionDebounce = 1.0 +ChangeDirectionTime = 1.0 + +class DistributedMMPiano(DistributedObject.DistributedObject): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.spinStartTime = 0.0 + self.rpm = 0.0 + self.degreesPerSecond = self.rpm / 60.0 * 360.0 + self.offset = 0.0 + self.oldOffset = 0.0 + self.lerpStart = 0.0 + self.lerpFinish = 1.0 + self.speedUpSound = None + self.changeDirectionSound = None + self.lastChangeDirection = 0.0 + return + + def generate(self): + self.piano = base.cr.playGame.hood.loader.piano + base.cr.parentMgr.registerParent(ToontownGlobals.SPMinniesPiano, self.piano) + self.accept('enterlarge_round_keyboard_collisions', self.__handleOnFloor) + self.accept('exitlarge_round_keyboard_collisions', self.__handleOffFloor) + self.accept('entero7', self.__handleChangeDirectionButton) + self.speedUpSound = base.loadSfx('phase_6/audio/sfx/SZ_MM_gliss.mp3') + self.changeDirectionSound = base.loadSfx('phase_6/audio/sfx/SZ_MM_cymbal.mp3') + self.__setupSpin() + DistributedObject.DistributedObject.generate(self) + + def __setupSpin(self): + taskMgr.add(self.__updateSpin, self.taskName('pianoSpinTask')) + + def __stopSpin(self): + taskMgr.remove(self.taskName('pianoSpinTask')) + + def __updateSpin(self, task): + now = globalClock.getFrameTime() + if now > self.lerpFinish: + offset = self.offset + elif now > self.lerpStart: + t = (now - self.lerpStart) / (self.lerpFinish - self.lerpStart) + offset = self.oldOffset + t * (self.offset - self.oldOffset) + else: + offset = self.oldOffset + heading = self.degreesPerSecond * (now - self.spinStartTime) + offset + self.piano.setHprScale(heading % 360.0, 0.0, 0.0, 1.0, 1.0, 1.0) + return Task.cont + + def disable(self): + del self.piano + base.cr.parentMgr.unregisterParent(ToontownGlobals.SPMinniesPiano) + self.ignore('enterlarge_round_keyboard_collisions') + self.ignore('exitlarge_round_keyboard_collisions') + self.ignore('entero7') + self.ignore('entericon_center_collisions') + self.speedUpSound = None + self.changeDirectionSound = None + self.__stopSpin() + DistributedObject.DistributedObject.disable(self) + return + + def setSpeed(self, rpm, offset, timestamp): + timestamp = globalClockDelta.networkToLocalTime(timestamp) + degreesPerSecond = rpm / 60.0 * 360.0 + now = globalClock.getFrameTime() + oldHeading = self.degreesPerSecond * (now - self.spinStartTime) + self.offset + oldHeading = oldHeading % 360.0 + oldOffset = oldHeading - degreesPerSecond * (now - timestamp) + self.rpm = rpm + self.degreesPerSecond = degreesPerSecond + self.offset = offset + self.spinStartTime = timestamp + while oldOffset - offset < -180.0: + oldOffset += 360.0 + + while oldOffset - offset > 180.0: + oldOffset -= 360.0 + + self.oldOffset = oldOffset + self.lerpStart = now + self.lerpFinish = timestamp + ChangeDirectionTime + + def playSpeedUp(self, avId): + if avId != base.localAvatar.doId: + base.playSfx(self.speedUpSound) + + def playChangeDirection(self, avId): + if avId != base.localAvatar.doId: + base.playSfx(self.changeDirectionSound) + + def __handleOnFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('OnPiano') + self.sendUpdate('requestSpeedUp', []) + base.playSfx(self.speedUpSound) + + def __handleOffFloor(self, collEntry): + self.cr.playGame.getPlace().activityFsm.request('off') + + def __handleSpeedUpButton(self, collEntry): + self.sendUpdate('requestSpeedUp', []) + base.playSfx(self.speedUpSound) + + def __handleChangeDirectionButton(self, collEntry): + now = globalClock.getFrameTime() + if now - self.lastChangeDirection < ChangeDirectionDebounce: + return + self.lastChangeDirection = now + self.sendUpdate('requestChangeDirection', []) + base.playSfx(self.changeDirectionSound) diff --git a/toontown/safezone/DistributedMMPianoAI.py b/toontown/safezone/DistributedMMPianoAI.py new file mode 100644 index 0000000..bea79e7 --- /dev/null +++ b/toontown/safezone/DistributedMMPianoAI.py @@ -0,0 +1,79 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI +from direct.task import Task +PianoSpeeds = [ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0] +PianoMaxSpeed = PianoSpeeds[len(PianoSpeeds) - 1] +PianoSlowDownFactor = 0.7 +PianoSlowDownInterval = 10.0 +PianoSlowDownMinimum = 0.1 + +class DistributedMMPianoAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.spinStartTime = 0.0 + self.rpm = 0.0 + self.degreesPerSecond = self.rpm / 60.0 * 360.0 + self.offset = 0.0 + self.direction = 1 + return None + + def delete(self): + DistributedObjectAI.DistributedObjectAI.delete(self) + + def requestSpeedUp(self): + if self.rpm < PianoMaxSpeed: + for speed in PianoSpeeds: + if speed > self.rpm: + break + + self.updateSpeed(speed, self.direction) + self.d_playSpeedUp(self.air.getAvatarIdFromSender()) + self.__slowDownLater() + + def requestChangeDirection(self): + rpm = self.rpm + if rpm == 0.0: + rpm = PianoSpeeds[0] + self.updateSpeed(rpm, -self.direction) + self.__slowDownLater() + self.d_playChangeDirection(self.air.getAvatarIdFromSender()) + + def d_setSpeed(self, rpm, offset, startTime): + self.sendUpdate('setSpeed', [rpm, offset, globalClockDelta.localToNetworkTime(startTime)]) + + def d_playSpeedUp(self, avId): + self.sendUpdate('playSpeedUp', [avId]) + + def d_playChangeDirection(self, avId): + self.sendUpdate('playChangeDirection', [avId]) + + def updateSpeed(self, rpm, direction): + now = globalClock.getRealTime() + heading = self.degreesPerSecond * (now - self.spinStartTime) + self.offset + self.rpm = rpm + self.direction = direction + self.degreesPerSecond = rpm / 60.0 * 360.0 * direction + self.offset = heading % 360.0 + self.spinStartTime = now + self.d_setSpeed(self.rpm * self.direction, self.offset, self.spinStartTime) + + def start(self): + return None + + def __slowDownLater(self): + taskName = self.uniqueName('slowDown') + taskMgr.remove(taskName) + taskMgr.doMethodLater(PianoSlowDownInterval, self.__slowDown, taskName) + + def __slowDown(self, task): + rpm = self.rpm * PianoSlowDownFactor + if rpm < PianoSlowDownMinimum: + self.updateSpeed(0.0, self.direction) + else: + self.updateSpeed(rpm, self.direction) + self.__slowDownLater() + return Task.done diff --git a/toontown/safezone/DistributedMMTreasure.py b/toontown/safezone/DistributedMMTreasure.py new file mode 100644 index 0000000..25f1058 --- /dev/null +++ b/toontown/safezone/DistributedMMTreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedMMTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_6/models/props/music_treasure' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedMMTreasureAI.py b/toontown/safezone/DistributedMMTreasureAI.py new file mode 100644 index 0000000..1ed2fea --- /dev/null +++ b/toontown/safezone/DistributedMMTreasureAI.py @@ -0,0 +1,6 @@ +import DistributedSZTreasureAI + +class DistributedMMTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/safezone/DistributedOZTreasure.py b/toontown/safezone/DistributedOZTreasure.py new file mode 100644 index 0000000..5c14fcc --- /dev/null +++ b/toontown/safezone/DistributedOZTreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedOZTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_6/models/props/acorn_treasure' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedOZTreasureAI.py b/toontown/safezone/DistributedOZTreasureAI.py new file mode 100644 index 0000000..726b390 --- /dev/null +++ b/toontown/safezone/DistributedOZTreasureAI.py @@ -0,0 +1,6 @@ +import DistributedSZTreasureAI + +class DistributedOZTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/safezone/DistributedPartyGate.py b/toontown/safezone/DistributedPartyGate.py new file mode 100644 index 0000000..6ed2de8 --- /dev/null +++ b/toontown/safezone/DistributedPartyGate.py @@ -0,0 +1,205 @@ +from pandac.PandaModules import Point3, CollisionSphere, CollisionNode, BitMask32, Vec3, NodePath, TextNode, Vec4 +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPLocalizer +from direct.interval.IntervalGlobal import Sequence, Parallel, SoundInterval +from direct.interval.FunctionInterval import Wait +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.gui import DirectLabel +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.parties.ServerTimeGui import ServerTimeGui +from toontown.parties.PublicPartyGui import PublicPartyGui +from toontown.parties import PartyGlobals + +class DistributedPartyGate(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyGate') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.publicPartyChooseGuiDoneEvent = 'doneChoosingPublicParty' + self.publicPartyGui = PublicPartyGui(self.publicPartyChooseGuiDoneEvent) + self.publicPartyGui.stash() + self.loadClockSounds() + self.hourSoundInterval = Sequence() + self.accept('stoppedAsleep', self.handleSleep) + + def loadClockSounds(self): + self.clockSounds = [] + for i in range(1, 13): + if i < 10: + si = '0%d' % i + else: + si = '%d' % i + self.clockSounds.append(base.loadSfx('phase_4/audio/sfx/clock%s.mp3' % si)) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + loader = self.cr.playGame.hood.loader + partyGate = loader.geom.find('**/partyGate_grp') + if partyGate.isEmpty(): + self.notify.warning('Could not find partyGate_grp in loader.geom') + return + self.clockFlat = partyGate.find('**/clock_flat') + collSphere = CollisionSphere(0, 0, 0, 6.9) + collSphere.setTangible(1) + self.partyGateSphere = CollisionNode('PartyGateSphere') + self.partyGateSphere.addSolid(collSphere) + self.partyGateCollNodePath = partyGate.find('**/partyGate_stepsLocator').attachNewNode(self.partyGateSphere) + self.__enableCollisions() + self.toontownTimeGui = ServerTimeGui(partyGate, hourCallback=self.hourChange) + self.toontownTimeGui.setPos(partyGate.find('**/clockText_locator').getPos() + Point3(0.0, 0.0, -0.2)) + self.toontownTimeGui.setHpr(partyGate.find('**/clockText_locator').getHpr()) + self.toontownTimeGui.setScale(12.0, 1.0, 26.0) + self.toontownTimeGui.amLabel.setPos(-0.035, 0, -0.032) + self.toontownTimeGui.amLabel.setScale(0.5) + self.toontownTimeGui.updateTime() + self.setupSignText() + + def setupSignText(self): + loader = self.cr.playGame.hood.loader + partyGate = loader.geom.find('**/partyGateSignGroup') + if partyGate.isEmpty(): + self.notify.warning('Could not find partyGate_grp in loader.geom') + return + gateFont = ToontownGlobals.getMinnieFont() + leftSign = partyGate.find('**/signTextL_locatorBack') + signScale = 0.35 + wordWrap = 8 + leftText = DirectLabel.DirectLabel(parent=leftSign, pos=(0, 0.0, 0.0), relief=None, text=TTLocalizer.PartyGateLeftSign, text_align=TextNode.ACenter, text_font=gateFont, text_wordwrap=wordWrap, text_fg=Vec4(0.7, 0.3, 0.3, 1.0), scale=signScale) + rightSign = partyGate.find('**/signTextR_locatorFront') + rightText = DirectLabel.DirectLabel(parent=rightSign, pos=(0, 0.0, 0.0), relief=None, text=TTLocalizer.PartyGateRightSign, text_align=TextNode.ACenter, text_font=gateFont, text_wordwrap=wordWrap, text_fg=Vec4(0.7, 0.3, 0.3, 1.0), scale=signScale) + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + if ToontownGlobals.dnaMap.has_key(self.zoneId): + playground = ToontownGlobals.dnaMap[self.zoneId] + else: + playground = ToontownGlobals.dnaMap[2000] + self.toontownTimeGui.hourLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + self.toontownTimeGui.colonLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + self.toontownTimeGui.minutesLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + self.toontownTimeGui.amLabel['text_fg'] = PartyGlobals.PlayGroundToPartyClockColors[playground] + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.__disableCollisions() + self.toontownTimeGui.ival.finish() + self.hourSoundInterval.finish() + if self.publicPartyGui: + self.publicPartyGui.stash() + self.publicPartyGui.destroy() + self.publicPartyGui = None + return + + def delete(self): + DistributedObject.DistributedObject.delete(self) + self.toontownTimeGui.destroy() + del self.toontownTimeGui + self.hourSoundInterval.finish() + del self.hourSoundInterval + del self.clockFlat + if self.publicPartyGui: + self.publicPartyGui.destroy() + del self.publicPartyGui + self.partyGateCollNodePath.removeNode() + del self.partyGateCollNodePath + self.ignoreAll() + + def showMessage(self, message): + self.messageDoneEvent = self.uniqueName('messageDoneEvent') + self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) + self.messageGui = TTDialog.TTGlobalDialog(doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge) + + def __handleMessageDone(self): + self.ignore(self.messageDoneEvent) + self.freeAvatar() + self.messageGui.cleanup() + self.messageGui = None + return + + def __handleAskDone(self): + DistributedPartyGate.notify.debug('__handleAskDone') + self.ignore(self.publicPartyChooseGuiDoneEvent) + doneStatus = self.publicPartyGui.doneStatus + self.publicPartyGui.stash() + if doneStatus is None: + self.freeAvatar() + return + self.sendUpdate('partyChoiceRequest', [base.localAvatar.doId, doneStatus[0], doneStatus[1]]) + return + + def partyRequestDenied(self, reason): + DistributedPartyGate.notify.debug('partyRequestDenied( reason=%s )' % PartyGlobals.PartyGateDenialReasons.getString(reason)) + if reason == PartyGlobals.PartyGateDenialReasons.Unavailable: + self.showMessage(TTLocalizer.PartyGatePartyUnavailable) + elif reason == PartyGlobals.PartyGateDenialReasons.Full: + self.showMessage(TTLocalizer.PartyGatePartyFull) + + def setParty(self, partyInfoTuple): + DistributedPartyGate.notify.debug('setParty') + self.freeAvatar() + if partyInfoTuple[0] == 0: + DistributedPartyGate.notify.debug('Public Party closed before toon could get to it.') + return + shardId, zoneId, numberOfGuests, hostName, activityIds, lane = partyInfoTuple + if base.localAvatar.defaultShard == shardId: + shardId = None + base.cr.playGame.getPlace().requestLeave({'loader': 'safeZoneLoader', + 'where': 'party', + 'how': 'teleportIn', + 'hoodId': ToontownGlobals.PartyHood, + 'zoneId': zoneId, + 'shardId': shardId, + 'avId': -1}) + return + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + + def hourChange(self, currentHour): + currentHour = currentHour % 12 + if currentHour == 0: + currentHour = 12 + self.hourSoundInterval = Parallel() + seq1 = Sequence() + for i in range(currentHour): + seq1.append(SoundInterval(self.clockSounds[i])) + seq1.append(Wait(0.2)) + + timeForEachDeformation = seq1.getDuration() / currentHour + seq2 = Sequence() + for i in range(currentHour): + seq2.append(self.clockFlat.scaleInterval(timeForEachDeformation / 2.0, Vec3(0.9, 1.0, 1.2), blendType='easeInOut')) + seq2.append(self.clockFlat.scaleInterval(timeForEachDeformation / 2.0, Vec3(1.2, 1.0, 0.9), blendType='easeInOut')) + + seq2.append(self.clockFlat.scaleInterval(timeForEachDeformation / 2.0, Vec3(1.0, 1.0, 1.0), blendType='easeInOut')) + self.hourSoundInterval.append(seq1) + self.hourSoundInterval.append(seq2) + self.hourSoundInterval.start() + + def handleEnterGateSphere(self, collEntry): + self.notify.debug('Entering steps Sphere....') + base.cr.playGame.getPlace().fsm.request('stopped') + self.sendUpdate('getPartyList', [base.localAvatar.doId]) + + def listAllPublicParties(self, publicPartyInfo): + self.notify.debug('listAllPublicParties : publicPartyInfo = %s' % publicPartyInfo) + self.acceptOnce(self.publicPartyChooseGuiDoneEvent, self.__handleAskDone) + self.publicPartyGui.refresh(publicPartyInfo) + self.publicPartyGui.unstash() + + def __enableCollisions(self): + self.accept('enterPartyGateSphere', self.handleEnterGateSphere) + self.partyGateSphere.setCollideMask(OTPGlobals.WallBitmask) + + def __disableCollisions(self): + self.ignore('enterPartyGateSphere') + self.partyGateSphere.setCollideMask(BitMask32(0)) + + def handleSleep(self): + if hasattr(self, 'messageGui') and self.messageGui: + self.__handleMessageDone() diff --git a/toontown/safezone/DistributedPicnicBasket.py b/toontown/safezone/DistributedPicnicBasket.py new file mode 100644 index 0000000..54d7cb2 --- /dev/null +++ b/toontown/safezone/DistributedPicnicBasket.py @@ -0,0 +1,572 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from toontown.golf import GolfGlobals +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.distributed import DelayDelete +from toontown.toonbase.ToontownTimer import ToontownTimer +from direct.task.Task import Task +from direct.showbase import PythonUtil +from toontown.toon import ToonDNA +from direct.showbase import RandomNumGen +from toontown.battle.BattleSounds import * + +class DistributedPicnicBasket(DistributedObject.DistributedObject): + seatState = Enum('Empty, Full, Eating') + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPicnicBasket') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.localToonOnBoard = 0 + self.seed = 0 + self.random = None + self.picnicCountdownTime = base.config.GetFloat('picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME) + self.picnicBasketTrack = None + self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [State.State('off', self.enterOff, self.exitOff, ['waitEmpty', 'waitCountdown']), State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty'])], 'off', 'off') + self.fsm.enterInitialState() + self.__toonTracks = {} + return + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.loader = self.cr.playGame.hood.loader + self.foodLoader = ['phase_6/models/golf/picnic_sandwich.bam', + 'phase_6/models/golf/picnic_apple.bam', + 'phase_6/models/golf/picnic_cupcake.bam', + 'phase_6/models/golf/picnic_chocolate_cake.bam'] + self.fullSeat = [] + self.food = [] + for i in range(4): + self.food.append(None) + self.fullSeat.append(self.seatState.Empty) + + self.picnicItem = 0 + return + + def announceGenerate(self): + self.picnicTable = self.loader.geom.find('**/*picnic_table_' + str(self.tableNumber)) + self.picnicTableSphereNodes = [] + self.numSeats = 4 + self.seats = [] + self.jumpOffsets = [] + self.basket = None + for i in range(self.numSeats): + self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1))) + self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1))) + + self.tablecloth = self.picnicTable.find('**/basket_locator') + DistributedObject.DistributedObject.announceGenerate(self) + for i in range(self.numSeats): + self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i)))) + self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2)) + + self.tableclothSphereNode = self.tablecloth.attachNewNode(CollisionNode('tablecloth_sphere')) + self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -1, 4)) + angle = self.startingHpr[0] + angle -= 90 + radAngle = deg2Rad(angle) + unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + unitVec *= 30.0 + self.endPos = self.startingPos + unitVec + dist = Vec3(self.endPos - self.enteringPos).length() + wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360 + self.seatNumber = 0 + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + return + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.fsm.request('off') + self.clearToonTracks() + for i in range(self.numSeats): + del self.picnicTableSphereNodes[0] + + del self.picnicTableSphereNodes + self.notify.debug('Deleted self loader ' + str(self.getDoId())) + self.picnicTable.removeNode() + self.picnicBasketTrack = None + return + + def delete(self): + self.notify.debug('Golf kart getting deleted: %s' % self.getDoId()) + DistributedObject.DistributedObject.delete(self) + del self.fsm + + def setState(self, state, seed, timestamp): + self.seed = seed + if not self.random: + self.random = RandomNumGen.RandomNumGen(seed) + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def handleEnterPicnicTableSphere(self, i, collEntry): + self.seatNumber = i + self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId()) + self.loader.place.detectedPicnicTableSphereCollision(self) + + def handleEnterPicnicTable(self, i): + toon = base.localAvatar + self.sendUpdate('requestBoard', [i]) + + 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 fillSlot(self, index, avId): + self.notify.debug('fill Slot: %d for %d' % (index, avId)) + if avId == 0: + pass + else: + self.fullSeat[index] = self.seatState.Full + if avId == base.localAvatar.getDoId(): + self.clockNode.show() + if index == 0 or index == 3: + side = -1 + else: + side = 1 + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.fsm.request('boarding', [self.tablecloth, side]) + else: + self.notify.warning('fillSlot no trolley in place') + self.localToonOnBoard = 1 + if avId == base.localAvatar.getDoId(): + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.fsm.request('boarded') + self.loader.place.trolley.exitButton.hide() + if self.cr.doId2do.has_key(avId): + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.tablecloth) + sitStartDuration = toon.getDuration('sit-start') + jumpTrack = self.generateToonJumpTrack(toon, index) + track = Sequence(jumpTrack, Func(toon.setAnimState, 'Sit', 1.0)) + self.notify.debug('### fillSlot: fullSeat = %s' % self.fullSeat) + if self.fullSeat.count(0) == 3: + self.notify.debug('### fillSlot: adding basketAppear') + if self.picnicBasketTrack: + self.picnicBasketTrack.finish() + waitDuration = track.getDuration() + self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketAppearTrack()) + self.picnicBasketTrack.start() + track.append(self.generateFoodAppearTrack(index)) + track.append(Sequence(Func(self.clearToonTrack, avId), name=toon.uniqueName('fillTrolley'), autoPause=1)) + if avId == base.localAvatar.getDoId(): + if hasattr(self.loader.place, 'trolley'): + track.append(Func(self.loader.place.trolley.exitButton.show)) + track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.fillSlot') + self.storeToonTrack(avId, track) + track.start() + + def emptySlot0(self, avId, timestamp): + self.emptySlot(0, avId, timestamp) + + def emptySlot1(self, avId, timestamp): + self.emptySlot(1, avId, timestamp) + + def emptySlot2(self, avId, timestamp): + self.emptySlot(2, avId, timestamp) + + def emptySlot3(self, avId, timestamp): + self.emptySlot(3, avId, timestamp) + + def notifyToonOffTrolley(self, toon): + toon.setAnimState('neutral', 1.0) + if hasattr(base, 'localAvatar') and toon == base.localAvatar: + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.handleOffTrolley() + self.localToonOnBoard = 0 + else: + toon.startSmooth() + + def emptySlot(self, index, avId, timestamp): + + def emptySeat(index): + self.notify.debug('### seat %s now empty' % index) + self.fullSeat[index] = self.seatState.Empty + + if avId == 0: + pass + elif avId == 1: + self.fullSeat[index] = self.seatState.Empty + track = Sequence(self.generateFoodDisappearTrack(index)) + self.notify.debug('### empty slot - unexpetected: fullSeat = %s' % self.fullSeat) + if self.fullSeat.count(0) == 4: + self.notify.debug('### empty slot - unexpected: losing basket') + if self.picnicBasketTrack: + self.picnicBasketTrack.finish() + waitDuration = track.getDuration() + self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack()) + self.picnicBasketTrack.start() + track.start() + else: + self.fullSeat[index] = self.seatState.Empty + if self.cr.doId2do.has_key(avId): + if avId == base.localAvatar.getDoId(): + if self.clockNode: + self.clockNode.hide() + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + track = Sequence(jumpOutTrack) + track.append(self.generateFoodDisappearTrack(index)) + self.notify.debug('### empty slot: fullSeat = %s' % self.fullSeat) + if self.fullSeat.count(0) == 4: + self.notify.debug('### empty slot: losing basket') + if self.picnicBasketTrack: + self.picnicBasketTrack.finish() + waitDuration = track.getDuration() + self.picnicBasketTrack = Sequence(Wait(waitDuration), self.generateBasketDisappearTrack()) + self.picnicBasketTrack.start() + track.append(Sequence(Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), Func(self.doneExit, avId), Func(emptySeat, index), name=toon.uniqueName('emptyTrolley'), autoPause=1)) + track.delayDelete = DelayDelete.DelayDelete(toon, 'PicnicBasket.emptySlot') + self.storeToonTrack(avId, track) + track.start() + + def rejectBoard(self, avId): + self.loader.place.trolley.handleRejectBoard() + + def __enableCollisions(self): + for i in range(self.numSeats): + self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i]) + self.accept('enterPicnicTableOK_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTable, [i]) + self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + for i in range(self.numSeats): + self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i)) + self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i)) + + for i in range(self.numSeats): + self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0)) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterWaitEmpty(self, ts): + self.__enableCollisions() + + def exitWaitEmpty(self): + self.__disableCollisions() + + def enterWaitCountdown(self, ts): + self.__enableCollisions() + self.accept('trolleyExitButton', self.handleExitButton) + self.clockNode.countdown(self.picnicCountdownTime, self.handleExitButton) + + def handleExitButton(self): + self.sendUpdate('requestExit') + self.clockNode.hide() + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + self.clockNode.reset() + + def getStareAtNodeAndOffset(self): + return (self.tablecloth, Point3(0, 0, 4)) + + 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() + DelayDelete.cleanupDelayDeletes(oldTrack) + 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 doneExit(self, avId): + if avId == base.localAvatar.getDoId(): + self.sendUpdate('doneExit') + + 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) + + def setTableNumber(self, tn): + self.tableNumber = tn + + 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.tablecloth): + dest = Vec3(self.tablecloth.getPos(av.getParent())) + seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1)) + dest += seatNode.getPos(self.tablecloth) + dna = av.getStyle() + dest -= hipOffset + if seatIndex == 2 or seatIndex == 3: + dest.setY(dest.getY() + 2 * hipOffset.getY()) + dest.setZ(dest.getZ() + 0.2) + return dest + + def getJumpHpr(av = av, node = self.tablecloth): + hpr = self.seats[seatIndex].getHpr(av.getParent()) + angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) + hpr.setX(angle) + 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.tablecloth)) + return jumpTrack + + 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(self.tablecloth) + dest += self.jumpOffsets[seatIndex].getPos(self.tablecloth) + 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.tablecloth) + jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) + return jumpTrack + + def generateBasketAppearTrack(self): + if self.basket == None: + self.basket = loader.loadModel('phase_6/models/golf/picnic_basket.bam') + self.basket.setScale(0.1) + basketTrack = Sequence( + Func(self.basket.show), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), + node=self.basket), + Func(self.basket.reparentTo, self.tablecloth), + Func(self.basket.setPos, 0, 0, 0.2), + Func(self.basket.setHpr, 45, 0, 0), + Func(self.basket.wrtReparentTo, render), + Func(self.basket.setShear, 0, 0, 0), + Sequence( + LerpScaleInterval( + self.basket, + scale=Point3(1.1, 1.1, 0.1), + duration=0.2), + LerpScaleInterval( + self.basket, + scale=Point3(1.6, 1.6, 0.2), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.0, 1.0, 0.4), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.5, 1.5, 2.5), + duration=0.2), + LerpScaleInterval( + self.basket, + scale=Point3(2.5, 2.5, 1.5), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(2.0, 2.0, 2.0), + duration=0.1), + Func(self.basket.wrtReparentTo, self.tablecloth), + Func(self.basket.setPos, 0, 0, 0))) + return basketTrack + + def generateBasketDisappearTrack(self): + if not self.basket: + return Sequence() + pos = self.basket.getPos() + pos.addZ(-1) + basketTrack = Sequence( + LerpScaleInterval( + self.basket, + scale=Point3(2.0, 2.0, 1.8), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.0, 1.0, 2.5), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(2.0, 2.0, 0.5), + duration=0.2), + LerpScaleInterval( + self.basket, + scale=Point3(0.5, 0.5, 1.0), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(1.1, 1.1, 0.1), + duration=0.1), + LerpScaleInterval( + self.basket, + scale=Point3(0.1, 0.1, 0.1), + duration=0.2), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), + node=self.basket), + Wait(0.2), + LerpPosInterval( + self.basket, + pos=pos, + duration=0.2), + Func(self.basket.hide)) + return basketTrack + + def generateFoodAppearTrack(self, seat): + if self.fullSeat[seat] == self.seatState.Full: + self.notify.debug('### food appear: self.fullSeat = %s' % self.fullSeat) + if not self.food[seat]: + self.food[seat] = loader.loadModel(self.random.choice(self.foodLoader)) + self.notify.debug('### food appear: self.food = %s' % self.food) + self.food[seat].setScale(0.1) + self.food[seat].reparentTo(self.tablecloth) + self.food[seat].setPos(self.seats[seat].getPos(self.tablecloth)[0] / 2, self.seats[seat].getPos(self.tablecloth)[1] / 2, 0) + foodTrack = Sequence( + Func(self.food[seat].show), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), + node=self.food[seat]), + Func(self.food[seat].reparentTo, self.tablecloth), + Func(self.food[seat].setHpr, 45, 0, 0), + Func(self.food[seat].wrtReparentTo, render), + Func(self.food[seat].setShear, 0, 0, 0), + Sequence( + LerpScaleInterval( + self.food[seat], + scale=Point3(1.1, 1.1, 0.1), + duration=0.2), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.6, 1.6, 0.2), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.0, 1.0, 0.4), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.5, 1.5, 2.5), + duration=0.2), + LerpScaleInterval( + self.food[seat], + scale=Point3(2.5, 2.5, 1.5), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(2.0, 2.0, 2.0), + duration=0.1), + Func(self.food[seat].wrtReparentTo, self.tablecloth))) + return foodTrack + else: + return Sequence() + + def generateFoodDisappearTrack(self, seat): + if not self.food[seat]: + return Sequence() + pos = self.food[seat].getPos() + pos.addZ(-1.0) + foodTrack = Sequence( + LerpScaleInterval( + self.food[seat], + scale=Point3(2.0, 2.0, 1.8), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.0, 1.0, 2.5), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(2.0, 2.0, 0.5), + duration=0.2), + LerpScaleInterval( + self.food[seat], + scale=Point3(0.5, 0.5, 1.0), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(1.1, 1.1, 0.1), + duration=0.1), + LerpScaleInterval( + self.food[seat], + scale=Point3(0.1, 0.1, 0.1), + duration=0.2), + SoundInterval( + globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), + node=self.food[seat]), + Wait(0.2), + LerpPosInterval( + self.food[seat], + pos=pos, + duration=0.2), + Func(self.food[seat].hide)) + return foodTrack + + def destroy(self, node): + node.removeNode() + node = None + self.basket.removeNode() + self.basket = None + for food in self.food: + food.removeNode() + + self.food = None + self.clockNode.removeNode() + del self.clockNode + self.clockNode = None + return + + def setPicnicDone(self): + if self.localToonOnBoard: + if hasattr(self.loader.place, 'trolley'): + self.loader.place.trolley.fsm.request('final') + self.loader.place.trolley.fsm.request('start') + self.localToonOnBoard = 0 + messenger.send('picnicDone') diff --git a/toontown/safezone/DistributedPicnicBasketAI.py b/toontown/safezone/DistributedPicnicBasketAI.py new file mode 100644 index 0000000..782923f --- /dev/null +++ b/toontown/safezone/DistributedPicnicBasketAI.py @@ -0,0 +1,254 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from TrolleyConstants import * +from toontown.toonbase import ToontownGlobals +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 direct.showbase import RandomNumGen +from toontown.minigame import MinigameCreatorAI +from toontown.quest import Quests +from toontown.minigame import TrolleyHolidayMgrAI +from toontown.golf import GolfManagerAI +from toontown.golf import GolfGlobals + +class DistributedPicnicBasketAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPicnicBasketAI') + + def __init__(self, air, tableNumber, x, y, z, h, p, r): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.seats = [ + None, None, None, None] + self.posHpr = (x, y, z, h, p, r) + self.tableNumber = int(tableNumber) + self.seed = RandomNumGen.randHash(globalClock.getRealTime()) + self.accepting = 0 + self.numPlayersExiting = 0 + self.trolleyCountdownTime = simbase.config.GetFloat('picnic-countdown-time', ToontownGlobals.PICNIC_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM('DistributedPicnicBasketAI', [ + State.State('off', self.enterOff, self.exitOff, [ + 'waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, [ + 'waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, [ + 'waitEmpty'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + 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 rejectingBoardersHandler(self, avId, si): + self.rejectBoarder(avId) + + def rejectBoarder(self, avId): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId]) + + def acceptingBoardersHandler(self, avId, si): + self.notify.debug('acceptingBoardersHandler') + seatIndex = si + if not seatIndex == None: + self.acceptBoarder(avId, seatIndex) + return + + def acceptBoarder(self, avId, seatIndex): + self.notify.debug('acceptBoarder %d' % avId) + if self.findAvatar(avId) != None: + return + self.seats[seatIndex] = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.timeOfBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [ + avId]) + self.waitCountdown() + return + + 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.clearEmptyNowUnexpected(seatIndex) + if self.countFullSeats() == 0: + self.waitEmpty() + return + + def clearEmptyNowUnexpected(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [ + 1, globalClockDelta.getRealNetworkTime()]) + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + else: + self.clearFullNow(seatIndex) + self.sendUpdate('emptySlot' + str(seatIndex), [ + avId, globalClockDelta.getRealNetworkTime()]) + taskMgr.doMethodLater(TOON_EXIT_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,)) + return + + def clearEmptyNow(self, seatIndex): + self.notify.debugStateCall(self) + self.sendUpdate('emptySlot' + str(seatIndex), [ + 0, globalClockDelta.getRealNetworkTime()]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == 0: + 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, seed): + self.sendUpdate('setState', [state, seed, globalClockDelta.getRealNetworkTime()]) + + def getState(self): + return self.fsm.getCurrentState().getName() + + def requestBoard(self, si): + 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: + if av.hp > 0 and self.accepting and self.seats[si] == None: + self.notify.debug('accepting boarder %d' % avId) + self.acceptingBoardersHandler(avId, si) + else: + self.notify.debug('rejecting boarder %d' % avId) + self.rejectingBoardersHandler(avId, si) + else: + self.notify.warning('avid: %s does not exist, but tried to board a trolley' % avId) + return + + def requestExit(self, *args): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + if self.countFullSeats() > 0: + newArgs = ( + avId,) + args + self.numPlayersExiting += 1 + if self.accepting: + self.acceptingExitersHandler(*newArgs) + else: + self.rejectingExitersHandler(*newArgs) + else: + self.notify.debug('Player tried to exit after AI already kicked everyone out') + else: + self.notify.warning('avId: %s does not exist, but tried to exit a trolley' % avId) + + def doneExit(self): + if self.numPlayersExiting > 0: + self.numPlayersExiting -= 1 + if self.numPlayersExiting == 0 and self.countFullSeats() == 0: + self.waitEmpty() + + def start(self): + self.waitEmpty() + + def enterOff(self): + self.accepting = 0 + if hasattr(self, 'doId'): + for seatIndex in range(4): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + def exitOff(self): + self.accepting = 0 + + def waitEmptyTask(self, task): + self.waitEmpty() + return Task.done + + def waitEmpty(self): + self.fsm.request('waitEmpty') + + def enterWaitEmpty(self): + self.notify.debugStateCall(self) + self.d_setState('waitEmpty', self.seed) + self.seats = [None, None, None, None] + self.accepting = 1 + return + + def exitWaitEmpty(self): + self.notify.debugStateCall(self) + self.accepting = 0 + + def waitCountdown(self): + self.notify.debugStateCall(self) + self.fsm.request('waitCountdown') + + def enterWaitCountdown(self): + self.notify.debugStateCall(self) + self.d_setState('waitCountdown', self.seed) + self.accepting = 1 + taskMgr.doMethodLater(self.trolleyCountdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + self.accepting = 0 + if self.countFullSeats() > 0: + for x in range(len(self.seats)): + if not self.seats[x] == None: + self.sendUpdateToAvatarId(self.seats[x], 'setPicnicDone', []) + self.acceptExiter(self.seats[x]) + self.numPlayersExiting += 1 + + self.waitEmpty() + return Task.done + + def exitWaitCountdown(self): + self.notify.debugStateCall(self) + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def getPosHpr(self): + return self.posHpr + + def getTableNumber(self): + return self.tableNumber diff --git a/toontown/safezone/DistributedPicnicTable.py b/toontown/safezone/DistributedPicnicTable.py new file mode 100644 index 0000000..8c12575 --- /dev/null +++ b/toontown/safezone/DistributedPicnicTable.py @@ -0,0 +1,651 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedNode +from direct.distributed.ClockDelta import globalClockDelta +from ChineseCheckersBoard import ChineseCheckersBoard +from GameTutorials import * +from GameMenu import GameMenu +from direct.fsm import ClassicFSM, State +from direct.fsm import StateData +from toontown.distributed import DelayDelete +from toontown.toonbase.ToontownTimer import ToontownTimer +from toontown.toonbase import ToontownGlobals +from direct.showbase import PythonUtil +from otp.otpbase import OTPGlobals + +class DistributedPicnicTable(DistributedNode.DistributedNode): + + def __init__(self, cr): + self.cr = cr + NodePath.__init__(self, 'DistributedPicnicTable') + DistributedNode.DistributedNode.__init__(self, cr) + self.reparentTo(render) + self.picnicTable = loader.loadModel('phase_6/models/golf/game_table.bam') + self.picnicTable.reparentTo(self) + self.picnicTableSphereNodes = [] + self.numSeats = 6 + self.seats = [] + self.jumpOffsets = [] + self.inGame = False + self.requestSeat = None + self.gameState = None + self.cameraBoardTrack = Func(self.doNothing) + self.seatBumpForObserve = 0 + self.winTrack = Sequence() + self.outTrack = Sequence() + self.joinButton = None + self.observeButton = None + self.tutorialButton = None + self.exitButton = None + self.isPlaying = False + self.gameMenu = None + self.game = None + self.gameZone = None + self.tutorial = None + self.timerFunc = None + self.gameDoId = None + self.gameWantTimer = False + self.tableState = [None, + None, + None, + None, + None, + None] + self.haveAnimated = [] + self.winSound = base.loadSfx('phase_6/audio/sfx/KART_Applause_1.mp3') + self.happyDance = base.loadSfx('phase_5/audio/sfx/AA_heal_happydance.mp3') + self.accept('stoppedAsleep', self.handleSleep) + base.localAvatar.startSleepWatch(self.handleSleep) + self.__toonTracks = {} + self.fsm = ClassicFSM.ClassicFSM('PicnicTable', [State.State('off', self.enterOff, self.exitOff, ['chooseMode', 'observing']), + State.State('chooseMode', self.enterChooseMode, self.exitChooseMode, ['sitting', 'off', 'observing']), + State.State('sitting', self.enterSitting, self.exitSitting, ['off']), + State.State('observing', self.enterObserving, self.exitObserving, ['off'])], 'off', 'off') + self.fsm.enterInitialState() + for i in range(self.numSeats): + self.seats.append(self.picnicTable.find('**/*seat%d' % (i + 1))) + self.jumpOffsets.append(self.picnicTable.find('**/*jumpOut%d' % (i + 1))) + + self.tableCloth = self.picnicTable.find('**/basket_locator') + self.tableclothSphereNode = self.tableCloth.attachNewNode(CollisionNode('tablecloth_sphere')) + self.tableclothSphereNode.node().addSolid(CollisionSphere(0, 0, -2, 5.5)) + self.clockNode = ToontownTimer() + self.clockNode.setPos(1.16, 0, -0.83) + self.clockNode.setScale(0.3) + self.clockNode.hide() + return + + def announceGenerate(self): + DistributedNode.DistributedNode.announceGenerate(self) + for i in range(self.numSeats): + self.picnicTableSphereNodes.append(self.seats[i].attachNewNode(CollisionNode('picnicTable_sphere_%d_%d' % (self.getDoId(), i)))) + self.picnicTableSphereNodes[i].node().addSolid(CollisionSphere(0, 0, 0, 2)) + + self.tableState = [None, + None, + None, + None, + None, + None] + self.requestTableState() + 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') + angle = self.getH() + angle -= 90 + radAngle = deg2Rad(angle) + unitVec = Vec3(math.cos(radAngle), math.sin(radAngle), 0) + unitVec *= 30.0 + self.endPos = self.getPos() + unitVec + dist = Vec3(self.endPos - self.getPos()).length() + wheelAngle = dist / (0.5 * 1.4 * math.pi) * 360 + self.__enableCollisions() + return + + def handleSleep(self, task = None): + if self.fsm.getCurrentState().getName() == 'chooseMode': + self.cancelButtonPushed() + elif self.fsm.getCurrentState().getName() == 'sitting': + self.sendUpdate('requestExit', []) + if self.gameMenu != None: + self.gameMenu.removeButtons() + self.gameMenu.picnicFunction = None + self.gameMenu = None + if task != None: + task.done + return + + def disable(self): + DistributedNode.DistributedNode.disable(self) + self.ignore('stoppedAsleep') + self.clearToonTracks() + self.__disableCollisions() + self.disableChoiceButtons() + self.picnicTable.removeNode() + self.cameraBoardTrack = None + return + + def delete(self): + self.__disableCollisions() + self.ignore('stoppedAsleep') + DistributedNode.DistributedNode.delete(self) + self.disableChoiceButtons() + self.cameraBoardTrack = None + del self.winTrack + del self.outTrack + self.fsm = None + self.gameZone = None + self.clearToonTracks() + self.cameraBoardTrack = None + return + + def setName(self, name): + self.name = name + + def setGameDoId(self, doId): + self.gameDoId = doId + self.game = self.cr.doId2do[doId] + self.game.setHpr(self.getHpr()) + self.gameWantTimer = self.game.wantTimer + if self.gameState == 1: + self.game.fsm.request('playing') + + def setTimerFunc(self, function): + self.timerFunc = function + + def setTimer(self, timerEnd): + self.clockNode.stop() + time = globalClockDelta.networkToLocalTime(timerEnd) + self.timeLeft = int(time - globalClock.getRealTime()) + if self.gameWantTimer and self.game != None: + self.showTimer() + return + + def showTimer(self): + self.clockNode.stop() + self.clockNode.countdown(self.timeLeft, self.timerFunc) + self.clockNode.show() + + def requestTableState(self): + self.sendUpdate('requestTableState', []) + + def setTableState(self, tableStateList, isplaying): + y = 0 + print 'SET TABLE STATE' + if isplaying == 0: + self.isPlaying = False + else: + self.isPlaying = True + for x in tableStateList: + if x != 0: + if x not in self.tableState and self.cr.doId2do.has_key(x) and x not in self.haveAnimated: + seatIndex = tableStateList.index(x) + toon = self.cr.doId2do[x] + toon.stopSmooth() + toon.setAnimState('Sit', 1.0) + dest = self.seats[seatIndex].getPos(self.tableCloth) + hpr = self.seats[seatIndex].getHpr(render) + toon.setHpr(hpr) + if seatIndex > 2: + toon.setH(self.getH() + 180) + toon.wrtReparentTo(self) + toon.setPos(dest) + toon.setZ(toon.getZ() + 1.35) + if seatIndex > 2: + toon.setY(toon.getY() - 1.0) + else: + toon.setY(toon.getY() + 1.0) + if x != 0: + self.tableState[y] = x + else: + self.tableState[y] = None + y = y + 1 + + numPlayers = 0 + for x in self.tableState: + if x != None: + numPlayers += 1 + + print ' GETTING 2', self.gameMenu, numPlayers + if self.gameMenu: + if numPlayers > 2: + print ' GETTING HERE!!' + self.gameMenu.FindFour.setColor(0.7, 0.7, 0.7, 0.7) + self.gameMenu.FindFour['command'] = self.doNothing + self.gameMenu.findFourText['fg'] = (0.7, 0.7, 0.7, 0.7) + self.gameMenu.Checkers.setColor(0.7, 0.7, 0.7, 0.7) + self.gameMenu.Checkers['command'] = self.doNothing + self.gameMenu.checkersText['fg'] = (0.7, 0.7, 0.7, 0.7) + return + + def setIsPlaying(self, isPlaying): + if isPlaying == 0: + self.isPlaying = False + elif isPlaying == 1: + self.isPlaying = True + + def announceWinner(self, winString, avId): + if avId == base.localAvatar.getDoId(): + sound = Sequence(Wait(2.0), Parallel(SoundInterval(self.winSound), SoundInterval(self.happyDance))) + sound.start() + base.cr.playGame.getPlace().setState('walk') + if winString == 'Chinese Checkers': + whisper = WhisperPopup(TTLocalizer.ChineseCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Checkers': + whisper = WhisperPopup(TTLocalizer.RegularCheckersYouWon, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Find Four': + whisper = WhisperPopup('You won a game of Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif self.cr.doId2do.has_key(avId): + stateString = self.fsm.getCurrentState().getName() + if stateString == 'sitting' or stateString == 'observing': + base.cr.playGame.getPlace().setState('walk') + av = self.cr.doId2do[avId] + if winString == 'Chinese Checkers': + whisper = WhisperPopup(av.getName() + TTLocalizer.ChineseCheckersGameOf + TTLocalizer.ChineseCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Checkers': + whisper = WhisperPopup(av.getName() + TTLocalizer.RegularCheckersGameOf + TTLocalizer.RegularCheckers, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + elif winString == 'Find Four': + whisper = WhisperPopup(av.getName() + ' has won a game of' + ' Find Four!', OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + if self.cr.doId2do.has_key(avId): + toon = self.cr.doId2do[avId] + self.winTrack = Sequence(autoFinish=1) + if self.outTrack.isPlaying(): + self.winTrack.append(Wait(2.0)) + if avId == base.localAvatar.getDoId(): + self.winTrack.append(Func(self.stopToWalk)) + self.winTrack.append(ActorInterval(toon, 'happy-dance')) + if avId == base.localAvatar.getDoId(): + self.winTrack.append(Func(self.allowToWalk)) + self.winTrack.start() + whisper.manage(base.marginManager) + + def handleEnterPicnicTableSphere(self, i, collEntry): + self.notify.debug('Entering Picnic Table Sphere.... %s' % self.getDoId()) + self.requestSeat = i + self.seatBumpForObserve = i + self.fsm.request('chooseMode') + + def enableChoiceButtons(self): + if self.tableState[self.seatBumpForObserve] == None and self.isPlaying == False: + self.joinButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableJoinButton, + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(0, 0, 0.8), + scale=0.15, + command=lambda self = self: self.joinButtonPushed()) + if self.isPlaying == True: + self.observeButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableObserveButton, + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(0, 0, 0.6), + scale=0.15, + command=lambda self = self: self.observeButtonPushed()) + self.exitButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableCancelButton, + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.8, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(1, 0, 0.6), + scale=0.15, + command=lambda self = self: self.cancelButtonPushed()) + self.tutorialButton = DirectButton( + relief=None, + text=TTLocalizer.PicnicTableTutorial, + text_fg=(1, 1, 0.65, 1), + text_pos=(-.05, -.13), + text_scale=0.55, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(-1, 0, 0.6), + scale=0.15, + command=lambda self = self: self.tutorialButtonPushed()) + base.cr.playGame.getPlace().setState('stopped') + return + + def tutorialButtonPushed(self): + self.disableChoiceButtons() + self.gameMenu = GameMenu(self.tutorialFunction, 1) + self.tutorialButton.destroy() + self.tutorialButton = None + return + + def tutorialFunction(self, tutVal): + if tutVal == 1: + self.tutorial = ChineseTutorial(self.tutorialDone) + elif tutVal == 2: + self.tutorial = CheckersTutorial(self.tutorialDone) + self.gameMenu.picnicFunction = None + self.gameMenu = None + return + + def tutorialDone(self): + self.requestSeat = None + self.fsm.request('off') + self.tutorial = None + return + + def joinButtonPushed(self): + toon = base.localAvatar + self.sendUpdate('requestJoin', [self.requestSeat, + toon.getX(), + toon.getY(), + toon.getZ(), + toon.getH(), + toon.getP(), + toon.getR()]) + self.requestSeat = None + self.fsm.request('sitting') + return + + def rejectJoin(self): + self.fsm.request('off') + self.allowToWalk() + + def cancelButtonPushed(self): + base.cr.playGame.getPlace().setState('walk') + self.requestSeat = None + self.fsm.request('off') + return + + def disableChoiceButtons(self): + if self.joinButton: + self.joinButton.destroy() + if self.observeButton: + self.observeButton.destroy() + if self.exitButton: + self.exitButton.destroy() + if self.tutorialButton: + self.tutorialButton.destroy() + + def pickFunction(self, gameNum): + if gameNum == 1: + self.sendUpdate('requestPickedGame', [gameNum]) + elif gameNum == 2: + self.sendUpdate('requestPickedGame', [gameNum]) + elif gameNum == 3: + self.sendUpdate('requestPickedGame', [gameNum]) + + def allowPick(self): + self.gameMenu = GameMenu(self.pickFunction, 2) + + def setZone(self, zoneId): + if self.fsm.getCurrentState().getName() == 'sitting' or self.fsm.getCurrentState().getName() == 'observing': + if self.tutorial == None: + self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard') + if self.gameMenu != None: + self.gameMenu.removeButtons() + self.gameMenu.picnicFunction = None + self.gameMenu = None + return + + def fillSlot(self, avId, index, x, y, z, h, p, r, timestamp, parentDoId): + self.notify.debug('fill Slot: %d for %d' % (index, avId)) + if avId not in self.haveAnimated: + self.haveAnimated.append(avId) + if avId == base.localAvatar.getDoId(): + if self.inGame == True: + return + else: + self.inGame = True + self.seatPos = index + if self.cr.doId2do.has_key(avId): + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.tableCloth) + sitStartDuration = toon.getDuration('sit-start') + jumpTrack = self.generateToonJumpTrack(toon, index) + track = Sequence(autoFinish=1) + if avId == base.localAvatar.getDoId(): + if not base.cr.playGame.getPlace() == None: + self.moveCamera(index) + track.append(Func(self.__disableCollisions)) + track.append(jumpTrack) + track.append(Func(toon.setAnimState, 'Sit', 1.0)) + track.append(Func(self.clearToonTrack, avId)) + self.storeToonTrack(avId, track) + track.start() + return + + def emptySlot(self, avId, index, timestamp): + self.notify.debug('### seat %s now empty' % index) + if index == 255 and self.game != None: + self.stopObserveButtonPushed() + return + if avId in self.haveAnimated: + self.haveAnimated.remove(avId) + if self.cr.doId2do.has_key(avId): + if avId == base.localAvatar.getDoId(): + if self.gameZone: + base.cr.removeInterest(self.gameZone) + if self.inGame == True: + self.inGame = False + else: + return + toon = self.cr.doId2do[avId] + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) + self.outTrack = Sequence(jumpOutTrack) + if base.localAvatar.getDoId() == avId: + self.outTrack.append(Func(self.__enableCollisions)) + self.outTrack.append(Func(self.allowToWalk)) + self.fsm.request('off') + val = self.jumpOffsets[index].getPos(render) + self.outTrack.append(Func(toon.setPos, val)) + self.outTrack.append(Func(toon.startSmooth)) + self.outTrack.start() + return + + def stopToWalk(self): + base.cr.playGame.getPlace().setState('stopped') + + def allowToWalk(self): + base.cr.playGame.getPlace().setState('walk') + + def moveCamera(self, seatIndex): + self.oldCameraPos = camera.getPos() + self.oldCameraHpr = camera.getHpr() + camera.wrtReparentTo(self.picnicTable) + heading = PythonUtil.fitDestAngle2Src(camera.getH(), 90) + if seatIndex < 3: + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(0, -90, 0)) + elif camera.getH() < 0: + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(-180, -90, 0)) + else: + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.0, Point3(0, 0, 17), Point3(180, -90, 0)) + self.cameraBoardTrack.start() + + def moveCameraBack(self): + self.cameraBoardTrack = LerpPosHprInterval(camera, 2.5, self.oldCameraPos, self.oldCameraHpr) + self.cameraBoardTrack.start() + + def __enableCollisions(self): + for i in range(self.numSeats): + self.accept('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i), self.handleEnterPicnicTableSphere, [i]) + self.picnicTableSphereNodes[i].setCollideMask(ToontownGlobals.WallBitmask) + + self.tableclothSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + for i in range(self.numSeats): + self.ignore('enterpicnicTable_sphere_%d_%d' % (self.getDoId(), i)) + self.ignore('enterPicnicTableOK_%d_%d' % (self.getDoId(), i)) + + for i in range(self.numSeats): + self.picnicTableSphereNodes[i].setCollideMask(BitMask32(0)) + + self.tableclothSphereNode.setCollideMask(BitMask32(0)) + + def enterOff(self): + base.setCellsAvailable(base.leftCells + base.bottomCells, 0) + + def exitOff(self): + base.setCellsAvailable(base.bottomCells, 0) + + def enterChooseMode(self): + self.winTrack = Sequence(autoFinish=1) + self.enableChoiceButtons() + + def exitChooseMode(self): + self.disableChoiceButtons() + + def enterObserving(self): + self.enableStopObserveButton() + self.moveCamera(self.seatBumpForObserve) + self.sendUpdate('requestGameZone') + + def exitObserving(self): + if self.cameraBoardTrack.isPlaying(): + self.cameraBoardTrack.pause() + self.allowToWalk() + self.stopObserveButton.destroy() + + def enterSitting(self): + pass + + def exitSitting(self): + self.gameMenu = None + return + + def setGameZone(self, zoneId, gamestate): + self.gameZone = base.cr.addInterest(base.localAvatar.defaultShard, zoneId, 'gameBoard') + self.gameState = gamestate + + def observeButtonPushed(self): + self.requestSeat = None + self.fsm.request('observing') + return + + def enableStopObserveButton(self): + self.stopObserveButton = DirectButton( + relief=None, + text='Stop Observing', + text_fg=(1, 1, 0.65, 1), + text_pos=(0, -.23), + text_scale=0.45, + image=(self.upButton, self.downButton, self.rolloverButton), + image_color=(1, 0, 0, 1), + image_scale=(20, 1, 11), + pos=(0.92, 0, 0.4), + scale=0.15, + command=lambda self = self: self.stopObserveButtonPushed()) + return + + def stopObserveButtonPushed(self): + self.sendUpdate('leaveObserve', []) + self.gameState = None + if self.game: + self.game.fsm.request('gameOver') + base.cr.removeInterest(self.gameZone) + self.fsm.request('off') + return + + 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(self.tableCloth) + dest += self.jumpOffsets[seatIndex].getPos(self.tableCloth) + 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.tableCloth) + jumpTrack = Sequence(toonJumpTrack, Func(av.loop, 'neutral'), Func(av.wrtReparentTo, render)) + return jumpTrack + + 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.tableCloth): + dest = Vec3(self.tableCloth.getPos(av.getParent())) + seatNode = self.picnicTable.find('**/seat' + str(seatIndex + 1)) + dest += seatNode.getPos(self.tableCloth) + dna = av.getStyle() + dest -= hipOffset + if seatIndex > 2: + dest.setY(dest.getY() - 2.0) + if seatIndex == 1: + dest.setY(dest.getY() - 0.5) + dest.setZ(dest.getZ() + 0.2) + return dest + + def getJumpHpr(av = av, node = self.tableCloth): + hpr = self.seats[seatIndex].getHpr(av.getParent()) + if seatIndex < 3: + hpr.setX(hpr.getX()) + elif av.getH() < 0: + hpr.setX(hpr.getX() - 180) + else: + hpr.setX(hpr.getX() + 180) + return hpr + + toonJumpTrack = Parallel(ActorInterval(av, 'jump'), Sequence(Wait(0.43), Parallel(LerpHprInterval(av, hpr=getJumpHpr, duration=1), ProjectileInterval(av, endPos=getJumpDest, duration=1)))) + 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.tableCloth)) + return jumpTrack + + 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() + cleanupDelayDeletes(oldTrack) + + 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 doNothing(self): + pass diff --git a/toontown/safezone/DistributedPicnicTableAI.py b/toontown/safezone/DistributedPicnicTableAI.py new file mode 100644 index 0000000..d218d1e --- /dev/null +++ b/toontown/safezone/DistributedPicnicTableAI.py @@ -0,0 +1,290 @@ +from direct.distributed.DistributedNodeAI import DistributedNodeAI +from direct.distributed.ClockDelta import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.fsm import StateData +from toontown.safezone import DistributedChineseCheckersAI +from toontown.safezone import DistributedCheckersAI +from toontown.safezone import DistributedFindFourAI + +class DistributedPicnicTableAI(DistributedNodeAI): + + def __init__(self, air, zone, name, x, y, z, h, p, r): + DistributedNodeAI.__init__(self, air) + self.name = name + self.air = air + self.seats = [ + None, None, None, None, None, None] + self.setPos(x, y, z) + self.setHpr(h, p, r) + self.playersSitting = 0 + self.myPos = ( + x, y, z) + self.myHpr = (h, p, r) + self.playerIdList = [] + self.checkersZoneId = None + self.generateOtpObject(air.districtId, zone, optionalFields=['setX', 'setY', 'setZ', 'setH', 'setP', 'setR']) + self.observers = [] + self.allowPickers = [] + self.hasPicked = False + self.game = None + self.gameDoId = None + self.isAccepting = True + return + + def announceGenerate(self): + pass + + def delete(self): + DistributedNodeAI.delete(self) + self.game = None + self.gameDoId = None + return + + def setGameDoId(self, doId): + self.gameDoId = doId + self.game = self.air.doId2do.get(doId) + + def requestTableState(self): + avId = self.air.getAvatarIdFromSender() + self.getTableState() + + def getTableState(self): + tableStateList = [] + for x in self.seats: + if x == None: + tableStateList.append(0) + else: + tableStateList.append(x) + + if self.game and self.game.fsm.getCurrentState().getName() == 'playing': + self.sendUpdate('setTableState', [tableStateList, 1]) + else: + self.sendUpdate('setTableState', [tableStateList, 0]) + return + + def sendIsPlaying(self): + if self.game.fsm.getCurrentState().getName() == 'playing': + self.sendUpdate('setIsPlaying', [1]) + else: + self.sendUpdate('setIsPlaying', [0]) + + def announceWinner(self, gameName, avId): + self.sendUpdate('announceWinner', [gameName, avId]) + self.gameDoId = None + self.game = None + return + + def requestJoin(self, si, x, y, z, h, p, r): + 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: + if av.hp > 0 and self.isAccepting and self.seats[si] == None: + self.notify.debug('accepting boarder %d' % avId) + self.acceptBoarder(avId, si, x, y, z, h, p, r) + else: + self.notify.debug('rejecting boarder %d' % avId) + self.sendUpdateToAvatarId(avId, 'rejectJoin', []) + else: + self.notify.warning('avid: %s does not exist, but tried to board a picnicTable' % avId) + return + + def acceptBoarder(self, avId, seatIndex, x, y, z, h, p, r): + self.notify.debug('acceptBoarder %d' % avId) + if self.findAvatar(avId) != None: + return + isEmpty = True + for xx in self.seats: + if xx != None: + isEmpty = False + break + + if isEmpty == True or self.hasPicked == False: + self.sendUpdateToAvatarId(avId, 'allowPick', []) + self.allowPickers.append(avId) + if self.hasPicked == True: + self.sendUpdateToAvatarId(avId, 'setZone', [self.game.zoneId]) + self.seats[seatIndex] = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.timeOfBoarding = globalClock.getRealTime() + if self.game: + self.game.informGameOfPlayer() + self.sendUpdate('fillSlot', [ + avId, seatIndex, x, y, z, h, p, r, globalClockDelta.localToNetworkTime(self.timeOfBoarding), self.doId]) + self.getTableState() + return + + def requestPickedGame(self, gameNum): + avId = self.air.getAvatarIdFromSender() + if self.hasPicked == False and avId in self.allowPickers: + self.hasPicked = True + numPickers = len(self.allowPickers) + self.allowPickers = [] + self.pickGame(gameNum) + if self.game: + for x in range(numPickers): + self.game.informGameOfPlayer() + + def pickGame(self, gameNum): + x = 0 + for x in self.seats: + if x != None: + x += 1 + + if gameNum == 1: + if simbase.config.GetBool('want-chinese', 0): + self.game = DistributedChineseCheckersAI.DistributedChineseCheckersAI(self.air, self.doId, 'chinese', self.getX(), self.getY(), self.getZ() + 2.83, self.getH(), self.getP(), self.getR()) + self.sendUpdate('setZone', [self.game.zoneId]) + else: + if gameNum == 2: + if x <= 2: + if simbase.config.GetBool('want-checkers', 0): + self.game = DistributedCheckersAI.DistributedCheckersAI(self.air, self.doId, 'checkers', self.getX(), self.getY(), self.getZ() + 2.83, self.getH(), self.getP(), self.getR()) + self.sendUpdate('setZone', [self.game.zoneId]) + else: + if x <= 2: + if simbase.config.GetBool('want-findfour', 0): + self.game = DistributedFindFourAI.DistributedFindFourAI(self.air, self.doId, 'findFour', self.getX(), self.getY(), self.getZ() + 2.83, self.getH(), self.getP(), self.getR()) + self.sendUpdate('setZone', [self.game.zoneId]) + return + + def requestZone(self): + avId = self.air.getAvatarIdFromSender() + self.sendUpdateToAvatarId(avId, 'setZone', [self.game.zoneId]) + + def requestGameZone(self): + if self.hasPicked == True: + avId = self.air.getAvatarIdFromSender() + if self.game: + self.game.playersObserving.append(avId) + self.observers.append(avId) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleObserverExit, extraArgs=[avId]) + if self.game: + if self.game.fsm.getCurrentState().getName() == 'playing': + self.sendUpdateToAvatarId(avId, 'setGameZone', [self.checkersZoneId, 1]) + else: + self.sendUpdateToAvatarId(avId, 'setGameZone', [self.checkersZoneId, 0]) + + def leaveObserve(self): + avId = self.air.getAvatarIdFromSender() + if self.game: + if avId in self.game.playersObserving: + self.game.playersObserving.remove(avId) + + def handleObserverExit(self, avId): + if self.game and avId in self.game.playersObserving: + if self.game: + self.game.playersObserving.remove(avId) + self.ignore(self.air.getAvatarExitEvent(avId)) + + def requestExit(self): + self.notify.debug('requestExit') + avId = self.air.getAvatarIdFromSender() + av = self.air.doId2do.get(avId) + if av: + if self.countFullSeats() > 0: + self.acceptExiter(avId) + else: + self.notify.debug('Player tried to exit after AI already kicked everyone out') + else: + self.notify.warning('avId: %s does not exist, but tried to exit picnicTable' % avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + if avId in self.observers: + self.sendUpdateToAvatarId(avId, 'emptySlot', [avId, 255, globalClockDelta.getRealNetworkTime()]) + else: + self.seats[seatIndex] = None + self.ignore(self.air.getAvatarExitEvent(avId)) + self.sendUpdate('emptySlot', [ + avId, seatIndex, globalClockDelta.getRealNetworkTime()]) + self.getTableState() + numActive = 0 + for x in self.seats: + if x != None: + numActive = numActive + 1 + + if self.game: + self.game.informGameOfPlayerLeave() + self.game.handlePlayerExit(avId) + if numActive == 0: + self.isAccepting = True + if self.game: + self.game.handleEmptyGame() + self.game.requestDelete() + self.game = None + self.hasPicked = False + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + else: + self.seats[seatIndex] = None + self.ignore(self.air.getAvatarExitEvent(avId)) + if self.game: + self.game.informGameOfPlayerLeave() + self.game.handlePlayerExit(avId) + self.hasPicked = False + self.getTableState() + numActive = 0 + for x in self.seats: + if x != None: + numActive = numActive + 1 + + if numActive == 0 and self.game: + simbase.air.deallocateZone(self.game.zoneId) + self.game.requestDelete() + self.game = None + self.gameDoId = None + return + + def informGameOfPlayerExit(self, avId): + self.game.handlePlayerExit(avId) + + def handleGameOver(self): + for x in self.observers: + self.acceptExiter(x) + self.observers.remove(x) + + if self.game: + self.game.playersObserving = [] + for x in self.seats: + if x != None: + self.acceptExiter(x) + + self.game = None + self.gameDoId = None + self.hasPicked = False + 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 findAvailableSeat(self): + for i in range(len(self.seats)): + if self.seats[i] == None: + return i + + return + + def setCheckersZoneId(self, zoneId): + self.checkersZoneId = zoneId diff --git a/toontown/safezone/DistributedSZTreasure.py b/toontown/safezone/DistributedSZTreasure.py new file mode 100644 index 0000000..00f88ba --- /dev/null +++ b/toontown/safezone/DistributedSZTreasure.py @@ -0,0 +1,88 @@ +import DistributedTreasure +from pandac.PandaModules import VBase3, VBase4 +from direct.interval.IntervalGlobal import Sequence, Wait, Func, LerpColorScaleInterval, LerpScaleInterval +from toontown.toonbase import ToontownGlobals + +class DistributedSZTreasure(DistributedTreasure.DistributedTreasure): + + def __init__(self, cr): + DistributedTreasure.DistributedTreasure.__init__(self, cr) + self.fadeTrack = None + self.heartThrobIval = None + self.accept('ValentinesDayStart', self.startValentinesDay) + self.accept('ValentinesDayStop', self.stopValentinesDay) + return + + def delete(self): + DistributedTreasure.DistributedTreasure.delete(self) + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + if self.heartThrobIval: + self.heartThrobIval.finish() + self.heartThrobIval = None + self.ignore('ValentinesDayStart') + self.ignore('ValentinesDayStop') + return + + def setHolidayModelPath(self): + self.defaultModelPath = self.modelPath + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.VALENTINES_DAY in holidayIds: + self.modelPath = 'phase_4/models/props/tt_m_ara_ext_heart' + + def loadModel(self, modelPath, modelFindString = None): + self.setHolidayModelPath() + DistributedTreasure.DistributedTreasure.loadModel(self, self.modelPath, modelFindString) + + def startValentinesDay(self): + newModelPath = 'phase_4/models/props/tt_m_ara_ext_heart' + self.replaceTreasure(newModelPath) + self.startAnimation() + + def stopValentinesDay(self): + self.replaceTreasure(self.defaultModelPath) + self.stopAnimation() + + def replaceTreasure(self, newModelPath): + if self.fadeTrack: + self.fadeTrack.finish() + self.fadeTrack = None + + def replaceTreasureFunc(newModelPath): + if self.nodePath == None: + self.makeNodePath() + else: + self.treasure.getChildren().detach() + model = loader.loadModel(newModelPath) + model.instanceTo(self.treasure) + return + + def getFadeOutTrack(): + fadeOutTrack = LerpColorScaleInterval(self.nodePath, 0.8, colorScale=VBase4(0, 0, 0, 0), startColorScale=VBase4(1, 1, 1, 1), blendType='easeIn') + return fadeOutTrack + + def getFadeInTrack(): + fadeInTrack = LerpColorScaleInterval(self.nodePath, 0.5, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(0, 0, 0, 0), blendType='easeOut') + return fadeInTrack + + base.playSfx(self.rejectSound, node=self.nodePath) + self.fadeTrack = Sequence(getFadeOutTrack(), Func(replaceTreasureFunc, newModelPath), getFadeInTrack(), name=self.uniqueName('treasureFadeTrack')) + self.fadeTrack.start() + return + + def startAnimation(self): + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.VALENTINES_DAY in holidayIds: + originalScale = self.nodePath.getScale() + throbScale = VBase3(0.85, 0.85, 0.85) + throbInIval = LerpScaleInterval(self.nodePath, 0.3, scale=throbScale, startScale=originalScale, blendType='easeIn') + throbOutIval = LerpScaleInterval(self.nodePath, 0.3, scale=originalScale, startScale=throbScale, blendType='easeOut') + self.heartThrobIval = Sequence(throbInIval, throbOutIval, Wait(0.75)) + self.heartThrobIval.loop() + + def stopAnimation(self): + if self.heartThrobIval: + self.heartThrobIval.finish() + self.heartThrobIval = None + return diff --git a/toontown/safezone/DistributedSZTreasureAI.py b/toontown/safezone/DistributedSZTreasureAI.py new file mode 100644 index 0000000..c6dd0e1 --- /dev/null +++ b/toontown/safezone/DistributedSZTreasureAI.py @@ -0,0 +1,23 @@ +import DistributedTreasureAI +from toontown.toonbase import ToontownGlobals + +class DistributedSZTreasureAI(DistributedTreasureAI.DistributedTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedTreasureAI.DistributedTreasureAI.__init__(self, air, treasurePlanner, x, y, z) + self.healAmount = treasurePlanner.healAmount + + def validAvatar(self, av): + return av.hp >= -1 and av.hp < av.maxHp + + def d_setGrab(self, avId): + DistributedTreasureAI.DistributedTreasureAI.d_setGrab(self, avId) + if self.air.doId2do.has_key(avId): + av = self.air.doId2do[avId] + if self.validAvatar(av): + if av.hp == -1: + av.hp = 0 + if simbase.air.holidayManager.currentHolidays.has_key(ToontownGlobals.VALENTINES_DAY): + av.toonUp(self.healAmount * 2) + else: + av.toonUp(self.healAmount) diff --git a/toontown/safezone/DistributedTTTreasure.py b/toontown/safezone/DistributedTTTreasure.py new file mode 100644 index 0000000..80a66e7 --- /dev/null +++ b/toontown/safezone/DistributedTTTreasure.py @@ -0,0 +1,8 @@ +import DistributedSZTreasure + +class DistributedTTTreasure(DistributedSZTreasure.DistributedSZTreasure): + + def __init__(self, cr): + DistributedSZTreasure.DistributedSZTreasure.__init__(self, cr) + self.modelPath = 'phase_4/models/props/icecream' + self.grabSoundPath = 'phase_4/audio/sfx/SZ_DD_treasure.mp3' diff --git a/toontown/safezone/DistributedTTTreasureAI.py b/toontown/safezone/DistributedTTTreasureAI.py new file mode 100644 index 0000000..23ab220 --- /dev/null +++ b/toontown/safezone/DistributedTTTreasureAI.py @@ -0,0 +1,6 @@ +import DistributedSZTreasureAI + +class DistributedTTTreasureAI(DistributedSZTreasureAI.DistributedSZTreasureAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedSZTreasureAI.DistributedSZTreasureAI.__init__(self, air, treasurePlanner, x, y, z) diff --git a/toontown/safezone/DistributedTreasure.py b/toontown/safezone/DistributedTreasure.py new file mode 100644 index 0000000..0772927 --- /dev/null +++ b/toontown/safezone/DistributedTreasure.py @@ -0,0 +1,160 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal + +class DistributedTreasure(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTreasure') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.av = None + self.treasureFlyTrack = None + self.modelPath = None + self.nodePath = None + self.dropShadow = None + self.modelFindString = None + self.grabSoundPath = None + self.rejectSoundPath = 'phase_4/audio/sfx/ring_miss.mp3' + self.playSoundForRemoteToons = 1 + self.scale = 1.0 + self.shadow = 1 + self.fly = 1 + self.zOffset = 0.0 + self.billboard = 0 + return + + def disable(self): + self.ignoreAll() + self.nodePath.detachNode() + DistributedObject.DistributedObject.disable(self) + + def delete(self): + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + DistributedObject.DistributedObject.delete(self) + self.nodePath.removeNode() + return + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.loadModel(self.modelPath, self.modelFindString) + self.startAnimation() + self.nodePath.wrtReparentTo(render) + self.accept(self.uniqueName('entertreasureSphere'), self.handleEnterSphere) + + def handleEnterSphere(self, collEntry = None): + localAvId = base.localAvatar.getDoId() + if not self.fly: + self.handleGrab(localAvId) + self.d_requestGrab() + + def d_requestGrab(self): + self.sendUpdate('requestGrab', []) + + def getSphereRadius(self): + return 2.0 + + def loadModel(self, modelPath, modelFindString = None): + self.grabSound = base.loadSfx(self.grabSoundPath) + self.rejectSound = base.loadSfx(self.rejectSoundPath) + if self.nodePath == None: + self.makeNodePath() + else: + self.treasure.getChildren().detach() + model = loader.loadModel(modelPath) + if modelFindString != None: + model = model.find('**/' + modelFindString) + model.instanceTo(self.treasure) + return + + def makeNodePath(self): + self.nodePath = NodePath(self.uniqueName('treasure')) + if self.billboard: + self.nodePath.setBillboardPointEye() + self.nodePath.setScale(0.9 * self.scale) + self.treasure = self.nodePath.attachNewNode('treasure') + if self.shadow: + if not self.dropShadow: + self.dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.dropShadow.setColor(0, 0, 0, 0.5) + self.dropShadow.setPos(0, 0, 0.025) + self.dropShadow.setScale(0.4 * self.scale) + self.dropShadow.flattenLight() + self.dropShadow.reparentTo(self.nodePath) + collSphere = CollisionSphere(0, 0, 0, self.getSphereRadius()) + collSphere.setTangible(0) + collNode = CollisionNode(self.uniqueName('treasureSphere')) + collNode.setIntoCollideMask(WallBitmask) + collNode.addSolid(collSphere) + self.collNodePath = self.nodePath.attachNewNode(collNode) + self.collNodePath.stash() + + def getParentNodePath(self): + return render + + def setPosition(self, x, y, z): + if not self.nodePath: + self.makeNodePath() + self.nodePath.reparentTo(self.getParentNodePath()) + self.nodePath.setPos(x, y, z + self.zOffset) + self.collNodePath.unstash() + + def setGrab(self, avId): + if avId == 0: + return + if self.fly or avId != base.localAvatar.getDoId(): + self.handleGrab(avId) + + def setReject(self): + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + base.playSfx(self.rejectSound, node=self.nodePath) + self.treasureFlyTrack = Sequence(LerpColorScaleInterval(self.nodePath, 0.8, colorScale=VBase4(0, 0, 0, 0), startColorScale=VBase4(1, 1, 1, 1), blendType='easeIn'), LerpColorScaleInterval(self.nodePath, 0.2, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(0, 0, 0, 0), blendType='easeOut'), name=self.uniqueName('treasureFlyTrack')) + self.treasureFlyTrack.start() + return + + def handleGrab(self, avId): + self.collNodePath.stash() + self.avId = avId + if self.cr.doId2do.has_key(avId): + av = self.cr.doId2do[avId] + self.av = av + else: + self.nodePath.detachNode() + return + if self.playSoundForRemoteToons or self.avId == base.localAvatar.getDoId(): + base.playSfx(self.grabSound, node=self.nodePath) + if not self.fly: + self.nodePath.detachNode() + return + self.nodePath.wrtReparentTo(av) + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + avatarGoneName = self.av.uniqueName('disable') + self.accept(avatarGoneName, self.handleUnexpectedExit) + flytime = 1.0 + track = Sequence(LerpPosInterval(self.nodePath, flytime, pos=Point3(0, 0, 3), startPos=self.nodePath.getPos(), blendType='easeInOut'), Func(self.nodePath.detachNode), Func(self.ignore, avatarGoneName)) + if self.shadow: + self.treasureFlyTrack = Sequence(HideInterval(self.dropShadow), track, ShowInterval(self.dropShadow), name=self.uniqueName('treasureFlyTrack')) + else: + self.treasureFlyTrack = Sequence(track, name=self.uniqueName('treasureFlyTrack')) + self.treasureFlyTrack.start() + return + + def handleUnexpectedExit(self): + self.notify.warning('While getting treasure, ' + str(self.avId) + ' disconnected.') + if self.treasureFlyTrack: + self.treasureFlyTrack.finish() + self.treasureFlyTrack = None + return + + def getStareAtNodeAndOffset(self): + return (self.nodePath, Point3()) + + def startAnimation(self): + pass diff --git a/toontown/safezone/DistributedTreasureAI.py b/toontown/safezone/DistributedTreasureAI.py new file mode 100644 index 0000000..6d230f3 --- /dev/null +++ b/toontown/safezone/DistributedTreasureAI.py @@ -0,0 +1,36 @@ +from otp.ai.AIBase import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObjectAI + +class DistributedTreasureAI(DistributedObjectAI.DistributedObjectAI): + + def __init__(self, air, treasurePlanner, x, y, z): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.treasurePlanner = treasurePlanner + self.pos = (x, y, z) + + def requestGrab(self): + avId = self.air.getAvatarIdFromSender() + self.treasurePlanner.grabAttempt(avId, self.getDoId()) + + def validAvatar(self, av): + return 1 + + def d_setGrab(self, avId): + self.sendUpdate('setGrab', [avId]) + + def d_setReject(self): + self.sendUpdate('setReject', []) + + def getPosition(self): + return self.pos + + def setPosition(self, x, y, z): + self.pos = (x, y, z) + + def b_setPosition(self, x, y, z): + self.setPosition(x, y, z) + self.d_setPosition(x, y, z) + + def d_setPosition(self, x, y, z): + self.sendUpdate('setPosition', [x, y, z]) diff --git a/toontown/safezone/DistributedTrolley.py b/toontown/safezone/DistributedTrolley.py new file mode 100644 index 0000000..912205b --- /dev/null +++ b/toontown/safezone/DistributedTrolley.py @@ -0,0 +1,406 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from toontown.toonbase import ToontownGlobals +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from toontown.distributed import DelayDelete +from direct.task.Task import Task +from toontown.hood import ZoneUtil +from toontown.toontowngui import TeaserPanel + +class DistributedTrolley(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTrolley') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.localToonOnBoard = 0 + self.trolleyCountdownTime = base.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM('DistributedTrolley', [State.State('off', self.enterOff, self.exitOff, ['entering', + 'waitEmpty', + 'waitCountdown', + 'leaving']), + State.State('entering', self.enterEntering, self.exitEntering, ['waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, ['waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, ['waitEmpty', 'leaving']), + State.State('leaving', self.enterLeaving, self.exitLeaving, ['entering'])], 'off', 'off') + self.fsm.enterInitialState() + self.trolleyAwaySfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_away.mp3') + self.trolleyBellSfx = base.loadSfx('phase_4/audio/sfx/SZ_trolley_bell.mp3') + self.__toonTracks = {} + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.loader = self.cr.playGame.hood.loader + self.trolleyStation = self.loader.geom.find('**/*trolley_station*') + self.trolleyCar = self.trolleyStation.find('**/trolley_car') + self.trolleySphereNode = self.trolleyStation.find('**/trolley_sphere').node() + exitFog = Fog('TrolleyExitFog') + exitFog.setColor(0.0, 0.0, 0.0) + exitFog.setLinearOnsetPoint(30.0, 14.0, 0.0) + exitFog.setLinearOpaquePoint(37.0, 14.0, 0.0) + exitFog.setLinearFallback(70.0, 999.0, 1000.0) + self.trolleyExitFog = self.trolleyStation.attachNewNode(exitFog) + self.trolleyExitFogNode = exitFog + enterFog = Fog('TrolleyEnterFog') + enterFog.setColor(0.0, 0.0, 0.0) + enterFog.setLinearOnsetPoint(0.0, 14.0, 0.0) + enterFog.setLinearOpaquePoint(-7.0, 14.0, 0.0) + enterFog.setLinearFallback(70.0, 999.0, 1000.0) + self.trolleyEnterFog = self.trolleyStation.attachNewNode(enterFog) + self.trolleyEnterFogNode = enterFog + self.trolleyCar.setFogOff() + self.keys = self.trolleyCar.findAllMatches('**/key') + self.numKeys = self.keys.getNumPaths() + self.keyInit = [] + self.keyRef = [] + for i in range(self.numKeys): + key = self.keys[i] + key.setTwoSided(1) + ref = self.trolleyCar.attachNewNode('key' + `i` + 'ref') + ref.iPosHpr(key) + self.keyRef.append(ref) + self.keyInit.append(key.getTransform()) + + self.frontWheels = self.trolleyCar.findAllMatches('**/front_wheels') + self.numFrontWheels = self.frontWheels.getNumPaths() + self.frontWheelInit = [] + self.frontWheelRef = [] + for i in range(self.numFrontWheels): + wheel = self.frontWheels[i] + ref = self.trolleyCar.attachNewNode('frontWheel' + `i` + 'ref') + ref.iPosHpr(wheel) + self.frontWheelRef.append(ref) + self.frontWheelInit.append(wheel.getTransform()) + + self.backWheels = self.trolleyCar.findAllMatches('**/back_wheels') + self.numBackWheels = self.backWheels.getNumPaths() + self.backWheelInit = [] + self.backWheelRef = [] + for i in range(self.numBackWheels): + wheel = self.backWheels[i] + ref = self.trolleyCar.attachNewNode('backWheel' + `i` + 'ref') + ref.iPosHpr(wheel) + self.backWheelRef.append(ref) + self.backWheelInit.append(wheel.getTransform()) + + trolleyAnimationReset = Func(self.resetAnimation) + trolleyEnterStartPos = Point3(-20, 14, -1) + trolleyEnterEndPos = Point3(15, 14, -1) + trolleyEnterPos = Sequence(name='TrolleyEnterPos') + if base.wantFog: + trolleyEnterPos.append(Func(self.trolleyCar.setFog, self.trolleyEnterFogNode)) + trolleyEnterPos.append(self.trolleyCar.posInterval(TROLLEY_ENTER_TIME, trolleyEnterEndPos, startPos=trolleyEnterStartPos, blendType='easeOut')) + if base.wantFog: + trolleyEnterPos.append(Func(self.trolleyCar.setFogOff)) + trolleyEnterTrack = Sequence(trolleyAnimationReset, trolleyEnterPos, name='trolleyEnter') + keyAngle = round(TROLLEY_ENTER_TIME) * 360 + dist = Vec3(trolleyEnterEndPos - trolleyEnterStartPos).length() + wheelAngle = dist / (2.0 * math.pi * 0.95) * 360 + trolleyEnterAnimateInterval = LerpFunctionInterval(self.animateTrolley, duration=TROLLEY_ENTER_TIME, blendType='easeOut', extraArgs=[keyAngle, wheelAngle], name='TrolleyAnimate') + trolleyEnterSoundTrack = SoundInterval(self.trolleyAwaySfx, node=self.trolleyCar) + self.trolleyEnterTrack = Parallel(trolleyEnterTrack, trolleyEnterAnimateInterval, trolleyEnterSoundTrack) + trolleyExitStartPos = Point3(15, 14, -1) + trolleyExitEndPos = Point3(50, 14, -1) + trolleyExitPos = Sequence(name='TrolleyExitPos') + if base.wantFog: + trolleyExitPos.append(Func(self.trolleyCar.setFog, self.trolleyExitFogNode)) + trolleyExitPos.append(self.trolleyCar.posInterval(TROLLEY_EXIT_TIME, trolleyExitEndPos, startPos=trolleyExitStartPos, blendType='easeIn')) + if base.wantFog: + trolleyExitPos.append(Func(self.trolleyCar.setFogOff)) + trolleyExitBellInterval = SoundInterval(self.trolleyBellSfx, node=self.trolleyCar) + trolleyExitAwayInterval = SoundInterval(self.trolleyAwaySfx, node=self.trolleyCar) + keyAngle = round(TROLLEY_EXIT_TIME) * 360 + dist = Vec3(trolleyExitEndPos - trolleyExitStartPos).length() + wheelAngle = dist / (2.0 * math.pi * 0.95) * 360 + trolleyExitAnimateInterval = LerpFunctionInterval(self.animateTrolley, duration=TROLLEY_EXIT_TIME, blendType='easeIn', extraArgs=[keyAngle, wheelAngle], name='TrolleyAnimate') + self.trolleyExitTrack = Parallel(trolleyExitPos, trolleyExitBellInterval, trolleyExitAwayInterval, trolleyExitAnimateInterval, name=self.uniqueName('trolleyExit')) + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.fsm.request('off') + self.clearToonTracks() + self.trolleyExitFog.removeNode() + del self.trolleyExitFog + del self.trolleyExitFogNode + self.trolleyEnterFog.removeNode() + del self.trolleyEnterFog + del self.trolleyEnterFogNode + del self.loader + self.trolleyEnterTrack.pause() + self.trolleyEnterTrack = None + del self.trolleyEnterTrack + self.trolleyExitTrack.pause() + self.trolleyExitTrack = None + del self.trolleyExitTrack + del self.trolleyStation + del self.trolleyCar + del self.keys + del self.numKeys + del self.keyInit + del self.keyRef + del self.frontWheels + del self.numFrontWheels + del self.frontWheelInit + del self.frontWheelRef + del self.backWheels + del self.numBackWheels + del self.backWheelInit + del self.backWheelRef + return + + def delete(self): + del self.trolleyAwaySfx + del self.trolleyBellSfx + DistributedObject.DistributedObject.delete(self) + del self.fsm + + def setState(self, state, timestamp): + self.fsm.request(state, [globalClockDelta.localElapsedTime(timestamp)]) + + def allowedToEnter(self): + if hasattr(base, 'ttAccess') and base.ttAccess and base.ttAccess.canAccess(): + return True + return False + + def handleOkTeaser(self): + self.dialog.destroy() + del self.dialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + + def handleEnterTrolleySphere(self, collEntry): + self.notify.debug('Entering Trolley Sphere....') + if base.localAvatar.getPos(render).getZ() < self.trolleyCar.getPos(render).getZ(): + return + if self.allowedToEnter(): + self.loader.place.detectedTrolleyCollision() + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='minigames', doneFunc=self.handleOkTeaser) + + def handleEnterTrolley(self): + toon = base.localAvatar + self.sendUpdate('requestBoard', []) + + 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 fillSlot(self, index, avId): + if avId == 0: + pass + else: + if avId == base.localAvatar.getDoId(): + if not (self.fsm.getCurrentState().getName() == 'waitEmpty' or self.fsm.getCurrentState().getName() == 'waitCountdown'): + self.notify.warning("Can't board the trolley while in the '%s' state." % self.fsm.getCurrentState().getName()) + self.loader.place.fsm.request('walk') + return + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('boarding', [self.trolleyCar]) + self.localToonOnBoard = 1 + self.loader.place.trolley.fsm.request('boarded') + else: + self.notify.warning("Can't board the trolley because it doesn't exist") + self.sendUpdate('requestExit') + if self.cr.doId2do.has_key(avId): + toon = self.cr.doId2do[avId] + toon.stopSmooth() + toon.wrtReparentTo(self.trolleyCar) + toon.setAnimState('run', 1.0) + toon.headsUp(-5, -4.5 + index * 3, 1.4) + sitStartDuration = toon.getDuration('sit-start') + track = Sequence(LerpPosInterval(toon, TOON_BOARD_TIME * 0.75, Point3(-5, -4.5 + index * 3, 1.4)), LerpHprInterval(toon, TOON_BOARD_TIME * 0.25, Point3(90, 0, 0)), Parallel(Sequence(Wait(sitStartDuration * 0.25), LerpPosInterval(toon, sitStartDuration * 0.25, Point3(-3.9, -4.5 + index * 3, 3.0))), ActorInterval(toon, 'sit-start')), Func(toon.setAnimState, 'Sit', 1.0), Func(self.clearToonTrack, avId), name=toon.uniqueName('fillTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'Trolley.fillSlot') + self.storeToonTrack(avId, track) + track.start() + else: + DistributedTrolley.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot board the trolley!') + + def emptySlot0(self, avId, timestamp): + self.emptySlot(0, avId, timestamp) + + def emptySlot1(self, avId, timestamp): + self.emptySlot(1, avId, timestamp) + + def emptySlot2(self, avId, timestamp): + self.emptySlot(2, avId, timestamp) + + def emptySlot3(self, avId, timestamp): + self.emptySlot(3, avId, timestamp) + + def notifyToonOffTrolley(self, toon): + toon.setAnimState('neutral', 1.0) + if toon == base.localAvatar: + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.handleOffTrolley() + self.localToonOnBoard = 0 + else: + toon.startSmooth() + + def emptySlot(self, index, avId, timestamp): + if avId == 0: + pass + elif self.cr.doId2do.has_key(avId): + toon = self.cr.doId2do[avId] + toon.setHpr(self.trolleyCar, 90, 0, 0) + toon.wrtReparentTo(render) + toon.stopSmooth() + sitStartDuration = toon.getDuration('sit-start') + track = Sequence(Parallel(ActorInterval(toon, 'sit-start', startTime=sitStartDuration, endTime=0.0), Sequence(Wait(sitStartDuration * 0.5), LerpPosInterval(toon, sitStartDuration * 0.25, Point3(-5, -4.5 + index * 3, 1.4), other=self.trolleyCar))), Func(toon.setAnimState, 'run', 1.0), LerpPosInterval(toon, TOON_EXIT_TIME, Point3(21 - index * 3, -5, 0.02), other=self.trolleyStation), Func(self.notifyToonOffTrolley, toon), Func(self.clearToonTrack, avId), name=toon.uniqueName('emptyTrolley'), autoPause=1) + track.delayDelete = DelayDelete.DelayDelete(toon, 'Trolley.emptySlot') + self.storeToonTrack(avId, track) + track.start() + if avId == base.localAvatar.getDoId() and hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('exiting') + else: + DistributedTrolley.notify.warning('toon: ' + str(avId) + " doesn't exist, and" + ' cannot exit the trolley!') + + def rejectBoard(self, avId): + self.loader.place.trolley.handleRejectBoard() + + def setMinigameZone(self, zoneId, minigameId): + self.localToonOnBoard = 0 + messenger.send('playMinigame', [zoneId, minigameId]) + + def __enableCollisions(self): + self.accept('entertrolley_sphere', self.handleEnterTrolleySphere) + self.accept('enterTrolleyOK', self.handleEnterTrolley) + self.trolleySphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __disableCollisions(self): + self.ignore('entertrolley_sphere') + self.ignore('enterTrolleyOK') + self.trolleySphereNode.setCollideMask(BitMask32(0)) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterEntering(self, ts): + self.trolleyEnterTrack.start(ts) + + def exitEntering(self): + self.trolleyEnterTrack.finish() + + def enterWaitEmpty(self, ts): + self.__enableCollisions() + + def exitWaitEmpty(self): + self.__disableCollisions() + + def enterWaitCountdown(self, ts): + self.__enableCollisions() + self.accept('trolleyExitButton', self.handleExitButton) + self.clockNode = TextNode('trolleyClock') + self.clockNode.setFont(ToontownGlobals.getSignFont()) + self.clockNode.setAlign(TextNode.ACenter) + self.clockNode.setTextColor(0.9, 0.1, 0.1, 1) + self.clockNode.setText('10') + self.clock = self.trolleyStation.attachNewNode(self.clockNode) + self.clock.setBillboardAxis() + self.clock.setPosHprScale(15.86, 13.82, 11.68, -0.0, 0.0, 0.0, 3.02, 3.02, 3.02) + if ts < self.trolleyCountdownTime: + self.countdown(self.trolleyCountdownTime - ts) + + 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('trolleyTimerTask') + return taskMgr.add(countdownTask, 'trolleyTimerTask') + + def handleExitButton(self): + self.sendUpdate('requestExit') + + def exitWaitCountdown(self): + self.__disableCollisions() + self.ignore('trolleyExitButton') + taskMgr.remove('trolleyTimerTask') + self.clock.removeNode() + del self.clock + del self.clockNode + + def enterLeaving(self, ts): + self.trolleyExitTrack.start(ts) + if self.localToonOnBoard: + if hasattr(self.loader.place, 'trolley') and self.loader.place.trolley: + self.loader.place.trolley.fsm.request('trolleyLeaving') + + def exitLeaving(self): + self.trolleyExitTrack.finish() + + def animateTrolley(self, t, keyAngle, wheelAngle): + for i in range(self.numKeys): + key = self.keys[i] + ref = self.keyRef[i] + key.setH(ref, t * keyAngle) + + for i in range(self.numFrontWheels): + frontWheel = self.frontWheels[i] + ref = self.frontWheelRef[i] + frontWheel.setH(ref, t * wheelAngle) + + for i in range(self.numBackWheels): + backWheel = self.backWheels[i] + ref = self.backWheelRef[i] + backWheel.setH(ref, t * wheelAngle) + + def resetAnimation(self): + for i in range(self.numKeys): + self.keys[i].setTransform(self.keyInit[i]) + + for i in range(self.numFrontWheels): + self.frontWheels[i].setTransform(self.frontWheelInit[i]) + + for i in range(self.numBackWheels): + self.backWheels[i].setTransform(self.backWheelInit[i]) + + def getStareAtNodeAndOffset(self): + return (self.trolleyCar, Point3(0, 0, 4)) + + 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() + DelayDelete.cleanupDelayDeletes(oldTrack) + 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/safezone/DistributedTrolleyAI.py b/toontown/safezone/DistributedTrolleyAI.py new file mode 100644 index 0000000..2388480 --- /dev/null +++ b/toontown/safezone/DistributedTrolleyAI.py @@ -0,0 +1,324 @@ +from otp.ai.AIBase import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from TrolleyConstants 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.minigame import MinigameCreatorAI +from toontown.quest import Quests +from toontown.minigame import TrolleyHolidayMgrAI +from toontown.minigame import TrolleyWeekendMgrAI +from toontown.toonbase import ToontownAccessAI + +class DistributedTrolleyAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTrolleyAI') + + def __init__(self, air): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.seats = [ + None, None, None, None] + self.accepting = 0 + self.trolleyCountdownTime = simbase.config.GetFloat('trolley-countdown-time', TROLLEY_COUNTDOWN_TIME) + self.fsm = ClassicFSM.ClassicFSM('DistributedTrolleyAI', [ + State.State('off', self.enterOff, self.exitOff, [ + 'entering']), + State.State('entering', self.enterEntering, self.exitEntering, [ + 'waitEmpty']), + State.State('waitEmpty', self.enterWaitEmpty, self.exitWaitEmpty, [ + 'waitCountdown']), + State.State('waitCountdown', self.enterWaitCountdown, self.exitWaitCountdown, [ + 'waitEmpty', 'allAboard']), + State.State('allAboard', self.enterAllAboard, self.exitAllAboard, [ + 'leaving', 'waitEmpty']), + State.State('leaving', self.enterLeaving, self.exitLeaving, [ + 'entering'])], 'off', 'off') + self.fsm.enterInitialState() + return + + def delete(self): + self.fsm.requestFinalState() + del self.fsm + DistributedObjectAI.DistributedObjectAI.delete(self) + + 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 rejectingBoardersHandler(self, avId): + self.rejectBoarder(avId) + + def rejectBoarder(self, avId): + self.sendUpdateToAvatarId(avId, 'rejectBoard', [avId]) + + def acceptingBoardersHandler(self, avId): + self.notify.debug('acceptingBoardersHandler') + seatIndex = self.findAvailableSeat() + if seatIndex == None: + self.rejectBoarder(avId) + 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.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.timeOfBoarding = globalClock.getRealTime() + self.sendUpdate('fillSlot' + str(seatIndex), [ + avId]) + self.waitCountdown() + return + + 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.waitEmpty() + return + + def rejectingExitersHandler(self, avId): + self.rejectExiter(avId) + + def rejectExiter(self, avId): + pass + + def acceptingExitersHandler(self, avId): + self.acceptExiter(avId) + + def acceptExiter(self, avId): + seatIndex = self.findAvatar(avId) + if seatIndex == None: + pass + else: + self.clearFullNow(seatIndex) + self.sendUpdate('emptySlot' + str(seatIndex), [ + avId, globalClockDelta.getRealNetworkTime()]) + if self.countFullSeats() == 0: + self.waitEmpty() + taskMgr.doMethodLater(TOON_EXIT_TIME, self.clearEmptyNow, self.uniqueName('clearEmpty-%s' % seatIndex), extraArgs=(seatIndex,)) + return + + def clearEmptyNow(self, seatIndex): + self.sendUpdate('emptySlot' + str(seatIndex), [ + 0, globalClockDelta.getRealNetworkTime()]) + + def clearFullNow(self, seatIndex): + avId = self.seats[seatIndex] + if avId == 0: + self.notify.warning('Clearing an empty seat index: ' + str(seatIndex) + ' ... Strange...') + else: + self.seats[seatIndex] = None + self.sendUpdate('fillSlot' + str(seatIndex), [ + 0]) + self.ignore(simbase.air.getAvatarExitEvent(avId)) + return + + def d_setState(self, state): + self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) + + def getState(self): + return self.fsm.getCurrentState().getName() + + 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 + if not ToontownAccessAI.canAccess(avId, self.zoneId, 'DistributedTrolleyAI.requestBoard'): + self.notify.warning('Tooon %s does not have access to the trolley.' % avId) + self.rejectingBoardersHandler(*newArgs) + return + if av.hp > 0 and self.accepting: + self.acceptingBoardersHandler(*newArgs) + else: + self.rejectingBoardersHandler(*newArgs) + else: + self.notify.warning('avid: %s does not exist, but tried to board a trolley' % 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 a trolley' % avId) + + def start(self): + self.enter() + + def enterOff(self): + self.accepting = 0 + if hasattr(self, 'doId'): + for seatIndex in range(4): + taskMgr.remove(self.uniqueName('clearEmpty-' + str(seatIndex))) + + def exitOff(self): + self.accepting = 0 + + def enter(self): + self.fsm.request('entering') + + def enterEntering(self): + self.d_setState('entering') + self.accepting = 0 + self.seats = [None, None, None, None] + taskMgr.doMethodLater(TROLLEY_ENTER_TIME, self.waitEmptyTask, self.uniqueName('entering-timer')) + return + + def exitEntering(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('entering-timer')) + + def waitEmptyTask(self, task): + self.waitEmpty() + return Task.done + + def waitEmpty(self): + if hasattr(self, 'fsm') and self.fsm: + self.fsm.request('waitEmpty') + else: + self.notify.warning('waitEmpty no fsm avoided AI crash TOON-1984') + + def enterWaitEmpty(self): + self.d_setState('waitEmpty') + self.accepting = 1 + + def exitWaitEmpty(self): + self.accepting = 0 + + def waitCountdown(self): + self.fsm.request('waitCountdown') + + def enterWaitCountdown(self): + self.d_setState('waitCountdown') + self.accepting = 1 + taskMgr.doMethodLater(self.trolleyCountdownTime, self.timeToGoTask, self.uniqueName('countdown-timer')) + + def timeToGoTask(self, task): + if self.countFullSeats() > 0: + self.allAboard() + else: + self.waitEmpty() + return Task.done + + def exitWaitCountdown(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('countdown-timer')) + + def allAboard(self): + self.fsm.request('allAboard') + + def enterAllAboard(self): + self.accepting = 0 + currentTime = globalClock.getRealTime() + elapsedTime = currentTime - self.timeOfBoarding + self.notify.debug('elapsed time: ' + str(elapsedTime)) + waitTime = max(TOON_BOARD_TIME - elapsedTime, 0) + taskMgr.doMethodLater(waitTime, self.leaveTask, self.uniqueName('waitForAllAboard')) + + def exitAllAboard(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('waitForAllAboard')) + + def leaveTask(self, task): + if self.countFullSeats() > 0: + self.leave() + else: + self.waitEmpty() + return Task.done + + def leave(self): + self.fsm.request('leaving') + + def enterLeaving(self): + self.d_setState('leaving') + self.accepting = 0 + taskMgr.doMethodLater(TROLLEY_EXIT_TIME, self.trolleyLeftTask, self.uniqueName('leaving-timer')) + + def trolleyLeftTask(self, task): + self.trolleyLeft() + return Task.done + + def trolleyLeft(self): + numPlayers = self.countFullSeats() + if numPlayers > 0: + newbieIds = [] + for avId in self.seats: + if avId: + toon = self.air.doId2do.get(avId) + if toon: + if Quests.avatarHasTrolleyQuest(toon): + if not Quests.avatarHasCompletedTrolleyQuest(toon): + newbieIds.append(avId) + + playerArray = [] + for i in self.seats: + if i not in [None, 0]: + playerArray.append(i) + + startingVotes = None + metagameRound = -1 + trolleyGoesToMetagame = simbase.config.GetBool('trolley-goes-to-metagame', 0) + trolleyHoliday = bboard.get(TrolleyHolidayMgrAI.TrolleyHolidayMgrAI.PostName) + trolleyWeekend = bboard.get(TrolleyWeekendMgrAI.TrolleyWeekendMgrAI.PostName) + if trolleyGoesToMetagame or trolleyHoliday or trolleyWeekend: + metagameRound = 0 + if simbase.config.GetBool('metagame-min-2-players', 1) and len(playerArray) == 1: + metagameRound = -1 + mgDict = MinigameCreatorAI.createMinigame(self.air, playerArray, self.zoneId, newbieIds=newbieIds, startingVotes=startingVotes, metagameRound=metagameRound) + minigameZone = mgDict['minigameZone'] + minigameId = mgDict['minigameId'] + for seatIndex in range(len(self.seats)): + avId = self.seats[seatIndex] + if avId: + self.sendUpdateToAvatarId(avId, 'setMinigameZone', [minigameZone, minigameId]) + self.clearFullNow(seatIndex) + + else: + self.notify.warning('The trolley left, but was empty.') + self.enter() + return + + def exitLeaving(self): + self.accepting = 0 + taskMgr.remove(self.uniqueName('leaving-timer')) diff --git a/toontown/safezone/GSPlayground.py b/toontown/safezone/GSPlayground.py new file mode 100644 index 0000000..1c37620 --- /dev/null +++ b/toontown/safezone/GSPlayground.py @@ -0,0 +1,116 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +import Playground +from toontown.launcher import DownloadForceAcknowledge +from toontown.building import Elevator +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from direct.fsm import State + +class GSPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.parentFSM = parentFSM + self.startingBlockDoneEvent = 'startingBlockDone' + self.fsm.addState(State.State('startingBlock', self.enterStartingBlock, self.exitStartingBlock, ['walk'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('startingBlock') + + def load(self): + Playground.Playground.load(self) + + def unload(self): + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + blimp = base.cr.playGame.hood.loader.geom.find('**/GS_blimp') + blimp.setPos(-70, 250, -70) + blimpBase = NodePath('blimpBase') + blimpBase.setPos(0, -200, 25) + blimpBase.setH(-40) + blimp.reparentTo(blimpBase) + blimpRoot = NodePath('blimpRoot') + blimpRoot.setPos(0, -70, 40) + blimpRoot.reparentTo(base.cr.playGame.hood.loader.geom) + blimpBase.reparentTo(blimpRoot) + self.rotateBlimp = blimpRoot.hprInterval(360, Vec3(360, 0, 0)) + self.rotateBlimp.loop() + + def exit(self): + Playground.Playground.exit(self) + self.rotateBlimp.finish() + + def doRequestLeave(self, requestStatus): + self.fsm.request('trialerFA', [requestStatus]) + + def enterDFA(self, requestStatus): + doneEvent = 'dfaDoneEvent' + self.accept(doneEvent, self.enterDFACallback, [requestStatus]) + self.dfa = DownloadForceAcknowledge.DownloadForceAcknowledge(doneEvent) + if requestStatus['hoodId'] == ToontownGlobals.MyEstate: + self.dfa.enter(base.cr.hoodMgr.getPhaseFromHood(ToontownGlobals.MyEstate)) + else: + self.dfa.enter(5) + + def enterTeleportIn(self, requestStatus): + reason = requestStatus.get('reason') + if reason == RaceGlobals.Exit_Barrier: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeout, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_Slow: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RacerTooSlow, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_BarrierNoRefund: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeoutNoRefund, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + Playground.Playground.enterTeleportIn(self, requestStatus) + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def enterStartingBlock(self, distStartingBlock): + import pdb + pdb.set_trace() + self.accept(self.startingBlockDoneEvent, self.handleStartingBlockDone) + self.startingBlock = Elevator.Elevator(self.fsm.getStateNamed('startingBlock'), self.startingBlockDoneEvent, distStartingBlock) + distStartingBlock.elevatorFSM = self.startingBlock + self.startingBlock.load() + self.startingBlock.enter() + + def exitStartingBlock(self): + self.ignore(self.startingBlockDoneEvent) + self.startingBlock.unload() + self.startingBlock.exit() + del self.startingBlock + + def detectedStartingBlockCollision(self, distStartingBlock): + import pdb + pdb.set_trace() + self.fsm.request('startingBlock', [distStartingBlock]) + + def handleStartingBlockDone(self, doneStatus): + self.notify.debug('handling StartingBlock done event') + where = doneStatus['where'] + if where == 'reject': + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'racetrack': + print 'Entering Racetrack' + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleStartingBlockDone') + + def showPaths(self): + from toontown.classicchars import CCharPaths + from toontown.toonbase import TTLocalizer + self.showPathPoints(CCharPaths.getPaths(TTLocalizer.Goofy, 1)) diff --git a/toontown/safezone/GSSafeZoneLoader.py b/toontown/safezone/GSSafeZoneLoader.py new file mode 100644 index 0000000..a8b70cd --- /dev/null +++ b/toontown/safezone/GSSafeZoneLoader.py @@ -0,0 +1,116 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import * +from toontown.hood import ZoneUtil +from toontown.launcher import DownloadForceAcknowledge +from toontown.safezone.SafeZoneLoader import SafeZoneLoader +from toontown.safezone.GSPlayground import GSPlayground +from toontown.effects.CarSmoke import CarSmoke +from toontown.toonbase import ToontownGlobals +import random +if (__debug__): + import pdb + +class GSSafeZoneLoader(SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.musicFile = 'phase_6/audio/bgm/GS_SZ.mid' + self.activityMusicFile = 'phase_6/audio/bgm/GS_KartShop.mid' + self.dnaFile = 'phase_6/dna/goofy_speedway_sz.dna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_GS_sz.dna' + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone', 'racetrack']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior', 'racetrack']), + State.State('racetrack', self.enterRacetrack, self.exitRacetrack, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.smoke = None + return + + def load(self): + SafeZoneLoader.load(self) + if base.cr.newsManager: + holidayIds = base.cr.newsManager.getDecorationHolidayId() + if ToontownGlobals.CRASHED_LEADERBOARD in holidayIds: + self.startSmokeEffect() + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + + def unload(self): + del self.birdSound + if self.smoke != None: + self.stopSmokeEffect() + SafeZoneLoader.unload(self) + return + + def enterPlayground(self, requestStatus): + self.playgroundClass = GSPlayground + SafeZoneLoader.enterPlayground(self, requestStatus) + + def exitPlayground(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + SafeZoneLoader.exitPlayground(self) + self.playgroundClass = None + return + + def handlePlaygroundDone(self): + status = self.place.doneStatus + if self.enteringARace(status) and status.get('shardId') == None: + zoneId = status['zoneId'] + self.fsm.request('quietZone', [status]) + elif ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enteringARace(self, status): + if not status['where'] == 'racetrack': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterRacetrack(self, requestStatus): + self.trackId = requestStatus['trackId'] + self.accept('raceOver', self.handleRaceOver) + self.accept('leavingRace', self.handleLeftRace) + base.transitions.fadeOut(t=0) + + def exitRacetrack(self): + del self.trackId + + def handleRaceOver(self): + print 'you done!!' + + def handleLeftRace(self): + req = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'zoneId': 8000, + 'hoodId': 8000, + 'shardId': None} + self.fsm.request('quietZone', [req]) + return + + def startSmokeEffect(self): + if base.config.GetBool('want-crashedLeaderBoard-Smoke', 1): + leaderBoard = self.geom.find('**/*crashed*') + locator = leaderBoard.find('**/*locator_smoke*') + if locator != None: + self.smoke = CarSmoke(locator) + self.smoke.start() + return + + def stopSmokeEffect(self): + if base.config.GetBool('want-crashedLeaderBoard-Smoke', 1): + if self.smoke != None: + self.smoke.stop() + self.smoke.destroy() + self.smoke = None + return diff --git a/toontown/safezone/GZPlayground.py b/toontown/safezone/GZPlayground.py new file mode 100644 index 0000000..035c5f9 --- /dev/null +++ b/toontown/safezone/GZPlayground.py @@ -0,0 +1,141 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +import Playground +from toontown.launcher import DownloadForceAcknowledge +from toontown.building import Elevator +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from direct.fsm import State +from toontown.safezone import GolfKart + +class GZPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.parentFSM = parentFSM + self.golfKartBlockDoneEvent = 'golfKartBlockDone' + self.fsm.addState(State.State('golfKartBlock', self.enterGolfKartBlock, self.exitGolfKartBlock, ['walk'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('golfKartBlock') + self.golfKartDoneEvent = 'golfKartDone' + + def load(self): + Playground.Playground.load(self) + self.hub = loader.loadModel('phase_6/models/golf/golf_hub2') + self.hub.reparentTo(render) + self.dnaroot = render.find('**/goofy_speedway_DNARoot') + self.dnaroot = base.cr.playGame.hood.loader.geom.find('**/goofy_speedway_DNARoot') + if not self.dnaroot.isEmpty(): + self.dnaroot.removeNode() + + def unload(self): + Playground.Playground.unload(self) + self.hub.removeNode() + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + blimp = base.cr.playGame.hood.loader.geom.find('**/GS_blimp') + if blimp.isEmpty(): + return + blimp.setPos(-70, 250, -70) + blimpBase = NodePath('blimpBase') + blimpBase.setPos(0, -200, 25) + blimpBase.setH(-40) + blimp.reparentTo(blimpBase) + blimpRoot = NodePath('blimpRoot') + blimpRoot.setPos(0, -70, 40) + blimpRoot.reparentTo(base.cr.playGame.hood.loader.geom) + blimpBase.reparentTo(blimpRoot) + self.rotateBlimp = blimpRoot.hprInterval(360, Vec3(360, 0, 0)) + self.rotateBlimp.loop() + + def exit(self): + Playground.Playground.exit(self) + if hasattr(self, 'rotateBlimp'): + self.rotateBlimp.finish() + + def doRequestLeave(self, requestStatus): + self.fsm.request('trialerFA', [requestStatus]) + + def enterDFA(self, requestStatus): + doneEvent = 'dfaDoneEvent' + self.accept(doneEvent, self.enterDFACallback, [requestStatus]) + self.dfa = DownloadForceAcknowledge.DownloadForceAcknowledge(doneEvent) + if requestStatus['hoodId'] == ToontownGlobals.MyEstate: + self.dfa.enter(base.cr.hoodMgr.getPhaseFromHood(ToontownGlobals.MyEstate)) + else: + self.dfa.enter(5) + + def enterTeleportIn(self, requestStatus): + reason = requestStatus.get('reason') + if reason == RaceGlobals.Exit_Barrier: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeout, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_Slow: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RacerTooSlow, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_BarrierNoRefund: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeoutNoRefund, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + Playground.Playground.enterTeleportIn(self, requestStatus) + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def enterGolfKartBlock(self, golfKart): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('off', 1) + self.accept(self.golfKartDoneEvent, self.handleGolfKartDone) + self.trolley = GolfKart.GolfKart(self, self.fsm, self.golfKartDoneEvent, golfKart.getDoId()) + self.trolley.load() + self.trolley.enter() + + def exitGolfKartBlock(self): + base.localAvatar.laffMeter.stop() + self.ignore(self.trolleyDoneEvent) + self.trolley.unload() + self.trolley.exit() + del self.trolley + + def detectedGolfKartCollision(self, golfKart): + self.notify.debug('detectedGolfkartCollision()') + self.fsm.request('golfKartBlock', [golfKart]) + + def handleStartingBlockDone(self, doneStatus): + self.notify.debug('handling StartingBlock done event') + where = doneStatus['where'] + if where == 'reject': + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'racetrack': + print 'Entering Racetrack' + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleStartingBlockDone') + + def handleGolfKartDone(self, doneStatus): + self.notify.debug('handling golf kart done event') + mode = doneStatus['mode'] + if mode == 'reject': + self.fsm.request('walk') + elif mode == 'exit': + self.fsm.request('walk') + elif mode == 'golfcourse': + self.doneStatus = {'loader': 'golfcourse', + 'where': 'golfcourse', + 'hoodId': self.loader.hood.id, + 'zoneId': doneStatus['zoneId'], + 'shardId': None, + 'courseId': doneStatus['courseId']} + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + mode + ' in handleGolfKartDone') + return diff --git a/toontown/safezone/GZSafeZoneLoader.py b/toontown/safezone/GZSafeZoneLoader.py new file mode 100644 index 0000000..b3ea62a --- /dev/null +++ b/toontown/safezone/GZSafeZoneLoader.py @@ -0,0 +1,109 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui import DirectGui +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import * +from toontown.hood import ZoneUtil +from toontown.launcher import DownloadForceAcknowledge +from toontown.safezone.SafeZoneLoader import SafeZoneLoader +from toontown.safezone.GZPlayground import GZPlayground +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +import random +if (__debug__): + import pdb + +class GZSafeZoneLoader(SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.musicFile = 'phase_6/audio/bgm/GZ_SZ.mid' + self.activityMusicFile = 'phase_6/audio/bgm/GS_KartShop.mid' + self.dnaFile = 'phase_6/dna/golf_zone_sz.dna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_GZ_sz.dna' + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone', 'golfcourse']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior', 'golfcourse']), + State.State('golfcourse', self.enterGolfCourse, self.exitGolfCourse, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + + def unload(self): + del self.birdSound + SafeZoneLoader.unload(self) + + def enterPlayground(self, requestStatus): + self.playgroundClass = GZPlayground + SafeZoneLoader.enterPlayground(self, requestStatus) + top = self.geom.find('**/linktunnel_bosshq_10000_DNARoot') + sign = top.find('**/Sign_5') + sign.node().setEffect(DecalEffect.make()) + locator = top.find('**/sign_origin') + signText = DirectGui.OnscreenText(text=TextEncoder.upper(TTLocalizer.BossbotHQ[-1]), font=ToontownGlobals.getSuitFont(), scale=TTLocalizer.GZSZLsignText, fg=(0, 0, 0, 1), mayChange=False, parent=sign) + signText.setPosHpr(locator, 0, 0, -0.3, 0, 0, 0) + signText.setDepthWrite(0) + + def exitPlayground(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + SafeZoneLoader.exitPlayground(self) + self.playgroundClass = None + return + + def handlePlaygroundDone(self): + status = self.place.doneStatus + if self.enteringAGolfCourse(status) and status.get('shardId') == None: + zoneId = status['zoneId'] + self.fsm.request('quietZone', [status]) + elif ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enteringARace(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enteringAGolfCourse(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterGolfCourse(self, requestStatus): + if requestStatus.has_key('curseId'): + self.golfCourseId = requestStatus['courseId'] + else: + self.golfCourseId = 0 + self.accept('raceOver', self.handleRaceOver) + self.accept('leavingGolf', self.handleLeftGolf) + base.transitions.irisOut(t=0.2) + + def exitGolfCourse(self): + del self.golfCourseId + + def handleRaceOver(self): + print 'you done!!' + + def handleLeftGolf(self): + req = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'zoneId': 17000, + 'hoodId': 17000, + 'shardId': None} + self.fsm.request('quietZone', [req]) + return diff --git a/toontown/safezone/GameMenu.py b/toontown/safezone/GameMenu.py new file mode 100644 index 0000000..6a880df --- /dev/null +++ b/toontown/safezone/GameMenu.py @@ -0,0 +1,141 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from direct.task.Task import Task +from direct.interval.IntervalGlobal import * +from TrolleyConstants import * +from direct.gui.DirectGui import * +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals + +class GameMenu(DirectFrame): + + def __init__(self, picnicFunction, menuType): + self.picnicFunction = picnicFunction + DirectFrame.__init__( + self, + pos=(0.0, 0.0, 0.85), + image_color=ToontownGlobals.GlobalDialogColor, + image_scale=(1.8, 0.9, 0.13), + text='', + text_scale=0.05) + self['image'] = DGG.getDefaultDialogGeom() + if menuType == 1: + self.title = DirectLabel( + self, + relief=None, + text=TTLocalizer.PicnicTableMenuTutorial, + text_pos=(0.0, -0.038), + text_fg=(1, 0, 0, 1), + text_scale=0.09, + text_font=ToontownGlobals.getSignFont(), + text_shadow=(1, 1, 1, 1)) + elif menuType == 2: + self.title = DirectLabel( + self, + relief=None, + text=TTLocalizer.PicnicTableMenuSelect, + text_pos=(0.0, -0.04), + text_fg=(1, 0, 0, 1), + text_scale=0.09, + text_font=ToontownGlobals.getSignFont()) + self.selectionButtons = loader.loadModel('phase_6/models/golf/picnic_game_menu.bam') + btn1 = self.selectionButtons.find('**/Btn1') + btn2 = self.selectionButtons.find('**/Btn2') + btn3 = self.selectionButtons.find('**/Btn3') + self.ChineseCheckers = DirectButton( + self, + image=(btn1.find('**/checkersBtnUp'), btn1.find('**/checkersBtnDn'), btn1.find('**/checkersBtnHi'), btn1.find('**/checkersBtnUp')), + scale=0.36, + relief=0, + pos=(0, 0, -0.7), + command=self.checkersSelected) + self.Checkers = DirectButton( + self, + image=(btn2.find('**/regular_checkersBtnUp'), btn2.find('**/regular_checkersBtnDn'), btn2.find('**/regular_checkersBtnHi'), btn2.find('**/regular_checkersBtnUp')), + scale=0.36, + relief=0, + pos=(0.8, 0, -0.7), + command=self.regCheckersSelected) + self.FindFour = DirectButton( + self, + image=(btn3.find('**/findfourBtnUp'), btn3.find('**/findfourBtnDn'), btn3.find('**/findfourBtnHi'), btn3.find('**/findfourBtnUp')), + scale=0.36, + relief=0, + pos=(-0.8, 0, -0.7), + command=self.findFourSelected) + if not base.config.GetBool('want-chinese', 0): + self.ChineseCheckers['command'] = self.doNothing + self.ChineseCheckers.setColor(0.7, 0.7, 0.7, 0.7) + if not base.config.GetBool('want-checkers', 0): + self.Checkers['command'] = self.doNothing + self.Checkers.setColor(0.7, 0.7, 0.7, 0.7) + if not base.config.GetBool('want-findfour', 0): + self.FindFour['command'] = self.doNothing + self.FindFour.setColor(0.7, 0.7, 0.7, 0.7) + self.chineseText = OnscreenText( + text='Chinese Checkers', + pos=(0, 0.56, -0.8), + scale=0.15, + fg=Vec4(1, 1, 1, 1), + align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), + wordwrap=7, + shadow=(0, 0, 0, 0.8), + shadowOffset=(-0.1, -0.1), + mayChange=True) + self.chineseText.setR(-8) + self.checkersText = OnscreenText( + text='Checkers', + pos=(0.81, -.1, -0.8), + scale=0.15, + fg=Vec4(1, 1, 1, 1), + align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), + wordwrap=7, + shadow=(0, 0, 0, 0.8), + shadowOffset=(0.1, -0.1), + mayChange=True) + self.findFourText = OnscreenText( + text='Find Four', + pos=(-0.81, -.08, -0.8), + scale=0.15, + fg=Vec4(1, 1, 1, 1), + align=TextNode.ACenter, + font=ToontownGlobals.getMinnieFont(), + wordwrap=8, + shadow=(0, 0, 0, 0.8), + shadowOffset=(-0.1, -0.1), + mayChange=True) + self.findFourText.setR(-8) + self.checkersText.setR(8) + return + + def delete(self): + self.removeButtons() + + def removeButtons(self): + self.ChineseCheckers.destroy() + self.Checkers.destroy() + self.FindFour.destroy() + self.chineseText.destroy() + self.checkersText.destroy() + self.findFourText.destroy() + DirectFrame.destroy(self) + + def checkersSelected(self): + self.picnicFunction(1) + self.picnicFunction = None + return + + def regCheckersSelected(self): + self.picnicFunction(2) + self.picnicFunction = None + return + + def findFourSelected(self): + self.picnicFunction(3) + self.picnicFunction = None + return + + def doNothing(self): + pass diff --git a/toontown/safezone/GameTutorials.py b/toontown/safezone/GameTutorials.py new file mode 100644 index 0000000..b2578c7 --- /dev/null +++ b/toontown/safezone/GameTutorials.py @@ -0,0 +1,259 @@ +from direct.gui.DirectGui import * +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * + +class ChineseTutorial(DirectFrame, FSM.FSM): + + def __init__(self, doneFunction, doneEvent = None, callback = None): + FSM.FSM.__init__(self, 'ChineseTutorial') + self.doneFunction = doneFunction + base.localAvatar.startSleepWatch(self.handleQuit) + self.doneEvent = doneEvent + self.callback = callback + self.setStateArray(['Page1', 'Page2', 'Quit']) + base.localAvatar.startSleepWatch(self.handleQuit) + DirectFrame.__init__(self, pos=(-0.7, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.5, 1.0), text='', text_scale=0.06) + self.accept('stoppedAsleep', self.handleQuit) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.4), text_fg=(1, 0, 0, 1), text_scale=0.13, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_6/models/golf/checker_tutorial.bam') + images.setTransparency(1) + self.iPage1 = images.find('**/tutorialPage1*') + self.iPage1.reparentTo(aspect2d) + self.iPage1.setPos(0.43, -0.1, 0.0) + self.iPage1.setScale(13.95) + self.iPage1.setTransparency(1) + self.iPage1.hide() + self.iPage1.getChildren()[1].hide() + self.iPage2 = images.find('**/tutorialPage3*') + self.iPage2.reparentTo(aspect2d) + self.iPage2.setPos(0.43, -0.1, 0.5) + self.iPage2.setScale(13.95) + self.iPage2.setTransparency(1) + self.iPage2.hide() + self.iPage3 = images.find('**/tutorialPage2*') + self.iPage3.reparentTo(aspect2d) + self.iPage3.setPos(0.43, -0.1, -0.5) + self.iPage3.setScale(13.95) + self.iPage3.setTransparency(1) + self.iPage3.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.ChineseTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.35, -0.3, -0.33), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.ChineseTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.35, -0.3, -0.33), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.ChineseTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, -0.3, -0.33), command=self.handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.request('Page1') + return + + def __del__(self): + self.cleanup() + + def enterPage1(self, *args): + self.bNext.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle1,) + self['text'] = TTLocalizer.ChinesePage1 + self['text_pos'] = (0.0, 0.23) + self['text_wordwrap'] = 13.5 + self.bPrev['state'] = DGG.DISABLED + self.bPrev.hide() + self.bNext['state'] = DGG.NORMAL + self.iPage1.show() + self.blinker = Sequence() + obj = self.iPage1.getChildren()[1] + self.iPage1.getChildren()[1].show() + self.blinker.append(LerpColorInterval(obj, 0.5, Vec4(0.5, 0.5, 0, 0.0), Vec4(0.2, 0.2, 0.2, 1))) + self.blinker.append(LerpColorInterval(obj, 0.5, Vec4(0.2, 0.2, 0.2, 1), Vec4(0.5, 0.5, 0, 0.0))) + self.blinker.loop() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + self.iPage1.getChildren()[1].hide() + self.blinker.finish() + + def enterPage2(self, *args): + self.bPrev.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.ChinesePage2 + self['text_pos'] = (0.0, 0.28) + self['text_wordwrap'] = 12.5 + self.bNext['state'] = DGG.DISABLED + self.bNext.hide() + self.iPage2.show() + self.iPage3.show() + self.bQuit.show() + + def exitPage2(self, *args): + self.iPage2.hide() + self.bQuit.hide() + self.iPage3.hide() + + def enterQuit(self, *args): + self.iPage1.removeNode() + self.iPage2.removeNode() + self.iPage3.removeNode() + self.bNext.destroy() + self.bPrev.destroy() + self.bQuit.destroy() + DirectFrame.destroy(self) + + def exitQuit(self, *args): + pass + + def handleQuit(self, task = None): + base.cr.playGame.getPlace().setState('walk') + self.forceTransition('Quit') + self.doneFunction() + if task != None: + task.done + return + + +class CheckersTutorial(DirectFrame, FSM.FSM): + + def __init__(self, doneFunction, doneEvent = None, callback = None): + FSM.FSM.__init__(self, 'CheckersTutorial') + self.doneFunction = doneFunction + base.localAvatar.startSleepWatch(self.handleQuit) + self.doneEvent = doneEvent + self.callback = callback + self.setStateArray(['Page1', + 'Page2', + 'Page3', + 'Quit']) + DirectFrame.__init__(self, pos=(-0.7, 0.0, 0.0), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.5, 1.0), text='', text_scale=0.06) + self.accept('stoppedAsleep', self.handleQuit) + self['image'] = DGG.getDefaultDialogGeom() + self.title = DirectLabel(self, relief=None, text='', text_pos=(0.0, 0.4), text_fg=(1, 0, 0, 1), text_scale=0.13, text_font=ToontownGlobals.getSignFont()) + images = loader.loadModel('phase_6/models/golf/regularchecker_tutorial.bam') + images.setTransparency(1) + self.iPage1 = images.find('**/tutorialPage1*') + self.iPage1.reparentTo(aspect2d) + self.iPage1.setPos(0.43, -0.1, 0.0) + self.iPage1.setScale(0.4) + self.iPage1.setTransparency(1) + self.iPage1.hide() + self.iPage2 = images.find('**/tutorialPage2*') + self.iPage2.reparentTo(aspect2d) + self.iPage2.setPos(0.43, -0.1, 0.0) + self.iPage2.setScale(0.4) + self.iPage2.setTransparency(1) + self.iPage2.hide() + self.iPage3 = images.find('**/tutorialPage3*') + self.iPage3.reparentTo(aspect2d) + self.iPage3.setPos(0.6, -0.1, 0.5) + self.iPage3.setScale(0.4) + self.iPage3.setTransparency(1) + self.obj = self.iPage3.find('**/king*') + self.iPage3.hide() + self.iPage4 = images.find('**/tutorialPage4*') + self.iPage4.reparentTo(aspect2d) + self.iPage4.setPos(0.6, -0.1, -0.5) + self.iPage4.setScale(0.4) + self.iPage4.setTransparency(1) + self.iPage4.hide() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.bNext = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), relief=None, text=TTLocalizer.ChineseTutorialNext, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.35, -0.3, -0.33), command=self.requestNext) + self.bPrev = DirectButton(self, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), image_scale=(-1.0, 1.0, 1.0), relief=None, text=TTLocalizer.ChineseTutorialPrev, text3_fg=Vec4(0, 0, 0, 0.5), text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.35, -0.3, -0.33), command=self.requestPrev) + self.bQuit = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.ChineseTutorialDone, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, -0.3, -0.33), command=self.handleQuit) + self.bQuit.hide() + buttons.removeNode() + gui.removeNode() + self.request('Page1') + return + + def __del__(self): + self.cleanup() + + def enterPage1(self, *args): + self.bNext.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle1,) + self['text'] = TTLocalizer.CheckersPage1 + self['text_pos'] = (0.0, 0.23) + self['text_wordwrap'] = 13.5 + self['text_scale'] = 0.06 + self.bPrev['state'] = DGG.DISABLED + self.bPrev.hide() + self.bNext['state'] = DGG.NORMAL + self.iPage1.show() + + def exitPage1(self, *args): + self.bPrev['state'] = DGG.NORMAL + self.iPage1.hide() + + def enterPage2(self, *args): + self.bPrev.show() + self.bNext.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.CheckersPage2 + self['text_pos'] = (0.0, 0.28) + self['text_wordwrap'] = 12.5 + self['text_scale'] = 0.06 + self.bNext['state'] = DGG.NORMAL + self.iPage2.show() + + def exitPage2(self, *args): + self.iPage2.hide() + + def enterPage3(self, *args): + self.bPrev.show() + self.title['text'] = (TTLocalizer.ChineseTutorialTitle2,) + self['text'] = TTLocalizer.CheckersPage3 + '\n\n' + TTLocalizer.CheckersPage4 + self['text_pos'] = (0.0, 0.32) + self['text_wordwrap'] = 19 + self['text_scale'] = 0.05 + self.bNext['state'] = DGG.DISABLED + self.blinker = Sequence() + self.blinker.append(LerpColorInterval(self.obj, 0.5, Vec4(0.5, 0.5, 0, 0.0), Vec4(0.9, 0.9, 0, 1))) + self.blinker.append(LerpColorInterval(self.obj, 0.5, Vec4(0.9, 0.9, 0, 1), Vec4(0.5, 0.5, 0, 0.0))) + self.blinker.loop() + self.bNext.hide() + self.iPage3.show() + self.iPage4.show() + self.bQuit.show() + + def exitPage3(self, *args): + self.blinker.finish() + self.iPage3.hide() + self.bQuit.hide() + self.iPage4.hide() + + def enterQuit(self, *args): + self.iPage1.removeNode() + self.iPage2.removeNode() + self.iPage3.removeNode() + self.bNext.destroy() + self.bPrev.destroy() + self.bQuit.destroy() + DirectFrame.destroy(self) + + def exitQuit(self, *args): + pass + + def handleQuit(self, task = None): + self.forceTransition('Quit') + base.cr.playGame.getPlace().setState('walk') + self.doneFunction() + if task != None: + task.done + return diff --git a/toontown/safezone/GolfKart.py b/toontown/safezone/GolfKart.py new file mode 100644 index 0000000..b95091b --- /dev/null +++ b/toontown/safezone/GolfKart.py @@ -0,0 +1,223 @@ +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.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil + +class GolfKart(StateData.StateData): + + def __init__(self, safeZone, parentFSM, doneEvent, golfCourse): + StateData.StateData.__init__(self, doneEvent) + self.golfCourse = golfCourse + self.fsm = ClassicFSM.ClassicFSM('GolfKart', [ + State.State('start', + self.enterStart, + self.exitStart, [ + 'requestBoard', + 'trolleyHFA', + 'trolleyTFA']), + State.State('trolleyHFA', + self.enterTrolleyHFA, + self.exitTrolleyHFA, [ + 'final']), + State.State('trolleyTFA', + self.enterTrolleyTFA, + self.exitTrolleyTFA, [ + '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', + 'trolleyLeaving', + 'final']), + State.State('requestExit', + self.enterRequestExit, + self.exitRequestExit, [ + 'exiting', + 'trolleyLeaving']), + State.State('trolleyLeaving', + self.enterTrolleyLeaving, + self.exitTrolleyLeaving, [ + 'final']), + State.State('exiting', + self.enterExiting, + self.exitExiting, [ + 'final']), + State.State('final', + self.enterFinal, + self.exitFinal, [ + 'start'])], + 'start', 'final') + self.parentFSM = parentFSM + return None + + def load(self): + self.parentFSM.getStateNamed('golfKartBlock').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.parentFSM.getStateNamed('trolley').removeChild(self.fsm) + del self.fsm + del self.parentFSM + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + + def enter(self): + self.fsm.enterInitialState() + if base.localAvatar.hp > 0: + messenger.send('enterGolfKartOK_%d' % self.golfCourse) + self.fsm.request('requestBoard') + else: + self.fsm.request('trolleyHFA') + return None + + def exit(self): + self.ignoreAll() + return None + + def enterStart(self): + return None + + def exitStart(self): + return None + + def enterTrolleyHFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyHFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyHFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def enterTrolleyTFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyTFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyTFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def __handleNoTrolleyAck(self): + ntbDoneStatus = self.noTrolleyBox.doneStatus + if ntbDoneStatus == 'ok': + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error('Unrecognized doneStatus: ' + str(ntbDoneStatus)) + + def enterRequestBoard(self): + return None + + def handleRejectBoard(self): + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + + def exitRequestBoard(self): + return None + + def enterBoarding(self, nodePath): + camera.wrtReparentTo(nodePath) + heading = PythonUtil.fitDestAngle2Src(camera.getH(nodePath), 180) + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(0, 18, 8), Point3(heading, -10, 0)) + self.cameraBoardTrack.start() + return None + + def exitBoarding(self): + self.ignore('boardedTrolley') + return None + + def enterBoarded(self): + self.enableExitButton() + return None + + def exitBoarded(self): + self.cameraBoardTrack.finish() + self.disableExitButton() + return None + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.TrolleyHopOff, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit')) + return + + def disableExitButton(self): + self.exitButton.destroy() + + def enterRequestExit(self): + messenger.send('trolleyExitButton') + return None + + def exitRequestExit(self): + return None + + def enterTrolleyLeaving(self): + self.acceptOnce('playMinigame', self.handlePlayMinigame) + self.acceptOnce('playGolf', self.handlePlayGolf) + return None + + def handlePlayMinigame(self, zoneId, minigameId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'minigame' + doneStatus['zoneId'] = zoneId + doneStatus['minigameId'] = minigameId + messenger.send(self.doneEvent, [doneStatus]) + + def handlePlayGolf(self, zoneId, courseId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'golfcourse' + doneStatus['zoneId'] = zoneId + doneStatus['courseId'] = courseId + messenger.send(self.doneEvent, [doneStatus]) + + def exitTrolleyLeaving(self): + self.ignore('playMinigame') + taskMgr.remove('leavingCamera') + return None + + def enterExiting(self): + return None + + def handleOffTrolley(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + return None + + def exitExiting(self): + return None + + def enterFinal(self): + return None + + def exitFinal(self): + return None diff --git a/toontown/safezone/MMPlayground.py b/toontown/safezone/MMPlayground.py new file mode 100644 index 0000000..c5a195d --- /dev/null +++ b/toontown/safezone/MMPlayground.py @@ -0,0 +1,49 @@ +from pandac.PandaModules import * +import Playground +import random +from direct.fsm import ClassicFSM, State +from direct.actor import Actor +from toontown.toonbase import ToontownGlobals + +class MMPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.activityFsm = ClassicFSM.ClassicFSM('Activity', [State.State('off', self.enterOff, self.exitOff, ['OnPiano']), State.State('OnPiano', self.enterOnPiano, self.exitOnPiano, ['off'])], 'off', 'off') + self.activityFsm.enterInitialState() + + def load(self): + Playground.Playground.load(self) + + def unload(self): + del self.activityFsm + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + + def exit(self): + Playground.Playground.exit(self) + + def handleBookClose(self): + Playground.Playground.handleBookClose(self) + + def teleportInDone(self): + Playground.Playground.teleportInDone(self) + + def enterOff(self): + return None + + def exitOff(self): + return None + + def enterOnPiano(self): + base.localAvatar.b_setParent(ToontownGlobals.SPMinniesPiano) + + def exitOnPiano(self): + base.localAvatar.b_setParent(ToontownGlobals.SPRender) + + def showPaths(self): + from toontown.classicchars import CCharPaths + from toontown.toonbase import TTLocalizer + self.showPathPoints(CCharPaths.getPaths(TTLocalizer.Minnie)) diff --git a/toontown/safezone/MMSafeZoneLoader.py b/toontown/safezone/MMSafeZoneLoader.py new file mode 100644 index 0000000..c6aa8e7 --- /dev/null +++ b/toontown/safezone/MMSafeZoneLoader.py @@ -0,0 +1,28 @@ +from pandac.PandaModules import * +import SafeZoneLoader +import MMPlayground +from toontown.toonbase import ToontownGlobals + +class MMSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = MMPlayground.MMPlayground + self.musicFile = 'phase_6/audio/bgm/MM_nbrhood.mid' + self.activityMusicFile = 'phase_6/audio/bgm/MM_SZ_activity.mid' + self.dnaFile = 'phase_6/dna/minnies_melody_land_sz.dna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_MM_sz.dna' + + def load(self): + print 'loading MM safezone' + SafeZoneLoader.SafeZoneLoader.load(self) + self.piano = self.geom.find('**/center_icon') + if self.piano.isEmpty(): + self.notify.error('Piano not found') + else: + hq = self.geom.find('**/*toon_landmark_hqMM_DNARoot') + hq.wrtReparentTo(self.piano) + + def unload(self): + SafeZoneLoader.SafeZoneLoader.unload(self) + del self.piano diff --git a/toontown/safezone/MMTreasurePlannerAI.py b/toontown/safezone/MMTreasurePlannerAI.py new file mode 100644 index 0000000..d89184b --- /dev/null +++ b/toontown/safezone/MMTreasurePlannerAI.py @@ -0,0 +1,31 @@ +from toontown.toonbase.ToontownGlobals import * +import RegenTreasurePlannerAI, DistributedMMTreasureAI + +class MMTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + + def __init__(self, zoneId): + self.healAmount = 10 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedMMTreasureAI.DistributedMMTreasureAI, 'MMTreasurePlanner', 20, 2) + return None + + def initSpawnPoints(self): + self.spawnPoints = [(118, -39, 3.3), + (118, 1, 3.3), + (112, -22, 0.8), + (108, -74, -4.5), + (110, -65, -4.5), + (102, 23.5, -4.5), + (60, -115, 6.5), + (-5, -115, 6.5), + (-64, -77, 6.5), + (-77, -44, 6.5), + (-76, 3, 6.5), + (44, 76, 6.5), + (136, -96, -13.5), + (85, -6.7, -13.5), + (60, -95, -14.5), + (72, 60, -13.5), + (-55, -23, -14.5), + (-21, 47, -14.5), + (-24, -75, -14.5)] + return self.spawnPoints diff --git a/toontown/safezone/OZPlayground.py b/toontown/safezone/OZPlayground.py new file mode 100644 index 0000000..1166e42 --- /dev/null +++ b/toontown/safezone/OZPlayground.py @@ -0,0 +1,182 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +import Playground +from toontown.launcher import DownloadForceAcknowledge +from toontown.building import Elevator +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from toontown.racing import RaceGlobals +from direct.fsm import State +from toontown.safezone import PicnicBasket +from toontown.safezone import GolfKart +from direct.task.Task import Task + +class OZPlayground(Playground.Playground): + waterLevel = -0.53 + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + self.parentFSM = parentFSM + self.picnicBasketBlockDoneEvent = 'picnicBasketBlockDone' + self.cameraSubmerged = -1 + self.toonSubmerged = -1 + self.fsm.addState(State.State('picnicBasketBlock', self.enterPicnicBasketBlock, self.exitPicnicBasketBlock, ['walk'])) + state = self.fsm.getStateNamed('walk') + state.addTransition('picnicBasketBlock') + self.picnicBasketDoneEvent = 'picnicBasketDone' + + def load(self): + Playground.Playground.load(self) + + def unload(self): + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('oz-check-toon-underwater') + taskMgr.remove('oz-check-cam-underwater') + self.loader.hood.setNoFog() + + def doRequestLeave(self, requestStatus): + self.fsm.request('trialerFA', [requestStatus]) + + def enterDFA(self, requestStatus): + doneEvent = 'dfaDoneEvent' + self.accept(doneEvent, self.enterDFACallback, [requestStatus]) + self.dfa = DownloadForceAcknowledge.DownloadForceAcknowledge(doneEvent) + if requestStatus['hoodId'] == ToontownGlobals.MyEstate: + self.dfa.enter(base.cr.hoodMgr.getPhaseFromHood(ToontownGlobals.MyEstate)) + else: + self.dfa.enter(5) + + def enterStart(self): + self.cameraSubmerged = 0 + self.toonSubmerged = 0 + taskMgr.add(self.__checkToonUnderwater, 'oz-check-toon-underwater') + taskMgr.add(self.__checkCameraUnderwater, 'oz-check-cam-underwater') + + def __checkCameraUnderwater(self, task): + if camera.getZ(render) < self.waterLevel: + self.__submergeCamera() + else: + self.__emergeCamera() + return Task.cont + + def __checkToonUnderwater(self, task): + if base.localAvatar.getZ() < -4.0: + self.__submergeToon() + else: + self.__emergeToon() + return Task.cont + + def __submergeCamera(self): + if self.cameraSubmerged == 1: + return + self.loader.hood.setUnderwaterFog() + base.playSfx(self.loader.underwaterSound, looping=1, volume=0.8) + self.cameraSubmerged = 1 + self.walkStateData.setSwimSoundAudible(1) + + def __emergeCamera(self): + if self.cameraSubmerged == 0: + return + self.loader.hood.setNoFog() + self.loader.underwaterSound.stop() + self.cameraSubmerged = 0 + self.walkStateData.setSwimSoundAudible(0) + + def __submergeToon(self): + if self.toonSubmerged == 1: + return + base.playSfx(self.loader.submergeSound) + if base.config.GetBool('disable-flying-glitch') == 0: + self.fsm.request('walk') + self.walkStateData.fsm.request('swimming', [self.loader.swimSound]) + pos = base.localAvatar.getPos(render) + base.localAvatar.d_playSplashEffect(pos[0], pos[1], self.waterLevel) + self.toonSubmerged = 1 + + def __emergeToon(self): + if self.toonSubmerged == 0: + return + self.walkStateData.fsm.request('walking') + self.toonSubmerged = 0 + + def enterTeleportIn(self, requestStatus): + reason = requestStatus.get('reason') + if reason == RaceGlobals.Exit_Barrier: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeout, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_Slow: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RacerTooSlow, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + elif reason == RaceGlobals.Exit_BarrierNoRefund: + requestStatus['nextState'] = 'popup' + self.dialog = TTDialog.TTDialog(text=TTLocalizer.KartRace_RaceTimeoutNoRefund, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + self.toonSubmerged = -1 + taskMgr.remove('oz-check-toon-underwater') + Playground.Playground.enterTeleportIn(self, requestStatus) + + def teleportInDone(self): + self.toonSubmerged = -1 + taskMgr.add(self.__checkToonUnderwater, 'oz-check-toon-underwater') + Playground.Playground.teleportInDone(self) + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def enterPicnicBasketBlock(self, picnicBasket): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.cantLeaveGame = 1 + self.accept(self.picnicBasketDoneEvent, self.handlePicnicBasketDone) + self.trolley = PicnicBasket.PicnicBasket(self, self.fsm, self.picnicBasketDoneEvent, picnicBasket.getDoId(), picnicBasket.seatNumber) + self.trolley.load() + self.trolley.enter() + + def exitPicnicBasketBlock(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.trolleyDoneEvent) + self.trolley.unload() + self.trolley.exit() + del self.trolley + + def detectedPicnicTableSphereCollision(self, picnicBasket): + self.fsm.request('picnicBasketBlock', [picnicBasket]) + + def handleStartingBlockDone(self, doneStatus): + self.notify.debug('handling StartingBlock done event') + where = doneStatus['where'] + if where == 'reject': + self.fsm.request('walk') + elif where == 'exit': + self.fsm.request('walk') + elif where == 'racetrack': + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleStartingBlockDone') + + def handlePicnicBasketDone(self, doneStatus): + self.notify.debug('handling picnic basket done event') + mode = doneStatus['mode'] + if mode == 'reject': + self.fsm.request('walk') + elif mode == 'exit': + self.fsm.request('walk') + else: + self.notify.error('Unknown mode: ' + mode + ' in handlePicnicBasketDone') + + def showPaths(self): + from toontown.classicchars import CCharPaths + from toontown.toonbase import TTLocalizer + self.showPathPoints(CCharPaths.getPaths(TTLocalizer.Chip)) diff --git a/toontown/safezone/OZSafeZoneLoader.py b/toontown/safezone/OZSafeZoneLoader.py new file mode 100644 index 0000000..429cce2 --- /dev/null +++ b/toontown/safezone/OZSafeZoneLoader.py @@ -0,0 +1,383 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from pandac.PandaModules import * +from otp.avatar import Avatar +from toontown.hood import ZoneUtil +from toontown.launcher import DownloadForceAcknowledge +from toontown.safezone.SafeZoneLoader import SafeZoneLoader +from toontown.safezone.OZPlayground import OZPlayground +from direct.actor import Actor +from direct.interval.IntervalGlobal import * +import random +from toontown.distributed import DelayDelete +from direct.distributed.ClockDelta import * +from otp.otpbase import OTPGlobals +import copy +from toontown.effects import Bubbles +import random +if (__debug__): + import pdb + +class OZSafeZoneLoader(SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.musicFile = 'phase_6/audio/bgm/OZ_SZ.mid' + self.activityMusicFile = 'phase_6/audio/bgm/GS_KartShop.mid' + self.dnaFile = 'phase_6/dna/outdoor_zone_sz.dna' + self.safeZoneStorageDNAFile = 'phase_6/dna/storage_OZ_sz.dna' + self.__toonTracks = {} + del self.fsm + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone', 'golfcourse']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior', 'golfcourse']), + State.State('golfcourse', self.enterGolfCourse, self.exitGolfCourse, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + + def load(self): + self.done = 0 + self.geyserTrack = None + SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + self.underwaterSound = base.loadSfx('phase_4/audio/sfx/AV_ambient_water.mp3') + self.swimSound = base.loadSfx('phase_4/audio/sfx/AV_swim_single_stroke.mp3') + self.submergeSound = base.loadSfx('phase_5.5/audio/sfx/AV_jump_in_water.mp3') + geyserPlacer = self.geom.find('**/geyser*') + waterfallPlacer = self.geom.find('**/waterfall*') + binMgr = CullBinManager.getGlobalPtr() + binMgr.addBin('water', CullBinManager.BTFixed, 29) + binMgr = CullBinManager.getGlobalPtr() + self.water = self.geom.find('**/water1*') + self.water.setTransparency(1) + self.water.setColorScale(1.0, 1.0, 1.0, 1.0) + self.water.setBin('water', 51, 1) + pool = self.geom.find('**/pPlane5*') + pool.setTransparency(1) + pool.setColorScale(1.0, 1.0, 1.0, 1.0) + pool.setBin('water', 50, 1) + self.geyserModel = loader.loadModel('phase_6/models/golf/golf_geyser_model') + self.geyserSound = loader.loadSfx('phase_6/audio/sfx/OZ_Geyser.mp3') + self.geyserSoundInterval = SoundInterval(self.geyserSound, node=geyserPlacer, listenerNode=base.camera, seamlessLoop=False, volume=1.0, cutOff=120) + self.geyserSoundNoToon = loader.loadSfx('phase_6/audio/sfx/OZ_Geyser_No_Toon.mp3') + self.geyserSoundNoToonInterval = SoundInterval(self.geyserSoundNoToon, node=geyserPlacer, listenerNode=base.camera, seamlessLoop=False, volume=1.0, cutOff=120) + if self.geyserModel: + self.geyserActor = Actor.Actor(self.geyserModel) + self.geyserActor.loadAnims({'idle': 'phase_6/models/golf/golf_geyser'}) + self.geyserActor.reparentTo(render) + self.geyserActor.setPlayRate(8.6, 'idle') + self.geyserActor.loop('idle') + self.geyserActor.setDepthWrite(0) + self.geyserActor.setTwoSided(True, 11) + self.geyserActor.setColorScale(1.0, 1.0, 1.0, 1.0) + self.geyserActor.setBin('fixed', 0) + mesh = self.geyserActor.find('**/mesh_tide1') + joint = self.geyserActor.find('**/uvj_WakeWhiteTide1') + mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.geyserActor) + self.geyserActor.setPos(geyserPlacer.getPos()) + self.geyserActor.setZ(geyserPlacer.getZ() - 100.0) + self.geyserPos = geyserPlacer.getPos() + self.geyserPlacer = geyserPlacer + self.startGeyser() + base.sfxPlayer.setCutoffDistance(160) + self.geyserPoolSfx = loader.loadSfx('phase_6/audio/sfx/OZ_Geyser_BuildUp_Loop.wav') + self.geyserPoolSoundInterval = SoundInterval(self.geyserPoolSfx, node=self.geyserPlacer, listenerNode=base.camera, seamlessLoop=True, volume=1.0, cutOff=120) + self.geyserPoolSoundInterval.loop() + self.bubbles = Bubbles.Bubbles(self.geyserPlacer, render) + self.bubbles.renderParent.setDepthWrite(0) + self.bubbles.start() + self.collBase = render.attachNewNode('collisionBase') + self.geyserCollSphere = CollisionSphere(0, 0, 0, 7.5) + self.geyserCollSphere.setTangible(1) + self.geyserCollNode = CollisionNode('barrelSphere') + self.geyserCollNode.setIntoCollideMask(OTPGlobals.WallBitmask) + self.geyserCollNode.addSolid(self.geyserCollSphere) + self.geyserNodePath = self.collBase.attachNewNode(self.geyserCollNode) + self.geyserNodePath.setPos(self.geyserPos[0], self.geyserPos[1], self.geyserPos[2] - 100.0) + self.waterfallModel = loader.loadModel('phase_6/models/golf/golf_waterfall_model') + if self.waterfallModel: + self.waterfallActor = Actor.Actor(self.waterfallModel) + self.waterfallActor.loadAnims({'idle': 'phase_6/models/golf/golf_waterfall'}) + self.waterfallActor.reparentTo(render) + self.waterfallActor.setPlayRate(3.5, 'idle') + self.waterfallActor.loop('idle') + mesh = self.waterfallActor.find('**/mesh_tide1') + joint = self.waterfallActor.find('**/uvj_WakeWhiteTide1') + mesh.setTexProjector(mesh.findTextureStage('default'), joint, self.waterfallActor) + self.waterfallActor.setPos(waterfallPlacer.getPos()) + self.accept('clientLogout', self._handleLogout) + return + + def exit(self): + self.clearToonTracks() + SafeZoneLoader.exit(self) + self.ignore('clientLogout') + + def startGeyser(self, task = None): + if hasattr(base.cr, 'DTimer') and base.cr.DTimer: + self.geyserCycleTime = 20.0 + useTime = base.cr.DTimer.getTime() + timeToNextGeyser = 20.0 - useTime % 20.0 + taskMgr.doMethodLater(timeToNextGeyser, self.doGeyser, 'geyser Task') + else: + taskMgr.doMethodLater(5.0, self.startGeyser, 'start geyser Task') + + def doGeyser(self, task = None): + if not self.done: + self.setGeyserAnim() + useTime = base.cr.DTimer.getTime() + timeToNextGeyser = 20.0 - useTime % 20.0 + taskMgr.doMethodLater(timeToNextGeyser, self.doGeyser, 'geyser Task') + return task.done + + def restoreLocal(self, task = None): + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.collisionsOn() + base.localAvatar.dropShadow.show() + + def restoreRemote(self, remoteAv, task = None): + if remoteAv in Avatar.Avatar.ActiveAvatars: + remoteAv.startSmooth() + remoteAv.dropShadow.show() + + def setGeyserAnim(self, task = None): + if self.done: + return + maxSize = 0.4 * random.random() + 0.75 + time = 1.0 + self.geyserTrack = Sequence() + upPos = Vec3(self.geyserPos[0], self.geyserPos[1], self.geyserPos[2]) + downPos = Vec3(self.geyserPos[0], self.geyserPos[1], self.geyserPos[2] - 8.0) + avList = copy.copy(Avatar.Avatar.ActiveAvatars) + avList.append(base.localAvatar) + playSound = 0 + for av in avList: + distance = self.geyserPlacer.getDistance(av) + if distance < 7.0: + place = base.cr.playGame.getPlace() + local = 0 + avPos = av.getPos() + upToon = Vec3(avPos[0], avPos[1], maxSize * self.geyserPos[2] + 40.0) + midToon = Vec3(avPos[0], avPos[1], maxSize * self.geyserPos[2] + 30.0) + downToon = Vec3(avPos[0], avPos[1], self.geyserPos[2]) + returnPoints = [(7, 7), + (8, 0), + (-8, 3), + (-7, 7), + (3, -7), + (0, 8), + (-10, 0), + (8, -3), + (5, 8), + (-8, 5), + (-1, 7)] + pick = int((float(av.doId) - 11.0) / 13.0 % len(returnPoints)) + returnChoice = returnPoints[pick] + toonReturn = Vec3(self.geyserPos[0] + returnChoice[0], self.geyserPos[1] + returnChoice[1], self.geyserPos[2] - 1.5) + topTrack = Sequence() + av.dropShadow.hide() + playSound = 1 + if av == base.localAvatar: + base.cr.playGame.getPlace().setState('fishing') + base.localAvatar.setTeleportAvailable(0) + base.localAvatar.collisionsOff() + local = 1 + else: + topTrack.delayDeletes = [DelayDelete.DelayDelete(av, 'OZSafeZoneLoader.setGeyserAnim')] + av.stopSmooth() + animTrack = Parallel() + toonTrack = Sequence() + toonTrack.append(Wait(0.5)) + animTrack.append(ActorInterval(av, 'jump-idle', loop=1, endTime=11.5 * time)) + animTrack.append(ActorInterval(av, 'neutral', loop=0, endTime=0.25 * time)) + holder = render.attachNewNode('toon hold') + base.holder = holder + toonPos = av.getPos(render) + toonHpr = av.getHpr(render) + print 'av Pos %s' % av.getPos() + base.toonPos = toonPos + holder.setPos(toonPos) + av.reparentTo(holder) + av.setPos(0, 0, 0) + lookAt = 180 + toonH = (lookAt + toonHpr[0]) % 360 + newHpr = Vec3(toonH, toonHpr[1], toonHpr[2]) + if toonH < 180: + lookIn = Vec3(0 + lookAt, -30, 0) + else: + lookIn = Vec3(360 + lookAt, -30, 0) + print 'Camera Hprs toon %s; lookIn %s; final %s' % (newHpr, lookIn, lookIn - newHpr) + if local == 1: + camPosOriginal = camera.getPos() + camHprOriginal = camera.getHpr() + camParentOriginal = camera.getParent() + cameraPivot = holder.attachNewNode('camera pivot') + chooseHeading = random.choice([-10.0, 15.0, 40.0]) + cameraPivot.setHpr(chooseHeading, -20.0, 0.0) + cameraArm = cameraPivot.attachNewNode('camera arm') + cameraArm.setPos(0.0, -23.0, 3.0) + camPosStart = Point3(0.0, 0.0, 0.0) + camHprStart = Vec3(0.0, 0.0, 0.0) + self.changeCamera(cameraArm, camPosStart, camHprStart) + cameraTrack = Sequence() + cameraTrack.append(Wait(11.0 * time)) + cameraTrack.append(Func(self.changeCamera, camParentOriginal, camPosOriginal, camHprOriginal)) + cameraTrack.start() + moveTrack = Sequence() + moveTrack.append(Wait(0.5)) + moveTrack.append(LerpPosInterval(holder, 3.0 * time, pos=upToon, startPos=downToon, blendType='easeOut')) + moveTrack.append(LerpPosInterval(holder, 2.0 * time, pos=midToon, startPos=upToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 1.0 * time, pos=upToon, startPos=midToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 2.0 * time, pos=midToon, startPos=upToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 1.0 * time, pos=upToon, startPos=midToon, blendType='easeInOut')) + moveTrack.append(LerpPosInterval(holder, 2.5 * time, pos=toonReturn, startPos=upToon, blendType='easeIn')) + animTrack.append(moveTrack) + animTrack.append(toonTrack) + topTrack.append(animTrack) + topTrack.append(Func(av.setPos, toonReturn)) + topTrack.append(Func(av.reparentTo, render)) + topTrack.append(Func(holder.remove)) + if local == 1: + topTrack.append(Func(self.restoreLocal)) + else: + topTrack.append(Func(self.restoreRemote, av)) + topTrack.append(Func(self.clearToonTrack, av.doId)) + self.storeToonTrack(av.doId, topTrack) + topTrack.start() + + self.geyserTrack.append(Func(self.doPrint, 'geyser start')) + self.geyserTrack.append(Func(self.geyserNodePath.setPos, self.geyserPos[0], self.geyserPos[1], self.geyserPos[2])) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 2.0 * time, 0.75, 0.01), LerpPosInterval(self.geyserActor, 2.0 * time, pos=downPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, time, maxSize, 0.75), LerpPosInterval(self.geyserActor, time, pos=upPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 2.0 * time, 0.75, maxSize), LerpPosInterval(self.geyserActor, 2.0 * time, pos=downPos, startPos=upPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, time, maxSize, 0.75), LerpPosInterval(self.geyserActor, time, pos=upPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 2.0 * time, 0.75, maxSize), LerpPosInterval(self.geyserActor, 2.0 * time, pos=downPos, startPos=upPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, time, maxSize, 0.75), LerpPosInterval(self.geyserActor, time, pos=upPos, startPos=downPos))) + self.geyserTrack.append(Parallel(LerpScaleInterval(self.geyserActor, 4.0 * time, 0.01, maxSize), LerpPosInterval(self.geyserActor, 4.0 * time, pos=downPos, startPos=upPos))) + self.geyserTrack.append(Func(self.geyserNodePath.setPos, self.geyserPos[0], self.geyserPos[1], self.geyserPos[2] - 100.0)) + self.geyserTrack.append(Func(self.doPrint, 'geyser end')) + self.geyserTrack.start() + if playSound: + self.geyserSoundInterval.start() + else: + self.geyserSoundNoToonInterval.start() + + def changeCamera(self, newParent, newPos, newHpr): + camera.reparentTo(newParent) + camera.setPosHpr(newPos, newHpr) + + def doPrint(self, thing): + return 0 + print thing + + def unload(self): + del self.birdSound + SafeZoneLoader.unload(self) + self.done = 1 + self.collBase.remove() + if self.geyserTrack: + self.geyserTrack.finish() + self.geyserTrack = None + self.geyserActor.cleanup() + self.geyserModel.remove() + self.waterfallActor.cleanup() + self.waterfallModel.remove() + self.bubbles.destroy() + del self.bubbles + self.geyserPoolSoundInterval.finish() + self.geyserPoolSfx.stop() + self.geyserPoolSfx = None + self.geyserPoolSoundInterval = None + self.geyserSoundInterval.finish() + self.geyserSound.stop() + self.geyserSoundInterval = None + self.geyserSound = None + self.geyserSoundNoToonInterval.finish() + self.geyserSoundNoToon.stop() + self.geyserSoundNoToonInterval = None + self.geyserSoundNoToon = None + return + + def enterPlayground(self, requestStatus): + self.playgroundClass = OZPlayground + SafeZoneLoader.enterPlayground(self, requestStatus) + + def exitPlayground(self): + taskMgr.remove('titleText') + self.hood.hideTitleText() + SafeZoneLoader.exitPlayground(self) + self.playgroundClass = None + return + + def handlePlaygroundDone(self): + status = self.place.doneStatus + self.doneStatus = status + messenger.send(self.doneEvent) + + def enteringARace(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enteringAGolfCourse(self, status): + if not status['where'] == 'golfcourse': + return 0 + if ZoneUtil.isDynamicZone(status['zoneId']): + return status['hoodId'] == self.hood.hoodId + else: + return ZoneUtil.getHoodId(status['zoneId']) == self.hood.hoodId + + def enterGolfCourse(self, requestStatus): + if requestStatus.has_key('curseId'): + self.golfCourseId = requestStatus['courseId'] + else: + self.golfCourseId = 0 + self.accept('raceOver', self.handleRaceOver) + self.accept('leavingGolf', self.handleLeftGolf) + base.transitions.irisOut(t=0.2) + + def exitGolfCourse(self): + del self.golfCourseId + + def handleRaceOver(self): + print 'you done!!' + + def handleLeftGolf(self): + req = {'loader': 'safeZoneLoader', + 'where': 'playground', + 'how': 'teleportIn', + 'zoneId': 6000, + 'hoodId': 6000, + 'shardId': None} + self.fsm.request('quietZone', [req]) + return + + def _handleLogout(self): + self.clearToonTracks() + + 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() + DelayDelete.cleanupDelayDeletes(oldTrack) + 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/safezone/OZTreasurePlannerAI.py b/toontown/safezone/OZTreasurePlannerAI.py new file mode 100644 index 0000000..ea0f77f --- /dev/null +++ b/toontown/safezone/OZTreasurePlannerAI.py @@ -0,0 +1,26 @@ +from toontown.toonbase.ToontownGlobals import * +import RegenTreasurePlannerAI, DistributedOZTreasureAI + +class OZTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + + def __init__(self, zoneId): + self.healAmount = 3 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedOZTreasureAI.DistributedOZTreasureAI, 'OZTreasurePlanner', 20, 5) + + def initSpawnPoints(self): + self.spawnPoints = [(-156.9, -118.9, 0.025), + (-35.6, 86.0, 1.25), + (116.8, 10.8, 0.104), + (-35, 145.7, 0.025), + (-198.8, -45.1, 0.025), + (-47.1, -25.5, 0.809), + (59.15, 34.8, 1.767), + (-81.02, -72.2, 0.026), + (-167.9, 124.5, 0.025), + (-226.7, -27.6, 0.025), + (-16.0, -108.9, 0.025), + (18.0, 58.5, 5.919), + (91.4, 127.8, 0.025), + (-86.5, -75.9, 0.025), + (-48.751, -32.3, 1.143)] + return self.spawnPoints diff --git a/toontown/safezone/PicnicBasket.py b/toontown/safezone/PicnicBasket.py new file mode 100644 index 0000000..93aea6a --- /dev/null +++ b/toontown/safezone/PicnicBasket.py @@ -0,0 +1,222 @@ +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.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil + +class PicnicBasket(StateData.StateData): + + def __init__(self, safeZone, parentFSM, doneEvent, tableNumber, seatNumber): + StateData.StateData.__init__(self, doneEvent) + self.tableNumber = tableNumber + self.seatNumber = seatNumber + self.fsm = ClassicFSM.ClassicFSM('PicnicBasket', [ + State.State('start', + self.enterStart, + self.exitStart, [ + 'requestBoard', + 'trolleyHFA', + 'trolleyTFA']), + State.State('trolleyHFA', + self.enterTrolleyHFA, + self.exitTrolleyHFA, [ + 'final']), + State.State('trolleyTFA', + self.enterTrolleyTFA, + self.exitTrolleyTFA, [ + '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', + 'trolleyLeaving', + 'final', + 'exiting']), + State.State('requestExit', + self.enterRequestExit, + self.exitRequestExit, [ + 'exiting', + 'trolleyLeaving']), + State.State('trolleyLeaving', + self.enterTrolleyLeaving, + self.exitTrolleyLeaving, [ + 'final']), + State.State('exiting', + self.enterExiting, + self.exitExiting, [ + 'final']), + State.State('final', + self.enterFinal, + self.exitFinal, [ + 'start'])], + 'start', 'final') + self.parentFSM = parentFSM + return None + + def load(self): + self.parentFSM.getStateNamed('picnicBasketBlock').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.parentFSM.getStateNamed('trolley').removeChild(self.fsm) + del self.fsm + del self.parentFSM + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + + def enter(self): + self.fsm.enterInitialState() + if base.localAvatar.hp > 0: + messenger.send('enterPicnicTableOK_%d_%d' % (self.tableNumber, self.seatNumber)) + self.fsm.request('requestBoard') + else: + self.fsm.request('trolleyHFA') + return None + + def exit(self): + self.ignoreAll() + return None + + def enterStart(self): + return None + + def exitStart(self): + return None + + def enterTrolleyHFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyHFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyHFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def enterTrolleyTFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyTFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyTFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def __handleNoTrolleyAck(self): + ntbDoneStatus = self.noTrolleyBox.doneStatus + if ntbDoneStatus == 'ok': + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error('Unrecognized doneStatus: ' + str(ntbDoneStatus)) + + def enterRequestBoard(self): + return None + + def handleRejectBoard(self): + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + + def exitRequestBoard(self): + return None + + def enterBoarding(self, nodePath, side): + camera.wrtReparentTo(nodePath) + heading = PythonUtil.fitDestAngle2Src(camera.getH(nodePath), 90 * side) + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(14.4072 * side, 0, 3.8667), Point3(heading, -15, 0)) + self.cameraBoardTrack.start() + return None + + def exitBoarding(self): + self.ignore('boardedTrolley') + return None + + def enterBoarded(self): + self.enableExitButton() + return None + + def exitBoarded(self): + self.cameraBoardTrack.finish() + self.disableExitButton() + return None + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.TrolleyHopOff, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=0.8, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit')) + return + + def disableExitButton(self): + self.exitButton.destroy() + + def enterRequestExit(self): + messenger.send('trolleyExitButton') + return None + + def exitRequestExit(self): + return None + + def enterTrolleyLeaving(self): + self.acceptOnce('playMinigame', self.handlePlayMinigame) + self.acceptOnce('picnicDone', self.handlePicnicDone) + return None + + def handlePlayMinigame(self, zoneId, minigameId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'minigame' + doneStatus['zoneId'] = zoneId + doneStatus['minigameId'] = minigameId + messenger.send(self.doneEvent, [doneStatus]) + + def handlePicnicDone(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + + def exitTrolleyLeaving(self): + self.ignore('playMinigame') + taskMgr.remove('leavingCamera') + return self.notify.debug('handling golf kart done event') + + def enterExiting(self): + return None + + def handleOffTrolley(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + return None + + def exitExiting(self): + return None + + def enterFinal(self): + return None + + def exitFinal(self): + return None diff --git a/toontown/safezone/Playground.py b/toontown/safezone/Playground.py new file mode 100644 index 0000000..51753f0 --- /dev/null +++ b/toontown/safezone/Playground.py @@ -0,0 +1,668 @@ +from direct.interval.IntervalGlobal import * +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 direct.task import Task +from toontown.toon import DeathForceAcknowledge +from toontown.toon import HealthForceAcknowledge +from toontown.tutorial import TutorialForceAcknowledge +from toontown.toon import NPCForceAcknowledge +from toontown.trolley import Trolley +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toon.Toon import teleportDebug +from toontown.toonbase import TTLocalizer +from direct.gui import DirectLabel +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.quest import Quests + +class Playground(Place.Place): + notify = DirectNotifyGlobal.directNotify.newCategory('Playground') + + def __init__(self, loader, parentFSM, doneEvent): + Place.Place.__init__(self, loader, doneEvent) + self.tfaDoneEvent = 'tfaDoneEvent' + self.fsm = ClassicFSM.ClassicFSM('Playground', [ + State.State('start', + self.enterStart, + self.exitStart, [ + 'walk', + 'deathAck', + 'doorIn', + 'tunnelIn']), + State.State('walk', + self.enterWalk, + self.exitWalk, [ + 'drive', + 'sit', + 'stickerBook', + 'TFA', + 'DFA', + 'trialerFA', + 'trolley', + 'final', + 'doorOut', + 'options', + 'quest', + 'purchase', + 'stopped', + 'fishing']), + State.State('stickerBook', + self.enterStickerBook, + self.exitStickerBook, [ + 'walk', + 'DFA', + 'TFA', + 'trolley', + 'final', + 'doorOut', + 'quest', + 'purchase', + 'stopped', + 'fishing', + 'trialerFA']), + State.State('sit', + self.enterSit, + self.exitSit, [ + 'walk', + 'DFA', + 'trialerFA']), + State.State('drive', + self.enterDrive, + self.exitDrive, [ + 'walk', + 'DFA', + 'trialerFA']), + State.State('trolley', + self.enterTrolley, + self.exitTrolley, [ + 'walk']), + State.State('doorIn', + self.enterDoorIn, + self.exitDoorIn, [ + 'walk']), + State.State('doorOut', + self.enterDoorOut, + self.exitDoorOut, [ + 'walk']), + State.State('TFA', + self.enterTFA, + self.exitTFA, [ + 'TFAReject', + 'DFA']), + State.State('TFAReject', + self.enterTFAReject, + self.exitTFAReject, [ + 'walk']), + 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', + 'NPCFA', + 'HFA']), + State.State('DFAReject', + self.enterDFAReject, + self.exitDFAReject, [ + 'walk']), + State.State('NPCFA', + self.enterNPCFA, + self.exitNPCFA, [ + 'NPCFAReject', + 'HFA']), + 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('deathAck', + self.enterDeathAck, + self.exitDeathAck, [ + 'teleportIn']), + State.State('teleportIn', + self.enterTeleportIn, + self.exitTeleportIn, [ + 'walk', + 'popup']), + State.State('popup', + self.enterPopup, + self.exitPopup, [ + 'walk']), + State.State('teleportOut', + self.enterTeleportOut, + self.exitTeleportOut, [ + 'deathAck', + 'teleportIn']), + State.State('died', + self.enterDied, + self.exitDied, [ + 'final']), + State.State('tunnelIn', + self.enterTunnelIn, + self.exitTunnelIn, [ + 'walk']), + State.State('tunnelOut', + self.enterTunnelOut, + self.exitTunnelOut, [ + 'final']), + State.State('quest', + self.enterQuest, + self.exitQuest, [ + 'walk']), + State.State('purchase', + self.enterPurchase, + self.exitPurchase, [ + 'walk']), + State.State('stopped', + self.enterStopped, + self.exitStopped, [ + 'walk']), + State.State('fishing', + self.enterFishing, + self.exitFishing, [ + 'walk']), + State.State('final', + self.enterFinal, + self.exitFinal, [ + 'start'])], + 'start', 'final') + self.parentFSM = parentFSM + self.tunnelOriginList = [] + self.trolleyDoneEvent = 'trolleyDone' + self.hfaDoneEvent = 'hfaDoneEvent' + self.npcfaDoneEvent = 'npcfaDoneEvent' + self.dialog = None + self.deathAckBox = None + return + + def enter(self, requestStatus): + self.fsm.enterInitialState() + messenger.send('enterPlayground') + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + for i in self.loader.nodeList: + self.loader.enterAnimatedProps(i) + + self._telemLimiter = TLGatherAllAvs('Playground', RotationLimitToH) + + def __lightDecorationOn__(): + geom = base.cr.playGame.hood.loader.geom + self.loader.hood.halloweenLights = geom.findAllMatches('**/*light*') + self.loader.hood.halloweenLights += geom.findAllMatches('**/*lamp*') + self.loader.hood.halloweenLights += geom.findAllMatches('**/prop_snow_tree*') + for light in self.loader.hood.halloweenLights: + light.setColorScaleOff(0) + + newsManager = base.cr.newsManager + if newsManager: + holidayIds = base.cr.newsManager.getDecorationHolidayId() + if (ToontownGlobals.HALLOWEEN_COSTUMES in holidayIds or ToontownGlobals.SPOOKY_COSTUMES in holidayIds) and self.loader.hood.spookySkyFile: + lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.55, 0.55, 0.65, 1)), Func(self.loader.hood.startSpookySky), Func(__lightDecorationOn__)) + lightsOff.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + NametagGlobals.setMasterArrowsOn(1) + self.zoneId = requestStatus['zoneId'] + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.loader.nodeList, self.zoneId) + how = requestStatus['how'] + if how == 'teleportIn': + how = 'deathAck' + self.fsm.request(how, [requestStatus]) + + def exit(self): + self.ignoreAll() + messenger.send('exitPlayground') + self._telemLimiter.destroy() + del self._telemLimiter + for node in self.tunnelOriginList: + node.removeNode() + + del self.tunnelOriginList + self.loader.geom.reparentTo(hidden) + + def __lightDecorationOff__(): + for light in self.loader.hood.halloweenLights: + light.reparentTo(hidden) + + newsManager = base.cr.newsManager + NametagGlobals.setMasterArrowsOn(0) + for i in self.loader.nodeList: + self.loader.exitAnimatedProps(i) + + self.loader.hood.stopSky() + self.loader.music.stop() + + def load(self): + Place.Place.load(self) + self.parentFSM.getStateNamed('playground').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('playground').removeChild(self.fsm) + del self.parentFSM + del self.fsm + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if self.deathAckBox: + self.deathAckBox.cleanup() + self.deathAckBox = None + TTDialog.cleanupDialog('globalDialog') + self.ignoreAll() + Place.Place.unload(self) + return + + def showTreasurePoints(self, points): + self.hideDebugPointText() + for i in range(len(points)): + p = points[i] + self.showDebugPointText(str(i), p) + + def showDropPoints(self, points): + self.hideDebugPointText() + for i in range(len(points)): + p = points[i] + self.showDebugPointText(str(i), p) + + def showPaths(self): + pass + + def hidePaths(self): + self.hideDebugPointText() + + def showPathPoints(self, paths, waypoints = None): + self.hideDebugPointText() + lines = LineSegs() + lines.setColor(1, 0, 0, 1) + from toontown.classicchars import CCharPaths + for name, pointDef in paths.items(): + self.showDebugPointText(name, pointDef[0]) + for connectTo in pointDef[1]: + toDef = paths[connectTo] + fromP = pointDef[0] + toP = toDef[0] + lines.moveTo(fromP[0], fromP[1], fromP[2] + 2.0) + wpList = CCharPaths.getWayPoints(name, connectTo, paths, waypoints) + for wp in wpList: + lines.drawTo(wp[0], wp[1], wp[2] + 2.0) + self.showDebugPointText('*', wp) + + lines.drawTo(toP[0], toP[1], toP[2] + 2.0) + + self.debugText.attachNewNode(lines.create()) + + def hideDebugPointText(self): + if hasattr(self, 'debugText'): + children = self.debugText.getChildren() + for i in range(children.getNumPaths()): + children[i].removeNode() + + def showDebugPointText(self, text, point): + if not hasattr(self, 'debugText'): + self.debugText = self.loader.geom.attachNewNode('debugText') + self.debugTextNode = TextNode('debugTextNode') + self.debugTextNode.setTextColor(1, 0, 0, 1) + self.debugTextNode.setAlign(TextNode.ACenter) + self.debugTextNode.setFont(ToontownGlobals.getSignFont()) + self.debugTextNode.setText(text) + np = self.debugText.attachNewNode(self.debugTextNode.generate()) + np.setPos(point[0], point[1], point[2]) + np.setScale(4.0) + np.setBillboardPointEye() + + def enterTrolley(self): + base.localAvatar.laffMeter.start() + base.localAvatar.b_setAnimState('off', 1) + base.localAvatar.cantLeaveGame = 1 + self.accept(self.trolleyDoneEvent, self.handleTrolleyDone) + self.trolley = Trolley.Trolley(self, self.fsm, self.trolleyDoneEvent) + self.trolley.load() + self.trolley.enter() + + def exitTrolley(self): + base.localAvatar.laffMeter.stop() + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.trolleyDoneEvent) + self.trolley.unload() + self.trolley.exit() + del self.trolley + + def detectedTrolleyCollision(self): + self.fsm.request('trolley') + + def handleTrolleyDone(self, doneStatus): + self.notify.debug('handling trolley done event') + mode = doneStatus['mode'] + if mode == 'reject': + self.fsm.request('walk') + elif mode == 'exit': + self.fsm.request('walk') + elif mode == 'minigame': + self.doneStatus = {'loader': 'minigame', + 'where': 'minigame', + 'hoodId': self.loader.hood.id, + 'zoneId': doneStatus['zoneId'], + 'shardId': None, + 'minigameId': doneStatus['minigameId']} + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + mode + ' in handleTrolleyDone') + return + + def debugStartMinigame(self, zoneId, minigameId): + self.doneStatus = {'loader': 'minigame', + 'where': 'minigame', + 'hoodId': self.loader.hood.id, + 'zoneId': zoneId, + 'shardId': None, + 'minigameId': minigameId} + messenger.send(self.doneEvent) + return + + def enterTFACallback(self, requestStatus, doneStatus): + self.tfa.exit() + del self.tfa + doneStatusMode = doneStatus['mode'] + if doneStatusMode == 'complete': + self.requestLeave(requestStatus) + elif doneStatusMode == 'incomplete': + self.fsm.request('TFAReject') + else: + self.notify.error('Unknown mode: %s' % doneStatusMode) + + 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 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': + if requestStatus.get('partyHat', 0): + outHow = {'teleportIn': 'tunnelOut'} + else: + 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 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': + self.fsm.request('HFA', [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 enterWalk(self, teleportIn = 0): + if self.deathAckBox: + self.ignore('deathAck') + self.deathAckBox.cleanup() + self.deathAckBox = None + Place.Place.enterWalk(self, teleportIn) + return + + def enterDeathAck(self, requestStatus): + self.deathAckBox = None + self.fsm.request('teleportIn', [requestStatus]) + return + + def exitDeathAck(self): + if self.deathAckBox: + self.ignore('deathAck') + self.deathAckBox.cleanup() + self.deathAckBox = None + return + + def enterTeleportIn(self, requestStatus): + imgScale = 0.25 + if self.dialog: + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + elif base.localAvatar.hp < 1: + requestStatus['nextState'] = 'popup' + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + self.accept('deathAck', self.__handleDeathAck, extraArgs=[requestStatus]) + self.deathAckBox = DeathForceAcknowledge.DeathForceAcknowledge(doneEvent='deathAck') + elif base.localAvatar.hp > 0 and (Quests.avatarHasTrolleyQuest(base.localAvatar) or Quests.avatarHasFirstCogQuest(base.localAvatar) or Quests.avatarHasFriendQuest(base.localAvatar) or Quests.avatarHasPhoneQuest(base.localAvatar) and Quests.avatarHasCompletedPhoneQuest(base.localAvatar)) and self.loader.hood.id == ToontownGlobals.ToontownCentral: + requestStatus['nextState'] = 'popup' + imageModel = loader.loadModel('phase_4/models/gui/tfa_images') + if base.localAvatar.quests[0][0] == Quests.TROLLEY_QUEST_ID: + if not Quests.avatarHasCompletedTrolleyQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralInitialDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage3 + imgNodePath = imageModel.find('**/trolley-dialog-image') + imgPos = (0, 0, 0.04) + imgScale = 0.5 + else: + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage4 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, -0.02) + imgScale = 0.5 + elif base.localAvatar.quests[0][0] == Quests.FIRST_COG_QUEST_ID: + if not Quests.avatarHasCompletedFirstCogQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralTunnelDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage5 + imgNodePath = imageModel.find('**/tunnelSignA') + imgPos = (0, 0, 0.04) + imgScale = 0.5 + else: + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage6 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, 0.05) + imgScale = 0.5 + elif base.localAvatar.quests[0][0] == Quests.FRIEND_QUEST_ID: + if not Quests.avatarHasCompletedFriendQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralInitialDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage7 + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + imgNodePath = gui.find('**/FriendsBox_Closed') + imgPos = (0, 0, 0.04) + imgScale = 1.0 + gui.removeNode() + else: + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage8 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, 0.05) + imgScale = 0.5 + elif base.localAvatar.quests[0][0] == Quests.PHONE_QUEST_ID: + if Quests.avatarHasCompletedPhoneQuest(base.localAvatar): + x, y, z, h, p, r = base.cr.hoodMgr.getDropPoint(base.cr.hoodMgr.ToontownCentralHQDropPoints) + msg = TTLocalizer.NPCForceAcknowledgeMessage9 + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, 0.05) + imgScale = 0.5 + self.dialog = TTDialog.TTDialog(text=msg, command=self.__cleanupDialog, style=TTDialog.Acknowledge) + imgLabel = DirectLabel.DirectLabel(parent=self.dialog, relief=None, pos=imgPos, scale=TTLocalizer.PimgLabel, image=imgNodePath, image_scale=imgScale) + imageModel.removeNode() + else: + requestStatus['nextState'] = 'walk' + x, y, z, h, p, r = base.cr.hoodMgr.getPlaygroundCenterFromId(self.loader.hood.id) + base.localAvatar.detachNode() + base.localAvatar.setPosHpr(render, x, y, z, h, p, r) + Place.Place.enterTeleportIn(self, requestStatus) + return + + def __cleanupDialog(self, value): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + if hasattr(self, 'fsm'): + self.fsm.request('walk', [1]) + return + + def __handleDeathAck(self, requestStatus): + if self.deathAckBox: + self.ignore('deathAck') + self.deathAckBox.cleanup() + self.deathAckBox = None + self.fsm.request('walk', [1]) + return + + def enterPopup(self, teleportIn = 0): + if base.localAvatar.hp < 1: + base.localAvatar.b_setAnimState('Sad', 1) + else: + base.localAvatar.b_setAnimState('neutral', 1.0) + self.accept('teleportQuery', self.handleTeleportQuery) + base.localAvatar.setTeleportAvailable(1) + base.localAvatar.startSleepWatch(self.__handleFallingAsleepPopup) + + def exitPopup(self): + base.localAvatar.stopSleepWatch() + base.localAvatar.setTeleportAvailable(0) + self.ignore('teleportQuery') + + def __handleFallingAsleepPopup(self, task): + if hasattr(self, 'fsm'): + self.fsm.request('walk') + base.localAvatar.forceGotoSleep() + return Task.done + + def enterTeleportOut(self, requestStatus): + Place.Place.enterTeleportOut(self, requestStatus, self.__teleportOutDone) + + def __teleportOutDone(self, requestStatus): + teleportDebug(requestStatus, 'Playground.__teleportOutDone(%s)' % (requestStatus,)) + if hasattr(self, 'activityFsm'): + self.activityFsm.requestFinalState() + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + avId = requestStatus['avId'] + shardId = requestStatus['shardId'] + if hoodId == self.loader.hood.hoodId and zoneId == self.loader.hood.hoodId and shardId == None: + teleportDebug(requestStatus, 'same playground') + self.fsm.request('deathAck', [requestStatus]) + elif hoodId == ToontownGlobals.MyEstate: + teleportDebug(requestStatus, 'estate') + self.getEstateZoneAndGoHome(requestStatus) + else: + teleportDebug(requestStatus, 'different hood/zone') + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + def exitTeleportOut(self): + Place.Place.exitTeleportOut(self) + + def createPlayground(self, dnaFile): + loader.loadDNAFile(self.loader.dnaStore, self.safeZoneStorageDNAFile) + node = loader.loadDNAFile(self.loader.dnaStore, dnaFile) + if node.getNumParents() == 1: + self.geom = NodePath(node.getParent(0)) + self.geom.reparentTo(hidden) + else: + self.geom = hidden.attachNewNode(node) + self.makeDictionaries(self.loader.dnaStore) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.nodeList, self.zoneId) + self.geom.flattenMedium() + gsg = base.win.getGsg() + if gsg: + self.geom.prepareScene(gsg) + + def makeDictionaries(self, dnaStore): + self.nodeList = [] + for i in range(dnaStore.getNumDNAVisGroups()): + groupFullName = dnaStore.getDNAVisGroupName(i) + groupName = base.cr.hoodMgr.extractGroupName(groupFullName) + groupNode = self.geom.find('**/' + groupFullName) + if groupNode.isEmpty(): + self.notify.error('Could not find visgroup') + self.nodeList.append(groupNode) + + self.removeLandmarkBlockNodes() + self.loader.dnaStore.resetPlaceNodes() + self.loader.dnaStore.resetDNAGroups() + self.loader.dnaStore.resetDNAVisGroups() + self.loader.dnaStore.resetDNAVisGroupsAI() + + def removeLandmarkBlockNodes(self): + npc = self.geom.findAllMatches('**/suit_building_origin') + for i in range(npc.getNumPaths()): + npc.getPath(i).removeNode() + + def enterTFA(self, requestStatus): + self.acceptOnce(self.tfaDoneEvent, self.enterTFACallback, [requestStatus]) + self.tfa = TutorialForceAcknowledge.TutorialForceAcknowledge(self.tfaDoneEvent) + self.tfa.enter() + + def exitTFA(self): + self.ignore(self.tfaDoneEvent) + + def enterTFAReject(self): + self.fsm.request('walk') + + def exitTFAReject(self): + pass diff --git a/toontown/safezone/PublicWalk.py b/toontown/safezone/PublicWalk.py new file mode 100644 index 0000000..4951f1e --- /dev/null +++ b/toontown/safezone/PublicWalk.py @@ -0,0 +1,60 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import Walk + +class PublicWalk(Walk.Walk): + notify = DirectNotifyGlobal.directNotify.newCategory('PublicWalk') + + def __init__(self, parentFSM, doneEvent): + Walk.Walk.__init__(self, doneEvent) + self.parentFSM = parentFSM + + def load(self): + Walk.Walk.load(self) + + def unload(self): + Walk.Walk.unload(self) + del self.parentFSM + + def enter(self, slowWalk = 0): + Walk.Walk.enter(self, slowWalk) + base.localAvatar.book.showButton() + self.accept(StickerBookHotkey, self.__handleStickerBookEntry) + self.accept('enterStickerBook', self.__handleStickerBookEntry) + self.accept(OptionsPageHotkey, self.__handleOptionsEntry) + base.localAvatar.laffMeter.start() + base.localAvatar.beginAllowPies() + + def exit(self): + Walk.Walk.exit(self) + base.localAvatar.book.hideButton() + self.ignore(StickerBookHotkey) + self.ignore('enterStickerBook') + self.ignore(OptionsPageHotkey) + base.localAvatar.laffMeter.stop() + base.localAvatar.endAllowPies() + + def __handleStickerBookEntry(self): + currentState = base.localAvatar.animFSM.getCurrentState().getName() + if currentState == 'jumpAirborne': + return + if base.localAvatar.book.isObscured(): + return + else: + doneStatus = {} + doneStatus['mode'] = 'StickerBook' + messenger.send(self.doneEvent, [doneStatus]) + return + + def __handleOptionsEntry(self): + currentState = base.localAvatar.animFSM.getCurrentState().getName() + if currentState == 'jumpAirborne': + return + if base.localAvatar.book.isObscured(): + return + else: + doneStatus = {} + doneStatus['mode'] = 'Options' + messenger.send(self.doneEvent, [doneStatus]) + return diff --git a/toontown/safezone/RegenTreasurePlannerAI.py b/toontown/safezone/RegenTreasurePlannerAI.py new file mode 100644 index 0000000..72b5d32 --- /dev/null +++ b/toontown/safezone/RegenTreasurePlannerAI.py @@ -0,0 +1,44 @@ +from direct.distributed.ClockDelta import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +import random +import TreasurePlannerAI + +class RegenTreasurePlannerAI(TreasurePlannerAI.TreasurePlannerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('RegenTreasurePlannerAI') + + def __init__(self, zoneId, treasureConstructor, taskName, spawnInterval, maxTreasures, callback = None): + TreasurePlannerAI.TreasurePlannerAI.__init__(self, zoneId, treasureConstructor, callback) + self.taskName = '%s-%s' % (taskName, zoneId) + self.spawnInterval = spawnInterval + self.maxTreasures = maxTreasures + + def start(self): + self.preSpawnTreasures() + self.startSpawning() + + def stop(self): + self.stopSpawning() + + def stopSpawning(self): + taskMgr.remove(self.taskName) + + def startSpawning(self): + self.stopSpawning() + taskMgr.doMethodLater(self.spawnInterval, self.upkeepTreasurePopulation, self.taskName) + + def upkeepTreasurePopulation(self, task): + if self.numTreasures() < self.maxTreasures: + self.placeRandomTreasure() + taskMgr.doMethodLater(self.spawnInterval, self.upkeepTreasurePopulation, self.taskName) + return Task.done + + def placeRandomTreasure(self): + self.notify.debug('Placing a Treasure...') + spawnPointIndex = self.nthEmptyIndex(random.randrange(self.countEmptySpawnPoints())) + self.placeTreasure(spawnPointIndex) + + def preSpawnTreasures(self): + for i in range(self.maxTreasures): + self.placeRandomTreasure() diff --git a/toontown/safezone/SafeZoneLoader.py b/toontown/safezone/SafeZoneLoader.py new file mode 100644 index 0000000..a8a99fb --- /dev/null +++ b/toontown/safezone/SafeZoneLoader.py @@ -0,0 +1,255 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToonBaseGlobal import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.hood import ZoneUtil +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 direct.task import Task +from toontown.launcher import DownloadForceAcknowledge +from toontown.toon import HealthForceAcknowledge +from toontown.toon.Toon import teleportDebug +from toontown.tutorial import TutorialForceAcknowledge +from toontown.toonbase.ToontownGlobals import * +from toontown.building import ToonInterior +from toontown.hood import QuietZoneState + +class SafeZoneLoader(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('SafeZoneLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.hood = hood + self.parentFSMState = parentFSMState + self.fsm = ClassicFSM.ClassicFSM('SafeZoneLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'playground', 'toonInterior']), + State.State('playground', self.enterPlayground, self.exitPlayground, ['quietZone']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['playground', 'toonInterior']), + State.State('golfcourse', self.enterGolfcourse, self.exitGolfcourse, ['quietZone', 'playground']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.placeDoneEvent = 'placeDone' + self.place = None + self.playgroundClass = None + return + + def load(self): + self.music = base.loadMusic(self.musicFile) + self.activityMusic = base.loadMusic(self.activityMusicFile) + self.createSafeZone(self.dnaFile) + self.parentFSMState.addChild(self.fsm) + + def unload(self): + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + self.geom.removeNode() + del self.geom + del self.fsm + del self.hood + del self.nodeList + del self.playgroundClass + del self.music + del self.activityMusic + del self.holidayPropTransforms + self.deleteAnimatedProps() + self.ignoreAll() + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + self.fsm.enterInitialState() + messenger.send('enterSafeZone') + self.setState(requestStatus['where'], requestStatus) + + def exit(self): + messenger.send('exitSafeZone') + + def setState(self, stateName, requestStatus): + self.fsm.request(stateName, [requestStatus]) + + def createSafeZone(self, dnaFile): + if self.safeZoneStorageDNAFile: + loader.loadDNAFile(self.hood.dnaStore, self.safeZoneStorageDNAFile) + node = loader.loadDNAFile(self.hood.dnaStore, dnaFile) + if node.getNumParents() == 1: + self.geom = NodePath(node.getParent(0)) + self.geom.reparentTo(hidden) + else: + self.geom = hidden.attachNewNode(node) + self.makeDictionaries(self.hood.dnaStore) + self.createAnimatedProps(self.nodeList) + self.holidayPropTransforms = {} + npl = self.geom.findAllMatches('**/=DNARoot=holiday_prop') + for i in range(npl.getNumPaths()): + np = npl.getPath(i) + np.setTag('transformIndex', `i`) + self.holidayPropTransforms[i] = np.getNetTransform() + + self.geom.flattenMedium() + gsg = base.win.getGsg() + if gsg: + self.geom.prepareScene(gsg) + + def makeDictionaries(self, dnaStore): + self.nodeList = [] + for i in range(dnaStore.getNumDNAVisGroups()): + groupFullName = dnaStore.getDNAVisGroupName(i) + groupName = base.cr.hoodMgr.extractGroupName(groupFullName) + groupNode = self.geom.find('**/' + groupFullName) + if groupNode.isEmpty(): + self.notify.error('Could not find visgroup') + self.nodeList.append(groupNode) + + self.removeLandmarkBlockNodes() + self.hood.dnaStore.resetPlaceNodes() + self.hood.dnaStore.resetDNAGroups() + self.hood.dnaStore.resetDNAVisGroups() + self.hood.dnaStore.resetDNAVisGroupsAI() + + def removeLandmarkBlockNodes(self): + npc = self.geom.findAllMatches('**/suit_building_origin') + for i in range(npc.getNumPaths()): + npc.getPath(i).removeNode() + + def enterStart(self): + pass + + def exitStart(self): + pass + + def enterPlayground(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.handlePlaygroundDone) + self.place = self.playgroundClass(self, self.fsm, self.placeDoneEvent) + self.place.load() + self.place.enter(requestStatus) + base.cr.playGame.setPlace(self.place) + + def exitPlayground(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handlePlaygroundDone(self): + status = self.place.doneStatus + teleportDebug(status, 'handlePlaygroundDone, doneStatus=%s' % (status,)) + if ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + teleportDebug(status, 'same branch') + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + teleportDebug(status, 'different hood') + messenger.send(self.doneEvent) + return + + def enterToonInterior(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.handleToonInteriorDone) + self.place = ToonInterior.ToonInterior(self, self.fsm.getStateNamed('toonInterior'), self.placeDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitToonInterior(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleToonInteriorDone(self): + status = self.place.doneStatus + if ZoneUtil.getBranchZone(status['zoneId']) == self.hood.hoodId and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + if status['where'] == 'estate': + self.doneStatus = status + messenger.send(self.doneEvent) + else: + self.fsm.request(status['where'], [status]) + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def createAnimatedProps(self, nodeList): + self.animPropDict = {} + for i in nodeList: + animPropNodes = i.findAllMatches('**/animated_prop_*') + numAnimPropNodes = animPropNodes.getNumPaths() + for j in range(numAnimPropNodes): + animPropNode = animPropNodes.getPath(j) + if animPropNode.getName().startswith('animated_prop_generic'): + className = 'GenericAnimatedProp' + else: + className = animPropNode.getName()[14:-8] + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + animPropObj = classObj(animPropNode) + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(animPropObj) + + interactivePropNodes = i.findAllMatches('**/interactive_prop_*') + numInteractivePropNodes = interactivePropNodes.getNumPaths() + for j in range(numInteractivePropNodes): + interactivePropNode = interactivePropNodes.getPath(j) + className = 'GenericAnimatedProp' + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + interactivePropObj = classObj(interactivePropNode) + animPropList = self.animPropDict.get(i) + if animPropList is None: + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(interactivePropObj) + + return + + def deleteAnimatedProps(self): + for zoneNode, animPropList in self.animPropDict.items(): + for animProp in animPropList: + animProp.delete() + + del self.animPropDict + + def enterAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.enter() + + def exitAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.exit() + + def enterGolfcourse(self, requestStatus): + base.transitions.fadeOut(t=0) + + def exitGolfcourse(self): + pass diff --git a/toontown/safezone/SafeZoneManager.py b/toontown/safezone/SafeZoneManager.py new file mode 100644 index 0000000..6890e03 --- /dev/null +++ b/toontown/safezone/SafeZoneManager.py @@ -0,0 +1,25 @@ +from pandac.PandaModules import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal + +class SafeZoneManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('SafeZoneManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.accept('enterSafeZone', self.d_enterSafeZone) + self.accept('exitSafeZone', self.d_exitSafeZone) + + def disable(self): + self.ignoreAll() + DistributedObject.DistributedObject.disable(self) + + def d_enterSafeZone(self): + self.sendUpdate('enterSafeZone', []) + + def d_exitSafeZone(self): + self.sendUpdate('exitSafeZone', []) diff --git a/toontown/safezone/TTPlayground.py b/toontown/safezone/TTPlayground.py new file mode 100644 index 0000000..ebad592 --- /dev/null +++ b/toontown/safezone/TTPlayground.py @@ -0,0 +1,54 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +import Playground +import random +from toontown.launcher import DownloadForceAcknowledge +from direct.task.Task import Task +from toontown.hood import ZoneUtil + +class TTPlayground(Playground.Playground): + + def __init__(self, loader, parentFSM, doneEvent): + Playground.Playground.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Playground.Playground.load(self) + + def unload(self): + Playground.Playground.unload(self) + + def enter(self, requestStatus): + Playground.Playground.enter(self, requestStatus) + taskMgr.doMethodLater(1, self.__birds, 'TT-birds') + + def exit(self): + Playground.Playground.exit(self) + taskMgr.remove('TT-birds') + + def __birds(self, task): + base.playSfx(random.choice(self.loader.birdSound)) + t = random.random() * 20.0 + 1 + taskMgr.doMethodLater(t, self.__birds, 'TT-birds') + return Task.done + + def doRequestLeave(self, requestStatus): + self.fsm.request('trialerFA', [requestStatus]) + + def enterDFA(self, requestStatus): + doneEvent = 'dfaDoneEvent' + self.accept(doneEvent, self.enterDFACallback, [requestStatus]) + self.dfa = DownloadForceAcknowledge.DownloadForceAcknowledge(doneEvent) + hood = ZoneUtil.getCanonicalZoneId(requestStatus['hoodId']) + if hood == ToontownGlobals.MyEstate: + self.dfa.enter(base.cr.hoodMgr.getPhaseFromHood(ToontownGlobals.MyEstate)) + elif hood == ToontownGlobals.GoofySpeedway: + self.dfa.enter(base.cr.hoodMgr.getPhaseFromHood(ToontownGlobals.GoofySpeedway)) + elif hood == ToontownGlobals.PartyHood: + self.dfa.enter(base.cr.hoodMgr.getPhaseFromHood(ToontownGlobals.PartyHood)) + else: + self.dfa.enter(5) + + def showPaths(self): + from toontown.classicchars import CCharPaths + from toontown.toonbase import TTLocalizer + self.showPathPoints(CCharPaths.getPaths(TTLocalizer.Mickey)) diff --git a/toontown/safezone/TTSafeZoneLoader.py b/toontown/safezone/TTSafeZoneLoader.py new file mode 100644 index 0000000..3ee394a --- /dev/null +++ b/toontown/safezone/TTSafeZoneLoader.py @@ -0,0 +1,29 @@ +from pandac.PandaModules import * +import SafeZoneLoader +import TTPlayground +import random +from toontown.launcher import DownloadForceAcknowledge + +class TTSafeZoneLoader(SafeZoneLoader.SafeZoneLoader): + + def __init__(self, hood, parentFSM, doneEvent): + SafeZoneLoader.SafeZoneLoader.__init__(self, hood, parentFSM, doneEvent) + self.playgroundClass = TTPlayground.TTPlayground + self.musicFile = 'phase_4/audio/bgm/TC_nbrhood.mid' + self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.mid' + self.dnaFile = 'phase_4/dna/toontown_central_sz.dna' + self.safeZoneStorageDNAFile = 'phase_4/dna/storage_TT_sz.dna' + + def load(self): + SafeZoneLoader.SafeZoneLoader.load(self) + self.birdSound = map(base.loadSfx, ['phase_4/audio/sfx/SZ_TC_bird1.mp3', 'phase_4/audio/sfx/SZ_TC_bird2.mp3', 'phase_4/audio/sfx/SZ_TC_bird3.mp3']) + + def unload(self): + del self.birdSound + SafeZoneLoader.SafeZoneLoader.unload(self) + + def enter(self, requestStatus): + SafeZoneLoader.SafeZoneLoader.enter(self, requestStatus) + + def exit(self): + SafeZoneLoader.SafeZoneLoader.exit(self) diff --git a/toontown/safezone/TTTreasurePlannerAI.py b/toontown/safezone/TTTreasurePlannerAI.py new file mode 100644 index 0000000..85cf3b4 --- /dev/null +++ b/toontown/safezone/TTTreasurePlannerAI.py @@ -0,0 +1,32 @@ +from toontown.toonbase.ToontownGlobals import * +import RegenTreasurePlannerAI, DistributedTTTreasureAI + +class TTTreasurePlannerAI(RegenTreasurePlannerAI.RegenTreasurePlannerAI): + + def __init__(self, zoneId): + self.healAmount = 3 + RegenTreasurePlannerAI.RegenTreasurePlannerAI.__init__(self, zoneId, DistributedTTTreasureAI.DistributedTTTreasureAI, 'TTTreasurePlanner', 20, 5) + + def initSpawnPoints(self): + self.spawnPoints = [(-59.9, -6.9, 0.84), + (-90.6, -3.0, -0.75), + (27.1, -93.5, 2.5), + (94.2, 33.5, 4), + (35.4, 43.1, 4), + (67.1, 105.5, 2.5), + (-99.15, -87.3407, 0.52499), + (1.60586, -119.492, 3.025), + (43.2026, -76.287, 3.025), + (129.137, -61.9039, 2.525), + (92.99, -158.399, 3.025), + (111.749, -8.59927, 4.57466), + (41.999, -30.2923, 4.025), + (31.0649, -43.9149, 4.025), + (10.0156, 105.218, 2.525), + (46.9667, 169.143, 3.025), + (100.68, 93.9896, 2.525), + (129.285, 58.6107, 2.525), + (-28.6272, 85.9833, 0.525), + (-114.613, 86.1727, 0.525), + (-132.528, 31.255, 0.025)] + return self.spawnPoints diff --git a/toontown/safezone/Train.py b/toontown/safezone/Train.py new file mode 100644 index 0000000..8393035 --- /dev/null +++ b/toontown/safezone/Train.py @@ -0,0 +1,162 @@ +from pandac.PandaModules import * +from direct.showbase.DirectObject import DirectObject +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import globalClockDelta +from direct.distributed.ClockDelta import NetworkTimePrecision +import random +from direct.task.Task import Task +from direct.directnotify.DirectNotifyGlobal import directNotify +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.directutil import Mopath +from toontown.toonbase import ToontownGlobals +from direct.actor import Actor + +class Train(DirectObject): + notify = directNotify.newCategory('Train') + nameId = 0 + Sfx_TrainPass = 'phase_10/audio/sfx/CBHQ_TRAIN_pass.mp3' + Sfx_TrainStopStart = 'phase_10/audio/sfx/CBHQ_TRAIN_stopstart.mp3' + LocomotiveFile = 'phase_10/models/cogHQ/CashBotLocomotive' + CarFiles = ['phase_10/models/cogHQ/CashBotBoxCar', 'phase_10/models/cogHQ/CashBotTankCar', 'phase_10/models/cogHQ/CashBotFlatCar'] + CarLength = 88 + MarkDelta = 15 + + def __init__(self, trackStartPos, trackEndPos, trackNum, numTotalTracks): + self.trackStartPos = trackStartPos + self.trackEndPos = trackEndPos + self.numCars = len(self.CarFiles) + self.locomotive = loader.loadModel(self.LocomotiveFile) + self.cars = [] + self.trainPassingSfx = base.loadSfx(self.Sfx_TrainPass) + self.trainStopStartSfx = base.loadSfx(self.Sfx_TrainStopStart) + self.trainId = trackNum + self.bFlipped = False + if trackStartPos[0] < trackEndPos[0]: + self.locomotive.setHpr(180, 0, 0) + self.bFlipped = True + self.collNodeName = 'CollNode-%s' % self.trainId + self.firstMark = self.MarkDelta / numTotalTracks * trackNum + currentTime = self.__networkTimeInSeconds() + currentRun = int((currentTime - self.firstMark) / self.MarkDelta) + self.lastMark = currentRun * self.MarkDelta + self.firstMark + self.doNextRun(True) + self.hide() + + def hide(self): + if self.locomotive: + self.locomotive.reparentTo(hidden) + + def show(self): + if self.locomotive: + self.locomotive.reparentTo(render) + + def __networkTimeInSeconds(self): + time = globalClockDelta.getRealNetworkTime(bits=32) / NetworkTimePrecision + return time + + def doNextRun(self, bFirstRun = False): + if self.locomotive: + if bFirstRun: + nextMark = self.lastMark + else: + nextMark = self.lastMark + self.MarkDelta + self.nextRun.finish() + self.notify.debug('Next mark %s' % nextMark) + currentTime = self.__networkTimeInSeconds() + timeTillNextMark = nextMark - currentTime + self.notify.debug('Time diff %s' % timeTillNextMark) + runNumber = int((nextMark - self.firstMark) / self.MarkDelta) + S = random.getstate() + random.seed(self.trainId + runNumber) + self.nextRun = self.__getNextRun() + random.setstate(S) + self.__startNextRun(timeTillNextMark) + self.lastMark = nextMark + return Task.done + + def __startNextRun(self, timeTillMark): + if self.locomotive: + self.__disableCollisions() + if timeTillMark > 0: + self.nextRun = Sequence(Wait(timeTillMark), self.nextRun) + self.nextRun.start() + else: + self.nextRun.start(-1 * timeTillMark) + self.__enableCollisions() + return Task.done + + def __cleanupCars(self): + self.__disableCollisions() + for car in self.cars: + car.removeNode() + + self.cars = [] + + def __getCars(self): + self.__cleanupCars() + numCarsThisRun = random.randrange(1, 10) + for nCar in range(numCarsThisRun): + carType = random.randrange(0, self.numCars) + car = loader.loadModel(self.CarFiles[carType]) + car.reparentTo(self.locomotive) + car.setPos(self.CarLength * (nCar + 1), 0, 0) + self.cars.append(car) + + def __showStart(self): + self.notify.debug('Starting train %s at %s.' % (self.trainId, self.__networkTimeInSeconds())) + + def __getNextRun(self): + self.__getCars() + trainShouldStop = random.randrange(0, 4) + nextRun = Sequence(Func(self.__showStart)) + if trainShouldStop is 0: + waitTime = 3 + totalTime = random.randrange(4, (self.MarkDelta - waitTime) / 2) + sfxStopTime = 4.3 + halfway = (self.trackStartPos + self.trackEndPos) / 2 + halfway.setX(150) + nextRun.append(Parallel(Sequence(Wait(totalTime - sfxStopTime), SoundInterval(self.trainStopStartSfx, volume=0.5)), Sequence(LerpPosInterval(self.locomotive, totalTime, halfway, self.trackStartPos, blendType='easeInOut'), WaitInterval(waitTime), LerpPosInterval(self.locomotive, totalTime, self.trackEndPos, halfway, blendType='easeIn')))) + else: + totalTime = random.randrange(6, self.MarkDelta - 1) + sfxTime = 7 + sfxStartTime = totalTime / 2 - sfxTime / 2 + if self.bFlipped: + sfxStartTime -= 1 + else: + sfxStartTime += 1 + nextRun.append(Parallel(Sequence(Wait(sfxStartTime), SoundInterval(self.trainPassingSfx, volume=0.5)), LerpPosInterval(self.locomotive, totalTime, self.trackEndPos, self.trackStartPos))) + nextRun.append(Func(self.doNextRun)) + return nextRun + + def delete(self): + self.__cleanupCars() + self.locomotive.removeNode() + self.locomotive = None + self.nextRun.finish() + self.nextRun = None + del self.trainPassingSfx + del self.trainStopStartSfx + return + + def uniqueName(self, name): + Train.nameId += 1 + return name + '-%d' % Train.nameId + + def __enableCollisions(self): + allColls = self.locomotive.findAllMatches('**/+CollisionNode') + for car in self.cars: + carColls = car.findAllMatches('**/+CollisionNode') + allColls += carColls + + for collNode in allColls: + collNode.setName(self.collNodeName) + collNode.setCollideMask(ToontownGlobals.WallBitmask) + + self.accept('enter' + self.collNodeName, self.__handleCollisionSphereEnter) + + def __disableCollisions(self): + self.ignore('enter' + self.collNodeName) + + def __handleCollisionSphereEnter(self, collEntry = None): + base.localAvatar.b_squish(10) diff --git a/toontown/safezone/TreasurePlannerAI.py b/toontown/safezone/TreasurePlannerAI.py new file mode 100644 index 0000000..5437790 --- /dev/null +++ b/toontown/safezone/TreasurePlannerAI.py @@ -0,0 +1,141 @@ +from direct.distributed.ClockDelta import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.task import Task +import random + +class TreasurePlannerAI(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TreasurePlannerAI') + + def __init__(self, zoneId, treasureConstructor, callback = None): + self.zoneId = zoneId + self.treasureConstructor = treasureConstructor + self.callback = callback + self.initSpawnPoints() + self.treasures = [] + for spawnPoint in self.spawnPoints: + self.treasures.append(None) + + self.deleteTaskNames = set() + self.lastRequestId = None + self.requestStartTime = None + self.requestCount = None + return + + def initSpawnPoints(self): + self.spawnPoints = [] + return self.spawnPoints + + def numTreasures(self): + counter = 0 + for treasure in self.treasures: + if treasure: + counter += 1 + + return counter + + def countEmptySpawnPoints(self): + counter = 0 + for treasure in self.treasures: + if treasure == None: + counter += 1 + + return counter + + def nthEmptyIndex(self, n): + emptyCounter = -1 + spawnPointCounter = -1 + while emptyCounter < n: + spawnPointCounter += 1 + if self.treasures[spawnPointCounter] == None: + emptyCounter += 1 + + return spawnPointCounter + + def findIndexOfTreasureId(self, treasureId): + counter = 0 + for treasure in self.treasures: + if treasure == None: + pass + elif treasureId == treasure.getDoId(): + return counter + counter += 1 + + return + + def placeAllTreasures(self): + index = 0 + for treasure in self.treasures: + if not treasure: + self.placeTreasure(index) + index += 1 + + def placeTreasure(self, index): + spawnPoint = self.spawnPoints[index] + treasure = self.treasureConstructor(simbase.air, self, spawnPoint[0], spawnPoint[1], spawnPoint[2]) + treasure.generateWithRequired(self.zoneId) + self.treasures[index] = treasure + + def grabAttempt(self, avId, treasureId): + 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, 'TreasurePlannerAI.grabAttempt %s treasures in %s seconds' % (self.requestCount, elapsed)) + else: + self.lastRequestId = avId + self.requestCount = 1 + self.requestStartTime = globalClock.getFrameTime() + index = self.findIndexOfTreasureId(treasureId) + if index == None: + pass + else: + av = simbase.air.doId2do.get(avId) + if av == None: + simbase.air.writeServerEvent('suspicious', avId, 'TreasurePlannerAI.grabAttempt unknown avatar') + self.notify.warning('avid: %s does not exist' % avId) + else: + treasure = self.treasures[index] + if treasure.validAvatar(av): + self.treasures[index] = None + if self.callback: + self.callback(avId) + treasure.d_setGrab(avId) + self.deleteTreasureSoon(treasure) + else: + treasure.d_setReject() + return + + def deleteTreasureSoon(self, treasure): + taskName = treasure.uniqueName('deletingTreasure') + taskMgr.doMethodLater(5, self.__deleteTreasureNow, taskName, extraArgs=(treasure, taskName)) + self.deleteTaskNames.add(taskName) + + def deleteAllTreasuresNow(self): + for treasure in self.treasures: + if treasure: + treasure.requestDelete() + + for taskName in self.deleteTaskNames: + tasks = taskMgr.getTasksNamed(taskName) + if len(tasks): + treasure = tasks[0].getArgs()[0] + treasure.requestDelete() + taskMgr.remove(taskName) + + self.deleteTaskNames = set() + self.treasures = [] + for spawnPoint in self.spawnPoints: + self.treasures.append(None) + + return + + def __deleteTreasureNow(self, treasure, taskName): + treasure.requestDelete() + self.deleteTaskNames.remove(taskName) diff --git a/toontown/safezone/TrolleyConstants.py b/toontown/safezone/TrolleyConstants.py new file mode 100644 index 0000000..7c1198e --- /dev/null +++ b/toontown/safezone/TrolleyConstants.py @@ -0,0 +1,5 @@ +TROLLEY_ENTER_TIME = 2.0 +TROLLEY_EXIT_TIME = 5.0 +TROLLEY_COUNTDOWN_TIME = 10.0 +TOON_BOARD_TIME = 1.0 +TOON_EXIT_TIME = 1.0 diff --git a/toontown/safezone/Walk.py b/toontown/safezone/Walk.py new file mode 100644 index 0000000..0ec522b --- /dev/null +++ b/toontown/safezone/Walk.py @@ -0,0 +1,109 @@ +from pandac.PandaModules import * +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State + +class Walk(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Walk') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Walk', [State.State('off', self.enterOff, self.exitOff, ['walking', 'swimming', 'slowWalking']), + State.State('walking', self.enterWalking, self.exitWalking, ['swimming', 'slowWalking']), + State.State('swimming', self.enterSwimming, self.exitSwimming, ['walking', 'slowWalking']), + State.State('slowWalking', self.enterSlowWalking, self.exitSlowWalking, ['walking', 'swimming'])], 'off', 'off') + self.fsm.enterInitialState() + self.IsSwimSoundAudible = 0 + self.swimSoundPlaying = 0 + + def load(self): + pass + + def unload(self): + del self.fsm + + def enter(self, slowWalk = 0): + base.localAvatar.startPosHprBroadcast() + base.localAvatar.startBlink() + base.localAvatar.attachCamera() + shouldPush = 1 + if len(base.localAvatar.cameraPositions) > 0: + shouldPush = not base.localAvatar.cameraPositions[base.localAvatar.cameraIndex][4] + base.localAvatar.startUpdateSmartCamera(shouldPush) + base.localAvatar.showName() + base.localAvatar.collisionsOn() + base.localAvatar.startGlitchKiller() + base.localAvatar.enableAvatarControls() + + def exit(self): + self.fsm.request('off') + self.ignore('control') + base.localAvatar.disableAvatarControls() + base.localAvatar.stopUpdateSmartCamera() + base.localAvatar.stopPosHprBroadcast() + base.localAvatar.stopBlink() + base.localAvatar.detachCamera() + base.localAvatar.stopGlitchKiller() + base.localAvatar.collisionsOff() + base.localAvatar.controlManager.placeOnFloor() + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterWalking(self): + if base.localAvatar.hp > 0: + base.localAvatar.startTrackAnimToSpeed() + base.localAvatar.setWalkSpeedNormal() + else: + self.fsm.request('slowWalking') + + def exitWalking(self): + base.localAvatar.stopTrackAnimToSpeed() + + def setSwimSoundAudible(self, IsSwimSoundAudible): + self.IsSwimSoundAudible = IsSwimSoundAudible + if IsSwimSoundAudible == 0 and self.swimSoundPlaying: + self.swimSound.stop() + self.swimSoundPlaying = 0 + + def enterSwimming(self, swimSound): + base.localAvatar.setWalkSpeedNormal() + self.swimSound = swimSound + self.swimSoundPlaying = 0 + base.localAvatar.b_setAnimState('swim', base.localAvatar.animMultiplier) + base.localAvatar.startSleepSwimTest() + taskMgr.add(self.__swim, 'localToonSwimming') + + def exitSwimming(self): + taskMgr.remove('localToonSwimming') + self.swimSound.stop() + del self.swimSound + self.swimSoundPlaying = 0 + base.localAvatar.stopSleepSwimTest() + + def __swim(self, task): + speed = base.mouseInterfaceNode.getSpeed() + if speed == 0 and self.swimSoundPlaying: + self.swimSoundPlaying = 0 + self.swimSound.stop() + elif speed > 0 and self.swimSoundPlaying == 0 and self.IsSwimSoundAudible: + self.swimSoundPlaying = 1 + base.playSfx(self.swimSound, looping=1) + return Task.cont + + def enterSlowWalking(self): + self.accept(base.localAvatar.uniqueName('positiveHP'), self.__handlePositiveHP) + base.localAvatar.startTrackAnimToSpeed() + base.localAvatar.setWalkSpeedSlow() + + def __handlePositiveHP(self): + self.fsm.request('walking') + + def exitSlowWalking(self): + base.localAvatar.stopTrackAnimToSpeed() + self.ignore(base.localAvatar.uniqueName('positiveHP')) diff --git a/toontown/safezone/__init__.py b/toontown/safezone/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/shtiker/CogPageGlobals.py b/toontown/shtiker/CogPageGlobals.py new file mode 100644 index 0000000..0871916 --- /dev/null +++ b/toontown/shtiker/CogPageGlobals.py @@ -0,0 +1,6 @@ +COG_QUOTAS = ((30, 25, 20, 15, 10, 5, 2, 1), (45, 40, 35, 30, 25, 20, 15, 10)) +COG_UNSEEN = 1 +COG_BATTLED = 2 +COG_DEFEATED = 3 +COG_COMPLETE1 = 4 +COG_COMPLETE2 = 5 diff --git a/toontown/shtiker/DeleteManager.py b/toontown/shtiker/DeleteManager.py new file mode 100644 index 0000000..46af28f --- /dev/null +++ b/toontown/shtiker/DeleteManager.py @@ -0,0 +1,21 @@ +from pandac.PandaModules import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal + +class DeleteManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DeleteManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + self.accept('deleteItems', self.d_setInventory) + + def disable(self): + self.ignore('deleteItems') + DistributedObject.DistributedObject.disable(self) + + def d_setInventory(self, newInventoryString): + self.sendUpdate('setInventory', [newInventoryString]) diff --git a/toontown/shtiker/DirectNewsFrame.py b/toontown/shtiker/DirectNewsFrame.py new file mode 100644 index 0000000..612c246 --- /dev/null +++ b/toontown/shtiker/DirectNewsFrame.py @@ -0,0 +1,414 @@ +import os +import time +import datetime +from pandac.PandaModules import Filename, DSearchPath, TextNode +from pandac.PandaModules import HTTPClient, Ramfile, DocumentSpec +from direct.showbase import DirectObject +from direct.gui.DirectGui import DirectFrame, DGG +from direct.directnotify import DirectNotifyGlobal +from direct.task.Task import Task +from direct.showbase import AppRunnerGlobal +from toontown.shtiker import IssueFrame +from toontown.shtiker import IssueFrameV2 +from toontown.toonbase import TTLocalizer + +class DirectNewsFrame(DirectObject.DirectObject): + TaskName = 'HtmlViewUpdateTask' + TaskChainName = 'RedownladTaskChain' + RedownloadTaskName = 'RedownloadNewsTask' + NewsBaseDir = config.GetString('news-base-dir', '/httpNews') + NewsStageDir = config.GetString('news-stage-dir', 'news') + FrameDimensions = (-1.30666637421, + 1.30666637421, + -0.751666665077, + 0.751666665077) + notify = DirectNotifyGlobal.directNotify.newCategory('DirectNewsFrame') + NewsIndexFilename = config.GetString('news-index-filename', 'http_news_index.txt') + NewsOverHttp = config.GetBool('news-over-http', True) + CacheIndexFilename = 'cache_index.txt' + SectionIdents = ['hom', + 'new', + 'evt', + 'tot', + 'att', + 'tnr'] + + def __init__(self, parent = aspect2d): + DirectObject.DirectObject.__init__(self) + self.accept('newsSnapshot', self.doSnapshot) + self.active = False + self.parent = parent + self.issues = [] + self.accept('newsChangeWeek', self.changeWeek) + self.curIssueIndex = 0 + self.strFilenames = None + self.redownloadingNews = False + self.startRedownload = datetime.datetime.now() + self.endRedownload = datetime.datetime.now() + self.load() + self.percentDownloaded = 0.0 + self.numIssuesExpected = 0 + self.needsParseNews = True + self.newsIndexEntries = [] + if self.NewsOverHttp: + self.redownloadNews() + self.accept('newIssueOut', self.handleNewIssueOut) + self.accept('clientCleanup', self.handleClientCleanup) + return + + def parseNewsContent(self): + if not self.needsParseNews: + return + self.needsParseNews = False + result = False + newsDir = self.findNewsDir() + if newsDir: + allHomeFiles = self.getAllHomeFilenames(newsDir) + self.notify.debug('len allHomeFiles = %s' % len(allHomeFiles)) + self.numIssuesExpected = len(allHomeFiles) + if allHomeFiles: + for myIssueIndex, oneHomeFile in enumerate(allHomeFiles): + if type(oneHomeFile) == type(''): + justFilename = oneHomeFile + else: + justFilename = oneHomeFile.getFilename().getBasename() + self.notify.debug('parseNewContent %s' % justFilename) + parts = justFilename.split('_') + dateStr = parts[3] + majorVer, minorVer = self.calcIssueVersion(dateStr) + if majorVer == 1: + oneIssue = IssueFrame.IssueFrame(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames) + elif majorVer == 2: + oneIssue = IssueFrameV2.IssueFrameV2(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries) + else: + self.notify.warning('Dont know how to handle version %s, asuming v2' % majorVer) + oneIssue = IssueFrameV2.IssueFrameV2(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries) + oneIssue.hide() + self.issues.append(oneIssue) + + if self.issues: + self.issues[-1].show() + self.curIssueIndex = len(self.issues) - 1 + result = True + if hasattr(base.cr, 'inGameNewsMgr') and base.cr.inGameNewsMgr: + self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() + self.notify.debug('setting created time to latest issue %s' % self.createdTime) + else: + self.createdTime = base.cr.toontownTimeManager.getCurServerDateTime() + self.notify.debug('setting created time cur server time %s' % self.createdTime) + return result + + def getAllHomeFilenames(self, newsDir): + self.notify.debug('getAllHomeFilenames') + newsDirAsFile = vfs.getFile(Filename(newsDir)) + fileList = newsDirAsFile.scanDirectory() + fileNames = fileList.getFiles() + self.notify.debug('filenames=%s' % fileNames) + homeFileNames = set([]) + for name in fileNames: + self.notify.debug('processing %s' % name) + baseName = name.getFilename().getBasename() + self.notify.debug('baseName=%s' % baseName) + if 'hom1.' in baseName: + homeFileNames.add(name) + else: + self.notify.debug('hom1. not in baseName') + + if not homeFileNames: + self.notify.warning('couldnt find hom1. in %s' % fileNames) + self.setErrorMessage(TTLocalizer.NewsPageNoIssues) + return [] + + def fileCmp(fileA, fileB): + return fileA.getFilename().compareTo(fileB.getFilename()) + + homeFileNames = list(homeFileNames) + homeFileNames.sort(cmp=fileCmp) + self.notify.debug('returned homeFileNames=%s' % homeFileNames) + return homeFileNames + + def findNewsDir(self): + if self.NewsOverHttp: + return self.NewsStageDir + 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.NewsIndexFilename) + found = vfs.resolveFilename(pfile, searchPath) + if not found: + self.notify.warning('findNewsDir - no path: %s' % self.NewsIndexFilename) + self.setErrorMessage(TTLocalizer.NewsPageErrorDownloadingFile % self.NewsIndexFilename) + return None + self.notify.debug('found index file %s' % pfile) + realDir = pfile.getDirname() + return realDir + + def load(self): + self.loadBackground() + + def loadBackground(self): + upsellBackground = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_newsStatusBackground') + imageScaleX = self.FrameDimensions[1] - self.FrameDimensions[0] + imageScaleY = self.FrameDimensions[3] - self.FrameDimensions[2] + self.backFrame = DirectFrame(parent=self.parent, image=upsellBackground, image_scale=(imageScaleX, 1, imageScaleY), frameColor=(1, 1, 1, 0), frameSize=self.FrameDimensions, pos=(0, 0, 0), relief=DGG.FLAT, text=TTLocalizer.NewsPageDownloadingNews1, text_scale=0.06, text_pos=(0, -0.4)) + + def addDownloadingTextTask(self): + self.removeDownloadingTextTask() + task = taskMgr.doMethodLater(1, self.loadingTextTask, 'DirectNewsFrameDownloadingTextTask') + task.startTime = globalClock.getFrameTime() + self.loadingTextTask(task) + + def removeDownloadingTextTask(self): + taskMgr.remove('DirectNewsFrameDownloadingTextTask') + + def loadMainPage(self): + self.mainFrame = DirectFrame(parent=self.backFrame, frameSize=self.FrameDimensions, frameColor=(1, 0, 0, 1)) + + def activate(self): + if hasattr(self, 'createdTime') and self.createdTime < base.cr.inGameNewsMgr.getLatestIssue() and self.NewsOverHttp and not self.redownloadingNews: + self.redownloadNews() + else: + self.addDownloadingTextTask() + if self.needsParseNews and not self.redownloadingNews: + self.parseNewsContent() + self.active = True + + def deactivate(self): + self.removeDownloadingTextTask() + self.active = False + + def unload(self): + self.removeDownloadingTextTask() + result = taskMgr.remove(self.RedownloadTaskName) + self.ignore('newsSnapshot') + self.ignore('newsChangeWeek') + self.ignore('newIssueOut') + self.ignore('clientCleanup') + + def handleClientCleanup(self): + pass + + def doSnapshot(self): + pass + + def changeWeek(self, issueIndex): + if 0 <= issueIndex and issueIndex < len(self.issues): + self.issues[self.curIssueIndex].hide() + self.issues[issueIndex].show() + self.curIssueIndex = issueIndex + + 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) + if self.backFrame['text'] != textToDisplay: + if TTLocalizer.NewsPageDownloadingNewsSubstr in self.backFrame['text']: + self.backFrame['text'] = textToDisplay + return task.again + + def setErrorMessage(self, errText): + self.backFrame['text'] = errText + + def redownloadNews(self): + if self.redownloadingNews: + self.notify.warning('averting potential crash redownloadNews called twice, just returning') + return + self.percentDownloaded = 0.0 + self.notify.info('starting redownloadNews') + self.startRedownload = datetime.datetime.now() + self.redownloadingNews = True + self.addDownloadingTextTask() + for issue in self.issues: + issue.destroy() + + self.issues = [] + self.curIssueIndex = 0 + self.strFilenames = None + self.needsParseNews = True + self.newsUrl = self.getInGameNewsUrl() + self.newsDir = Filename(self.findNewsDir()) + Filename(self.newsDir + '/.').makeDir() + http = HTTPClient.getGlobalPtr() + self.url = self.newsUrl + self.NewsIndexFilename + self.ch = http.makeChannel(True) + self.ch.beginGetDocument(self.url) + self.rf = Ramfile() + self.ch.downloadToRam(self.rf) + taskMgr.remove(self.RedownloadTaskName) + taskMgr.add(self.downloadIndexTask, self.RedownloadTaskName) + return + + def downloadIndexTask(self, task): + if self.ch.run(): + return task.cont + if not self.ch.isValid(): + self.notify.warning('Unable to download %s' % self.url) + self.redownloadingNews = False + return task.done + self.newsFiles = [] + filename = self.rf.readline() + while filename: + filename = filename.strip() + if filename: + self.newsFiles.append(filename) + filename = self.rf.readline() + + del self.rf + self.newsFiles.sort() + self.newsIndexEntries = list(self.newsFiles) + self.notify.info('Server lists %s news files' % len(self.newsFiles)) + self.notify.debug('self.newsIndexEntries=%s' % self.newsIndexEntries) + self.readNewsCache() + for basename in os.listdir(self.newsDir.toOsSpecific()): + if basename != self.CacheIndexFilename and basename not in self.newsCache: + junk = Filename(self.newsDir, basename) + self.notify.info('Removing %s' % junk) + junk.unlink() + + self.nextNewsFile = 0 + return self.downloadNextFile(task) + + def downloadNextFile(self, task): + while self.nextNewsFile < len(self.newsFiles) and 'aaver' in self.newsFiles[self.nextNewsFile]: + self.nextNewsFile += 1 + + if self.nextNewsFile >= len(self.newsFiles): + self.notify.info('Done downloading news.') + self.percentDownloaded = 1 + del self.newsFiles + del self.nextNewsFile + del self.newsUrl + del self.newsDir + del self.ch + del self.url + if hasattr(self, 'filename'): + del self.filename + self.redownloadingNews = False + if self.active: + self.parseNewsContent() + return task.done + self.percentDownloaded = float(self.nextNewsFile) / float(len(self.newsFiles)) + self.filename = self.newsFiles[self.nextNewsFile] + self.nextNewsFile += 1 + self.url = self.newsUrl + self.filename + localFilename = Filename(self.newsDir, self.filename) + self.notify.info('testing for %s' % localFilename.getFullpath()) + doc = DocumentSpec(self.url) + if self.filename in self.newsCache: + size, date = self.newsCache[self.filename] + if date and localFilename.exists() and (size == 0 or localFilename.getFileSize() == size): + doc.setDate(date) + doc.setRequestMode(doc.RMNewer) + self.ch.beginGetDocument(doc) + self.ch.downloadToFile(localFilename) + taskMgr.remove(self.RedownloadTaskName) + taskMgr.add(self.downloadCurrentFileTask, self.RedownloadTaskName) + + def downloadCurrentFileTask(self, task): + if self.ch.run(): + return task.cont + if self.ch.getStatusCode() == 304: + self.notify.info('already cached: %s' % self.filename) + return self.downloadNextFile(task) + localFilename = Filename(self.newsDir, self.filename) + if not self.ch.isValid(): + self.notify.warning('Unable to download %s' % self.url) + localFilename.unlink() + if self.filename in self.newsCache: + del self.newsCache[self.filename] + self.saveNewsCache() + return self.downloadNextFile(task) + self.notify.info('downloaded %s' % self.filename) + size = self.ch.getFileSize() + doc = self.ch.getDocumentSpec() + date = '' + if doc.hasDate(): + date = doc.getDate().getString() + self.newsCache[self.filename] = (size, date) + self.saveNewsCache() + return self.downloadNextFile(task) + + def readNewsCache(self): + cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) + self.newsCache = {} + if cacheIndexFilename.isRegularFile(): + file = open(cacheIndexFilename.toOsSpecific(), 'r') + for line in file.readlines(): + line = line.strip() + keywords = line.split('\t') + if len(keywords) == 3: + filename, size, date = keywords + if filename in self.newsFiles: + try: + size = int(size) + except ValueError: + size = 0 + + self.newsCache[filename] = (size, date) + + def saveNewsCache(self): + cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) + try: + file = open(cacheIndexFilename.toOsSpecific(), 'w') + except IOError, e: + self.notify.warning('error opening news cache file %s: %s' % (cacheIndexFilename, str(e))) + return + + for filename, (size, date) in self.newsCache.items(): + print >> file, '%s\t%s\t%s' % (filename, size, date) + + def handleNewIssueOut(self): + if hasattr(self, 'createdTime') and base.cr.inGameNewsMgr.getLatestIssue() < self.createdTime: + self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() + elif self.NewsOverHttp and not self.redownloadingNews: + if not self.active: + self.redownloadNews() + + def getInGameNewsUrl(self): + result = base.config.GetString('fallback-news-url', 'http://cdn.toontown.disney.go.com/toontown/en/gamenews/') + override = base.config.GetString('in-game-news-url', '') + if override: + self.notify.info('got an override url, using %s for in game news' % override) + result = override + else: + try: + launcherUrl = base.launcher.getValue('GAME_IN_GAME_NEWS_URL', '') + if launcherUrl: + result = launcherUrl + self.notify.info('got GAME_IN_GAME_NEWS_URL from launcher using %s' % result) + else: + self.notify.info('blank GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) + except: + self.notify.warning('got exception getting GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) + + return result + + def calcIssueVersion(self, dateStr): + majorVer = 1 + minorVer = 0 + for entry in self.newsIndexEntries: + if 'aaver' in entry and dateStr in entry: + parts = entry.split('_') + if len(parts) > 5: + try: + majorVer = int(parts[5]) + except: + self.notify.warning('could not int %s' % parts[5]) + + else: + self.notify.warning('expected more than 5 parts in %s' % entry) + if len(parts) > 6: + try: + minorVer = int(parts[6]) + except: + self.notify.warning('could not int %s' % parts[6]) + + else: + self.notify.warning('expected more than 6 parts in %s' % entry) + break + + return (majorVer, minorVer) diff --git a/toontown/shtiker/DisguisePage.py b/toontown/shtiker/DisguisePage.py new file mode 100644 index 0000000..bc485bf --- /dev/null +++ b/toontown/shtiker/DisguisePage.py @@ -0,0 +1,196 @@ +import ShtikerPage +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.suit import SuitDNA +from toontown.battle import SuitBattleGlobals +from toontown.minigame import MinigamePowerMeter +from toontown.coghq import CogDisguiseGlobals +DeptColors = (Vec4(0.647, 0.608, 0.596, 1.0), + Vec4(0.588, 0.635, 0.671, 1.0), + Vec4(0.596, 0.714, 0.659, 1.0), + Vec4(0.761, 0.678, 0.69, 1.0)) +NumParts = max(CogDisguiseGlobals.PartsPerSuit) +PartNames = ('lUpleg', 'lLowleg', 'lShoe', 'rUpleg', 'rLowleg', 'rShoe', 'lShoulder', 'rShoulder', 'chest', 'waist', 'hip', 'lUparm', 'lLowarm', 'lHand', 'rUparm', 'rLowarm', 'rHand') + +class DisguisePage(ShtikerPage.ShtikerPage): + meterColor = Vec4(0.87, 0.87, 0.827, 1.0) + meterActiveColor = Vec4(0.7, 0.3, 0.3, 1) + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.activeTab = 0 + self.progressTitle = None + return + + def load(self): + ShtikerPage.ShtikerPage.load(self) + gui = loader.loadModel('phase_9/models/gui/cog_disguises') + self.frame = DirectFrame(parent=self, relief=None, scale=0.47, pos=(0.02, 1, 0)) + self.bkgd = DirectFrame(parent=self.frame, geom=gui.find('**/base'), relief=None, scale=(0.98, 1, 1)) + self.bkgd.setTextureOff(1) + self.tabs = [] + self.pageFrame = DirectFrame(parent=self.frame, relief=None) + for dept in SuitDNA.suitDepts: + if dept == 'c': + tabIndex = 1 + textPos = (1.57, 0.75) + elif dept == 'l': + tabIndex = 2 + textPos = (1.57, 0.12) + elif dept == 'm': + tabIndex = 3 + textPos = (1.57, -0.47) + elif dept == 's': + tabIndex = 4 + textPos = (1.57, -1.05) + pageGeom = gui.find('**/page%d' % tabIndex) + tabGeom = gui.find('**/tab%d' % tabIndex) + tab = DirectButton(parent=self.pageFrame, relief=None, geom=tabGeom, geom_color=DeptColors[tabIndex - 1], text=SuitDNA.suitDeptFullnames[dept], text_font=ToontownGlobals.getSuitFont(), text_pos=textPos, text_roll=-90, text_scale=TTLocalizer.DPtab, text_align=TextNode.ACenter, text1_fg=Vec4(1, 0, 0, 1), text2_fg=Vec4(0.5, 0.4, 0.4, 1), text3_fg=Vec4(0.4, 0.4, 0.4, 1), command=self.doTab, extraArgs=[len(self.tabs)], pressEffect=0) + self.tabs.append(tab) + page = DirectFrame(parent=tab, relief=None, geom=pageGeom) + + self.deptLabel = DirectLabel(parent=self.frame, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=TTLocalizer.DPdeptLabel, text_pos=(-0.1, 0.8)) + DirectFrame(parent=self.frame, relief=None, geom=gui.find('**/pipe_frame')) + self.tube = DirectFrame(parent=self.frame, relief=None, geom=gui.find('**/tube')) + DirectFrame(parent=self.frame, relief=None, geom=gui.find('**/robot/face')) + DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_cog_disguises'), geom_pos=(0, 0.1, 0)) + self.meritTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_merit_progress'), geom_pos=(0, 0.1, 0)) + self.meritTitle.hide() + self.cogbuckTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_cashbuck_progress'), geom_pos=(0, 0.1, 0)) + self.cogbuckTitle.hide() + self.juryNoticeTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_jury_notice_progress'), geom_pos=(0, 0.1, 0)) + self.juryNoticeTitle.hide() + self.stockOptionTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_stock_option_progress'), geom_pos=(0, 0.1, 0)) + self.stockOptionTitle.hide() + self.progressTitle = self.meritTitle + self.promotionTitle = DirectLabel(parent=self.frame, relief=None, geom=gui.find('**/text_ready4promotion'), geom_pos=(0, 0.1, 0)) + self.cogName = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=TTLocalizer.DPcogName, text_align=TextNode.ACenter, pos=(-0.948, 0, -1.15)) + self.cogLevel = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=0.09, text_align=TextNode.ACenter, pos=(-0.91, 0, -1.02)) + self.partFrame = DirectFrame(parent=self.frame, relief=None) + self.parts = [] + for partNum in range(0, NumParts): + self.parts.append(DirectFrame(parent=self.partFrame, relief=None, geom=gui.find('**/robot/' + PartNames[partNum]))) + + self.holes = [] + for partNum in range(0, NumParts): + self.holes.append(DirectFrame(parent=self.partFrame, relief=None, geom=gui.find('**/robot_hole/' + PartNames[partNum]))) + + self.cogPartRatio = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=0.08, text_align=TextNode.ACenter, pos=(-0.91, 0, -0.82)) + self.cogMeritRatio = DirectLabel(parent=self.frame, relief=None, text='', text_font=ToontownGlobals.getSuitFont(), text_scale=0.08, text_align=TextNode.ACenter, pos=(0.45, 0, -0.36)) + meterFace = gui.find('**/meter_face_whole') + meterFaceHalf = gui.find('**/meter_face_half') + self.meterFace = DirectLabel(parent=self.frame, relief=None, geom=meterFace, color=self.meterColor, pos=(0.455, 0.0, 0.04)) + self.meterFaceHalf1 = DirectLabel(parent=self.frame, relief=None, geom=meterFaceHalf, color=self.meterActiveColor, pos=(0.455, 0.0, 0.04)) + self.meterFaceHalf2 = DirectLabel(parent=self.frame, relief=None, geom=meterFaceHalf, color=self.meterColor, pos=(0.455, 0.0, 0.04)) + self.frame.hide() + self.activeTab = 3 + self.updatePage() + return + + def unload(self): + ShtikerPage.ShtikerPage.unload(self) + + def enter(self): + self.frame.show() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.frame.hide() + ShtikerPage.ShtikerPage.exit(self) + + def updatePage(self): + self.doTab(self.activeTab) + + def updatePartsDisplay(self, index, numParts, numPartsRequired): + partBitmask = 1 + groupingBitmask = CogDisguiseGlobals.PartsPerSuitBitmasks[index] + previousPart = 0 + for part in self.parts: + groupingBit = groupingBitmask & partBitmask + if numParts & partBitmask & groupingBit: + part.show() + self.holes[self.parts.index(part)].hide() + if groupingBit: + previousPart = 1 + elif not groupingBit and previousPart: + part.show() + self.holes[self.parts.index(part)].hide() + else: + self.holes[self.parts.index(part)].show() + part.hide() + previousPart = 0 + partBitmask = partBitmask << 1 + + def updateMeritBar(self, dept): + merits = base.localAvatar.cogMerits[dept] + totalMerits = CogDisguiseGlobals.getTotalMerits(base.localAvatar, dept) + if totalMerits == 0: + progress = 1 + else: + progress = min(merits / float(totalMerits), 1) + self.updateMeritDial(progress) + if base.localAvatar.readyForPromotion(dept): + self.cogMeritRatio['text'] = TTLocalizer.DisguisePageMeritFull + self.promotionTitle.show() + self.progressTitle.hide() + else: + self.cogMeritRatio['text'] = '%d/%d' % (merits, totalMerits) + self.promotionTitle.hide() + self.progressTitle.show() + + def updateMeritDial(self, progress): + if progress == 0: + self.meterFaceHalf1.hide() + self.meterFaceHalf2.hide() + self.meterFace.setColor(self.meterColor) + elif progress == 1: + self.meterFaceHalf1.hide() + self.meterFaceHalf2.hide() + self.meterFace.setColor(self.meterActiveColor) + else: + self.meterFaceHalf1.show() + self.meterFaceHalf2.show() + self.meterFace.setColor(self.meterColor) + if progress < 0.5: + self.meterFaceHalf2.setColor(self.meterColor) + else: + self.meterFaceHalf2.setColor(self.meterActiveColor) + progress = progress - 0.5 + self.meterFaceHalf2.setR(180 * (progress / 0.5)) + + def doTab(self, index): + self.activeTab = index + self.tabs[index].reparentTo(self.pageFrame) + for i in range(len(self.tabs)): + tab = self.tabs[i] + if i == index: + tab['text0_fg'] = (1, 0, 0, 1) + tab['text2_fg'] = (1, 0, 0, 1) + else: + tab['text0_fg'] = (0, 0, 0, 1) + tab['text2_fg'] = (0.5, 0.4, 0.4, 1) + + self.bkgd.setColor(DeptColors[index]) + self.deptLabel['text'] = (SuitDNA.suitDeptFullnames[SuitDNA.suitDepts[index]],) + cogIndex = base.localAvatar.cogTypes[index] + SuitDNA.suitsPerDept * index + cog = SuitDNA.suitHeadTypes[cogIndex] + self.progressTitle.hide() + if SuitDNA.suitDepts[index] == 'm': + self.progressTitle = self.cogbuckTitle + elif SuitDNA.suitDepts[index] == 'l': + self.progressTitle = self.juryNoticeTitle + elif SuitDNA.suitDepts[index] == 'c': + self.progressTitle = self.stockOptionTitle + else: + self.progressTitle = self.meritTitle + self.progressTitle.show() + self.cogName['text'] = SuitBattleGlobals.SuitAttributes[cog]['name'] + cogLevel = base.localAvatar.cogLevels[index] + self.cogLevel['text'] = TTLocalizer.DisguisePageCogLevel % str(cogLevel + 1) + numParts = base.localAvatar.cogParts[index] + numPartsRequired = CogDisguiseGlobals.PartsPerSuit[index] + self.updatePartsDisplay(index, numParts, numPartsRequired) + self.updateMeritBar(index) + self.cogPartRatio['text'] = '%d/%d' % (CogDisguiseGlobals.getTotalParts(numParts), numPartsRequired) diff --git a/toontown/shtiker/DisplaySettingsDialog.py b/toontown/shtiker/DisplaySettingsDialog.py new file mode 100644 index 0000000..90ebdf4 --- /dev/null +++ b/toontown/shtiker/DisplaySettingsDialog.py @@ -0,0 +1,444 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.task.Task import Task +from direct.fsm import StateData +from direct.showbase import AppRunnerGlobal +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.DisplayOptions import DisplayOptions + +class DisplaySettingsDialog(DirectFrame, StateData.StateData): + ApplyTimeoutSeconds = 15 + TimeoutCountdownTask = 'DisplaySettingsTimeoutCountdown' + WindowedMode = 0 + FullscreenMode = 1 + EmbeddedMode = 2 + notify = DirectNotifyGlobal.directNotify.newCategory('DisplaySettingsDialog') + + def __init__(self): + DirectFrame.__init__(self, pos=(0, 0, 0.3), relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 1.2), image_pos=(0, 0, -0.05), image_color=ToontownGlobals.GlobalDialogColor, text=TTLocalizer.DisplaySettingsTitle, text_scale=0.12, text_pos=(0, 0.4), borderWidth=(0.01, 0.01)) + StateData.StateData.__init__(self, 'display-settings-done') + self.setBin('gui-popup', 0) + self.initialiseoptions(DisplaySettingsDialog) + return + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + DirectFrame.destroy(self) + return None + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + self.anyChanged = 0 + self.apiChanged = 0 + self.screenSizes = ((640, 480), + (800, 600), + (1024, 768), + (1280, 1024), + (1600, 1200)) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + nameShopGui = loader.loadModel('phase_3/models/gui/nameshop_gui') + circle = nameShopGui.find('**/namePanelCircle') + innerCircle = circle.copyTo(hidden) + innerCircle.setPos(0, 0, 0.2) + self.c1b = circle.copyTo(self, -1) + self.c1b.setColor(0, 0, 0, 1) + self.c1b.setPos(0.044, 0, -0.21) + 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, -2) + self.c2b.setColor(0, 0, 0, 1) + self.c2b.setPos(0.044, 0, -0.3) + 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, -2) + self.c3b.setColor(0, 0, 0, 1) + self.c3b.setPos(0.044, 0, -0.4) + self.c3b.setScale(0.4) + c3f = circle.copyTo(self.c3b) + c3f.setColor(1, 1, 1, 1) + c3f.setScale(0.8) + self.introText = DirectLabel(parent=self, relief=None, scale=TTLocalizer.DSDintroText, text=TTLocalizer.DisplaySettingsIntro, text_wordwrap=TTLocalizer.DSDintroTextWordwrap, text_align=TextNode.ALeft, pos=(-0.725, 0, 0.3)) + self.introTextSimple = DirectLabel(parent=self, relief=None, scale=0.06, text=TTLocalizer.DisplaySettingsIntroSimple, text_wordwrap=25, text_align=TextNode.ALeft, pos=(-0.725, 0, 0.3)) + self.apiLabel = DirectLabel(parent=self, relief=None, scale=0.06, text=TTLocalizer.DisplaySettingsApi, text_align=TextNode.ARight, pos=(-0.08, 0, 0)) + self.apiMenu = DirectOptionMenu(parent=self, relief=DGG.RAISED, scale=0.06, items=['x'], pos=(0, 0, 0)) + self.screenSizeLabel = DirectLabel(parent=self, relief=None, scale=0.06, text=TTLocalizer.DisplaySettingsResolution, text_align=TextNode.ARight, pos=(-0.08, 0, -0.1)) + self.screenSizeLeftArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), scale=(-1.0, 1.0, 1.0), pos=(0.04, 0, -0.085), command=self.__doScreenSizeLeft) + self.screenSizeRightArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), pos=(0.54, 0, -0.085), command=self.__doScreenSizeRight) + self.screenSizeValueText = DirectLabel(parent=self, relief=None, text='x', text_align=TextNode.ACenter, text_scale=0.06, pos=(0.29, 0, -0.1)) + self.windowedButton = DirectCheckButton(parent=self, relief=None, text=TTLocalizer.DisplaySettingsWindowed, text_align=TextNode.ALeft, text_scale=0.6, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=TTLocalizer.DSDwindowedButtonPos, command=self.__doWindowed) + self.fullscreenButton = DirectCheckButton(parent=self, relief=None, text=TTLocalizer.DisplaySettingsFullscreen, text_align=TextNode.ALeft, text_scale=0.6, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=TTLocalizer.DSDfullscreenButtonPos, command=self.__doFullscreen) + self.embeddedButton = DirectCheckButton(parent=self, relief=None, text=TTLocalizer.DisplaySettingsEmbedded, text_align=TextNode.ALeft, text_scale=0.6, scale=0.1, boxImage=innerCircle, boxImageScale=2.5, boxImageColor=VBase4(0, 0.25, 0.5, 1), boxRelief=None, pos=TTLocalizer.DSDembeddedButtonPos, command=self.__doEmbedded) + self.apply = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.6, 1, 1), text=TTLocalizer.DisplaySettingsApply, text_scale=0.06, text_pos=(0, -0.02), pos=(0.52, 0, -0.53), command=self.__apply) + self.cancel = DirectButton(parent=self, relief=None, text=TTLocalizer.DisplaySettingsCancel, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(0.6, 1, 1), text_scale=TTLocalizer.DSDcancel, text_pos=TTLocalizer.DSDcancelPos, pos=(0.2, 0, -0.53), command=self.__cancel) + guiButton.removeNode() + gui.removeNode() + nameShopGui.removeNode() + innerCircle.removeNode() + self.hide() + return + + def enter(self, changeDisplaySettings, changeDisplayAPI): + if self.isEntered == 1: + return None + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + self.applyDialog = None + self.timeoutDialog = None + self.restoreDialog = None + self.revertDialog = None + base.transitions.fadeScreen(0.5) + properties = base.win.getProperties() + self.screenSizeIndex = self.chooseClosestScreenSize(properties.getXSize(), properties.getYSize()) + self.isFullscreen = properties.getFullscreen() + if self.isCurrentlyEmbedded(): + self.displayMode = self.EmbeddedMode + elif self.isFullscreen: + self.displayMode = self.FullscreenMode + else: + self.displayMode = self.WindowedMode + self.updateApiMenu(changeDisplaySettings, changeDisplayAPI) + self.updateWindowed() + self.updateScreenSize() + if changeDisplaySettings: + self.introText.show() + self.introTextSimple.hide() + if changeDisplayAPI and len(self.apis) > 1: + self.apiLabel.show() + self.apiMenu.show() + else: + self.apiLabel.hide() + self.apiMenu.hide() + if DisplayOptions.isWindowedPossible(): + self.c1b.show() + self.windowedButton.show() + else: + self.c1b.hide() + self.windowedButton.hide() + self.c2b.show() + self.fullscreenButton.show() + if DisplayOptions.isEmbeddedPossible(): + self.c3b.show() + self.embeddedButton.show() + else: + self.c3b.hide() + self.embeddedButton.hide() + else: + self.introText.hide() + self.introTextSimple.show() + self.apiLabel.hide() + self.apiMenu.hide() + self.windowedButton.hide() + self.fullscreenButton.hide() + self.c1b.hide() + self.c2b.hide() + self.c3b.hide() + self.anyChanged = 0 + self.apiChanged = 0 + self.show() + return + + def exit(self): + if self.isEntered == 0: + return None + self.isEntered = 0 + self.cleanupDialogs() + base.transitions.noTransitions() + taskMgr.remove(self.TimeoutCountdownTask) + self.ignoreAll() + self.hide() + messenger.send(self.doneEvent, [self.anyChanged, self.apiChanged]) + return None + + def cleanupDialogs(self): + if self.applyDialog != None: + self.applyDialog.cleanup() + self.applyDialog = None + if self.timeoutDialog != None: + self.timeoutDialog.cleanup() + self.timeoutDialog = None + if self.restoreDialog != None: + self.restoreDialog.cleanup() + self.restoreDialog = None + if self.revertDialog != None: + self.revertDialog.cleanup() + self.revertDialog = None + return + + def updateApiMenu(self, changeDisplaySettings, changeDisplayAPI): + self.apis = [] + self.apiPipes = [] + if changeDisplayAPI: + base.makeAllPipes() + for pipe in base.pipeList: + if pipe.isValid(): + self.apiPipes.append(pipe) + self.apis.append(pipe.getInterfaceName()) + + self.apiMenu['items'] = self.apis + self.apiMenu.set(base.pipe.getInterfaceName()) + + def updateWindowed(self): + if self.displayMode == self.FullscreenMode: + self.windowedButton['indicatorValue'] = 0 + self.fullscreenButton['indicatorValue'] = 1 + self.embeddedButton['indicatorValue'] = 0 + elif self.displayMode == self.WindowedMode: + self.windowedButton['indicatorValue'] = 1 + self.fullscreenButton['indicatorValue'] = 0 + self.embeddedButton['indicatorValue'] = 0 + elif self.displayMode == self.EmbeddedMode: + self.windowedButton['indicatorValue'] = 0 + self.fullscreenButton['indicatorValue'] = 0 + self.embeddedButton['indicatorValue'] = 1 + + def updateScreenSize(self): + xSize, ySize = self.screenSizes[self.screenSizeIndex] + self.screenSizeValueText['text'] = '%s x %s' % (xSize, ySize) + if self.screenSizeIndex > 0: + self.screenSizeLeftArrow.show() + else: + self.screenSizeLeftArrow.hide() + if self.screenSizeIndex < len(self.screenSizes) - 1: + self.screenSizeRightArrow.show() + else: + self.screenSizeRightArrow.hide() + + def chooseClosestScreenSize(self, currentXSize, currentYSize): + for i in range(len(self.screenSizes)): + xSize, ySize = self.screenSizes[i] + if currentXSize == xSize and currentYSize == ySize: + return i + + currentCount = currentXSize * currentYSize + bestDiff = None + bestI = None + for i in range(len(self.screenSizes)): + xSize, ySize = self.screenSizes[i] + diff = abs(xSize * ySize - currentCount) + if bestI == None or diff < bestDiff: + bestI = i + bestDiff = diff + + return bestI + + def __doWindowed(self, value): + self.displayMode = self.WindowedMode + self.updateWindowed() + + def __doFullscreen(self, value): + self.displayMode = self.FullscreenMode + self.updateWindowed() + + def __doEmbedded(self, value): + self.displayMode = self.EmbeddedMode + self.updateWindowed() + + def __doScreenSizeLeft(self): + if self.screenSizeIndex > 0: + self.screenSizeIndex = self.screenSizeIndex - 1 + self.updateScreenSize() + + def __doScreenSizeRight(self): + if self.screenSizeIndex < len(self.screenSizes) - 1: + self.screenSizeIndex = self.screenSizeIndex + 1 + self.updateScreenSize() + + def __apply(self): + self.cleanupDialogs() + self.clearBin() + self.applyDialog = TTDialog.TTDialog(dialogName='DisplaySettingsApply', style=TTDialog.TwoChoice, text=TTLocalizer.DisplaySettingsApplyWarning % self.ApplyTimeoutSeconds, text_wordwrap=15, command=self.__applyDone) + self.applyDialog.setBin('gui-popup', 0) + + def __applyDone(self, command): + self.applyDialog.cleanup() + self.applyDialog = None + self.setBin('gui-popup', 0) + base.transitions.fadeScreen(0.5) + if command != DGG.DIALOG_OK: + return + self.origPipe = base.pipe + self.origProperties = base.win.getProperties() + pipe = self.apiPipes[self.apiMenu.selectedIndex] + properties = WindowProperties() + xSize, ySize = self.screenSizes[self.screenSizeIndex] + properties.setSize(xSize, ySize) + properties.setFullscreen(self.displayMode == self.FullscreenMode) + fullscreen = self.displayMode == self.FullscreenMode + embedded = self.displayMode == self.EmbeddedMode + if embedded: + if DisplayOptions.isEmbeddedPossible(): + pass + else: + self.notify.warning('how was the player able to choose embedded') + embedded = False + if not self.changeDisplayProperties(pipe, xSize, ySize, fullscreen, embedded): + self.__revertBack(1) + return + self.clearBin() + self.timeoutDialog = TTDialog.TTDialog(dialogName='DisplaySettingsTimeout', style=TTDialog.TwoChoice, text=TTLocalizer.DisplaySettingsAccept % self.ApplyTimeoutSeconds, text_wordwrap=15, command=self.__timeoutDone) + self.timeoutDialog.setBin('gui-popup', 0) + self.timeoutRemaining = self.ApplyTimeoutSeconds + self.timeoutStart = None + taskMgr.add(self.__timeoutCountdown, self.TimeoutCountdownTask) + return + + def changeDisplayProperties(self, pipe, width, height, fullscreen = False, embedded = False): + result = False + self.notify.info('changeDisplayProperties') + if embedded: + if DisplayOptions.isEmbeddedPossible(): + width = base.appRunner.windowProperties.getXSize() + height = base.appRunner.windowProperties.getYSize() + self.current_pipe = base.pipe + self.current_properties = WindowProperties(base.win.getProperties()) + properties = self.current_properties + self.notify.debug('DISPLAY PREVIOUS:') + self.notify.debug(' EMBEDDED: %s' % bool(properties.getParentWindow())) + self.notify.debug(' FULLSCREEN: %s' % bool(properties.getFullscreen())) + self.notify.debug(' X SIZE: %s' % properties.getXSize()) + self.notify.debug(' Y SIZE: %s' % properties.getYSize()) + self.notify.debug('DISPLAY REQUESTED:') + self.notify.debug(' EMBEDDED: %s' % bool(embedded)) + self.notify.debug(' FULLSCREEN: %s' % bool(fullscreen)) + self.notify.debug(' X SIZE: %s' % width) + self.notify.debug(' Y SIZE: %s' % height) + if self.current_pipe == pipe and bool(self.current_properties.getParentWindow()) == bool(embedded) and self.current_properties.getFullscreen() == fullscreen and self.current_properties.getXSize() == width and self.current_properties.getYSize() == height: + self.notify.info('DISPLAY NO CHANGE REQUIRED') + state = True + else: + properties = WindowProperties() + properties.setSize(width, height) + properties.setFullscreen(fullscreen) + properties.setParentWindow(0) + if embedded: + properties = base.appRunner.windowProperties + original_sort = base.win.getSort() + if self.resetDisplayProperties(pipe, properties): + self.notify.debug('DISPLAY CHANGE SET') + properties = base.win.getProperties() + self.notify.debug('DISPLAY ACHIEVED:') + self.notify.debug(' EMBEDDED: %s' % bool(properties.getParentWindow())) + self.notify.debug(' FULLSCREEN: %s' % bool(properties.getFullscreen())) + self.notify.debug(' X SIZE: %s' % properties.getXSize()) + self.notify.debug(' Y SIZE: %s' % properties.getYSize()) + if bool(properties.getParentWindow()) == bool(embedded) and properties.getFullscreen() == fullscreen and properties.getXSize() == width and properties.getYSize() == height: + self.notify.info('DISPLAY CHANGE VERIFIED') + result = True + else: + self.notify.warning('DISPLAY CHANGE FAILED, RESTORING PREVIOUS DISPLAY') + else: + self.notify.warning('DISPLAY CHANGE FAILED') + base.win.setSort(original_sort) + base.graphicsEngine.renderFrame() + base.graphicsEngine.renderFrame() + return result + + def __timeoutCountdown(self, task): + if self.timeoutStart == None: + self.timeoutStart = globalClock.getRealTime() + elapsed = int(globalClock.getFrameTime() - self.timeoutStart) + remaining = max(self.ApplyTimeoutSeconds - elapsed, 0) + if remaining < self.timeoutRemaining: + self.timeoutRemaining = remaining + self.timeoutDialog['text'] = (TTLocalizer.DisplaySettingsAccept % remaining,) + if remaining == 0: + self.__timeoutDone('cancel') + return Task.done + return Task.cont + + def __timeoutDone(self, command): + taskMgr.remove(self.TimeoutCountdownTask) + self.timeoutDialog.cleanup() + self.timeoutDialog = None + self.setBin('gui-popup', 0) + base.transitions.fadeScreen(0.5) + if command == DGG.DIALOG_OK: + self.anyChanged = 1 + self.exit() + return + self.__revertBack(0) + return + + def __revertBack(self, reason): + if not self.resetDisplayProperties(self.origPipe, self.origProperties): + self.notify.warning("Couldn't restore original display settings!") + base.panda3dRenderError() + self.clearBin() + if reason == 0: + revertText = TTLocalizer.DisplaySettingsRevertUser + else: + revertText = TTLocalizer.DisplaySettingsRevertFailed + self.revertDialog = TTDialog.TTDialog(dialogName='DisplaySettingsRevert', style=TTDialog.Acknowledge, text=revertText, text_wordwrap=15, command=self.__revertDone) + self.revertDialog.setBin('gui-popup', 0) + + def __revertDone(self, command): + self.revertDialog.cleanup() + self.revertDialog = None + self.setBin('gui-popup', 0) + base.transitions.fadeScreen(0.5) + return + + def __cancel(self): + self.exit() + + def resetDisplayProperties(self, pipe, properties): + if base.win: + currentProperties = base.win.getProperties() + gsg = base.win.getGsg() + else: + currentProperties = WindowProperties.getDefault() + gsg = None + newProperties = WindowProperties(currentProperties) + newProperties.addProperties(properties) + if base.pipe != pipe: + self.apiChanged = 1 + gsg = None + if gsg == None or currentProperties.getFullscreen() != newProperties.getFullscreen() or currentProperties.getParentWindow() != newProperties.getParentWindow(): + self.notify.debug('window properties: %s' % properties) + self.notify.debug('gsg: %s' % gsg) + base.pipe = pipe + if not base.openMainWindow(props=properties, gsg=gsg, keepCamera=True): + self.notify.warning('OPEN MAIN WINDOW FAILED') + return 0 + self.notify.info('OPEN MAIN WINDOW PASSED') + base.disableShowbaseMouse() + NametagGlobals.setCamera(base.cam) + NametagGlobals.setMouseWatcher(base.mouseWatcherNode) + base.graphicsEngine.renderFrame() + base.graphicsEngine.renderFrame() + base.graphicsEngine.openWindows() + if base.win.isClosed(): + self.notify.info('Window did not open, removing.') + base.closeWindow(base.win) + return 0 + else: + self.notify.debug('Adjusting properties') + base.win.requestProperties(properties) + base.graphicsEngine.renderFrame() + return 1 + + def isCurrentlyEmbedded(self): + result = False + if base.win.getProperties().getParentWindow(): + result = True + return result diff --git a/toontown/shtiker/EventsPage.py b/toontown/shtiker/EventsPage.py new file mode 100644 index 0000000..8c2cebf --- /dev/null +++ b/toontown/shtiker/EventsPage.py @@ -0,0 +1,1062 @@ +import urllib +from pandac.PandaModules import Vec4, Vec3, TextNode, PNMImage, StringStream, Texture, HTTPClient, DocumentSpec, Ramfile, Point3 +from direct.task.Task import Task +from direct.gui.DirectGui import DirectFrame, DirectLabel, DirectButton, DirectScrolledList, DirectCheckButton, OnscreenText +from direct.gui import DirectGuiGlobals +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TTDialog +from toontown.parties import PartyGlobals +from toontown.parties import PartyUtils +from toontown.parties.CalendarGuiMonth import CalendarGuiMonth +from toontown.parties.PartyUtils import getPartyActivityIcon +from toontown.parties.Party import Party +from toontown.parties.ServerTimeGui import ServerTimeGui +import ShtikerPage +EventsPage_Host = 0 +EventsPage_Invited = 1 +EventsPage_Calendar = 2 +EventsPage_News = 3 + +class EventsPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('EventsPage') + UseNewsTab = base.config.GetBool('want-news-tab', 0) + DefaultNewsUrl = '/news/news_urls.txt' + NewsUrl = base.config.GetString('news-url', DefaultNewsUrl) + DownloadArticlesTaskName = 'downloadArticlesTask' + NonblockingDownload = base.config.GetBool('news-nonblocking', 1) + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.mode = EventsPage_Calendar + self.setMode(self.mode) + self.noTeleport = config.GetBool('Parties-page-disable', 0) + self.isPrivate = True + self.gotRssFeed = False + self.gotArticles = False + self.newsList = None + self.articleTextList = None + self.articleIndexList = None + self.hostedPartyInfo = None + self.downloadArticlesInProgress = False + return + + def load(self): + self.scrollButtonGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.hostingGui = loader.loadModel('phase_4/models/parties/schtickerbookHostingGUI') + self.invitationGui = loader.loadModel('phase_4/models/parties/schtickerbookInvitationGUI') + self.activityIconsModel = loader.loadModel('phase_4/models/parties/eventSignIcons') + self.decorationModels = loader.loadModel('phase_4/models/parties/partyDecorations') + self.loadTabs() + self.loadHostingTab() + self.loadInvitationsTab() + self.loadCalendarTab() + self.loadNewsTab() + self.titleLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.EventsPageHostTabTitle, text_scale=TTLocalizer.EPtitleLabel, textMayChange=True, pos=self.hostingGui.find('**/myNextParty_text_locator').getPos()) + return + + def loadTabs(self): + normalColor = (1.0, 1.0, 1.0, 1.0) + clickColor = (0.8, 0.8, 0.0, 1.0) + rolloverColor = (0.15, 0.82, 1.0, 1.0) + diabledColor = (1.0, 0.98, 0.15, 1.0) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + self.hostTab = DirectButton(parent=self, relief=None, text=TTLocalizer.EventsPageHostTabName, text_scale=TTLocalizer.EPhostTab, text_align=TextNode.ACenter, text_pos=(0.12, 0.0), image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[EventsPage_Host], pos=(0.92, 0, 0.55)) + self.invitedTab = DirectButton(parent=self, relief=None, text=TTLocalizer.EventsPageInvitedTabName, text_scale=TTLocalizer.EPinvitedTab, text_pos=(0.12, 0.0), text_align=TextNode.ACenter, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[EventsPage_Invited], pos=(0.92, 0, 0.1)) + self.calendarTab = DirectButton(parent=self, relief=None, text=TTLocalizer.EventsPageCalendarTabName, text_scale=TTLocalizer.EPcalendarTab, text_pos=(0.12, 0.0), text_align=TextNode.ACenter, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[EventsPage_Calendar], pos=(0.92, 0, 0.1)) + self.newsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.EventsPageNewsTabName, text_scale=TTLocalizer.EPnewsTab, text_pos=(0.12, 0.0), text_align=TextNode.ACenter, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[EventsPage_News], pos=(0.92, 0, 0.1)) + self.newsTab.hide() + if self.UseNewsTab: + self.newsTab.show() + self.calendarTab.setPos(-0.75, 0, 0.775) + self.hostTab.setPos(-0.33, 0, 0.775) + self.invitedTab.setPos(0.09, 0, 0.775) + self.newsTab.setPos(0.51, 0, 0.775) + else: + self.calendarTab.setPos(-0.55, 0, 0.775) + self.hostTab.setPos(-0.13, 0, 0.775) + self.invitedTab.setPos(0.28, 0, 0.775) + return + + def loadHostingTab(self): + self.hostedPartyDisplay = self.attachNewNode('Hosting') + self.hostedPartyDisplay.setPos(0.0, 0.0, 0.04) + self.hostingBackgroundFlat = DirectFrame(parent=self.hostedPartyDisplay, relief=None, geom=self.hostingGui.find('**/background_flat')) + self.hostingGuestList, self.hostingGuestLabel = self.createListAndLabel(self.hostedPartyDisplay, self.hostingGui, 'guests', 7) + self.hostingActivityList, self.hostingActivityLabel = self.createListAndLabel(self.hostedPartyDisplay, self.hostingGui, 'activities', 1) + self.hostingDecorationList, self.hostingDecorationLabel = self.createListAndLabel(self.hostedPartyDisplay, self.hostingGui, 'decorations', 1) + self.hostingDateLabel = DirectLabel(parent=self.hostedPartyDisplay, relief=None, text='', scale=TTLocalizer.EPhostingDateLabel, text_align=TextNode.ACenter, text_wordwrap=10, textMayChange=True, pos=self.hostingGui.find('**/date_locator').getPos()) + pos = self.hostingGui.find('**/cancel_text_locator').getPos() + self.hostingCancelButton = DirectButton(parent=self.hostedPartyDisplay, relief=None, geom=(self.hostingGui.find('**/cancelPartyButton_up'), + self.hostingGui.find('**/cancelPartyButton_down'), + self.hostingGui.find('**/cancelPartyButton_rollover'), + self.hostingGui.find('**/cancelPartyButton_inactive')), text=TTLocalizer.EventsPageHostTabCancelButton, text_scale=TTLocalizer.EPhostingCancelButton, text_pos=(pos[0], pos[2]), command=self.__doCancelParty) + pos = self.hostingGui.find('**/startParty_text_locator').getPos() + self.partyGoButton = DirectButton(parent=self.hostedPartyDisplay, relief=None, geom=(self.hostingGui.find('**/startPartyButton_up'), + self.hostingGui.find('**/startPartyButton_down'), + self.hostingGui.find('**/startPartyButton_rollover'), + self.hostingGui.find('**/startPartyButton_inactive')), text=TTLocalizer.EventsPageGoButton, text_scale=TTLocalizer.EPpartyGoButton, text_pos=(pos[0], pos[2]), textMayChange=True, command=self._startParty) + self.publicPrivateLabel = DirectLabel(parent=self.hostedPartyDisplay, relief=None, text=TTLocalizer.EventsPageHostTabPublicPrivateLabel, text_scale=TTLocalizer.EPpublicPrivateLabel, text_align=TextNode.ACenter, pos=self.hostingGui.find('**/thisPartyIs_text_locator').getPos()) + pos = self.hostingGui.find('**/public_text_locator').getPos() + checkedImage = self.hostingGui.find('**/checked_button') + uncheckedImage = self.hostingGui.find('**/unchecked_button') + self.publicButton = DirectCheckButton(parent=self.hostedPartyDisplay, relief=None, scale=0.1, boxBorder=0.08, boxImage=(uncheckedImage, checkedImage, None), boxImageScale=10, boxRelief=None, text=TTLocalizer.EventsPageHostTabToggleToPublic, text_align=TextNode.ALeft, text_scale=TTLocalizer.EPpublicButton, pos=pos, command=self.__changePublicPrivate, indicator_pos=(-0.7, 0, 0.2)) + pos = self.hostingGui.find('**/private_text_locator').getPos() + self.privateButton = DirectCheckButton(parent=self.hostedPartyDisplay, relief=None, scale=0.1, boxBorder=0.08, boxImage=(uncheckedImage, checkedImage, None), boxImageScale=10, boxRelief=None, text=TTLocalizer.EventsPageHostTabToggleToPrivate, text_align=TextNode.ALeft, text_scale=TTLocalizer.EPprivateButton, pos=pos, command=self.__changePublicPrivate, indicator_pos=(-0.7, 0, 0.2)) + self.confirmCancelPartyEvent = 'confirmCancelPartyEvent' + self.accept(self.confirmCancelPartyEvent, self.confirmCancelOfParty) + self.confirmCancelPartyGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('confirmCancelPartyGui'), doneEvent=self.confirmCancelPartyEvent, message=TTLocalizer.EventsPageConfirmCancel % int(PartyGlobals.PartyRefundPercentage * 100.0), style=TTDialog.YesNo, okButtonText=OTPLocalizer.DialogYes, cancelButtonText=OTPLocalizer.DialogNo) + self.confirmCancelPartyGui.doneStatus = '' + self.confirmCancelPartyGui.hide() + self.confirmTooLatePartyEvent = 'confirmTooLatePartyEvent' + self.accept(self.confirmTooLatePartyEvent, self.confirmTooLateParty) + self.confirmTooLatePartyGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('confirmTooLatePartyGui'), doneEvent=self.confirmTooLatePartyEvent, message=TTLocalizer.EventsPageTooLateToStart, style=TTDialog.Acknowledge) + self.confirmTooLatePartyGui.hide() + self.confirmPublicPrivateChangeEvent = 'confirmPublicPrivateChangeEvent' + self.accept(self.confirmPublicPrivateChangeEvent, self.confirmPublicPrivateChange) + self.confirmPublicPrivateGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('confirmPublicPrivateGui'), doneEvent=self.confirmPublicPrivateChangeEvent, message=TTLocalizer.EventsPagePublicPrivateNoGo, style=TTDialog.Acknowledge) + self.confirmPublicPrivateGui.hide() + self.cancelPartyResultGuiEvent = 'cancelPartyResultGuiEvent' + self.accept(self.cancelPartyResultGuiEvent, self.cancelPartyResultGuiCommand) + self.cancelPartyResultGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('cancelPartyResultGui'), doneEvent=self.cancelPartyResultGuiEvent, message=TTLocalizer.EventsPageCancelPartyResultOk % 0, style=TTDialog.Acknowledge) + self.cancelPartyResultGui.doneStatus = '' + self.cancelPartyResultGui.hide() + self.__setPublicPrivateButton() + return + + def loadInvitationsTab(self): + self.invitationDisplay = self.attachNewNode('invitations') + self.invitationDisplay.setPos(0.0, 0.0, 0.04) + self.invitationBackgroundFlat = DirectFrame(parent=self.invitationDisplay, relief=None, geom=self.invitationGui.find('**/background_flat')) + self.invitationPartiesFlat = DirectFrame(parent=self.invitationDisplay, relief=None, geom=self.invitationGui.find('**/parties_background')) + self.invitationActivtiesFlat = DirectFrame(parent=self.invitationDisplay, relief=None, geom=self.invitationGui.find('**/activities_background')) + self.invitationPartyList, self.invitationPartyLabel = self.createListAndLabel(self.invitationDisplay, self.invitationGui, 'parties', 7, 'ButtonDown', 'ButtonUp', 'Text_locator') + self.invitationActivityList, self.invitationActivityLabel = self.createListAndLabel(self.invitationDisplay, self.invitationGui, 'activities', 1, 'ButtonDown', 'ButtonUp', 'Text_locator') + pos = self.invitationGui.find('**/startText_locator').getPos() + self.invitePartyGoButton = DirectButton(parent=self.invitationDisplay, relief=None, geom=(self.invitationGui.find('**/startButton_up'), + self.invitationGui.find('**/startButton_down'), + self.invitationGui.find('**/startButton_rollover'), + self.invitationGui.find('**/startButton_inactive')), text=TTLocalizer.EventsPageInviteGoButton, text_scale=TTLocalizer.EPinvitePartyGoButton, text_pos=(pos[0], pos[2]), textMayChange=True, command=self._inviteStartParty) + self.invitationDateTimeLabel = DirectLabel(parent=self.invitationDisplay, relief=None, text='', textMayChange=True, text_scale=0.07, pos=(0, 0, -0.65)) + return + + def loadCalendarTab(self): + self.calendarDisplay = self.attachNewNode('calendar') + self.toontownTimeLabel = DirectLabel(parent=self.calendarDisplay, pos=(0.175, 0, -0.69), text_align=TextNode.ARight, relief=None, text=TTLocalizer.EventsPageToontownTimeIs, text_scale=0.065, text_font=ToontownGlobals.getMinnieFont(), text_fg=(255 / 255.0, + 146 / 255.0, + 113 / 255.0, + 1), textMayChange=0) + curServerDate = base.cr.toontownTimeManager.getCurServerDateTime() + self.calendarGuiMonth = CalendarGuiMonth(self.calendarDisplay, curServerDate, onlyFutureMonthsClickable=True) + pos = (0.35, 0, -0.69) + self.toontownTimeGui = ServerTimeGui(self.calendarDisplay, pos) + return + + def loadNewsTab(self): + self.newsDisplay = self.attachNewNode('news') + newspaper = loader.loadModel('phase_4/models/parties/tt_m_gui_sbk_newspaper.bam') + self.newsFrame = DirectLabel(relief=None, parent=self.newsDisplay, pos=(0, 0, -0.1)) + newspaper.reparentTo(self.newsFrame) + self.createArticleTextList() + self.articleImage = None + self.newsStatusLabel = DirectLabel(text=TTLocalizer.EventsPageNewsDownloading, relief=None, text_scale=0.1, text_wordwrap=13, parent=self.newsFrame, pos=(0, 0, 0.275)) + self.createArticleIndexList() + titlePos = self.newsFrame.find('**/loc_toontimeTimes').getPos() + self.newsPaperTitle = DirectLabel(text=TTLocalizer.EventsPageNewsPaperTitle, relief=None, text_scale=(0.13, 0.25, 1), text_align=TextNode.ACenter, text_font=ToontownGlobals.getMinnieFont(), parent=self.newsFrame, pos=titlePos) + subLeftPos = self.newsFrame.find('**/loc_subheaderLf').getPos() + subRightPos = self.newsFrame.find('**/loc_subheaderRt').getPos() + self.subLeft = DirectLabel(text=TTLocalizer.EventsPageNewsLeftSubtitle, relief=None, text_scale=0.05, text_align=TextNode.ALeft, parent=self.newsFrame, pos=subLeftPos) + self.subRight = DirectLabel(text=TTLocalizer.EventsPageNewsRightSubtitle, relief=None, text_scale=0.05, text_align=TextNode.ARight, parent=self.newsFrame, pos=subRightPos) + if self.UseNewsTab: + self.downloadArticles() + return + + def getGuestItem(self, name, inviteStatus): + label = DirectLabel(relief=None, text=name, text_scale=0.045, text_align=TextNode.ALeft, textMayChange=True) + dot = DirectFrame(relief=None, geom=self.hostingGui.find('**/questionMark'), pos=(0.5, 0.0, 0.01)) + if inviteStatus == PartyGlobals.InviteStatus.Accepted: + dot['geom'] = (self.hostingGui.find('**/checkmark'),) + elif inviteStatus == PartyGlobals.InviteStatus.Rejected: + dot['geom'] = (self.hostingGui.find('**/x'),) + PartyUtils.truncateTextOfLabelBasedOnWidth(label, name, PartyGlobals.EventsPageGuestNameMaxWidth) + dot.reparentTo(label) + return label + + def getActivityItem(self, activityBase, count = 1): + activityName = TTLocalizer.PartyActivityNameDict[activityBase.activityId]['generic'] + if count == 1: + textForActivity = activityName + else: + textForActivity = '%s x %d' % (activityName, count) + iconString = PartyGlobals.ActivityIds.getString(activityBase.activityId) + geom = getPartyActivityIcon(self.activityIconsModel, iconString) + label = DirectLabel(relief=None, geom=geom, geom_scale=0.38, geom_pos=Vec3(0.0, 0.0, -0.17), text=textForActivity, text_scale=TTLocalizer.EPactivityItemLabel, text_align=TextNode.ACenter, text_pos=(-0.01, -0.43), text_wordwrap=7.0) + return label + + def getDecorationItem(self, decorBase, count = 1): + decorationName = TTLocalizer.PartyDecorationNameDict[decorBase.decorId]['editor'] + if count == 1: + textForDecoration = decorationName + else: + textForDecoration = decorationName + ' x ' + str(count) + assetName = PartyGlobals.DecorationIds.getString(decorBase.decorId) + if assetName == 'Hydra': + assetName = 'StageSummer' + label = DirectLabel(relief=None, geom=self.decorationModels.find('**/partyDecoration_%s' % assetName), text=textForDecoration, text_scale=TTLocalizer.EPdecorationItemLabel, text_align=TextNode.ACenter, text_pos=(-0.01, -0.43), text_wordwrap=7.0) + label['geom_scale'] = (2.6, 0.01, 0.05) + label['geom_pos'] = (0.0, 0.0, -0.33) + return label + + def getToonNameFromAvId(self, avId): + result = TTLocalizer.EventsPageUnknownToon + sender = base.cr.identifyAvatar(avId) + if sender: + result = sender.getName() + return result + + def loadInvitations(self): + EventsPage.notify.debug('loadInvitations') + self.selectedInvitationItem = None + self.invitationPartyList.removeAndDestroyAllItems() + self.invitationActivityList.removeAndDestroyAllItems() + self.invitePartyGoButton['state'] = DirectGuiGlobals.DISABLED + for partyInfo in base.localAvatar.partiesInvitedTo: + if partyInfo.status == PartyGlobals.PartyStatus.Cancelled or partyInfo.status == PartyGlobals.PartyStatus.Finished: + continue + inviteInfo = None + for inviteInfo in base.localAvatar.invites: + if partyInfo.partyId == inviteInfo.partyId: + break + + if inviteInfo is None: + EventsPage.notify.error('No invitation info for party id %d' % partyInfo.partyId) + return + if inviteInfo.status == PartyGlobals.InviteStatus.NotRead: + continue + hostName = self.getToonNameFromAvId(partyInfo.hostId) + item = DirectButton(relief=None, text=hostName, text_align=TextNode.ALeft, text_bg=Vec4(0.0, 0.0, 0.0, 0.0), text_scale=0.045, textMayChange=True, command=self.invitePartyClicked) + PartyUtils.truncateTextOfLabelBasedOnWidth(item, hostName, PartyGlobals.EventsPageHostNameMaxWidth) + item['extraArgs'] = [item] + item.setPythonTag('activityIds', partyInfo.getActivityIds()) + item.setPythonTag('partyStatus', partyInfo.status) + item.setPythonTag('hostId', partyInfo.hostId) + item.setPythonTag('startTime', partyInfo.startTime) + self.invitationPartyList.addItem(item) + + return + + def invitePartyClicked(self, item): + if item.getPythonTag('partyStatus') == PartyGlobals.PartyStatus.Started: + self.invitePartyGoButton['state'] = DirectGuiGlobals.NORMAL + else: + self.invitePartyGoButton['state'] = DirectGuiGlobals.DISABLED + if self.selectedInvitationItem is not None: + self.selectedInvitationItem['state'] = DirectGuiGlobals.NORMAL + self.selectedInvitationItem['text_bg'] = Vec4(0.0, 0.0, 0.0, 0.0) + self.selectedInvitationItem = item + self.selectedInvitationItem['state'] = DirectGuiGlobals.DISABLED + self.selectedInvitationItem['text_bg'] = Vec4(1.0, 1.0, 0.0, 1.0) + self.fillInviteActivityList(item.getPythonTag('activityIds')) + startTime = item.getPythonTag('startTime') + self.invitationDateTimeLabel['text'] = TTLocalizer.EventsPageInvitedTabTime % (PartyUtils.formatDate(startTime.year, startTime.month, startTime.day), PartyUtils.formatTime(startTime.hour, startTime.minute)) + return + + def fillInviteActivityList(self, activityIds): + self.invitationActivityList.removeAndDestroyAllItems() + countDict = {} + for actId in activityIds: + if actId not in countDict: + countDict[actId] = 1 + else: + countDict[actId] += 1 + + for activityId in countDict: + if countDict[activityId] == 1: + textOfActivity = TTLocalizer.PartyActivityNameDict[activityId]['generic'] + else: + textOfActivity = TTLocalizer.PartyActivityNameDict[activityId]['generic'] + ' x ' + str(countDict[activityId]) + geom = getPartyActivityIcon(self.activityIconsModel, PartyGlobals.ActivityIds.getString(activityId)) + item = DirectLabel(relief=None, text=textOfActivity, text_align=TextNode.ACenter, text_scale=0.05, text_pos=(0.0, -0.15), geom_scale=0.3, geom_pos=Vec3(0.0, 0.0, 0.07), geom=geom) + self.invitationActivityList.addItem(item) + + return + + def _inviteStartParty(self): + if self.selectedInvitationItem is None: + self.invitePartyGoButton['state'] = DirectGuiGlobals.DISABLED + return + self.doneStatus = {'mode': 'startparty', + 'firstStart': False, + 'hostId': self.selectedInvitationItem.getPythonTag('hostId')} + messenger.send(self.doneEvent) + return + + def loadHostedPartyInfo(self): + self.unloadGuests() + self.unloadActivities() + self.unloadDecorations() + self.hostedPartyInfo = None + self.confirmCancelPartyGui.doneStatus = '' + self.confirmCancelPartyGui.hide() + self.cancelPartyResultGui.doneStatus = '' + self.cancelPartyResultGui.hide() + if base.localAvatar.hostedParties is not None and len(base.localAvatar.hostedParties) > 0: + for partyInfo in base.localAvatar.hostedParties: + if partyInfo.status == PartyGlobals.PartyStatus.Pending or partyInfo.status == PartyGlobals.PartyStatus.CanStart or partyInfo.status == PartyGlobals.PartyStatus.NeverStarted or partyInfo.status == PartyGlobals.PartyStatus.Started: + self.hostedPartyInfo = partyInfo + self.loadGuests() + self.loadActivities() + self.loadDecorations() + self.hostingDateLabel['text'] = TTLocalizer.EventsPageHostTabDateTimeLabel % (PartyUtils.formatDate(partyInfo.startTime.year, partyInfo.startTime.month, partyInfo.startTime.day), PartyUtils.formatTime(partyInfo.startTime.hour, partyInfo.startTime.minute)) + self.isPrivate = partyInfo.isPrivate + self.__setPublicPrivateButton() + if partyInfo.status == PartyGlobals.PartyStatus.CanStart: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + self.partyGoButton['text'] = (TTLocalizer.EventsPageGoButton,) + elif partyInfo.status == PartyGlobals.PartyStatus.Started: + place = base.cr.playGame.getPlace() + if isinstance(place, Party): + if hasattr(base, 'distributedParty'): + if base.distributedParty.partyInfo.hostId == base.localAvatar.doId: + self.partyGoButton['state'] = DirectGuiGlobals.DISABLED + else: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + else: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + self.notify.warning('base.distributedParty is not defined when base.cr.playGame.getPlace is party. This should never happen.') + else: + self.partyGoButton['state'] = DirectGuiGlobals.NORMAL + self.partyGoButton['text'] = (TTLocalizer.EventsPageGoBackButton,) + else: + self.partyGoButton['text'] = (TTLocalizer.EventsPageGoButton,) + self.partyGoButton['state'] = DirectGuiGlobals.DISABLED + if partyInfo.status not in (PartyGlobals.PartyStatus.Pending, PartyGlobals.PartyStatus.CanStart): + self.hostingCancelButton['state'] = DirectGuiGlobals.DISABLED + else: + self.hostingCancelButton['state'] = DirectGuiGlobals.NORMAL + self.hostingDateLabel.show() + self.hostedPartyDisplay.show() + return + + self.hostingDateLabel['text'] = TTLocalizer.EventsPageHostingTabNoParty + self.hostingCancelButton['state'] = DirectGuiGlobals.DISABLED + self.partyGoButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton['state'] = DirectGuiGlobals.DISABLED + self.privateButton['state'] = DirectGuiGlobals.DISABLED + self.hostedPartyDisplay.show() + return + + def checkCanStartHostedParty(self): + result = True + if self.hostedPartyInfo.endTime < base.cr.toontownTimeManager.getCurServerDateTime() and self.hostedPartyInfo.status == PartyGlobals.PartyStatus.CanStart: + result = False + self.confirmTooLatePartyGui.show() + return result + + def confirmTooLateParty(self): + if hasattr(self, 'confirmTooLatePartyGui'): + self.confirmTooLatePartyGui.hide() + + def confirmPublicPrivateChange(self): + if hasattr(self, 'confirmPublicPrivateGui'): + self.confirmPublicPrivateGui.hide() + + def _startParty(self): + if not self.checkCanStartHostedParty(): + return + if self.hostedPartyInfo.status == PartyGlobals.PartyStatus.CanStart: + firstStart = True + else: + firstStart = False + self.doneStatus = {'mode': 'startparty', + 'firstStart': firstStart, + 'hostId': None} + messenger.send(self.doneEvent) + return + + def loadGuests(self): + for partyReplyInfoBase in base.localAvatar.partyReplyInfoBases: + if partyReplyInfoBase.partyId == self.hostedPartyInfo.partyId: + for singleReply in partyReplyInfoBase.replies: + toonName = self.getToonNameFromAvId(singleReply.inviteeId) + self.hostingGuestList.addItem(self.getGuestItem(toonName, singleReply.status)) + + def loadActivities(self): + countDict = {} + for activityBase in self.hostedPartyInfo.activityList: + if activityBase.activityId not in countDict: + countDict[activityBase.activityId] = 1 + else: + countDict[activityBase.activityId] += 1 + + idsUsed = [] + for activityBase in self.hostedPartyInfo.activityList: + if activityBase.activityId not in idsUsed: + idsUsed.append(activityBase.activityId) + count = countDict[activityBase.activityId] + self.hostingActivityList.addItem(self.getActivityItem(activityBase, count)) + + def loadDecorations(self): + countDict = {} + for decorBase in self.hostedPartyInfo.decors: + if decorBase.decorId not in countDict: + countDict[decorBase.decorId] = 1 + else: + countDict[decorBase.decorId] += 1 + + idsUsed = [] + for decorBase in self.hostedPartyInfo.decors: + if decorBase.decorId not in idsUsed: + count = countDict[decorBase.decorId] + self.hostingDecorationList.addItem(self.getDecorationItem(decorBase, count)) + idsUsed.append(decorBase.decorId) + + def unloadGuests(self): + self.hostingGuestList.removeAndDestroyAllItems() + + def unloadActivities(self): + self.hostingActivityList.removeAndDestroyAllItems() + + def unloadDecorations(self): + self.hostingDecorationList.removeAndDestroyAllItems() + + def unload(self): + self.scrollButtonGui.removeNode() + self.hostingGui.removeNode() + self.invitationGui.removeNode() + self.activityIconsModel.removeNode() + self.decorationModels.removeNode() + del self.titleLabel + self.hostingGuestList.removeAndDestroyAllItems() + self.hostingGuestList.destroy() + del self.hostingGuestList + self.hostingActivityList.removeAndDestroyAllItems() + self.hostingActivityList.destroy() + del self.hostingActivityList + self.hostingDecorationList.removeAndDestroyAllItems() + self.hostingDecorationList.destroy() + del self.hostingDecorationList + self.invitationPartyList.removeAndDestroyAllItems() + self.invitationPartyList.destroy() + del self.invitationPartyList + self.invitationActivityList.removeAndDestroyAllItems() + self.invitationActivityList.destroy() + del self.invitationActivityList + self.confirmCancelPartyGui.cleanup() + del self.confirmCancelPartyGui + self.confirmTooLatePartyGui.cleanup() + del self.confirmTooLatePartyGui + self.confirmPublicPrivateGui.cleanup() + del self.confirmPublicPrivateGui + self.ignore('changePartyPrivateResponseReceived') + taskMgr.remove('changePartyPrivateResponseReceivedTimeOut') + self.cancelPartyResultGui.cleanup() + del self.cancelPartyResultGui + self.ignore(self.confirmCancelPartyEvent) + self.ignore(self.cancelPartyResultGuiEvent) + if hasattr(self, 'rssFeed') and self.rssFeed: + self.rssFeed = None + if self.articleTextList: + self.articleTextList.removeAndDestroyAllItems() + self.articleTextList.destroy() + self.articleTextList = None + if self.articleIndexList: + self.articleIndexList.removeAndDestroyAllItems() + self.articleIndexList.destroy() + self.articleIndexList = None + if self.newsList: + self.newsList.removeAndDestroyAllItems() + self.newsList.destroy() + self.newsList = None + self.avatar = None + self.hostingCancelButton.destroy() + del self.hostingCancelButton + self.partyGoButton.destroy() + del self.partyGoButton + self.publicButton.destroy() + del self.publicButton + self.privateButton.destroy() + del self.privateButton + self.invitePartyGoButton.destroy() + del self.invitePartyGoButton + self.hostTab.destroy() + self.invitedTab.destroy() + self.calendarTab.destroy() + self.calendarGuiMonth.destroy() + self.toontownTimeGui.destroy() + taskMgr.remove('EventsPageUpdateTask-doLater') + taskMgr.remove(self.DownloadArticlesTaskName) + ShtikerPage.ShtikerPage.unload(self) + return + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + self.unloadGuests() + self.unloadActivities() + self.unloadDecorations() + + def __handleConfirm(self): + self.ignore('confirmDone') + self.confirm.cleanup() + del self.confirm + + def createListAndLabel(self, parent, gui, typeString, itemsVisible, downString = 'DownArrow', upString = 'UpArrow', textString = '_text_locator'): + list = DirectScrolledList(parent=parent, relief=None, incButton_image=(gui.find('**/%s%s_up' % (typeString, downString)), + gui.find('**/%s%s_down' % (typeString, downString)), + gui.find('**/%s%s_rollover' % (typeString, downString)), + gui.find('**/%s%s_inactive' % (typeString, downString))), incButton_relief=None, decButton_image=(gui.find('**/%s%s_up' % (typeString, upString)), + gui.find('**/%s%s_down' % (typeString, upString)), + gui.find('**/%s%s_rollover' % (typeString, upString)), + gui.find('**/%s%s_inactive' % (typeString, upString))), decButton_relief=None, itemFrame_pos=gui.find('**/%s_locator' % typeString).getPos(), itemFrame_relief=None, numItemsVisible=itemsVisible, forceHeight=0.07) + strings = {'guests': TTLocalizer.EventsPageHostingTabGuestListTitle, + 'activities': TTLocalizer.EventsPageHostingTabActivityListTitle, + 'decorations': TTLocalizer.EventsPageHostingTabDecorationsListTitle, + 'parties': TTLocalizer.EventsPageHostingTabPartiesListTitle} + label = DirectLabel(parent=parent, relief=None, text=strings[typeString], text_scale=TTLocalizer.EPcreateListAndLabel, pos=gui.find('**/%s%s' % (typeString, textString)).getPos()) + return (list, label) + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if updateAnyways == False: + if self.mode == mode: + return + else: + self.mode = mode + self.show() + self.updatePage() + + def getMode(self): + return self.mode + + def updatePage(self): + if self.mode == EventsPage_Host: + self.hostTab['state'] = DirectGuiGlobals.DISABLED + self.invitedTab['state'] = DirectGuiGlobals.NORMAL + self.calendarTab['state'] = DirectGuiGlobals.NORMAL + self.newsTab['state'] = DirectGuiGlobals.NORMAL + self.invitationDisplay.hide() + self.hostedPartyDisplay.show() + self.calendarDisplay.hide() + self.newsDisplay.hide() + self.loadHostedPartyInfo() + if self.hostedPartyInfo is None: + self.titleLabel['text'] = TTLocalizer.EventsPageHostTabTitleNoParties + else: + self.titleLabel['text'] = TTLocalizer.EventsPageHostTabTitle + elif self.mode == EventsPage_Invited: + self.titleLabel['text'] = TTLocalizer.EventsPageInvitedTabTitle + self.hostTab['state'] = DirectGuiGlobals.NORMAL + self.invitedTab['state'] = DirectGuiGlobals.DISABLED + self.calendarTab['state'] = DirectGuiGlobals.NORMAL + self.newsTab['state'] = DirectGuiGlobals.NORMAL + self.hostedPartyDisplay.hide() + self.invitationDisplay.show() + self.calendarDisplay.hide() + self.newsDisplay.hide() + self.loadInvitations() + elif self.mode == EventsPage_Calendar: + self.titleLabel['text'] = '' + self.hostTab['state'] = DirectGuiGlobals.NORMAL + self.invitedTab['state'] = DirectGuiGlobals.NORMAL + self.calendarTab['state'] = DirectGuiGlobals.DISABLED + self.newsTab['state'] = DirectGuiGlobals.NORMAL + self.hostedPartyDisplay.hide() + self.invitationDisplay.hide() + self.calendarDisplay.show() + self.newsDisplay.hide() + self.calendarGuiMonth.changeMonth(0) + elif self.mode == EventsPage_News: + self.titleLabel['text'] = '' + self.hostTab['state'] = DirectGuiGlobals.NORMAL + self.invitedTab['state'] = DirectGuiGlobals.NORMAL + self.calendarTab['state'] = DirectGuiGlobals.NORMAL + self.newsTab['state'] = DirectGuiGlobals.DISABLED + self.hostedPartyDisplay.hide() + self.invitationDisplay.hide() + self.calendarDisplay.hide() + if not self.gotRssFeed: + pass + self.newsDisplay.show() + return + + def __setPublicPrivateButton(self): + if self.isPrivate: + self.privateButton['indicatorValue'] = True + self.publicButton['indicatorValue'] = False + self.privateButton['state'] = DirectGuiGlobals.DISABLED + self.publicButton['state'] = DirectGuiGlobals.NORMAL + else: + self.privateButton['indicatorValue'] = False + self.publicButton['indicatorValue'] = True + self.privateButton['state'] = DirectGuiGlobals.NORMAL + self.publicButton['state'] = DirectGuiGlobals.DISABLED + + def __changePublicPrivate(self, indicator): + self.__setPublicPrivateButton() + self.confirmPublicPrivateGui['text'] = TTLocalizer.EventsPagePublicPrivateChange + self.confirmPublicPrivateGui.buttonList[0].hide() + self.confirmPublicPrivateGui.show() + base.cr.partyManager.sendChangePrivateRequest(self.hostedPartyInfo.partyId, not self.isPrivate) + self.accept('changePartyPrivateResponseReceived', self.changePartyPrivateResponseReceived) + taskMgr.doMethodLater(5.0, self.changePartyPrivateResponseReceived, 'changePartyPrivateResponseReceivedTimeOut', [0, 0, PartyGlobals.ChangePartyFieldErrorCode.DatabaseError]) + + def changePartyPrivateResponseReceived(self, partyId, newPrivateStatus, errorCode): + EventsPage.notify.debug('changePartyPrivateResponseReceived called with partyId = %d, newPrivateStatus = %d, errorCode = %d' % (partyId, newPrivateStatus, errorCode)) + taskMgr.remove('changePartyPrivateResponseReceivedTimeOut') + self.ignore('changePartyPrivateResponseReceived') + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AllOk: + self.isPrivate = newPrivateStatus + self.confirmPublicPrivateGui.hide() + else: + self.confirmPublicPrivateGui.buttonList[0].show() + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AlreadyStarted: + self.confirmPublicPrivateGui['text'] = TTLocalizer.EventsPagePublicPrivateAlreadyStarted + else: + self.confirmPublicPrivateGui['text'] = TTLocalizer.EventsPagePublicPrivateNoGo + self.__setPublicPrivateButton() + + def __doCancelParty(self): + if self.hostedPartyInfo: + if self.hostedPartyInfo.status == PartyGlobals.PartyStatus.Pending or self.hostedPartyInfo.status == PartyGlobals.PartyStatus.CanStart or self.hostedPartyInfo.status == PartyGlobals.PartyStatus.NeverStarted: + self.hostingCancelButton['state'] = DirectGuiGlobals.DISABLED + self.confirmCancelPartyGui.show() + + def confirmCancelOfParty(self): + self.confirmCancelPartyGui.hide() + if self.confirmCancelPartyGui.doneStatus == 'ok': + base.cr.partyManager.sendChangePartyStatusRequest(self.hostedPartyInfo.partyId, PartyGlobals.PartyStatus.Cancelled) + self.accept('changePartyStatusResponseReceived', self.changePartyStatusResponseReceived) + else: + self.hostingCancelButton['state'] = DirectGuiGlobals.NORMAL + + def changePartyStatusResponseReceived(self, partyId, newPartyStatus, errorCode, beansRefunded): + EventsPage.notify.debug('changePartyStatusResponseReceived called with partyId = %d, newPartyStatus = %d, errorCode = %d' % (partyId, newPartyStatus, errorCode)) + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AllOk: + if newPartyStatus == PartyGlobals.PartyStatus.Cancelled: + self.loadHostedPartyInfo() + self.cancelPartyResultGui['text'] = TTLocalizer.EventsPageCancelPartyResultOk % beansRefunded + self.cancelPartyResultGui.show() + elif errorCode == PartyGlobals.ChangePartyFieldErrorCode.AlreadyRefunded and newPartyStatus == PartyGlobals.PartyStatus.NeverStarted: + self.loadHostedPartyInfo() + self.cancelPartyResultGui['text'] = TTLocalizer.EventsPageCancelPartyAlreadyRefunded + self.cancelPartyResultGui.show() + else: + self.cancelPartyResultGui['text'] = TTLocalizer.EventsPageCancelPartyResultError + self.cancelPartyResultGui.show() + self.hostingCancelButton['state'] = DirectGuiGlobals.NORMAL + + def cancelPartyResultGuiCommand(self): + self.cancelPartyResultGui.hide() + + def updateToontownTime(self): + self.toontownTimeGui.updateTime() + + def createArticleTextList(self): + bottomLeft = self.newsFrame.find('**/loc_textBoxBtmLf') + topRight = self.newsFrame.find('**/loc_textBoxTopRt') + topLeft = self.newsFrame.find('**/loc_textBoxTopLf') + self.notify.debug('bottomLeft=%s topRight=%s' % (bottomLeft.getPos(), topRight.getPos())) + buttonOffSet = 0.045 + selectedIndex = 0 + self.articleListXorigin = bottomLeft.getPos().getX() + self.articleListFrameSizeX = topRight.getPos().getX() - bottomLeft.getPos().getX() + self.articleListZorigin = bottomLeft.getPos().getZ() + self.articleListFrameSizeZ = topRight.getPos().getZ() - bottomLeft.getPos().getZ() + self.articleArrowButtonScale = 1.3 + self.articleItemFrameXorigin = bottomLeft.getPos().getX() + self.articleButtonXstart = self.articleItemFrameXorigin + 0.25 + + def makeButton(itemName, itemNum, *extraArgs): + + def buttonCommand(): + print itemName, itemNum + + return DirectLabel(text=itemName, relief=None, text_align=TextNode.ALeft, scale=0.06) + + itemHeight = 0.062 + topLeftStart = topLeft.getPos() + self.notify.debug('topLeft=%s topLeftStart=%s' % (topLeft.getPos(), topLeftStart)) + scrollTopUp = self.newsFrame.find('**/scrollTopUp') + scrollTopDown = self.newsFrame.find('**/scrollTopDown') + scrollTopHover = self.newsFrame.find('**/scrollTopHover') + scrollBtmUp = self.newsFrame.find('**/scrollBtmUp') + scrollBtmDown = self.newsFrame.find('**/scrollBtmDown') + scrollBtmHover = self.newsFrame.find('**/scrollBtmHover') + decButtonPos = scrollTopUp.getPos(topLeft) + incButtonPos = scrollBtmDown.getPos(topLeft) + self.notify.debug('scrollTopUp pos wrt topLeft %s' % decButtonPos) + self.notify.debug('scrollTopUp pos normal %s' % scrollTopUp.getPos()) + scrollTopUp.setPos(0, 0, 0) + scrollTopDown.setPos(0, 0, 0) + scrollTopHover.setPos(0, 0, 0) + scrollBtmUp.setPos(0, 0, 0) + scrollBtmDown.setPos(0, 0, 0) + scrollBtmHover.setPos(0, 0, 0) + self.numLinesInTextList = 13 + self.articleTextList = DirectScrolledList(parent=self.newsFrame, relief=None, pos=topLeftStart, incButton_image=(self.newsFrame.find('**/scrollBtmUp'), + self.newsFrame.find('**/scrollBtmDown'), + self.newsFrame.find('**/scrollBtmHover'), + self.newsFrame.find('**/scrollBtmUp')), incButton_relief=None, incButton_pos=incButtonPos, incButton_image3_color=(1.0, 1.0, 1.0, 0.1), decButton_image=(self.newsFrame.find('**/scrollTopUp'), + self.newsFrame.find('**/scrollTopDown'), + self.newsFrame.find('**/scrollTopHover'), + self.newsFrame.find('**/scrollTopUp')), decButton_relief=None, decButton_pos=decButtonPos, decButton_image3_color=(1.0, 1.0, 1.0, 0.1), text_scale=0.05, frameSize=(self.articleListXorigin, + self.articleListXorigin + self.articleListFrameSizeX, + self.articleListZorigin, + self.articleListZorigin + self.articleListFrameSizeZ), frameColor=(0.82, 0.8, 0.75, 1), borderWidth=(0.01, 0.01), numItemsVisible=self.numLinesInTextList, itemMakeFunction=makeButton, forceHeight=itemHeight) + oldParent = self.articleTextList.decButton.getParent() + self.newsFrame.find('**/scroll').hide() + return + + def createArticleIndexList(self): + self.articleIndexList = DirectScrolledList(parent=self.newsFrame, relief=None, pos=(0, 0, 0), incButton_image=(self.newsFrame.find('**/pageRtUp'), + self.newsFrame.find('**/pageRtUp'), + self.newsFrame.find('**/pageRtHover'), + None), incButton_relief=None, incButton_scale=1, decButton_image=(self.newsFrame.find('**/pageLfUp'), + self.newsFrame.find('**/pageLfUp'), + self.newsFrame.find('**/pageLfHover'), + None), decButton_relief=None, decButton_scale=1, text_scale=0.05, numItemsVisible=1) + self.newsFrame.find('**/pageRtUp').hide() + self.newsFrame.find('**/pageRtHover').hide() + self.newsFrame.find('**/pageLfUp').hide() + self.newsFrame.find('**/pageLfHover').hide() + self.articleIndexList['command'] = self.articleIndexChanged + return + + def articleIndexChanged(self): + if not self.articleIndexList['items']: + return + curArticleIndex = self.articleIndexList.getSelectedIndex() + if curArticleIndex in self.articleImages and curArticleIndex in self.articleText: + self.displayArticle(self.articleImages[curArticleIndex], self.articleText[curArticleIndex]) + + def getRssFeed(self): + if self.gotRssFeed: + return + self.rssFeed = feedparser.parse('http://www.wdwinfo.com/news/rss.xml') + feedText = [] + + def addFeedText(unicodeText, textSize = 0.03, color = (0, 0, 0, 1), feedText = feedText): + feedText.append(DirectLabel(relief=None, text=unicodeText, text_scale=textSize, text_align=TextNode.ALeft, text_fg=color, textMayChange=0, pos=(0, 0, 0))) + return + + addFeedText(self.rssFeed['channel']['title'], 0.06) + addFeedText(self.rssFeed['channel']['subtitle'], 0.04) + for entry in self.rssFeed['entries']: + addFeedText('') + addFeedText(entry['title'], 0.04, (0, 0, 1, 1)) + (addFeedText(entry['updated'], 0.025),) + addFeedText(entry['summary'], 0.035) + + self.feedText = feedText + self.createNewsList() + self.gotRssFeed = True + + def downloadArticles(self): + if self.gotArticles: + return + if not self.NonblockingDownload: + if not taskMgr.hasTaskNamed(self.DownloadArticlesTaskName): + taskMgr.doMethodLater(0.5, self.downloadArticlesTask, self.DownloadArticlesTaskName) + elif not self.downloadArticlesInProgress: + self.downloadArticlesNonblocking() + + def downloadArticlesTask(self, task): + self.articleImages = {} + self.articleText = {} + try: + urlfile = urllib.urlopen(self.getNewsUrl()) + except IOError: + self.notify.warning('Could not open %s' % self.getNewsUrl()) + self.newsStatusLabel['text'] = TTLocalizer.EventsPageNewsUnavailable + return + + urlStrings = urlfile.read() + urlfile.close() + urls = urlStrings.split('\r\n') + for index in xrange(len(urls) / 2): + imageUrl = urls[index * 2] + textUrl = urls[index * 2 + 1] + img = PNMImage() + self.articleImages[index] = img + try: + self.notify.info('opening %s' % imageUrl) + imageFile = urllib.urlopen(imageUrl) + data = imageFile.read() + img.read(StringStream(data)) + imageFile.close() + except IOError: + self.notify.warning('image url %d could not open %s' % (index, imageUrl)) + + text = '' + self.articleText[index] = text + try: + self.notify.info('opening %s' % textUrl) + textFile = urllib.urlopen(textUrl) + data = textFile.read() + data = data.replace('\\1', '\x01') + data = data.replace('\\2', '\x02') + data = data.replace('\r', ' ') + self.articleText[index] = data + textFile.close() + except IOError: + self.notify.warning('text url %d could not open %s' % (index, textUrl)) + + self.articleIndexList.addItem('') + + self.newsStatusLabel['text'] = '' + self.gotArticles = True + return task.done + + def displayArticle(self, img, articleText): + self.displayArticleImage(img) + self.displayArticleText(articleText) + + def displayArticleImage(self, img): + xSize = img.getXSize() + ySize = img.getYSize() + bottomLeft = self.newsFrame.find('**/loc_picture_BtmLf').getPos() + topRight = self.newsFrame.find('**/loc_picture_TopRt').getPos() + maxFrameXSize = topRight.getX() - bottomLeft.getX() + maxFrameYSize = topRight.getZ() - bottomLeft.getZ() + center = (bottomLeft + topRight) / 2.0 + maxAspectRatio = maxFrameXSize / maxFrameYSize + if ySize: + imgAspectRatio = float(xSize) / float(ySize) + else: + imgAspectRatio = maxAspectRatio + curXSize = maxFrameXSize + curYSize = maxFrameYSize + shrinkY = True + if imgAspectRatio > maxAspectRatio: + if xSize: + curYSize = maxFrameXSize * ySize / xSize + elif ySize: + curXSize = maxFrameYSize * xSize / ySize + shrinkY = False + minX = -curXSize / 2.0 + maxX = curXSize / 2.0 + minY = -curYSize / 2.0 + maxY = curYSize / 2.0 + webTexture = Texture('webTexture') + if img.isValid(): + webTexture.load(img) + else: + webTexture = None + if self.articleImage: + self.articleImage.destroy() + self.articleImage = DirectFrame(parent=self.newsFrame, relief=DirectGuiGlobals.FLAT, image=webTexture, image_scale=(curXSize / 2.0, 1, curYSize / 2.0), pos=center, frameSize=(minX, + maxX, + minY, + maxY)) + foo1 = Point3(0, 0, 0) + foo2 = Point3(0, 0, 0) + self.articleImage.calcTightBounds(foo1, foo2) + foo3 = self.articleImage.getBounds() + return + + def displayArticleText(self, articleText): + playaLabel = DirectLabel(parent=None, relief=None, text_align=TextNode.ALeft, text=articleText, text_scale=0.06, text_wordwrap=13.5) + playaLabel.hide() + textN = playaLabel.component(playaLabel.components()[0]) + if type(textN) == OnscreenText: + wrappedText = textN.textNode.getWordwrappedText() + items = wrappedText.split('\n') + self.articleTextList.removeAndDestroyAllItems() + for item in items: + self.articleTextList.addItem(item) + + if len(items) <= self.numLinesInTextList: + self.articleTextList.decButton.hide() + self.articleTextList.incButton.hide() + else: + self.articleTextList.decButton.show() + self.articleTextList.incButton.show() + playaLabel.destroy() + return + + def createNewsList(self): + buttonOffSet = 0.045 + if self.newsList: + self.newsList.removeAllItems() + self.newsList.destroy() + selectedIndex = 0 + self.newsListXorigin = -0.02 + self.newsListFrameSizeX = 1.55 + self.newsListZorigin = -0.95 + self.newsListFrameSizeZ = 1.02 + self.newsArrowButtonScale = 1.3 + self.newsItemFrameXorigin = -0.237 + self.newsButtonXstart = self.newsItemFrameXorigin + 0.725 + self.newsList = DirectScrolledList(parent=self.newsFrame, items=self.feedText, relief=None, pos=(-0.5, 0, 0.4), incButton_image=(self.scrollButtonGui.find('**/FndsLst_ScrollUp'), + self.scrollButtonGui.find('**/FndsLst_ScrollDN'), + self.scrollButtonGui.find('**/FndsLst_ScrollUp_Rllvr'), + self.scrollButtonGui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(self.newsArrowButtonScale, self.newsArrowButtonScale, -self.newsArrowButtonScale), incButton_pos=(self.newsButtonXstart, 0, self.newsListZorigin - buttonOffSet), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(self.scrollButtonGui.find('**/FndsLst_ScrollUp'), + self.scrollButtonGui.find('**/FndsLst_ScrollDN'), + self.scrollButtonGui.find('**/FndsLst_ScrollUp_Rllvr'), + self.scrollButtonGui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(self.newsArrowButtonScale, self.newsArrowButtonScale, self.newsArrowButtonScale), decButton_pos=(self.newsButtonXstart, 0, self.newsListZorigin + self.newsListFrameSizeZ + buttonOffSet), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(self.newsItemFrameXorigin, 0, 0), itemFrame_scale=1.0, itemFrame_relief=DirectGuiGlobals.SUNKEN, itemFrame_frameSize=(self.newsListXorigin, + self.newsListXorigin + self.newsListFrameSizeX, + self.newsListZorigin, + self.newsListZorigin + self.newsListFrameSizeZ), itemFrame_frameColor=(0.82, 0.8, 0.75, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=14) + self.newsList.scrollTo(selectedIndex) + return + + def downloadArticlesNonblocking(self): + self.notify.info('Starting download of news articles.') + self.articleImages = {} + self.articleText = {} + self.downloadArticlesInProgress = True + self.httpSession = HTTPClient() + self.nonBlock = self.httpSession.makeChannel(True) + newsUrl = self.getNewsUrl() + self.curUrlIndex = -1 + self.curArticleIndex = -1 + self.curDownloadIsJpg = True + self.downloadUrl(newsUrl) + + def downloadUrl(self, newsUrl): + if 'http:' in newsUrl: + self.getHttpUrl(newsUrl) + else: + self.getFileUrl(newsUrl) + + def doneGettingUrl(self, url, data, allOk): + self.notify.debug('doneGettingUrl %s %s %s' % (url, type(data), allOk)) + self.printCurFields() + if url == self.getNewsUrl(): + if allOk: + if type(data) == Ramfile: + self.urls = data.getData().split('\r\n') + else: + self.urls = data.split('\r\n') + else: + self.notify.warning('Could not open %s' % url) + self.newsStatusLabel['text'] = TTLocalizer.EventsPageNewsUnavailable + self.gotArticles = True + return + if self.urls: + self.subRight['text'] = self.urls[0] + self.urls = self.urls[1:] + self.curUrlIndex = 0 + self.curArticleIndex = 0 + self.curDownloadIsJpg = True + if self.curUrlIndex < len(self.urls): + url = self.urls[self.curUrlIndex] + self.downloadUrl(url) + else: + if self.curDownloadIsJpg: + img = PNMImage() + self.articleImages[self.curArticleIndex] = img + try: + if type(data) == Ramfile: + if allOk: + img.read(StringStream(data.getData())) + else: + img.read(StringStream(data)) + except: + self.notify.warning('image url %d could not read %s' % (self.curArticleIndex, url)) + + else: + text = '' + self.articleText[self.curArticleIndex] = text + if type(data) == Ramfile: + textData = data.getData() + else: + textData = data + textData = textData.replace('\\1', '\x01') + textData = textData.replace('\\2', '\x02') + textData = textData.replace('\r', ' ') + self.articleText[self.curArticleIndex] = textData + self.articleIndexList.addItem('') + if self.newsStatusLabel['text']: + self.newsStatusLabel['text'] = '' + self.incrementCurFields() + if self.downloadedAllArticles(): + self.notify.debug('we got everything') + else: + url = self.urls[self.curUrlIndex] + self.downloadUrl(url) + + def downloadedAllArticles(self): + maxArticles = len(self.urls) / 2 + result = False + if self.curArticleIndex >= maxArticles: + result = True + if self.curUrlIndex >= len(self.urls): + result = True + return result + + def incrementCurFields(self): + if self.curDownloadIsJpg: + self.curDownloadIsJpg = False + self.curUrlIndex += 1 + else: + self.curDownloadIsJpg = True + self.curUrlIndex += 1 + self.curArticleIndex += 1 + + def printCurFields(self): + self.notify.debug('curUrlIndex=%s curArticleIndex=%s curDownloadIsJpg=%s' % (self.curUrlIndex, self.curArticleIndex, self.curDownloadIsJpg)) + + def getFileUrl(self, fileUrl): + result = True + urlStrings = '' + try: + urlfile = urllib.urlopen(fileUrl) + urlStrings = urlfile.read() + urlfile.close() + except IOError: + self.notify.warning('Could not open %s' % fileUrl) + result = False + + self.doneGettingUrl(fileUrl, urlStrings, result) + + def getHttpUrl(self, httpUrl): + docSpec = DocumentSpec(httpUrl) + self.ramfile = Ramfile() + self.nonBlock.beginGetDocument(docSpec) + self.nonBlock.downloadToRam(self.ramfile) + self.startCheckingAsyncRequest(httpUrl) + + def startCheckingAsyncRequest(self, url): + taskMgr.remove(self.DownloadArticlesTaskName) + task = taskMgr.doMethodLater(0.5, self.pollDownloadTask, self.DownloadArticlesTaskName) + task.url = url + + def stopCheckingAsyncRequest(self): + taskMgr.remove(self.DownloadArticlesTaskName) + + def pollDownloadTask(self, task): + result = self.nonBlock.run() + if result == 0: + self.stopCheckingAsyncRequest() + allOk = False + if self.nonBlock.getStatusString() == 'OK': + allOk = True + else: + self.notify.warning('%s for %s' % (self.nonBlock.getStatusString(), task.url)) + self.doneGettingUrl(task.url, self.ramfile, allOk) + else: + return Task.again + + def getNewsUrl(self): + result = '' + if self.NewsUrl == self.DefaultNewsUrl: + serverAddress = base.cr.getServerAddress() + if 'test.toontown' in serverAddress.getServer(): + result = 'http://play.test.toontown.com' + elif 'ttown4' in serverAddress.getServer(): + result = 'http://ttown4.online.disney.com:1601' + elif 'qa.toontown' in serverAddress.getServer(): + result = 'http://play.qa.toontown.com' + else: + result = 'http://play.toontown.com' + result += self.NewsUrl + else: + result = self.NewsUrl + return result diff --git a/toontown/shtiker/FishPage.py b/toontown/shtiker/FishPage.py new file mode 100644 index 0000000..e341ff3 --- /dev/null +++ b/toontown/shtiker/FishPage.py @@ -0,0 +1,300 @@ +from toontown.toonbase import ToontownGlobals +import ShtikerPage +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.fishing import FishPicker +from toontown.fishing import FishBrowser +from toontown.fishing import FishGlobals +FishPage_Tank = 0 +FishPage_Collection = 1 +FishPage_Trophy = 2 +TROPHIES_PER_ROW = 5 + +class FishPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('FishPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.avatar = None + self.mode = FishPage_Tank + return + + def enter(self): + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + self.accept(localAvatar.uniqueName('fishTankChange'), self.updatePage) + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + self.ignore(localAvatar.uniqueName('fishTankChange')) + ShtikerPage.ShtikerPage.exit(self) + + def setAvatar(self, av): + self.avatar = av + + def getAvatar(self): + return self.avatar + + def load(self): + ShtikerPage.ShtikerPage.load(self) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + rodFrame = gui.find('**/bucket/fram1') + rodFrame.removeNode() + trophyCase = gui.find('**/trophyCase1') + trophyCase.find('glass1').reparentTo(trophyCase, -1) + trophyCase.find('shelf').reparentTo(trophyCase, -1) + self.trophyCase = trophyCase + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + self.tankTab = DirectButton(parent=self, relief=None, text=TTLocalizer.FishPageTankTab, text_scale=TTLocalizer.FPtankTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[FishPage_Tank], pos=(0.92, 0, 0.55)) + self.collectionTab = DirectButton(parent=self, relief=None, text=TTLocalizer.FishPageCollectionTab, text_scale=TTLocalizer.FPcollectionTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[FishPage_Collection], pos=(0.92, 0, 0.1)) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.FishPageTrophyTab, text_scale=TTLocalizer.FPtrophyTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[FishPage_Trophy], pos=(0.92, 0, -0.3)) + self.tankTab.setPos(-0.55, 0, 0.775) + self.collectionTab.setPos(-0.13, 0, 0.775) + self.trophyTab.setPos(0.28, 0, 0.775) + return + + def createFishPicker(self): + if not hasattr(self, 'picker'): + self.picker = FishPicker.FishPicker(self) + self.picker.setPos(-0.555, 0, 0.1) + self.picker.setScale(0.95) + self.rod = DirectLabel(parent=self.picker, relief=None, text='', text_align=TextNode.ALeft, text_scale=0.06, pos=(0.9, 0, -0.65)) + return + + def createFishBrowser(self): + if not hasattr(self, 'browser'): + self.browser = FishBrowser.FishBrowser(self) + self.browser.setScale(1.1) + self.collectedTotal = DirectLabel(parent=self.browser, relief=None, text='', text_scale=0.06, pos=(0, 0, -0.61)) + return + + def createFishTrophyFrame(self): + if not hasattr(self, 'trophyFrame'): + self.trophyFrame = DirectFrame(parent=self, relief=None, image=self.trophyCase, image_pos=(0, 1, 0), image_scale=0.034) + self.trophyFrame.hide() + self.trophies = [] + hOffset = -0.5 + vOffset = 0.4 + for level, trophyDesc in FishGlobals.TrophyDict.items(): + trophy = FishingTrophy(-1) + trophy.nameLabel['text'] = trophyDesc[0] + trophy.reparentTo(self.trophyFrame) + trophy.setScale(0.36) + if level % TROPHIES_PER_ROW == 0: + hOffset = -0.5 + vOffset -= 0.4 + trophy.setPos(hOffset, 0, vOffset) + hOffset += 0.25 + self.trophies.append(trophy) + + return + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + self.show() + if mode == FishPage_Tank: + self.title['text'] = TTLocalizer.FishPageTitleTank + if not hasattr(self, 'picker'): + self.createFishPicker() + self.picker.show() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.tankTab['state'] = DGG.DISABLED + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + elif mode == FishPage_Collection: + self.title['text'] = TTLocalizer.FishPageTitleCollection + if hasattr(self, 'picker'): + self.picker.hide() + if not hasattr(self, 'browser'): + self.createFishBrowser() + self.browser.show() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.tankTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + elif mode == FishPage_Trophy: + self.title['text'] = TTLocalizer.FishPageTitleTrophy + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if not hasattr(self, 'trophyFrame'): + self.createFishTrophyFrame() + self.trophyFrame.show() + self.tankTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + self.updatePage() + + def unload(self): + self.avatar = None + if hasattr(self, 'trophies'): + del self.trophies + if hasattr(self, 'trophyCase'): + del self.trophyCase + self.tankTab.destroy() + self.collectionTab.destroy() + self.trophyTab.destroy() + ShtikerPage.ShtikerPage.unload(self) + return + + def updatePage(self): + if hasattr(self, 'collectedTotal'): + self.collectedTotal['text'] = TTLocalizer.FishPageCollectedTotal % (len(base.localAvatar.fishCollection), FishGlobals.getTotalNumFish()) + if hasattr(self, 'rod'): + rod = base.localAvatar.fishingRod + rodName = TTLocalizer.FishingRodNameDict[rod] + rodWeightRange = FishGlobals.getRodWeightRange(rod) + self.rod['text'] = TTLocalizer.FishPageRodInfo % (rodName, rodWeightRange[0], rodWeightRange[1]) + if self.mode == FishPage_Tank: + if hasattr(self, 'picker'): + newTankFish = base.localAvatar.fishTank.getFish() + self.picker.update(newTankFish) + elif self.mode == FishPage_Collection: + if hasattr(self, 'browser'): + self.browser.update() + elif self.mode == FishPage_Trophy: + if hasattr(self, 'trophies'): + for trophy in self.trophies: + trophy.setLevel(-1) + + for trophyId in base.localAvatar.getFishingTrophies(): + self.trophies[trophyId].setLevel(trophyId) + + def destroy(self): + self.notify.debug('destroy') + DirectFrame.destroy(self) + + +class FishingTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('FishingTrophy') + + def __init__(self, level): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(FishingTrophy) + self.trophy = loader.loadModel('phase_3.5/models/gui/fishingTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.bowl = loader.loadModel('phase_3.5/models/gui/fishingTrophyBowl') + self.bowl.reparentTo(self) + self.bowl.setPos(0, 1, 0) + self.bowl.setScale(2.0) + self.bowlTop = self.bowl.find('**/fishingTrophyGreyBowl') + self.bowlBase = self.bowl.find('**/fishingTrophyBase') + self.bowlBase.setScale(1.25, 1, 1) + self.bowlBase.setColorScale(1, 1, 0.8, 1) + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='Trophy Text', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + if level == -1: + self.trophy.hide() + self.bowl.hide() + self.nameLabel.hide() + elif level == 0: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level == 1: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -0.5) + self.__bronze() + elif level == 2: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__silver() + elif level == 3: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.61878) + self.top.setPos(0, 0, 0.5) + self.__silver() + elif level == 4: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 3.11878) + self.top.setPos(0, 0, 1) + self.__gold() + elif level == 5: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(1.75) + self.nameLabel.show() + self.__bronze() + elif level == 6: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.0) + self.nameLabel.show() + self.__silver() + elif level >= 7: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.25) + self.nameLabel.show() + self.__gold() + + def __bronze(self): + self.top.setColorScale(0.9, 0.6, 0.33, 1) + self.bowlTop.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.top.setColorScale(0.9, 0.9, 1, 1) + self.bowlTop.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.top.setColorScale(1, 0.95, 0.1, 1) + self.bowlTop.setColorScale(1, 0.95, 0.1, 1) + + def destroy(self): + self.trophy.removeNode() + self.bowl.removeNode() + self.shadow.removeNode() + DirectFrame.destroy(self) + + def show(self): + ShtikerPage.show(self) diff --git a/toontown/shtiker/GardenPage.py b/toontown/shtiker/GardenPage.py new file mode 100644 index 0000000..2a72aaa --- /dev/null +++ b/toontown/shtiker/GardenPage.py @@ -0,0 +1,481 @@ +from direct.directnotify import DirectNotifyGlobal +import ShtikerPage +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.estate import FlowerBrowser +from toontown.estate import GardenGlobals +from toontown.estate import FlowerPicker +from toontown.estate import SpecialsPhoto +from toontown.toontowngui import TTDialog +GardenPage_Basket = 0 +GardenPage_Collection = 1 +GardenPage_Trophy = 2 +GardenPage_Specials = 3 +TROPHIES_PER_ROW = 5 + +class GardenPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('GardenPage') + + def __init__(self): + self.notify.debug('__init__') + ShtikerPage.ShtikerPage.__init__(self) + self.mode = GardenPage_Basket + self.accept('use-special-response', self.useSpecialDone) + self.resultDialog = None + return + + def enter(self): + self.notify.debug('enter') + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + self.accept(localAvatar.uniqueName('flowerBasketChange'), self.updatePage) + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.notify.debug('exit') + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'specialsFrame'): + self.specialsFrame.hide() + if hasattr(self, 'specialsPhoto'): + self.specialsPhoto.hide() + if hasattr(self, 'useSpecialButton'): + self.hide() + self.cleanupResultDialog() + ShtikerPage.ShtikerPage.exit(self) + + def load(self): + self.notify.debug('load') + ShtikerPage.ShtikerPage.load(self) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + trophyCase = gui.find('**/trophyCase1') + trophyCase.find('glass1').reparentTo(trophyCase, -1) + trophyCase.find('shelf').reparentTo(trophyCase, -1) + self.trophyCase = trophyCase + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + self.basketTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageBasketTab, text_scale=TTLocalizer.GPbasketTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Basket], pos=(0.92, 0, 0.55)) + self.collectionTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageCollectionTab, text_scale=TTLocalizer.GPcollectionTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Collection], pos=(0.92, 0, 0.1)) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageTrophyTab, text_scale=TTLocalizer.GPtrophyTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Trophy], pos=(0.92, 0, -0.3)) + self.specialsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GardenPageSpecialsTab, text_scale=TTLocalizer.GPspecialsTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[GardenPage_Specials], pos=(0.92, 0, -0.3)) + self.basketTab.setPos(-0.75, 0, 0.775) + self.collectionTab.setPos(-0.33, 0, 0.775) + self.trophyTab.setPos(0.09, 0, 0.775) + self.specialsTab.setPos(0.51, 0, 0.775) + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.gardenSpecialsList = 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, -1.1), 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.2, 0.0, 0.05), itemFrame_relief=None, numItemsVisible=18, items=[], pos=(-0.6, 0, 0.45)) + self.gardenSpecialsList.hide() + self.specialsFrame = DirectFrame(parent=self, relief=None, pos=(0.45, 0.0, 0.25), text='', text_wordwrap=14.4, text_pos=(0, -0.46), text_scale=0.06) + self.specialsInfo = DirectLabel(parent=self.specialsFrame, relief=None, pos=(0.0, 0.0, -0.0), text=' ', text_wordwrap=12.4, text_pos=(0, -0.46), text_scale=0.06) + self.specialsPhoto = SpecialsPhoto.SpecialsPhoto(-1, parent=self.specialsFrame) + self.specialsPhoto.setBackBounds(-0.3, 0.3, -0.235, 0.25) + self.specialsPhoto.setBackColor(1.0, 1.0, 0.74901, 1.0) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.useSpecialButton = DirectButton(parent=self, relief=None, image=okImageList, pos=(0.45, 0, -0.5), text=TTLocalizer.UseSpecial, text_scale=0.06, text_pos=(0, -0.1), command=self.__useSpecial) + buttons.removeNode() + return + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + self.gardenSpecialsList.hide() + self.specialsPhoto.hide() + self.specialsFrame.hide() + self.useSpecialButton.hide() + if mode == GardenPage_Basket: + self.title['text'] = TTLocalizer.GardenPageTitleBasket + if not hasattr(self, 'picker'): + self.createFlowerPicker() + self.picker.show() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.basketTab['state'] = DGG.DISABLED + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + self.specialsTab['state'] = DGG.NORMAL + elif mode == GardenPage_Collection: + self.title['text'] = TTLocalizer.GardenPageTitleCollection + if hasattr(self, 'picker'): + self.picker.hide() + if not hasattr(self, 'browser'): + self.createAlbumBrowser() + self.browser.show() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.basketTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + self.specialsTab['state'] = DGG.NORMAL + elif mode == GardenPage_Trophy: + self.title['text'] = TTLocalizer.GardenPageTitleTrophy + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if not hasattr(self, 'trophyFrame'): + self.createGardenTrophyFrame() + self.trophyFrame.show() + self.basketTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + self.specialsTab['state'] = DGG.NORMAL + elif mode == GardenPage_Specials: + self.title['text'] = TTLocalizer.GardenPageTitleSpecials + if hasattr(self, 'picker'): + self.picker.hide() + if hasattr(self, 'browser'): + self.browser.hide() + if hasattr(self, 'trophyFrame'): + self.trophyFrame.hide() + self.basketTab['state'] = DGG.NORMAL + self.collectionTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + self.specialsTab['state'] = DGG.DISABLED + self.gardenSpecialsList.show() + specialsList = localAvatar.getGardenSpecials() + self.specialsPhoto.show() + self.specialsFrame.show() + self.createGardenSpecialsList() + self.updatePage() + + def createGardenSpecialsList(self): + self.clearGS() + self.specialsInfo['text'] = '' + self.useSpecialButton.hide() + self.specialsPhoto.hide() + self.specialsPhoto.update(-1) + self.specialsPhoto.show() + specialsList = localAvatar.getGardenSpecials() + firstEntry = None + if len(specialsList) == 0: + self.gardenSpecialsList['incButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + self.gardenSpecialsList['incButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + self.gardenSpecialsList['decButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + self.gardenSpecialsList['decButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 0.0) + else: + self.gardenSpecialsList['incButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + self.gardenSpecialsList['incButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + self.gardenSpecialsList['decButton_image1_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + self.gardenSpecialsList['decButton_image3_color'] = Vec4(1.0, 0.9, 0.4, 1.0) + for entry in specialsList: + if not firstEntry: + firstEntry = entry + someItem = DirectScrolledListItem(parent=self.gardenSpecialsList, text='%s x %s' % (GardenGlobals.Specials[entry[0]]['photoName'], entry[1]), text_align=TextNode.ALeft, text_fg=(0.0, 0.0, 0.0, 1), text_bg=(1.0, 1.0, 1, 0), text_scale=0.06, relief=None, command=self.showSpecialsPanel, extraArgs=[entry]) + self.gardenSpecialsList.addItem(someItem) + self.specialsPhoto.show() + + if firstEntry: + self.showSpecialsPanel(firstEntry) + return + + def showSpecialsPanel(self, entry): + type = entry[0] + number = entry[1] + self.specialsPhoto.hide() + self.specialsPhoto.update(type) + self.specialsPhoto.show() + self.specialsInfo['text'] = GardenGlobals.Specials[entry[0]]['description'] + self.selectedSpecial = type + specialInfo = GardenGlobals.Specials[entry[0]] + if specialInfo.has_key('useFromShtiker') and specialInfo['useFromShtiker']: + self.useSpecialButton.show() + else: + self.useSpecialButton.hide() + + def __useSpecial(self): + self.useSpecialButton['state'] = DGG.DISABLED + localAvatar.sendUpdate('reqUseSpecial', [self.selectedSpecial]) + + def clearGS(self): + while len(self.gardenSpecialsList['items']) > 0: + for item in self.gardenSpecialsList['items']: + self.gardenSpecialsList.removeItem(item, 1) + if hasattr(item, 'destroy'): + item.destroy() + if hasattr(item, 'delete'): + item.delete() + del item + + def createAlbumBrowser(self): + if not hasattr(self, 'browser'): + self.browser = FlowerBrowser.FlowerBrowser(self) + self.browser.setScale(1.1) + self.collectedTotal = DirectLabel(parent=self.browser, relief=None, text='', text_scale=0.06, pos=(0, 0, -0.61)) + return + + def createGardenTrophyFrame(self): + if not hasattr(self, 'trophyFrame'): + self.trophyFrame = DirectFrame(parent=self, relief=None, image=self.trophyCase, image_pos=(0, 1, 0), image_scale=0.034) + self.trophyFrame.hide() + self.trophies = [] + hOffset = -0.5 + vOffset = 0.4 + for level, trophyDesc in GardenGlobals.TrophyDict.items(): + trophy = GardenTrophy(-1) + trophy.nameLabel['text'] = trophyDesc[0] + trophy.reparentTo(self.trophyFrame) + trophy.setScale(0.36) + if level % TROPHIES_PER_ROW == 0: + hOffset = -0.5 + vOffset -= 0.4 + trophy.setPos(hOffset, 0, vOffset) + hOffset += 0.25 + self.trophies.append(trophy) + + return + + def createFlowerPicker(self): + if not hasattr(self, 'picker'): + self.picker = FlowerPicker.FlowerPicker(self) + self.picker.setPos(-0.555, 0, 0.1) + self.picker.setScale(0.95) + self.FUDGE_FACTOR = 0.01 + self.barLength = 1.1 + self.shovelBar = DirectWaitBar(parent=self.picker, pos=(0.95, 0, -0.55), relief=DGG.SUNKEN, frameSize=(-0.65, + 1.05, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=0.45, frameColor=(0.8, 0.8, 0.7, 1), barColor=(0.6, 0.4, 0.2, 1), range=self.barLength + self.FUDGE_FACTOR, value=self.barLength * 0.5 + self.FUDGE_FACTOR, text=' ' + TTLocalizer.Laff, text_scale=0.11, text_fg=(0.05, 0.14, 0.2, 1), text_align=TextNode.ALeft, text_pos=(-0.57, -0.035)) + self.wateringCanBar = DirectWaitBar(parent=self.picker, pos=(0.95, 0, -0.75), relief=DGG.SUNKEN, frameSize=(-0.65, + 1.05, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=0.45, frameColor=(0.8, 0.8, 0.7, 1), barColor=(0.4, 0.6, 1.0, 1), range=self.barLength + self.FUDGE_FACTOR, value=self.barLength * 0.5 + self.FUDGE_FACTOR, text=' ' + TTLocalizer.Laff, text_scale=0.11, text_fg=(0.05, 0.14, 0.2, 1), text_align=TextNode.ALeft, text_pos=(-0.57, -0.035)) + + def unload(self): + print 'gardenPage Unloading' + if hasattr(self, 'specialsPhoto'): + del self.specialsPhoto + if hasattr(self, 'trophies'): + del self.trophies + if hasattr(self, 'trophyCase'): + del self.trophyCase + if hasattr(self, 'useSpecialButton'): + self.useSpecialButton.destroy() + del self.useSpecialButton + self.cleanupResultDialog() + self.gardenSpecialsList.destroy() + self.basketTab.destroy() + self.collectionTab.destroy() + self.trophyTab.destroy() + self.specialsTab.destroy() + ShtikerPage.ShtikerPage.unload(self) + + def updatePage(self): + if hasattr(self, 'collectedTotal'): + self.collectedTotal['text'] = TTLocalizer.GardenPageCollectedTotal % (len(base.localAvatar.flowerCollection), GardenGlobals.getNumberOfFlowerVarieties()) + if hasattr(self, 'shovelBar'): + shovel = base.localAvatar.shovel + shovelName = TTLocalizer.ShovelNameDict[shovel] + curShovelSkill = base.localAvatar.shovelSkill + maxShovelSkill = GardenGlobals.ShovelAttributes[shovel]['skillPts'] + if shovel == GardenGlobals.MAX_SHOVELS - 1: + maxShovelSkill -= 1 + wateringCan = base.localAvatar.wateringCan + wateringCanName = TTLocalizer.WateringCanNameDict[wateringCan] + curWateringCanSkill = base.localAvatar.wateringCanSkill + maxWateringCanSkill = GardenGlobals.WateringCanAttributes[wateringCan]['skillPts'] + if wateringCan == GardenGlobals.MAX_WATERING_CANS - 1: + maxWateringCanSkill -= 1 + textToUse = TTLocalizer.GardenPageShovelInfo % (shovelName, curShovelSkill, maxShovelSkill) + self.shovelBar['text'] = textToUse + self.shovelBar['value'] = float(curShovelSkill) / float(maxShovelSkill) * self.barLength + self.FUDGE_FACTOR + textToUse = TTLocalizer.GardenPageWateringCanInfo % (wateringCanName, curWateringCanSkill, maxWateringCanSkill) + self.wateringCanBar['text'] = textToUse + self.wateringCanBar['value'] = float(curWateringCanSkill) / float(maxWateringCanSkill) * self.barLength + self.FUDGE_FACTOR + else: + print 'no shovel bar' + if self.mode == GardenPage_Collection: + if hasattr(self, 'browser'): + self.browser.update() + elif self.mode == GardenPage_Basket: + if hasattr(self, 'picker'): + newBasketFlower = base.localAvatar.flowerBasket.getFlower() + self.picker.update(newBasketFlower) + elif self.mode == GardenPage_Trophy: + if hasattr(self, 'trophies'): + for trophy in self.trophies: + trophy.setLevel(-1) + + for trophyId in base.localAvatar.getGardenTrophies(): + self.trophies[trophyId].setLevel(trophyId) + + elif self.mode == GardenPage_Specials: + self.createGardenSpecialsList() + if not base.cr.playGame.getPlace().getState() == 'stickerBook': + self.specialsPhoto.hide() + + def destroy(self): + self.notify.debug('destroy') + self.useSpecialButton.destroy() + if hasattr(self, 'gardenSpecialsList'): + self.clearGS() + self.gardenSpecialsList.destroy() + self.ignoreAll() + self.cleanupResultDialog() + DirectFrame.destroy(self) + + def useSpecialDone(self, response): + stringToShow = '' + if response == 'success': + stringToShow = TTLocalizer.UseSpecialSuccess + elif response == 'badlocation': + stringToShow = TTLocalizer.UseSpecialBadLocation + else: + stringToShow = 'Unknown response %s' % response + self.resultDialog = TTDialog.TTDialog(parent=aspect2dp, style=TTDialog.Acknowledge, text=stringToShow, command=self.cleanupResultDialog) + + def cleanupResultDialog(self, value = None): + if self.resultDialog: + self.resultDialog.destroy() + self.resultDialog = None + self.useSpecialButton['state'] = DGG.NORMAL + return + + +class GardenTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GardenTrophy') + + def __init__(self, level): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(GardenTrophy) + self.trophy = loader.loadModel('phase_3.5/models/gui/fishingTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.bowl = loader.loadModel('phase_3.5/models/gui/fishingTrophyBowl') + self.bowl.reparentTo(self) + self.bowl.setPos(0, 1, 0) + self.bowl.setScale(2.0) + self.bowlTop = self.bowl.find('**/fishingTrophyGreyBowl') + self.bowlBase = self.bowl.find('**/fishingTrophyBase') + self.bowlBase.setScale(1.25, 1, 1) + self.bowlBase.setColorScale(1, 1, 0.8, 1) + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='Trophy Text', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + order = ('C', 'D', 'B', 'A') + scales = (0.25, 0.25, 0.22, 0.25) + metalTrophy = ('wheelbarrel', 'shovels', 'flower', 'watering_can') + if self.level >= 0 and self.level < len(order): + modelStr = 'phase_5.5/models/estate/trophy' + modelStr += order[level] + self.gardenTrophy = loader.loadModel(modelStr) + self.gardenTrophy.setScale(scales[level]) + self.gardenTrophy.reparentTo(self) + self.metalTrophy = self.gardenTrophy.find('**/%s' % metalTrophy[level]) + if level == -1: + self.trophy.hide() + self.bowl.hide() + self.nameLabel.hide() + elif level == 0: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level == 1: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -0.5) + self.__bronze() + elif level == 2: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__silver() + elif level == 3: + self.trophy.show() + self.trophy.hide() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 2.61878) + self.top.setPos(0, 0, 0.5) + self.__silver() + elif level == 4: + self.trophy.show() + self.bowl.hide() + self.nameLabel.show() + self.column.setScale(1.3229, 1.26468, 3.11878) + self.top.setPos(0, 0, 1) + self.__gold() + elif level == 5: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(1.75) + self.nameLabel.show() + self.__bronze() + elif level == 6: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.0) + self.nameLabel.show() + self.__silver() + elif level >= 7: + self.trophy.hide() + self.bowl.show() + self.bowlTop.setScale(2.25) + self.nameLabel.show() + self.__gold() + + def __bronze(self): + self.top.setColorScale(0.9, 0.6, 0.33, 1) + self.bowlTop.setColorScale(0.9, 0.6, 0.33, 1) + self.metalTrophy.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.top.setColorScale(0.9, 0.9, 1, 1) + self.bowlTop.setColorScale(0.9, 0.9, 1, 1) + self.metalTrophy.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.top.setColorScale(1, 0.95, 0.1, 1) + self.bowlTop.setColorScale(1, 0.95, 0.1, 1) + self.metalTrophy.setColorScale(1, 0.95, 0.1, 1) + + def destroy(self): + self.trophy.removeNode() + self.bowl.removeNode() + self.shadow.removeNode() + if hasattr(self, 'gardenTrophy'): + self.gardenTrophy.removeNode() + DirectFrame.destroy(self) diff --git a/toontown/shtiker/GolfPage.py b/toontown/shtiker/GolfPage.py new file mode 100644 index 0000000..35e4096 --- /dev/null +++ b/toontown/shtiker/GolfPage.py @@ -0,0 +1,451 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.fishing.FishPhoto import DirectRegion +from toontown.shtiker.ShtikerPage import ShtikerPage +from toontown.toonbase import ToontownGlobals, TTLocalizer +from FishPage import FishingTrophy +from toontown.golf import GolfGlobals +if (__debug__): + import pdb +PageMode = PythonUtil.Enum('Records, Trophy') + +class GolfPage(ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfPage') + + def __init__(self): + ShtikerPage.__init__(self) + self.avatar = None + self.mode = PageMode.Trophy + return + + def enter(self): + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + ShtikerPage.enter(self) + + def exit(self): + self.golfTrophies.hide() + self.golfRecords.hide() + ShtikerPage.exit(self) + + def setAvatar(self, av): + self.avatar = av + + def getAvatar(self): + return self.avatar + + def load(self): + ShtikerPage.load(self) + self.golfRecords = GolfingRecordsUI(self.avatar, self) + self.golfRecords.hide() + self.golfRecords.load() + self.golfTrophies = GolfTrophiesUI(self.avatar, self) + self.golfTrophies.hide() + self.golfTrophies.load() + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + self.recordsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GolfPageRecordsTab, text_scale=TTLocalizer.GPrecordsTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Records], pos=TTLocalizer.GPrecordsTabPos) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.GolfPageTrophyTab, text_scale=TTLocalizer.GPtrophyTab, text_pos=TTLocalizer.GPtrophyTabTextPos, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Trophy], pos=TTLocalizer.GPtrophyTabPos) + self.recordsTab.setPos(-0.13, 0, 0.775) + self.trophyTab.setPos(0.28, 0, 0.775) + adjust = -0.2 + self.recordsTab.setX(self.recordsTab.getX() + adjust) + self.trophyTab.setX(self.trophyTab.getX() + adjust) + gui.removeNode() + return + + def unload(self): + self.avatar = None + ShtikerPage.unload(self) + return + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + if mode == PageMode.Records: + self.title['text'] = TTLocalizer.GolfPageTitleRecords + self.recordsTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + elif mode == PageMode.Trophy: + self.title['text'] = TTLocalizer.GolfPageTitleTrophy + self.recordsTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + else: + raise StandardError, 'GolfPage::setMode - Invalid Mode %s' % mode + self.updatePage() + + def updatePage(self): + if self.mode == PageMode.Records: + self.golfTrophies.hide() + self.golfRecords.show() + elif self.mode == PageMode.Trophy: + self.golfTrophies.show() + self.golfRecords.hide() + else: + raise StandardError, 'GolfPage::updatePage - Invalid Mode %s' % self.mode + + +class GolfingRecordsUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfingRecordsUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.bestDisplayList = [] + self.lastHoleBest = [] + self.lastCourseBest = [] + self.scrollList = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + self.gui.removeNode() + self.scrollList.destroy() + del self.avatar + del self.lastHoleBest + del self.lastCourseBest + del self.bestDisplayList + del self.scrollList + DirectFrame.destroy(self) + + def load(self): + self.gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.listXorigin = -0.5 + self.listFrameSizeX = 1.5 + self.listZorigin = -0.9 + self.listFrameSizeZ = 1.04 + self.arrowButtonScale = 1.3 + self.itemFrameXorigin = -0.237 + self.itemFrameZorigin = 0.365 + self.labelXstart = self.itemFrameXorigin + 0.293 + self.scrollList = DirectScrolledList(parent=self, relief=None, pos=(0, 0, 0), incButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(self.arrowButtonScale, self.arrowButtonScale, -self.arrowButtonScale), incButton_pos=(self.labelXstart, 0, self.itemFrameZorigin - 0.999), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(self.arrowButtonScale, self.arrowButtonScale, self.arrowButtonScale), decButton_pos=(self.labelXstart, 0, self.itemFrameZorigin + 0.227), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(self.itemFrameXorigin, 0, self.itemFrameZorigin), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(self.listXorigin, + self.listXorigin + self.listFrameSizeX, + self.listZorigin, + self.listZorigin + self.listFrameSizeZ), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=12, forceHeight=0.083, items=[]) + for courseId in GolfGlobals.CourseInfo: + courseName = GolfGlobals.getCourseName(courseId) + frame = DirectFrame(parent=self.scrollList, relief=None) + courseNameDisplay = DirectLabel(parent=frame, relief=None, pos=(-0.475, 0, 0.05), text=courseName, text_align=TextNode.ALeft, text_scale=0.075, text_fg=(0.85, 0.64, 0.13, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont()) + bestScoreDisplay = DirectLabel(parent=frame, relief=None, pos=(0.9, 0, 0.05), text=TTLocalizer.KartRace_Unraced, text_scale=0.06, text_fg=(0.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getToonFont()) + self.bestDisplayList.append(bestScoreDisplay) + self.scrollList.addItem(frame) + + for holeId in GolfGlobals.HoleInfo: + holeName = GolfGlobals.getHoleName(holeId) + frame = DirectFrame(parent=self.scrollList, relief=None) + holeNameDisplay = DirectLabel(parent=frame, relief=None, pos=(-0.475, 0, 0.05), text=holeName, text_align=TextNode.ALeft, text_scale=0.075, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont()) + bestScoreDisplay = DirectLabel(parent=frame, relief=None, pos=(0.9, 0, 0.05), text=TTLocalizer.KartRace_Unraced, text_scale=0.06, text_fg=(0.0, 0.0, 0.0, 1.0), text_font=ToontownGlobals.getToonFont()) + self.bestDisplayList.append(bestScoreDisplay) + self.scrollList.addItem(frame) + + return + + def show(self): + bestHoles = self.avatar.getGolfHoleBest() + bestCourses = self.avatar.getGolfCourseBest() + if bestHoles != self.lastHoleBest or bestCourses != self.lastCourseBest: + numCourse = len(GolfGlobals.CourseInfo.keys()) + numHoles = len(GolfGlobals.HoleInfo.keys()) + for i in xrange(numCourse): + score = bestCourses[i] + if score != 0: + self.bestDisplayList[i]['text'] = (str(score),) + else: + self.bestDisplayList[i]['text'] = TTLocalizer.KartRace_Unraced + + for i in xrange(numHoles): + score = bestHoles[i] + if score != 0: + self.bestDisplayList[i + numCourse]['text'] = str(score) + else: + self.bestDisplayList[i + numCourse]['text'] = TTLocalizer.KartRace_Unraced + + self.lastHoleBest = bestHoles[:] + self.lastCourseBest = bestCourses[:] + DirectFrame.show(self) + + def regenerateScrollList(self): + print '### regen scroll' + selectedIndex = 0 + if self.scrollList: + selectedIndex = self.scrollList.getSelectedIndex() + for label in self.bestDisplayList: + label.detachNode() + + self.scrollList.destroy() + self.scrollList = None + self.scrollList.scrollTo(selectedIndex) + return + + +class GolfTrophiesUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfTrophiesUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.trophyPanels = [] + self.cupPanels = [] + self.trophies = None + self.cups = None + self.trophyTextDisplay = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + for panel in self.trophyPanels: + panel.destroy() + + for panel in self.cupPanels: + panel.destroy() + + self.currentHistory.destroy() + self.trophyTextDisplay.destroy() + del self.avatar + del self.currentHistory + del self.trophyPanels + del self.trophies + del self.trophyTextDisplay + del self.cups + del self.cupPanels + DirectFrame.destroy(self) + + def load(self): + self.trophies = base.localAvatar.getGolfTrophies()[:] + self.cups = base.localAvatar.getGolfCups()[:] + xStart = -0.76 + yStart = 0.475 + xOffset = 0.17 + yOffset = 0.23 + for j in range(GolfGlobals.NumCups): + for i in range(GolfGlobals.TrophiesPerCup): + trophyPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart - j * yOffset), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + trophyPanel.scale = 0.2 + trophyPanel.setScale(trophyPanel.scale) + self.trophyPanels.append(trophyPanel) + + xStart = -0.25 + yStart = -0.38 + xOffset = 0.25 + for i in range(GolfGlobals.NumCups): + cupPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + cupPanel.scale = 0.3 + cupPanel.setScale(cupPanel.scale) + self.cupPanels.append(cupPanel) + + self.currentHistory = DirectLabel(parent=self, relief=None, text='', text_scale=0.05, text_fg=(0, 0, 0.95, 1.0), text_pos=(0, -0.65)) + self.trophyTextDisplay = DirectLabel(parent=self, relief=None, text='', text_scale=0.07, text_fg=(1, 0, 0, 1), text_shadow=(0, 0, 0, 0), text_pos=(0.0, -0.175), text_font=ToontownGlobals.getInterfaceFont()) + self.updateTrophies() + return + + def grow(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 0.8, 1.0) + if index < GolfGlobals.NumTrophies: + self.trophyTextDisplay['text'] = TTLocalizer.GolfTrophyTextDisplay % {'number': index + 1, + 'desc': TTLocalizer.GolfTrophyDescriptions[index]} + historyIndex = GolfGlobals.getHistoryIndexForTrophy(index) + if historyIndex >= 0: + self.currentHistory['text'] = TTLocalizer.GolfCurrentHistory % {'historyDesc': TTLocalizer.GolfHistoryDescriptions[historyIndex], + 'num': self.avatar.getGolfHistory()[historyIndex]} + + def shrink(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + self.trophyTextDisplay['text'] = '' + self.currentHistory['text'] = '' + + def growCup(self, index, pos): + self.cupPanels[index]['image_color'] = Vec4(1.0, 1.0, 0.8, 1.0) + if index < GolfGlobals.NumTrophies: + self.trophyTextDisplay['text'] = TTLocalizer.GolfCupTextDisplay % {'number': index + 1, + 'desc': TTLocalizer.GolfCupDescriptions[index]} + + def shrinkCup(self, index, pos): + self.cupPanels[index]['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + self.trophyTextDisplay['text'] = '' + + def show(self): + self.currentHistory['text'] = '' + if self.trophies != base.localAvatar.getGolfTrophies(): + self.trophies = base.localAvatar.getGolfTrophies() + self.cups = base.localAvatar.getGolfCups() + self.updateTrophies() + DirectFrame.show(self) + + def updateTrophies(self): + for t in range(len(self.trophyPanels)): + if self.trophies[t]: + trophyPanel = self.trophyPanels[t] + trophyPanel['text'] = '' + golfTrophy = trophyPanel.find('**/*GolfTrophy*') + if golfTrophy.isEmpty(): + trophyModel = GolfTrophy(t) + trophyModel.reparentTo(trophyPanel) + trophyModel.nameLabel.hide() + trophyModel.setPos(0, 0, -0.4) + trophyPanel['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + trophyPanel.bind(DGG.ENTER, self.grow, extraArgs=[t]) + trophyPanel.bind(DGG.EXIT, self.shrink, extraArgs=[t]) + else: + trophyPanel = self.trophyPanels[t] + toBeNukedGolfTrophy = trophyPanel.find('**/*GolfTrophy*') + if not toBeNukedGolfTrophy.isEmpty(): + toBeNukedGolfTrophy.removeNode() + trophyPanel['text'] = TTLocalizer.SuitPageMystery[0] + trophyPanel['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + trophyPanel.unbind(DGG.ENTER) + trophyPanel.unbind(DGG.EXIT) + + for t in range(len(self.cupPanels)): + if self.cups[t]: + cupPanel = self.cupPanels[t] + cupPanel['text'] = '' + cupTrophy = cupPanel.find('**/*GolfTrophy*') + if cupTrophy.isEmpty(): + cupModel = GolfTrophy(t + GolfGlobals.NumTrophies) + cupModel.reparentTo(cupPanel) + cupModel.nameLabel.hide() + cupModel.setPos(0, 0, -0.4) + cupPanel['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + cupPanel.bind(DGG.ENTER, self.growCup, extraArgs=[t]) + cupPanel.bind(DGG.EXIT, self.shrinkCup, extraArgs=[t]) + else: + cupPanel = self.cupPanels[t] + toBeNukedGolfCup = cupPanel.find('**/*GolfTrophy*') + if not toBeNukedGolfCup.isEmpty(): + toBeNukedGolfCup.removeNode() + cupPanel['text'] = TTLocalizer.SuitPageMystery[0] + cupPanel['image_color'] = Vec4(0.8, 0.8, 0.8, 1) + cupPanel.unbind(DGG.ENTER) + cupPanel.unbind(DGG.EXIT) + + +class GolfTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('GolfTrophy') + + def __init__(self, level, *args, **kwargs): + opts = {'relief': None} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.trophy = loader.loadModel('phase_6/models/golf/golfTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.topBase.setColorScale(1, 1, 0.8, 1) + self.greyBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl2') + self.greyBowl.reparentTo(self) + self.greyBowl.setPos(0, 0.5, 0) + self.greyBowl.setScale(2.0) + self.goldBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl') + self.goldBowl.reparentTo(self) + self.goldBowl.setPos(0, 0.5, 0) + self.goldBowl.setScale(2.0) + self.goldBowlBase = self.goldBowl.find('**/fishingTrophyBase') + self.goldBowlBase.hide() + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + if level == -1: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.hide() + self.nameLabel.hide() + else: + self.nameLabel.show() + if level >= 30: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.show() + self.goldBowlBase.hide() + else: + self.trophy.show() + self.goldBowl.hide() + self.greyBowl.hide() + if level == 30: + self.goldBowl.setScale(4.4, 3.1, 3.1) + elif level == 31: + self.goldBowl.setScale(3.6, 3.5, 3.5) + elif level >= 32: + self.goldBowl.setScale(5.6, 3.9, 3.9) + if level % 3 == 0: + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level % 3 == 1: + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -.5) + self.__silver() + elif level % 3 == 2: + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__gold() + if level < 10: + self.__tealColumn() + elif level < 20: + self.__purpleColumn() + elif level < 30: + self.__blueColumn() + else: + self.__redColumn() + + def __bronze(self): + self.statue.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.statue.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __platinum(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __tealColumn(self): + self.column.setColorScale(0.5, 1.2, 0.85, 1) + + def __purpleColumn(self): + self.column.setColorScale(1, 0.7, 0.95, 1) + + def __redColumn(self): + self.column.setColorScale(1.2, 0.6, 0.6, 1) + + def __yellowColumn(self): + self.column.setColorScale(1, 1, 0.8, 1) + + def __blueColumn(self): + self.column.setColorScale(0.6, 0.75, 1.2, 1) + + def destroy(self): + self.trophy.removeNode() + self.goldBowl.removeNode() + self.greyBowl.removeNode() + self.shadow.removeNode() + DirectFrame.destroy(self) diff --git a/toontown/shtiker/HtmlView.py b/toontown/shtiker/HtmlView.py new file mode 100644 index 0000000..ab300f6 --- /dev/null +++ b/toontown/shtiker/HtmlView.py @@ -0,0 +1,283 @@ +import array, sys +from direct.showbase.DirectObject import DirectObject +from direct.task.Task import Task +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import Texture +from pandac.PandaModules import CardMaker +from pandac.PandaModules import NodePath +from pandac.PandaModules import Point3, Vec3, Vec4, VBase4D, Point2 +from pandac.PandaModules import PNMImage +from pandac.PandaModules import TextureStage +from pandac.PandaModules import Texture +from pandac.PandaModules import WindowProperties +from direct.interval.IntervalGlobal import * +from pandac.PandaModules import AwWebView +from pandac.PandaModules import AwWebCore +WEB_WIDTH_PIXELS = 784 +WEB_HEIGHT_PIXELS = 451 +WEB_WIDTH = 1024 +WEB_HEIGHT = 512 +WEB_HALF_WIDTH = WEB_WIDTH / 2 +WIN_WIDTH = 800 +WIN_HEIGHT = 600 +GlobalWebcore = None + +class HtmlView(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('HtmlView') + useHalfTexture = base.config.GetBool('news-half-texture', 0) + + def __init__(self, parent = aspect2d): + global GlobalWebcore + self.parent = parent + self.mx = 0 + self.my = 0 + self.htmlFile = 'index.html' + self.transparency = False + if GlobalWebcore: + pass + else: + GlobalWebcore = AwWebCore(AwWebCore.LOGVERBOSE, True, AwWebCore.PFBGRA) + GlobalWebcore.setBaseDirectory('.') + for errResponse in xrange(400, 600): + GlobalWebcore.setCustomResponsePage(errResponse, 'error.html') + + self.webView = GlobalWebcore.createWebView(WEB_WIDTH, WEB_HEIGHT, self.transparency, False, 70) + frameName = '' + inGameNewsUrl = self.getInGameNewsUrl() + self.imgBuffer = array.array('B') + for i in xrange(WEB_WIDTH * WEB_HEIGHT): + self.imgBuffer.append(0) + self.imgBuffer.append(0) + self.imgBuffer.append(0) + self.imgBuffer.append(255) + + if self.useHalfTexture: + self.leftBuffer = array.array('B') + for i in xrange(WEB_HALF_WIDTH * WEB_HEIGHT): + self.leftBuffer.append(0) + self.leftBuffer.append(0) + self.leftBuffer.append(0) + self.leftBuffer.append(255) + + self.rightBuffer = array.array('B') + for i in xrange(WEB_HALF_WIDTH * WEB_HEIGHT): + self.rightBuffer.append(0) + self.rightBuffer.append(0) + self.rightBuffer.append(0) + self.rightBuffer.append(255) + + self.setupTexture() + if self.useHalfTexture: + self.setupHalfTextures() + self.accept('mouse1', self.mouseDown, [AwWebView.LEFTMOUSEBTN]) + self.accept('mouse3', self.mouseDown, [AwWebView.RIGHTMOUSEBTN]) + self.accept('mouse1-up', self.mouseUp, [AwWebView.LEFTMOUSEBTN]) + self.accept('mouse3-up', self.mouseUp, [AwWebView.RIGHTMOUSEBTN]) + + def getInGameNewsUrl(self): + result = base.config.GetString('fallback-news-url', 'http://cdn.toontown.disney.go.com/toontown/en/gamenews/') + override = base.config.GetString('in-game-news-url', '') + if override: + self.notify.info('got an override url, using %s for in a game news' % override) + result = override + else: + try: + launcherUrl = base.launcher.getValue('GAME_IN_GAME_NEWS_URL', '') + if launcherUrl: + result = launcherUrl + self.notify.info('got GAME_IN_GAME_NEWS_URL from launcher using %s' % result) + else: + self.notify.info('blank GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) + except: + self.notify.warning('got exception getting GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) + + return result + + def setupTexture(self): + cm = CardMaker('quadMaker') + cm.setColor(1.0, 1.0, 1.0, 1.0) + aspect = base.camLens.getAspectRatio() + htmlWidth = 2.0 * aspect * WEB_WIDTH_PIXELS / float(WIN_WIDTH) + htmlHeight = 2.0 * float(WEB_HEIGHT_PIXELS) / float(WIN_HEIGHT) + cm.setFrame(-htmlWidth / 2.0, htmlWidth / 2.0, -htmlHeight / 2.0, htmlHeight / 2.0) + bottomRightX = WEB_WIDTH_PIXELS / float(WEB_WIDTH + 1) + bottomRightY = WEB_HEIGHT_PIXELS / float(WEB_HEIGHT + 1) + cm.setUvRange(Point2(0, 1 - bottomRightY), Point2(bottomRightX, 1)) + card = cm.generate() + self.quad = NodePath(card) + self.quad.reparentTo(self.parent) + self.guiTex = Texture('guiTex') + self.guiTex.setupTexture(Texture.TT2dTexture, WEB_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) + self.guiTex.setMinfilter(Texture.FTLinear) + self.guiTex.setKeepRamImage(True) + self.guiTex.makeRamImage() + self.guiTex.setWrapU(Texture.WMRepeat) + self.guiTex.setWrapV(Texture.WMRepeat) + ts = TextureStage('webTS') + self.quad.setTexture(ts, self.guiTex) + self.quad.setTexScale(ts, 1.0, -1.0) + self.quad.setTransparency(0) + self.quad.setTwoSided(True) + self.quad.setColor(1.0, 1.0, 1.0, 1.0) + self.calcMouseLimits() + + def setupHalfTextures(self): + self.setupLeftTexture() + self.setupRightTexture() + self.fullPnmImage = PNMImage(WEB_WIDTH, WEB_HEIGHT, 4) + self.leftPnmImage = PNMImage(WEB_HALF_WIDTH, WEB_HEIGHT, 4) + self.rightPnmImage = PNMImage(WEB_HALF_WIDTH, WEB_HEIGHT, 4) + + def setupLeftTexture(self): + cm = CardMaker('quadMaker') + cm.setColor(1.0, 1.0, 1.0, 1.0) + aspect = base.camLens.getAspectRatio() + htmlWidth = 2.0 * aspect * WEB_WIDTH / float(WIN_WIDTH) + htmlHeight = 2.0 * float(WEB_HEIGHT) / float(WIN_HEIGHT) + cm.setFrame(-htmlWidth / 2.0, 0, -htmlHeight / 2.0, htmlHeight / 2.0) + card = cm.generate() + self.leftQuad = NodePath(card) + self.leftQuad.reparentTo(self.parent) + self.leftGuiTex = Texture('guiTex') + self.leftGuiTex.setupTexture(Texture.TT2dTexture, WEB_HALF_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) + self.leftGuiTex.setKeepRamImage(True) + self.leftGuiTex.makeRamImage() + self.leftGuiTex.setWrapU(Texture.WMClamp) + self.leftGuiTex.setWrapV(Texture.WMClamp) + ts = TextureStage('leftWebTS') + self.leftQuad.setTexture(ts, self.leftGuiTex) + self.leftQuad.setTexScale(ts, 1.0, -1.0) + self.leftQuad.setTransparency(0) + self.leftQuad.setTwoSided(True) + self.leftQuad.setColor(1.0, 1.0, 1.0, 1.0) + + def setupRightTexture(self): + cm = CardMaker('quadMaker') + cm.setColor(1.0, 1.0, 1.0, 1.0) + aspect = base.camLens.getAspectRatio() + htmlWidth = 2.0 * aspect * WEB_WIDTH / float(WIN_WIDTH) + htmlHeight = 2.0 * float(WEB_HEIGHT) / float(WIN_HEIGHT) + cm.setFrame(0, htmlWidth / 2.0, -htmlHeight / 2.0, htmlHeight / 2.0) + card = cm.generate() + self.rightQuad = NodePath(card) + self.rightQuad.reparentTo(self.parent) + self.rightGuiTex = Texture('guiTex') + self.rightGuiTex.setupTexture(Texture.TT2dTexture, WEB_HALF_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) + self.rightGuiTex.setKeepRamImage(True) + self.rightGuiTex.makeRamImage() + self.rightGuiTex.setWrapU(Texture.WMClamp) + self.rightGuiTex.setWrapV(Texture.WMClamp) + ts = TextureStage('rightWebTS') + self.rightQuad.setTexture(ts, self.rightGuiTex) + self.rightQuad.setTexScale(ts, 1.0, -1.0) + self.rightQuad.setTransparency(0) + self.rightQuad.setTwoSided(True) + self.rightQuad.setColor(1.0, 1.0, 1.0, 1.0) + + def calcMouseLimits(self): + ll = Point3() + ur = Point3() + self.quad.calcTightBounds(ll, ur) + self.notify.debug('ll=%s ur=%s' % (ll, ur)) + offset = self.quad.getPos(aspect2d) + self.notify.debug('offset = %s ' % offset) + ll.setZ(ll.getZ() + offset.getZ()) + ur.setZ(ur.getZ() + offset.getZ()) + self.notify.debug('new LL=%s, UR=%s' % (ll, ur)) + relPointll = self.quad.getRelativePoint(aspect2d, ll) + self.notify.debug('relPoint = %s' % relPointll) + self.mouseLL = (aspect2d.getScale()[0] * ll[0], aspect2d.getScale()[2] * ll[2]) + self.mouseUR = (aspect2d.getScale()[0] * ur[0], aspect2d.getScale()[2] * ur[2]) + self.notify.debug('original mouseLL=%s, mouseUR=%s' % (self.mouseLL, self.mouseUR)) + + def writeTex(self, filename = 'guiText.png'): + self.notify.debug('writing texture') + self.guiTex.generateRamMipmapImages() + self.guiTex.write(filename) + + def toggleRotation(self): + if self.interval.isPlaying(): + self.interval.finish() + else: + self.interval.loop() + + def mouseDown(self, button): + messenger.send('wakeup') + self.webView.injectMouseDown(button) + + def mouseUp(self, button): + self.webView.injectMouseUp(button) + + def reload(self): + pass + + def zoomIn(self): + self.webView.zoomIn() + + def zoomOut(self): + self.webView.zoomOut() + + def toggleTransparency(self): + self.transparency = not self.transparency + self.webView.setTransparent(self.transparency) + + def update(self, task): + if base.mouseWatcherNode.hasMouse(): + x, y = self._translateRelativeCoordinates(base.mouseWatcherNode.getMouseX(), base.mouseWatcherNode.getMouseY()) + if self.mx - x != 0 or self.my - y != 0: + self.webView.injectMouseMove(x, y) + self.mx, self.my = x, y + if self.webView.isDirty(): + self.webView.render(self.imgBuffer.buffer_info()[0], WEB_WIDTH * 4, 4) + Texture.setTexturesPower2(2) + textureBuffer = self.guiTex.modifyRamImage() + textureBuffer.setData(self.imgBuffer.tostring()) + if self.useHalfTexture: + self.guiTex.store(self.fullPnmImage) + self.leftPnmImage.copySubImage(self.fullPnmImage, 0, 0, 0, 0, WEB_HALF_WIDTH, WEB_HEIGHT) + self.rightPnmImage.copySubImage(self.fullPnmImage, 0, 0, WEB_HALF_WIDTH, 0, WEB_HALF_WIDTH, WEB_HEIGHT) + self.leftGuiTex.load(self.leftPnmImage) + self.rightGuiTex.load(self.rightPnmImage) + self.quad.hide() + Texture.setTexturesPower2(1) + GlobalWebcore.update() + return Task.cont + + def _translateRelativeCoordinates(self, x, y): + sx = int((x - self.mouseLL[0]) / (self.mouseUR[0] - self.mouseLL[0]) * WEB_WIDTH_PIXELS) + sy = WEB_HEIGHT_PIXELS - int((y - self.mouseLL[1]) / (self.mouseUR[1] - self.mouseLL[1]) * WEB_HEIGHT_PIXELS) + return (sx, sy) + + def unload(self): + self.ignoreAll() + self.webView.destroy() + self.webView = None + return + + def onCallback(self, name, args): + if name == 'requestFPS': + pass + + def onBeginNavigation(self, url, frameName): + pass + + def onBeginLoading(self, url, frameName, statusCode, mimeType): + pass + + def onFinishLoading(self): + self.notify.debug('finished loading') + + def onReceiveTitle(self, title, frameName): + pass + + def onChangeTooltip(self, tooltip): + pass + + def onChangeCursor(self, cursor): + pass + + def onChangeKeyboardFocus(self, isFocused): + pass + + def onChangeTargetURL(self, url): + pass diff --git a/toontown/shtiker/InGameNewsFrame.py b/toontown/shtiker/InGameNewsFrame.py new file mode 100644 index 0000000..44166d9 --- /dev/null +++ b/toontown/shtiker/InGameNewsFrame.py @@ -0,0 +1,39 @@ +import datetime +from toontown.shtiker import HtmlView + +class InGameNewsFrame(HtmlView.HtmlView): + TaskName = 'HtmlViewUpdateTask' + + def __init__(self, parent = aspect2d): + HtmlView.HtmlView.__init__(self, parent) + self.initialLoadDone = False + self.accept('newsSnapshot', self.doSnapshot) + + def activate(self): + self.quad.show() + self.calcMouseLimits() + if not self.initialLoadDone: + inGameNewsUrl = self.getInGameNewsUrl() + self.webView.loadURL2(inGameNewsUrl) + self.initialLoadDone = True + taskMgr.add(self.update, self.TaskName) + + def deactivate(self): + self.quad.hide() + taskMgr.remove(self.TaskName) + + def unload(self): + self.deactivate() + HtmlView.HtmlView.unload(self) + self.ignore('newsSnapshot') + + def doSnapshot(self): + curtime = datetime.datetime.now() + filename = 'news_snapshot_' + curtime.isoformat() + filename = filename.replace(':', '-') + filename = filename.replace('.', '-') + pngfilename = filename + '.png' + self.writeTex(pngfilename) + jpgfilename = filename + '.jpg' + self.writeTex(jpgfilename) + return jpgfilename diff --git a/toontown/shtiker/InventoryPage.py b/toontown/shtiker/InventoryPage.py new file mode 100644 index 0000000..49c2b83 --- /dev/null +++ b/toontown/shtiker/InventoryPage.py @@ -0,0 +1,159 @@ +import ShtikerPage +from toontown.toonbase import ToontownBattleGlobals +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class InventoryPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.currentTrackInfo = None + self.onscreen = 0 + self.lastInventoryTime = globalClock.getRealTime() + return + + def load(self): + ShtikerPage.ShtikerPage.load(self) + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.InventoryPageTitle, text_scale=0.12, textMayChange=1, pos=(0, 0, 0.62)) + self.gagFrame = DirectFrame(parent=self, relief=None, pos=(0.1, 0, -0.47), scale=(0.35, 0.35, 0.35), geom=DGG.getDefaultDialogGeom(), geom_color=ToontownGlobals.GlobalDialogColor) + self.trackInfo = DirectFrame(parent=self, relief=None, pos=(-0.4, 0, -0.47), scale=(0.35, 0.35, 0.35), geom=DGG.getDefaultDialogGeom(), geom_scale=(1.4, 1, 1), geom_color=ToontownGlobals.GlobalDialogColor, text='', text_wordwrap=11, text_align=TextNode.ALeft, text_scale=0.12, text_pos=(-0.65, 0.3), text_fg=(0.05, 0.14, 0.4, 1)) + self.trackProgress = DirectWaitBar(parent=self.trackInfo, pos=(0, 0, -0.2), relief=DGG.SUNKEN, frameSize=(-0.6, + 0.6, + -0.1, + 0.1), borderWidth=(0.025, 0.025), scale=1.1, frameColor=(0.4, 0.6, 0.4, 1), barColor=(0.9, 1, 0.7, 1), text='0/0', text_scale=0.15, text_fg=(0.05, 0.14, 0.4, 1), text_align=TextNode.ACenter, text_pos=(0, -0.22)) + self.trackProgress.hide() + jarGui = loader.loadModel('phase_3.5/models/gui/jar_gui') + self.moneyDisplay = DirectLabel(parent=self, relief=None, pos=(0.55, 0, -0.5), scale=0.8, text=str(base.localAvatar.getMoney()), text_scale=0.18, text_fg=(0.95, 0.95, 0, 1), text_shadow=(0, 0, 0, 1), text_pos=(0, -0.1, 0), image=jarGui.find('**/Jar'), text_font=ToontownGlobals.getSignFont()) + jarGui.removeNode() + return + + def unload(self): + del self.title + ShtikerPage.ShtikerPage.unload(self) + + def __moneyChange(self, money): + self.moneyDisplay['text'] = str(money) + + def enter(self): + ShtikerPage.ShtikerPage.enter(self) + base.localAvatar.inventory.setActivateMode('book') + base.localAvatar.inventory.show() + base.localAvatar.inventory.reparentTo(self) + self.moneyDisplay['text'] = str(base.localAvatar.getMoney()) + self.accept('enterBookDelete', self.enterDeleteMode) + self.accept('exitBookDelete', self.exitDeleteMode) + self.accept('enterTrackFrame', self.updateTrackInfo) + self.accept('exitTrackFrame', self.clearTrackInfo) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + self.clearTrackInfo(self.currentTrackInfo) + self.ignore('enterBookDelete') + self.ignore('exitBookDelete') + self.ignore('enterTrackFrame') + self.ignore('exitTrackFrame') + self.ignore(localAvatar.uniqueName('moneyChange')) + self.makePageWhite(None) + base.localAvatar.inventory.hide() + base.localAvatar.inventory.reparentTo(hidden) + self.exitDeleteMode() + return + + def enterDeleteMode(self): + self.title['text'] = TTLocalizer.InventoryPageDeleteTitle + self.title['text_fg'] = (0, 0, 0, 1) + self.book['image_color'] = Vec4(1, 1, 0, 1) + + def exitDeleteMode(self): + self.title['text'] = TTLocalizer.InventoryPageTitle + self.title['text_fg'] = (0, 0, 0, 1) + self.book['image_color'] = Vec4(1, 1, 1, 1) + + def updateTrackInfo(self, trackIndex): + self.currentTrackInfo = trackIndex + trackName = TextEncoder.upper(ToontownBattleGlobals.Tracks[trackIndex]) + if base.localAvatar.hasTrackAccess(trackIndex): + curExp, nextExp = base.localAvatar.inventory.getCurAndNextExpValues(trackIndex) + trackText = '%s / %s' % (curExp, nextExp) + self.trackProgress['range'] = nextExp + self.trackProgress['value'] = curExp + if curExp >= ToontownBattleGlobals.regMaxSkill: + str = TTLocalizer.InventoryPageTrackFull % trackName + trackText = TTLocalizer.InventoryUberTrackExp % {'nextExp': ToontownBattleGlobals.MaxSkill - curExp} + self.trackProgress['range'] = ToontownBattleGlobals.UberSkill + uberCurrExp = curExp - ToontownBattleGlobals.regMaxSkill + self.trackProgress['value'] = uberCurrExp + else: + morePoints = nextExp - curExp + if morePoints == 1: + str = TTLocalizer.InventoryPageSinglePoint % {'trackName': trackName, + 'numPoints': morePoints} + else: + str = TTLocalizer.InventoryPagePluralPoints % {'trackName': trackName, + 'numPoints': morePoints} + self.trackInfo['text'] = str + self.trackProgress['text'] = trackText + self.trackProgress['frameColor'] = (ToontownBattleGlobals.TrackColors[trackIndex][0] * 0.6, + ToontownBattleGlobals.TrackColors[trackIndex][1] * 0.6, + ToontownBattleGlobals.TrackColors[trackIndex][2] * 0.6, + 1) + self.trackProgress['barColor'] = (ToontownBattleGlobals.TrackColors[trackIndex][0], + ToontownBattleGlobals.TrackColors[trackIndex][1], + ToontownBattleGlobals.TrackColors[trackIndex][2], + 1) + self.trackProgress.show() + else: + str = TTLocalizer.InventoryPageNoAccess % trackName + self.trackInfo['text'] = str + self.trackProgress.hide() + + def clearTrackInfo(self, trackIndex): + if self.currentTrackInfo == trackIndex: + self.trackInfo['text'] = '' + self.trackProgress.hide() + self.currentTrackInfo = None + return + + def acceptOnscreenHooks(self): + self.accept(ToontownGlobals.InventoryHotkeyOn, self.showInventoryOnscreen) + self.accept(ToontownGlobals.InventoryHotkeyOff, self.hideInventoryOnscreen) + + def ignoreOnscreenHooks(self): + self.ignore(ToontownGlobals.InventoryHotkeyOn) + self.ignore(ToontownGlobals.InventoryHotkeyOff) + + def showInventoryOnscreen(self): + messenger.send('wakeup') + timedif = globalClock.getRealTime() - self.lastInventoryTime + if timedif < 0.7: + return + self.lastInventoryTime = globalClock.getRealTime() + if self.onscreen or base.localAvatar.questPage.onscreen: + return + self.onscreen = 1 + base.localAvatar.inventory.setActivateMode('book') + base.localAvatar.inventory.show() + base.localAvatar.inventory.reparentTo(self) + self.moneyDisplay['text'] = str(base.localAvatar.getMoney()) + self.accept('enterTrackFrame', self.updateTrackInfo) + self.accept('exitTrackFrame', self.clearTrackInfo) + self.accept(localAvatar.uniqueName('moneyChange'), self.__moneyChange) + self.reparentTo(aspect2d) + self.title.hide() + self.show() + + def hideInventoryOnscreen(self): + if not self.onscreen: + return + self.onscreen = 0 + self.ignore('enterTrackFrame') + self.ignore('exitTrackFrame') + self.ignore(localAvatar.uniqueName('moneyChange')) + base.localAvatar.inventory.hide() + base.localAvatar.inventory.reparentTo(hidden) + self.reparentTo(self.book) + self.title.show() + self.hide() diff --git a/toontown/shtiker/IssueFrame.py b/toontown/shtiker/IssueFrame.py new file mode 100644 index 0000000..1e80b3d --- /dev/null +++ b/toontown/shtiker/IssueFrame.py @@ -0,0 +1,391 @@ +import os +from pandac.PandaModules import VirtualFileSystem, Filename, DSearchPath +from pandac.PandaModules import Texture, CardMaker, PNMImage, TextureStage +from pandac.PandaModules import NodePath +from pandac.PandaModules import Point2 +from direct.showbase import DirectObject +from direct.gui.DirectGui import DirectFrame, DirectButton, DGG, DirectLabel +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +WEB_WIDTH_PIXELS = 784 +WEB_HEIGHT_PIXELS = 451 +WEB_WIDTH = 1024 +WEB_HEIGHT = 512 +WEB_HALF_WIDTH = WEB_WIDTH / 2 +WIN_WIDTH = 800 +WIN_HEIGHT = 600 + +class IssueFrame(DirectFrame): + NewsBaseDir = config.GetString('news-base-dir', '/httpNews') + FrameDimensions = (-1.30666637421, + 1.30666637421, + -0.751666665077, + 0.751666665077) + notify = DirectNotifyGlobal.directNotify.newCategory('IssueFrame') + ContentPattern = 'tt_i_art_%s_%s%d.jpg' + SectionIdents = ['hom', + 'new', + 'evt', + 'tot', + 'att', + 'tnr'] + + def __init__(self, parent, newsDir, dateStr, myIssueIndex, numIssues, strFilenames): + DirectFrame.__init__(self, frameColor=(1, 1, 1, 0), frameSize=self.FrameDimensions, relief=DGG.FLAT, parent=parent) + self.hide() + self.accept('newsSnapshot', self.doSnapshot) + self.parent = parent + self.newsDir = newsDir + self.dateStr = dateStr + self.myIssueIndex = myIssueIndex + self.numIssues = numIssues + self.strFilenames = strFilenames + self.sectionList = [] + self.sectionFrames = {} + self.flatSubsectionList = [] + self.parseNewsContent() + self.load() + self.curSection = 0 + self.curSubsection = 0 + + def parseNewsContent(self): + for section, ident in enumerate(self.SectionIdents): + subSectionList = [] + curSubSection = 0 + endSearch = False + while not endSearch: + justName = self.ContentPattern % (self.dateStr, ident, curSubSection + 1) + fullName = Filename(self.newsDir + '/' + justName) + if self.strFilenames: + if justName in self.strFilenames: + subSectionList.append(fullName) + self.flatSubsectionList.append((section, curSubSection)) + curSubSection += 1 + else: + endSearch = True + else: + theFile = vfs.getFile(Filename(fullName), status_only=1) + if theFile: + subSectionList.append(fullName) + self.flatSubsectionList.append((section, curSubSection)) + curSubSection += 1 + else: + endSearch = True + + if not subSectionList: + self.notify.warning('Could not load %s' % fullName) + subSectionList.append('error_' + str(fullName)) + self.flatSubsectionList.append((section, 0)) + self.sectionList.append(subSectionList) + + self.notify.debug('%s' % self.sectionList) + + def getPreviousTarget(self, section, subSection): + result = None + if (section, subSection) in self.flatSubsectionList: + index = self.flatSubsectionList.index((section, subSection)) + if index > 0: + result = self.flatSubsectionList[index - 1] + return result + + def getNextTarget(self, section, subSection): + result = None + if (section, subSection) in self.flatSubsectionList: + index = self.flatSubsectionList.index((section, subSection)) + if index < len(self.flatSubsectionList) - 1: + result = self.flatSubsectionList[index + 1] + return result + + def load(self): + self.gui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_directNewsGui') + self.guiNav = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_directNewsGuiNav') + numPagesLoaded = 0 + totalNumberOfPages = len(self.flatSubsectionList) + for section, subSectionList in enumerate(self.sectionList): + self.notify.debug('loading section %d' % section) + self.sectionFrames[section] = {} + for subsection, fullFilename in enumerate(subSectionList): + self.notify.debug('loading subsection %d' % subsection) + newPage = self.createPage(section, subsection, fullFilename) + numPagesLoaded += 1 + self.sectionFrames[section][subsection] = newPage + + def createPage(self, section, subsection, fullFilename): + upsellBackground = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_newsStatusBackground') + imageScaleX = self.FrameDimensions[1] - self.FrameDimensions[0] + imageScaleY = self.FrameDimensions[3] - self.FrameDimensions[2] + pageFrame = DirectFrame(frameColor=(1, 1, 1, 0), frameSize=self.FrameDimensions, image=upsellBackground, image_scale=(imageScaleX, 1, imageScaleY), relief=DGG.FLAT, parent=self, text='', text_scale=0.06, text_pos=(0, -0.4)) + if 'error_' in str(fullFilename): + pageFrame['text'] = TTLocalizer.NewsPageErrorDownloadingFileCanStillRead % fullFilename[len('error_'):] + else: + quad = self.loadFlatQuad(fullFilename) + if quad: + quad.reparentTo(pageFrame) + else: + pageFrame['text'] = TTLocalizer.NewsPageErrorDownloadingFileCanStillRead % fullFilename + self.loadRightArrow(section, subsection, pageFrame) + self.loadLeftArrow(section, subsection, pageFrame) + if section == 0 and subsection == 0: + self.loadHomePageButtons(section, subsection, pageFrame) + else: + self.loadNavButtons(pageFrame) + pageFrame.hide() + return pageFrame + + def loadRightArrow(self, section, subsection, pageFrame): + nextTarget = self.getNextTarget(section, subsection) + position = (1.16, 0, -0.69) + xSize = 48 + desiredXSize = 22 + imageScale = float(desiredXSize) / xSize + if nextTarget: + image = self.gui.find('**/tt_i_art_btn_ArrowRight') + rollover = self.gui.find('**/tt_i_art_btn_ArrowRightRo') + rightArrow = DirectButton(relief=None, parent=pageFrame, command=self.gotoPage, extraArgs=(nextTarget[0], nextTarget[1]), image=(image, + image, + rollover, + image), pos=position, image_scale=imageScale) + return + + def loadLeftArrow(self, section, subsection, pageFrame): + prevTarget = self.getPreviousTarget(section, subsection) + position = (-1.16, 0, -0.69) + xSize = 48 + desiredXSize = 22 + imageScale = float(desiredXSize) / xSize + if prevTarget: + image = self.gui.find('**/tt_i_art_btn_ArrowLeft') + rollover = self.gui.find('**/tt_i_art_btn_ArrowLeftRo') + rightArrow = DirectButton(relief=None, parent=pageFrame, command=self.gotoPage, extraArgs=(prevTarget[0], prevTarget[1]), image=(image, + image, + rollover, + image), pos=position, image_scale=imageScale) + return + + def loadHomePageButtons(self, section, subsection, pageFrame): + buttonNames = ['', + 'tt_i_art_btn_HomNew', + 'tt_i_art_btn_HomEvt', + 'tt_i_art_btn_HomTot', + 'tt_i_art_btn_HomAsk', + 'tt_i_art_btn_HomTnr'] + rolloverButtonNames = [] + for name in buttonNames: + ro = name + 'Ro' + rolloverButtonNames.append(ro) + + positions = [(0, 0.0), + (-1.05333, 0, 0.29333), + (-1.05333, 0, 0.0666667), + (-1.05333, 0, -0.156667), + (-1.05333, 0, -0.383333), + (-1.05333, 0, -0.606667)] + xSize = 136 + desiredXSize = 69 + image_scale = float(desiredXSize) / xSize + image_scale *= float(69) / 70 + self.sectionBtns = [] + for section in xrange(1, len(self.SectionIdents)): + image = self.gui.find('**/%s' % buttonNames[section]) + rolloverImage = self.gui.find('**/%s' % rolloverButtonNames[section]) + if image.isEmpty(): + self.notify.error('cant find %s' % buttonNames[section]) + sectionBtn = DirectButton(relief=None, parent=pageFrame, image=(image, + image, + rolloverImage, + image), image_scale=image_scale, command=self.gotoPage, extraArgs=(section, 0), enableEdit=1, pos=positions[section]) + + readMorePos = (0.906666, 0, -0.19) + readImage = self.gui.find('**/tt_i_art_btn_ReadMore') + readRollover = self.gui.find('**/tt_i_art_btn_ReadMoreRo') + xSize = 228.0 + desiredXSize = 113.0 + imageScale = desiredXSize / xSize + readMoreBtn = DirectButton(relief=None, parent=pageFrame, image=(readImage, + readImage, + readRollover, + readImage), image_scale=imageScale, command=self.gotoPage, extraArgs=(1, 0), enableEdit=1, pos=readMorePos) + self.loadWeekNavButtons(pageFrame) + return + + def loadWeekNavButtons(self, pageFrame): + if self.numIssues <= 1: + return + if self.myIssueIndex == self.numIssues - 1: + weekStr = TTLocalizer.IssueFrameThisWeek + elif self.myIssueIndex == self.numIssues - 2: + weekStr = TTLocalizer.IssueFrameLastWeek + else: + weeksAgo = self.numIssues - self.myIssueIndex - 1 + weekStr = TTLocalizer.IssueFrameWeeksAgo % weeksAgo + prevImage = self.gui.find('**/tt_i_art_btn_ArchiveArrwLeftNormal') + prevImageRo = self.gui.find('**/tt_i_art_btn_ArchiveArrwLeftRo') + prevImageDisabled = self.gui.find('**/tt_i_art_btn_ArchiveArrwLeftDisabled') + actualY1 = 78.0 + desiredY1 = 42.0 + y1Scale = desiredY1 / actualY1 + prevWeekBtn = DirectButton(relief=None, parent=pageFrame, image=[prevImage, + prevImage, + prevImageRo, + prevImageDisabled], image_scale=y1Scale, command=self.changeWeek, extraArgs=(self.myIssueIndex - 1,), pos=(0.806666, 0, 0.62)) + if self.myIssueIndex == 0: + prevWeekBtn['state'] = DGG.DISABLED + nextImage = self.gui.find('**/tt_i_art_btn_ArchiveArrwRightNormal') + nextImageRo = self.gui.find('**/tt_i_art_btn_ArchiveArrwRightRo') + nextImageDisabled = self.gui.find('**/tt_i_art_btn_ArchiveArrwRightDisabled') + actualY2Scale = 63.0 + desiredY2Scale = 34.0 + y2Scale = desiredY2Scale / actualY2Scale + nextWeekBtn = DirectButton(relief=None, parent=pageFrame, image=[nextImage, + nextImage, + nextImageRo, + nextImageDisabled], image_scale=y2Scale, command=self.changeWeek, extraArgs=(self.myIssueIndex + 1,), pos=(1.16, 0, 0.623333)) + if self.myIssueIndex == self.numIssues - 1: + nextWeekBtn['state'] = DGG.DISABLED + actualX = 176.0 + desiredX = 89.0 + imageScale = desiredX / actualX + midImage = self.gui.find('**/tt_i_art_btn_ArchiveMiddle') + weekColor = (0.0 / 255.0, + 23.0 / 255.0, + 140.0 / 255.0, + 1.0) + weekLabel = DirectLabel(relief=None, image=midImage, image_scale=imageScale, parent=pageFrame, text=weekStr, text_font=ToontownGlobals.InterfaceFont, text_fg=weekColor, text_scale=0.043, text_pos=(0, -0.01, 0), pos=(0.983333, 0, 0.62)) + return + + def loadNavButtons(self, pageFrame): + buttonNames = ['tt_i_art_btn_NavHom', + 'tt_i_art_btn_NavNew', + 'tt_i_art_btn_NavEvt', + 'tt_i_art_btn_NavTot', + 'tt_i_art_btn_NavAtt', + 'tt_i_art_btn_NavTnr'] + rolloverButtonNames = [] + for name in buttonNames: + ro = name + 'Ro' + rolloverButtonNames.append(ro) + + xPos = 1.24667 + positions = [(xPos, 0, 0.623333), + (xPos, 0, 0.536663), + (xPos, 0, 0.45), + (xPos, 0, 0.36333), + (xPos, 0, 0.276667), + (xPos, 0, 0.19)] + xSize1 = 177 + desiredXSize1 = 90 + image_scale1 = float(desiredXSize1) / xSize1 + image_scale = 1 + xSize2 = 300 + desiredXSize2 = 152 + image_scale2 = float(desiredXSize2) / xSize2 + image_scale2 *= 30.0 / 30.0 + rolloverPositions = [(1.15, 0, 0.623333), + (1.15, 0, 0.533333), + (1.15, 0, 0.443333), + (1.045, 0, 0.353333), + (1.045, 0, 0.263334), + (1.045, 0, 0.173333)] + imageScales = [image_scale1, + image_scale1, + image_scale1, + image_scale2, + image_scale2, + image_scale2] + frameSizeAdj1 = 0.1 + frameSize1 = (-0.04 + frameSizeAdj1, + 0.04 + frameSizeAdj1, + -0.04, + 0.04) + frameSizeAdj2 = 0.21 + frameSize2 = (-0.04 + frameSizeAdj2, + 0.04 + frameSizeAdj2, + -0.04, + 0.04) + frameSizes = (frameSize1, + frameSize1, + frameSize1, + frameSize2, + frameSize2, + frameSize2) + self.sectionBtns = [] + for section in xrange(0, len(self.SectionIdents)): + image = self.guiNav.find('**/%s' % buttonNames[section]) + rolloverImage = self.guiNav.find('**/%s' % rolloverButtonNames[section]) + if image.isEmpty(): + self.notify.error('cant find %s' % buttonNames[section]) + sectionBtn = DirectButton(relief=None, parent=pageFrame, frameSize=frameSizes[section], image=(image, + rolloverImage, + rolloverImage, + image), image_scale=imageScales[section], command=self.gotoPage, extraArgs=(section, 0), enableEdit=1, pos=rolloverPositions[section]) + + return + + def gotoPage(self, section, subsection): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: INGAMENEWS: Goto Page') + self.sectionFrames[self.curSection][self.curSubsection].hide() + self.sectionFrames[section][subsection].show() + self.curSection = section + self.curSubsection = subsection + messenger.send('wakeup') + base.cr.centralLogger.writeClientEvent('news gotoPage %s %s %s' % (self.dateStr, section, subsection)) + + def loadFlatQuad(self, fullFilename): + cm = CardMaker('cm-%s' % fullFilename) + cm.setColor(1.0, 1.0, 1.0, 1.0) + aspect = base.camLens.getAspectRatio() + htmlWidth = 2.0 * aspect * WEB_WIDTH_PIXELS / float(WIN_WIDTH) + htmlHeight = 2.0 * float(WEB_HEIGHT_PIXELS) / float(WIN_HEIGHT) + cm.setFrame(-htmlWidth / 2.0, htmlWidth / 2.0, -htmlHeight / 2.0, htmlHeight / 2.0) + bottomRightX = WEB_WIDTH_PIXELS / float(WEB_WIDTH + 1) + bottomRightY = WEB_HEIGHT_PIXELS / float(WEB_HEIGHT + 1) + cm.setUvRange(Point2(0, 1 - bottomRightY), Point2(bottomRightX, 1)) + card = cm.generate() + quad = NodePath(card) + jpgFile = PNMImage(WEB_WIDTH, WEB_HEIGHT) + smallerJpgFile = PNMImage() + readFile = smallerJpgFile.read(Filename(fullFilename)) + if readFile: + jpgFile.copySubImage(smallerJpgFile, 0, 0) + guiTex = Texture('guiTex') + guiTex.setupTexture(Texture.TT2dTexture, WEB_WIDTH, WEB_HEIGHT, 1, Texture.TUnsignedByte, Texture.FRgba) + guiTex.setMinfilter(Texture.FTLinear) + guiTex.load(jpgFile) + guiTex.setWrapU(Texture.WMClamp) + guiTex.setWrapV(Texture.WMClamp) + ts = TextureStage('webTS') + quad.setTexture(ts, guiTex) + quad.setTransparency(0) + quad.setTwoSided(True) + quad.setColor(1.0, 1.0, 1.0, 1.0) + result = quad + else: + result = None + Texture.setTexturesPower2(1) + return result + + def loadBackground(self): + self.backFrame = DirectFrame(parent=self, frameColor=(1, 1, 1, 1), frameSize=self.FrameDimensions, pos=(0, 0, 0), relief=None) + return + + def loadMainPage(self): + self.mainFrame = DirectFrame(parent=self, frameSize=self.FrameDimensions, frameColor=(1, 0, 0, 1)) + + def activate(self): + pass + + def deactivate(self): + pass + + def unload(self): + self.ignore('newsSnapshot') + + def doSnapshot(self): + pass + + def changeWeek(self, newIssueWeek): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: INGAMENEWS: Change Week') + messenger.send('newsChangeWeek', [newIssueWeek]) diff --git a/toontown/shtiker/IssueFrameV2.py b/toontown/shtiker/IssueFrameV2.py new file mode 100644 index 0000000..96fc51c --- /dev/null +++ b/toontown/shtiker/IssueFrameV2.py @@ -0,0 +1,159 @@ +from pandac.PandaModules import Filename +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import DGG, DirectFrame, DirectButton +from toontown.shtiker import IssueFrame + +class IssueFrameV2(IssueFrame.IssueFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('IssueFrameV2') + SectionIdents = ['hom', + 'new', + 'evt', + 'tot', + 'att', + 'tnr', + 'ext'] + + def __init__(self, parent, newsDir, dateStr, myIssueIndex, numIssues, strFilenames, newsIndexEntries): + self.newsIndexEntries = newsIndexEntries + self.dateStr = dateStr + self.calcActualSectionsInThisIssue() + IssueFrame.IssueFrame.__init__(self, parent, newsDir, dateStr, myIssueIndex, numIssues, strFilenames) + self.notify.debug('version2 %s' % dateStr) + + def load(self): + self.guiNavV2 = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_directNewsGuiNavV2') + IssueFrame.IssueFrame.load(self) + + def calcActualSectionsInThisIssue(self): + self.actualSectionIdents = [] + for ident in self.SectionIdents: + identTest = self.dateStr + '_' + ident + '1' + if self.isSectionInIndex(identTest): + self.actualSectionIdents.append(ident) + + def isSectionInIndex(self, sectionIdent): + for name in self.newsIndexEntries: + if sectionIdent in name and self.dateStr in name: + return True + + return False + + def parseNewsContent(self): + existingSectionIndex = 0 + for section, ident in enumerate(self.SectionIdents): + subSectionList = [] + curSubSection = 0 + endSearch = False + while not endSearch: + justName = self.ContentPattern % (self.dateStr, ident, curSubSection + 1) + fullName = Filename(self.newsDir + '/' + justName) + if self.strFilenames: + if justName in self.strFilenames: + subSectionList.append(fullName) + self.flatSubsectionList.append((existingSectionIndex, curSubSection)) + curSubSection += 1 + else: + endSearch = True + else: + theFile = vfs.getFile(Filename(fullName), status_only=1) + if theFile: + subSectionList.append(fullName) + self.flatSubsectionList.append((existingSectionIndex, curSubSection)) + curSubSection += 1 + else: + if curSubSection == 0 and self.isSectionInIndex(ident): + self.notify.warning('could not find %s' % fullName) + subSectionList.append(fullName) + self.flatSubsectionList.append((existingSectionIndex, curSubSection)) + endSearch = True + + if not subSectionList: + pass + else: + self.sectionList.append(subSectionList) + existingSectionIndex += 1 + + self.notify.debug('IssueFrameV2 self.sectionList=%s' % self.sectionList) + + def loadHomePageButtons(self, section, subsection, pageFrame): + self.notify.debug('Doing nothing for loadNavButtons') + if section == 0 and subsection == 0: + self.loadNavButtons(pageFrame) + self.parentOfWeekNav = DirectFrame(frameColor=(1, 1, 1, 0), relief=DGG.FLAT, parent=pageFrame) + self.loadWeekNavButtons(self.parentOfWeekNav) + self.parentOfWeekNav.setPos(-1.94, 0, 0) + + def loadNavButtons(self, pageFrame): + identToButtonNames = {'hom': 'tt_i_art_btn_NavHom2', + 'new': 'tt_i_art_btn_NavNew2', + 'evt': 'tt_i_art_btn_NavEvt2', + 'tot': 'tt_i_art_btn_NavTot2', + 'att': 'tt_i_art_btn_NavAtt2', + 'tnr': 'tt_i_art_btn_NavTnr2', + 'ext': 'tt_i_art_btn_NavExt2'} + identToRolloverButtonNames = {'hom': 'tt_i_art_btn_NavHomRo2', + 'new': 'tt_i_art_btn_NavNewRo2', + 'evt': 'tt_i_art_btn_NavEvtRo2', + 'tot': 'tt_i_art_btn_NavTotRo2', + 'att': 'tt_i_art_btn_NavAttRo2', + 'tnr': 'tt_i_art_btn_NavTnrRo2', + 'ext': 'tt_i_art_btn_NavExtRo2'} + xPos = 1.24667 + positions = [(xPos, 0, 0.623333), + (xPos, 0, 0.536663), + (xPos, 0, 0.45), + (xPos, 0, 0.36333), + (xPos, 0, 0.276667), + (xPos, 0, 0.19), + (xPos, 0, 0.08)] + xSize1 = 177 + desiredXSize1 = 90 + image_scale1 = float(desiredXSize1) / xSize1 + image_scale = 1 + xSize2 = 300 + desiredXSize2 = 152 + image_scale2 = float(desiredXSize2) / xSize2 + image_scale2 *= 30.0 / 30.0 + rolloverPositions = [(1.045, 0, 0.623333), + (1.045, 0, 0.533333), + (1.045, 0, 0.443333), + (1.045, 0, 0.353333), + (1.045, 0, 0.263334), + (1.045, 0, 0.173333), + (1.045, 0, 0.09)] + imageScales = [image_scale2, + image_scale2, + image_scale2, + image_scale2, + image_scale2, + image_scale2, + image_scale2] + frameSizeAdj1 = 0.1 + frameSize1 = (-0.04 + frameSizeAdj1, + 0.04 + frameSizeAdj1, + -0.04, + 0.04) + frameSizeAdj2 = 0.21 + frameSize2 = (-0.04 + frameSizeAdj2, + 0.04 + frameSizeAdj2, + -0.04, + 0.04) + frameSizes = (frameSize2, + frameSize2, + frameSize2, + frameSize2, + frameSize2, + frameSize2, + frameSize2) + self.sectionBtns = [] + for section, ident in enumerate(self.actualSectionIdents): + image = self.guiNavV2.find('**/%s' % identToButtonNames[ident]) + rolloverImage = self.guiNavV2.find('**/%s' % identToRolloverButtonNames[ident]) + if image.isEmpty(): + self.notify.error('cant find %s' % identToButtonNames[ident]) + sectionBtn = DirectButton(relief=None, parent=pageFrame, frameSize=frameSizes[section], image=(image, + rolloverImage, + rolloverImage, + image), image_scale=imageScales[section], command=self.gotoPage, extraArgs=(section, 0), enableEdit=1, pos=rolloverPositions[section]) + + return diff --git a/toontown/shtiker/KartPage.py b/toontown/shtiker/KartPage.py new file mode 100644 index 0000000..0deb99f --- /dev/null +++ b/toontown/shtiker/KartPage.py @@ -0,0 +1,1049 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.fishing.FishPhoto import DirectRegion +from toontown.racing.KartDNA import * +from toontown.racing.Kart import Kart +from toontown.racing import RaceGlobals +from toontown.shtiker.ShtikerPage import ShtikerPage +from toontown.toonbase import ToontownGlobals, TTLocalizer +from FishPage import FishingTrophy +if (__debug__): + import pdb +PageMode = PythonUtil.Enum('Customize, Records, Trophy') + +class KartPage(ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('KartPage') + + def __init__(self): + ShtikerPage.__init__(self) + self.avatar = None + self.mode = PageMode.Customize + return + + def enter(self): + if not hasattr(self, 'title'): + self.load() + self.setMode(self.mode, 1) + ShtikerPage.enter(self) + + def exit(self): + self.kartCustomizer.hide() + self.racingTrophies.hide() + self.racingRecords.hide() + ShtikerPage.exit(self) + + def setAvatar(self, av): + self.avatar = av + + def getAvatar(self): + return self.avatar + + def load(self): + ShtikerPage.load(self) + self.kartCustomizer = KartCustomizeUI(self.avatar, self) + self.kartCustomizer.hide() + self.kartCustomizer.load() + self.racingRecords = RacingRecordsUI(self.avatar, self) + self.racingRecords.hide() + self.racingRecords.load() + self.racingTrophies = RacingTrophiesUI(self.avatar, self) + self.racingTrophies.hide() + self.racingTrophies.load() + self.title = DirectLabel(parent=self, relief=None, text='', text_scale=0.1, pos=(0, 0, 0.65)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + self.customizeTab = DirectButton(parent=self, relief=None, text=TTLocalizer.KartPageCustomizeTab, text_scale=TTLocalizer.KPkartTab, text_align=TextNode.ALeft, text_pos=(-0.025, 0.0, 0.0), image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Customize], pos=(0.92, 0, 0.55)) + self.recordsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.KartPageRecordsTab, text_scale=TTLocalizer.KPkartTab, text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Records], pos=(0.92, 0, 0.1)) + self.trophyTab = DirectButton(parent=self, relief=None, text=TTLocalizer.KartPageTrophyTab, text_scale=TTLocalizer.KPkartTab, text_pos=(0.03, 0.0, 0.0), text_align=TextNode.ALeft, image=gui.find('**/tabs/polySurface3'), image_pos=(-0.28, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Trophy], pos=(0.92, 0, -0.3)) + self.customizeTab.setPos(-0.55, 0, 0.775) + self.recordsTab.setPos(-0.13, 0, 0.775) + self.trophyTab.setPos(0.28, 0, 0.775) + gui.removeNode() + return + + def unload(self): + ShtikerPage.unload(self) + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + if mode == PageMode.Customize: + self.title['text'] = TTLocalizer.KartPageTitleCustomize + self.customizeTab['state'] = DGG.DISABLED + self.recordsTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.NORMAL + elif mode == PageMode.Records: + self.title['text'] = TTLocalizer.KartPageTitleRecords + self.customizeTab['state'] = DGG.NORMAL + self.recordsTab['state'] = DGG.DISABLED + self.trophyTab['state'] = DGG.NORMAL + elif mode == PageMode.Trophy: + self.title['text'] = TTLocalizer.KartPageTitleTrophy + self.customizeTab['state'] = DGG.NORMAL + self.recordsTab['state'] = DGG.NORMAL + self.trophyTab['state'] = DGG.DISABLED + else: + raise StandardError, 'KartPage::setMode - Invalid Mode %s' % mode + self.updatePage() + + def updatePage(self): + if self.mode == PageMode.Customize: + self.kartCustomizer.show() + self.racingTrophies.hide() + self.racingRecords.hide() + elif self.mode == PageMode.Records: + self.kartCustomizer.hide() + self.racingTrophies.hide() + self.racingRecords.show() + elif self.mode == PageMode.Trophy: + self.kartCustomizer.hide() + self.racingTrophies.show() + self.racingRecords.hide() + else: + raise StandardError, 'KartPage::updatePage - Invalid Mode %s' % self.mode + + +class KartCustomizeUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartCustomizeUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + self.itemSelector.destroy() + self.kartViewer.destroy() + del self.avatar + del self.itemSelector + del self.kartViewer + DirectFrame.destroy(self) + + def load(self): + uiRootNode = loader.loadModel('phase_6/models/gui/ShtikerBookUI') + self.itemSelector = ItemSelector(self.avatar, parent=self) + self.itemSelector.setPos(uiRootNode.find('**/uiAccessoryIcons').getPos()) + self.itemSelector.load(uiRootNode) + self.kartViewer = KartViewer(list(self.avatar.getKartDNA()), parent=self) + self.kartViewer.setPos(uiRootNode.find('**/uiKartView').getPos()) + self.kartViewer.load(uiRootNode, 'uiKartViewerFrame1', ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0.275, -.08)], ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (-.27, -.08)], (0, -.08)) + self.kartViewer.uiRotateLeft.setZ(-.25) + self.kartViewer.uiRotateRight.setZ(-.25) + self.itemSelector.itemViewers['main'].leftArrowButton.setZ(self.kartViewer.getZ() + 0.25) + self.itemSelector.itemViewers['main'].rightArrowButton.setZ(self.kartViewer.getZ() + 0.25) + self.kartViewer.setBounds(-0.38, 0.38, -0.25, 0.325) + self.kartViewer.setBgColor(1.0, 1.0, 0.8, 1.0) + uiRootNode.removeNode() + + def getKartViewer(self): + return self.kartViewer + + def show(self): + self.itemSelector.itemViewers['main'].initUpdatedDNA() + self.itemSelector.setupAccessoryIcons() + self.itemSelector.show() + self.kartViewer.show(list(self.avatar.getKartDNA())) + DirectFrame.show(self) + + def hide(self): + if hasattr(self, 'itemSelector'): + if hasattr(self.itemSelector.itemViewers['main'], 'updatedDNA'): + self.itemSelector.itemViewers['main'].setUpdatedDNA() + self.itemSelector.resetAccessoryIcons() + if hasattr(self.itemSelector.itemViewers['main'], 'confirmDlg'): + self.itemSelector.itemViewers['main'].confirmDlg.hide() + self.itemSelector.hide() + if hasattr(self, 'kartViewer'): + self.kartViewer.hide() + DirectFrame.hide(self) + + +class RacingRecordsUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RacingRecordsUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.timeDisplayList = [] + self.lastTimes = [] + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + del self.avatar + del self.lastTimes + del self.timeDisplayList + DirectFrame.destroy(self) + + def load(self): + offset = 0 + trackNameArray = TTLocalizer.KartRace_TrackNames + for trackId in RaceGlobals.TrackIds: + trackName = trackNameArray[trackId] + trackNameDisplay = DirectLabel(parent=self, relief=None, text=trackName, text_align=TextNode.ALeft, text_scale=0.075, text_fg=(0.95, 0.95, 0.0, 1.0), text_shadow=(0, 0, 0, 1), text_pos=(-0.8, 0.5 - offset), text_font=ToontownGlobals.getSignFont()) + bestTimeDisplay = DirectLabel(parent=self, relief=None, text=TTLocalizer.KartRace_Unraced, text_scale=0.06, text_fg=(0.0, 0.0, 0.0, 1.0), text_pos=(0.65, 0.5 - offset), text_font=ToontownGlobals.getToonFont()) + offset += 0.1 + self.timeDisplayList.append(bestTimeDisplay) + + return + + def show(self): + bestTimes = self.avatar.getKartingPersonalBestAll() + if bestTimes != self.lastTimes: + for i in range(0, len(bestTimes)): + time = bestTimes[i] + if time != 0.0: + whole, part = divmod(time, 1) + min, sec = divmod(whole, 60) + timeText = '%02d:%02d:%02d' % (min, sec, part * 100) + self.timeDisplayList[i]['text'] = (timeText,) + + self.lastTimes = bestTimes + DirectFrame.show(self) + + +class RacingTrophiesUI(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RacingTrophiesUI') + + def __init__(self, avatar, parent = aspect2d): + self.avatar = avatar + self.trophyPanels = [] + self.trophies = None + self.trophyTextDisplay = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + for panel in self.trophyPanels: + panel.destroy() + + self.ticketDisplay.destroy() + self.trophyTextDisplay.destroy() + del self.avatar + del self.ticketDisplay + del self.trophyPanels + del self.trophies + del self.trophyTextDisplay + DirectFrame.destroy(self) + + def load(self): + self.trophies = base.localAvatar.getKartingTrophies() + xStart = -0.76 + yStart = 0.475 + xOffset = 0.17 + yOffset = 0.23 + for j in range(RaceGlobals.NumCups): + for i in range(RaceGlobals.TrophiesPerCup): + trophyPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart - j * yOffset), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + trophyPanel.scale = 0.2 + trophyPanel.setScale(trophyPanel.scale) + self.trophyPanels.append(trophyPanel) + + xStart = -0.25 + yStart = -0.38 + xOffset = 0.25 + for i in range(RaceGlobals.NumCups): + cupPanel = DirectLabel(parent=self, relief=None, pos=(xStart + i * xOffset, 0.0, yStart), state=DGG.NORMAL, image=DGG.getDefaultDialogGeom(), image_scale=(0.75, 1, 1), image_color=(0.8, 0.8, 0.8, 1), text=TTLocalizer.SuitPageMystery[0], text_scale=0.45, text_fg=(0, 0, 0, 1), text_pos=(0, 0, -0.25), text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=5.5) + cupPanel.scale = 0.3 + cupPanel.setScale(cupPanel.scale) + self.trophyPanels.append(cupPanel) + + self.ticketDisplay = DirectLabel(parent=self, relief=None, image=loader.loadModel('phase_6/models/karting/tickets'), image_pos=(0.2, 0, -0.635), image_scale=0.2, text=TTLocalizer.KartPageTickets + str(self.avatar.getTickets()), text_scale=0.07, text_fg=(0, 0, 0.95, 1.0), text_pos=(0, -0.65), text_font=ToontownGlobals.getSignFont()) + self.trophyTextDisplay = DirectLabel(parent=self, relief=None, text='', text_scale=0.07, text_fg=(1, 0, 0, 1), text_shadow=(0, 0, 0, 0), text_pos=(0.0, -0.175), text_font=ToontownGlobals.getInterfaceFont()) + self.updateTrophies() + return + + def grow(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 0.8, 1.0) + self.trophyTextDisplay['text'] = TTLocalizer.KartPageTrophyDetail % (index + 1, TTLocalizer.KartTrophyDescriptions[index]) + + def shrink(self, index, pos): + self.trophyPanels[index]['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + self.trophyTextDisplay['text'] = '' + + def show(self): + self.ticketDisplay['text'] = (TTLocalizer.KartPageTickets + str(self.avatar.getTickets()),) + if self.trophies != base.localAvatar.getKartingTrophies(): + self.trophies = base.localAvatar.getKartingTrophies() + self.updateTrophies() + DirectFrame.show(self) + + def updateTrophies(self): + for t in range(len(self.trophyPanels)): + if self.trophies[t]: + trophyPanel = self.trophyPanels[t] + trophyPanel['text'] = '' + trophyModel = RacingTrophy(t) + trophyModel.reparentTo(trophyPanel) + trophyModel.nameLabel.hide() + trophyModel.setPos(0, 0, -0.4) + trophyPanel['image_color'] = Vec4(1.0, 1.0, 1.0, 1.0) + trophyPanel.bind(DGG.ENTER, self.grow, extraArgs=[t]) + trophyPanel.bind(DGG.EXIT, self.shrink, extraArgs=[t]) + + +class ItemSelector(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ItemSelector') + + class ItemViewer(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ItemViewer') + + def __init__(self, avatar, parent = aspect2d): + self.currItem = None + self.itemList = None + self.parent = parent + self.avatar = avatar + self.currAccessoryType = None + self.texCount = 1 + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0, 0, 0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + self.uiBgFrame.destroy() + self.uiImagePlane.destroy() + self.uiTextBox.destroy() + self.leftArrowButton.destroy() + self.rightArrowButton.destroy() + del self.avatar + del self.parent + del self.currItem + del self.itemList + del self.uiBgFrame + del self.uiImagePlane + del self.uiTextBox + del self.leftArrowButton + del self.rightArrowButton + del self.deleteButton + DirectFrame.destroy(self) + + def setCurrentItem(self, currItem): + self.currItem = currItem + + def getCurrentItem(self): + return self.currItem + + def initUpdatedDNA(self): + self.updatedDNA = list(self.avatar.getKartDNA()) + + def setUpdatedDNA(self): + currKartDNA = self.avatar.getKartDNA() + for i in xrange(len(self.updatedDNA)): + if self.updatedDNA[i] != currKartDNA[i]: + self.avatar.requestKartDNAFieldUpdate(i, self.updatedDNA[i]) + + del self.updatedDNA + + def setItemList(self, itemList): + self.itemList = itemList + + def load(self, uiRootNode): + self.uiBgFrame = DirectFrame(parent=self, relief=None, geom=uiRootNode.find('**/uiAccessoryViewerFrame'), scale=1.0) + self.uiImagePlane = DirectFrame(parent=self, relief=None, geom=uiRootNode.find('**/uiAccessoryImagePlane'), scale=0.75) + bounds = self.uiImagePlane.getBounds() + cm = CardMaker('uiImagePlane') + cm.setFrame(bounds[0], bounds[1], bounds[2], bounds[3]) + self.uiImagePlane['geom'] = NodePath(cm.generate()) + self.uiImagePlane.component('geom0').setColorScale(1.0, 1.0, 0.8, 1.0) + self.uiImagePlane.component('geom0').setTransparency(True) + self.locator1 = self.attachNewNode('locator1') + self.locator2 = self.attachNewNode('locator2') + self.locator1.setPos(0, 0, 0.035) + self.locator2.setPos(0.0, 0.0, 0.0) + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + self.uiImagePlane.component('geom0').setTexture(tex, self.texCount) + self.texCount += 1 + self.uiTextBox = DirectFrame(parent=self, relief=None, scale=1.0, text='', text_font=ToontownGlobals.getInterfaceFont(), text_fg=(0.5, 0, 0, 1), text_shadow=(0, 0, 0, 1), text_scale=0.0715, text_pos=(0.0, -0.23, 0.0)) + self.deleteButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/uiAccessorydelete_up'), + uiRootNode.find('**/uiAccessorydelete_down'), + uiRootNode.find('**/uiAccessorydelete_rollover'), + uiRootNode.find('**/uiAccessorydelete_rollover')), text=TTLocalizer.KartShtikerDelete, text_font=ToontownGlobals.getSignFont(), text_pos=(0, -0.125, 0), text_scale=TTLocalizer.KPdeleteButton, text_fg=(1, 1, 1, 1), scale=1.0, pressEffect=False, command=lambda : self.__handleItemDeleteConfirm()) + self.deleteButton.hide() + self.leftArrowButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/ArrowLeftButtonUp'), + uiRootNode.find('**/ArrowLeftButtonDown'), + uiRootNode.find('**/ArrowLeftButtonRollover'), + uiRootNode.find('**/ArrowLeftButtonInactive')), scale=1.0, pressEffect=False, command=lambda : self.__handleItemChange(-1)) + self.rightArrowButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/ArrowRightButtonUp'), + uiRootNode.find('**/ArrowRightButtonDown'), + uiRootNode.find('**/ArrowRightButtonRollover'), + uiRootNode.find('**/ArrowRightButtonInactive')), scale=1.0, pressEffect=False, command=lambda : self.__handleItemChange(1)) + return + + def enable(self): + self.leftArrowButton['state'] = DGG.NORMAL + self.rightArrowButton['state'] = DGG.NORMAL + + def disable(self): + self.leftArrowButton['state'] = DGG.DISABLED + self.rightArrowButton['state'] = DGG.DISABLED + + def setupViewer(self, category): + colorTypeList = [KartDNA.bodyColor, KartDNA.accColor] + if category == InvalidEntry: + self.__handleHideItem() + self.__updateDeleteButton(DGG.DISABLED) + self.disable() + else: + accessDict = getAccessDictByType(self.avatar.getKartAccessoriesOwned()) + self.currAccessoryType = category + if category in colorTypeList: + self.itemList = list(accessDict.get(KartDNA.bodyColor, [])) + self.itemList.append(InvalidEntry) + elif category == KartDNA.rimsType: + self.itemList = list(accessDict.get(KartDNA.rimsType, [])) + self.itemList.append(InvalidEntry) + else: + self.itemList = list(accessDict.get(category, [])) + self.currItem = self.updatedDNA[category] + if category in colorTypeList: + if self.currItem == InvalidEntry or self.currItem not in accessDict.get(KartDNA.bodyColor): + self.__updateDeleteButton(DGG.DISABLED) + else: + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + self.__handleShowItem() + elif category == KartDNA.rimsType: + if self.currItem == InvalidEntry: + self.__updateDeleteButton(DGG.DISABLED) + else: + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + self.__handleShowItem() + elif self.currItem != InvalidEntry and self.itemList != []: + if self.currItem in self.avatar.accessories: + self.__handleShowItem() + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + else: + self.__handleHideItem() + self.__updateDeleteButton(DGG.DISABLED) + if len(self.itemList) == 1: + if self.currAccessoryType == KartDNA.rimsType: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currAccessoryType in colorTypeList: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + else: + self.enable() + elif len(self.itemList) == 0: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + else: + if self.currAccessoryType == KartDNA.rimsType: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currAccessoryType in colorTypeList: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currItem == InvalidEntry: + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.enable() + + def resetViewer(self): + self.itemList = None + self.currItem = None + self.disable() + return + + def __updateDeleteButton(self, state, text = TTLocalizer.KartShtikerDelete): + self.deleteButton['state'] = state + self.deleteButton['text'] = text + if state == DGG.NORMAL: + self.uiImagePlane.setPos(self.locator1.getPos()) + self.deleteButton.show() + else: + self.uiImagePlane.setPos(self.locator2.getPos()) + self.deleteButton.hide() + + def setViewerText(self, text): + self.uiTextBox['text'] = text + + def __updateViewerUI(self): + accList = [KartDNA.bodyColor, KartDNA.accColor, KartDNA.rimsType] + if self.currItem != InvalidEntry: + self.__handleShowItem() + if self.currItem not in self.avatar.accessories and self.currAccessoryType in accList: + self.__updateDeleteButton(DGG.DISABLED) + else: + self.__updateDeleteButton(DGG.NORMAL, TTLocalizer.KartShtikerDelete) + else: + if self.currAccessoryType in accList: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.__handleShowItem() + else: + self.__handleHideItem() + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.__updateDeleteButton(DGG.DISABLED) + + def __handleItemChange(self, direction): + self.notify.debug('__handleItemChange: currItem %s' % self.currItem) + + def updateItem(self = self, direction = direction): + if self.itemList.count(self.currItem) != 0: + index = self.itemList.index(self.currItem) + index += direction + if index < 0 or index >= len(self.itemList): + invalidList = [KartDNA.bodyColor, KartDNA.accColor, KartDNA.rimsType] + if self.currAccessoryType not in invalidList: + self.currItem = InvalidEntry + elif direction > 0: + self.currItem = self.itemList[0] + else: + self.currItem = self.itemList[-1] + else: + self.currItem = self.itemList[index] + elif self.itemList == []: + self.currItem = InvalidEntry + elif direction > 0: + self.currItem = self.itemList[0] + else: + self.currItem = self.itemList[-1] + + messenger.send('wakeup') + updateItem() + self.__updateViewerUI() + self.notify.debug('__handleItemChange: currItem %s' % self.currItem) + self.updatedDNA[self.currAccessoryType] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(self.currAccessoryType, self.currItem) + + def __handleShowItem(self): + self.uiImagePlane.component('geom0').setColorScale(1.0, 1.0, 1.0, 1.0) + if self.currAccessoryType in [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType]: + texNodePath = getTexCardNode(self.currItem) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif self.currAccessoryType == KartDNA.rimsType: + if self.currItem == InvalidEntry: + texNodePath = getTexCardNode(getDefaultRim()) + else: + texNodePath = getTexCardNode(self.currItem) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath, 'phase_6/maps/%s_a.rgb' % texNodePath) + elif self.currAccessoryType in [KartDNA.bodyColor, KartDNA.accColor]: + tex = loader.loadTexture('phase_6/maps/Kartmenu_paintbucket.jpg', 'phase_6/maps/Kartmenu_paintbucket_a.rgb') + if self.currItem == InvalidEntry: + self.uiImagePlane.component('geom0').setColorScale(getDefaultColor()) + else: + self.uiImagePlane.component('geom0').setColorScale(getAccessory(self.currItem)) + elif self.currAccessoryType == KartDNA.decalType: + kart = self.parent.parent.getKartViewer().getKart() + kartDecal = getDecalId(kart.kartDNA[KartDNA.bodyType]) + texNodePath = getTexCardNode(self.currItem) + tex = loader.loadTexture('phase_6/maps/%s.jpg' % texNodePath % kartDecal, 'phase_6/maps/%s_a.rgb' % texNodePath % kartDecal) + else: + tex = loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb') + colorTypeList = [KartDNA.bodyColor, KartDNA.accColor] + if self.currItem == InvalidEntry: + if self.currAccessoryType == KartDNA.rimsType: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currAccessoryType in colorTypeList: + self.setViewerText(TTLocalizer.KartShtikerDefault % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + elif self.currItem == InvalidEntry: + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + else: + self.setViewerText(getAccName(self.currItem) + ' ' + getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + self.uiImagePlane.component('geom0').setTexture(tex, self.texCount) + self.texCount += 1 + + def __handleHideItem(self): + self.uiImagePlane.component('geom0').setColorScale(1.0, 1.0, 1.0, 1.0) + self.uiImagePlane.component('geom0').setTexture(loader.loadTexture('phase_6/maps/NoAccessoryIcon3.jpg', 'phase_6/maps/NoAccessoryIcon3_a.rgb'), self.texCount) + self.texCount += 1 + + def __handleItemDeleteConfirm(self): + self.notify.debug('__handleItemDeleteConfirm:') + if not hasattr(self, 'confirmDlg'): + uiRootNode = loader.loadModel('phase_6/models/gui/ShtikerBookUI') + self.confirmDlg = DirectFrame(parent=aspect2d, relief=None, geom=uiRootNode.find('**/uiAccessoryNotice'), geom_scale=1.0, text=TTLocalizer.KartPageConfirmDelete, text_scale=0.07, text_pos=(0, 0.022)) + self.confirmDlg.hide() + self.confirmDlg.setPos(aspect2d, 0, -.195, -.195) + self.cancelButton = DirectButton(parent=self.confirmDlg, relief=None, image=(uiRootNode.find('**/CancelButtonUp'), uiRootNode.find('**/CancelButtonDown'), uiRootNode.find('**/CancelButtonRollover')), geom=uiRootNode.find('**/CancelIcon'), scale=1.0, pressEffect=False, command=self.confirmDlg.hide) + self.confirmButton = DirectButton(parent=self.confirmDlg, relief=None, image=(uiRootNode.find('**/CheckButtonUp'), uiRootNode.find('**/CheckButtonDown'), uiRootNode.find('**/CheckButtonRollover')), geom=uiRootNode.find('**/CheckIcon'), scale=1.0, pressEffect=False, command=self.__handleItemDelete) + self.confirmDlg.show() + return + + def __handleItemDelete(self): + + def handleColorDelete(self = self): + if self.currAccessoryType == KartDNA.bodyColor: + if self.updatedDNA[KartDNA.accColor] == deletedItem: + self.avatar.requestKartDNAFieldUpdate(KartDNA.accColor, self.currItem) + self.updatedDNA[KartDNA.accColor] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(KartDNA.accColor, self.currItem) + elif self.currAccessoryType == KartDNA.accColor: + if self.updatedDNA[KartDNA.bodyColor] == deletedItem: + self.avatar.requestKartDNAFieldUpdate(KartDNA.bodyColor, self.currItem) + self.updatedDNA[KartDNA.bodyColor] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(KartDNA.bodyColor, self.currItem) + + self.notify.debug('__handleItemDelete: Delete request on accessory %s' % self.currItem) + self.confirmDlg.hide() + messenger.send('wakeup') + deletedItem = self.currItem + self.avatar.requestRemoveOwnedAccessory(deletedItem) + index = self.itemList.index(self.currItem) + self.itemList.pop(index) + self.currItem = InvalidEntry + self.__updateViewerUI() + self.updatedDNA[self.currAccessoryType] = self.currItem + kart = self.parent.parent.getKartViewer().getKart() + kart.updateDNAField(self.currAccessoryType, self.currItem) + if self.avatar.getAccessoryByType(self.currAccessoryType) == deletedItem: + self.avatar.requestKartDNAFieldUpdate(self.currAccessoryType, self.currItem) + if self.currAccessoryType in [KartDNA.bodyColor, KartDNA.accColor]: + handleColorDelete() + if self.itemList == [] or self.itemList[0] == InvalidEntry: + self.disable() + self.setViewerText(TTLocalizer.KartShtikerNo % getattr(TTLocalizer, AccessoryTypeNameDict[self.currAccessoryType])) + + def __init__(self, avatar, parent = aspect2d): + self.state = InvalidEntry + self.avatar = avatar + self.itemViewers = {} + self.buttonDict = {} + self.parent = parent + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0, 0, 0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + for key in self.buttonDict.keys(): + self.buttonDict[key].destroy() + del self.buttonDict[key] + + for key in self.itemViewers.keys(): + self.itemViewers[key].destroy() + del self.itemViewers[key] + + del self.avatar + del self.itemViewers + del self.buttonDict + del self.ebButton + del self.fwwButton + del self.bwwButton + del self.rimButton + del self.decalButton + del self.paintKartButton + del self.paintAccessoryButton + DirectFrame.destroy(self) + + def load(self, uiRootNode): + self.itemViewers['main'] = ItemSelector.ItemViewer(self.avatar, self) + self.itemViewers['main'].load(uiRootNode) + self.itemViewers['main'].setPos(self.getParent(), uiRootNode.find('**/uiAccessoryView').getPos()) + self.ebButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/eBlockButton_up'), + uiRootNode.find('**/eBlockButton_rollover'), + uiRootNode.find('**/eBlockButton_rollover'), + uiRootNode.find('**/eBlockButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.ebType)) + self.buttonDict[KartDNA.ebType] = self.ebButton + self.spButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/spoilerButton_up'), + uiRootNode.find('**/spoilerButton_rollover'), + uiRootNode.find('**/spoilerButton_rollover'), + uiRootNode.find('**/spoilerButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.spType)) + self.buttonDict[KartDNA.spType] = self.spButton + self.fwwButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/frontButton_up'), + uiRootNode.find('**/frontButton_rollover'), + uiRootNode.find('**/frontButton_rollover'), + uiRootNode.find('**/frontButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.fwwType)) + self.buttonDict[KartDNA.fwwType] = self.fwwButton + self.bwwButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/rearButton_up'), + uiRootNode.find('**/rearButton_rollover'), + uiRootNode.find('**/rearButton_rollover'), + uiRootNode.find('**/rearButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.bwwType)) + self.buttonDict[KartDNA.bwwType] = self.bwwButton + self.rimButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/rimButton_up'), + uiRootNode.find('**/rimButton_rollover'), + uiRootNode.find('**/rimButton_rollover'), + uiRootNode.find('**/rimButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.rimsType)) + self.buttonDict[KartDNA.rimsType] = self.rimButton + self.decalButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/decalButton_up'), + uiRootNode.find('**/decalButton_rollover'), + uiRootNode.find('**/decalButton_rollover'), + uiRootNode.find('**/decalButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.decalType)) + self.buttonDict[KartDNA.decalType] = self.decalButton + self.paintKartButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/paintKartButton_up'), + uiRootNode.find('**/paintKartButton_rollover'), + uiRootNode.find('**/paintKartButton_rollover'), + uiRootNode.find('**/paintKartButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.bodyColor)) + self.buttonDict[KartDNA.bodyColor] = self.paintKartButton + self.paintAccessoryButton = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/paintAccessoryButton_up'), + uiRootNode.find('**/paintAccessoryButton_rollover'), + uiRootNode.find('**/paintAccessoryButton_rollover'), + uiRootNode.find('**/paintAccessoryButton_inactive')), scale=1.0, pressEffect=False, command=lambda : self.__changeItemCategory(KartDNA.accColor)) + self.buttonDict[KartDNA.accColor] = self.paintAccessoryButton + return + + def setupAccessoryIcons(self): + accessDict = getAccessDictByType(self.avatar.getKartAccessoriesOwned()) + if accessDict == {}: + self.itemViewers['main'].disable() + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerNoAccessories) + return + self.__changeItemCategory(self.state) + + def resetAccessoryIcons(self): + for key in self.buttonDict.keys(): + self.buttonDict[key].setProp('state', DGG.NORMAL) + + self.itemViewers['main'].show() + self.itemViewers['main'].setViewerText('') + self.state = InvalidEntry + self.itemViewers['main'].resetViewer() + + def __changeItemCategory(self, buttonType): + if buttonType == KartDNA.ebType: + self.ebButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerEngineBlocks) + self.itemViewers['main'].setupViewer(KartDNA.ebType) + elif buttonType == KartDNA.spType: + self.spButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerSpoilers) + self.itemViewers['main'].setupViewer(KartDNA.spType) + elif buttonType == KartDNA.fwwType: + self.fwwButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerFrontWheelWells) + self.itemViewers['main'].setupViewer(KartDNA.fwwType) + elif buttonType == KartDNA.bwwType: + self.bwwButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerBackWheelWells) + self.itemViewers['main'].setupViewer(KartDNA.bwwType) + elif buttonType == KartDNA.rimsType: + self.rimButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerRims) + self.itemViewers['main'].setupViewer(KartDNA.rimsType) + elif buttonType == KartDNA.decalType: + self.decalButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerDecals) + self.itemViewers['main'].setupViewer(KartDNA.decalType) + elif buttonType == KartDNA.bodyColor: + self.paintKartButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerBodyColors) + self.itemViewers['main'].setupViewer(KartDNA.bodyColor) + elif buttonType == KartDNA.accColor: + self.paintAccessoryButton['state'] = DGG.DISABLED + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerAccColors) + self.itemViewers['main'].setupViewer(KartDNA.accColor) + elif buttonType == InvalidEntry: + self.itemViewers['main'].setViewerText(TTLocalizer.KartShtikerSelect) + self.itemViewers['main'].setupViewer(buttonType) + else: + raise StandardError, 'KartPage.py::__changeItemCategory - INVALID Category Type!' + if self.state != buttonType and self.state != InvalidEntry: + self.buttonDict[self.state]['state'] = DGG.NORMAL + self.buttonDict[self.state].setColorScale(1, 1, 1, 1) + self.state = buttonType + + +class KartViewer(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('KartViewer') + + def __init__(self, dna, parent): + self.kart = None + self.dna = dna + self.parent = parent + self.kartFrame = None + self.bounds = None + self.colors = None + self.uiRotateRight = None + self.uiRotateLeft = None + self.uiRotateLabel = None + DirectFrame.__init__(self, parent=parent, relief=None, pos=(0, 0, 0), scale=(1.0, 1.0, 1.0)) + return + + def destroy(self): + taskMgr.remove('kartRotateTask') + if self.kart != None: + self.kart.delete() + self.kart = None + if hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion.unload() + if hasattr(self, 'uiBgFrame'): + self.uiBgFrame.destroy() + del self.uiBgFrame + if hasattr(self, 'uiRotateLeft') and self.uiRotateLeft: + self.uiRotateLeft.destroy() + del self.uiRotateLeft + if hasattr(self, 'uiRotateRight') and self.uiRotateRight: + self.uiRotateRight.destroy() + del self.uiRotateRight + if hasattr(self, 'uiRotateLabelt') and self.uiRotateLabel: + self.uiRotateLabel.destroy() + del self.uiRotateLabel + if hasattr(self, 'dna'): + del self.dna + if hasattr(self, 'parent'): + del self.parent + DirectFrame.destroy(self) + return + + def load(self, uiRootNode, bgFrame = 'uiKartViewerFrame1', rightArrow = ['rotate_right_up', + 'rotate_right_down', + 'rotate_right_roll', + 'rotate_right_down', + (0, 0)], leftArrow = ['rotate_left_up', + 'rotate_left_down', + 'rotate_left_roll', + 'rotate_left_down', + (0, 0)], rotatePos = (0, 0)): + self.uiBgFrame = DirectFrame(parent=self, relief=None, geom=uiRootNode.find('**/' + bgFrame), scale=1.0) + if leftArrow and len(leftArrow) == 5: + self.uiRotateLeft = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/' + leftArrow[0]), + uiRootNode.find('**/' + leftArrow[1]), + uiRootNode.find('**/' + leftArrow[2]), + uiRootNode.find('**/' + leftArrow[3])), scale=1.0, text=TTLocalizer.KartView_Left, text_scale=TTLocalizer.KProtateButton, text_pos=(leftArrow[4][0], leftArrow[4][1], 0), text_fg=(1, 1, 1, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), pressEffect=False) + self.uiRotateLeft.bind(DGG.B1PRESS, self.__handleKartRotate, [-3]) + self.uiRotateLeft.bind(DGG.B1RELEASE, self.__endKartRotate) + if rightArrow and len(rightArrow) == 5: + self.uiRotateRight = DirectButton(parent=self, relief=None, geom=(uiRootNode.find('**/' + rightArrow[0]), + uiRootNode.find('**/' + rightArrow[1]), + uiRootNode.find('**/' + rightArrow[2]), + uiRootNode.find('**/' + rightArrow[3])), scale=1.0, text=TTLocalizer.KartView_Right, text_scale=TTLocalizer.KProtateButton, text_pos=(rightArrow[4][0], rightArrow[4][1], 0), text_fg=(1, 1, 1, 1.0), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getSignFont(), pressEffect=False) + self.uiRotateRight.bind(DGG.B1PRESS, self.__handleKartRotate, [3]) + self.uiRotateRight.bind(DGG.B1RELEASE, self.__endKartRotate) + return + + def setBounds(self, *bounds): + self.bounds = bounds + + def setBgColor(self, *colors): + self.colors = colors + + def makeKartFrame(self): + if self.kart != None: + self.kart.delete() + self.kart = None + if not hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion = DirectRegion(parent=self) + apply(self.kartDisplayRegion.setBounds, self.bounds) + apply(self.kartDisplayRegion.setColor, self.colors) + frame = self.kartDisplayRegion.load() + if self.dna: + self.kart = Kart() + self.kart.setDNA(self.dna) + self.kart.generateKart(forGui=1) + self.kart.setDepthTest(1) + self.kart.setDepthWrite(1) + self.pitch = frame.attachNewNode('pitch') + self.rotate = self.pitch.attachNewNode('rotate') + self.scale = self.rotate.attachNewNode('scale') + self.kart.reparentTo(self.scale) + bMin, bMax = self.kart.getKartBounds() + center = (bMin + bMax) / 2.0 + self.kart.setPos(-center[0], -center[1], -center[2]) + self.scale.setScale(0.5) + self.rotate.setH(-35) + self.pitch.setP(0) + self.pitch.setY(getKartViewDist(self.kart.getBodyType())) + self.kart.setScale(1, 1, 1.5) + self.kart.setTwoSided(1) + if self.uiRotateRight: + self.uiRotateRight.show() + if self.uiRotateLeft: + self.uiRotateLeft.show() + if self.uiRotateLabel: + self.uiRotateLabel.show() + else: + if self.uiRotateRight: + self.uiRotateRight.hide() + if self.uiRotateLeft: + self.uiRotateLeft.hide() + if self.uiRotateLabel: + self.uiRotateLabel.hide() + return frame + + def show(self, dna = None): + if self.kartFrame: + if self.kart != None: + self.kart.delete() + self.kart = None + if hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion.unload() + self.hide() + self.uiBgFrame.show() + self.refresh(dna) + self.__handleKartRotate(1) + return + + def hide(self): + self.uiBgFrame.hide() + if self.kart != None: + self.kart.delete() + self.kart = None + if hasattr(self, 'kartDisplayRegion'): + self.kartDisplayRegion.unload() + return + + def __handleKartRotate(self, direction, extraArgs = []): + taskMgr.add(self.__rotateTask, 'kartRotateTask', extraArgs=[direction]) + + def __rotateTask(self, direction): + if hasattr(self, 'pitch'): + self.pitch.setH(self.pitch.getH() + 0.4 * direction) + return Task.cont + else: + return Task.done + + def __endKartRotate(self, extraArgs = []): + taskMgr.remove('kartRotateTask') + + def getKart(self): + return self.kart + + def setDNA(self, dna): + self.dna = dna + + def refresh(self, dna = None): + taskMgr.removeTasksMatching('kartRotateTask') + if dna: + self.dna = dna + curPitch = 0 + if hasattr(self, 'pitch'): + curPitch = self.pitch.getH() + else: + curPitch = 0 + if self.kart != None: + self.kart.delete() + self.kart = None + del self.kartFrame + self.kartFrame = self.makeKartFrame() + if hasattr(self, 'pitch'): + self.pitch.setH(curPitch) + return + + +class RacingTrophy(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('RacingTrophy') + + def __init__(self, level, *args, **kwargs): + opts = {'relief': None} + opts.update(kwargs) + DirectFrame.__init__(self, *args, **opts) + self.trophy = loader.loadModel('phase_6/models/gui/racingTrophy') + self.trophy.reparentTo(self) + self.trophy.setPos(0, 1, 0) + self.trophy.setScale(0.1) + self.base = self.trophy.find('**/trophyBase') + self.column = self.trophy.find('**/trophyColumn') + self.top = self.trophy.find('**/trophyTop') + self.topBase = self.trophy.find('**/trophyTopBase') + self.statue = self.trophy.find('**/trophyStatue') + self.base.setColorScale(1, 1, 0.8, 1) + self.topBase.setColorScale(1, 1, 0.8, 1) + self.greyBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl2') + self.greyBowl.reparentTo(self) + self.greyBowl.setPos(0, 0.5, 0) + self.greyBowl.setScale(2.0) + self.goldBowl = loader.loadModel('phase_6/models/gui/racingTrophyBowl') + self.goldBowl.reparentTo(self) + self.goldBowl.setPos(0, 0.5, 0) + self.goldBowl.setScale(2.0) + self.goldBowlBase = self.goldBowl.find('**/fishingTrophyBase') + self.goldBowlBase.hide() + self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, -0.15), text='', text_scale=0.125, text_fg=Vec4(0.9, 0.9, 0.4, 1)) + self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.shadow.reparentTo(self) + self.shadow.setColor(1, 1, 1, 0.2) + self.shadow.setPosHprScale(0, 1, 0.35, 0, 90, 0, 0.1, 0.14, 0.1) + self.setLevel(level) + return + + def setLevel(self, level): + self.level = level + if level == -1: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.hide() + self.nameLabel.hide() + else: + self.nameLabel.show() + if level < 30 and level % 10 == 9: + self.trophy.hide() + self.goldBowl.hide() + self.greyBowl.show() + self.greyBowl.setScale(8.25, 3.5, 3.5) + elif level >= 30: + self.trophy.hide() + self.greyBowl.hide() + self.goldBowl.show() + self.goldBowlBase.hide() + else: + self.trophy.show() + self.goldBowl.hide() + self.greyBowl.hide() + if level == 30: + self.goldBowl.setScale(4.4, 3.1, 3.1) + elif level == 31: + self.goldBowl.setScale(3.6, 3.5, 3.5) + elif level >= 32: + self.goldBowl.setScale(5.6, 3.9, 3.9) + if level % 10 == 9: + pass + elif level % 10 % 3 == 0: + self.column.setScale(1.3229, 1.26468, 1.11878) + self.top.setPos(0, 0, -1) + self.__bronze() + elif level % 10 % 3 == 1: + self.column.setScale(1.3229, 1.26468, 1.61878) + self.top.setPos(0, 0, -.5) + self.__silver() + elif level % 10 % 3 == 2: + self.column.setScale(1.3229, 1.26468, 2.11878) + self.top.setPos(0, 0, 0) + self.__gold() + if level < 10: + self.__tealColumn() + elif level < 20: + self.__purpleColumn() + elif level < 30: + self.__blueColumn() + else: + self.__redColumn() + + def __bronze(self): + self.statue.setColorScale(0.9, 0.6, 0.33, 1) + + def __silver(self): + self.statue.setColorScale(0.9, 0.9, 1, 1) + + def __gold(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __platinum(self): + self.statue.setColorScale(1, 0.95, 0.1, 1) + + def __tealColumn(self): + self.column.setColorScale(0.5, 1.2, 0.85, 1) + + def __purpleColumn(self): + self.column.setColorScale(1, 0.7, 0.95, 1) + + def __redColumn(self): + self.column.setColorScale(1.2, 0.6, 0.6, 1) + + def __yellowColumn(self): + self.column.setColorScale(1, 1, 0.8, 1) + + def __blueColumn(self): + self.column.setColorScale(0.6, 0.75, 1.2, 1) + + def destroy(self): + self.trophy.removeNode() + self.goldBowl.removeNode() + self.greyBowl.removeNode() + self.shadow.removeNode() + DirectFrame.destroy(self) diff --git a/toontown/shtiker/MapPage.py b/toontown/shtiker/MapPage.py new file mode 100644 index 0000000..eae2a0c --- /dev/null +++ b/toontown/shtiker/MapPage.py @@ -0,0 +1,252 @@ +import ShtikerPage +from toontown.toonbase import ToontownGlobals +from direct.showbase import PythonUtil +from toontown.hood import ZoneUtil +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer + +class MapPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + ShtikerPage.ShtikerPage.load(self) + mapModel = loader.loadModel('phase_3.5/models/gui/toontown_map') + self.map = DirectFrame(parent=self, relief=None, image=mapModel.find('**/toontown_map'), image_scale=(1.8, 1, 1.35), scale=0.97, pos=(0, 0, 0.0775)) + mapModel.removeNode() + self.allZones = [] + for hood in ToontownGlobals.Hoods: + if hood not in [ToontownGlobals.GolfZone, ToontownGlobals.FunnyFarm]: + self.allZones.append(hood) + + self.cloudScaleList = (((0.55, 0, 0.4), (0.35, 0, 0.25)), + (), + ((0.45, 0, 0.45), (0.5, 0, 0.4)), + ((0.7, 0, 0.45),), + ((0.55, 0, 0.4),), + ((0.6, 0, 0.4), (0.5332, 0, 0.32)), + ((0.7, 0, 0.45), (0.7, 0, 0.45)), + ((0.7998, 0, 0.39),), + ((0.5, 0, 0.4),), + ((-0.45, 0, 0.4),), + ((-0.45, 0, 0.35),), + ((0.5, 0, 0.35),), + ((0.5, 0, 0.35),)) + self.cloudPosList = (((0.575, 0.0, -0.04), (0.45, 0.0, -0.25)), + (), + ((0.375, 0.0, 0.4), (0.5625, 0.0, 0.2)), + ((-0.02, 0.0, 0.23),), + ((-0.3, 0.0, -0.4),), + ((0.25, 0.0, -0.425), (0.125, 0.0, -0.36)), + ((-0.5625, 0.0, -0.07), (-0.45, 0.0, 0.2125)), + ((-0.125, 0.0, 0.5),), + ((0.66, 0.0, -0.4),), + ((-0.68, 0.0, -0.444),), + ((-0.6, 0.0, 0.45),), + ((0.66, 0.0, 0.5),), + ((0.4, 0.0, -0.35),)) + self.labelPosList = ((0.594, 0.0, -0.075), + (0.0, 0.0, -0.1), + (0.475, 0.0, 0.25), + (0.1, 0.0, 0.15), + (-0.3, 0.0, -0.375), + (0.2, 0.0, -0.45), + (-0.55, 0.0, 0.0), + (-0.088, 0.0, 0.47), + (0.7, 0.0, -0.5), + (-0.7, 0.0, -0.5), + (-0.7, 0.0, 0.5), + (0.7, 0.0, 0.5), + (0.45, 0.0, -0.45)) + self.labels = [] + self.clouds = [] + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + buttonLoc = (0.45, 0, - 0.74) + if base.housingEnabled: + buttonLoc = (0.55, 0, -0.74) + self.safeZoneButton = DirectButton( + parent=self.map, + relief=None, + image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), + image_scale=(1.3, 1.1, 1.1), + pos=buttonLoc, + text=TTLocalizer.MapPageBackToPlayground, + text_scale=TTLocalizer.MPsafeZoneButton, + text_pos=(0, -0.02), + textMayChange=0, + command=self.backToSafeZone) + self.goHomeButton = DirectButton( + parent=self.map, + relief=None, + image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), + image_scale=(0.66, 1.1, 1.1), + pos=(0.15, 0, -.74), + text=TTLocalizer.MapPageGoHome, + text_scale=TTLocalizer.MPgoHomeButton, + text_pos=(0, -0.02), + textMayChange=0, + command=self.goHome) + self.goHomeButton.hide() + guiButton.removeNode() + self.hoodLabel = DirectLabel( + parent=self.map, + relief=None, + pos=(-0.43, 0, -0.726), + text='', + text_scale=TTLocalizer.MPhoodLabel, + text_pos=(0, 0), + text_wordwrap=TTLocalizer.MPhoodLabelWordwrap) + self.hoodLabel.hide() + cloudModel = loader.loadModel('phase_3.5/models/gui/cloud') + cloudImage = cloudModel.find('**/cloud') + for hood in self.allZones: + abbrev = base.cr.hoodMgr.getNameFromId(hood) + fullname = base.cr.hoodMgr.getFullnameFromId(hood) + hoodIndex = self.allZones.index(hood) + label = DirectButton( + parent=self.map, + relief=None, + pos=self.labelPosList[hoodIndex], + pad=(0.2, 0.16), + text=('', fullname, fullname), + text_bg=Vec4(1, 1, 1, 0.4), + text_scale=0.055, + text_wordwrap=8, + rolloverSound=None, + clickSound=None, + pressEffect=0, + command=self.__buttonCallback, + extraArgs=[hood], + sortOrder=1) + label.bind(DGG.WITHIN, self.__hoverCallback, extraArgs=[1, hoodIndex]) + label.bind(DGG.WITHOUT, self.__hoverCallback, extraArgs=[0, hoodIndex]) + label.resetFrameSize() + self.labels.append(label) + hoodClouds = [] + for cloudScale, cloudPos in zip(self.cloudScaleList[hoodIndex], self.cloudPosList[hoodIndex]): + cloud = DirectFrame( + parent=self.map, + relief=None, + state=DGG.DISABLED, + image=cloudImage, + scale=(cloudScale[0], cloudScale[1], cloudScale[2]), + pos=(cloudPos[0], cloudPos[1], cloudPos[2])) + cloud.hide() + hoodClouds.append(cloud) + + self.clouds.append(hoodClouds) + + cloudModel.removeNode() + self.resetFrameSize() + return + + def unload(self): + for labelButton in self.labels: + labelButton.destroy() + + del self.labels + del self.clouds + self.safeZoneButton.destroy() + self.goHomeButton.destroy() + ShtikerPage.ShtikerPage.unload(self) + + def enter(self): + ShtikerPage.ShtikerPage.enter(self) + try: + zone = base.cr.playGame.getPlace().getZoneId() + except: + zone = 0 + + if base.localAvatar.lastHood >= ToontownGlobals.BossbotHQ: + self.safeZoneButton['text'] = TTLocalizer.MapPageBackToCogHQ + else: + self.safeZoneButton['text'] = TTLocalizer.MapPageBackToPlayground + if zone and ZoneUtil.isPlayground(zone) or self.book.safeMode: + self.safeZoneButton.hide() + else: + self.safeZoneButton.show() + if base.cr.playGame.getPlaceId() == ToontownGlobals.MyEstate and base.cr.playGame.hood.loader.atMyEstate() or self.book.safeMode: + self.goHomeButton.hide() + elif base.housingEnabled: + self.goHomeButton.show() + if base.cr.playGame.getPlaceId() == ToontownGlobals.MyEstate: + if base.cr.playGame.hood.loader.atMyEstate(): + self.hoodLabel['text'] = TTLocalizer.MapPageYouAreAtHome + self.hoodLabel.show() + else: + avatar = base.cr.identifyAvatar(base.cr.playGame.hood.loader.estateOwnerId) + if avatar: + avName = avatar.getName() + self.hoodLabel['text'] = TTLocalizer.MapPageYouAreAtSomeonesHome % TTLocalizer.GetPossesive(avName) + self.hoodLabel.show() + elif zone: + hoodName = ToontownGlobals.hoodNameMap.get(ZoneUtil.getCanonicalHoodId(zone), ('',))[-1] + streetName = ToontownGlobals.StreetNames.get(ZoneUtil.getCanonicalBranchZone(zone), ('',))[-1] + if hoodName: + self.hoodLabel['text'] = TTLocalizer.MapPageYouAreHere % (hoodName, streetName) + self.hoodLabel.show() + else: + self.hoodLabel.hide() + else: + self.hoodLabel.hide() + safeZonesVisited = base.localAvatar.hoodsVisited + hoodsAvailable = base.cr.hoodMgr.getAvailableZones() + hoodVisibleList = PythonUtil.intersection(safeZonesVisited, hoodsAvailable) + hoodTeleportList = base.localAvatar.getTeleportAccess() + for hood in self.allZones: + label = self.labels[self.allZones.index(hood)] + clouds = self.clouds[self.allZones.index(hood)] + if not self.book.safeMode and hood in hoodVisibleList: + label['text_fg'] = (0, 0, 0, 1) + label.show() + for cloud in clouds: + cloud.hide() + + fullname = base.cr.hoodMgr.getFullnameFromId(hood) + if hood in hoodTeleportList: + text = TTLocalizer.MapPageGoTo % fullname + label['text'] = ('', text, text) + else: + label['text'] = ('', fullname, fullname) + else: + label['text_fg'] = (0, 0, 0, 0.65) + label.show() + for cloud in clouds: + cloud.show() + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + + def backToSafeZone(self): + self.doneStatus = {'mode': 'teleport', + 'hood': base.localAvatar.lastHood} + messenger.send(self.doneEvent) + + def goHome(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: VISITESTATE: Visit estate') + self.doneStatus = {'mode': 'gohome', + 'hood': base.localAvatar.lastHood} + messenger.send(self.doneEvent) + + def __buttonCallback(self, hood): + if hood in base.localAvatar.getTeleportAccess() and hood in base.cr.hoodMgr.getAvailableZones(): + base.localAvatar.sendUpdate('checkTeleportAccess', [hood]) + self.doneStatus = {'mode': 'teleport', + 'hood': hood} + messenger.send(self.doneEvent) + + def __hoverCallback(self, inside, hoodIndex, pos): + alpha = PythonUtil.choice(inside, 0.25, 1.0) + try: + clouds = self.clouds[hoodIndex] + except ValueError: + clouds = [] + + for cloud in clouds: + cloud.setColor((1, + 1, + 1, + alpha)) diff --git a/toontown/shtiker/NPCFriendPage.py b/toontown/shtiker/NPCFriendPage.py new file mode 100644 index 0000000..db3ff09 --- /dev/null +++ b/toontown/shtiker/NPCFriendPage.py @@ -0,0 +1,32 @@ +import ShtikerPage +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toon import NPCFriendPanel +from toontown.toonbase import TTLocalizer + +class NPCFriendPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.NPCFriendPageTitle, text_scale=0.12, textMayChange=0, pos=(0, 0, 0.6)) + self.friendPanel = NPCFriendPanel.NPCFriendPanel(parent=self) + self.friendPanel.setScale(0.1225) + self.friendPanel.setZ(-0.03) + return + + def unload(self): + ShtikerPage.ShtikerPage.unload(self) + del self.title + del self.friendPanel + + def updatePage(self): + self.friendPanel.update(base.localAvatar.NPCFriendsDict, fCallable=0) + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) diff --git a/toontown/shtiker/NewbiePurchaseManager.py b/toontown/shtiker/NewbiePurchaseManager.py new file mode 100644 index 0000000..f4b48f5 --- /dev/null +++ b/toontown/shtiker/NewbiePurchaseManager.py @@ -0,0 +1,55 @@ +import PurchaseManager +from toontown.quest import QuestParser +from toontown.toon import NPCToons + +class NewbiePurchaseManager(PurchaseManager.PurchaseManager): + + def setOwnedNewbieId(self, ownedNewbieId): + self.ownedNewbieId = ownedNewbieId + + def calcHasLocalToon(self): + return base.localAvatar.doId == self.ownedNewbieId + + def announceGenerate(self): + PurchaseManager.PurchaseManager.announceGenerate(self) + if self.hasLocalToon: + self.npc = NPCToons.createLocalNPC(2011) + self.npc.addActive() + + def getDoId(): + return 0 + + self.npc.getDoId = getDoId + + def acquireDelayDelete(name): + return serialNum() + + self.npc.acquireDelayDelete = acquireDelayDelete + + def releaseDelayDelete(token): + pass + + self.npc.releaseDelayDelete = releaseDelayDelete + + def uniqueName(string): + return string + + self.npc.uniqueName = uniqueName + self.accept('gagScreenIsUp', self.playMovie) + self.purchase = base.cr.playGame.hood.purchase + self.purchase.enterTutorialMode(self.ownedNewbieId) + + def disable(self): + PurchaseManager.PurchaseManager.disable(self) + if hasattr(self, 'movie'): + self.npc.removeActive() + self.npc.delete() + del self.npc + del self.movie + + def playMovie(self): + self.movie = QuestParser.NPCMoviePlayer('gag_intro', base.localAvatar, self.npc) + self.movie.setVar('backToPlaygroundButton', self.purchase.backToPlayground) + self.movie.setVar('playAgainButton', self.purchase.playAgain) + self.movie.setVar('purchaseBg', self.purchase.bg) + self.movie.play() diff --git a/toontown/shtiker/NewbiePurchaseManagerAI.py b/toontown/shtiker/NewbiePurchaseManagerAI.py new file mode 100644 index 0000000..6d8cc66 --- /dev/null +++ b/toontown/shtiker/NewbiePurchaseManagerAI.py @@ -0,0 +1,22 @@ +import PurchaseManagerAI + +class NewbiePurchaseManagerAI(PurchaseManagerAI.PurchaseManagerAI): + + def __init__(self, air, newbieId, playerArray, mpArray, previousMinigameId, trolleyZone): + self.ownedNewbieId = newbieId + newbieList = [] + PurchaseManagerAI.PurchaseManagerAI.__init__(self, air, playerArray, mpArray, previousMinigameId, trolleyZone, newbieList) + + def startCountdown(self): + pass + + def getOwnedNewbieId(self): + return self.ownedNewbieId + + def getInvolvedPlayerIds(self): + return [self.ownedNewbieId] + + def handlePlayerLeaving(self, avId): + toon = self.air.doId2do.get(avId) + if toon: + self.air.questManager.toonRodeTrolleyFirstTime(toon) diff --git a/toontown/shtiker/NewsPage.py b/toontown/shtiker/NewsPage.py new file mode 100644 index 0000000..5e5bfff --- /dev/null +++ b/toontown/shtiker/NewsPage.py @@ -0,0 +1,82 @@ +from direct.fsm import StateData +from direct.gui.DirectGui import DirectFrame +from direct.gui.DirectGui import DGG +from direct.gui.DirectGui import DirectLabel +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.shtiker import ShtikerPage +from toontown.toonbase import TTLocalizer +UseDirectNewsFrame = config.GetBool('use-direct-news-frame', True) +HaveNewsFrame = True +if UseDirectNewsFrame: + from toontown.shtiker import DirectNewsFrame +else: + try: + from toontown.shtiker import InGameNewsFrame + except: + HaveNewsFrame = False + +class NewsPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('NewsPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + self.noNewsLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.NewsPageImportError, text_scale=0.12) + if HaveNewsFrame: + if UseDirectNewsFrame: + import datetime + start = datetime.datetime.now() + self.newsFrame = DirectNewsFrame.DirectNewsFrame(parent=self) + ending = datetime.datetime.now() + self.notify.info('time to load news = %s' % str(ending - start)) + else: + self.newsFrame = InGameNewsFrame.InGameNewsFrame(parent=self) + self.newsFrame.activate() + return + + def unload(self): + if HaveNewsFrame: + self.newsFrame.unload() + del self.newsFrame + + def clearPage(self): + pass + + def updatePage(self): + pass + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + if HaveNewsFrame: + if self.book: + self.book.prevArrow.hide() + self.book.disableAllPageTabs() + self.newsFrame.activate() + base.setCellsAvailable(base.leftCells, 0) + base.setCellsAvailable([base.rightCells[1]], 0) + localAvatar.book.bookCloseButton.hide() + localAvatar.setLastTimeReadNews(base.cr.toontownTimeManager.getCurServerDateTime()) + + def exit(self): + self.clearPage() + if self.book: + self.book.prevArrow.show() + self.book.enableAllPageTabs() + ShtikerPage.ShtikerPage.exit(self) + if HaveNewsFrame: + self.newsFrame.deactivate() + base.setCellsAvailable(base.leftCells, 1) + base.setCellsAvailable([base.rightCells[1]], 1) + if localAvatar.book.shouldBookButtonBeHidden(): + localAvatar.book.bookCloseButton.hide() + else: + localAvatar.book.bookCloseButton.show() + + def doSnapshot(self): + if HaveNewsFrame: + return self.newsFrame.doSnapshot() + else: + return 'No News Frame' diff --git a/toontown/shtiker/OptionsPage.py b/toontown/shtiker/OptionsPage.py new file mode 100644 index 0000000..2c67999 --- /dev/null +++ b/toontown/shtiker/OptionsPage.py @@ -0,0 +1,610 @@ +from pandac.PandaModules import * +import ShtikerPage +from toontown.toontowngui import TTDialog +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import DisplaySettingsDialog +from direct.task import Task +from otp.speedchat import SpeedChat +from otp.speedchat import SCColorScheme +from otp.speedchat import SCStaticTextTerminal +from direct.showbase import PythonUtil +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +speedChatStyles = ((2000, + (200 / 255.0, 60 / 255.0, 229 / 255.0), + (200 / 255.0, 135 / 255.0, 255 / 255.0), + (220 / 255.0, 195 / 255.0, 229 / 255.0)), + (2001, + (0 / 255.0, 0 / 255.0, 255 / 255.0), + (140 / 255.0, 150 / 255.0, 235 / 255.0), + (201 / 255.0, 215 / 255.0, 255 / 255.0)), + (2002, + (90 / 255.0, 175 / 255.0, 225 / 255.0), + (120 / 255.0, 215 / 255.0, 255 / 255.0), + (208 / 255.0, 230 / 255.0, 250 / 255.0)), + (2003, + (130 / 255.0, 235 / 255.0, 235 / 255.0), + (120 / 255.0, 225 / 255.0, 225 / 255.0), + (234 / 255.0, 255 / 255.0, 255 / 255.0)), + (2004, + (0 / 255.0, 200 / 255.0, 70 / 255.0), + (0 / 255.0, 200 / 255.0, 80 / 255.0), + (204 / 255.0, 255 / 255.0, 204 / 255.0)), + (2005, + (235 / 255.0, 230 / 255.0, 0 / 255.0), + (255 / 255.0, 250 / 255.0, 100 / 255.0), + (255 / 255.0, 250 / 255.0, 204 / 255.0)), + (2006, + (255 / 255.0, 153 / 255.0, 0 / 255.0), + (229 / 255.0, 147 / 255.0, 0 / 255.0), + (255 / 255.0, 234 / 255.0, 204 / 255.0)), + (2007, + (255 / 255.0, 0 / 255.0, 50 / 255.0), + (229 / 255.0, 0 / 255.0, 50 / 255.0), + (255 / 255.0, 204 / 255.0, 204 / 255.0)), + (2008, + (255 / 255.0, 153 / 255.0, 193 / 255.0), + (240 / 255.0, 157 / 255.0, 192 / 255.0), + (255 / 255.0, 215 / 255.0, 238 / 255.0)), + (2009, + (170 / 255.0, 120 / 255.0, 20 / 255.0), + (165 / 255.0, 120 / 255.0, 50 / 255.0), + (210 / 255.0, 200 / 255.0, 180 / 255.0))) +PageMode = PythonUtil.Enum('Options, Codes') + +class OptionsPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('OptionsPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + ShtikerPage.ShtikerPage.load(self) + self.optionsTabPage = OptionsTabPage(self) + self.optionsTabPage.hide() + self.codesTabPage = CodesTabPage(self) + self.codesTabPage.hide() + titleHeight = 0.61 + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.OptionsPageTitle, text_scale=0.12, pos=(0, 0, titleHeight)) + normalColor = (1, 1, 1, 1) + clickColor = (0.8, 0.8, 0, 1) + rolloverColor = (0.15, 0.82, 1.0, 1) + diabledColor = (1.0, 0.98, 0.15, 1) + gui = loader.loadModel('phase_3.5/models/gui/fishingBook') + self.optionsTab = DirectButton(parent=self, relief=None, text=TTLocalizer.OptionsPageTitle, text_scale=TTLocalizer.OPoptionsTab, text_align=TextNode.ALeft, text_pos=(0.01, 0.0, 0.0), image=gui.find('**/tabs/polySurface1'), image_pos=(0.55, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Options], pos=(-0.36, 0, 0.77)) + self.codesTab = DirectButton(parent=self, relief=None, text=TTLocalizer.OptionsPageCodesTab, text_scale=TTLocalizer.OPoptionsTab, text_align=TextNode.ALeft, text_pos=(-0.035, 0.0, 0.0), image=gui.find('**/tabs/polySurface2'), image_pos=(0.12, 1, -0.91), image_hpr=(0, 0, -90), image_scale=(0.033, 0.033, 0.035), image_color=normalColor, image1_color=clickColor, image2_color=rolloverColor, image3_color=diabledColor, text_fg=Vec4(0.2, 0.1, 0, 1), command=self.setMode, extraArgs=[PageMode.Codes], pos=(0.11, 0, 0.77)) + return + + def enter(self): + self.setMode(PageMode.Options, updateAnyways=1) + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.optionsTabPage.exit() + self.codesTabPage.exit() + ShtikerPage.ShtikerPage.exit(self) + + def unload(self): + self.optionsTabPage.unload() + del self.title + ShtikerPage.ShtikerPage.unload(self) + + def setMode(self, mode, updateAnyways = 0): + messenger.send('wakeup') + if not updateAnyways: + if self.mode == mode: + return + else: + self.mode = mode + if mode == PageMode.Options: + self.mode = PageMode.Options + self.title['text'] = TTLocalizer.OptionsPageTitle + self.optionsTab['state'] = DGG.DISABLED + self.optionsTabPage.enter() + self.codesTab['state'] = DGG.NORMAL + self.codesTabPage.exit() + elif mode == PageMode.Codes: + self.mode = PageMode.Codes + self.title['text'] = TTLocalizer.CdrPageTitle + self.optionsTab['state'] = DGG.NORMAL + self.optionsTabPage.exit() + self.codesTab['state'] = DGG.DISABLED + self.codesTabPage.enter() + else: + raise StandardError, 'OptionsPage::setMode - Invalid Mode %s' % mode + + +class OptionsTabPage(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('OptionsTabPage') + DisplaySettingsTaskName = 'save-display-settings' + DisplaySettingsDelay = 60 + ChangeDisplaySettings = base.config.GetBool('change-display-settings', 1) + ChangeDisplayAPI = base.config.GetBool('change-display-api', 0) + DisplaySettingsApiMap = {'OpenGL': Settings.GL, + 'DirectX7': Settings.DX7, + 'DirectX8': Settings.DX8} + + def __init__(self, parent = aspect2d): + self.parent = parent + self.currentSizeIndex = None + DirectFrame.__init__(self, parent=self.parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + self.load() + return + + def destroy(self): + self.parent = None + DirectFrame.destroy(self) + return + + def load(self): + self.displaySettings = None + self.displaySettingsChanged = 0 + self.displaySettingsSize = (None, None) + self.displaySettingsFullscreen = None + self.displaySettingsEmbedded = None + self.displaySettingsApi = None + self.displaySettingsApiChanged = 0 + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + titleHeight = 0.61 + textStartHeight = 0.45 + textRowHeight = 0.145 + leftMargin = -0.72 + buttonbase_xcoord = 0.35 + buttonbase_ycoord = 0.45 + button_image_scale = (0.7, 1, 1) + button_textpos = (0, -0.02) + options_text_scale = 0.052 + disabled_arrow_color = Vec4(0.6, 0.6, 0.6, 1.0) + self.speed_chat_scale = 0.055 + self.Music_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, pos=(leftMargin, 0, textStartHeight)) + self.SoundFX_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - textRowHeight)) + self.Friends_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - 3 * textRowHeight)) + self.Whispers_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=16, pos=(leftMargin, 0, textStartHeight - 4 * textRowHeight)) + self.DisplaySettings_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=10, pos=(leftMargin, 0, textStartHeight - 5 * textRowHeight)) + self.SpeedChatStyle_Label = DirectLabel(parent=self, relief=None, text=TTLocalizer.OptionsPageSpeedChatStyleLabel, text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=10, pos=(leftMargin, 0, textStartHeight - 6 * textRowHeight)) + self.ToonChatSounds_Label = DirectLabel(parent=self, relief=None, text='', text_align=TextNode.ALeft, text_scale=options_text_scale, text_wordwrap=15, pos=(leftMargin, 0, textStartHeight - 2 * textRowHeight + 0.025)) + self.ToonChatSounds_Label.setScale(0.9) + self.Music_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord), command=self.__doToggleMusic) + self.SoundFX_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight), command=self.__doToggleSfx) + self.Friends_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 3), command=self.__doToggleAcceptFriends) + self.Whispers_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=button_image_scale, text='', text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 4), command=self.__doToggleAcceptWhispers) + self.DisplaySettingsButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image3_color=Vec4(0.5, 0.5, 0.5, 0.5), image_scale=button_image_scale, text=TTLocalizer.OptionsPageChange, text3_fg=(0.5, 0.5, 0.5, 0.75), text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 5), command=self.__doDisplaySettings) + self.speedChatStyleLeftArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), scale=(-1.0, 1.0, 1.0), pos=(0.25, 0, buttonbase_ycoord - textRowHeight * 6), command=self.__doSpeedChatStyleLeft) + self.speedChatStyleRightArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), pos=(0.65, 0, buttonbase_ycoord - textRowHeight * 6), command=self.__doSpeedChatStyleRight) + self.ToonChatSounds_toggleButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), + guiButton.find('**/QuitBtn_DN'), + guiButton.find('**/QuitBtn_RLVR'), + guiButton.find('**/QuitBtn_UP')), image3_color=Vec4(0.5, 0.5, 0.5, 0.5), image_scale=button_image_scale, text='', text3_fg=(0.5, 0.5, 0.5, 0.75), text_scale=options_text_scale, text_pos=button_textpos, pos=(buttonbase_xcoord, 0.0, buttonbase_ycoord - textRowHeight * 2 + 0.025), command=self.__doToggleToonChatSounds) + self.ToonChatSounds_toggleButton.setScale(0.8) + self.speedChatStyleText = SpeedChat.SpeedChat(name='OptionsPageStyleText', structure=[2000], backgroundModelName='phase_3/models/gui/ChatPanel', guiModelName='phase_3.5/models/gui/speedChatGui') + self.speedChatStyleText.setScale(self.speed_chat_scale) + self.speedChatStyleText.setPos(0.37, 0, buttonbase_ycoord - textRowHeight * 6 + 0.03) + self.speedChatStyleText.reparentTo(self, DGG.FOREGROUND_SORT_INDEX) + self.exitButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=1.15, text=TTLocalizer.OptionsPageExitToontown, text_scale=options_text_scale, text_pos=button_textpos, textMayChange=0, pos=(0.45, 0, -0.6), command=self.__handleExitShowWithConfirm) + guiButton.removeNode() + gui.removeNode() + return + + def enter(self): + self.show() + taskMgr.remove(self.DisplaySettingsTaskName) + self.settingsChanged = 0 + self.__setMusicButton() + self.__setSoundFXButton() + self.__setAcceptFriendsButton() + self.__setAcceptWhispersButton() + self.__setDisplaySettings() + self.__setToonChatSoundsButton() + self.speedChatStyleText.enter() + self.speedChatStyleIndex = base.localAvatar.getSpeedChatStyleIndex() + self.updateSpeedChatStyle() + if self.parent.book.safeMode: + self.exitButton.hide() + else: + self.exitButton.show() + + def exit(self): + self.ignore('confirmDone') + self.hide() + if self.settingsChanged != 0: + Settings.writeSettings() + self.speedChatStyleText.exit() + if self.displaySettingsChanged: + taskMgr.doMethodLater(self.DisplaySettingsDelay, self.writeDisplaySettings, self.DisplaySettingsTaskName) + + def unload(self): + self.writeDisplaySettings() + taskMgr.remove(self.DisplaySettingsTaskName) + if self.displaySettings != None: + self.ignore(self.displaySettings.doneEvent) + self.displaySettings.unload() + self.displaySettings = None + self.exitButton.destroy() + self.Music_toggleButton.destroy() + self.SoundFX_toggleButton.destroy() + self.Friends_toggleButton.destroy() + self.Whispers_toggleButton.destroy() + self.DisplaySettingsButton.destroy() + self.speedChatStyleLeftArrow.destroy() + self.speedChatStyleRightArrow.destroy() + del self.exitButton + del self.SoundFX_Label + del self.Music_Label + del self.Friends_Label + del self.Whispers_Label + del self.SpeedChatStyle_Label + del self.SoundFX_toggleButton + del self.Music_toggleButton + del self.Friends_toggleButton + del self.Whispers_toggleButton + del self.speedChatStyleLeftArrow + del self.speedChatStyleRightArrow + self.speedChatStyleText.exit() + self.speedChatStyleText.destroy() + del self.speedChatStyleText + self.currentSizeIndex = None + return + + def __doToggleMusic(self): + messenger.send('wakeup') + if base.musicActive: + base.enableMusic(0) + Settings.setMusic(0) + else: + base.enableMusic(1) + Settings.setMusic(1) + self.settingsChanged = 1 + self.__setMusicButton() + + def __setMusicButton(self): + if base.musicActive: + self.Music_Label['text'] = TTLocalizer.OptionsPageMusicOnLabel + self.Music_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.Music_Label['text'] = TTLocalizer.OptionsPageMusicOffLabel + self.Music_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + + def __doToggleSfx(self): + messenger.send('wakeup') + if base.sfxActive: + base.enableSoundEffects(0) + Settings.setSfx(0) + else: + base.enableSoundEffects(1) + Settings.setSfx(1) + self.settingsChanged = 1 + self.__setSoundFXButton() + + def __doToggleToonChatSounds(self): + messenger.send('wakeup') + if base.toonChatSounds: + base.toonChatSounds = 0 + Settings.setToonChatSounds(0) + else: + base.toonChatSounds = 1 + Settings.setToonChatSounds(1) + self.settingsChanged = 1 + self.__setToonChatSoundsButton() + + def __setSoundFXButton(self): + if base.sfxActive: + self.SoundFX_Label['text'] = TTLocalizer.OptionsPageSFXOnLabel + self.SoundFX_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.SoundFX_Label['text'] = TTLocalizer.OptionsPageSFXOffLabel + self.SoundFX_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + self.__setToonChatSoundsButton() + + def __setToonChatSoundsButton(self): + if base.toonChatSounds: + self.ToonChatSounds_Label['text'] = TTLocalizer.OptionsPageToonChatSoundsOnLabel + self.ToonChatSounds_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.ToonChatSounds_Label['text'] = TTLocalizer.OptionsPageToonChatSoundsOffLabel + self.ToonChatSounds_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + if base.sfxActive: + self.ToonChatSounds_Label.setColorScale(1.0, 1.0, 1.0, 1.0) + self.ToonChatSounds_toggleButton['state'] = DGG.NORMAL + else: + self.ToonChatSounds_Label.setColorScale(0.5, 0.5, 0.5, 0.5) + self.ToonChatSounds_toggleButton['state'] = DGG.DISABLED + + def __doToggleAcceptFriends(self): + messenger.send('wakeup') + if base.localAvatar.acceptingNewFriends: + base.localAvatar.acceptingNewFriends = 0 + Settings.setAcceptingNewFriends(0) + else: + base.localAvatar.acceptingNewFriends = 1 + Settings.setAcceptingNewFriends(1) + self.settingsChanged = 1 + self.__setAcceptFriendsButton() + + def __doToggleAcceptWhispers(self): + messenger.send('wakeup') + if base.localAvatar.acceptingNonFriendWhispers: + base.localAvatar.acceptingNonFriendWhispers = 0 + Settings.setAcceptingNonFriendWhispers(0) + else: + base.localAvatar.acceptingNonFriendWhispers = 1 + Settings.setAcceptingNonFriendWhispers(1) + self.settingsChanged = 1 + self.__setAcceptWhispersButton() + + def __setAcceptFriendsButton(self): + if base.localAvatar.acceptingNewFriends: + self.Friends_Label['text'] = TTLocalizer.OptionsPageFriendsEnabledLabel + self.Friends_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.Friends_Label['text'] = TTLocalizer.OptionsPageFriendsDisabledLabel + self.Friends_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + + def __setAcceptWhispersButton(self): + if base.localAvatar.acceptingNonFriendWhispers: + self.Whispers_Label['text'] = TTLocalizer.OptionsPageWhisperEnabledLabel + self.Whispers_toggleButton['text'] = TTLocalizer.OptionsPageToggleOff + else: + self.Whispers_Label['text'] = TTLocalizer.OptionsPageWhisperDisabledLabel + self.Whispers_toggleButton['text'] = TTLocalizer.OptionsPageToggleOn + + def __doDisplaySettings(self): + if self.displaySettings == None: + self.displaySettings = DisplaySettingsDialog.DisplaySettingsDialog() + self.displaySettings.load() + self.accept(self.displaySettings.doneEvent, self.__doneDisplaySettings) + self.displaySettings.enter(self.ChangeDisplaySettings, self.ChangeDisplayAPI) + return + + def __doneDisplaySettings(self, anyChanged, apiChanged): + if anyChanged: + self.__setDisplaySettings() + properties = base.win.getProperties() + self.displaySettingsChanged = 1 + self.displaySettingsSize = (properties.getXSize(), properties.getYSize()) + self.displaySettingsFullscreen = properties.getFullscreen() + self.displaySettingsEmbedded = self.isPropertiesEmbedded(properties) + self.displaySettingsApi = base.pipe.getInterfaceName() + self.displaySettingsApiChanged = apiChanged + + def isPropertiesEmbedded(self, properties): + result = False + if properties.getParentWindow(): + result = True + return result + + def __setDisplaySettings(self): + properties = base.win.getProperties() + if properties.getFullscreen(): + screensize = '%s x %s' % (properties.getXSize(), properties.getYSize()) + else: + screensize = TTLocalizer.OptionsPageDisplayWindowed + isEmbedded = self.isPropertiesEmbedded(properties) + if isEmbedded: + screensize = TTLocalizer.OptionsPageDisplayEmbedded + api = base.pipe.getInterfaceName() + settings = {'screensize': screensize, + 'api': api} + if self.ChangeDisplayAPI: + OptionsPage.notify.debug('change display settings...') + text = TTLocalizer.OptionsPageDisplaySettings % settings + else: + OptionsPage.notify.debug('no change display settings...') + text = TTLocalizer.OptionsPageDisplaySettingsNoApi % settings + self.DisplaySettings_Label['text'] = text + + def __doSpeedChatStyleLeft(self): + if self.speedChatStyleIndex > 0: + self.speedChatStyleIndex = self.speedChatStyleIndex - 1 + self.updateSpeedChatStyle() + + def __doSpeedChatStyleRight(self): + if self.speedChatStyleIndex < len(speedChatStyles) - 1: + self.speedChatStyleIndex = self.speedChatStyleIndex + 1 + self.updateSpeedChatStyle() + + def updateSpeedChatStyle(self): + nameKey, arrowColor, rolloverColor, frameColor = speedChatStyles[self.speedChatStyleIndex] + newSCColorScheme = SCColorScheme.SCColorScheme(arrowColor=arrowColor, rolloverColor=rolloverColor, frameColor=frameColor) + self.speedChatStyleText.setColorScheme(newSCColorScheme) + self.speedChatStyleText.clearMenu() + colorName = SCStaticTextTerminal.SCStaticTextTerminal(nameKey) + self.speedChatStyleText.append(colorName) + self.speedChatStyleText.finalize() + self.speedChatStyleText.setPos(0.445 - self.speedChatStyleText.getWidth() * self.speed_chat_scale / 2, 0, self.speedChatStyleText.getPos()[2]) + if self.speedChatStyleIndex > 0: + self.speedChatStyleLeftArrow['state'] = DGG.NORMAL + else: + self.speedChatStyleLeftArrow['state'] = DGG.DISABLED + if self.speedChatStyleIndex < len(speedChatStyles) - 1: + self.speedChatStyleRightArrow['state'] = DGG.NORMAL + else: + self.speedChatStyleRightArrow['state'] = DGG.DISABLED + base.localAvatar.b_setSpeedChatStyleIndex(self.speedChatStyleIndex) + + def writeDisplaySettings(self, task = None): + if not self.displaySettingsChanged: + return + taskMgr.remove(self.DisplaySettingsTaskName) + self.notify.info('writing new display settings %s, fullscreen %s, embedded %s, %s to SettingsFile.' % (self.displaySettingsSize, + self.displaySettingsFullscreen, + self.displaySettingsEmbedded, + self.displaySettingsApi)) + Settings.setResolutionDimensions(self.displaySettingsSize[0], self.displaySettingsSize[1]) + Settings.setWindowedMode(not self.displaySettingsFullscreen) + Settings.setEmbeddedMode(self.displaySettingsEmbedded) + if self.displaySettingsApiChanged: + api = self.DisplaySettingsApiMap.get(self.displaySettingsApi) + if api == None: + self.notify.warning('Cannot save unknown display API: %s' % self.displaySettingsApi) + else: + Settings.setDisplayDriver(api) + Settings.writeSettings() + self.displaySettingsChanged = 0 + return Task.done + + def __handleExitShowWithConfirm(self): + self.confirm = TTDialog.TTGlobalDialog(doneEvent='confirmDone', message=TTLocalizer.OptionsPageExitConfirm, style=TTDialog.TwoChoice) + self.confirm.show() + self.parent.doneStatus = {'mode': 'exit', + 'exitTo': 'closeShard'} + self.accept('confirmDone', self.__handleConfirm) + + def __handleConfirm(self): + status = self.confirm.doneStatus + self.ignore('confirmDone') + self.confirm.cleanup() + del self.confirm + if status == 'ok': + base.cr._userLoggingOut = True + messenger.send(self.parent.doneEvent) + + +class CodesTabPage(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('CodesTabPage') + + def __init__(self, parent = aspect2d): + self.parent = parent + DirectFrame.__init__(self, parent=self.parent, relief=None, pos=(0.0, 0.0, 0.0), scale=(1.0, 1.0, 1.0)) + self.load() + return + + def destroy(self): + self.parent = None + DirectFrame.destroy(self) + return + + def load(self): + cdrGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_sbk_codeRedemptionGui') + instructionGui = cdrGui.find('**/tt_t_gui_sbk_cdrPresent') + flippyGui = cdrGui.find('**/tt_t_gui_sbk_cdrFlippy') + codeBoxGui = cdrGui.find('**/tt_t_gui_sbk_cdrCodeBox') + self.resultPanelSuccessGui = cdrGui.find('**/tt_t_gui_sbk_cdrResultPanel_success') + self.resultPanelFailureGui = cdrGui.find('**/tt_t_gui_sbk_cdrResultPanel_failure') + self.resultPanelErrorGui = cdrGui.find('**/tt_t_gui_sbk_cdrResultPanel_error') + self.successSfx = base.loadSfx('phase_3.5/audio/sfx/tt_s_gui_sbk_cdrSuccess.mp3') + self.failureSfx = base.loadSfx('phase_3.5/audio/sfx/tt_s_gui_sbk_cdrFailure.mp3') + self.instructionPanel = DirectFrame(parent=self, relief=None, image=instructionGui, image_scale=0.8, text=TTLocalizer.CdrInstructions, text_pos=TTLocalizer.OPCodesInstructionPanelTextPos, text_align=TextNode.ACenter, text_scale=TTLocalizer.OPCodesResultPanelTextScale, text_wordwrap=TTLocalizer.OPCodesInstructionPanelTextWordWrap, pos=(-0.429, 0, -0.05)) + self.codeBox = DirectFrame(parent=self, relief=None, image=codeBoxGui, pos=(0.433, 0, 0.35)) + self.flippyFrame = DirectFrame(parent=self, relief=None, image=flippyGui, pos=(0.44, 0, -0.353)) + self.codeInput = DirectEntry(parent=self.codeBox, relief=DGG.GROOVE, scale=0.08, pos=(-0.33, 0, -0.006), borderWidth=(0.05, 0.05), frameColor=((1, 1, 1, 1), (1, 1, 1, 1), (0.5, 0.5, 0.5, 0.5)), state=DGG.NORMAL, text_align=TextNode.ALeft, text_scale=TTLocalizer.OPCodesInputTextScale, width=10.5, numLines=1, focus=1, backgroundFocus=0, cursorKeys=1, text_fg=(0, 0, 0, 1), suppressMouse=1, autoCapitalize=0, command=self.__submitCode) + submitButtonGui = loader.loadModel('phase_3/models/gui/quit_button') + self.submitButton = DirectButton(parent=self, relief=None, image=(submitButtonGui.find('**/QuitBtn_UP'), + submitButtonGui.find('**/QuitBtn_DN'), + submitButtonGui.find('**/QuitBtn_RLVR'), + submitButtonGui.find('**/QuitBtn_UP')), image3_color=Vec4(0.5, 0.5, 0.5, 0.5), image_scale=1.15, state=DGG.NORMAL, text=TTLocalizer.NameShopSubmitButton, text_scale=TTLocalizer.OPCodesSubmitTextScale, text_align=TextNode.ACenter, text_pos=TTLocalizer.OPCodesSubmitTextPos, text3_fg=(0.5, 0.5, 0.5, 0.75), textMayChange=0, pos=(0.45, 0.0, 0.0896), command=self.__submitCode) + self.resultPanel = DirectFrame(parent=self, relief=None, image=self.resultPanelSuccessGui, text='', text_pos=TTLocalizer.OPCodesResultPanelTextPos, text_align=TextNode.ACenter, text_scale=TTLocalizer.OPCodesResultPanelTextScale, text_wordwrap=TTLocalizer.OPCodesResultPanelTextWordWrap, pos=(-0.42, 0, -0.0567)) + self.resultPanel.hide() + closeButtonGui = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.closeButton = DirectButton(parent=self.resultPanel, pos=(0.296, 0, -0.466), relief=None, state=DGG.NORMAL, image=(closeButtonGui.find('**/CloseBtn_UP'), closeButtonGui.find('**/CloseBtn_DN'), closeButtonGui.find('**/CloseBtn_Rllvr')), image_scale=(1, 1, 1), command=self.__hideResultPanel) + closeButtonGui.removeNode() + cdrGui.removeNode() + submitButtonGui.removeNode() + return + + def enter(self): + self.show() + localAvatar.chatMgr.fsm.request('otherDialog') + self.codeInput['focus'] = 1 + self.codeInput.enterText('') + self.__enableCodeEntry() + + def exit(self): + self.resultPanel.hide() + self.hide() + localAvatar.chatMgr.fsm.request('mainMenu') + + def unload(self): + self.instructionPanel.destroy() + self.instructionPanel = None + self.codeBox.destroy() + self.codeBox = None + self.flippyFrame.destroy() + self.flippyFrame = None + self.codeInput.destroy() + self.codeInput = None + self.submitButton.destroy() + self.submitButton = None + self.resultPanel.destroy() + self.resultPanel = None + self.closeButton.destroy() + self.closeButton = None + del self.successSfx + del self.failureSfx + return + + def __submitCode(self, input = None): + if input == None: + input = self.codeInput.get() + self.codeInput['focus'] = 1 + if input == '': + return + messenger.send('wakeup') + if hasattr(base, 'codeRedemptionMgr'): + base.codeRedemptionMgr.redeemCode(input, self.__getCodeResult) + self.codeInput.enterText('') + self.__disableCodeEntry() + return + + def __getCodeResult(self, result, awardMgrResult): + self.notify.debug('result = %s' % result) + self.notify.debug('awardMgrResult = %s' % awardMgrResult) + self.__enableCodeEntry() + if result == 0: + self.resultPanel['image'] = self.resultPanelSuccessGui + self.resultPanel['text'] = TTLocalizer.CdrResultSuccess + elif result == 1 or result == 3: + self.resultPanel['image'] = self.resultPanelFailureGui + self.resultPanel['text'] = TTLocalizer.CdrResultInvalidCode + elif result == 2: + self.resultPanel['image'] = self.resultPanelFailureGui + self.resultPanel['text'] = TTLocalizer.CdrResultExpiredCode + elif result == 4: + self.resultPanel['image'] = self.resultPanelErrorGui + if awardMgrResult == 0: + self.resultPanel['text'] = TTLocalizer.CdrResultSuccess + elif awardMgrResult == 1 or awardMgrResult == 2 or awardMgrResult == 15 or awardMgrResult == 16: + self.resultPanel['text'] = TTLocalizer.CdrResultUnknownError + elif awardMgrResult == 3 or awardMgrResult == 4: + self.resultPanel['text'] = TTLocalizer.CdrResultMailboxFull + elif awardMgrResult == 5 or awardMgrResult == 10: + self.resultPanel['text'] = TTLocalizer.CdrResultAlreadyInMailbox + elif awardMgrResult == 6 or awardMgrResult == 7 or awardMgrResult == 11: + self.resultPanel['text'] = TTLocalizer.CdrResultAlreadyInQueue + elif awardMgrResult == 8: + self.resultPanel['text'] = TTLocalizer.CdrResultAlreadyInCloset + elif awardMgrResult == 9: + self.resultPanel['text'] = TTLocalizer.CdrResultAlreadyBeingWorn + elif awardMgrResult == 12 or awardMgrResult == 13 or awardMgrResult == 14: + self.resultPanel['text'] = TTLocalizer.CdrResultAlreadyReceived + elif result == 5: + self.resultPanel['text'] = TTLocalizer.CdrResultTooManyFails + self.__disableCodeEntry() + elif result == 6: + self.resultPanel['text'] = TTLocalizer.CdrResultServiceUnavailable + self.__disableCodeEntry() + if result == 0: + self.successSfx.play() + else: + self.failureSfx.play() + self.resultPanel.show() + + def __hideResultPanel(self): + self.resultPanel.hide() + + def __disableCodeEntry(self): + self.codeInput['state'] = DGG.DISABLED + self.submitButton['state'] = DGG.DISABLED + + def __enableCodeEntry(self): + self.codeInput['state'] = DGG.NORMAL + self.codeInput['focus'] = 1 + self.submitButton['state'] = DGG.NORMAL diff --git a/toontown/shtiker/PhotoAlbumPage.py b/toontown/shtiker/PhotoAlbumPage.py new file mode 100644 index 0000000..6b0923f --- /dev/null +++ b/toontown/shtiker/PhotoAlbumPage.py @@ -0,0 +1,234 @@ +from pandac.PandaModules import * +import ShtikerPage +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import os +from toontown.toonbase import ToontownGlobals + +class PhotoAlbumPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + 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.photos = {} + self.selectedFileName = None + self.photoIndex = 0 + return + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text='Photo Album', text_scale=0.1, pos=(0, 0, 0.6)) + self.pictureImage = loader.loadModel('phase_3.5/models/gui/photo_frame') + self.pictureImage.setScale(0.2) + self.pictureImage.setPos(0.44, 0, 0.25) + self.pictureImage.reparentTo(self) + self.pictureFg = self.pictureImage.find('**/fg') + self.pictureFg.setColor(1, 1, 1, 0.1) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.pictureCaption = DirectLabel(parent=self, relief=None, text='Caption', text_scale=0.05, text_wordwrap=10, text_align=TextNode.ACenter, pos=(0.45, 0, -0.22)) + self.renameButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(1, 1, 1), pos=(0.45, 0, -0.35), text='Caption', text_scale=0.06, text_pos=(0, -0.02), command=self.renameImage, state=DGG.DISABLED) + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.deleteButton = DirectButton(parent=self, image=(trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')), text=('', 'Delete', 'Delete'), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, -0.1), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(0.73, 0, -0.33), scale=0.4, state=DGG.DISABLED, command=self.deleteImage) + guiButton.removeNode() + trashcanGui.removeNode() + gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.scrollList = DirectScrolledList(parent=self, relief=None, forceHeight=0.07, pos=(-0.5, 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.51), 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.51), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(-0.237, 0, 0.41), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(-0.05, + 0.66, + -0.88, + 0.06), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=13, items=[]) + self.renamePanel = DirectFrame(parent=self, relief=None, pos=(0.45, 0, -0.45), image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='Caption Photo', text_scale=0.06, text_pos=(0.0, 0.13), sortOrder=NO_FADE_SORT_INDEX) + self.renameEntry = DirectEntry(parent=self.renamePanel, relief=DGG.SUNKEN, scale=0.06, pos=(-0.3, 0, 0), borderWidth=(0.1, 0.1), numLines=1, cursorKeys=0, frameColor=(0.8, 0.8, 0.5, 1), frameSize=(-0.2, + 10, + -0.4, + 1.1), command=self.renameDialog) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.bCancel = DirectButton(parent=self.renamePanel, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text='Cancel', text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.renameCancel) + self.renamePanel.hide() + self.deletePanel = DirectFrame(parent=self, relief=None, pos=(0, 0, 0), image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='Delete Photo?', text_scale=0.06, text_pos=(0.0, 0.13), sortOrder=NO_FADE_SORT_INDEX) + self.dOk = DirectButton(parent=self.deletePanel, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text='Ok', text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.1, 0.0, -0.1), command=self.deleteConfirm) + self.dCancel = DirectButton(parent=self.deletePanel, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text='Cancel', text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.1, 0.0, -0.1), command=self.deleteCancel) + self.deletePanel.hide() + self.leftArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), scale=(-1.0, 1.0, 1.0), pos=(0.15, 0, -0.21), command=self.prevPhoto) + self.rightArrow = DirectButton(parent=self, relief=None, image=(gui.find('**/Horiz_Arrow_UP'), + gui.find('**/Horiz_Arrow_DN'), + gui.find('**/Horiz_Arrow_Rllvr'), + gui.find('**/Horiz_Arrow_UP')), image3_color=Vec4(1, 1, 1, 0.5), pos=(0.75, 0, -0.21), command=self.nextPhoto) + gui.removeNode() + buttons.removeNode() + return + + def unload(self): + del self.title + del self.scrollList + self.pictureImage.removeNode() + self.pictureFg.removeNode() + del self.pictureCaption + del self.deleteButton + del self.renameButton + del self.renamePanel + del self.renameEntry + del self.bCancel + del self.deletePanel + del self.dOk + del self.dCancel + del self.leftArrow + del self.rightArrow + ShtikerPage.ShtikerPage.unload(self) + + def renameDialog(self, str): + separator = '_' + validChars = string.letters + string.digits + ' -' + str = filter(lambda s: s in validChars, str) + if not str: + self.renameCleanup() + return 0 + oldName = self.selectedFileName + numUnders = oldName.count(separator) + if numUnders == 0: + newName = oldName[0:11] + separator + str + separator + oldName[10:] + elif numUnders == 2: + sp = oldName.split(separator) + newName = sp[0] + separator + str + separator + sp[2] + else: + self.renameCleanup() + return 0 + os.rename(oldName, newName) + self.renameCleanup() + self.updateScrollList() + self.chosePhoto(newName) + return 1 + + def renameCancel(self): + self.renameCleanup() + + def renameCleanup(self): + self.renamePanel.hide() + chatEntry = base.localAvatar.chatMgr.chatInputNormal.chatEntry + chatEntry['backgroundFocus'] = self.oldFocus + + def renameImage(self): + self.deleteCleanup() + self.renameEntry.set(self.getPhotoName(self.selectedFileName)) + self.renamePanel.show() + chatEntry = base.localAvatar.chatMgr.chatInputNormal.chatEntry + chatEntry['backgroundFocus'] = 0 + self.renameEntry['focus'] = 1 + print self.selectedFileName + + def deleteConfirm(self): + os.remove(self.selectedFileName) + self.selectedFileName = None + self.deleteCleanup() + self.updateScrollList() + return + + def deleteCancel(self): + self.deleteCleanup() + + def deleteCleanup(self): + self.deletePanel.hide() + + def deleteImage(self): + self.renameCleanup() + self.deletePanel['text'] = 'Delete Photo?\n%s' % self.getPhotoName(self.selectedFileName) + self.deletePanel.show() + + def makePhotoButton(self, fileName): + return DirectButton(relief=None, text=self.getPhotoName(fileName), text_scale=0.06, text_align=TextNode.ALeft, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, command=self.chosePhoto, extraArgs=[fileName]) + + def getPhotoName(self, fileName): + separator = '_' + numUnders = fileName.count(separator) + if numUnders == 0: + return 'noname' + elif numUnders == 2: + return fileName.split(separator)[1] + else: + return 'unknown' + + def chosePhoto(self, fileName): + if fileName: + self.selectedFileName = fileName + photoTexture = loader.loadTexture(fileName) + photoName = self.getPhotoName(fileName) + self.pictureFg.setTexture(photoTexture, 1) + self.pictureFg.setColor(1, 1, 1, 1) + self.pictureCaption['text'] = photoName + self.renameButton['state'] = DGG.NORMAL + self.deleteButton['state'] = DGG.NORMAL + self.renameEntry.set(photoName) + else: + self.selectedFileName = None + self.pictureFg.clearTexture() + self.pictureFg.setColor(1, 1, 1, 0.1) + self.pictureCaption['text'] = '' + self.renameButton['state'] = DGG.DISABLED + self.deleteButton['state'] = DGG.DISABLED + self.renameEntry.set('') + return + + def getPhotos(self): + files = os.listdir('.') + photos = [] + for fileName in files: + if fileName[0:10] == 'screenshot' and fileName[-4:] == '.jpg': + photos.append(fileName) + + return photos + + def newScreenshot(self, filename): + self.updateScrollList() + + def updateScrollList(self): + newPhotos = self.getPhotos() + for photo in self.photos.keys(): + if photo not in newPhotos: + photoButton = self.photos[photo] + self.scrollList.removeItem(photoButton) + photoButton.destroy() + del self.photos[photo] + + for photo in newPhotos: + if not self.photos.has_key(photo): + photoButton = self.makePhotoButton(photo) + self.scrollList.addItem(photoButton) + self.photos[photo] = photoButton + + if self.photos.keys(): + self.chosePhoto(self.photos.keys()[0]) + else: + self.chosePhoto(None) + return + + def enter(self): + self.accept('screenshot', self.newScreenshot) + self.updateScrollList() + chatEntry = base.localAvatar.chatMgr.chatInputNormal.chatEntry + self.oldFocus = chatEntry['backgroundFocus'] + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.ignore('screenshot') + self.renameCleanup() + self.deleteCleanup() + ShtikerPage.ShtikerPage.exit(self) + + def updateArrows(self): + pass + + def prevPhoto(self): + pass + + def nextPhoto(self): + pass diff --git a/toontown/shtiker/PurchaseManager.py b/toontown/shtiker/PurchaseManager.py new file mode 100644 index 0000000..6707bfd --- /dev/null +++ b/toontown/shtiker/PurchaseManager.py @@ -0,0 +1,108 @@ +from pandac.PandaModules import * +from PurchaseManagerConstants import * +from direct.distributed.ClockDelta import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import TravelGameGlobals + +class PurchaseManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('PurchaseManager') + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + self.playAgain = 0 + + def disable(self): + DistributedObject.DistributedObject.disable(self) + self.ignoreAll() + + def setPlayerIds(self, *playerIds): + self.notify.debug('setPlayerIds: %s' % (playerIds,)) + self.playerIds = playerIds + + def setNewbieIds(self, newbieIds): + self.notify.debug('setNewbieIds: %s' % (newbieIds,)) + self.newbieIds = newbieIds + + def setMinigamePoints(self, *mpArray): + self.notify.debug('setMinigamePoints: %s' % (mpArray,)) + self.mpArray = mpArray + + def setPlayerMoney(self, *moneyArray): + self.notify.debug('setPlayerMoney: %s' % (moneyArray,)) + self.moneyArray = moneyArray + + def setPlayerStates(self, *stateArray): + self.notify.debug('setPlayerStates: %s' % (stateArray,)) + self.playerStates = stateArray + if self.isGenerated() and self.hasLocalToon: + self.announcePlayerStates() + + def setCountdown(self, timestamp): + self.countdownTimestamp = timestamp + + def announcePlayerStates(self): + messenger.send('purchaseStateChange', [self.playerStates]) + + def announceGenerate(self): + DistributedObject.DistributedObject.announceGenerate(self) + self.hasLocalToon = self.calcHasLocalToon() + if self.hasLocalToon: + self.announcePlayerStates() + et = globalClockDelta.localElapsedTime(self.countdownTimestamp) + remain = PURCHASE_COUNTDOWN_TIME - et + self.acceptOnce('purchasePlayAgain', self.playAgainHandler) + self.acceptOnce('purchaseBackToToontown', self.backToToontownHandler) + self.acceptOnce('purchaseTimeout', self.setPurchaseExit) + self.accept('boughtGag', self.__handleBoughtGag) + base.cr.playGame.hood.fsm.request('purchase', [self.mpArray, + self.moneyArray, + self.playerIds, + self.playerStates, + remain, + self.metagameRound, + self.votesArray]) + + def calcHasLocalToon(self): + retval = base.localAvatar.doId not in self.newbieIds and base.localAvatar.doId in self.playerIds + if self.metagameRound > -1 and self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex: + retval = base.localAvatar.doId in self.playerIds + self.notify.debug('calcHasLocalToon returning %s' % retval) + return retval + + def playAgainHandler(self): + self.d_requestPlayAgain() + + def backToToontownHandler(self): + self.notify.debug('requesting exit to toontown...') + self.d_requestExit() + self.playAgain = 0 + self.setPurchaseExit() + + def d_requestExit(self): + self.sendUpdate('requestExit', []) + + def d_requestPlayAgain(self): + self.notify.debug('requesting play again...') + self.sendUpdate('requestPlayAgain', []) + self.playAgain = 1 + + def d_setInventory(self, invString, money, done): + self.sendUpdate('setInventory', [invString, money, done]) + + def __handleBoughtGag(self): + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney(), 0) + + def setPurchaseExit(self): + if self.hasLocalToon: + self.ignore('boughtGag') + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney(), 1) + messenger.send('purchaseOver', [self.playAgain]) + + def setMetagameRound(self, round): + self.notify.debug('setMetagameRound: %s' % (round,)) + self.metagameRound = round + + def setVotesArray(self, votesArray): + self.notify.debug('setVotesArray: %s' % votesArray) + self.votesArray = votesArray diff --git a/toontown/shtiker/PurchaseManagerAI.py b/toontown/shtiker/PurchaseManagerAI.py new file mode 100644 index 0000000..8f932e9 --- /dev/null +++ b/toontown/shtiker/PurchaseManagerAI.py @@ -0,0 +1,367 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from PurchaseManagerConstants import * +import copy +from direct.task.Task import Task +from direct.distributed import DistributedObjectAI +from direct.directnotify import DirectNotifyGlobal +from toontown.minigame import TravelGameGlobals +from toontown.toonbase import ToontownGlobals +from toontown.minigame import MinigameGlobals + +class PurchaseManagerAI(DistributedObjectAI.DistributedObjectAI): + notify = DirectNotifyGlobal.directNotify.newCategory('PurchaseManagerAI') + + def __init__(self, air, playerArray, mpArray, previousMinigameId, trolleyZone, newbieIdList = [], votesArray = None, metagameRound = -1, desiredNextGame = None): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + self.playerIds = copy.deepcopy(playerArray) + self.minigamePoints = copy.deepcopy(mpArray) + self.previousMinigameId = previousMinigameId + self.trolleyZone = trolleyZone + self.newbieIds = copy.deepcopy(newbieIdList) + self.isShutdown = 0 + if votesArray: + self.votesArray = copy.deepcopy(votesArray) + else: + self.votesArray = [] + self.metagameRound = metagameRound + self.desiredNextGame = desiredNextGame + for i in range(len(self.playerIds), 4): + self.playerIds.append(0) + + for i in range(len(self.minigamePoints), 4): + self.minigamePoints.append(0) + + self.playerStates = [None, + None, + None, + None] + self.playersReported = [None, + None, + None, + None] + self.playerMoney = [0, + 0, + 0, + 0] + for i in range(len(self.playerIds)): + avId = self.playerIds[i] + if avId <= 3: + self.playerStates[i] = PURCHASE_NO_CLIENT_STATE + self.playersReported[i] = PURCHASE_CANTREPORT_STATE + elif self.air.doId2do.has_key(avId): + if avId not in self.getInvolvedPlayerIds(): + self.playerStates[i] = PURCHASE_EXIT_STATE + self.playersReported[i] = PURCHASE_REPORTED_STATE + else: + self.playerStates[i] = PURCHASE_WAITING_STATE + self.playersReported[i] = PURCHASE_UNREPORTED_STATE + else: + self.playerStates[i] = PURCHASE_DISCONNECTED_STATE + self.playersReported[i] = PURCHASE_CANTREPORT_STATE + + for avId in self.getInvolvedPlayerIds(): + if avId > 3 and self.air.doId2do.has_key(avId): + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + av = self.air.doId2do[avId] + avIndex = self.findAvIndex(avId) + money = av.getMoney() + if avIndex == None: + self.notify.warning('__init__ avIndex is none but avId=%s' % avId) + continue + self.playerMoney[avIndex] = money + if self.playerMoney[avIndex] < 0: + simbase.air.writeServerEvent('suspicious', avId, 'toon has invalid money %s, forcing to zero' % money) + self.playerMoney[avIndex] = 0 + av.addMoney(self.minigamePoints[avIndex]) + self.air.writeServerEvent('minigame', avId, '%s|%s|%s|%s' % (self.previousMinigameId, + self.trolleyZone, + self.playerIds, + self.minigamePoints[avIndex])) + if self.metagameRound == TravelGameGlobals.FinalMetagameRoundIndex: + numPlayers = len(self.votesArray) + extraBeans = self.votesArray[avIndex] * TravelGameGlobals.PercentOfVotesConverted[numPlayers] / 100.0 + if self.air.holidayManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY) or self.air.holidayManager.isHolidayRunning(ToontownGlobals.JELLYBEAN_TROLLEY_HOLIDAY_MONTH): + extraBeans *= MinigameGlobals.JellybeanTrolleyHolidayScoreMultiplier + av.addMoney(extraBeans) + self.air.writeServerEvent('minigame_extraBeans', avId, '%s|%s|%s|%s' % (self.previousMinigameId, + self.trolleyZone, + self.playerIds, + extraBeans)) + + self.receivingInventory = 1 + self.receivingButtons = 1 + return + + def delete(self): + taskMgr.remove(self.uniqueName('countdown-timer')) + self.ignoreAll() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def getInvolvedPlayerIds(self): + avIds = [] + for avId in self.playerIds: + if avId not in self.newbieIds: + avIds.append(avId) + elif self.metagameRound > -1 and self.metagameRound < TravelGameGlobals.FinalMetagameRoundIndex: + avIds.append(avId) + + return avIds + + def getMinigamePoints(self): + return self.minigamePoints + + def getPlayerIds(self): + return self.playerIds + + def getNewbieIds(self): + return self.newbieIds + + def getPlayerMoney(self): + return self.playerMoney + + def d_setPlayerStates(self, stateArray): + self.sendUpdate('setPlayerStates', stateArray) + return None + + def getPlayerStates(self): + return self.playerStates + + def getCountdown(self): + self.startCountdown() + return globalClockDelta.getRealNetworkTime() + + def startCountdown(self): + if not config.GetBool('disable-purchase-timer', 0): + taskMgr.doMethodLater(PURCHASE_COUNTDOWN_TIME, self.timeIsUpTask, self.uniqueName('countdown-timer')) + + def requestExit(self): + avId = self.air.getAvatarIdFromSender() + avIndex = self.findAvIndex(avId) + if avIndex is None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit: unknown avatar: %s' % (avId,)) + return + if self.receivingButtons: + if self.air.doId2do.has_key(avId): + av = self.air.doId2do[avId] + if avIndex == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit not on list') + self.notify.warning('Avatar ' + str(avId) + ' requested Exit, but is not on the list!') + else: + avState = self.playerStates[avIndex] + if avState == PURCHASE_PLAYAGAIN_STATE or avState == PURCHASE_WAITING_STATE: + self.playerStates[avIndex] = PURCHASE_EXIT_STATE + self.handlePlayerLeaving(avId) + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit invalid transition to exit') + self.notify.warning('Invalid transition to exit state.') + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit unknown avatar') + self.notify.warning('Avatar ' + str(avId) + ' requested Exit, but is not in doId2do.' + ' Assuming disconnected.') + self.playerStates[avIndex] = PURCHASE_DISCONNECTED_STATE + self.playersReported[avIndex] = PURCHASE_CANTREPORT_STATE + self.ignore(self.air.getAvatarExitEvent(avId)) + self.d_setPlayerStates(self.playerStates) + if self.getNumUndecided() == 0: + self.timeIsUp() + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestExit not receiving requests now') + self.notify.warning('Avatar ' + str(avId) + ' requested Exit, but I am not receiving button requests now.') + return + + def requestPlayAgain(self): + avId = self.air.getAvatarIdFromSender() + if self.findAvIndex(avId) == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain: unknown avatar') + return + if self.receivingButtons: + if self.air.doId2do.has_key(avId): + av = self.air.doId2do[avId] + avIndex = self.findAvIndex(avId) + if avIndex == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain not on list') + self.notify.warning('Avatar ' + str(avId) + ' requested PlayAgain, but is not on the list!') + else: + avState = self.playerStates[avIndex] + if avState == PURCHASE_WAITING_STATE: + self.notify.debug(str(avId) + ' wants to play again') + self.playerStates[avIndex] = PURCHASE_PLAYAGAIN_STATE + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain invalid transition to PlayAgain') + self.notify.warning('Invalid transition to PlayAgain state.') + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain unknown avatar') + self.notify.warning('Avatar ' + str(avId) + ' requested PlayAgain, but is not in doId2do.' + ' Assuming disconnected.') + avIndex = self.findAvIndex(avId) + self.playerStates[avIndex] = PURCHASE_DISCONNECTED_STATE + self.playersReported[avIndex] = PURCHASE_CANTREPORT_STATE + self.ignore(self.air.getAvatarExitEvent(avId)) + self.d_setPlayerStates(self.playerStates) + if self.getNumUndecided() == 0: + self.timeIsUp() + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.requestPlayAgain not receiving requests now') + self.notify.warning('Avatar ' + str(avId) + ' requested PlayAgain, but I am not receiving button ' + 'requests now.') + return + + def setInventory(self, blob, newMoney, done): + avId = self.air.getAvatarIdFromSender() + if self.receivingInventory: + if self.air.doId2do.has_key(avId): + av = self.air.doId2do[avId] + avIndex = self.findAvIndex(avId) + if avIndex == None: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory not on list') + self.notify.warning('Avatar ' + str(avId) + ' requested purchase, but is not on the list!') + else: + newInventory = av.inventory.makeFromNetString(blob) + currentMoney = av.getMoney() + if av.inventory.validatePurchase(newInventory, currentMoney, newMoney): + av.setMoney(newMoney) + if not done: + return + if self.playersReported[avIndex] != PURCHASE_UNREPORTED_STATE: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory bad report state') + self.notify.warning('Bad report state: ' + str(self.playersReported[avIndex])) + else: + av.d_setInventory(av.inventory.makeNetString()) + av.d_setMoney(newMoney) + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory invalid purchase') + self.notify.warning('Avatar ' + str(avId) + ' attempted an invalid purchase.') + av.d_setInventory(av.inventory.makeNetString()) + av.d_setMoney(av.getMoney()) + self.playersReported[avIndex] = PURCHASE_REPORTED_STATE + if self.getNumUnreported() == 0: + self.shutDown() + else: + self.air.writeServerEvent('suspicious', avId, 'PurchaseManager.setInventory not receiving inventory') + self.notify.warning('Not receiving inventory. Ignored ' + str(avId) + "'s request") + return + + def d_setPurchaseExit(self): + self.sendUpdate('setPurchaseExit', []) + return None + + def timeIsUpTask(self, task): + self.timeIsUp() + return Task.done + + def timeIsUp(self): + self.d_setPurchaseExit() + taskMgr.remove(self.uniqueName('countdown-timer')) + self.receivingButtons = 0 + self.receivingInventory = 1 + return None + + def getVotesArrayMatchingPlayAgainList(self, playAgainList): + retval = [] + for playAgainIndex in range(len(playAgainList)): + avId = playAgainList[playAgainIndex] + origIndex = self.playerIds.index(avId) + if self.votesArray and origIndex < len(self.votesArray): + retval.append(self.votesArray[origIndex]) + else: + retval.append(0) + + return retval + + def shutDown(self): + if self.isShutdown: + self.notify.warning('Got shutDown twice') + return + self.isShutdown = 1 + from toontown.minigame import MinigameCreatorAI + playAgainNum = self.getNumPlayAgain() + if playAgainNum > 0: + playAgainList = self.getPlayAgainList() + newVotesArray = self.getVotesArrayMatchingPlayAgainList(playAgainList) + newRound = self.metagameRound + newbieIdsToPass = [] + if newRound > -1: + newbieIdsToPass = self.newbieIds + if newRound < TravelGameGlobals.FinalMetagameRoundIndex: + newRound += 1 + else: + newRound = 0 + newVotesArray = [TravelGameGlobals.DefaultStartingVotes] * len(playAgainList) + if len(playAgainList) == 1 and simbase.config.GetBool('metagame-min-2-players', 1): + newRound = -1 + MinigameCreatorAI.createMinigame(self.air, playAgainList, self.trolleyZone, minigameZone=self.zoneId, previousGameId=self.previousMinigameId, newbieIds=newbieIdsToPass, startingVotes=newVotesArray, metagameRound=newRound, desiredNextGame=self.desiredNextGame) + else: + MinigameCreatorAI.releaseMinigameZone(self.zoneId) + self.requestDelete() + self.ignoreAll() + return None + + def findAvIndex(self, avId): + for i in range(len(self.playerIds)): + if avId == self.playerIds[i]: + return i + + return None + + def getNumUndecided(self): + undecidedCounter = 0 + for playerState in self.playerStates: + if playerState == PURCHASE_WAITING_STATE: + undecidedCounter += 1 + + return undecidedCounter + + def getPlayAgainList(self): + playAgainList = [] + for i in range(len(self.playerStates)): + if self.playerStates[i] == PURCHASE_PLAYAGAIN_STATE: + playAgainList.append(self.playerIds[i]) + + return playAgainList + + def getNumPlayAgain(self): + playAgainCounter = 0 + for playerState in self.playerStates: + if playerState == PURCHASE_PLAYAGAIN_STATE: + playAgainCounter += 1 + + return playAgainCounter + + def getNumUnreported(self): + unreportedCounter = 0 + for playerState in self.playersReported: + if playerState == PURCHASE_UNREPORTED_STATE: + unreportedCounter += 1 + elif playerState == PURCHASE_REPORTED_STATE: + pass + elif playerState == PURCHASE_CANTREPORT_STATE: + pass + else: + self.notify.warning('Weird report state: ' + str(playerState)) + + return unreportedCounter + + def __handleUnexpectedExit(self, avId): + self.notify.warning('Avatar: ' + str(avId) + ' has exited unexpectedly') + index = self.findAvIndex(avId) + if index == None: + self.notify.warning('Something is seriously screwed up...' + 'An avatar exited unexpectedly, and they' + ' are not on my list!') + else: + self.playerStates[index] = PURCHASE_DISCONNECTED_STATE + self.playersReported[index] = PURCHASE_CANTREPORT_STATE + self.d_setPlayerStates(self.playerStates) + if self.receivingButtons: + if self.getNumUndecided() == 0: + self.timeIsUp() + if self.receivingInventory: + if self.getNumUnreported() == 0: + self.shutDown() + return + + def handlePlayerLeaving(self, avId): + pass + + def getMetagameRound(self): + return self.metagameRound + + def getVotesArray(self): + return self.votesArray diff --git a/toontown/shtiker/PurchaseManagerConstants.py b/toontown/shtiker/PurchaseManagerConstants.py new file mode 100644 index 0000000..1114f9f --- /dev/null +++ b/toontown/shtiker/PurchaseManagerConstants.py @@ -0,0 +1,9 @@ +PURCHASE_NO_CLIENT_STATE = 0 +PURCHASE_WAITING_STATE = 1 +PURCHASE_PLAYAGAIN_STATE = 2 +PURCHASE_EXIT_STATE = 3 +PURCHASE_DISCONNECTED_STATE = 4 +PURCHASE_UNREPORTED_STATE = 10 +PURCHASE_REPORTED_STATE = 11 +PURCHASE_CANTREPORT_STATE = 12 +PURCHASE_COUNTDOWN_TIME = 120 diff --git a/toontown/shtiker/QuestPage.py b/toontown/shtiker/QuestPage.py new file mode 100644 index 0000000..1df830a --- /dev/null +++ b/toontown/shtiker/QuestPage.py @@ -0,0 +1,175 @@ +from pandac.PandaModules import * +import ShtikerPage +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.quest import Quests +from toontown.toon import NPCToons +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.quest import QuestBookPoster +from direct.directnotify import DirectNotifyGlobal + +class QuestPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('QuestPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.quests = {0: None, + 1: None, + 2: None, + 3: None} + 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.onscreen = 0 + self.lastQuestTime = globalClock.getRealTime() + return + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.QuestPageToonTasks, text_scale=0.12, textMayChange=0, pos=(0, 0, 0.6)) + questFramePlaceList = ((-0.45, + 0, + 0.25, + 0, + 0, + 0), + (-0.45, + 0, + -0.35, + 0, + 0, + 0), + (0.45, 0, 0.25, 0, 0, 0), + (0.45, + 0, + -0.35, + 0, + 0, + 0)) + self.questFrames = [] + for i in range(ToontownGlobals.MaxQuestCarryLimit): + frame = QuestBookPoster.QuestBookPoster(reverse=i > 1, mapIndex=i + 1) + frame.reparentTo(self) + frame.setPosHpr(*questFramePlaceList[i]) + frame.setScale(1.06) + self.questFrames.append(frame) + + self.accept('questsChanged', self.updatePage) + return + + def acceptOnscreenHooks(self): + self.accept(ToontownGlobals.QuestsHotkeyOn, self.showQuestsOnscreen) + self.accept(ToontownGlobals.QuestsHotkeyOff, self.hideQuestsOnscreen) + + def ignoreOnscreenHooks(self): + self.ignore(ToontownGlobals.QuestsHotkeyOn) + self.ignore(ToontownGlobals.QuestsHotkeyOff) + + def unload(self): + self.ignore('questsChanged') + del self.title + del self.quests + del self.questFrames + loader.unloadModel('phase_3.5/models/gui/stickerbook_gui') + ShtikerPage.ShtikerPage.unload(self) + + def clearQuestFrame(self, index): + self.questFrames[index].clear() + self.quests[index] = None + return + + def fillQuestFrame(self, questDesc, index): + self.questFrames[index].update(questDesc) + self.quests[index] = questDesc + + def getLowestUnusedIndex(self): + for i in range(ToontownGlobals.MaxQuestCarryLimit): + if self.quests[i] == None: + return i + + return -1 + + def updatePage(self): + self.notify.debug('updatePage()') + newQuests = base.localAvatar.quests + carryLimit = base.localAvatar.getQuestCarryLimit() + for i in range(ToontownGlobals.MaxQuestCarryLimit): + if i < carryLimit: + self.questFrames[i].show() + else: + self.questFrames[i].hide() + + for index, questDesc in self.quests.items(): + if questDesc is not None and list(questDesc) not in newQuests: + self.clearQuestFrame(index) + + for questDesc in newQuests: + newQuestDesc = tuple(questDesc) + if newQuestDesc not in self.quests.values(): + index = self.getLowestUnusedIndex() + self.fillQuestFrame(newQuestDesc, index) + + for i, questDesc in self.quests.iteritems(): + if questDesc: + if self.canDeleteQuest(questDesc): + self.questFrames[i].setDeleteCallback(self.__deleteQuest) + else: + self.questFrames[i].setDeleteCallback(None) + self.questFrames[i].update(questDesc) + else: + self.questFrames[i].unbindMouseEnter() + + messenger.send('questPageUpdated') + return + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) + + def showQuestsOnscreenTutorial(self): + self.setPos(0, 0, -0.2) + self.showQuestsOnscreen() + + def showQuestsOnscreen(self): + messenger.send('wakeup') + timedif = globalClock.getRealTime() - self.lastQuestTime + if timedif < 0.7: + return + self.lastQuestTime = globalClock.getRealTime() + if self.onscreen or base.localAvatar.invPage.onscreen: + return + self.onscreen = 1 + for i in range(ToontownGlobals.MaxQuestCarryLimit): + if hasattr(self.questFrames[i], 'mapIndex'): + self.questFrames[i].mapIndex.show() + + self.updatePage() + self.reparentTo(aspect2d) + self.title.hide() + self.show() + + def hideQuestsOnscreenTutorial(self): + self.setPos(0, 0, 0) + self.hideQuestsOnscreen() + + def hideQuestsOnscreen(self): + if not self.onscreen: + return + self.onscreen = 0 + for i in range(ToontownGlobals.MaxQuestCarryLimit): + if hasattr(self.questFrames[i], 'mapIndex'): + self.questFrames[i].mapIndex.hide() + + self.reparentTo(self.book) + self.title.show() + self.hide() + + def canDeleteQuest(self, questDesc): + return Quests.isQuestJustForFun(questDesc[0], questDesc[3]) and self.onscreen == 0 + + def __deleteQuest(self, questDesc): + base.localAvatar.d_requestDeleteQuest(questDesc) diff --git a/toontown/shtiker/ShardPage.py b/toontown/shtiker/ShardPage.py new file mode 100644 index 0000000..00ee82f --- /dev/null +++ b/toontown/shtiker/ShardPage.py @@ -0,0 +1,306 @@ +from pandac.PandaModules import * +import ShtikerPage +from direct.task.Task import Task +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.distributed import ToontownDistrictStats +from toontown.toontowngui import TTDialog +POP_COLORS_NTT = (Vec4(0.0, 1.0, 0.0, 1.0), Vec4(1.0, 1.0, 0.0, 1.0), Vec4(1.0, 0.0, 0.0, 1.0)) +POP_COLORS = (Vec4(0.4, 0.4, 1.0, 1.0), Vec4(0.4, 1.0, 0.4, 1.0), Vec4(1.0, 0.4, 0.4, 1.0)) + +class ShardPage(ShtikerPage.ShtikerPage): + notify = DirectNotifyGlobal.directNotify.newCategory('ShardPage') + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.shardButtonMap = {} + self.shardButtons = [] + self.scrollList = None + 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.ShardInfoUpdateInterval = 5.0 + self.lowPop, self.midPop, self.highPop = base.getShardPopLimits() + self.showPop = config.GetBool('show-total-population', 0) + self.noTeleport = config.GetBool('shard-page-disable', 0) + return + + def load(self): + main_text_scale = 0.06 + title_text_scale = 0.12 + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.ShardPageTitle, text_scale=title_text_scale, textMayChange=0, pos=(0, 0, 0.6)) + helpText_ycoord = 0.403 + self.helpText = DirectLabel(parent=self, relief=None, text='', text_scale=main_text_scale, text_wordwrap=12, text_align=TextNode.ALeft, textMayChange=1, pos=(0.058, 0, helpText_ycoord)) + shardPop_ycoord = helpText_ycoord - 0.523 + totalPop_ycoord = shardPop_ycoord - 0.26 + self.totalPopulationText = DirectLabel(parent=self, relief=None, text=TTLocalizer.ShardPagePopulationTotal % 1, text_scale=main_text_scale, text_wordwrap=8, textMayChange=1, text_align=TextNode.ACenter, pos=(0.38, 0, totalPop_ycoord)) + if self.showPop: + self.totalPopulationText.show() + else: + self.totalPopulationText.hide() + self.gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.listXorigin = -0.02 + self.listFrameSizeX = 0.67 + self.listZorigin = -0.96 + self.listFrameSizeZ = 1.04 + self.arrowButtonScale = 1.3 + self.itemFrameXorigin = -0.237 + self.itemFrameZorigin = 0.365 + self.buttonXstart = self.itemFrameXorigin + 0.293 + self.regenerateScrollList() + scrollTitle = DirectFrame(parent=self.scrollList, text=TTLocalizer.ShardPageScrollTitle, text_scale=main_text_scale, text_align=TextNode.ACenter, relief=None, pos=(self.buttonXstart, 0, self.itemFrameZorigin + 0.127)) + return + + def unload(self): + self.gui.removeNode() + del self.title + self.scrollList.destroy() + del self.scrollList + del self.shardButtons + taskMgr.remove('ShardPageUpdateTask-doLater') + ShtikerPage.ShtikerPage.unload(self) + + def regenerateScrollList(self): + selectedIndex = 0 + if self.scrollList: + selectedIndex = self.scrollList.getSelectedIndex() + for button in self.shardButtons: + button.detachNode() + + self.scrollList.destroy() + self.scrollList = None + self.scrollList = DirectScrolledList(parent=self, relief=None, pos=(-0.5, 0, 0), incButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(self.arrowButtonScale, self.arrowButtonScale, -self.arrowButtonScale), incButton_pos=(self.buttonXstart, 0, self.itemFrameZorigin - 0.999), incButton_image3_color=Vec4(1, 1, 1, 0.2), decButton_image=(self.gui.find('**/FndsLst_ScrollUp'), + self.gui.find('**/FndsLst_ScrollDN'), + self.gui.find('**/FndsLst_ScrollUp_Rllvr'), + self.gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(self.arrowButtonScale, self.arrowButtonScale, self.arrowButtonScale), decButton_pos=(self.buttonXstart, 0, self.itemFrameZorigin + 0.227), decButton_image3_color=Vec4(1, 1, 1, 0.2), itemFrame_pos=(self.itemFrameXorigin, 0, self.itemFrameZorigin), itemFrame_scale=1.0, itemFrame_relief=DGG.SUNKEN, itemFrame_frameSize=(self.listXorigin, + self.listXorigin + self.listFrameSizeX, + self.listZorigin, + self.listZorigin + self.listFrameSizeZ), itemFrame_frameColor=(0.85, 0.95, 1, 1), itemFrame_borderWidth=(0.01, 0.01), numItemsVisible=15, forceHeight=0.065, items=self.shardButtons) + self.scrollList.scrollTo(selectedIndex) + return + + def askForShardInfoUpdate(self, task = None): + ToontownDistrictStats.refresh('shardInfoUpdated') + taskMgr.doMethodLater(self.ShardInfoUpdateInterval, self.askForShardInfoUpdate, 'ShardPageUpdateTask-doLater') + return Task.done + + def makeShardButton(self, shardId, shardName, shardPop): + shardButtonParent = DirectFrame() + shardButtonL = DirectButton(parent=shardButtonParent, relief=None, text=shardName, text_scale=0.06, text_align=TextNode.ALeft, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, textMayChange=0, command=self.getPopChoiceHandler(shardPop), extraArgs=[shardId]) + if self.showPop: + popText = str(shardPop) + if shardPop == None: + popText = '' + shardButtonR = DirectButton(parent=shardButtonParent, relief=None, text=popText, text_scale=0.06, text_align=TextNode.ALeft, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, textMayChange=1, pos=(0.5, 0, 0), command=self.choseShard, extraArgs=[shardId]) + else: + model = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + button = model.find('**/minnieCircle') + shardButtonR = DirectButton(parent=shardButtonParent, relief=None, image=button, image_scale=(0.3, 1, 0.3), image2_scale=(0.35, 1, 0.35), image_color=self.getPopColor(shardPop), pos=(0.6, 0, 0.0125), text=self.getPopText(shardPop), text_scale=0.06, text_align=TextNode.ACenter, text_pos=(-0.0125, -0.0125), text_fg=Vec4(0, 0, 0, 0), text1_fg=Vec4(0, 0, 0, 0), text2_fg=Vec4(0, 0, 0, 1), text3_fg=Vec4(0, 0, 0, 0), command=self.getPopChoiceHandler(shardPop), extraArgs=[shardId]) + del model + del button + return (shardButtonParent, shardButtonR, shardButtonL) + + def getPopColor(self, pop): + if base.cr.productName == 'JP': + if pop < self.midPop: + color1 = POP_COLORS_NTT[0] + color2 = POP_COLORS_NTT[1] + popRange = self.midPop - self.lowPop + pop = pop - self.lowPop + else: + color1 = POP_COLORS_NTT[1] + color2 = POP_COLORS_NTT[2] + popRange = self.highPop - self.midPop + pop = pop - self.midPop + popPercent = pop / float(popRange) + if popPercent > 1: + popPercent = 1 + newColor = color2 * popPercent + color1 * (1 - popPercent) + elif pop <= self.lowPop: + newColor = POP_COLORS[0] + elif pop <= self.midPop: + newColor = POP_COLORS[1] + else: + newColor = POP_COLORS[2] + return newColor + + def getPopText(self, pop): + if pop <= self.lowPop: + popText = TTLocalizer.ShardPageLow + elif pop <= self.midPop: + popText = TTLocalizer.ShardPageMed + else: + popText = TTLocalizer.ShardPageHigh + return popText + + def getPopChoiceHandler(self, pop): + if base.cr.productName == 'JP': + handler = self.choseShard + elif pop <= self.midPop: + if self.noTeleport and not self.showPop: + handler = self.shardChoiceReject + else: + handler = self.choseShard + elif self.showPop: + handler = self.choseShard + else: + handler = self.shardChoiceReject + return handler + + def getCurrentZoneId(self): + try: + zoneId = base.cr.playGame.getPlace().getZoneId() + except: + zoneId = None + + return zoneId + + def getCurrentShardId(self): + zoneId = self.getCurrentZoneId() + if zoneId != None and ZoneUtil.isWelcomeValley(zoneId): + return ToontownGlobals.WelcomeValleyToken + else: + return base.localAvatar.defaultShard + return + + def updateScrollList(self): + curShardTuples = base.cr.listActiveShards() + + def compareShardTuples(a, b): + if a[1] < b[1]: + return -1 + elif b[1] < a[1]: + return 1 + else: + return 0 + + curShardTuples.sort(compareShardTuples) + if base.cr.welcomeValleyManager: + curShardTuples.append((ToontownGlobals.WelcomeValleyToken, + TTLocalizer.WelcomeValley[-1], + 0, + 0)) + currentShardId = self.getCurrentShardId() + actualShardId = base.localAvatar.defaultShard + actualShardName = None + anyChanges = 0 + totalPop = 0 + totalWVPop = 0 + currentMap = {} + self.shardButtons = [] + for i in range(len(curShardTuples)): + shardId, name, pop, WVPop = curShardTuples[i] + if shardId == actualShardId: + actualShardName = name + totalPop += pop + totalWVPop += WVPop + currentMap[shardId] = 1 + buttonTuple = self.shardButtonMap.get(shardId) + if buttonTuple == None: + buttonTuple = self.makeShardButton(shardId, name, pop) + self.shardButtonMap[shardId] = buttonTuple + anyChanges = 1 + elif self.showPop: + buttonTuple[1]['text'] = str(pop) + else: + buttonTuple[1]['image_color'] = self.getPopColor(pop) + if not base.cr.productName == 'JP': + buttonTuple[1]['text'] = self.getPopText(pop) + buttonTuple[1]['command'] = self.getPopChoiceHandler(pop) + buttonTuple[2]['command'] = self.getPopChoiceHandler(pop) + self.shardButtons.append(buttonTuple[0]) + if shardId == currentShardId or self.book.safeMode: + buttonTuple[1]['state'] = DGG.DISABLED + buttonTuple[2]['state'] = DGG.DISABLED + else: + buttonTuple[1]['state'] = DGG.NORMAL + buttonTuple[2]['state'] = DGG.NORMAL + + for shardId, buttonTuple in self.shardButtonMap.items(): + if shardId not in currentMap: + buttonTuple[0].destroy() + del self.shardButtonMap[shardId] + anyChanges = 1 + + buttonTuple = self.shardButtonMap.get(ToontownGlobals.WelcomeValleyToken) + if buttonTuple: + if self.showPop: + buttonTuple[1]['text'] = str(totalWVPop) + else: + buttonTuple[1]['image_color'] = self.getPopColor(totalWVPop) + if not base.cr.productName == 'JP': + buttonTuple[1]['text'] = self.getPopText(totalWVPop) + buttonTuple[1]['command'] = self.getPopChoiceHandler(totalWVPop) + buttonTuple[2]['command'] = self.getPopChoiceHandler(totalWVPop) + if anyChanges: + self.regenerateScrollList() + self.totalPopulationText['text'] = TTLocalizer.ShardPagePopulationTotal % totalPop + helpText = TTLocalizer.ShardPageHelpIntro + if actualShardName: + if currentShardId == ToontownGlobals.WelcomeValleyToken: + helpText += TTLocalizer.ShardPageHelpWelcomeValley % actualShardName + else: + helpText += TTLocalizer.ShardPageHelpWhere % actualShardName + if not self.book.safeMode: + helpText += TTLocalizer.ShardPageHelpMove + self.helpText['text'] = helpText + return + + def enter(self): + self.askForShardInfoUpdate() + self.updateScrollList() + currentShardId = self.getCurrentShardId() + buttonTuple = self.shardButtonMap.get(currentShardId) + if buttonTuple: + i = self.shardButtons.index(buttonTuple[0]) + self.scrollList.scrollTo(i, centered=1) + ShtikerPage.ShtikerPage.enter(self) + self.accept('shardInfoUpdated', self.updateScrollList) + + def exit(self): + self.ignore('shardInfoUpdated') + self.ignore('confirmDone') + taskMgr.remove('ShardPageUpdateTask-doLater') + ShtikerPage.ShtikerPage.exit(self) + + def shardChoiceReject(self, shardId): + self.confirm = TTDialog.TTGlobalDialog(doneEvent='confirmDone', message=TTLocalizer.ShardPageChoiceReject, style=TTDialog.Acknowledge) + self.confirm.show() + self.accept('confirmDone', self.__handleConfirm) + + def __handleConfirm(self): + self.ignore('confirmDone') + self.confirm.cleanup() + del self.confirm + + def choseShard(self, shardId): + zoneId = self.getCurrentZoneId() + canonicalHoodId = ZoneUtil.getCanonicalHoodId(base.localAvatar.lastHood) + currentShardId = self.getCurrentShardId() + if shardId == currentShardId: + return + elif shardId == ToontownGlobals.WelcomeValleyToken: + self.doneStatus = {'mode': 'teleport', + 'hood': ToontownGlobals.WelcomeValleyToken} + messenger.send(self.doneEvent) + elif shardId == base.localAvatar.defaultShard: + self.doneStatus = {'mode': 'teleport', + 'hood': canonicalHoodId} + messenger.send(self.doneEvent) + else: + try: + place = base.cr.playGame.getPlace() + except: + try: + place = base.cr.playGame.hood.loader.place + except: + place = base.cr.playGame.hood.place + + place.requestTeleport(canonicalHoodId, canonicalHoodId, shardId, -1) diff --git a/toontown/shtiker/ShtikerBook.py b/toontown/shtiker/ShtikerBook.py new file mode 100644 index 0000000..fc82e71 --- /dev/null +++ b/toontown/shtiker/ShtikerBook.py @@ -0,0 +1,452 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.effects import DistributedFireworkShow +from toontown.parties import DistributedPartyFireworksActivity +from direct.directnotify import DirectNotifyGlobal + +class ShtikerBook(DirectFrame, StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ShtikerBook') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.initialiseoptions(ShtikerBook) + StateData.StateData.__init__(self, doneEvent) + self.pages = [] + self.pageTabs = [] + self.currPageTabIndex = None + self.pageTabFrame = DirectFrame(parent=self, relief=None, pos=(0.93, 1, 0.575), scale=1.25) + self.pageTabFrame.hide() + self.currPageIndex = None + self.pageBeforeNews = None + self.entered = 0 + self.safeMode = 0 + self.__obscured = 0 + self.__shown = 0 + self.__isOpen = 0 + self.hide() + self.setPos(0, 0, 0.1) + self.pageOrder = [TTLocalizer.OptionsPageTitle, + TTLocalizer.ShardPageTitle, + TTLocalizer.MapPageTitle, + TTLocalizer.InventoryPageTitle, + TTLocalizer.QuestPageToonTasks, + TTLocalizer.TrackPageShortTitle, + TTLocalizer.SuitPageTitle, + TTLocalizer.FishPageTitle, + TTLocalizer.KartPageTitle, + TTLocalizer.DisguisePageTitle, + TTLocalizer.NPCFriendPageTitle, + TTLocalizer.GardenPageTitle, + TTLocalizer.GolfPageTitle, + TTLocalizer.EventsPageName, + TTLocalizer.NewsPageName] + return + + def setSafeMode(self, setting): + self.safeMode = setting + + def enter(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SHTICKERBOOK: Open') + if self.entered: + return + self.entered = 1 + messenger.send('releaseDirector') + messenger.send('stickerBookEntered') + base.playSfx(self.openSound) + base.disableMouse() + base.render.hide() + base.setBackgroundColor(0.05, 0.15, 0.4) + base.setCellsAvailable([base.rightCells[0]], 0) + self.oldMin2dAlpha = NametagGlobals.getMin2dAlpha() + self.oldMax2dAlpha = NametagGlobals.getMax2dAlpha() + NametagGlobals.setMin2dAlpha(0.8) + NametagGlobals.setMax2dAlpha(1.0) + self.__isOpen = 1 + self.__setButtonVisibility() + self.show() + self.showPageArrows() + if not self.safeMode: + self.accept('shtiker-page-done', self.__pageDone) + self.accept(ToontownGlobals.StickerBookHotkey, self.__close) + self.accept(ToontownGlobals.OptionsPageHotkey, self.__close) + self.pageTabFrame.show() + self.pages[self.currPageIndex].enter() + if hasattr(localAvatar, 'newsButtonMgr') and localAvatar.newsButtonMgr: + localAvatar.newsButtonMgr.hideNewIssueButton() + + def exit(self): + if not self.entered: + return + self.entered = 0 + messenger.send('stickerBookExited') + base.playSfx(self.closeSound) + self.pages[self.currPageIndex].exit() + base.render.show() + setBlackBackground = 0 + for obj in base.cr.doId2do.values(): + if isinstance(obj, DistributedFireworkShow.DistributedFireworkShow) or isinstance(obj, DistributedPartyFireworksActivity.DistributedPartyFireworksActivity): + setBlackBackground = 1 + + if setBlackBackground: + base.setBackgroundColor(Vec4(0, 0, 0, 1)) + else: + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + gsg = base.win.getGsg() + if gsg: + base.render.prepareScene(gsg) + NametagGlobals.setMin2dAlpha(self.oldMin2dAlpha) + NametagGlobals.setMax2dAlpha(self.oldMax2dAlpha) + base.setCellsAvailable([base.rightCells[0]], 1) + self.__isOpen = 0 + self.hide() + self.hideButton() + cleanupDialog('globalDialog') + self.pageTabFrame.hide() + self.ignore('shtiker-page-done') + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore(ToontownGlobals.OptionsPageHotkey) + self.ignore('arrow_right') + self.ignore('arrow_left') + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SHTICKERBOOK: Close') + + def load(self): + self.checkGardenStarted = localAvatar.getGardenStarted() + bookModel = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + self['image'] = bookModel.find('**/big_book') + self['image_scale'] = (2, 1, 1.5) + self.resetFrameSize() + self.bookOpenButton = DirectButton(image=(bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_RLVR')), relief=None, pos=(1.175, 0, -0.83), scale=0.305, command=self.__open) + self.bookCloseButton = DirectButton(image=(bookModel.find('**/BookIcon_OPEN'), bookModel.find('**/BookIcon_CLSD'), bookModel.find('**/BookIcon_RLVR2')), relief=None, pos=(1.175, 0, -0.83), scale=0.305, command=self.__close) + self.bookOpenButton.hide() + self.bookCloseButton.hide() + self.nextArrow = DirectButton(parent=self, relief=None, image=(bookModel.find('**/arrow_button'), bookModel.find('**/arrow_down'), bookModel.find('**/arrow_rollover')), scale=(0.1, 0.1, 0.1), pos=(0.838, 0, -0.661), command=self.__pageChange, extraArgs=[1]) + self.prevArrow = DirectButton(parent=self, relief=None, image=(bookModel.find('**/arrow_button'), bookModel.find('**/arrow_down'), bookModel.find('**/arrow_rollover')), scale=(-0.1, 0.1, 0.1), pos=(-0.838, 0, -0.661), command=self.__pageChange, extraArgs=[-1]) + bookModel.removeNode() + self.openSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_open.mp3') + self.closeSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_delete.mp3') + self.pageSound = base.loadSfx('phase_3.5/audio/sfx/GUI_stickerbook_turn.mp3') + return + + def unload(self): + loader.unloadModel('phase_3.5/models/gui/stickerbook_gui') + self.destroy() + self.bookOpenButton.destroy() + del self.bookOpenButton + self.bookCloseButton.destroy() + del self.bookCloseButton + self.nextArrow.destroy() + del self.nextArrow + self.prevArrow.destroy() + del self.prevArrow + for page in self.pages: + page.unload() + + del self.pages + for pageTab in self.pageTabs: + pageTab.destroy() + + del self.pageTabs + del self.currPageTabIndex + del self.openSound + del self.closeSound + del self.pageSound + + def addPage(self, page, pageName = 'Page'): + if pageName not in self.pageOrder: + self.notify.error('Trying to add page %s in the ShtickerBook. Page not listed in the order.' % pageName) + return + pageIndex = 0 + if len(self.pages): + newIndex = len(self.pages) + prevIndex = newIndex - 1 + if self.pages[prevIndex].pageName == TTLocalizer.NewsPageName: + self.pages.insert(prevIndex, page) + pageIndex = prevIndex + if self.currPageIndex >= pageIndex: + self.currPageIndex += 1 + else: + self.pages.append(page) + pageIndex = len(self.pages) - 1 + else: + self.pages.append(page) + pageIndex = len(self.pages) - 1 + page.setBook(self) + page.setPageName(pageName) + page.reparentTo(self) + self.addPageTab(page, pageIndex, pageName) + from toontown.shtiker import MapPage + if isinstance(page, MapPage.MapPage): + self.pageBeforeNews = page + + def addPageTab(self, page, pageIndex, pageName = 'Page'): + tabIndex = len(self.pageTabs) + + def goToPage(): + messenger.send('wakeup') + base.playSfx(self.pageSound) + self.setPage(page) + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SHTICKERBOOK: Browse tabs %s' % page.pageName) + localAvatar.newsButtonMgr.setGoingToNewsPageFromStickerBook(False) + localAvatar.newsButtonMgr.showAppropriateButton() + + yOffset = 0.07 * pageIndex + iconGeom = None + iconImage = None + iconScale = 1 + iconColor = Vec4(1) + buttonPressedCommand = goToPage + extraArgs = [] + if pageName == TTLocalizer.OptionsPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/switch') + iconModels.detachNode() + elif pageName == TTLocalizer.ShardPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/district') + iconModels.detachNode() + elif pageName == TTLocalizer.MapPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/teleportIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.InventoryPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/inventory_icons') + iconGeom = iconModels.find('**/inventory_tart') + iconScale = 7 + iconModels.detachNode() + elif pageName == TTLocalizer.QuestPageToonTasks: + iconModels = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + iconGeom = iconModels.find('**/questCard') + iconScale = 0.9 + iconModels.detachNode() + elif pageName == TTLocalizer.TrackPageShortTitle: + iconGeom = iconModels = loader.loadModel('phase_3.5/models/gui/filmstrip') + iconScale = 1.1 + iconColor = Vec4(0.7, 0.7, 0.7, 1) + iconModels.detachNode() + elif pageName == TTLocalizer.SuitPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/gui_gear') + iconModels.detachNode() + elif pageName == TTLocalizer.FishPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/fish') + iconModels.detachNode() + elif pageName == TTLocalizer.GardenPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/gardenIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.DisguisePageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/disguise2') + iconColor = Vec4(0.7, 0.7, 0.7, 1) + iconModels.detachNode() + elif pageName == TTLocalizer.NPCFriendPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/playingCard') + iconImage = iconModels.find('**/card_back') + iconGeom = iconModels.find('**/logo') + iconScale = 0.22 + iconModels.detachNode() + elif pageName == TTLocalizer.KartPageTitle: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/kartIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.GolfPageTitle: + iconModels = loader.loadModel('phase_6/models/golf/golf_gui') + iconGeom = iconModels.find('**/score_card_icon') + iconModels.detachNode() + elif pageName == TTLocalizer.EventsPageName: + iconModels = loader.loadModel('phase_4/models/parties/partyStickerbook') + iconGeom = iconModels.find('**/Stickerbook_PartyIcon') + iconModels.detachNode() + elif pageName == TTLocalizer.NewsPageName: + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/tt_t_gui_sbk_newsPageTab') + iconModels.detachNode() + buttonPressedCommand = self.goToNewsPage + extraArgs = [page] + if pageName == TTLocalizer.OptionsPageTitle: + pageName = TTLocalizer.OptionsTabTitle + pageTab = DirectButton(parent=self.pageTabFrame, relief=DGG.RAISED, frameSize=(-0.575, + 0.575, + -0.575, + 0.575), borderWidth=(0.05, 0.05), text=('', + '', + pageName, + ''), text_align=TextNode.ALeft, text_pos=(1, -0.2), text_scale=TTLocalizer.SBpageTab, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=iconImage, image_scale=iconScale, geom=iconGeom, geom_scale=iconScale, geom_color=iconColor, pos=(0, 0, -yOffset), scale=0.06, command=buttonPressedCommand, extraArgs=extraArgs) + self.pageTabs.insert(pageIndex, pageTab) + return + + def setPage(self, page, enterPage = True): + if self.currPageIndex is not None: + self.pages[self.currPageIndex].exit() + self.currPageIndex = self.pages.index(page) + self.setPageTabIndex(self.currPageIndex) + if enterPage: + self.showPageArrows() + page.enter() + from toontown.shtiker import NewsPage + if not isinstance(page, NewsPage.NewsPage): + self.pageBeforeNews = page + return + + def setPageBeforeNews(self, enterPage = True): + self.setPage(self.pageBeforeNews, enterPage) + self.accept(ToontownGlobals.StickerBookHotkey, self.__close) + self.accept(ToontownGlobals.OptionsPageHotkey, self.__close) + + def setPageTabIndex(self, pageTabIndex): + if self.currPageTabIndex is not None and pageTabIndex != self.currPageTabIndex: + self.pageTabs[self.currPageTabIndex]['relief'] = DGG.RAISED + self.currPageTabIndex = pageTabIndex + self.pageTabs[self.currPageTabIndex]['relief'] = DGG.SUNKEN + return + + def isOnPage(self, page): + result = False + if self.currPageIndex is not None: + curPage = self.pages[self.currPageIndex] + if curPage == page: + result = True + return result + + def obscureButton(self, obscured): + self.__obscured = obscured + self.__setButtonVisibility() + + def isObscured(self): + return self.__obscured + + def showButton(self): + self.__shown = 1 + self.__setButtonVisibility() + localAvatar.newsButtonMgr.showAppropriateButton() + + def hideButton(self): + self.__shown = 0 + self.__setButtonVisibility() + localAvatar.newsButtonMgr.request('Hidden') + + def __setButtonVisibility(self): + if self.__isOpen: + self.bookOpenButton.hide() + self.bookCloseButton.show() + elif self.__shown and not self.__obscured: + self.bookOpenButton.show() + self.bookCloseButton.hide() + else: + self.bookOpenButton.hide() + self.bookCloseButton.hide() + + def shouldBookButtonBeHidden(self): + result = False + if self.__isOpen: + pass + elif self.__shown and not self.__obscured: + pass + else: + result = True + return result + + def __open(self): + messenger.send('enterStickerBook') + if not localAvatar.getGardenStarted(): + for tab in self.pageTabs: + if tab['text'][2] == TTLocalizer.GardenPageTitle: + tab.hide() + + def __close(self): + base.playSfx(self.closeSound) + self.doneStatus = {'mode': 'close'} + messenger.send('exitStickerBook') + messenger.send(self.doneEvent) + + def closeBook(self): + self.__close() + + def __pageDone(self): + page = self.pages[self.currPageIndex] + pageDoneStatus = page.getDoneStatus() + if pageDoneStatus: + if pageDoneStatus['mode'] == 'close': + self.__close() + else: + self.doneStatus = pageDoneStatus + messenger.send(self.doneEvent) + + def __pageChange(self, offset): + messenger.send('wakeup') + base.playSfx(self.pageSound) + self.pages[self.currPageIndex].exit() + self.currPageIndex = self.currPageIndex + offset + messenger.send('stickerBookPageChange-' + str(self.currPageIndex)) + self.currPageIndex = max(self.currPageIndex, 0) + self.currPageIndex = min(self.currPageIndex, len(self.pages) - 1) + self.setPageTabIndex(self.currPageIndex) + self.showPageArrows() + page = self.pages[self.currPageIndex] + from toontown.shtiker import NewsPage + if isinstance(page, NewsPage.NewsPage): + self.goToNewsPage(page) + else: + page.enter() + self.pageBeforeNews = page + + def showPageArrows(self): + if self.currPageIndex == len(self.pages) - 1: + self.prevArrow.show() + self.nextArrow.hide() + else: + self.prevArrow.show() + self.nextArrow.show() + self.__checkForNewsPage() + if self.currPageIndex == 0: + self.prevArrow.hide() + self.nextArrow.show() + + def __checkForNewsPage(self): + from toontown.shtiker import NewsPage + self.ignore('arrow_left') + self.ignore('arrow_right') + if isinstance(self.pages[self.currPageIndex], NewsPage.NewsPage): + self.ignore('arrow_left') + self.ignore('arrow_right') + else: + self.accept('arrow_right', self.__pageChange, [1]) + self.accept('arrow_left', self.__pageChange, [-1]) + + def goToNewsPage(self, page): + messenger.send('wakeup') + base.playSfx(self.pageSound) + localAvatar.newsButtonMgr.setGoingToNewsPageFromStickerBook(True) + localAvatar.newsButtonMgr.showAppropriateButton() + self.setPage(page) + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: SHTICKERBOOK: Browse tabs %s' % page.pageName) + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore(ToontownGlobals.OptionsPageHotkey) + localAvatar.newsButtonMgr.acceptEscapeKeyPress() + + def disableBookCloseButton(self): + if self.bookCloseButton: + self.bookCloseButton['command'] = None + return + + def enableBookCloseButton(self): + if self.bookCloseButton: + self.bookCloseButton['command'] = self.__close + + def disableAllPageTabs(self): + for button in self.pageTabs: + button['state'] = DGG.DISABLED + + def enableAllPageTabs(self): + for button in self.pageTabs: + button['state'] = DGG.NORMAL diff --git a/toontown/shtiker/ShtikerPage.py b/toontown/shtiker/ShtikerPage.py new file mode 100644 index 0000000..6a48845 --- /dev/null +++ b/toontown/shtiker/ShtikerPage.py @@ -0,0 +1,45 @@ +import ShtikerBook +from direct.fsm import StateData +from direct.gui.DirectGui import * +from pandac.PandaModules import * + +class ShtikerPage(DirectFrame, StateData.StateData): + + def __init__(self): + DirectFrame.__init__(self, relief=None, sortOrder=DGG.BACKGROUND_SORT_INDEX) + self.initialiseoptions(ShtikerPage) + StateData.StateData.__init__(self, 'shtiker-page-done') + self.book = None + self.hide() + return + + def load(self): + pass + + def unload(self): + self.ignoreAll() + del self.book + + def enter(self): + self.show() + + def exit(self): + self.hide() + + def setBook(self, book): + self.book = book + + def setPageName(self, pageName): + self.pageName = pageName + + def makePageWhite(self, item): + white = Vec4(1, 1, 1, 1) + self.book['image_color'] = white + self.book.nextArrow['image_color'] = white + self.book.prevArrow['image_color'] = white + + def makePageRed(self, item): + red = Vec4(1, 0.5, 0.5, 1) + self.book['image_color'] = red + self.book.nextArrow['image_color'] = red + self.book.prevArrow['image_color'] = red diff --git a/toontown/shtiker/SuitPage.py b/toontown/shtiker/SuitPage.py new file mode 100644 index 0000000..9a0583d --- /dev/null +++ b/toontown/shtiker/SuitPage.py @@ -0,0 +1,589 @@ +import ShtikerPage +from direct.task.Task import Task +import SummonCogDialog +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.battle import SuitBattleGlobals +from CogPageGlobals import * +SCALE_FACTOR = 1.5 +RADAR_DELAY = 0.2 +BUILDING_RADAR_POS = (0.375, + 0.065, + -0.225, + -0.5) +PANEL_COLORS = (Vec4(0.8, 0.78, 0.77, 1), + Vec4(0.75, 0.78, 0.8, 1), + Vec4(0.75, 0.82, 0.79, 1), + Vec4(0.825, 0.76, 0.77, 1)) +PANEL_COLORS_COMPLETE1 = (Vec4(0.7, 0.725, 0.545, 1), + Vec4(0.625, 0.725, 0.65, 1), + Vec4(0.6, 0.75, 0.525, 1), + Vec4(0.675, 0.675, 0.55, 1)) +PANEL_COLORS_COMPLETE2 = (Vec4(0.9, 0.725, 0.32, 1), + Vec4(0.825, 0.725, 0.45, 1), + Vec4(0.8, 0.75, 0.325, 1), + Vec4(0.875, 0.675, 0.35, 1)) +SHADOW_SCALE_POS = ((1.225, + 0, + 10, + -0.03), + (0.9, + 0, + 10, + 0), + (1.125, + 0, + 10, + -0.015), + (1.0, + 0, + 10, + -0.02), + (1.0, + -0.02, + 10, + -0.01), + (1.05, + 0, + 10, + -0.0425), + (1.0, + 0, + 10, + -0.05), + (0.9, + -0.0225, + 10, + -0.025), + (1.25, + 0, + 10, + -0.03), + (1.0, + 0, + 10, + -0.01), + (1.0, + 0.005, + 10, + -0.01), + (1.0, + 0, + 10, + -0.01), + (0.9, + 0.005, + 10, + -0.01), + (0.95, + 0, + 10, + -0.01), + (1.125, + 0.005, + 10, + -0.035), + (0.85, + -0.005, + 10, + -0.035), + (1.2, + 0, + 10, + -0.01), + (1.05, + 0, + 10, + 0), + (1.1, + 0, + 10, + -0.04), + (1.0, + 0, + 10, + 0), + (0.95, + 0.0175, + 10, + -0.015), + (1.0, + 0, + 10, + -0.06), + (0.95, + 0.02, + 10, + -0.0175), + (0.9, + 0, + 10, + -0.03), + (1.15, + 0, + 10, + -0.01), + (1.0, + 0, + 10, + 0), + (1.0, + 0, + 10, + 0), + (1.1, + 0, + 10, + -0.04), + (0.93, + 0.005, + 10, + -0.01), + (0.95, + 0.005, + 10, + -0.01), + (1.0, + 0, + 10, + -0.02), + (0.9, + 0.0025, + 10, + -0.03)) + +class SuitPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + + def load(self): + ShtikerPage.ShtikerPage.load(self) + frameModel = loader.loadModel('phase_3.5/models/gui/suitpage_frame') + frameModel.setScale(0.03375, 1, 0.045) + frameModel.setPos(0, 10, -0.575) + self.guiTop = NodePath('guiTop') + self.guiTop.reparentTo(self) + self.frameNode = NodePath('frameNode') + self.frameNode.reparentTo(self.guiTop) + self.panelNode = NodePath('panelNode') + self.panelNode.reparentTo(self.guiTop) + self.iconNode = NodePath('iconNode') + self.iconNode.reparentTo(self.guiTop) + self.enlargedPanelNode = NodePath('enlargedPanelNode') + self.enlargedPanelNode.reparentTo(self.guiTop) + frame = frameModel.find('**/frame') + frame.wrtReparentTo(self.frameNode) + screws = frameModel.find('**/screws') + screws.wrtReparentTo(self.iconNode) + icons = frameModel.find('**/icons') + del frameModel + self.title = DirectLabel(parent=self.iconNode, relief=None, text=TTLocalizer.SuitPageTitle, text_scale=0.1, text_pos=(0.04, 0), textMayChange=0) + self.radarButtons = [] + icon = icons.find('**/corp_icon') + self.corpRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=icon, image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[0]) + self.radarButtons.append(self.corpRadarButton) + icon = icons.find('**/legal_icon') + self.legalRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=icon, image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[1]) + self.radarButtons.append(self.legalRadarButton) + icon = icons.find('**/money_icon') + self.moneyRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=(icon, icon, icon), image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[2]) + self.radarButtons.append(self.moneyRadarButton) + icon = icons.find('**/sales_icon') + self.salesRadarButton = DirectButton(parent=self.iconNode, relief=None, state=DGG.DISABLED, image=(icon, icon, icon), image_scale=(0.03375, 1, 0.045), image2_color=Vec4(1.0, 1.0, 1.0, 0.75), pos=(-0.2, 10, -0.575), command=self.toggleRadar, extraArgs=[3]) + self.radarButtons.append(self.salesRadarButton) + for radarButton in self.radarButtons: + radarButton.building = 0 + radarButton.buildingRadarLabel = None + + gui = loader.loadModel('phase_3.5/models/gui/suitpage_gui') + self.panelModel = gui.find('**/card') + self.shadowModels = [] + for index in range(1, len(SuitDNA.suitHeadTypes) + 1): + self.shadowModels.append(gui.find('**/shadow' + str(index))) + + del gui + self.makePanels() + self.radarOn = [0, + 0, + 0, + 0] + priceScale = 0.1 + 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=(-0.25, 0, -0.69), 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) + self.goldLabel = DirectLabel(parent=self, relief=None, pos=(0.25, 0, -0.69), 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) + if not base.cr.wantEmblems: + self.silverLabel.hide() + self.goldLabel.hide() + self.accept(localAvatar.uniqueName('emblemsChange'), self.__emblemChange) + self.guiTop.setZ(0.625) + return + + def unload(self): + self.ignoreAll() + self.title.destroy() + self.corpRadarButton.destroy() + self.legalRadarButton.destroy() + self.moneyRadarButton.destroy() + self.salesRadarButton.destroy() + for panel in self.panels: + panel.destroy() + + del self.panels + for shadow in self.shadowModels: + shadow.removeNode() + + self.panelModel.removeNode() + ShtikerPage.ShtikerPage.unload(self) + + def enter(self): + self.updatePage() + self.bigPanel = None + self.nextPanel = None + ShtikerPage.ShtikerPage.enter(self) + return + + def exit(self): + taskMgr.remove('buildingListResponseTimeout-later') + taskMgr.remove('suitListResponseTimeout-later') + taskMgr.remove('showCogRadarLater') + taskMgr.remove('showBuildingRadarLater') + for index in range(0, len(self.radarOn)): + if self.radarOn[index]: + self.toggleRadar(index) + self.radarButtons[index]['state'] = DGG.NORMAL + + ShtikerPage.ShtikerPage.exit(self) + + def __emblemChange(self, newEmblems): + self.silverLabel['text'] = str(newEmblems[0]) + self.goldLabel['text'] = str(newEmblems[1]) + + def grow(self, panel, pos): + if self.bigPanel: + print 'setting next panel - ' + str(panel) + self.nextPanel = panel + self.nextPanelPos = pos + return + print 'big panel - ' + str(panel) + self.bigPanel = panel + panel.reparentTo(self.enlargedPanelNode) + panel.setScale(panel.getScale() * SCALE_FACTOR) + if panel.summonButton: + panel.summonButton.show() + panel.summonButton['state'] = DGG.NORMAL + + def shrink(self, panel, pos): + print 'trying to shrink - ' + str(panel) + if panel != self.bigPanel: + self.nextPanel = None + return + print 'shrink panel - ' + str(panel) + self.bigPanel = None + panel.setScale(panel.scale) + panel.reparentTo(self.panelNode) + if panel.summonButton: + panel.summonButton.hide() + panel.summonButton['state'] = DGG.DISABLED + if self.nextPanel: + self.grow(self.nextPanel, self.nextPanelPos) + return + + def toggleRadar(self, deptNum): + messenger.send('wakeup') + if self.radarOn[deptNum]: + self.radarOn[deptNum] = 0 + else: + self.radarOn[deptNum] = 1 + deptSize = SuitDNA.suitsPerDept + panels = self.panels[deptSize * deptNum:SuitDNA.suitsPerDept * (deptNum + 1)] + if self.radarOn[deptNum]: + if hasattr(base.cr, 'currSuitPlanner'): + if base.cr.currSuitPlanner != None: + base.cr.currSuitPlanner.d_suitListQuery() + self.acceptOnce('suitListResponse', self.updateCogRadar, extraArgs=[deptNum, panels]) + taskMgr.doMethodLater(1.0, self.suitListResponseTimeout, 'suitListResponseTimeout-later', extraArgs=(deptNum, panels)) + if self.radarButtons[deptNum].building: + base.cr.currSuitPlanner.d_buildingListQuery() + self.acceptOnce('buildingListResponse', self.updateBuildingRadar, extraArgs=[deptNum]) + taskMgr.doMethodLater(1.0, self.buildingListResponseTimeout, 'buildingListResponseTimeout-later', extraArgs=(deptNum,)) + else: + self.updateCogRadar(deptNum, panels) + self.updateBuildingRadar(deptNum) + else: + self.updateCogRadar(deptNum, panels) + self.updateBuildingRadar(deptNum) + self.radarButtons[deptNum]['state'] = DGG.DISABLED + else: + self.updateCogRadar(deptNum, panels) + self.updateBuildingRadar(deptNum) + return + + def suitListResponseTimeout(self, deptNum, panels): + self.updateCogRadar(deptNum, panels, 1) + + def buildingListResponseTimeout(self, deptNum): + self.updateBuildingRadar(deptNum, 1) + + def makePanels(self): + self.panels = [] + base.panels = [] + xStart = -0.66 + yStart = -0.18 + xOffset = 0.199 + yOffset = 0.284 + for dept in range(0, len(SuitDNA.suitDepts)): + row = [] + color = PANEL_COLORS[dept] + for type in range(0, SuitDNA.suitsPerDept): + panel = DirectLabel(parent=self.panelNode, pos=(xStart + type * xOffset, 0.0, yStart - dept * yOffset), relief=None, state=DGG.NORMAL, image=self.panelModel, image_scale=(1, 1, 1), image_color=color, text=TTLocalizer.SuitPageMystery, text_scale=0.045, text_fg=(0, 0, 0, 1), text_pos=(0, 0.185, 0), text_font=ToontownGlobals.getSuitFont(), text_wordwrap=7) + panel.scale = 0.6 + panel.setScale(panel.scale) + panel.quotaLabel = None + panel.head = None + panel.shadow = None + panel.count = 0 + panel.summonButton = None + self.addCogRadarLabel(panel) + self.panels.append(panel) + base.panels.append(panel) + + return + + def addQuotaLabel(self, panel): + index = self.panels.index(panel) + count = str(base.localAvatar.cogCounts[index]) + if base.localAvatar.cogs[index] < COG_COMPLETE1: + quota = str(COG_QUOTAS[0][index % SuitDNA.suitsPerDept]) + else: + quota = str(COG_QUOTAS[1][index % SuitDNA.suitsPerDept]) + quotaLabel = DirectLabel(parent=panel, pos=(0.0, 0.0, -0.215), relief=None, state=DGG.DISABLED, text=TTLocalizer.SuitPageQuota % (count, quota), text_scale=0.045, text_fg=(0, 0, 0, 1), text_font=ToontownGlobals.getSuitFont()) + panel.quotaLabel = quotaLabel + return + + def addSuitHead(self, panel, suitName): + panelIndex = self.panels.index(panel) + shadow = panel.attachNewNode('shadow') + shadowModel = self.shadowModels[panelIndex] + shadowModel.copyTo(shadow) + coords = SHADOW_SCALE_POS[panelIndex] + shadow.setScale(coords[0]) + shadow.setPos(coords[1], coords[2], coords[3]) + panel.shadow = shadow + panel.head = Suit.attachSuitHead(panel, suitName) + + def addCogRadarLabel(self, panel): + cogRadarLabel = DirectLabel(parent=panel, pos=(0.0, 0.0, -0.215), relief=None, state=DGG.DISABLED, text='', text_scale=0.05, text_fg=(0, 0, 0, 1), text_font=ToontownGlobals.getSuitFont()) + panel.cogRadarLabel = cogRadarLabel + return + + def addSummonButton(self, panel): + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + okButtonList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + gui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + iconGeom = gui.find('**/summons') + summonButton = DirectButton(parent=panel, pos=(0.1, 0.0, -0.13), scale=0.1, relief=None, state=DGG.NORMAL, image=okButtonList, image_scale=13.0, geom=iconGeom, geom_scale=0.7, text=('', + TTLocalizer.IssueSummons, + TTLocalizer.IssueSummons, + ''), text_scale=0.4, text_pos=(-1.1, -0.4), command=self.summonButtonPressed, extraArgs=[panel]) + panel.summonButton = summonButton + return + + def summonButtonPressed(self, panel): + panelIndex = self.panels.index(panel) + self.summonDialog = SummonCogDialog.SummonCogDialog(panelIndex) + self.summonDialog.load() + self.accept(self.summonDialog.doneEvent, self.summonDone, extraArgs=[panel]) + self.summonDialog.enter() + + def summonDone(self, panel): + if self.summonDialog: + self.summonDialog.unload() + self.summonDialog = None + index = self.panels.index(panel) + if not base.localAvatar.hasCogSummons(index): + panel.summonButton.hide() + return + + def addBuildingRadarLabel(self, button): + gui = loader.loadModel('phase_3.5/models/gui/suit_detail_panel') + zPos = BUILDING_RADAR_POS[self.radarButtons.index(button)] + buildingRadarLabel = DirectLabel(parent=button, relief=None, pos=(0.225, 0.0, zPos), state=DGG.DISABLED, image=gui.find('**/avatar_panel'), image_hpr=(0, 0, 90), image_scale=(0.05, 1, 0.1), image_pos=(0, 0, 0.015), text=TTLocalizer.SuitPageBuildingRadarP % '0', text_scale=0.05, text_fg=(1, 0, 0, 1), text_font=ToontownGlobals.getSuitFont()) + gui.removeNode() + button.buildingRadarLabel = buildingRadarLabel + return + + def resetPanel(self, dept, type): + panel = self.panels[dept * SuitDNA.suitsPerDept + type] + panel['text'] = TTLocalizer.SuitPageMystery + if panel.cogRadarLabel: + panel.cogRadarLabel.hide() + if panel.quotaLabel: + panel.quotaLabel.hide() + if panel.head: + panel.head.hide() + if panel.shadow: + panel.shadow.hide() + if panel.summonButton: + panel.summonButton.hide() + color = PANEL_COLORS[dept] + panel['image_color'] = color + for button in self.radarButtons: + if button.buildingRadarLabel: + button.buildingRadarLabel.hide() + + def setPanelStatus(self, panel, status): + index = self.panels.index(panel) + if status == COG_UNSEEN: + panel['text'] = TTLocalizer.SuitPageMystery + elif status == COG_BATTLED: + suitName = SuitDNA.suitHeadTypes[index] + suitFullName = SuitBattleGlobals.SuitAttributes[suitName]['name'] + panel['text'] = suitFullName + if panel.quotaLabel: + panel.quotaLabel.show() + else: + self.addQuotaLabel(panel) + if panel.head and panel.shadow: + panel.head.show() + panel.shadow.show() + else: + self.addSuitHead(panel, suitName) + if base.localAvatar.hasCogSummons(index): + if panel.summonButton: + panel.summonButton.show() + else: + self.addSummonButton(panel) + elif status == COG_DEFEATED: + count = str(base.localAvatar.cogCounts[index]) + if base.localAvatar.cogs[index] < COG_COMPLETE1: + quota = str(COG_QUOTAS[0][index % SuitDNA.suitsPerDept]) + else: + quota = str(COG_QUOTAS[1][index % SuitDNA.suitsPerDept]) + panel.quotaLabel['text'] = TTLocalizer.SuitPageQuota % (count, quota) + elif status == COG_COMPLETE1: + panel['image_color'] = PANEL_COLORS_COMPLETE1[index / SuitDNA.suitsPerDept] + elif status == COG_COMPLETE2: + panel['image_color'] = PANEL_COLORS_COMPLETE2[index / SuitDNA.suitsPerDept] + + def updateAllCogs(self, status): + for index in range(0, len(base.localAvatar.cogs)): + base.localAvatar.cogs[index] = status + + self.updatePage() + + def updatePage(self): + index = 0 + cogs = base.localAvatar.cogs + for dept in range(0, len(SuitDNA.suitDepts)): + for type in range(0, SuitDNA.suitsPerDept): + self.updateCogStatus(dept, type, cogs[index]) + index += 1 + + self.updateCogRadarButtons(base.localAvatar.cogRadar) + self.updateBuildingRadarButtons(base.localAvatar.buildingRadar) + + def updateCogStatus(self, dept, type, status): + if dept < 0 or dept > len(SuitDNA.suitDepts): + print 'ucs: bad cog dept: ', dept + elif type < 0 or type > SuitDNA.suitsPerDept: + print 'ucs: bad cog type: ', type + elif status < COG_UNSEEN or status > COG_COMPLETE2: + print 'ucs: bad status: ', status + else: + self.resetPanel(dept, type) + panel = self.panels[dept * SuitDNA.suitsPerDept + type] + if status == COG_UNSEEN: + self.setPanelStatus(panel, COG_UNSEEN) + elif status == COG_BATTLED: + self.setPanelStatus(panel, COG_BATTLED) + elif status == COG_DEFEATED: + self.setPanelStatus(panel, COG_BATTLED) + self.setPanelStatus(panel, COG_DEFEATED) + elif status == COG_COMPLETE1: + self.setPanelStatus(panel, COG_BATTLED) + self.setPanelStatus(panel, COG_DEFEATED) + self.setPanelStatus(panel, COG_COMPLETE1) + elif status == COG_COMPLETE2: + self.setPanelStatus(panel, COG_BATTLED) + self.setPanelStatus(panel, COG_DEFEATED) + self.setPanelStatus(panel, COG_COMPLETE2) + + def updateCogRadarButtons(self, radars): + for index in range(0, len(radars)): + if radars[index] == 1: + self.radarButtons[index]['state'] = DGG.NORMAL + + def updateCogRadar(self, deptNum, panels, timeout = 0): + taskMgr.remove('suitListResponseTimeout-later') + if not timeout and hasattr(base.cr, 'currSuitPlanner') and base.cr.currSuitPlanner != None: + cogList = base.cr.currSuitPlanner.suitList + else: + cogList = [] + for panel in panels: + panel.count = 0 + + for cog in cogList: + self.panels[cog].count += 1 + + for panel in panels: + panel.cogRadarLabel['text'] = TTLocalizer.SuitPageCogRadar % panel.count + if self.radarOn[deptNum]: + panel.quotaLabel.hide() + + def showLabel(label): + label.show() + + taskMgr.doMethodLater(RADAR_DELAY * panels.index(panel), showLabel, 'showCogRadarLater', extraArgs=(panel.cogRadarLabel,)) + + def activateButton(s = self, index = deptNum): + self.radarButtons[index]['state'] = DGG.NORMAL + return Task.done + + if not self.radarButtons[deptNum].building: + taskMgr.doMethodLater(RADAR_DELAY * len(panels), activateButton, 'activateButtonLater') + else: + panel.cogRadarLabel.hide() + panel.quotaLabel.show() + + return + + def updateBuildingRadarButtons(self, radars): + for index in range(0, len(radars)): + if radars[index] == 1: + self.radarButtons[index].building = 1 + + def updateBuildingRadar(self, deptNum, timeout = 0): + taskMgr.remove('buildingListResponseTimeout-later') + if not timeout and hasattr(base.cr, 'currSuitPlanner') and base.cr.currSuitPlanner != None: + buildingList = base.cr.currSuitPlanner.buildingList + else: + buildingList = [0, + 0, + 0, + 0] + button = self.radarButtons[deptNum] + if button.building: + if not button.buildingRadarLabel: + self.addBuildingRadarLabel(button) + if self.radarOn[deptNum]: + num = buildingList[deptNum] + if num == 1: + button.buildingRadarLabel['text'] = TTLocalizer.SuitPageBuildingRadarS % num + else: + button.buildingRadarLabel['text'] = TTLocalizer.SuitPageBuildingRadarP % num + + def showLabel(button): + button.buildingRadarLabel.show() + button['state'] = DGG.NORMAL + + taskMgr.doMethodLater(RADAR_DELAY * SuitDNA.suitsPerDept, showLabel, 'showBuildingRadarLater', extraArgs=(button,)) + else: + button.buildingRadarLabel.hide() + return diff --git a/toontown/shtiker/SummonCogDialog.py b/toontown/shtiker/SummonCogDialog.py new file mode 100644 index 0000000..a944b6e --- /dev/null +++ b/toontown/shtiker/SummonCogDialog.py @@ -0,0 +1,176 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.suit import SuitDNA +from toontown.suit import Suit +from toontown.battle import SuitBattleGlobals +from toontown.toon import NPCToons +TTL = TTLocalizer + +class SummonCogDialog(DirectFrame, StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('SummonCogDialog') + notify.setInfo(True) + + def __init__(self, suitIndex): + DirectFrame.__init__(self, parent=aspect2dp, pos=(0, 0, 0.3), relief=None, image=DGG.getDefaultDialogGeom(), image_scale=(1.6, 1, 0.7), image_pos=(0, 0, 0.18), image_color=ToontownGlobals.GlobalDialogColor, text=TTL.SummonDlgTitle, text_scale=0.12, text_pos=(0, 0.4), borderWidth=(0.01, 0.01), sortOrder=NO_FADE_SORT_INDEX) + StateData.StateData.__init__(self, 'summon-cog-done') + self.initialiseoptions(SummonCogDialog) + self.suitIndex = suitIndex + base.summonDialog = self + self.popup = None + self.suitName = SuitDNA.suitHeadTypes[self.suitIndex] + self.suitFullName = SuitBattleGlobals.SuitAttributes[self.suitName]['name'] + return + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + DirectFrame.destroy(self) + return None + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + gui = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.head = Suit.attachSuitHead(self, self.suitName) + z = self.head.getZ() + self.head.setPos(-0.4, -0.1, z + 0.2) + self.suitLabel = DirectLabel(parent=self, relief=None, text=self.suitFullName, text_font=ToontownGlobals.getSuitFont(), pos=(-0.4, 0, 0), scale=0.07) + closeButtonImage = (gui.find('**/CloseBtn_UP'), gui.find('**/CloseBtn_DN'), gui.find('**/CloseBtn_Rllvr')) + buttonImage = (guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')) + disabledColor = Vec4(0.5, 0.5, 0.5, 1) + self.summonSingleButton = DirectButton(parent=self, relief=None, text=TTL.SummonDlgButton1, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.06, text_pos=(0, -0.01), pos=(0.3, 0, 0.25), command=self.issueSummons, extraArgs=['single']) + self.summonBuildingButton = DirectButton(parent=self, relief=None, text=TTL.SummonDlgButton2, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.06, text_pos=(0, -0.01), pos=(0.3, 0, 0.125), command=self.issueSummons, extraArgs=['building']) + self.summonInvasionButton = DirectButton(parent=self, relief=None, text=TTL.SummonDlgButton3, image=buttonImage, image_scale=(1.7, 1, 1), image3_color=disabledColor, text_scale=0.06, text_pos=(0, -0.01), pos=(0.3, 0, 0.0), command=self.issueSummons, extraArgs=['invasion']) + self.statusLabel = DirectLabel(parent=self, relief=None, text='', text_wordwrap=12, pos=(0.3, 0, 0.25), scale=0.07) + self.cancel = DirectButton(parent=self, relief=None, image=closeButtonImage, pos=(0.7, 0, -0.1), command=self.__cancel) + gui.removeNode() + guiButton.removeNode() + self.hide() + return + + def enter(self): + if self.isEntered == 1: + return None + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + self.disableButtons() + self.enableButtons() + self.popup = None + base.transitions.fadeScreen(0.5) + self.show() + return + + def exit(self): + if self.isEntered == 0: + return None + self.isEntered = 0 + self.cleanupDialogs() + base.transitions.noTransitions() + self.ignoreAll() + self.hide() + messenger.send(self.doneEvent, []) + return None + + def cleanupDialogs(self): + self.head = None + if self.popup != None: + self.popup.cleanup() + self.popup = None + return + + def cogSummonsDone(self, returnCode, suitIndex, buildingId): + self.cancel['state'] = DGG.NORMAL + if self.summonsType == 'single': + if returnCode == 'success': + self.statusLabel['text'] = TTL.SummonDlgSingleSuccess + elif returnCode == 'badlocation': + self.statusLabel['text'] = TTL.SummonDlgSingleBadLoc + elif returnCode == 'fail': + self.statusLabel['text'] = TTL.SummonDlgInvasionFail + elif self.summonsType == 'building': + if returnCode == 'success': + building = base.cr.doId2do.get(buildingId) + dnaStore = base.cr.playGame.dnaStore + buildingTitle = dnaStore.getTitleFromBlockNumber(building.block) + buildingInteriorZone = building.zoneId + 500 + building.block + npcName = TTLocalizer.SummonDlgShopkeeper + npcId = NPCToons.zone2NpcDict.get(buildingInteriorZone) + if npcId: + npcName = NPCToons.getNPCName(npcId[0]) + if buildingTitle: + self.statusLabel['text'] = TTL.SummonDlgBldgSuccess % (npcName, buildingTitle) + else: + self.statusLabel['text'] = TTL.SummonDlgBldgSuccess2 + elif returnCode == 'badlocation': + self.statusLabel['text'] = TTL.SummonDlgBldgBadLoc + elif returnCode == 'fail': + self.statusLabel['text'] = TTL.SummonDlgInvasionFail + elif self.summonsType == 'invasion': + if returnCode == 'success': + self.statusLabel['text'] = TTL.SummonDlgInvasionSuccess + elif returnCode == 'busy': + self.statusLabel['text'] = TTL.SummonDlgInvasionBusy % self.suitFullName + elif returnCode == 'fail': + self.statusLabel['text'] = TTL.SummonDlgInvasionFail + + def hideSummonButtons(self): + self.summonSingleButton.hide() + self.summonBuildingButton.hide() + self.summonInvasionButton.hide() + + def issueSummons(self, summonsType): + if summonsType == 'single': + text = TTL.SummonDlgSingleConf + elif summonsType == 'building': + text = TTL.SummonDlgBuildingConf + elif summonsType == 'invasion': + text = TTL.SummonDlgInvasionConf + text = text % self.suitFullName + + def handleResponse(resp): + self.popup.cleanup() + self.popup = None + self.reparentTo(self.getParent(), NO_FADE_SORT_INDEX) + base.transitions.fadeScreen(0.5) + if resp == DGG.DIALOG_OK: + self.notify.info('issuing %s summons for %s' % (summonsType, self.suitIndex)) + self.accept('cog-summons-response', self.cogSummonsDone) + self.summonsType = summonsType + self.doIssueSummonsText() + base.localAvatar.d_reqCogSummons(self.summonsType, self.suitIndex) + self.hideSummonButtons() + self.cancel['state'] = DGG.DISABLED + return + + self.reparentTo(self.getParent(), 0) + self.popup = TTDialog.TTDialog(parent=aspect2dp, style=TTDialog.YesNo, text=text, fadeScreen=1, command=handleResponse) + + def doIssueSummonsText(self): + self.disableButtons() + self.statusLabel['text'] = TTL.SummonDlgDelivering + + def disableButtons(self): + self.summonSingleButton['state'] = DGG.DISABLED + self.summonBuildingButton['state'] = DGG.DISABLED + self.summonInvasionButton['state'] = DGG.DISABLED + + def enableButtons(self): + if base.localAvatar.hasCogSummons(self.suitIndex, 'single'): + self.summonSingleButton['state'] = DGG.NORMAL + if base.localAvatar.hasCogSummons(self.suitIndex, 'building'): + self.summonBuildingButton['state'] = DGG.NORMAL + if base.localAvatar.hasCogSummons(self.suitIndex, 'invasion'): + self.summonInvasionButton['state'] = DGG.NORMAL + + def __cancel(self): + self.exit() diff --git a/toontown/shtiker/TIPPage.py b/toontown/shtiker/TIPPage.py new file mode 100644 index 0000000..630b8f1 --- /dev/null +++ b/toontown/shtiker/TIPPage.py @@ -0,0 +1,35 @@ +from pandac.PandaModules import * +import ShtikerPage +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toon import NPCToons +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer + +class TIPPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + 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) + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.TIPPageTitle, text_scale=0.12, textMayChange=0, pos=(0, 0, 0.6)) + return + + def unload(self): + del self.title + loader.unloadModel('phase_3.5/models/gui/stickerbook_gui') + ShtikerPage.ShtikerPage.unload(self) + + def updatePage(self): + pass + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + ShtikerPage.ShtikerPage.exit(self) diff --git a/toontown/shtiker/TrackPage.py b/toontown/shtiker/TrackPage.py new file mode 100644 index 0000000..57693e0 --- /dev/null +++ b/toontown/shtiker/TrackPage.py @@ -0,0 +1,180 @@ +from pandac.PandaModules import * +import ShtikerPage +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.quest import Quests +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import TTLocalizer +from toontown.toon import Toon +MAX_FRAMES = 18 +Track2Anim = {ToontownBattleGlobals.HEAL_TRACK: 'juggle', + ToontownBattleGlobals.TRAP_TRACK: 'toss', + ToontownBattleGlobals.LURE_TRACK: 'hypnotize', + ToontownBattleGlobals.SOUND_TRACK: 'sound', + ToontownBattleGlobals.THROW_TRACK: 'throw', + ToontownBattleGlobals.SQUIRT_TRACK: 'firehose', + ToontownBattleGlobals.DROP_TRACK: 'pushbutton'} + +class TrackFrame(DirectFrame): + + def __init__(self, index): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(TrackFrame) + filmstrip = loader.loadModel('phase_3.5/models/gui/filmstrip') + self.index = index + self.frame = DirectFrame(parent=self, relief=None, image=filmstrip, image_scale=1, text=str(self.index - 1), text_pos=(0.26, -0.22), text_fg=(1, 1, 1, 1), text_scale=0.1) + self.question = DirectLabel(parent=self.frame, relief=None, pos=(0, 0, -0.15), text='?', text_scale=0.4, text_pos=(0, 0.04), text_fg=(0.72, 0.72, 0.72, 1)) + self.toon = None + filmstrip.removeNode() + return + + def makeToon(self): + if not self.toon: + self.toon = Toon.Toon() + self.toon.setDNA(base.localAvatar.getStyle()) + self.toon.getGeomNode().setDepthWrite(1) + self.toon.getGeomNode().setDepthTest(1) + self.toon.useLOD(500) + self.toon.reparentTo(self.frame) + self.toon.setPosHprScale(0, 10, -0.25, 210, 0, 0, 0.12, 0.12, 0.12) + self.ignore('nametagAmbientLightChanged') + + def play(self, trackId): + if not base.launcher or base.launcher and base.launcher.getPhaseComplete(5): + anim = Track2Anim[trackId] + else: + anim = 'neutral' + if self.toon: + numFrames = self.toon.getNumFrames(anim) - 1 + fromFrame = 0 + toFrame = (self.toon.getNumFrames(anim) - 1) / MAX_FRAMES * self.index + self.toon.play(anim, None, fromFrame, toFrame - 1) + return + + def setTrained(self, trackId): + if self.toon == None: + self.makeToon() + if not base.launcher or base.launcher and base.launcher.getPhaseComplete(5): + anim = Track2Anim[trackId] + frame = (self.toon.getNumFrames(anim) - 1) / MAX_FRAMES * self.index + else: + anim = 'neutral' + frame = 0 + self.toon.pose(anim, frame) + self.toon.show() + self.question.hide() + trackColorR, trackColorG, trackColorB = ToontownBattleGlobals.TrackColors[trackId] + self.frame['image_color'] = Vec4(trackColorR, trackColorG, trackColorB, 1) + self.frame['text_fg'] = Vec4(trackColorR * 0.3, trackColorG * 0.3, trackColorB * 0.3, 1) + return + + def setUntrained(self, trackId): + if self.toon: + self.toon.delete() + self.toon = None + self.question.show() + if trackId == -1: + self.frame['image_color'] = Vec4(0.7, 0.7, 0.7, 1) + self.frame['text_fg'] = Vec4(0.5, 0.5, 0.5, 1) + self.question['text_fg'] = Vec4(0.6, 0.6, 0.6, 1) + else: + trackColorR, trackColorG, trackColorB = ToontownBattleGlobals.TrackColors[trackId] + self.frame['image_color'] = Vec4(trackColorR * 0.7, trackColorG * 0.7, trackColorB * 0.7, 1) + self.frame['text_fg'] = Vec4(trackColorR * 0.3, trackColorG * 0.3, trackColorB * 0.3, 1) + self.question['text_fg'] = Vec4(trackColorR * 0.6, trackColorG * 0.6, trackColorB * 0.6, 1) + return + + +class TrackPage(ShtikerPage.ShtikerPage): + + def __init__(self): + ShtikerPage.ShtikerPage.__init__(self) + self.trackFrames = [] + + def placeFrames(self): + rowY = 0.38 + rowSpace = -0.32 + rowPos = [] + for i in range(3): + rowPos.append(rowY) + rowY += rowSpace + + colX = -0.7 + colSpace = 0.276 + colPos = [] + for i in range(6): + colPos.append(colX) + colX += colSpace + + for index in range(1, MAX_FRAMES + 1): + frame = self.trackFrames[index - 1] + col = (index - 1) % 6 + row = (index - 1) / 6 + frame.setPos(colPos[col], 0, rowPos[row]) + frame.setScale(0.39) + + def load(self): + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.TrackPageTitle, text_scale=0.1, pos=(0, 0, 0.65)) + self.subtitle = DirectLabel(parent=self, relief=None, text=TTLocalizer.TrackPageSubtitle, text_scale=0.05, text_fg=(0.5, 0.1, 0.1, 1), pos=(0, 0, 0.56)) + self.trackText = DirectLabel(parent=self, relief=None, text='', text_scale=0.05, text_fg=(0.5, 0.1, 0.1, 1), pos=(0, 0, -0.5)) + for index in range(1, MAX_FRAMES + 1): + frame = TrackFrame(index) + frame.reparentTo(self) + self.trackFrames.append(frame) + + self.placeFrames() + self.startFrame = self.trackFrames[0] + self.endFrame = self.trackFrames[-1] + self.startFrame.frame['text'] = '' + self.startFrame.frame['text_scale'] = TTLocalizer.TPstartFrame + self.startFrame.frame['image_color'] = Vec4(0.2, 0.2, 0.2, 1) + self.startFrame.frame['text_fg'] = (1, 1, 1, 1) + self.startFrame.frame['text_pos'] = (0, 0.08) + self.startFrame.question.hide() + self.endFrame.frame['text'] = TTLocalizer.TrackPageDone + self.endFrame.frame['text_scale'] = TTLocalizer.TPendFrame + self.endFrame.frame['image_color'] = Vec4(0.2, 0.2, 0.2, 1) + self.endFrame.frame['text_fg'] = (1, 1, 1, 1) + self.endFrame.frame['text_pos'] = (0, 0) + self.endFrame.question.hide() + return + + def unload(self): + del self.title + del self.subtitle + del self.trackText + del self.trackFrames + ShtikerPage.ShtikerPage.unload(self) + + def clearPage(self): + for index in range(1, MAX_FRAMES - 1): + self.trackFrames[index].setUntrained(-1) + + self.startFrame.frame['text'] = '' + self.trackText['text'] = TTLocalizer.TrackPageClear + + def updatePage(self): + trackId, trackProgress = base.localAvatar.getTrackProgress() + if trackId == -1: + self.clearPage() + else: + trackName = ToontownBattleGlobals.Tracks[trackId].capitalize() + self.trackText['text'] = TTLocalizer.TrackPageTraining % (trackName, trackName) + trackProgressArray = base.localAvatar.getTrackProgressAsArray() + for index in range(1, MAX_FRAMES - 2): + if trackProgressArray[index - 1]: + self.trackFrames[index].setTrained(trackId) + else: + self.trackFrames[index].setUntrained(trackId) + + self.trackFrames[MAX_FRAMES - 2].setUntrained(trackId) + self.startFrame.frame['text'] = TTLocalizer.TrackPageFilmTitle % trackName + + def enter(self): + self.updatePage() + ShtikerPage.ShtikerPage.enter(self) + + def exit(self): + self.clearPage() + ShtikerPage.ShtikerPage.exit(self) diff --git a/toontown/shtiker/__init__.py b/toontown/shtiker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/speedchat/TTSCAprilToonsMenu.py b/toontown/speedchat/TTSCAprilToonsMenu.py new file mode 100644 index 0000000..f2c6e71 --- /dev/null +++ b/toontown/speedchat/TTSCAprilToonsMenu.py @@ -0,0 +1,63 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +AprilToonsMenu = [(OTPLocalizer.AprilToonsMenuSections[1], [30100, 30102]), + (OTPLocalizer.AprilToonsMenuSections[2], [30110, + 30111, + 30112, + 30113, + 30114, + 30115]), + (OTPLocalizer.AprilToonsMenuSections[3], [30120, + 30121, + 30122, + 30123, + 30124, + 30125, + 30126]), + (OTPLocalizer.AprilToonsMenuSections[4], [30130, + 30131, + 30132, + 30133]), + (OTPLocalizer.AprilToonsMenuSections[0], [30140, 30141])] + +class TTSCAprilToonsMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__aprilToonsMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __aprilToonsMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in AprilToonsMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link April Toons phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link April Toons phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCBoardingMenu.py b/toontown/speedchat/TTSCBoardingMenu.py new file mode 100644 index 0000000..de84ae7 --- /dev/null +++ b/toontown/speedchat/TTSCBoardingMenu.py @@ -0,0 +1,72 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +BoardingMenuGuide = [(OTPLocalizer.BoardingMenuSections[0], []), + (OTPLocalizer.BoardingMenuSections[1], []), + (OTPLocalizer.BoardingMenuSections[2], []), + (OTPLocalizer.BoardingMenuSections[3], [5005, + 5006, + 5007, + 5008, + 5009])] +GroupPhrases = [5000, + 5001, + 5002, + 5003, + 5004] +ZoneIdsToMsgs = {10000: [GroupPhrases, [5100, 5101, 5102], [5200, 5201, 5202]], + 10100: [GroupPhrases, [5103], [5203]], + 11100: [GroupPhrases, [5104], [5204]], + 11200: [GroupPhrases, [5105, 5106], [5205, 5206]], + 12000: [GroupPhrases, [5107, 5108, 5109], [5207, 5208, 5209]], + 12100: [GroupPhrases, [5110], [5210]], + 13100: [GroupPhrases, [5111], [5211]], + 13200: [GroupPhrases, [5112, + 5113, + 5114, + 5115], [5212, + 5213, + 5214, + 5215]]} + +class TTSCBoardingMenu(SCMenu): + + def __init__(self, zoneId): + SCMenu.__init__(self) + self.__boardingMessagesChanged(zoneId) + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __boardingMessagesChanged(self, zoneId): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for count in xrange(len(BoardingMenuGuide)): + section = BoardingMenuGuide[count] + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link boarding phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + phrases = ZoneIdsToMsgs[zoneId][count] + for phrase in phrases: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link boarding phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCCogMenu.py b/toontown/speedchat/TTSCCogMenu.py new file mode 100644 index 0000000..12ce194 --- /dev/null +++ b/toontown/speedchat/TTSCCogMenu.py @@ -0,0 +1,13 @@ +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal + +class TTSCCogMenu(SCMenu): + + def __init__(self, indices): + SCMenu.__init__(self) + for index in indices: + term = SCStaticTextTerminal(index) + self.append(term) + + def destroy(self): + SCMenu.destroy(self) diff --git a/toontown/speedchat/TTSCDecoders.py b/toontown/speedchat/TTSCDecoders.py new file mode 100644 index 0000000..2bd89c3 --- /dev/null +++ b/toontown/speedchat/TTSCDecoders.py @@ -0,0 +1,2 @@ +from TTSCToontaskTerminal import decodeTTSCToontaskMsg +from TTSCResistanceTerminal import decodeTTSCResistanceMsg diff --git a/toontown/speedchat/TTSCFactoryMenu.py b/toontown/speedchat/TTSCFactoryMenu.py new file mode 100644 index 0000000..6f1f749 --- /dev/null +++ b/toontown/speedchat/TTSCFactoryMenu.py @@ -0,0 +1,91 @@ +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +ZoneToMsgs = {3: [1803, 1903], + 4: [1804, 1904], + 5: [1805, 1905], + 6: [1806, 1906], + 7: [1807, 1907], + 8: [1808, 1908], + 9: [1809, 1909], + 10: [1810, 1910], + 11: [1811, 1911], + 12: [1812, 1912], + 13: [1813, 1913], + 14: [1814, 1914], + 15: [1815, 1915], + 16: [1816, 1916], + 17: [1817, 1917], + 18: [1818, 1918], + 19: [1819, 1919], + 20: [1820, 1920], + 21: [1821, 1921], + 22: [1822, 1922], + 23: [1823, 1923], + 24: [1824, 1924], + 25: [1825, 1925], + 27: [1827, 1927], + 30: [1830, 1930], + 31: [1831, 1931], + 32: [1832, 1932], + 33: [1833, 1933], + 34: [1834, 1934], + 35: [1835, 1935], + 36: [1836, 1936], + 37: [1837, 1937], + 38: [1838, 1938], + 40: [1840, 1940], + 41: [1841, 1941], + 60: [1860, 1960], + 61: [1861, 1961]} +GLOBAL_MSGS = [1700, + 1701, + 1702, + 1703, + 1704] + +class TTSCFactoryMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.meetMenuHolder = None + zoneId = base.cr.playGame.getPlaceId() + if zoneId and zoneId == 11000: + meetMenu = SCMenu() + for msgIndex in OTPLocalizer.SCFactoryMeetMenuIndexes: + term = SCStaticTextTerminal(msgIndex) + meetMenu.append(term) + + self.meetMenuHolder = SCMenuHolder.SCMenuHolder(OTPLocalizer.SCMenuFactoryMeet, meetMenu) + self[0:0] = [self.meetMenuHolder] + self.accept('factoryZoneChanged', self.__zoneChanged) + self.__zoneChanged() + return + + def destroy(self): + self.ignore('factoryZoneChanged') + SCMenu.destroy(self) + + def __zoneChanged(self, zoneId = 0): + if self.meetMenuHolder: + del self[0] + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + phrases = [] + + def addTerminal(terminal, self = self, phrases = phrases): + displayText = terminal.getDisplayText() + if displayText not in phrases: + self.append(terminal) + phrases.append(displayText) + + for msg in GLOBAL_MSGS + ZoneToMsgs.get(zoneId, []): + addTerminal(SCStaticTextTerminal(msg)) + + if self.meetMenuHolder: + self[0:0] = [self.meetMenuHolder] diff --git a/toontown/speedchat/TTSCGolfMenu.py b/toontown/speedchat/TTSCGolfMenu.py new file mode 100644 index 0000000..c300af1 --- /dev/null +++ b/toontown/speedchat/TTSCGolfMenu.py @@ -0,0 +1,68 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +GolfMenuGuide = [(OTPLocalizer.GolfMenuSections[1], [4100, + 4101, + 4102, + 4103, + 4104, + 4105]), + (OTPLocalizer.GolfMenuSections[2], [4200, + 4201, + 4202, + 4203, + 4204, + 4205, + 4206, + 4207]), + (OTPLocalizer.GolfMenuSections[3], [4300, + 4301, + 4302, + 4303, + 4304, + 4305, + 4306, + 4307]), + (OTPLocalizer.GolfMenuSections[0], [4000, 4001, 4002])] + +class TTSCGolfMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('golfMessagesChanged', self.__golfMessagesChanged) + self.__golfMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __golfMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in GolfMenuGuide: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link golf phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link golf phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCHalloweenMenu.py b/toontown/speedchat/TTSCHalloweenMenu.py new file mode 100644 index 0000000..f55a9e3 --- /dev/null +++ b/toontown/speedchat/TTSCHalloweenMenu.py @@ -0,0 +1,44 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +HalloweenMenu = [(OTPLocalizer.HalloweenMenuSections[0], [30250, 30251, 30252])] + +class TTSCHalloweenMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__messagesChanged() + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in HalloweenMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Halloween phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Halloween phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCIdesOfMarchMenu.py b/toontown/speedchat/TTSCIdesOfMarchMenu.py new file mode 100644 index 0000000..d3f802c --- /dev/null +++ b/toontown/speedchat/TTSCIdesOfMarchMenu.py @@ -0,0 +1,44 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +IdesOfMarchMenu = [(OTPLocalizer.IdesOfMarchMenuSections[0], [30450, 30451, 30452])] + +class TTSCIdesOfMarchMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__messagesChanged() + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in IdesOfMarchMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link IdesOfMarch phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link IdesOfMarch phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCIndexedTerminal.py b/toontown/speedchat/TTSCIndexedTerminal.py new file mode 100644 index 0000000..318041b --- /dev/null +++ b/toontown/speedchat/TTSCIndexedTerminal.py @@ -0,0 +1,18 @@ +from otp.speedchat.SCTerminal import * +from otp.otpbase.OTPLocalizer import SpeedChatStaticText +TTSCIndexedMsgEvent = 'SCIndexedMsg' + +def decodeTTSCIndexedMsg(msgIndex): + return SpeedChatStaticText.get(msgIndex, None) + + +class TTSCIndexedTerminal(SCTerminal): + + def __init__(self, msg, msgIndex): + SCTerminal.__init__(self) + self.text = msg + self.msgIndex = msgIndex + + def handleSelect(self): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(TTSCIndexedMsgEvent), [self.msgIndex]) diff --git a/toontown/speedchat/TTSCJellybeanJamMenu.py b/toontown/speedchat/TTSCJellybeanJamMenu.py new file mode 100644 index 0000000..ec60150 --- /dev/null +++ b/toontown/speedchat/TTSCJellybeanJamMenu.py @@ -0,0 +1,58 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +JellybeanJamMenu = [(OTPLocalizer.JellybeanJamMenuSections[0], [30180, + 30181, + 30182, + 30183, + 30184, + 30185]), (OTPLocalizer.JellybeanJamMenuSections[1], [30186, + 30187, + 30188, + 30189, + 30190])] +JellybeanJamPhases = PythonUtil.Enum('TROLLEY, FISHING, PARTIES') +PhaseSpecifPhrases = [30180, 30181, 30182] + +class TTSCJellybeanJamMenu(SCMenu): + + def __init__(self, phase): + SCMenu.__init__(self) + if phase in JellybeanJamPhases: + self.__messagesChanged(phase) + else: + print 'warning: tried to add Jellybean Jam phase %s which does not seem to exist' % phase + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self, phase): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in JellybeanJamMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Jellybean Jam phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Jellybean Jam phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCKartRacingMenu.py b/toontown/speedchat/TTSCKartRacingMenu.py new file mode 100644 index 0000000..29a61d0 --- /dev/null +++ b/toontown/speedchat/TTSCKartRacingMenu.py @@ -0,0 +1,106 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +KartRacingMenuGuide = [(OTPLocalizer.KartRacingMenuSections[1], [3130, + 3160, + 3190, + 3170, + 3180, + 3150, + 3110]), + (OTPLocalizer.KartRacingMenuSections[2], [3200, + 3201, + 3210, + 3211, + 3220, + 3221, + 3222, + 3223, + 3224, + 3225, + 3230, + 3231, + 3232, + 3233, + 3234, + 3235]), + (OTPLocalizer.KartRacingMenuSections[3], [3600, + 3601, + 3602, + 3603, + 3640, + 3641, + 3642, + 3643, + 3660, + 3661, + 3662, + 3663]), + (OTPLocalizer.KartRacingMenuSections[4], [3300, + 3301, + 3310, + 3320, + 3330, + 3340, + 3350, + 3360]), + (OTPLocalizer.KartRacingMenuSections[5], [3410, + 3400, + 3430, + 3450, + 3451, + 3452, + 3453, + 3460, + 3461, + 3462, + 3470]), + (OTPLocalizer.KartRacingMenuSections[0], [3010, + 3020, + 3030, + 3040, + 3050, + 3060, + 3061])] + +class TTSCKartRacingMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('kartRacingMessagesChanged', self.__kartRacingMessagesChanged) + self.__kartRacingMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __kartRacingMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in KartRacingMenuGuide: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link kart phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link kart phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCPetTrickMenu.py b/toontown/speedchat/TTSCPetTrickMenu.py new file mode 100644 index 0000000..5830e91 --- /dev/null +++ b/toontown/speedchat/TTSCPetTrickMenu.py @@ -0,0 +1,32 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +from toontown.pets import PetTricks + +class TTSCPetTrickMenu(SCMenu): + notify = DirectNotifyGlobal.directNotify.newCategory('TTSCPetTrickMenu') + + def __init__(self): + SCMenu.__init__(self) + self.accept('petTrickPhrasesChanged', self.__phrasesChanged) + self.__phrasesChanged() + + def destroy(self): + self.ignore('petTrickPhrasesChanged') + SCMenu.destroy(self) + + def __phrasesChanged(self, zoneId = 0): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for trickId in lt.petTrickPhrases: + if trickId not in PetTricks.TrickId2scIds: + TTSCPetTrickMenu.notify.warning('unknown trick ID: %s' % trickId) + else: + for msg in PetTricks.TrickId2scIds[trickId]: + self.append(SCStaticTextTerminal(msg)) diff --git a/toontown/speedchat/TTSCPromotionalMenu.py b/toontown/speedchat/TTSCPromotionalMenu.py new file mode 100644 index 0000000..78541c4 --- /dev/null +++ b/toontown/speedchat/TTSCPromotionalMenu.py @@ -0,0 +1,40 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +from toontown.toonbase import ToontownGlobals +holidayId2menuInfo = {ToontownGlobals.ELECTION_PROMOTION: (OTPLocalizer.SCMenuElection, [10000, + 10001, + 10006, + 10007])} + +class TTSCPromotionalMenu(SCMenu): + notify = DirectNotifyGlobal.directNotify.newCategory('TTSCPromotionalMenu') + + def __init__(self): + SCMenu.__init__(self) + base.TTSCPromotionalMenu = self + self.curHolidayId = None + self.clearMenu() + return + + def destroy(self): + del base.TTSCPromotionalMenu + SCMenu.destroy(self) + + def startHoliday(self, holidayId): + if self.curHolidayId is not None: + TTSCPromotionalMenu.notify.warning('overriding existing holidayId %s with %s' % (self.curHolidayId, holidayId)) + self.curHolidayId = holidayId + title, structure = holidayId2menuInfo[holidayId] + self.rebuildFromStructure(structure, title=title) + return + + def endHoliday(self, holidayId): + if holidayId != self.curHolidayId: + TTSCPromotionalMenu.notify.warning('unexpected holidayId: %s' % holidayId) + return + self.curHolidayId = None + self.clearMenu() + return diff --git a/toontown/speedchat/TTSCResistanceMenu.py b/toontown/speedchat/TTSCResistanceMenu.py new file mode 100644 index 0000000..ac03983 --- /dev/null +++ b/toontown/speedchat/TTSCResistanceMenu.py @@ -0,0 +1,39 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from toontown.chat import ResistanceChat +from TTSCResistanceTerminal import TTSCResistanceTerminal + +class TTSCResistanceMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('resistanceMessagesChanged', self.__resistanceMessagesChanged) + self.__resistanceMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __resistanceMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + phrases = lt.resistanceMessages + for menuIndex in ResistanceChat.resistanceMenu: + menu = SCMenu() + for itemIndex in ResistanceChat.getItems(menuIndex): + textId = ResistanceChat.encodeId(menuIndex, itemIndex) + charges = lt.getResistanceMessageCharges(textId) + if charges > 0: + menu.append(TTSCResistanceTerminal(textId, charges)) + + textId = ResistanceChat.encodeId(menuIndex, 0) + menuName = ResistanceChat.getMenuName(textId) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCResistanceTerminal.py b/toontown/speedchat/TTSCResistanceTerminal.py new file mode 100644 index 0000000..6e75cd5 --- /dev/null +++ b/toontown/speedchat/TTSCResistanceTerminal.py @@ -0,0 +1,22 @@ +from otp.speedchat.SCTerminal import SCTerminal +from toontown.chat import ResistanceChat +TTSCResistanceMsgEvent = 'TTSCResistanceMsg' + +def decodeTTSCResistanceMsg(textId): + return ResistanceChat.getChatText(textId) + + +class TTSCResistanceTerminal(SCTerminal): + + def __init__(self, textId, charges): + SCTerminal.__init__(self) + self.setCharges(charges) + self.textId = textId + self.text = ResistanceChat.getItemText(self.textId) + + def isWhisperable(self): + return False + + def handleSelect(self): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(TTSCResistanceMsgEvent), [self.textId]) diff --git a/toontown/speedchat/TTSCSellbotFieldOfficeMenu.py b/toontown/speedchat/TTSCSellbotFieldOfficeMenu.py new file mode 100644 index 0000000..2579806 --- /dev/null +++ b/toontown/speedchat/TTSCSellbotFieldOfficeMenu.py @@ -0,0 +1,47 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from toontown.speedchat.TTSCIndexedTerminal import TTSCIndexedTerminal +from otp.otpbase import OTPLocalizer +from toontown.cogdominium import CogdoInterior +SellbotFieldOfficeMenu = [(OTPLocalizer.SellbotFieldOfficeMenuSections[0], range(30404, 30409)), (OTPLocalizer.SellbotFieldOfficeMenuSections[1], range(30409, 30419))] + +class TTSCSellbotFieldOfficeMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('sellbotFieldOfficeChanged', self.__messagesChanged) + self.__messagesChanged(False) + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self, inSellbotFieldOffice): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SellbotFieldOfficeMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Winter phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + elif inSellbotFieldOffice: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Halloween phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSellbotInvasionMenu.py b/toontown/speedchat/TTSCSellbotInvasionMenu.py new file mode 100644 index 0000000..8c00026 --- /dev/null +++ b/toontown/speedchat/TTSCSellbotInvasionMenu.py @@ -0,0 +1,45 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from toontown.speedchat.TTSCIndexedTerminal import TTSCIndexedTerminal +from otp.otpbase import OTPLocalizer +SellbotInvasionMenu = [(OTPLocalizer.SellbotInvasionMenuSections[0], range(30400, 30404))] + +class TTSCSellbotInvasionMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__messagesChanged() + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SellbotInvasionMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Winter phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Halloween phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSellbotNerfMenu.py b/toontown/speedchat/TTSCSellbotNerfMenu.py new file mode 100644 index 0000000..825a543 --- /dev/null +++ b/toontown/speedchat/TTSCSellbotNerfMenu.py @@ -0,0 +1,67 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +SellbotNerfMenu = [(OTPLocalizer.SellbotNerfMenuSections[0], [30150, + 30151, + 30152, + 30153, + 30154, + 30155, + 30156]), (OTPLocalizer.SellbotNerfMenuSections[1], [30157, + 30158, + 30159, + 30160, + 30161, + 30162, + 30163, + 30164]), (OTPLocalizer.SellbotNerfMenuSections[2], [30165, + 30166, + 30167, + 30168, + 30169, + 30170, + 30171, + 30172, + 30173, + 30174, + 30175])] + +class TTSCSellbotNerfMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__messagesChanged() + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SellbotNerfMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Sellbot Nerf phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Sellbot Nerf phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSillyPhaseFiveMenu.py b/toontown/speedchat/TTSCSillyPhaseFiveMenu.py new file mode 100644 index 0000000..2b86973 --- /dev/null +++ b/toontown/speedchat/TTSCSillyPhaseFiveMenu.py @@ -0,0 +1,49 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +SillyPhaseFiveMenu = [(OTPLocalizer.SillyHolidayMenuSections[1], [30325, 30326, 30327]), (OTPLocalizer.SillyHolidayMenuSections[2], [30328, + 30329, + 30330, + 30331, + 30332])] + +class TTSCSillyPhaseFiveMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__SillyPhaseFiveMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __SillyPhaseFiveMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SillyPhaseFiveMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseFive phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseFive phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSillyPhaseFourMenu.py b/toontown/speedchat/TTSCSillyPhaseFourMenu.py new file mode 100644 index 0000000..c32afae --- /dev/null +++ b/toontown/speedchat/TTSCSillyPhaseFourMenu.py @@ -0,0 +1,48 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +SillyPhaseFourMenu = [(OTPLocalizer.SillyHolidayMenuSections[1], [30325, 30326, 30327]), (OTPLocalizer.SillyHolidayMenuSections[2], [30329, + 30330, + 30331, + 30332])] + +class TTSCSillyPhaseFourMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__SillyPhaseFourMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __SillyPhaseFourMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SillyPhaseFourMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseFour phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseFour phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSillyPhaseOneMenu.py b/toontown/speedchat/TTSCSillyPhaseOneMenu.py new file mode 100644 index 0000000..6eb3ac8 --- /dev/null +++ b/toontown/speedchat/TTSCSillyPhaseOneMenu.py @@ -0,0 +1,48 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +SillyPhaseOneMenu = [(OTPLocalizer.SillyHolidayMenuSections[1], [30303, + 30304, + 30305, + 30306]), (OTPLocalizer.SillyHolidayMenuSections[2], [30307, 30308]), (OTPLocalizer.SillyHolidayMenuSections[0], [30301, 30302])] + +class TTSCSillyPhaseOneMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__SillyPhaseOneMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __SillyPhaseOneMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SillyPhaseOneMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseOne phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseOne phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSillyPhaseThreeMenu.py b/toontown/speedchat/TTSCSillyPhaseThreeMenu.py new file mode 100644 index 0000000..52672ba --- /dev/null +++ b/toontown/speedchat/TTSCSillyPhaseThreeMenu.py @@ -0,0 +1,53 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +SillyPhaseThreeMenu = [(OTPLocalizer.SillyHolidayMenuSections[1], [30323, + 30324, + 30325, + 30326, + 30327]), (OTPLocalizer.SillyHolidayMenuSections[2], [30318, + 30319, + 30320, + 30321, + 30322])] + +class TTSCSillyPhaseThreeMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__SillyPhaseThreeMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __SillyPhaseThreeMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SillyPhaseThreeMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseThree phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseThree phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSillyPhaseTwoMenu.py b/toontown/speedchat/TTSCSillyPhaseTwoMenu.py new file mode 100644 index 0000000..e4a25d9 --- /dev/null +++ b/toontown/speedchat/TTSCSillyPhaseTwoMenu.py @@ -0,0 +1,50 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase import OTPLocalizer +SillyPhaseTwoMenu = [(OTPLocalizer.SillyHolidayMenuSections[1], [30310, + 30311, + 30312, + 30313, + 30314, + 30315]), (OTPLocalizer.SillyHolidayMenuSections[2], [30316, 30317]), (OTPLocalizer.SillyHolidayMenuSections[0], [30309])] + +class TTSCSillyPhaseTwoMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__SillyPhaseTwoMessagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __SillyPhaseTwoMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in SillyPhaseTwoMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseTwo phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Silly PhaseTwo phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCSingingMenu.py b/toontown/speedchat/TTSCSingingMenu.py new file mode 100644 index 0000000..ec001d1 --- /dev/null +++ b/toontown/speedchat/TTSCSingingMenu.py @@ -0,0 +1,52 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from TTSCSingingTerminal import TTSCSingingTerminal +from otp.otpbase import OTPLocalizer +SingingMenuGuide = [(OTPLocalizer.SingingMenuSections[0], [{9000: 25}, + {9001: 26}, + {9002: 27}, + {9003: 28}, + {9004: 29}, + {9005: 30}, + {9006: 31}, + {9007: 32}, + {9008: 33}])] + +class TTSCSingingMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__singingMessagesChanged() + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __singingMessagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for count in xrange(len(SingingMenuGuide)): + section = SingingMenuGuide[count] + if section[0] == -1: + for phrase in section[1]: + emote = None + if type(phrase) == type({}): + item = phrase.keys()[0] + emote = phrase[item] + phrase = item + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link a singing phrase %s which does not seem to exist' % phrase + break + terminal = TTSCSingingTerminal(phrase) + if emote is not None: + terminal.setLinkedEmote(emote) + self.append(terminal) + + return diff --git a/toontown/speedchat/TTSCSingingTerminal.py b/toontown/speedchat/TTSCSingingTerminal.py new file mode 100644 index 0000000..74184ad --- /dev/null +++ b/toontown/speedchat/TTSCSingingTerminal.py @@ -0,0 +1,24 @@ +from otp.speedchat.SCTerminal import SCTerminal +from otp.otpbase.OTPLocalizer import SpeedChatStaticText +TTSCSingingMsgEvent = 'SCSingingMsg' + +def decodeSCStaticTextMsg(textId): + return SpeedChatStaticText.get(textId, None) + + +class TTSCSingingTerminal(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(TTSCSingingMsgEvent), [self.textId]) + + def finalize(self): + args = {'rolloverSound': None, + 'clickSound': None} + SCTerminal.finalize(self, args) + return diff --git a/toontown/speedchat/TTSCToontaskMenu.py b/toontown/speedchat/TTSCToontaskMenu.py new file mode 100644 index 0000000..2e6ec68 --- /dev/null +++ b/toontown/speedchat/TTSCToontaskMenu.py @@ -0,0 +1,47 @@ +from otp.speedchat.SCMenu import SCMenu +from TTSCToontaskTerminal import TTSCToontaskTerminal +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from toontown.quest import Quests + +class TTSCToontaskMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.accept('questsChanged', self.__tasksChanged) + self.__tasksChanged() + + def destroy(self): + SCMenu.destroy(self) + + def __tasksChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + phrases = [] + + def addTerminal(terminal, self = self, phrases = phrases): + displayText = terminal.getDisplayText() + if displayText not in phrases: + self.append(terminal) + phrases.append(displayText) + + for task in lt.quests: + taskId, fromNpcId, toNpcId, rewardId, toonProgress = task + q = Quests.getQuest(taskId) + if q is None: + continue + msgs = q.getSCStrings(toNpcId, toonProgress) + if type(msgs) != type([]): + msgs = [msgs] + for i in xrange(len(msgs)): + addTerminal(TTSCToontaskTerminal(msgs[i], taskId, toNpcId, toonProgress, i)) + + needToontask = 1 + if hasattr(lt, 'questCarryLimit'): + needToontask = len(lt.quests) != lt.questCarryLimit + if needToontask: + addTerminal(SCStaticTextTerminal(1299)) + return diff --git a/toontown/speedchat/TTSCToontaskTerminal.py b/toontown/speedchat/TTSCToontaskTerminal.py new file mode 100644 index 0000000..755e0c3 --- /dev/null +++ b/toontown/speedchat/TTSCToontaskTerminal.py @@ -0,0 +1,39 @@ +from otp.speedchat.SCTerminal import * +from toontown.quest import Quests +from toontown.toon import NPCToons +TTSCToontaskMsgEvent = 'SCToontaskMsg' + +def decodeTTSCToontaskMsg(taskId, toNpcId, toonProgress, msgIndex): + q = Quests.getQuest(taskId) + if q is None: + return + name = NPCToons.getNPCName(toNpcId) + if name is None: + return + msgs = q.getSCStrings(toNpcId, toonProgress) + if type(msgs) != type([]): + msgs = [msgs] + if msgIndex >= len(msgs): + return + return msgs[msgIndex] + + +class TTSCToontaskTerminal(SCTerminal): + + def __init__(self, msg, taskId, toNpcId, toonProgress, msgIndex): + SCTerminal.__init__(self) + self.msg = msg + self.taskId = taskId + self.toNpcId = toNpcId + self.toonProgress = toonProgress + self.msgIndex = msgIndex + + def getDisplayText(self): + return self.msg + + def handleSelect(self): + SCTerminal.handleSelect(self) + messenger.send(self.getEventName(TTSCToontaskMsgEvent), [self.taskId, + self.toNpcId, + self.toonProgress, + self.msgIndex]) diff --git a/toontown/speedchat/TTSCVictoryPartiesMenu.py b/toontown/speedchat/TTSCVictoryPartiesMenu.py new file mode 100644 index 0000000..4c89498 --- /dev/null +++ b/toontown/speedchat/TTSCVictoryPartiesMenu.py @@ -0,0 +1,57 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from otp.otpbase.OTPLocalizer import SpeedChatStaticText +from toontown.speedchat.TTSCIndexedTerminal import TTSCIndexedTerminal +from otp.otpbase import OTPLocalizer +VictoryPartiesMenu = [(OTPLocalizer.VictoryPartiesMenuSections[1], [30350, + 30351, + 30352, + 30353, + 30354]), (OTPLocalizer.VictoryPartiesMenuSections[2], [30355, + 30356, + 30357, + 30358, + 30359, + 30360, + 30361]), (OTPLocalizer.VictoryPartiesMenuSections[0], [])] + +class TTSCVictoryPartiesMenu(SCMenu): + + def __init__(self): + SCMenu.__init__(self) + self.__messagesChanged() + submenus = [] + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + for section in VictoryPartiesMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Victory Parties phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Victory Parties phrase %s which does not seem to exist' % phrase + break + menu.append(SCStaticTextTerminal(phrase)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) diff --git a/toontown/speedchat/TTSCWinterMenu.py b/toontown/speedchat/TTSCWinterMenu.py new file mode 100644 index 0000000..d2c0572 --- /dev/null +++ b/toontown/speedchat/TTSCWinterMenu.py @@ -0,0 +1,57 @@ +from direct.showbase import PythonUtil +from otp.speedchat.SCMenu import SCMenu +from otp.speedchat.SCMenuHolder import SCMenuHolder +from otp.speedchat.SCStaticTextTerminal import SCStaticTextTerminal +from toontown.speedchat.TTSCIndexedTerminal import TTSCIndexedTerminal +from otp.otpbase import OTPLocalizer +WinterMenu = [(OTPLocalizer.WinterMenuSections[0], {30200: 30220, + 30201: 30221, + 30202: 30222, + 30203: 30223, + 30204: 30224, + 30205: 30225}), (OTPLocalizer.WinterMenuSections[1], [30275, 30276, 30277])] + +class TTSCWinterMenu(SCMenu): + + def __init__(self, carol): + SCMenu.__init__(self) + self.__messagesChanged(carol) + + def destroy(self): + SCMenu.destroy(self) + + def clearMenu(self): + SCMenu.clearMenu(self) + + def __messagesChanged(self, carol): + self.clearMenu() + try: + lt = base.localAvatar + except: + return + + winterMenu = [] + if carol: + winterMenu.append(WinterMenu[0]) + winterMenu.append(WinterMenu[1]) + for section in winterMenu: + if section[0] == -1: + for phrase in section[1]: + if phrase not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Winter phrase %s which does not seem to exist' % phrase + break + self.append(SCStaticTextTerminal(phrase)) + + else: + menu = SCMenu() + for phrase in section[1].keys(): + blatherTxt = section[1][phrase] + if blatherTxt not in OTPLocalizer.SpeedChatStaticText: + print 'warning: tried to link Winter phrase %s which does not seem to exist' % phrase + break + menu.append(TTSCIndexedTerminal(OTPLocalizer.SpeedChatStaticText.get(phrase, None), blatherTxt)) + + menuName = str(section[0]) + self.append(SCMenuHolder(menuName, menu)) + + return diff --git a/toontown/speedchat/TTSpeedChatGlobals.py b/toontown/speedchat/TTSpeedChatGlobals.py new file mode 100644 index 0000000..21a588d --- /dev/null +++ b/toontown/speedchat/TTSpeedChatGlobals.py @@ -0,0 +1,2 @@ +from TTSCToontaskTerminal import TTSCToontaskMsgEvent +from TTSCResistanceTerminal import TTSCResistanceMsgEvent diff --git a/toontown/speedchat/TTSpeedChatTypes.py b/toontown/speedchat/TTSpeedChatTypes.py new file mode 100644 index 0000000..f32871b --- /dev/null +++ b/toontown/speedchat/TTSpeedChatTypes.py @@ -0,0 +1,27 @@ +from TTSCToontaskMenu import TTSCToontaskMenu +from TTSCFactoryMenu import TTSCFactoryMenu +from TTSCCogMenu import TTSCCogMenu +if hasattr(base, 'wantPets') and base.wantPets: + from TTSCPetTrickMenu import TTSCPetTrickMenu +from TTSCPromotionalMenu import TTSCPromotionalMenu +from TTSCToontaskTerminal import TTSCToontaskTerminal +from TTSCResistanceMenu import TTSCResistanceMenu +from TTSCResistanceTerminal import TTSCResistanceTerminal +from TTSCKartRacingMenu import TTSCKartRacingMenu +from TTSCGolfMenu import TTSCGolfMenu +from TTSCBoardingMenu import TTSCBoardingMenu +from TTSCSingingMenu import TTSCSingingMenu +from TTSCAprilToonsMenu import TTSCAprilToonsMenu +from TTSCSillyPhaseOneMenu import TTSCSillyPhaseOneMenu +from TTSCSillyPhaseTwoMenu import TTSCSillyPhaseTwoMenu +from TTSCSillyPhaseThreeMenu import TTSCSillyPhaseThreeMenu +from TTSCSillyPhaseFourMenu import TTSCSillyPhaseFourMenu +from TTSCSillyPhaseFiveMenu import TTSCSillyPhaseFiveMenu +from TTSCVictoryPartiesMenu import TTSCVictoryPartiesMenu +from TTSCSellbotNerfMenu import TTSCSellbotNerfMenu +from TTSCJellybeanJamMenu import TTSCJellybeanJamMenu +from TTSCHalloweenMenu import TTSCHalloweenMenu +from TTSCWinterMenu import TTSCWinterMenu +from TTSCSellbotInvasionMenu import TTSCSellbotInvasionMenu +from TTSCSellbotFieldOfficeMenu import TTSCSellbotFieldOfficeMenu +from TTSCIdesOfMarchMenu import TTSCIdesOfMarchMenu diff --git a/toontown/speedchat/__init__.py b/toontown/speedchat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/suit/BossCog.py b/toontown/suit/BossCog.py new file mode 100644 index 0000000..35426cb --- /dev/null +++ b/toontown/suit/BossCog.py @@ -0,0 +1,542 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.actor import Actor +from otp.avatar import Avatar +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from direct.fsm import FSM +from direct.fsm import State +from toontown.toonbase import TTLocalizer +from toontown.battle import BattleParticles +import Suit +from direct.task.Task import Task +import SuitDNA +from toontown.battle import BattleProps +from direct.showbase.PythonUtil import Functor +import string +import types +GenericModel = 'phase_9/models/char/bossCog' +ModelDict = {'s': 'phase_9/models/char/sellbotBoss', + 'm': 'phase_10/models/char/cashbotBoss', + 'l': 'phase_11/models/char/lawbotBoss', + 'c': 'phase_12/models/char/bossbotBoss'} +AnimList = ('Ff_speech', 'ltTurn2Wave', 'wave', 'Ff_lookRt', 'turn2Fb', 'Ff_neutral', 'Bb_neutral', 'Ff2Bb_spin', 'Bb2Ff_spin', 'Fb_neutral', 'Bf_neutral', 'Fb_firstHit', 'Fb_downNeutral', 'Fb_downHit', 'Fb_fall', 'Fb_down2Up', 'Fb_downLtSwing', 'Fb_downRtSwing', 'Fb_DownThrow', 'Fb_UpThrow', 'Fb_jump', 'golf_swing') + +class BossCog(Avatar.Avatar): + notify = DirectNotifyGlobal.directNotify.newCategory('BossCog') + healthColors = Suit.Suit.healthColors + healthGlowColors = Suit.Suit.healthGlowColors + + def __init__(self): + Avatar.Avatar.__init__(self) + self.setFont(ToontownGlobals.getSuitFont()) + self.setPlayerType(NametagGroup.CCSuit) + self.setPickable(0) + self.doorA = None + self.doorB = None + self.bubbleL = None + self.bubbleR = None + self.raised = 1 + self.forward = 1 + self.happy = 1 + self.dizzy = 0 + self.nowRaised = 1 + self.nowForward = 1 + self.nowHappy = 1 + self.currentAnimIval = None + self.queuedAnimIvals = [] + self.treadsLeftPos = 0 + self.treadsRightPos = 0 + self.healthBar = None + self.healthCondition = 0 + self.animDoneEvent = 'BossCogAnimDone' + self.animIvalName = 'BossCogAnimIval' + return + + def delete(self): + Avatar.Avatar.delete(self) + self.removeHealthBar() + self.setDizzy(0) + self.stopAnimate() + if self.doorA: + self.doorA.request('Off') + self.doorB.request('Off') + self.doorA = None + self.doorB = None + return + + def setDNAString(self, dnaString): + self.dna = SuitDNA.SuitDNA() + self.dna.makeFromNetString(dnaString) + self.setDNA(self.dna) + + def setDNA(self, dna): + if self.style: + pass + else: + self.style = dna + self.generateBossCog() + self.initializeDropShadow() + if base.wantNametags: + self.initializeNametag3d() + + def generateBossCog(self): + self.throwSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_frisbee_gears.mp3') + self.swingSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_swipe.mp3') + self.spinSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_spin.mp3') + self.rainGearsSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_raining_gears.mp3') + self.swishSfx = loader.loadSfx('phase_5/audio/sfx/General_throw_miss.mp3') + self.boomSfx = loader.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3') + self.deathSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_big_death.mp3') + self.upSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_raise_up.mp3') + self.downSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_collapse.mp3') + self.reelSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_reeling_backwards.mp3') + self.birdsSfx = loader.loadSfx('phase_4/audio/sfx/SZ_TC_bird1.mp3') + self.dizzyAlert = loader.loadSfx('phase_5/audio/sfx/AA_sound_aoogah.mp3') + self.grunt = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_grunt.mp3') + self.murmur = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_murmur.mp3') + self.statement = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_statement.mp3') + self.question = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_question.mp3') + self.dialogArray = [self.grunt, + self.murmur, + self.statement, + self.question, + self.statement, + self.statement] + dna = self.style + filePrefix = ModelDict[dna.dept] + self.loadModel(GenericModel + '-legs-zero', 'legs') + self.loadModel(filePrefix + '-torso-zero', 'torso') + self.loadModel(filePrefix + '-head-zero', 'head') + self.twoFaced = dna.dept == 's' + self.attach('head', 'torso', 'joint34') + self.attach('torso', 'legs', 'joint_pelvis') + self.rotateNode = self.attachNewNode('rotate') + geomNode = self.getGeomNode() + geomNode.reparentTo(self.rotateNode) + self.frontAttack = self.rotateNode.attachNewNode('frontAttack') + self.frontAttack.setPos(0, -10, 10) + self.frontAttack.setScale(2) + self.setHeight(26) + self.nametag3d.setScale(2) + for partName in ('legs', 'torso', 'head'): + animDict = {} + for anim in AnimList: + animDict[anim] = '%s-%s-%s' % (GenericModel, partName, anim) + + self.loadAnims(animDict, partName) + + self.stars = BattleProps.globalPropPool.getProp('stun') + self.stars.setPosHprScale(7, 0, 0, 0, 0, -90, 3, 3, 3) + self.stars.loop('stun') + self.pelvis = self.getPart('torso') + self.pelvisForwardHpr = VBase3(0, 0, 0) + self.pelvisReversedHpr = VBase3(-180, 0, 0) + self.neck = self.getPart('head') + self.neckForwardHpr = VBase3(0, 0, 0) + self.neckReversedHpr = VBase3(0, -540, 0) + self.axle = self.find('**/joint_axle') + self.doorA = self.__setupDoor('**/joint_doorFront', 'doorA', self.doorACallback, VBase3(0, 0, 0), VBase3(0, 0, -80), CollisionPolygon(Point3(5, -4, 0.32), Point3(0, -4, 0), Point3(0, 4, 0), Point3(5, 4, 0.32))) + self.doorB = self.__setupDoor('**/joint_doorRear', 'doorB', self.doorBCallback, VBase3(0, 0, 0), VBase3(0, 0, 80), CollisionPolygon(Point3(-5, 4, 0.84), Point3(0, 4, 0), Point3(0, -4, 0), Point3(-5, -4, 0.84))) + treadsModel = loader.loadModel('%s-treads' % GenericModel) + treadsModel.reparentTo(self.axle) + self.treadsLeft = treadsModel.find('**/right_tread') + self.treadsRight = treadsModel.find('**/left_tread') + self.doorA.request('Closed') + self.doorB.request('Closed') + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def generateHealthBar(self): + self.removeHealthBar() + chestNull = self.find('**/joint_lifeMeter') + if chestNull.isEmpty(): + return + model = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + button = model.find('**/minnieCircle') + button.setScale(6.0) + button.setP(-20) + button.setColor(self.healthColors[0]) + button.reparentTo(chestNull) + self.healthBar = button + glow = BattleProps.globalPropPool.getProp('glow') + glow.reparentTo(self.healthBar) + glow.setScale(0.28) + glow.setPos(-0.005, 0.01, 0.015) + glow.setColor(self.healthGlowColors[0]) + button.flattenLight() + self.healthBarGlow = glow + self.healthCondition = 0 + + def updateHealthBar(self): + if self.healthBar == None: + return + health = 1.0 - float(self.bossDamage) / float(self.bossMaxDamage) + if health > 0.95: + condition = 0 + elif health > 0.7: + condition = 1 + elif health > 0.3: + condition = 2 + elif health > 0.05: + condition = 3 + elif health > 0.0: + condition = 4 + else: + condition = 5 + if self.healthCondition != condition: + if condition == 4: + blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75), Task(self.__blinkGray), Task.pause(0.1)) + taskMgr.add(blinkTask, self.uniqueName('blink-task')) + elif condition == 5: + if self.healthCondition == 4: + taskMgr.remove(self.uniqueName('blink-task')) + blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25), Task(self.__blinkGray), Task.pause(0.1)) + taskMgr.add(blinkTask, self.uniqueName('blink-task')) + else: + self.healthBar.setColor(self.healthColors[condition], 1) + self.healthBarGlow.setColor(self.healthGlowColors[condition], 1) + self.healthCondition = condition + return + + def __blinkRed(self, task): + self.healthBar.setColor(self.healthColors[3], 1) + self.healthBarGlow.setColor(self.healthGlowColors[3], 1) + if self.healthCondition == 5: + self.healthBar.setScale(1.17) + return Task.done + + def __blinkGray(self, task): + self.healthBar.setColor(self.healthColors[4], 1) + self.healthBarGlow.setColor(self.healthGlowColors[4], 1) + if self.healthCondition == 5: + self.healthBar.setScale(1.0) + return Task.done + + def removeHealthBar(self): + if self.healthBar: + self.healthBar.removeNode() + self.healthBar = None + if self.healthCondition == 4 or self.healthCondition == 5: + taskMgr.remove(self.uniqueName('blink-task')) + self.healthCondition = 0 + return + + def reverseHead(self): + self.neck.setHpr(self.neckReversedHpr) + + def forwardHead(self): + self.neck.setHpr(self.neckForwardHpr) + + def reverseBody(self): + self.pelvis.setHpr(self.pelvisReversedHpr) + + def forwardBody(self): + self.pelvis.setHpr(self.pelvisForwardHpr) + + def getShadowJoint(self): + return self.getGeomNode() + + def getNametagJoints(self): + return [] + + def getDialogueArray(self): + return self.dialogArray + + def doorACallback(self, isOpen): + pass + + def doorBCallback(self, isOpen): + pass + + def __rollTreadsInterval(self, object, start = 0, duration = 0, rate = 1): + + def rollTexMatrix(t, object = object): + object.setTexOffset(TextureStage.getDefault(), t, 0) + + return LerpFunctionInterval(rollTexMatrix, fromData=start, toData=start + rate * duration, duration=duration) + + def rollLeftTreads(self, duration, rate): + start = self.treadsLeftPos + self.treadsLeftPos += duration * rate + return self.__rollTreadsInterval(self.treadsLeft, start=start, duration=duration, rate=rate) + + def rollRightTreads(self, duration, rate): + start = self.treadsRightPos + self.treadsRightPos += duration * rate + return self.__rollTreadsInterval(self.treadsRight, start=start, duration=duration, rate=rate) + + class DoorFSM(FSM.FSM): + + def __init__(self, name, animate, callback, openedHpr, closedHpr, uniqueName): + FSM.FSM.__init__(self, name) + self.animate = animate + self.callback = callback + self.openedHpr = openedHpr + self.closedHpr = closedHpr + self.uniqueName = uniqueName + self.ival = 0 + self.openSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_door_open.mp3') + self.closeSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_door_close.mp3') + self.request('Closed') + + def filterOpening(self, request, args): + if request == 'close': + return 'Closing' + return self.defaultFilter(request, args) + + def enterOpening(self): + intervalName = self.uniqueName('open-%s' % self.animate.getName()) + self.callback(0) + ival = Parallel(SoundInterval(self.openSfx, node=self.animate, volume=0.2), self.animate.hprInterval(1, self.openedHpr, blendType='easeInOut'), Sequence(Wait(0.2), Func(self.callback, 1)), name=intervalName) + ival.start() + self.ival = ival + + def exitOpening(self): + self.ival.pause() + self.ival = None + return + + def filterOpened(self, request, args): + if request == 'close': + return 'Closing' + return self.defaultFilter(request, args) + + def enterOpened(self): + self.animate.setHpr(self.openedHpr) + self.callback(1) + + def filterClosing(self, request, args): + if request == 'open': + return 'Opening' + return self.defaultFilter(request, args) + + def enterClosing(self): + intervalName = self.uniqueName('close-%s' % self.animate.getName()) + self.callback(1) + ival = Parallel(SoundInterval(self.closeSfx, node=self.animate, volume=0.2), self.animate.hprInterval(1, self.closedHpr, blendType='easeInOut'), Sequence(Wait(0.8), Func(self.callback, 0)), name=intervalName) + ival.start() + self.ival = ival + + def exitClosing(self): + self.ival.pause() + self.ival = None + return + + def filterClosed(self, request, args): + if request == 'open': + return 'Opening' + return self.defaultFilter(request, args) + + def enterClosed(self): + self.animate.setHpr(self.closedHpr) + self.callback(0) + + def __setupDoor(self, jointName, name, callback, openedHpr, closedHpr, cPoly): + joint = self.find(jointName) + children = joint.getChildren() + animate = joint.attachNewNode(name) + children.reparentTo(animate) + cnode = CollisionNode('BossZap') + cnode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask | ToontownGlobals.CameraBitmask) + cnode.addSolid(cPoly) + animate.attachNewNode(cnode) + fsm = self.DoorFSM(name, animate, callback, openedHpr, closedHpr, self.uniqueName) + return fsm + + def doAnimate(self, anim = None, now = 0, queueNeutral = 1, raised = None, forward = None, happy = None): + if now: + self.stopAnimate() + if not self.twoFaced: + happy = 1 + if raised == None: + raised = self.raised + if forward == None: + forward = self.forward + if happy == None: + happy = self.happy + if now: + self.raised = raised + self.forward = forward + self.happy = happy + if self.currentAnimIval == None: + self.accept(self.animDoneEvent, self.__getNextAnim) + else: + queueNeutral = 0 + ival, changed = self.__getAnimIval(anim, raised, forward, happy) + if changed or queueNeutral: + self.queuedAnimIvals.append((ival, + self.raised, + self.forward, + self.happy)) + if self.currentAnimIval == None: + self.__getNextAnim() + return + + def stopAnimate(self): + self.ignore(self.animDoneEvent) + self.queuedAnimIvals = [] + if self.currentAnimIval: + self.currentAnimIval.setDoneEvent('') + self.currentAnimIval.finish() + self.currentAnimIval = None + self.raised = self.nowRaised + self.forward = self.nowForward + self.happy = self.nowHappy + return + + def __getNextAnim(self): + if self.queuedAnimIvals: + ival, raised, forward, happy = self.queuedAnimIvals[0] + del self.queuedAnimIvals[0] + else: + ival, changed = self.__getAnimIval(None, self.raised, self.forward, self.happy) + raised = self.raised + forward = self.forward + happy = self.happy + if self.currentAnimIval: + self.currentAnimIval.setDoneEvent('') + self.currentAnimIval.finish() + self.currentAnimIval = ival + self.currentAnimIval.start() + self.nowRaised = raised + self.nowForward = forward + self.nowHappy = happy + return + + def __getAnimIval(self, anim, raised, forward, happy): + ival, changed = self.__doGetAnimIval(anim, raised, forward, happy) + seq = Sequence(ival, name=self.animIvalName) + seq.setDoneEvent(self.animDoneEvent) + return (seq, changed) + + def __doGetAnimIval(self, anim, raised, forward, happy): + if raised == self.raised and forward == self.forward and happy == self.happy: + return (self.getAnim(anim), anim != None) + startsHappy = self.happy + endsHappy = self.happy + ival = Sequence() + if raised and not self.raised: + upIval = self.getAngryActorInterval('Fb_down2Up') + if self.forward: + ival = upIval + else: + ival = Sequence(Func(self.reverseBody), upIval, Func(self.forwardBody)) + ival = Parallel(SoundInterval(self.upSfx, node=self), ival) + if forward != self.forward: + if forward: + animName = 'Bb2Ff_spin' + else: + animName = 'Ff2Bb_spin' + ival = Sequence(ival, ActorInterval(self, animName)) + startsHappy = 1 + endsHappy = 1 + startNeckHpr = self.neckForwardHpr + endNeckHpr = self.neckForwardHpr + if self.happy != startsHappy: + startNeckHpr = self.neckReversedHpr + if happy != endsHappy: + endNeckHpr = self.neckReversedHpr + if startNeckHpr != endNeckHpr: + ival = Sequence(Func(self.neck.setHpr, startNeckHpr), ParallelEndTogether(ival, Sequence(self.neck.hprInterval(0.5, endNeckHpr, startHpr=startNeckHpr, blendType='easeInOut'), Func(self.neck.setHpr, self.neckForwardHpr)))) + elif endNeckHpr != self.neckForwardHpr: + ival = Sequence(Func(self.neck.setHpr, startNeckHpr), ival, Func(self.neck.setHpr, self.neckForwardHpr)) + if not raised and self.raised: + downIval = self.getAngryActorInterval('Fb_down2Up', playRate=-1) + if forward: + ival = Sequence(ival, downIval) + else: + ival = Sequence(ival, Func(self.reverseBody), downIval, Func(self.forwardBody)) + ival = Parallel(SoundInterval(self.downSfx, node=self), ival) + self.raised = raised + self.forward = forward + self.happy = happy + if anim != None: + ival = Sequence(ival, self.getAnim(anim)) + return (ival, 1) + + def setDizzy(self, dizzy): + if dizzy and not self.dizzy: + base.playSfx(self.dizzyAlert) + self.dizzy = dizzy + if dizzy: + self.stars.reparentTo(self.neck) + base.playSfx(self.birdsSfx, looping=1) + else: + self.stars.detachNode() + self.birdsSfx.stop() + + def getAngryActorInterval(self, animName, **kw): + if self.happy: + ival = Sequence(Func(self.reverseHead), ActorInterval(self, animName, **kw), Func(self.forwardHead)) + else: + ival = ActorInterval(self, animName, **kw) + return ival + + def getAnim(self, anim): + ival = None + if anim == None: + partName = None + if self.happy: + animName = 'Ff_neutral' + else: + animName = 'Fb_neutral' + if self.raised: + ival = ActorInterval(self, animName) + else: + ival = Parallel(ActorInterval(self, animName, partName=['torso', 'head']), ActorInterval(self, 'Fb_downNeutral', partName='legs')) + if not self.forward: + ival = Sequence(Func(self.reverseBody), ival, Func(self.forwardBody)) + elif anim == 'down2Up': + ival = Parallel(SoundInterval(self.upSfx, node=self), self.getAngryActorInterval('Fb_down2Up')) + self.raised = 1 + elif anim == 'up2Down': + ival = Parallel(SoundInterval(self.downSfx, node=self), self.getAngryActorInterval('Fb_down2Up', playRate=-1)) + self.raised = 0 + elif anim == 'throw': + self.doAnimate(None, raised=1, happy=0, queueNeutral=0) + ival = Parallel(Sequence(SoundInterval(self.throwSfx, node=self), duration=0), self.getAngryActorInterval('Fb_UpThrow')) + elif anim == 'hit': + if self.raised: + self.raised = 0 + ival = self.getAngryActorInterval('Fb_firstHit') + else: + ival = self.getAngryActorInterval('Fb_downHit') + ival = Parallel(SoundInterval(self.reelSfx, node=self), ival) + elif anim == 'ltSwing' or anim == 'rtSwing': + self.doAnimate(None, raised=0, happy=0, queueNeutral=0) + if anim == 'ltSwing': + ival = Sequence(Track((0, self.getAngryActorInterval('Fb_downLtSwing')), (0.9, SoundInterval(self.swingSfx, node=self)), (1, Func(self.bubbleL.unstash))), Func(self.bubbleL.stash)) + else: + ival = Sequence(Track((0, self.getAngryActorInterval('Fb_downRtSwing')), (0.9, SoundInterval(self.swingSfx, node=self)), (1, Func(self.bubbleR.unstash))), Func(self.bubbleR.stash)) + elif anim == 'frontAttack': + self.doAnimate(None, raised=1, happy=0, queueNeutral=0) + pe = BattleParticles.loadParticleFile('bossCogFrontAttack.ptf') + ival = Sequence(Func(self.reverseHead), ActorInterval(self, 'Bb2Ff_spin'), Func(self.forwardHead)) + if self.forward: + ival = Sequence(Func(self.reverseBody), ParallelEndTogether(ival, self.pelvis.hprInterval(0.5, self.pelvisForwardHpr, blendType='easeInOut'))) + ival = Sequence(Track((0, ival), (0, SoundInterval(self.spinSfx, node=self)), (0.9, Parallel(SoundInterval(self.rainGearsSfx, node=self), ParticleInterval(pe, self.frontAttack, worldRelative=0, duration=1.5, cleanup=True), duration=0)), (1.9, Func(self.bubbleF.unstash))), Func(self.bubbleF.stash)) + self.forward = 1 + self.happy = 0 + self.raised = 1 + elif anim == 'areaAttack': + if self.twoFaced: + self.doAnimate(None, raised=1, happy=0, queueNeutral=0) + else: + self.doAnimate(None, raised=1, happy=1, queueNeutral=1) + ival = Parallel(ActorInterval(self, 'Fb_jump'), Sequence(SoundInterval(self.swishSfx, duration=1.1, node=self), SoundInterval(self.boomSfx, duration=1.9)), Sequence(Wait(1.21), Func(self.announceAreaAttack))) + if self.twoFaced: + self.happy = 0 + else: + self.happy = 1 + self.raised = 1 + elif anim == 'Fb_fall': + ival = Parallel(ActorInterval(self, 'Fb_fall'), Sequence(SoundInterval(self.reelSfx, node=self), SoundInterval(self.deathSfx))) + elif isinstance(anim, types.StringType): + ival = ActorInterval(self, anim) + else: + ival = anim + return ival diff --git a/toontown/suit/DistributedBossCog.py b/toontown/suit/DistributedBossCog.py new file mode 100644 index 0000000..ee58ce2 --- /dev/null +++ b/toontown/suit/DistributedBossCog.py @@ -0,0 +1,1127 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import DistributedAvatar +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.battle import BattleExperience +from toontown.battle import BattleBase +import BossCog +import SuitDNA +from toontown.coghq import CogDisguiseGlobals +from direct.showbase import Transitions +from toontown.hood import ZoneUtil +from toontown.building import ElevatorUtils +from toontown.building import ElevatorConstants +from toontown.distributed import DelayDelete +from toontown.effects import DustCloud +from toontown.toonbase import TTLocalizer +from toontown.friends import FriendsListManager +from direct.controls.ControlManager import CollisionHandlerRayStart +from direct.showbase import PythonUtil +import random + +class DistributedBossCog(DistributedAvatar.DistributedAvatar, BossCog.BossCog): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossCog') + allowClickedNameTag = True + + def __init__(self, cr): + DistributedAvatar.DistributedAvatar.__init__(self, cr) + BossCog.BossCog.__init__(self) + self.gotAllToons = 0 + self.toonsA = [] + self.toonsB = [] + self.involvedToons = [] + self.toonRequest = None + self.battleNumber = 0 + self.battleAId = None + self.battleBId = None + self.battleA = None + self.battleB = None + self.battleRequest = None + self.arenaSide = 0 + self.toonSphere = None + self.localToonIsSafe = 0 + self.__toonsStuckToFloor = [] + self.cqueue = None + self.rays = None + self.ray1 = None + self.ray2 = None + self.ray3 = None + self.e1 = None + self.e2 = None + self.e3 = None + self.battleANode = self.attachNewNode('battleA') + self.battleBNode = self.attachNewNode('battleB') + self.battleANode.setPosHpr(*ToontownGlobals.BossCogBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.BossCogBattleBPosHpr) + self.activeIntervals = {} + self.flashInterval = None + self.elevatorType = ElevatorConstants.ELEVATOR_VP + return + + def announceGenerate(self): + DistributedAvatar.DistributedAvatar.announceGenerate(self) + self.prevCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + nearBubble = CollisionSphere(0, 0, 0, 50) + nearBubble.setTangible(0) + nearBubbleNode = CollisionNode('NearBoss') + nearBubbleNode.setCollideMask(ToontownGlobals.WallBitmask) + nearBubbleNode.addSolid(nearBubble) + self.attachNewNode(nearBubbleNode) + self.accept('enterNearBoss', self.avatarNearEnter) + self.accept('exitNearBoss', self.avatarNearExit) + self.collNode.removeSolid(0) + tube1 = CollisionTube(6.5, -7.5, 2, 6.5, 7.5, 2, 2.5) + tube2 = CollisionTube(-6.5, -7.5, 2, -6.5, 7.5, 2, 2.5) + roof = CollisionPolygon(Point3(-4.4, 7.1, 5.5), Point3(-4.4, -7.1, 5.5), Point3(4.4, -7.1, 5.5), Point3(4.4, 7.1, 5.5)) + side1 = CollisionPolygon(Point3(-4.4, -7.1, 5.5), Point3(-4.4, 7.1, 5.5), Point3(-4.4, 7.1, 0), Point3(-4.4, -7.1, 0)) + side2 = CollisionPolygon(Point3(4.4, 7.1, 5.5), Point3(4.4, -7.1, 5.5), Point3(4.4, -7.1, 0), Point3(4.4, 7.1, 0)) + front1 = CollisionPolygon(Point3(4.4, -7.1, 5.5), Point3(-4.4, -7.1, 5.5), Point3(-4.4, -7.1, 5.2), Point3(4.4, -7.1, 5.2)) + back1 = CollisionPolygon(Point3(-4.4, 7.1, 5.5), Point3(4.4, 7.1, 5.5), Point3(4.4, 7.1, 5.2), Point3(-4.4, 7.1, 5.2)) + self.collNode.addSolid(tube1) + self.collNode.addSolid(tube2) + self.collNode.addSolid(roof) + self.collNode.addSolid(side1) + self.collNode.addSolid(side2) + self.collNode.addSolid(front1) + self.collNode.addSolid(back1) + self.collNodePath.reparentTo(self.axle) + self.collNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask | ToontownGlobals.CameraBitmask) + self.collNode.setName('BossZap') + self.setTag('attackCode', str(ToontownGlobals.BossCogElectricFence)) + self.accept('enterBossZap', self.__touchedBoss) + bubbleL = CollisionSphere(10, -5, 0, 10) + bubbleL.setTangible(0) + bubbleLNode = CollisionNode('BossZap') + bubbleLNode.setCollideMask(ToontownGlobals.WallBitmask) + bubbleLNode.addSolid(bubbleL) + self.bubbleL = self.axle.attachNewNode(bubbleLNode) + self.bubbleL.setTag('attackCode', str(ToontownGlobals.BossCogSwatLeft)) + self.bubbleL.stash() + bubbleR = CollisionSphere(-10, -5, 0, 10) + bubbleR.setTangible(0) + bubbleRNode = CollisionNode('BossZap') + bubbleRNode.setCollideMask(ToontownGlobals.WallBitmask) + bubbleRNode.addSolid(bubbleR) + self.bubbleR = self.axle.attachNewNode(bubbleRNode) + self.bubbleR.setTag('attackCode', str(ToontownGlobals.BossCogSwatRight)) + self.bubbleR.stash() + bubbleF = CollisionSphere(0, -25, 0, 12) + bubbleF.setTangible(0) + bubbleFNode = CollisionNode('BossZap') + bubbleFNode.setCollideMask(ToontownGlobals.WallBitmask) + bubbleFNode.addSolid(bubbleF) + self.bubbleF = self.rotateNode.attachNewNode(bubbleFNode) + self.bubbleF.setTag('attackCode', str(ToontownGlobals.BossCogFrontAttack)) + self.bubbleF.stash() + + def disable(self): + DistributedAvatar.DistributedAvatar.disable(self) + self.battleAId = None + self.battleBId = None + self.battleA = None + self.battleB = None + self.cr.relatedObjectMgr.abortRequest(self.toonRequest) + self.toonRequest = None + self.cr.relatedObjectMgr.abortRequest(self.battleRequest) + self.battleRequest = None + self.stopAnimate() + self.cleanupIntervals() + self.cleanupFlash() + self.disableLocalToonSimpleCollisions() + self.ignoreAll() + return + + def delete(self): + try: + self.DistributedBossCog_deleted + except: + self.DistributedBossCog_deleted = 1 + self.ignoreAll() + DistributedAvatar.DistributedAvatar.delete(self) + BossCog.BossCog.delete(self) + + def setDNAString(self, dnaString): + BossCog.BossCog.setDNAString(self, dnaString) + + def getDNAString(self): + return self.dna.makeNetString() + + def setDNA(self, dna): + BossCog.BossCog.setDNA(self, dna) + + def setToonIds(self, involvedToons, toonsA, toonsB): + self.involvedToons = involvedToons + self.toonsA = toonsA + self.toonsB = toonsB + self.cr.relatedObjectMgr.abortRequest(self.toonRequest) + self.gotAllToons = 0 + self.toonRequest = self.cr.relatedObjectMgr.requestObjects(self.involvedToons, allCallback=self.__gotAllToons, eachCallback=self.gotToon) + + def getDialogueArray(self, *args): + return BossCog.BossCog.getDialogueArray(self, *args) + + 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 = 1): + 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) + 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 d_avatarEnter(self): + self.sendUpdate('avatarEnter', []) + + def d_avatarExit(self): + self.sendUpdate('avatarExit', []) + + def avatarNearEnter(self, entry): + self.sendUpdate('avatarNearEnter', []) + + def avatarNearExit(self, entry): + self.sendUpdate('avatarNearExit', []) + + def hasLocalToon(self): + doId = localAvatar.doId + return doId in self.toonsA or doId in self.toonsB + + 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, id4, origExp4, earnedExp4, origQuests4, items4, missedItems4, origMerits4, merits4, parts4, id5, origExp5, earnedExp5, origQuests5, items5, missedItems5, origMerits5, merits5, parts5, id6, origExp6, earnedExp6, origQuests6, items6, missedItems6, origMerits6, merits6, parts6, id7, origExp7, earnedExp7, origQuests7, items7, missedItems7, origMerits7, merits7, parts7, deathList, uberList, helpfulToons): + self.deathList = deathList + self.uberList = uberList + self.helpfulToons = helpfulToons + 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), + (id4, + origExp4, + earnedExp4, + origQuests4, + items4, + missedItems4, + origMerits4, + merits4, + parts4), + (id5, + origExp5, + earnedExp5, + origQuests5, + items5, + missedItems5, + origMerits5, + merits5, + parts5), + (id6, + origExp6, + earnedExp6, + origQuests6, + items6, + missedItems6, + origMerits6, + merits6, + parts6), + (id7, + origExp7, + earnedExp7, + origQuests7, + items7, + missedItems7, + origMerits7, + merits7, + parts7)) + self.toonRewardDicts = BattleExperience.genRewardDicts(entries) + self.toonRewardIds = [id0, + id1, + id2, + id3, + id4, + id5, + id6, + id7] + + def setArenaSide(self, arenaSide): + self.arenaSide = arenaSide + + def setState(self, state): + self.request(state) + + def gotToon(self, toon): + stateName = self.state + + def __gotAllToons(self, toons): + self.gotAllToons = 1 + messenger.send('gotAllToons') + + def setBattleIds(self, battleNumber, battleAId, battleBId): + self.battleNumber = battleNumber + self.battleAId = battleAId + self.battleBId = battleBId + self.cr.relatedObjectMgr.abortRequest(self.battleRequest) + self.battleRequest = self.cr.relatedObjectMgr.requestObjects([self.battleAId, self.battleBId], allCallback=self.__gotBattles) + + def __gotBattles(self, battles): + self.battleRequest = None + if self.battleA and self.battleA != battles[0]: + self.battleA.cleanupBattle() + if self.battleB and self.battleB != battles[1]: + self.battleB.cleanupBattle() + self.battleA = battles[0] + self.battleB = battles[1] + return + + def cleanupBattles(self): + if self.battleA: + self.battleA.cleanupBattle() + if self.battleB: + self.battleB.cleanupBattle() + + def makeEndOfBattleMovie(self, hasLocalToon): + return Sequence() + + def controlToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.stopLookAround() + toon.stopSmooth() + + if self.hasLocalToon(): + self.toMovieMode() + + def enableLocalToonSimpleCollisions(self): + if not self.toonSphere: + sphere = CollisionSphere(0, 0, 1, 1) + sphere.setRespectEffectiveNormal(0) + sphereNode = CollisionNode('SimpleCollisions') + sphereNode.setFromCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) + sphereNode.setIntoCollideMask(BitMask32.allOff()) + sphereNode.addSolid(sphere) + self.toonSphere = NodePath(sphereNode) + self.toonSphereHandler = CollisionHandlerPusher() + self.toonSphereHandler.addCollider(self.toonSphere, localAvatar) + self.toonSphere.reparentTo(localAvatar) + base.cTrav.addCollider(self.toonSphere, self.toonSphereHandler) + + def disableLocalToonSimpleCollisions(self): + if self.toonSphere: + base.cTrav.removeCollider(self.toonSphere) + self.toonSphere.detachNode() + + def toOuchMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('ouch') + + def toCraneMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('crane') + + def toMovieMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('movie') + + def toWalkMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('walk') + + def toFinalBattleMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('finalBattle') + + def releaseToons(self, finalBattle = 0): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + if self.battleA and toon in self.battleA.toons: + pass + elif self.battleB and toon in self.battleB.toons: + pass + else: + toon.startLookAround() + toon.startSmooth() + toon.wrtReparentTo(render) + if toon == localAvatar: + if finalBattle: + self.toFinalBattleMode() + else: + self.toWalkMode() + + def stickToonsToFloor(self): + self.unstickToons() + rayNode = CollisionNode('stickToonsToFloor') + rayNode.addSolid(CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0)) + rayNode.setFromCollideMask(ToontownGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + ray = NodePath(rayNode) + lifter = CollisionHandlerFloor() + lifter.setOffset(ToontownGlobals.FloorOffset) + lifter.setReach(10.0) + for toonId in self.involvedToons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonRay = ray.instanceTo(toon) + lifter.addCollider(toonRay, toon) + base.cTrav.addCollider(toonRay, lifter) + self.__toonsStuckToFloor.append(toonRay) + + def unstickToons(self): + for toonRay in self.__toonsStuckToFloor: + base.cTrav.removeCollider(toonRay) + toonRay.removeNode() + + self.__toonsStuckToFloor = [] + + def stickBossToFloor(self): + self.unstickBoss() + self.ray1 = CollisionRay(0.0, 10.0, 20.0, 0.0, 0.0, -1.0) + self.ray2 = CollisionRay(0.0, 0.0, 20.0, 0.0, 0.0, -1.0) + self.ray3 = CollisionRay(0.0, -10.0, 20.0, 0.0, 0.0, -1.0) + rayNode = CollisionNode('stickBossToFloor') + rayNode.addSolid(self.ray1) + rayNode.addSolid(self.ray2) + rayNode.addSolid(self.ray3) + rayNode.setFromCollideMask(ToontownGlobals.FloorBitmask) + rayNode.setIntoCollideMask(BitMask32.allOff()) + self.rays = self.attachNewNode(rayNode) + self.cqueue = CollisionHandlerQueue() + base.cTrav.addCollider(self.rays, self.cqueue) + + def rollBoss(self, t, fromPos, deltaPos): + self.setPos(fromPos + deltaPos * t) + if not self.cqueue: + return + self.cqueue.sortEntries() + numEntries = self.cqueue.getNumEntries() + if numEntries != 0: + for i in range(self.cqueue.getNumEntries() - 1, -1, -1): + entry = self.cqueue.getEntry(i) + solid = entry.getFrom() + if solid == self.ray1: + self.e1 = entry + elif solid == self.ray2: + self.e2 = entry + elif solid == self.ray3: + self.e3 = entry + else: + self.notify.warning('Unexpected ray in __liftBoss') + return + + self.cqueue.clearEntries() + if not (self.e1 and self.e2 and self.e3): + self.notify.debug('Some points missed in __liftBoss') + return + p1 = self.e1.getSurfacePoint(self) + p2 = self.e2.getSurfacePoint(self) + p3 = self.e3.getSurfacePoint(self) + p2a = (p1 + p3) / 2 + if p2a[2] > p2[2]: + center = p2a + else: + center = p2 + self.setZ(self, center[2]) + if p1[2] > p2[2] + 0.01 or p3[2] > p2[2] + 0.01: + mat = Mat4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + if abs(p3[2] - center[2]) < abs(p1[2] - center[2]): + lookAt(mat, Vec3(p1 - center), CSDefault) + else: + lookAt(mat, Vec3(center - p3), CSDefault) + self.rotateNode.setMat(mat) + else: + self.rotateNode.clearTransform() + + def unstickBoss(self): + if self.rays: + base.cTrav.removeCollider(self.rays) + self.rays.removeNode() + self.rays = None + self.ray1 = None + self.ray2 = None + self.ray3 = None + self.e1 = None + self.e2 = None + self.e3 = None + self.rotateNode.clearTransform() + self.cqueue = None + return + + def rollBossToPoint(self, fromPos, fromHpr, toPos, toHpr, reverse): + vector = Vec3(toPos - fromPos) + distance = vector.length() + if toHpr == None: + mat = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0) + headsUp(mat, vector, CSDefault) + scale = VBase3(0, 0, 0) + shear = VBase3(0, 0, 0) + toHpr = VBase3(0, 0, 0) + decomposeMatrix(mat, scale, shear, toHpr, CSDefault) + if fromHpr: + newH = PythonUtil.fitDestAngle2Src(fromHpr[0], toHpr[0]) + toHpr = VBase3(newH, 0, 0) + else: + fromHpr = toHpr + turnTime = abs(toHpr[0] - fromHpr[0]) / ToontownGlobals.BossCogTurnSpeed + if toHpr[0] < fromHpr[0]: + leftRate = ToontownGlobals.BossCogTreadSpeed + else: + leftRate = -ToontownGlobals.BossCogTreadSpeed + if reverse: + rollTreadRate = -ToontownGlobals.BossCogTreadSpeed + else: + rollTreadRate = ToontownGlobals.BossCogTreadSpeed + rollTime = distance / ToontownGlobals.BossCogRollSpeed + deltaPos = toPos - fromPos + track = Sequence(Func(self.setPos, fromPos), Func(self.headsUp, toPos), Parallel(self.hprInterval(turnTime, toHpr, fromHpr), self.rollLeftTreads(turnTime, leftRate), self.rollRightTreads(turnTime, -leftRate)), Parallel(LerpFunctionInterval(self.rollBoss, duration=rollTime, extraArgs=[fromPos, deltaPos]), self.rollLeftTreads(rollTime, rollTreadRate), self.rollRightTreads(rollTime, rollTreadRate))) + return (track, toHpr) + + def setupElevator(self, elevatorModel): + self.elevatorModel = elevatorModel + 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.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.openDoors = ElevatorUtils.getOpenInterval(self, self.leftDoor, self.rightDoor, self.openSfx, self.finalOpenSfx, self.elevatorType) + self.closeDoors = ElevatorUtils.getCloseInterval(self, self.leftDoor, self.rightDoor, self.closeSfx, self.finalCloseSfx, self.elevatorType) + self.closeDoors.start() + self.closeDoors.finish() + + def putToonInCogSuit(self, toon): + if not toon.isDisguised: + deptIndex = SuitDNA.suitDepts.index(self.style.dept) + toon.setCogIndex(deptIndex) + toon.getGeomNode().hide() + + def placeToonInElevator(self, toon): + self.putToonInCogSuit(toon) + toonIndex = self.involvedToons.index(toon.doId) + toon.reparentTo(self.elevatorModel) + toon.setPos(*ElevatorConstants.BigElevatorPoints[toonIndex]) + toon.setHpr(180, 0, 0) + toon.suit.loop('neutral') + + def toonNormalEyes(self, toons, bArrayOfObjs = False): + if bArrayOfObjs: + toonObjs = toons + else: + toonObjs = [] + for toonId in toons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonObjs.append(toon) + + seq = Sequence() + for toon in toonObjs: + seq.append(Func(toon.normalEyes)) + seq.append(Func(toon.blinkEyes)) + + return seq + + def toonDied(self, avId): + if avId == localAvatar.doId: + self.localToonDied() + + def localToonToSafeZone(self): + target_sz = ZoneUtil.getSafeZoneId(localAvatar.defaultZone) + place = self.cr.playGame.getPlace() + place.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 localToonDied(self): + target_sz = ZoneUtil.getSafeZoneId(localAvatar.defaultZone) + place = self.cr.playGame.getPlace() + place.fsm.request('died', [{'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 toonsToBattlePosition(self, toonIds, battleNode): + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + self.notify.debug('toonsToBattlePosition: points = %s' % points[0][0]) + for i in range(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.reparentTo(render) + pos, h = points[i] + self.notify.debug('toonsToBattlePosition: battleNode=%s %.2f %.2f %.2f %.2f %.2f %.2f' % (battleNode, + pos[0], + pos[1], + pos[2], + h, + 0, + 0)) + self.notify.debug('old toon pos %s' % toon.getPos()) + self.notify.debug('pos=%.2f %.2f %.2f h=%.2f' % (pos[0], + pos[1], + pos[2], + h)) + self.notify.debug('battleNode.pos = %s' % battleNode.getPos()) + self.notify.debug('battleNode.hpr = %s' % battleNode.getHpr()) + toon.setPosHpr(battleNode, pos[0], pos[1], pos[2], h, 0, 0) + self.notify.debug('new toon pos %s ' % toon.getPos()) + + def __touchedBoss(self, entry): + self.notify.debug('%s' % entry) + self.notify.debug('fromPos = %s' % entry.getFromNodePath().getPos(render)) + self.notify.debug('intoPos = %s' % entry.getIntoNodePath().getPos(render)) + attackCodeStr = entry.getIntoNodePath().getNetTag('attackCode') + if attackCodeStr == '': + self.notify.warning('Node %s has no attackCode tag.' % repr(entry.getIntoNodePath())) + return + attackCode = int(attackCodeStr) + if attackCode == ToontownGlobals.BossCogLawyerAttack and self.dna.dept != 'l': + self.notify.warning('got lawyer attack but not in CJ boss battle') + return + self.zapLocalToon(attackCode) + + def zapLocalToon(self, attackCode, origin = None): + if self.localToonIsSafe or localAvatar.ghostMode or localAvatar.isStunned: + return + messenger.send('interrupt-pie') + place = self.cr.playGame.getPlace() + currentState = None + if place: + currentState = place.fsm.getCurrentState().getName() + if currentState != 'walk' and currentState != 'finalBattle' and currentState != 'crane': + return + toon = localAvatar + fling = 1 + shake = 0 + if attackCode == ToontownGlobals.BossCogAreaAttack: + fling = 0 + shake = 1 + if fling: + if origin == None: + origin = self + camera.wrtReparentTo(render) + toon.headsUp(origin) + camera.wrtReparentTo(toon) + bossRelativePos = toon.getPos(self.getGeomNode()) + bp2d = Vec2(bossRelativePos[0], bossRelativePos[1]) + bp2d.normalize() + pos = toon.getPos() + hpr = toon.getHpr() + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('zapToon', [pos[0], + pos[1], + pos[2], + hpr[0] % 360.0, + hpr[1], + hpr[2], + bp2d[0], + bp2d[1], + attackCode, + timestamp]) + self.doZapToon(toon, fling=fling, shake=shake) + return + + def showZapToon(self, toonId, x, y, z, h, p, r, attackCode, timestamp): + if toonId == localAvatar.doId: + return + ts = globalClockDelta.localElapsedTime(timestamp) + pos = Point3(x, y, z) + hpr = VBase3(h, p, r) + fling = 1 + toon = self.cr.doId2do.get(toonId) + if toon: + if attackCode == ToontownGlobals.BossCogAreaAttack: + pos = None + hpr = None + fling = 0 + else: + ts -= toon.smoother.getDelay() + self.doZapToon(toon, pos=pos, hpr=hpr, ts=ts, fling=fling) + return + + def doZapToon(self, toon, pos = None, hpr = None, ts = 0, fling = 1, shake = 1): + zapName = toon.uniqueName('zap') + self.clearInterval(zapName) + zapTrack = Sequence(name=zapName) + if toon == localAvatar: + self.toOuchMode() + messenger.send('interrupt-pie') + self.enableLocalToonSimpleCollisions() + else: + zapTrack.append(Func(toon.stopSmooth)) + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -5, 0)) + + if pos != None and hpr != None: + (zapTrack.append(Func(toon.setPosHpr, pos, hpr)),) + toonTrack = Parallel() + if shake and toon == localAvatar: + toonTrack.append(Sequence(Func(camera.setZ, camera, 1), Wait(0.15), Func(camera.setZ, camera, -2), Wait(0.15), Func(camera.setZ, camera, 1))) + if fling: + toonTrack += [ActorInterval(toon, 'slip-backward'), toon.posInterval(0.5, getSlideToPos, fluid=1)] + else: + toonTrack += [ActorInterval(toon, 'slip-forward')] + zapTrack.append(toonTrack) + if toon == localAvatar: + zapTrack.append(Func(self.disableLocalToonSimpleCollisions)) + currentState = self.state + if currentState == 'BattleThree': + zapTrack.append(Func(self.toFinalBattleMode)) + elif hasattr(self, 'chairs'): + zapTrack.append(Func(self.toFinalBattleMode)) + else: + zapTrack.append(Func(self.toWalkMode)) + else: + zapTrack.append(Func(toon.startSmooth)) + if ts > 0: + startTime = ts + else: + zapTrack = Sequence(Wait(-ts), zapTrack) + startTime = 0 + zapTrack.append(Func(self.clearInterval, zapName)) + zapTrack.delayDelete = DelayDelete.DelayDelete(toon, 'BossCog.doZapToon') + zapTrack.start(startTime) + self.storeInterval(zapTrack, zapName) + return + + def setAttackCode(self, attackCode, avId = 0): + self.attackCode = attackCode + self.attackAvId = avId + if attackCode == ToontownGlobals.BossCogDizzy: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate(None, raised=0, happy=1) + elif attackCode == ToontownGlobals.BossCogDizzyNow: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate('hit', happy=1, now=1) + elif attackCode == ToontownGlobals.BossCogSwatLeft: + self.setDizzy(0) + self.doAnimate('ltSwing', now=1) + elif attackCode == ToontownGlobals.BossCogSwatRight: + self.setDizzy(0) + self.doAnimate('rtSwing', now=1) + elif attackCode == ToontownGlobals.BossCogAreaAttack: + self.setDizzy(0) + self.doAnimate('areaAttack', now=1) + elif attackCode == ToontownGlobals.BossCogFrontAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogRecoverDizzyAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogDirectedAttack or attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + self.setDizzy(0) + self.doDirectedAttack(avId, attackCode) + elif attackCode == ToontownGlobals.BossCogNoAttack: + self.setDizzy(0) + self.doAnimate(None, raised=1) + return + + def cleanupAttacks(self): + pass + + def cleanupFlash(self): + if self.flashInterval: + self.flashInterval.finish() + self.flashInterval = None + return + + def flashRed(self): + self.cleanupFlash() + self.setColorScale(1, 1, 1, 1) + i = Sequence(self.colorScaleInterval(0.1, colorScale=VBase4(1, 0, 0, 1)), self.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + self.flashInterval = i + i.start() + + def flashGreen(self): + self.cleanupFlash() + if not self.isEmpty(): + self.setColorScale(1, 1, 1, 1) + i = Sequence(self.colorScaleInterval(0.1, colorScale=VBase4(0, 1, 0, 1)), self.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + self.flashInterval = i + i.start() + + def getGearFrisbee(self): + return loader.loadModel('phase_9/models/char/gearProp') + + def backupToonsToBattlePosition(self, toonIds, battleNode): + self.notify.debug('backupToonsToBattlePosition:') + ival = Parallel() + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + for i in range(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + pos, h = points[i] + pos = render.getRelativePoint(battleNode, pos) + ival.append(Sequence(Func(toon.setPlayRate, -0.8, 'walk'), Func(toon.loop, 'walk'), toon.posInterval(3, pos), Func(toon.setPlayRate, 1, 'walk'), Func(toon.loop, 'neutral'))) + + return ival + + def loseCogSuits(self, toons, battleNode, camLoc, arrayOfObjs = False): + seq = Sequence() + if not toons: + return seq + self.notify.debug('battleNode=%s camLoc=%s' % (battleNode, camLoc)) + seq.append(Func(camera.setPosHpr, battleNode, *camLoc)) + suitsOff = Parallel() + if arrayOfObjs: + toonArray = toons + else: + toonArray = [] + for toonId in toons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonArray.append(toon) + + for toon in toonArray: + dustCloud = DustCloud.DustCloud() + dustCloud.setPos(0, 2, 3) + dustCloud.setScale(0.5) + dustCloud.setDepthWrite(0) + dustCloud.setBin('fixed', 0) + dustCloud.createTrack() + suitsOff.append(Sequence(Func(dustCloud.reparentTo, toon), Parallel(dustCloud.track, Sequence(Wait(0.3), Func(toon.takeOffSuit), Func(toon.sadEyes), Func(toon.blinkEyes), Func(toon.play, 'slip-backward'), Wait(0.7))), Func(dustCloud.detachNode), Func(dustCloud.destroy))) + + seq.append(suitsOff) + return seq + + def doDirectedAttack(self, avId, attackCode): + toon = base.cr.doId2do.get(avId) + if toon: + gearRoot = self.rotateNode.attachNewNode('gearRoot') + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.2) + gearRoot.headsUp(toon) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(toon) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in range(4): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 5.85, 4.0) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, 50, z), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1)), Func(node.detachNode))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('throw') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(gearRoot.detachNode), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(throwAnim, neutral2Anim))) + self.doAnimate(seq, now=1, raised=1) + + def announceAreaAttack(self): + if not getattr(localAvatar.controlManager.currentControls, 'isAirborne', 0): + self.zapLocalToon(ToontownGlobals.BossCogAreaAttack) + + def loadEnvironment(self): + self.elevatorMusic = base.loadMusic('phase_7/audio/bgm/tt_elevator.mid') + self.stingMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.mid') + self.battleOneMusic = base.loadMusic('phase_3.5/audio/bgm/encntr_general_bg.mid') + self.battleThreeMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.mid') + self.epilogueMusic = base.loadMusic('phase_9/audio/bgm/encntr_hall_of_fame.mid') + + def unloadEnvironment(self): + pass + + def enterOff(self): + self.cleanupIntervals() + self.hide() + self.clearChat() + self.toWalkMode() + + def exitOff(self): + self.show() + + def enterWaitForToons(self): + self.cleanupIntervals() + self.hide() + if self.gotAllToons: + self.__doneWaitForToons() + else: + self.accept('gotAllToons', self.__doneWaitForToons) + self.transitions = Transitions.Transitions(loader) + self.transitions.IrisModelName = 'phase_3/models/misc/iris' + self.transitions.FadeModelName = 'phase_3/models/misc/fade' + self.transitions.fadeScreen(alpha=1) + NametagGlobals.setMasterArrowsOn(0) + + def __doneWaitForToons(self): + self.doneBarrier('WaitForToons') + + def exitWaitForToons(self): + self.show() + self.transitions.noFade() + del self.transitions + NametagGlobals.setMasterArrowsOn(1) + + def enterElevator(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.stopLookAround() + toon.stopSmooth() + self.placeToonInElevator(toon) + + self.toMovieMode() + camera.reparentTo(self.elevatorModel) + camera.setPosHpr(0, 30, 8, 180, 0, 0) + base.playMusic(self.elevatorMusic, looping=1, volume=1.0) + ival = Sequence(ElevatorUtils.getRideElevatorInterval(self.elevatorType), ElevatorUtils.getRideElevatorInterval(self.elevatorType), self.openDoors, Func(camera.wrtReparentTo, render), Func(self.__doneElevator)) + intervalName = 'ElevatorMovie' + ival.start() + self.storeInterval(ival, intervalName) + + def __doneElevator(self): + self.doneBarrier('Elevator') + + def exitElevator(self): + intervalName = 'ElevatorMovie' + self.clearInterval(intervalName) + self.elevatorMusic.stop() + ElevatorUtils.closeDoors(self.leftDoor, self.rightDoor, self.elevatorType) + + def enterIntroduction(self): + self.controlToons() + ElevatorUtils.openDoors(self.leftDoor, self.rightDoor, self.elevatorType) + NametagGlobals.setMasterArrowsOn(0) + intervalName = 'IntroductionMovie' + delayDeletes = [] + seq = Sequence(self.makeIntroductionMovie(delayDeletes), Func(self.__beginBattleOne), name=intervalName) + seq.delayDeletes = delayDeletes + seq.start() + self.storeInterval(seq, intervalName) + + def __beginBattleOne(self): + intervalName = 'IntroductionMovie' + self.clearInterval(intervalName) + self.doneBarrier('Introduction') + + def exitIntroduction(self): + self.notify.debug('DistributedBossCog.exitIntroduction:') + intervalName = 'IntroductionMovie' + self.clearInterval(intervalName) + self.unstickToons() + self.releaseToons() + NametagGlobals.setMasterArrowsOn(1) + ElevatorUtils.closeDoors(self.leftDoor, self.rightDoor, self.elevatorType) + + def enterBattleOne(self): + self.cleanupIntervals() + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(1) + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + self.releaseToons() + base.playMusic(self.battleOneMusic, looping=1, volume=0.9) + + def exitBattleOne(self): + self.cleanupBattles() + self.battleOneMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + + def enterBattleThree(self): + self.cleanupIntervals() + self.releaseToons(finalBattle=1) + self.accept('clickedNametag', self.__clickedNameTag) + self.accept('friendAvatar', self.__handleFriendAvatar) + self.accept('avatarDetails', self.__handleAvatarDetails) + NametagGlobals.setMasterArrowsOn(0) + NametagGlobals.setMasterNametagsActive(1) + + def exitBattleThree(self): + self.ignore('clickedNameTag') + self.ignore('friendAvatar') + self.ignore('avatarDetails') + self.cleanupIntervals() + + def __clickedNameTag(self, avatar): + self.notify.debug('__clickedNameTag') + if not (self.state == 'BattleThree' or self.state == 'BattleFour'): + return + if not self.allowClickedNameTag: + return + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + FriendsListManager.FriendsListManager._FriendsListManager__handleClickedNametag(place, avatar) + + def __handleFriendAvatar(self, avId, avName, avDisableName): + self.notify.debug('__handleFriendAvatar') + if not (self.state == 'BattleThree' or self.state == 'BattleFour'): + return + if not self.allowClickedNameTag: + return + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + FriendsListManager.FriendsListManager._FriendsListManager__handleFriendAvatar(place, avId, avName, avDisableName) + + def __handleAvatarDetails(self, avId, avName, playerId = None): + self.notify.debug('__handleAvatarDetails') + if not (self.state == 'BattleThree' or self.state == 'BattleFour'): + return + if not self.allowClickedNameTag: + return + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + FriendsListManager.FriendsListManager._FriendsListManager__handleAvatarDetails(place, avId, avName, playerId) + + def enterBattleFour(self): + self.cleanupIntervals() + self.releaseToons(finalBattle=1) + self.accept('clickedNametag', self.__clickedNameTag) + self.accept('friendAvatar', self.__handleFriendAvatar) + self.accept('avatarDetails', self.__handleAvatarDetails) + NametagGlobals.setMasterArrowsOn(0) + NametagGlobals.setMasterNametagsActive(1) + + def exitBattleFour(self): + self.ignore('clickedNameTag') + self.ignore('friendAvatar') + self.ignore('avatarDetails') + self.cleanupIntervals() + + def enterFrolic(self): + self.cleanupIntervals() + self.clearChat() + self.reparentTo(render) + self.stopAnimate() + self.pose('Ff_neutral', 0) + self.releaseToons() + + def exitFrolic(self): + pass + + def setToonsToNeutral(self, toonIds): + for i in range(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + if toon.isDisguised: + toon.suit.loop('neutral') + toon.loop('neutral') + + def wearCogSuits(self, toons, battleNode, camLoc, arrayOfObjs = False, waiter = False): + seq = Sequence() + if not toons: + return seq + self.notify.debug('battleNode=%s camLoc=%s' % (battleNode, camLoc)) + if camLoc: + seq.append(Func(camera.setPosHpr, battleNode, *camLoc)) + suitsOff = Parallel() + if arrayOfObjs: + toonArray = toons + else: + toonArray = [] + for toonId in toons: + toon = base.cr.doId2do.get(toonId) + if toon: + toonArray.append(toon) + + for toon in toonArray: + dustCloud = DustCloud.DustCloud() + dustCloud.setPos(0, 2, 3) + dustCloud.setScale(0.5) + dustCloud.setDepthWrite(0) + dustCloud.setBin('fixed', 0) + dustCloud.createTrack() + makeWaiter = Sequence() + if waiter: + makeWaiter = Func(toon.makeWaiter) + suitsOff.append(Sequence(Func(dustCloud.reparentTo, toon), Parallel(dustCloud.track, Sequence(Wait(0.3), Func(self.putToonInCogSuit, toon), makeWaiter, Wait(0.7))), Func(dustCloud.detachNode))) + + seq.append(suitsOff) + return seq diff --git a/toontown/suit/DistributedBossCogAI.py b/toontown/suit/DistributedBossCogAI.py new file mode 100644 index 0000000..01bffa4 --- /dev/null +++ b/toontown/suit/DistributedBossCogAI.py @@ -0,0 +1,686 @@ +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import DistributedAvatarAI +from toontown.battle import BattleExperienceAI +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toon import InventoryBase +from toontown.battle import DistributedBattleFinalAI +from toontown.building import SuitPlannerInteriorAI +from toontown.battle import BattleBase +from toontown.coghq import CogDisguiseGlobals +from pandac.PandaModules import * +import SuitDNA, random +AllBossCogs = [] + +class DistributedBossCogAI(DistributedAvatarAI.DistributedAvatarAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossCogAI') + + def __init__(self, air, dept): + DistributedAvatarAI.DistributedAvatarAI.__init__(self, air) + self.dept = dept + self.dna = SuitDNA.SuitDNA() + self.dna.newBossCog(self.dept) + self.deptIndex = SuitDNA.suitDepts.index(self.dept) + self.resetBattleCounters() + self.looseToons = [] + self.involvedToons = [] + self.toonsA = [] + self.toonsB = [] + self.nearToons = [] + self.suitsA = [] + self.activeSuitsA = [] + self.suitsB = [] + self.activeSuitsB = [] + self.reserveSuits = [] + self.barrier = None + self.keyStates = [ + 'BattleOne', 'BattleTwo', 'BattleThree', 'Victory'] + self.bossDamage = 0 + self.battleThreeStart = 0 + self.battleThreeDuration = 1800 + self.attackCode = None + self.attackAvId = 0 + self.hitCount = 0 + self.nerfed = False + self.numRentalDiguises = 0 + self.numNormalDiguises = 0 + AllBossCogs.append(self) + return + + def generateWithRequired(self, zoneId): + self.numRentalDiguises, self.numNormalDiguises = self.countDisguises() + DistributedAvatarAI.DistributedAvatarAI.generateWithRequired(self, zoneId) + + def delete(self): + self.ignoreAll() + if self in AllBossCogs: + i = AllBossCogs.index(self) + del AllBossCogs[i] + return DistributedAvatarAI.DistributedAvatarAI.delete(self) + + def getDNAString(self): + return self.dna.makeNetString() + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + self.addToon(avId) + + def avatarExit(self): + avId = self.air.getAvatarIdFromSender() + self.removeToon(avId) + + def avatarNearEnter(self): + avId = self.air.getAvatarIdFromSender() + if avId not in self.nearToons: + self.nearToons.append(avId) + + def avatarNearExit(self): + avId = self.air.getAvatarIdFromSender() + try: + self.nearToons.remove(avId) + except: + pass + + def __handleUnexpectedExit(self, avId): + self.removeToon(avId) + + def addToon(self, avId): + if avId not in self.looseToons and avId not in self.involvedToons: + self.looseToons.append(avId) + event = self.air.getAvatarExitEvent(avId) + self.acceptOnce(event, self.__handleUnexpectedExit, extraArgs=[avId]) + + def removeToon(self, avId): + resendIds = 0 + try: + self.looseToons.remove(avId) + except: + pass + + try: + self.involvedToons.remove(avId) + resendIds = 1 + except: + pass + + try: + self.toonsA.remove(avId) + except: + pass + + try: + self.toonsB.remove(avId) + except: + pass + + try: + self.nearToons.remove(avId) + except: + pass + + event = self.air.getAvatarExitEvent(avId) + self.ignore(event) + if not self.hasToons(): + taskMgr.doMethodLater(10, self.__bossDone, self.uniqueName('BossDone')) + + def __bossDone(self, task): + if self.air: + self.air.writeServerEvent('bossBattleDone', self.doId, '%s' % self.dept) + self.b_setState('Off') + messenger.send(self.uniqueName('BossDone')) + self.ignoreAll() + + def hasToons(self): + return self.looseToons or self.involvedToons + + def hasToonsAlive(self): + alive = 0 + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + hp = toon.getHp() + if hp > 0: + alive = 1 + + return alive + + def isToonKnown(self, toonId): + return toonId in self.involvedToons or toonId in self.looseToons + + def isToonWearingRentalSuit(self, toonId): + if not self.isToonKnown(toonId): + self.notify.warning('isToonWearingRentalSuit: unknown toonId %s' % toonId) + return False + toon = self.air.doId2do.get(toonId) + if toon: + if hasattr(toon, 'forceRentalDisguise') and toon.forceRentalDisguise: + return True + else: + return not CogDisguiseGlobals.isPaidSuitComplete(toon, toon.getCogParts(), self.dept) + else: + self.notify.warning('isToonWearingRentalSuit: toonId %s does not exist' % toonId) + return False + + def __countNormalDisguiseToons(self): + return len(self.involvedToons) + len(self.looseToons) - self.__countRentalDisguiseToons() + + def __countRentalDisguiseToons(self): + count = 0 + for toonId in self.involvedToons + self.looseToons: + if self.isToonWearingRentalSuit(toonId): + count += 1 + + return count + + def countDisguises(self): + rentals = self.__countRentalDisguiseToons() + normals = self.__countNormalDisguiseToons() + return ( + rentals, normals) + + def sendBattleIds(self): + self.sendUpdate('setBattleIds', [self.battleNumber, self.battleAId, self.battleBId]) + + def sendToonIds(self): + self.sendUpdate('setToonIds', [self.involvedToons, self.toonsA, self.toonsB]) + + def damageToon(self, toon, deduction): + toon.takeDamage(deduction) + if toon.getHp() <= 0: + self.sendUpdate('toonDied', [toon.doId]) + empty = InventoryBase.InventoryBase(toon) + toon.b_setInventory(empty.makeNetString()) + self.removeToon(toon.doId) + + def healToon(self, toon, increment): + toon.toonUp(increment) + + def d_setBattleExperience(self): + self.sendUpdate('setBattleExperience', self.getBattleExperience()) + + def getBattleExperience(self): + result = BattleExperienceAI.getBattleExperience(8, self.involvedToons, self.toonExp, self.toonSkillPtsGained, self.toonOrigQuests, self.toonItems, self.toonOrigMerits, self.toonMerits, self.toonParts, self.suitsKilled, self.helpfulToons) + return result + + def b_setArenaSide(self, arenaSide): + self.setArenaSide(arenaSide) + self.d_setArenaSide(arenaSide) + + def setArenaSide(self, arenaSide): + self.arenaSide = arenaSide + + def d_setArenaSide(self, arenaSide): + self.sendUpdate('setArenaSide', [arenaSide]) + + def b_setState(self, state): + self.setState(state) + self.d_setState(state) + + def d_setState(self, state): + self.sendUpdate('setState', [state]) + + def setState(self, state): + self.demand(state) + if self.air: + if state in self.keyStates: + self.air.writeServerEvent('bossBattle', self.doId, '%s|%s|%s|%s|%s|%s' % (self.dept, state, self.involvedToons, self.formatReward(), self.formatLaffLevels(), self.formatSuitType())) + + def getState(self): + return self.state + + def formatReward(self): + return 'unspecified' + + def formatLaffLevels(self): + try: + return map(lambda id: simbase.air.doId2do.get(id).getMaxHp(), self.involvedToons) + except Exception, e: + self.notify.warning(e) + return [] + + def formatSuitType(self): + try: + + def hasSuit(id): + if not self.isToonWearingRentalSuit(id): + return 1 + else: + return 0 + + return map(hasSuit, self.involvedToons) + except Exception, e: + self.notify.warning(e) + return [] + + def enterOff(self): + self.resetBattles() + self.resetToons() + self.resetBattleCounters() + + def exitOff(self): + pass + + def enterWaitForToons(self): + self.acceptNewToons() + self.barrier = self.beginBarrier('WaitForToons', self.involvedToons, 5, self.__doneWaitForToons) + + def __doneWaitForToons(self, toons): + self.b_setState('Elevator') + + def exitWaitForToons(self): + self.ignoreBarrier(self.barrier) + + def enterElevator(self): + if self.notify.getDebug(): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + self.notify.debug('%s. involved toon %s, %s/%s' % (self.doId, toonId, toon.getHp(), toon.getMaxHp())) + + self.resetBattles() + self.barrier = self.beginBarrier('Elevator', self.involvedToons, 30, self.__doneElevator) + + def __doneElevator(self, avIds): + self.b_setState('Introduction') + + def exitElevator(self): + self.ignoreBarrier(self.barrier) + + def enterIntroduction(self): + self.resetBattles() + self.arenaSide = None + self.makeBattleOneBattles() + self.barrier = self.beginBarrier('Introduction', self.involvedToons, 45, self.doneIntroduction) + return + + def doneIntroduction(self, avIds): + self.b_setState('BattleOne') + + def exitIntroduction(self): + self.ignoreBarrier(self.barrier) + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setCogIndex(-1) + + def enterBattleOne(self): + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def exitBattleOne(self): + self.resetBattles() + + def enterReward(self): + self.resetBattles() + self.barrier = self.beginBarrier('Reward', self.involvedToons, BattleBase.BUILDING_REWARD_TIMEOUT, self.__doneReward) + + def __doneReward(self, avIds): + self.b_setState('Epilogue') + + def exitReward(self): + pass + + def enterEpilogue(self): + pass + + def exitEpilogue(self): + pass + + def enterFrolic(self): + self.resetBattles() + + def exitFrolic(self): + pass + + def resetBattleCounters(self): + self.battleNumber = 0 + self.battleA = None + self.battleAId = 0 + self.battleB = None + self.battleBId = 0 + self.arenaSide = None + self.toonSkillPtsGained = {} + self.toonExp = {} + self.toonOrigQuests = {} + self.toonItems = {} + self.toonOrigMerits = {} + self.toonMerits = {} + self.toonParts = {} + self.suitsKilled = [] + self.helpfulToons = [] + return + + def resetBattles(self): + sendReset = 0 + if self.battleA: + self.battleA.requestDelete() + self.battleA = None + self.battleAId = 0 + sendReset = 1 + if self.battleB: + self.battleB.requestDelete() + self.battleB = None + self.battleBId = 0 + sendReset = 1 + for suit in self.suitsA + self.suitsB: + suit.requestDelete() + + for suit, joinChance in self.reserveSuits: + suit.requestDelete() + + self.suitsA = [] + self.activeSuitsA = [] + self.suitsB = [] + self.activeSuitsB = [] + self.reserveSuits = [] + self.battleNumber = 0 + if sendReset: + self.sendBattleIds() + return + + def resetToons(self): + if self.toonsA or self.toonsB: + self.looseToons = self.looseToons + self.involvedToons + self.involvedToons = [] + self.toonsA = [] + self.toonsB = [] + self.sendToonIds() + + def divideToons(self): + if self.nerfed: + splitMethod = self.__balancedDivide + else: + splitMethod = self.__randomDivide + self.toonsA, self.toonsB, loose = splitMethod() + self.looseToons += loose + self.sendToonIds() + + def __randomDivide(self): + toons = self.involvedToons[:] + random.shuffle(toons) + numToons = min(len(toons), 8) + if numToons < 4: + numToonsB = numToons / 2 + else: + numToonsB = (numToons + random.choice([0, 1])) / 2 + teamA = toons[numToonsB:numToons] + teamB = toons[:numToonsB] + loose = toons[numToons:] + return ( + teamA, teamB, loose) + + def __balancedDivide(self): + toons = self.involvedToons[:] + random.shuffle(toons) + teamA, teamB, loose = [], [], [] + for i, toon in enumerate(sorted(toons, key=self.isToonWearingRentalSuit)): + if i < 8: + if i % 2 == 0: + teamA.append(toon) + else: + teamB.append(toon) + else: + loose.append(toon) + + return ( + teamA, teamB, loose) + + def acceptNewToons(self): + sourceToons = self.looseToons + self.looseToons = [] + for toonId in sourceToons: + toon = self.air.doId2do.get(toonId) + if toon and not toon.ghostMode: + self.involvedToons.append(toonId) + else: + self.looseToons.append(toonId) + + for avId in self.involvedToons: + toon = self.air.doId2do.get(avId) + if toon: + p = [] + for t in ToontownBattleGlobals.Tracks: + p.append(toon.experience.getExp(t)) + + self.toonExp[avId] = p + self.toonOrigMerits[avId] = toon.cogMerits[:] + + self.divideToons() + + def initializeBattles(self, battleNumber, bossCogPosHpr): + self.resetBattles() + if not self.involvedToons: + self.notify.warning('initializeBattles: no toons!') + return + self.battleNumber = battleNumber + suitHandles = self.generateSuits(battleNumber) + self.suitsA = suitHandles['activeSuits'] + self.activeSuitsA = self.suitsA[:] + self.reserveSuits = suitHandles['reserveSuits'] + suitHandles = self.generateSuits(battleNumber) + self.suitsB = suitHandles['activeSuits'] + self.activeSuitsB = self.suitsB[:] + self.reserveSuits += suitHandles['reserveSuits'] + if self.toonsA: + self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.BossCogBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0) + self.battleAId = self.battleA.doId + else: + self.moveSuits(self.activeSuitsA) + self.suitsA = [] + self.activeSuitsA = [] + if self.arenaSide == None: + self.b_setArenaSide(0) + if self.toonsB: + self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.BossCogBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1) + self.battleBId = self.battleB.doId + else: + self.moveSuits(self.activeSuitsB) + self.suitsB = [] + self.activeSuitsB = [] + if self.arenaSide == None: + self.b_setArenaSide(1) + self.sendBattleIds() + return + + def makeBattle(self, bossCogPosHpr, battlePosHpr, roundCallback, finishCallback, battleNumber, battleSide): + battle = DistributedBattleFinalAI.DistributedBattleFinalAI(self.air, self, roundCallback, finishCallback, battleSide) + self.setBattlePos(battle, bossCogPosHpr, battlePosHpr) + battle.suitsKilled = self.suitsKilled + 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 + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(battleNumber) + battle.battleCalc.setSkillCreditMultiplier(mult) + battle.generateWithRequired(self.zoneId) + return battle + + def setBattlePos(self, battle, cogPosHpr, battlePosHpr): + bossNode = NodePath('bossNode') + bossNode.setPosHpr(*cogPosHpr) + battleNode = bossNode.attachNewNode('battleNode') + battleNode.setPosHpr(*battlePosHpr) + suitNode = battleNode.attachNewNode('suitNode') + suitNode.setPos(0, 1, 0) + battle.pos = battleNode.getPos(NodePath()) + battle.initialSuitPos = suitNode.getPos(NodePath()) + + def moveSuits(self, active): + for suit in active: + self.reserveSuits.append((suit, 0)) + + def handleRoundADone(self, toonIds, totalHp, deadSuits): + if self.battleA: + self.handleRoundDone(self.battleA, self.suitsA, self.activeSuitsA, toonIds, totalHp, deadSuits) + + def handleRoundBDone(self, toonIds, totalHp, deadSuits): + if self.battleB: + self.handleRoundDone(self.battleB, self.suitsB, self.activeSuitsB, toonIds, totalHp, deadSuits) + + def handleBattleADone(self, zoneId, toonIds): + if self.battleA: + self.battleA.requestDelete() + self.battleA = None + self.battleAId = 0 + self.sendBattleIds() + if self.arenaSide == None: + self.b_setArenaSide(0) + if not self.battleB and self.hasToons() and self.hasToonsAlive(): + self.b_setState(self.postBattleState) + return + + def handleBattleBDone(self, zoneId, toonIds): + if self.battleB: + self.battleB.requestDelete() + self.battleB = None + self.battleBId = 0 + self.sendBattleIds() + if self.arenaSide == None: + self.b_setArenaSide(1) + if not self.battleA and self.hasToons() and self.hasToonsAlive(): + self.b_setState(self.postBattleState) + return + + def invokeSuitPlanner(self, buildingCode, skelecog): + planner = SuitPlannerInteriorAI.SuitPlannerInteriorAI(1, buildingCode, self.dna.dept, self.zoneId) + planner.respectInvasions = 0 + suits = planner.genFloorSuits(0) + if skelecog: + for suit in suits['activeSuits']: + suit.b_setSkelecog(1) + + for reserve in suits['reserveSuits']: + suit = reserve[0] + suit.b_setSkelecog(1) + + return suits + + def generateSuits(self, battleNumber): + raise StandardError, 'generateSuits unimplemented' + + def handleRoundDone(self, battle, suits, activeSuits, toonIds, totalHp, deadSuits): + totalMaxHp = 0 + for suit in suits: + totalMaxHp += suit.maxHP + + for suit in deadSuits: + activeSuits.remove(suit) + + joinedReserves = [] + if len(self.reserveSuits) > 0 and len(activeSuits) < 4: + hpPercent = 100 - totalHp / totalMaxHp * 100.0 + for info in self.reserveSuits: + if info[1] <= hpPercent and len(activeSuits) < 4: + suits.append(info[0]) + activeSuits.append(info[0]) + joinedReserves.append(info) + + for info in joinedReserves: + self.reserveSuits.remove(info) + + battle.resume(joinedReserves) + + def getBattleThreeTime(self): + elapsed = globalClock.getFrameTime() - self.battleThreeStart + t1 = elapsed / float(self.battleThreeDuration) + return t1 + + def progressValue(self, fromValue, toValue): + t0 = float(self.bossDamage) / float(self.bossMaxDamage) + elapsed = globalClock.getFrameTime() - self.battleThreeStart + t1 = elapsed / float(self.battleThreeDuration) + t = max(t0, t1) + return fromValue + (toValue - fromValue) * min(t, 1) + + def progressRandomValue(self, fromValue, toValue, radius=0.2): + t = self.progressValue(0, 1) + radius = radius * (1.0 - abs(t - 0.5) * 2.0) + t += radius * random.uniform(-1, 1) + t = max(min(t, 1.0), 0.0) + return fromValue + (toValue - fromValue) * t + + def reportToonHealth(self): + if self.notify.getDebug(): + str = '' + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + str += ', %s (%s/%s)' % (toonId, toon.getHp(), toon.getMaxHp()) + + self.notify.debug('%s.toons = %s' % (self.doId, str[2:])) + + def getDamageMultiplier(self): + return 1.0 + + def zapToon(self, x, y, z, h, p, r, bpx, bpy, attackCode, timestamp): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'zapToon from unknown avatar'): + return + if attackCode == ToontownGlobals.BossCogLawyerAttack and self.dna.dept != 'l': + self.notify.warning('got lawyer attack but not in CJ boss battle') + return + toon = simbase.air.doId2do.get(avId) + if toon: + self.d_showZapToon(avId, x, y, z, h, p, r, attackCode, timestamp) + damage = ToontownGlobals.BossCogDamageLevels.get(attackCode) + if damage == None: + self.notify.warning('No damage listed for attack code %s' % attackCode) + damage = 5 + damage *= self.getDamageMultiplier() + damage = max(int(damage), 1) + self.damageToon(toon, damage) + currState = self.getCurrentOrNextState() + if attackCode == ToontownGlobals.BossCogElectricFence and (currState == 'RollToBattleTwo' or currState == 'BattleThree'): + if bpy < 0 and abs(bpx / bpy) > 0.5: + if bpx < 0: + self.b_setAttackCode(ToontownGlobals.BossCogSwatRight) + else: + self.b_setAttackCode(ToontownGlobals.BossCogSwatLeft) + return + + def d_showZapToon(self, avId, x, y, z, h, p, r, attackCode, timestamp): + self.sendUpdate('showZapToon', [avId, x, y, z, h, p, r, attackCode, timestamp]) + + def b_setAttackCode(self, attackCode, avId=0): + self.d_setAttackCode(attackCode, avId) + self.setAttackCode(attackCode, avId) + + def setAttackCode(self, attackCode, avId=0): + self.attackCode = attackCode + self.attackAvId = avId + if attackCode == ToontownGlobals.BossCogDizzy or attackCode == ToontownGlobals.BossCogDizzyNow: + delayTime = self.progressValue(20, 5) + self.hitCount = 0 + else: + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + delayTime = ToontownGlobals.BossCogAttackTimes.get(attackCode) + delayTime += self.progressValue(10, 0) + else: + delayTime = ToontownGlobals.BossCogAttackTimes.get(attackCode) + if delayTime == None: + return + self.waitForNextAttack(delayTime) + return + + def d_setAttackCode(self, attackCode, avId=0): + self.sendUpdate('setAttackCode', [attackCode, avId]) + + def waitForNextAttack(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextAttack') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextAttack, taskName) + + def stopAttacks(self): + taskName = self.uniqueName('NextAttack') + taskMgr.remove(taskName) + + def doNextAttack(self, task): + self.b_setAttackCode(ToontownGlobals.BossCogNoAttack) diff --git a/toontown/suit/DistributedBossbotBoss.py b/toontown/suit/DistributedBossbotBoss.py new file mode 100644 index 0000000..d9d1189 --- /dev/null +++ b/toontown/suit/DistributedBossbotBoss.py @@ -0,0 +1,1520 @@ +import math +import random +from pandac.PandaModules import NametagGroup, CFSpeech, VBase3, CollisionPlane, CollisionNode, CollisionSphere, CollisionTube, NodePath, Plane, Vec3, Vec2, Point3, BitMask32, CollisionHandlerEvent, TextureStage, VBase4, BoundingSphere +from direct.interval.IntervalGlobal import Sequence, Wait, Func, LerpHprInterval, Parallel, LerpPosInterval, Track, ActorInterval, ParallelEndTogether, LerpFunctionInterval, LerpScaleInterval, LerpPosHprInterval, SoundInterval +from direct.task import Task +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.ClockDelta import globalClockDelta +from direct.showbase import PythonUtil +from direct.task import Task +from toontown.distributed import DelayDelete +from toontown.toonbase import ToontownGlobals +from toontown.suit import DistributedBossCog +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.suit import SuitDNA +from toontown.toon import Toon +from toontown.toon import ToonDNA +from toontown.building import ElevatorConstants +from toontown.toonbase import ToontownTimer +from toontown.toonbase import ToontownBattleGlobals +from toontown.battle import RewardPanel +from toontown.battle import MovieToonVictory +from toontown.coghq import CogDisguiseGlobals +from toontown.suit import Suit +from toontown.suit import SuitDNA +from toontown.effects import DustCloud +OneBossCog = None +TTL = TTLocalizer + +class DistributedBossbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossbotBoss') + BallLaunchOffset = Point3(10.5, 8.5, -5) + + def __init__(self, cr): + self.notify.debug('----- __init___') + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedBossbotBoss') + self.bossDamage = 0 + self.bossMaxDamage = ToontownGlobals.BossbotBossMaxDamage + self.elevatorType = ElevatorConstants.ELEVATOR_BB + self.resistanceToon = None + self.resistanceToonOnstage = 0 + self.battleANode.setPosHpr(*ToontownGlobals.WaiterBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.WaiterBattleBPosHpr) + self.toonFoodStatus = {} + self.belts = [None, None] + self.tables = {} + self.golfSpots = {} + self.servingTimer = None + self.notDeadList = None + self.moveTrack = None + self.speedDamage = 0 + self.maxSpeedDamage = ToontownGlobals.BossbotMaxSpeedDamage + self.speedRecoverRate = 0 + self.speedRecoverStartTime = 0 + self.ballLaunch = None + self.moveTrack = None + self.lastZapLocalTime = 0 + self.numAttacks = 0 + return + + def announceGenerate(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.loadEnvironment() + self.__makeResistanceToon() + localAvatar.chatMgr.chatInputSpeedChat.addCEOMenu() + if OneBossCog != None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + render.setTag('pieCode', str(ToontownGlobals.PieCodeNotBossCog)) + self.setTag('attackCode', str(ToontownGlobals.BossCogGolfAttack)) + target = CollisionTube(0, -2, -2, 0, -1, 9, 4.0) + targetNode = CollisionNode('BossZap') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.targetNodePath = self.pelvis.attachNewNode(targetNode) + self.targetNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + self.axle.getParent().setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + disk = loader.loadModel('phase_9/models/char/bossCog-gearCollide') + disk.find('**/+CollisionNode').setName('BossZap') + disk.reparentTo(self.pelvis) + disk.setZ(0.8) + closeBubble = CollisionSphere(0, 0, 0, 10) + closeBubble.setTangible(0) + closeBubbleNode = CollisionNode('CloseBoss') + closeBubbleNode.setIntoCollideMask(BitMask32(0)) + closeBubbleNode.setFromCollideMask(ToontownGlobals.BanquetTableBitmask) + closeBubbleNode.addSolid(closeBubble) + self.closeBubbleNode = closeBubbleNode + self.closeHandler = CollisionHandlerEvent() + self.closeHandler.addInPattern('closeEnter') + self.closeHandler.addOutPattern('closeExit') + self.closeBubbleNodePath = self.attachNewNode(closeBubbleNode) + (base.cTrav.addCollider(self.closeBubbleNodePath, self.closeHandler),) + self.accept('closeEnter', self.closeEnter) + self.accept('closeExit', self.closeExit) + self.treads = self.find('**/treads') + demotedCeo = Suit.Suit() + demotedCeo.dna = SuitDNA.SuitDNA() + demotedCeo.dna.newSuit('f') + demotedCeo.setDNA(demotedCeo.dna) + demotedCeo.reparentTo(self.geom) + demotedCeo.loop('neutral') + demotedCeo.stash() + self.demotedCeo = demotedCeo + self.bossClub = loader.loadModel('phase_12/models/char/bossbotBoss-golfclub') + overtimeOneClubSequence = Sequence(self.bossClub.colorScaleInterval(0.1, colorScale=VBase4(0, 1, 0, 1)), self.bossClub.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + overtimeTwoClubSequence = Sequence(self.bossClub.colorScaleInterval(0.1, colorScale=VBase4(1, 0, 0, 1)), self.bossClub.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1))) + self.bossClubIntervals = [overtimeOneClubSequence, overtimeTwoClubSequence] + self.rightHandJoint = self.find('**/joint17') + self.setPosHpr(*ToontownGlobals.BossbotBossBattleOnePosHpr) + self.reparentTo(render) + self.toonUpSfx = loader.loadSfx('phase_11/audio/sfx/LB_toonup.mp3') + self.warningSfx = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_grunt.mp3') + self.swingClubSfx = loader.loadSfx('phase_5/audio/sfx/SA_hardball.mp3') + self.moveBossTaskName = 'CEOMoveTask' + return + + def disable(self): + global OneBossCog + self.notify.debug('----- disable') + DistributedBossCog.DistributedBossCog.disable(self) + self.demotedCeo.delete() + base.cTrav.removeCollider(self.closeBubbleNodePath) + taskMgr.remove('RecoverSpeedDamage') + self.request('Off') + self.unloadEnvironment() + self.__cleanupResistanceToon() + if self.servingTimer: + self.servingTimer.destroy() + del self.servingTimer + localAvatar.chatMgr.chatInputSpeedChat.removeCEOMenu() + if OneBossCog == self: + OneBossCog = None + self.promotionMusic.stop() + self.betweenPhaseMusic.stop() + self.phaseTwoMusic.stop() + self.phaseFourMusic.stop() + self.interruptMove() + for ival in self.bossClubIntervals: + ival.finish() + + self.belts = [] + self.tables = {} + self.removeAllTasks() + return + + def loadEnvironment(self): + self.notify.debug('----- loadEnvironment') + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.geom = loader.loadModel('phase_12/models/bossbotHQ/BanquetInterior_1') + self.elevatorEntrance = self.geom.find('**/elevator_origin') + elevatorModel = loader.loadModel('phase_12/models/bossbotHQ/BB_Inside_Elevator') + if not elevatorModel: + elevatorModel = loader.loadModel('phase_12/models/bossbotHQ/BB_Elevator') + elevatorModel.reparentTo(self.elevatorEntrance) + self.setupElevator(elevatorModel) + self.banquetDoor = self.geom.find('**/door3') + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + planeNode = CollisionNode('dropPlane') + planeNode.addSolid(plane) + planeNode.setCollideMask(ToontownGlobals.PieBitmask) + self.geom.attachNewNode(planeNode) + self.geom.reparentTo(render) + self.promotionMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.mid') + self.betweenPhaseMusic = base.loadMusic('phase_9/audio/bgm/encntr_toon_winning.mid') + self.phaseTwoMusic = base.loadMusic('phase_12/audio/bgm/BossBot_CEO_v1.mid') + self.phaseFourMusic = base.loadMusic('phase_12/audio/bgm/BossBot_CEO_v2.mid') + self.pickupFoodSfx = loader.loadSfx('phase_6/audio/sfx/SZ_MM_gliss.mp3') + self.explodeSfx = loader.loadSfx('phase_4/audio/sfx/firework_distance_02.mp3') + + def unloadEnvironment(self): + self.notify.debug('----- unloadEnvironment') + for belt in self.belts: + if belt: + belt.cleanup() + + for spot in self.golfSpots.values(): + if spot: + spot.cleanup() + + self.golfSpots = {} + self.geom.removeNode() + del self.geom + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + + def __makeResistanceToon(self): + if self.resistanceToon: + return + npc = Toon.Toon() + npc.setName(TTLocalizer.BossbotResistanceToonName) + npc.setPickable(0) + npc.setPlayerType(NametagGroup.CCNonPlayer) + dna = ToonDNA.ToonDNA() + dna.newToonRandom(11237, 'm', 1) + dna.head = 'sls' + npc.setDNAString(dna.makeNetString()) + npc.animFSM.request('neutral') + npc.loop('neutral') + self.resistanceToon = npc + self.resistanceToon.setPosHpr(*ToontownGlobals.BossbotRTIntroStartPosHpr) + state = random.getstate() + random.seed(self.doId) + self.resistanceToon.suitType = SuitDNA.getRandomSuitByDept('c') + random.setstate(state) + + def __cleanupResistanceToon(self): + self.__hideResistanceToon() + if self.resistanceToon: + self.resistanceToon.takeOffSuit() + self.resistanceToon.removeActive() + self.resistanceToon.delete() + self.resistanceToon = None + return + + def __showResistanceToon(self, withSuit): + if not self.resistanceToonOnstage: + self.resistanceToon.addActive() + self.resistanceToon.reparentTo(self.geom) + self.resistanceToonOnstage = 1 + if withSuit: + suit = self.resistanceToon.suitType + self.resistanceToon.putOnSuit(suit, False) + else: + self.resistanceToon.takeOffSuit() + + def __hideResistanceToon(self): + if self.resistanceToonOnstage: + self.resistanceToon.removeActive() + self.resistanceToon.detachNode() + self.resistanceToonOnstage = 0 + + def enterElevator(self): + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.resistanceToon.removeActive() + self.__showResistanceToon(True) + self.resistanceToon.suit.loop('neutral') + base.camera.setPos(0, 21, 7) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.BossbotBossBattleOnePosHpr) + self.loop('Ff_neutral') + self.show() + + def enterIntroduction(self): + if not self.resistanceToonOnstage: + self.__showResistanceToon(True) + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + base.playMusic(self.promotionMusic, looping=1, volume=0.9) + + def exitIntroduction(self): + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.promotionMusic.stop() + + def makeIntroductionMovie(self, delayDeletes): + rToon = self.resistanceToon + rToonStartPos = Point3(ToontownGlobals.BossbotRTIntroStartPosHpr[0], ToontownGlobals.BossbotRTIntroStartPosHpr[1], ToontownGlobals.BossbotRTIntroStartPosHpr[2]) + rToonEndPos = rToonStartPos + Point3(40, 0, 0) + elevCamPosHpr = ToontownGlobals.BossbotElevCamPosHpr + closeUpRTCamPos = Point3(elevCamPosHpr[0], elevCamPosHpr[1], elevCamPosHpr[2]) + closeUpRTCamHpr = Point3(elevCamPosHpr[3], elevCamPosHpr[4], elevCamPosHpr[5]) + closeUpRTCamPos.setY(closeUpRTCamPos.getY() + 20) + closeUpRTCamPos.setZ(closeUpRTCamPos.getZ() + -2) + closeUpRTCamHpr = Point3(0, 5, 0) + loseSuitCamPos = Point3(rToonStartPos) + loseSuitCamPos += Point3(0, -5, 4) + loseSuitCamHpr = Point3(180, 0, 0) + waiterCamPos = Point3(rToonStartPos) + waiterCamPos += Point3(-5, -10, 5) + waiterCamHpr = Point3(-30, 0, 0) + track = Sequence(Func(camera.reparentTo, render), Func(camera.setPosHpr, *elevCamPosHpr), Func(rToon.setChatAbsolute, TTL.BossbotRTWelcome, CFSpeech), LerpPosHprInterval(camera, 3, closeUpRTCamPos, closeUpRTCamHpr), Func(rToon.setChatAbsolute, TTL.BossbotRTRemoveSuit, CFSpeech), Wait(3), Func(self.clearChat), self.loseCogSuits(self.toonsA + self.toonsB, render, (loseSuitCamPos[0], + loseSuitCamPos[1], + loseSuitCamPos[2], + loseSuitCamHpr[0], + loseSuitCamHpr[1], + loseSuitCamHpr[2])), self.toonNormalEyes(self.involvedToons), Wait(2), Func(camera.setPosHpr, closeUpRTCamPos, closeUpRTCamHpr), Func(rToon.setChatAbsolute, TTL.BossbotRTFightWaiter, CFSpeech), Wait(1), LerpHprInterval(camera, 2, Point3(-15, 5, 0)), Sequence(Func(rToon.suit.loop, 'walk'), rToon.hprInterval(1, VBase3(270, 0, 0)), rToon.posInterval(2.5, rToonEndPos), Func(rToon.suit.loop, 'neutral')), Wait(3), Func(rToon.clearChat), Func(self.__hideResistanceToon)) + return track + + def enterFrolic(self): + self.notify.debug('----- enterFrolic') + self.setPosHpr(*ToontownGlobals.BossbotBossBattleOnePosHpr) + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.show() + + def enterPrepareBattleTwo(self): + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.takeOffSuit() + + self.__showResistanceToon(True) + self.resistanceToon.setPosHpr(*ToontownGlobals.BossbotRTPreTwoPosHpr) + self.__arrangeToonsAroundResistanceToon() + intervalName = 'PrepareBattleTwoMovie' + delayDeletes = [] + seq = Sequence(self.makePrepareBattleTwoMovie(delayDeletes), Func(self.__onToBattleTwo), name=intervalName) + seq.delayDeletes = delayDeletes + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenPhaseMusic, looping=1, volume=0.9) + + def makePrepareBattleTwoMovie(self, delayDeletes): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'BossbotBoss.makePrepareBattleTwoMovie')) + + rToon = self.resistanceToon + rToonStartPos = Point3(ToontownGlobals.BossbotRTPreTwoPosHpr[0], ToontownGlobals.BossbotRTPreTwoPosHpr[1], ToontownGlobals.BossbotRTPreTwoPosHpr[2]) + rToonEndPos = rToonStartPos + Point3(-40, 0, 0) + bossPos = Point3(ToontownGlobals.BossbotBossPreTwoPosHpr[0], ToontownGlobals.BossbotBossPreTwoPosHpr[1], ToontownGlobals.BossbotBossPreTwoPosHpr[2]) + bossEndPos = Point3(ToontownGlobals.BossbotBossBattleOnePosHpr[0], ToontownGlobals.BossbotBossBattleOnePosHpr[1], ToontownGlobals.BossbotBossBattleOnePosHpr[2]) + tempNode = self.attachNewNode('temp') + tempNode.setPos(0, -40, 18) + + def getCamBossPos(tempNode = tempNode): + return tempNode.getPos(render) + + rNode = rToon.attachNewNode('temp2') + rNode.setPos(-5, 25, 12) + + def getCamRTPos(rNode = rNode): + return rNode.getPos(render) + + track = Sequence( + Func(camera.reparentTo, render), + Func(camera.setPos, rToon, 0, 22, 6), + Func(camera.setHpr, 0, 0, 0), + Func(rToon.setChatAbsolute, TTL.BossbotRTWearWaiter, CFSpeech), + Wait(3.0), + self.wearCogSuits(self.toonsA + self.toonsB, render, None, waiter=True), + Func(rToon.clearChat), + Func(self.setPosHpr, bossPos, Point3(0, 0, 0)), + Parallel(LerpHprInterval(self.banquetDoor, 2, Point3(90, 0, 0)), + LerpPosInterval(camera, 2, getCamBossPos)), + Func(self.setChatAbsolute, TTL.BossbotBossPreTwo1, CFSpeech), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotBossPreTwo2, CFSpeech), + Wait(3.0), + Parallel( + LerpHprInterval(self.banquetDoor, 2, Point3(0, 0, 0)), + LerpPosHprInterval(camera, 2, getCamRTPos, Point3(10, -8, 0))), + Func(self.setPos, bossEndPos), + Func(self.clearChat), + Func(rToon.setChatAbsolute, TTL.BossbotRTServeFood1, CFSpeech), + Wait(3.0), + Func(rToon.setChatAbsolute, TTL.BossbotRTServeFood2, CFSpeech), + Wait(1.0), + LerpHprInterval(self.banquetDoor, 2, Point3(120, 0, 0)), + Sequence( + Func(rToon.suit.loop, 'walk'), + rToon.hprInterval(1, VBase3(90, 0, 0)), + rToon.posInterval(2.5, rToonEndPos), + Func(rToon.suit.loop, 'neutral')), + self.createWalkInInterval(), + Func(self.banquetDoor.setH, 0), + Func(rToon.clearChat), + Func(self.__hideResistanceToon)) + return track + + def createWalkInInterval(self): + retval = Parallel() + delay = 0 + index = 0 + for toonId in self.involvedToons: + toon = base.cr.doId2do.get(toonId) + if not toon: + continue + destPos = Point3(-14 + index * 4, 25, 0) + + def toWalk(toon): + if hasattr(toon, 'suit') and toon.suit: + toon.suit.loop('walk') + + def toNeutral(toon): + if hasattr(toon, 'suit') and toon.suit: + toon.suit.loop('neutral') + + retval.append(Sequence(Wait(delay), Func(toon.wrtReparentTo, render), Func(toWalk, toon), Func(toon.headsUp, 0, 0, 0), LerpPosInterval(toon, 3, Point3(0, 0, 0)), Func(toon.headsUp, destPos), LerpPosInterval(toon, 3, destPos), LerpHprInterval(toon, 1, Point3(0, 0, 0)), Func(toNeutral, toon))) + if toon == base.localAvatar: + retval.append(Sequence(Wait(delay), Func(camera.reparentTo, toon), Func(camera.setPos, toon.cameraPositions[0][0]), Func(camera.setHpr, 0, 0, 0))) + delay += 1.0 + index += 1 + + return retval + + def __onToBattleTwo(self, elapsedTime = 0): + self.doneBarrier('PrepareBattleTwo') + + def exitPrepareBattleTwo(self): + self.clearInterval('PrepareBattleTwoMovie') + self.betweenPhaseMusic.stop() + + def __arrangeToonsAroundResistanceToon(self): + radius = 9 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in range(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 25 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.reparentTo(render) + toon.setPos(self.resistanceToon, x, y, 0) + toon.headsUp(self.resistanceToon) + toon.loop('neutral') + toon.show() + + def enterBattleTwo(self): + self.releaseToons(finalBattle=1) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + self.putToonInCogSuit(toon) + + self.servingTimer = ToontownTimer.ToontownTimer() + self.servingTimer.posInTopRightCorner() + self.servingTimer.countdown(ToontownGlobals.BossbotBossServingDuration) + base.playMusic(self.phaseTwoMusic, looping=1, volume=0.9) + + def exitBattleTwo(self): + if self.servingTimer: + self.servingTimer.destroy() + del self.servingTimer + self.servingTimer = None + for toonId in self.involvedToons: + self.removeFoodFromToon(toonId) + + self.phaseTwoMusic.stop() + return + + def setBelt(self, belt, beltIndex): + if beltIndex < len(self.belts): + self.belts[beltIndex] = belt + + def localToonTouchedBeltFood(self, beltIndex, foodIndex, foodNum): + avId = base.localAvatar.doId + doRequest = False + if avId not in self.toonFoodStatus: + doRequest = True + elif not self.toonFoodStatus[avId]: + doRequest = True + if doRequest: + self.sendUpdate('requestGetFood', [beltIndex, foodIndex, foodNum]) + + def toonGotFood(self, avId, beltIndex, foodIndex, foodNum): + if self.belts[beltIndex]: + self.belts[beltIndex].removeFood(foodIndex) + self.putFoodOnToon(avId, beltIndex, foodNum) + + def putFoodOnToon(self, avId, beltIndex, foodNum): + self.toonFoodStatus[avId] = (beltIndex, foodNum) + av = base.cr.doId2do.get(avId) + if av: + intervalName = self.uniqueName('loadFoodSoundIval-%d' % avId) + seq = SoundInterval(self.pickupFoodSfx, node=av, name=intervalName) + oldSeq = self.activeIntervals.get(intervalName) + if oldSeq: + oldSeq.finish() + seq.start() + self.activeIntervals[intervalName] = seq + foodModel = loader.loadModel('phase_12/models/bossbotHQ/canoffood') + foodModel.setName('cogFood') + foodModel.setScale(ToontownGlobals.BossbotFoodModelScale) + foodModel.reparentTo(av.suit.getRightHand()) + foodModel.setHpr(52.1961, 180.4983, -4.2882) + curAnim = av.suit.getCurrentAnim() + self.notify.debug('curAnim=%s' % curAnim) + if curAnim in ('walk', 'run'): + av.suit.loop('tray-walk') + elif curAnim == 'neutral': + self.notify.debug('looping tray-netural') + av.suit.loop('tray-neutral') + else: + self.notify.warning("don't know what to do with anim=%s" % curAnim) + + def removeFoodFromToon(self, avId): + self.toonFoodStatus[avId] = None + av = base.cr.doId2do.get(avId) + if av: + cogFood = av.find('**/cogFood') + if not cogFood.isEmpty(): + cogFood.removeNode() + return + + def detachFoodFromToon(self, avId): + cogFood = None + self.toonFoodStatus[avId] = None + av = base.cr.doId2do.get(avId) + if av: + cogFood = av.find('**/cogFood') + if not cogFood.isEmpty(): + retval = cogFood + cogFood.wrtReparentTo(render) + curAnim = av.suit.getCurrentAnim() + self.notify.debug('curAnim=%s' % curAnim) + if curAnim == 'tray-walk': + av.suit.loop('run') + elif curAnim == 'tray-neutral': + av.suit.loop('neutral') + else: + self.notify.warning("don't know what to do with anim=%s" % curAnim) + return cogFood + + def setTable(self, table, tableIndex): + self.tables[tableIndex] = table + + def localToonTouchedChair(self, tableIndex, chairIndex): + avId = base.localAvatar.doId + if avId in self.toonFoodStatus and self.toonFoodStatus[avId] != None: + self.sendUpdate('requestServeFood', [tableIndex, chairIndex]) + return + + def toonServeFood(self, avId, tableIndex, chairIndex): + food = self.detachFoodFromToon(avId) + table = self.tables[tableIndex] + table.serveFood(food, chairIndex) + + def enterPrepareBattleThree(self): + self.calcNotDeadList() + self.battleANode.setPosHpr(*ToontownGlobals.DinerBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.DinerBattleBPosHpr) + self.cleanupIntervals() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + self.putToonInCogSuit(toon) + + intervalName = 'PrepareBattleThreeMovie' + seq = Sequence(self.makePrepareBattleThreeMovie(), Func(self.__onToBattleThree), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenPhaseMusic, looping=1, volume=0.9) + + def calcNotDeadList(self): + if not self.notDeadList: + self.notDeadList = [] + for tableIndex in xrange(len(self.tables)): + table = self.tables[tableIndex] + tableInfo = table.getNotDeadInfo() + self.notDeadList += tableInfo + + def exitPrepareBattleThree(self): + self.clearInterval('PrepareBattleThreeMovie') + self.betweenPhaseMusic.stop() + + def __onToBattleThree(self, elapsedTime = 0): + self.doneBarrier('PrepareBattleThree') + + def makePrepareBattleThreeMovie(self): + loseSuitCamAngle = (0, 19, 6, -180, -5, 0) + track = Sequence( + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -45, 5)), + Func(camera.setHpr, Point3(0, 14, 0)), + Func(self.setChatAbsolute, TTL.BossbotPhase3Speech1, CFSpeech), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotPhase3Speech2, CFSpeech), + Wait(3.0), + Func(camera.setPosHpr, base.localAvatar, *loseSuitCamAngle), + Wait(1.0), + self.loseCogSuits(self.toonsA + self.toonsB, base.localAvatar, loseSuitCamAngle), + self.toonNormalEyes(self.involvedToons), + Wait(2), + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -45, 5)), + Func(camera.setHpr, Point3(0, 14, 0)), + Func(self.setChatAbsolute, TTL.BossbotPhase3Speech3, CFSpeech), + Wait(3.0), + Func(self.clearChat)) + return track + + def enterBattleThree(self): + self.cleanupIntervals() + self.calcNotDeadList() + for table in self.tables.values(): + table.setAllDinersToSitNeutral() + + self.battleANode.setPosHpr(*ToontownGlobals.DinerBattleAPosHpr) + self.battleBNode.setPosHpr(*ToontownGlobals.DinerBattleBPosHpr) + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.takeOffSuit() + + mult = 1 + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + self.releaseToons() + base.playMusic(self.battleOneMusic, looping=1, volume=0.9) + + def exitBattleThree(self): + self.cleanupBattles() + self.battleOneMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + + def claimOneChair(self): + chairInfo = None + if self.notDeadList: + chairInfo = self.notDeadList.pop() + return chairInfo + + def enterPrepareBattleFour(self): + self.controlToons() + intervalName = 'PrepareBattleFourMovie' + seq = Sequence(self.makePrepareBattleFourMovie(), Func(self.__onToBattleFour), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.phaseFourMusic, looping=1, volume=0.9) + + def exitPrepareBattleFour(self): + self.clearInterval('PrepareBattleFourMovie') + self.phaseFourMusic.stop() + + def makePrepareBattleFourMovie(self): + rToon = self.resistanceToon + offsetZ = rToon.suit.getHeight() / 2.0 + track = Sequence( + Func(self.__showResistanceToon, True), + Func(rToon.setPos, Point3(0, -5, 0)), + Func(rToon.setHpr, Point3(0, 0, 0)), + Func(camera.reparentTo, rToon), + Func(camera.setPos, Point3(0, 13, 3 + offsetZ)), + Func(camera.setHpr, Point3(-180, 0, 0)), + Func(self.banquetDoor.setH, 90), + Func(rToon.setChatAbsolute, TTL.BossbotRTPhase4Speech1, CFSpeech), + Wait(4.0), + Func(rToon.setChatAbsolute, TTL.BossbotRTPhase4Speech2, CFSpeech), + Wait(4.0), + Func(self.__hideResistanceToon), + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -45, 5)), + Func(camera.setHpr, Point3(0, 14, 0)), + Func(self.setChatAbsolute, TTL.BossbotPhase4Speech1, CFSpeech), + Func(self.banquetDoor.setH, 0), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotPhase4Speech2, CFSpeech), + Func(self.bossClub.setScale, 0.01), + Func(self.bossClub.reparentTo, self.rightHandJoint), + LerpScaleInterval(self.bossClub, 3, Point3(1, 1, 1)), + Func(self.clearChat)) + return track + + def __onToBattleFour(self, elapsedTime = 0): + self.doneBarrier('PrepareBattleFour') + + def enterBattleFour(self): + DistributedBossCog.DistributedBossCog.enterBattleFour(self) + self.releaseToons(finalBattle=1) + self.setToonsToNeutral(self.involvedToons) + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.takeOffSuit() + + self.bossClub.reparentTo(self.rightHandJoint) + self.generateHealthBar() + self.updateHealthBar() + base.playMusic(self.phaseFourMusic, looping=1, volume=0.9) + + def exitBattleFour(self): + DistributedBossCog.DistributedBossCog.exitBattleFour(self) + self.phaseFourMusic.stop() + + def d_hitBoss(self, bossDamage): + self.sendUpdate('hitBoss', [bossDamage]) + + def d_ballHitBoss(self, bossDamage): + self.sendUpdate('ballHitBoss', [bossDamage]) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + if bossDamage > self.bossDamage: + delta = bossDamage - self.bossDamage + self.flashRed() + self.showHpText(-delta, scale=5) + self.bossDamage = bossDamage + self.updateHealthBar() + + def setGolfSpot(self, golfSpot, golfSpotIndex): + self.golfSpots[golfSpotIndex] = golfSpot + + def enterVictory(self): + self.notify.debug('----- enterVictory') + self.cleanupIntervals() + self.cleanupAttacks() + self.doAnimate('Ff_neutral', now=1) + self.stopMoveTask() + if hasattr(self, 'tableIndex'): + table = self.tables[self.tableIndex] + table.tableGroup.hide() + self.loop('neutral') + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.clearChat() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.happy = 1 + self.raised = 1 + self.forward = 1 + intervalName = 'VictoryMovie' + seq = Sequence(self.makeVictoryMovie(), Func(self.__continueVictory), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.phaseFourMusic, looping=1, volume=0.9) + + def __continueVictory(self): + self.notify.debug('----- __continueVictory') + self.stopAnimate() + self.doneBarrier('Victory') + + def exitVictory(self): + self.notify.debug('----- exitVictory') + self.stopAnimate() + self.unstash() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.phaseFourMusic.stop() + + def makeVictoryMovie(self): + self.show() + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1) + dustCloud.reparentTo(self) + dustCloud.setPos(0, -10, 3) + dustCloud.setScale(4) + dustCloud.wrtReparentTo(self.geom) + dustCloud.createTrack(12) + newHpr = self.getHpr() + newHpr.setX(newHpr.getX() + 180) + bossTrack = Sequence( + Func(self.show), + Func(camera.reparentTo, self), + Func(camera.setPos, Point3(0, -35, 25)), + Func(camera.setHpr, Point3(0, -20, 0)), + Func(self.setChatAbsolute, TTL.BossbotRewardSpeech1, CFSpeech), + Wait(3.0), + Func(self.setChatAbsolute, TTL.BossbotRewardSpeech2, CFSpeech), + Wait(2.0), + Func(self.clearChat), + Parallel( + Sequence( + Wait(0.5), + Func(self.demotedCeo.setPos, self.getPos()), + Func(self.demotedCeo.setHpr, newHpr), + Func(self.hide), + Wait(0.5), + Func(self.demotedCeo.reparentTo, self.geom), + Func(self.demotedCeo.unstash)), + Sequence(dustCloud.track)), + Wait(2.0), + Func(dustCloud.destroy)) + return bossTrack + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'BossbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + base.playMusic(self.betweenPhaseMusic, looping=1, volume=0.9) + + def __doneReward(self): + self.notify.debug('----- __doneReward') + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + self.notify.debug('----- exitReward') + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.betweenPhaseMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + self.__showResistanceToon(False) + self.resistanceToon.reparentTo(render) + self.resistanceToon.setPosHpr(*ToontownGlobals.BossbotRTEpiloguePosHpr) + self.resistanceToon.loop('Sit') + self.__arrangeToonsAroundResistanceToonForReward() + camera.reparentTo(render) + camera.setPos(self.resistanceToon, -9, 12, 6) + camera.lookAt(self.resistanceToon, 0, 0, 3) + intervalName = 'EpilogueMovie' + seq = Sequence(self.makeEpilogueMovie(), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.accept('doneChatPage', self.__doneEpilogue) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __doneEpilogue(self, elapsedTime = 0): + self.notify.debug('----- __doneEpilogue') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.notify.debug('----- exitEpilogue') + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.epilogueMusic.stop() + + def makeEpilogueMovie(self): + epSpeech = TTLocalizer.BossbotRTCongratulations + epSpeech = self.__talkAboutPromotion(epSpeech) + bossTrack = Sequence(Func(self.resistanceToon.animFSM.request, 'neutral'), Func(self.resistanceToon.setLocalPageChat, epSpeech, 0)) + return bossTrack + + def __talkAboutPromotion(self, speech): + if self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.BossbotRTLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.BossbotRTHPBoost + else: + speech += TTLocalizer.BossbotRTMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + return speech + + def __arrangeToonsAroundResistanceToonForReward(self): + radius = 7 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in range(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.resistanceToon, x, y, 0) + toon.headsUp(self.resistanceToon) + toon.loop('neutral') + toon.show() + + def doDirectedAttack(self, avId, attackCode): + toon = base.cr.doId2do.get(avId) + if toon: + distance = toon.getDistance(self) + gearRoot = self.rotateNode.attachNewNode('gearRoot-atk%d' % self.numAttacks) + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.2) + gearRoot.headsUp(toon) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(toon) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in range(4): + nodeName = '%s-%s' % (str(i), globalClock.getFrameTime()) + node = gearRoot.attachNewNode(nodeName) + node.hide() + node.setPos(0, 5.85, 4.0) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + h = random.uniform(-720, 720) + if i == 2: + x = 0 + z = 0 + + def detachNode(node): + if not node.isEmpty(): + node.detachNode() + return Task.done + + def detachNodeLater(node = node): + if node.isEmpty(): + return + center = node.node().getBounds().getCenter() + node.node().setBounds(BoundingSphere(center, distance * 1.5)) + node.node().setFinal(1) + self.doMethodLater(0.005, detachNode, 'detach-%s-%s' % (gearRoot.getName(), node.getName()), extraArgs=[node]) + + gearTrack.append(Sequence(Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, distance, z), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1)), Func(detachNodeLater))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('throw') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + + def detachGearRoot(task, gearRoot = gearRoot): + if not gearRoot.isEmpty(): + gearRoot.detachNode() + return task.done + + def detachGearRootLater(gearRoot = gearRoot): + if gearRoot.isEmpty(): + return + self.doMethodLater(0.01, detachGearRoot, 'detach-%s' % gearRoot.getName()) + + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(detachGearRootLater), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(throwAnim, neutral2Anim))) + self.doAnimate(seq, now=1, raised=1) + + def setBattleDifficulty(self, diff): + self.notify.debug('battleDifficulty = %d' % diff) + self.battleDifficulty = diff + + def doMoveAttack(self, tableIndex): + self.tableIndex = tableIndex + table = self.tables[tableIndex] + fromPos = self.getPos() + fromHpr = self.getHpr() + toPos = table.getPos() + foo = render.attachNewNode('foo') + foo.setPos(self.getPos()) + foo.setHpr(self.getHpr()) + foo.lookAt(table.getLocator()) + toHpr = foo.getHpr() + toHpr.setX(toHpr.getX() - 180) + foo.removeNode() + reverse = False + moveTrack, hpr = self.moveBossToPoint(fromPos, fromHpr, toPos, toHpr, reverse) + self.moveTrack = moveTrack + self.moveTrack.start() + self.storeInterval(self.moveTrack, 'moveTrack') + + def interruptMove(self): + if self.moveTrack and self.moveTrack.isPlaying(): + self.moveTrack.pause() + self.stopMoveTask() + + def setAttackCode(self, attackCode, avId = 0): + if self.state != 'BattleFour': + return + self.numAttacks += 1 + self.notify.debug('numAttacks=%d' % self.numAttacks) + self.attackCode = attackCode + self.attackAvId = avId + if attackCode == ToontownGlobals.BossCogMoveAttack: + self.interruptMove() + self.doMoveAttack(avId) + elif attackCode == ToontownGlobals.BossCogGolfAttack: + self.interruptMove() + self.cleanupAttacks() + self.doGolfAttack(avId, attackCode) + elif attackCode == ToontownGlobals.BossCogDizzy: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate(None, raised=0, happy=1) + elif attackCode == ToontownGlobals.BossCogDizzyNow: + self.setDizzy(1) + self.cleanupAttacks() + self.doAnimate('hit', happy=1, now=1) + elif attackCode == ToontownGlobals.BossCogSwatLeft: + self.setDizzy(0) + self.doAnimate('ltSwing', now=1) + elif attackCode == ToontownGlobals.BossCogSwatRight: + self.setDizzy(0) + self.doAnimate('rtSwing', now=1) + elif attackCode == ToontownGlobals.BossCogAreaAttack: + self.setDizzy(0) + self.doAnimate('areaAttack', now=1) + elif attackCode == ToontownGlobals.BossCogFrontAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogRecoverDizzyAttack: + self.setDizzy(0) + self.doAnimate('frontAttack', now=1) + elif attackCode == ToontownGlobals.BossCogDirectedAttack or attackCode == ToontownGlobals.BossCogSlowDirectedAttack or attackCode == ToontownGlobals.BossCogGearDirectedAttack: + self.interruptMove() + self.setDizzy(0) + self.doDirectedAttack(avId, attackCode) + elif attackCode == ToontownGlobals.BossCogGolfAreaAttack: + self.interruptMove() + self.setDizzy(0) + self.doGolfAreaAttack() + elif attackCode == ToontownGlobals.BossCogNoAttack: + self.setDizzy(0) + self.doAnimate(None, raised=1) + elif attackCode == ToontownGlobals.BossCogOvertimeAttack: + self.interruptMove() + self.setDizzy(0) + self.cleanupAttacks() + self.doOvertimeAttack(avId) + return + + def signalAtTable(self): + self.sendUpdate('reachedTable', [self.tableIndex]) + + def closeEnter(self, colEntry): + tableStr = colEntry.getIntoNodePath().getNetTag('tableIndex') + if tableStr: + tableIndex = int(tableStr) + self.sendUpdate('hitTable', [tableIndex]) + + def closeExit(self, colEntry): + tableStr = colEntry.getIntoNodePath().getNetTag('tableIndex') + if tableStr: + tableIndex = int(tableStr) + if self.tableIndex != tableIndex: + self.sendUpdate('awayFromTable', [tableIndex]) + + def setSpeedDamage(self, speedDamage, recoverRate, timestamp): + recoverStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.speedDamage = speedDamage + self.speedRecoverRate = recoverRate + self.speedRecoverStartTime = recoverStartTime + speedFraction = max(1 - speedDamage / self.maxSpeedDamage, 0) + self.treads.setColorScale(1, speedFraction, speedFraction, 1) + taskName = 'RecoverSpeedDamage' + taskMgr.remove(taskName) + if self.speedRecoverRate: + taskMgr.add(self.__recoverSpeedDamage, taskName) + + def getSpeedDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.speedRecoverStartTime + return max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + + def getFractionalSpeedDamage(self): + result = self.getSpeedDamage() / self.maxSpeedDamage + return result + + def __recoverSpeedDamage(self, task): + speedDamage = self.getSpeedDamage() + speedFraction = max(1 - speedDamage / self.maxSpeedDamage, 0) + self.treads.setColorScale(1, speedFraction, speedFraction, 1) + return task.cont + + def moveBossToPoint(self, fromPos, fromHpr, toPos, toHpr, reverse): + vector = Vec3(toPos - fromPos) + distance = vector.length() + self.distanceToTravel = distance + self.notify.debug('self.distanceToTravel = %s' % self.distanceToTravel) + if toHpr == None: + mat = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0) + headsUp(mat, vector, CSDefault) + scale = VBase3(0, 0, 0) + shear = VBase3(0, 0, 0) + toHpr = VBase3(0, 0, 0) + decomposeMatrix(mat, scale, shear, toHpr, CSDefault) + if fromHpr: + newH = PythonUtil.fitDestAngle2Src(fromHpr[0], toHpr[0]) + toHpr = VBase3(newH, 0, 0) + else: + fromHpr = toHpr + turnTime = abs(toHpr[0] - fromHpr[0]) / self.getCurTurnSpeed() + if toHpr[0] < fromHpr[0]: + leftRate = ToontownGlobals.BossCogTreadSpeed + else: + leftRate = -ToontownGlobals.BossCogTreadSpeed + if reverse: + rollTreadRate = -ToontownGlobals.BossCogTreadSpeed + else: + rollTreadRate = ToontownGlobals.BossCogTreadSpeed + rollTime = distance / ToontownGlobals.BossCogRollSpeed + deltaPos = toPos - fromPos + self.toPos = toPos + self.fromPos = fromPos + self.dirVector = self.toPos - self.fromPos + self.dirVector.normalize() + track = Sequence(Func(self.setPos, fromPos), Func(self.headsUp, toPos), Parallel(self.hprInterval(turnTime, toHpr, fromHpr), self.rollLeftTreads(turnTime, leftRate), self.rollRightTreads(turnTime, -leftRate)), Func(self.startMoveTask)) + return (track, toHpr) + + def getCurTurnSpeed(self): + result = ToontownGlobals.BossbotTurnSpeedMax - (ToontownGlobals.BossbotTurnSpeedMax - ToontownGlobals.BossbotTurnSpeedMin) * self.getFractionalSpeedDamage() + return result + + def getCurRollSpeed(self): + result = ToontownGlobals.BossbotRollSpeedMax - (ToontownGlobals.BossbotRollSpeedMax - ToontownGlobals.BossbotRollSpeedMin) * self.getFractionalSpeedDamage() + return result + + def getCurTreadSpeed(self): + result = ToontownGlobals.BossbotTreadSpeedMax - (ToontownGlobals.BossbotTreadSpeedMax - ToontownGlobals.BossbotTreadSpeedMin) * self.getFractionalSpeedDamage() + return result + + def startMoveTask(self): + taskMgr.add(self.moveBossTask, self.moveBossTaskName) + + def stopMoveTask(self): + taskMgr.remove(self.moveBossTaskName) + + def moveBossTask(self, task): + dt = globalClock.getDt() + distanceTravelledThisFrame = dt * self.getCurRollSpeed() + diff = self.toPos - self.getPos() + distanceLeft = diff.length() + + def rollTexMatrix(t, object = object): + object.setTexOffset(TextureStage.getDefault(), t, 0) + + self.treadsLeftPos += dt * self.getCurTreadSpeed() + self.treadsRightPos += dt * self.getCurTreadSpeed() + rollTexMatrix(self.treadsLeftPos, self.treadsLeft) + rollTexMatrix(self.treadsRightPos, self.treadsRight) + if distanceTravelledThisFrame >= distanceLeft: + self.setPos(self.toPos) + self.signalAtTable() + return Task.done + else: + newPos = self.getPos() + self.dirVector * dt * self.getCurRollSpeed() + self.setPos(newPos) + return Task.cont + + def doZapToon(self, toon, pos = None, hpr = None, ts = 0, fling = 1, shake = 1): + zapName = toon.uniqueName('zap') + self.clearInterval(zapName) + zapTrack = Sequence(name=zapName) + if toon == localAvatar: + self.toOuchMode() + messenger.send('interrupt-pie') + self.enableLocalToonSimpleCollisions() + else: + zapTrack.append(Func(toon.stopSmooth)) + + def getSlideToPos(toon = toon): + return render.getRelativePoint(toon, Point3(0, -5, 0)) + + if pos != None and hpr != None: + (zapTrack.append(Func(toon.setPosHpr, pos, hpr)),) + toonTrack = Parallel() + if shake and toon == localAvatar: + toonTrack.append(Sequence(Func(camera.setZ, camera, 1), Wait(0.15), Func(camera.setZ, camera, -2), Wait(0.15), Func(camera.setZ, camera, 1))) + if fling: + if self.isToonRoaming(toon.doId): + toonTrack += [ActorInterval(toon, 'slip-backward')] + toonTrack += [toon.posInterval(0.5, getSlideToPos, fluid=1)] + else: + toonTrack += [ActorInterval(toon, 'slip-forward')] + zapTrack.append(toonTrack) + if toon == localAvatar: + zapTrack.append(Func(self.disableLocalToonSimpleCollisions)) + currentState = self.state + if currentState in ('BattleFour', 'BattleTwo'): + zapTrack.append(Func(self.toFinalBattleMode)) + else: + self.notify.warning('doZapToon going to walkMode, how did this happen?') + zapTrack.append(Func(self.toWalkMode)) + else: + zapTrack.append(Func(toon.startSmooth)) + if ts > 0: + startTime = ts + else: + zapTrack = Sequence(Wait(-ts), zapTrack) + startTime = 0 + zapTrack.append(Func(self.clearInterval, zapName)) + zapTrack.delayDelete = DelayDelete.DelayDelete(toon, 'BossbotBoss.doZapToon') + zapTrack.start(startTime) + self.storeInterval(zapTrack, zapName) + return + + def zapLocalToon(self, attackCode, origin = None): + if self.localToonIsSafe or localAvatar.ghostMode or localAvatar.isStunned: + return + if globalClock.getFrameTime() < self.lastZapLocalTime + 1.0: + return + else: + self.lastZapLocalTime = globalClock.getFrameTime() + self.notify.debug('zapLocalToon frameTime=%s' % globalClock.getFrameTime()) + messenger.send('interrupt-pie') + place = self.cr.playGame.getPlace() + currentState = None + if place: + currentState = place.fsm.getCurrentState().getName() + if currentState != 'walk' and currentState != 'finalBattle' and currentState != 'crane': + return + self.notify.debug('continuing zap') + toon = localAvatar + fling = 1 + shake = 0 + if attackCode == ToontownGlobals.BossCogAreaAttack: + fling = 0 + shake = 1 + if fling: + if origin == None: + origin = self + if self.isToonRoaming(toon.doId): + camera.wrtReparentTo(render) + toon.headsUp(origin) + camera.wrtReparentTo(toon) + bossRelativePos = toon.getPos(self.getGeomNode()) + bp2d = Vec2(bossRelativePos[0], bossRelativePos[1]) + bp2d.normalize() + pos = toon.getPos() + hpr = toon.getHpr() + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('zapToon', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2], + bp2d[0], + bp2d[1], + attackCode, + timestamp]) + self.doZapToon(toon, fling=fling, shake=shake) + return + + def getToonTableIndex(self, toonId): + tableIndex = -1 + for table in self.tables.values(): + if table.avId == toonId: + tableIndex = table.index + break + + return tableIndex + + def getToonGolfSpotIndex(self, toonId): + golfSpotIndex = -1 + for golfSpot in self.golfSpots.values(): + if golfSpot.avId == toonId: + golfSpotIndex = golfSpot.index + break + + return golfSpotIndex + + def isToonOnTable(self, toonId): + result = self.getToonTableIndex(toonId) != -1 + return result + + def isToonOnGolfSpot(self, toonId): + result = self.getToonGolfSpotIndex(toonId) != -1 + return result + + def isToonRoaming(self, toonId): + result = not self.isToonOnTable(toonId) and not self.isToonOnGolfSpot(toonId) + return result + + def getGolfBall(self): + golfRoot = NodePath('golfRoot') + golfBall = loader.loadModel('phase_6/models/golf/golf_ball') + golfBall.setColorScale(0.75, 0.75, 0.75, 0.5) + golfBall.setTransparency(1) + ballScale = 5 + golfBall.setScale(ballScale) + golfBall.reparentTo(golfRoot) + cs = CollisionSphere(0, 0, 0, ballScale * 0.25) + cs.setTangible(0) + cn = CollisionNode('BossZap') + cn.addSolid(cs) + cn.setIntoCollideMask(ToontownGlobals.WallBitmask) + cnp = golfRoot.attachNewNode(cn) + return golfRoot + + def doGolfAttack(self, avId, attackCode): + toon = base.cr.doId2do.get(avId) + if toon: + distance = toon.getDistance(self) + self.notify.debug('distance = %s' % distance) + gearRoot = self.rotateNode.attachNewNode('gearRoot-atk%d' % self.numAttacks) + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGolfBall() + self.ballLaunch = NodePath('') + self.ballLaunch.reparentTo(gearRoot) + self.ballLaunch.setPos(self.BallLaunchOffset) + gearRoot.headsUp(toon) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(toon) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in range(5): + nodeName = '%s-%s' % (str(i), globalClock.getFrameTime()) + node = gearRoot.attachNewNode(nodeName) + node.hide() + node.reparentTo(self.ballLaunch) + node.wrtReparentTo(gearRoot) + distance = toon.getDistance(node) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + p = random.uniform(-720, -90) + y = distance + random.uniform(5, 15) + if i == 2: + x = 0 + z = 0 + y = distance + 10 + + def detachNode(node): + if not node.isEmpty(): + node.detachNode() + return Task.done + + def detachNodeLater(node = node): + if node.isEmpty(): + return + node.node().setBounds(BoundingSphere(Point3(0, 0, 0), distance * 1.5)) + node.node().setFinal(1) + self.doMethodLater(0.005, detachNode, 'detach-%s-%s' % (gearRoot.getName(), node.getName()), extraArgs=[node]) + + gearTrack.append(Sequence(Wait(26.0 / 24.0), Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, z), fluid=1), node.hprInterval(1, VBase3(0, p, 0), fluid=1)), Func(detachNodeLater))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('golf_swing') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + + def detachGearRoot(task, gearRoot = gearRoot): + if not gearRoot.isEmpty(): + gearRoot.detachNode() + return task.done + + def detachGearRootLater(gearRoot = gearRoot): + self.doMethodLater(0.01, detachGearRoot, 'detach-%s' % gearRoot.getName()) + + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(detachGearRootLater), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(throwAnim, neutral2Anim), Sequence(Wait(0.85), SoundInterval(self.swingClubSfx, node=self, duration=0.45, cutOff=300, listenerNode=base.localAvatar)))) + self.doAnimate(seq, now=1, raised=1) + + def doGolfAreaAttack(self): + toons = [] + for toonId in self.involvedToons: + toon = base.cr.doId2do.get(toonId) + if toon: + toons.append(toon) + + if not toons: + return + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('golf_swing') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if False: + extraAnim = ActorInterval(self, neutral) + gearModel = self.getGolfBall() + toToonH = self.rotateNode.getH() + 360 + self.notify.debug('toToonH = %s' % toToonH) + gearRoots = [] + allGearTracks = Parallel() + for toon in toons: + gearRoot = self.rotateNode.attachNewNode('gearRoot-atk%d-%d' % (self.numAttacks, toons.index(toon))) + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(ToontownGlobals.BossCogGolfAreaAttack)) + gearRoot.lookAt(toon) + ballLaunch = NodePath('') + ballLaunch.reparentTo(gearRoot) + ballLaunch.setPos(self.BallLaunchOffset) + gearTrack = Parallel() + for i in range(5): + nodeName = '%s-%s' % (str(i), globalClock.getFrameTime()) + node = gearRoot.attachNewNode(nodeName) + node.hide() + node.reparentTo(ballLaunch) + node.wrtReparentTo(gearRoot) + distance = toon.getDistance(node) + toonPos = toon.getPos(render) + nodePos = node.getPos(render) + vector = toonPos - nodePos + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + p = random.uniform(-720, -90) + y = distance + random.uniform(5, 15) + if i == 2: + x = 0 + z = 0 + y = distance + 10 + + def detachNode(node): + if not node.isEmpty(): + node.detachNode() + return Task.done + + def detachNodeLater(node = node): + if node.isEmpty(): + return + node.node().setBounds(BoundingSphere(Point3(0, 0, 0), distance * 1.5)) + node.node().setFinal(1) + self.doMethodLater(0.005, detachNode, 'detach-%s-%s' % (gearRoot.getName(), node.getName()), extraArgs=[node]) + + gearTrack.append(Sequence(Wait(26.0 / 24.0), Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, z), fluid=1), node.hprInterval(1, VBase3(0, p, 0), fluid=1)), Func(detachNodeLater))) + + allGearTracks.append(gearTrack) + + def detachGearRoots(gearRoots = gearRoots): + for gearRoot in gearRoots: + + def detachGearRoot(task, gearRoot = gearRoot): + if not gearRoot.isEmpty(): + gearRoot.detachNode() + return task.done + + if gearRoot.isEmpty(): + continue + self.doMethodLater(0.01, detachGearRoot, 'detach-%s' % gearRoot.getName()) + + gearRoots = [] + + rotateFire = Parallel(self.pelvis.hprInterval(2, VBase3(toToonH + 1440, 0, 0)), allGearTracks) + seq = Sequence(Func(base.playSfx, self.warningSfx), Func(self.saySomething, TTLocalizer.GolfAreaAttackTaunt), ParallelEndTogether(self.pelvis.hprInterval(2, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(rotateFire, Func(detachGearRoots), Func(self.pelvis.setHpr, VBase3(0, 0, 0))), Sequence(throwAnim, neutral2Anim), Sequence(Wait(0.85), SoundInterval(self.swingClubSfx, node=self, duration=0.45, cutOff=300, listenerNode=base.localAvatar)))) + self.doAnimate(seq, now=1, raised=1) + + def saySomething(self, chatString): + intervalName = 'CEOTaunt' + seq = Sequence(name=intervalName) + seq.append(Func(self.setChatAbsolute, chatString, CFSpeech)) + seq.append(Wait(4.0)) + seq.append(Func(self.clearChat)) + oldSeq = self.activeIntervals.get(intervalName) + if oldSeq: + oldSeq.finish() + seq.start() + self.activeIntervals[intervalName] = seq + + def d_hitToon(self, toonId): + self.notify.debug('----- d_hitToon') + self.sendUpdate('hitToon', [toonId]) + + def toonGotHealed(self, toonId): + toon = base.cr.doId2do.get(toonId) + if toon: + base.playSfx(self.toonUpSfx, node=toon) + + def localToonTouchedBeltToonup(self, beltIndex, toonupIndex, toonupNum): + avId = base.localAvatar.doId + doRequest = True + if doRequest: + self.sendUpdate('requestGetToonup', [beltIndex, toonupIndex, toonupNum]) + + def toonGotToonup(self, avId, beltIndex, toonupIndex, toonupNum): + if self.belts[beltIndex]: + self.belts[beltIndex].removeToonup(toonupIndex) + toon = base.cr.doId2do.get(avId) + if toon: + base.playSfx(self.toonUpSfx, node=toon) + + def doOvertimeAttack(self, index): + attackCode = ToontownGlobals.BossCogOvertimeAttack + attackBelts = Sequence() + if index < len(self.belts): + belt = self.belts[index] + self.saySomething(TTLocalizer.OvertimeAttackTaunts[index]) + if index: + self.bossClubIntervals[0].finish() + self.bossClubIntervals[1].loop() + else: + self.bossClubIntervals[1].finish() + self.bossClubIntervals[0].loop() + distance = belt.beltModel.getDistance(self) + gearRoot = self.rotateNode.attachNewNode('gearRoot') + gearRoot.setZ(10) + gearRoot.setTag('attackCode', str(attackCode)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.2) + gearRoot.headsUp(belt.beltModel) + toToonH = PythonUtil.fitDestAngle2Src(0, gearRoot.getH() + 180) + gearRoot.lookAt(belt.beltModel) + neutral = 'Fb_neutral' + if not self.twoFaced: + neutral = 'Ff_neutral' + gearTrack = Parallel() + for i in range(4): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 5.85, 4.0) + gear = gearModel.instanceTo(node) + x = random.uniform(-5, 5) + z = random.uniform(-3, 3) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * 0.15), Func(node.show), Parallel(node.posInterval(1, Point3(x, distance, z), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1)), Func(node.detachNode))) + + if not self.raised: + neutral1Anim = self.getAnim('down2Up') + self.raised = 1 + else: + neutral1Anim = ActorInterval(self, neutral, startFrame=48) + throwAnim = self.getAnim('throw') + neutral2Anim = ActorInterval(self, neutral) + extraAnim = Sequence() + if attackCode == ToontownGlobals.BossCogSlowDirectedAttack: + extraAnim = ActorInterval(self, neutral) + seq = Sequence(ParallelEndTogether(self.pelvis.hprInterval(1, VBase3(toToonH, 0, 0)), neutral1Anim), extraAnim, Parallel(Sequence(Wait(0.19), gearTrack, Func(gearRoot.detachNode), Func(self.explodeSfx.play), self.pelvis.hprInterval(0.2, VBase3(0, 0, 0))), Sequence(throwAnim, neutral2Anim)), Func(belt.request, 'Inactive')) + attackBelts.append(seq) + self.notify.debug('attackBelts duration= %.2f' % attackBelts.getDuration()) + self.doAnimate(attackBelts, now=1, raised=1) diff --git a/toontown/suit/DistributedBossbotBossAI.py b/toontown/suit/DistributedBossbotBossAI.py new file mode 100644 index 0000000..ea9dafc --- /dev/null +++ b/toontown/suit/DistributedBossbotBossAI.py @@ -0,0 +1,887 @@ +import random, math +from pandac.PandaModules import Point3 +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import FSM +from direct.interval.IntervalGlobal import LerpPosInterval +from toontown.coghq import DistributedFoodBeltAI +from toontown.coghq import DistributedBanquetTableAI +from toontown.coghq import DistributedGolfSpotAI +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.suit import DistributedBossCogAI +from toontown.suit import DistributedSuitAI +from toontown.suit import SuitDNA +from toontown.building import SuitBuildingGlobals +from toontown.battle import DistributedBattleWaitersAI +from toontown.battle import DistributedBattleDinersAI +from toontown.battle import BattleExperienceAI +from direct.distributed.ClockDelta import globalClockDelta + +class DistributedBossbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossbotBossAI') + maxToonLevels = 77 + toonUpLevels = [1, 2, 3, 4] + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'c') + FSM.FSM.__init__(self, 'DistributedBossbotBossAI') + self.battleOneBattlesMade = False + self.battleThreeBattlesMade = False + self.battleFourSetup = False + self.foodBelts = [] + self.numTables = 1 + self.numDinersPerTable = 3 + self.tables = [] + self.numGolfSpots = 4 + self.golfSpots = [] + self.toonFoodStatus = {} + self.bossMaxDamage = ToontownGlobals.BossbotBossMaxDamage + self.threatDict = {} + self.keyStates.append('BattleFour') + self.battleFourStart = 0 + self.battleDifficulty = 0 + self.movingToTable = False + self.tableDest = -1 + self.curTable = -1 + self.speedDamage = 0 + self.maxSpeedDamage = ToontownGlobals.BossbotMaxSpeedDamage + self.speedRecoverRate = ToontownGlobals.BossbotSpeedRecoverRate + self.speedRecoverStartTime = 0 + self.battleFourTimeStarted = 0 + self.numDinersExploded = 0 + self.numMoveAttacks = 0 + self.numGolfAttacks = 0 + self.numGearAttacks = 0 + self.numGolfAreaAttacks = 0 + self.numToonupGranted = 0 + self.totalLaffHealed = 0 + self.toonupsGranted = [] + self.doneOvertimeOneAttack = False + self.doneOvertimeTwoAttack = False + self.overtimeOneTime = simbase.air.config.GetInt('overtime-one-time', 1200) + self.battleFourDuration = simbase.air.config.GetInt('battle-four-duration', 1800) + self.overtimeOneStart = float(self.overtimeOneTime) / self.battleFourDuration + self.moveAttackAllowed = True + + def delete(self): + self.notify.debug('DistributedBossbotBossAI.delete') + self.deleteBanquetTables() + self.deleteFoodBelts() + self.deleteGolfSpots() + return DistributedBossCogAI.DistributedBossCogAI.delete(self) + + def enterElevator(self): + DistributedBossCogAI.DistributedBossCogAI.enterElevator(self) + self.makeBattleOneBattles() + + def enterIntroduction(self): + self.arenaSide = None + self.makeBattleOneBattles() + self.barrier = self.beginBarrier('Introduction', self.involvedToons, 45, self.doneIntroduction) + return + + def makeBattleOneBattles(self): + if not self.battleOneBattlesMade: + self.postBattleState = 'PrepareBattleTwo' + self.initializeBattles(1, ToontownGlobals.BossbotBossBattleOnePosHpr) + self.battleOneBattlesMade = True + + def getHoodId(self): + return ToontownGlobals.LawbotHQ + + def generateSuits(self, battleNumber): + if battleNumber == 1: + weakenedValue = ( + (1, 1), (2, 2), (2, 2), (1, 1), (1, 1, 1, 1, 1)) + listVersion = list(SuitBuildingGlobals.SuitBuildingInfo) + if simbase.config.GetBool('bossbot-boss-cheat', 0): + listVersion[14] = weakenedValue + SuitBuildingGlobals.SuitBuildingInfo = tuple(listVersion) + retval = self.invokeSuitPlanner(14, 0) + return retval + else: + suits = self.generateDinerSuits() + return suits + + def invokeSuitPlanner(self, buildingCode, skelecog): + suits = DistributedBossCogAI.DistributedBossCogAI.invokeSuitPlanner(self, buildingCode, skelecog) + activeSuits = suits['activeSuits'][:] + reserveSuits = suits['reserveSuits'][:] + if len(activeSuits) + len(reserveSuits) >= 4: + while len(activeSuits) < 4: + activeSuits.append(reserveSuits.pop()[0]) + + retval = {'activeSuits': activeSuits, 'reserveSuits': reserveSuits} + return retval + + def makeBattle(self, bossCogPosHpr, battlePosHpr, roundCallback, finishCallback, battleNumber, battleSide): + if battleNumber == 1: + battle = DistributedBattleWaitersAI.DistributedBattleWaitersAI(self.air, self, roundCallback, finishCallback, battleSide) + else: + battle = DistributedBattleDinersAI.DistributedBattleDinersAI(self.air, self, roundCallback, finishCallback, battleSide) + self.setBattlePos(battle, bossCogPosHpr, battlePosHpr) + battle.suitsKilled = self.suitsKilled + 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 + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(battleNumber) + battle.battleCalc.setSkillCreditMultiplier(mult) + activeSuits = self.activeSuitsA + if battleSide: + activeSuits = self.activeSuitsB + for suit in activeSuits: + battle.addSuit(suit) + + battle.generateWithRequired(self.zoneId) + return battle + + def initializeBattles(self, battleNumber, bossCogPosHpr): + self.resetBattles() + if not self.involvedToons: + self.notify.warning('initializeBattles: no toons!') + return + self.battleNumber = battleNumber + suitHandles = self.generateSuits(battleNumber) + self.suitsA = suitHandles['activeSuits'] + self.activeSuitsA = self.suitsA[:] + self.reserveSuits = suitHandles['reserveSuits'] + if battleNumber == 3: + if self.toonsB: + movedSuit = self.suitsA.pop() + self.suitsB = [movedSuit] + self.activeSuitsB = [movedSuit] + self.activeSuitsA.remove(movedSuit) + else: + self.suitsB = [] + self.activeSuitsB = [] + else: + suitHandles = self.generateSuits(battleNumber) + self.suitsB = suitHandles['activeSuits'] + self.activeSuitsB = self.suitsB[:] + self.reserveSuits += suitHandles['reserveSuits'] + if self.toonsA: + if battleNumber == 1: + self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.WaiterBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0) + self.battleAId = self.battleA.doId + else: + self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.DinerBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0) + self.battleAId = self.battleA.doId + else: + self.moveSuits(self.activeSuitsA) + self.suitsA = [] + self.activeSuitsA = [] + if self.arenaSide == None: + self.b_setArenaSide(0) + if self.toonsB: + if battleNumber == 1: + self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.WaiterBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1) + self.battleBId = self.battleB.doId + else: + self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.DinerBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1) + self.battleBId = self.battleB.doId + else: + self.moveSuits(self.activeSuitsB) + self.suitsB = [] + self.activeSuitsB = [] + if self.arenaSide == None: + self.b_setArenaSide(1) + self.sendBattleIds() + return + + def enterPrepareBattleTwo(self): + self.barrier = self.beginBarrier('PrepareBattleTwo', self.involvedToons, 45, self.__donePrepareBattleTwo) + self.createFoodBelts() + self.createBanquetTables() + + def __donePrepareBattleTwo(self, avIds): + self.b_setState('BattleTwo') + + def exitPrepareBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def createFoodBelts(self): + if self.foodBelts: + return + for i in xrange(2): + newBelt = DistributedFoodBeltAI.DistributedFoodBeltAI(self.air, self, i) + self.foodBelts.append(newBelt) + newBelt.generateWithRequired(self.zoneId) + + def deleteFoodBelts(self): + for belt in self.foodBelts: + belt.requestDelete() + + self.foodBelts = [] + + def createBanquetTables(self): + if self.tables: + return + self.calcAndSetBattleDifficulty() + diffInfo = ToontownGlobals.BossbotBossDifficultySettings[self.battleDifficulty] + self.diffInfo = diffInfo + self.numTables = diffInfo[0] + self.numDinersPerTable = diffInfo[1] + dinerLevel = diffInfo[2] + for i in xrange(self.numTables): + newTable = DistributedBanquetTableAI.DistributedBanquetTableAI(self.air, self, i, self.numDinersPerTable, dinerLevel) + self.tables.append(newTable) + newTable.generateWithRequired(self.zoneId) + + def deleteBanquetTables(self): + for table in self.tables: + table.requestDelete() + + self.tables = [] + + def enterBattleTwo(self): + self.resetBattles() + self.createFoodBelts() + self.createBanquetTables() + for belt in self.foodBelts: + belt.turnOn() + + for table in self.tables: + table.turnOn() + + self.barrier = self.beginBarrier('BattleTwo', self.involvedToons, ToontownGlobals.BossbotBossServingDuration + 1, self.__doneBattleTwo) + + def exitBattleTwo(self): + self.ignoreBarrier(self.barrier) + for table in self.tables: + table.goInactive() + + for belt in self.foodBelts: + belt.goInactive() + + def __doneBattleTwo(self, avIds): + self.b_setState('PrepareBattleThree') + + def requestGetFood(self, beltIndex, foodIndex, foodNum): + grantRequest = False + avId = self.air.getAvatarIdFromSender() + if self.state != 'BattleTwo': + grantRequest = False + else: + if ( + beltIndex, foodNum) not in self.toonFoodStatus.values(): + if avId not in self.toonFoodStatus: + grantRequest = True + elif self.toonFoodStatus[avId] == None: + grantRequest = True + if grantRequest: + self.toonFoodStatus[avId] = ( + beltIndex, foodNum) + self.sendUpdate('toonGotFood', [avId, beltIndex, foodIndex, foodNum]) + return + + def requestServeFood(self, tableIndex, chairIndex): + grantRequest = False + avId = self.air.getAvatarIdFromSender() + if self.state != 'BattleTwo': + grantRequest = False + else: + if tableIndex < len(self.tables): + table = self.tables[tableIndex] + dinerStatus = table.getDinerStatus(chairIndex) + if dinerStatus in (table.HUNGRY, table.ANGRY): + if self.toonFoodStatus[avId]: + grantRequest = True + if grantRequest: + self.toonFoodStatus[avId] = None + table.foodServed(chairIndex) + self.sendUpdate('toonServeFood', [avId, tableIndex, chairIndex]) + return + + def enterPrepareBattleThree(self): + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, ToontownGlobals.BossbotBossServingDuration + 1, self.__donePrepareBattleThree) + self.divideToons() + self.makeBattleThreeBattles() + + def exitPrepareBattleThree(self): + self.ignoreBarrier(self.barrier) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def makeBattleThreeBattles(self): + if not self.battleThreeBattlesMade: + if not self.tables: + self.createBanquetTables() + for table in self.tables: + table.turnOn() + table.goInactive() + + notDeadList = [] + for table in self.tables: + tableInfo = table.getNotDeadInfo() + notDeadList += tableInfo + + self.notDeadList = notDeadList + self.postBattleState = 'PrepareBattleFour' + self.initializeBattles(3, ToontownGlobals.BossbotBossBattleThreePosHpr) + self.battleThreeBattlesMade = True + + def generateDinerSuits(self): + diners = [] + for i in xrange(len(self.notDeadList)): + if simbase.config.GetBool('bossbot-boss-cheat', 0): + suit = self.__genSuitObject(self.zoneId, 2, 'c', 2, 0) + else: + info = self.notDeadList[i] + suitType = info[2] - 4 + suitLevel = info[2] + suit = self.__genSuitObject(self.zoneId, suitType, 'c', suitLevel, 1) + diners.append((suit, 100)) + + active = [] + for i in xrange(2): + if simbase.config.GetBool('bossbot-boss-cheat', 0): + suit = self.__genSuitObject(self.zoneId, 2, 'c', 2, 0) + else: + suitType = 8 + suitLevel = 12 + suit = self.__genSuitObject(self.zoneId, suitType, 'c', suitLevel, 1) + active.append(suit) + + return {'activeSuits': active, 'reserveSuits': diners} + + 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 __setupSuitInfo(self, suit, bldgTrack, suitLevel, suitType): + 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 False + + def enterBattleThree(self): + self.makeBattleThreeBattles() + self.notify.debug('self.battleA = %s' % self.battleA) + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def exitBattleThree(self): + self.resetBattles() + + def enterPrepareBattleFour(self): + self.resetBattles() + self.setupBattleFourObjects() + self.barrier = self.beginBarrier('PrepareBattleFour', self.involvedToons, 45, self.__donePrepareBattleFour) + + def __donePrepareBattleFour(self, avIds): + self.b_setState('BattleFour') + + def exitPrepareBattleFour(self): + self.ignoreBarrier(self.barrier) + + def enterBattleFour(self): + self.battleFourTimeStarted = globalClock.getFrameTime() + self.numToonsAtStart = len(self.involvedToons) + self.resetBattles() + self.setupBattleFourObjects() + self.battleFourStart = globalClock.getFrameTime() + self.waitForNextAttack(5) + + def exitBattleFour(self): + self.recordCeoInfo() + for belt in self.foodBelts: + belt.goInactive() + + def recordCeoInfo(self): + didTheyWin = 0 + if self.bossDamage == self.bossMaxDamage: + didTheyWin = 1 + self.battleFourTimeInMin = globalClock.getFrameTime() - self.battleFourTimeStarted + self.battleFourTimeInMin /= 60.0 + self.numToonsAtEnd = 0 + toonHps = [] + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + self.numToonsAtEnd += 1 + toonHps.append(toon.hp) + + self.air.writeServerEvent('ceoInfo', self.doId, '%d|%.2f|%d|%d|%d|%d|%d|%d|%s|%s|%.1f|%d|%d|%d|%d|%d}%d|%s|' % (didTheyWin, self.battleFourTimeInMin, self.battleDifficulty, self.numToonsAtStart, self.numToonsAtEnd, self.numTables, self.numTables * self.numDinersPerTable, self.numDinersExploded, toonHps, self.involvedToons, self.speedDamage, self.numMoveAttacks, self.numGolfAttacks, self.numGearAttacks, self.numGolfAreaAttacks, self.numToonupGranted, self.totalLaffHealed, 'ceoBugfixes')) + + def setupBattleFourObjects(self): + if self.battleFourSetup: + return + if not self.tables: + self.createBanquetTables() + for table in self.tables: + table.goFree() + + if not self.golfSpots: + self.createGolfSpots() + self.createFoodBelts() + for belt in self.foodBelts: + belt.goToonup() + + self.battleFourSetup = True + + def hitBoss(self, bossDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'DistributedBossbotBossAI.hitBoss from unknown avatar'): + return + self.validate(avId, bossDamage <= 3, 'invalid bossDamage %s' % bossDamage) + if bossDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleFour': + return + bossDamage *= 2 + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage >= self.bossMaxDamage: + self.b_setState('Victory') + else: + self.__recordHit(bossDamage) + + def __recordHit(self, bossDamage): + now = globalClock.getFrameTime() + self.hitCount += 1 + avId = self.air.getAvatarIdFromSender() + self.addThreat(avId, bossDamage) + + def getBossDamage(self): + return self.bossDamage + + def b_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.d_setBossDamage(bossDamage, recoverRate, recoverStartTime) + self.setBossDamage(bossDamage, recoverRate, recoverStartTime) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + + def d_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setBossDamage', [bossDamage, recoverRate, timestamp]) + + def getSpeedDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.speedRecoverStartTime + self.notify.debug('elapsed=%s' % elapsed) + floatSpeedDamage = max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + self.notify.debug('floatSpeedDamage = %s' % floatSpeedDamage) + return int(max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0)) + + def getFloatSpeedDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.speedRecoverStartTime + floatSpeedDamage = max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + self.notify.debug('floatSpeedDamage = %s' % floatSpeedDamage) + return max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0) + + def b_setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime): + self.d_setSpeedDamage(speedDamage, recoverRate, recoverStartTime) + self.setSpeedDamage(speedDamage, recoverRate, recoverStartTime) + + def setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime): + self.speedDamage = speedDamage + self.speedRecoverRate = recoverRate + self.speedRecoverStartTime = recoverStartTime + + def d_setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setSpeedDamage', [speedDamage, recoverRate, timestamp]) + + def createGolfSpots(self): + if self.golfSpots: + return + for i in xrange(self.numGolfSpots): + newGolfSpot = DistributedGolfSpotAI.DistributedGolfSpotAI(self.air, self, i) + self.golfSpots.append(newGolfSpot) + newGolfSpot.generateWithRequired(self.zoneId) + newGolfSpot.forceFree() + + def deleteGolfSpots(self): + for spot in self.golfSpots: + spot.requestDelete() + + self.golfSpots = [] + + def ballHitBoss(self, speedDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'DistributedBossbotBossAI.ballHitBoss from unknown avatar'): + return + if speedDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleFour': + return + now = globalClock.getFrameTime() + newDamage = self.getSpeedDamage() + speedDamage + self.notify.debug('newDamage = %s' % newDamage) + speedDamage = min(self.getFloatSpeedDamage() + speedDamage, self.maxSpeedDamage) + self.b_setSpeedDamage(speedDamage, self.speedRecoverRate, now) + self.addThreat(avId, 0.1) + + def enterVictory(self): + self.resetBattles() + for table in self.tables: + table.turnOff() + + for golfSpot in self.golfSpots: + golfSpot.turnOff() + + self.suitsKilled.append({'type': None, 'level': None, 'track': self.dna.dept, 'isSkelecog': 0, 'isForeman': 0, 'isVP': 1, 'isCFO': 0, 'isSupervisor': 0, 'isVirtual': 0, 'activeToons': self.involvedToons[:]}) + self.barrier = self.beginBarrier('Victory', self.involvedToons, 30, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + self.givePinkSlipReward(toon) + toon.b_promote(self.deptIndex) + + def givePinkSlipReward(self, toon): + self.notify.debug('TODO give pink slip to %s' % toon) + toon.addPinkSlips(self.battleDifficulty + 1) + + def getThreat(self, toonId): + if toonId in self.threatDict: + return self.threatDict[toonId] + else: + return 0 + + def addThreat(self, toonId, threat): + if toonId in self.threatDict: + self.threatDict[toonId] += threat + else: + self.threatDict[toonId] = threat + + def subtractThreat(self, toonId, threat): + if toonId in self.threatDict: + self.threatDict[toonId] -= threat + else: + self.threatDict[toonId] = 0 + if self.threatDict[toonId] < 0: + self.threatDict[toonId] = 0 + + def waitForNextAttack(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleFour': + taskName = self.uniqueName('NextAttack') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextAttack, taskName) + + def doNextAttack(self, task): + attackCode = -1 + optionalParam = None + if self.movingToTable: + self.waitForNextAttack(5) + elif self.attackCode == ToontownGlobals.BossCogDizzyNow: + attackCode = ToontownGlobals.BossCogRecoverDizzyAttack + elif self.getBattleFourTime() > self.overtimeOneStart and not self.doneOvertimeOneAttack: + attackCode = ToontownGlobals.BossCogOvertimeAttack + self.doneOvertimeOneAttack = True + optionalParam = 0 + elif self.getBattleFourTime() > 1.0 and not self.doneOvertimeTwoAttack: + attackCode = ToontownGlobals.BossCogOvertimeAttack + self.doneOvertimeTwoAttack = True + optionalParam = 1 + else: + attackCode = random.choice([ToontownGlobals.BossCogGolfAreaAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack]) + if attackCode == ToontownGlobals.BossCogAreaAttack: + self.__doAreaAttack() + if attackCode == ToontownGlobals.BossCogGolfAreaAttack: + self.__doGolfAreaAttack() + elif attackCode == ToontownGlobals.BossCogDirectedAttack: + self.__doDirectedAttack() + elif attackCode >= 0: + self.b_setAttackCode(attackCode, optionalParam) + return + + def progressValue(self, fromValue, toValue): + t0 = float(self.bossDamage) / float(self.bossMaxDamage) + elapsed = globalClock.getFrameTime() - self.battleFourStart + t1 = elapsed / float(self.battleThreeDuration) + t = max(t0, t1) + progVal = fromValue + (toValue - fromValue) * min(t, 1) + self.notify.debug('progVal=%s' % progVal) + import pdb + pdb.set_trace() + return progVal + + def __doDirectedAttack(self): + toonId = self.getMaxThreatToon() + self.notify.debug('toonToAttack=%s' % toonId) + unflattenedToons = self.getUnflattenedToons() + attackTotallyRandomToon = random.random() < 0.1 + if unflattenedToons and (attackTotallyRandomToon or toonId == 0): + toonId = random.choice(unflattenedToons) + if toonId: + toonThreat = self.getThreat(toonId) + toonThreat *= 0.25 + threatToSubtract = max(toonThreat, 10) + self.subtractThreat(toonId, threatToSubtract) + if self.isToonRoaming(toonId): + self.b_setAttackCode(ToontownGlobals.BossCogGolfAttack, toonId) + self.numGolfAttacks += 1 + elif self.isToonOnTable(toonId): + doesMoveAttack = simbase.air.config.GetBool('ceo-does-move-attack', 1) + if doesMoveAttack: + chanceToShoot = 0.25 + else: + chanceToShoot = 1.0 + if not self.moveAttackAllowed: + self.notify.debug('moveAttack is not allowed, doing gearDirectedAttack') + chanceToShoot = 1.0 + if random.random() < chanceToShoot: + self.b_setAttackCode(ToontownGlobals.BossCogGearDirectedAttack, toonId) + self.numGearAttacks += 1 + else: + tableIndex = self.getToonTableIndex(toonId) + self.doMoveAttack(tableIndex) + else: + self.b_setAttackCode(ToontownGlobals.BossCogGolfAttack, toonId) + else: + uprightTables = self.getUprightTables() + if uprightTables: + tableToMoveTo = random.choice(uprightTables) + self.doMoveAttack(tableToMoveTo) + else: + self.waitForNextAttack(4) + + def doMoveAttack(self, tableIndex): + self.numMoveAttacks += 1 + self.movingToTable = True + self.tableDest = tableIndex + self.b_setAttackCode(ToontownGlobals.BossCogMoveAttack, tableIndex) + + def getUnflattenedToons(self): + result = [] + uprightTables = self.getUprightTables() + for toonId in self.involvedToons: + toonTable = self.getToonTableIndex(toonId) + if toonTable >= 0 and toonTable not in uprightTables: + pass + else: + result.append(toonId) + + return result + + def getMaxThreatToon(self): + returnedToonId = 0 + maxThreat = 0 + maxToons = [] + for toonId in self.threatDict: + curThreat = self.threatDict[toonId] + tableIndex = self.getToonTableIndex(toonId) + if tableIndex > -1 and self.tables[tableIndex].state == 'Flat': + pass + elif curThreat > maxThreat: + maxToons = [ + toonId] + maxThreat = curThreat + elif curThreat == maxThreat: + maxToons.append(toonId) + + if maxToons: + returnedToonId = random.choice(maxToons) + return returnedToonId + + def getToonDifficulty(self): + highestCogSuitLevel = 0 + totalCogSuitLevels = 0.0 + totalNumToons = 0.0 + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toonLevel = toon.getNumPromotions(self.dept) + totalCogSuitLevels += toonLevel + totalNumToons += 1 + if toon.cogLevels > highestCogSuitLevel: + highestCogSuitLevel = toonLevel + + if not totalNumToons: + totalNumToons = 1.0 + averageLevel = totalCogSuitLevels / totalNumToons + self.notify.debug('toons average level = %f, highest level = %d' % (averageLevel, highestCogSuitLevel)) + retval = min(averageLevel, self.maxToonLevels) + return retval + + def calcAndSetBattleDifficulty(self): + self.toonLevels = self.getToonDifficulty() + numDifficultyLevels = len(ToontownGlobals.BossbotBossDifficultySettings) + battleDifficulty = int(self.toonLevels / self.maxToonLevels * numDifficultyLevels) + if battleDifficulty >= numDifficultyLevels: + battleDifficulty = numDifficultyLevels - 1 + self.b_setBattleDifficulty(battleDifficulty) + + def b_setBattleDifficulty(self, batDiff): + self.setBattleDifficulty(batDiff) + self.d_setBattleDifficulty(batDiff) + + def setBattleDifficulty(self, batDiff): + self.battleDifficulty = batDiff + + def d_setBattleDifficulty(self, batDiff): + self.sendUpdate('setBattleDifficulty', [batDiff]) + + def getUprightTables(self): + tableList = [] + for table in self.tables: + if table.state != 'Flat': + tableList.append(table.index) + + return tableList + + def getToonTableIndex(self, toonId): + tableIndex = -1 + for table in self.tables: + if table.avId == toonId: + tableIndex = table.index + break + + return tableIndex + + def getToonGolfSpotIndex(self, toonId): + golfSpotIndex = -1 + for golfSpot in self.golfSpots: + if golfSpot.avId == toonId: + golfSpotIndex = golfSpot.index + break + + return golfSpotIndex + + def isToonOnTable(self, toonId): + result = self.getToonTableIndex(toonId) != -1 + return result + + def isToonOnGolfSpot(self, toonId): + result = self.getToonGolfSpotIndex(toonId) != -1 + return result + + def isToonRoaming(self, toonId): + result = not self.isToonOnTable(toonId) and not self.isToonOnGolfSpot(toonId) + return result + + def reachedTable(self, tableIndex): + if self.movingToTable and self.tableDest == tableIndex: + self.movingToTable = False + self.curTable = self.tableDest + self.tableDest = -1 + + def hitTable(self, tableIndex): + self.notify.debug('hitTable tableIndex=%d' % tableIndex) + if tableIndex < len(self.tables): + table = self.tables[tableIndex] + if table.state != 'Flat': + table.goFlat() + + def awayFromTable(self, tableIndex): + self.notify.debug('awayFromTable tableIndex=%d' % tableIndex) + if tableIndex < len(self.tables): + taskName = 'Unflatten-%d' % tableIndex + unflattenTime = self.diffInfo[3] + taskMgr.doMethodLater(unflattenTime, self.unflattenTable, taskName, extraArgs=[tableIndex]) + + def unflattenTable(self, tableIndex): + if tableIndex < len(self.tables): + table = self.tables[tableIndex] + if table.state == 'Flat': + if table.avId and table.avId in self.involvedToons: + table.forceControl(table.avId) + else: + table.goFree() + + def incrementDinersExploded(self): + self.numDinersExploded += 1 + + def magicWordHit(self, damage, avId): + self.hitBoss(damage) + + def __doAreaAttack(self): + self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack) + + def __doGolfAreaAttack(self): + self.numGolfAreaAttacks += 1 + self.b_setAttackCode(ToontownGlobals.BossCogGolfAreaAttack) + + def hitToon(self, toonId): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId != toonId, 'hitToon on self'): + return + if avId not in self.involvedToons or toonId not in self.involvedToons: + return + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, 1) + self.sendUpdate('toonGotHealed', [toonId]) + + def requestGetToonup(self, beltIndex, toonupIndex, toonupNum): + grantRequest = False + avId = self.air.getAvatarIdFromSender() + if self.state != 'BattleFour': + grantRequest = False + else: + if ( + beltIndex, toonupNum) not in self.toonupsGranted: + toon = simbase.air.doId2do.get(avId) + if toon: + grantRequest = True + if grantRequest: + self.toonupsGranted.insert(0, (beltIndex, toonupNum)) + if len(self.toonupsGranted) > 8: + self.toonupsGranted = self.toonupsGranted[0:8] + self.sendUpdate('toonGotToonup', [avId, beltIndex, toonupIndex, toonupNum]) + if toonupIndex < len(self.toonUpLevels): + self.healToon(toon, self.toonUpLevels[toonupIndex]) + self.numToonupGranted += 1 + self.totalLaffHealed += self.toonUpLevels[toonupIndex] + else: + self.notify.warning('requestGetToonup this should not happen') + self.healToon(toon, 1) + + def toonLeftTable(self, tableIndex): + if self.movingToTable and self.tableDest == tableIndex: + if random.random() < 0.5: + self.movingToTable = False + self.waitForNextAttack(0) + + def getBattleFourTime(self): + if self.state != 'BattleFour': + t1 = 0 + else: + elapsed = globalClock.getFrameTime() - self.battleFourStart + t1 = elapsed / float(self.battleFourDuration) + return t1 + + def getDamageMultiplier(self): + mult = 1.0 + if self.doneOvertimeOneAttack and not self.doneOvertimeTwoAttack: + mult = 1.25 + if self.getBattleFourTime() > 1.0: + mult = self.getBattleFourTime() + 1 + return mult + + def toggleMove(self): + self.moveAttackAllowed = not self.moveAttackAllowed + return self.moveAttackAllowed diff --git a/toontown/suit/DistributedCashbotBoss.py b/toontown/suit/DistributedCashbotBoss.py new file mode 100644 index 0000000..cf0a736 --- /dev/null +++ b/toontown/suit/DistributedCashbotBoss.py @@ -0,0 +1,937 @@ +from direct.interval.IntervalGlobal import * +from direct.task.TaskManagerGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import TTLocalizer +import DistributedBossCog +from direct.task.Task import Task +import DistributedCashbotBossGoon +import SuitDNA +from toontown.toon import Toon +from toontown.toon import ToonDNA +from direct.fsm import FSM +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from toontown.building import ElevatorUtils +from toontown.building import ElevatorConstants +from toontown.battle import MovieToonVictory +from toontown.battle import RewardPanel +from toontown.distributed import DelayDelete +from toontown.chat import ResistanceChat +from toontown.coghq import CogDisguiseGlobals +from pandac.PandaModules import * +import random +import math +OneBossCog = None +TTL = TTLocalizer + +class DistributedCashbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBoss') + numFakeGoons = 3 + + def __init__(self, cr): + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedSellbotBoss') + self.resistanceToon = None + self.resistanceToonOnstage = 0 + self.cranes = {} + self.safes = {} + self.goons = [] + self.bossMaxDamage = ToontownGlobals.CashbotBossMaxDamage + self.elevatorType = ElevatorConstants.ELEVATOR_CFO + base.boss = self + return + + def announceGenerate(self): + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.setName(TTLocalizer.CashbotBossName) + nameInfo = TTLocalizer.BossCogNameWithDept % {'name': self.name, + 'dept': SuitDNA.getDeptFullname(self.style.dept)} + self.setDisplayName(nameInfo) + target = CollisionSphere(2, 0, 0, 3) + targetNode = CollisionNode('headTarget') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.headTarget = self.neck.attachNewNode(targetNode) + shield = CollisionSphere(0, 0, 0.8, 7) + shieldNode = CollisionNode('shield') + shieldNode.addSolid(shield) + shieldNode.setCollideMask(ToontownGlobals.PieBitmask) + shieldNodePath = self.pelvis.attachNewNode(shieldNode) + self.heldObject = None + self.bossDamage = 0 + self.loadEnvironment() + self.__makeResistanceToon() + self.physicsMgr = PhysicsManager() + integrator = LinearEulerIntegrator() + self.physicsMgr.attachLinearIntegrator(integrator) + fn = ForceNode('gravity') + self.fnp = self.geom.attachNewNode(fn) + gravity = LinearVectorForce(0, 0, -32) + fn.addForce(gravity) + self.physicsMgr.addLinearForce(gravity) + localAvatar.chatMgr.chatInputSpeedChat.addCFOMenu() + global OneBossCog + if OneBossCog != None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + return + + def disable(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.disable(self) + self.demand('Off') + self.unloadEnvironment() + self.__cleanupResistanceToon() + self.fnp.removeNode() + self.physicsMgr.clearLinearForces() + self.battleThreeMusic.stop() + self.epilogueMusic.stop() + localAvatar.chatMgr.chatInputSpeedChat.removeCFOMenu() + if OneBossCog == self: + OneBossCog = None + return + + def __makeResistanceToon(self): + if self.resistanceToon: + return + npc = Toon.Toon() + npc.setName(TTLocalizer.ResistanceToonName) + npc.setPickable(0) + npc.setPlayerType(NametagGroup.CCNonPlayer) + dna = ToonDNA.ToonDNA() + dna.newToonRandom(11237, 'f', 1) + dna.head = 'pls' + npc.setDNAString(dna.makeNetString()) + npc.animFSM.request('neutral') + self.resistanceToon = npc + self.resistanceToon.setPosHpr(*ToontownGlobals.CashbotRTBattleOneStartPosHpr) + state = random.getstate() + random.seed(self.doId) + self.resistanceToon.suitType = SuitDNA.getRandomSuitByDept('m') + random.setstate(state) + self.fakeGoons = [] + for i in range(self.numFakeGoons): + goon = DistributedCashbotBossGoon.DistributedCashbotBossGoon(base.cr) + goon.doId = -1 - i + goon.setBossCogId(self.doId) + goon.generate() + goon.announceGenerate() + self.fakeGoons.append(goon) + + self.__hideFakeGoons() + + def __cleanupResistanceToon(self): + self.__hideResistanceToon() + if self.resistanceToon: + self.resistanceToon.removeActive() + self.resistanceToon.delete() + self.resistanceToon = None + for i in range(self.numFakeGoons): + self.fakeGoons[i].disable() + self.fakeGoons[i].delete() + self.fakeGoons[i] = None + + return + + def __showResistanceToon(self, withSuit): + if not self.resistanceToonOnstage: + self.resistanceToon.addActive() + self.resistanceToon.reparentTo(self.geom) + self.resistanceToonOnstage = 1 + if withSuit: + suit = self.resistanceToon.suitType + self.resistanceToon.putOnSuit(suit, False) + else: + self.resistanceToon.takeOffSuit() + + def __hideResistanceToon(self): + if self.resistanceToonOnstage: + self.resistanceToon.removeActive() + self.resistanceToon.detachNode() + self.resistanceToonOnstage = 0 + + def __hideFakeGoons(self): + if self.fakeGoons: + for goon in self.fakeGoons: + goon.request('Off') + + def __showFakeGoons(self, state): + print self.fakeGoons + if self.fakeGoons: + for goon in self.fakeGoons: + goon.request(state) + + def loadEnvironment(self): + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.midVault = loader.loadModel('phase_10/models/cogHQ/MidVault.bam') + self.endVault = loader.loadModel('phase_10/models/cogHQ/EndVault.bam') + 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.safe = loader.loadModel('phase_10/models/cogHQ/CBSafe.bam') + self.eyes = loader.loadModel('phase_10/models/cogHQ/CashBotBossEyes.bam') + self.cableTex = self.craneArm.findTexture('MagnetControl') + self.eyes.setPosHprScale(4.5, 0, -2.5, 90, 90, 0, 0.4, 0.4, 0.4) + self.eyes.reparentTo(self.neck) + self.eyes.hide() + self.midVault.setPos(0, -222, -70.7) + self.endVault.setPos(84, -201, -6) + self.geom = NodePath('geom') + self.midVault.reparentTo(self.geom) + self.endVault.reparentTo(self.geom) + 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 | BitMask32.lowerOn(3) << 21) + self.door1 = self.midVault.find('**/SlidingDoor1/') + self.door2 = self.midVault.find('**/SlidingDoor/') + self.door3 = self.endVault.find('**/SlidingDoor/') + elevatorModel = loader.loadModel('phase_10/models/cogHQ/CFOElevator') + elevatorOrigin = self.midVault.find('**/elevator_origin') + elevatorOrigin.setScale(1) + elevatorModel.reparentTo(elevatorOrigin) + leftDoor = elevatorModel.find('**/left_door') + leftDoor.setName('left-door') + rightDoor = elevatorModel.find('**/right_door') + rightDoor.setName('right-door') + self.setupElevator(elevatorOrigin) + ElevatorUtils.closeDoors(leftDoor, rightDoor, ElevatorConstants.ELEVATOR_CFO) + 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 = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + planeNode = CollisionNode('dropPlane') + planeNode.addSolid(plane) + planeNode.setCollideMask(ToontownGlobals.PieBitmask) + self.geom.attachNewNode(planeNode) + self.geom.reparentTo(render) + + def unloadEnvironment(self): + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + self.geom.removeNode() + + def replaceCollisionPolysWithPlanes(self, model): + newCollisionNode = CollisionNode('collisions') + newCollideMask = BitMask32(0) + planes = [] + collList = model.findAllMatches('**/+CollisionNode') + if not collList: + collList = [model] + for cnp in collList: + cn = cnp.node() + if not isinstance(cn, 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, CollisionPolygon): + plane = 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 = CollisionPlane(plane) + newCollisionNode.addSolid(cp) + lastPlane = plane + + return NodePath(newCollisionNode) + + def __makeGoonMovieForIntro(self): + goonTrack = Parallel() + goon = self.fakeGoons[0] + goonTrack.append(Sequence( + goon.posHprInterval(0, Point3(111, -287, 0), VBase3(165, 0, 0)), + goon.posHprInterval(9, Point3(101, -323, 0), VBase3(165, 0, 0)), + goon.hprInterval(1, VBase3(345, 0, 0)), + goon.posHprInterval(9, Point3(111, -287, 0), VBase3(345, 0, 0)), + goon.hprInterval(1, VBase3(165, 0, 0)), + goon.posHprInterval(9.5, Point3(104, -316, 0), VBase3(165, 0, 0)), + Func(goon.request, 'Stunned'), + Wait(1))) + goon = self.fakeGoons[1] + goonTrack.append(Sequence( + goon.posHprInterval(0, Point3(119, -315, 0), VBase3(357, 0, 0)), + goon.posHprInterval(9, Point3(121, -280, 0), VBase3(357, 0, 0)), + goon.hprInterval(1, VBase3(177, 0, 0)), + goon.posHprInterval(9, Point3(119, -315, 0), VBase3(177, 0, 0)), + goon.hprInterval(1, VBase3(357, 0, 0)), + goon.posHprInterval(9, Point3(121, -280, 0), VBase3(357, 0, 0)))) + goon = self.fakeGoons[2] + goonTrack.append(Sequence( + goon.posHprInterval(0, Point3(102, -320, 0), VBase3(231, 0, 0)), + goon.posHprInterval(9, Point3(127, -337, 0), VBase3(231, 0, 0)), + goon.hprInterval(1, VBase3(51, 0, 0)), + goon.posHprInterval(9, Point3(102, -320, 0), VBase3(51, 0, 0)), + goon.hprInterval(1, VBase3(231, 0, 0)), + goon.posHprInterval(9, Point3(127, -337, 0), VBase3(231, 0, 0)))) + return Sequence(Func(self.__showFakeGoons, 'Walk'), goonTrack, Func(self.__hideFakeGoons)) + + def makeIntroductionMovie(self, delayDeletes): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'CashbotBoss.makeIntroductionMovie')) + + rtTrack = Sequence() + startPos = Point3(ToontownGlobals.CashbotBossOffstagePosHpr[0], ToontownGlobals.CashbotBossOffstagePosHpr[1], ToontownGlobals.CashbotBossOffstagePosHpr[2]) + battlePos = Point3(ToontownGlobals.CashbotBossBattleOnePosHpr[0], ToontownGlobals.CashbotBossBattleOnePosHpr[1], ToontownGlobals.CashbotBossBattleOnePosHpr[2]) + battleHpr = VBase3(ToontownGlobals.CashbotBossBattleOnePosHpr[3], ToontownGlobals.CashbotBossBattleOnePosHpr[4], ToontownGlobals.CashbotBossBattleOnePosHpr[5]) + bossTrack = Sequence() + bossTrack.append(Func(self.reparentTo, render)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.pelvis.setHpr, self.pelvisForwardHpr)) + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, hpr, battlePos, battleHpr, 0) + bossTrack.append(track) + bossTrack.append(Func(self.getGeomNode().setH, 0)) + bossTrack.append(Func(self.pelvis.setHpr, self.pelvisReversedHpr)) + goonTrack = self.__makeGoonMovieForIntro() + attackToons = TTL.CashbotBossCogAttack + rToon = self.resistanceToon + rToon.setPosHpr(*ToontownGlobals.CashbotRTBattleOneStartPosHpr) + track = Sequence( + Func(camera.setPosHpr, 82, -219, 5, 267, 0, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonWelcome, CFSpeech), + Wait(3), + Sequence(goonTrack, duration=0), + Parallel( + camera.posHprInterval(4, Point3(108, -244, 4), VBase3(211.5, 0, 0)), + Sequence( + Func(rToon.suit.setPlayRate, 1.4, 'walk'), + Func(rToon.suit.loop, 'walk'), + Parallel( + rToon.hprInterval(1, VBase3(180, 0, 0)), + rToon.posInterval(3, VBase3(120, -255, 0)), + Sequence( + Wait(2), + Func(rToon.clearChat))), + Func(rToon.suit.loop, 'neutral'), + self.door2.posInterval(3, VBase3(0, 0, 30)))), + Func(rToon.setHpr, 0, 0, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonTooLate, CFSpeech), + Func(camera.reparentTo, render), + Func(camera.setPosHpr, 61.1, -228.8, 10.2, -90, 0, 0), + self.door1.posInterval(2, VBase3(0, 0, 30)), + Parallel( + bossTrack, + Sequence( + Wait(3), + Func(rToon.clearChat), + self.door1.posInterval(3, VBase3(0, 0, 0)))), + Func(self.setChatAbsolute, TTL.CashbotBossDiscoverToons1, CFSpeech), + camera.posHprInterval(1.5, Point3(93.3, -230, 0.7), VBase3(-92.9, 39.7, 8.3)), + Func(self.setChatAbsolute, TTL.CashbotBossDiscoverToons2, CFSpeech), + Wait(4), + Func(self.clearChat), + self.loseCogSuits(self.toonsA + self.toonsB, render, (113, -228, 10, 90, 0, 0)), + Wait(1), + Func(rToon.setHpr, 0, 0, 0), + self.loseCogSuits([rToon], render, (133, -243, 5, 143, 0, 0), True), + Func(rToon.setChatAbsolute, TTL.ResistanceToonKeepHimBusy, CFSpeech), + Wait(1), + Func(self.__showResistanceToon, False), + Sequence( + Func(rToon.animFSM.request, 'run'), + rToon.hprInterval(1, VBase3(180, 0, 0)), + Parallel( + Sequence( + rToon.posInterval(1.5, VBase3(109, -294, 0)), + Parallel(Func(rToon.animFSM.request, 'jump')), + rToon.posInterval(1.5, VBase3(93.935, -341.065, 2))), + self.door2.posInterval(3, VBase3(0, 0, 0))), + Func(rToon.animFSM.request, 'neutral')), + self.toonNormalEyes(self.involvedToons), + self.toonNormalEyes([self.resistanceToon], True), + Func(rToon.clearChat), + Func(camera.setPosHpr, 93.3, -230, 0.7, -92.9, 39.7, 8.3), + Func(self.setChatAbsolute, attackToons, CFSpeech), + Wait(2), + Func(self.clearChat)) + return Sequence(Func(camera.reparentTo, render), track) + + def __makeGoonMovieForBattleThree(self): + goonPosHprs = [[Point3(111, -287, 0), + VBase3(165, 0, 0), + Point3(101, -323, 0), + VBase3(165, 0, 0)], [Point3(119, -315, 0), + VBase3(357, 0, 0), + Point3(121, -280, 0), + VBase3(357, 0, 0)], [Point3(102, -320, 0), + VBase3(231, 0, 0), + Point3(127, -337, 0), + VBase3(231, 0, 0)]] + mainGoon = self.fakeGoons[0] + goonLoop = Parallel() + print self.fakeGoons + for i in range(1, self.numFakeGoons): + goon = self.fakeGoons[i] + goonLoop.append(Sequence(goon.posHprInterval(8, goonPosHprs[i][0], goonPosHprs[i][1]), goon.posHprInterval(8, goonPosHprs[i][2], goonPosHprs[i][3]))) + + goonTrack = Sequence(Func(self.__showFakeGoons, 'Walk'), Func(mainGoon.request, 'Stunned'), Func(goonLoop.loop), Wait(20)) + return goonTrack + + def makePrepareBattleThreeMovie(self, delayDeletes, crane, safe): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'CashbotBoss.makePrepareBattleThreeMovie')) + + startPos = Point3(ToontownGlobals.CashbotBossBattleOnePosHpr[0], ToontownGlobals.CashbotBossBattleOnePosHpr[1], ToontownGlobals.CashbotBossBattleOnePosHpr[2]) + battlePos = Point3(ToontownGlobals.CashbotBossBattleThreePosHpr[0], ToontownGlobals.CashbotBossBattleThreePosHpr[1], ToontownGlobals.CashbotBossBattleThreePosHpr[2]) + startHpr = Point3(ToontownGlobals.CashbotBossBattleOnePosHpr[3], ToontownGlobals.CashbotBossBattleOnePosHpr[4], ToontownGlobals.CashbotBossBattleOnePosHpr[5]) + battleHpr = VBase3(ToontownGlobals.CashbotBossBattleThreePosHpr[3], ToontownGlobals.CashbotBossBattleThreePosHpr[4], ToontownGlobals.CashbotBossBattleThreePosHpr[5]) + finalHpr = VBase3(135, 0, 0) + bossTrack = Sequence() + bossTrack.append(Func(self.reparentTo, render)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.pelvis.setHpr, self.pelvisForwardHpr)) + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, startHpr, startPos, battleHpr, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, battleHpr, battlePos, finalHpr, 0) + bossTrack.append(track) + rToon = self.resistanceToon + rToon.setPosHpr(93.935, -341.065, 0, -45, 0, 0) + goon = self.fakeGoons[0] + crane = self.cranes[0] + track = Sequence( + Func(self.__hideToons), + Func(crane.request, 'Movie'), + Func(crane.accomodateToon, rToon), + Func(goon.request, 'Stunned'), + Func(goon.setPosHpr, 104, -316, 0, 165, 0, 0), + Parallel( + self.door2.posInterval(4.5, VBase3(0, 0, 30)), + self.door3.posInterval(4.5, VBase3(0, 0, 30)), + bossTrack), + Func(rToon.loop, 'leverNeutral'), + Func(camera.reparentTo, self.geom), + Func(camera.setPosHpr, 105, -326, 5, 136.3, 0, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonWatchThis, CFSpeech), + Wait(2), + Func(rToon.clearChat), + Func(camera.setPosHpr, 105, -326, 20, -45.3, 11, 0), + Func(self.setChatAbsolute, TTL.CashbotBossGetAwayFromThat, CFSpeech), + Wait(2), + Func(self.clearChat), + camera.posHprInterval(1.5, Point3(105, -326, 5), Point3(136.3, 0, 0), blendType='easeInOut'), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions1, CFSpeech), + Wait(4), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions2, CFSpeech), + Wait(4), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions3, CFSpeech), + Wait(4), + Func(rToon.setChatAbsolute, TTL.ResistanceToonCraneInstructions4, CFSpeech), + Wait(4), + Func(rToon.clearChat), + Func(camera.setPosHpr, 102, -323.6, 0.9, -10.6, 14, 0), + Func(goon.request, 'Recovery'), + Wait(2), + Func(camera.setPosHpr, 95.4, -332.6, 4.2, 167.1, -13.2, 0), + Func(rToon.setChatAbsolute, TTL.ResistanceToonGetaway, CFSpeech), + Func(rToon.animFSM.request, 'jump'), + Wait(1.8), + Func(rToon.clearChat), + Func(camera.setPosHpr, 109.1, -300.7, 13.9, -15.6, -13.6, 0), + Func(rToon.animFSM.request, 'run'), + Func(goon.request, 'Walk'), + Parallel( + self.door3.posInterval(3, VBase3(0, 0, 0)), + rToon.posHprInterval(3, Point3(136, -212.9, 0), VBase3(-14, 0, 0), startPos=Point3(110.8, -292.7, 0), startHpr=VBase3(-14, 0, 0)), + goon.posHprInterval(3, Point3(125.2, -243.5, 0), VBase3(-14, 0, 0), startPos=Point3(104.8, -309.5, 0), startHpr=VBase3(-14, 0, 0))), + Func(self.__hideFakeGoons), + Func(crane.request, 'Free'), + Func(self.getGeomNode().setH, 0), + self.moveToonsToBattleThreePos(self.involvedToons), + Func(self.__showToons)) + return Sequence(Func(camera.reparentTo, self), Func(camera.setPosHpr, 0, -27, 25, 0, -18, 0), track) + + def moveToonsToBattleThreePos(self, toons): + track = Parallel() + for i in range(len(toons)): + toon = base.cr.doId2do.get(toons[i]) + if toon: + posHpr = ToontownGlobals.CashbotToonsBattleThreeStartPosHpr[i] + pos = Point3(*posHpr[0:3]) + hpr = VBase3(*posHpr[3:6]) + track.append(toon.posHprInterval(0.2, pos, hpr)) + + return track + + def makeBossFleeMovie(self): + hadEnough = TTLocalizer.CashbotBossHadEnough + outtaHere = TTLocalizer.CashbotBossOuttaHere + loco = loader.loadModel('phase_10/models/cogHQ/CashBotLocomotive') + car1 = loader.loadModel('phase_10/models/cogHQ/CashBotBoxCar') + car2 = loader.loadModel('phase_10/models/cogHQ/CashBotTankCar') + trainPassingSfx = base.loadSfx('phase_10/audio/sfx/CBHQ_TRAIN_pass.mp3') + boomSfx = loader.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3') + rollThroughDoor = self.rollBossToPoint(fromPos=Point3(120, -280, 0), fromHpr=None, toPos=Point3(120, -250, 0), toHpr=None, reverse=0) + rollTrack = Sequence(Func(self.getGeomNode().setH, 180), rollThroughDoor[0], Func(self.getGeomNode().setH, 0)) + g = 80.0 / 300.0 + trainTrack = Track( + (0 * g, loco.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (1 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (2 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (3 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (4 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (5 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (6 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (7 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (8 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (9 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (10 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (11 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (12 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (13 * g, car2.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0))), + (14 * g, car1.posInterval(0.5, Point3(0, -242, 0), startPos=Point3(150, -242, 0)))) + bossTrack = Track( + (0.0, Sequence( + Func(camera.reparentTo, render), + Func(camera.setPosHpr, 105, -280, 20, -158, -3, 0), + Func(self.reparentTo, render), + Func(self.show), + Func(self.clearChat), + Func(self.setPosHpr, *ToontownGlobals.CashbotBossBattleThreePosHpr), + Func(self.reverseHead), + ActorInterval(self, 'Fb_firstHit'), + ActorInterval(self, 'Fb_down2Up'))), + (1.0, Func(self.setChatAbsolute, hadEnough, CFSpeech)), + (5.5, Parallel( + Func(camera.setPosHpr, 100, -315, 16, -20, 0, 0), + Func(self.hideBattleThreeObjects), + Func(self.forwardHead), + Func(self.loop, 'Ff_neutral'), + rollTrack, + self.door3.posInterval(2.5, Point3(0, 0, 25), startPos=Point3(0, 0, 18)))), + (5.5, Func(self.setChatAbsolute, outtaHere, CFSpeech)), + (5.5, SoundInterval(trainPassingSfx)), + (8.1, Func(self.clearChat)), + (9.4, Sequence( + Func(loco.reparentTo, render), + Func(car1.reparentTo, render), + Func(car2.reparentTo, render), + trainTrack, + Func(loco.detachNode), + Func(car1.detachNode), + Func(car2.detachNode), + Wait(2))), + (9.5, SoundInterval(boomSfx)), + (9.5, Sequence( + self.posInterval(0.4, Point3(0, -250, 0)), + Func(self.stash)))) + return bossTrack + + def grabObject(self, obj): + obj.wrtReparentTo(self.neck) + obj.hideShadows() + obj.stashCollisions() + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.lerpInterval = Parallel(obj.posInterval(ToontownGlobals.CashbotBossToMagnetTime, Point3(-1, 0, 0.2)), obj.quatInterval(ToontownGlobals.CashbotBossToMagnetTime, VBase3(0, -90, 90)), Sequence(Wait(ToontownGlobals.CashbotBossToMagnetTime), ShowInterval(self.eyes)), obj.toMagnetSoundInterval) + obj.lerpInterval.start() + self.heldObject = obj + + def dropObject(self, obj): + if obj.lerpInterval: + obj.lerpInterval.finish() + obj.lerpInterval = None + obj = self.heldObject + obj.wrtReparentTo(render) + obj.setHpr(obj.getH(), 0, 0) + self.eyes.hide() + obj.showShadows() + obj.unstashCollisions() + self.heldObject = None + return + + def setBossDamage(self, bossDamage): + if bossDamage > self.bossDamage: + delta = bossDamage - self.bossDamage + self.flashRed() + self.doAnimate('hit', now=1) + self.showHpText(-delta, scale=5) + self.bossDamage = bossDamage + self.updateHealthBar() + + def setRewardId(self, rewardId): + self.rewardId = rewardId + + def d_applyReward(self): + self.sendUpdate('applyReward', []) + + def stunAllGoons(self): + for goon in self.goons: + if goon.state == 'Walk' or goon.state == 'Battle': + goon.demand('Stunned') + goon.sendUpdate('requestStunned', [0]) + + def destroyAllGoons(self): + for goon in self.goons: + if goon.state != 'Off' and not goon.isDead: + goon.b_destroyGoon() + + def deactivateCranes(self): + for crane in self.cranes.values(): + crane.demand('Free') + + def hideBattleThreeObjects(self): + for goon in self.goons: + goon.demand('Off') + + for safe in self.safes.values(): + safe.demand('Off') + + for crane in self.cranes.values(): + crane.demand('Off') + + def __doPhysics(self, task): + dt = globalClock.getDt() + self.physicsMgr.doPhysics(dt) + return Task.cont + + def __hideToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.hide() + + def __showToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.show() + + def __arrangeToonsAroundResistanceToon(self): + radius = 7 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in range(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.resistanceToon, x, y, 0) + toon.headsUp(self.resistanceToon) + toon.loop('neutral') + toon.show() + + def __talkAboutPromotion(self, speech): + if self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.ResistanceToonLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.ResistanceToonHPBoost + else: + speech += TTLocalizer.ResistanceToonMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + return speech + + def enterOff(self): + DistributedBossCog.DistributedBossCog.enterOff(self) + if self.resistanceToon: + self.resistanceToon.clearChat() + + def enterWaitForToons(self): + DistributedBossCog.DistributedBossCog.enterWaitForToons(self) + self.detachNode() + self.geom.hide() + self.resistanceToon.removeActive() + + def exitWaitForToons(self): + DistributedBossCog.DistributedBossCog.exitWaitForToons(self) + self.geom.show() + self.resistanceToon.addActive() + + def enterElevator(self): + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.detachNode() + self.resistanceToon.removeActive() + self.endVault.stash() + self.midVault.unstash() + self.__showResistanceToon(True) + + def exitElevator(self): + DistributedBossCog.DistributedBossCog.exitElevator(self) + self.resistanceToon.addActive() + + def enterIntroduction(self): + self.detachNode() + self.stopAnimate() + self.endVault.unstash() + self.evWalls.stash() + self.midVault.unstash() + self.__showResistanceToon(True) + base.playMusic(self.stingMusic, looping=1, volume=0.9) + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + + def exitIntroduction(self): + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.stingMusic.stop() + + def enterBattleOne(self): + DistributedBossCog.DistributedBossCog.enterBattleOne(self) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleOnePosHpr) + self.show() + self.pelvis.setHpr(self.pelvisReversedHpr) + self.doAnimate() + self.endVault.stash() + self.midVault.unstash() + self.__hideResistanceToon() + + def exitBattleOne(self): + DistributedBossCog.DistributedBossCog.exitBattleOne(self) + + def enterPrepareBattleThree(self): + self.controlToons() + NametagGlobals.setMasterArrowsOn(0) + intervalName = 'PrepareBattleThreeMovie' + delayDeletes = [] + self.movieCrane = self.cranes[0] + self.movieSafe = self.safes[1] + self.movieCrane.request('Movie') + seq = Sequence(self.makePrepareBattleThreeMovie(delayDeletes, self.movieCrane, self.movieSafe), Func(self.__beginBattleThree), name=intervalName) + seq.delayDeletes = delayDeletes + seq.start() + self.storeInterval(seq, intervalName) + self.endVault.unstash() + self.evWalls.stash() + self.midVault.unstash() + self.__showResistanceToon(False) + taskMgr.add(self.__doPhysics, self.uniqueName('physics'), priority=25) + + def __beginBattleThree(self): + intervalName = 'PrepareBattleThreeMovie' + self.clearInterval(intervalName) + self.doneBarrier('PrepareBattleThree') + + def exitPrepareBattleThree(self): + intervalName = 'PrepareBattleThreeMovie' + self.clearInterval(intervalName) + self.unstickToons() + self.releaseToons() + if self.newState == 'BattleThree': + self.movieCrane.request('Free') + self.movieSafe.request('Initial') + NametagGlobals.setMasterArrowsOn(1) + ElevatorUtils.closeDoors(self.leftDoor, self.rightDoor, ElevatorConstants.ELEVATOR_CFO) + taskMgr.remove(self.uniqueName('physics')) + + def enterBattleThree(self): + DistributedBossCog.DistributedBossCog.enterBattleThree(self) + self.clearChat() + self.resistanceToon.clearChat() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.endVault.unstash() + self.evWalls.unstash() + self.midVault.stash() + self.__hideResistanceToon() + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.generateHealthBar() + self.updateHealthBar() + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + taskMgr.add(self.__doPhysics, self.uniqueName('physics'), priority=25) + + def exitBattleThree(self): + DistributedBossCog.DistributedBossCog.exitBattleThree(self) + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.ignore(bossDoneEventName) + self.stopAnimate() + self.cleanupAttacks() + self.setDizzy(0) + self.removeHealthBar() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + if self.newState != 'Victory': + self.battleThreeMusic.stop() + taskMgr.remove(self.uniqueName('physics')) + + def enterVictory(self): + self.cleanupIntervals() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.stopAnimate() + self.endVault.unstash() + self.evWalls.unstash() + self.midVault.unstash() + self.__hideResistanceToon() + self.__hideToons() + self.clearChat() + self.resistanceToon.clearChat() + self.deactivateCranes() + if self.cranes: + self.cranes[1].demand('Off') + self.releaseToons(finalBattle=1) + if self.hasLocalToon(): + self.toMovieMode() + intervalName = 'VictoryMovie' + seq = Sequence(self.makeBossFleeMovie(), Func(self.__continueVictory), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + if self.oldState != 'BattleThree': + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + + def __continueVictory(self): + self.doneBarrier('Victory') + + def exitVictory(self): + self.cleanupIntervals() + if self.newState != 'Reward': + if self.hasLocalToon(): + self.toWalkMode() + self.__showToons() + self.door3.setPos(0, 0, 0) + if self.newState != 'Reward': + self.battleThreeMusic.stop() + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'CashbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + if self.oldState != 'Victory': + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + + def __doneReward(self): + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + if self.newState != 'Epilogue': + self.releaseToons() + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.battleThreeMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.resistanceToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + self.__showResistanceToon(False) + self.resistanceToon.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.resistanceToon.loop('neutral') + self.__arrangeToonsAroundResistanceToon() + camera.reparentTo(render) + camera.setPos(self.resistanceToon, -9, 12, 6) + camera.lookAt(self.resistanceToon, 0, 0, 3) + intervalName = 'EpilogueMovie' + text = ResistanceChat.getChatText(self.rewardId) + menuIndex, itemIndex = ResistanceChat.decodeId(self.rewardId) + value = ResistanceChat.getItemValue(self.rewardId) + if menuIndex == ResistanceChat.RESISTANCE_TOONUP: + if value == -1: + instructions = TTLocalizer.ResistanceToonToonupAllInstructions + else: + instructions = TTLocalizer.ResistanceToonToonupInstructions % value + elif menuIndex == ResistanceChat.RESISTANCE_MONEY: + if value == -1: + instructions = TTLocalizer.ResistanceToonMoneyAllInstructions + else: + instructions = TTLocalizer.ResistanceToonMoneyInstructions % value + elif menuIndex == ResistanceChat.RESISTANCE_RESTOCK: + if value == -1: + instructions = TTLocalizer.ResistanceToonRestockAllInstructions + else: + trackName = TTLocalizer.BattleGlobalTracks[value] + instructions = TTLocalizer.ResistanceToonRestockInstructions % trackName + speech = TTLocalizer.ResistanceToonCongratulations % (text, instructions) + speech = self.__talkAboutPromotion(speech) + self.resistanceToon.setLocalPageChat(speech, 0) + self.accept('nextChatPage', self.__epilogueChatNext) + self.accept('doneChatPage', self.__epilogueChatDone) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __epilogueChatNext(self, pageNumber, elapsed): + if pageNumber == 1: + toon = self.resistanceToon + playRate = 0.75 + track = Sequence(ActorInterval(toon, 'victory', playRate=playRate, startFrame=0, endFrame=9), ActorInterval(toon, 'victory', playRate=playRate, startFrame=9, endFrame=0), Func(self.resistanceToon.loop, 'neutral')) + intervalName = 'EpilogueMovieToonAnim' + self.storeInterval(track, intervalName) + track.start() + elif pageNumber == 3: + self.d_applyReward() + ResistanceChat.doEffect(self.rewardId, self.resistanceToon, self.involvedToons) + + def __epilogueChatDone(self, elapsed): + self.resistanceToon.setChatAbsolute(TTLocalizer.CagedToonGoodbye, CFSpeech) + self.ignore('nextChatPage') + self.ignore('doneChatPage') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(ActorInterval(self.resistanceToon, 'wave'), Func(self.resistanceToon.loop, 'neutral')), Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.epilogueMusic.stop() + + def enterFrolic(self): + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.setPosHpr(*ToontownGlobals.CashbotBossBattleOnePosHpr) + self.releaseToons() + if self.hasLocalToon(): + self.toWalkMode() + self.door3.setZ(25) + self.door2.setZ(25) + self.endVault.unstash() + self.evWalls.stash() + self.midVault.unstash() + self.__hideResistanceToon() + + def exitFrolic(self): + self.door3.setZ(0) + self.door2.setZ(0) diff --git a/toontown/suit/DistributedCashbotBossAI.py b/toontown/suit/DistributedCashbotBossAI.py new file mode 100644 index 0000000..f62c4c8 --- /dev/null +++ b/toontown/suit/DistributedCashbotBossAI.py @@ -0,0 +1,477 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.coghq import DistributedCashbotBossCraneAI +from toontown.coghq import DistributedCashbotBossSafeAI +from toontown.suit import DistributedCashbotBossGoonAI +from toontown.coghq import DistributedCashbotBossTreasureAI +from toontown.battle import BattleExperienceAI +from toontown.chat import ResistanceChat +from direct.fsm import FSM +import DistributedBossCogAI, SuitDNA, random, math + +class DistributedCashbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossAI') + maxGoons = 8 + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'm') + FSM.FSM.__init__(self, 'DistributedCashbotBossAI') + self.cranes = None + self.safes = None + self.goons = None + self.treasures = {} + self.grabbingTreasures = {} + self.recycledTreasures = [] + self.healAmount = 0 + self.rewardId = ResistanceChat.getRandomId() + self.rewardedToons = [] + self.scene = NodePath('scene') + self.reparentTo(self.scene) + cn = CollisionNode('walls') + cs = CollisionSphere(0, 0, 0, 13) + cn.addSolid(cs) + cs = CollisionInvSphere(0, 0, 0, 42) + cn.addSolid(cs) + self.attachNewNode(cn) + self.heldObject = None + self.waitingForHelmet = 0 + self.avatarHelmets = {} + self.bossMaxDamage = ToontownGlobals.CashbotBossMaxDamage + return + + def generate(self): + DistributedBossCogAI.DistributedBossCogAI.generate(self) + if __dev__: + self.scene.reparentTo(self.getRender()) + + def getHoodId(self): + return ToontownGlobals.CashbotHQ + + def formatReward(self): + return str(self.rewardId) + + def makeBattleOneBattles(self): + self.postBattleState = 'PrepareBattleThree' + self.initializeBattles(1, ToontownGlobals.CashbotBossBattleOnePosHpr) + + def generateSuits(self, battleNumber): + cogs = self.invokeSuitPlanner(11, 0) + skelecogs = self.invokeSuitPlanner(12, 1) + activeSuits = cogs['activeSuits'] + skelecogs['activeSuits'] + reserveSuits = cogs['reserveSuits'] + skelecogs['reserveSuits'] + random.shuffle(activeSuits) + while len(activeSuits) > 4: + suit = activeSuits.pop() + reserveSuits.append((suit, 100)) + + def compareJoinChance(a, b): + return cmp(a[1], b[1]) + + reserveSuits.sort(compareJoinChance) + return {'activeSuits': activeSuits, 'reserveSuits': reserveSuits} + + def removeToon(self, avId): + if self.cranes != None: + for crane in self.cranes: + crane.removeToon(avId) + + if self.safes != None: + for safe in self.safes: + safe.removeToon(avId) + + if self.goons != None: + for goon in self.goons: + goon.removeToon(avId) + + DistributedBossCogAI.DistributedBossCogAI.removeToon(self, avId) + return + + def __makeBattleThreeObjects(self): + if self.cranes == None: + self.cranes = [] + for index in range(len(ToontownGlobals.CashbotBossCranePosHprs)): + crane = DistributedCashbotBossCraneAI.DistributedCashbotBossCraneAI(self.air, self, index) + crane.generateWithRequired(self.zoneId) + self.cranes.append(crane) + + if self.safes == None: + self.safes = [] + for index in range(len(ToontownGlobals.CashbotBossSafePosHprs)): + safe = DistributedCashbotBossSafeAI.DistributedCashbotBossSafeAI(self.air, self, index) + safe.generateWithRequired(self.zoneId) + self.safes.append(safe) + + if self.goons == None: + self.goons = [] + return + + def __resetBattleThreeObjects(self): + if self.cranes != None: + for crane in self.cranes: + crane.request('Free') + + if self.safes != None: + for safe in self.safes: + safe.request('Initial') + + return + + def __deleteBattleThreeObjects(self): + if self.cranes != None: + for crane in self.cranes: + crane.request('Off') + crane.requestDelete() + + self.cranes = None + if self.safes != None: + for safe in self.safes: + safe.request('Off') + safe.requestDelete() + + self.safes = None + if self.goons != None: + for goon in self.goons: + goon.request('Off') + goon.requestDelete() + + self.goons = None + return + + def doNextAttack(self, task): + self.__doDirectedAttack() + if self.heldObject == None and not self.waitingForHelmet: + self.waitForNextHelmet() + return + + def __doDirectedAttack(self): + if self.toonsToAttack: + toonId = self.toonsToAttack.pop(0) + while toonId not in self.involvedToons: + if not self.toonsToAttack: + self.b_setAttackCode(ToontownGlobals.BossCogNoAttack) + return + toonId = self.toonsToAttack.pop(0) + + self.toonsToAttack.append(toonId) + self.b_setAttackCode(ToontownGlobals.BossCogSlowDirectedAttack, toonId) + + def reprieveToon(self, avId): + if avId in self.toonsToAttack: + i = self.toonsToAttack.index(avId) + del self.toonsToAttack[i] + self.toonsToAttack.append(avId) + + def makeTreasure(self, goon): + if self.state != 'BattleThree': + return + pos = goon.getPos(self) + v = Vec3(pos[0], pos[1], 0.0) + if not v.normalize(): + v = Vec3(1, 0, 0) + v = v * 27 + angle = random.uniform(0.0, 2.0 * math.pi) + radius = 10 + dx = radius * math.cos(angle) + dy = radius * math.sin(angle) + fpos = self.scene.getRelativePoint(self, Point3(v[0] + dx, v[1] + dy, 0)) + if goon.strength <= 10: + style = ToontownGlobals.ToontownCentral + healAmount = 3 + else: + if goon.strength <= 15: + style = random.choice([ToontownGlobals.DonaldsDock, ToontownGlobals.DaisyGardens, ToontownGlobals.MinniesMelodyland]) + healAmount = 10 + else: + style = random.choice([ToontownGlobals.TheBrrrgh, ToontownGlobals.DonaldsDreamland]) + healAmount = 12 + if self.recycledTreasures: + treasure = self.recycledTreasures.pop(0) + treasure.d_setGrab(0) + treasure.b_setGoonId(goon.doId) + treasure.b_setStyle(style) + treasure.b_setPosition(pos[0], pos[1], 0) + treasure.b_setFinalPosition(fpos[0], fpos[1], 0) + else: + treasure = DistributedCashbotBossTreasureAI.DistributedCashbotBossTreasureAI(self.air, self, goon, style, fpos[0], fpos[1], 0) + treasure.generateWithRequired(self.zoneId) + treasure.healAmount = healAmount + self.treasures[treasure.doId] = treasure + + def grabAttempt(self, avId, treasureId): + av = self.air.doId2do.get(avId) + if not av: + return + treasure = self.treasures.get(treasureId) + if treasure: + if treasure.validAvatar(av): + del self.treasures[treasureId] + treasure.d_setGrab(avId) + self.grabbingTreasures[treasureId] = treasure + taskMgr.doMethodLater(5, self.__recycleTreasure, treasure.uniqueName('recycleTreasure'), extraArgs=[treasure]) + else: + treasure.d_setReject() + + def __recycleTreasure(self, treasure): + if self.grabbingTreasures.has_key(treasure.doId): + del self.grabbingTreasures[treasure.doId] + self.recycledTreasures.append(treasure) + + def deleteAllTreasures(self): + for treasure in self.treasures.values(): + treasure.requestDelete() + + self.treasures = {} + for treasure in self.grabbingTreasures.values(): + taskMgr.remove(treasure.uniqueName('recycleTreasure')) + treasure.requestDelete() + + self.grabbingTreasures = {} + for treasure in self.recycledTreasures: + treasure.requestDelete() + + self.recycledTreasures = [] + + def getMaxGoons(self): + t = self.getBattleThreeTime() + if t <= 1.0: + return self.maxGoons + else: + if t <= 1.1: + return self.maxGoons + 1 + else: + if t <= 1.2: + return self.maxGoons + 2 + else: + if t <= 1.3: + return self.maxGoons + 3 + else: + if t <= 1.4: + return self.maxGoons + 4 + else: + return self.maxGoons + 8 + + def makeGoon(self, side=None): + if side == None: + side = random.choice(['EmergeA', 'EmergeB']) + goon = self.__chooseOldGoon() + if goon == None: + if len(self.goons) >= self.getMaxGoons(): + return + goon = DistributedCashbotBossGoonAI.DistributedCashbotBossGoonAI(self.air, self) + goon.generateWithRequired(self.zoneId) + self.goons.append(goon) + if self.getBattleThreeTime() > 1.0: + goon.STUN_TIME = 4 + goon.b_setupGoon(velocity=8, hFov=90, attackRadius=20, strength=30, scale=1.8) + else: + goon.STUN_TIME = self.progressValue(30, 8) + goon.b_setupGoon(velocity=self.progressRandomValue(3, 7), hFov=self.progressRandomValue(70, 80), attackRadius=self.progressRandomValue(6, 15), strength=int(self.progressRandomValue(5, 25)), scale=self.progressRandomValue(0.5, 1.5)) + goon.request(side) + return + + def __chooseOldGoon(self): + for goon in self.goons: + if goon.state == 'Off': + return goon + + def waitForNextGoon(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextGoon') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextGoon, taskName) + + def stopGoons(self): + taskName = self.uniqueName('NextGoon') + taskMgr.remove(taskName) + + def doNextGoon(self, task): + if self.attackCode != ToontownGlobals.BossCogDizzy: + self.makeGoon() + delayTime = self.progressValue(10, 2) + self.waitForNextGoon(delayTime) + + def waitForNextHelmet(self): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextHelmet') + taskMgr.remove(taskName) + delayTime = self.progressValue(45, 15) + taskMgr.doMethodLater(delayTime, self.__donHelmet, taskName) + self.waitingForHelmet = 1 + + def __donHelmet(self, task): + self.waitingForHelmet = 0 + if self.heldObject == None: + safe = self.safes[0] + safe.request('Grabbed', self.doId, self.doId) + self.heldObject = safe + return + + def stopHelmets(self): + self.waitingForHelmet = 0 + taskName = self.uniqueName('NextHelmet') + taskMgr.remove(taskName) + + def acceptHelmetFrom(self, avId): + now = globalClock.getFrameTime() + then = self.avatarHelmets.get(avId, None) + if then == None or now - then > 300: + self.avatarHelmets[avId] = now + return 1 + return 0 + + def magicWordHit(self, damage, avId): + if self.heldObject: + self.heldObject.demand('Dropped', avId, self.doId) + self.heldObject.avoidHelmet = 1 + self.heldObject = None + self.waitForNextHelmet() + else: + self.recordHit(damage) + return + + def magicWordReset(self): + if self.state == 'BattleThree': + self.__resetBattleThreeObjects() + + def magicWordResetGoons(self): + if self.state == 'BattleThree': + if self.goons != None: + for goon in self.goons: + goon.request('Off') + goon.requestDelete() + + self.goons = None + self.__makeBattleThreeObjects() + return + + def recordHit(self, damage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'recordHit from unknown avatar'): + return + if self.state != 'BattleThree': + return + self.b_setBossDamage(self.bossDamage + damage) + if self.bossDamage >= self.bossMaxDamage: + self.b_setState('Victory') + else: + if self.attackCode != ToontownGlobals.BossCogDizzy: + if damage >= ToontownGlobals.CashbotBossKnockoutDamage: + self.b_setAttackCode(ToontownGlobals.BossCogDizzy) + self.stopHelmets() + else: + self.b_setAttackCode(ToontownGlobals.BossCogNoAttack) + self.stopHelmets() + self.waitForNextHelmet() + + def b_setBossDamage(self, bossDamage): + self.d_setBossDamage(bossDamage) + self.setBossDamage(bossDamage) + + def setBossDamage(self, bossDamage): + self.reportToonHealth() + self.bossDamage = bossDamage + + def d_setBossDamage(self, bossDamage): + self.sendUpdate('setBossDamage', [bossDamage]) + + def d_setRewardId(self, rewardId): + self.sendUpdate('setRewardId', [rewardId]) + + def applyReward(self): + avId = self.air.getAvatarIdFromSender() + if avId in self.involvedToons and avId not in self.rewardedToons: + self.rewardedToons.append(avId) + toon = self.air.doId2do.get(avId) + if toon: + toon.doResistanceEffect(self.rewardId) + + def enterOff(self): + DistributedBossCogAI.DistributedBossCogAI.enterOff(self) + self.rewardedToons = [] + + def exitOff(self): + DistributedBossCogAI.DistributedBossCogAI.exitOff(self) + + def enterIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.enterIntroduction(self) + self.__makeBattleThreeObjects() + self.__resetBattleThreeObjects() + + def exitIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.exitIntroduction(self) + self.__deleteBattleThreeObjects() + + def enterPrepareBattleThree(self): + self.resetBattles() + self.__makeBattleThreeObjects() + self.__resetBattleThreeObjects() + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, 55, self.__donePrepareBattleThree) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def exitPrepareBattleThree(self): + if self.newState != 'BattleThree': + self.__deleteBattleThreeObjects() + self.ignoreBarrier(self.barrier) + + def enterBattleThree(self): + self.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr) + self.__makeBattleThreeObjects() + self.__resetBattleThreeObjects() + self.reportToonHealth() + self.toonsToAttack = self.involvedToons[:] + random.shuffle(self.toonsToAttack) + self.b_setBossDamage(0) + self.battleThreeStart = globalClock.getFrameTime() + self.resetBattles() + self.waitForNextAttack(15) + self.waitForNextHelmet() + self.makeGoon(side='EmergeA') + self.makeGoon(side='EmergeB') + taskName = self.uniqueName('NextGoon') + taskMgr.remove(taskName) + taskMgr.doMethodLater(2, self.__doInitialGoons, taskName) + + def __doInitialGoons(self, task): + self.makeGoon(side='EmergeA') + self.makeGoon(side='EmergeB') + self.waitForNextGoon(10) + + def exitBattleThree(self): + helmetName = self.uniqueName('helmet') + taskMgr.remove(helmetName) + if self.newState != 'Victory': + self.__deleteBattleThreeObjects() + self.deleteAllTreasures() + self.stopAttacks() + self.stopGoons() + self.stopHelmets() + self.heldObject = None + return + + def enterVictory(self): + self.resetBattles() + self.suitsKilled.append({'type': None, 'level': None, 'track': self.dna.dept, 'isSkelecog': 0, 'isForeman': 0, 'isVP': 0, 'isCFO': 1, 'isSupervisor': 0, 'isVirtual': 0, 'activeToons': self.involvedToons[:]}) + self.barrier = self.beginBarrier('Victory', self.involvedToons, 30, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + toon.addResistanceMessage(self.rewardId) + toon.b_promote(self.deptIndex) + + def exitVictory(self): + self.__deleteBattleThreeObjects() + + def enterEpilogue(self): + DistributedBossCogAI.DistributedBossCogAI.enterEpilogue(self) + self.d_setRewardId(self.rewardId) diff --git a/toontown/suit/DistributedCashbotBossGoon.py b/toontown/suit/DistributedCashbotBossGoon.py new file mode 100644 index 0000000..d87e5b0 --- /dev/null +++ b/toontown/suit/DistributedCashbotBossGoon.py @@ -0,0 +1,254 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.task.TaskManagerGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +import GoonGlobals +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from toontown.coghq import DistributedCashbotBossObject +from direct.showbase import PythonUtil +import DistributedGoon + +class DistributedCashbotBossGoon(DistributedGoon.DistributedGoon, DistributedCashbotBossObject.DistributedCashbotBossObject): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossGoon') + walkGrabZ = -3.6 + stunGrabZ = -2.2 + wiggleFreeTime = 2 + craneFrictionCoef = 0.15 + craneSlideSpeed = 10 + craneRotateSpeed = 20 + + def __init__(self, cr): + DistributedCashbotBossObject.DistributedCashbotBossObject.__init__(self, cr) + DistributedGoon.DistributedGoon.__init__(self, cr) + self.target = None + self.arrivalTime = None + self.flyToMagnetSfx = loader.loadSfx('phase_5/audio/sfx/TL_rake_throw_only.mp3') + self.hitMagnetSfx = loader.loadSfx('phase_4/audio/sfx/AA_drop_anvil_miss.mp3') + self.toMagnetSoundInterval = Sequence(SoundInterval(self.flyToMagnetSfx, duration=ToontownGlobals.CashbotBossToMagnetTime, node=self), SoundInterval(self.hitMagnetSfx, node=self)) + self.hitFloorSfx = loader.loadSfx('phase_5/audio/sfx/AA_drop_flowerpot.mp3') + self.hitFloorSoundInterval = SoundInterval(self.hitFloorSfx, duration=1.0, node=self) + self.wiggleSfx = loader.loadSfx('phase_5/audio/sfx/SA_finger_wag.mp3') + return + + def generate(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.generate(self) + DistributedGoon.DistributedGoon.generate(self) + + def announceGenerate(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.announceGenerate(self) + self.setupPhysics('goon') + DistributedGoon.DistributedGoon.announceGenerate(self) + self.name = 'goon-%s' % self.doId + self.setName(self.name) + self.setTag('doId', str(self.doId)) + self.collisionNode.setName('goon') + cs = CollisionSphere(0, 0, 4, 4) + self.collisionNode.addSolid(cs) + self.collisionNode.setIntoCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CashbotBossObjectBitmask) + self.wiggleTaskName = self.uniqueName('wiggleTask') + self.wiggleFreeName = self.uniqueName('wiggleFree') + self.boss.goons.append(self) + self.reparentTo(render) + + def disable(self): + i = self.boss.goons.index(self) + del self.boss.goons[i] + DistributedGoon.DistributedGoon.disable(self) + DistributedCashbotBossObject.DistributedCashbotBossObject.disable(self) + + def delete(self): + DistributedGoon.DistributedGoon.delete(self) + DistributedCashbotBossObject.DistributedCashbotBossObject.delete(self) + + def hideShadows(self): + self.dropShadow.hide() + + def showShadows(self): + self.dropShadow.show() + + def getMinImpact(self): + return ToontownGlobals.CashbotBossGoonImpact + + def doHitBoss(self, impact): + self.d_hitBoss(impact) + self.b_destroyGoon() + + def __startWalk(self): + self.__stopWalk() + if self.target: + now = globalClock.getFrameTime() + availableTime = self.arrivalTime - now + if availableTime > 0: + origH = self.getH() + h = PythonUtil.fitDestAngle2Src(origH, self.targetH) + delta = abs(h - origH) + turnTime = delta / (self.velocity * 5) + dist = Vec3(self.target - self.getPos()).length() + walkTime = dist / self.velocity + denom = turnTime + walkTime + if denom != 0: + timeCompress = availableTime / denom + self.walkTrack = Sequence(self.hprInterval(turnTime * timeCompress, VBase3(h, 0, 0)), self.posInterval(walkTime * timeCompress, self.target)) + self.walkTrack.start() + else: + self.setPos(self.target) + self.setH(self.targetH) + + def __stopWalk(self): + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + return + + def __wiggleTask(self, task): + elapsed = globalClock.getFrameTime() - self.wiggleStart + h = math.sin(elapsed * 17) * 5 + p = math.sin(elapsed * 29) * 10 + self.crane.wiggleMagnet.setHpr(h, p, 0) + return Task.cont + + def __wiggleFree(self, task): + self.crane.releaseObject() + self.stashCollisions() + return Task.done + + def fellOut(self): + self.b_destroyGoon() + + def handleToonDetect(self, collEntry = None): + if self.boss.localToonIsSafe: + return + DistributedGoon.DistributedGoon.handleToonDetect(self, collEntry) + + def prepareGrab(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.prepareGrab(self) + if self.isStunned or self.boss.localToonIsSafe: + self.pose('collapse', 48) + self.grabPos = (0, 0, self.stunGrabZ * self.scale) + else: + self.setPlayRate(4, 'walk') + self.loop('walk') + self.grabPos = (0, 0, self.walkGrabZ * self.scale) + self.wiggleStart = globalClock.getFrameTime() + taskMgr.add(self.__wiggleTask, self.wiggleTaskName) + base.sfxPlayer.playSfx(self.wiggleSfx, node=self) + if self.avId == localAvatar.doId: + taskMgr.doMethodLater(self.wiggleFreeTime, self.__wiggleFree, self.wiggleFreeName) + self.radar.hide() + + def prepareRelease(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.prepareRelease(self) + self.crane.wiggleMagnet.setHpr(0, 0, 0) + taskMgr.remove(self.wiggleTaskName) + taskMgr.remove(self.wiggleFreeName) + self.setPlayRate(self.animMultiplier, 'walk') + + def setObjectState(self, state, avId, craneId): + if state == 'W': + self.demand('Walk') + elif state == 'B': + if self.state != 'Battle': + self.demand('Battle') + elif state == 'S': + if self.state != 'Stunned': + self.demand('Stunned') + elif state == 'R': + if self.state != 'Recovery': + self.demand('Recovery') + elif state == 'a': + self.demand('EmergeA') + elif state == 'b': + self.demand('EmergeB') + else: + DistributedCashbotBossObject.DistributedCashbotBossObject.setObjectState(self, state, avId, craneId) + + def setTarget(self, x, y, h, arrivalTime): + self.target = Point3(x, y, 0) + self.targetH = h + now = globalClock.getFrameTime() + self.arrivalTime = globalClockDelta.networkToLocalTime(arrivalTime, now) + if self.state == 'Walk': + self.__startWalk() + + def d_destroyGoon(self): + self.sendUpdate('destroyGoon') + + def b_destroyGoon(self): + if not self.isDead: + self.d_destroyGoon() + self.destroyGoon() + + def destroyGoon(self): + if not self.isDead: + self.playCrushMovie(None, None) + self.demand('Off') + return + + def enterOff(self): + DistributedGoon.DistributedGoon.enterOff(self) + DistributedCashbotBossObject.DistributedCashbotBossObject.enterOff(self) + + def exitOff(self): + DistributedCashbotBossObject.DistributedCashbotBossObject.exitOff(self) + DistributedGoon.DistributedGoon.exitOff(self) + + def enterWalk(self, avId = None, ts = 0): + self.startToonDetect() + self.isStunned = 0 + self.__startWalk() + self.loop('walk', 0) + self.unstashCollisions() + + def exitWalk(self): + self.__stopWalk() + self.stopToonDetect() + self.stop() + + def enterEmergeA(self): + self.undead() + self.reparentTo(render) + self.stopToonDetect() + self.boss.doorA.request('open') + self.radar.hide() + self.__startWalk() + self.loop('walk', 0) + + def exitEmergeA(self): + if self.boss.doorA: + self.boss.doorA.request('close') + self.radar.show() + self.__stopWalk() + + def enterEmergeB(self): + self.undead() + self.reparentTo(render) + self.stopToonDetect() + self.boss.doorB.request('open') + self.radar.hide() + self.__startWalk() + self.loop('walk', 0) + + def exitEmergeB(self): + if self.boss.doorB: + self.boss.doorB.request('close') + self.radar.show() + self.__stopWalk() + + def enterBattle(self, avId = None, ts = 0): + DistributedGoon.DistributedGoon.enterBattle(self, avId, ts) + avatar = base.cr.doId2do.get(avId) + if avatar: + messenger.send('exitCrane') + avatar.stunToon() + self.unstashCollisions() + + def enterStunned(self, ts = 0): + DistributedGoon.DistributedGoon.enterStunned(self, ts) + self.unstashCollisions() + + def enterRecovery(self, ts = 0, pauseTime = 0): + DistributedGoon.DistributedGoon.enterRecovery(self, ts, pauseTime) + self.unstashCollisions() diff --git a/toontown/suit/DistributedCashbotBossGoonAI.py b/toontown/suit/DistributedCashbotBossGoonAI.py new file mode 100644 index 0000000..1bd123c --- /dev/null +++ b/toontown/suit/DistributedCashbotBossGoonAI.py @@ -0,0 +1,299 @@ +from pandac.PandaModules import * +from direct.task.TaskManagerGlobal import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +import GoonGlobals +from direct.task.Task import Task +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals +from toontown.coghq import DistributedCashbotBossObjectAI +from direct.showbase import PythonUtil +import DistributedGoonAI, math, random + +class DistributedCashbotBossGoonAI(DistributedGoonAI.DistributedGoonAI, DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI): + legLength = 10 + directionTable = [ + (0, 15), (10, 10), (-10, 10), (20, 8), (-20, 8), (40, 5), (-40, 5), (60, 4), (-60, 4), (80, 3), (-80, 3), (120, 2), (-120, 2), (180, 1)] + offMask = BitMask32(0) + onMask = CollisionNode.getDefaultCollideMask() + + def __init__(self, air, boss): + DistributedGoonAI.DistributedGoonAI.__init__(self, air, 0) + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.__init__(self, air, boss) + cn = CollisionNode('tubeNode') + self.tube = CollisionTube(0, 0, 0, 0, 0, 0, 2) + cn.addSolid(self.tube) + self.tubeNode = cn + self.tubeNodePath = self.attachNewNode(self.tubeNode) + self.feelers = [] + cn = CollisionNode('feelerNode') + self.feelerLength = self.legLength * 1.5 + feelerStart = 1 + for heading, weight in self.directionTable: + rad = deg2Rad(heading) + x = -math.sin(rad) + y = math.cos(rad) + seg = CollisionSegment(x * feelerStart, y * feelerStart, 0, x * self.feelerLength, y * self.feelerLength, 0) + cn.addSolid(seg) + self.feelers.append(seg) + + cn.setIntoCollideMask(self.offMask) + self.feelerNodePath = self.attachNewNode(cn) + self.isWalking = 0 + self.cTrav = CollisionTraverser('goon') + self.cQueue = CollisionHandlerQueue() + self.cTrav.addCollider(self.feelerNodePath, self.cQueue) + + def requestBattle(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + avatar = self.air.doId2do.get(avId) + if avatar: + self.boss.damageToon(avatar, self.strength) + DistributedGoonAI.DistributedGoonAI.requestBattle(self, pauseTime) + + def sendMovie(self, type, avId=0, pauseTime=0): + if type == GoonGlobals.GOON_MOVIE_WALK: + self.demand('Walk') + else: + if type == GoonGlobals.GOON_MOVIE_BATTLE: + self.demand('Battle') + else: + if type == GoonGlobals.GOON_MOVIE_STUNNED: + self.demand('Stunned') + else: + if type == GoonGlobals.GOON_MOVIE_RECOVERY: + self.demand('Recovery') + else: + self.notify.warning('Ignoring movie type %s' % type) + + def __chooseTarget(self, extraDelay=0): + direction = self.__chooseDirection() + if direction == None: + self.target = None + self.arrivalTime = None + self.b_destroyGoon() + return + heading, dist = direction + dist = min(dist, self.legLength) + targetH = PythonUtil.reduceAngle(self.getH() + heading) + origH = self.getH() + h = PythonUtil.fitDestAngle2Src(origH, targetH) + delta = abs(h - origH) + turnTime = delta / (self.velocity * 5) + walkTime = dist / self.velocity + self.setH(targetH) + self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0)) + self.departureTime = globalClock.getFrameTime() + self.arrivalTime = self.departureTime + turnTime + walkTime + extraDelay + self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime)) + return + + def __chooseDirection(self): + self.tubeNode.setIntoCollideMask(self.offMask) + self.cTrav.traverse(self.boss.scene) + self.tubeNode.setIntoCollideMask(self.onMask) + entries = {} + self.cQueue.sortEntries() + for i in range(self.cQueue.getNumEntries() - 1, -1, -1): + entry = self.cQueue.getEntry(i) + dist = Vec3(entry.getSurfacePoint(self)).length() + if dist < 1.2: + dist = 0 + entries[entry.getFrom()] = dist + + netScore = 0 + scoreTable = [] + for i in range(len(self.directionTable)): + heading, weight = self.directionTable[i] + seg = self.feelers[i] + dist = entries.get(seg, self.feelerLength) + score = dist * weight + netScore += score + scoreTable.append(score) + + if netScore == 0: + self.notify.info('Could not find a path for %s' % self.doId) + return None + s = random.uniform(0, netScore) + for i in range(len(self.directionTable)): + s -= scoreTable[i] + if s <= 0: + heading, weight = self.directionTable[i] + seg = self.feelers[i] + dist = entries.get(seg, self.feelerLength) + return ( + heading, dist) + + self.notify.warning('Fell off end of weighted table.') + return ( + 0, self.legLength) + + def __startWalk(self): + if self.arrivalTime == None: + return + now = globalClock.getFrameTime() + availableTime = self.arrivalTime - now + if availableTime > 0: + point = self.getRelativePoint(self.boss.scene, self.target) + self.tube.setPointB(point) + self.node().resetPrevTransform() + taskMgr.doMethodLater(availableTime, self.__reachedTarget, self.uniqueName('reachedTarget')) + self.isWalking = 1 + else: + self.__reachedTarget(None) + return + + def __stopWalk(self, pauseTime=None): + if self.isWalking: + taskMgr.remove(self.uniqueName('reachedTarget')) + if pauseTime == None: + now = globalClock.getFrameTime() + t = (now - self.departureTime) / (self.arrivalTime - self.departureTime) + else: + t = pauseTime / (self.arrivalTime - self.departureTime) + t = min(t, 1.0) + pos = self.getPos() + self.setPos(pos + (self.target - pos) * t) + self.tube.setPointB(0, 0, 0) + self.isWalking = 0 + return + + def __reachedTarget(self, task): + self.__stopWalk() + self.__chooseTarget() + self.__startWalk() + + def __recoverWalk(self, task): + self.demand('Walk') + return Task.done + + def doFree(self, task): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.doFree(self, task) + self.demand('Walk') + return Task.done + + def requestStunned(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + if avId not in self.boss.involvedToons: + return + if self.state == 'Stunned' or self.state == 'Grabbed': + return + toon = self.air.doId2do.get(avId) + if toon: + toonDistance = self.getPos(toon).length() + if toonDistance > self.attackRadius * 2: + self.air.writeServerEvent('suspicious', avId, 'Stunned a goon, but outside of attack radius. Possible multihack.') + taskMgr.doMethodLater(0, self.__recoverWalk, self.uniqueName('recoverWalk')) + return + self.__stopWalk(pauseTime) + self.boss.makeTreasure(self) + DistributedGoonAI.DistributedGoonAI.requestStunned(self, pauseTime) + + 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' or self.state == 'Grabbed': + if not self.boss.heldObject: + damage = int(impact * 25 * self.scale) + self.boss.recordHit(max(damage, 2)) + self.b_destroyGoon() + + def d_setTarget(self, x, y, h, arrivalTime): + self.sendUpdate('setTarget', [x, y, h, arrivalTime]) + + def d_destroyGoon(self): + self.sendUpdate('destroyGoon') + + def b_destroyGoon(self): + self.d_destroyGoon() + self.destroyGoon() + + def destroyGoon(self): + self.demand('Off') + + def enterOff(self): + self.tubeNodePath.stash() + self.feelerNodePath.stash() + + def exitOff(self): + self.tubeNodePath.unstash() + self.feelerNodePath.unstash() + + def enterGrabbed(self, avId, craneId): + DistributedCashbotBossObjectAI.DistributedCashbotBossObjectAI.enterGrabbed(self, avId, craneId) + taskMgr.remove(self.taskName('recovery')) + taskMgr.remove(self.taskName('resumeWalk')) + + def enterWalk(self): + self.avId = 0 + self.craneId = 0 + self.__chooseTarget() + self.__startWalk() + self.d_setObjectState('W', 0, 0) + + def exitWalk(self): + self.__stopWalk() + + def enterEmergeA(self): + self.avId = 0 + self.craneId = 0 + h = 0 + dist = 15 + pos = self.boss.getPos() + walkTime = dist / self.velocity + self.setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.d_setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0)) + self.departureTime = globalClock.getFrameTime() + self.arrivalTime = self.departureTime + walkTime + self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime)) + self.__startWalk() + self.d_setObjectState('a', 0, 0) + taskMgr.doMethodLater(walkTime, self.__recoverWalk, self.uniqueName('recoverWalk')) + + def exitEmergeA(self): + self.__stopWalk() + taskMgr.remove(self.uniqueName('recoverWalk')) + + def enterEmergeB(self): + self.avId = 0 + self.craneId = 0 + h = 180 + dist = 15 + pos = self.boss.getPos() + walkTime = dist / self.velocity + self.setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.d_setPosHpr(pos[0], pos[1], pos[2], h, 0, 0) + self.target = self.boss.scene.getRelativePoint(self, Point3(0, dist, 0)) + self.departureTime = globalClock.getFrameTime() + self.arrivalTime = self.departureTime + walkTime + self.d_setTarget(self.target[0], self.target[1], h, globalClockDelta.localToNetworkTime(self.arrivalTime)) + self.__startWalk() + self.d_setObjectState('b', 0, 0) + taskMgr.doMethodLater(walkTime, self.__recoverWalk, self.uniqueName('recoverWalk')) + + def exitEmergeB(self): + self.__stopWalk() + taskMgr.remove(self.uniqueName('recoverWalk')) + + def enterBattle(self): + self.d_setObjectState('B', 0, 0) + + def exitBattle(self): + taskMgr.remove(self.taskName('resumeWalk')) + + def enterStunned(self): + self.d_setObjectState('S', 0, 0) + + def exitStunned(self): + taskMgr.remove(self.taskName('recovery')) + + def enterRecovery(self): + self.d_setObjectState('R', 0, 0) + taskMgr.doMethodLater(2.0, self.__recoverWalk, self.uniqueName('recoverWalk')) + + def exitRecovery(self): + self.__stopWalk() + taskMgr.remove(self.uniqueName('recoverWalk')) diff --git a/toontown/suit/DistributedFactorySuit.py b/toontown/suit/DistributedFactorySuit.py new file mode 100644 index 0000000..39cc715 --- /dev/null +++ b/toontown/suit/DistributedFactorySuit.py @@ -0,0 +1,414 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +import DistributedSuitBase +from direct.task.Task import Task +import random +from toontown.toonbase import ToontownGlobals +from otp.level import LevelConstants +from toontown.distributed.DelayDeletable import DelayDeletable + +class DistributedFactorySuit(DistributedSuitBase.DistributedSuitBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactorySuit') + + def __init__(self, cr): + try: + self.DistributedSuit_initialized + except: + self.DistributedSuit_initialized = 1 + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedSuit', [State.State('Off', self.enterOff, self.exitOff, ['Walk', 'Battle']), + State.State('Walk', self.enterWalk, self.exitWalk, ['WaitForBattle', 'Battle', 'Chase']), + State.State('Chase', self.enterChase, self.exitChase, ['WaitForBattle', 'Battle', 'Return']), + State.State('Return', self.enterReturn, self.exitReturn, ['WaitForBattle', 'Battle', 'Walk']), + State.State('Battle', self.enterBattle, self.exitBattle, ['Walk', 'Chase', 'Return']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['Battle'])], 'Off', 'Off') + self.path = None + self.walkTrack = None + self.chaseTrack = None + self.returnTrack = None + self.fsm.enterInitialState() + self.chasing = 0 + self.paused = 0 + self.pauseTime = 0 + self.velocity = 3 + self.factoryRequest = None + + return + + def generate(self): + DistributedSuitBase.DistributedSuitBase.generate(self) + + def setLevelDoId(self, levelDoId): + self.notify.debug('setLevelDoId(%s)' % levelDoId) + self.levelDoId = levelDoId + + def setCogId(self, cogId): + self.cogId = cogId + + def setReserve(self, reserve): + self.reserve = reserve + + def denyBattle(self): + self.notify.warning('denyBattle()') + place = self.cr.playGame.getPlace() + if place.fsm.getCurrentState().getName() == 'WaitForBattle': + place.setState('walk') + + def doReparent(self): + self.notify.debug('Suit requesting reparenting') + if not hasattr(self, 'factory'): + self.notify.warning('no factory, get Redmond to look at DistributedFactorySuit.announceGenerate()') + self.factory.requestReparent(self, self.spec['parentEntId']) + if self.pathEntId: + self.factory.setEntityCreateCallback(self.pathEntId, self.setPath) + else: + self.setPath() + + def setCogSpec(self, spec): + self.spec = spec + self.setPos(spec['pos']) + self.setH(spec['h']) + self.originalPos = spec['pos'] + self.escapePos = spec['pos'] + self.pathEntId = spec['path'] + self.behavior = spec['behavior'] + self.skeleton = spec['skeleton'] + self.revives = spec.get('revives') + self.boss = spec['boss'] + if self.reserve: + self.reparentTo(hidden) + else: + self.doReparent() + + def comeOutOfReserve(self): + self.doReparent() + + def getCogSpec(self, cogId): + if self.reserve: + return self.factory.getReserveCogSpec(cogId) + else: + return self.factory.getCogSpec(cogId) + + def announceGenerate(self): + self.notify.debug('announceGenerate %s' % self.doId) + + def onFactoryGenerate(factoryList, self = self): + self.factory = factoryList[0] + + def onFactoryReady(self = self): + self.notify.debug('factory ready, read spec') + spec = self.getCogSpec(self.cogId) + self.setCogSpec(spec) + self.factoryRequest = None + return + + self.factory.setEntityCreateCallback(LevelConstants.LevelMgrEntId, onFactoryReady) + + self.factoryRequest = self.cr.relatedObjectMgr.requestObjects([self.levelDoId], onFactoryGenerate) + DistributedSuitBase.DistributedSuitBase.announceGenerate(self) + + def disable(self): + self.ignoreAll() + if self.factoryRequest is not None: + self.cr.relatedObjectMgr.abortRequest(self.factoryRequest) + self.factoryRequest = None + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.setState('Off') + if self.walkTrack: + del self.walkTrack + self.walkTrack = None + DistributedSuitBase.DistributedSuitBase.disable(self) + taskMgr.remove(self.taskName('returnTask')) + taskMgr.remove(self.taskName('checkStray')) + taskMgr.remove(self.taskName('chaseTask')) + return + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.factory.lockVisibility(zoneNum=self.factory.getEntityZoneEntId(self.spec['parentEntId'])) + self.sendUpdate('requestBattle', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]]) + + def handleBattleBlockerCollision(self): + self.__handleToonCollision(None) + return + + def __handleToonCollision(self, collEntry): + if collEntry: + if collEntry.getFromNodePath().getParent().getKey() != localAvatar.getKey(): + return + if hasattr(self, 'factory') and hasattr(self.factory, 'lastToonZone'): + factoryZone = self.factory.lastToonZone + unitsBelow = self.getPos(render)[2] - base.localAvatar.getPos(render)[2] + if factoryZone == 24 and unitsBelow > 10.0: + self.notify.warning('Ignoring toon collision in %d from %f below.' % (factoryZone, unitsBelow)) + return + if not base.localAvatar.wantBattles: + return + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit %d: requesting a Battle with toon: %d' % (self.doId, toonId)) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + return None + + def setPath(self): + self.notify.debug('setPath %s' % self.doId) + if self.pathEntId != None: + parent = self.factory.entities.get(self.spec['parentEntId']) + self.path = self.factory.entities.get(self.pathEntId) + self.idealPathNode = self.path.attachNewNode('idealPath') + self.reparentTo(self.idealPathNode) + self.setPos(0, 0, 0) + self.path.reparentTo(parent) + self.walkTrack = self.path.makePathTrack(self.idealPathNode, self.velocity, self.uniqueName('suitWalk')) + self.setState('Walk') + return + + def initializeBodyCollisions(self, collIdStr): + DistributedSuitBase.DistributedSuitBase.initializeBodyCollisions(self, collIdStr) + self.sSphere = CollisionSphere(0.0, 0.0, 0.0, 15) + name = self.uniqueName('toonSphere') + self.sSphereNode = CollisionNode(name) + self.sSphereNode.addSolid(self.sSphere) + self.sSphereNodePath = self.attachNewNode(self.sSphereNode) + self.sSphereNodePath.hide() + self.sSphereBitMask = ToontownGlobals.WallBitmask + self.sSphereNode.setCollideMask(self.sSphereBitMask) + self.sSphere.setTangible(0) + self.accept('enter' + name, self.__handleToonCollision) + + def enableBattleDetect(self, name, handler): + DistributedSuitBase.DistributedSuitBase.enableBattleDetect(self, name, handler) + self.lookForToon(1) + + def disableBattleDetect(self): + DistributedSuitBase.DistributedSuitBase.disableBattleDetect(self) + self.lookForToon(0) + + def subclassManagesParent(self): + return 1 + + def enterWalk(self, ts = 0): + self.enableBattleDetect('walk', self.__handleToonCollision) + if self.path: + if self.walkTrack: + self.walkTrack.loop() + self.walkTrack.pause() + if self.paused: + self.walkTrack.setT(self.pauseTime) + else: + self.walkTrack.setT(ts) + self.walkTrack.resume() + self.loop('walk', 0) + self.paused = 0 + else: + self.loop('neutral', 0) + + def exitWalk(self): + self.disableBattleDetect() + if self.walkTrack: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + + def lookForToon(self, on = 1): + if self.behavior in ['chase']: + if on: + self.accept(self.uniqueName('entertoonSphere'), self.__handleToonAlert) + else: + self.ignore(self.uniqueName('entertoonSphere')) + + def __handleToonAlert(self, collEntry): + self.notify.debug('%s: ahah! i saw you' % self.doId) + toonZ = base.localAvatar.getZ(render) + suitZ = self.getZ(render) + dZ = abs(toonZ - suitZ) + if dZ < 8.0: + self.sendUpdate('setAlert', [base.localAvatar.doId]) + + def resumePath(self, state): + self.setState('Walk') + + def enterChase(self): + self.enableBattleDetect('walk', self.__handleToonCollision) + self.startChaseTime = globalClock.getFrameTime() + self.startCheckStrayTask(1) + self.startChaseTask() + + def exitChase(self): + self.disableBattleDetect() + taskMgr.remove(self.taskName('chaseTask')) + if self.chaseTrack: + self.chaseTrack.pause() + del self.chaseTrack + self.chaseTrack = None + self.chasing = 0 + self.startCheckStrayTask(0) + return + + def setConfrontToon(self, avId): + self.notify.debug('DistributedFactorySuit.setConfrontToon %d' % avId) + self.chasing = avId + self.setState('Chase') + + def startChaseTask(self, delay = 0): + self.notify.debug('DistributedFactorySuit.startChaseTask delay=%s' % delay) + taskMgr.remove(self.taskName('chaseTask')) + taskMgr.doMethodLater(delay, self.chaseTask, self.taskName('chaseTask')) + + def chaseTask(self, task): + if not self.chasing: + return Task.done + av = base.cr.doId2do.get(self.chasing, None) + if not av: + self.notify.warning("avatar %s isn't here to chase" % self.chasing) + return Task.done + if globalClock.getFrameTime() - self.startChaseTime > 3.0: + self.setReturn() + return Task.done + toonPos = av.getPos(self.getParent()) + suitPos = self.getPos() + distance = Vec3(suitPos - toonPos).length() + if self.chaseTrack: + self.chaseTrack.pause() + del self.chaseTrack + self.chaseTrack = None + import random + rand1 = 0.5 + rand2 = 0.5 + rand3 = 0.5 + targetPos = Vec3(toonPos[0] + 4.0 * (rand1 - 0.5), toonPos[1] + 4.0 * (rand2 - 0.5), suitPos[2]) + track = Sequence(Func(self.headsUp, targetPos[0], targetPos[1], targetPos[2]), Func(self.loop, 'walk', 0)) + chaseSpeed = 4.0 + duration = distance / chaseSpeed + track.extend([LerpPosInterval(self, duration=duration, pos=Point3(targetPos), startPos=Point3(suitPos))]) + self.chaseTrack = track + self.chaseTrack.start() + self.startChaseTask(1.0) + return + + def startCheckStrayTask(self, on = 1): + taskMgr.remove(self.taskName('checkStray')) + if on: + taskMgr.add(self.checkStrayTask, self.taskName('checkStray')) + + def checkStrayTask(self, task): + curPos = self.getPos() + distance = Vec3(curPos - self.originalPos).length() + if distance > 10.0: + self.sendUpdate('setStrayed', []) + + def enterReturn(self): + self.enableBattleDetect('walk', self.__handleToonCollision) + self.lookForToon(0) + self.startReturnTask() + + def exitReturn(self): + self.disableBattleDetect() + taskMgr.remove(self.taskName('checkStray')) + taskMgr.remove(self.taskName('returnTask')) + if self.returnTrack: + self.returnTrack.pause() + self.returnTrack = None + return + + def setReturn(self): + self.notify.debug('DistributedFactorySuit.setReturn') + self.setState('Return') + + def startReturnTask(self, delay = 0): + taskMgr.remove(self.taskName('returnTask')) + taskMgr.doMethodLater(delay, self.returnTask, self.taskName('returnTask')) + + def returnTask(self, task): + self.factory.requestReparent(self, self.spec['parentEntId']) + if self.returnTrack: + self.returnTrack.pause() + self.returnTrack = None + if self.path: + targetPos = VBase3(0, 0, 0) + else: + targetPos = self.originalPos + track = Sequence(Func(self.headsUp, targetPos[0], targetPos[1], targetPos[2]), Func(self.loop, 'walk', 0)) + curPos = self.getPos() + distance = Vec3(curPos - targetPos).length() + duration = distance / 3.0 + track.append(LerpPosInterval(self, duration=duration, pos=Point3(targetPos), startPos=Point3(curPos))) + track.append(Func(self.returnDone)) + self.returnTrack = track + self.returnTrack.start() + return + + def returnDone(self): + self.setHpr(self.spec['h'], 0, 0) + self.setState('Walk') + if not self.path: + self.loop('neutral') + + def setActive(self, active): + if active: + self.setState('Walk') + else: + self.setState('Off') + + def disableBattleDetect(self): + if self.battleDetectName: + self.ignore('enter' + self.battleDetectName) + self.battleDetectName = None + if self.collNodePath: + self.collNodePath.removeNode() + self.collNodePath = None + return + + def disableBodyCollisions(self): + self.disableBattleDetect() + self.enableRaycast(0) + if self.cRayNodePath: + self.cRayNodePath.removeNode() + if hasattr(self, 'cRayNode'): + del self.cRayNode + if hasattr(self, 'cRay'): + del self.cRay + if hasattr(self, 'lifter'): + del self.lifter + + def removeCollisions(self): + self.enableRaycast(0) + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.lifter = None + self.cTrav = None + return + + def setVirtual(self, isVirtual = 1): + self.virtual = isVirtual + if self.virtual: + actorNode = self.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 getVirtual(self): + return self.virtual diff --git a/toontown/suit/DistributedFactorySuitAI.py b/toontown/suit/DistributedFactorySuitAI.py new file mode 100644 index 0000000..31d1bea --- /dev/null +++ b/toontown/suit/DistributedFactorySuitAI.py @@ -0,0 +1,122 @@ +from otp.ai.AIBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +import DistributedSuitBaseAI, SuitDialog +from direct.showbase.PythonUtil import StackTrace + +class DistributedFactorySuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFactorySuitAI') + + def __init__(self, air, suitPlanner): + DistributedSuitBaseAI.DistributedSuitBaseAI.__init__(self, air, suitPlanner) + self.blocker = None + self.battleCellIndex = None + self.chasing = 0 + self.factoryGone = 0 + return + + def factoryIsGoingDown(self): + self.factoryGone = 1 + + def delete(self): + if not self.factoryGone: + try: + self.setBattleCellIndex(None) + except Exception, e: + simbase.air.writeServerEvent('avoid_crash', self.doId, 'DistributedFactorySuitAI.delete, factoryIsNotGone, got Exception %s' % str(e)) + self.notify.warning('delete, factoryIsNotGone, got Exception %s' % str(e)) + self.notify.warning('stackTrace=%s' % StackTrace().compact()) + self.notify.warning('doId=%s' % self.doId) + if hasattr(self, 'levelDoId'): + self.notify.warning('levelDoId=%s' % self.levelDoId) + else: + self.notify.warning('no levelDoId') + + del self.blocker + self.ignoreAll() + DistributedSuitBaseAI.DistributedSuitBaseAI.delete(self) + return + + def setLevelDoId(self, levelDoId): + self.levelDoId = levelDoId + + def getLevelDoId(self): + return self.levelDoId + + def setCogId(self, cogId): + self.cogId = cogId + + def getCogId(self): + return self.cogId + + def setReserve(self, reserve): + self.reserve = reserve + + def getReserve(self): + return self.reserve + + def requestBattle(self, x, y, z, h, p, r): + toonId = self.air.getAvatarIdFromSender() + if self.notify.getDebug(): + self.notify.debug(str(self.getDoId()) + str(self.zoneId) + ': request battle with toon: %d' % toonId) + self.confrontPos = Point3(x, y, z) + self.confrontHpr = Vec3(h, p, r) + if self.sp.requestBattle(self, toonId): + if self.notify.getDebug(): + self.notify.debug('Suit %d requesting battle in zone %d with toon %d' % (self.getDoId(), self.zoneId, toonId)) + else: + if self.notify.getDebug(): + self.notify.debug('requestBattle from suit %d, toon %d- denied by battle manager' % (toonId, self.getDoId())) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + + def getConfrontPosHpr(self): + return ( + self.confrontPos, self.confrontHpr) + + def setBattleCellIndex(self, battleCellIndex): + self.sp.suitBattleCellChange(self, oldCell=self.battleCellIndex, newCell=battleCellIndex) + self.battleCellIndex = battleCellIndex + self.attachBattleBlocker() + self.accept(self.sp.getBattleBlockerEvent(self.battleCellIndex), self.attachBattleBlocker) + + def getBattleCellIndex(self): + return self.battleCellIndex + + def attachBattleBlocker(self): + blocker = self.sp.battleMgr.battleBlockers.get(self.battleCellIndex) + self.blocker = blocker + + def setAlert(self, avId): + if avId == self.air.getAvatarIdFromSender(): + av = self.air.doId2do.get(avId) + if av: + self.chasing = avId + if self.sp.battleMgr.cellHasBattle(self.battleCellIndex): + pass + else: + self.sendUpdate('setConfrontToon', [avId]) + + def setStrayed(self): + if self.chasing > 0: + self.chasing = 0 + self.sendUpdate('setReturn', []) + + def resume(self): + self.notify.debug('Suit %s resume' % self.doId) + if self.currHP <= 0: + messenger.send(self.getDeathEvent()) + self.notify.debug('Suit %s dead after resume' % self.doId) + self.requestRemoval() + else: + self.sendUpdate('setReturn', []) + return None + + def isForeman(self): + return self.boss + + def setVirtual(self, isVirtual=1): + self.virtual = isVirtual + + def getVirtual(self): + return self.virtual diff --git a/toontown/suit/DistributedGoon.py b/toontown/suit/DistributedGoon.py new file mode 100644 index 0000000..f04febc --- /dev/null +++ b/toontown/suit/DistributedGoon.py @@ -0,0 +1,495 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from GoonGlobals import * +from direct.fsm import FSM +from direct.distributed import ClockDelta +from otp.level import BasicEntities +from otp.level import DistributedEntity +from direct.directnotify import DirectNotifyGlobal +from toontown.coghq import DistributedCrushableEntity +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MovingPlatform +import Goon +from direct.task.Task import Task +from otp.level import PathEntity +import GoonDeath +import random + +class DistributedGoon(DistributedCrushableEntity.DistributedCrushableEntity, Goon.Goon, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoon') + + def __init__(self, cr): + try: + self.DistributedGoon_initialized + except: + self.DistributedGoon_initialized = 1 + DistributedCrushableEntity.DistributedCrushableEntity.__init__(self, cr) + Goon.Goon.__init__(self) + FSM.FSM.__init__(self, 'DistributedGoon') + + self.setCacheable(0) + self.rayNode = None + self.checkForWalls = 0 + self.triggerEvent = None + self.animTrack = None + self.walkTrack = None + self.pauseTime = 0 + self.paused = 0 + self.path = None + self.dir = GOON_FORWARD + self.animMultiplier = 1.0 + self.isDead = 0 + self.isStunned = 0 + self.collapseSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_hunker_down.mp3') + self.recoverSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_rattle_shake.mp3') + self.attackSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_tractor_beam_alarmed.mp3') + return + + def announceGenerate(self): + DistributedCrushableEntity.DistributedCrushableEntity.announceGenerate(self) + if hasattr(self, 'goonType'): + self.initGoon(self.goonType) + else: + self.initGoon('pg') + self.scaleRadar() + self.colorHat() + if self.level: + self.initClipPlanes() + self.level.setEntityCreateCallback(self.parentEntId, self.initPath) + else: + self.enterOff() + taskMgr.doMethodLater(0.1, self.makeCollidable, self.taskName('makeCollidable')) + self.setGoonScale(self.scale) + self.animMultiplier = self.velocity / (ANIM_WALK_RATE * self.scale) + self.setPlayRate(self.animMultiplier, 'walk') + + def initPath(self): + self.enterOff() + self.setPath() + taskMgr.doMethodLater(0.1, self.makeCollidable, self.taskName('makeCollidable')) + + def makeCollidable(self, task): + self.initCollisions() + self.initializeBodyCollisions() + triggerName = self.uniqueName('GoonTrigger') + self.trigger.setName(triggerName) + self.triggerEvent = 'enter%s' % triggerName + self.startToonDetect() + + def generate(self): + DistributedCrushableEntity.DistributedCrushableEntity.generate(self) + + def scaleRadar(self): + Goon.Goon.scaleRadar(self) + self.trigger = self.radar.find('**/trigger') + triggerName = self.uniqueName('GoonTrigger') + self.trigger.setName(triggerName) + + def initCollisions(self): + self.cSphere = CollisionSphere(0.0, 0.0, 1.0, 1.0) + self.cSphereNode = CollisionNode('goonCollSphere') + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.head.attachNewNode(self.cSphereNode) + self.cSphereNodePath.hide() + self.cSphereBitMask = ToontownGlobals.WallBitmask + self.cSphereNode.setCollideMask(self.cSphereBitMask) + self.cSphere.setTangible(1) + self.sSphere = CollisionSphere(0.0, 0.0, self.headHeight + 0.8, 0.2) + self.sSphereNode = CollisionNode('toonSphere') + self.sSphereNode.addSolid(self.sSphere) + self.sSphereNodePath = self.head.attachNewNode(self.sSphereNode) + self.sSphereNodePath.hide() + self.sSphereBitMask = ToontownGlobals.WallBitmask + self.sSphereNode.setCollideMask(self.sSphereBitMask) + self.sSphere.setTangible(1) + + def initializeBodyCollisions(self): + self.cSphereNode.setName(self.uniqueName('goonCollSphere')) + self.sSphereNode.setName(self.uniqueName('toonSphere')) + self.accept(self.uniqueName('entertoonSphere'), self.__handleStun) + + def disableBodyCollisions(self): + self.ignore(self.uniqueName('entertoonSphere')) + + def deleteCollisions(self): + if hasattr(self, 'sSphereNodePath'): + self.sSphereNodePath.removeNode() + del self.sSphereNodePath + del self.sSphereNode + del self.sSphere + if hasattr(self, 'cSphereNodePath'): + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + del self.cSphereNode + del self.cSphere + + def initClipPlanes(self): + zoneNum = self.getZoneEntity().getZoneNum() + clipList = self.level.goonClipPlanes.get(zoneNum) + if clipList: + for id in clipList: + clipPlane = self.level.getEntity(id) + self.radar.setClipPlane(clipPlane.getPlane()) + + def disableClipPlanes(self): + if self.radar: + self.radar.clearClipPlane() + + if __dev__: + + def refreshPath(self): + self.setPath() + self.request('Off') + self.request('Walk') + + def setPath(self): + self.path = self.level.getEntity(self.parentEntId) + if __dev__: + if hasattr(self, 'pathChangeEvent'): + self.ignore(self.pathChangeEvent) + self.pathChangeEvent = self.path.getChangeEvent() + self.accept(self.pathChangeEvent, self.refreshPath) + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + self.walkTrack = self.path.makePathTrack(self, self.velocity, self.uniqueName('goonWalk'), turnTime=T_TURN) + if self.gridId != None: + self.sendUpdate('setParameterize', [self.path.pos[0], + self.path.pos[1], + self.path.pos[2], + self.path.pathIndex]) + return + + def disable(self): + self.notify.debug('DistributedGoon %d: disabling' % self.getDoId()) + self.ignoreAll() + self.stopToonDetect() + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.remove(self.taskName('recoveryDone')) + self.request('Off') + self.disableBodyCollisions() + self.disableClipPlanes() + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + DistributedCrushableEntity.DistributedCrushableEntity.disable(self) + return + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedGoon %d: deleting' % self.getDoId()) + taskMgr.remove(self.taskName('makeCollidable')) + self.deleteCollisions() + self.head.removeNode() + del self.head + del self.attackSound + del self.collapseSound + del self.recoverSound + DistributedCrushableEntity.DistributedCrushableEntity.delete(self) + Goon.Goon.delete(self) + + def enterOff(self, *args): + self.hideNametag3d() + self.hideNametag2d() + self.hide() + self.isStunned = 0 + self.isDead = 0 + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + return + + def exitOff(self): + self.show() + self.showNametag3d() + self.showNametag2d() + + def enterWalk(self, avId = None, ts = 0): + self.notify.debug('enterWalk, ts = %s' % ts) + self.startToonDetect() + self.loop('walk', 0) + self.isStunned = 0 + if self.path: + if not self.walkTrack: + self.walkTrack = self.path.makePathTrack(self, self.velocity, self.uniqueName('goonWalk'), turnTime=T_TURN) + self.startWalk(ts) + + def startWalk(self, ts): + tOffset = ts % self.walkTrack.getDuration() + self.walkTrack.loop() + self.walkTrack.pause() + self.walkTrack.setT(tOffset) + self.walkTrack.resume() + self.paused = 0 + + def exitWalk(self): + self.notify.debug('exitWalk') + self.stopToonDetect() + if self.walkTrack and not self.paused: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + self.stop() + + def enterBattle(self, avId = None, ts = 0): + self.notify.debug('enterBattle') + self.stopToonDetect() + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.isStunned = 0 + if avId == base.localAvatar.doId: + if self.level: + self.level.b_setOuch(self.strength) + self.animTrack = self.makeAttackTrack() + self.animTrack.loop() + return + + def exitBattle(self): + self.notify.debug('exitBattle') + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.head.setHpr(0, 0, 0) + return + + def enterStunned(self, ts = 0): + self.ignore(self.uniqueName('entertoonSphere')) + self.isStunned = 1 + self.notify.debug('enterStunned') + if self.radar: + self.radar.hide() + self.animTrack = Parallel(Sequence(ActorInterval(self, 'collapse'), Func(self.pose, 'collapse', 48)), SoundInterval(self.collapseSound, node=self)) + self.animTrack.start(ts) + + def exitStunned(self): + self.notify.debug('exitStunned') + if self.radar: + self.radar.show() + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.accept(self.uniqueName('entertoonSphere'), self.__handleStun) + return + + def enterRecovery(self, ts = 0, pauseTime = 0): + self.notify.debug('enterRecovery') + self.ignore(self.uniqueName('entertoonSphere')) + self.isStunned = 1 + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.animTrack = self.getRecoveryTrack() + duration = self.animTrack.getDuration() + self.animTrack.start(ts) + delay = max(0, duration - ts) + taskMgr.remove(self.taskName('recoveryDone')) + taskMgr.doMethodLater(delay, self.recoveryDone, self.taskName('recoveryDone'), extraArgs=(pauseTime,)) + return + + def getRecoveryTrack(self): + return Parallel(Sequence(ActorInterval(self, 'recovery'), Func(self.pose, 'recovery', 96)), Func(base.playSfx, self.recoverSound, node=self)) + + def recoveryDone(self, pauseTime): + self.request('Walk', None, pauseTime) + return + + def exitRecovery(self): + self.notify.debug('exitRecovery') + taskMgr.remove(self.taskName('recoveryDone')) + if self.animTrack: + self.animTrack.finish() + self.animTrack = None + self.accept(self.uniqueName('entertoonSphere'), self.__handleStun) + return + + def makeAttackTrack(self): + h = self.head.getH() + freakDeg = 60 + hatZ = self.hat.getZ() + track = Parallel(Sequence(LerpColorScaleInterval(self.eye, 0.2, Vec4(1, 0, 0, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(0, 0, 1, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(1, 0, 0, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(0, 0, 1, 1)), Func(self.eye.clearColorScale)), SoundInterval(self.attackSound, node=self, volume=0.4)) + return track + + def doDetect(self): + pass + + def doAttack(self, avId): + pass + + def __startResumeWalkTask(self, ts): + resumeTime = 1.5 + if ts < resumeTime: + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.doMethodLater(resumeTime - ts, self.request, self.taskName('resumeWalk'), extraArgs=('Walk',)) + else: + self.request('Walk', ts - resumeTime) + + def __reverseWalk(self, task): + self.request('Walk') + return Task.done + + def __startRecoverTask(self, ts): + stunTime = 4.0 + if ts < stunTime: + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.doMethodLater(stunTime - ts, self.request, self.taskName('resumeWalk'), extraArgs=('Recovery',)) + else: + self.request('Recovery', ts - stunTime) + + def startToonDetect(self): + self.radar.show() + if self.triggerEvent: + self.accept(self.triggerEvent, self.handleToonDetect) + + def stopToonDetect(self): + if self.triggerEvent: + self.ignore(self.triggerEvent) + + def handleToonDetect(self, collEntry = None): + if base.localAvatar.isStunned: + return + if self.state == 'Off': + return + self.stopToonDetect() + self.request('Battle', base.localAvatar.doId) + if self.walkTrack: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + if self.dclass and hasattr(self, 'dclass'): + self.sendUpdate('requestBattle', [self.pauseTime]) + else: + self.notify.info('Goon deleted and still trying to call handleToonDetect()') + + def __handleStun(self, collEntry): + toon = base.localAvatar + if toon: + toonDistance = self.getPos(toon).length() + if toonDistance > self.attackRadius: + self.notify.warning('Stunned a good, but outside of attack radius') + return + else: + self.request('Stunned') + if self.walkTrack: + self.pauseTime = self.walkTrack.pause() + self.paused = 1 + self.sendUpdate('requestStunned', [self.pauseTime]) + + def setMovie(self, mode, avId, pauseTime, timestamp): + if self.isDead: + return + ts = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.notify.debug('%s: setMovie(%s,%s,%s,%s)' % (self.doId, + mode, + avId, + pauseTime, + ts)) + if mode == GOON_MOVIE_BATTLE: + if self.state != 'Battle': + self.request('Battle', avId, ts) + elif mode == GOON_MOVIE_STUNNED: + if self.state != 'Stunned': + toon = base.cr.doId2do.get(avId) + if toon: + toonDistance = self.getPos(toon).length() + if toonDistance > self.attackRadius: + self.notify.warning('Stunned a goon, but outside of attack radius') + return + else: + self.request('Stunned', ts) + elif mode == GOON_MOVIE_RECOVERY: + if self.state != 'Recovery': + self.request('Recovery', ts, pauseTime) + elif mode == GOON_MOVIE_SYNC: + if self.walkTrack: + self.walkTrack.pause() + self.paused = 1 + if self.state == 'Off' or self.state == 'Walk': + self.request('Walk', avId, pauseTime + ts) + else: + if self.walkTrack: + self.walkTrack.pause() + self.walkTrack = None + self.request('Walk', avId, pauseTime + ts) + return + + def stunToon(self, avId): + self.notify.debug('stunToon(%s)' % avId) + av = base.cr.doId2do.get(avId) + if av != None: + av.stunToon() + return + + def isLocalToon(self, avId): + if avId == base.localAvatar.doId: + return 1 + return 0 + + def playCrushMovie(self, crusherId, axis): + goonPos = self.getPos() + sx = random.uniform(0.3, 0.8) * self.scale + sz = random.uniform(0.3, 0.8) * self.scale + crushTrack = Sequence(GoonDeath.createGoonExplosion(self.getParent(), goonPos, VBase3(sx, 1, sz)), name=self.uniqueName('crushTrack'), autoFinish=1) + self.dead() + crushTrack.start() + + def setVelocity(self, velocity): + self.velocity = velocity + self.animMultiplier = velocity / (ANIM_WALK_RATE * self.scale) + self.setPlayRate(self.animMultiplier, 'walk') + + def dead(self): + if not self.isDead and not self.isDisabled(): + self.stopToonDetect() + self.detachNode() + self.isDead = 1 + + def undead(self): + if self.isDead: + self.startToonDetect() + self.reparentTo(render) + self.isDead = 0 + + def resync(self): + if not self.isDead: + self.sendUpdate('requestResync') + + def setHFov(self, hFov): + if hFov != self.hFov: + self.hFov = hFov + if self.isGenerated(): + self.scaleRadar() + + def setAttackRadius(self, attackRadius): + if attackRadius != self.attackRadius: + self.attackRadius = attackRadius + if self.isGenerated(): + self.scaleRadar() + + def setStrength(self, strength): + if strength != self.strength: + self.strength = strength + if self.isGenerated(): + self.colorHat() + + def setGoonScale(self, scale): + if scale != self.scale: + self.scale = scale + if self.isGenerated(): + self.getGeomNode().setScale(self.scale) + self.scaleRadar() + + def setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.setVelocity(velocity) + self.setHFov(hFov) + self.setAttackRadius(attackRadius) + self.setStrength(strength) + self.setGoonScale(scale) diff --git a/toontown/suit/DistributedGoonAI.py b/toontown/suit/DistributedGoonAI.py new file mode 100644 index 0000000..0ca48f7 --- /dev/null +++ b/toontown/suit/DistributedGoonAI.py @@ -0,0 +1,259 @@ +from otp.ai.AIBaseGlobal import * +from GoonGlobals import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +from toontown.coghq import DistributedCrushableEntityAI +import GoonPathData +from direct.distributed import ClockDelta +import random +from direct.task import Task + +class DistributedGoonAI(DistributedCrushableEntityAI.DistributedCrushableEntityAI): + UPDATE_TIMESTAMP_INTERVAL = 180.0 + STUN_TIME = 4 + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoonAI') + + def __init__(self, level, entId): + self.hFov = 70 + self.attackRadius = 15 + self.strength = 15 + self.velocity = 4 + self.scale = 1.0 + DistributedCrushableEntityAI.DistributedCrushableEntityAI.__init__(self, level, entId) + self.curInd = 0 + self.dir = GOON_FORWARD + self.parameterized = 0 + self.width = 1 + self.crushed = 0 + self.pathStartTime = None + self.walkTrackTime = 0.0 + self.totalPathTime = 1.0 + return + + def delete(self): + taskMgr.remove(self.taskName('sync')) + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.remove(self.taskName('recovery')) + taskMgr.remove(self.taskName('deleteGoon')) + taskMgr.remove(self.taskName('GoonBombCheck')) + DistributedCrushableEntityAI.DistributedCrushableEntityAI.delete(self) + + def generate(self): + self.notify.debug('generate') + DistributedCrushableEntityAI.DistributedCrushableEntityAI.generate(self) + if self.level: + self.level.setEntityCreateCallback(self.parentEntId, self.startGoon) + + def startGoon(self): + ts = 100 * random.random() + self.sendMovie(GOON_MOVIE_WALK, pauseTime=ts) + + def requestBattle(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestBattle, avId = %s' % avId) + self.sendMovie(GOON_MOVIE_BATTLE, avId, pauseTime) + taskMgr.remove(self.taskName('resumeWalk')) + taskMgr.doMethodLater(5, self.sendMovie, self.taskName('resumeWalk'), extraArgs=(GOON_MOVIE_WALK, avId, pauseTime)) + + def requestStunned(self, pauseTime): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('requestStunned(%s)' % avId) + if self.level: + if not hasattr(self.level, 'goonStunRequests'): + self.level.goonStunRequests = {} + if taskMgr.hasTaskNamed(self.taskName('GoonBombCheck')): + if self.level.goonStunRequests.has_key(avId): + self.level.goonStunRequests[avId] = self.level.goonStunRequests[avId] + 1 + else: + self.level.goonStunRequests[avId] = 1 + else: + self.level.goonStunRequests[avId] = 1 + taskMgr.doMethodLater(0.1, self.accumulateGoonMessages, self.taskName('GoonBombCheck')) + self.sendMovie(GOON_MOVIE_STUNNED, avId, pauseTime) + taskMgr.remove(self.taskName('recovery')) + taskMgr.doMethodLater(self.STUN_TIME, self.sendMovie, self.taskName('recovery'), extraArgs=(GOON_MOVIE_RECOVERY, avId, pauseTime)) + + def accumulateGoonMessages(self, task): + if not hasattr(self.level, 'goonStunRequests'): + return + for toonId in self.level.goonStunRequests: + if self.level.goonStunRequests[toonId] > 2: + self.air.writeServerEvent('suspicious', toonId, 'Stunned multiple goons very close together. Possible multihack.') + + self.level.goonStunRequests.clear() + del self.level.goonStunRequests + + def requestResync(self, task=None): + self.notify.debug('resyncGoon') + self.sendMovie(GOON_MOVIE_SYNC) + self.updateGrid() + + def sendMovie(self, type, avId=0, pauseTime=0): + if type == GOON_MOVIE_WALK: + self.pathStartTime = globalClock.getFrameTime() + if self.parameterized: + self.walkTrackTime = pauseTime % self.totalPathTime + else: + self.walkTrackTime = pauseTime + self.notify.debug('GOON_MOVIE_WALK doId = %s, pathStartTime = %s, walkTrackTime = %s' % (self.doId, self.pathStartTime, self.walkTrackTime)) + if type == GOON_MOVIE_WALK or type == GOON_MOVIE_SYNC: + curT = globalClock.getFrameTime() + elapsedT = curT - self.pathStartTime + pathT = self.walkTrackTime + elapsedT + if self.parameterized: + pathT = pathT % self.totalPathTime + self.sendUpdate('setMovie', [type, avId, pathT, ClockDelta.globalClockDelta.localToNetworkTime(curT)]) + taskMgr.remove(self.taskName('sync')) + taskMgr.doMethodLater(self.UPDATE_TIMESTAMP_INTERVAL, self.requestResync, self.taskName('sync'), extraArgs=None) + else: + self.sendUpdate('setMovie', [type, avId, pauseTime, ClockDelta.globalClockDelta.getFrameNetworkTime()]) + return + + def updateGrid(self): + if not self.parameterized: + return + if self.grid and hasattr(self, 'entId'): + self.grid.removeObject(self.entId) + if not self.crushed: + curT = globalClock.getFrameTime() + if self.pathStartTime: + elapsedT = curT - self.pathStartTime + else: + elapsedT = 0 + pathT = (self.walkTrackTime + elapsedT) % self.totalPathTime + pt = self.getPathPoint(pathT) + if not self.grid.addObjectByPos(self.entId, pt): + self.notify.warning("updateGrid: couldn't put goon in grid") + + def doCrush(self, crusherId, axis): + self.notify.debug('doCrush %s' % self.doId) + DistributedCrushableEntityAI.DistributedCrushableEntityAI.doCrush(self, crusherId, axis) + self.crushed = 1 + self.grid.removeObject(self.entId) + taskMgr.doMethodLater(5.0, self.doDelete, self.taskName('deleteGoon')) + + def doDelete(self, task): + self.requestDelete() + return Task.done + + def setParameterize(self, x, y, z, pathIndex): + if not hasattr(self, 'level') or not self.level: + return + pathId = GoonPathData.taskZoneId2pathId[self.level.getTaskZoneId()] + pathData = GoonPathData.Paths[pathId] + self.pathOrigin = Vec3(x, y, z) + if pathIndex > len(pathData): + self.notify.warning('Invalid path index given, using 0') + pathIndex = 0 + pathPts = pathData[pathIndex] + [pathData[pathIndex][0]] + invVel = 1.0 / self.velocity + t = 0 + self.tSeg = [t] + self.pathSeg = [] + for i in range(len(pathPts) - 1): + ptA = pathPts[i] + ptB = pathPts[i + 1] + t += T_TURN + self.tSeg.append(t) + self.pathSeg.append([Vec3(0, 0, 0), 0, ptA]) + seg = Vec3(ptB - ptA) + segLength = seg.length() + t += invVel * segLength + self.tSeg.append(t) + self.pathSeg.append([seg, segLength, ptA]) + + self.totalPathTime = t + self.pathPts = pathPts + self.parameterized = 1 + + def getPathPoint(self, t): + for i in range(len(self.tSeg) - 1): + if t >= self.tSeg[i] and t < self.tSeg[i + 1]: + tSeg = t - self.tSeg[i] + t = tSeg / (self.tSeg[i + 1] - self.tSeg[i]) + seg = self.pathSeg[i][0] + ptA = self.pathSeg[i][2] + pt = ptA + seg * t + return self.pathOrigin + pt + + self.notify.warning("Couldn't find valid path point") + return Vec3(0, 0, 0) + + def b_setVelocity(self, velocity): + self.setVelocity(velocity) + self.d_setVelocity(velocity) + + def setVelocity(self, velocity): + self.velocity = velocity + + def d_setVelocity(self, velocity): + self.sendUpdate('setVelocity', [velocity]) + + def getVelocity(self): + return self.velocity + + def b_setHFov(self, hFov): + self.setHFov(hFov) + self.d_setHFov(hFov) + + def setHFov(self, hFov): + self.hFov = hFov + + def d_setHFov(self, hFov): + self.sendUpdate('setHFov', [hFov]) + + def getHFov(self): + return self.hFov + + def b_setAttackRadius(self, attackRadius): + self.setAttackRadius(attackRadius) + self.d_setAttackRadius(attackRadius) + + def setAttackRadius(self, attackRadius): + self.attackRadius = attackRadius + + def d_setAttackRadius(self, attackRadius): + self.sendUpdate('setAttackRadius', [attackRadius]) + + def getAttackRadius(self): + return self.attackRadius + + def b_setStrength(self, strength): + self.setStrength(strength) + self.d_setStrength(strength) + + def setStrength(self, strength): + self.strength = strength + + def d_setStrength(self, strength): + self.sendUpdate('setStrength', [strength]) + + def getStrength(self): + return self.strength + + def b_setGoonScale(self, scale): + self.setGoonScale(scale) + self.d_setGoonScale(scale) + + def setGoonScale(self, scale): + self.scale = scale + + def d_setGoonScale(self, scale): + self.sendUpdate('setGoonScale', [scale]) + + def getGoonScale(self): + return self.scale + + def b_setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.setupGoon(velocity, hFov, attackRadius, strength, scale) + self.d_setupGoon(velocity, hFov, attackRadius, strength, scale) + + def setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.setVelocity(velocity) + self.setHFov(hFov) + self.setAttackRadius(attackRadius) + self.setStrength(strength) + self.setGoonScale(scale) + + def d_setupGoon(self, velocity, hFov, attackRadius, strength, scale): + self.sendUpdate('setupGoon', [velocity, hFov, attackRadius, strength, scale]) diff --git a/toontown/suit/DistributedGridGoon.py b/toontown/suit/DistributedGridGoon.py new file mode 100644 index 0000000..476c232 --- /dev/null +++ b/toontown/suit/DistributedGridGoon.py @@ -0,0 +1,52 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from direct.directnotify import DirectNotifyGlobal +import DistributedGoon +from toontown.toonbase import ToontownGlobals +from toontown.coghq import MovingPlatform + +class DistributedGridGoon(DistributedGoon.DistributedGoon): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoon') + + def __init__(self, cr, type = 'sg'): + try: + self.DistributedGridGoon_initialized + except: + self.DistributedGridGoon_initialized = 1 + DistributedGoon.DistributedGoon.__init__(self, cr, type) + + def generate(self): + DistributedGoon.DistributedGoon.generate(self) + self.ignore(self.uniqueName('wallHit')) + self.mazeWalkTrack = None + return + + def delete(self): + if self.mazeWalkTrack: + self.mazeWalkTrack.pause() + del self.mazeWalkTrack + DistributedGoon.DistributedGoon.delete(self) + + def setH(self, h): + self.h = h + + def setPathPts(self, xi, yi, zi, xf, yf, zf): + self.notify.debug('setPathPts') + if self.mazeWalkTrack: + self.mazeWalkTrack.pause() + del self.mazeWalkTrack + self.mazeWalkTrack = None + curPos = Point3(xi, yi, zi) + nextPos = Point3(xf, yf, zf) + distance = Vec3(curPos - nextPos).length() + duration = distance / self.velocity + self.mazeWalkTrack = Sequence(Func(self.headsUp, nextPos[0], nextPos[1], nextPos[2]), LerpPosInterval(self, duration=duration, pos=nextPos, startPos=curPos), name=self.uniqueName('mazeWalkTrack')) + self.mazeWalkTrack.start() + return + + def enterWalk(self, avId = None, ts = 0): + pass + + def exitWalk(self): + pass diff --git a/toontown/suit/DistributedGridGoonAI.py b/toontown/suit/DistributedGridGoonAI.py new file mode 100644 index 0000000..830a92b --- /dev/null +++ b/toontown/suit/DistributedGridGoonAI.py @@ -0,0 +1,47 @@ +from otp.ai.AIBaseGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +import DistributedGoonAI +from direct.task.Task import Task +from toontown.coghq import DistributedCrushableEntityAI +import random + +class DistributedGridGoonAI(DistributedGoonAI.DistributedGoonAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGridGoonAI') + + def __init__(self, level, entId): + self.grid = None + self.h = 0 + DistributedGoonAI.DistributedGoonAI.__init__(self, level, entId) + return + + def generate(self): + self.notify.debug('generate') + DistributedCrushableEntityAI.DistributedCrushableEntityAI.generate(self) + + def initGridDependents(self): + taskMgr.doMethodLater(2, self.goToNextPoint, self.taskName('walkTask')) + + def getPosition(self): + if self.grid: + return self.grid.getObjPos(self.entId) + + def getH(self): + return self.h + + def goToNextPoint(self, task): + if not self.grid: + self.notify.warning("couldn't find grid, not starting") + return + if self.grid.checkMoveDir(self.entId, self.h): + ptA = Point3(*self.getPosition()) + self.grid.doMoveDir(self.entId, self.h) + ptB = Point3(*self.getPosition()) + self.sendUpdate('setPathPts', [ptA[0], ptA[1], ptA[2], ptB[0], ptB[1], ptB[2]]) + tPathSegment = Vec3(ptA - ptB).length() / self.velocity + else: + turn = int(random.randrange(1, 4) * 90) + self.h = (self.h + turn) % 360 + tPathSegment = 0.1 + taskMgr.doMethodLater(tPathSegment, self.goToNextPoint, self.taskName('walkTask')) + return Task.done diff --git a/toontown/suit/DistributedLawbotBoss.py b/toontown/suit/DistributedLawbotBoss.py new file mode 100644 index 0000000..0d8b10d --- /dev/null +++ b/toontown/suit/DistributedLawbotBoss.py @@ -0,0 +1,1846 @@ +from direct.showbase.ShowBase import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from direct.distributed.ClockDelta import * +from direct.showbase.PythonUtil import Functor +from direct.showbase.PythonUtil import StackTrace +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import FSM +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +import DistributedBossCog +from toontown.toonbase import TTLocalizer +import SuitDNA +from toontown.toon import Toon +from toontown.battle import BattleBase +from direct.directutil import Mopath +from direct.showutil import Rope +from toontown.distributed import DelayDelete +from toontown.battle import MovieToonVictory +from toontown.building import ElevatorUtils +from toontown.battle import RewardPanel +from toontown.toon import NPCToons +from direct.task import Task +import random +import math +from toontown.coghq import CogDisguiseGlobals +from toontown.building import ElevatorConstants +from toontown.toonbase import ToontownTimer +OneBossCog = None + +class DistributedLawbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBoss') + debugPositions = False + + def __init__(self, cr): + self.notify.debug('----- __init___') + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedLawbotBoss') + self.lawyers = [] + self.lawyerRequest = None + self.bossDamage = 0 + self.attackCode = None + self.attackAvId = 0 + self.recoverRate = 0 + self.recoverStartTime = 0 + self.bossDamageMovie = None + self.everThrownPie = 0 + self.battleThreeMusicTime = 0 + self.insidesANodePath = None + self.insidesBNodePath = None + self.strafeInterval = None + self.onscreenMessage = None + self.bossMaxDamage = ToontownGlobals.LawbotBossMaxDamage + self.elevatorType = ElevatorConstants.ELEVATOR_CJ + self.gavels = {} + self.chairs = {} + self.cannons = {} + self.useCannons = 1 + self.juryBoxIval = None + self.juryTimer = None + self.witnessToon = None + self.witnessToonOnstage = False + self.numToonJurorsSeated = 0 + self.mainDoor = None + self.reflectedMainDoor = None + self.panFlashInterval = None + self.panDamage = ToontownGlobals.LawbotBossDefensePanDamage + if base.config.GetBool('lawbot-boss-cheat', 0): + self.panDamage = 25 + self.evidenceHitSfx = None + self.toonUpSfx = None + self.bonusTimer = None + self.warningSfx = None + self.juryMovesSfx = None + self.baseColStashed = False + self.battleDifficulty = 0 + self.bonusWeight = 0 + self.numJurorsLocalToonSeated = 0 + self.cannonIndex = -1 + return + + def announceGenerate(self): + global OneBossCog + self.notify.debug('----- announceGenerate') + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.setName(TTLocalizer.LawbotBossName) + nameInfo = TTLocalizer.BossCogNameWithDept % {'name': self.name, + 'dept': SuitDNA.getDeptFullname(self.style.dept)} + self.setDisplayName(nameInfo) + self.piesRestockSfx = loader.loadSfx('phase_5/audio/sfx/LB_receive_evidence.mp3') + self.rampSlideSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_ramp_slide.mp3') + self.evidenceHitSfx = loader.loadSfx('phase_11/audio/sfx/LB_evidence_hit.mp3') + self.warningSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_tractor_beam_alarmed.mp3') + self.juryMovesSfx = loader.loadSfx('phase_11/audio/sfx/LB_jury_moves.wav') + self.toonUpSfx = loader.loadSfx('phase_11/audio/sfx/LB_toonup.mp3') + self.strafeSfx = [] + for i in range(10): + self.strafeSfx.append(loader.loadSfx('phase_3.5/audio/sfx/SA_shred.mp3')) + + render.setTag('pieCode', str(ToontownGlobals.PieCodeNotBossCog)) + insidesA = CollisionPolygon(Point3(4.0, -2.0, 5.0), Point3(-4.0, -2.0, 5.0), Point3(-4.0, -2.0, 0.5), Point3(4.0, -2.0, 0.5)) + insidesANode = CollisionNode('BossZap') + insidesANode.addSolid(insidesA) + insidesANode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesANodePath = self.axle.attachNewNode(insidesANode) + self.insidesANodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesANodePath.stash() + insidesB = CollisionPolygon(Point3(-4.0, 2.0, 5.0), Point3(4.0, 2.0, 5.0), Point3(4.0, 2.0, 0.5), Point3(-4.0, 2.0, 0.5)) + insidesBNode = CollisionNode('BossZap') + insidesBNode.addSolid(insidesB) + insidesBNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesBNodePath = self.axle.attachNewNode(insidesBNode) + self.insidesBNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesBNodePath.stash() + target = CollisionTube(0, -1, 4, 0, -1, 9, 3.5) + targetNode = CollisionNode('BossZap') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.targetNodePath = self.pelvis.attachNewNode(targetNode) + self.targetNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + shield = CollisionTube(0, 1, 4, 0, 1, 7, 3.5) + shieldNode = CollisionNode('BossZap') + shieldNode.addSolid(shield) + shieldNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask) + shieldNodePath = self.pelvis.attachNewNode(shieldNode) + disk = loader.loadModel('phase_9/models/char/bossCog-gearCollide') + disk.find('**/+CollisionNode').setName('BossZap') + disk.reparentTo(self.pelvis) + disk.setZ(0.8) + self.loadEnvironment() + self.__makeWitnessToon() + self.__loadMopaths() + localAvatar.chatMgr.chatInputSpeedChat.addCJMenu() + if OneBossCog != None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + return + + def disable(self): + global OneBossCog + self.notify.debug('----- disable') + DistributedBossCog.DistributedBossCog.disable(self) + self.request('Off') + self.unloadEnvironment() + self.__cleanupWitnessToon() + self.__unloadMopaths() + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + self.__cleanupStrafe() + self.__cleanupJuryBox() + render.clearTag('pieCode') + self.targetNodePath.detachNode() + self.cr.relatedObjectMgr.abortRequest(self.lawyerRequest) + self.lawyerRequest = None + self.betweenBattleMusic.stop() + self.promotionMusic.stop() + self.stingMusic.stop() + self.battleTwoMusic.stop() + self.battleThreeMusic.stop() + self.epilogueMusic.stop() + if self.juryTimer: + self.juryTimer.destroy() + del self.juryTimer + if self.bonusTimer: + self.bonusTimer.destroy() + del self.bonusTimer + localAvatar.chatMgr.chatInputSpeedChat.removeCJMenu() + if OneBossCog == self: + OneBossCog = None + return + + def delete(self): + self.notify.debug('----- delete') + DistributedBossCog.DistributedBossCog.delete(self) + + def d_hitBoss(self, bossDamage): + self.notify.debug('----- d_hitBoss') + self.sendUpdate('hitBoss', [bossDamage]) + + def d_healBoss(self, bossHeal): + self.notify.debug('----- d_bossHeal') + self.sendUpdate('healBoss', [bossHeal]) + + def d_hitBossInsides(self): + self.notify.debug('----- d_hitBossInsides') + self.sendUpdate('hitBossInsides', []) + + def d_hitDefensePan(self): + self.notify.debug('----- d_hitDefensePan') + self.sendUpdate('hitDefensePan', []) + + def d_hitProsecutionPan(self): + self.notify.debug('----- d_hitProsecutionPan') + self.sendUpdate('hitProsecutionPan', []) + + def d_hitToon(self, toonId): + self.notify.debug('----- d_hitToon') + self.sendUpdate('hitToon', [toonId]) + + def gotToon(self, toon): + stateName = self.state + if stateName == 'Elevator': + self.placeToonInElevator(toon) + + def setLawyerIds(self, lawyerIds): + self.lawyers = [] + self.cr.relatedObjectMgr.abortRequest(self.lawyerRequest) + self.lawyerRequest = self.cr.relatedObjectMgr.requestObjects(lawyerIds, allCallback=self.__gotLawyers) + + def __gotLawyers(self, lawyers): + self.lawyerRequest = None + self.lawyers = lawyers + for i in range(len(self.lawyers)): + suit = self.lawyers[i] + suit.fsm.request('neutral') + suit.loop('neutral') + suit.setBossCogId(self.doId) + + return + + def setBossDamage(self, bossDamage, recoverRate, timestamp): + recoverStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + if self.bossDamageMovie: + if self.bossDamage >= self.bossMaxDamage: + self.notify.debug('finish the movie then transition to NearVictory') + self.bossDamageMovie.resumeUntil(self.bossDamageMovie.getDuration()) + else: + self.bossDamageMovie.resumeUntil(self.bossDamage * self.bossDamageToMovie) + if self.recoverRate: + taskMgr.add(self.__recoverBossDamage, taskName) + self.makeScaleReflectDamage() + + def getBossDamage(self): + self.notify.debug('----- getBossDamage') + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0) + + def __recoverBossDamage(self, task): + self.notify.debug('----- __recoverBossDamage') + if self.bossDamageMovie: + self.bossDamageMovie.setT(self.getBossDamage() * self.bossDamageToMovie) + return Task.cont + + def __walkToonToPromotion(self, toonId, delay, mopath, track, delayDeletes): + self.notify.debug('----- __walkToonToPromotion') + toon = base.cr.doId2do.get(toonId) + if toon: + destPos = toon.getPos() + self.placeToonInElevator(toon) + toon.wrtReparentTo(render) + ival = Sequence(Wait(delay), Func(toon.suit.setPlayRate, 1, 'walk'), Func(toon.suit.loop, 'walk'), toon.posInterval(1, Point3(0, 90, 20)), ParallelEndTogether(MopathInterval(mopath, toon), toon.posInterval(2, destPos, blendType='noBlend')), Func(toon.suit.loop, 'neutral')) + track.append(ival) + delayDeletes.append(DelayDelete.DelayDelete(toon, 'LawbotBoss.__walkToonToPromotion')) + + def __walkSuitToPoint(self, node, fromPos, toPos): + self.notify.debug('----- __walkSuitToPoint') + vector = Vec3(toPos - fromPos) + distance = vector.length() + time = distance / (ToontownGlobals.SuitWalkSpeed * 1.8) + return Sequence(Func(node.setPos, fromPos), Func(node.headsUp, toPos), node.posInterval(time, toPos)) + + def __makeRollToBattleTwoMovie(self): + startPos = Point3(ToontownGlobals.LawbotBossBattleOnePosHpr[0], ToontownGlobals.LawbotBossBattleOnePosHpr[1], ToontownGlobals.LawbotBossBattleOnePosHpr[2]) + if self.arenaSide: + topRampPos = Point3(*ToontownGlobals.LawbotBossTopRampPosB) + topRampTurnPos = Point3(*ToontownGlobals.LawbotBossTopRampTurnPosB) + p3Pos = Point3(*ToontownGlobals.LawbotBossP3PosB) + else: + topRampPos = Point3(*ToontownGlobals.LawbotBossTopRampPosA) + topRampTurnPos = Point3(*ToontownGlobals.LawbotBossTopRampTurnPosA) + p3Pos = Point3(*ToontownGlobals.LawbotBossP3PosA) + battlePos = Point3(ToontownGlobals.LawbotBossBattleTwoPosHpr[0], ToontownGlobals.LawbotBossBattleTwoPosHpr[1], ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + battleHpr = VBase3(ToontownGlobals.LawbotBossBattleTwoPosHpr[3], ToontownGlobals.LawbotBossBattleTwoPosHpr[4], ToontownGlobals.LawbotBossBattleTwoPosHpr[5]) + bossTrack = Sequence() + self.notify.debug('calling setPosHpr') + myInterval = camera.posHprInterval(8, Point3(-22, -100, 35), Point3(-10, -13, 0), startPos=Point3(-22, -90, 35), startHpr=Point3(-10, -13, 0), blendType='easeInOut') + chatTrack = Sequence(Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempJury1, CFSpeech), Func(camera.reparentTo, localAvatar), Func(camera.setPos, localAvatar.cameraPositions[0][0]), Func(camera.setHpr, 0, 0, 0), Func(self.releaseToons, 1)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, hpr, battlePos, battleHpr, 0) + self.makeToonsWait() + finalPodiumPos = Point3(self.podium.getX(), self.podium.getY(), self.podium.getZ() + ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + finalReflectedPodiumPos = Point3(self.reflectedPodium.getX(), self.reflectedPodium.getY(), self.reflectedPodium.getZ() + ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + return Sequence(chatTrack, bossTrack, Func(self.getGeomNode().setH, 0), Parallel(self.podium.posInterval(5.0, finalPodiumPos), self.reflectedPodium.posInterval(5.0, finalReflectedPodiumPos), Func(self.stashBoss), self.posInterval(5.0, battlePos), Func(taskMgr.doMethodLater, 0.01, self.unstashBoss, 'unstashBoss')), name=self.uniqueName('BattleTwoMovie')) + + def __makeRollToBattleThreeMovie(self): + startPos = Point3(ToontownGlobals.LawbotBossBattleTwoPosHpr[0], ToontownGlobals.LawbotBossBattleTwoPosHpr[1], ToontownGlobals.LawbotBossBattleTwoPosHpr[2]) + battlePos = Point3(ToontownGlobals.LawbotBossBattleThreePosHpr[0], ToontownGlobals.LawbotBossBattleThreePosHpr[1], ToontownGlobals.LawbotBossBattleThreePosHpr[2]) + battleHpr = VBase3(ToontownGlobals.LawbotBossBattleThreePosHpr[3], ToontownGlobals.LawbotBossBattleThreePosHpr[4], ToontownGlobals.LawbotBossBattleThreePosHpr[5]) + bossTrack = Sequence() + myInterval = camera.posHprInterval(8, Point3(-22, -100, 35), Point3(-10, -13, 0), startPos=Point3(-22, -90, 35), startHpr=Point3(-10, -13, 0), blendType='easeInOut') + chatTrack = Sequence(Func(self.setChatAbsolute, TTLocalizer.LawbotBossTrialChat1, CFSpeech), Func(camera.reparentTo, localAvatar), Func(camera.setPos, localAvatar.cameraPositions[0][0]), Func(camera.setHpr, 0, 0, 0), Func(self.releaseToons, 1)) + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, None, battlePos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(battlePos, hpr, battlePos, battleHpr, 0) + self.makeToonsWait() + return Sequence(chatTrack, bossTrack, Func(self.getGeomNode().setH, 0), name=self.uniqueName('BattleTwoMovie')) + + def toNeutralMode(self): + if self.cr: + place = self.cr.playGame.getPlace() + if place and hasattr(place, 'fsm'): + place.setState('waitForBattle') + + def makeToonsWait(self): + self.notify.debug('makeToonsWait') + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.stopLookAround() + toon.stopSmooth() + + if self.hasLocalToon(): + self.toMovieMode() + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.loop('neutral') + + def makeEndOfBattleMovie(self, hasLocalToon): + name = self.uniqueName('Drop') + seq = Sequence(name=name) + seq += [Wait(0.0)] + if hasLocalToon: + seq += [Func(self.show), + Func(camera.reparentTo, localAvatar), + Func(camera.setPos, localAvatar.cameraPositions[0][0]), + Func(camera.setHpr, 0, 0, 0)] + seq.append(Func(self.setChatAbsolute, TTLocalizer.LawbotBossPassExam, CFSpeech)) + seq.append(Wait(5.0)) + seq.append(Func(self.clearChat)) + return seq + + def __makeBossDamageMovie(self): + self.notify.debug('---- __makeBossDamageMovie') + startPos = Point3(ToontownGlobals.LawbotBossBattleThreePosHpr[0], ToontownGlobals.LawbotBossBattleThreePosHpr[1], ToontownGlobals.LawbotBossBattleThreePosHpr[2]) + startHpr = Point3(*ToontownGlobals.LawbotBossBattleThreeHpr) + bottomPos = Point3(*ToontownGlobals.LawbotBossBottomPos) + deathPos = Point3(*ToontownGlobals.LawbotBossDeathPos) + self.setPosHpr(startPos, startHpr) + bossTrack = Sequence() + bossTrack.append(Func(self.loop, 'Ff_neutral')) + track, hpr = self.rollBossToPoint(startPos, startHpr, bottomPos, None, 1) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(bottomPos, startHpr, deathPos, None, 1) + bossTrack.append(track) + duration = bossTrack.getDuration() + return bossTrack + + def __showOnscreenMessage(self, text): + self.notify.debug('----- __showOnscreenmessage') + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + self.onscreenMessage = DirectLabel(text=text, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1) + return + + def __clearOnscreenMessage(self): + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + return + + def __showWaitingMessage(self, task): + self.notify.debug('----- __showWaitingMessage') + self.__showOnscreenMessage(TTLocalizer.BuildingWaitingForVictors) + + def loadEnvironment(self): + self.notify.debug('----- loadEnvironment') + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.geom = loader.loadModel('phase_11/models/lawbotHQ/LawbotCourtroom3') + self.geom.setPos(0, 0, -71.601) + self.geom.setScale(1) + self.elevatorEntrance = self.geom.find('**/elevator_origin') + self.elevatorEntrance.getChildren().detach() + self.elevatorEntrance.setScale(1) + elevatorModel = loader.loadModel('phase_11/models/lawbotHQ/LB_Elevator') + elevatorModel.reparentTo(self.elevatorEntrance) + self.setupElevator(elevatorModel) + self.promotionMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.mid') + self.betweenBattleMusic = base.loadMusic('phase_9/audio/bgm/encntr_toon_winning.mid') + self.battleTwoMusic = base.loadMusic('phase_11/audio/bgm/LB_juryBG.mid') + floor = self.geom.find('**/MidVaultFloor1') + if floor.isEmpty(): + floor = self.geom.find('**/CR3_Floor') + self.evFloor = self.replaceCollisionPolysWithPlanes(floor) + self.evFloor.reparentTo(self.geom) + self.evFloor.setName('floor') + plane = CollisionPlane(Plane(Vec3(0, 0, 1), Point3(0, 0, -50))) + planeNode = CollisionNode('dropPlane') + planeNode.addSolid(plane) + planeNode.setCollideMask(ToontownGlobals.PieBitmask) + self.geom.attachNewNode(planeNode) + self.door3 = self.geom.find('**/SlidingDoor1/') + if self.door3.isEmpty(): + self.door3 = self.geom.find('**/interior/CR3_Door') + self.mainDoor = self.geom.find('**/Door_1') + if not self.mainDoor.isEmpty(): + itemsToHide = ['interior/Door_1'] + for str in itemsToHide: + stuffToHide = self.geom.find('**/%s' % str) + if not stuffToHide.isEmpty(): + self.notify.debug('found %s' % stuffToHide) + stuffToHide.wrtReparentTo(self.mainDoor) + else: + self.notify.debug('not found %s' % stuffToHide) + + self.reflectedMainDoor = self.geom.find('**/interiorrefl/CR3_Door') + if not self.reflectedMainDoor.isEmpty(): + itemsToHide = ['Reflections/Door_1'] + for str in itemsToHide: + stuffToHide = self.geom.find('**/%s' % str) + if not stuffToHide.isEmpty(): + self.notify.debug('found %s' % stuffToHide) + stuffToHide.wrtReparentTo(self.reflectedMainDoor) + else: + self.notify.debug('not found %s' % stuffToHide) + + self.geom.reparentTo(render) + self.loadWitnessStand() + self.loadScale() + self.scaleNodePath.stash() + self.loadJuryBox() + self.loadPodium() + ug = self.geom.find('**/Reflections') + ug.setBin('ground', -10) + + def loadJuryBox(self): + self.juryBox = self.geom.find('**/JuryBox') + juryBoxPos = self.juryBox.getPos() + newPos = juryBoxPos - Point3(*ToontownGlobals.LawbotBossJuryBoxRelativeEndPos) + if not self.debugPositions: + self.juryBox.setPos(newPos) + self.reflectedJuryBox = self.geom.find('**/JuryBox_Geo_Reflect') + reflectedJuryBoxPos = self.reflectedJuryBox.getPos() + newReflectedPos = reflectedJuryBoxPos - Point3(*ToontownGlobals.LawbotBossJuryBoxRelativeEndPos) + if not self.debugPositions: + self.reflectedJuryBox.setPos(newReflectedPos) + if not self.reflectedJuryBox.isEmpty(): + if self.debugPositions: + self.reflectedJuryBox.show() + self.reflectedJuryBox.setZ(self.reflectedJuryBox.getZ() + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[2]) + + def loadPodium(self): + self.podium = self.geom.find('**/Podium') + newZ = self.podium.getZ() - ToontownGlobals.LawbotBossBattleTwoPosHpr[2] + if not self.debugPositions: + self.podium.setZ(newZ) + self.reflectedPodium = self.geom.find('**/Podium_Geo1_Refl') + reflectedZ = self.reflectedPodium.getZ() + if not self.debugPositions: + self.reflectedPodium.setZ(reflectedZ) + if not self.reflectedPodium.isEmpty(): + if self.debugPositions: + self.reflectedPodium.show() + + def loadCannons(self): + pass + + def loadWitnessStand(self): + self.realWitnessStand = self.geom.find('**/WitnessStand') + if not self.realWitnessStand.isEmpty(): + pass + self.reflectedWitnessStand = self.geom.find('**/Witnessstand_Geo_Reflect') + if not self.reflectedWitnessStand.isEmpty(): + pass + colNode = self.realWitnessStand.find('**/witnessStandCollisions/Witnessstand_Collision') + colNode.setName('WitnessStand') + + def loadScale(self): + self.useProgrammerScale = base.config.GetBool('want-injustice-scale-debug', 0) + if self.useProgrammerScale: + self.loadScaleOld() + else: + self.loadScaleNew() + + def __debugScale(self): + prosecutionPanPos = self.prosecutionPanNodePath.getPos() + origin = Point3(0, 0, 0) + prosecutionPanRelPos = self.scaleNodePath.getRelativePoint(self.prosecutionPanNodePath, origin) + panRenderPos = render.getRelativePoint(self.prosecutionPanNodePath, origin) + self.notify.debug('prosecutionPanPos = %s' % prosecutionPanPos) + self.notify.debug('prosecutionPanRelPos = %s' % prosecutionPanRelPos) + self.notify.debug('panRenderPos = %s' % panRenderPos) + prosecutionLocatorPos = self.prosecutionLocator.getPos() + prosecutionLocatorRelPos = self.scaleNodePath.getRelativePoint(self.prosecutionLocator, origin) + locatorRenderPos = render.getRelativePoint(self.prosecutionLocator, origin) + self.notify.debug('prosecutionLocatorPos = %s ' % prosecutionLocatorPos) + self.notify.debug('prosecutionLocatorRelPos = %s ' % prosecutionLocatorRelPos) + self.notify.debug('locatorRenderPos = %s' % locatorRenderPos) + beamPos = self.beamNodePath.getPos() + beamRelPos = self.scaleNodePath.getRelativePoint(self.beamNodePath, origin) + beamRenderPos = render.getRelativePoint(self.beamNodePath, origin) + self.notify.debug('beamPos = %s' % beamPos) + self.notify.debug('beamRelPos = %s' % beamRelPos) + self.notify.debug('beamRenderPos = %s' % beamRenderPos) + beamBoundsCenter = self.beamNodePath.getBounds().getCenter() + self.notify.debug('beamBoundsCenter = %s' % beamBoundsCenter) + beamLocatorBounds = self.beamLocator.getBounds() + beamLocatorPos = beamLocatorBounds.getCenter() + self.notify.debug('beamLocatorPos = %s' % beamLocatorPos) + + def loadScaleNew(self): + self.scaleNodePath = loader.loadModel('phase_11/models/lawbotHQ/scale') + self.beamNodePath = self.scaleNodePath.find('**/scaleBeam') + self.defensePanNodePath = self.scaleNodePath.find('**/defensePan') + self.prosecutionPanNodePath = self.scaleNodePath.find('**/prosecutionPan') + self.defenseColNodePath = self.scaleNodePath.find('**/DefenseCol') + self.defenseColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeDefensePan)) + self.prosecutionColNodePath = self.scaleNodePath.find('**/ProsecutionCol') + self.prosecutionColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeProsecutionPan)) + self.standNodePath = self.scaleNodePath.find('**/scaleStand') + self.scaleNodePath.setPosHpr(*ToontownGlobals.LawbotBossInjusticePosHpr) + self.defenseLocator = self.scaleNodePath.find('**/DefenseLocator') + defenseLocBounds = self.defenseLocator.getBounds() + defenseLocPos = defenseLocBounds.getCenter() + self.notify.debug('defenseLocatorPos = %s' % defenseLocPos) + self.defensePanNodePath.setPos(defenseLocPos) + self.defensePanNodePath.reparentTo(self.beamNodePath) + self.notify.debug('defensePanNodePath.getPos()=%s' % self.defensePanNodePath.getPos()) + self.prosecutionLocator = self.scaleNodePath.find('**/ProsecutionLocator') + prosecutionLocBounds = self.prosecutionLocator.getBounds() + prosecutionLocPos = prosecutionLocBounds.getCenter() + self.notify.debug('prosecutionLocatorPos = %s' % prosecutionLocPos) + self.prosecutionPanNodePath.setPos(prosecutionLocPos) + self.prosecutionPanNodePath.reparentTo(self.beamNodePath) + self.beamLocator = self.scaleNodePath.find('**/StandLocator1') + beamLocatorBounds = self.beamLocator.getBounds() + beamLocatorPos = beamLocatorBounds.getCenter() + negBeamLocatorPos = -beamLocatorPos + self.notify.debug('beamLocatorPos = %s' % beamLocatorPos) + self.notify.debug('negBeamLocatorPos = %s' % negBeamLocatorPos) + self.beamNodePath.setPos(beamLocatorPos) + self.scaleNodePath.setScale(*ToontownGlobals.LawbotBossInjusticeScale) + self.scaleNodePath.wrtReparentTo(self.geom) + self.baseHighCol = self.scaleNodePath.find('**/BaseHighCol') + oldBitMask = self.baseHighCol.getCollideMask() + newBitMask = oldBitMask & ~ToontownGlobals.PieBitmask + newBitMask = newBitMask & ~ToontownGlobals.CameraBitmask + self.baseHighCol.setCollideMask(newBitMask) + self.defenseHighCol = self.scaleNodePath.find('**/DefenseHighCol') + self.defenseHighCol.stash() + self.defenseHighCol.setCollideMask(newBitMask) + self.baseTopCol = self.scaleNodePath.find('**/Scale_base_top_collision') + self.baseSideCol = self.scaleNodePath.find('**/Scale_base_side_col') + self.defenseLocator.hide() + self.prosecutionLocator.hide() + self.beamLocator.hide() + + def loadScaleOld(self): + startingTilt = 0 + self.scaleNodePath = NodePath('injusticeScale') + beamGeom = self.createBlock(0.25, 2, 0.125, -0.25, -2, -0.125, 0, 1.0, 0, 1.0) + self.beamNodePath = NodePath('scaleBeam') + self.beamNodePath.attachNewNode(beamGeom) + self.beamNodePath.setPos(0, 0, 3) + self.beamNodePath.reparentTo(self.scaleNodePath) + defensePanGeom = self.createBlock(0.5, 0.5, 0, -0.5, -0.5, -2, 0, 0, 1.0, 0.25) + self.defensePanNodePath = NodePath('defensePan') + self.defensePanNodePath.attachNewNode(defensePanGeom) + self.defensePanNodePath.setPos(0, -2, 0) + self.defensePanNodePath.reparentTo(self.beamNodePath) + defenseTube = CollisionTube(0, 0, -0.5, 0, 0, -1.5, 0.6) + defenseTube.setTangible(1) + defenseCollNode = CollisionNode('DefenseCol') + defenseCollNode.addSolid(defenseTube) + self.defenseColNodePath = self.defensePanNodePath.attachNewNode(defenseCollNode) + self.defenseColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeDefensePan)) + prosecutionPanGeom = self.createBlock(0.5, 0.5, 0, -0.5, -0.5, -2, 1.0, 0, 0, 1.0) + self.prosecutionPanNodePath = NodePath('prosecutionPan') + self.prosecutionPanNodePath.attachNewNode(prosecutionPanGeom) + self.prosecutionPanNodePath.setPos(0, 2, 0) + self.prosecutionPanNodePath.reparentTo(self.beamNodePath) + prosecutionTube = CollisionTube(0, 0, -0.5, 0, 0, -1.5, 0.6) + prosecutionTube.setTangible(1) + prosecutionCollNode = CollisionNode(self.uniqueName('ProsecutionCol')) + prosecutionCollNode.addSolid(prosecutionTube) + self.prosecutionColNodePath = self.prosecutionPanNodePath.attachNewNode(prosecutionCollNode) + self.prosecutionColNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeProsecutionPan)) + standGeom = self.createBlock(0.25, 0.25, 0, -0.25, -0.25, 3) + self.standNodePath = NodePath('scaleStand') + self.standNodePath.attachNewNode(standGeom) + self.standNodePath.reparentTo(self.scaleNodePath) + self.scaleNodePath.setPosHpr(*ToontownGlobals.LawbotBossInjusticePosHpr) + self.scaleNodePath.setScale(5.0) + self.scaleNodePath.wrtReparentTo(self.geom) + self.setScaleTilt(startingTilt) + + def setScaleTilt(self, tilt): + self.beamNodePath.setP(tilt) + if self.useProgrammerScale: + self.defensePanNodePath.setP(-tilt) + self.prosecutionPanNodePath.setP(-tilt) + else: + self.defensePanNodePath.setP(-tilt) + self.prosecutionPanNodePath.setP(-tilt) + + def stashBaseCol(self): + if not self.baseColStashed: + self.notify.debug('stashBaseCol') + self.baseTopCol.stash() + self.baseSideCol.stash() + self.baseColStashed = True + + def unstashBaseCol(self): + if self.baseColStashed: + self.notify.debug('unstashBaseCol') + self.baseTopCol.unstash() + self.baseSideCol.unstash() + self.baseColStashed = False + + def makeScaleReflectDamage(self): + diffDamage = self.bossDamage - ToontownGlobals.LawbotBossInitialDamage + diffDamage *= 1.0 + if diffDamage >= 0: + percentDamaged = diffDamage / (ToontownGlobals.LawbotBossMaxDamage - ToontownGlobals.LawbotBossInitialDamage) + tilt = percentDamaged * ToontownGlobals.LawbotBossWinningTilt + else: + percentDamaged = diffDamage / (ToontownGlobals.LawbotBossInitialDamage - 0) + tilt = percentDamaged * ToontownGlobals.LawbotBossWinningTilt + self.setScaleTilt(tilt) + if self.bossDamage < ToontownGlobals.LawbotBossMaxDamage * 0.85: + self.unstashBaseCol() + else: + self.stashBaseCol() + + def unloadEnvironment(self): + self.notify.debug('----- unloadEnvironment') + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + self.geom.removeNode() + del self.geom + + def __loadMopaths(self): + self.notify.debug('----- __loadMopaths') + self.toonsEnterA = Mopath.Mopath() + self.toonsEnterA.loadFile('phase_9/paths/bossBattle-toonsEnterA') + self.toonsEnterA.fFaceForward = 1 + self.toonsEnterA.timeScale = 35 + self.toonsEnterB = Mopath.Mopath() + self.toonsEnterB.loadFile('phase_9/paths/bossBattle-toonsEnterB') + self.toonsEnterB.fFaceForward = 1 + self.toonsEnterB.timeScale = 35 + + def __unloadMopaths(self): + self.notify.debug('----- __unloadMopaths') + self.toonsEnterA.reset() + self.toonsEnterB.reset() + + def enterOff(self): + self.notify.debug('----- enterOff') + DistributedBossCog.DistributedBossCog.enterOff(self) + if self.witnessToon: + self.witnessToon.clearChat() + + def enterWaitForToons(self): + self.notify.debug('----- enterWaitForToons') + DistributedBossCog.DistributedBossCog.enterWaitForToons(self) + self.geom.hide() + self.witnessToon.removeActive() + + def exitWaitForToons(self): + self.notify.debug('----- exitWaitForToons') + DistributedBossCog.DistributedBossCog.exitWaitForToons(self) + self.geom.show() + self.witnessToon.addActive() + + def enterElevator(self): + self.notify.debug('----- enterElevator') + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.witnessToon.removeActive() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleOnePosHpr) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.__hideWitnessToon() + if not self.mainDoor.isEmpty(): + self.mainDoor.stash() + if not self.reflectedMainDoor.isEmpty(): + self.reflectedMainDoor.stash() + camera.reparentTo(self.elevatorModel) + camera.setPosHpr(0, 30, 8, 180, 0, 0) + + def exitElevator(self): + self.notify.debug('----- exitElevator') + DistributedBossCog.DistributedBossCog.exitElevator(self) + self.witnessToon.removeActive() + + def enterIntroduction(self): + self.notify.debug('----- enterIntroduction') + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleOnePosHpr) + self.stopAnimate() + self.__hideWitnessToon() + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + base.playMusic(self.promotionMusic, looping=1, volume=0.9) + if not self.mainDoor.isEmpty(): + self.mainDoor.stash() + if not self.reflectedMainDoor.isEmpty(): + self.reflectedMainDoor.stash() + + def exitIntroduction(self): + self.notify.debug('----- exitIntroduction') + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.promotionMusic.stop() + if not self.mainDoor.isEmpty(): + pass + if not self.reflectedMainDoor.isEmpty(): + self.reflectedMainDoor.unstash() + if not self.elevatorEntrance.isEmpty(): + pass + + def enterBattleOne(self): + self.notify.debug('----- LawbotBoss.enterBattleOne ') + DistributedBossCog.DistributedBossCog.enterBattleOne(self) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleOnePosHpr) + self.clearChat() + self.loop('Ff_neutral') + self.notify.debug('self.battleANode = %s' % self.battleANode) + self.__hideWitnessToon() + if self.battleA == None or self.battleB == None: + pass + return + + def exitBattleOne(self): + self.notify.debug('----- exitBattleOne') + DistributedBossCog.DistributedBossCog.exitBattleOne(self) + + def stashBoss(self): + self.stash() + + def unstashBoss(self, task): + self.unstash() + self.reparentTo(render) + + def enterRollToBattleTwo(self): + self.notify.debug('----- enterRollToBattleTwo') + self.releaseToons(finalBattle=1) + self.stashBoss() + self.toonsToBattlePosition(self.involvedToons, self.battleANode) + self.stickBossToFloor() + intervalName = 'RollToBattleTwo' + seq = Sequence(self.__makeRollToBattleTwoMovie(), Func(self.__onToPrepareBattleTwo), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + taskMgr.doMethodLater(0.01, self.unstashBoss, 'unstashBoss') + + def __onToPrepareBattleTwo(self): + self.notify.debug('----- __onToPrepareBattleTwo') + self.unstickBoss() + self.setPosHpr(*ToontownGlobals.LawbotBossBattleTwoPosHpr) + self.doneBarrier('RollToBattleTwo') + + def exitRollToBattleTwo(self): + self.notify.debug('----- exitRollToBattleTwo') + self.unstickBoss() + intervalName = 'RollToBattleTwo' + self.clearInterval(intervalName) + self.betweenBattleMusic.stop() + + def enterPrepareBattleTwo(self): + self.notify.debug('----- enterPrepareBattleTwo') + self.cleanupIntervals() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.clearChat() + self.reparentTo(render) + self.__showWitnessToon() + prepareBattleTwoMovie = self.__makePrepareBattleTwoMovie() + intervalName = 'prepareBattleTwo' + seq = Sequence(prepareBattleTwoMovie, name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.acceptOnce('doneChatPage', self.__showCannonsAppearing) + base.playMusic(self.stingMusic, looping=0, volume=1.0) + + def __showCannonsAppearing(self, elapsedTime = 0): + allCannonsAppear = Sequence(Func(self.__positionToonsInFrontOfCannons), Func(camera.reparentTo, localAvatar), Func(camera.setPos, localAvatar.cameraPositions[2][0]), Func(camera.lookAt, localAvatar)) + multiCannons = Parallel() + index = 0 + self.involvedToons.sort() + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + if self.cannons.has_key(index): + cannon = self.cannons[index] + cannonSeq = cannon.generateCannonAppearTrack(toon) + multiCannons.append(cannonSeq) + index += 1 + else: + self.notify.warning('No cannon %d but we have a toon =%d' % (index, toonId)) + + allCannonsAppear.append(multiCannons) + intervalName = 'prepareBattleTwoCannonsAppear' + seq = Sequence(allCannonsAppear, Func(self.__onToBattleTwo), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + + def __onToBattleTwo(self, elapsedTime = 0): + self.notify.debug('----- __onToBattleTwo') + self.doneBarrier('PrepareBattleTwo') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleTwo(self): + self.notify.debug('----- exitPrepareBattleTwo') + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + self.__clearOnscreenMessage() + self.stingMusic.stop() + + def enterBattleTwo(self): + self.notify.debug('----- enterBattleTwo') + self.cleanupIntervals() + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(2) + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleTwoPosHpr) + self.clearChat() + self.witnessToon.clearChat() + self.releaseToons(finalBattle=1) + self.__showWitnessToon() + if not self.useCannons: + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + base.playMusic(self.battleTwoMusic, looping=1, volume=0.9) + self.startJuryBoxMoving() + for index in range(len(self.cannons)): + cannon = self.cannons[index] + cannon.cannon.show() + + def getChairParent(self): + return self.juryBox + + def startJuryBoxMoving(self): + curPos = self.juryBox.getPos() + endingAbsPos = Point3(curPos[0] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[0], curPos[1] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[1], curPos[2] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[2]) + curReflectedPos = self.reflectedJuryBox.getPos() + reflectedEndingAbsPos = Point3(curReflectedPos[0] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[0], curReflectedPos[1] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[1], curReflectedPos[2] + ToontownGlobals.LawbotBossJuryBoxRelativeEndPos[2]) + self.juryBoxIval = Parallel(self.juryBox.posInterval(ToontownGlobals.LawbotBossJuryBoxMoveTime, endingAbsPos), self.reflectedJuryBox.posInterval(ToontownGlobals.LawbotBossJuryBoxMoveTime, reflectedEndingAbsPos), SoundInterval(self.juryMovesSfx, node=self.chairs[2].nodePath, duration=ToontownGlobals.LawbotBossJuryBoxMoveTime, loop=1, volume=1.0)) + self.juryBoxIval.start() + self.juryTimer = ToontownTimer.ToontownTimer() + self.juryTimer.posInTopRightCorner() + self.juryTimer.countdown(ToontownGlobals.LawbotBossJuryBoxMoveTime) + + def exitBattleTwo(self): + self.notify.debug('----- exitBattleTwo') + intervalName = self.uniqueName('Drop') + self.clearInterval(intervalName) + self.cleanupBattles() + self.battleTwoMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + if self.juryTimer: + self.juryTimer.destroy() + del self.juryTimer + self.juryTimer = None + for chair in self.chairs.values(): + chair.stopCogsFlying() + + return + + def enterRollToBattleThree(self): + self.notify.debug('----- enterRollToBattleThree') + self.reparentTo(render) + self.stickBossToFloor() + intervalName = 'RollToBattleThree' + seq = Sequence(self.__makeRollToBattleThreeMovie(), Func(self.__onToPrepareBattleThree), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + + def __onToPrepareBattleThree(self): + self.notify.debug('----- __onToPrepareBattleThree') + self.unstickBoss() + self.setPosHpr(*ToontownGlobals.LawbotBossBattleThreePosHpr) + self.doneBarrier('RollToBattleThree') + + def exitRollToBattleThree(self): + self.notify.debug('----- exitRollToBattleThree') + self.unstickBoss() + intervalName = 'RollToBattleThree' + self.clearInterval(intervalName) + self.betweenBattleMusic.stop() + + def enterPrepareBattleThree(self): + self.notify.debug('----- enterPrepareBattleThree') + self.cleanupIntervals() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.clearChat() + self.reparentTo(render) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + self.__showWitnessToon() + prepareBattleThreeMovie = self.__makePrepareBattleThreeMovie() + self.acceptOnce('doneChatPage', self.__onToBattleThree) + intervalName = 'prepareBattleThree' + seq = Sequence(prepareBattleThreeMovie, name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + + def __onToBattleThree(self, elapsed): + self.notify.debug('----- __onToBattleThree') + self.doneBarrier('PrepareBattleThree') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleThree(self): + self.notify.debug('----- exitPrepareBattleThree') + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + intervalName = 'PrepareBattleThree' + self.clearInterval(intervalName) + self.__clearOnscreenMessage() + self.betweenBattleMusic.stop() + + def enterBattleThree(self): + DistributedBossCog.DistributedBossCog.enterBattleThree(self) + self.scaleNodePath.unstash() + localAvatar.setPos(-3, 0, 0) + camera.reparentTo(localAvatar) + camera.setPos(localAvatar.cameraPositions[0][0]) + camera.setHpr(0, 0, 0) + self.clearChat() + self.witnessToon.clearChat() + self.reparentTo(render) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.accept('enterWitnessStand', self.__touchedWitnessStand) + self.accept('pieSplat', self.__pieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + self.accept('begin-pie', self.__foundPieButton) + self.accept('enterDefenseCol', self.__enterDefenseCol) + self.accept('enterProsecutionCol', self.__enterProsecutionCol) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + taskMgr.doMethodLater(30, self.__howToGetPies, self.uniqueName('PieAdvice')) + self.stickBossToFloor() + self.setPosHpr(*ToontownGlobals.LawbotBossBattleThreePosHpr) + self.bossMaxDamage = ToontownGlobals.LawbotBossMaxDamage + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + self.__showWitnessToon() + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + if diffSettings[4]: + localAvatar.chatMgr.chatInputSpeedChat.removeCJMenu() + localAvatar.chatMgr.chatInputSpeedChat.addCJMenu(self.bonusWeight) + + def __doneBattleThree(self): + self.notify.debug('----- __doneBattleThree') + self.setState('NearVictory') + self.unstickBoss() + + def exitBattleThree(self): + self.notify.debug('----- exitBattleThree') + DistributedBossCog.DistributedBossCog.exitBattleThree(self) + NametagGlobals.setMasterArrowsOn(1) + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.ignore(bossDoneEventName) + taskMgr.remove(self.uniqueName('StandUp')) + self.ignore('enterWitnessStand') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.ignore('begin-pie') + self.ignore('enterDefenseCol') + self.ignore('enterProsecutionCol') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + if self.bossDamageMovie: + self.bossDamageMovie.finish() + self.bossDamageMovie = None + self.unstickBoss() + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + return + + def enterNearVictory(self): + self.cleanupIntervals() + self.reparentTo(render) + self.setPos(*ToontownGlobals.LawbotBossDeathPos) + self.setHpr(*ToontownGlobals.LawbotBossBattleThreeHpr) + self.clearChat() + self.releaseToons(finalBattle=1) + self.accept('pieSplat', self.__finalPieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.happy = 0 + self.raised = 0 + self.forward = 1 + self.doAnimate() + self.setDizzy(1) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def exitNearVictory(self): + self.notify.debug('----- exitNearVictory') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.setDizzy(0) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterVictory(self): + self.notify.debug('----- enterVictory') + self.cleanupIntervals() + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.LawbotBossBattleThreePosHpr) + self.loop('neutral') + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.clearChat() + self.witnessToon.clearChat() + self.controlToons() + self.setToonsToNeutral(self.involvedToons) + self.happy = 1 + self.raised = 1 + self.forward = 1 + intervalName = 'VictoryMovie' + seq = Sequence(self.makeVictoryMovie(), Func(self.__continueVictory), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __continueVictory(self): + self.notify.debug('----- __continueVictory') + self.stopAnimate() + self.doneBarrier('Victory') + + def exitVictory(self): + self.notify.debug('----- exitVictory') + self.stopAnimate() + self.unstash() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterDefeat(self): + self.notify.debug('----- enterDefeat') + self.cleanupIntervals() + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.reparentTo(render) + self.clearChat() + self.releaseToons(finalBattle=1) + self.happy = 0 + self.raised = 0 + self.forward = 1 + intervalName = 'DefeatMovie' + seq = Sequence(self.makeDefeatMovie(), Func(self.__continueDefeat), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __continueDefeat(self): + self.notify.debug('----- __continueDefeat') + self.stopAnimate() + self.doneBarrier('Defeat') + + def exitDefeat(self): + self.notify.debug('----- exitDefeat') + self.stopAnimate() + self.unstash() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.witnessToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'LawbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __doneReward(self): + self.notify.debug('----- __doneReward') + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + self.notify.debug('----- exitReward') + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.battleThreeMusicTime = 0 + self.battleThreeMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.witnessToon.clearChat() + self.stash() + self.stopAnimate() + self.controlToons() + self.__showWitnessToon() + self.witnessToon.reparentTo(render) + self.witnessToon.setPosHpr(*ToontownGlobals.LawbotBossWitnessEpiloguePosHpr) + self.witnessToon.loop('Sit') + self.__arrangeToonsAroundWitnessToon() + camera.reparentTo(render) + camera.setPos(self.witnessToon, -9, 12, 6) + camera.lookAt(self.witnessToon, 0, 0, 3) + intervalName = 'EpilogueMovie' + seq = Sequence(self.makeEpilogueMovie(), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.accept('doneChatPage', self.__doneEpilogue) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __doneEpilogue(self, elapsedTime = 0): + self.notify.debug('----- __doneEpilogue') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.notify.debug('----- exitEpilogue') + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.epilogueMusic.stop() + + def enterFrolic(self): + self.notify.debug('----- enterFrolic') + self.setPosHpr(*ToontownGlobals.LawbotBossBattleOnePosHpr) + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.show() + + def doorACallback(self, isOpen): + if self.insidesANodePath: + if isOpen: + self.insidesANodePath.unstash() + else: + self.insidesANodePath.stash() + + def doorBCallback(self, isOpen): + if self.insidesBNodePath: + if isOpen: + self.insidesBNodePath.unstash() + else: + self.insidesBNodePath.stash() + + def __toonsToPromotionPosition(self, toonIds, battleNode): + self.notify.debug('----- __toonsToPromotionPosition') + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + for i in range(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.reparentTo(render) + pos, h = points[i] + toon.setPosHpr(battleNode, pos[0], pos[1] + 10, pos[2], h, 0, 0) + + def __outOfPies(self): + self.notify.debug('----- outOfPies') + self.__showOnscreenMessage(TTLocalizer.LawbotBossNeedMoreEvidence) + taskMgr.doMethodLater(20, self.__howToGetPies, self.uniqueName('PieAdvice')) + + def __howToGetPies(self, task): + self.notify.debug('----- __howToGetPies') + self.__showOnscreenMessage(TTLocalizer.LawbotBossHowToGetEvidence) + + def __howToThrowPies(self, task): + self.notify.debug('----- __howToThrowPies') + self.__showOnscreenMessage(TTLocalizer.LawbotBossHowToThrowPies) + + def __foundPieButton(self): + self.everThrownPie = 1 + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + + def __touchedWitnessStand(self, entry): + self.sendUpdate('touchWitnessStand', []) + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + base.playSfx(self.piesRestockSfx) + if not self.everThrownPie: + taskMgr.doMethodLater(30, self.__howToThrowPies, self.uniqueName('PieAdvice')) + + def __pieSplat(self, toon, pieCode): + if pieCode == ToontownGlobals.PieCodeBossInsides: + if toon == localAvatar: + self.d_hitBossInsides() + self.flashRed() + elif pieCode == ToontownGlobals.PieCodeBossCog: + if toon == localAvatar: + self.d_hitBoss(1) + if self.dizzy: + self.flashRed() + self.doAnimate('hit', now=1) + elif pieCode == ToontownGlobals.PieCodeDefensePan: + self.flashRed() + self.flashPanBlue() + base.playSfx(self.evidenceHitSfx, node=self.defensePanNodePath, volume=0.25) + if toon == localAvatar: + self.d_hitBoss(self.panDamage) + elif pieCode == ToontownGlobals.PieCodeProsecutionPan: + self.flashGreen() + if toon == localAvatar: + pass + elif pieCode == ToontownGlobals.PieCodeLawyer: + pass + + def __localPieSplat(self, pieCode, entry): + if pieCode == ToontownGlobals.PieCodeLawyer: + self.__lawyerGotHit(entry) + if pieCode != ToontownGlobals.PieCodeToon: + return + avatarDoId = entry.getIntoNodePath().getNetTag('avatarDoId') + if avatarDoId == '': + self.notify.warning('Toon %s has no avatarDoId tag.' % repr(entry.getIntoNodePath())) + return + doId = int(avatarDoId) + if doId != localAvatar.doId: + self.d_hitToon(doId) + + def __lawyerGotHit(self, entry): + lawyerCol = entry.getIntoNodePath() + names = lawyerCol.getName().split('-') + lawyerDoId = int(names[1]) + for lawyer in self.lawyers: + if lawyerDoId == lawyer.doId: + lawyer.sendUpdate('hitByToon', []) + + def __finalPieSplat(self, toon, pieCode): + if pieCode != ToontownGlobals.PieCodeDefensePan: + return + self.sendUpdate('finalPieSplat', []) + self.ignore('pieSplat') + + def cleanupAttacks(self): + self.notify.debug('----- cleanupAttacks') + self.__cleanupStrafe() + + def __cleanupStrafe(self): + self.notify.debug('----- __cleanupStrage') + if self.strafeInterval: + self.strafeInterval.finish() + self.strafeInterval = None + return + + def __cleanupJuryBox(self): + self.notify.debug('----- __cleanupJuryBox') + if self.juryBoxIval: + self.juryBoxIval.finish() + self.juryBoxIval = None + if self.juryBox: + self.juryBox.removeNode() + return + + def doStrafe(self, side, direction): + gearRoot = self.rotateNode.attachNewNode('gearRoot') + if side == 0: + gearRoot.setPos(0, -7, 3) + gearRoot.setHpr(180, 0, 0) + door = self.doorA + else: + gearRoot.setPos(0, 7, 3) + door = self.doorB + gearRoot.setTag('attackCode', str(ToontownGlobals.BossCogStrafeAttack)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.1) + t = self.getBossDamage() / 100.0 + gearTrack = Parallel() + numGears = int(4 + 6 * t + 0.5) + time = 5.0 - 4.0 * t + spread = 60 * math.pi / 180.0 + if direction == 1: + spread = -spread + dist = 50 + rate = time / numGears + for i in range(numGears): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 0, 0) + gear = gearModel.instanceTo(node) + angle = (float(i) / (numGears - 1) - 0.5) * spread + x = dist * math.sin(angle) + y = dist * math.cos(angle) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * rate), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, 0), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1), Sequence(SoundInterval(self.strafeSfx[i], volume=0.2, node=self), duration=0)), Func(node.detachNode))) + + seq = Sequence(Func(door.request, 'open'), Wait(0.7), gearTrack, Func(door.request, 'close')) + self.__cleanupStrafe() + self.strafeInterval = seq + seq.start() + + def replaceCollisionPolysWithPlanes(self, model): + newCollisionNode = CollisionNode('collisions') + newCollideMask = BitMask32(0) + planes = [] + collList = model.findAllMatches('**/+CollisionNode') + if not collList: + collList = [model] + for cnp in collList: + cn = cnp.node() + if not isinstance(cn, 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, CollisionPolygon): + plane = 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 = CollisionPlane(plane) + newCollisionNode.addSolid(cp) + lastPlane = plane + + return NodePath(newCollisionNode) + + def makeIntroductionMovie(self, delayDeletes): + self.notify.debug('----- makeIntroductionMovie') + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'LawbotBoss.makeIntroductionMovie')) + + track = Parallel() + bossAnimTrack = Sequence( + ActorInterval(self, 'Ff_speech', startTime=2, duration=10, loop=1), + ActorInterval(self, 'Ff_lookRt', duration=3), + ActorInterval(self, 'Ff_lookRt', duration=3, startTime=3, endTime=0), + ActorInterval(self, 'Ff_neutral', duration=2), + ActorInterval(self, 'Ff_speech', duration=7, loop=1)) + track.append(bossAnimTrack) + attackToons = TTLocalizer.BossCogAttackToons + dialogTrack = Track( + (0, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro0, CFSpeech)), + (5.6, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro1, CFSpeech)), + (12, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro2, CFSpeech)), + (18, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro3, CFSpeech)), + (22, Func(self.setChatAbsolute, TTLocalizer.LawbotBossTempIntro4, CFSpeech)), + (24, Sequence( + Func(self.clearChat), + self.loseCogSuits(self.toonsA + self.toonsB, render, (-2.798, -70, 10, 180, 0, 0)))), + (27, Sequence( + self.toonNormalEyes(self.involvedToons), + Func(self.loop, 'Ff_neutral'), + Func(self.setChatAbsolute, attackToons, CFSpeech)))) + track.append(dialogTrack) + return Sequence( + Func(self.stickToonsToFloor), + track, + Func(self.unstickToons), name=self.uniqueName('Introduction')) + + def walkToonsToBattlePosition(self, toonIds, battleNode): + self.notify.debug('walkToonsToBattlePosition-----------------------------------------------') + self.notify.debug('toonIds=%s battleNode=%s' % (toonIds, battleNode)) + ival = Parallel() + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + self.notify.debug('walkToonsToBattlePosition: points = %s' % points[0][0]) + for i in range(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + pos, h = points[i] + origPos = pos + self.notify.debug('origPos = %s' % origPos) + self.notify.debug('batlleNode.getTransform = %s render.getTransform=%s' % (battleNode.getTransform(), render.getTransform())) + self.notify.debug('render.getScale()=%s battleNode.getScale()=%s' % (render.getScale(), battleNode.getScale())) + myCurPos = self.getPos() + self.notify.debug('myCurPos = %s' % self.getPos()) + self.notify.debug('battleNode.parent() = %s' % battleNode.getParent()) + self.notify.debug('battleNode.parent().getPos() = %s' % battleNode.getParent().getPos()) + bnParent = battleNode.getParent() + battleNode.wrtReparentTo(render) + bnWorldPos = battleNode.getPos() + battleNode.wrtReparentTo(bnParent) + self.notify.debug('battle node world pos = %s' % bnWorldPos) + pos = render.getRelativePoint(battleNode, pos) + self.notify.debug('walktToonsToBattlePosition: render.getRelativePoint result = %s' % pos) + self.notify.debug('walkToonsToBattlePosition: final pos = %s' % pos) + ival.append(Sequence(Func(toon.setPlayRate, 0.8, 'walk'), Func(toon.loop, 'walk'), toon.posInterval(3, pos), Func(toon.setPlayRate, 1, 'walk'), Func(toon.loop, 'neutral'))) + + return ival + + def toonsToBattlePosition(self, toonIds, battleNode): + self.notify.debug('DistrutedLawbotBoss.toonsToBattlePosition----------------------------------------') + self.notify.debug('toonIds=%s battleNode=%s' % (toonIds, battleNode)) + if len(toonIds) < 5: + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + else: + points = list(BattleBase.BattleBase.toonPoints[3]) + points.extend(BattleBase.BattleBase.toonPoints[len(toonIds) - 5]) + self.notify.debug('toonsToBattlePosition: points = %s' % points[0][0]) + for i in range(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.wrtReparentTo(render) + pos, h = points[i] + if i > 3: + pos.setY(pos.getY() + 2.0) + bnParent = battleNode.getParent() + battleNode.wrtReparentTo(render) + bnWorldPos = battleNode.getPos() + battleNode.wrtReparentTo(bnParent) + toon.setPosHpr(battleNode, pos[0], pos[1], pos[2], h, 0, 0) + self.notify.debug('new toon pos %s ' % toon.getPos()) + + def touchedGavel(self, gavel, entry): + self.notify.debug('touchedGavel') + attackCodeStr = entry.getIntoNodePath().getNetTag('attackCode') + if attackCodeStr == '': + self.notify.warning('Node %s has no attackCode tag.' % repr(entry.getIntoNodePath())) + return + attackCode = int(attackCodeStr) + into = entry.getIntoNodePath() + self.zapLocalToon(attackCode, into) + + def touchedGavelHandle(self, gavel, entry): + attackCodeStr = entry.getIntoNodePath().getNetTag('attackCode') + if attackCodeStr == '': + self.notify.warning('Node %s has no attackCode tag.' % repr(entry.getIntoNodePath())) + return + attackCode = int(attackCodeStr) + into = entry.getIntoNodePath() + self.zapLocalToon(attackCode, into) + + def createBlock(self, x1, y1, z1, x2, y2, z2, r = 1.0, g = 1.0, b = 1.0, a = 1.0): + gFormat = GeomVertexFormat.getV3n3cpt2() + myVertexData = GeomVertexData('holds my vertices', gFormat, Geom.UHDynamic) + vertexWriter = GeomVertexWriter(myVertexData, 'vertex') + normalWriter = GeomVertexWriter(myVertexData, 'normal') + colorWriter = GeomVertexWriter(myVertexData, 'color') + texWriter = GeomVertexWriter(myVertexData, 'texcoord') + vertexWriter.addData3f(x1, y1, z1) + vertexWriter.addData3f(x2, y1, z1) + vertexWriter.addData3f(x1, y2, z1) + vertexWriter.addData3f(x2, y2, z1) + vertexWriter.addData3f(x1, y1, z2) + vertexWriter.addData3f(x2, y1, z2) + vertexWriter.addData3f(x1, y2, z2) + vertexWriter.addData3f(x2, y2, z2) + for index in range(8): + normalWriter.addData3f(1.0, 1.0, 1.0) + colorWriter.addData4f(r, g, b, a) + texWriter.addData2f(1.0, 1.0) + + tris = GeomTriangles(Geom.UHDynamic) + tris.addVertex(0) + tris.addVertex(1) + tris.addVertex(2) + tris.closePrimitive() + tris.addVertex(1) + tris.addVertex(3) + tris.addVertex(2) + tris.closePrimitive() + tris.addVertex(2) + tris.addVertex(3) + tris.addVertex(6) + tris.closePrimitive() + tris.addVertex(3) + tris.addVertex(7) + tris.addVertex(6) + tris.closePrimitive() + tris.addVertex(0) + tris.addVertex(2) + tris.addVertex(4) + tris.closePrimitive() + tris.addVertex(2) + tris.addVertex(6) + tris.addVertex(4) + tris.closePrimitive() + tris.addVertex(1) + tris.addVertex(5) + tris.addVertex(3) + tris.closePrimitive() + tris.addVertex(3) + tris.addVertex(5) + tris.addVertex(7) + tris.closePrimitive() + tris.addVertex(0) + tris.addVertex(4) + tris.addVertex(5) + tris.closePrimitive() + tris.addVertex(1) + tris.addVertex(0) + tris.addVertex(5) + tris.closePrimitive() + tris.addVertex(4) + tris.addVertex(6) + tris.addVertex(7) + tris.closePrimitive() + tris.addVertex(7) + tris.addVertex(5) + tris.addVertex(4) + tris.closePrimitive() + cubeGeom = Geom(myVertexData) + cubeGeom.addPrimitive(tris) + cubeGN = GeomNode('cube') + cubeGN.addGeom(cubeGeom) + return cubeGN + + def __enterDefenseCol(self, entry): + self.notify.debug('__enterDefenseCol') + + def __enterProsecutionCol(self, entry): + self.notify.debug('__enterProsecutionCol') + + def makeVictoryMovie(self): + myFromPos = Point3(ToontownGlobals.LawbotBossBattleThreePosHpr[0], ToontownGlobals.LawbotBossBattleThreePosHpr[1], ToontownGlobals.LawbotBossBattleThreePosHpr[2]) + myToPos = Point3(myFromPos[0], myFromPos[1] + 30, myFromPos[2]) + rollThroughDoor = self.rollBossToPoint(fromPos=myFromPos, fromHpr=None, toPos=myToPos, toHpr=None, reverse=0) + rollTrack = Sequence( + Func(self.getGeomNode().setH, 180), + rollThroughDoor[0], + Func(self.getGeomNode().setH, 0)) + rollTrackDuration = rollTrack.getDuration() + self.notify.debug('rollTrackDuration = %f' % rollTrackDuration) + doorStartPos = self.door3.getPos() + doorEndPos = Point3(doorStartPos[0], doorStartPos[1], doorStartPos[2] + 25) + bossTrack = Track( + (0.5, Sequence( + Func(self.clearChat), + Func(camera.reparentTo, render), + Func(camera.setPos, -3, 45, 25), + Func(camera.setHpr, 0, 10, 0))), + (1.0, Func(self.setChatAbsolute, TTLocalizer.LawbotBossDefenseWins1, CFSpeech)), + (5.5, Func(self.setChatAbsolute, TTLocalizer.LawbotBossDefenseWins2, CFSpeech)), + (9.5, Sequence(Func(camera.wrtReparentTo, render))), + (9.6, Parallel( + rollTrack, + Func(self.setChatAbsolute, TTLocalizer.LawbotBossDefenseWins3, CFSpeech), + self.door3.posInterval(2, doorEndPos, startPos=doorStartPos))), + (13.1, Sequence(self.door3.posInterval(1, doorStartPos)))) + retTrack = Parallel(bossTrack, ActorInterval(self, 'Ff_speech', loop=1)) + return bossTrack + + def makeEpilogueMovie(self): + epSpeech = TTLocalizer.WitnessToonCongratulations + epSpeech = self.__talkAboutPromotion(epSpeech) + bossTrack = Sequence(Func(self.witnessToon.animFSM.request, 'neutral'), Func(self.witnessToon.setLocalPageChat, epSpeech, 0)) + return bossTrack + + def makeDefeatMovie(self): + bossTrack = Track((0.0, Sequence(Func(self.clearChat), Func(self.reverseHead), ActorInterval(self, 'Ff_speech'))), (1.0, Func(self.setChatAbsolute, TTLocalizer.LawbotBossProsecutionWins, CFSpeech))) + return bossTrack + + def __makeWitnessToon(self): + dnaNetString = 't\x1b\x00\x01\x01\x00\x03\x00\x03\x01\x10\x13\x00\x13\x13' + npc = Toon.Toon() + npc.setDNAString(dnaNetString) + npc.setName(TTLocalizer.WitnessToonName) + npc.setPickable(0) + npc.setPlayerType(NametagGroup.CCNonPlayer) + npc.animFSM.request('Sit') + self.witnessToon = npc + self.witnessToon.setPosHpr(*ToontownGlobals.LawbotBossWitnessStandPosHpr) + + def __cleanupWitnessToon(self): + self.__hideWitnessToon() + if self.witnessToon: + self.witnessToon.removeActive() + self.witnessToon.delete() + self.witnessToon = None + return + + def __showWitnessToon(self): + if not self.witnessToonOnstage: + self.witnessToon.addActive() + self.witnessToon.reparentTo(self.geom) + seatCenter = self.realWitnessStand.find('**/witnessStandSeatEdge') + center = seatCenter.getPos() + self.notify.debug('center = %s' % center) + self.witnessToon.setPos(center) + self.witnessToon.setH(180) + self.witnessToon.setZ(self.witnessToon.getZ() - 1.5) + self.witnessToon.setY(self.witnessToon.getY() - 1.15) + self.witnessToonOnstage = 1 + + def __hideWitnessToon(self): + if self.witnessToonOnstage: + self.witnessToon.removeActive() + self.witnessToon.detachNode() + self.witnessToonOnstage = 0 + + def __hideToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.hide() + + def __showToons(self): + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + toon.show() + + def __arrangeToonsAroundWitnessToon(self): + radius = 7 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in range(numToons): + toon = self.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 90 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.witnessToon, x, y, 0) + toon.headsUp(self.witnessToon) + toon.loop('neutral') + toon.show() + + def __talkAboutPromotion(self, speech): + if self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.WitnessToonLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.WitnessToonHPBoost + else: + speech += TTLocalizer.WitnessToonMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + return speech + + def __positionToonsInFrontOfCannons(self): + self.notify.debug('__positionToonsInFrontOfCannons') + index = 0 + self.involvedToons.sort() + for toonId in self.involvedToons: + if self.cannons.has_key(index): + cannon = self.cannons[index] + toon = self.cr.doId2do.get(toonId) + self.notify.debug('cannonId = %d' % cannon.doId) + cannonPos = cannon.nodePath.getPos(render) + self.notify.debug('cannonPos = %s' % cannonPos) + if toon: + self.notify.debug('toon = %s' % toon.getName()) + toon.reparentTo(cannon.nodePath) + toon.setPos(0, 8, 0) + toon.setH(180) + renderPos = toon.getPos(render) + self.notify.debug('renderPos =%s' % renderPos) + index += 1 + + self.notify.debug('done with positionToons') + + def __makePrepareBattleTwoMovie(self): + chatString = TTLocalizer.WitnessToonPrepareBattleTwo % ToontownGlobals.LawbotBossJurorsForBalancedScale + movie = Sequence(Func(camera.reparentTo, self.witnessToon), Func(camera.setPos, 0, 8, 2), Func(camera.setHpr, 180, 10, 0), Func(self.witnessToon.setLocalPageChat, chatString, 0)) + return movie + + def __doWitnessPrepareBattleThreeChat(self): + self.notify.debug('__doWitnessPrepareBattleThreeChat: original self.numToonJurorsSeated = %d' % self.numToonJurorsSeated) + self.countToonJurors() + self.notify.debug('after calling self.countToonJurors, numToonJurorsSeated=%d' % self.numToonJurorsSeated) + if self.numToonJurorsSeated == 0: + juryResult = TTLocalizer.WitnessToonNoJuror + elif self.numToonJurorsSeated == 1: + juryResult = TTLocalizer.WitnessToonOneJuror + elif self.numToonJurorsSeated == 12: + juryResult = TTLocalizer.WitnessToonAllJurors + else: + juryResult = TTLocalizer.WitnessToonSomeJurors % self.numToonJurorsSeated + juryResult += '\x07' + trialSpeech = juryResult + trialSpeech += TTLocalizer.WitnessToonPrepareBattleThree + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + if diffSettings[4]: + newWeight, self.bonusWeight, self.numJurorsLocalToonSeated = self.calculateWeightOfToon(base.localAvatar.doId) + if self.bonusWeight > 0: + if self.bonusWeight == 1: + juryWeightBonus = TTLocalizer.WitnessToonJuryWeightBonusSingular.get(self.battleDifficulty) + else: + juryWeightBonus = TTLocalizer.WitnessToonJuryWeightBonusPlural.get(self.battleDifficulty) + if juryWeightBonus: + weightBonusText = juryWeightBonus % (self.numJurorsLocalToonSeated, self.bonusWeight) + trialSpeech += '\x07' + trialSpeech += weightBonusText + self.witnessToon.setLocalPageChat(trialSpeech, 0) + + def __makePrepareBattleThreeMovie(self): + movie = Sequence(Func(camera.reparentTo, render), Func(camera.setPos, -15, 15, 20), Func(camera.setHpr, -90, 0, 0), Wait(3), Func(camera.reparentTo, self.witnessToon), Func(camera.setPos, 0, 8, 2), Func(camera.setHpr, 180, 10, 0), Func(self.__doWitnessPrepareBattleThreeChat)) + return movie + + def countToonJurors(self): + self.numToonJurorsSeated = 0 + for key in self.chairs.keys(): + chair = self.chairs[key] + if chair.state == 'ToonJuror' or chair.state == None and chair.newState == 'ToonJuror': + self.numToonJurorsSeated += 1 + + self.notify.debug('self.numToonJurorsSeated = %d' % self.numToonJurorsSeated) + return + + def cleanupPanFlash(self): + if self.panFlashInterval: + self.panFlashInterval.finish() + self.panFlashInterval = None + return + + def flashPanBlue(self): + self.cleanupPanFlash() + intervalName = 'FlashPanBlue' + self.defensePanNodePath.setColorScale(1, 1, 1, 1) + seq = Sequence(self.defensePanNodePath.colorScaleInterval(0.1, colorScale=VBase4(0, 0, 1, 1)), self.defensePanNodePath.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1)), name=intervalName) + self.panFlashInterval = seq + seq.start() + self.storeInterval(seq, intervalName) + + def saySomething(self, chatString): + intervalName = 'ChiefJusticeTaunt' + seq = Sequence(name=intervalName) + seq.append(Func(self.setChatAbsolute, chatString, CFSpeech)) + seq.append(Wait(4.0)) + seq.append(Func(self.clearChat)) + oldSeq = self.activeIntervals.get(intervalName) + if oldSeq: + oldSeq.finish() + seq.start() + self.storeInterval(seq, intervalName) + + def setTaunt(self, tauntIndex, extraInfo): + gotError = False + if not hasattr(self, 'state'): + self.notify.warning('returning from setTaunt, no attr state') + gotError = True + elif not self.state == 'BattleThree': + self.notify.warning('returning from setTaunt, not in battle three state, state=%s', self.state) + gotError = True + if not hasattr(self, 'nametag'): + self.notify.warning('returning from setTaunt, no attr nametag') + gotError = True + if gotError: + st = StackTrace() + print st + return + chatString = TTLocalizer.LawbotBossTaunts[1] + if tauntIndex == 0: + if extraInfo < len(self.involvedToons): + toonId = self.involvedToons[extraInfo] + toon = base.cr.doId2do.get(toonId) + if toon: + chatString = TTLocalizer.LawbotBossTaunts[tauntIndex] % toon.getName() + else: + chatString = TTLocalizer.LawbotBossTaunts[tauntIndex] + self.saySomething(chatString) + + def toonGotHealed(self, toonId): + toon = base.cr.doId2do.get(toonId) + if toon: + base.playSfx(self.toonUpSfx, node=toon) + + def hideBonusTimer(self): + if self.bonusTimer: + self.bonusTimer.hide() + + def enteredBonusState(self): + self.witnessToon.clearChat() + text = TTLocalizer.WitnessToonBonus % (ToontownGlobals.LawbotBossBonusWeightMultiplier, ToontownGlobals.LawbotBossBonusDuration) + self.witnessToon.setChatAbsolute(text, CFSpeech | CFTimeout) + base.playSfx(self.toonUpSfx) + if not self.bonusTimer: + self.bonusTimer = ToontownTimer.ToontownTimer() + self.bonusTimer.posInTopRightCorner() + self.bonusTimer.show() + self.bonusTimer.countdown(ToontownGlobals.LawbotBossBonusDuration, self.hideBonusTimer) + + def setAttackCode(self, attackCode, avId = 0): + DistributedBossCog.DistributedBossCog.setAttackCode(self, attackCode, avId) + if attackCode == ToontownGlobals.BossCogAreaAttack: + self.saySomething(TTLocalizer.LawbotBossAreaAttackTaunt) + base.playSfx(self.warningSfx) + + def setBattleDifficulty(self, diff): + self.notify.debug('battleDifficulty = %d' % diff) + self.battleDifficulty = diff + + def toonEnteredCannon(self, toonId, cannonIndex): + if base.localAvatar.doId == toonId: + self.cannonIndex = cannonIndex + + def numJurorsSeatedByCannon(self, cannonIndex): + retVal = 0 + for chair in self.chairs.values(): + if chair.state == 'ToonJuror': + if chair.toonJurorIndex == cannonIndex: + retVal += 1 + + return retVal + + def calculateWeightOfToon(self, toonId): + defaultWeight = 1 + bonusWeight = 0 + newWeight = 1 + cannonIndex = self.cannonIndex + numJurors = 0 + if not cannonIndex == None and cannonIndex >= 0: + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + if diffSettings[4]: + numJurors = self.numJurorsSeatedByCannon(cannonIndex) + bonusWeight = numJurors - diffSettings[5] + if bonusWeight < 0: + bonusWeight = 0 + newWeight = defaultWeight + bonusWeight + self.notify.debug('toon %d has weight of %d' % (toonId, newWeight)) + return (newWeight, bonusWeight, numJurors) diff --git a/toontown/suit/DistributedLawbotBossAI.py b/toontown/suit/DistributedLawbotBossAI.py new file mode 100644 index 0000000..41795a5 --- /dev/null +++ b/toontown/suit/DistributedLawbotBossAI.py @@ -0,0 +1,857 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +import DistributedBossCogAI +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import DistributedAvatarAI +import DistributedSuitAI +from toontown.battle import BattleExperienceAI +from direct.fsm import FSM +from toontown.toonbase import ToontownGlobals +from toontown.toon import InventoryBase +from toontown.toonbase import TTLocalizer +from toontown.battle import BattleBase +from toontown.toon import NPCToons +from toontown.building import SuitBuildingGlobals +import SuitDNA, random +from toontown.coghq import DistributedLawbotBossGavelAI +from toontown.suit import DistributedLawbotBossSuitAI +from toontown.coghq import DistributedLawbotCannonAI +from toontown.coghq import DistributedLawbotChairAI +from toontown.toonbase import ToontownBattleGlobals + +class DistributedLawbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossAI') + limitHitCount = 6 + hitCountDamage = 35 + numPies = 10 + maxToonLevels = 77 + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'l') + FSM.FSM.__init__(self, 'DistributedLawbotBossAI') + self.lawyers = [] + self.cannons = None + self.chairs = None + self.gavels = None + self.cagedToonNpcId = random.choice(NPCToons.npcFriends.keys()) + self.bossMaxDamage = ToontownGlobals.LawbotBossMaxDamage + self.recoverRate = 0 + self.recoverStartTime = 0 + self.bossDamage = ToontownGlobals.LawbotBossInitialDamage + self.useCannons = 1 + self.numToonJurorsSeated = 0 + self.cannonBallsLeft = {} + self.toonLevels = 0 + if 'Defeat' not in self.keyStates: + self.keyStates.append('Defeat') + self.toonupValue = 1 + self.bonusState = False + self.bonusTimeStarted = 0 + self.numBonusStates = 0 + self.battleThreeTimeStarted = 0 + self.battleThreeTimeInMin = 0 + self.numAreaAttacks = 0 + self.lastAreaAttackTime = 0 + self.weightPerToon = {} + self.cannonIndexPerToon = {} + self.battleDifficulty = 0 + return + + def delete(self): + self.notify.debug('DistributedLawbotBossAI.delete') + self.__deleteBattleThreeObjects() + self.__deleteBattleTwoObjects() + taskName = self.uniqueName('clearBonus') + taskMgr.remove(taskName) + return DistributedBossCogAI.DistributedBossCogAI.delete(self) + + def getHoodId(self): + return ToontownGlobals.LawbotHQ + + def getCagedToonNpcId(self): + return self.cagedToonNpcId + + def magicWordHit(self, damage, avId): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + self.hitBossInsides() + self.hitBoss(damage) + + def hitBoss(self, bossDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'DistributedLawbotbossAI.hitBoss from unknown avatar'): + return + self.validate(avId, bossDamage == 1, 'invalid bossDamage %s' % bossDamage) + if bossDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + if bossDamage <= 12: + newWeight = self.weightPerToon.get(avId) + if newWeight: + bossDamage = newWeight + if self.bonusState and bossDamage <= 12: + bossDamage *= ToontownGlobals.LawbotBossBonusWeightMultiplier + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage >= self.bossMaxDamage: + self.b_setState('Victory') + else: + self.__recordHit() + + def healBoss(self, bossHeal): + bossDamage = -bossHeal + avId = self.air.getAvatarIdFromSender() + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + bossDamage = max(bossDamage, 0) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage == 0: + self.b_setState('Defeat') + else: + self.__recordHit() + + def hitBossInsides(self): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBossInsides from unknown avatar'): + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + self.b_setAttackCode(ToontownGlobals.BossCogDizzyNow) + self.b_setBossDamage(self.getBossDamage(), 0, 0) + + def hitToon(self, toonId): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId != toonId, 'hitToon on self'): + return + if avId not in self.involvedToons or toonId not in self.involvedToons: + return + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, self.toonupValue) + self.sendUpdate('toonGotHealed', [toonId]) + + def touchCage(self): + avId = self.air.getAvatarIdFromSender() + currState = self.getCurrentOrNextState() + if currState != 'BattleThree' and currState != 'NearVictory': + return + if not self.validate(avId, avId in self.involvedToons, 'touchCage from unknown avatar'): + return + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(self.numPies) + toon.__touchedCage = 1 + + def touchWitnessStand(self): + self.touchCage() + + def finalPieSplat(self): + self.notify.debug('finalPieSplat') + if self.state != 'NearVictory': + return + self.b_setState('Victory') + + def doTaunt(self): + if not self.state == 'BattleThree': + return + tauntIndex = random.randrange(len(TTLocalizer.LawbotBossTaunts)) + extraInfo = 0 + if tauntIndex == 0 and self.involvedToons: + extraInfo = random.randrange(len(self.involvedToons)) + self.sendUpdate('setTaunt', [tauntIndex, extraInfo]) + + def doNextAttack(self, task): + for lawyer in self.lawyers: + lawyer.doNextAttack(self) + + self.waitForNextAttack(ToontownGlobals.LawbotBossLawyerCycleTime) + timeSinceLastAttack = globalClock.getFrameTime() - self.lastAreaAttackTime + allowedByTime = 15 < timeSinceLastAttack or self.lastAreaAttackTime == 0 + doAttack = random.randrange(1,101) + self.notify.debug('allowedByTime=%d doAttack=%d' % (allowedByTime, doAttack)) + if doAttack <= ToontownGlobals.LawbotBossChanceToDoAreaAttack and allowedByTime: + self.__doAreaAttack() + self.numAreaAttacks += 1 + self.lastAreaAttackTime = globalClock.getFrameTime() + else: + chanceToDoTaunt = ToontownGlobals.LawbotBossChanceForTaunt + action = random.randrange(1,101) + if action <= chanceToDoTaunt: + self.doTaunt() + pass + return + if self.attackCode == ToontownGlobals.BossCogDizzyNow: + attackCode = ToontownGlobals.BossCogRecoverDizzyAttack + else: + attackCode = random.choice([ToontownGlobals.BossCogAreaAttack, + ToontownGlobals.BossCogFrontAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack, + ToontownGlobals.BossCogDirectedAttack]) + if attackCode == ToontownGlobals.BossCogAreaAttack: + self.__doAreaAttack() + elif attackCode == ToontownGlobals.BossCogDirectedAttack: + self.__doDirectedAttack() + else: + self.b_setAttackCode(attackCode) + + def __doAreaAttack(self): + self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack) + + def __doDirectedAttack(self): + if self.nearToons: + toonId = random.choice(self.nearToons) + self.b_setAttackCode(ToontownGlobals.BossCogDirectedAttack, toonId) + else: + self.__doAreaAttack() + + def b_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.d_setBossDamage(bossDamage, recoverRate, recoverStartTime) + self.setBossDamage(bossDamage, recoverRate, recoverStartTime) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + + def getBossDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return int(max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0)) + + def d_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setBossDamage', [bossDamage, recoverRate, timestamp]) + + def waitForNextStrafe(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextStrafe, taskName) + + def stopStrafes(self): + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + + def doNextStrafe(self, task): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + side = random.choice([0, 1]) + direction = random.choice([0, 1]) + self.sendUpdate('doStrafe', [side, direction]) + delayTime = 9 + self.waitForNextStrafe(delayTime) + + def __sendLawyerIds(self): + lawyerIds = [] + for suit in self.lawyers: + lawyerIds.append(suit.doId) + + self.sendUpdate('setLawyerIds', [lawyerIds]) + + def d_cagedToonBattleThree(self, index, avId): + self.sendUpdate('cagedToonBattleThree', [index, avId]) + + def formatReward(self): + return str(self.cagedToonNpcId) + + def makeBattleOneBattles(self): + self.postBattleState = 'RollToBattleTwo' + self.initializeBattles(1, ToontownGlobals.LawbotBossBattleOnePosHpr) + + def generateSuits(self, battleNumber): + if battleNumber == 1: + weakenedValue = ( + (1, 1), (2, 2), (2, 2), (1, 1), (1, 1, 1, 1, 1)) + listVersion = list(SuitBuildingGlobals.SuitBuildingInfo) + if simbase.config.GetBool('lawbot-boss-cheat', 0): + listVersion[13] = weakenedValue + SuitBuildingGlobals.SuitBuildingInfo = tuple(listVersion) + return self.invokeSuitPlanner(13, 0) + else: + return self.invokeSuitPlanner(13, 1) + + def removeToon(self, avId): + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(0) + DistributedBossCogAI.DistributedBossCogAI.removeToon(self, avId) + + def enterOff(self): + self.notify.debug('enterOff') + DistributedBossCogAI.DistributedBossCogAI.enterOff(self) + self.__deleteBattleThreeObjects() + self.__resetLawyers() + + def enterElevator(self): + self.notify.debug('enterElevatro') + DistributedBossCogAI.DistributedBossCogAI.enterElevator(self) + self.b_setBossDamage(ToontownGlobals.LawbotBossInitialDamage, 0, 0) + + def enterIntroduction(self): + self.notify.debug('enterIntroduction') + DistributedBossCogAI.DistributedBossCogAI.enterIntroduction(self) + self.b_setBossDamage(ToontownGlobals.LawbotBossInitialDamage, 0, 0) + self.__makeChairs() + + def exitIntroduction(self): + self.notify.debug('exitIntroduction') + DistributedBossCogAI.DistributedBossCogAI.exitIntroduction(self) + + def enterRollToBattleTwo(self): + self.divideToons() + self.__makeCannons() + self.barrier = self.beginBarrier('RollToBattleTwo', self.involvedToons, 50, self.__doneRollToBattleTwo) + + def __doneRollToBattleTwo(self, avIds): + self.b_setState('PrepareBattleTwo') + + def exitRollToBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def enterPrepareBattleTwo(self): + self.__makeCannons() + self.barrier = self.beginBarrier('PrepareBattleTwo', self.involvedToons, 45, self.__donePrepareBattleTwo) + self.makeBattleTwoBattles() + + def __donePrepareBattleTwo(self, avIds): + self.b_setState('BattleTwo') + + def exitPrepareBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def __makeCannons(self): + if self.cannons == None: + self.cannons = [] + startPt = Point3(*ToontownGlobals.LawbotBossCannonPosA) + endPt = Point3(*ToontownGlobals.LawbotBossCannonPosB) + totalDisplacement = endPt - startPt + self.notify.debug('totalDisplacement=%s' % totalDisplacement) + numToons = len(self.involvedToons) + stepDisplacement = totalDisplacement / (numToons + 1) + for index in range(numToons): + newPos = stepDisplacement * (index + 1) + self.notify.debug('curDisplacement = %s' % newPos) + newPos += startPt + self.notify.debug('newPos = %s' % newPos) + cannon = DistributedLawbotCannonAI.DistributedLawbotCannonAI(self.air, self, index, newPos[0], newPos[1], newPos[2], -90, 0, 0) + cannon.generateWithRequired(self.zoneId) + self.cannons.append(cannon) + + return + + def __makeChairs(self): + if self.chairs == None: + self.chairs = [] + for index in range(12): + chair = DistributedLawbotChairAI.DistributedLawbotChairAI(self.air, self, index) + chair.generateWithRequired(self.zoneId) + self.chairs.append(chair) + + return + + def __makeBattleTwoObjects(self): + self.__makeCannons() + self.__makeChairs() + + def __deleteCannons(self): + if self.cannons != None: + for cannon in self.cannons: + cannon.requestDelete() + + self.cannons = None + return + + def __deleteChairs(self): + if self.chairs != None: + for chair in self.chairs: + chair.requestDelete() + + self.chairs = None + return + + def __stopChairs(self): + if self.chairs != None: + for chair in self.chairs: + chair.stopCogs() + + return + + def __deleteBattleTwoObjects(self): + self.__deleteCannons() + self.__deleteChairs() + + def getCannonBallsLeft(self, avId): + if self.cannonBallsLeft.has_key(avId): + return self.cannonBallsLeft[avId] + else: + self.notify.warning('getCannonBalsLeft invalid avId: %d' % avId) + return 0 + + def decrementCannonBallsLeft(self, avId): + if self.cannonBallsLeft.has_key(avId): + self.cannonBallsLeft[avId] -= 1 + if self.cannonBallsLeft[avId] < 0: + self.notify.warning('decrementCannonBallsLeft <0 cannonballs for %d' % avId) + self.cannonBallsLeft[avId] = 0 + else: + self.notify.warning('decrementCannonBallsLeft invalid avId: %d' % avId) + + def makeBattleTwoBattles(self): + self.postBattleState = 'RollToBattleThree' + if self.useCannons: + self.__makeBattleTwoObjects() + else: + self.initializeBattles(2, ToontownGlobals.LawbotBossBattleTwoPosHpr) + + def enterBattleTwo(self): + if self.useCannons: + self.cannonBallsLeft = {} + for toonId in self.involvedToons: + self.cannonBallsLeft[toonId] = ToontownGlobals.LawbotBossCannonBallMax + + for chair in self.chairs: + chair.requestEmptyJuror() + + self.barrier = self.beginBarrier('BattleTwo', self.involvedToons, ToontownGlobals.LawbotBossJuryBoxMoveTime + 1, self.__doneBattleTwo) + if not self.useCannons: + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def __doneBattleTwo(self, avIds): + if self.useCannons: + self.b_setState('PrepareBattleThree') + else: + self.b_setState('RollToBattleThree') + + def exitBattleTwo(self): + self.resetBattles() + self.numToonJurorsSeated = 0 + for chair in self.chairs: + self.notify.debug('chair.state==%s' % chair.state) + if chair.state == 'ToonJuror': + self.numToonJurorsSeated += 1 + + self.notify.debug('numToonJurorsSeated=%d' % self.numToonJurorsSeated) + self.air.writeServerEvent('jurorsSeated', self.doId, '%s|%s|%s' % (self.dept, self.involvedToons, self.numToonJurorsSeated)) + self.__deleteCannons() + self.__stopChairs() + + def enterRollToBattleThree(self): + self.divideToons() + self.barrier = self.beginBarrier('RollToBattleThree', self.involvedToons, 20, self.__doneRollToBattleThree) + + def __doneRollToBattleThree(self, avIds): + self.b_setState('PrepareBattleThree') + + def exitRollToBattleThree(self): + self.ignoreBarrier(self.barrier) + + def enterPrepareBattleThree(self): + self.calcAndSetBattleDifficulty() + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, 45, self.__donePrepareBattleThree) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def exitPrepareBattleThree(self): + self.ignoreBarrier(self.barrier) + + def enterBattleThree(self): + self.battleThreeTimeStarted = globalClock.getFrameTime() + self.calcAndSetBattleDifficulty() + self.calculateWeightPerToon() + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + self.ammoCount = diffSettings[0] + self.numGavels = diffSettings[1] + if self.numGavels >= len(ToontownGlobals.LawbotBossGavelPosHprs): + self.numGavels = len(ToontownGlobals.LawbotBossGavelPosHprs) + self.numLawyers = diffSettings[2] + if self.numLawyers >= len(ToontownGlobals.LawbotBossLawyerPosHprs): + self.numLawyers = len(ToontownGlobals.LawbotBossLawyerPosHprs) + self.toonupValue = diffSettings[3] + self.notify.debug('diffLevel=%d ammoCount=%d gavels=%d lawyers = %d, toonup=%d' % (self.battleDifficulty, self.ammoCount, self.numGavels, self.numLawyers, self.toonupValue)) + self.air.writeServerEvent('lawbotBossSettings', self.doId, '%s|%s|%s|%s|%s|%s' % (self.dept, self.battleDifficulty, self.ammoCount, self.numGavels, self.numLawyers, self.toonupValue)) + self.__makeBattleThreeObjects() + self.__makeLawyers() + self.numPies = self.ammoCount + self.resetBattles() + self.setPieType() + jurorsOver = self.numToonJurorsSeated - ToontownGlobals.LawbotBossJurorsForBalancedScale + dmgAdjust = jurorsOver * ToontownGlobals.LawbotBossDamagePerJuror + self.b_setBossDamage(ToontownGlobals.LawbotBossInitialDamage + dmgAdjust, 0, 0) + if simbase.config.GetBool('lawbot-boss-cheat', 0): + self.b_setBossDamage(ToontownGlobals.LawbotBossMaxDamage - 1, 0, 0) + self.battleThreeStart = globalClock.getFrameTime() + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.__touchedCage = 0 + + for aGavel in self.gavels: + aGavel.turnOn() + + self.waitForNextAttack(5) + self.notify.debug('battleDifficulty = %d' % self.battleDifficulty) + self.numToonsAtStart = len(self.involvedToons) + + def getToonDifficulty(self): + highestCogSuitLevel = 0 + totalCogSuitLevels = 0.0 + totalNumToons = 0.0 + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toonLevel = toon.getNumPromotions(self.dept) + totalCogSuitLevels += toonLevel + totalNumToons += 1 + if toon.cogLevels > highestCogSuitLevel: + highestCogSuitLevel = toonLevel + + if not totalNumToons: + totalNumToons = 1.0 + averageLevel = totalCogSuitLevels / totalNumToons + self.notify.debug('toons average level = %f, highest level = %d' % (averageLevel, highestCogSuitLevel)) + retval = min(averageLevel, self.maxToonLevels) + return retval + + def __saySomething(self, task=None): + index = None + avId = 0 + if len(self.involvedToons) == 0: + return + avId = random.choice(self.involvedToons) + toon = simbase.air.doId2do.get(avId) + if toon.__touchedCage: + if self.cagedToonDialogIndex <= TTLocalizer.CagedToonBattleThreeMaxAdvice: + index = self.cagedToonDialogIndex + self.cagedToonDialogIndex += 1 + elif random.random() < 0.2: + index = random.randrange(100, TTLocalizer.CagedToonBattleThreeMaxAdvice + 1) + else: + index = random.randrange(20, TTLocalizer.CagedToonBattleThreeMaxTouchCage + 1) + if index: + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + return + + def __saySomethingLater(self, delayTime=15): + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.__saySomething, taskName) + + def __goodJump(self, avId): + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + index = random.randrange(10, TTLocalizer.CagedToonBattleThreeMaxGivePies + 1) + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + + def __makeBattleThreeObjects(self): + if self.gavels == None: + self.gavels = [] + for index in range(self.numGavels): + gavel = DistributedLawbotBossGavelAI.DistributedLawbotBossGavelAI(self.air, self, index) + gavel.generateWithRequired(self.zoneId) + self.gavels.append(gavel) + + return + + def __deleteBattleThreeObjects(self): + if self.gavels != None: + for gavel in self.gavels: + gavel.request('Off') + gavel.requestDelete() + + self.gavels = None + return + + def doBattleThreeInfo(self): + didTheyWin = 0 + if self.bossDamage == ToontownGlobals.LawbotBossMaxDamage: + didTheyWin = 1 + self.battleThreeTimeInMin = globalClock.getFrameTime() - self.battleThreeTimeStarted + self.battleThreeTimeInMin /= 60.0 + self.numToonsAtEnd = 0 + toonHps = [] + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + self.numToonsAtEnd += 1 + toonHps.append(toon.hp) + + self.air.writeServerEvent('b3Info', self.doId, '%d|%.2f|%d|%d|%d|%d|%d|%d|%d|%d|%d|%d|%s|%s' % (didTheyWin, self.battleThreeTimeInMin, self.numToonsAtStart, self.numToonsAtEnd, self.numToonJurorsSeated, self.battleDifficulty, self.ammoCount, self.numGavels, self.numLawyers, self.toonupValue, self.numBonusStates, self.numAreaAttacks, toonHps, self.weightPerToon)) + + def exitBattleThree(self): + self.doBattleThreeInfo() + self.stopAttacks() + self.stopStrafes() + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + self.__resetLawyers() + self.__deleteBattleThreeObjects() + + def enterNearVictory(self): + self.resetBattles() + + def exitNearVictory(self): + pass + + def enterVictory(self): + self.resetBattles() + self.suitsKilled.append({'type': None, 'level': None, 'track': self.dna.dept, 'isSkelecog': 0, 'isForeman': 0, 'isVP': 1, 'isCFO': 0, 'isSupervisor': 0, 'isVirtual': 0, 'activeToons': self.involvedToons[:]}) + self.barrier = self.beginBarrier('Victory', self.involvedToons, 30, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + preferredDept = random.randrange(len(SuitDNA.suitDepts)) + typeWeights = ['single'] * 70 + ['building'] * 27 + ['invasion'] * 3 + preferredSummonType = random.choice(typeWeights) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + self.giveCogSummonReward(toon, preferredDept, preferredSummonType) + toon.b_promote(self.deptIndex) + + def giveCogSummonReward(self, toon, prefDeptIndex, prefSummonType): + cogLevel = int(self.toonLevels / self.maxToonLevels * SuitDNA.suitsPerDept) + cogLevel = min(cogLevel, SuitDNA.suitsPerDept - 1) + deptIndex = prefDeptIndex + summonType = prefSummonType + hasSummon = toon.hasParticularCogSummons(prefDeptIndex, cogLevel, prefSummonType) + if hasSummon: + self.notify.debug('trying to find another reward') + if not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'single'): + summonType = 'single' + elif not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'building'): + summonType = 'building' + elif not toon.hasParticularCogSummons(prefDeptIndex, cogLevel, 'invasion'): + summonType = 'invasion' + else: + foundOne = False + for curDeptIndex in range(len(SuitDNA.suitDepts)): + if not toon.hasParticularCogSummons(curDeptIndex, cogLevel, prefSummonType): + deptIndex = curDeptIndex + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'single'): + deptIndex = curDeptIndex + summonType = 'single' + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'building'): + deptIndex = curDeptIndex + summonType = 'building' + foundOne = True + break + elif not toon.hasParticularCogSummons(curDeptIndex, cogLevel, 'invasion'): + summonType = 'invasion' + deptIndex = curDeptIndex + foundOne = True + break + + possibleCogLevel = range(SuitDNA.suitsPerDept) + possibleDeptIndex = range(len(SuitDNA.suitDepts)) + possibleSummonType = ['single', 'building', 'invasion'] + typeWeights = [ + 'single'] * 70 + ['building'] * 27 + ['invasion'] * 3 + if not foundOne: + for i in range(5): + randomCogLevel = random.choice(possibleCogLevel) + randomSummonType = random.choice(typeWeights) + randomDeptIndex = random.choice(possibleDeptIndex) + if not toon.hasParticularCogSummons(randomDeptIndex, randomCogLevel, randomSummonType): + foundOne = True + cogLevel = randomCogLevel + summonType = randomSummonType + deptIndex = randomDeptIndex + break + + for curType in possibleSummonType: + if foundOne: + break + for curCogLevel in possibleCogLevel: + if foundOne: + break + for curDeptIndex in possibleDeptIndex: + if foundOne: + break + if not toon.hasParticularCogSummons(curDeptIndex, curCogLevel, curType): + foundOne = True + cogLevel = curCogLevel + summonType = curType + deptIndex = curDeptIndex + + if not foundOne: + cogLevel = None + summonType = None + deptIndex = None + toon.assignNewCogSummons(cogLevel, summonType, deptIndex) + return + + def exitVictory(self): + self.takeAwayPies() + + def enterDefeat(self): + self.resetBattles() + self.barrier = self.beginBarrier('Defeat', self.involvedToons, 10, self.__doneDefeat) + + def __doneDefeat(self, avIds): + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + toon.b_setHp(0) + + def exitDefeat(self): + self.takeAwayPies() + + def enterFrolic(self): + DistributedBossCogAI.DistributedBossCogAI.enterFrolic(self) + self.b_setBossDamage(0, 0, 0) + + def setPieType(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.d_setPieType(ToontownBattleGlobals.MAX_TRACK_INDEX + 1) + + def takeAwayPies(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setNumPies(0) + + def __recordHit(self): + now = globalClock.getFrameTime() + self.hitCount += 1 + if self.hitCount < self.limitHitCount or self.bossDamage < self.hitCountDamage: + return + + def __resetLawyers(self): + for suit in self.lawyers: + suit.requestDelete() + + self.lawyers = [] + + def __makeLawyers(self): + self.__resetLawyers() + lawCogChoices = [ + 'b', 'dt', 'ac', 'bs', 'sd', 'le', 'bw'] + for i in range(self.numLawyers): + suit = DistributedLawbotBossSuitAI.DistributedLawbotBossSuitAI(self.air, None) + suit.dna = SuitDNA.SuitDNA() + lawCog = random.choice(lawCogChoices) + suit.dna.newSuit(lawCog) + suit.setPosHpr(*ToontownGlobals.LawbotBossLawyerPosHprs[i]) + suit.setBoss(self) + suit.generateWithRequired(self.zoneId) + self.lawyers.append(suit) + + self.__sendLawyerIds() + return + + def hitChair(self, chairIndex, npcToonIndex): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitChair from unknown avatar'): + return + if not self.chairs: + return + if chairIndex < 0 or chairIndex >= len(self.chairs): + self.notify.warning('invalid chairIndex = %d' % chairIndex) + return + if not self.state == 'BattleTwo': + return + self.chairs[chairIndex].b_setToonJurorIndex(npcToonIndex) + self.chairs[chairIndex].requestToonJuror() + + def clearBonus(self, taskName): + if self and hasattr(self, 'bonusState'): + self.bonusState = False + + def startBonusState(self): + self.notify.debug('startBonusState') + self.bonusTimeStarted = globalClock.getFrameTime() + self.bonusState = True + self.numBonusStates += 1 + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, ToontownGlobals.LawbotBossBonusToonup) + + taskMgr.doMethodLater(ToontownGlobals.LawbotBossBonusDuration, self.clearBonus, self.uniqueName('clearBonus')) + self.sendUpdate('enteredBonusState', []) + + def areAllLawyersStunned(self): + for lawyer in self.lawyers: + if not lawyer.stunned: + return False + + return True + + def checkForBonusState(self): + if self.bonusState: + return + if not self.areAllLawyersStunned(): + return + curTime = globalClock.getFrameTime() + delta = curTime - self.bonusTimeStarted + if ToontownGlobals.LawbotBossBonusWaitTime < delta: + self.startBonusState() + + def toonEnteredCannon(self, toonId, cannonIndex): + self.cannonIndexPerToon[toonId] = cannonIndex + + def numJurorsSeatedByCannon(self, cannonIndex): + retVal = 0 + for chair in self.chairs: + if chair.state == 'ToonJuror': + if chair.toonJurorIndex == cannonIndex: + retVal += 1 + + return retVal + + def calculateWeightPerToon(self): + for toonId in self.involvedToons: + defaultWeight = 1 + bonusWeight = 0 + cannonIndex = self.cannonIndexPerToon.get(toonId) + if not cannonIndex == None: + diffSettings = ToontownGlobals.LawbotBossDifficultySettings[self.battleDifficulty] + if diffSettings[4]: + bonusWeight = self.numJurorsSeatedByCannon(cannonIndex) - diffSettings[5] + if bonusWeight < 0: + bonusWeight = 0 + newWeight = defaultWeight + bonusWeight + self.weightPerToon[toonId] = newWeight + self.notify.debug('toon %d has weight of %d' % (toonId, newWeight)) + + return + + def b_setBattleDifficulty(self, batDiff): + self.setBattleDifficulty(batDiff) + self.d_setBattleDifficulty(batDiff) + + def setBattleDifficulty(self, batDiff): + self.battleDifficulty = batDiff + + def d_setBattleDifficulty(self, batDiff): + self.sendUpdate('setBattleDifficulty', [batDiff]) + + def calcAndSetBattleDifficulty(self): + self.toonLevels = self.getToonDifficulty() + numDifficultyLevels = len(ToontownGlobals.LawbotBossDifficultySettings) + battleDifficulty = int(self.toonLevels / self.maxToonLevels * numDifficultyLevels) + if battleDifficulty >= numDifficultyLevels: + battleDifficulty = numDifficultyLevels - 1 + self.b_setBattleDifficulty(battleDifficulty) diff --git a/toontown/suit/DistributedLawbotBossSuit.py b/toontown/suit/DistributedLawbotBossSuit.py new file mode 100644 index 0000000..b4aaf8e --- /dev/null +++ b/toontown/suit/DistributedLawbotBossSuit.py @@ -0,0 +1,384 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +import DistributedSuitBase +from toontown.toonbase import ToontownGlobals +from toontown.battle import MovieUtil + +class DistributedLawbotBossSuit(DistributedSuitBase.DistributedSuitBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossSuit') + timeToShow = 1.0 + timeToRelease = 3.15 + throwPaperEndTime = 4.33 + + def __init__(self, cr): + self.flyingEvidenceTrack = None + try: + self.DistributedSuit_initialized + except: + self.DistributedSuit_initialized = 1 + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.activeIntervals = {} + self.boss = None + self.fsm = ClassicFSM.ClassicFSM('DistributedLawbotBossSuit', [ + State.State('Off', + self.enterOff, + self.exitOff, [ + 'Walk', + 'Battle', + 'neutral']), + State.State('Walk', + self.enterWalk, + self.exitWalk, [ + 'WaitForBattle', + 'Battle']), + State.State('Battle', + self.enterBattle, + self.exitBattle, []), + State.State('neutral', + self.enterNeutral, + self.exitNeutral, [ + 'PreThrowProsecute', + 'PreThrowAttack', + 'Stunned']), + State.State('PreThrowProsecute', + self.enterPreThrowProsecute, + self.exitPreThrowProsecute, + ['PostThrowProsecute', + 'neutral', + 'Stunned']), + State.State('PostThrowProsecute', + self.enterPostThrowProsecute, + self.exitPostThrowProsecute, [ + 'neutral', + 'Stunned']), + State.State('PreThrowAttack', + self.enterPreThrowAttack, + self.exitPreThrowAttack, [ + 'PostThrowAttack', + 'neutral', + 'Stunned']), + State.State('PostThrowAttack', + self.enterPostThrowAttack, + self.exitPostThrowAttack, [ + 'neutral', + 'Stunned']), + State.State('Stunned', + self.enterStunned, + self.exitStunned, [ + 'neutral']), + State.State('WaitForBattle', + self.enterWaitForBattle, + self.exitWaitForBattle, [ + 'Battle'])], + 'Off', 'Off') + self.fsm.enterInitialState() + + return + + def generate(self): + self.notify.debug('DLBS.generate:') + DistributedSuitBase.DistributedSuitBase.generate(self) + + def announceGenerate(self): + DistributedSuitBase.DistributedSuitBase.announceGenerate(self) + self.notify.debug('DLBS.announceGenerate') + colNode = self.find('**/distAvatarCollNode*') + colNode.setTag('pieCode', str(ToontownGlobals.PieCodeLawyer)) + self.attackEvidenceA = self.getEvidence(True) + self.attackEvidenceB = self.getEvidence(True) + self.attackEvidence = self.attackEvidenceA + self.prosecuteEvidence = self.getEvidence(False) + self.hideName() + self.setPickable(False) + + def disable(self): + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.setState('Off') + DistributedSuitBase.DistributedSuitBase.disable(self) + self.cleanupIntervals() + self.boss = None + return + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.sendUpdate('requestBattle', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]]) + return None + + def __handleToonCollision(self, collEntry): + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit: requesting a Battle with ' + 'toon: %d' % toonId) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + return None + + def enterWalk(self): + self.notify.debug('enterWalk') + self.enableBattleDetect('walk', self.__handleToonCollision) + self.loop('walk', 0) + pathPoints = [Vec3(50, 15, 0), + Vec3(50, 25, 0), + Vec3(20, 25, 0), + Vec3(20, 15, 0), + Vec3(50, 15, 0)] + self.tutWalkTrack = self.makePathTrack(self, pathPoints, 4.5, 'tutFlunkyWalk') + self.tutWalkTrack.loop() + + def exitWalk(self): + self.notify.debug('exitWalk') + self.disableBattleDetect() + self.tutWalkTrack.pause() + self.tutWalkTrack = None + return + + def enterNeutral(self): + self.notify.debug('enterNeutral') + self.notify.debug('DistributedLawbotBossSuit: Neutral') + self.loop('neutral', 0) + + def exitNeutral(self): + self.notify.debug('exitNeutral') + + def doAttack(self, x1, y1, z1, x2, y2, z2): + self.notify.debug('x1=%.2f y1=%.2f z2=%.2f x2=%.2f y2=%.2f z2=%.2f' % (x1, + y1, + z1, + x2, + y2, + z2)) + self.curTargetPt = Point3(x2, y2, z2) + self.fsm.request('PreThrowAttack') + return + attackEvidence = self.getEvidence(True) + nodePath = render + node = nodePath.attachNewNode('attackEvidence-%s' % self.doId) + node.setPos(x1, y1, z1) + duration = 3.0 + throwName = self.uniqueName('lawyerAttack') + throwingSeq = self.makeAttackThrowingTrack(attackEvidence, duration, Point3(x2, y2, z2)) + fullSequence = Sequence(throwingSeq, name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def doProsecute(self): + self.notify.debug('doProsecute') + bounds = self.boss.prosecutionColNodePath.getBounds() + panCenter = bounds.getCenter() + localPos = panCenter + prosecutionPanPos = render.getRelativePoint(self.boss.prosecutionColNodePath, localPos) + self.curTargetPt = prosecutionPanPos + self.fsm.request('PreThrowProsecute') + return + attackEvidence = self.getEvidence(False) + nodePath = render + node = nodePath.attachNewNode('prosecuteEvidence-%s' % self.doId) + node.setPos(self.getPos()) + duration = ToontownGlobals.LawbotBossLawyerToPanTime + throwName = self.uniqueName('lawyerProsecute') + throwingSeq = self.makeProsecuteThrowingTrack(attackEvidence, duration, prosecutionPanPos) + fullSequence = Sequence(throwingSeq, Func(self.boss.flashGreen), Func(self.clearInterval, throwName), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def makeDummySequence(self): + retval = Sequence(Wait(10)) + return retval + + def makeProsecuteThrowingTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper')) + throwPaperDuration = suitTrack.getDuration() + inFlight = Parallel(evidence.posInterval(inFlightDuration, hitPos, fluid=1)) + origHpr = self.getHpr() + self.headsUp(hitPos) + newHpr = self.getHpr() + self.setHpr(origHpr) + rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1)) + propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow), Func(evidence.wrtReparentTo, render), Func(self.makeDummySequence), inFlight, Func(evidence.detachNode)) + throwingTrack = Parallel(suitTrack, propTrack, rotateTrack) + return throwingTrack + + def makeAttackThrowingTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper')) + throwPaperDuration = suitTrack.getDuration() + origHpr = self.getHpr() + self.headsUp(hitPos) + newHpr = self.getHpr() + self.setHpr(origHpr) + rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1)) + propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow), Func(evidence.wrtReparentTo, render), Func(evidence.setZ, 1.3), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.detachNode)) + throwingTrack = Parallel(suitTrack, propTrack, rotateTrack) + return throwingTrack + + def makePreThrowAttackTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper', endTime=self.timeToRelease)) + throwPaperDuration = suitTrack.getDuration() + origHpr = self.getHpr() + self.headsUp(hitPos) + newHpr = self.getHpr() + self.setHpr(origHpr) + rotateTrack = Sequence(self.hprInterval(self.timeToShow, newHpr, fluid=1)) + propTrack = Sequence(Func(evidence.hide), Func(evidence.setPos, 0, 0.5, -0.3), Func(evidence.setScale, 1), Func(evidence.setHpr, 0, 0, 0), Func(evidence.reparentTo, self.getRightHand()), Wait(self.timeToShow), Func(evidence.show), Wait(self.timeToRelease - self.timeToShow)) + throwingTrack = Parallel(suitTrack, propTrack, rotateTrack) + return throwingTrack + + def makePostThrowAttackTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper', startTime=self.timeToRelease)) + propTrack = Sequence(Func(evidence.wrtReparentTo, render), Func(evidence.setScale, 1), Func(evidence.show), Func(evidence.setZ, 1.3), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.hide)) + return (suitTrack, propTrack) + + def makePreThrowProsecuteTrack(self, evidence, inFlightDuration, hitPos): + return self.makePreThrowAttackTrack(evidence, inFlightDuration, hitPos) + + def makePostThrowProsecuteTrack(self, evidence, inFlightDuration, hitPos): + suitTrack = Sequence() + suitTrack.append(ActorInterval(self, 'throw-paper', startTime=self.timeToRelease)) + propTrack = Sequence(Func(evidence.wrtReparentTo, render), Func(evidence.setScale, 1), Func(evidence.show), evidence.posInterval(inFlightDuration, hitPos, fluid=1), Func(evidence.hide)) + return (suitTrack, propTrack) + + def getEvidence(self, usedForAttack = False): + model = loader.loadModel('phase_5/models/props/lawbook') + if usedForAttack: + bounds = model.getBounds() + center = bounds.getCenter() + radius = bounds.getRadius() + sphere = CollisionSphere(center.getX(), center.getY(), center.getZ(), radius) + colNode = CollisionNode('BossZap') + colNode.setTag('attackCode', str(ToontownGlobals.BossCogLawyerAttack)) + colNode.addSolid(sphere) + model.attachNewNode(colNode) + model.setTransparency(1) + model.setAlphaScale(0.5) + return model + + def cleanupIntervals(self): + for interval in self.activeIntervals.values(): + interval.finish() + + self.activeIntervals = {} + + def clearInterval(self, name, finish = 1): + if self.activeIntervals.has_key(name): + ival = self.activeIntervals[name] + if finish: + ival.finish() + else: + ival.pause() + if self.activeIntervals.has_key(name): + del self.activeIntervals[name] + else: + self.notify.debug('interval: %s already cleared' % name) + + def setBossCogId(self, bossCogId): + self.bossCogId = bossCogId + self.boss = base.cr.doId2do[bossCogId] + + def doStun(self): + self.notify.debug('doStun') + self.fsm.request('Stunned') + + def enterPreThrowProsecute(self): + duration = ToontownGlobals.LawbotBossLawyerToPanTime + throwName = self.uniqueName('preThrowProsecute') + preThrowTrack = self.makePreThrowProsecuteTrack(self.prosecuteEvidence, duration, self.curTargetPt) + fullSequence = Sequence(preThrowTrack, Func(self.requestStateIfNotInFlux, 'PostThrowProsecute'), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def exitPreThrowProsecute(self): + throwName = self.uniqueName('preThrowProsecute') + if self.activeIntervals.has_key(throwName): + self.activeIntervals[throwName].pause() + del self.activeIntervals[throwName] + + def enterPostThrowProsecute(self): + duration = ToontownGlobals.LawbotBossLawyerToPanTime + throwName = self.uniqueName('postThrowProsecute') + postThrowTrack, self.flyingEvidenceTrack = self.makePostThrowProsecuteTrack(self.prosecuteEvidence, duration, self.curTargetPt) + fullSequence = Sequence(postThrowTrack, Func(self.requestStateIfNotInFlux, 'neutral'), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + flyName = self.uniqueName('flyingEvidence') + self.activeIntervals[flyName] = self.flyingEvidenceTrack + self.flyingEvidenceTrack.append(Func(self.finishedWithFlying, 'prosecute')) + self.flyingEvidenceTrack.start() + + def exitPostThrowProsecute(self): + throwName = self.uniqueName('postThrowProsecute') + if self.activeIntervals.has_key(throwName): + self.activeIntervals[throwName].finish() + del self.activeIntervals[throwName] + + def requestStateIfNotInFlux(self, state): + if not self.fsm._ClassicFSM__internalStateInFlux: + self.fsm.request(state) + + def enterPreThrowAttack(self): + if self.attackEvidence == self.attackEvidenceA: + self.attackEvidence = self.attackEvidenceB + else: + self.attackEvidence = self.attackEvidenceA + duration = 3.0 + throwName = self.uniqueName('preThrowAttack') + preThrowTrack = self.makePreThrowAttackTrack(self.attackEvidence, duration, self.curTargetPt) + fullSequence = Sequence(preThrowTrack, Func(self.requestStateIfNotInFlux, 'PostThrowAttack'), name=throwName) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + + def exitPreThrowAttack(self): + throwName = self.uniqueName('preThrowAttack') + if self.activeIntervals.has_key(throwName): + self.activeIntervals[throwName].pause() + del self.activeIntervals[throwName] + + def enterPostThrowAttack(self): + duration = 3.0 + throwName = self.uniqueName('postThrowAttack') + postThrowTrack, self.flyingEvidenceTrack = self.makePostThrowAttackTrack(self.attackEvidence, duration, self.curTargetPt) + fullSequence = Sequence(postThrowTrack, Func(self.requestStateIfNotInFlux, 'neutral'), name=throwName) + self.notify.debug('duration of postThrowAttack = %f' % fullSequence.getDuration()) + self.activeIntervals[throwName] = fullSequence + fullSequence.start() + flyName = self.uniqueName('flyingEvidence') + self.activeIntervals[flyName] = self.flyingEvidenceTrack + self.flyingEvidenceTrack.append(Func(self.finishedWithFlying, 'attack')) + self.flyingEvidenceTrack.start() + + def finishedWithFlying(self, str): + self.notify.debug('finished flyingEvidenceTrack %s' % str) + + def exitPostThrowAttack(self): + throwName = self.uniqueName('postThrowAttack') + if self.activeIntervals.has_key(throwName): + self.activeIntervals[throwName].finish() + del self.activeIntervals[throwName] + + def enterStunned(self): + stunSequence = MovieUtil.createSuitStunInterval(self, 0, ToontownGlobals.LawbotBossLawyerStunTime) + seqName = stunSequence.getName() + stunSequence.append(Func(self.fsm.request, 'neutral')) + self.activeIntervals[seqName] = stunSequence + stunSequence.start() + + def exitStunned(self): + self.prosecuteEvidence.hide() + self.attackEvidence.hide() diff --git a/toontown/suit/DistributedLawbotBossSuitAI.py b/toontown/suit/DistributedLawbotBossSuitAI.py new file mode 100644 index 0000000..3a0b1cb --- /dev/null +++ b/toontown/suit/DistributedLawbotBossSuitAI.py @@ -0,0 +1,192 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +from toontown.toonbase import ToontownGlobals +import DistributedSuitBaseAI, random +from direct.fsm import ClassicFSM, State +from direct.fsm import State + +class DistributedLawbotBossSuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLawbotBossSuitAI') + + def __init__(self, air, suitPlanner): + DistributedSuitBaseAI.DistributedSuitBaseAI.__init__(self, air, suitPlanner) + self.stunned = False + self.timeToRelease = 3.15 + self.timeProsecuteStarted = 0 + self.fsm = ClassicFSM.ClassicFSM('DistributedLawbotBossSuitAI', [ + State.State('Off', self.enterOff, self.exitOff, [ + 'neutral']), + State.State('neutral', self.enterNeutral, self.exitNeutral, [ + 'PreThrowProsecute', 'PreThrowAttack', 'Stunned']), + State.State('PreThrowProsecute', self.enterPreThrowProsecute, self.exitPreThrowProsecute, [ + 'PostThrowProsecute', 'neutral', 'Stunned']), + State.State('PostThrowProsecute', self.enterPostThrowProsecute, self.exitPostThrowProsecute, [ + 'neutral', 'Stunned']), + State.State('PreThrowAttack', self.enterPreThrowAttack, self.exitPreThrowAttack, [ + 'PostThrowAttack', 'neutral', 'Stunned']), + State.State('PostThrowAttack', self.enterPostThrowAttack, self.exitPostThrowAttack, [ + 'neutral', 'Stunned']), + State.State('Stunned', self.enterStunned, self.exitStunned, [ + 'neutral'])], 'Off', 'Off') + self.fsm.enterInitialState() + + def delete(self): + self.notify.debug('delete %s' % self.doId) + self.ignoreAll() + DistributedSuitBaseAI.DistributedSuitBaseAI.delete(self) + self.notify.debug('setting self.boss to None') + self.boss = None + taskName = self.uniqueName('ProsecutionHealsBoss') + if taskMgr.hasTaskNamed(taskName): + self.notify.debug('still has task %s' % taskName) + taskMgr.remove(taskName) + taskName = self.uniqueName('unstun') + if taskMgr.hasTaskNamed(taskName): + self.notify.debug('still has task %s' % taskName) + taskMgr.remove(taskName) + self.fsm = None + return + + def requestBattle(self, x, y, z, h, p, r): + toonId = self.air.getAvatarIdFromSender() + if self.notify.getDebug(): + self.notify.debug(str(self.getDoId()) + str(self.zoneId) + ': request battle with toon: %d' % toonId) + self.confrontPos = Point3(x, y, z) + self.confrontHpr = Vec3(h, p, r) + if self.sp.requestBattle(self.zoneId, self, toonId): + self.acceptOnce(self.getDeathEvent(), self._logDeath, [toonId]) + if self.notify.getDebug(): + self.notify.debug('Suit %d requesting battle in zone %d' % (self.getDoId(), self.zoneId)) + else: + if self.notify.getDebug(): + self.notify.debug('requestBattle from suit %d - denied by battle manager' % self.getDoId()) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + + def getPosHpr(self): + return ( + self.getX(), self.getY(), self.getZ(), self.getH(), self.getP(), self.getR()) + + def getConfrontPosHpr(self): + return ( + self.confrontPos, self.confrontHpr) + + def _logDeath(self, toonId): + pass + + def doNextAttack(self, lawbotBoss): + if self.stunned: + return + chanceToDoAttack = ToontownGlobals.LawbotBossLawyerChanceToAttack + action = random.randrange(1, 101) + if action > chanceToDoAttack: + self.doProsecute() + else: + if not lawbotBoss.involvedToons: + return + toonToAttackId = random.choice(lawbotBoss.involvedToons) + toon = self.air.doId2do.get(toonToAttackId) + if not toon: + self.doProsecute() + return + toonPos = toon.getPos() + z2 = toonPos[2] + 1.3 + toonPos = Point3(toonPos.getX(), toonPos.getY(), 0) + lawyerPos = self.getPos() + lawyerPos = Point3(self.getPos().getX(), self.getPos().getY(), 0) + dirVector = toonPos - lawyerPos + dirVector.normalize() + dirVector *= 200 + destPos = Point3(lawyerPos[0] + dirVector[0], lawyerPos[1] + dirVector[1], lawyerPos[2] + dirVector[2] + 1.3) + self.d_doAttack(lawyerPos[0], lawyerPos[1], lawyerPos[2], destPos[0], destPos[1], destPos[2]) + + def doProsecute(self): + self.notify.debug('doProsecute') + self.timeProsecuteStarted = globalClockDelta.getRealNetworkTime() + self.d_doProsecute() + taskName = self.uniqueName('ProsecutionHealsBoss') + duration = 5.65 + taskMgr.doMethodLater(duration, self.__prosecutionHeal, taskName) + + def __prosecutionHeal(self, extraArg): + self.notify.debug('__prosecutionHeal extraArg %s' % extraArg) + if self.boss: + self.boss.healBoss(ToontownGlobals.LawbotBossLawyerHeal) + + def d_doProsecute(self): + self.notify.debug('d_doProsecute') + self.sendUpdate('doProsecute', []) + + def d_doAttack(self, x1, y1, z1, x2, y2, z2): + self.notify.debug('doAttack: x1=%.2f y1=%.2f z2=%.2f x2=%.2f y2=%.2f z2=%.2f' % (x1, y1, z1, x2, y2, z2)) + self.sendUpdate('doAttack', [x1, y1, z1, x2, y2, z2]) + + def setBoss(self, lawbotBoss): + self.boss = lawbotBoss + + def hitByToon(self): + self.notify.debug('I got hit by a toon') + if not self.stunned: + curTime = globalClockDelta.getRealNetworkTime() + deltaTime = curTime - self.timeProsecuteStarted + deltaTime /= 100.0 + self.notify.debug('deltaTime = %f, curTime=%f, prosecuteStarted=%f' % (deltaTime, curTime, self.timeProsecuteStarted)) + if deltaTime < self.timeToRelease: + taskName = self.uniqueName('ProsecutionHealsBoss') + taskMgr.remove(taskName) + self.sendUpdate('doStun', []) + self.setStun(True) + taskName = self.uniqueName('unstun') + taskMgr.doMethodLater(ToontownGlobals.LawbotBossLawyerStunTime, self.unStun, taskName) + if self.boss: + self.boss.checkForBonusState() + + def setStun(self, val): + self.stunned = val + + def unStun(self, taskName): + self.setStun(False) + + def enterPreThrowProsecute(self): + pass + + def exitPreThrowProsecute(self): + pass + + def enterPostThrowProsecute(self): + pass + + def exitPostThrowProsecute(self): + pass + + def enterPreThrowAttack(self): + pass + + def exitPreThrowAttack(self): + pass + + def enterPostThrowAttack(self): + pass + + def exitPostThrowAttack(self): + pass + + def enterStunned(self): + pass + + def exitStunned(self): + pass + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterNeutral(self): + pass + + def exitNeutral(self): + pass diff --git a/toontown/suit/DistributedMintSuit.py b/toontown/suit/DistributedMintSuit.py new file mode 100644 index 0000000..6d736a0 --- /dev/null +++ b/toontown/suit/DistributedMintSuit.py @@ -0,0 +1,5 @@ +from toontown.suit import DistributedFactorySuit +from direct.directnotify import DirectNotifyGlobal + +class DistributedMintSuit(DistributedFactorySuit.DistributedFactorySuit): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintSuit') diff --git a/toontown/suit/DistributedMintSuitAI.py b/toontown/suit/DistributedMintSuitAI.py new file mode 100644 index 0000000..d5e4024 --- /dev/null +++ b/toontown/suit/DistributedMintSuitAI.py @@ -0,0 +1,14 @@ +from toontown.suit import DistributedFactorySuitAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedMintSuitAI(DistributedFactorySuitAI.DistributedFactorySuitAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedMintSuitAI') + + def isForeman(self): + return 0 + + def isSupervisor(self): + return self.boss + + def isVirtual(self): + return 0 diff --git a/toontown/suit/DistributedSellbotBoss.py b/toontown/suit/DistributedSellbotBoss.py new file mode 100644 index 0000000..09c05b1 --- /dev/null +++ b/toontown/suit/DistributedSellbotBoss.py @@ -0,0 +1,1284 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from toontown.battle.BattleProps import * +from direct.distributed.ClockDelta import * +from direct.showbase.PythonUtil import Functor +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.fsm import FSM +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +import DistributedBossCog +from toontown.toonbase import TTLocalizer +import SuitDNA +from toontown.toon import Toon +from toontown.battle import BattleBase +from direct.directutil import Mopath +from direct.showutil import Rope +from toontown.distributed import DelayDelete +from toontown.battle import MovieToonVictory +from toontown.building import ElevatorUtils +from toontown.battle import RewardPanel +from toontown.toon import NPCToons +from direct.task import Task +import random +import math +from toontown.coghq import CogDisguiseGlobals +from toontown.suit import SellbotBossGlobals +OneBossCog = None + +class DistributedSellbotBoss(DistributedBossCog.DistributedBossCog, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSellbotBoss') + cageHeights = [100, + 81, + 63, + 44, + 25, + 18] + + def __init__(self, cr): + DistributedBossCog.DistributedBossCog.__init__(self, cr) + FSM.FSM.__init__(self, 'DistributedSellbotBoss') + self.cagedToonNpcId = None + self.doobers = [] + self.dooberRequest = None + self.bossDamage = 0 + self.attackCode = None + self.attackAvId = 0 + self.recoverRate = 0 + self.recoverStartTime = 0 + self.bossDamageMovie = None + self.cagedToon = None + self.cageShadow = None + self.cageIndex = 0 + self.everThrownPie = 0 + self.battleThreeMusicTime = 0 + self.insidesANodePath = None + self.insidesBNodePath = None + self.rampA = None + self.rampB = None + self.rampC = None + self.strafeInterval = None + self.onscreenMessage = None + self.toonMopathInterval = [] + self.nerfed = ToontownGlobals.SELLBOT_NERF_HOLIDAY in base.cr.newsManager.getHolidayIdList() + self.localToonPromoted = True + self.resetMaxDamage() + return + + def announceGenerate(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.announceGenerate(self) + self.setName(TTLocalizer.SellbotBossName) + nameInfo = TTLocalizer.BossCogNameWithDept % {'name': self.name, + 'dept': SuitDNA.getDeptFullname(self.style.dept)} + self.setDisplayName(nameInfo) + self.cageDoorSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_door.mp3') + self.cageLandSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_SOS_cage_land.mp3') + self.cageLowerSfx = loader.loadSfx('phase_5/audio/sfx/CHQ_SOS_cage_lower.mp3') + self.piesRestockSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_SOS_pies_restock.mp3') + self.rampSlideSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_ramp_slide.mp3') + self.strafeSfx = [] + for i in range(10): + self.strafeSfx.append(loader.loadSfx('phase_3.5/audio/sfx/SA_shred.mp3')) + + render.setTag('pieCode', str(ToontownGlobals.PieCodeNotBossCog)) + insidesA = CollisionPolygon(Point3(4.0, -2.0, 5.0), Point3(-4.0, -2.0, 5.0), Point3(-4.0, -2.0, 0.5), Point3(4.0, -2.0, 0.5)) + insidesANode = CollisionNode('BossZap') + insidesANode.addSolid(insidesA) + insidesANode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesANodePath = self.axle.attachNewNode(insidesANode) + self.insidesANodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesANodePath.stash() + insidesB = CollisionPolygon(Point3(-4.0, 2.0, 5.0), Point3(4.0, 2.0, 5.0), Point3(4.0, 2.0, 0.5), Point3(-4.0, 2.0, 0.5)) + insidesBNode = CollisionNode('BossZap') + insidesBNode.addSolid(insidesB) + insidesBNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask) + self.insidesBNodePath = self.axle.attachNewNode(insidesBNode) + self.insidesBNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossInsides)) + self.insidesBNodePath.stash() + target = CollisionTube(0, -1, 4, 0, -1, 9, 3.5) + targetNode = CollisionNode('BossZap') + targetNode.addSolid(target) + targetNode.setCollideMask(ToontownGlobals.PieBitmask) + self.targetNodePath = self.pelvis.attachNewNode(targetNode) + self.targetNodePath.setTag('pieCode', str(ToontownGlobals.PieCodeBossCog)) + shield = CollisionTube(0, 1, 4, 0, 1, 7, 3.5) + shieldNode = CollisionNode('BossZap') + shieldNode.addSolid(shield) + shieldNode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask) + shieldNodePath = self.pelvis.attachNewNode(shieldNode) + disk = loader.loadModel('phase_9/models/char/bossCog-gearCollide') + disk.find('**/+CollisionNode').setName('BossZap') + disk.reparentTo(self.pelvis) + disk.setZ(0.8) + self.loadEnvironment() + self.__makeCagedToon() + self.__loadMopaths() + if OneBossCog != None: + self.notify.warning('Multiple BossCogs visible.') + OneBossCog = self + return + + def disable(self): + global OneBossCog + DistributedBossCog.DistributedBossCog.disable(self) + self.request('Off') + self.unloadEnvironment() + self.__unloadMopaths() + self.__cleanupCagedToon() + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + self.__cleanupStrafe() + render.clearTag('pieCode') + self.targetNodePath.detachNode() + self.cr.relatedObjectMgr.abortRequest(self.dooberRequest) + self.dooberRequest = None + self.betweenBattleMusic.stop() + self.promotionMusic.stop() + self.stingMusic.stop() + self.battleTwoMusic.stop() + self.battleThreeMusic.stop() + self.epilogueMusic.stop() + while len(self.toonMopathInterval): + toonMopath = self.toonMopathInterval[0] + toonMopath.finish() + toonMopath.destroy() + self.toonMopathInterval.remove(toonMopath) + + if OneBossCog == self: + OneBossCog = None + return + + def resetMaxDamage(self): + if self.nerfed: + self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamageNerfed + else: + self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamage + + def d_hitBoss(self, bossDamage): + self.sendUpdate('hitBoss', [bossDamage]) + + def d_hitBossInsides(self): + self.sendUpdate('hitBossInsides', []) + + def d_hitToon(self, toonId): + self.sendUpdate('hitToon', [toonId]) + + def setCagedToonNpcId(self, npcId): + self.cagedToonNpcId = npcId + + def gotToon(self, toon): + stateName = self.state + if stateName == 'Elevator': + self.placeToonInElevator(toon) + + def setDooberIds(self, dooberIds): + self.doobers = [] + self.cr.relatedObjectMgr.abortRequest(self.dooberRequest) + self.dooberRequest = self.cr.relatedObjectMgr.requestObjects(dooberIds, allCallback=self.__gotDoobers) + + def __gotDoobers(self, doobers): + self.dooberRequest = None + self.doobers = doobers + return + + def setBossDamage(self, bossDamage, recoverRate, timestamp): + recoverStartTime = globalClockDelta.networkToLocalTime(timestamp) + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + if self.bossDamageMovie: + if self.bossDamage >= self.bossMaxDamage: + self.bossDamageMovie.resumeUntil(self.bossDamageMovie.getDuration()) + else: + self.bossDamageMovie.resumeUntil(self.bossDamage * self.bossDamageToMovie) + if self.recoverRate: + taskMgr.add(self.__recoverBossDamage, taskName) + + def getBossDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0) + + def __recoverBossDamage(self, task): + self.bossDamageMovie.setT(self.getBossDamage() * self.bossDamageToMovie) + return Task.cont + + def __makeCagedToon(self): + if self.cagedToon: + return + self.cagedToon = NPCToons.createLocalNPC(self.cagedToonNpcId) + self.cagedToon.addActive() + self.cagedToon.reparentTo(self.cage) + self.cagedToon.setPosHpr(0, -2, 0, 180, 0, 0) + self.cagedToon.loop('neutral') + touch = CollisionPolygon(Point3(-3.0382, 3.0382, -1), Point3(3.0382, 3.0382, -1), Point3(3.0382, -3.0382, -1), Point3(-3.0382, -3.0382, -1)) + touchNode = CollisionNode('Cage') + touchNode.setCollideMask(ToontownGlobals.WallBitmask) + touchNode.addSolid(touch) + self.cage.attachNewNode(touchNode) + + def __cleanupCagedToon(self): + if self.cagedToon: + self.cagedToon.removeActive() + self.cagedToon.delete() + self.cagedToon = None + return + + def __walkToonToPromotion(self, toonId, delay, mopath, track, delayDeletes): + toon = base.cr.doId2do.get(toonId) + if toon: + destPos = toon.getPos() + self.placeToonInElevator(toon) + toon.wrtReparentTo(render) + walkMopath = MopathInterval(mopath, toon) + ival = Sequence(Wait(delay), Func(toon.suit.setPlayRate, 1, 'walk'), Func(toon.suit.loop, 'walk'), toon.posInterval(1, Point3(0, 90, 20)), ParallelEndTogether(walkMopath, toon.posInterval(2, destPos, blendType='noBlend')), Func(toon.suit.loop, 'neutral')) + self.toonMopathInterval.append(walkMopath) + track.append(ival) + delayDeletes.append(DelayDelete.DelayDelete(toon, 'SellbotBoss.__walkToonToPromotion')) + + def __walkDoober(self, suit, delay, turnPos, track, delayDeletes): + turnPos = Point3(*turnPos) + turnPosDown = Point3(*ToontownGlobals.SellbotBossDooberTurnPosDown) + flyPos = Point3(*ToontownGlobals.SellbotBossDooberFlyPos) + seq = Sequence(Func(suit.headsUp, turnPos), Wait(delay), Func(suit.loop, 'walk', 0), self.__walkSuitToPoint(suit, suit.getPos(), turnPos), self.__walkSuitToPoint(suit, turnPos, turnPosDown), self.__walkSuitToPoint(suit, turnPosDown, flyPos), suit.beginSupaFlyMove(flyPos, 0, 'flyAway'), Func(suit.fsm.request, 'Off')) + track.append(seq) + delayDeletes.append(DelayDelete.DelayDelete(suit, 'SellbotBoss.__walkDoober')) + + def __walkSuitToPoint(self, node, fromPos, toPos): + vector = Vec3(toPos - fromPos) + distance = vector.length() + time = distance / (ToontownGlobals.SuitWalkSpeed * 1.8) + return Sequence(Func(node.setPos, fromPos), Func(node.headsUp, toPos), node.posInterval(time, toPos)) + + def makeIntroductionMovie(self, delayDeletes): + track = Parallel() + camera.reparentTo(render) + camera.setPosHpr(0, 25, 30, 0, 0, 0) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + dooberTrack = Parallel() + if self.doobers: + self.__doobersToPromotionPosition(self.doobers[:4], self.battleANode) + self.__doobersToPromotionPosition(self.doobers[4:], self.battleBNode) + turnPosA = ToontownGlobals.SellbotBossDooberTurnPosA + turnPosB = ToontownGlobals.SellbotBossDooberTurnPosB + self.__walkDoober(self.doobers[0], 0, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[1], 4, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[2], 8, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[3], 12, turnPosA, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[7], 2, turnPosB, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[6], 6, turnPosB, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[5], 10, turnPosB, dooberTrack, delayDeletes) + self.__walkDoober(self.doobers[4], 14, turnPosB, dooberTrack, delayDeletes) + toonTrack = Parallel() + self.__toonsToPromotionPosition(self.toonsA, self.battleANode) + self.__toonsToPromotionPosition(self.toonsB, self.battleBNode) + delay = 0 + for toonId in self.toonsA: + self.__walkToonToPromotion(toonId, delay, self.toonsEnterA, toonTrack, delayDeletes) + delay += 1 + + for toonId in self.toonsB: + self.__walkToonToPromotion(toonId, delay, self.toonsEnterB, toonTrack, delayDeletes) + delay += 1 + + toonTrack.append(Sequence(Wait(delay), self.closeDoors)) + self.rampA.request('extended') + self.rampB.request('extended') + self.rampC.request('retracted') + self.clearChat() + self.cagedToon.clearChat() + promoteDoobers = TTLocalizer.BossCogPromoteDoobers % SuitDNA.getDeptFullnameP(self.style.dept) + doobersAway = TTLocalizer.BossCogDoobersAway[self.style.dept] + welcomeToons = TTLocalizer.BossCogWelcomeToons + promoteToons = TTLocalizer.BossCogPromoteToons % SuitDNA.getDeptFullnameP(self.style.dept) + discoverToons = TTLocalizer.BossCogDiscoverToons + attackToons = TTLocalizer.BossCogAttackToons + interruptBoss = TTLocalizer.CagedToonInterruptBoss + rescueQuery = TTLocalizer.CagedToonRescueQuery + bossAnimTrack = Sequence( + ActorInterval(self, 'Ff_speech', startTime=2, duration=10, loop=1), + ActorInterval(self, 'ltTurn2Wave', duration=2), + ActorInterval(self, 'wave', duration=4, loop=1), + ActorInterval(self, 'ltTurn2Wave', startTime=2, endTime=0), + ActorInterval(self, 'Ff_speech', duration=7, loop=1)) + track.append(bossAnimTrack) + dialogTrack = Track( + (0, Parallel( + camera.posHprInterval(8, Point3(-22, -100, 35), Point3(-10, -13, 0), blendType='easeInOut'), + IndirectInterval(toonTrack, 0, 18))), + (5.6, Func(self.setChatAbsolute, promoteDoobers, CFSpeech)), + (9, IndirectInterval(dooberTrack, 0, 9)), + (10, Sequence( + Func(self.clearChat), + Func(camera.setPosHpr, -23.1, 15.7, 17.2, -160, -2.4, 0))), + (12, Func(self.setChatAbsolute, doobersAway, CFSpeech)), + (16, Parallel( + Func(self.clearChat), + Func(camera.setPosHpr, -25, -99, 10, -14, 10, 0), + IndirectInterval(dooberTrack, 14), + IndirectInterval(toonTrack, 30))), + (18, Func(self.setChatAbsolute, welcomeToons, CFSpeech)), + (22, Func(self.setChatAbsolute, promoteToons, CFSpeech)), + (22.2, Sequence( + Func(self.cagedToon.nametag3d.setScale, 2), + Func(self.cagedToon.setChatAbsolute, interruptBoss, CFSpeech), + ActorInterval(self.cagedToon, 'wave'), + Func(self.cagedToon.loop, 'neutral'))), + (25, Sequence( + Func(self.clearChat), + Func(self.cagedToon.clearChat), + Func(camera.setPosHpr, -12, -15, 27, -151, -15, 0), + ActorInterval(self, 'Ff_lookRt'))), + (27, Sequence( + Func(self.cagedToon.setChatAbsolute, rescueQuery, CFSpeech), + Func(camera.setPosHpr, -12, 48, 94, -26, 20, 0), + ActorInterval(self.cagedToon, 'wave'), + Func(self.cagedToon.loop, 'neutral'))), + (31, Sequence( + Func(camera.setPosHpr, -20, -35, 10, -88, 25, 0), + Func(self.setChatAbsolute, discoverToons, CFSpeech), + Func(self.cagedToon.nametag3d.setScale, 1), + Func(self.cagedToon.clearChat), + ActorInterval(self, 'turn2Fb'))), + (34, Sequence( + Func(self.clearChat), + self.loseCogSuits(self.toonsA, self.battleANode, (0, 18, 5, -180, 0, 0)), + self.loseCogSuits(self.toonsB, self.battleBNode, (0, 18, 5, -180, 0, 0)))), + (37, Sequence( + self.toonNormalEyes(self.involvedToons), + Func(camera.setPosHpr, -23.4, -145.6, 44.0, -10.0, -12.5, 0), + Func(self.loop, 'Fb_neutral'), + Func(self.rampA.request, 'retract'), + Func(self.rampB.request, 'retract'), + Parallel(self.backupToonsToBattlePosition(self.toonsA, self.battleANode), + self.backupToonsToBattlePosition(self.toonsB, self.battleBNode), + Sequence( + Wait(2), + Func(self.setChatAbsolute, attackToons, CFSpeech)))))) + track.append(dialogTrack) + return Sequence(Func(self.stickToonsToFloor), track, Func(self.unstickToons), name=self.uniqueName('Introduction')) + + def __makeRollToBattleTwoMovie(self): + startPos = Point3(ToontownGlobals.SellbotBossBattleOnePosHpr[0], ToontownGlobals.SellbotBossBattleOnePosHpr[1], ToontownGlobals.SellbotBossBattleOnePosHpr[2]) + if self.arenaSide: + topRampPos = Point3(*ToontownGlobals.SellbotBossTopRampPosB) + topRampTurnPos = Point3(*ToontownGlobals.SellbotBossTopRampTurnPosB) + p3Pos = Point3(*ToontownGlobals.SellbotBossP3PosB) + else: + topRampPos = Point3(*ToontownGlobals.SellbotBossTopRampPosA) + topRampTurnPos = Point3(*ToontownGlobals.SellbotBossTopRampTurnPosA) + p3Pos = Point3(*ToontownGlobals.SellbotBossP3PosA) + battlePos = Point3(ToontownGlobals.SellbotBossBattleTwoPosHpr[0], ToontownGlobals.SellbotBossBattleTwoPosHpr[1], ToontownGlobals.SellbotBossBattleTwoPosHpr[2]) + battleHpr = VBase3(ToontownGlobals.SellbotBossBattleTwoPosHpr[3], ToontownGlobals.SellbotBossBattleTwoPosHpr[4], ToontownGlobals.SellbotBossBattleTwoPosHpr[5]) + bossTrack = Sequence() + bossTrack.append(Func(self.getGeomNode().setH, 180)) + bossTrack.append(Func(self.loop, 'Fb_neutral')) + track, hpr = self.rollBossToPoint(startPos, None, topRampPos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(topRampPos, hpr, topRampTurnPos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(topRampTurnPos, hpr, p3Pos, None, 0) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(p3Pos, hpr, battlePos, None, 0) + bossTrack.append(track) + return Sequence(bossTrack, Func(self.getGeomNode().setH, 0), name=self.uniqueName('BattleTwo')) + + def cagedToonMovieFunction(self, instruct, cageIndex): + self.notify.debug('cagedToonMovieFunction()') + if not (hasattr(self, 'cagedToon') and hasattr(self.cagedToon, 'nametag') and hasattr(self.cagedToon, 'nametag3d')): + return + if instruct == 1: + self.cagedToon.nametag3d.setScale(2) + elif instruct == 2: + self.cagedToon.setChatAbsolute(TTLocalizer.CagedToonDrop[cageIndex], CFSpeech) + elif instruct == 3: + self.cagedToon.nametag3d.setScale(1) + elif instruct == 4: + self.cagedToon.clearChat() + + def makeEndOfBattleMovie(self, hasLocalToon): + name = self.uniqueName('CageDrop') + seq = Sequence(name=name) + seq.append(Func(self.cage.setPos, self.cagePos[self.cageIndex])) + if hasLocalToon: + seq += [Func(camera.reparentTo, render), + Func(camera.setPosHpr, self.cage, 0, -50, 0, 0, 0, 0), + Func(localAvatar.setCameraFov, ToontownGlobals.CogHQCameraFov), + Func(self.hide)] + seq += [Wait(0.5), + Parallel(self.cage.posInterval(1, self.cagePos[self.cageIndex + 1], blendType='easeInOut'), SoundInterval(self.cageLowerSfx, duration=1)), + Func(self.cagedToonMovieFunction, 1, self.cageIndex), + Func(self.cagedToonMovieFunction, 2, self.cageIndex), + Wait(3), + Func(self.cagedToonMovieFunction, 3, self.cageIndex), + Func(self.cagedToonMovieFunction, 4, self.cageIndex)] + if hasLocalToon: + seq += [Func(self.show), + Func(camera.reparentTo, localAvatar), + Func(camera.setPos, localAvatar.cameraPositions[0][0]), + Func(camera.setHpr, 0, 0, 0)] + self.cageIndex += 1 + return seq + + def __makeBossDamageMovie(self): + startPos = Point3(ToontownGlobals.SellbotBossBattleTwoPosHpr[0], ToontownGlobals.SellbotBossBattleTwoPosHpr[1], ToontownGlobals.SellbotBossBattleTwoPosHpr[2]) + startHpr = Point3(*ToontownGlobals.SellbotBossBattleThreeHpr) + bottomPos = Point3(*ToontownGlobals.SellbotBossBottomPos) + deathPos = Point3(*ToontownGlobals.SellbotBossDeathPos) + self.setPosHpr(startPos, startHpr) + bossTrack = Sequence() + bossTrack.append(Func(self.loop, 'Fb_neutral')) + track, hpr = self.rollBossToPoint(startPos, startHpr, bottomPos, None, 1) + bossTrack.append(track) + track, hpr = self.rollBossToPoint(bottomPos, startHpr, deathPos, None, 1) + bossTrack.append(track) + duration = bossTrack.getDuration() + return bossTrack + + def __talkAboutPromotion(self, speech): + if not self.localToonPromoted: + pass + elif self.prevCogSuitLevel < ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.CagedToonPromotion + newCogSuitLevel = localAvatar.getCogLevels()[CogDisguiseGlobals.dept2deptIndex(self.style.dept)] + if newCogSuitLevel == ToontownGlobals.MaxCogSuitLevel: + speech += TTLocalizer.CagedToonLastPromotion % (ToontownGlobals.MaxCogSuitLevel + 1) + if newCogSuitLevel in ToontownGlobals.CogSuitHPLevels: + speech += TTLocalizer.CagedToonHPBoost + else: + speech += TTLocalizer.CagedToonMaxed % (ToontownGlobals.MaxCogSuitLevel + 1) + return speech + + def __makeCageOpenMovie(self): + speech = TTLocalizer.CagedToonThankYou + speech = self.__talkAboutPromotion(speech) + name = self.uniqueName('CageOpen') + seq = Sequence( + Func(self.cage.setPos, self.cagePos[4]), + Func(self.cageDoor.setHpr, VBase3(0, 0, 0)), + Func(self.cagedToon.setPos, Point3(0, -2, 0)), + Parallel( + self.cage.posInterval(0.5, self.cagePos[5], 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.2), + Func(self.cagedToon.loop, 'walk'), + self.cagedToon.posInterval(0.8, Point3(0, -6, 0)), + Func(self.cagedToon.setChatAbsolute, TTLocalizer.CagedToonYippee, CFSpeech), + ActorInterval(self.cagedToon, 'jump'), + Func(self.cagedToon.loop, 'neutral'), + Func(self.cagedToon.headsUp, localAvatar), + Func(self.cagedToon.setLocalPageChat, speech, 0), + Func(camera.reparentTo, localAvatar), + Func(camera.setPos, 0, -9, 9), + Func(camera.lookAt, self.cagedToon, Point3(0, 0, 2)), name=name) + return seq + + def __showOnscreenMessage(self, text): + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + self.onscreenMessage = DirectLabel(text=text, text_fg=VBase4(1, 1, 1, 1), text_align=TextNode.ACenter, relief=None, pos=(0, 0, 0.35), scale=0.1) + return + + def __clearOnscreenMessage(self): + if self.onscreenMessage: + self.onscreenMessage.destroy() + self.onscreenMessage = None + return + + def __showWaitingMessage(self, task): + self.__showOnscreenMessage(TTLocalizer.BuildingWaitingForVictors) + + def __placeCageShadow(self): + if self.cageShadow == None: + self.cageShadow = loader.loadModel('phase_3/models/props/drop_shadow') + self.cageShadow.setPos(0, 77.9, 18) + self.cageShadow.setColorScale(1, 1, 1, 0.6) + self.cageShadow.reparentTo(render) + return + + def __removeCageShadow(self): + if self.cageShadow != None: + self.cageShadow.detachNode() + return + + def setCageIndex(self, cageIndex): + self.cageIndex = cageIndex + self.cage.setPos(self.cagePos[self.cageIndex]) + if self.cageIndex >= 4: + self.__placeCageShadow() + else: + self.__removeCageShadow() + + def loadEnvironment(self): + DistributedBossCog.DistributedBossCog.loadEnvironment(self) + self.geom = loader.loadModel('phase_9/models/cogHQ/BossRoomHQ') + self.rampA = self.__findRamp('rampA', '**/west_ramp2') + self.rampB = self.__findRamp('rampB', '**/west_ramp') + self.rampC = self.__findRamp('rampC', '**/west_ramp1') + self.cage = self.geom.find('**/cage') + elevatorEntrance = self.geom.find('**/elevatorEntrance') + elevatorEntrance.getChildren().detach() + elevatorEntrance.setScale(1) + elevatorModel = loader.loadModel('phase_9/models/cogHQ/cogHQ_elevator') + elevatorModel.reparentTo(elevatorEntrance) + self.setupElevator(elevatorModel) + pos = self.cage.getPos() + self.cagePos = [] + for height in self.cageHeights: + self.cagePos.append(Point3(pos[0], pos[1], height)) + + self.cageDoor = self.geom.find('**/cage_door') + self.cage.setScale(1) + self.rope = Rope.Rope(name='supportChain') + self.rope.reparentTo(self.cage) + self.rope.setup(2, ((self.cage, (0.15, 0.13, 16)), (self.geom, (0.23, 78, 120)))) + self.rope.ropeNode.setRenderMode(RopeNode.RMBillboard) + self.rope.ropeNode.setUvMode(RopeNode.UVDistance) + self.rope.ropeNode.setUvDirection(0) + self.rope.ropeNode.setUvScale(0.8) + self.rope.setTexture(self.cage.findTexture('hq_chain')) + self.rope.setTransparency(1) + self.promotionMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.mid') + self.betweenBattleMusic = base.loadMusic('phase_9/audio/bgm/encntr_toon_winning.mid') + self.battleTwoMusic = base.loadMusic('phase_7/audio/bgm/encntr_suit_winning_indoor.mid') + self.geom.reparentTo(render) + + def unloadEnvironment(self): + DistributedBossCog.DistributedBossCog.unloadEnvironment(self) + self.geom.removeNode() + del self.geom + del self.cage + self.rampA.requestFinalState() + self.rampB.requestFinalState() + self.rampC.requestFinalState() + del self.rampA + del self.rampB + del self.rampC + + def __loadMopaths(self): + self.toonsEnterA = Mopath.Mopath() + self.toonsEnterA.loadFile('phase_9/paths/bossBattle-toonsEnterA') + self.toonsEnterA.fFaceForward = 1 + self.toonsEnterA.timeScale = 35 + self.toonsEnterB = Mopath.Mopath() + self.toonsEnterB.loadFile('phase_9/paths/bossBattle-toonsEnterB') + self.toonsEnterB.fFaceForward = 1 + self.toonsEnterB.timeScale = 35 + + def __unloadMopaths(self): + self.toonsEnterA.reset() + self.toonsEnterB.reset() + + def __findRamp(self, name, path): + ramp = self.geom.find(path) + children = ramp.getChildren() + animate = ramp.attachNewNode(name) + children.reparentTo(animate) + fsm = ClassicFSM.ClassicFSM(name, [ + State.State('extend', + Functor(self.enterRampExtend, animate), + Functor(self.exitRampExtend, animate), [ + 'extended', + 'retract', + 'retracted']), + State.State('extended', + Functor(self.enterRampExtended, animate), + Functor(self.exitRampExtended, animate), [ + 'retract', + 'retracted']), + State.State('retract', + Functor(self.enterRampRetract, animate), + Functor(self.exitRampRetract, animate), [ + 'extend', + 'extended', + 'retracted']), + State.State('retracted', + Functor(self.enterRampRetracted, animate), + Functor(self.exitRampRetracted, animate), [ + 'extend', + 'extended']), + State.State('off', + Functor(self.enterRampOff, animate), + Functor(self.exitRampOff, animate))], + 'off', 'off', onUndefTransition=ClassicFSM.ClassicFSM.DISALLOW) + fsm.enterInitialState() + return fsm + + def enterRampExtend(self, animate): + intervalName = self.uniqueName('extend-%s' % animate.getName()) + adjustTime = 2.0 * animate.getX() / 18.0 + ival = Parallel(SoundInterval(self.rampSlideSfx, node=animate), animate.posInterval(adjustTime, Point3(0, 0, 0), blendType='easeInOut', name=intervalName)) + ival.start() + self.storeInterval(ival, intervalName) + + def exitRampExtend(self, animate): + intervalName = self.uniqueName('extend-%s' % animate.getName()) + self.clearInterval(intervalName) + + def enterRampExtended(self, animate): + animate.setPos(0, 0, 0) + + def exitRampExtended(self, animate): + pass + + def enterRampRetract(self, animate): + intervalName = self.uniqueName('retract-%s' % animate.getName()) + adjustTime = 2.0 * (18 - animate.getX()) / 18.0 + ival = Parallel(SoundInterval(self.rampSlideSfx, node=animate), animate.posInterval(adjustTime, Point3(18, 0, 0), blendType='easeInOut', name=intervalName)) + ival.start() + self.storeInterval(ival, intervalName) + + def exitRampRetract(self, animate): + intervalName = self.uniqueName('retract-%s' % animate.getName()) + self.clearInterval(intervalName) + + def enterRampRetracted(self, animate): + animate.setPos(18, 0, 0) + + def exitRampRetracted(self, animate): + pass + + def enterRampOff(self, animate): + pass + + def exitRampOff(self, animate): + pass + + def enterOff(self): + DistributedBossCog.DistributedBossCog.enterOff(self) + if self.cagedToon: + self.cagedToon.clearChat() + if self.rampA: + self.rampA.request('off') + if self.rampB: + self.rampB.request('off') + if self.rampC: + self.rampC.request('off') + + def enterWaitForToons(self): + DistributedBossCog.DistributedBossCog.enterWaitForToons(self) + self.geom.hide() + self.cagedToon.removeActive() + + def exitWaitForToons(self): + DistributedBossCog.DistributedBossCog.exitWaitForToons(self) + self.geom.show() + self.cagedToon.addActive() + + def enterElevator(self): + DistributedBossCog.DistributedBossCog.enterElevator(self) + self.rampA.request('extended') + self.rampB.request('extended') + self.rampC.request('retracted') + self.setCageIndex(0) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + self.happy = 1 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.cagedToon.removeActive() + + def exitElevator(self): + DistributedBossCog.DistributedBossCog.exitElevator(self) + self.cagedToon.addActive() + + def enterIntroduction(self): + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + self.stopAnimate() + DistributedBossCog.DistributedBossCog.enterIntroduction(self) + self.rampA.request('extended') + self.rampB.request('extended') + self.rampC.request('retracted') + self.setCageIndex(0) + base.playMusic(self.promotionMusic, looping=1, volume=0.9) + + def exitIntroduction(self): + DistributedBossCog.DistributedBossCog.exitIntroduction(self) + self.promotionMusic.stop() + + def enterBattleOne(self): + DistributedBossCog.DistributedBossCog.enterBattleOne(self) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + self.clearChat() + self.cagedToon.clearChat() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('retract') + if self.battleA == None or self.battleB == None: + cageIndex = 1 + else: + cageIndex = 0 + self.setCageIndex(cageIndex) + return + + def exitBattleOne(self): + DistributedBossCog.DistributedBossCog.exitBattleOne(self) + + def enterRollToBattleTwo(self): + self.disableToonCollision() + self.releaseToons() + if self.arenaSide: + self.rampA.request('retract') + self.rampB.request('extend') + else: + self.rampA.request('extend') + self.rampB.request('retract') + self.rampC.request('retract') + self.reparentTo(render) + self.setCageIndex(2) + self.stickBossToFloor() + intervalName = 'RollToBattleTwo' + seq = Sequence(self.__makeRollToBattleTwoMovie(), Func(self.__onToPrepareBattleTwo), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + self.__showEasyBarrels() + taskMgr.doMethodLater(0.5, self.enableToonCollision, 'enableToonCollision') + + def __onToPrepareBattleTwo(self): + self.disableToonCollision() + self.unstickBoss() + self.setPosHpr(*ToontownGlobals.SellbotBossBattleTwoPosHpr) + self.doneBarrier('RollToBattleTwo') + + def exitRollToBattleTwo(self): + self.unstickBoss() + intervalName = 'RollToBattleTwo' + self.clearInterval(intervalName) + self.betweenBattleMusic.stop() + + def disableToonCollision(self): + base.localAvatar.collisionsOff() + + def enableToonCollision(self, task): + base.localAvatar.collisionsOn() + + def enterPrepareBattleTwo(self): + self.cleanupIntervals() + self.__hideEasyBarrels() + self.controlToons() + self.clearChat() + self.cagedToon.clearChat() + self.reparentTo(render) + if self.arenaSide: + self.rampA.request('retract') + self.rampB.request('extend') + else: + self.rampA.request('extend') + self.rampB.request('retract') + self.rampC.request('retract') + self.reparentTo(render) + self.setCageIndex(2) + camera.reparentTo(render) + camera.setPosHpr(self.cage, 0, -17, 3.3, 0, 0, 0) + (localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov),) + self.hide() + self.acceptOnce('doneChatPage', self.__onToBattleTwo) + self.cagedToon.setLocalPageChat(TTLocalizer.CagedToonPrepareBattleTwo, 1) + base.playMusic(self.stingMusic, looping=0, volume=1.0) + taskMgr.doMethodLater(0.5, self.enableToonCollision, 'enableToonCollision') + + def __onToBattleTwo(self, elapsed): + self.doneBarrier('PrepareBattleTwo') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleTwo(self): + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + self.__clearOnscreenMessage() + self.stingMusic.stop() + + def enterBattleTwo(self): + self.cleanupIntervals() + mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(2) + localAvatar.inventory.setBattleCreditMultiplier(mult) + self.reparentTo(render) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleTwoPosHpr) + self.clearChat() + self.cagedToon.clearChat() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('retract') + self.releaseToons() + self.toonsToBattlePosition(self.toonsA, self.battleANode) + self.toonsToBattlePosition(self.toonsB, self.battleBNode) + if self.battleA == None or self.battleB == None: + cageIndex = 3 + else: + cageIndex = 2 + self.setCageIndex(cageIndex) + base.playMusic(self.battleTwoMusic, looping=1, volume=0.9) + return + + def exitBattleTwo(self): + intervalName = self.uniqueName('cageDrop') + self.clearInterval(intervalName) + self.cleanupBattles() + self.battleTwoMusic.stop() + localAvatar.inventory.setBattleCreditMultiplier(1) + + def enterPrepareBattleThree(self): + self.cleanupIntervals() + self.controlToons() + self.clearChat() + self.cagedToon.clearChat() + self.reparentTo(render) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.setCageIndex(4) + camera.reparentTo(render) + camera.setPosHpr(self.cage, 0, -17, 3.3, 0, 0, 0) + (localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov),) + self.hide() + self.acceptOnce('doneChatPage', self.__onToBattleThree) + self.cagedToon.setLocalPageChat(TTLocalizer.CagedToonPrepareBattleThree, 1) + base.playMusic(self.betweenBattleMusic, looping=1, volume=0.9) + + def __onToBattleThree(self, elapsed): + self.doneBarrier('PrepareBattleThree') + taskMgr.doMethodLater(1, self.__showWaitingMessage, self.uniqueName('WaitingMessage')) + + def exitPrepareBattleThree(self): + self.show() + taskMgr.remove(self.uniqueName('WaitingMessage')) + self.ignore('doneChatPage') + intervalName = 'PrepareBattleThree' + self.clearInterval(intervalName) + self.__clearOnscreenMessage() + self.betweenBattleMusic.stop() + + def enterBattleThree(self): + DistributedBossCog.DistributedBossCog.enterBattleThree(self) + self.clearChat() + self.cagedToon.clearChat() + self.reparentTo(render) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.setCageIndex(4) + self.happy = 0 + self.raised = 1 + self.forward = 1 + self.doAnimate() + self.accept('enterCage', self.__touchedCage) + self.accept('pieSplat', self.__pieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + self.accept('begin-pie', self.__foundPieButton) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + taskMgr.doMethodLater(30, self.__howToGetPies, self.uniqueName('PieAdvice')) + self.stickBossToFloor() + self.bossDamageMovie = self.__makeBossDamageMovie() + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.bossDamageMovie.setDoneEvent(bossDoneEventName) + self.acceptOnce(bossDoneEventName, self.__doneBattleThree) + self.resetMaxDamage() + self.bossDamageToMovie = self.bossDamageMovie.getDuration() / self.bossMaxDamage + self.bossDamageMovie.setT(self.bossDamage * self.bossDamageToMovie) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9) + + def __doneBattleThree(self): + self.setState('NearVictory') + self.unstickBoss() + + def exitBattleThree(self): + DistributedBossCog.DistributedBossCog.exitBattleThree(self) + bossDoneEventName = self.uniqueName('DestroyedBoss') + self.ignore(bossDoneEventName) + taskMgr.remove(self.uniqueName('StandUp')) + self.ignore('enterCage') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.ignore('begin-pie') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.__removeCageShadow() + self.bossDamageMovie.finish() + self.bossDamageMovie = None + self.unstickBoss() + taskName = 'RecoverBossDamage' + taskMgr.remove(taskName) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + return + + def enterNearVictory(self): + self.cleanupIntervals() + self.reparentTo(render) + self.setPos(*ToontownGlobals.SellbotBossDeathPos) + self.setHpr(*ToontownGlobals.SellbotBossBattleThreeHpr) + self.clearChat() + self.cagedToon.clearChat() + self.setCageIndex(4) + self.releaseToons(finalBattle=1) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.accept('enterCage', self.__touchedCage) + self.accept('pieSplat', self.__finalPieSplat) + self.accept('localPieSplat', self.__localPieSplat) + self.accept('outOfPies', self.__outOfPies) + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.happy = 0 + self.raised = 0 + self.forward = 1 + self.doAnimate() + self.setDizzy(1) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def exitNearVictory(self): + self.ignore('enterCage') + self.ignore('pieSplat') + self.ignore('localPieSplat') + self.ignore('outOfPies') + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.__removeCageShadow() + self.setDizzy(0) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterVictory(self): + self.cleanupIntervals() + localAvatar.setCameraFov(ToontownGlobals.BossBattleCameraFov) + self.reparentTo(render) + self.setPos(*ToontownGlobals.SellbotBossDeathPos) + self.setHpr(*ToontownGlobals.SellbotBossBattleThreeHpr) + self.clearChat() + self.cagedToon.clearChat() + self.setCageIndex(4) + self.releaseToons(finalBattle=1) + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.happy = 0 + self.raised = 0 + self.forward = 1 + self.doAnimate('Fb_fall', now=1) + self.acceptOnce(self.animDoneEvent, self.__continueVictory) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __continueVictory(self): + self.stopAnimate() + self.stash() + self.doneBarrier('Victory') + + def exitVictory(self): + self.stopAnimate() + self.unstash() + self.__removeCageShadow() + localAvatar.setCameraFov(ToontownGlobals.CogHQCameraFov) + self.battleThreeMusicTime = self.battleThreeMusic.getTime() + self.battleThreeMusic.stop() + + def enterReward(self): + self.cleanupIntervals() + self.clearChat() + self.cagedToon.clearChat() + self.stash() + self.stopAnimate() + self.setCageIndex(4) + self.releaseToons(finalBattle=1) + self.toMovieMode() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + panelName = self.uniqueName('reward') + self.rewardPanel = RewardPanel.RewardPanel(panelName) + victory, camVictory, skipper = MovieToonVictory.doToonVictory(1, self.involvedToons, self.toonRewardIds, self.toonRewardDicts, self.deathList, self.rewardPanel, allowGroupShot=0, uberList=self.uberList, noSkip=True) + ival = Sequence(Parallel(victory, camVictory), Func(self.__doneReward)) + intervalName = 'RewardMovie' + delayDeletes = [] + for toonId in self.involvedToons: + toon = self.cr.doId2do.get(toonId) + if toon: + delayDeletes.append(DelayDelete.DelayDelete(toon, 'SellbotBoss.enterReward')) + + ival.delayDeletes = delayDeletes + ival.start() + self.storeInterval(ival, intervalName) + base.playMusic(self.battleThreeMusic, looping=1, volume=0.9, time=self.battleThreeMusicTime) + + def __doneReward(self): + self.doneBarrier('Reward') + self.toWalkMode() + + def exitReward(self): + intervalName = 'RewardMovie' + self.clearInterval(intervalName) + self.unstash() + self.rewardPanel.destroy() + del self.rewardPanel + self.__removeCageShadow() + self.battleThreeMusicTime = 0 + self.battleThreeMusic.stop() + + def enterEpilogue(self): + self.cleanupIntervals() + self.clearChat() + self.cagedToon.clearChat() + self.stash() + self.stopAnimate() + self.setCageIndex(4) + self.controlToons() + self.rampA.request('retract') + self.rampB.request('retract') + self.rampC.request('extend') + self.__arrangeToonsAroundCage() + camera.reparentTo(render) + camera.setPosHpr(-24, 52, 27.5, -53, -13, 0) + intervalName = 'EpilogueMovie' + seq = Sequence(self.__makeCageOpenMovie(), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + self.accept('nextChatPage', self.__epilogueChatNext) + self.accept('doneChatPage', self.__epilogueChatDone) + base.playMusic(self.epilogueMusic, looping=1, volume=0.9) + + def __epilogueChatNext(self, pageNumber, elapsed): + if pageNumber == 2: + if self.cagedToon.style.torso[1] == 'd': + track = ActorInterval(self.cagedToon, 'curtsy') + else: + track = ActorInterval(self.cagedToon, 'bow') + track = Sequence(track, Func(self.cagedToon.loop, 'neutral')) + intervalName = 'EpilogueMovieToonAnim' + self.storeInterval(track, intervalName) + track.start() + + def __epilogueChatDone(self, elapsed): + self.cagedToon.setChatAbsolute(TTLocalizer.CagedToonGoodbye, CFSpeech) + self.ignore('nextChatPage') + self.ignore('doneChatPage') + intervalName = 'EpilogueMovieToonAnim' + self.clearInterval(intervalName) + track = Parallel(Sequence(ActorInterval(self.cagedToon, 'wave'), Func(self.cagedToon.loop, 'neutral')), Sequence(Wait(0.5), Func(self.localToonToSafeZone))) + self.storeInterval(track, intervalName) + track.start() + + def exitEpilogue(self): + self.clearInterval('EpilogueMovieToonAnim') + self.unstash() + self.__removeCageShadow() + self.epilogueMusic.stop() + + def __arrangeToonsAroundCage(self): + radius = 15 + numToons = len(self.involvedToons) + center = (numToons - 1) / 2.0 + for i in range(numToons): + toon = base.cr.doId2do.get(self.involvedToons[i]) + if toon: + angle = 270 - 15 * (i - center) + radians = angle * math.pi / 180.0 + x = math.cos(radians) * radius + y = math.sin(radians) * radius + toon.setPos(self.cage, x, y, 0) + toon.setZ(18.0) + toon.headsUp(self.cage) + + def enterFrolic(self): + DistributedBossCog.DistributedBossCog.enterFrolic(self) + self.setPosHpr(*ToontownGlobals.SellbotBossBattleOnePosHpr) + + def doorACallback(self, isOpen): + if self.insidesANodePath: + if isOpen: + self.insidesANodePath.unstash() + else: + self.insidesANodePath.stash() + + def doorBCallback(self, isOpen): + if self.insidesBNodePath: + if isOpen: + self.insidesBNodePath.unstash() + else: + self.insidesBNodePath.stash() + + def __toonsToPromotionPosition(self, toonIds, battleNode): + points = BattleBase.BattleBase.toonPoints[len(toonIds) - 1] + for i in range(len(toonIds)): + toon = base.cr.doId2do.get(toonIds[i]) + if toon: + toon.reparentTo(render) + pos, h = points[i] + toon.setPosHpr(battleNode, pos[0], pos[1] + 10, pos[2], h, 0, 0) + + def __doobersToPromotionPosition(self, doobers, battleNode): + points = BattleBase.BattleBase.toonPoints[len(doobers) - 1] + for i in range(len(doobers)): + suit = doobers[i] + suit.fsm.request('neutral') + suit.loop('neutral') + pos, h = points[i] + suit.setPosHpr(battleNode, pos[0], pos[1] + 10, pos[2], h, 0, 0) + + def __touchedCage(self, entry): + self.sendUpdate('touchCage', []) + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + base.playSfx(self.piesRestockSfx) + if not self.everThrownPie: + taskMgr.doMethodLater(30, self.__howToThrowPies, self.uniqueName('PieAdvice')) + + def __outOfPies(self): + self.__showOnscreenMessage(TTLocalizer.BossBattleNeedMorePies) + taskMgr.doMethodLater(20, self.__howToGetPies, self.uniqueName('PieAdvice')) + + def __howToGetPies(self, task): + self.__showOnscreenMessage(TTLocalizer.BossBattleHowToGetPies) + + def __howToThrowPies(self, task): + self.__showOnscreenMessage(TTLocalizer.BossBattleHowToThrowPies) + + def __foundPieButton(self): + self.everThrownPie = 1 + self.__clearOnscreenMessage() + taskMgr.remove(self.uniqueName('PieAdvice')) + + def __pieSplat(self, toon, pieCode): + if base.config.GetBool('easy-vp', 0): + if not self.dizzy: + pieCode = ToontownGlobals.PieCodeBossInsides + if pieCode == ToontownGlobals.PieCodeBossInsides: + if toon == localAvatar: + self.d_hitBossInsides() + self.flashRed() + elif pieCode == ToontownGlobals.PieCodeBossCog: + if toon == localAvatar: + self.d_hitBoss(1) + if self.dizzy: + self.flashRed() + self.doAnimate('hit', now=1) + + def __localPieSplat(self, pieCode, entry): + if pieCode != ToontownGlobals.PieCodeToon: + return + avatarDoId = entry.getIntoNodePath().getNetTag('avatarDoId') + if avatarDoId == '': + self.notify.warning('Toon %s has no avatarDoId tag.' % repr(entry.getIntoNodePath())) + return + doId = int(avatarDoId) + if doId != localAvatar.doId: + self.d_hitToon(doId) + + def __finalPieSplat(self, toon, pieCode): + if pieCode != ToontownGlobals.PieCodeBossCog: + return + self.sendUpdate('finalPieSplat', []) + self.ignore('pieSplat') + + def cagedToonBattleThree(self, index, avId): + str = TTLocalizer.CagedToonBattleThree.get(index) + if str: + toonName = '' + if avId: + toon = self.cr.doId2do.get(avId) + if not toon: + self.cagedToon.clearChat() + return + toonName = toon.getName() + text = str % {'toon': toonName} + self.cagedToon.setChatAbsolute(text, CFSpeech | CFTimeout) + else: + self.cagedToon.clearChat() + + def cleanupAttacks(self): + self.__cleanupStrafe() + + def __cleanupStrafe(self): + if self.strafeInterval: + self.strafeInterval.finish() + self.strafeInterval = None + return + + def doStrafe(self, side, direction): + gearRoot = self.rotateNode.attachNewNode('gearRoot') + if side == 0: + gearRoot.setPos(0, -7, 3) + gearRoot.setHpr(180, 0, 0) + door = self.doorA + else: + gearRoot.setPos(0, 7, 3) + door = self.doorB + gearRoot.setTag('attackCode', str(ToontownGlobals.BossCogStrafeAttack)) + gearModel = self.getGearFrisbee() + gearModel.setScale(0.1) + t = self.getBossDamage() / 100.0 + gearTrack = Parallel() + numGears = int(4 + 6 * t + 0.5) + time = 5.0 - 4.0 * t + spread = 60 * math.pi / 180.0 + if direction == 1: + spread = -spread + dist = 50 + rate = time / numGears + for i in range(numGears): + node = gearRoot.attachNewNode(str(i)) + node.hide() + node.setPos(0, 0, 0) + gear = gearModel.instanceTo(node) + angle = (float(i) / (numGears - 1) - 0.5) * spread + x = dist * math.sin(angle) + y = dist * math.cos(angle) + h = random.uniform(-720, 720) + gearTrack.append(Sequence(Wait(i * rate), Func(node.show), Parallel(node.posInterval(1, Point3(x, y, 0), fluid=1), node.hprInterval(1, VBase3(h, 0, 0), fluid=1), Sequence(SoundInterval(self.strafeSfx[i], volume=0.2, node=self), duration=0)), Func(node.detachNode))) + + seq = Sequence(Func(door.request, 'open'), Wait(0.7), gearTrack, Func(door.request, 'close')) + self.__cleanupStrafe() + self.strafeInterval = seq + seq.start() + + def __showEasyBarrels(self): + barrelNodes = hidden.findAllMatches('**/Distributed*Barrel-*') + if not barrelNodes or barrelNodes.isEmpty(): + return + if render.find('barrelsRootNode'): + self.notify.warning('__showEasyBarrels(): barrelsRootNode already exists') + return + self.barrelsRootNode = render.attachNewNode('barrelsRootNode') + self.barrelsRootNode.setPos(*SellbotBossGlobals.BarrelsStartPos) + if self.arenaSide == 0: + self.barrelsRootNode.setHpr(180, 0, 0) + else: + self.barrelsRootNode.setHpr(0, 0, 0) + for i, barrelNode in enumerate(barrelNodes): + barrel = base.cr.doId2do.get(int(barrelNode.getNetTag('doId'))) + SellbotBossGlobals.setBarrelAttr(barrel, barrel.entId) + if hasattr(barrel, 'applyLabel'): + barrel.applyLabel() + barrel.setPosHpr(barrel.pos, barrel.hpr) + barrel.reparentTo(self.barrelsRootNode) + + intervalName = 'MakeBarrelsAppear' + seq = Sequence(LerpPosInterval(self.barrelsRootNode, 0.5, Vec3(*SellbotBossGlobals.BarrelsFinalPos), blendType='easeInOut'), name=intervalName) + seq.start() + self.storeInterval(seq, intervalName) + + def __hideEasyBarrels(self): + if hasattr(self, 'barrelsRootNode'): + self.barrelsRootNode.removeNode() + intervalName = 'MakeBarrelsAppear' + self.clearInterval(intervalName) + + def toonPromoted(self, promoted): + self.localToonPromoted = promoted diff --git a/toontown/suit/DistributedSellbotBossAI.py b/toontown/suit/DistributedSellbotBossAI.py new file mode 100644 index 0000000..831cb86 --- /dev/null +++ b/toontown/suit/DistributedSellbotBossAI.py @@ -0,0 +1,453 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed.ClockDelta import * +import DistributedBossCogAI +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import DistributedAvatarAI +import DistributedSuitAI +from toontown.battle import BattleExperienceAI +from direct.fsm import FSM +from toontown.toonbase import ToontownGlobals +from toontown.toon import InventoryBase +from toontown.toonbase import TTLocalizer +from toontown.battle import BattleBase +from toontown.toon import NPCToons +from toontown.suit import SellbotBossGlobals +import SuitDNA, random + +class DistributedSellbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSellbotBossAI') + limitHitCount = 6 + numPies = ToontownGlobals.FullPies + + def __init__(self, air): + DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 's') + FSM.FSM.__init__(self, 'DistributedSellbotBossAI') + self.doobers = [] + self.nerfed = ToontownGlobals.SELLBOT_NERF_HOLIDAY in self.air.holidayManager.currentHolidays + if self.nerfed: + self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamageNerfed + self.pieHitToonup = SellbotBossGlobals.PieToonupNerfed + self.pieDamageMult = SellbotBossGlobals.PieDamageMultNerfed + self.hitCountDamage = SellbotBossGlobals.HitCountDamageNerfed + else: + self.bossMaxDamage = ToontownGlobals.SellbotBossMaxDamage + self.pieHitToonup = SellbotBossGlobals.PieToonup + self.pieDamageMult = SellbotBossGlobals.PieDamageMult + self.hitCountDamage = SellbotBossGlobals.HitCountDamage + self.recoverRate = 0 + self.recoverStartTime = 0 + + def generateWithRequired(self, zoneId): + self.numRentalDiguises, self.numNormalDiguises = self.countDisguises() + self.__setCagedToonNpcId() + DistributedBossCogAI.DistributedBossCogAI.generateWithRequired(self, zoneId) + + def delete(self): + self.destroyEasyModeBarrels() + return DistributedBossCogAI.DistributedBossCogAI.delete(self) + + def getHoodId(self): + return ToontownGlobals.SellbotHQ + + def getCagedToonNpcId(self): + return self.cagedToonNpcId + + def __setCagedToonNpcId(self): + + def npcFriendsMaxStars(stars): + return [ id for id in NPCToons.npcFriends.keys() if NPCToons.getNPCTrackLevelHpRarity(id)[3] <= stars ] + + if self.numRentalDiguises >= 4: + self.cagedToonNpcId = random.choice(NPCToons.npcFriendsMinMaxStars(3, 3)) + else: + if 1 <= self.numRentalDiguises <= 3: + self.cagedToonNpcId = random.choice(NPCToons.npcFriendsMinMaxStars(3, 4)) + else: + self.cagedToonNpcId = random.choice(NPCToons.npcFriendsMinMaxStars(3, 5)) + + def magicWordHit(self, damage, avId): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + self.hitBossInsides() + self.hitBoss(damage) + + def hitBoss(self, bossDamage): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'DistributedSellbotBossAI.hitBoss from unknown avatar'): + return + self.validate(avId, bossDamage == 1, 'invalid bossDamage %s' % bossDamage) + bossDamage = int(round(bossDamage * self.pieDamageMult)) + if bossDamage < 1: + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + return + bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage) + self.b_setBossDamage(bossDamage, 0, 0) + if self.bossDamage >= self.bossMaxDamage: + self.setState('NearVictory') + else: + self.__recordHit() + + def hitBossInsides(self): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId in self.involvedToons, 'hitBossInsides from unknown avatar'): + return + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + self.b_setAttackCode(ToontownGlobals.BossCogDizzyNow) + self.b_setBossDamage(self.getBossDamage(), 0, 0) + + def hitToon(self, toonId): + avId = self.air.getAvatarIdFromSender() + if not self.validate(avId, avId != toonId, 'hitToon on self'): + return + if avId not in self.involvedToons or toonId not in self.involvedToons: + return + toon = self.air.doId2do.get(toonId) + if toon: + self.healToon(toon, self.pieHitToonup) + + def getDamageMultiplier(self): + if self.nerfed: + return SellbotBossGlobals.AttackMultNerfed + else: + return SellbotBossGlobals.AttackMult + + def touchCage(self): + avId = self.air.getAvatarIdFromSender() + currState = self.getCurrentOrNextState() + if currState != 'BattleThree' and currState != 'NearVictory': + return + if not self.validate(avId, avId in self.involvedToons, 'touchCage from unknown avatar'): + return + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(self.numPies) + toon.__touchedCage = 1 + self.__goodJump(avId) + + def finalPieSplat(self): + if self.state != 'NearVictory': + return + self.b_setState('Victory') + + def doNextAttack(self, task): + if self.attackCode == ToontownGlobals.BossCogDizzyNow: + attackCode = ToontownGlobals.BossCogRecoverDizzyAttack + else: + attackCode = random.choice([ToontownGlobals.BossCogAreaAttack, ToontownGlobals.BossCogFrontAttack, ToontownGlobals.BossCogDirectedAttack, ToontownGlobals.BossCogDirectedAttack, ToontownGlobals.BossCogDirectedAttack, ToontownGlobals.BossCogDirectedAttack]) + if attackCode == ToontownGlobals.BossCogAreaAttack: + self.__doAreaAttack() + else: + if attackCode == ToontownGlobals.BossCogDirectedAttack: + self.__doDirectedAttack() + else: + self.b_setAttackCode(attackCode) + + def __doAreaAttack(self): + self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack) + if self.recoverRate: + newRecoverRate = min(200, self.recoverRate * 1.2) + else: + newRecoverRate = 2 + now = globalClock.getFrameTime() + self.b_setBossDamage(self.getBossDamage(), newRecoverRate, now) + + def __doDirectedAttack(self): + if self.nearToons: + toonId = random.choice(self.nearToons) + self.b_setAttackCode(ToontownGlobals.BossCogDirectedAttack, toonId) + else: + self.__doAreaAttack() + + def b_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.d_setBossDamage(bossDamage, recoverRate, recoverStartTime) + self.setBossDamage(bossDamage, recoverRate, recoverStartTime) + + def setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + self.bossDamage = bossDamage + self.recoverRate = recoverRate + self.recoverStartTime = recoverStartTime + + def getBossDamage(self): + now = globalClock.getFrameTime() + elapsed = now - self.recoverStartTime + return int(max(self.bossDamage - self.recoverRate * elapsed / 60.0, 0)) + + def d_setBossDamage(self, bossDamage, recoverRate, recoverStartTime): + timestamp = globalClockDelta.localToNetworkTime(recoverStartTime) + self.sendUpdate('setBossDamage', [bossDamage, recoverRate, timestamp]) + + def waitForNextStrafe(self, delayTime): + currState = self.getCurrentOrNextState() + if currState == 'BattleThree': + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.doNextStrafe, taskName) + + def stopStrafes(self): + taskName = self.uniqueName('NextStrafe') + taskMgr.remove(taskName) + + def doNextStrafe(self, task): + if self.attackCode != ToontownGlobals.BossCogDizzyNow: + side = random.choice([0, 1]) + direction = random.choice([0, 1]) + self.sendUpdate('doStrafe', [side, direction]) + delayTime = 9 + self.waitForNextStrafe(delayTime) + + def __sendDooberIds(self): + dooberIds = [] + for suit in self.doobers: + dooberIds.append(suit.doId) + + self.sendUpdate('setDooberIds', [dooberIds]) + + def d_cagedToonBattleThree(self, index, avId): + self.sendUpdate('cagedToonBattleThree', [index, avId]) + + def formatReward(self): + return str(self.cagedToonNpcId) + + def makeBattleOneBattles(self): + self.postBattleState = 'RollToBattleTwo' + self.initializeBattles(1, ToontownGlobals.SellbotBossBattleOnePosHpr) + + def generateSuits(self, battleNumber): + if self.nerfed: + if battleNumber == 1: + return self.invokeSuitPlanner(15, 0) + else: + return self.invokeSuitPlanner(16, 1) + else: + if battleNumber == 1: + return self.invokeSuitPlanner(9, 0) + else: + return self.invokeSuitPlanner(10, 1) + + def removeToon(self, avId): + toon = simbase.air.doId2do.get(avId) + if toon: + toon.b_setNumPies(0) + DistributedBossCogAI.DistributedBossCogAI.removeToon(self, avId) + + def enterOff(self): + DistributedBossCogAI.DistributedBossCogAI.enterOff(self) + self.__resetDoobers() + + def enterElevator(self): + DistributedBossCogAI.DistributedBossCogAI.enterElevator(self) + self.b_setBossDamage(0, 0, 0) + if self.nerfed: + self.createEasyModeBarrels() + + def enterIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.enterIntroduction(self) + self.__makeDoobers() + self.b_setBossDamage(0, 0, 0) + + def exitIntroduction(self): + DistributedBossCogAI.DistributedBossCogAI.exitIntroduction(self) + self.__resetDoobers() + + def enterRollToBattleTwo(self): + self.divideToons() + self.barrier = self.beginBarrier('RollToBattleTwo', self.involvedToons, 45, self.__doneRollToBattleTwo) + + def __doneRollToBattleTwo(self, avIds): + self.b_setState('PrepareBattleTwo') + + def exitRollToBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def enterPrepareBattleTwo(self): + self.barrier = self.beginBarrier('PrepareBattleTwo', self.involvedToons, 30, self.__donePrepareBattleTwo) + self.makeBattleTwoBattles() + + def __donePrepareBattleTwo(self, avIds): + self.b_setState('BattleTwo') + + def exitPrepareBattleTwo(self): + self.ignoreBarrier(self.barrier) + + def makeBattleTwoBattles(self): + self.postBattleState = 'PrepareBattleThree' + self.initializeBattles(2, ToontownGlobals.SellbotBossBattleTwoPosHpr) + + def enterBattleTwo(self): + if self.battleA: + self.battleA.startBattle(self.toonsA, self.suitsA) + if self.battleB: + self.battleB.startBattle(self.toonsB, self.suitsB) + + def exitBattleTwo(self): + self.resetBattles() + + def enterPrepareBattleThree(self): + self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, 30, self.__donePrepareBattleThree) + + def __donePrepareBattleThree(self, avIds): + self.b_setState('BattleThree') + + def exitPrepareBattleThree(self): + self.ignoreBarrier(self.barrier) + + def enterBattleThree(self): + self.resetBattles() + self.setPieType() + self.b_setBossDamage(0, 0, 0) + self.battleThreeStart = globalClock.getFrameTime() + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.__touchedCage = 0 + + self.waitForNextAttack(5) + self.waitForNextStrafe(9) + self.cagedToonDialogIndex = 100 + self.__saySomethingLater() + + def __saySomething(self, task=None): + index = None + avId = 0 + if len(self.involvedToons) == 0: + return + avId = random.choice(self.involvedToons) + toon = simbase.air.doId2do.get(avId) + if toon.__touchedCage: + if self.cagedToonDialogIndex <= TTLocalizer.CagedToonBattleThreeMaxAdvice: + index = self.cagedToonDialogIndex + self.cagedToonDialogIndex += 1 + elif random.random() < 0.2: + index = random.randrange(100, TTLocalizer.CagedToonBattleThreeMaxAdvice + 1) + else: + index = random.randrange(20, TTLocalizer.CagedToonBattleThreeMaxTouchCage + 1) + if index: + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + return + + def __saySomethingLater(self, delayTime=15): + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + taskMgr.doMethodLater(delayTime, self.__saySomething, taskName) + + def __goodJump(self, avId): + currState = self.getCurrentOrNextState() + if currState != 'BattleThree': + return + index = random.randrange(10, TTLocalizer.CagedToonBattleThreeMaxGivePies + 1) + self.d_cagedToonBattleThree(index, avId) + self.__saySomethingLater() + + def exitBattleThree(self): + self.stopAttacks() + self.stopStrafes() + taskName = self.uniqueName('CagedToonSaySomething') + taskMgr.remove(taskName) + + def enterNearVictory(self): + self.resetBattles() + + def exitNearVictory(self): + pass + + def enterVictory(self): + self.resetBattles() + self.suitsKilled.append({'type': None, 'level': None, 'track': self.dna.dept, 'isSkelecog': 0, 'isForeman': 0, 'isVP': 1, 'isCFO': 0, 'isSupervisor': 0, 'isVirtual': 0, 'activeToons': self.involvedToons[:]}) + self.barrier = self.beginBarrier('Victory', self.involvedToons, 10, self.__doneVictory) + return + + def __doneVictory(self, avIds): + self.d_setBattleExperience() + self.b_setState('Reward') + BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons) + for toonId in self.involvedToons: + toon = self.air.doId2do.get(toonId) + if toon: + configMax = simbase.config.GetInt('max-sos-cards', 16) + if configMax == 8: + maxNumCalls = 1 + else: + maxNumCalls = 2 + if not toon.attemptAddNPCFriend(self.cagedToonNpcId, numCalls=maxNumCalls): + self.notify.info('%s.unable to add NPCFriend %s to %s.' % (self.doId, self.cagedToonNpcId, toonId)) + if self.__shouldPromoteToon(toon): + toon.b_promote(self.deptIndex) + self.sendUpdateToAvatarId(toonId, 'toonPromoted', [1]) + else: + self.sendUpdateToAvatarId(toonId, 'toonPromoted', [0]) + + def __shouldPromoteToon(self, toon): + if not toon.readyForPromotion(self.deptIndex): + return False + else: + if self.isToonWearingRentalSuit(toon.doId): + return False + return True + + def exitVictory(self): + self.takeAwayPies() + + def enterFrolic(self): + DistributedBossCogAI.DistributedBossCogAI.enterFrolic(self) + self.b_setBossDamage(0, 0, 0) + + def __resetDoobers(self): + for suit in self.doobers: + suit.requestDelete() + + self.doobers = [] + + def __makeDoobers(self): + self.__resetDoobers() + for i in range(8): + suit = DistributedSuitAI.DistributedSuitAI(self.air, None) + level = random.randrange(len(SuitDNA.suitsPerLevel)) + suit.dna = SuitDNA.SuitDNA() + suit.dna.newSuitRandom(level=level, dept=self.dna.dept) + suit.setLevel(level) + suit.generateWithRequired(self.zoneId) + self.doobers.append(suit) + + self.__sendDooberIds() + return + + def setPieType(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.d_setPieType(4) + + def takeAwayPies(self): + for toonId in self.involvedToons: + toon = simbase.air.doId2do.get(toonId) + if toon: + toon.b_setNumPies(0) + + def __recordHit(self): + now = globalClock.getFrameTime() + self.hitCount += 1 + if self.hitCount < self.limitHitCount or self.bossDamage < self.hitCountDamage: + return + self.b_setAttackCode(ToontownGlobals.BossCogRecoverDizzyAttack) + + def createEasyModeBarrels(self): + self.barrels = [] + for entId, entDef in SellbotBossGlobals.BarrelDefs.iteritems(): + barrelType = entDef['type'] + barrel = barrelType(self.air, entId) + SellbotBossGlobals.setBarrelAttr(barrel, entId) + barrel.generateWithRequired(self.zoneId) + self.barrels.append(barrel) + + def destroyEasyModeBarrels(self): + if hasattr(self, 'barrels') and self.barrels: + for barrel in self.barrels: + barrel.requestDelete() + + self.barrels = [] diff --git a/toontown/suit/DistributedStageSuit.py b/toontown/suit/DistributedStageSuit.py new file mode 100644 index 0000000..4652a27 --- /dev/null +++ b/toontown/suit/DistributedStageSuit.py @@ -0,0 +1,32 @@ +from toontown.suit import DistributedFactorySuit +from toontown.suit.Suit import * +from direct.directnotify import DirectNotifyGlobal +from direct.actor import Actor +from otp.avatar import Avatar +import SuitDNA +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from toontown.battle import SuitBattleGlobals +from direct.task import Task +from toontown.battle import BattleProps +from toontown.toonbase import TTLocalizer +import string + +class DistributedStageSuit(DistributedFactorySuit.DistributedFactorySuit): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageSuit') + + def setCogSpec(self, spec): + self.spec = spec + self.setPos(spec['pos']) + self.setH(spec['h']) + self.originalPos = spec['pos'] + self.escapePos = spec['pos'] + self.pathEntId = spec['path'] + self.behavior = spec['behavior'] + self.skeleton = spec['skeleton'] + self.boss = spec['boss'] + self.revives = spec.get('revives') + if self.reserve: + self.reparentTo(hidden) + else: + self.doReparent() diff --git a/toontown/suit/DistributedStageSuitAI.py b/toontown/suit/DistributedStageSuitAI.py new file mode 100644 index 0000000..a9314dd --- /dev/null +++ b/toontown/suit/DistributedStageSuitAI.py @@ -0,0 +1,14 @@ +from toontown.suit import DistributedFactorySuitAI +from direct.directnotify import DirectNotifyGlobal + +class DistributedStageSuitAI(DistributedFactorySuitAI.DistributedFactorySuitAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedStageSuitAI') + + def isForeman(self): + return 0 + + def isSupervisor(self): + return self.boss + + def isVirtual(self): + return self.virtual diff --git a/toontown/suit/DistributedSuit.py b/toontown/suit/DistributedSuit.py new file mode 100644 index 0000000..7ddb648 --- /dev/null +++ b/toontown/suit/DistributedSuit.py @@ -0,0 +1,696 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.directtools.DirectGeometry import CLAMP +from direct.task import Task +from otp.avatar import DistributedAvatar +import Suit +from toontown.toonbase import ToontownGlobals +from toontown.battle import DistributedBattle +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import SuitTimings +import SuitBase +import DistributedSuitPlanner +from direct.directnotify import DirectNotifyGlobal +import SuitDialog +from toontown.battle import BattleProps +from toontown.distributed.DelayDeletable import DelayDeletable +import math +import copy +import DistributedSuitBase +from otp.otpbase import OTPLocalizer +import random +STAND_OUTSIDE_DOOR = 2.5 +BATTLE_IGNORE_TIME = 6 +BATTLE_WAIT_TIME = 3 +CATCHUP_SPEED_MULTIPLIER = 3 +ALLOW_BATTLE_DETECT = 1 + +class DistributedSuit(DistributedSuitBase.DistributedSuitBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuit') + ENABLE_EXPANDED_NAME = 0 + + def __init__(self, cr): + try: + self.DistributedSuit_initialized + return + except: + self.DistributedSuit_initialized = 1 + + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.spDoId = None + self.pathEndpointStart = 0 + self.pathEndpointEnd = 0 + self.minPathLen = 0 + self.maxPathLen = 0 + self.pathPositionIndex = 0 + self.pathPositionTimestamp = 0.0 + self.pathState = 0 + self.path = None + self.localPathState = 0 + self.currentLeg = -1 + self.pathStartTime = 0.0 + self.legList = None + self.initState = None + self.finalState = None + self.buildingSuit = 0 + self.fsm = ClassicFSM.ClassicFSM('DistributedSuit', [ + State.State('Off', + self.enterOff, + self.exitOff, [ + 'FromSky', + 'FromSuitBuilding', + 'Walk', + 'Battle', + 'neutral', + 'ToToonBuilding', + 'ToSuitBuilding', + 'ToCogHQ', + 'FromCogHQ', + 'ToSky', + 'FlyAway', + 'DanceThenFlyAway', + 'WalkToStreet', + 'WalkFromStreet']), + State.State('FromSky', + self.enterFromSky, + self.exitFromSky, [ + 'Walk', + 'Battle', + 'neutral', + 'ToSky', + 'WalkFromStreet']), + State.State('FromSuitBuilding', + self.enterFromSuitBuilding, + self.exitFromSuitBuilding, [ + 'WalkToStreet', + 'Walk', + 'Battle', + 'neutral', + 'ToSky']), + State.State('WalkToStreet', + self.enterWalkToStreet, + self.exitWalkToStreet, [ + 'Walk', + 'Battle', + 'neutral', + 'ToSky', + 'ToToonBuilding', + 'ToSuitBuilding', + 'ToCogHQ', + 'WalkFromStreet']), + State.State('WalkFromStreet', + self.enterWalkFromStreet, + self.exitWalkFromStreet, [ + 'ToToonBuilding', + 'ToSuitBuilding', + 'ToCogHQ', + 'Battle', + 'neutral', + 'ToSky']), + State.State('Walk', + self.enterWalk, + self.exitWalk, [ + 'WaitForBattle', + 'Battle', + 'neutral', + 'WalkFromStreet', + 'ToSky', + 'ToCogHQ', + 'Walk']), + State.State('Battle', + self.enterBattle, + self.exitBattle, [ + 'Walk', + 'ToToonBuilding', + 'ToCogHQ', + 'ToSuitBuilding', + 'ToSky']), + State.State('neutral', + self.enterNeutral, + self.exitNeutral, []), + State.State('WaitForBattle', + self.enterWaitForBattle, + self.exitWaitForBattle, [ + 'Battle', + 'neutral', + 'Walk', + 'WalkToStreet', + 'WalkFromStreet', + 'ToToonBuilding', + 'ToCogHQ', + 'ToSuitBuilding', + 'ToSky']), + State.State('ToToonBuilding', + self.enterToToonBuilding, + self.exitToToonBuilding, [ + 'neutral', + 'Battle']), + State.State('ToSuitBuilding', + self.enterToSuitBuilding, + self.exitToSuitBuilding, [ + 'neutral', + 'Battle']), + State.State('ToCogHQ', + self.enterToCogHQ, + self.exitToCogHQ, [ + 'neutral', + 'Battle']), + State.State('FromCogHQ', + self.enterFromCogHQ, + self.exitFromCogHQ, [ + 'neutral', + 'Battle', + 'Walk']), + State.State('ToSky', + self.enterToSky, + self.exitToSky, [ + 'Battle']), + State.State('FlyAway', + self.enterFlyAway, + self.exitFlyAway, + []), + State.State('DanceThenFlyAway', + self.enterDanceThenFlyAway, + self.exitDanceThenFlyAway, + [])], + 'Off', 'Off') + self.fsm.enterInitialState() + self.soundSequenceList = [] + self.__currentDialogue = None + return + + def generate(self): + DistributedSuitBase.DistributedSuitBase.generate(self) + + def disable(self): + for soundSequence in self.soundSequenceList: + soundSequence.finish() + + self.soundSequenceList = [] + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.resumePath(0) + self.stopPathNow() + self.setState('Off') + DistributedSuitBase.DistributedSuitBase.disable(self) + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def setPathEndpoints(self, start, end, minPathLen, maxPathLen): + if self.pathEndpointStart == start and self.pathEndpointEnd == end and self.minPathLen == minPathLen and self.maxPathLen == maxPathLen and self.path != None: + return + self.pathEndpointStart = start + self.pathEndpointEnd = end + self.minPathLen = minPathLen + self.maxPathLen = maxPathLen + self.path = None + self.pathLength = 0 + self.currentLeg = -1 + self.legList = None + if self.maxPathLen == 0: + return + if not self.verifySuitPlanner(): + return + self.startPoint = self.sp.pointIndexes[self.pathEndpointStart] + self.endPoint = self.sp.pointIndexes[self.pathEndpointEnd] + path = self.sp.genPath(self.startPoint, self.endPoint, self.minPathLen, self.maxPathLen) + self.setPath(path) + self.makeLegList() + return + + def verifySuitPlanner(self): + if self.sp == None and self.spDoId != 0: + self.notify.warning('Suit %d does not have a suit planner! Expected SP doId %s.' % (self.doId, self.spDoId)) + self.sp = self.cr.doId2do.get(self.spDoId, None) + if self.sp == None: + return 0 + return 1 + + def setPathPosition(self, index, timestamp): + if not self.verifySuitPlanner(): + return + if self.path == None: + self.setPathEndpoints(self.pathEndpointStart, self.pathEndpointEnd, self.minPathLen, self.maxPathLen) + self.pathPositionIndex = index + self.pathPositionTimestamp = globalClockDelta.networkToLocalTime(timestamp) + if self.legList != None: + self.pathStartTime = self.pathPositionTimestamp - self.legList.getStartTime(self.pathPositionIndex) + return + + def setPathState(self, state): + self.pathState = state + self.resumePath(state) + + def debugSuitPosition(self, elapsed, currentLeg, x, y, timestamp): + now = globalClock.getFrameTime() + chug = globalClock.getRealTime() - now + messageAge = now - globalClockDelta.networkToLocalTime(timestamp, now) + if messageAge < -(chug + 0.5) or messageAge > chug + 1.0: + print 'Apparently out of sync with AI by %0.2f seconds. Suggest resync!' % messageAge + return + localElapsed = now - self.pathStartTime + timeDiff = localElapsed - (elapsed + messageAge) + if abs(timeDiff) > 0.2: + print "%s (%d) appears to be %0.2f seconds out of sync along its path. Suggest '~cogs sync'." % (self.getName(), self.getDoId(), timeDiff) + return + if self.legList == None: + print "%s (%d) doesn't have a legList yet." % (self.getName(), self.getDoId()) + return + netPos = Point3(x, y, 0.0) + leg = self.legList.getLeg(currentLeg) + calcPos = leg.getPosAtTime(elapsed - leg.getStartTime()) + calcPos.setZ(0.0) + calcDelta = Vec3(netPos - calcPos) + diff = calcDelta.length() + if diff > 4.0: + print '%s (%d) is %0.2f feet from the AI computed path!' % (self.getName(), self.getDoId(), diff) + print 'Probably your DNA files are out of sync.' + return + localPos = Point3(self.getX(), self.getY(), 0.0) + localDelta = Vec3(netPos - localPos) + diff = localDelta.length() + if diff > 10.0: + print '%s (%d) in state %s is %0.2f feet from its correct position!' % (self.getName(), + self.getDoId(), + self.fsm.getCurrentState().getName(), + diff) + print 'Should be at (%0.2f, %0.2f), but is at (%0.2f, %0.2f).' % (x, + y, + localPos[0], + localPos[1]) + return + print '%s (%d) is in the correct position.' % (self.getName(), self.getDoId()) + return + + def denyBattle(self): + DistributedSuitBase.DistributedSuitBase.denyBattle(self) + self.disableBattleDetect() + + def resumePath(self, state): + if self.localPathState != state: + self.localPathState = state + if state == 0: + self.stopPathNow() + elif state == 1: + self.moveToNextLeg(None) + elif state == 2: + self.stopPathNow() + if self.sp != None: + self.setState('Off') + self.setState('FlyAway') + elif state == 3: + pass + elif state == 4: + self.stopPathNow() + if self.sp != None: + self.setState('Off') + self.setState('DanceThenFlyAway') + else: + self.notify.error('No such state as: ' + str(state)) + return + + def moveToNextLeg(self, task): + if self.legList == None: + self.notify.warning('Suit %d does not have a path!' % self.getDoId()) + return Task.done + now = globalClock.getFrameTime() + elapsed = now - self.pathStartTime + nextLeg = self.legList.getLegIndexAtTime(elapsed, self.currentLeg) + numLegs = self.legList.getNumLegs() + if self.currentLeg != nextLeg: + self.currentLeg = nextLeg + self.doPathLeg(self.legList[nextLeg], elapsed - self.legList.getStartTime(nextLeg)) + nextLeg += 1 + if nextLeg < numLegs: + nextTime = self.legList.getStartTime(nextLeg) + delay = nextTime - elapsed + name = self.taskName('move') + taskMgr.remove(name) + taskMgr.doMethodLater(delay, self.moveToNextLeg, name) + return Task.done + + def doPathLeg(self, leg, time): + self.fsm.request(SuitLeg.getTypeName(leg.getType()), [leg, time]) + return 0 + + def stopPathNow(self): + name = self.taskName('move') + taskMgr.remove(name) + self.currentLeg = -1 + + def calculateHeading(self, a, b): + xdelta = b[0] - a[0] + ydelta = b[1] - a[1] + if ydelta == 0: + if xdelta > 0: + return -90 + else: + return 90 + elif xdelta == 0: + if ydelta > 0: + return 0 + else: + return 180 + else: + angle = math.atan2(ydelta, xdelta) + return rad2Deg(angle) - 90 + + def beginBuildingMove(self, moveIn, doneEvent, suit = 0): + doorPt = Point3(0) + buildingPt = Point3(0) + streetPt = Point3(0) + if self.virtualPos: + doorPt.assign(self.virtualPos) + else: + doorPt.assign(self.getPos()) + if moveIn: + streetPt = self.prevPointPos() + else: + streetPt = self.currPointPos() + dx = doorPt[0] - streetPt[0] + dy = doorPt[1] - streetPt[1] + buildingPt = Point3(doorPt[0] + dx, doorPt[1] + dy, doorPt[2]) + if moveIn: + if suit: + moveTime = SuitTimings.toSuitBuilding + else: + moveTime = SuitTimings.toToonBuilding + return self.beginMove(doneEvent, buildingPt, time=moveTime) + else: + return self.beginMove(doneEvent, doorPt, buildingPt, time=SuitTimings.fromSuitBuilding) + return None + + def setSPDoId(self, doId): + self.spDoId = doId + self.sp = self.cr.doId2do.get(doId, None) + if self.sp == None and self.spDoId != 0: + self.notify.warning('Suit %s created before its suit planner, %d' % (self.doId, self.spDoId)) + return + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.sendUpdate('requestBattle', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]]) + + def __handleToonCollision(self, collEntry): + if not base.localAvatar.wantBattles: + return + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit: requesting a Battle with ' + 'toon: %d' % toonId) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + + def setAnimState(self, state): + self.setState(state) + + def enterFromSky(self, leg, time): + self.enableBattleDetect('fromSky', self.__handleToonCollision) + self.loop('neutral', 0) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + h = self.calculateHeading(a, b) + self.setPosHprScale(a[0], a[1], a[2], h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = self.beginSupaFlyMove(a, 1, 'fromSky') + self.mtrack.start(time) + + def exitFromSky(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + def enterWalkToStreet(self, leg, time): + self.enableBattleDetect('walkToStreet', self.__handleToonCollision) + self.loop('walk', 0) + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta *= (length - STAND_OUTSIDE_DOOR) / length + a1 = Point3(b - delta) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b, startPos=a1), name=self.taskName('walkToStreet')) + self.mtrack.start(time) + + def exitWalkToStreet(self): + self.disableBattleDetect() + self.enableRaycast(0) + self.mtrack.finish() + del self.mtrack + + def enterWalkFromStreet(self, leg, time): + self.enableBattleDetect('walkFromStreet', self.__handleToonCollision) + self.loop('walk', 0) + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta *= (length - STAND_OUTSIDE_DOOR) / length + b1 = Point3(a + delta) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b1, startPos=a), name=self.taskName('walkFromStreet')) + self.mtrack.start(time) + + def exitWalkFromStreet(self): + self.disableBattleDetect() + self.enableRaycast(0) + self.mtrack.finish() + del self.mtrack + + def enterWalk(self, leg, time): + self.enableBattleDetect('bellicose', self.__handleToonCollision) + self.loop('walk', 0) + a = leg.getPosA() + b = leg.getPosB() + h = self.calculateHeading(a, b) + pos = leg.getPosAtTime(time) + self.setPosHprScale(pos[0], pos[1], pos[2], h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b, startPos=a), name=self.taskName('bellicose')) + self.mtrack.start(time) + + def exitWalk(self): + self.disableBattleDetect() + self.mtrack.pause() + del self.mtrack + + def enterToSky(self, leg, time): + self.enableBattleDetect('toSky', self.__handleToonCollision) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + h = self.calculateHeading(a, b) + self.setPosHprScale(b[0], b[1], b[2], h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = self.beginSupaFlyMove(b, 0, 'toSky') + self.mtrack.start(time) + + def exitToSky(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + def enterFromSuitBuilding(self, leg, time): + self.enableBattleDetect('fromSuitBuilding', self.__handleToonCollision) + self.loop('walk', 0) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta2 = delta * (self.sp.suitWalkSpeed * leg.getLegTime()) / length + delta *= (length - STAND_OUTSIDE_DOOR) / length + b1 = Point3(b - delta) + a1 = Point3(b1 - delta2) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b1, startPos=a1), name=self.taskName('fromSuitBuilding')) + self.mtrack.start(time) + + def exitFromSuitBuilding(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + + def enterToToonBuilding(self, leg, time): + self.loop('neutral', 0) + + def exitToToonBuilding(self): + pass + + def enterToSuitBuilding(self, leg, time): + self.loop('walk', 0) + if not self.verifySuitPlanner(): + return + a = leg.getPosA() + b = leg.getPosB() + delta = Vec3(b - a) + length = delta.length() + delta2 = delta * (self.sp.suitWalkSpeed * leg.getLegTime()) / length + delta *= (length - STAND_OUTSIDE_DOOR) / length + a1 = Point3(a + delta) + b1 = Point3(a1 + delta2) + self.enableRaycast(1) + h = self.calculateHeading(a, b) + self.setHprScale(h, 0.0, 0.0, 1.0, 1.0, 1.0) + self.mtrack = Sequence(LerpPosInterval(self, leg.getLegTime(), b1, startPos=a1), name=self.taskName('toSuitBuilding')) + self.mtrack.start(time) + + def exitToSuitBuilding(self): + self.mtrack.finish() + del self.mtrack + + def enterToCogHQ(self, leg, time): + self.loop('neutral', 0) + + def exitToCogHQ(self): + pass + + def enterFromCogHQ(self, leg, time): + self.loop('neutral', 0) + self.detachNode() + + def exitFromCogHQ(self): + self.reparentTo(render) + + def enterBattle(self): + DistributedSuitBase.DistributedSuitBase.enterBattle(self) + self.resumePath(0) + + def enterNeutral(self): + self.notify.debug('DistributedSuit: Neutral (entering a Door)') + self.resumePath(0) + self.loop('neutral', 0) + + def exitNeutral(self): + pass + + def enterWaitForBattle(self): + DistributedSuitBase.DistributedSuitBase.enterWaitForBattle(self) + self.resumePath(0) + + def enterFlyAway(self): + self.enableBattleDetect('flyAway', self.__handleToonCollision) + if not self.verifySuitPlanner(): + return + b = Point3(self.getPos()) + self.mtrack = self.beginSupaFlyMove(b, 0, 'flyAway') + self.mtrack.start() + + def exitFlyAway(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + def enterDanceThenFlyAway(self): + self.enableBattleDetect('danceThenFlyAway', self.__handleToonCollision) + if not self.verifySuitPlanner(): + return + danceTrack = self.actorInterval('victory') + b = Point3(self.getPos()) + flyMtrack = self.beginSupaFlyMove(b, 0, 'flyAway') + self.mtrack = Sequence(danceTrack, flyMtrack, name=self.taskName('danceThenFlyAway')) + self.mtrack.start() + + def exitDanceThenFlyAway(self): + self.disableBattleDetect() + self.mtrack.finish() + del self.mtrack + self.detachPropeller() + + 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: + if self.nametag.getNumChatPages() > 0: + self.playDialogueForString(self.nametag.getChat()) + if self.soundChatBubble != None: + base.playSfx(self.soundChatBubble, node=self) + elif self.nametag.getChatStomp() > 0: + self.playDialogueForString(self.nametag.getStompText(), self.nametag.getStompDelay()) + return + + def playDialogueForString(self, chatString, delay = 0.0): + if len(chatString) == 0: + return + 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, delay) + + def playDialogue(self, type, length, delay = 0.0): + 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: + soundSequence = Sequence(Wait(delay), SoundInterval(dialogueArray[sfxIndex], node=None, listenerNode=base.localAvatar, loop=0, volume=1.0)) + self.soundSequenceList.append(soundSequence) + soundSequence.start() + self.cleanUpSoundList() + return + + def cleanUpSoundList(self): + removeList = [] + for soundSequence in self.soundSequenceList: + if soundSequence.isStopped(): + removeList.append(soundSequence) + + for soundSequence in removeList: + self.soundSequenceList.remove(soundSequence) diff --git a/toontown/suit/DistributedSuitAI.py b/toontown/suit/DistributedSuitAI.py new file mode 100644 index 0000000..f9dc970 --- /dev/null +++ b/toontown/suit/DistributedSuitAI.py @@ -0,0 +1,349 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +from otp.avatar import DistributedAvatarAI +import SuitTimings +from direct.task import Task +import SuitPlannerBase, SuitBase, SuitDialog, SuitDNA +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +from toontown.building import FADoorCodes +import DistributedSuitBaseAI +from toontown.hood import ZoneUtil +import random + +class DistributedSuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI): + SUIT_BUILDINGS = simbase.config.GetBool('want-suit-buildings', 1) + DEBUG_SUIT_POSITIONS = simbase.config.GetBool('debug-suit-positions', 0) + UPDATE_TIMESTAMP_INTERVAL = 180.0 + myId = 0 + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitAI') + + def __init__(self, air, suitPlanner): + DistributedSuitBaseAI.DistributedSuitBaseAI.__init__(self, air, suitPlanner) + self.bldgTrack = None + self.branchId = None + if suitPlanner: + self.branchId = suitPlanner.zoneId + self.pathEndpointStart = 0 + self.pathEndpointEnd = 0 + self.minPathLen = 0 + self.maxPathLen = 0 + self.pathPositionIndex = 0 + self.pathPositionTimestamp = 0.0 + self.pathState = 0 + self.currentLeg = 0 + self.legType = SuitLeg.TOff + self.flyInSuit = 0 + self.buildingSuit = 0 + self.attemptingTakeover = 0 + self.takeoverIsCogdo = False + self.buildingDestination = None + self.buildingDestinationIsCogdo = False + return + + def stopTasks(self): + taskMgr.remove(self.taskName('flyAwayNow')) + taskMgr.remove(self.taskName('danceNowFlyAwayLater')) + taskMgr.remove(self.taskName('move')) + + def pointInMyPath(self, point, elapsedTime): + if self.pathState != 1: + return 0 + then = globalClock.getFrameTime() + elapsedTime + elapsed = then - self.pathStartTime + if not self.sp: + pass + return self.legList.isPointInRange(point, elapsed - self.sp.PATH_COLLISION_BUFFER, elapsed + self.sp.PATH_COLLISION_BUFFER) + + def requestBattle(self, x, y, z, h, p, r): + toonId = self.air.getAvatarIdFromSender() + if self.air.doId2do.get(toonId) == None: + return + if self.pathState == 3: + pass + else: + if self.pathState != 1: + if self.notify.getDebug(): + self.notify.debug('requestBattle() - suit %d not on path' % self.getDoId()) + if self.pathState == 2 or self.pathState == 4: + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + return + else: + if self.legType != SuitLeg.TWalk: + if self.notify.getDebug(): + self.notify.debug('requestBattle() - suit %d not in Bellicose' % self.getDoId()) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + return + self.confrontPos = Point3(x, y, z) + self.confrontHpr = Vec3(h, p, r) + if self.sp.requestBattle(self.zoneId, self, toonId): + if self.notify.getDebug(): + self.notify.debug('Suit %d requesting battle in zone %d' % (self.getDoId(), self.zoneId)) + else: + if self.notify.getDebug(): + self.notify.debug('requestBattle from suit %d - denied by battle manager' % self.getDoId()) + self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName())) + self.d_denyBattle(toonId) + return + + def getConfrontPosHpr(self): + return ( + self.confrontPos, self.confrontHpr) + + def flyAwayNow(self): + self.b_setPathState(2) + self.stopPathNow() + name = self.taskName('flyAwayNow') + taskMgr.remove(name) + taskMgr.doMethodLater(SuitTimings.toSky, self.finishFlyAwayNow, name) + + def danceNowFlyAwayLater(self): + self.b_setPathState(4) + self.stopPathNow() + name = self.taskName('danceNowFlyAwayLater') + taskMgr.remove(name) + taskMgr.doMethodLater(SuitTimings.victoryDance + SuitTimings.toSky, self.finishFlyAwayNow, name) + + def finishFlyAwayNow(self, task): + self.notify.debug('Suit %s finishFlyAwayNow' % self.doId) + self.requestRemoval() + return Task.done + + def d_setSPDoId(self, doId): + self.sendUpdate('setSPDoId', [doId]) + + def getSPDoId(self): + if self.sp: + return self.sp.getDoId() + else: + return 0 + + def releaseControl(self): + self.b_setPathState(0) + + def b_setPathEndpoints(self, start, end, minPathLen, maxPathLen): + self.setPathEndpoints(start, end, minPathLen, maxPathLen) + self.d_setPathEndpoints(start, end, minPathLen, maxPathLen) + + def d_setPathEndpoints(self, start, end, minPathLen, maxPathLen): + self.sendUpdate('setPathEndpoints', [start, end, minPathLen, maxPathLen]) + + def setPathEndpoints(self, start, end, minPathLen, maxPathLen): + self.pathEndpointStart = start + self.pathEndpointEnd = end + self.minPathLen = minPathLen + self.maxPathLen = maxPathLen + + def getPathEndpoints(self): + return ( + self.pathEndpointStart, self.pathEndpointEnd, self.minPathLen, self.maxPathLen) + + def b_setPathPosition(self, index, timestamp): + self.setPathPosition(index, timestamp) + self.d_setPathPosition(index, timestamp) + + def d_setPathPosition(self, index, timestamp): + self.notify.debug('Suit %d reaches point %d at time %0.2f' % (self.getDoId(), index, timestamp)) + self.sendUpdate('setPathPosition', [index, globalClockDelta.localToNetworkTime(timestamp)]) + + def setPathPosition(self, index, timestamp): + self.pathPositionIndex = index + self.pathPositionTimestamp = timestamp + + def getPathPosition(self): + return ( + self.pathPositionIndex, globalClockDelta.localToNetworkTime(self.pathPositionTimestamp)) + + def b_setPathState(self, state): + self.setPathState(state) + self.d_setPathState(state) + + def d_setPathState(self, state): + self.sendUpdate('setPathState', [state]) + + def setPathState(self, state): + if self.pathState != state: + self.pathState = state + if state == 0: + self.stopPathNow() + elif state == 1: + self.moveToNextLeg(None) + elif state == 2: + self.stopPathNow() + elif state == 3: + pass + elif state == 4: + self.stopPathNow() + else: + self.notify.error('Invalid state: ' + str(state)) + return + + def getPathState(self): + return self.pathState + + def d_debugSuitPosition(self, elapsed, currentLeg, x, y, timestamp): + timestamp = globalClockDelta.localToNetworkTime(timestamp) + self.sendUpdate('debugSuitPosition', [ + elapsed, currentLeg, x, y, timestamp]) + + def initializePath(self): + self.makeLegList() + if self.notify.getDebug(): + self.notify.debug('Leg list:') + print self.legList + idx1 = self.startPoint.getIndex() + idx2 = self.endPoint.getIndex() + self.pathStartTime = globalClock.getFrameTime() + self.setPathEndpoints(idx1, idx2, self.minPathLen, self.maxPathLen) + self.setPathPosition(0, self.pathStartTime) + self.pathState = 1 + self.currentLeg = 0 + self.zoneId = ZoneUtil.getTrueZoneId(self.legList.getZoneId(0), self.branchId) + self.legType = self.legList.getType(0) + if self.notify.getDebug(): + self.notify.debug('creating suit in zone %d' % self.zoneId) + + def resync(self): + self.b_setPathPosition(self.currentLeg, self.pathStartTime + self.legList.getStartTime(self.currentLeg)) + + def moveToNextLeg(self, task): + now = globalClock.getFrameTime() + elapsed = now - self.pathStartTime + nextLeg = self.legList.getLegIndexAtTime(elapsed, self.currentLeg) + numLegs = self.legList.getNumLegs() + if self.currentLeg != nextLeg: + self.currentLeg = nextLeg + self.__beginLegType(self.legList.getType(nextLeg)) + zoneId = self.legList.getZoneId(nextLeg) + zoneId = ZoneUtil.getTrueZoneId(zoneId, self.branchId) + self.__enterZone(zoneId) + self.notify.debug('Suit %d reached leg %d of %d in zone %d.' % (self.getDoId(), nextLeg, numLegs - 1, self.zoneId)) + if self.DEBUG_SUIT_POSITIONS: + leg = self.legList.getLeg(nextLeg) + pos = leg.getPosAtTime(elapsed - leg.getStartTime()) + self.d_debugSuitPosition(elapsed, nextLeg, pos[0], pos[1], now) + if now - self.pathPositionTimestamp > self.UPDATE_TIMESTAMP_INTERVAL: + self.resync() + if self.pathState != 1: + return Task.done + nextLeg += 1 + while nextLeg + 1 < numLegs and self.legList.getZoneId(nextLeg) == ZoneUtil.getCanonicalZoneId(self.zoneId) and self.legList.getType(nextLeg) == self.legType: + nextLeg += 1 + + if nextLeg < numLegs: + nextTime = self.legList.getStartTime(nextLeg) + delay = nextTime - elapsed + taskMgr.remove(self.taskName('move')) + taskMgr.doMethodLater(delay, self.moveToNextLeg, self.taskName('move')) + else: + if self.attemptingTakeover: + self.startTakeOver() + self.requestRemoval() + return Task.done + + def stopPathNow(self): + taskMgr.remove(self.taskName('move')) + + def __enterZone(self, zoneId): + if zoneId != self.zoneId: + self.sp.zoneChange(self, self.zoneId, zoneId) + self.air.sendSetZone(self, zoneId) + self.zoneId = zoneId + if self.pathState == 1: + self.sp.checkForBattle(zoneId, self) + + def __beginLegType(self, legType): + self.legType = legType + if legType == SuitLeg.TWalkFromStreet: + self.checkBuildingState() + else: + if legType == SuitLeg.TToToonBuilding: + self.openToonDoor() + else: + if legType == SuitLeg.TToSuitBuilding: + self.openSuitDoor() + else: + if legType == SuitLeg.TToCoghq: + self.openCogHQDoor(1) + else: + if legType == SuitLeg.TFromCoghq: + self.openCogHQDoor(0) + + def resume(self): + self.notify.debug('Suit %s resume' % self.doId) + if self.currHP <= 0: + self.notify.debug('Suit %s dead after resume' % self.doId) + self.requestRemoval() + else: + self.danceNowFlyAwayLater() + + def prepareToJoinBattle(self): + self.b_setPathState(0) + + def interruptMove(self): + SuitBase.SuitBase.interruptMove(self) + + def checkBuildingState(self): + blockNumber = self.buildingDestination + if blockNumber == None: + return + building = self.sp.buildingMgr.getBuilding(blockNumber) + if self.attemptingTakeover: + if not building.isToonBlock(): + self.flyAwayNow() + return + if not hasattr(building, 'door'): + self.flyAwayNow() + return + building.door.setDoorLock(FADoorCodes.SUIT_APPROACHING) + else: + if not building.isSuitBlock(): + self.flyAwayNow() + return + + def openToonDoor(self): + blockNumber = self.buildingDestination + building = self.sp.buildingMgr.getBuilding(blockNumber) + if not building.isToonBlock(): + self.flyAwayNow() + return + if not hasattr(building, 'door'): + self.flyAwayNow() + return + building.door.requestSuitEnter(self.getDoId()) + + def openSuitDoor(self): + blockNumber = self.buildingDestination + building = self.sp.buildingMgr.getBuilding(blockNumber) + if not building.isSuitBlock(): + self.flyAwayNow() + return + + def openCogHQDoor(self, enter): + blockNumber = self.legList.getBlockNumber(self.currentLeg) + try: + door = self.sp.cogHQDoors[blockNumber] + except: + self.notify.error('No CogHQ door %s in zone %s' % (blockNumber, self.sp.zoneId)) + return + + if enter: + door.requestSuitEnter(self.getDoId()) + else: + door.requestSuitExit(self.getDoId()) + + def startTakeOver(self): + if not self.SUIT_BUILDINGS: + return + blockNumber = self.buildingDestination + if not self.sp.buildingMgr.isSuitBlock(blockNumber): + self.notify.debug('Suit %d taking over building %d in %d' % (self.getDoId(), blockNumber, self.zoneId)) + difficulty = self.getActualLevel() - 1 + dept = SuitDNA.getSuitDept(self.dna.name) + if self.buildingDestinationIsCogdo: + self.sp.cogdoTakeOver(blockNumber, dept, difficulty, self.buildingHeight) + else: + self.sp.suitTakeOver(blockNumber, dept, difficulty, self.buildingHeight) diff --git a/toontown/suit/DistributedSuitBase.py b/toontown/suit/DistributedSuitBase.py new file mode 100644 index 0000000..73940d1 --- /dev/null +++ b/toontown/suit/DistributedSuitBase.py @@ -0,0 +1,451 @@ +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.directtools.DirectGeometry import CLAMP +from direct.controls.ControlManager import CollisionHandlerRayStart +from direct.task import Task +from otp.otpbase import OTPGlobals +from otp.avatar import DistributedAvatar +import Suit +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import TTLocalizer +from toontown.battle import DistributedBattle +from direct.fsm import ClassicFSM +from direct.fsm import State +import SuitTimings +import SuitBase +import DistributedSuitPlanner +import SuitDNA +from direct.directnotify import DirectNotifyGlobal +import SuitDialog +from toontown.battle import BattleProps +import math +import copy + +class DistributedSuitBase(DistributedAvatar.DistributedAvatar, Suit.Suit, SuitBase.SuitBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitBase') + + def __init__(self, cr): + try: + self.DistributedSuitBase_initialized + return + except: + self.DistributedSuitBase_initialized = 1 + + DistributedAvatar.DistributedAvatar.__init__(self, cr) + Suit.Suit.__init__(self) + SuitBase.SuitBase.__init__(self) + self.activeShadow = 0 + self.virtual = 0 + self.battleDetectName = None + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.cRayBitMask = None + self.lifter = None + self.cTrav = None + self.sp = None + self.fsm = None + self.prop = None + self.propInSound = None + self.propOutSound = None + self.reparentTo(hidden) + self.loop('neutral') + self.skeleRevives = 0 + self.maxSkeleRevives = 0 + self.sillySurgeText = False + self.interactivePropTrackBonus = -1 + return + + def setVirtual(self, virtual): + pass + + def getVirtual(self): + return 0 + + def setSkeleRevives(self, num): + if num == None: + num = 0 + self.skeleRevives = num + if num > self.maxSkeleRevives: + self.maxSkeleRevives = num + if self.getSkeleRevives() > 0: + nameInfo = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': '%s%s' % (self.getActualLevel(), TTLocalizer.SkeleRevivePostFix)} + self.setDisplayName(nameInfo) + else: + nameInfo = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': self.getActualLevel()} + self.setDisplayName(nameInfo) + return + + def getSkeleRevives(self): + return self.skeleRevives + + def getMaxSkeleRevives(self): + return self.maxSkeleRevives + + def generate(self): + DistributedAvatar.DistributedAvatar.generate(self) + + def disable(self): + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.ignoreAll() + self.__removeCollisionData() + self.cleanupLoseActor() + self.stop() + taskMgr.remove(self.uniqueName('blink-task')) + DistributedAvatar.DistributedAvatar.disable(self) + + def delete(self): + try: + self.DistributedSuitBase_deleted + except: + self.DistributedSuitBase_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.dna + del self.sp + DistributedAvatar.DistributedAvatar.delete(self) + Suit.Suit.delete(self) + SuitBase.SuitBase.delete(self) + + def setDNAString(self, dnaString): + Suit.Suit.setDNAString(self, dnaString) + + def setDNA(self, dna): + Suit.Suit.setDNA(self, dna) + + def getHP(self): + return self.currHP + + def setHP(self, hp): + if hp > self.maxHP: + self.currHP = self.maxHP + else: + self.currHP = hp + return None + + def getDialogueArray(self, *args): + return Suit.Suit.getDialogueArray(self, *args) + + def __removeCollisionData(self): + self.enableRaycast(0) + self.cRay = None + self.cRayNode = None + self.cRayNodePath = None + self.lifter = None + self.cTrav = None + return + + def setHeight(self, height): + Suit.Suit.setHeight(self, height) + + def getRadius(self): + return Suit.Suit.getRadius(self) + + def setLevelDist(self, level): + if self.notify.getDebug(): + self.notify.debug('Got level %d from server for suit %d' % (level, self.getDoId())) + self.setLevel(level) + + def attachPropeller(self): + if self.prop == None: + self.prop = BattleProps.globalPropPool.getProp('propeller') + if self.propInSound == None: + self.propInSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_in.mp3') + if self.propOutSound == None: + self.propOutSound = base.loadSfx('phase_5/audio/sfx/ENC_propeller_out.mp3') + if base.config.GetBool('want-new-cogs', 0): + head = self.find('**/to_head') + if head.isEmpty(): + head = self.find('**/joint_head') + else: + head = self.find('**/joint_head') + self.prop.reparentTo(head) + return + + def detachPropeller(self): + if self.prop: + self.prop.removeNode() + self.prop = None + if self.propInSound: + self.propInSound = None + if self.propOutSound: + self.propOutSound = None + return + + def beginSupaFlyMove(self, pos, moveIn, trackName): + skyPos = Point3(pos) + if moveIn: + skyPos.setZ(pos.getZ() + SuitTimings.fromSky * ToontownGlobals.SuitWalkSpeed) + else: + skyPos.setZ(pos.getZ() + SuitTimings.toSky * ToontownGlobals.SuitWalkSpeed) + groundF = 28 + dur = self.getDuration('landing') + fr = self.getFrameRate('landing') + animTimeInAir = groundF / fr + impactLength = dur - animTimeInAir + timeTillLanding = SuitTimings.fromSky - impactLength + waitTime = timeTillLanding - animTimeInAir + if self.prop == None: + self.prop = BattleProps.globalPropPool.getProp('propeller') + propDur = self.prop.getDuration('propeller') + lastSpinFrame = 8 + fr = self.prop.getFrameRate('propeller') + spinTime = lastSpinFrame / fr + openTime = (lastSpinFrame + 1) / fr + if moveIn: + lerpPosTrack = Sequence(self.posInterval(timeTillLanding, pos, startPos=skyPos), Wait(impactLength)) + shadowScale = self.dropShadow.getScale() + shadowTrack = Sequence(Func(self.dropShadow.reparentTo, render), Func(self.dropShadow.setPos, pos), self.dropShadow.scaleInterval(timeTillLanding, self.scale, startScale=Vec3(0.01, 0.01, 1.0)), Func(self.dropShadow.reparentTo, self.getShadowJoint()), Func(self.dropShadow.setPos, 0, 0, 0), Func(self.dropShadow.setScale, shadowScale)) + fadeInTrack = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(1, colorScale=VBase4(1, 1, 1, 1), startColorScale=VBase4(1, 1, 1, 0)), Func(self.clearColorScale), Func(self.clearTransparency)) + animTrack = Sequence(Func(self.pose, 'landing', 0), Wait(waitTime), ActorInterval(self, 'landing', duration=dur), Func(self.loop, 'walk')) + self.attachPropeller() + propTrack = Parallel(SoundInterval(self.propInSound, duration=waitTime + dur, node=self), Sequence(ActorInterval(self.prop, 'propeller', constrainedLoop=1, duration=waitTime + spinTime, startTime=0.0, endTime=spinTime), ActorInterval(self.prop, 'propeller', duration=propDur - openTime, startTime=openTime), Func(self.detachPropeller))) + return Parallel(lerpPosTrack, shadowTrack, fadeInTrack, animTrack, propTrack, name=self.taskName('trackName')) + else: + lerpPosTrack = Sequence(Wait(impactLength), LerpPosInterval(self, timeTillLanding, skyPos, startPos=pos)) + shadowTrack = Sequence(Func(self.dropShadow.reparentTo, render), Func(self.dropShadow.setPos, pos), self.dropShadow.scaleInterval(timeTillLanding, Vec3(0.01, 0.01, 1.0), startScale=self.scale), Func(self.dropShadow.reparentTo, self.getShadowJoint()), Func(self.dropShadow.setPos, 0, 0, 0)) + fadeOutTrack = Sequence(Func(self.setTransparency, 1), self.colorScaleInterval(1, colorScale=VBase4(1, 1, 1, 0), startColorScale=VBase4(1, 1, 1, 1)), Func(self.clearColorScale), Func(self.clearTransparency), Func(self.reparentTo, hidden)) + actInt = ActorInterval(self, 'landing', loop=0, startTime=dur, endTime=0.0) + self.attachPropeller() + self.prop.hide() + propTrack = Parallel(SoundInterval(self.propOutSound, duration=waitTime + dur, node=self), Sequence(Func(self.prop.show), ActorInterval(self.prop, 'propeller', endTime=openTime, startTime=propDur), ActorInterval(self.prop, 'propeller', constrainedLoop=1, duration=propDur - openTime, startTime=spinTime, endTime=0.0), Func(self.detachPropeller))) + return Parallel(ParallelEndTogether(lerpPosTrack, shadowTrack, fadeOutTrack), actInt, propTrack, name=self.taskName('trackName')) + return + + def enableBattleDetect(self, name, handler): + if self.collTube: + self.battleDetectName = self.taskName(name) + self.collNode = CollisionNode(self.battleDetectName) + self.collNode.addSolid(self.collTube) + self.collNodePath = self.attachNewNode(self.collNode) + self.collNode.setCollideMask(ToontownGlobals.WallBitmask) + self.accept('enter' + self.battleDetectName, handler) + return Task.done + + def disableBattleDetect(self): + if self.battleDetectName: + self.ignore('enter' + self.battleDetectName) + self.battleDetectName = None + if self.collNodePath: + self.collNodePath.removeNode() + self.collNodePath = None + return + + def enableRaycast(self, enable = 1): + if not self.cTrav or not hasattr(self, 'cRayNode') or not self.cRayNode: + return + self.cTrav.removeCollider(self.cRayNodePath) + if enable: + if self.notify.getDebug(): + self.notify.debug('enabling raycast') + self.cTrav.addCollider(self.cRayNodePath, self.lifter) + elif self.notify.getDebug(): + self.notify.debug('disabling raycast') + + def b_setBrushOff(self, index): + self.setBrushOff(index) + self.d_setBrushOff(index) + + def d_setBrushOff(self, index): + self.sendUpdate('setBrushOff', [index]) + + def setBrushOff(self, index): + self.setChatAbsolute(SuitDialog.getBrushOffText(self.getStyleName(), index), CFSpeech | CFTimeout) + + def initializeBodyCollisions(self, collIdStr): + DistributedAvatar.DistributedAvatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + self.cRay = CollisionRay(0.0, 0.0, CollisionHandlerRayStart, 0.0, 0.0, -1.0) + self.cRayNode = CollisionNode(self.taskName('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(6.0) + self.lifter.setMaxVelocity(8.0) + self.lifter.addCollider(self.cRayNodePath, self) + self.cTrav = base.cTrav + + def disableBodyCollisions(self): + self.disableBattleDetect() + self.enableRaycast(0) + if self.cRayNodePath: + self.cRayNodePath.removeNode() + del self.cRayNode + del self.cRay + del self.lifter + + def denyBattle(self): + self.notify.debug('denyBattle()') + place = self.cr.playGame.getPlace() + if place.fsm.getCurrentState().getName() == 'WaitForBattle': + place.setState('walk') + self.resumePath(self.pathState) + + def makePathTrack(self, nodePath, posPoints, velocity, name): + track = Sequence(name=name) + restOfPosPoints = posPoints[1:] + for pointIndex in range(len(posPoints) - 1): + startPoint = posPoints[pointIndex] + endPoint = posPoints[pointIndex + 1] + track.append(Func(nodePath.headsUp, endPoint[0], endPoint[1], endPoint[2])) + distance = Vec3(endPoint - startPoint).length() + duration = distance / velocity + track.append(LerpPosInterval(nodePath, duration=duration, pos=Point3(endPoint), startPos=Point3(startPoint))) + + return track + + def setState(self, state): + if self.fsm == None: + return 0 + if self.fsm.getCurrentState().getName() == state: + return 0 + return self.fsm.request(state) + + def subclassManagesParent(self): + return 0 + + def enterOff(self, *args): + self.hideNametag3d() + self.hideNametag2d() + if not self.subclassManagesParent(): + self.setParent(ToontownGlobals.SPHidden) + + def exitOff(self): + if not self.subclassManagesParent(): + self.setParent(ToontownGlobals.SPRender) + self.showNametag3d() + self.showNametag2d() + self.loop('neutral', 0) + + def enterBattle(self): + self.loop('neutral', 0) + self.disableBattleDetect() + self.corpMedallion.hide() + self.healthBar.show() + if self.currHP < self.maxHP: + self.updateHealthBar(0, 1) + + def exitBattle(self): + self.healthBar.hide() + self.corpMedallion.show() + self.currHP = self.maxHP + self.interactivePropTrackBonus = -1 + + def enterWaitForBattle(self): + self.loop('neutral', 0) + + def exitWaitForBattle(self): + pass + + def setSkelecog(self, flag): + SuitBase.SuitBase.setSkelecog(self, flag) + if flag: + Suit.Suit.makeSkeleton(self) + + def showHpText(self, number, bonus = 0, scale = 1, attackTrack = -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)) + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLY_SURGE_HOLIDAY): + self.sillySurgeText = True + absNum = abs(number) + if absNum > 0 and absNum <= 10: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[1]) + elif absNum > 10 and absNum <= 20: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[2]) + elif absNum > 20 and absNum <= 30: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[3]) + elif absNum > 30 and absNum <= 40: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[4]) + elif absNum > 40 and absNum <= 50: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[5]) + elif absNum > 50 and absNum <= 60: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[6]) + elif absNum > 60 and absNum <= 70: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[7]) + elif absNum > 70 and absNum <= 80: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[8]) + elif absNum > 80 and absNum <= 90: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[9]) + elif absNum > 90 and absNum <= 100: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[10]) + elif absNum > 100 and absNum <= 110: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[11]) + else: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.SillySurgeTerms[12]) + if self.interactivePropTrackBonus > -1 and self.interactivePropTrackBonus == attackTrack: + self.sillySurgeText = True + if attackTrack in TTLocalizer.InteractivePropTrackBonusTerms: + self.HpTextGenerator.setText(str(number) + '\n' + TTLocalizer.InteractivePropTrackBonusTerms[attackTrack]) + 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 + if self.interactivePropTrackBonus > -1 and self.interactivePropTrackBonus == attackTrack: + r = 0 + g = 0 + b = 1 + 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) + if self.sillySurgeText: + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 99) + 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 hideHpTextTask(self, task): + self.hideHpText() + if self.sillySurgeText: + self.nametag3d.clearDepthTest() + self.nametag3d.clearBin() + self.sillySurgeText = False + return Task.done + + def getAvIdName(self): + try: + level = self.getActualLevel() + except: + level = '???' + + return '%s\n%s\nLevel %s' % (self.getName(), self.doId, level) diff --git a/toontown/suit/DistributedSuitBaseAI.py b/toontown/suit/DistributedSuitBaseAI.py new file mode 100644 index 0000000..03719c2 --- /dev/null +++ b/toontown/suit/DistributedSuitBaseAI.py @@ -0,0 +1,181 @@ +from otp.ai.AIBaseGlobal import * +from otp.avatar import DistributedAvatarAI +import SuitPlannerBase, SuitBase, SuitDNA +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals + +class DistributedSuitBaseAI(DistributedAvatarAI.DistributedAvatarAI, SuitBase.SuitBase): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitBaseAI') + + def __init__(self, air, suitPlanner): + DistributedAvatarAI.DistributedAvatarAI.__init__(self, air) + SuitBase.SuitBase.__init__(self) + self.sp = suitPlanner + self.maxHP = 10 + self.currHP = 10 + self.zoneId = 0 + self.dna = None + self.virtual = 0 + self.skeleRevives = 0 + self.maxSkeleRevives = 0 + self.reviveFlag = 0 + self.buildingHeight = None + return + + def generate(self): + DistributedAvatarAI.DistributedAvatarAI.generate(self) + + def delete(self): + self.sp = None + del self.dna + DistributedAvatarAI.DistributedAvatarAI.delete(self) + return + + def requestRemoval(self): + if self.sp != None: + self.sp.removeSuit(self) + else: + self.requestDelete() + return + + def setLevel(self, lvl=None): + attributes = SuitBattleGlobals.SuitAttributes[self.dna.name] + if lvl: + self.level = lvl - attributes['level'] - 1 + else: + self.level = SuitBattleGlobals.pickFromFreqList(attributes['freq']) + self.notify.debug('Assigning level ' + str(lvl)) + if hasattr(self, 'doId'): + self.d_setLevelDist(self.level) + hp = attributes['hp'][self.level] + self.maxHP = hp + self.currHP = hp + + def getLevelDist(self): + return self.getLevel() + + def d_setLevelDist(self, level): + self.sendUpdate('setLevelDist', [level]) + + def setupSuitDNA(self, level, type, track): + dna = SuitDNA.SuitDNA() + dna.newSuitRandom(type, track) + self.dna = dna + self.track = track + self.setLevel(level) + return None + + def getDNAString(self): + if self.dna: + return self.dna.makeNetString() + else: + self.notify.debug('No dna has been created for suit %d!' % self.getDoId()) + return '' + + def b_setBrushOff(self, index): + self.setBrushOff(index) + self.d_setBrushOff(index) + return None + + def d_setBrushOff(self, index): + self.sendUpdate('setBrushOff', [index]) + + def setBrushOff(self, index): + pass + + def d_denyBattle(self, toonId): + self.sendUpdateToAvatarId(toonId, 'denyBattle', []) + + def b_setSkeleRevives(self, num): + if num == None: + num = 0 + self.setSkeleRevives(num) + self.d_setSkeleRevives(self.getSkeleRevives()) + return + + def d_setSkeleRevives(self, num): + self.sendUpdate('setSkeleRevives', [num]) + + def getSkeleRevives(self): + return self.skeleRevives + + def setSkeleRevives(self, num): + if num == None: + num = 0 + self.skeleRevives = num + if num > self.maxSkeleRevives: + self.maxSkeleRevives = num + return + + def getMaxSkeleRevives(self): + return self.maxSkeleRevives + + def useSkeleRevive(self): + self.skeleRevives -= 1 + self.currHP = self.maxHP + self.reviveFlag = 1 + + def reviveCheckAndClear(self): + returnValue = 0 + if self.reviveFlag == 1: + returnValue = 1 + self.reviveFlag = 0 + return returnValue + + def getHP(self): + return self.currHP + + def setHP(self, hp): + if hp > self.maxHP: + self.currHP = self.maxHP + else: + self.currHP = hp + return None + + def b_setHP(self, hp): + self.setHP(hp) + self.d_setHP(hp) + + def d_setHP(self, hp): + self.sendUpdate('setHP', [hp]) + + def releaseControl(self): + return None + + def getDeathEvent(self): + return 'cogDead-%s' % self.doId + + def resume(self): + self.notify.debug('resume, hp=%s' % self.currHP) + if self.currHP <= 0: + messenger.send(self.getDeathEvent()) + self.requestRemoval() + return None + + def prepareToJoinBattle(self): + pass + + def b_setSkelecog(self, flag): + self.setSkelecog(flag) + self.d_setSkelecog(flag) + + def setSkelecog(self, flag): + SuitBase.SuitBase.setSkelecog(self, flag) + + def d_setSkelecog(self, flag): + self.sendUpdate('setSkelecog', [flag]) + + def isForeman(self): + return 0 + + def isSupervisor(self): + return 0 + + def setVirtual(self, virtual): + pass + + def getVirtual(self): + return 0 + + def isVirtual(self): + return self.getVirtual() diff --git a/toontown/suit/DistributedSuitPlanner.py b/toontown/suit/DistributedSuitPlanner.py new file mode 100644 index 0000000..209fa34 --- /dev/null +++ b/toontown/suit/DistributedSuitPlanner.py @@ -0,0 +1,126 @@ +from pandac.PandaModules import * +from direct.distributed import DistributedObject +import SuitPlannerBase +from toontown.toonbase import ToontownGlobals + +class DistributedSuitPlanner(DistributedObject.DistributedObject, SuitPlannerBase.SuitPlannerBase): + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + SuitPlannerBase.SuitPlannerBase.__init__(self) + self.suitList = [] + self.buildingList = [0, + 0, + 0, + 0] + self.pathViz = None + return + + def generate(self): + self.notify.info('DistributedSuitPlanner %d: generating' % self.getDoId()) + DistributedObject.DistributedObject.generate(self) + base.cr.currSuitPlanner = self + + def disable(self): + self.notify.info('DistributedSuitPlanner %d: disabling' % self.getDoId()) + self.hidePaths() + DistributedObject.DistributedObject.disable(self) + base.cr.currSuitPlanner = None + return + + def d_suitListQuery(self): + self.sendUpdate('suitListQuery') + + def suitListResponse(self, suitList): + self.suitList = suitList + messenger.send('suitListResponse') + + def d_buildingListQuery(self): + self.sendUpdate('buildingListQuery') + + def buildingListResponse(self, buildingList): + self.buildingList = buildingList + messenger.send('buildingListResponse') + + def hidePaths(self): + if self.pathViz: + self.pathViz.detachNode() + self.pathViz = None + return + + def showPaths(self): + self.hidePaths() + vizNode = GeomNode(self.uniqueName('PathViz')) + lines = LineSegs() + self.pathViz = render.attachNewNode(vizNode) + points = self.frontdoorPointList + self.sidedoorPointList + self.cogHQDoorPointList + self.streetPointList + while len(points) > 0: + self.__doShowPoints(vizNode, lines, None, points) + + cnode = CollisionNode('battleCells') + cnode.setCollideMask(BitMask32.allOff()) + for zoneId, cellPos in self.battlePosDict.items(): + cnode.addSolid(CollisionSphere(cellPos, 9)) + text = '%s' % zoneId + self.__makePathVizText(text, cellPos[0], cellPos[1], cellPos[2] + 9, (1, 1, 1, 1)) + + self.pathViz.attachNewNode(cnode).show() + return + + def __doShowPoints(self, vizNode, lines, p, points): + if p == None: + pi = len(points) - 1 + if pi < 0: + return + p = points[pi] + del points[pi] + else: + if p not in points: + return + pi = points.index(p) + del points[pi] + text = '%s' % p.getIndex() + pos = p.getPos() + if p.getPointType() == DNASuitPoint.FRONTDOORPOINT: + color = (1, 0, 0, 1) + elif p.getPointType() == DNASuitPoint.SIDEDOORPOINT: + color = (0, 0, 1, 1) + else: + color = (0, 1, 0, 1) + self.__makePathVizText(text, pos[0], pos[1], pos[2], color) + adjacent = self.dnaStore.getAdjacentPoints(p) + numPoints = adjacent.getNumPoints() + for i in range(numPoints): + qi = adjacent.getPointIndex(i) + q = self.dnaStore.getSuitPointWithIndex(qi) + pp = p.getPos() + qp = q.getPos() + v = Vec3(qp - pp) + v.normalize() + c = v.cross(Vec3.up()) + p1a = pp + v * 2 + c * 0.5 + p1b = pp + v * 3 + p1c = pp + v * 2 - c * 0.5 + lines.reset() + lines.moveTo(pp) + lines.drawTo(qp) + lines.moveTo(p1a) + lines.drawTo(p1b) + lines.drawTo(p1c) + lines.create(vizNode, 0) + self.__doShowPoints(vizNode, lines, q, points) + + return + + def __makePathVizText(self, text, x, y, z, color): + if not hasattr(self, 'debugTextNode'): + self.debugTextNode = TextNode('debugTextNode') + self.debugTextNode.setAlign(TextNode.ACenter) + self.debugTextNode.setFont(ToontownGlobals.getSignFont()) + self.debugTextNode.setTextColor(*color) + self.debugTextNode.setText(text) + np = self.pathViz.attachNewNode(self.debugTextNode.generate()) + np.setPos(x, y, z + 1) + np.setScale(1.0) + np.setBillboardPointEye(2) + np.node().setAttrib(TransparencyAttrib.make(TransparencyAttrib.MDual), 2) diff --git a/toontown/suit/DistributedSuitPlannerAI.py b/toontown/suit/DistributedSuitPlannerAI.py new file mode 100644 index 0000000..ae6db8d --- /dev/null +++ b/toontown/suit/DistributedSuitPlannerAI.py @@ -0,0 +1,1181 @@ +from otp.ai.AIBaseGlobal import * +from direct.distributed import DistributedObjectAI +import SuitPlannerBase, DistributedSuitAI +from toontown.battle import BattleManagerAI +from direct.task import Task +from direct.directnotify import DirectNotifyGlobal +import SuitDNA +from toontown.battle import SuitBattleGlobals +import SuitTimings +from toontown.toon import NPCToons +from toontown.building import HQBuildingAI +from toontown.hood import ZoneUtil +from toontown.building import SuitBuildingGlobals +from toontown.building.DistributedBuildingAI import DistributedBuildingAI +from toontown.toonbase import ToontownBattleGlobals +from toontown.toonbase import ToontownGlobals +import math, time, random + +class DistributedSuitPlannerAI(DistributedObjectAI.DistributedObjectAI, SuitPlannerBase.SuitPlannerBase): + CogdoPopFactor = config.GetFloat('cogdo-pop-factor', 1.5) + CogdoRatio = min(1.0, max(0.0, config.GetFloat('cogdo-ratio', 0.5))) + MinimumOfOne = config.GetBool('minimum-of-one-building', 0) + SuitHoodInfo = [ + [ + 2100, 5, 15, 0, 5, 20, 3, (1, 5, 10, 40, 60, 80), (25, 25, 25, 25), (1, 2, 3), []], [2200, 3, 10, 0, 5, 15, 3, (1, 5, 10, 40, 60, 80), (10, 70, 10, 10), (1, 2, 3), []], [2300, 3, 10, 0, 5, 15, 3, (1, 5, 10, 40, 60, 80), (10, 10, 40, 40), (1, 2, 3), []], [1100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (90, 10, 0, 0), (2, 3, 4), []], [1200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 0, 90, 10), (3, 4, 5, 6), []], [1300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (40, 40, 10, 10), (3, 4, 5, 6), []], [3100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (90, 10, 0, 0), (5, 6, 7), []], [3200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (10, 20, 30, 40), (5, 6, 7), []], [3300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (5, 85, 5, 5), (7, 8, 9), []], [4100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 0, 50, 50), (2, 3, 4), []], [4200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 0, 90, 10), (3, 4, 5, 6), []], [4300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (50, 50, 0, 0), (3, 4, 5, 6), []], [5100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 20, 10, 70), (2, 3, 4), []], [5200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (10, 70, 0, 20), (3, 4, 5, 6), []], [5300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (5, 5, 5, 85), (3, 4, 5, 6), []], [9100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (25, 25, 25, 25), (6, 7, 8, 9), []], [9200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (5, 5, 85, 5), (6, 7, 8, 9), []], [11000, 3, 15, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 0, 0, 100), (4, 5, 6), []], [11200, 10, 20, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 0, 0, 100), (4, 5, 6), []], [12000, 10, 20, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 0, 100, 0), (7, 8, 9), []], [13000, 10, 20, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 100, 0, 0), (8, 9, 10), []]] + SUIT_HOOD_INFO_ZONE = 0 + SUIT_HOOD_INFO_MIN = 1 + SUIT_HOOD_INFO_MAX = 2 + SUIT_HOOD_INFO_BMIN = 3 + SUIT_HOOD_INFO_BMAX = 4 + SUIT_HOOD_INFO_BWEIGHT = 5 + SUIT_HOOD_INFO_SMAX = 6 + SUIT_HOOD_INFO_JCHANCE = 7 + SUIT_HOOD_INFO_TRACK = 8 + SUIT_HOOD_INFO_LVL = 9 + SUIT_HOOD_INFO_HEIGHTS = 10 + MAX_SUIT_TYPES = 6 + POP_UPKEEP_DELAY = 10 + POP_ADJUST_DELAY = 300 + PATH_COLLISION_BUFFER = 5 + TOTAL_MAX_SUITS = 50 + MIN_PATH_LEN = 40 + MAX_PATH_LEN = 300 + MIN_TAKEOVER_PATH_LEN = 2 + SUITS_ENTER_BUILDINGS = 1 + SUIT_BUILDING_NUM_SUITS = 1.5 + SUIT_BUILDING_TIMEOUT = [ + None, None, None, None, None, None, 72, 60, 48, 36, 24, 12, 6, 3, 1, 0.5] + TOTAL_SUIT_BUILDING_PCT = 18 * CogdoPopFactor + BUILDING_HEIGHT_DISTRIBUTION = [ + 14, 18, 25, 23, 20] + TOTAL_BWEIGHT = 0 + TOTAL_BWEIGHT_PER_TRACK = [ + 0, 0, 0, 0] + TOTAL_BWEIGHT_PER_HEIGHT = [ + 0, 0, 0, 0, 0] + for currHoodInfo in SuitHoodInfo: + weight = currHoodInfo[SUIT_HOOD_INFO_BWEIGHT] + tracks = currHoodInfo[SUIT_HOOD_INFO_TRACK] + levels = currHoodInfo[SUIT_HOOD_INFO_LVL] + heights = [ + 0, 0, 0, 0, 0] + for level in levels: + minFloors, maxFloors = SuitBuildingGlobals.SuitBuildingInfo[level - 1][0] + for i in range(minFloors - 1, maxFloors): + heights[i] += 1 + + currHoodInfo[SUIT_HOOD_INFO_HEIGHTS] = heights + TOTAL_BWEIGHT += weight + TOTAL_BWEIGHT_PER_TRACK[0] += weight * tracks[0] + TOTAL_BWEIGHT_PER_TRACK[1] += weight * tracks[1] + TOTAL_BWEIGHT_PER_TRACK[2] += weight * tracks[2] + TOTAL_BWEIGHT_PER_TRACK[3] += weight * tracks[3] + TOTAL_BWEIGHT_PER_HEIGHT[0] += weight * heights[0] + TOTAL_BWEIGHT_PER_HEIGHT[1] += weight * heights[1] + TOTAL_BWEIGHT_PER_HEIGHT[2] += weight * heights[2] + TOTAL_BWEIGHT_PER_HEIGHT[3] += weight * heights[3] + TOTAL_BWEIGHT_PER_HEIGHT[4] += weight * heights[4] + + defaultSuitName = simbase.config.GetString('suit-type', 'random') + if defaultSuitName == 'random': + defaultSuitName = None + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitPlannerAI') + + def __init__(self, air, zoneId): + DistributedObjectAI.DistributedObjectAI.__init__(self, air) + SuitPlannerBase.SuitPlannerBase.__init__(self) + self.air = air + self.zoneId = zoneId + self.canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + if simbase.air.wantCogdominiums: + if not hasattr(self.__class__, 'CogdoPopAdjusted'): + self.__class__.CogdoPopAdjusted = True + for index in xrange(len(self.SuitHoodInfo)): + hoodInfo = self.SuitHoodInfo[index] + hoodInfo[self.SUIT_HOOD_INFO_BMIN] = int(0.5 + self.CogdoPopFactor * hoodInfo[self.SUIT_HOOD_INFO_BMIN]) + hoodInfo[self.SUIT_HOOD_INFO_BMAX] = int(0.5 + self.CogdoPopFactor * hoodInfo[self.SUIT_HOOD_INFO_BMAX]) + + self.hoodInfoIdx = -1 + for index in range(len(self.SuitHoodInfo)): + currHoodInfo = self.SuitHoodInfo[index] + if currHoodInfo[self.SUIT_HOOD_INFO_ZONE] == self.canonicalZoneId: + self.hoodInfoIdx = index + + self.currDesired = None + self.baseNumSuits = (self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_MIN] + self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_MAX]) / 2 + self.targetNumCogdos = 0 + if simbase.air.wantCogdominiums: + self.targetNumCogdos = int(0.5 + self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_BMIN] * self.CogdoRatio) + if self.MinimumOfOne: + self.targetNumCogdos = max(self.targetNumCogdos, 1) + self.targetNumSuitBuildings = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_BMIN] + self.targetNumSuitBuildings -= self.targetNumCogdos + if self.MinimumOfOne: + self.targetNumSuitBuildings = max(self.targetNumSuitBuildings, 1) + if ZoneUtil.isWelcomeValley(self.zoneId): + self.targetNumCogdos = 0 + self.targetNumSuitBuildings = 0 + self.pendingBuildingTracks = [] + self.pendingBuildingHeights = [] + self.pendingCogdoHeights = [] + self.suitList = [] + self.numFlyInSuits = 0 + self.numBuildingSuits = 0 + self.numAttemptingTakeover = 0 + self.numAttemptingCogdoTakeover = 0 + self.zoneInfo = {} + self.zoneIdToPointMap = None + self.cogHQDoors = [] + self.battleList = [] + self.battleMgr = BattleManagerAI.BattleManagerAI(self.air) + self.setupDNA() + if self.notify.getDebug(): + self.notify.debug('Creating a building manager AI in zone' + str(self.zoneId)) + self.buildingMgr = self.air.buildingManagers.get(self.zoneId) + if self.buildingMgr: + blocks, hqBlocks, gagshopBlocks, petshopBlocks, kartshopBlocks, animBldgBlocks = self.buildingMgr.getDNABlockLists() + for currBlock in blocks: + bldg = self.buildingMgr.getBuilding(currBlock) + bldg.setSuitPlannerExt(self) + + for currBlock in animBldgBlocks: + bldg = self.buildingMgr.getBuilding(currBlock) + bldg.setSuitPlannerExt(self) + + self.dnaStore.resetBlockNumbers() + self.initBuildingsAndPoints() + numSuits = simbase.config.GetInt('suit-count', -1) + if numSuits >= 0: + self.currDesired = numSuits + suitHood = simbase.config.GetInt('suits-only-in-hood', -1) + if suitHood >= 0: + if self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_ZONE] != suitHood: + self.currDesired = 0 + self.suitCountAdjust = 0 + return + + def cleanup(self): + taskMgr.remove(self.taskName('sptUpkeepPopulation')) + taskMgr.remove(self.taskName('sptAdjustPopulation')) + for suit in self.suitList: + suit.stopTasks() + if suit.isGenerated(): + self.zoneChange(suit, suit.zoneId) + suit.requestDelete() + + self.suitList = [] + self.numFlyInSuits = 0 + self.numBuildingSuits = 0 + self.numAttemptingTakeover = 0 + self.numAttemptingCogdoTakeover = 0 + + def delete(self): + self.cleanup() + DistributedObjectAI.DistributedObjectAI.delete(self) + + def initBuildingsAndPoints(self): + if not self.buildingMgr: + return + if self.notify.getDebug(): + self.notify.debug('Initializing building points') + self.buildingFrontDoors = {} + self.buildingSideDoors = {} + for p in self.frontdoorPointList: + blockNumber = p.getLandmarkBuildingIndex() + if p < 0: + self.notify.warning('No landmark building for (%s) in zone %d' % (repr(p), self.zoneId)) + elif self.buildingFrontDoors.has_key(blockNumber): + self.notify.warning('Multiple front doors for building %d in zone %d' % (blockNumber, self.zoneId)) + else: + self.buildingFrontDoors[blockNumber] = p + + for p in self.sidedoorPointList: + blockNumber = p.getLandmarkBuildingIndex() + if p < 0: + self.notify.warning('No landmark building for (%s) in zone %d' % (repr(p), self.zoneId)) + elif self.buildingSideDoors.has_key(blockNumber): + self.buildingSideDoors[blockNumber].append(p) + else: + self.buildingSideDoors[blockNumber] = [ + p] + + for bldg in self.buildingMgr.getBuildings(): + if isinstance(bldg, HQBuildingAI.HQBuildingAI): + continue + blockNumber = bldg.getBlock()[0] + if not self.buildingFrontDoors.has_key(blockNumber): + self.notify.warning('No front door for building %d in zone %d' % (blockNumber, self.zoneId)) + if not self.buildingSideDoors.has_key(blockNumber): + self.notify.warning('No side door for building %d in zone %d' % (blockNumber, self.zoneId)) + + def countNumSuitsPerTrack(self, count): + for suit in self.suitList: + if count.has_key(suit.track): + count[suit.track] += 1 + else: + count[suit.track] = 1 + + def countNumBuildingsPerTrack(self, count): + if self.buildingMgr: + for building in self.buildingMgr.getBuildings(): + if building.isSuitBuilding(): + if count.has_key(building.track): + count[building.track] += 1 + else: + count[building.track] = 1 + + def countNumBuildingsPerHeight(self, count): + if self.buildingMgr: + for building in self.buildingMgr.getBuildings(): + if building.isSuitBuilding(): + height = building.numFloors - 1 + if count.has_key(height): + count[height] += 1 + else: + count[height] = 1 + + def formatNumSuitsPerTrack(self, count): + result = ' ' + for track, num in count.items(): + result += ' %s:%d' % (track, num) + + return result[2:] + + def calcDesiredNumFlyInSuits(self): + if self.currDesired != None: + return 0 + return self.baseNumSuits + self.suitCountAdjust + + def calcDesiredNumBuildingSuits(self): + if self.currDesired != None: + return self.currDesired + if not self.buildingMgr: + return 0 + suitBuildings = self.buildingMgr.getEstablishedSuitBlocks() + return int(len(suitBuildings) * self.SUIT_BUILDING_NUM_SUITS) + + def getZoneIdToPointMap(self): + if self.zoneIdToPointMap != None: + return self.zoneIdToPointMap + self.zoneIdToPointMap = {} + for point in self.streetPointList: + points = self.dnaStore.getAdjacentPoints(point) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + p = self.pointIndexes[pi] + i -= 1 + zoneName = self.dnaStore.getSuitEdgeZone(point.getIndex(), p.getIndex()) + zoneId = int(self.extractGroupName(zoneName)) + if self.zoneIdToPointMap.has_key(zoneId): + self.zoneIdToPointMap[zoneId].append(point) + else: + self.zoneIdToPointMap[zoneId] = [ + point] + + return self.zoneIdToPointMap + + def getStreetPointsForBuilding(self, blockNumber): + pointList = [] + if self.buildingSideDoors.has_key(blockNumber): + for doorPoint in self.buildingSideDoors[blockNumber]: + points = self.dnaStore.getAdjacentPoints(doorPoint) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + point = self.pointIndexes[pi] + if point.getPointType() == DNASuitPoint.STREETPOINT: + pointList.append(point) + i -= 1 + + if self.buildingFrontDoors.has_key(blockNumber): + doorPoint = self.buildingFrontDoors[blockNumber] + points = self.dnaStore.getAdjacentPoints(doorPoint) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + pointList.append(self.pointIndexes[pi]) + i -= 1 + + return pointList + + def createNewSuit(self, blockNumbers, streetPoints, toonBlockTakeover=None, cogdoTakeover=None, minPathLen=None, maxPathLen=None, buildingHeight=None, suitLevel=None, suitType=None, suitTrack=None, suitName=None, skelecog=None, revives=None): + startPoint = None + blockNumber = None + if self.notify.getDebug(): + self.notify.debug('Choosing origin from %d+%d possibles.' % (len(streetPoints), len(blockNumbers))) + while startPoint == None and len(blockNumbers) > 0: + bn = random.choice(blockNumbers) + blockNumbers.remove(bn) + if self.buildingSideDoors.has_key(bn): + for doorPoint in self.buildingSideDoors[bn]: + points = self.dnaStore.getAdjacentPoints(doorPoint) + i = points.getNumPoints() - 1 + while blockNumber == None and i >= 0: + pi = points.getPointIndex(i) + p = self.pointIndexes[pi] + i -= 1 + startTime = SuitTimings.fromSuitBuilding + startTime += self.dnaStore.getSuitEdgeTravelTime(doorPoint.getIndex(), pi, self.suitWalkSpeed) + if not self.pointCollision(p, doorPoint, startTime): + startTime = SuitTimings.fromSuitBuilding + startPoint = doorPoint + blockNumber = bn + + while startPoint == None and len(streetPoints) > 0: + p = random.choice(streetPoints) + streetPoints.remove(p) + if not self.pointCollision(p, None, SuitTimings.fromSky): + startPoint = p + startTime = SuitTimings.fromSky + + if startPoint == None: + return + newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, self) + newSuit.startPoint = startPoint + if blockNumber != None: + newSuit.buildingSuit = 1 + if suitTrack == None: + suitTrack = self.buildingMgr.getBuildingTrack(blockNumber) + else: + newSuit.flyInSuit = 1 + newSuit.attemptingTakeover = self.newSuitShouldAttemptTakeover() + if newSuit.attemptingTakeover: + cogdosNeeded = self.countNumNeededCogdos() + bldgsNeeded = self.countNumNeededBuildings() + cogdosAvailable = cogdosNeeded - self.numAttemptingCogdoTakeover + bldgsAvailable = bldgsNeeded - (self.numAttemptingTakeover - self.numAttemptingCogdoTakeover) + totalAvailable = cogdosAvailable + bldgsAvailable + if cogdoTakeover is None: + cogdoTakeover = False + if simbase.air.wantCogdominiums: + if totalAvailable > 0: + r = random.randrange(totalAvailable) + if r < cogdosAvailable: + cogdoTakeover = True + newSuit.takeoverIsCogdo = cogdoTakeover + if newSuit.takeoverIsCogdo: + pendingTracks = [ + 's'] + pendingHeights = self.pendingCogdoHeights + else: + pendingTracks = self.pendingBuildingTracks + pendingHeights = self.pendingBuildingHeights + if suitTrack == None and len(pendingTracks) > 0: + suitTrack = pendingTracks[0] + del pendingTracks[0] + pendingTracks.append(suitTrack) + if buildingHeight == None and len(pendingHeights) > 0: + buildingHeight = pendingHeights[0] + del pendingHeights[0] + pendingHeights.append(buildingHeight) + else: + if cogdoTakeover and suitTrack == None: + suitTrack = random.choice(['s']) + if suitName == None: + if not cogdoTakeover: + suitName, skelecog = self.air.suitInvasionManager.getInvadingCog() + if suitName == None: + suitName = self.defaultSuitName + if suitType == None and suitName != None: + suitType = SuitDNA.getSuitType(suitName) + suitTrack = SuitDNA.getSuitDept(suitName) + if suitLevel == None and buildingHeight != None: + if not cogdoTakeover: + suitLevel = self.chooseSuitLevel(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL], buildingHeight) + else: + suitLevel = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL][-1] + 1 + suitLevel, suitType, suitTrack = self.pickLevelTypeAndTrack(suitLevel, suitType, suitTrack) + newSuit.setupSuitDNA(suitLevel, suitType, suitTrack) + newSuit.buildingHeight = buildingHeight + gotDestination = self.chooseDestination(newSuit, startTime, toonBlockTakeover=toonBlockTakeover, cogdoTakeover=cogdoTakeover, minPathLen=minPathLen, maxPathLen=maxPathLen) + if not gotDestination: + self.notify.debug("Couldn't get a destination in %d!" % self.zoneId) + newSuit.doNotDeallocateChannel = None + newSuit.delete() + return + newSuit.initializePath() + self.zoneChange(newSuit, None, newSuit.zoneId) + if skelecog: + newSuit.setSkelecog(skelecog) + if revives: + newSuit.setSkeleRevives(revives) + newSuit.generateWithRequired(newSuit.zoneId) + newSuit.moveToNextLeg(None) + self.suitList.append(newSuit) + if newSuit.flyInSuit: + self.numFlyInSuits += 1 + if newSuit.buildingSuit: + self.numBuildingSuits += 1 + if newSuit.attemptingTakeover: + self.numAttemptingTakeover += 1 + if newSuit.takeoverIsCogdo: + self.numAttemptingCogdoTakeover += 1 + return newSuit + + def countNumNeededBuildings(self): + if not self.buildingMgr: + return 0 + numSuitBuildings = len(self.buildingMgr.getSuitBlocks()) - len(self.buildingMgr.getCogdoBlocks()) + numNeeded = self.targetNumSuitBuildings - numSuitBuildings + return numNeeded + + def countNumNeededCogdos(self): + if not self.buildingMgr: + return 0 + numCogdos = len(self.buildingMgr.getCogdoBlocks()) + numNeeded = self.targetNumCogdos - numCogdos + return numNeeded + + def newSuitShouldAttemptTakeover(self): + if not self.SUITS_ENTER_BUILDINGS: + return 0 + numNeeded = self.countNumNeededBuildings() + if simbase.air.wantCogdominiums: + numNeeded += self.countNumNeededCogdos() + if self.numAttemptingTakeover >= numNeeded: + self.pendingBuildingTracks = [] + return 0 + self.notify.debug('DSP %d is planning a takeover attempt in zone %d' % (self.getDoId(), self.zoneId)) + return 1 + + def chooseDestination(self, suit, startTime, toonBlockTakeover=None, cogdoTakeover=None, minPathLen=None, maxPathLen=None): + possibles = [] + backup = [] + if cogdoTakeover is None: + cogdoTakeover = False + if toonBlockTakeover != None: + suit.attemptingTakeover = 1 + blockNumber = toonBlockTakeover + if self.buildingFrontDoors.has_key(blockNumber): + possibles.append((blockNumber, self.buildingFrontDoors[blockNumber])) + elif suit.attemptingTakeover: + for blockNumber in self.buildingMgr.getToonBlocks(): + building = self.buildingMgr.getBuilding(blockNumber) + extZoneId, intZoneId = building.getExteriorAndInteriorZoneId() + if not NPCToons.isZoneProtected(intZoneId): + if self.buildingFrontDoors.has_key(blockNumber): + possibles.append((blockNumber, self.buildingFrontDoors[blockNumber])) + + else: + if self.buildingMgr: + for blockNumber in self.buildingMgr.getSuitBlocks(): + track = self.buildingMgr.getBuildingTrack(blockNumber) + if track == suit.track and self.buildingSideDoors.has_key(blockNumber): + for doorPoint in self.buildingSideDoors[blockNumber]: + possibles.append((blockNumber, doorPoint)) + + backup = [] + for p in self.streetPointList: + backup.append((None, p)) + + if self.notify.getDebug(): + self.notify.debug('Choosing destination point from %d+%d possibles.' % (len(possibles), len(backup))) + if len(possibles) == 0: + possibles = backup + backup = [] + if minPathLen == None: + if suit.attemptingTakeover: + minPathLen = self.MIN_TAKEOVER_PATH_LEN + else: + minPathLen = self.MIN_PATH_LEN + if maxPathLen == None: + maxPathLen = self.MAX_PATH_LEN + retryCount = 0 + while len(possibles) > 0 and retryCount < 50: + p = random.choice(possibles) + possibles.remove(p) + if len(possibles) == 0: + possibles = backup + backup = [] + path = self.genPath(suit.startPoint, p[1], minPathLen, maxPathLen) + if path and not self.pathCollision(path, startTime): + suit.endPoint = p[1] + suit.minPathLen = minPathLen + suit.maxPathLen = maxPathLen + suit.buildingDestination = p[0] + suit.buildingDestinationIsCogdo = cogdoTakeover + suit.setPath(path) + return 1 + retryCount += 1 + + return 0 + + def pathCollision(self, path, elapsedTime): + pathLength = path.getNumPoints() + i = 0 + pi = path.getPointIndex(i) + point = self.pointIndexes[pi] + adjacentPoint = self.pointIndexes[path.getPointIndex(i + 1)] + while point.getPointType() == DNASuitPoint.FRONTDOORPOINT or point.getPointType() == DNASuitPoint.SIDEDOORPOINT: + i += 1 + lastPi = pi + pi = path.getPointIndex(i) + adjacentPoint = point + point = self.pointIndexes[pi] + elapsedTime += self.dnaStore.getSuitEdgeTravelTime(lastPi, pi, self.suitWalkSpeed) + + result = self.pointCollision(point, adjacentPoint, elapsedTime) + return result + + def pointCollision(self, point, adjacentPoint, elapsedTime): + for suit in self.suitList: + if suit.pointInMyPath(point, elapsedTime): + return 1 + + if adjacentPoint != None: + return self.battleCollision(point, adjacentPoint) + else: + points = self.dnaStore.getAdjacentPoints(point) + i = points.getNumPoints() - 1 + while i >= 0: + pi = points.getPointIndex(i) + p = self.pointIndexes[pi] + i -= 1 + if self.battleCollision(point, p): + return 1 + + return 0 + + def battleCollision(self, point, adjacentPoint): + zoneName = self.dnaStore.getSuitEdgeZone(point.getIndex(), adjacentPoint.getIndex()) + zoneId = int(self.extractGroupName(zoneName)) + return self.battleMgr.cellHasBattle(zoneId) + + def removeSuit(self, suit): + self.zoneChange(suit, suit.zoneId) + if self.suitList.count(suit) > 0: + self.suitList.remove(suit) + if suit.flyInSuit: + self.numFlyInSuits -= 1 + if suit.buildingSuit: + self.numBuildingSuits -= 1 + if suit.attemptingTakeover: + self.numAttemptingTakeover -= 1 + if suit.takeoverIsCogdo: + self.numAttemptingCogdoTakeover -= 1 + suit.requestDelete() + + def countTakeovers(self): + count = 0 + for suit in self.suitList: + if suit.attemptingTakeover: + count += 1 + + return count + + def countCogdoTakeovers(self): + count = 0 + for suit in self.suitList: + if suit.attemptingTakeover and suit.takeoverIsCogdo: + count += 1 + + return count + + def __waitForNextUpkeep(self): + t = random.random() * 2.0 + self.POP_UPKEEP_DELAY + taskMgr.doMethodLater(t, self.upkeepSuitPopulation, self.taskName('sptUpkeepPopulation')) + + def __waitForNextAdjust(self): + t = random.random() * 10.0 + self.POP_ADJUST_DELAY + taskMgr.doMethodLater(t, self.adjustSuitPopulation, self.taskName('sptAdjustPopulation')) + + def upkeepSuitPopulation(self, task): + targetFlyInNum = self.calcDesiredNumFlyInSuits() + targetFlyInNum = min(targetFlyInNum, self.TOTAL_MAX_SUITS - self.numBuildingSuits) + streetPoints = self.streetPointList[:] + flyInDeficit = (targetFlyInNum - self.numFlyInSuits + 3) / 4 + while flyInDeficit > 0: + if not self.createNewSuit([], streetPoints): + break + flyInDeficit -= 1 + + if self.buildingMgr: + suitBuildings = self.buildingMgr.getEstablishedSuitBlocks() + else: + suitBuildings = [] + if self.currDesired != None: + targetBuildingNum = max(0, self.currDesired - self.numFlyInSuits) + else: + targetBuildingNum = int(len(suitBuildings) * self.SUIT_BUILDING_NUM_SUITS) + targetBuildingNum += flyInDeficit + targetBuildingNum = min(targetBuildingNum, self.TOTAL_MAX_SUITS - self.numFlyInSuits) + buildingDeficit = (targetBuildingNum - self.numBuildingSuits + 3) / 4 + while buildingDeficit > 0: + if not self.createNewSuit(suitBuildings, streetPoints): + break + buildingDeficit -= 1 + + if self.notify.getDebug() and self.currDesired == None: + self.notify.debug('zone %d has %d of %d fly-in and %d of %d building suits.' % (self.zoneId, self.numFlyInSuits, targetFlyInNum, self.numBuildingSuits, targetBuildingNum)) + if buildingDeficit != 0: + self.notify.debug('remaining deficit is %d.' % buildingDeficit) + if self.buildingMgr: + suitBuildings = self.buildingMgr.getEstablishedSuitBlocks() + timeoutIndex = min(len(suitBuildings), len(self.SUIT_BUILDING_TIMEOUT) - 1) + timeout = self.SUIT_BUILDING_TIMEOUT[timeoutIndex] + if timeout != None: + timeout *= 3600.0 + oldest = None + oldestAge = 0 + now = time.time() + for b in suitBuildings: + building = self.buildingMgr.getBuilding(b) + if hasattr(building, 'elevator'): + if building.elevator.fsm.getCurrentState().getName() == 'waitEmpty': + age = now - building.becameSuitTime + if age > oldestAge: + oldest = building + oldestAge = age + + if oldestAge > timeout: + self.notify.info('Street %d has %d buildings; reclaiming %0.2f-hour-old building.' % (self.zoneId, len(suitBuildings), oldestAge / 3600.0)) + oldest.b_setVictorList([0, 0, 0, 0]) + oldest.updateSavedBy(None) + oldest.toonTakeOver() + self.__waitForNextUpkeep() + return Task.done + + def adjustSuitPopulation(self, task): + hoodInfo = self.SuitHoodInfo[self.hoodInfoIdx] + if hoodInfo[self.SUIT_HOOD_INFO_MAX] == 0: + self.__waitForNextAdjust() + return Task.done + min = hoodInfo[self.SUIT_HOOD_INFO_MIN] + max = hoodInfo[self.SUIT_HOOD_INFO_MAX] + adjustment = random.choice((-2, -1, -1, 0, 0, 0, 1, 1, 2)) + self.suitCountAdjust += adjustment + desiredNum = self.calcDesiredNumFlyInSuits() + if desiredNum < min: + self.suitCountAdjust = min - self.baseNumSuits + else: + if desiredNum > max: + self.suitCountAdjust = max - self.baseNumSuits + self.__waitForNextAdjust() + return Task.done + + def suitTakeOver(self, blockNumber, suitTrack, difficulty, buildingHeight): + if self.pendingBuildingTracks.count(suitTrack) > 0: + self.pendingBuildingTracks.remove(suitTrack) + if self.pendingBuildingHeights.count(buildingHeight) > 0: + self.pendingBuildingHeights.remove(buildingHeight) + building = self.buildingMgr.getBuilding(blockNumber) + building.suitTakeOver(suitTrack, difficulty, buildingHeight) + + def cogdoTakeOver(self, blockNumber, suitTrack, difficulty, buildingHeight): + if self.pendingCogdoHeights.count(buildingHeight) > 0: + self.pendingCogdoHeights.remove(buildingHeight) + building = self.buildingMgr.getBuilding(blockNumber) + building.cogdoTakeOver(suitTrack, difficulty, buildingHeight) + + def recycleBuilding(self, isCogdo): + bmin = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_BMIN] + current = len(self.buildingMgr.getSuitBlocks()) + target = self.targetNumSuitBuildings + self.targetNumCogdos + if target > bmin and current <= target: + if isCogdo: + if self.targetNumCogdos > choice(self.MinimumOfOne, 1, 0): + self.targetNumCogdos -= 1 + self.assignCogdos(1) + elif self.targetNumSuitBuildings > choice(self.MinimumOfOne, 1, 0): + self.targetNumSuitBuildings -= 1 + self.assignSuitBuildings(1) + + def assignInitialSuitBuildings(self): + totalBuildings = 0 + targetSuitBuildings = 0 + actualSuitBuildings = 0 + targetCogdos = 0 + actualCogdos = 0 + for sp in self.air.suitPlanners.values(): + totalBuildings += len(sp.frontdoorPointList) + targetSuitBuildings += sp.targetNumSuitBuildings + targetCogdos += sp.targetNumCogdos + if sp.buildingMgr: + numCogdoBlocks = len(sp.buildingMgr.getCogdoBlocks()) + actualSuitBuildings += len(sp.buildingMgr.getSuitBlocks()) - numCogdoBlocks + actualCogdos += numCogdoBlocks + + wantedSuitBuildings = int(totalBuildings * self.TOTAL_SUIT_BUILDING_PCT / 100) + if simbase.air.wantCogdominiums: + wantedCogdos = int(wantedSuitBuildings * self.CogdoRatio) + wantedSuitBuildings -= wantedCogdos + else: + wantedCogdos = 0 + self.notify.debug('Want %d out of %d total suit buildings; we currently have %d assigned, %d actual.' % (wantedSuitBuildings, totalBuildings, targetSuitBuildings, actualSuitBuildings)) + if actualSuitBuildings > 0: + numReassigned = 0 + for sp in self.air.suitPlanners.values(): + if sp.buildingMgr: + numBuildings = len(sp.buildingMgr.getSuitBlocks()) - len(sp.buildingMgr.getCogdoBlocks()) + else: + numBuildings = 0 + if numBuildings > sp.targetNumSuitBuildings: + more = numBuildings - sp.targetNumSuitBuildings + sp.targetNumSuitBuildings += more + targetSuitBuildings += more + numReassigned += more + + if numReassigned > 0: + self.notify.debug('Assigned %d buildings where suit buildings already existed.' % numReassigned) + if simbase.air.wantCogdominiums: + if actualCogdos > 0: + numReassigned = 0 + for sp in self.air.suitPlanners.values(): + if sp.buildingMgr: + numCogdos = len(sp.buildingMgr.getCogdoBlocks()) + else: + numCogdos = 0 + if numCogdos > sp.targetNumCogdos: + more = numCogdos - sp.targetNumCogdos + sp.targetNumCogdos += more + targetCogdos += more + numReassigned += more + + if numReassigned > 0: + self.notify.debug('Assigned %d cogdos where cogdos already existed.' % numReassigned) + if wantedSuitBuildings > targetSuitBuildings: + additionalBuildings = wantedSuitBuildings - targetSuitBuildings + self.assignSuitBuildings(additionalBuildings) + else: + if wantedSuitBuildings < targetSuitBuildings: + extraBuildings = targetSuitBuildings - wantedSuitBuildings + self.unassignSuitBuildings(extraBuildings) + if simbase.air.wantCogdominiums: + if wantedCogdos > targetCogdos: + additionalCogdos = wantedCogdos - targetCogdos + self.assignCogdos(additionalCogdos) + elif wantedCogdos < targetCogdos: + extraCogdos = targetCogdos - wantedCogdos + self.unassignCogdos(extraCogdos) + + def assignSuitBuildings(self, numToAssign): + hoodInfo = self.SuitHoodInfo[:] + totalWeight = self.TOTAL_BWEIGHT + totalWeightPerTrack = self.TOTAL_BWEIGHT_PER_TRACK[:] + totalWeightPerHeight = self.TOTAL_BWEIGHT_PER_HEIGHT[:] + numPerTrack = {'c': 0, 'l': 0, 'm': 0, 's': 0} + for sp in self.air.suitPlanners.values(): + sp.countNumBuildingsPerTrack(numPerTrack) + numPerTrack['c'] += sp.pendingBuildingTracks.count('c') + numPerTrack['l'] += sp.pendingBuildingTracks.count('l') + numPerTrack['m'] += sp.pendingBuildingTracks.count('m') + numPerTrack['s'] += sp.pendingBuildingTracks.count('s') + + numPerHeight = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0} + for sp in self.air.suitPlanners.values(): + sp.countNumBuildingsPerHeight(numPerHeight) + numPerHeight[0] += sp.pendingBuildingHeights.count(0) + numPerHeight[1] += sp.pendingBuildingHeights.count(1) + numPerHeight[2] += sp.pendingBuildingHeights.count(2) + numPerHeight[3] += sp.pendingBuildingHeights.count(3) + numPerHeight[4] += sp.pendingBuildingHeights.count(4) + + while numToAssign > 0: + smallestCount = None + smallestTracks = [] + for trackIndex in range(4): + if totalWeightPerTrack[trackIndex]: + track = SuitDNA.suitDepts[trackIndex] + count = numPerTrack[track] + if smallestCount == None or count < smallestCount: + smallestTracks = [ + track] + smallestCount = count + elif count == smallestCount: + smallestTracks.append(track) + + if not smallestTracks: + self.notify.info('No more room for buildings, with %s still to assign.' % numToAssign) + return + buildingTrack = random.choice(smallestTracks) + buildingTrackIndex = SuitDNA.suitDepts.index(buildingTrack) + smallestCount = None + smallestHeights = [] + for height in range(5): + if totalWeightPerHeight[height]: + count = float(numPerHeight[height]) / float(self.BUILDING_HEIGHT_DISTRIBUTION[height]) + if smallestCount == None or count < smallestCount: + smallestHeights = [ + height] + smallestCount = count + elif count == smallestCount: + smallestHeights.append(height) + + if not smallestHeights: + self.notify.info('No more room for buildings, with %s still to assign.' % numToAssign) + return + buildingHeight = random.choice(smallestHeights) + self.notify.info('Existing buildings are (%s, %s), choosing from (%s, %s), chose %s, %s.' % (self.formatNumSuitsPerTrack(numPerTrack), self.formatNumSuitsPerTrack(numPerHeight), smallestTracks, smallestHeights, buildingTrack, buildingHeight)) + repeat = 1 + while repeat and buildingTrack != None and buildingHeight != None: + if len(hoodInfo) == 0: + self.notify.warning('No more streets can have suit buildings, with %d buildings unassigned!' % numToAssign) + return + repeat = 0 + currHoodInfo = self.chooseStreetWithPreference(hoodInfo, buildingTrackIndex, buildingHeight) + zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE] + if self.air.suitPlanners.has_key(zoneId): + sp = self.air.suitPlanners[zoneId] + numCogdos = sp.targetNumCogdos + numBldgs = sp.targetNumSuitBuildings + numTotalBuildings = len(sp.frontdoorPointList) + else: + numCogdos = 0 + numBldgs = 0 + numTotalBuildings = 0 + if numCogdos + numBldgs >= currHoodInfo[self.SUIT_HOOD_INFO_BMAX] or numCogdos + numBldgs >= numTotalBuildings: + self.notify.info('Zone %d has enough buildings.' % zoneId) + hoodInfo.remove(currHoodInfo) + weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + tracks = currHoodInfo[self.SUIT_HOOD_INFO_TRACK] + heights = currHoodInfo[self.SUIT_HOOD_INFO_HEIGHTS] + totalWeight -= weight + totalWeightPerTrack[0] -= weight * tracks[0] + totalWeightPerTrack[1] -= weight * tracks[1] + totalWeightPerTrack[2] -= weight * tracks[2] + totalWeightPerTrack[3] -= weight * tracks[3] + totalWeightPerHeight[0] -= weight * heights[0] + totalWeightPerHeight[1] -= weight * heights[1] + totalWeightPerHeight[2] -= weight * heights[2] + totalWeightPerHeight[3] -= weight * heights[3] + totalWeightPerHeight[4] -= weight * heights[4] + if totalWeightPerTrack[buildingTrackIndex] <= 0: + buildingTrack = None + if totalWeightPerHeight[buildingHeight] <= 0: + buildingHeight = None + repeat = 1 + + if buildingTrack != None and buildingHeight != None: + sp.targetNumSuitBuildings += 1 + sp.pendingBuildingTracks.append(buildingTrack) + sp.pendingBuildingHeights.append(buildingHeight) + self.notify.info('Assigning building to zone %d, pending tracks = %s, pending heights = %s' % (zoneId, sp.pendingBuildingTracks, sp.pendingBuildingHeights)) + numPerTrack[buildingTrack] += 1 + numPerHeight[buildingHeight] += 1 + numToAssign -= 1 + + return + + def unassignSuitBuildings(self, numToAssign): + hoodInfo = self.SuitHoodInfo[:] + totalWeight = self.TOTAL_BWEIGHT + while numToAssign > 0: + repeat = 1 + while repeat: + if len(hoodInfo) == 0: + self.notify.warning('No more streets can remove suit buildings, with %d buildings too many!' % numToAssign) + return + repeat = 0 + currHoodInfo = self.chooseStreetNoPreference(hoodInfo, totalWeight) + zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE] + if self.air.suitPlanners.has_key(zoneId): + sp = self.air.suitPlanners[zoneId] + numCogdos = sp.targetNumCogdos + numBldgs = sp.targetNumSuitBuildings + numTotalBuildings = len(sp.frontdoorPointList) + else: + numCogdos = 0 + numBldgs = 0 + numTotalBuildings = 0 + overallStrapped = numCogdos + numBldgs <= currHoodInfo[self.SUIT_HOOD_INFO_BMIN] + bldgStrapped = numBldgs <= choice(self.MinimumOfOne, 1, 0) + if overallStrapped or bldgStrapped: + self.notify.info("Zone %d can't remove any more buildings." % zoneId) + hoodInfo.remove(currHoodInfo) + totalWeight -= currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + repeat = 1 + + self.notify.info('Unassigning building from zone %d.' % zoneId) + sp.targetNumSuitBuildings -= 1 + numToAssign -= 1 + + def assignCogdos(self, numToAssign): + hoodInfo = self.SuitHoodInfo[:] + totalWeight = self.TOTAL_BWEIGHT + while numToAssign > 0: + while 1: + if len(hoodInfo) == 0: + self.notify.warning('No more streets can have cogdos, with %d cogdos unassigned!' % numToAssign) + return + currHoodInfo = self.chooseStreetNoPreference(hoodInfo, totalWeight) + zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE] + if self.air.suitPlanners.has_key(zoneId): + sp = self.air.suitPlanners[zoneId] + numCogdos = sp.targetNumCogdos + numBldgs = sp.targetNumSuitBuildings + numTotalBuildings = len(sp.frontdoorPointList) + else: + numCogdos = 0 + numBldgs = 0 + numTotalBuildings = 0 + if numCogdos + numBldgs >= currHoodInfo[self.SUIT_HOOD_INFO_BMAX] or numCogdos + numBldgs >= numTotalBuildings: + self.notify.info('Zone %d has enough cogdos.' % zoneId) + hoodInfo.remove(currHoodInfo) + weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + totalWeight -= weight + continue + break + + sp.targetNumCogdos += 1 + sp.pendingCogdoHeights.append(DistributedBuildingAI.FieldOfficeNumFloors) + self.notify.info('Assigning cogdo to zone %d' % zoneId) + numToAssign -= 1 + + def unassignCogdos(self, numToAssign): + hoodInfo = self.SuitHoodInfo[:] + totalWeight = self.TOTAL_BWEIGHT + while numToAssign > 0: + while 1: + currHoodInfo = self.chooseStreetNoPreference(hoodInfo, totalWeight) + zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE] + if self.air.suitPlanners.has_key(zoneId): + sp = self.air.suitPlanners[zoneId] + numCogdos = sp.targetNumCogdos + numBldgs = sp.targetNumSuitBuildings + numTotalBuildings = len(sp.frontdoorPointList) + else: + numCogdos = 0 + numBldgs = 0 + numTotalBuildings = 0 + overallStrapped = numCogdos + numBldgs <= currHoodInfo[self.SUIT_HOOD_INFO_BMIN] + cogdoStrapped = numCogdos <= choice(self.MinimumOfOne, 1, 0) + if overallStrapped or cogdoStrapped: + self.notify.info("Zone %s can't remove any more cogdos." % zoneId) + hoodInfo.remove(currHoodInfo) + totalWeight -= currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + continue + break + + self.notify.info('Unassigning cogdo from zone %s.' % zoneId) + sp.targetNumCogdos -= 1 + numToAssign -= 1 + + def chooseStreetNoPreference(self, hoodInfo, totalWeight): + c = random.random() * totalWeight + t = 0 + for currHoodInfo in hoodInfo: + weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + t += weight + if c < t: + return currHoodInfo + + self.notify.warning('Weighted random choice failed! Total is %s, chose %s' % (t, c)) + return random.choice(hoodInfo) + + def chooseStreetWithPreference(self, hoodInfo, buildingTrackIndex, buildingHeight): + dist = [] + for currHoodInfo in hoodInfo: + weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT] + thisValue = weight * currHoodInfo[self.SUIT_HOOD_INFO_TRACK][buildingTrackIndex] * currHoodInfo[self.SUIT_HOOD_INFO_HEIGHTS][buildingHeight] + dist.append(thisValue) + + totalWeight = sum(dist) + c = random.random() * totalWeight + t = 0 + for i in range(len(hoodInfo)): + t += dist[i] + if c < t: + return hoodInfo[i] + + self.notify.warning('Weighted random choice failed! Total is %s, chose %s' % (t, c)) + return random.choice(hoodInfo) + + def chooseSuitLevel(self, possibleLevels, buildingHeight): + choices = [] + for level in possibleLevels: + minFloors, maxFloors = SuitBuildingGlobals.SuitBuildingInfo[level - 1][0] + if buildingHeight >= minFloors - 1 and buildingHeight <= maxFloors - 1: + choices.append(level) + + return random.choice(choices) + + def initTasks(self): + self.__waitForNextUpkeep() + self.__waitForNextAdjust() + + def resyncSuits(self): + for suit in self.suitList: + suit.resync() + + def flySuits(self): + for suit in self.suitList: + if suit.pathState == 1: + suit.flyAwayNow() + + def requestBattle(self, zoneId, suit, toonId): + self.notify.debug('requestBattle() - zone: %d suit: %d toon: %d' % (zoneId, suit.doId, toonId)) + canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + if not self.battlePosDict.has_key(canonicalZoneId): + return 0 + toon = self.air.doId2do.get(toonId) + if toon.getBattleId() > 0: + self.notify.warning('We tried to request a battle when the toon was already in battle') + return 0 + if toon: + if hasattr(toon, 'doId'): + print ( + 'Setting toonID ', toonId) + toon.b_setBattleId(toonId) + pos = self.battlePosDict[canonicalZoneId] + interactivePropTrackBonus = -1 + if simbase.config.GetBool('props-buff-battles', True) and self.cellToGagBonusDict.has_key(canonicalZoneId): + tentativeBonusTrack = self.cellToGagBonusDict[canonicalZoneId] + trackToHolidayDict = {ToontownBattleGlobals.SQUIRT_TRACK: ToontownGlobals.HYDRANTS_BUFF_BATTLES, ToontownBattleGlobals.THROW_TRACK: ToontownGlobals.MAILBOXES_BUFF_BATTLES, ToontownBattleGlobals.HEAL_TRACK: ToontownGlobals.TRASHCANS_BUFF_BATTLES} + if tentativeBonusTrack in trackToHolidayDict: + holidayId = trackToHolidayDict[tentativeBonusTrack] + if simbase.air.holidayManager.isHolidayRunning(holidayId) and simbase.air.holidayManager.getCurPhase(holidayId) >= 1: + interactivePropTrackBonus = tentativeBonusTrack + self.battleMgr.newBattle(zoneId, zoneId, pos, suit, toonId, self.__battleFinished, self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_SMAX], interactivePropTrackBonus) + for currOther in self.zoneInfo[zoneId]: + self.notify.debug('Found suit %d in this new battle zone %d' % (currOther.getDoId(), zoneId)) + if currOther != suit: + if currOther.pathState == 1 and currOther.legType == SuitLeg.TWalk: + self.checkForBattle(zoneId, currOther) + + return 1 + + def __battleFinished(self, zoneId): + self.notify.debug('DistSuitPlannerAI: battle in zone ' + str(zoneId) + ' finished') + currBattleIdx = 0 + while currBattleIdx < len(self.battleList): + currBattle = self.battleList[currBattleIdx] + if currBattle[0] == zoneId: + self.notify.debug('DistSuitPlannerAI: battle removed') + self.battleList.remove(currBattle) + else: + currBattleIdx = currBattleIdx + 1 + + return None + + def __suitCanJoinBattle(self, zoneId): + battle = self.battleMgr.getBattle(zoneId) + if len(battle.suits) >= 4: + return 0 + if battle: + if simbase.config.GetBool('suits-always-join', 0): + return 1 + jChanceList = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_JCHANCE] + ratioIdx = len(battle.toons) - battle.numSuitsEver + 2 + if ratioIdx >= 0: + if ratioIdx < len(jChanceList): + if random.randint(0, 99) < jChanceList[ratioIdx]: + return 1 + else: + self.notify.warning('__suitCanJoinBattle idx out of range!') + return 1 + return 0 + + def checkForBattle(self, zoneId, suit): + if self.battleMgr.cellHasBattle(zoneId): + if self.__suitCanJoinBattle(zoneId) and self.battleMgr.requestBattleAddSuit(zoneId, suit): + pass + else: + suit.flyAwayNow() + return 1 + else: + return 0 + + def postBattleResumeCheck(self, suit): + self.notify.debug('DistSuitPlannerAI:postBattleResumeCheck: suit ' + str(suit.getDoId()) + ' is leaving battle') + battleIndex = 0 + for currBattle in self.battleList: + if suit.zoneId == currBattle[0]: + self.notify.debug(' battle found' + str(suit.zoneId)) + for currPath in currBattle[1]: + for currPathPtSuit in range(suit.currWpt, suit.myPath.getNumPoints()): + ptIdx = suit.myPath.getPointIndex(currPathPtSuit) + if self.notify.getDebug(): + self.notify.debug(' comparing' + str(ptIdx) + 'with' + str(currPath)) + if currPath == ptIdx: + if self.notify.getDebug(): + self.notify.debug(' match found, telling' + 'suit to fly') + return 0 + + else: + battleIndex = battleIndex + 1 + + pointList = [] + for currPathPtSuit in range(suit.currWpt, suit.myPath.getNumPoints()): + ptIdx = suit.myPath.getPointIndex(currPathPtSuit) + if self.notify.getDebug(): + self.notify.debug(' appending point with index of' + str(ptIdx)) + pointList.append(ptIdx) + + self.battleList.append([suit.zoneId, pointList]) + return 1 + + def zoneChange(self, suit, oldZone, newZone=None): + if self.zoneInfo.has_key(oldZone) and suit in self.zoneInfo[oldZone]: + self.zoneInfo[oldZone].remove(suit) + if newZone != None: + if not self.zoneInfo.has_key(newZone): + self.zoneInfo[newZone] = [] + self.zoneInfo[newZone].append(suit) + return + + def d_setZoneId(self, zoneId): + self.sendUpdate('setZoneId', [self.getZoneId()]) + + def getZoneId(self): + return self.zoneId + + def suitListQuery(self): + suitIndexList = [] + for suit in self.suitList: + suitIndexList.append(SuitDNA.suitHeadTypes.index(suit.dna.name)) + + self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'suitListResponse', [suitIndexList]) + + def buildingListQuery(self): + buildingDict = {} + self.countNumBuildingsPerTrack(buildingDict) + buildingList = [0, 0, 0, 0] + for dept in SuitDNA.suitDepts: + if buildingDict.has_key(dept): + buildingList[SuitDNA.suitDepts.index(dept)] = buildingDict[dept] + + self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'buildingListResponse', [buildingList]) + + def pickLevelTypeAndTrack(self, level=None, type=None, track=None): + if level == None: + level = random.choice(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL]) + if type == None: + typeChoices = range(max(level - 4, 1), min(level, self.MAX_SUIT_TYPES) + 1) + type = random.choice(typeChoices) + else: + level = min(max(level, type), type + 4) + if track == None: + track = SuitDNA.suitDepts[SuitBattleGlobals.pickFromFreqList(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_TRACK])] + self.notify.debug('pickLevelTypeAndTrack: %d %d %s' % (level, type, track)) + return ( + level, type, track) + + @classmethod + def dump(cls): + s = '' + totalBldgs = 0 + totalCogdos = 0 + targetTotalBldgs = 0 + targetTotalCogdos = 0 + for index in range(len(cls.SuitHoodInfo)): + currHoodInfo = cls.SuitHoodInfo[index] + zoneId, min, max, bmin, bmax, bweight, smax, jchance, track, lvl, heights = currHoodInfo + sp = simbase.air.suitPlanners[zoneId] + targetCogdos = sp.targetNumCogdos + targetBldgs = sp.targetNumSuitBuildings + bm = simbase.air.buildingManagers.get(zoneId) + if bm: + numCogdos = len(bm.getCogdoBlocks()) + numBldgs = len(bm.getSuitBlocks()) - numCogdos + s += ' %s: %2s/%2s buildings, %2s/%2s cogdos\n' % (zoneId, numBldgs, targetBldgs, numCogdos, targetCogdos) + totalBldgs += numBldgs + totalCogdos += numCogdos + targetTotalBldgs += targetBldgs + targetTotalCogdos += targetCogdos + + header = '%s\n' % (cls.__name__,) + header += ' %s/%s buildings, %s/%s cogdos\n' % (totalBldgs, targetTotalBldgs, totalCogdos, targetTotalCogdos) + s = header + s + print s diff --git a/toontown/suit/DistributedTutorialSuit.py b/toontown/suit/DistributedTutorialSuit.py new file mode 100644 index 0000000..6444be6 --- /dev/null +++ b/toontown/suit/DistributedTutorialSuit.py @@ -0,0 +1,78 @@ +from pandac.PandaModules import * +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +from toontown.distributed.DelayDeletable import DelayDeletable +import DistributedSuitBase + +class DistributedTutorialSuit(DistributedSuitBase.DistributedSuitBase, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTutorialSuit') + + def __init__(self, cr): + try: + self.DistributedSuit_initialized + except: + self.DistributedSuit_initialized = 1 + DistributedSuitBase.DistributedSuitBase.__init__(self, cr) + self.fsm = ClassicFSM.ClassicFSM('DistributedSuit', [State.State('Off', self.enterOff, self.exitOff, ['Walk', 'Battle']), + State.State('Walk', self.enterWalk, self.exitWalk, ['WaitForBattle', 'Battle']), + State.State('Battle', self.enterBattle, self.exitBattle, []), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['Battle'])], 'Off', 'Off') + self.fsm.enterInitialState() + + return None + + def generate(self): + DistributedSuitBase.DistributedSuitBase.generate(self) + + def announceGenerate(self): + DistributedSuitBase.DistributedSuitBase.announceGenerate(self) + self.setState('Walk') + + def disable(self): + self.notify.debug('DistributedSuit %d: disabling' % self.getDoId()) + self.setState('Off') + DistributedSuitBase.DistributedSuitBase.disable(self) + + def delete(self): + try: + self.DistributedSuit_deleted + except: + self.DistributedSuit_deleted = 1 + self.notify.debug('DistributedSuit %d: deleting' % self.getDoId()) + del self.fsm + DistributedSuitBase.DistributedSuitBase.delete(self) + + def d_requestBattle(self, pos, hpr): + self.cr.playGame.getPlace().setState('WaitForBattle') + self.sendUpdate('requestBattle', [pos[0], + pos[1], + pos[2], + hpr[0], + hpr[1], + hpr[2]]) + return None + + def __handleToonCollision(self, collEntry): + toonId = base.localAvatar.getDoId() + self.notify.debug('Distributed suit: requesting a Battle with ' + 'toon: %d' % toonId) + self.d_requestBattle(self.getPos(), self.getHpr()) + self.setState('WaitForBattle') + return None + + def enterWalk(self): + self.enableBattleDetect('walk', self.__handleToonCollision) + self.loop('walk', 0) + pathPoints = [Vec3(55, 15, -0.5), + Vec3(55, 25, -0.5), + Vec3(25, 25, -0.5), + Vec3(25, 15, -0.5), + Vec3(55, 15, -0.5)] + self.tutWalkTrack = self.makePathTrack(self, pathPoints, 4.5, 'tutFlunkyWalk') + self.tutWalkTrack.loop() + + def exitWalk(self): + self.disableBattleDetect() + self.tutWalkTrack.pause() + self.tutWalkTrack = None + return diff --git a/toontown/suit/Goon.py b/toontown/suit/Goon.py new file mode 100644 index 0000000..b22e56f --- /dev/null +++ b/toontown/suit/Goon.py @@ -0,0 +1,151 @@ +from pandac.PandaModules import * +from direct.actor import Actor +from otp.avatar import Avatar +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +import GoonGlobals +import SuitDNA +import math +AnimDict = {'pg': (('walk', '-walk'), ('collapse', '-collapse'), ('recovery', '-recovery')), + 'sg': (('walk', '-walk'), ('collapse', '-collapse'), ('recovery', '-recovery'))} +ModelDict = {'pg': 'phase_9/models/char/Cog_Goonie', + 'sg': 'phase_9/models/char/Cog_Goonie'} + +class Goon(Avatar.Avatar): + + def __init__(self, dnaName = None): + try: + self.Goon_initialized + except: + self.Goon_initialized = 1 + Avatar.Avatar.__init__(self) + self.ignore('nametagAmbientLightChanged') + self.hFov = 70 + self.attackRadius = 15 + self.strength = 15 + self.velocity = 4 + self.scale = 1.0 + if dnaName is not None: + self.initGoon(dnaName) + + return + + def initGoon(self, dnaName): + dna = SuitDNA.SuitDNA() + dna.newGoon(dnaName) + self.setDNA(dna) + self.type = dnaName + self.createHead() + self.find('**/actorGeom').setH(180) + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def delete(self): + try: + self.Goon_deleted + except: + self.Goon_deleted = 1 + filePrefix = ModelDict[self.style.name] + loader.unloadModel(filePrefix + '-zero') + animList = AnimDict[self.style.name] + for anim in animList: + loader.unloadModel(filePrefix + anim[1]) + + Avatar.Avatar.delete(self) + + return None + + def setDNAString(self, dnaString): + self.dna = SuitDNA.SuitDNA() + self.dna.makeFromNetString(dnaString) + self.setDNA(self.dna) + + def setDNA(self, dna): + if self.style: + pass + else: + self.style = dna + self.generateGoon() + self.initializeDropShadow() + self.initializeNametag3d() + + def generateGoon(self): + dna = self.style + filePrefix = ModelDict[dna.name] + self.loadModel(filePrefix + '-zero') + animDict = {} + animList = AnimDict[dna.name] + for anim in animList: + animDict[anim[0]] = filePrefix + anim[1] + + self.loadAnims(animDict) + + def getShadowJoint(self): + return self.getGeomNode() + + def getNametagJoints(self): + return [] + + def createHead(self): + self.headHeight = 3.0 + head = self.find('**/joint35') + if head.isEmpty(): + head = self.find('**/joint40') + self.hat = self.find('**/joint8') + parentNode = head.getParent() + self.head = parentNode.attachNewNode('headRotate') + head.reparentTo(self.head) + self.hat.reparentTo(self.head) + if self.type == 'pg': + self.hat.find('**/security_hat').hide() + elif self.type == 'sg': + self.hat.find('**/hard_hat').hide() + else: + self.hat.find('**/security_hat').hide() + self.hat.find('**/hard_hat').hide() + self.eye = self.find('**/eye') + self.eye.setColorScale(1, 1, 1, 1) + self.eye.setColor(1, 1, 0, 1) + self.radar = None + return + + def scaleRadar(self): + if self.radar: + self.radar.removeNode() + self.radar = self.eye.attachNewNode('radar') + model = loader.loadModel('phase_9/models/cogHQ/alphaCone2') + beam = self.radar.attachNewNode('beam') + transformNode = model.find('**/transform') + transformNode.getChildren().reparentTo(beam) + self.radar.setPos(0, -.5, 0.4) + self.radar.setTransparency(1) + self.radar.setDepthWrite(0) + self.halfFov = self.hFov / 2.0 + fovRad = self.halfFov * math.pi / 180.0 + self.cosHalfFov = math.cos(fovRad) + kw = math.tan(fovRad) * self.attackRadius / 10.5 + kl = math.sqrt(self.attackRadius * self.attackRadius + 9.0) / 25.0 + beam.setScale(kw / self.scale, kl / self.scale, kw / self.scale) + beam.setHpr(0, self.halfFov, 0) + p = self.radar.getRelativePoint(beam, Point3(0, -6, -1.8)) + self.radar.setSz(-3.5 / p[2]) + self.radar.flattenMedium() + self.radar.setColor(1, 1, 1, 0.2) + + def colorHat(self): + if self.type == 'pg': + colorList = GoonGlobals.PG_COLORS + elif self.type == 'sg': + colorList = GoonGlobals.SG_COLORS + else: + return + if self.strength >= 20: + self.hat.setColorScale(colorList[0]) + elif self.strength >= 15: + self.hat.setColorScale(colorList[1]) + else: + self.hat.clearColorScale() diff --git a/toontown/suit/GoonDeath.py b/toontown/suit/GoonDeath.py new file mode 100644 index 0000000..bec62fa --- /dev/null +++ b/toontown/suit/GoonDeath.py @@ -0,0 +1,23 @@ +from direct.interval.IntervalGlobal import * +from pandac.PandaModules import * +from direct.particles import ParticleEffect +from toontown.battle import BattleParticles + +def createExplosionTrack(parent, deathNode, scale): + explosion = loader.loadModel('phase_3.5/models/props/explosion.bam') + explosion.getChild(0).setScale(scale) + explosion.reparentTo(deathNode) + explosion.setBillboardPointEye() + explosion.setPos(0, 0, 2) + return Sequence(Func(deathNode.reparentTo, parent), Wait(0.6), Func(deathNode.detachNode)) + + +def createGoonExplosion(parent, explosionPoint, scale): + BattleParticles.loadParticles() + deathNode = NodePath('goonDeath') + deathNode.setPos(explosionPoint) + explosion = createExplosionTrack(parent, deathNode, scale) + smallGearExplosion = BattleParticles.createParticleEffect('GearExplosion', numParticles=10) + bigGearExplosion = BattleParticles.createParticleEffect('WideGearExplosion', numParticles=30) + deathSound = base.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.mp3') + return Parallel(explosion, SoundInterval(deathSound), ParticleInterval(smallGearExplosion, deathNode, worldRelative=0, duration=4.3, cleanup=True), ParticleInterval(bigGearExplosion, deathNode, worldRelative=0, duration=1.0, cleanup=True), name='gears2MTrack') diff --git a/toontown/suit/GoonGlobals.py b/toontown/suit/GoonGlobals.py new file mode 100644 index 0000000..e633324 --- /dev/null +++ b/toontown/suit/GoonGlobals.py @@ -0,0 +1,14 @@ +from pandac.PandaModules import Vec4 +PG_COLORS = [Vec4(0.95, 0.0, 0.0, 1.0), Vec4(0.75, 0.35, 0.1, 1.0)] +SG_COLORS = [Vec4(0.0, 0.0, 0.95, 1.0), Vec4(0.35, 0.0, 0.75, 1.0)] +GOON_FORWARD = 1 +GOON_REVERSE = -1 +GOON_MOVIE_WALK = 0 +GOON_MOVIE_STUNNED = 1 +GOON_MOVIE_BATTLE = 2 +GOON_MOVIE_RECOVERY = 3 +GOON_MOVIE_SYNC = 4 +T_TURN = 3.0 +ANIM_WALK_RATE = 2.8 +ANIM_TURN_RATE = 20 +DEFAULT_WALK_RATE = 4 diff --git a/toontown/suit/GoonPathData.py b/toontown/suit/GoonPathData.py new file mode 100644 index 0000000..8c56206 --- /dev/null +++ b/toontown/suit/GoonPathData.py @@ -0,0 +1,215 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +taskZoneId2pathId = {ToontownGlobals.SellbotFactoryInt: 'sellbotFactory', + ToontownGlobals.CashbotMintIntA: 'cashbotMint', + ToontownGlobals.CashbotMintIntB: 'cashbotMint', + ToontownGlobals.CashbotMintIntC: 'cashbotMint', + ToontownGlobals.LawbotOfficeInt: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntA: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntB: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntC: 'lawOfficeStage', + ToontownGlobals.LawbotStageIntD: 'lawOfficeStage'} +Paths = {'sellbotFactory': {0: [Vec3(10.0, 0.0, 0.0), + Vec3(10.0, 10.0, 0.0), + Vec3(-10.0, 10.0, 0.0), + Vec3(-10.0, 0.0, 0.0)], + 1: [Vec3(10.0, 5.0, 0.0), Vec3(10.0, 0.0, 0.0), Vec3(-10.0, -5.0, 0.0)], + 2: [Vec3(-48.31, -0.001, 0), + Vec3(-48.0, -3.709, 0), + Vec3(35.041, -3.27, 0), + Vec3(34.751, -91.376, 0), + Vec3(39.869, -91.248, 0), + Vec3(39.93, -0.022, 0)], + 3: [Vec3(-47.9110107422, -6.86798095703, 0.0), + Vec3(27.691986084, -5.68200683594, 0.0), + Vec3(34.049987793, 3.55303955078, 0.0), + Vec3(-39.983001709, 3.68499755859, 0.0)], + 4: [Vec3(3.5649, 35.397, 0), + Vec3(-5.335, 36.067, 0), + Vec3(-4.605, -69.67, 0), + Vec3(17.815, -70.577, 0), + Vec3(17.4979, -50.997, 0), + Vec3(3.479, -46.775, 0)], + 5: [Vec3(-2.993, -21.085, 0), + Vec3(5.209, -20.966, 0), + Vec3(2.164, 74.742, 0), + Vec3(-50.439, 78.55, 0), + Vec3(-52.042, 58.831, 0), + Vec3(-3.549, 57.295, 0)], + 6: [Vec3(31.627, 2.093, 0.0), Vec3(4, 43, 0)], + 7: [Vec3(34.627, 2.093, 0.0), Vec3(32, 43, 0)], + 8: [Vec3(64.0, 43.0, 0.0), Vec3(59.5, 1.8, 0.0)], + 9: [Vec3(84.0, 43.0, 0.0), Vec3(93.0, 25.0, 0.0), Vec3(66.0, 2.0, 0.0)], + 10: [Vec3(85.0, 43.0, 0.0), + Vec3(66.0, 47.0, 0.0), + Vec3(53.0, 49.0, 0.0), + Vec3(71.0, 22.0, 0.0)], + 11: [Vec3(63.0, 43.0, 0.0), Vec3(33.0, 47.0, 0.0)], + 12: [Vec3(10.5139980316, 73.4393844604, 0.0), Vec3(-10.0053958893, 72.9239883423, 0.0), Vec3(5.55112314224, 90.2847213745, 0.0)], + 13: [Vec3(-2.62728261948, 50.5329399109, 0.0), Vec3(-2.21770620346, 3.07684659958, 0.0)], + 14: [Vec3(-15.9598636627, -15.0503959656, 0.0), + Vec3(8.35170555115, -17.5856513977, 0.0), + Vec3(8.34984397888, 3.9258685112, 0.0), + Vec3(-11.5888309479, 7.29379606247, 0.0)], + 15: [Vec3(-25.7975006104, 18.4752807617, 0.0), Vec3(12.6321563721, 19.1084594727, 0.0), Vec3(24.8442764282, -4.62582397461, 0.0)], + 16: [Vec3(6.70755004883, -4.16766357422, 0.0), + Vec3(-24.5565567017, -6.02560424805, 0.0), + Vec3(-24.4623031616, 15.6810913086, 0.0), + Vec3(3.93852233887, 17.1640014648, 0.0)], + 17: [Vec3(0.474000930786, -29.1558818817, 0.0), + Vec3(0.460096359253, -16.5713024139, 0.0), + Vec3(27.9419631958, -16.0025310516, 0.0), + Vec3(28.3607826233, -38.4133338928, 0.0), + Vec3(27.2721481323, 0.622072696686, 0.0), + Vec3(-29.8864364624, 2.13151788712, 0.0), + Vec3(-28.907831192, -38.875164032, 0.0), + Vec3(-26.5185241699, -16.1851940155, 0.0), + Vec3(0.0833129882813, -16.7483196259, 0.0)], + 18: [Vec3(-37.6936645508, 4.92616510391, 0.0), + Vec3(-6.09254932404, 3.79619073868, 0.0), + Vec3(-5.81788635254, 16.4960193634, 0.0), + Vec3(6.79872131348, 17.3943042755, 0.0), + Vec3(6.47452545166, 6.80963373184, 0.0), + Vec3(29.4301567078, 4.76448297501, 0.0)], + 19: [Vec3(-11.6953277588, -13.8257198334, -0.00400207517669), + Vec3(-26.5939941406, -38.6423110962, -0.00400207517669), + Vec3(-12.9963378906, -54.1221084595, -0.00400207517669), + Vec3(5.45291900635, -50.0818252563, -0.00400207517669), + Vec3(0.500541687012, -27.6731319427, -0.00400207517669), + Vec3(10.6271972656, -16.9095211029, -0.00400207517669), + Vec3(3.95051574707, -11.6894283295, -0.00400207517669)], + 20: [Vec3(1.44152832031, -18.6059322357, -0.00400207517669), + Vec3(-0.327140808105, -38.2261734009, -0.00400207517669), + Vec3(19.4757766724, -20.1440181732, -0.00400207517669), + Vec3(-13.617980957, -38.8843765259, -0.00400207517669), + Vec3(9.44762420654, -28.4113521576, -0.00400207517669)], + 21: [Vec3(26.8655929565, -13.403295517, -0.00400207517669), + Vec3(32.0896224976, -27.145483017, -0.00400207517669), + Vec3(23.4544830322, -40.3218765259, -0.00400207517669), + Vec3(14.4268798828, -42.4280166626, -0.00400207517669), + Vec3(6.38285827637, -40.3579483032, -0.00400207517669), + Vec3(-2.16972351074, -26.8708248138, -0.00400207517669), + Vec3(-3.16332244873, -17.7787837982, -0.00400207517669), + Vec3(-3.16332244873, -17.7787837982, -0.00400207517669), + Vec3(2.0150680542, -5.54251718521, -0.00400207517669), + Vec3(7.65352630615, -5.11417245865, -0.00400207517669)], + 22: [Vec3(-4.81932353973, 4.0960521698, 0.0), + Vec3(-37.2935218811, -35.963394165, 0.0), + Vec3(-32.3968849182, -49.8670196533, 0.0), + Vec3(-52.6336708069, -33.5889434814, 0.0), + Vec3(-53.0538520813, -21.5930957794, 0.0), + Vec3(-44.2506980896, -27.3775806427, 0.0), + Vec3(-1.58745121956, 3.91203117371, 0.0)], + 23: [Vec3(5.80584430695, 1.62095463276, 0.0), + Vec3(-10.3068342209, -10.9403858185, 0.0), + Vec3(-27.9555549622, 11.9806346893, 0.0), + Vec3(-2.36316990852, -15.246049881, 0.0), + Vec3(-12.8535642624, -3.13337898254, 0.0)], + 24: [Vec3(-12.7202939987, 15.884016037, 0.0), Vec3(16.7396354675, -14.0337085724, 0.0)], + 25: [Vec3(-17.7801113129, -8.74084472656, 0.0), Vec3(-13.1200618744, -9.26202392578, -0.0)], + 26: [Vec3(8.624584198, -10.9699707031, 0.0), Vec3(3.29245567322, -2.59405517578, 0.0)], + 27: [Vec3(12.3031358719, 7.98439788818, 0.0), Vec3(-13.3801307678, 7.98439788818, 0.0)], + 28: [Vec3(-32.1953697205, -85.9077148438, 0.0), + Vec3(-33.518032074, -60.6316223145, 0.0), + Vec3(-25.2015018463, -34.200378418, 0.0), + Vec3(-0.624415278435, -24.7206726074, 0.0)], + 29: [Vec3(2.76529407501, -19.7032775879, 0.0), + Vec3(12.5924119949, -5.15737915039, 0.0), + Vec3(1.32620728016, 16.3902587891, 0.0), + Vec3(-13.4380140305, 18.5373535156, 0.0), + Vec3(-9.65627574921, -6.18496704102, 0.0), + Vec3(-24.4610538483, -31.9492492676, 0.0), + Vec3(-14.1554517746, -43.6148376465, 0.0)], + 30: [Vec3(-0.175471186638, 24.0636901855, 0.0), + Vec3(-18.316400528, 61.1733856201, 0.0), + Vec3(-34.2972984314, 58.8515930176, 0.0), + Vec3(-18.7033634186, 27.5550231934, 0.0)], + 31: [Vec3(25.8017997742, 73.792678833, 0.0), Vec3(11.0946779251, 51.4213256836, 0.0), Vec3(31.6651115417, 43.7239074707, 0.0)], + 32: [Vec3(4.61576557159, 31.6040344238, 0.0), + Vec3(-10.1993589401, -7.03125, 0.0), + Vec3(-4.72947978973, -29.3165893555, 0.0), + Vec3(22.23179245, -28.7750854492, 0.0), + Vec3(11.0534486771, 0.391784667969, 0.0), + Vec3(18.2302970886, 28.4787597656, 0.0)], + 33: [Vec3(8.04011249542, -51.311706543, 0.0), + Vec3(25.7791862488, -46.4603881836, 0.0), + Vec3(42.3690605164, -57.4282226563, 0.0), + Vec3(34.1978569031, -77.1849365234, 0.0)], + 34: [Point3(2.75689697266, -21.3427124023, 0.0), Point3(2.78796386719, 37.0751342773, 0.000232696533203)], + 35: [Point3(7.67395019531, 40.565612793, -0.000492095947266), Point3(78.0889282227, 39.3659667969, -0.00810623168945)], + 36: [Point3(98.1296081543, 38.8174743652, -0.0102634429932), + Point3(104.098999023, 38.9267272949, -0.0109195709229), + Point3(105.774627686, 243.011169434, -0.0125999450684), + Point3(100.351745605, 242.852386475, -0.0125999450684)], + 37: [Point3(152.121673584, 222.440261841, -5.0125999450684), + Point3(249.456481934, 223.478790283, -5.01259994507), + Point3(247.701660156, 233.899627686, -5.01259994507), + Point3(247.01550293, 224.162445068, -5.01259994507)], + 38: [Point3(-1.65826416016, 25.0982055664, 0.025), + Point3(-1.44326782227, 9.02752685547, 0.025), + Point3(-8.76708984375, 11.2831420898, 0.025), + Point3(11.4426574707, 7.11584472656, 0.025), + Point3(2.77236938477, 7.02355957031, 0.025)], + 39: [Point3(-3.69488525391, -1.89245605469, 0.025), + Point3(-5.73229980469, -9.62658691406, 0.025), + Point3(4.14199829102, -10.7971191406, 0.025), + Point3(7.77349853516, 1.58245849609, 0.025)], + 40: [Point3(1.67153930664, 18.4436645508, 0.025), Point3(-7.59463500977, 18.5286254883, 0.025)], + 41: [Point3(0.726699829102, -48.1898803711, -4.99999809265), + Point3(0.787185668945, -74.4460754395, -4.99999809265), + Point3(39.1846923828, -76.5356445313, -4.99999809265), + Point3(0.787185668945, -74.4460754395, -4.99999809265)], + 42: [Vec3(6.0, -6.0, 0.0), + Vec3(6.0, 6.0, 0.0), + Vec3(-6.0, 6.0, 0.0), + Vec3(-6.0, -6.0, 0.0)]}, + 'cashbotMint': {28: [Vec3(-32.1953697205, -85.9077148438, 0.0), + Vec3(-33.518032074, -60.6316223145, 0.0), + Vec3(-25.2015018463, -34.200378418, 0.0), + Vec3(-0.624415278435, -24.7206726074, 0.0)], + 29: [Vec3(2.76529407501, -19.7032775879, 0.0), + Vec3(12.5924119949, -5.15737915039, 0.0), + Vec3(1.32620728016, 16.3902587891, 0.0), + Vec3(-13.4380140305, 18.5373535156, 0.0), + Vec3(-9.65627574921, -6.18496704102, 0.0), + Vec3(-24.4610538483, -31.9492492676, 0.0), + Vec3(-14.1554517746, -43.6148376465, 0.0)], + 30: [Vec3(-0.175471186638, 24.0636901855, 0.0), + Vec3(-18.316400528, 61.1733856201, 0.0), + Vec3(-34.2972984314, 58.8515930176, 0.0), + Vec3(-18.7033634186, 27.5550231934, 0.0)], + 0: [Vec3(-5, 0.0, 0.0), + Vec3(-5, 10.0, 0.0), + Vec3(5.0, 10.0, 0.0), + Vec3(5.0, 0.0, 0.0)], + 1: [Vec3(0.0, 0.0, 0.0), Vec3(-5.77, 10.0, 0.0), Vec3(5.77, 10.0, 0.0)], + 2: [Vec3(-5.77, 10.0, 0.0), + Vec3(5.77, 10.0, 0.0), + Vec3(-5.77, -10.0, 0.0), + Vec3(5.77, -10.0, 0.0)], + 3: [Vec3(-10, 0, 0), Vec3(10, 0, 0)]}, + 'lawOfficeStage': {28: [Vec3(-32.1953697205, -85.9077148438, 0.0), + Vec3(-33.518032074, -60.6316223145, 0.0), + Vec3(-25.2015018463, -34.200378418, 0.0), + Vec3(-0.624415278435, -24.7206726074, 0.0)], + 29: [Vec3(2.76529407501, -19.7032775879, 0.0), + Vec3(12.5924119949, -5.15737915039, 0.0), + Vec3(1.32620728016, 16.3902587891, 0.0), + Vec3(-13.4380140305, 18.5373535156, 0.0), + Vec3(-9.65627574921, -6.18496704102, 0.0), + Vec3(-24.4610538483, -31.9492492676, 0.0), + Vec3(-14.1554517746, -43.6148376465, 0.0)], + 30: [Vec3(-0.175471186638, 24.0636901855, 0.0), + Vec3(-18.316400528, 61.1733856201, 0.0), + Vec3(-34.2972984314, 58.8515930176, 0.0), + Vec3(-18.7033634186, 27.5550231934, 0.0)], + 0: [Vec3(-5, 0.0, 0.0), + Vec3(-5, 10.0, 0.0), + Vec3(5.0, 10.0, 0.0), + Vec3(5.0, 0.0, 0.0)], + 1: [Vec3(0.0, 0.0, 0.0), Vec3(-5.77, 10.0, 0.0), Vec3(5.77, 10.0, 0.0)], + 2: [Vec3(-5.77, 10.0, 0.0), + Vec3(5.77, 10.0, 0.0), + Vec3(-5.77, -10.0, 0.0), + Vec3(5.77, -10.0, 0.0)], + 3: [Vec3(-10, 0, 0), Vec3(10, 0, 0)]}} diff --git a/toontown/suit/RoguesGallery.py b/toontown/suit/RoguesGallery.py new file mode 100644 index 0000000..b3c04f3 --- /dev/null +++ b/toontown/suit/RoguesGallery.py @@ -0,0 +1,183 @@ +from pandac.PandaModules import * +from direct.fsm import StateData +import Suit +import SuitDNA +from toontown.toonbase import ToontownGlobals +import random + +class RoguesGallery(StateData.StateData): + + def __init__(self, rognamestr = None): + StateData.StateData.__init__(self, 'roguesDone') + self.rognamestr = rognamestr + self.left = -1.333 + self.right = 1.333 + self.bottom = -1.0 + self.top = 1.0 + self.sideMargins = 0.1 + self.topMargins = 0.1 + self.xSpaceBetweenDifferentSuits = 0.01 + self.xSpaceBetweenSameSuits = 0.0 + self.ySpaceBetweenSuits = 0.05 + self.labelScale = 1.0 + + def load(self): + if StateData.StateData.load(self): + self.width = self.right - self.left - self.sideMargins * 2.0 + self.height = self.top - self.bottom - self.topMargins * 2.0 + if self.rognamestr == None: + self.numSuitTypes = SuitDNA.suitsPerDept + self.numSuitDepts = len(SuitDNA.suitDepts) + else: + self.numSuitTypes = 1 + self.numSuitDepts = 1 + self.xSpaceBetweenDifferentSuits = 0.0 + self.xSpaceBetweenSameSuits = 0.0 + self.ySpaceBetweenSuits = 0.0 + self.ySuitInc = (self.height + self.ySpaceBetweenSuits) / self.numSuitDepts + self.ySuitMaxAllowed = self.ySuitInc - self.ySpaceBetweenSuits + self.xRowSpace = self.width - (self.numSuitTypes - 1) * self.xSpaceBetweenDifferentSuits - self.numSuitTypes * self.xSpaceBetweenSameSuits + self.__makeGallery() + return + + def unload(self): + if StateData.StateData.unload(self): + self.gallery.removeNode() + del self.suits + del self.actors + + def enter(self): + if StateData.StateData.enter(self): + render.hide() + aspect2d.hide() + self.gallery.reparentTo(render2d) + self.gallery.setMat(base.aspect2d.getMat()) + self.gallery.setPos(0.0, 10.0, 0.0) + base.setBackgroundColor(0.6, 0.6, 0.6) + + def exit(self): + if StateData.StateData.exit(self): + self.stop() + render.show() + aspect2d.show() + self.gallery.reparentTo(hidden) + self.gallery.clearMat() + base.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + self.ignoreAll() + + def animate(self): + self.load() + for suit in self.actors: + suit.pose('neutral', random.randint(0, suit.getNumFrames('neutral') - 1)) + suit.loop('neutral', 0) + + def stop(self): + self.load() + for suit in self.actors: + suit.pose('neutral', 30) + + def autoExit(self): + self.acceptOnce('mouse1', self.exit) + + def __makeGallery(self): + self.gallery = hidden.attachNewNode('gallery') + self.gallery.setDepthWrite(1) + self.gallery.setDepthTest(1) + self.suits = [] + self.actors = [] + self.text = TextNode('rogues') + self.text.setFont(ToontownGlobals.getInterfaceFont()) + self.text.setAlign(TextNode.ACenter) + self.text.setTextColor(0.0, 0.0, 0.0, 1.0) + self.rowHeight = 0.0 + self.minXScale = None + print "rognamestr='", self.rognamestr, "'\n" + if self.rognamestr == None or len(self.rognamestr) == 0: + for dept in SuitDNA.suitDepts: + self.__makeDept(dept) + + else: + self.suitRow = [] + self.rowWidth = 0.0 + self.__makeSuit(None, None, self.rognamestr) + self.minXScale = self.xRowSpace / self.rowWidth + self.suits.append((self.rowWidth, self.suitRow)) + del self.suitRow + self.__rescaleSuits() + return + + def __makeDept(self, dept): + self.suitRow = [] + self.rowWidth = 0.0 + for type in range(self.numSuitTypes): + self.__makeSuit(dept, type) + + xScale = self.xRowSpace / self.rowWidth + if self.minXScale == None or self.minXScale > xScale: + self.minXScale = xScale + self.suits.append((self.rowWidth, self.suitRow)) + del self.suitRow + return + + def __makeSuit(self, dept, type, name = None): + dna = SuitDNA.SuitDNA() + if name != None: + dna.newSuit(name) + else: + dna.newSuitRandom(type + 1, dept) + suit = Suit.Suit() + suit.setStyle(dna) + suit.generateSuit() + suit.pose('neutral', 30) + ll = Point3() + ur = Point3() + suit.update() + suit.calcTightBounds(ll, ur) + suitWidth = ur[0] - ll[0] + suitDepth = ur[1] - ll[1] + suitHeight = ur[2] - ll[2] + self.rowWidth += suitWidth + suitDepth + self.rowHeight = max(self.rowHeight, suitHeight) + suit.reparentTo(self.gallery) + suit.setHpr(180.0, 0.0, 0.0) + profile = Suit.Suit() + profile.setStyle(dna) + profile.generateSuit() + profile.pose('neutral', 30) + profile.reparentTo(self.gallery) + profile.setHpr(90.0, 0.0, 0.0) + self.suitRow.append((type, + suitWidth, + suit, + suitDepth, + profile)) + self.actors.append(suit) + self.actors.append(profile) + return + + def __rescaleSuits(self): + yScale = self.ySuitMaxAllowed / self.rowHeight + scale = min(self.minXScale, yScale) + y = self.top - self.topMargins + self.ySpaceBetweenSuits + for rowWidth, suitRow in self.suits: + rowWidth *= scale + extraSpace = self.xRowSpace - rowWidth + extraSpacePerSuit = extraSpace / (self.numSuitTypes * 2 - 1) + x = self.left + self.sideMargins + y -= self.ySuitInc + for type, width, suit, depth, profile in suitRow: + left = x + width *= scale + suit.setScale(scale) + suit.setPos(x + width / 2.0, 0.0, y) + x += width + self.xSpaceBetweenSameSuits + extraSpacePerSuit + depth *= scale + profile.setScale(scale) + profile.setPos(x + depth / 2.0, 0.0, y) + x += depth + right = x + x += self.xSpaceBetweenDifferentSuits + extraSpacePerSuit + self.text.setText(suit.getName()) + name = self.gallery.attachNewNode(self.text.generate()) + name.setPos((right + left) / 2.0, 0.0, y + (suit.height + self.labelScale * 0.5) * scale) + name.setScale(self.labelScale * scale) diff --git a/toontown/suit/SellbotBossGlobals.py b/toontown/suit/SellbotBossGlobals.py new file mode 100644 index 0000000..dcbba9e --- /dev/null +++ b/toontown/suit/SellbotBossGlobals.py @@ -0,0 +1,48 @@ +from pandac.PandaModules import * +from toontown.coghq import DistributedHealBarrelAI +from toontown.coghq import DistributedGagBarrelAI +PieToonup = 1 +PieToonupNerfed = 2 +PieDamageMult = 1.0 +PieDamageMultNerfed = 2.0 +AttackMult = 1.0 +AttackMultNerfed = 0.5 +HitCountDamage = 35 +HitCountDamageNerfed = 50 +BarrelDefs = {8000: {'type': DistributedHealBarrelAI.DistributedHealBarrelAI, + 'pos': Point3(15, 23, 0), + 'hpr': Vec3(-45, 0, 0), + 'rewardPerGrab': 50, + 'rewardPerGrabMax': 0}, + 8001: {'type': DistributedGagBarrelAI.DistributedGagBarrelAI, + 'pos': Point3(15, -23, 0), + 'hpr': Vec3(-135, 0, 0), + 'gagLevel': 3, + 'gagLevelMax': 0, + 'gagTrack': 3, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 8002: {'type': DistributedGagBarrelAI.DistributedGagBarrelAI, + 'pos': Point3(21, 20, 0), + 'hpr': Vec3(-45, 0, 0), + 'gagLevel': 3, + 'gagLevelMax': 0, + 'gagTrack': 4, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}, + 8003: {'type': DistributedGagBarrelAI.DistributedGagBarrelAI, + 'pos': Point3(21, -20, 0), + 'hpr': Vec3(-135, 0, 0), + 'gagLevel': 3, + 'gagLevelMax': 0, + 'gagTrack': 5, + 'rewardPerGrab': 10, + 'rewardPerGrabMax': 0}} + +def setBarrelAttr(barrel, entId): + for defAttr, defValue in BarrelDefs[entId].iteritems(): + setattr(barrel, defAttr, defValue) + + +BarrelsStartPos = (0, -36, -8) +BarrelsFinalPos = (0, -36, 0) diff --git a/toontown/suit/Suit.py b/toontown/suit/Suit.py new file mode 100644 index 0000000..2292dd3 --- /dev/null +++ b/toontown/suit/Suit.py @@ -0,0 +1,1009 @@ +from direct.actor import Actor +from otp.avatar import Avatar +import SuitDNA +from toontown.toonbase import ToontownGlobals +from pandac.PandaModules import * +from toontown.battle import SuitBattleGlobals +from direct.task.Task import Task +from toontown.battle import BattleProps +from toontown.toonbase import TTLocalizer +from pandac.PandaModules import VirtualFileMountHTTP, VirtualFileSystem, Filename, DSearchPath +from direct.showbase import AppRunnerGlobal +import string +import os +aSize = 6.06 +bSize = 5.29 +cSize = 4.14 +SuitDialogArray = [] +SkelSuitDialogArray = [] +AllSuits = (('walk', 'walk'), ('run', 'walk'), ('neutral', 'neutral')) +AllSuitsMinigame = (('victory', 'victory'), + ('flail', 'flailing'), + ('tug-o-war', 'tug-o-war'), + ('slip-backward', 'slip-backward'), + ('slip-forward', 'slip-forward')) +AllSuitsTutorialBattle = (('lose', 'lose'), ('pie-small-react', 'pie-small'), ('squirt-small-react', 'squirt-small')) +AllSuitsBattle = (('drop-react', 'anvil-drop'), + ('flatten', 'drop'), + ('sidestep-left', 'sidestep-left'), + ('sidestep-right', 'sidestep-right'), + ('squirt-large-react', 'squirt-large'), + ('landing', 'landing'), + ('reach', 'walknreach'), + ('rake-react', 'rake'), + ('hypnotized', 'hypnotize'), + ('soak', 'soak')) +SuitsCEOBattle = (('sit', 'sit'), + ('sit-eat-in', 'sit-eat-in'), + ('sit-eat-loop', 'sit-eat-loop'), + ('sit-eat-out', 'sit-eat-out'), + ('sit-angry', 'sit-angry'), + ('sit-hungry-left', 'leftsit-hungry'), + ('sit-hungry-right', 'rightsit-hungry'), + ('sit-lose', 'sit-lose'), + ('tray-walk', 'tray-walk'), + ('tray-neutral', 'tray-neutral'), + ('sit-lose', 'sit-lose')) +f = (('throw-paper', 'throw-paper', 3.5), ('phone', 'phone', 3.5), ('shredder', 'shredder', 3.5)) +p = (('pencil-sharpener', 'pencil-sharpener', 5), + ('pen-squirt', 'pen-squirt', 5), + ('hold-eraser', 'hold-eraser', 5), + ('finger-wag', 'finger-wag', 5), + ('hold-pencil', 'hold-pencil', 5)) +ym = (('throw-paper', 'throw-paper', 5), + ('golf-club-swing', 'golf-club-swing', 5), + ('magic3', 'magic3', 5), + ('rubber-stamp', 'rubber-stamp', 5), + ('smile', 'smile', 5)) +mm = (('speak', 'speak', 5), + ('effort', 'effort', 5), + ('magic1', 'magic1', 5), + ('pen-squirt', 'fountain-pen', 5), + ('finger-wag', 'finger-wag', 5)) +ds = (('magic1', 'magic1', 5), + ('magic2', 'magic2', 5), + ('throw-paper', 'throw-paper', 5), + ('magic3', 'magic3', 5)) +hh = (('pen-squirt', 'fountain-pen', 7), + ('glower', 'glower', 5), + ('throw-paper', 'throw-paper', 5), + ('magic1', 'magic1', 5), + ('roll-o-dex', 'roll-o-dex', 5)) +cr = (('pickpocket', 'pickpocket', 5), ('throw-paper', 'throw-paper', 3.5), ('glower', 'glower', 5)) +tbc = (('cigar-smoke', 'cigar-smoke', 8), + ('glower', 'glower', 5), + ('song-and-dance', 'song-and-dance', 8), + ('golf-club-swing', 'golf-club-swing', 5)) +cc = (('speak', 'speak', 5), + ('glower', 'glower', 5), + ('phone', 'phone', 3.5), + ('finger-wag', 'finger-wag', 5)) +tm = (('speak', 'speak', 5), + ('throw-paper', 'throw-paper', 5), + ('pickpocket', 'pickpocket', 5), + ('roll-o-dex', 'roll-o-dex', 5), + ('finger-wag', 'finger-wag', 5)) +nd = (('pickpocket', 'pickpocket', 5), + ('roll-o-dex', 'roll-o-dex', 5), + ('magic3', 'magic3', 5), + ('smile', 'smile', 5)) +gh = (('speak', 'speak', 5), ('pen-squirt', 'fountain-pen', 5), ('rubber-stamp', 'rubber-stamp', 5)) +ms = (('effort', 'effort', 5), + ('throw-paper', 'throw-paper', 5), + ('stomp', 'stomp', 5), + ('quick-jump', 'jump', 6)) +tf = (('phone', 'phone', 5), + ('smile', 'smile', 5), + ('throw-object', 'throw-object', 5), + ('glower', 'glower', 5)) +m = (('speak', 'speak', 5), + ('magic2', 'magic2', 5), + ('magic1', 'magic1', 5), + ('golf-club-swing', 'golf-club-swing', 5)) +mh = (('magic1', 'magic1', 5), + ('smile', 'smile', 5), + ('golf-club-swing', 'golf-club-swing', 5), + ('song-and-dance', 'song-and-dance', 5)) +sc = (('throw-paper', 'throw-paper', 3.5), ('watercooler', 'watercooler', 5), ('pickpocket', 'pickpocket', 5)) +pp = (('throw-paper', 'throw-paper', 5), ('glower', 'glower', 5), ('finger-wag', 'fingerwag', 5)) +tw = (('throw-paper', 'throw-paper', 3.5), + ('glower', 'glower', 5), + ('magic2', 'magic2', 5), + ('finger-wag', 'finger-wag', 5)) +bc = (('phone', 'phone', 5), ('hold-pencil', 'hold-pencil', 5)) +nc = (('phone', 'phone', 5), ('throw-object', 'throw-object', 5)) +mb = (('magic1', 'magic1', 5), ('throw-paper', 'throw-paper', 3.5)) +ls = (('throw-paper', 'throw-paper', 5), ('throw-object', 'throw-object', 5), ('hold-pencil', 'hold-pencil', 5)) +rb = (('glower', 'glower', 5), ('magic1', 'magic1', 5), ('golf-club-swing', 'golf-club-swing', 5)) +bf = (('pickpocket', 'pickpocket', 5), + ('rubber-stamp', 'rubber-stamp', 5), + ('shredder', 'shredder', 3.5), + ('watercooler', 'watercooler', 5)) +b = (('effort', 'effort', 5), + ('throw-paper', 'throw-paper', 5), + ('throw-object', 'throw-object', 5), + ('magic1', 'magic1', 5)) +dt = (('rubber-stamp', 'rubber-stamp', 5), + ('throw-paper', 'throw-paper', 5), + ('speak', 'speak', 5), + ('finger-wag', 'fingerwag', 5), + ('throw-paper', 'throw-paper', 5)) +ac = (('throw-object', 'throw-object', 5), + ('roll-o-dex', 'roll-o-dex', 5), + ('stomp', 'stomp', 5), + ('phone', 'phone', 5), + ('throw-paper', 'throw-paper', 5)) +bs = (('magic1', 'magic1', 5), ('throw-paper', 'throw-paper', 5), ('finger-wag', 'fingerwag', 5)) +sd = (('magic2', 'magic2', 5), + ('quick-jump', 'jump', 6), + ('stomp', 'stomp', 5), + ('magic3', 'magic3', 5), + ('hold-pencil', 'hold-pencil', 5), + ('throw-paper', 'throw-paper', 5)) +le = (('speak', 'speak', 5), + ('throw-object', 'throw-object', 5), + ('glower', 'glower', 5), + ('throw-paper', 'throw-paper', 5)) +bw = (('finger-wag', 'fingerwag', 5), + ('cigar-smoke', 'cigar-smoke', 8), + ('gavel', 'gavel', 8), + ('magic1', 'magic1', 5), + ('throw-object', 'throw-object', 5), + ('throw-paper', 'throw-paper', 5)) +if not base.config.GetBool('want-new-cogs', 0): + ModelDict = {'a': ('/models/char/suitA-', 4), + 'b': ('/models/char/suitB-', 4), + 'c': ('/models/char/suitC-', 3.5)} + TutorialModelDict = {'a': ('/models/char/suitA-', 4), + 'b': ('/models/char/suitB-', 4), + 'c': ('/models/char/suitC-', 3.5)} +else: + ModelDict = {'a': ('/models/char/tt_a_ene_cga_', 4), + 'b': ('/models/char/tt_a_ene_cgb_', 4), + 'c': ('/models/char/tt_a_ene_cgc_', 3.5)} + TutorialModelDict = {'a': ('/models/char/tt_a_ene_cga_', 4), + 'b': ('/models/char/tt_a_ene_cgb_', 4), + 'c': ('/models/char/tt_a_ene_cgc_', 3.5)} +HeadModelDict = {'a': ('/models/char/suitA-', 4), + 'b': ('/models/char/suitB-', 4), + 'c': ('/models/char/suitC-', 3.5)} + +def loadTutorialSuit(): + loader.loadModelNode('phase_3.5/models/char/suitC-mod') + loadDialog(1) + + +def loadSuits(level): + loadSuitModelsAndAnims(level, flag=1) + loadDialog(level) + + +def unloadSuits(level): + loadSuitModelsAndAnims(level, flag=0) + unloadDialog(level) + + +def loadSuitModelsAndAnims(level, flag = 0): + for key in ModelDict.keys(): + model, phase = ModelDict[key] + if base.config.GetBool('want-new-cogs', 0): + headModel, headPhase = HeadModelDict[key] + else: + headModel, headPhase = ModelDict[key] + if flag: + if base.config.GetBool('want-new-cogs', 0): + filepath = 'phase_3.5' + model + 'zero' + if cogExists(model + 'zero.bam'): + loader.loadModelNode(filepath) + else: + loader.loadModelNode('phase_3.5' + model + 'mod') + loader.loadModelNode('phase_' + str(headPhase) + headModel + 'heads') + else: + if base.config.GetBool('want-new-cogs', 0): + filepath = 'phase_3.5' + model + 'zero' + if cogExists(model + 'zero.bam'): + loader.unloadModel(filepath) + else: + loader.unloadModel('phase_3.5' + model + 'mod') + loader.unloadModel('phase_' + str(headPhase) + headModel + 'heads') + + +def cogExists(filePrefix): + searchPath = DSearchPath() + if AppRunnerGlobal.appRunner: + searchPath.appendDirectory(Filename.expandFrom('$TT_3_5_ROOT/phase_3.5')) + else: + basePath = os.path.expandvars('$TTMODELS') or './ttmodels' + searchPath.appendDirectory(Filename.fromOsSpecific(basePath + '/built/phase_3.5')) + filePrefix = filePrefix.strip('/') + pfile = Filename(filePrefix) + found = vfs.resolveFilename(pfile, searchPath) + if not found: + return False + return True + + +def loadSuitAnims(suit, flag = 1): + if suit in SuitDNA.suitHeadTypes: + try: + animList = eval(suit) + except NameError: + animList = () + + else: + print 'Invalid suit name: ', suit + return -1 + for anim in animList: + phase = 'phase_' + str(anim[2]) + filePrefix = ModelDict[bodyType][0] + animName = filePrefix + anim[1] + if flag: + loader.loadModelNode(animName) + else: + loader.unloadModel(animName) + + +def loadDialog(level): + global SuitDialogArray + if len(SuitDialogArray) > 0: + return + else: + loadPath = 'phase_3.5/audio/dial/' + SuitDialogFiles = ['COG_VO_grunt', + 'COG_VO_murmur', + 'COG_VO_statement', + 'COG_VO_question'] + for file in SuitDialogFiles: + SuitDialogArray.append(base.loadSfx(loadPath + file + '.mp3')) + + SuitDialogArray.append(SuitDialogArray[2]) + SuitDialogArray.append(SuitDialogArray[2]) + + +def loadSkelDialog(): + global SkelSuitDialogArray + if len(SkelSuitDialogArray) > 0: + return + else: + grunt = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_grunt.mp3') + murmur = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_murmur.mp3') + statement = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_statement.mp3') + question = loader.loadSfx('phase_5/audio/sfx/Skel_COG_VO_question.mp3') + SkelSuitDialogArray = [grunt, + murmur, + statement, + question, + statement, + statement] + + +def unloadDialog(level): + global SuitDialogArray + SuitDialogArray = [] + + +def unloadSkelDialog(): + global SkelSuitDialogArray + SkelSuitDialogArray = [] + + +def attachSuitHead(node, suitName): + suitIndex = SuitDNA.suitHeadTypes.index(suitName) + suitDNA = SuitDNA.SuitDNA() + suitDNA.newSuit(suitName) + suit = Suit() + suit.setDNA(suitDNA) + headParts = suit.getHeadParts() + head = node.attachNewNode('head') + for part in headParts: + copyPart = part.copyTo(head) + copyPart.setDepthTest(1) + copyPart.setDepthWrite(1) + + suit.delete() + suit = None + p1 = Point3() + p2 = Point3() + head.calcTightBounds(p1, p2) + d = p2 - p1 + biggest = max(d[0], d[2]) + column = suitIndex % SuitDNA.suitsPerDept + s = (0.2 + column / 100.0) / biggest + pos = -0.14 + (SuitDNA.suitsPerDept - column - 1) / 135.0 + head.setPosHprScale(0, 0, pos, 180, 0, 0, s, s, s) + return head + + +class Suit(Avatar.Avatar): + healthColors = (Vec4(0, 1, 0, 1), + Vec4(1, 1, 0, 1), + Vec4(1, 0.5, 0, 1), + Vec4(1, 0, 0, 1), + Vec4(0.3, 0.3, 0.3, 1)) + healthGlowColors = (Vec4(0.25, 1, 0.25, 0.5), + Vec4(1, 1, 0.25, 0.5), + Vec4(1, 0.5, 0.25, 0.5), + Vec4(1, 0.25, 0.25, 0.5), + Vec4(0.3, 0.3, 0.3, 0)) + medallionColors = {'c': Vec4(0.863, 0.776, 0.769, 1.0), + 's': Vec4(0.843, 0.745, 0.745, 1.0), + 'l': Vec4(0.749, 0.776, 0.824, 1.0), + 'm': Vec4(0.749, 0.769, 0.749, 1.0)} + + def __init__(self): + try: + self.Suit_initialized + return + except: + self.Suit_initialized = 1 + + Avatar.Avatar.__init__(self) + self.setFont(ToontownGlobals.getSuitFont()) + self.setPlayerType(NametagGroup.CCSuit) + self.setPickable(1) + self.leftHand = None + self.rightHand = None + self.shadowJoint = None + self.nametagJoint = None + self.headParts = [] + self.healthBar = None + self.healthCondition = 0 + self.isDisguised = 0 + self.isWaiter = 0 + self.isRental = 0 + return + + def delete(self): + try: + self.Suit_deleted + except: + self.Suit_deleted = 1 + if self.leftHand: + self.leftHand.removeNode() + self.leftHand = None + if self.rightHand: + self.rightHand.removeNode() + self.rightHand = None + if self.shadowJoint: + self.shadowJoint.removeNode() + self.shadowJoint = None + if self.nametagJoint: + self.nametagJoint.removeNode() + self.nametagJoint = None + for part in self.headParts: + part.removeNode() + + self.headParts = [] + self.removeHealthBar() + Avatar.Avatar.delete(self) + + return + + def setHeight(self, height): + Avatar.Avatar.setHeight(self, height) + self.nametag3d.setPos(0, 0, height + 1.0) + + def getRadius(self): + return 2 + + def setDNAString(self, dnaString): + self.dna = SuitDNA.SuitDNA() + self.dna.makeFromNetString(dnaString) + self.setDNA(self.dna) + + def setDNA(self, dna): + if self.style: + pass + else: + self.style = dna + self.generateSuit() + self.initializeDropShadow() + self.initializeNametag3d() + + def generateSuit(self): + dna = self.style + self.headParts = [] + self.headColor = None + self.headTexture = None + self.loseActor = None + self.isSkeleton = 0 + if dna.name == 'f': + self.scale = 4.0 / cSize + self.handColor = SuitDNA.corpPolyColor + self.generateBody() + self.generateHead('flunky') + self.generateHead('glasses') + self.setHeight(4.88) + elif dna.name == 'p': + self.scale = 3.35 / bSize + self.handColor = SuitDNA.corpPolyColor + self.generateBody() + self.generateHead('pencilpusher') + self.setHeight(5.0) + elif dna.name == 'ym': + self.scale = 4.125 / aSize + self.handColor = SuitDNA.corpPolyColor + self.generateBody() + self.generateHead('yesman') + self.setHeight(5.28) + elif dna.name == 'mm': + self.scale = 2.5 / cSize + self.handColor = SuitDNA.corpPolyColor + self.generateBody() + self.generateHead('micromanager') + self.setHeight(3.25) + elif dna.name == 'ds': + self.scale = 4.5 / bSize + self.handColor = SuitDNA.corpPolyColor + self.generateBody() + self.generateHead('beancounter') + self.setHeight(6.08) + elif dna.name == 'hh': + self.scale = 6.5 / aSize + self.handColor = SuitDNA.corpPolyColor + self.generateBody() + self.generateHead('headhunter') + self.setHeight(7.45) + elif dna.name == 'cr': + self.scale = 6.75 / cSize + self.handColor = VBase4(0.85, 0.55, 0.55, 1.0) + self.generateBody() + self.headTexture = 'corporate-raider.jpg' + self.generateHead('flunky') + self.setHeight(8.23) + elif dna.name == 'tbc': + self.scale = 7.0 / aSize + self.handColor = VBase4(0.75, 0.95, 0.75, 1.0) + self.generateBody() + self.generateHead('bigcheese') + self.setHeight(9.34) + elif dna.name == 'bf': + self.scale = 4.0 / cSize + self.handColor = SuitDNA.legalPolyColor + self.generateBody() + self.headTexture = 'bottom-feeder.jpg' + self.generateHead('tightwad') + self.setHeight(4.81) + elif dna.name == 'b': + self.scale = 4.375 / bSize + self.handColor = VBase4(0.95, 0.95, 1.0, 1.0) + self.generateBody() + self.headTexture = 'blood-sucker.jpg' + self.generateHead('movershaker') + self.setHeight(6.17) + elif dna.name == 'dt': + self.scale = 4.25 / aSize + self.handColor = SuitDNA.legalPolyColor + self.generateBody() + self.headTexture = 'double-talker.jpg' + self.generateHead('twoface') + self.setHeight(5.63) + elif dna.name == 'ac': + self.scale = 4.35 / bSize + self.handColor = SuitDNA.legalPolyColor + self.generateBody() + self.generateHead('ambulancechaser') + self.setHeight(6.39) + elif dna.name == 'bs': + self.scale = 4.5 / aSize + self.handColor = SuitDNA.legalPolyColor + self.generateBody() + self.generateHead('backstabber') + self.setHeight(6.71) + elif dna.name == 'sd': + self.scale = 5.65 / bSize + self.handColor = VBase4(0.5, 0.8, 0.75, 1.0) + self.generateBody() + self.headTexture = 'spin-doctor.jpg' + self.generateHead('telemarketer') + self.setHeight(7.9) + elif dna.name == 'le': + self.scale = 7.125 / aSize + self.handColor = VBase4(0.25, 0.25, 0.5, 1.0) + self.generateBody() + self.generateHead('legaleagle') + self.setHeight(8.27) + elif dna.name == 'bw': + self.scale = 7.0 / aSize + self.handColor = SuitDNA.legalPolyColor + self.generateBody() + self.generateHead('bigwig') + self.setHeight(8.69) + elif dna.name == 'sc': + self.scale = 3.6 / cSize + self.handColor = SuitDNA.moneyPolyColor + self.generateBody() + self.generateHead('coldcaller') + self.setHeight(4.77) + elif dna.name == 'pp': + self.scale = 3.55 / aSize + self.handColor = VBase4(1.0, 0.5, 0.6, 1.0) + self.generateBody() + self.generateHead('pennypincher') + self.setHeight(5.26) + elif dna.name == 'tw': + self.scale = 4.5 / cSize + self.handColor = SuitDNA.moneyPolyColor + self.generateBody() + self.generateHead('tightwad') + self.setHeight(5.41) + elif dna.name == 'bc': + self.scale = 4.4 / bSize + self.handColor = SuitDNA.moneyPolyColor + self.generateBody() + self.generateHead('beancounter') + self.setHeight(5.95) + elif dna.name == 'nc': + self.scale = 5.25 / aSize + self.handColor = SuitDNA.moneyPolyColor + self.generateBody() + self.generateHead('numbercruncher') + self.setHeight(7.22) + elif dna.name == 'mb': + self.scale = 5.3 / cSize + self.handColor = SuitDNA.moneyPolyColor + self.generateBody() + self.generateHead('moneybags') + self.setHeight(6.97) + elif dna.name == 'ls': + self.scale = 6.5 / bSize + self.handColor = VBase4(0.5, 0.85, 0.75, 1.0) + self.generateBody() + self.generateHead('loanshark') + self.setHeight(8.58) + elif dna.name == 'rb': + self.scale = 7.0 / aSize + self.handColor = SuitDNA.moneyPolyColor + self.generateBody() + self.headTexture = 'robber-baron.jpg' + self.generateHead('yesman') + self.setHeight(8.95) + elif dna.name == 'cc': + self.scale = 3.5 / cSize + self.handColor = VBase4(0.55, 0.65, 1.0, 1.0) + self.headColor = VBase4(0.25, 0.35, 1.0, 1.0) + self.generateBody() + self.generateHead('coldcaller') + self.setHeight(4.63) + elif dna.name == 'tm': + self.scale = 3.75 / bSize + self.handColor = SuitDNA.salesPolyColor + self.generateBody() + self.generateHead('telemarketer') + self.setHeight(5.24) + elif dna.name == 'nd': + self.scale = 4.35 / aSize + self.handColor = SuitDNA.salesPolyColor + self.generateBody() + self.headTexture = 'name-dropper.jpg' + self.generateHead('numbercruncher') + self.setHeight(5.98) + elif dna.name == 'gh': + self.scale = 4.75 / cSize + self.handColor = SuitDNA.salesPolyColor + self.generateBody() + self.generateHead('gladhander') + self.setHeight(6.4) + elif dna.name == 'ms': + self.scale = 4.75 / bSize + self.handColor = SuitDNA.salesPolyColor + self.generateBody() + self.generateHead('movershaker') + self.setHeight(6.7) + elif dna.name == 'tf': + self.scale = 5.25 / aSize + self.handColor = SuitDNA.salesPolyColor + self.generateBody() + self.generateHead('twoface') + self.setHeight(6.95) + elif dna.name == 'm': + self.scale = 5.75 / aSize + self.handColor = SuitDNA.salesPolyColor + self.generateBody() + self.headTexture = 'mingler.jpg' + self.generateHead('twoface') + self.setHeight(7.61) + elif dna.name == 'mh': + self.scale = 7.0 / aSize + self.handColor = SuitDNA.salesPolyColor + self.generateBody() + self.generateHead('yesman') + self.setHeight(8.95) + self.setName(SuitBattleGlobals.SuitAttributes[dna.name]['name']) + self.getGeomNode().setScale(self.scale) + self.generateHealthBar() + self.generateCorporateMedallion() + return + + def generateBody(self): + animDict = self.generateAnimDict() + filePrefix, bodyPhase = ModelDict[self.style.body] + if base.config.GetBool('want-new-cogs', 0): + if cogExists(filePrefix + 'zero.bam'): + self.loadModel('phase_3.5' + filePrefix + 'zero') + else: + self.loadModel('phase_3.5' + filePrefix + 'mod') + else: + self.loadModel('phase_3.5' + filePrefix + 'mod') + self.loadAnims(animDict) + self.setSuitClothes() + + def generateAnimDict(self): + animDict = {} + filePrefix, bodyPhase = ModelDict[self.style.body] + for anim in AllSuits: + animDict[anim[0]] = 'phase_' + str(bodyPhase) + filePrefix + anim[1] + + for anim in AllSuitsMinigame: + animDict[anim[0]] = 'phase_4' + filePrefix + anim[1] + + for anim in AllSuitsTutorialBattle: + filePrefix, bodyPhase = TutorialModelDict[self.style.body] + animDict[anim[0]] = 'phase_' + str(bodyPhase) + filePrefix + anim[1] + + for anim in AllSuitsBattle: + animDict[anim[0]] = 'phase_5' + filePrefix + anim[1] + + if not base.config.GetBool('want-new-cogs', 0): + if self.style.body == 'a': + animDict['neutral'] = 'phase_4/models/char/suitA-neutral' + for anim in SuitsCEOBattle: + animDict[anim[0]] = 'phase_12/models/char/suitA-' + anim[1] + + elif self.style.body == 'b': + animDict['neutral'] = 'phase_4/models/char/suitB-neutral' + for anim in SuitsCEOBattle: + animDict[anim[0]] = 'phase_12/models/char/suitB-' + anim[1] + + elif self.style.body == 'c': + animDict['neutral'] = 'phase_3.5/models/char/suitC-neutral' + for anim in SuitsCEOBattle: + animDict[anim[0]] = 'phase_12/models/char/suitC-' + anim[1] + + try: + animList = eval(self.style.name) + except NameError: + animList = () + + for anim in animList: + phase = 'phase_' + str(anim[2]) + animDict[anim[0]] = phase + filePrefix + anim[1] + + return animDict + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def setSuitClothes(self, modelRoot = None): + if not modelRoot: + modelRoot = self + dept = self.style.dept + phase = 3.5 + + def __doItTheOldWay__(): + torsoTex = loader.loadTexture('phase_%s/maps/%s_blazer.jpg' % (phase, dept)) + torsoTex.setMinfilter(Texture.FTLinearMipmapLinear) + torsoTex.setMagfilter(Texture.FTLinear) + legTex = loader.loadTexture('phase_%s/maps/%s_leg.jpg' % (phase, dept)) + legTex.setMinfilter(Texture.FTLinearMipmapLinear) + legTex.setMagfilter(Texture.FTLinear) + armTex = loader.loadTexture('phase_%s/maps/%s_sleeve.jpg' % (phase, dept)) + armTex.setMinfilter(Texture.FTLinearMipmapLinear) + armTex.setMagfilter(Texture.FTLinear) + modelRoot.find('**/torso').setTexture(torsoTex, 1) + modelRoot.find('**/arms').setTexture(armTex, 1) + modelRoot.find('**/legs').setTexture(legTex, 1) + modelRoot.find('**/hands').setColor(self.handColor) + self.leftHand = self.find('**/joint_Lhold') + self.rightHand = self.find('**/joint_Rhold') + self.shadowJoint = self.find('**/joint_shadow') + self.nametagJoint = self.find('**/joint_nameTag') + + if base.config.GetBool('want-new-cogs', 0): + if dept == 'c': + texType = 'bossbot' + elif dept == 'm': + texType = 'cashbot' + elif dept == 'l': + texType = 'lawbot' + elif dept == 's': + texType = 'sellbot' + if self.find('**/body').isEmpty(): + __doItTheOldWay__() + else: + filepath = 'phase_3.5/maps/tt_t_ene_' + texType + '.jpg' + if cogExists('/maps/tt_t_ene_' + texType + '.jpg'): + bodyTex = loader.loadTexture(filepath) + self.find('**/body').setTexture(bodyTex, 1) + self.leftHand = self.find('**/def_joint_left_hold') + self.rightHand = self.find('**/def_joint_right_hold') + self.shadowJoint = self.find('**/def_shadow') + self.nametagJoint = self.find('**/def_nameTag') + else: + __doItTheOldWay__() + + def makeWaiter(self, modelRoot = None): + if not modelRoot: + modelRoot = self + self.isWaiter = 1 + torsoTex = loader.loadTexture('phase_3.5/maps/waiter_m_blazer.jpg') + torsoTex.setMinfilter(Texture.FTLinearMipmapLinear) + torsoTex.setMagfilter(Texture.FTLinear) + legTex = loader.loadTexture('phase_3.5/maps/waiter_m_leg.jpg') + legTex.setMinfilter(Texture.FTLinearMipmapLinear) + legTex.setMagfilter(Texture.FTLinear) + armTex = loader.loadTexture('phase_3.5/maps/waiter_m_sleeve.jpg') + armTex.setMinfilter(Texture.FTLinearMipmapLinear) + armTex.setMagfilter(Texture.FTLinear) + modelRoot.find('**/torso').setTexture(torsoTex, 1) + modelRoot.find('**/arms').setTexture(armTex, 1) + modelRoot.find('**/legs').setTexture(legTex, 1) + + def makeRentalSuit(self, suitType, modelRoot = None): + if not modelRoot: + modelRoot = self.getGeomNode() + if suitType == 's': + torsoTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_blazer.jpg') + legTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_leg.jpg') + armTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_sleeve.jpg') + handTex = loader.loadTexture('phase_3.5/maps/tt_t_ene_sellbotRental_hand.jpg') + else: + self.notify.warning('No rental suit for cog type %s' % suitType) + return + self.isRental = 1 + modelRoot.find('**/torso').setTexture(torsoTex, 1) + modelRoot.find('**/arms').setTexture(armTex, 1) + modelRoot.find('**/legs').setTexture(legTex, 1) + modelRoot.find('**/hands').setTexture(handTex, 1) + + def generateHead(self, headType): + if base.config.GetBool('want-new-cogs', 0): + filePrefix, phase = HeadModelDict[self.style.body] + else: + filePrefix, phase = ModelDict[self.style.body] + headModel = loader.loadModel('phase_' + str(phase) + filePrefix + 'heads') + headReferences = headModel.findAllMatches('**/' + headType) + for i in range(0, headReferences.getNumPaths()): + if base.config.GetBool('want-new-cogs', 0): + headPart = self.instance(headReferences.getPath(i), 'modelRoot', 'to_head') + if not headPart: + headPart = self.instance(headReferences.getPath(i), 'modelRoot', 'joint_head') + else: + headPart = self.instance(headReferences.getPath(i), 'modelRoot', 'joint_head') + if self.headTexture: + headTex = loader.loadTexture('phase_' + str(phase) + '/maps/' + self.headTexture) + headTex.setMinfilter(Texture.FTLinearMipmapLinear) + headTex.setMagfilter(Texture.FTLinear) + headPart.setTexture(headTex, 1) + if self.headColor: + headPart.setColor(self.headColor) + self.headParts.append(headPart) + + headModel.removeNode() + + def generateCorporateTie(self, modelPath = None): + if not modelPath: + modelPath = self + dept = self.style.dept + tie = modelPath.find('**/tie') + if tie.isEmpty(): + self.notify.warning('skelecog has no tie model!!!') + return + if dept == 'c': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_boss.jpg') + elif dept == 's': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_sales.jpg') + elif dept == 'l': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_legal.jpg') + elif dept == 'm': + tieTex = loader.loadTexture('phase_5/maps/cog_robot_tie_money.jpg') + tieTex.setMinfilter(Texture.FTLinearMipmapLinear) + tieTex.setMagfilter(Texture.FTLinear) + tie.setTexture(tieTex, 1) + + def generateCorporateMedallion(self): + icons = loader.loadModel('phase_3/models/gui/cog_icons') + dept = self.style.dept + if base.config.GetBool('want-new-cogs', 0): + chestNull = self.find('**/def_joint_attachMeter') + if chestNull.isEmpty(): + chestNull = self.find('**/joint_attachMeter') + else: + chestNull = self.find('**/joint_attachMeter') + if dept == 'c': + self.corpMedallion = icons.find('**/CorpIcon').copyTo(chestNull) + elif dept == 's': + self.corpMedallion = icons.find('**/SalesIcon').copyTo(chestNull) + elif dept == 'l': + self.corpMedallion = icons.find('**/LegalIcon').copyTo(chestNull) + elif dept == 'm': + self.corpMedallion = icons.find('**/MoneyIcon').copyTo(chestNull) + self.corpMedallion.setPosHprScale(0.02, 0.05, 0.04, 180.0, 0.0, 0.0, 0.51, 0.51, 0.51) + self.corpMedallion.setColor(self.medallionColors[dept]) + icons.removeNode() + + def generateHealthBar(self): + self.removeHealthBar() + model = loader.loadModel('phase_3.5/models/gui/matching_game_gui') + button = model.find('**/minnieCircle') + button.setScale(3.0) + button.setH(180.0) + button.setColor(self.healthColors[0]) + if base.config.GetBool('want-new-cogs', 0): + chestNull = self.find('**/def_joint_attachMeter') + if chestNull.isEmpty(): + chestNull = self.find('**/joint_attachMeter') + else: + chestNull = self.find('**/joint_attachMeter') + button.reparentTo(chestNull) + self.healthBar = button + glow = BattleProps.globalPropPool.getProp('glow') + glow.reparentTo(self.healthBar) + glow.setScale(0.28) + glow.setPos(-0.005, 0.01, 0.015) + glow.setColor(self.healthGlowColors[0]) + button.flattenLight() + self.healthBarGlow = glow + self.healthBar.hide() + self.healthCondition = 0 + + def reseatHealthBarForSkele(self): + self.healthBar.setPos(0.0, 0.1, 0.0) + + def updateHealthBar(self, hp, forceUpdate = 0): + if hp > self.currHP: + hp = self.currHP + self.currHP -= hp + health = float(self.currHP) / float(self.maxHP) + if health > 0.95: + condition = 0 + elif health > 0.7: + condition = 1 + elif health > 0.3: + condition = 2 + elif health > 0.05: + condition = 3 + elif health > 0.0: + condition = 4 + else: + condition = 5 + if self.healthCondition != condition or forceUpdate: + if condition == 4: + blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75), Task(self.__blinkGray), Task.pause(0.1)) + taskMgr.add(blinkTask, self.uniqueName('blink-task')) + elif condition == 5: + if self.healthCondition == 4: + taskMgr.remove(self.uniqueName('blink-task')) + blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25), Task(self.__blinkGray), Task.pause(0.1)) + taskMgr.add(blinkTask, self.uniqueName('blink-task')) + else: + self.healthBar.setColor(self.healthColors[condition], 1) + self.healthBarGlow.setColor(self.healthGlowColors[condition], 1) + self.healthCondition = condition + + def __blinkRed(self, task): + self.healthBar.setColor(self.healthColors[3], 1) + self.healthBarGlow.setColor(self.healthGlowColors[3], 1) + if self.healthCondition == 5: + self.healthBar.setScale(1.17) + return Task.done + + def __blinkGray(self, task): + if not self.healthBar: + return + self.healthBar.setColor(self.healthColors[4], 1) + self.healthBarGlow.setColor(self.healthGlowColors[4], 1) + if self.healthCondition == 5: + self.healthBar.setScale(1.0) + return Task.done + + def removeHealthBar(self): + if self.healthBar: + self.healthBar.removeNode() + self.healthBar = None + if self.healthCondition == 4 or self.healthCondition == 5: + taskMgr.remove(self.uniqueName('blink-task')) + self.healthCondition = 0 + return + + def getLoseActor(self): + if base.config.GetBool('want-new-cogs', 0): + if self.find('**/body'): + return self + if self.loseActor == None: + if not self.isSkeleton: + filePrefix, phase = TutorialModelDict[self.style.body] + loseModel = 'phase_' + str(phase) + filePrefix + 'lose-mod' + loseAnim = 'phase_' + str(phase) + filePrefix + 'lose' + self.loseActor = Actor.Actor(loseModel, {'lose': loseAnim}) + loseNeck = self.loseActor.find('**/joint_head') + for part in self.headParts: + part.instanceTo(loseNeck) + + if self.isWaiter: + self.makeWaiter(self.loseActor) + else: + self.setSuitClothes(self.loseActor) + else: + loseModel = 'phase_5/models/char/cog' + string.upper(self.style.body) + '_robot-lose-mod' + filePrefix, phase = TutorialModelDict[self.style.body] + loseAnim = 'phase_' + str(phase) + filePrefix + 'lose' + self.loseActor = Actor.Actor(loseModel, {'lose': loseAnim}) + self.generateCorporateTie(self.loseActor) + self.loseActor.setScale(self.scale) + self.loseActor.setPos(self.getPos()) + self.loseActor.setHpr(self.getHpr()) + shadowJoint = self.loseActor.find('**/joint_shadow') + dropShadow = loader.loadModel('phase_3/models/props/drop_shadow') + dropShadow.setScale(0.45) + dropShadow.setColor(0.0, 0.0, 0.0, 0.5) + dropShadow.reparentTo(shadowJoint) + return self.loseActor + + def cleanupLoseActor(self): + self.notify.debug('cleanupLoseActor()') + if self.loseActor != None: + self.notify.debug('cleanupLoseActor() - got one') + self.loseActor.cleanup() + self.loseActor = None + return + + def makeSkeleton(self): + model = 'phase_5/models/char/cog' + string.upper(self.style.body) + '_robot-zero' + anims = self.generateAnimDict() + anim = self.getCurrentAnim() + dropShadow = self.dropShadow + if not dropShadow.isEmpty(): + dropShadow.reparentTo(hidden) + self.removePart('modelRoot') + self.loadModel(model) + self.loadAnims(anims) + self.getGeomNode().setScale(self.scale * 1.0173) + self.generateHealthBar() + self.generateCorporateMedallion() + self.generateCorporateTie() + self.setHeight(self.height) + parts = self.findAllMatches('**/pPlane*') + for partNum in range(0, parts.getNumPaths()): + bb = parts.getPath(partNum) + bb.setTwoSided(1) + + self.setName(TTLocalizer.Skeleton) + nameInfo = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': self.getActualLevel()} + self.setDisplayName(nameInfo) + self.leftHand = self.find('**/joint_Lhold') + self.rightHand = self.find('**/joint_Rhold') + self.shadowJoint = self.find('**/joint_shadow') + self.nametagNull = self.find('**/joint_nameTag') + if not dropShadow.isEmpty(): + dropShadow.setScale(0.75) + if not self.shadowJoint.isEmpty(): + dropShadow.reparentTo(self.shadowJoint) + self.loop(anim) + self.isSkeleton = 1 + + def getHeadParts(self): + return self.headParts + + def getRightHand(self): + return self.rightHand + + def getLeftHand(self): + return self.leftHand + + def getShadowJoint(self): + return self.shadowJoint + + def getNametagJoints(self): + return [] + + def getDialogueArray(self): + if self.isSkeleton: + loadSkelDialog() + return SkelSuitDialogArray + else: + return SuitDialogArray diff --git a/toontown/suit/SuitAvatarPanel.py b/toontown/suit/SuitAvatarPanel.py new file mode 100644 index 0000000..be400b0 --- /dev/null +++ b/toontown/suit/SuitAvatarPanel.py @@ -0,0 +1,71 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from otp.avatar import Avatar +from direct.distributed import DistributedObject +import SuitDNA +from toontown.toonbase import TTLocalizer +from otp.avatar import AvatarPanel +from toontown.friends import FriendsListPanel + +class SuitAvatarPanel(AvatarPanel.AvatarPanel): + currentAvatarPanel = None + + def __init__(self, avatar): + AvatarPanel.AvatarPanel.__init__(self, avatar, FriendsListPanel=FriendsListPanel) + self.avName = avatar.getName() + gui = loader.loadModel('phase_3.5/models/gui/suit_detail_panel') + self.frame = DirectFrame(geom=gui.find('**/avatar_panel'), geom_scale=0.21, geom_pos=(0, 0, 0.02), relief=None, pos=(1.1, 100, 0.525)) + disabledImageColor = Vec4(1, 1, 1, 0.4) + text0Color = Vec4(1, 1, 1, 1) + text1Color = Vec4(0.5, 1, 0.5, 1) + text2Color = Vec4(1, 1, 0.5, 1) + text3Color = Vec4(1, 1, 1, 0.2) + self.head = self.frame.attachNewNode('head') + for part in avatar.headParts: + copyPart = part.copyTo(self.head) + copyPart.setDepthTest(1) + copyPart.setDepthWrite(1) + + p1 = Point3() + p2 = Point3() + self.head.calcTightBounds(p1, p2) + d = p2 - p1 + biggest = max(d[0], d[1], d[2]) + s = 0.3 / biggest + self.head.setPosHprScale(0, 0, 0, 180, 0, 0, s, s, s) + self.nameLabel = DirectLabel(parent=self.frame, pos=(0.0125, 0, 0.36), relief=None, text=self.avName, text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.047, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + level = avatar.getActualLevel() + dept = SuitDNA.getSuitDeptFullname(avatar.dna.name) + self.levelLabel = DirectLabel(parent=self.frame, pos=(0, 0, -0.1), relief=None, text=TTLocalizer.AvatarPanelCogLevel % level, text_font=avatar.getFont(), text_align=TextNode.ACenter, text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.05, text_wordwrap=8.0) + corpIcon = avatar.corpMedallion.copyTo(hidden) + corpIcon.iPosHprScale() + self.corpIcon = DirectLabel(parent=self.frame, geom=corpIcon, geom_scale=0.13, pos=(0, 0, -0.175), relief=None) + corpIcon.removeNode() + self.deptLabel = DirectLabel(parent=self.frame, pos=(0, 0, -0.28), relief=None, text=dept, text_font=avatar.getFont(), text_align=TextNode.ACenter, text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.05, text_wordwrap=8.0) + self.closeButton = DirectButton(parent=self.frame, relief=None, pos=(0.0, 0, -0.36), text=TTLocalizer.AvatarPanelCogDetailClose, text_font=avatar.getFont(), text0_fg=Vec4(0, 0, 0, 1), text1_fg=Vec4(0.5, 0, 0, 1), text2_fg=Vec4(1, 0, 0, 1), text_pos=(0, 0), text_scale=0.05, command=self.__handleClose) + gui.removeNode() + menuX = -0.05 + menuScale = 0.064 + base.localAvatar.obscureFriendsListButton(1) + self.frame.show() + messenger.send('avPanelDone') + return + + def cleanup(self): + if self.frame == None: + return + self.frame.destroy() + del self.frame + self.frame = None + self.head.removeNode() + del self.head + base.localAvatar.obscureFriendsListButton(-1) + AvatarPanel.AvatarPanel.cleanup(self) + return + + def __handleClose(self): + self.cleanup() + AvatarPanel.currentAvatarPanel = None + return diff --git a/toontown/suit/SuitBase.py b/toontown/suit/SuitBase.py new file mode 100644 index 0000000..f77f71a --- /dev/null +++ b/toontown/suit/SuitBase.py @@ -0,0 +1,83 @@ +from pandac.PandaModules import * +from direct.distributed.ClockDelta import * +import math +import random +from pandac.PandaModules import Point3 +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import SuitBattleGlobals +import SuitTimings +import SuitDNA +from toontown.toonbase import TTLocalizer +TIME_BUFFER_PER_WPT = 0.25 +TIME_DIVISOR = 100 +DISTRIBUTE_TASK_CREATION = 0 + +class SuitBase: + notify = DirectNotifyGlobal.directNotify.newCategory('SuitBase') + + def __init__(self): + self.dna = None + self.level = 0 + self.maxHP = 10 + self.currHP = 10 + self.isSkelecog = 0 + return + + def delete(self): + pass + + def getStyleName(self): + if hasattr(self, 'dna') and self.dna: + return self.dna.name + else: + self.notify.error('called getStyleName() before dna was set!') + return 'unknown' + + def getStyleDept(self): + if hasattr(self, 'dna') and self.dna: + return SuitDNA.getDeptFullname(self.dna.dept) + else: + self.notify.error('called getStyleDept() before dna was set!') + return 'unknown' + + def getLevel(self): + return self.level + + def setLevel(self, level): + self.level = level + nameWLevel = TTLocalizer.SuitBaseNameWithLevel % {'name': self.name, + 'dept': self.getStyleDept(), + 'level': self.getActualLevel()} + self.setDisplayName(nameWLevel) + attributes = SuitBattleGlobals.SuitAttributes[self.dna.name] + self.maxHP = attributes['hp'][self.level] + self.currHP = self.maxHP + + def getSkelecog(self): + return self.isSkelecog + + def setSkelecog(self, flag): + self.isSkelecog = flag + + def getActualLevel(self): + if hasattr(self, 'dna'): + return SuitBattleGlobals.getActualFromRelativeLevel(self.getStyleName(), self.level) + 1 + else: + self.notify.warning('called getActualLevel with no DNA, returning 1 for level') + return 1 + + def setPath(self, path): + self.path = path + self.pathLength = self.path.getNumPoints() + + def getPath(self): + return self.path + + def printPath(self): + print '%d points in path' % self.pathLength + for currPathPt in range(self.pathLength): + indexVal = self.path.getPointIndex(currPathPt) + print '\t', self.sp.dnaStore.getSuitPointWithIndex(indexVal) + + def makeLegList(self): + self.legList = SuitLegList(self.path, self.sp.dnaStore, self.sp.suitWalkSpeed, SuitTimings.fromSky, SuitTimings.toSky, SuitTimings.fromSuitBuilding, SuitTimings.toSuitBuilding, SuitTimings.toToonBuilding) diff --git a/toontown/suit/SuitDNA.py b/toontown/suit/SuitDNA.py new file mode 100644 index 0000000..e25e1d0 --- /dev/null +++ b/toontown/suit/SuitDNA.py @@ -0,0 +1,269 @@ +import random +from pandac.PandaModules import * +from direct.directnotify.DirectNotifyGlobal import * +from toontown.toonbase import TTLocalizer +import random +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +from otp.avatar import AvatarDNA +notify = directNotify.newCategory('SuitDNA') +suitHeadTypes = ['f', + 'p', + 'ym', + 'mm', + 'ds', + 'hh', + 'cr', + 'tbc', + 'bf', + 'b', + 'dt', + 'ac', + 'bs', + 'sd', + 'le', + 'bw', + 'sc', + 'pp', + 'tw', + 'bc', + 'nc', + 'mb', + 'ls', + 'rb', + 'cc', + 'tm', + 'nd', + 'gh', + 'ms', + 'tf', + 'm', + 'mh'] +suitATypes = ['ym', + 'hh', + 'tbc', + 'dt', + 'bs', + 'le', + 'bw', + 'pp', + 'nc', + 'rb', + 'nd', + 'tf', + 'm', + 'mh'] +suitBTypes = ['p', + 'ds', + 'b', + 'ac', + 'sd', + 'bc', + 'ls', + 'tm', + 'ms'] +suitCTypes = ['f', + 'mm', + 'cr', + 'bf', + 'sc', + 'tw', + 'mb', + 'cc', + 'gh'] +suitDepts = ['c', + 'l', + 'm', + 's'] +suitDeptFullnames = {'c': TTLocalizer.Bossbot, + 'l': TTLocalizer.Lawbot, + 'm': TTLocalizer.Cashbot, + 's': TTLocalizer.Sellbot} +suitDeptFullnamesP = {'c': TTLocalizer.BossbotP, + 'l': TTLocalizer.LawbotP, + 'm': TTLocalizer.CashbotP, + 's': TTLocalizer.SellbotP} +corpPolyColor = VBase4(0.95, 0.75, 0.75, 1.0) +legalPolyColor = VBase4(0.75, 0.75, 0.95, 1.0) +moneyPolyColor = VBase4(0.65, 0.95, 0.85, 1.0) +salesPolyColor = VBase4(0.95, 0.75, 0.95, 1.0) +suitsPerLevel = [1, + 1, + 1, + 1, + 1, + 1, + 1, + 1] +suitsPerDept = 8 +goonTypes = ['pg', 'sg'] + +def getSuitBodyType(name): + if name in suitATypes: + return 'a' + elif name in suitBTypes: + return 'b' + elif name in suitCTypes: + return 'c' + else: + print 'Unknown body type for suit name: ', name + + +def getSuitDept(name): + index = suitHeadTypes.index(name) + if index < suitsPerDept: + return suitDepts[0] + elif index < suitsPerDept * 2: + return suitDepts[1] + elif index < suitsPerDept * 3: + return suitDepts[2] + elif index < suitsPerDept * 4: + return suitDepts[3] + else: + print 'Unknown dept for suit name: ', name + return None + return None + + +def getDeptFullname(dept): + return suitDeptFullnames[dept] + + +def getDeptFullnameP(dept): + return suitDeptFullnamesP[dept] + + +def getSuitDeptFullname(name): + return suitDeptFullnames[getSuitDept(name)] + + +def getSuitType(name): + index = suitHeadTypes.index(name) + return index % suitsPerDept + 1 + + +def getRandomSuitType(level, rng = random): + return random.randint(max(level - 4, 1), min(level, 8)) + + +def getRandomSuitByDept(dept): + deptNumber = suitDepts.index(dept) + return suitHeadTypes[suitsPerDept * deptNumber + random.randint(0, 7)] + + +class SuitDNA(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 == 's': + self.newSuit() + else: + self.type = 'u' + return + + def __str__(self): + if self.type == 's': + return 'type = %s\nbody = %s, dept = %s, name = %s' % ('suit', + self.body, + self.dept, + self.name) + elif self.type == 'b': + return 'type = boss cog\ndept = %s' % self.dept + else: + return 'type undefined' + + def makeNetString(self): + dg = PyDatagram() + dg.addFixedString(self.type, 1) + if self.type == 's': + dg.addFixedString(self.name, 3) + dg.addFixedString(self.dept, 1) + elif self.type == 'b': + dg.addFixedString(self.dept, 1) + 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 == 's': + self.name = dgi.getFixedString(3) + self.dept = dgi.getFixedString(1) + self.body = getSuitBodyType(self.name) + elif self.type == 'b': + self.dept = dgi.getFixedString(1) + else: + notify.error('unknown avatar type: ', self.type) + return None + + def __defaultGoon(self): + self.type = 'g' + self.name = goonTypes[0] + + def __defaultSuit(self): + self.type = 's' + self.name = 'ds' + self.dept = getSuitDept(self.name) + self.body = getSuitBodyType(self.name) + + def newSuit(self, name = None): + if name == None: + self.__defaultSuit() + else: + self.type = 's' + self.name = name + self.dept = getSuitDept(self.name) + self.body = getSuitBodyType(self.name) + return + + def newBossCog(self, dept): + self.type = 'b' + self.dept = dept + + def newSuitRandom(self, level = None, dept = None): + self.type = 's' + if level == None: + level = random.choice(range(1, len(suitsPerLevel))) + elif level < 0 or level > len(suitsPerLevel): + notify.error('Invalid suit level: %d' % level) + if dept == None: + dept = random.choice(suitDepts) + self.dept = dept + index = suitDepts.index(dept) + base = index * suitsPerDept + offset = 0 + if level > 1: + for i in range(1, level): + offset = offset + suitsPerLevel[i - 1] + + bottom = base + offset + top = bottom + suitsPerLevel[level - 1] + self.name = suitHeadTypes[random.choice(range(bottom, top))] + self.body = getSuitBodyType(self.name) + return + + def newGoon(self, name = None): + if type == None: + self.__defaultGoon() + else: + self.type = 'g' + if name in goonTypes: + self.name = name + else: + notify.error('unknown goon type: ', name) + return + + def getType(self): + if self.type == 's': + type = 'suit' + elif self.type == 'b': + type = 'boss' + else: + notify.error('Invalid DNA type: ', self.type) + return type diff --git a/toontown/suit/SuitDialog.py b/toontown/suit/SuitDialog.py new file mode 100644 index 0000000..80606f8 --- /dev/null +++ b/toontown/suit/SuitDialog.py @@ -0,0 +1,33 @@ +import random +from direct.directnotify import DirectNotifyGlobal +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +notify = DirectNotifyGlobal.directNotify.newCategory('SuitDialog') + +def getBrushOffIndex(suitName): + if SuitBrushOffs.has_key(suitName): + brushoffs = SuitBrushOffs[suitName] + else: + brushoffs = SuitBrushOffs[None] + num = len(brushoffs) + chunk = 100 / num + randNum = random.randint(0, 99) + count = chunk + for i in range(num): + if randNum < count: + return i + count += chunk + + notify.error('getBrushOffs() - no brush off found!') + return + + +def getBrushOffText(suitName, index): + if SuitBrushOffs.has_key(suitName): + brushoffs = SuitBrushOffs[suitName] + else: + brushoffs = SuitBrushOffs[None] + return brushoffs[index] + + +SuitBrushOffs = OTPLocalizer.SuitBrushOffs diff --git a/toontown/suit/SuitPlannerBase.py b/toontown/suit/SuitPlannerBase.py new file mode 100644 index 0000000..3ab8008 --- /dev/null +++ b/toontown/suit/SuitPlannerBase.py @@ -0,0 +1,601 @@ +from pandac.PandaModules import * +import random +import string +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals +from toontown.hood import HoodUtil +from toontown.building import SuitBuildingGlobals + +class SuitPlannerBase: + notify = DirectNotifyGlobal.directNotify.newCategory('SuitPlannerBase') + SuitHoodInfo = [[2100, + 5, + 15, + 0, + 5, + 20, + 3, + (1, + 5, + 10, + 40, + 60, + 80), + (25, + 25, + 25, + 25), + (1, 2, 3), + []], + [2200, + 3, + 10, + 0, + 5, + 15, + 3, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 70, + 10, + 10), + (1, 2, 3), + []], + [2300, + 3, + 10, + 0, + 5, + 15, + 3, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 10, + 40, + 40), + (1, 2, 3), + []], + [1100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (90, + 10, + 0, + 0), + (2, 3, 4), + []], + [1200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 90, + 10), + (3, + 4, + 5, + 6), + []], + [1300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (40, + 40, + 10, + 10), + (3, + 4, + 5, + 6), + []], + [3100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (90, + 10, + 0, + 0), + (5, 6, 7), + []], + [3200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 20, + 30, + 40), + (5, 6, 7), + []], + [3300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (5, + 85, + 5, + 5), + (7, 8, 9), + []], + [4100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 50, + 50), + (2, 3, 4), + []], + [4200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 90, + 10), + (3, + 4, + 5, + 6), + []], + [4300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (50, + 50, + 0, + 0), + (3, + 4, + 5, + 6), + []], + [5100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 20, + 10, + 70), + (2, 3, 4), + []], + [5200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (10, + 70, + 0, + 20), + (3, + 4, + 5, + 6), + []], + [5300, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (5, + 5, + 5, + 85), + (3, + 4, + 5, + 6), + []], + [9100, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (25, + 25, + 25, + 25), + (6, + 7, + 8, + 9), + []], + [9200, + 1, + 5, + 0, + 99, + 100, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (5, + 5, + 85, + 5), + (6, + 7, + 8, + 9), + []], + [11000, + 3, + 15, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 0, + 100), + (4, 5, 6), + []], + [11200, + 10, + 20, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 0, + 100), + (4, 5, 6), + []], + [12000, + 10, + 20, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 0, + 100, + 0), + (7, 8, 9), + []], + [13000, + 10, + 20, + 0, + 0, + 0, + 4, + (1, + 5, + 10, + 40, + 60, + 80), + (0, + 100, + 0, + 0), + (8, 9, 10), + []]] + SUIT_HOOD_INFO_ZONE = 0 + SUIT_HOOD_INFO_MIN = 1 + SUIT_HOOD_INFO_MAX = 2 + SUIT_HOOD_INFO_BMIN = 3 + SUIT_HOOD_INFO_BMAX = 4 + SUIT_HOOD_INFO_BWEIGHT = 5 + SUIT_HOOD_INFO_SMAX = 6 + SUIT_HOOD_INFO_JCHANCE = 7 + SUIT_HOOD_INFO_TRACK = 8 + SUIT_HOOD_INFO_LVL = 9 + SUIT_HOOD_INFO_HEIGHTS = 10 + TOTAL_BWEIGHT = 0 + TOTAL_BWEIGHT_PER_TRACK = [0, + 0, + 0, + 0] + TOTAL_BWEIGHT_PER_HEIGHT = [0, + 0, + 0, + 0, + 0] + for currHoodInfo in SuitHoodInfo: + weight = currHoodInfo[SUIT_HOOD_INFO_BWEIGHT] + tracks = currHoodInfo[SUIT_HOOD_INFO_TRACK] + levels = currHoodInfo[SUIT_HOOD_INFO_LVL] + heights = [0, + 0, + 0, + 0, + 0] + for level in levels: + minFloors, maxFloors = SuitBuildingGlobals.SuitBuildingInfo[level - 1][0] + for i in range(minFloors - 1, maxFloors): + heights[i] += 1 + + currHoodInfo[SUIT_HOOD_INFO_HEIGHTS] = heights + TOTAL_BWEIGHT += weight + TOTAL_BWEIGHT_PER_TRACK[0] += weight * tracks[0] + TOTAL_BWEIGHT_PER_TRACK[1] += weight * tracks[1] + TOTAL_BWEIGHT_PER_TRACK[2] += weight * tracks[2] + TOTAL_BWEIGHT_PER_TRACK[3] += weight * tracks[3] + TOTAL_BWEIGHT_PER_HEIGHT[0] += weight * heights[0] + TOTAL_BWEIGHT_PER_HEIGHT[1] += weight * heights[1] + TOTAL_BWEIGHT_PER_HEIGHT[2] += weight * heights[2] + TOTAL_BWEIGHT_PER_HEIGHT[3] += weight * heights[3] + TOTAL_BWEIGHT_PER_HEIGHT[4] += weight * heights[4] + + def __init__(self): + self.suitWalkSpeed = ToontownGlobals.SuitWalkSpeed + self.dnaStore = None + self.pointIndexes = {} + return + + def setupDNA(self): + if self.dnaStore: + return None + self.dnaStore = DNAStorage() + dnaFileName = self.genDNAFileName() + try: + simbase.air.loadDNAFileAI(self.dnaStore, dnaFileName) + except: + loader.loadDNAFileAI(self.dnaStore, dnaFileName) + + self.initDNAInfo() + return None + + def genDNAFileName(self): + try: + return simbase.air.genDNAFileName(self.getZoneId()) + except: + zoneId = ZoneUtil.getCanonicalZoneId(self.getZoneId()) + hoodId = ZoneUtil.getCanonicalHoodId(zoneId) + hood = ToontownGlobals.dnaMap[hoodId] + phase = ToontownGlobals.streetPhaseMap[hoodId] + if hoodId == zoneId: + zoneId = 'sz' + return 'phase_%s/dna/%s_%s.dna' % (phase, hood, zoneId) + + def getZoneId(self): + return self.zoneId + + def setZoneId(self, zoneId): + self.notify.debug('setting zone id for suit planner') + self.zoneId = zoneId + self.setupDNA() + + def extractGroupName(self, groupFullName): + return string.split(groupFullName, ':', 1)[0] + + def initDNAInfo(self): + numGraphs = self.dnaStore.discoverContinuity() + if numGraphs != 1: + self.notify.info('zone %s has %s disconnected suit paths.' % (self.zoneId, numGraphs)) + self.battlePosDict = {} + self.cellToGagBonusDict = {} + for i in range(self.dnaStore.getNumDNAVisGroupsAI()): + vg = self.dnaStore.getDNAVisGroupAI(i) + zoneId = int(self.extractGroupName(vg.getName())) + if vg.getNumBattleCells() == 1: + battleCell = vg.getBattleCell(0) + self.battlePosDict[zoneId] = vg.getBattleCell(0).getPos() + elif vg.getNumBattleCells() > 1: + self.notify.warning('multiple battle cells for zone: %d' % zoneId) + self.battlePosDict[zoneId] = vg.getBattleCell(0).getPos() + if True: + for i in range(vg.getNumChildren()): + childDnaGroup = vg.at(i) + if isinstance(childDnaGroup, DNAInteractiveProp): + self.notify.debug('got interactive prop %s' % childDnaGroup) + battleCellId = childDnaGroup.getCellId() + if battleCellId == -1: + self.notify.warning('interactive prop %s at %s not associated with a a battle' % (childDnaGroup, zoneId)) + elif battleCellId == 0: + if self.cellToGagBonusDict.has_key(zoneId): + self.notify.error('FIXME battle cell at zone %s has two props %s %s linked to it' % (zoneId, self.cellToGagBonusDict[zoneId], childDnaGroup)) + else: + name = childDnaGroup.getName() + propType = HoodUtil.calcPropType(name) + if propType in ToontownBattleGlobals.PropTypeToTrackBonus: + trackBonus = ToontownBattleGlobals.PropTypeToTrackBonus[propType] + self.cellToGagBonusDict[zoneId] = trackBonus + + self.dnaStore.resetDNAGroups() + self.dnaStore.resetDNAVisGroups() + self.dnaStore.resetDNAVisGroupsAI() + self.streetPointList = [] + self.frontdoorPointList = [] + self.sidedoorPointList = [] + self.cogHQDoorPointList = [] + numPoints = self.dnaStore.getNumSuitPoints() + for i in range(numPoints): + point = self.dnaStore.getSuitPointAtIndex(i) + if point.getPointType() == DNASuitPoint.FRONTDOORPOINT: + self.frontdoorPointList.append(point) + elif point.getPointType() == DNASuitPoint.SIDEDOORPOINT: + self.sidedoorPointList.append(point) + elif point.getPointType() == DNASuitPoint.COGHQINPOINT or point.getPointType() == DNASuitPoint.COGHQOUTPOINT: + self.cogHQDoorPointList.append(point) + else: + self.streetPointList.append(point) + self.pointIndexes[point.getIndex()] = point + + return None + + def performPathTest(self): + if not self.notify.getDebug(): + return None + startAndEnd = self.pickPath() + if not startAndEnd: + return None + startPoint = startAndEnd[0] + endPoint = startAndEnd[1] + path = self.dnaStore.getSuitPath(startPoint, endPoint) + numPathPoints = path.getNumPoints() + for i in range(numPathPoints - 1): + zone = self.dnaStore.getSuitEdgeZone(path.getPointIndex(i), path.getPointIndex(i + 1)) + travelTime = self.dnaStore.getSuitEdgeTravelTime(path.getPointIndex(i), path.getPointIndex(i + 1), self.suitWalkSpeed) + self.notify.debug('edge from point ' + `i` + ' to point ' + `(i + 1)` + ' is in zone: ' + `zone` + ' and will take ' + `travelTime` + ' seconds to walk.') + + return None + + def genPath(self, startPoint, endPoint, minPathLen, maxPathLen): + return self.dnaStore.getSuitPath(startPoint, endPoint, minPathLen, maxPathLen) + + def getDnaStore(self): + return self.dnaStore diff --git a/toontown/suit/SuitTimings.py b/toontown/suit/SuitTimings.py new file mode 100644 index 0000000..2706c1d --- /dev/null +++ b/toontown/suit/SuitTimings.py @@ -0,0 +1,7 @@ +dict = {} +fromSky = 6.5 +toSky = 6.5 +victoryDance = 9.08 +fromSuitBuilding = 2.0 +toSuitBuilding = 2.5 +toToonBuilding = 2.5 diff --git a/toontown/suit/__init__.py b/toontown/suit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/toon/AccessoryGlobals.py b/toontown/toon/AccessoryGlobals.py new file mode 100644 index 0000000..9979e35 --- /dev/null +++ b/toontown/toon/AccessoryGlobals.py @@ -0,0 +1,1140 @@ +HatTransTable = {'hl': ((0.0, -0.32198, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.1, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.14621, 0.382862), (180.0, 343.300751, 0.0), (0.350607, 0.403063, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.597447, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.188123, 0.623452), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))} +GlassesTransTable = {'hl': ((0.0, 0.118239, 0.073909), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.218512, 0.047869), (180.0, 320.0, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.181508, 0.06527), (180.0, 341.565063, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.181299, 0.089964), (180.0, 350.537689, 0.0), (0.282347, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.277646), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.31), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'bl': ((0.0, 0.22, 0.108153), (180.0, 350.0, 0.0), (0.27447, 0.262137, 0.287293)), + 'bs': ((0.0, 0.200803, 0.097391), (180.0, 350.0, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.0, 0.148353, 0.06), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.056374), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, -0.018402), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.301501, -0.030958), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.037004), (180.0, 350.537689, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.276756, -0.031632), (180.0, 354.289398, 0.0), (0.306648, 0.142252, 0.308336)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.44, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.441395, 0.278491, 0.398912)), + 'fl': ((0.0, 0.126437, 0.12), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.129622, 0.13), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))} +BackpackTransTable = {'s': ((0.184578, -1.585814, 0.134797), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.227545, -1.05, 0.053624), (180.0, 6.0, 0.0), (0.319532, 0.319532, 0.319532)), + 'l': ((0.26576, -1.28023, 0.0), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32))} +ExtendedHatTransTable = {1: {'hl': ((0.0, -0.295705, 0.406916), (180.0, 320.194427, 0.0), (0.254, 0.245594, 0.246926)), + 'hs': ((0.0, -0.195882, 0.244268), (180.0, 311.633545, 0.0), (0.263445, 0.243299, 0.243299)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.434684, 0.33591, 0.424631)), + 'ps': ((0.0, -0.063579, 0.353951), (180.0, 323.972626, 0.0), (0.365148, 0.3296, 0.348283)), + 'dl': ((0.0, -0.07662, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.022553, 0.46431), (180.0, 323.130096, 0.0), (0.315467, 0.315467, 0.315467)), + 'bl': ((0.0, -0.071458, 0.580411), (180.0, 341.565063, 0.0), (0.332492, 0.346676, 0.424343)), + 'bs': ((0.0, -0.000624, 0.392553), (180.0, 338.198578, 0.0), (0.310198, 0.337212, 0.32)), + 'cl': ((0.0, -0.011011, 0.596829), (180.0, 352.405365, 0.0), (0.394101, 0.374709, 0.342697)), + 'cs': ((0.0, -0.087502, 0.347226), (180.0, 343.300751, 0.0), (0.400106, 0.382564, 0.307962)), + 'sl': ((0.0, -0.118797, 0.296321), (180.0, 336.801422, 0.0), (0.285034, 0.366504, 0.352941)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.338425, 0.370884, 0.34839)), + 'rl': ((0.0, -0.055533, 0.353435), (180.0, 340.346161, 0.0), (0.29569, 0.252995, 0.262981)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.536616, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.483682, 0.423291, 0.468427)), + 'fl': ((0.0, -0.144887, 0.701924), (180.0, 341.565063, 0.0), (0.527401, 0.487251, 0.604765)), + 'fs': ((0.0, -0.178833, 0.632517), (180.0, 345.963745, 0.0), (0.489695, 0.479546, 0.458304))}, + 2: {'dl': ((0.0, -0.1, 0.685092), (180.0, 319.398712, 0.0), (0.295951, 0.295951, 0.295951)), + 'cs': ((-0.026207, -0.14621, 0.382862), (180.0, 343.300751, 6.0), (0.350607, 0.403063, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.383177, 0.383177, 0.383177))}, + 3: {'cs': ((0.0, -0.14621, 0.382862), (180.0, 343.300751, 0.0), (0.350607, 0.403063, 0.307962))}, + 5: {'hs': ((0.0, -0.280617, 0.335528), (180.0, 315.0, 0.0), (0.315279, 0.277932, 0.309252)), + 'cs': ((0.0, -0.14621, 0.382862), (180.0, 343.300751, 8.530766), (0.419412, 0.417991, 0.319367))}, + 6: {'cs': ((0.0, -0.073163, 0.411979), (180.0, 343.300751, 0.0), (0.350607, 0.403063, 0.307962))}, + 10: {'pl': ((-0.099051, -0.175888, 0.592198), (180.0, 330.0, 25.201124), (0.492972, 0.418136, 0.424631)), + 'ps': ((-0.041475, -0.06182, 0.393373), (180.0, 323.972626, 9.462322), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.070785, 0.704706), (180.0, 319.398712, 8.130102), (0.304895, 0.297812, 0.375712)), + 'ds': ((-0.021678, -0.01167, 0.498881), (180.0, 333.434937, 9.462322), (0.318405, 0.318405, 0.318405)), + 'bl': ((-0.002771, -0.172801, 0.738516), (180.0, 329.743561, 9.462322), (0.317369, 0.317369, 0.317369)), + 'bs': ((-0.050198, 0.004136, 0.404443), (194.03624, 341.565063, 14.036243), (0.310959, 0.310959, 0.310959)), + 'cl': ((-0.004527, 0.016114, 0.686246), (180.0, 6.009006, 8.130102), (0.370254, 0.370254, 0.370254)), + 'cs': ((-0.030719, 0.000539, 0.361591), (180.0, 0.0, 5.440332), (0.40023, 0.40023, 0.40023)), + 'sl': ((0.0, -0.137519, 0.341029), (180.0, 336.801422, 5.710593), (0.327711, 0.375616, 0.327711)), + 'ss': ((-0.027001, -0.172305, 0.180213), (180.0, 326.309937, 11.309933), (0.372096, 0.372096, 0.372096)), + 'rl': ((-0.015145, -0.026606, 0.253854), (180.0, 340.346161, 9.462322), (0.380015, 0.348041, 0.380015)), + 'rs': ((-0.009053, -0.011238, 0.288839), (124.99202, 355.236359, 0.0), (0.352183, 0.352183, 0.352183)), + 'ml': ((-0.017855, -0.026353, 0.768487), (180.0, 350.537689, 8.746162), (0.651424, 0.533307, 0.581963)), + 'ms': ((-0.049162, -0.051857, 0.556686), (180.0, 344.0, 6.709837), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.006534, -0.184093, 0.852904), (180.0, 0.0, 354.289398), (0.527988, 0.527988, 0.527988)), + 'fs': ((0.012731, -0.079046, 0.676545), (180.0, 6.340192, 354.289398), (0.527899, 0.527899, 0.527899))}, + 11: {'cs': ((-0.019078, -0.14621, 0.382862), (180.0, 343.300751, 6.0), (0.361787, 0.403063, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.30748, 0.383177, 0.368997))}, + 12: {'hl': ((0.0, -0.309314, 0.406916), (180.0, 320.194427, 0.0), (0.271819, 0.262823, 0.264249)), + 'pl': ((0.0, -0.181953, 0.57), (180.0, 330.0, 0.0), (0.45327, 0.377233, 0.424631)), + 'dl': ((0.0, -0.083903, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.308617, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.367935, 0.362207, 0.408829)), + 'bs': ((0.0, -0.021139, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.378772, 0.40683, 0.342697)), + 'cs': ((-0.010907, -0.107796, 0.382862), (180.0, 343.300751, 6.0), (0.406312, 0.377019, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.294358, 0.378979, 0.368997)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.356448, 0.387685, 0.34839)), + 'rl': ((0.0, -0.067593, 0.353435), (180.0, 340.346161, 0.0), (0.294372, 0.268597, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.37158, 0.322002, 0.352713)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.56417, 0.474334, 0.581963)), + 'ms': ((0.0, -0.088935, 0.588494), (180.0, 344.0, 0.0), (0.48211, 0.395267, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.530228, 0.524303, 0.604765)), + 'fs': ((0.0, -0.144201, 0.623452), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 13: {'hl': ((0.0, -0.412612, 0.487717), (180.0, 311.987213, 0.0), (0.16889, 0.16889, 0.16889)), + 'hs': ((0.0, -0.286132, 0.359341), (180.0, 311.633545, 0.0), (0.128881, 0.128881, 0.128881)), + 'pl': ((0.0, -0.268135, 0.72809), (180.0, 330.0, 0.0), (0.323023, 0.272871, 0.272871)), + 'ps': ((0.0, -0.094548, 0.408059), (180.0, 323.972626, 0.0), (0.320213, 0.296061, 0.296061)), + 'dl': ((0.0, -0.143021, 0.812855), (180.0, 333.434937, 0.0), (0.21997, 0.21997, 0.21997)), + 'ds': ((0.0, -0.060565, 0.575688), (180.0, 333.434937, 0.0), (0.248021, 0.248021, 0.248021)), + 'bl': ((0.0, -0.121603, 0.639803), (180.0, 341.565063, 0.0), (0.330776, 0.304925, 0.304925)), + 'bs': ((0.0, -0.102031, 0.466414), (180.0, 330.945404, 0.0), (0.2459, 0.2459, 0.2459)), + 'cl': ((0.0, -0.026431, 0.728948), (180.0, 352.405365, 0.0), (0.287266, 0.287266, 0.287266)), + 'cs': ((-0.050589, -0.081308, 0.469158), (180.0, 345.963745, 8.130102), (0.299346, 0.299346, 0.299346)), + 'sl': ((0.0, -0.169265, 0.446898), (180.0, 336.801422, 0.0), (0.240421, 0.256241, 0.256241)), + 'ss': ((0.0, -0.270782, 0.24262), (180.0, 319.398712, 0.0), (0.225974, 0.301156, 0.301156)), + 'rl': ((0.0, -0.049562, 0.413636), (180.0, 340.346161, 0.0), (0.279306, 0.207526, 0.207526)), + 'rs': ((-0.090265, -0.098991, 0.45038), (180.0, 336.801422, 23.962488), (0.218332, 0.218332, 0.218332)), + 'ml': ((0.0, -0.196901, 0.997155), (180.0, 333.434937, 0.0), (0.435704, 0.363724, 0.383599)), + 'ms': ((0.0, -0.149188, 0.735846), (180.0, 338.198578, 0.0), (0.385714, 0.313025, 0.313025)), + 'fl': ((-0.090384, -0.178217, 1.065685), (180.0, 0.0, 12.994617), (0.343651, 0.343651, 0.343651)), + 'fs': ((-0.101206, -0.188123, 0.832492), (180.0, 345.963745, 8.972627), (0.366177, 0.366177, 0.366177))}, + 14: {'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.509562, 0.377233, 0.424631)), + 'ps': ((0.0, -0.06878, 0.36349), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.023114, 0.591717), (180.0, 320.710602, 0.0), (0.408557, 0.3748, 0.3748)), + 'ds': ((0.0, -0.037187, 0.471363), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.390313, 0.392326, 0.408829)), + 'cl': ((0.0, -0.020176, 0.645978), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.085443, 0.382862), (180.0, 343.300751, 0.0), (0.377142, 0.403063, 0.307962)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.385168, 0.41761, 0.375282)), + 'rl': ((0.0, -0.031954, 0.353435), (180.0, 340.346161, 0.0), (0.303994, 0.276488, 0.25764)), + 'rs': ((0.0, -0.020569, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.06454, 0.768487), (180.0, 333.434937, 0.0), (0.597447, 0.505423, 0.581963)), + 'ms': ((0.0, -0.051857, 0.515682), (180.0, 344.0, 0.0), (0.631431, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.630656, 0.560066, 0.604765)), + 'fs': ((0.0, -0.188123, 0.623452), (180.0, 345.963745, 0.0), (0.615579, 0.526355, 0.458304))}, + 15: {'hl': ((0.0, -0.32198, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.243293)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.361169)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'cl': ((0.0, -0.044478, 0.434606), (180.0, 352.405365, 0.0), (0.3873, 0.436949, 0.368068)), + 'cs': ((0.0, -0.09931, 0.352211), (180.0, 0.0, 4.763642), (0.406173, 0.406173, 0.406173)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.298392)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 347.471191, 0.0), (0.597447, 0.45943, 0.464198)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 354.805573, 0.0), (0.542386, 0.507327, 0.420425))}, + 16: {'hs': ((0.0, -0.158257, 0.27033), (180.0, 311.633545, 0.0), (0.301503, 0.301503, 0.301503)), + 'pl': ((0.0, -0.175888, 0.575867), (180.0, 330.0, 0.0), (0.496736, 0.496736, 0.496736)), + 'ps': ((0.0, -0.052435, 0.353951), (180.0, 323.972626, 0.0), (0.388558, 0.388558, 0.388558)), + 'dl': ((0.0, -0.078244, 0.685092), (180.0, 333.434937, 0.0), (0.345497, 0.349719, 0.391548)), + 'ds': ((0.0, 0.024539, 0.46431), (180.0, 323.130096, 0.0), (0.347816, 0.347816, 0.347816)), + 'bl': ((0.009605, -0.098169, 0.555411), (180.0, 341.565063, 0.0), (0.399212, 0.391299, 0.391299)), + 'bs': ((0.0, 0.004136, 0.399126), (180.0, 338.198578, 0.0), (0.361711, 0.361711, 0.361711)), + 'cl': ((0.0, -0.013398, 0.50033), (180.0, 352.405365, 0.0), (0.456027, 0.456027, 0.456027)), + 'cs': ((0.0, -0.09551, 0.349781), (180.0, 343.300751, 0.0), (0.452026, 0.452026, 0.452026)), + 'sl': ((0.0, -0.048961, 0.296321), (180.0, 336.801422, 0.0), (0.365214, 0.365214, 0.365214)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.314149, 0.314149, 0.314149)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.379328, 0.379328, 0.379328)), + 'ml': ((0.0, -0.043199, 0.768487), (180.0, 333.434937, 0.0), (0.559512, 0.516789, 0.558125)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.523591, 0.523591, 0.523591)), + 'fl': ((0.0, -0.13484, 0.701924), (180.0, 341.565063, 0.0), (0.581492, 0.581492, 0.581492)), + 'fs': ((0.0, -0.136718, 0.623452), (180.0, 345.963745, 0.0), (0.551042, 0.551042, 0.551042))}, + 17: {'hl': ((0.0, -0.288235, 0.406916), (180.0, 320.194427, 0.0), (0.241566, 0.233572, 0.234839)), + 'hs': ((0.0, -0.191402, 0.247444), (180.0, 311.633545, 0.0), (0.269808, 0.237829, 0.249303)), + 'pl': ((0.0, -0.142213, 0.57), (180.0, 330.0, 0.0), (0.432458, 0.345032, 0.354144)), + 'dl': ((0.0, -0.079517, 0.685092), (180.0, 333.434937, 0.0), (0.270819, 0.265906, 0.332068)), + 'ds': ((0.0, -0.025315, 0.46431), (180.0, 323.130096, 0.0), (0.296901, 0.280258, 0.28153)), + 'bl': ((0.0, -0.06624, 0.574968), (180.0, 341.565063, 0.0), (0.311075, 0.341038, 0.367032)), + 'cl': ((0.0, -0.011135, 0.645978), (180.0, 352.405365, 0.0), (0.35892, 0.357605, 0.342697)), + 'cs': ((0.0, -0.077403, 0.382862), (180.0, 343.300751, 0.0), (0.363435, 0.35624, 0.307962)), + 'sl': ((0.0, -0.107696, 0.296321), (180.0, 336.801422, 0.0), (0.287417, 0.344961, 0.281365)), + 'ss': ((0.0, -0.108221, 0.126139), (180.0, 319.398712, 0.0), (0.340291, 0.368281, 0.34839)), + 'rl': ((0.0, -0.051276, 0.353435), (180.0, 340.346161, 0.0), (0.283907, 0.248885, 0.229335)), + 'rs': ((0.0, -0.013377, 0.30232), (180.0, 351.869904, 0.0), (0.334657, 0.285461, 0.29066)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.488528, 0.427275, 0.444023)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.452082, 0.355888, 0.354063)), + 'fl': ((0.0, -0.13699, 0.701924), (180.0, 341.565063, 0.0), (0.48533, 0.471774, 0.562384)), + 'fs': ((0.0, -0.146903, 0.63012), (180.0, 345.963745, 0.0), (0.476258, 0.448793, 0.431941))}, + 18: {'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.304127, 0.304127, 0.304127)), + 'rl': ((0.0, -0.054094, 0.353435), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.297602, 0.268747, 0.294379)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.454681, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.406678, 0.404195, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.120977, 0.623452), (180.0, 345.963745, 0.0), (0.481652, 0.465321, 0.418154))}, + 19: {'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997))}, + 20: {'hl': ((0.0, -0.275845, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.184604, 0.253956), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.146144, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.066372, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.042963, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.003173, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.054528, 0.574968), (180.0, 341.565063, 0.0), (0.334803, 0.339222, 0.408829)), + 'cl': ((0.0, 0.013352, 0.538953), (180.0, 352.405365, 0.0), (0.345773, 0.362333, 0.342697)), + 'cs': ((0.0, -0.044327, 0.382862), (180.0, 343.300751, 0.0), (0.366347, 0.3658, 0.307962)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.480187, 0.418133, 0.40536)), + 'fs': ((0.0, -0.115848, 0.623452), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 21: {'hl': ((0.0, -0.227, 0.279386), (180.0, 320.194427, 0.0), (0.276594, 0.281422, 0.318059)), + 'hs': ((0.0, -0.15377, 0.166578), (180.0, 311.633545, 0.0), (0.286051, 0.245637, 0.302753)), + 'pl': ((0.0, -0.175888, 0.46633), (180.0, 330.0, 0.0), (0.451908, 0.389167, 0.424631)), + 'ps': ((0.0, -0.046908, 0.240283), (180.0, 323.972626, 0.0), (0.365007, 0.365007, 0.365007)), + 'dl': ((0.0, -0.1, 0.685092), (180.0, 333.434937, 0.0), (0.25984, 0.243202, 0.322344)), + 'ds': ((0.0, -0.048714, 0.429002), (180.0, 323.130096, 0.0), (0.30725, 0.277881, 0.279921)), + 'sl': ((0.0, -0.137519, 0.182433), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ms': ((0.0, -0.10249, 0.556469), (180.0, 344.0, 0.0), (0.466104, 0.354776, 0.380968)), + 'fl': ((0.0, -0.184093, 0.642801), (180.0, 341.565063, 0.0), (0.496574, 0.482481, 0.588552)), + 'fs': ((0.0, -0.188123, 0.528571), (180.0, 345.963745, 0.0), (0.498111, 0.492008, 0.432218))}, + 22: {'hl': ((0.0, -0.265135, 0.418068), (180.0, 349.69516, 0.0), (0.274705, 0.284073, 0.285614)), + 'hs': ((0.0, -0.116422, 0.29659), (180.0, 343.300751, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.023007, 0.319507), (180.0, 337.380127, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.078245, 0.662967), (180.0, 17.10273, 0.0), (0.346512, 0.298172, 0.364691)), + 'ds': ((0.0, 0.064558, 0.46431), (180.0, 343.300751, 0.0), (0.352626, 0.352626, 0.352626)), + 'bl': ((0.0, -0.04906, 0.71477), (180.0, 347.905243, 0.0), (0.310724, 0.38662, 0.408829)), + 'bs': ((0.0, 0.054698, 0.374537), (180.0, 341.565063, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.077073, 0.656977), (180.0, 0.0, 0.0), (0.338451, 0.40683, 0.309464)), + 'cs': ((0.0, 0.027906, 0.378118), (180.0, 5.710593, 0.0), (0.370551, 0.334462, 0.304923)), + 'sl': ((0.0, -0.022724, 0.342427), (180.0, 342.897278, 0.0), (0.319084, 0.383177, 0.368997)), + 'ss': ((0.0, 0.009212, 0.200081), (180.0, 351.869904, 0.0), (0.348083, 0.387685, 0.34839)), + 'rl': ((0.0, -0.031396, 0.374398), (180.0, 28.072487, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.087833, 0.299892), (180.0, 32.471191, 0.0), (0.31491, 0.322002, 0.352713)), + 'ml': ((0.0, -0.030558, 0.768487), (180.0, 11.309933, 0.0), (0.564019, 0.45943, 0.581963)), + 'ms': ((0.0, -0.000704, 0.535926), (180.0, 10.619656, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.264786, 0.741353), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.188123, 0.630058), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 23: {'hl': ((0.0, -0.234499, 0.006963), (180.0, 320.194427, 0.0), (0.232292, 0.232292, 0.232292)), + 'hs': ((0.0, -0.153971, -0.084937), (180.0, 311.633545, 0.0), (0.235204, 0.213215, 0.222742)), + 'pl': ((0.0, -0.175888, 0.019199), (180.0, 330.0, 0.0), (0.388413, 0.305218, 0.330135)), + 'ps': ((0.0, -0.084697, -0.065677), (180.0, 323.972626, 0.0), (0.278152, 0.278152, 0.278152)), + 'dl': ((0.0, -0.136028, 0.167535), (180.0, 333.434937, 0.0), (0.262787, 0.275414, 0.275414)), + 'ds': ((0.0, -0.036535, 0.101389), (180.0, 323.130096, 0.0), (0.250177, 0.250177, 0.250177)), + 'bl': ((0.0, -0.217551, 0.145652), (180.0, 341.565063, 0.0), (0.293826, 0.293826, 0.293826)), + 'bs': ((0.0, -0.154371, -0.017713), (180.0, 338.198578, 0.0), (0.285894, 0.285894, 0.285894)), + 'cl': ((0.0, -0.262728, 0.16244), (180.0, 341.565063, 0.0), (0.319499, 0.319499, 0.319499)), + 'cs': ((0.0, -0.212642, -0.002031), (180.0, 339.44397, 0.0), (0.281438, 0.281438, 0.281438)), + 'sl': ((0.0, -0.205294, -0.330001), (180.0, 336.801422, 0.0), (0.284792, 0.336415, 0.368997)), + 'ss': ((0.0, -0.112658, -0.327791), (180.0, 319.398712, 0.0), (0.250103, 0.32364, 0.32364)), + 'rl': ((0.0, -0.150722, -0.181009), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.315328)), + 'rs': ((0.0, -0.178553, -0.229451), (180.0, 336.801422, 0.0), (0.245044, 0.278633, 0.333909)), + 'ml': ((0.0, -0.107093, 0.354219), (180.0, 320.194427, 0.0), (0.352015, 0.352015, 0.352015)), + 'ms': ((0.0, -0.205055, 0.073881), (180.0, 333.434937, 0.0), (0.361383, 0.334152, 0.334152)), + 'fl': ((0.0, -0.312542, 0.038707), (180.0, 341.565063, 0.0), (0.458827, 0.407775, 0.407775)), + 'fs': ((0.0, -0.463631, 0.088544), (180.0, 345.963745, 0.0), (0.401967, 0.401967, 0.401967))}, + 24: {'hl': ((-0.11, -0.184156, 0.592693), (144.460007, 349.700012, 23.200001), (0.151377, 0.11, 0.132524)), + 'hs': ((-0.108716, -0.050365, 0.439436), (197.354019, 8.746162, 23.198591), (0.143583, 0.101404, 0.135683)), + 'pl': ((-0.167152, -0.06624, 0.831271), (180.0, 15.255118, 16.38954), (0.18146, 0.157851, 0.20563)), + 'ps': ((-0.120682, 0.107162, 0.559596), (180.0, 3.814075, 18.434948), (0.173479, 0.173479, 0.173479)), + 'dl': ((-0.125428, 0.023363, 0.897022), (180.0, 20.0, 26.565052), (0.147892, 0.127208, 0.157488)), + 'ds': ((-0.090555, 0.08351, 0.687713), (180.0, 4.0, 15.0), (0.147183, 0.120944, 0.168701)), + 'bl': ((-0.061272, -0.038389, 0.91813), (180.0, 17.354025, 16.699244), (0.151674, 0.131145, 0.161627)), + 'bs': ((-0.10904, 0.08919, 0.587726), (180.0, 6.340192, 21.801409), (0.144145, 0.144145, 0.144145)), + 'cl': ((-0.196067, 0.131514, 0.803251), (197.525574, 26.565052, 23.198591), (0.186031, 0.15093, 0.175068)), + 'cs': ((-0.116544, 0.113971, 0.580452), (180.0, 8.0, 10.0), (0.151921, 0.13188, 0.163922)), + 'sl': ((-0.071043, 0.003687, 0.568108), (205.016891, 15.945395, 11.309933), (0.152124, 0.159079, 0.161514)), + 'ss': ((-0.102084, 0.029174, 0.439706), (203.629379, 5.0, 17.10273), (0.137263, 0.153714, 0.161588)), + 'rl': ((-0.10141, 0.080647, 0.488627), (200.556046, 30.465546, 15.524111), (0.142403, 0.132055, 0.141315)), + 'rs': ((-0.062125, 0.044286, 0.505601), (198.434952, 22.619865, 8.746162), (0.139132, 0.124646, 0.152884)), + 'ml': ((-0.174461, 0.038167, 1.181215), (198.434952, 18.434948, 11.309933), (0.20696, 0.185704, 0.183701)), + 'ms': ((-0.149403, 0.004724, 0.857602), (201.801407, 8.130102, 8.130102), (0.191181, 0.170632, 0.181855)), + 'fl': ((-0.208681, 0.112598, 1.082435), (208.300751, 36.027374, 8.130102), (0.213688, 0.16739, 0.17565)), + 'fs': ((-0.244239, 0.131354, 0.916029), (220.236359, 30.0, 7.125016), (0.21074, 0.159387, 0.18308))}, + 25: {'hl': ((0.0, -0.257746, 0.352813), (180.0, 320.194427, 0.0), (0.254668, 0.275077, 0.315898)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.261249, 0.268914, 0.335529)), + 'pl': ((0.0, -0.208555, 0.607816), (180.0, 323.130096, 0.0), (0.401451, 0.309874, 0.400292)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.1, 0.709002), (180.0, 333.434937, 0.0), (0.274969, 0.268582, 0.338836)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.312558, 0.340434, 0.345114)), + 'bs': ((0.0, -0.044201, 0.392553), (180.0, 338.198578, 0.0), (0.29647, 0.32595, 0.338802)), + 'cl': ((0.0, -0.044478, 0.644346), (180.0, 352.405365, 0.0), (0.309155, 0.344185, 0.358538)), + 'cs': ((-0.048082, -0.11322, 0.439645), (180.0, 343.300751, 9.462322), (0.315072, 0.318579, 0.300552)), + 'sl': ((0.0, -0.260325, 0.448953), (180.0, 330.255127, 0.0), (0.195717, 0.251657, 0.303867)), + 'ss': ((0.0, -0.363379, 0.297801), (180.0, 315.0, 0.0), (0.155729, 0.168846, 0.264391)), + 'rl': ((0.0, -0.068453, 0.308529), (180.0, 340.346161, 0.0), (0.322224, 0.254291, 0.407353)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.359496, 0.288956, 0.401003)), + 'ml': ((0.0, -0.306013, 1.133884), (180.0, 333.434937, 0.0), (0.243077, 0.186923, 0.360584)), + 'ms': ((0.0, -0.094093, 0.870924), (180.0, 344.0, 0.0), (0.194086, 0.169004, 0.332554)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.474508, 0.479617, 0.569219)), + 'fs': ((0.0, -0.188123, 0.623452), (180.0, 345.963745, 0.0), (0.448616, 0.464668, 0.480208))}, + 26: {'hl': ((0.0, -0.277809, 0.384787), (180.0, 310.601288, 0.0), (0.184928, 0.199671, 0.221602)), + 'hs': ((0.0, -0.157871, 0.252226), (180.0, 311.633545, 0.0), (0.206806, 0.190991, 0.215819)), + 'pl': ((0.0, 0.026523, 0.340678), (180.0, 330.0, 0.0), (0.377123, 0.292389, 0.381522)), + 'ps': ((0.0, -0.001829, 0.256953), (180.0, 323.972626, 0.0), (0.260122, 0.286854, 0.318477)), + 'dl': ((0.0, -0.051435, 0.581176), (180.0, 333.434937, 0.0), (0.232037, 0.226646, 0.285931)), + 'ds': ((0.0, -0.039145, 0.436639), (180.0, 323.130096, 0.0), (0.22056, 0.232648, 0.263107)), + 'bl': ((0.0, -0.05878, 0.529694), (180.0, 330.945404, 0.0), (0.24374, 0.287275, 0.303777)), + 'bs': ((0.0, 0.004136, 0.348848), (180.0, 338.198578, 0.0), (0.248931, 0.248931, 0.248931)), + 'cl': ((0.0, -0.020201, 0.462021), (180.0, 352.405365, 0.0), (0.29328, 0.330876, 0.318429)), + 'cs': ((0.0, -0.051409, 0.233579), (180.0, 343.300751, 0.0), (0.299086, 0.337743, 0.339323)), + 'sl': ((0.0, -0.111747, 0.296321), (180.0, 336.801422, 0.0), (0.199779, 0.25688, 0.247374)), + 'ss': ((0.0, -0.078654, 0.083501), (180.0, 319.398712, 0.0), (0.247413, 0.291703, 0.298101)), + 'rl': ((0.0, -0.066437, 0.315062), (180.0, 336.370636, 0.0), (0.190027, 0.223328, 0.238153)), + 'rs': ((0.0, -0.070996, 0.263283), (180.0, 330.945404, 0.0), (0.235096, 0.219512, 0.273396)), + 'ml': ((0.0, -0.006407, 0.517215), (180.0, 333.434937, 0.0), (0.416147, 0.38934, 0.475146)), + 'ms': ((0.0, 0.007342, 0.3426), (180.0, 325.007965, 0.0), (0.406536, 0.369898, 0.432582)), + 'fl': ((0.0, -0.089114, 0.528296), (180.0, 341.565063, 0.0), (0.380638, 0.441579, 0.526389)), + 'fs': ((0.0, -0.084017, 0.38766), (180.0, 326.309937, 0.0), (0.415967, 0.401863, 0.558658))}, + 27: {'hl': ((-0.524481, -0.206691, 0.50416), (265.236359, 318.366455, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((-0.543209, -0.294683, 0.298021), (281.309937, 326.309937, 334.798889), (0.291181, 0.268914, 0.268914)), + 'pl': ((-0.792834, -0.010562, 0.86648), (235.00798, 306.027374, 318.012787), (0.433654, 0.377233, 0.424631)), + 'ps': ((-0.652948, 0.063679, 0.399579), (261.869904, 337.380127, 330.945404), (0.348283, 0.348283, 0.348283)), + 'dl': ((-0.546014, 0.108562, 0.685092), (249.443954, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((-0.569904, 0.157461, 0.667186), (246.037506, 318.366455, 330.255127), (0.318405, 0.318405, 0.318405)), + 'bl': ((-0.567081, 0.388385, 0.528339), (230.906143, 333.434937, 15.945395), (0.334803, 0.38662, 0.408829)), + 'bs': ((-0.537026, 0.127819, 0.541201), (257.471191, 318.814087, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((-0.692766, 0.069207, 0.658108), (258.690063, 333.434937, 333.434937), (0.360603, 0.40683, 0.342697)), + 'cs': ((-0.636327, 0.216326, 0.572623), (245.556046, 319.763641, 345.068573), (0.350607, 0.403063, 0.307962)), + 'sl': ((-0.632226, 0.268632, 0.279659), (246.801407, 335.224854, 5.194429), (0.298001, 0.383177, 0.368997)), + 'ss': ((-0.61473, 0.19353, 0.375886), (246.801407, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((-0.515258, 0.181444, 0.355185), (233.130096, 333.434937, 333.434937), (0.289685, 0.276488, 0.25764)), + 'rs': ((-0.528824, 0.217179, 0.259516), (233.130096, 335.55603, 343.300751), (0.298414, 0.26948, 0.295182)), + 'ml': ((-0.916889, 0.32838, 0.892706), (228.366455, 323.130096, 335.224854), (0.597447, 0.45943, 0.581963)), + 'ms': ((-0.833346, 0.134992, 0.710331), (254.744888, 321.340179, 344.744873), (0.554896, 0.483187, 0.468427)), + 'fl': ((-0.84855, 0.495989, 0.585959), (225.0, 335.55603, 6.340192), (0.527025, 0.507327, 0.604765)), + 'fs': ((-0.80813, 0.176572, 0.642824), (250.463348, 338.198578, 345.963745), (0.527899, 0.51, 0.458304))}, + 28: {'hl': ((0.0, -0.261502, 0.37089), (180.0, 311.185913, 0.0), (0.21093, 0.229836, 0.205056)), + 'hs': ((0.0, -0.233683, 0.314243), (180.0, 319.398712, 0.0), (0.162104, 0.185872, 0.149708)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 320.194427, 0.0), (0.327264, 0.327264, 0.327264)), + 'ps': ((0.0, -0.052272, 0.386975), (180.0, 323.972626, 0.0), (0.264953, 0.264953, 0.264953)), + 'dl': ((0.0, -0.012768, 0.538493), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, 0.046735, 0.369845), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, 0.045479, 0.303616), (180.0, 329.743561, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.024414, 0.232217), (180.0, 326.309937, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.001005, 0.360967), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.021548, 0.247119), (180.0, 355.236359, 12.094757), (0.375144, 0.431271, 0.329515)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.230655, 0.294738, 0.294738)), + 'ss': ((0.0, -0.155476, 0.164886), (180.0, 319.398712, 0.0), (0.225954, 0.302162, 0.291002)), + 'rl': ((0.0, -0.039658, 0.3126), (180.0, 340.346161, 0.0), (0.231198, 0.231198, 0.231198)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.257363, 0.257363, 0.257363)), + 'ml': ((0.0, -0.068179, 0.768487), (180.0, 348.690063, 8.746162), (0.414675, 0.414675, 0.414675)), + 'ms': ((0.0, -0.000002, 0.541642), (180.0, 344.0, 4.398705), (0.389266, 0.389266, 0.389266)), + 'fl': ((0.058768, 0.008708, 0.382249), (180.0, 341.565063, 5.710593), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.061993, 0.504703), (180.0, 345.963745, 8.746162), (0.436285, 0.51, 0.458304))}, + 29: {'hl': ((0.0, -0.416379, 0.521259), (180.0, 320.194427, 0.0), (0.228402, 0.220843, 0.222041)), + 'hs': ((0.0, -0.255588, 0.433379), (180.0, 326.309937, 0.0), (0.187592, 0.173247, 0.173247)), + 'pl': ((0.0, -0.117104, 0.57), (180.0, 330.0, 0.0), (0.556625, 0.377233, 0.424631)), + 'ps': ((0.0, -0.072488, 0.435111), (180.0, 323.972626, 0.0), (0.425365, 0.348015, 0.348283)), + 'dl': ((0.0, -0.114301, 0.685092), (180.0, 333.434937, 0.0), (0.416012, 0.289076, 0.364691)), + 'ds': ((0.0, -0.104245, 0.567932), (180.0, 323.130096, 0.0), (0.33946, 0.318405, 0.318405)), + 'bl': ((0.0, -0.057791, 0.507975), (180.0, 336.037506, 0.0), (0.43777, 0.391952, 0.391952)), + 'bs': ((0.0, 0.014032, 0.392553), (180.0, 338.198578, 0.0), (0.446885, 0.370112, 0.38918)), + 'cl': ((0.0, -0.02548, 0.616522), (180.0, 352.405365, 0.0), (0.505136, 0.410945, 0.342697)), + 'cs': ((0.0, -0.074619, 0.363931), (180.0, 347.471191, 0.0), (0.55192, 0.428574, 0.43323)), + 'sl': ((0.0, -0.134215, 0.445321), (180.0, 336.801422, 0.0), (0.307777, 0.383177, 0.368997)), + 'ss': ((0.0, -0.251233, 0.287217), (180.0, 319.398712, 0.0), (0.307606, 0.387685, 0.34839)), + 'rl': ((0.0, -0.088597, 0.44314), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.090461, 0.398918), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.167995, 0.81218), (180.0, 333.434937, 0.0), (0.659638, 0.45943, 0.581963)), + 'ms': ((0.0, -0.121411, 0.641263), (180.0, 333.434937, 0.0), (0.620033, 0.483187, 0.468427)), + 'fl': ((0.0, -0.256771, 0.823526), (180.0, 348.690063, 0.0), (0.658947, 0.507327, 0.604765)), + 'fs': ((0.0, -0.171212, 0.623452), (180.0, 345.963745, 0.0), (0.717775, 0.572076, 0.485491))}, + 30: {'hl': ((0.0, -0.302346, 0.406916), (180.0, 320.194427, 0.0), (0.28056, 0.273055, 0.285614)), + 'hs': ((0.0, -0.179472, 0.236684), (180.0, 311.633545, 0.0), (0.284893, 0.261428, 0.268914)), + 'pl': ((0.0, -0.241035, 0.660515), (180.0, 330.0, 0.0), (0.411001, 0.351334, 0.477315)), + 'ps': ((0.0, -0.089396, 0.414964), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.093456, 0.725988), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.048714, 0.511672), (180.0, 323.130096, 0.0), (0.309983, 0.309983, 0.309983)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 323.972626, 0.0), (0.371269, 0.325894, 0.344614)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.353902, 0.340747, 0.326553)), + 'cl': ((0.0, 0.007365, 0.645978), (180.0, 0.0, 0.0), (0.390058, 0.370094, 0.342697)), + 'cs': ((0.0, -0.072315, 0.474424), (173.659805, 347.471191, 0.0), (0.350231, 0.343982, 0.307962)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.318263, 0.40923, 0.394086)), + 'ss': ((0.0, -0.165839, 0.22666), (180.0, 325.007965, 0.0), (0.310093, 0.376156, 0.354515)), + 'rl': ((0.0, -0.052403, 0.396194), (180.0, 340.346161, 0.0), (0.287082, 0.257379, 0.25764)), + 'rs': ((0.0, -0.047608, 0.345909), (180.0, 336.801422, 0.0), (0.344324, 0.310939, 0.340595)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.530841, 0.445802, 0.581963)), + 'ms': ((0.0, -0.089467, 0.663395), (180.0, 344.0, 0.0), (0.466715, 0.405444, 0.43353)), + 'fl': ((0.0, -0.171277, 0.739647), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.164048, 0.583369), (180.0, 345.963745, 0.0), (0.514292, 0.493466, 0.458304))}, + 31: {'hl': ((0.0, -0.220449, 0.119023), (180.0, 345.963745, 0.0), (0.219105, 0.211854, 0.213004)), + 'hs': ((0.0, -0.113965, 0.072509), (180.0, 341.565063, 0.0), (0.20267, 0.187171, 0.187171)), + 'pl': ((0.0, 0.14036, -0.04792), (180.0, 338.962494, 0.0), (0.37692, 0.32788, 0.369077)), + 'ps': ((0.0, 0.089531, -0.099235), (180.0, 342.349884, 0.0), (0.293212, 0.291542, 0.29589)), + 'dl': ((0.0, 0.252744, 0.11708), (180.0, 333.434937, 0.0), (0.277106, 0.289076, 0.364691)), + 'ds': ((0.0, 0.230431, 0.076485), (180.0, 344.054596, 0.0), (0.255877, 0.319512, 0.319512)), + 'bl': ((0.0, 0.13071, 0.0), (180.0, 341.565063, 0.0), (0.236665, 0.288235, 0.304792)), + 'bs': ((0.0, 0.247398, -0.020869), (180.0, 338.198578, 0.0), (0.275836, 0.275836, 0.275836)), + 'cl': ((0.0, -0.000939, 0.088425), (180.0, 6.340192, 0.0), (0.294071, 0.283581, 0.342697)), + 'cs': ((0.0, 0.144906, -0.046214), (180.0, 355.914398, 0.0), (0.288037, 0.354037, 0.307962)), + 'sl': ((0.0, 0.23445, -0.275263), (180.0, 344.744873, 0.0), (0.28155, 0.362024, 0.348627)), + 'ss': ((0.0, 0.112075, -0.209017), (180.0, 339.44397, 0.0), (0.290682, 0.315166, 0.283221)), + 'rl': ((0.0, 0.026388, -0.139429), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, 0.149484, -0.250004), (180.0, 336.801422, 0.0), (0.310614, 0.280498, 0.30725)), + 'ml': ((0.0, 0.127118, 0.101103), (180.0, 354.289398, 0.0), (0.411167, 0.371526, 0.470614)), + 'ms': ((0.0, 0.165484, -0.053722), (180.0, 353.659821, 0.0), (0.417746, 0.409935, 0.397413)), + 'fl': ((0.0, 0.156775, 0.016309), (180.0, 341.565063, 0.0), (0.410564, 0.454725, 0.454725)), + 'fs': ((0.0, 0.267444, -0.051085), (180.0, 345.963745, 0.0), (0.400003, 0.400003, 0.400003))}, + 32: {'hl': ((0.0, -0.268822, 0.406916), (180.0, 320.194427, 0.0), (0.269517, 0.260597, 0.262011)), + 'hs': ((0.0, -0.166399, 0.251857), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.188816, 0.613454), (180.0, 330.0, 0.0), (0.419755, 0.345824, 0.389276)), + 'ps': ((0.0, -0.084697, 0.387917), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.085445, 0.685092), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, -0.033759, 0.46431), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.333684, 0.385327, 0.407462)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.044478, 0.645978), (180.0, 352.405365, 0.0), (0.331049, 0.373487, 0.31461)), + 'ss': ((0.0, -0.128516, 0.126139), (180.0, 319.398712, 0.0), (0.337212, 0.387685, 0.34839)), + 'rl': ((0.0, -0.039212, 0.23774), (180.0, 340.346161, 0.0), (0.357267, 0.292876, 0.25764)), + 'rs': ((0.0, -0.083155, 0.32749), (180.0, 336.801422, 0.0), (0.344561, 0.290413, 0.352713)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.526441, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.46582, 0.405622, 0.393232)), + 'fl': ((0.0, -0.142518, 0.701924), (180.0, 341.565063, 0.0), (0.529662, 0.509865, 0.607791)), + 'fs': ((0.0, -0.117987, 0.623452), (180.0, 345.963745, 0.0), (0.496473, 0.51, 0.458304))}, + 33: {'hl': ((0.0, -0.276202, 0.449818), (180.0, 320.194427, 0.0), (0.160414, 0.164019, 0.164019)), + 'hs': ((0.0, -0.128921, 0.27467), (180.0, 323.130096, 0.0), (0.23257, 0.164909, 0.164909)), + 'pl': ((0.0, -0.123782, 0.57), (180.0, 333.434937, 0.0), (0.38581, 0.211429, 0.237994)), + 'ps': ((0.0, 0.044661, 0.244389), (180.0, 341.565063, 0.0), (0.325913, 0.240032, 0.240032)), + 'dl': ((0.0, 0.028148, 0.356259), (180.0, 0.0, 0.0), (0.337421, 0.243651, 0.307384)), + 'ds': ((0.0, 0.176612, 0.285511), (180.0, 351.253845, 0.0), (0.34149, 0.276383, 0.310987)), + 'bl': ((0.0, 0.280522, 0.28634), (180.0, 341.565063, 0.0), (0.355425, 0.355425, 0.355425)), + 'bs': ((0.0, 0.221584, 0.183878), (180.0, 338.198578, 0.0), (0.318295, 0.318295, 0.318295)), + 'cl': ((0.0, 0.119931, 0.181552), (180.0, 6.340192, 0.0), (0.382162, 0.346204, 0.346204)), + 'cs': ((0.0, 0.060867, 0.118824), (180.0, 356.185913, 0.0), (0.37923, 0.318294, 0.318294)), + 'sl': ((0.0, -0.006457, 0.119641), (180.0, 336.801422, 0.0), (0.308605, 0.267992, 0.267992)), + 'ss': ((0.0, -0.003716, 0.031253), (180.0, 330.255127, 0.0), (0.303881, 0.265755, 0.265755)), + 'rl': ((0.0, 0.130827, 0.192487), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, 0.026163, 0.170432), (180.0, 352.405365, 0.0), (0.33485, 0.248124, 0.254152)), + 'ml': ((0.0, 0.162528, 0.732716), (180.0, 355.914398, 0.0), (0.473264, 0.359797, 0.359797)), + 'ms': ((0.0, 0.030821, 0.470736), (180.0, 344.0, 0.0), (0.426564, 0.284277, 0.346083)), + 'fl': ((0.0, 0.002367, 0.411145), (180.0, 347.471191, 0.0), (0.498433, 0.397726, 0.457847)), + 'fs': ((0.0, -0.021222, 0.217551), (180.0, 345.963745, 0.0), (0.54123, 0.357232, 0.48969))}, + 34: {'hl': ((0.0, -0.241063, 0.36174), (180.0, 326.627808, 0.0), (0.35835, 0.348827, 0.359012)), + 'hs': ((0.0, -0.205193, 0.260101), (180.0, 323.130096, 0.0), (0.347553, 0.351846, 0.343119)), + 'pl': ((0.0, -0.015615, 0.335195), (180.0, 330.0, 0.0), (0.602317, 0.546368, 0.599335)), + 'ps': ((0.0, -0.014131, 0.262376), (180.0, 327.994629, 0.0), (0.47315, 0.477293, 0.452478)), + 'dl': ((0.0, 0.006244, 0.566997), (180.0, 345.963745, 0.0), (0.418932, 0.418932, 0.418932)), + 'ds': ((0.0, 0.051982, 0.475703), (180.0, 351.253845, 0.0), (0.421277, 0.421277, 0.421277)), + 'bl': ((0.0, -0.001356, 0.427197), (180.0, 341.565063, 0.0), (0.451408, 0.451408, 0.462089)), + 'bs': ((0.0, 0.088443, 0.187984), (180.0, 345.963745, 0.0), (0.469158, 0.443694, 0.443694)), + 'cl': ((0.0, -0.047778, 0.369149), (180.0, 8.0, 0.0), (0.548045, 0.517806, 0.488176)), + 'cs': ((0.0, -0.045206, 0.329218), (180.0, 8.130102, 0.0), (0.496898, 0.542449, 0.492921)), + 'sl': ((0.0, -0.085306, 0.108033), (180.0, 333.434937, 0.0), (0.542136, 0.523295, 0.49822)), + 'ss': ((0.0, -0.087918, 0.120867), (180.0, 333.434937, 0.0), (0.457195, 0.511239, 0.456877)), + 'rl': ((0.0, -0.060758, 0.120939), (180.0, 344.744873, 0.0), (0.53018, 0.434141, 0.415247)), + 'rs': ((0.0, -0.035385, 0.173212), (180.0, 355.914398, 0.0), (0.435072, 0.431223, 0.413015)), + 'ml': ((0.0, 0.026015, 0.465531), (180.0, 333.434937, 0.0), (0.754976, 0.676776, 0.676776)), + 'ms': ((0.0, 0.00079, 0.419305), (180.0, 344.0, 0.0), (0.715514, 0.571614, 0.59854)), + 'fl': ((0.0, -0.083368, 0.453166), (180.0, 341.565063, 0.0), (0.700743, 0.700743, 0.700743)), + 'fs': ((0.0, -0.0811, 0.457792), (180.0, 345.963745, 0.0), (0.703631, 0.6177, 0.6177))}, + 35: {'hl': ((0.0, -0.266643, 0.406916), (180.0, 348.690063, 0.0), (0.270584, 0.284073, 0.285614)), + 'hs': ((0.0, -0.1642, 0.253538), (180.0, 355.236359, 0.0), (0.304984, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.06062, 0.329113), (180.0, 339.44397, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, 0.017045, 0.675274), (180.0, 354.289398, 0.0), (0.328408, 0.289076, 0.364691)), + 'ds': ((0.0, 0.033582, 0.46431), (180.0, 341.565063, 0.0), (0.348472, 0.318405, 0.318405)), + 'bl': ((0.0, -0.045561, 0.641867), (180.0, 345.963745, 0.0), (0.316367, 0.38662, 0.408829)), + 'bs': ((0.0, 0.015046, 0.392553), (180.0, 352.874969, 0.0), (0.323645, 0.326553, 0.326553)), + 'cl': ((0.0, 0.042263, 0.635366), (180.0, 0.0, 0.0), (0.35145, 0.40683, 0.342697)), + 'cs': ((0.0, 0.072944, 0.366422), (180.0, 0.0, 0.0), (0.370864, 0.403063, 0.307962)), + 'sl': ((0.0, -0.037346, 0.301475), (180.0, 350.537689, 0.0), (0.317633, 0.383177, 0.368997)), + 'ss': ((0.0, 0.030194, 0.191854), (180.0, 348.690063, 0.0), (0.348539, 0.387685, 0.34839)), + 'rl': ((0.0, 0.032847, 0.34277), (180.0, 12.994617, 0.0), (0.302167, 0.276488, 0.25764)), + 'rs': ((0.0, -0.041031, 0.257018), (180.0, 21.250505, 0.0), (0.317561, 0.322002, 0.352713)), + 'ml': ((0.0, -0.000721, 0.750719), (180.0, 3.576334, 0.0), (0.559837, 0.45943, 0.581963)), + 'ms': ((0.0, -0.007377, 0.531291), (180.0, 4.763642, 0.0), (0.544796, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.701924), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765))}, + 36: {'hl': ((0.0, -0.347161, 0.406916), (180.0, 320.194427, 0.0), (0.319728, 0.300149, 0.285614)), + 'hs': ((0.0, -0.183052, 0.263381), (180.0, 311.633545, 0.0), (0.363318, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.553432, 0.377233, 0.424631)), + 'ps': ((0.0, -0.044527, 0.355215), (180.0, 323.972626, 0.0), (0.431773, 0.36121, 0.388921)), + 'dl': ((0.0, -0.081082, 0.726366), (180.0, 333.434937, 0.0), (0.383692, 0.289076, 0.364691)), + 'ds': ((0.0, -0.035633, 0.46431), (180.0, 323.130096, 0.0), (0.432336, 0.318405, 0.318405)), + 'bl': ((0.0, -0.098169, 0.574968), (180.0, 341.565063, 0.0), (0.43827, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.394945), (180.0, 338.198578, 0.0), (0.404135, 0.326553, 0.326553)), + 'cl': ((0.0, -0.060938, 0.645978), (180.0, 352.405365, 0.0), (0.459022, 0.40683, 0.342697)), + 'cs': ((0.0, -0.109454, 0.411967), (180.0, 343.300751, 0.0), (0.476119, 0.403063, 0.307962)), + 'sl': ((0.0, -0.099204, 0.296321), (180.0, 336.801422, 0.0), (0.390197, 0.383177, 0.368997)), + 'ss': ((0.0, -0.123037, 0.1702), (180.0, 319.398712, 0.0), (0.394785, 0.387685, 0.34839)), + 'rl': ((0.0, -0.075822, 0.353435), (180.0, 340.346161, 0.0), (0.374044, 0.276488, 0.25764)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.055902, 0.768487), (180.0, 333.434937, 0.0), (0.702092, 0.45943, 0.581963)), + 'ms': ((0.0, -0.124136, 0.614941), (180.0, 344.0, 0.0), (0.578858, 0.483187, 0.468427)), + 'fl': ((0.0, -0.250494, 0.701924), (180.0, 341.565063, 0.0), (0.675813, 0.628315, 0.604765)), + 'fs': ((0.0, -0.153541, 0.623452), (180.0, 345.963745, 0.0), (0.628635, 0.51, 0.458304))}, + 37: {'hl': ((0.0, -0.109185, 0.182812), (180.0, 320.194427, 0.0), (0.412429, 0.412429, 0.412429)), + 'hs': ((0.0, -0.115121, 0.194043), (180.0, 311.633545, 0.0), (0.407209, 0.407209, 0.407209)), + 'pl': ((0.0, -0.024329, 0.288434), (180.0, 330.0, 0.0), (0.594207, 0.594207, 0.594207)), + 'ps': ((0.0, 0.079729, 0.157044), (180.0, 323.972626, 0.0), (0.496503, 0.496503, 0.496503)), + 'dl': ((0.0, -0.000942, 0.470992), (180.0, 333.434937, 0.0), (0.415705, 0.415705, 0.415705)), + 'ds': ((0.0, 0.037145, 0.335535), (180.0, 323.130096, 0.0), (0.422923, 0.422923, 0.422923)), + 'bl': ((0.0, 0.031956, 0.375886), (180.0, 341.565063, 0.0), (0.416934, 0.416934, 0.416934)), + 'bs': ((0.0, 0.073504, 0.186075), (180.0, 338.198578, 0.0), (0.415699, 0.415699, 0.415699)), + 'cl': ((0.0, 0.004228, 0.316654), (180.0, 352.405365, 0.0), (0.492512, 0.492512, 0.492512)), + 'cs': ((0.0, -0.053196, 0.245083), (180.0, 343.300751, 0.0), (0.504193, 0.504193, 0.504193)), + 'sl': ((0.0, 0.002899, 0.005551), (180.0, 336.801422, 0.0), (0.509032, 0.509032, 0.509032)), + 'ss': ((0.0, 0.08639, -0.02387), (180.0, 319.398712, 0.0), (0.508092, 0.508092, 0.508092)), + 'rl': ((0.0, -0.000893, 0.084083), (180.0, 340.346161, 0.0), (0.464507, 0.464507, 0.464507)), + 'rs': ((0.0, -0.000596, 0.048775), (180.0, 336.801422, 0.0), (0.486521, 0.486521, 0.486521)), + 'ml': ((0.0, 0.005332, 0.38043), (180.0, 347.471191, 0.0), (0.621133, 0.621133, 0.621133)), + 'ms': ((0.0, 0.002662, 0.434725), (180.0, 344.0, 0.0), (0.591085, 0.591085, 0.591085)), + 'fl': ((0.0, -0.046462, 0.421144), (180.0, 341.565063, 0.0), (0.606256, 0.606256, 0.606256)), + 'fs': ((0.0, -0.075515, 0.4517), (180.0, 345.963745, 0.0), (0.588141, 0.588141, 0.588141))}, + 38: {'hl': ((0.0, -0.189091, 0.312985), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.135863, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.025753, 0.421012), (180.0, 319.398712, 0.0), (0.463344, 0.374374, 0.427883)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, 0.021175, 0.49384), (180.0, 333.434937, 0.0), (0.328248, 0.328248, 0.328248)), + 'ds': ((0.0, 0.035984, 0.40283), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, 0.009891, 0.420859), (180.0, 330.255127, 0.0), (0.34536, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.303897), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.026523, 0.329198), (180.0, 352.405365, 0.0), (0.41184, 0.41184, 0.41184)), + 'cs': ((0.0, -0.093244, 0.256664), (180.0, 343.300751, 0.0), (0.384419, 0.384419, 0.384419)), + 'sl': ((0.0, -0.05817, 0.151832), (180.0, 336.801422, 0.0), (0.362878, 0.362878, 0.362878)), + 'ss': ((0.0, -0.089353, 0.067922), (180.0, 319.398712, 0.0), (0.359873, 0.359873, 0.359873)), + 'rl': ((0.0, -0.00253, 0.209439), (180.0, 340.346161, 0.0), (0.331792, 0.331792, 0.331792)), + 'rs': ((0.0, -0.01575, 0.152739), (180.0, 336.801422, 0.0), (0.356531, 0.356531, 0.356531)), + 'ml': ((0.0, -0.032083, 0.604757), (180.0, 333.434937, 0.0), (0.597447, 0.45943, 0.581963)), + 'ms': ((0.0, -0.009932, 0.492561), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.061669, 0.439301), (180.0, 341.565063, 0.0), (0.548073, 0.548073, 0.548073)), + 'fs': ((0.0, -0.076523, 0.530715), (180.0, 345.963745, 0.0), (0.527899, 0.51, 0.458304))}, + 39: {'hl': ((0.0, -0.370962, 0.465244), (180.0, 315.0, 0.0), (0.194279, 0.194279, 0.194279)), + 'hs': ((0.0, -0.27024, 0.351372), (180.0, 311.633545, 0.0), (0.155642, 0.155642, 0.155642)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, 0.003874, 0.52465), (180.0, 333.434937, 0.0), (0.355373, 0.355373, 0.355373)), + 'ds': ((0.0, 0.069869, 0.31272), (180.0, 323.130096, 0.0), (0.36301, 0.36301, 0.36301)), + 'bl': ((0.0, -0.003084, 0.459231), (180.0, 340.016907, 0.0), (0.342305, 0.342305, 0.342305)), + 'bs': ((0.0, 0.032354, 0.298681), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.007892, 0.498436), (180.0, 352.405365, 0.0), (0.37638, 0.37638, 0.37638)), + 'cs': ((0.0, -0.049355, 0.279454), (180.0, 343.300751, 0.0), (0.388102, 0.388102, 0.388102)), + 'sl': ((0.0, -0.033132, 0.137355), (180.0, 336.037506, 0.0), (0.359353, 0.359353, 0.359353)), + 'ss': ((0.0, -0.092922, 0.06292), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, -0.016446, 0.309796), (180.0, 340.346161, 0.0), (0.289685, 0.276488, 0.25764)), + 'rs': ((0.0, -0.019304, 0.2101), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.040067, 0.564875), (180.0, 333.434937, 0.0), (0.597447, 0.45943, 0.581963)), + 'ms': ((0.0, -0.051857, 0.478629), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.092176, 0.491097), (180.0, 341.565063, 0.0), (0.527025, 0.507327, 0.604765)), + 'fs': ((0.0, -0.102106, 0.406191), (180.0, 345.963745, 0.0), (0.523747, 0.523747, 0.523747))}, + 40: {'hl': ((0.0, -0.251709, 0.329275), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, 0.011017, 0.32744), (180.0, 330.0, 0.0), (0.431377, 0.431377, 0.431377)), + 'ps': ((0.0, 0.073004, 0.277736), (180.0, 323.972626, 0.0), (0.362429, 0.362429, 0.362429)), + 'dl': ((0.0, -0.044813, 0.42961), (180.0, 333.434937, 0.0), (0.337081, 0.337081, 0.337081)), + 'ds': ((0.0, -0.048714, 0.350014), (180.0, 333.434937, 0.0), (0.340109, 0.340109, 0.340109)), + 'bl': ((0.0, 0.01989, 0.360009), (180.0, 336.037506, 0.0), (0.328004, 0.328004, 0.328004)), + 'bs': ((0.0, 0.000781, 0.296234), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.084146, 0.338479), (180.0, 340.016907, 0.0), (0.436503, 0.436503, 0.436503)), + 'cs': ((0.0, -0.084972, 0.275964), (180.0, 343.300751, 0.0), (0.440246, 0.440246, 0.440246)), + 'sl': ((0.0, -0.036041, 0.139261), (180.0, 336.801422, 0.0), (0.330952, 0.330952, 0.330952)), + 'ss': ((0.0, -0.03715, 0.039413), (180.0, 319.398712, 0.0), (0.342313, 0.342313, 0.342313)), + 'rl': ((0.0, -0.03045, 0.157932), (180.0, 340.346161, 0.0), (0.39319, 0.329908, 0.351655)), + 'rs': ((0.0, -0.062976, 0.121681), (180.0, 336.801422, 0.0), (0.356575, 0.322002, 0.352713)), + 'ml': ((0.0, -0.041088, 0.530235), (180.0, 333.434937, 0.0), (0.49824, 0.437567, 0.437567)), + 'ms': ((0.0, -0.051857, 0.449145), (180.0, 344.0, 0.0), (0.474517, 0.443477, 0.443477)), + 'fl': ((0.0, -0.144287, 0.503187), (180.0, 344.054596, 0.0), (0.516411, 0.516411, 0.516411)), + 'fs': ((0.0, -0.142902, 0.428907), (180.0, 345.963745, 0.0), (0.520535, 0.520535, 0.520535))}, + 41: {'hl': ((0.0, -0.084578, 0.354849), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, 0.008233, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, 0.143572, 0.398106), (180.0, 330.0, 0.0), (0.374423, 0.377233, 0.424631)), + 'ps': ((0.0, 0.154568, 0.286657), (180.0, 323.972626, 0.0), (0.317053, 0.348283, 0.348283)), + 'dl': ((0.0, 0.120854, 0.517766), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, 0.138863, 0.422914), (180.0, 343.300751, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, 0.162537, 0.383821), (180.0, 332.447174, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.147779, 0.392553), (180.0, 338.198578, 0.0), (0.33965, 0.33965, 0.33965)), + 'cl': ((0.0, 0.136744, 0.399326), (180.0, 352.405365, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, 0.094793, 0.26724), (180.0, 348.690063, 0.0), (0.350607, 0.403063, 0.307962)), + 'sl': ((0.0, 0.131652, 0.172827), (180.0, 336.801422, 0.0), (0.346467, 0.383177, 0.368997)), + 'ss': ((0.0, 0.113461, 0.126139), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, 0.07514, 0.106074), (180.0, 340.346161, 0.0), (0.35494, 0.310819, 0.325636)), + 'rs': ((0.0, 0.110525, 0.112845), (180.0, 336.801422, 0.0), (0.313714, 0.322002, 0.352713)), + 'ml': ((0.0, 0.201553, 0.490531), (180.0, 333.434937, 0.0), (0.505534, 0.467415, 0.581963)), + 'ms': ((0.0, 0.14557, 0.549109), (180.0, 344.0, 0.0), (0.428046, 0.421804, 0.469385)), + 'fl': ((0.0, 0.086472, 0.552777), (180.0, 341.565063, 0.0), (0.463455, 0.463455, 0.463455)), + 'fs': ((0.0, 0.065404, 0.441737), (180.0, 345.963745, 0.0), (0.450962, 0.450962, 0.450962))}, + 42: {'hl': ((0.0, -0.286549, 0.386187), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.017794, 0.340809), (180.0, 330.0, 0.0), (0.458436, 0.458436, 0.458436)), + 'ps': ((0.0, 0.097452, 0.239314), (180.0, 323.972626, 0.0), (0.389192, 0.389192, 0.389192)), + 'dl': ((0.0, 0.043303, 0.45846), (180.0, 352.405365, 0.0), (0.341148, 0.341148, 0.341148)), + 'ds': ((0.0, 0.03943, 0.391337), (180.0, 345.963745, 0.0), (0.324893, 0.324893, 0.324893)), + 'bl': ((0.0, 0.064688, 0.291713), (180.0, 341.565063, 0.0), (0.349791, 0.403928, 0.427131)), + 'bs': ((0.0, 0.061082, 0.270894), (180.0, 338.198578, 0.0), (0.367503, 0.367503, 0.367503)), + 'cl': ((0.0, 0.012301, 0.394174), (180.0, 5.194429, 0.0), (0.360603, 0.40683, 0.342697)), + 'cs': ((0.0, -0.027674, 0.270412), (180.0, 0.0, 0.0), (0.401389, 0.461443, 0.352567)), + 'sl': ((0.0, -0.049582, 0.109724), (180.0, 336.801422, 0.0), (0.377995, 0.437348, 0.32446)), + 'ss': ((0.0, -0.020503, 0.042076), (180.0, 319.398712, 0.0), (0.387488, 0.387488, 0.387488)), + 'rl': ((0.0, -0.05794, 0.19864), (180.0, 340.346161, 0.0), (0.318473, 0.318473, 0.318473)), + 'rs': ((0.0, -0.028572, 0.162014), (180.0, 336.801422, 0.0), (0.328632, 0.328632, 0.328632)), + 'ml': ((0.0, 0.06944, 0.497543), (180.0, 333.434937, 0.0), (0.525626, 0.525626, 0.525626)), + 'ms': ((0.0, -0.024846, 0.441586), (180.0, 344.0, 0.0), (0.456958, 0.456958, 0.456958)), + 'fl': ((0.0, 0.059544, 0.327538), (180.0, 341.565063, 0.0), (0.465083, 0.602695, 0.527746)), + 'fs': ((0.0, 0.008886, 0.310591), (180.0, 345.963745, 0.0), (0.493619, 0.518037, 0.518037))}, + 43: {'hl': ((0.0, -0.228686, 0.356353), (180.0, 320.194427, 0.0), (0.23157, 0.23157, 0.23157)), + 'hs': ((0.0, -0.182653, 0.236684), (180.0, 311.633545, 0.0), (0.21686, 0.21686, 0.21686)), + 'pl': ((0.0, -0.051214, 0.370904), (180.0, 330.0, 0.0), (0.368753, 0.354921, 0.368753)), + 'ps': ((0.0, 0.087279, 0.220612), (180.0, 338.198578, 0.0), (0.31121, 0.31121, 0.31121)), + 'dl': ((0.0, -0.030876, 0.485436), (180.0, 333.434937, 0.0), (0.295951, 0.289076, 0.364691)), + 'ds': ((0.0, 0.032724, 0.333517), (180.0, 333.434937, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.044879, 0.401084), (180.0, 337.380127, 0.0), (0.353446, 0.353446, 0.353446)), + 'bs': ((0.0, 0.010719, 0.297905), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.007381, 0.422222), (180.0, 352.405365, 0.0), (0.324078, 0.324078, 0.324078)), + 'cs': ((0.0, -0.051448, 0.21626), (180.0, 343.300751, 0.0), (0.350607, 0.350607, 0.350607)), + 'sl': ((0.0, -0.049603, 0.161602), (180.0, 336.801422, 0.0), (0.300563, 0.300563, 0.300563)), + 'ss': ((0.0, -0.039971, 0.032108), (180.0, 319.398712, 0.0), (0.327619, 0.327619, 0.327619)), + 'rl': ((0.0, -0.024611, 0.188832), (180.0, 340.346161, 0.0), (0.276816, 0.276816, 0.276816)), + 'rs': ((0.0, -0.039301, 0.158296), (180.0, 336.801422, 0.0), (0.276859, 0.276859, 0.276859)), + 'ml': ((0.0, -0.063149, 0.621559), (180.0, 344.054596, 0.0), (0.414732, 0.414732, 0.414732)), + 'ms': ((0.0, -0.051857, 0.43725), (180.0, 344.0, 0.0), (0.385786, 0.385786, 0.385786)), + 'fl': ((0.0, -0.115042, 0.523577), (180.0, 341.565063, 0.0), (0.451344, 0.451344, 0.451344)), + 'fs': ((0.0, -0.095579, 0.345507), (180.0, 345.963745, 0.0), (0.453144, 0.453144, 0.453144))}, + 44: {'hl': ((0.0, -0.328898, 0.453809), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.268249), (180.0, 311.633545, 0.0), (0.302123, 0.302123, 0.302123)), + 'pl': ((0.0, -0.175888, 0.628293), (180.0, 330.0, 0.0), (0.451127, 0.451127, 0.451127)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.381628, 0.381628, 0.381628)), + 'dl': ((0.0, -0.135721, 0.753186), (180.0, 333.434937, 0.0), (0.342229, 0.342229, 0.435064)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.352882, 0.352882, 0.352882)), + 'bl': ((0.0, -0.098169, 0.717164), (180.0, 341.565063, 0.0), (0.409048, 0.409048, 0.409048)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.373375, 0.373375, 0.373375)), + 'cl': ((0.0, -0.044478, 0.64138), (180.0, 352.405365, 0.0), (0.414064, 0.414064, 0.414064)), + 'cs': ((0.0, -0.107646, 0.391816), (180.0, 343.300751, 0.0), (0.436207, 0.436207, 0.436207)), + 'sl': ((0.0, -0.175185, 0.265483), (180.0, 330.945404, 0.0), (0.437525, 0.437525, 0.462425)), + 'ss': ((0.0, -0.138924, 0.175421), (180.0, 319.398712, 0.0), (0.42384, 0.42384, 0.42384)), + 'rl': ((0.0, -0.077274, 0.353435), (180.0, 340.346161, 0.0), (0.311073, 0.311073, 0.311073)), + 'rs': ((0.0, -0.064793, 0.312286), (180.0, 336.801422, 0.0), (0.329469, 0.329469, 0.329469)), + 'ml': ((0.0, -0.081206, 0.886824), (180.0, 350.537689, 0.0), (0.489672, 0.489672, 0.510828)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.554896, 0.483187, 0.468427)), + 'fl': ((0.0, -0.184093, 0.780337), (180.0, 341.565063, 0.0), (0.524276, 0.581964, 0.672796)), + 'fs': ((0.0, -0.159124, 0.658902), (180.0, 345.963745, 0.0), (0.550204, 0.550204, 0.550204))}, + 45: {'hl': ((0.0, -0.241128, 0.417908), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.200778, 0.303221), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.047732, 0.552948), (180.0, 348.690063, 0.0), (0.492423, 0.403813, 0.424631)), + 'ps': ((0.0, 0.056255, 0.354062), (180.0, 338.962494, 0.0), (0.403027, 0.396591, 0.375032)), + 'dl': ((0.0, -0.000591, 0.732163), (180.0, 0.0, 0.0), (0.325423, 0.325423, 0.371332)), + 'ds': ((0.0, 0.042396, 0.453346), (180.0, 323.130096, 0.0), (0.350369, 0.341622, 0.341622)), + 'bl': ((0.0, -0.035221, 0.649262), (180.0, 5.710593, 0.0), (0.34146, 0.334907, 0.350949)), + 'bs': ((0.0, 0.072326, 0.446993), (180.0, 0.0, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.034271, 0.544421), (180.0, 0.0, 0.0), (0.423135, 0.416994, 0.392359)), + 'cs': ((0.0, -0.026709, 0.30591), (180.0, 345.068573, 0.0), (0.485345, 0.429348, 0.364762)), + 'sl': ((0.0, -0.020918, 0.24747), (180.0, 345.068573, 0.0), (0.39036, 0.438042, 0.404339)), + 'ss': ((0.0, -0.101919, 0.14662), (180.0, 326.309937, 0.0), (0.366286, 0.421492, 0.364771)), + 'rl': ((0.0, -0.024001, 0.253725), (180.0, 348.690063, 0.0), (0.346643, 0.33651, 0.320607)), + 'rs': ((0.0, -0.012127, 0.304147), (180.0, 355.601288, 0.0), (0.342155, 0.318324, 0.316706)), + 'ml': ((0.0, -0.05639, 0.774362), (180.0, 353.659821, 0.0), (0.58308, 0.458441, 0.599689)), + 'ms': ((0.0, -0.051283, 0.614941), (180.0, 344.0, 0.0), (0.559086, 0.44277, 0.581816)), + 'fl': ((0.0, -0.098486, 0.693572), (180.0, 3.0, 0.0), (0.547692, 0.518618, 0.604765)), + 'fs': ((0.0, -0.1132, 0.623452), (180.0, 345.963745, 0.0), (0.555113, 0.51, 0.458304))}, + 46: {'hl': ((0.0, -0.2364, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.183944, 0.251387), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.096859, 0.57), (180.0, 330.0, 0.0), (0.494291, 0.377233, 0.424631)), + 'ps': ((0.0, -0.010074, 0.353951), (180.0, 323.972626, 0.0), (0.411516, 0.359163, 0.359163)), + 'dl': ((0.0, -0.021335, 0.685092), (180.0, 333.434937, 0.0), (0.317983, 0.317983, 0.384017)), + 'ds': ((0.0, 0.018677, 0.46431), (180.0, 323.130096, 0.0), (0.346891, 0.346891, 0.346891)), + 'bl': ((0.0, -0.050428, 0.574968), (180.0, 341.565063, 0.0), (0.350284, 0.369367, 0.395716)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, 0.028487, 0.645978), (180.0, 352.405365, 0.0), (0.411501, 0.376726, 0.342697)), + 'cs': ((0.0, -0.058881, 0.382862), (180.0, 343.300751, 0.0), (0.421838, 0.403063, 0.307962)), + 'sl': ((0.0, -0.085229, 0.296321), (180.0, 336.801422, 0.0), (0.332158, 0.382887, 0.382887)), + 'ss': ((0.0, -0.091855, 0.126139), (180.0, 319.398712, 0.0), (0.385279, 0.413317, 0.413317)), + 'rl': ((0.0, -0.025175, 0.353435), (180.0, 340.346161, 0.0), (0.325989, 0.281306, 0.251126)), + 'rs': ((0.0, -0.035506, 0.298774), (180.0, 336.801422, 0.0), (0.389809, 0.30633, 0.269237)), + 'ml': ((0.0, -0.078541, 0.768487), (180.0, 333.434937, 0.0), (0.561767, 0.470323, 0.422517)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.510816, 0.422641, 0.422641)), + 'fl': ((0.0, -0.115715, 0.701924), (180.0, 341.565063, 0.0), (0.56429, 0.507327, 0.515048)), + 'fs': ((0.0, -0.12798, 0.623452), (180.0, 345.963745, 0.0), (0.538414, 0.51, 0.458304))}, + 47: {'hl': ((0.0, -0.32198, 0.406916), (180.0, 320.194427, 0.0), (0.293796, 0.284073, 0.285614)), + 'hs': ((0.0, -0.205193, 0.236684), (180.0, 311.633545, 0.0), (0.291181, 0.268914, 0.268914)), + 'pl': ((0.0, -0.175888, 0.57), (180.0, 330.0, 0.0), (0.433654, 0.377233, 0.424631)), + 'dl': ((0.0, -0.1, 0.507172), (180.0, 333.434937, 0.0), (0.354193, 0.354193, 0.354193)), + 'ds': ((0.0, -0.048714, 0.46431), (180.0, 323.130096, 0.0), (0.327934, 0.327934, 0.327934)), + 'bl': ((0.0, -0.098169, 0.498031), (180.0, 341.565063, 0.0), (0.334803, 0.38662, 0.408829)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.326553, 0.326553, 0.326553)), + 'cl': ((0.0, -0.255482, 0.322577), (180.0, 352.405365, 0.0), (0.436398, 0.436398, 0.436398)), + 'cs': ((0.0, -0.14621, 0.247035), (180.0, 343.300751, 0.0), (0.406043, 0.406043, 0.406043)), + 'sl': ((0.0, -0.137519, 0.296321), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'ss': ((0.0, -0.138924, 0.126139), (180.0, 319.398712, 0.0), (0.357568, 0.387685, 0.34839)), + 'rl': ((0.0, -0.107251, 0.353435), (180.0, 340.346161, 0.0), (0.294208, 0.294208, 0.294208)), + 'rs': ((0.0, -0.064793, 0.257018), (180.0, 336.801422, 0.0), (0.356575, 0.356575, 0.356575)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.560363, 0.560363, 0.560363)), + 'ms': ((0.0, -0.088633, 0.45922), (180.0, 344.0, 0.0), (0.527299, 0.527299, 0.527299)), + 'fl': ((0.0, -0.184093, 0.503724), (180.0, 333.434937, 0.0), (0.554647, 0.554647, 0.554647)), + 'fs': ((0.0, -0.21074, 0.516329), (180.0, 345.963745, 0.0), (0.551474, 0.551474, 0.551474))}, + 48: {'hl': ((0.0, -0.379865, 0.459952), (180.0, 311.185913, 0.0), (0.220475, 0.220475, 0.220475)), + 'hs': ((0.0, -0.283891, 0.379233), (180.0, 311.633545, 0.0), (0.175105, 0.175105, 0.175105)), + 'pl': ((0.0, -0.175888, 0.607607), (180.0, 330.0, 0.0), (0.415325, 0.350414, 0.350414)), + 'ps': ((0.0, -0.084697, 0.353951), (180.0, 323.972626, 0.0), (0.348283, 0.348283, 0.348283)), + 'dl': ((0.0, -0.1, 0.803362), (180.0, 333.434937, 0.0), (0.249582, 0.249582, 0.249582)), + 'ds': ((0.0, -0.134309, 0.55885), (180.0, 323.130096, 0.0), (0.25313, 0.25313, 0.25313)), + 'bl': ((0.0, -0.081709, 0.574968), (180.0, 341.565063, 0.0), (0.350462, 0.355917, 0.408829)), + 'cl': ((0.0, -0.004879, 0.645978), (180.0, 352.405365, 0.0), (0.360603, 0.379238, 0.342697)), + 'cs': ((-0.052862, -0.045363, 0.425891), (180.0, 354.289398, 10.304847), (0.350607, 0.379473, 0.307962)), + 'sl': ((0.0, -0.171653, 0.530645), (180.0, 336.801422, 0.0), (0.196465, 0.209684, 0.209684)), + 'ss': ((0.0, -0.302327, 0.311569), (180.0, 319.398712, 0.0), (0.192818, 0.192818, 0.192818)), + 'rl': ((0.0, -0.061428, 0.368627), (180.0, 340.346161, 0.0), (0.283145, 0.283145, 0.283145)), + 'rs': ((0.0, -0.064793, 0.332653), (180.0, 336.801422, 0.0), (0.369749, 0.316126, 0.27979)), + 'ml': ((0.0, -0.107093, 0.841645), (180.0, 333.434937, 0.0), (0.531162, 0.427389, 0.427389)), + 'ms': ((0.0, -0.107107, 0.703806), (180.0, 344.0, 0.0), (0.446526, 0.360857, 0.446526)), + 'fl': ((0.132628, -0.076284, 1.146246), (180.0, 11.309933, 345.963745), (0.302753, 0.302753, 0.302753)), + 'fs': ((-0.096783, -0.068098, 0.950602), (180.0, 4.398705, 7.125016), (0.265791, 0.265791, 0.265791))}, + 49: {'hl': ((0.0, -0.18204, 0.334817), (180.0, 320.194427, 0.0), (0.316307, 0.316307, 0.316307)), + 'hs': ((0.0, -0.135678, 0.221156), (180.0, 311.633545, 0.0), (0.304362, 0.304362, 0.304362)), + 'pl': ((0.0, -0.110108, 0.572772), (180.0, 330.0, 0.0), (0.444693, 0.444457, 0.416579)), + 'ps': ((0.0, 0.055268, 0.322045), (180.0, 323.972626, 0.0), (0.372883, 0.372883, 0.372883)), + 'dl': ((0.0, 0.0344, 0.66062), (180.0, 350.0, 0.0), (0.344863, 0.324908, 0.376133)), + 'ds': ((0.0, 0.058199, 0.600436), (180.0, 355.0, 0.0), (0.314241, 0.311441, 0.331872)), + 'bl': ((0.0, 0.018331, 0.426509), (180.0, 335.55603, 0.0), (0.356082, 0.38662, 0.408829)), + 'bs': ((0.0, 0.07111, 0.303576), (180.0, 338.198578, 0.0), (0.350289, 0.390083, 0.367367)), + 'cl': ((0.0, -0.009119, 0.365049), (180.0, 348.690063, 0.0), (0.435275, 0.484142, 0.426705)), + 'cs': ((0.0, -0.08866, 0.311198), (180.0, 345.963745, 0.0), (0.446451, 0.509151, 0.438783)), + 'sl': ((0.0, -0.055102, 0.25231), (180.0, 340.0, 0.0), (0.365126, 0.419666, 0.367644)), + 'ss': ((0.0, -0.093716, 0.280104), (180.0, 340.0, 0.0), (0.308486, 0.360756, 0.308486)), + 'rl': ((0.0, -0.025355, 0.279759), (180.0, 350.0, 0.0), (0.330575, 0.30742, 0.351356)), + 'rs': ((0.0, -0.005626, 0.304083), (180.0, 350.0, 0.0), (0.330629, 0.321739, 0.334487)), + 'ml': ((0.0, 0.006205, 0.730745), (180.0, 350.0, 0.0), (0.541236, 0.495014, 0.505914)), + 'ms': ((0.0, -0.041817, 0.657982), (180.0, 350.0, 0.0), (0.528558, 0.428241, 0.440781)), + 'fl': ((0.0, -0.025665, 0.472024), (180.0, 341.565063, 0.0), (0.537187, 0.568602, 0.504242)), + 'fs': ((0.0, -0.053262, 0.541627), (180.0, 350.0, 0.0), (0.536297, 0.551713, 0.536297))}, + 50: {'hl': ((0.0, -0.161351, 0.238781), (180.0, 320.194427, 0.0), (0.22609, 0.22609, 0.253839)), + 'hs': ((0.0, -0.105822, 0.167984), (180.0, 311.633545, 0.0), (0.227463, 0.227463, 0.267459)), + 'pl': ((0.0, 0.044602, 0.222782), (180.0, 330.0, 0.0), (0.355647, 0.325393, 0.387358)), + 'ps': ((0.0, 0.074645, 0.161964), (180.0, 323.972626, 0.0), (0.290684, 0.279793, 0.345262)), + 'dl': ((0.0, 0.069948, 0.264177), (180.0, 333.434937, 0.0), (0.319059, 0.312181, 0.404947)), + 'ds': ((0.0, 0.079943, 0.178171), (180.0, 323.130096, 0.0), (0.318405, 0.318405, 0.395141)), + 'bl': ((0.0, 0.086611, 0.238876), (180.0, 324.462311, 0.0), (0.311766, 0.311766, 0.347182)), + 'bs': ((0.0, 0.09289, 0.177123), (180.0, 333.434937, 0.0), (0.278967, 0.278967, 0.321948)), + 'cl': ((0.0, -0.011046, 0.01), (180.0, 341.565063, 0.0), (0.400879, 0.358704, 0.482993)), + 'cs': ((0.0, -0.017134, 0.007449), (180.0, 343.300751, 0.0), (0.403063, 0.351357, 0.482298)), + 'sl': ((0.0, -0.003789, 0.041869), (180.0, 336.801422, 0.0), (0.287709, 0.305749, 0.336502)), + 'ss': ((0.0, 0.069593, -0.009474), (180.0, 319.398712, 0.0), (0.294156, 0.294156, 0.343695)), + 'rl': ((0.0, -0.005708, 0.072391), (180.0, 340.346161, 0.0), (0.292084, 0.251606, 0.305578)), + 'rs': ((0.0, -0.013988, 0.104925), (180.0, 336.801422, 0.0), (0.266919, 0.2285, 0.303516)), + 'ml': ((0.0, 0.055975, 0.422612), (180.0, 333.434937, 0.0), (0.414861, 0.349499, 0.475353)), + 'ms': ((0.0, 0.009975, 0.385303), (180.0, 344.0, 0.0), (0.383794, 0.323108, 0.434328)), + 'fl': ((0.0, -0.02587, 0.28487), (180.0, 341.565063, 0.0), (0.439388, 0.44, 0.509668)), + 'fs': ((0.0, -0.031342, 0.241033), (180.0, 345.963745, 0.0), (0.416083, 0.416083, 0.505574))}, + 51: {'hl': ((0.0, -0.336955, 0.45264), (180.0, 320.194427, 0.0), (0.146723, 0.146723, 0.146723)), + 'hs': ((0.0, -0.292919, 0.358681), (180.0, 309.805573, 0.0), (0.10362, 0.10362, 0.10362)), + 'pl': ((0.0, -0.109834, 0.526816), (180.0, 330.0, 0.0), (0.275856, 0.239965, 0.270116)), + 'ps': ((0.0, -0.128212, 0.441408), (180.0, 323.972626, 0.0), (0.19634, 0.179978, 0.19634)), + 'dl': ((0.0, -0.073249, 0.721831), (180.0, 333.434937, 0.0), (0.179183, 0.179183, 0.179183)), + 'ds': ((0.003206, -0.027011, 0.539462), (180.0, 333.434937, 0.0), (0.187017, 0.163523, 0.163523)), + 'bl': ((0.0, -0.098169, 0.613644), (180.0, 341.565063, 0.0), (0.191202, 0.191202, 0.191202)), + 'bs': ((0.0, 0.004136, 0.392553), (180.0, 338.198578, 0.0), (0.200878, 0.200878, 0.200878)), + 'cl': ((-0.036369, -0.011516, 0.645978), (180.0, 352.405365, 6.340192), (0.225296, 0.217393, 0.212222)), + 'cs': ((0.0, -0.062589, 0.382862), (180.0, 343.300751, 4.398705), (0.247978, 0.247978, 0.247978)), + 'sl': ((0.0, -0.103722, 0.311299), (180.0, 336.801422, 0.0), (0.189026, 0.22511, 0.230503)), + 'ss': ((0.0, -0.197966, 0.209493), (180.0, 319.398712, 0.0), (0.176416, 0.211725, 0.190265)), + 'rl': ((0.0, -0.052372, 0.353435), (180.0, 340.346161, 0.0), (0.168127, 0.160467, 0.149528)), + 'rs': ((0.0, -0.067906, 0.257018), (180.0, 336.801422, 0.0), (0.196729, 0.177654, 0.194598)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.347874, 0.286073, 0.358574)), + 'ms': ((0.0, -0.051857, 0.614941), (180.0, 344.0, 0.0), (0.286768, 0.249709, 0.242081)), + 'fl': ((0.022361, -0.185967, 0.682343), (198.434952, 341.565063, 0.0), (0.335775, 0.345189, 0.345189)), + 'fs': ((0.0, -0.150963, 0.623452), (180.0, 345.963745, 0.0), (0.311173, 0.322407, 0.311763))}, + 52: {'hl': ((0.0, -0.226334, 0.320665), (180.0, 320.194427, 0.0), (0.293796, 0.307458, 0.285614)), + 'hs': ((0.0, -0.177439, 0.236684), (180.0, 311.633545, 0.0), (0.295823, 0.290815, 0.268914)), + 'pl': ((0.0, -0.083734, 0.442113), (180.0, 330.0, 0.0), (0.456098, 0.426337, 0.424631)), + 'ps': ((0.0, 0.014579, 0.248759), (180.0, 333.434937, 0.0), (0.373453, 0.418225, 0.380561)), + 'dl': ((0.0, -0.0512, 0.611287), (180.0, 333.434937, 0.0), (0.321924, 0.332988, 0.332093)), + 'ds': ((0.0, -0.009323, 0.478144), (180.0, 333.434937, 0.0), (0.335801, 0.330875, 0.347303)), + 'bl': ((0.0, -0.039067, 0.459635), (180.0, 338.962494, 0.0), (0.339525, 0.395091, 0.384313)), + 'bs': ((0.0, 0.074196, 0.222437), (180.0, 320.710602, 0.0), (0.338939, 0.367465, 0.379679)), + 'cl': ((0.0, -0.027292, 0.402756), (180.0, 344.054596, 0.0), (0.3893, 0.442017, 0.371083)), + 'cs': ((0.0, -0.090977, 0.293858), (180.0, 343.300751, 0.0), (0.390856, 0.442619, 0.360844)), + 'sl': ((0.0, -0.077828, 0.180907), (180.0, 336.801422, 0.0), (0.338979, 0.448591, 0.326381)), + 'ss': ((0.0, -0.059941, 0.09653), (180.0, 323.130096, 0.0), (0.351759, 0.42779, 0.34839)), + 'rl': ((0.0, -0.032308, 0.217786), (180.0, 340.346161, 0.0), (0.351805, 0.335044, 0.277298)), + 'rs': ((0.0, -0.038272, 0.184249), (180.0, 336.801422, 0.0), (0.347485, 0.34102, 0.352713)), + 'ml': ((0.0, -0.085907, 0.611453), (180.0, 333.434937, 0.0), (0.558198, 0.492803, 0.571304)), + 'ms': ((0.0, -0.086949, 0.539928), (180.0, 330.945404, 0.0), (0.560604, 0.459901, 0.488545)), + 'fl': ((0.0, -0.126071, 0.564014), (180.0, 345.963745, 0.0), (0.525956, 0.534213, 0.488643)), + 'fs': ((0.0, -0.106, 0.440965), (180.0, 331.389526, 0.0), (0.533015, 0.52384, 0.458304))}, + 53: {'hl': ((0.020459, -0.210665, 0.345419), (180.0, 320.194427, 350.0), (0.266338, 0.284073, 0.308024)), + 'hs': ((0.02712, -0.18269, 0.236684), (180.0, 311.633545, 350.0), (0.267142, 0.258474, 0.268914)), + 'pl': ((0.014521, -0.093809, 0.505128), (180.0, 330.0, 350.0), (0.427842, 0.363547, 0.424631)), + 'ps': ((0.019338, -0.01701, 0.293182), (180.0, 323.972626, 350.0), (0.342165, 0.342165, 0.342165)), + 'dl': ((0.008258, -0.05772, 0.610897), (180.0, 333.434937, 350.0), (0.295951, 0.289076, 0.411137)), + 'ds': ((0.022613, 0.000299, 0.408023), (180.0, 323.130096, 350.0), (0.308948, 0.308948, 0.308948)), + 'bl': ((0.0, -0.070305, 0.536696), (180.0, 341.565063, 350.0), (0.334803, 0.353877, 0.407806)), + 'bs': ((0.019763, 0.007837, 0.370366), (180.0, 338.198578, 350.0), (0.31034, 0.31034, 0.31034)), + 'cl': ((0.016021, -0.025354, 0.499371), (180.0, 352.405365, 350.0), (0.360603, 0.360596, 0.376552)), + 'cs': ((0.024724, -0.091999, 0.360134), (180.0, 343.300751, 350.0), (0.350607, 0.382517, 0.307962)), + 'sl': ((0.022434, -0.104239, 0.212358), (180.0, 336.801422, 350.0), (0.30672, 0.383177, 0.369655)), + 'ss': ((0.042464, -0.107015, 0.123057), (180.0, 319.398712, 350.0), (0.310777, 0.379575, 0.316983)), + 'rl': ((0.030567, -0.035828, 0.27221), (180.0, 341.565063, 355.236359), (0.297828, 0.277349, 0.25764)), + 'rs': ((0.017497, -0.05696, 0.278086), (180.0, 350.0, 350.537689), (0.298738, 0.275566, 0.276556)), + 'ml': ((0.012491, -0.04269, 0.599857), (180.0, 333.434937, 350.0), (0.51396, 0.45943, 0.581963)), + 'ms': ((0.041512, -0.032625, 0.548914), (180.0, 344.0, 350.0), (0.499605, 0.416345, 0.468427)), + 'fl': ((0.0, -0.10554, 0.532121), (180.0, 341.565063, 350.0), (0.507808, 0.493757, 0.637366)), + 'fs': ((0.03488, -0.125225, 0.530575), (180.0, 345.963745, 350.0), (0.483888, 0.47173, 0.469081))}, + 54: {'hl': ((0.0, -0.172063, 0.39568), (180.0, 340.0, 0.0), (0.265415, 0.265415, 0.265415)), + 'hs': ((0.0, -0.100836, 0.354621), (180.0, 340.0, 0.0), (0.260796, 0.260796, 0.260796)), + 'pl': ((0.0, -0.105848, 0.504514), (180.0, 345.0, 0.0), (0.324843, 0.324843, 0.324843)), + 'ps': ((0.0, -0.05623, 0.375395), (180.0, 345.0, 0.0), (0.297445, 0.297445, 0.297445)), + 'dl': ((0.0, 0.056451, 0.571274), (180.0, 345.0, 0.0), (0.299061, 0.299061, 0.299061)), + 'ds': ((0.0, 0.096606, 0.556323), (180.0, 345.0, 0.0), (0.281711, 0.281711, 0.281711)), + 'bl': ((0.0, 0.064179, 0.411482), (180.0, 345.0, 0.0), (0.277218, 0.277218, 0.277218)), + 'bs': ((0.0, 0.082597, 0.366588), (180.0, 345.0, 0.0), (0.287716, 0.287716, 0.287716)), + 'cl': ((0.0, -0.209901, 0.348118), (180.0, 345.0, 0.0), (0.360488, 0.360488, 0.360488)), + 'cs': ((0.0, -0.292991, 0.321374), (180.0, 345.0, 0.0), (0.321124, 0.321124, 0.321124)), + 'sl': ((0.0, -0.07251, 0.192706), (180.0, 345.0, 0.0), (0.319115, 0.319115, 0.319115)), + 'ss': ((0.0, -0.016214, 0.186471), (180.0, 345.0, 0.0), (0.311696, 0.311696, 0.311696)), + 'rl': ((0.0, -0.042589, 0.219306), (180.0, 345.0, 0.0), (0.311668, 0.311668, 0.311668)), + 'rs': ((0.0, -0.067707, 0.218238), (180.0, 345.0, 0.0), (0.292378, 0.292378, 0.292378)), + 'ml': ((0.0, -0.047411, 0.786543), (180.0, 345.0, 0.0), (0.403984, 0.403984, 0.403984)), + 'ms': ((0.0, -0.012533, 0.652898), (180.0, 345.0, 0.0), (0.376858, 0.376858, 0.376858)), + 'fl': ((0.0, -0.148821, 0.654557), (180.0, 345.0, 0.0), (0.388997, 0.388997, 0.388997)), + 'fs': ((0.0, -0.200781, 0.623452), (180.0, 345.0, 0.0), (0.383601, 0.383601, 0.383601))}, + 55: {'hl': ((0.0, -0.228169, 0.399476), (180.0, 340.0, 0.0), (0.27971, 0.27971, 0.27971)), + 'hs': ((0.0, -0.122724, 0.311463), (180.0, 340.0, 0.0), (0.255711, 0.255711, 0.255711)), + 'pl': ((0.0, -0.08111, 0.509503), (180.0, 340.0, 0.0), (0.432522, 0.380665, 0.339927)), + 'ps': ((0.0, 0.021208, 0.266881), (180.0, 340.0, 0.0), (0.356491, 0.356491, 0.356491)), + 'dl': ((0.0, -0.001257, 0.50995), (180.0, 333.434937, 0.0), (0.328001, 0.328001, 0.328001)), + 'ds': ((0.0, 0.012151, 0.464755), (180.0, 339.44397, 0.0), (0.318405, 0.318405, 0.318405)), + 'bl': ((0.0, -0.032385, 0.428182), (180.0, 341.565063, 0.0), (0.334803, 0.334803, 0.334803)), + 'bs': ((0.0, -0.006513, 0.273053), (180.0, 338.198578, 0.0), (0.330546, 0.330546, 0.330546)), + 'cl': ((0.0, -0.032998, 0.508111), (180.0, 0.0, 0.0), (0.377949, 0.377949, 0.377949)), + 'cs': ((0.0, -0.115053, 0.368073), (180.0, 343.300751, 0.0), (0.358967, 0.328466, 0.328466)), + 'sl': ((0.0, -0.107067, 0.211263), (180.0, 336.801422, 0.0), (0.313981, 0.400916, 0.338408)), + 'ss': ((0.0, -0.128894, 0.14304), (180.0, 330.0, 0.0), (0.295818, 0.375986, 0.341114)), + 'rl': ((0.0, -0.040936, 0.19804), (180.0, 340.346161, 0.0), (0.333866, 0.312983, 0.329459)), + 'rs': ((-0.010545, -0.024856, 0.191691), (180.0, 340.0, 0.0), (0.345062, 0.328625, 0.328617)), + 'ml': ((0.0, -0.072264, 0.704137), (180.0, 350.0, 0.0), (0.495034, 0.470078, 0.444982)), + 'ms': ((0.0, -0.039924, 0.600244), (180.0, 350.0, 0.0), (0.458971, 0.411162, 0.410363)), + 'fl': ((0.0, -0.08348, 0.451257), (180.0, 341.565063, 0.0), (0.506291, 0.514455, 0.472984)), + 'fs': ((0.0, -0.123047, 0.514919), (180.0, 345.963745, 0.0), (0.497508, 0.476158, 0.438191))}, + 56: {'hl': ((0.0, -0.302389, 0.443009), (180.0, 320.194427, 0.0), (0.293796, 0.336659, 0.285614)), + 'hs': ((0.0, -0.235974, 0.280137), (180.0, 311.633545, 0.0), (0.352422, 0.32792, 0.268914)), + 'pl': ((0.0, -0.273686, 0.752608), (180.0, 330.0, 0.0), (0.377632, 0.377632, 0.377632)), + 'ps': ((0.0, -0.071794, 0.477607), (180.0, 335.0, 0.0), (0.36897, 0.36897, 0.36897)), + 'dl': ((0.0, -0.067366, 0.685092), (180.0, 333.434937, 0.0), (0.361692, 0.356022, 0.364691)), + 'ds': ((0.0, -0.073314, 0.516935), (180.0, 323.130096, 0.0), (0.364571, 0.364571, 0.364571)), + 'bl': ((0.0, -0.028989, 0.47762), (180.0, 331.389526, 0.0), (0.463479, 0.450893, 0.431349)), + 'bs': ((0.0, -0.015279, 0.389929), (180.0, 338.198578, 0.0), (0.419855, 0.419855, 0.419855)), + 'cl': ((0.0, -0.018993, 0.754007), (180.0, 352.405365, 0.0), (0.365337, 0.365337, 0.365337)), + 'cs': ((0.0, -0.079396, 0.533163), (180.0, 347.471191, 0.0), (0.359826, 0.359826, 0.359826)), + 'sl': ((0.0, -0.137519, 0.421834), (180.0, 336.801422, 0.0), (0.298001, 0.383177, 0.368997)), + 'ss': ((0.0, -0.272625, 0.275158), (180.0, 319.398712, 0.0), (0.352415, 0.352415, 0.352415)), + 'rl': ((0.0, -0.054797, 0.414761), (180.0, 340.346161, 0.0), (0.338241, 0.312043, 0.311832)), + 'rs': ((0.0, -0.099264, 0.463569), (180.0, 336.801422, 0.0), (0.305813, 0.305813, 0.305813)), + 'ml': ((0.0, -0.107093, 0.768487), (180.0, 333.434937, 0.0), (0.670874, 0.61412, 0.581963)), + 'ms': ((0.0, -0.051857, 0.656112), (180.0, 344.0, 0.0), (0.613962, 0.513484, 0.468427)), + 'fl': ((0.0, -0.149469, 0.711494), (180.0, 341.565063, 0.0), (0.633023, 0.668821, 0.589631)), + 'fs': ((0.0, -0.212405, 0.686062), (180.0, 335.224854, 0.0), (0.613244, 0.565097, 0.519809))}} +ExtendedGlassesTransTable = {1: {'fl': ((0.0, 0.143374, 0.12), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41))}, + 2: {'hl': ((0.0, 0.078454, 0.218205), (180.0, 351.869904, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.218512, 0.047869), (180.0, 320.0, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.142359, 0.203738), (180.0, 0.0, 0.0), (0.366415, 0.306053, 0.375362)), + 'ps': ((0.0, 0.155287, 0.151189), (180.0, 0.0, 0.0), (0.288139, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.277646), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.359171), (180.0, 8.130102, 0.0), (0.274636, 0.20839, 0.262733)), + 'cl': ((0.0, 0.148353, 0.121322), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.223866, 0.168877), (180.0, 4.085617, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, 0.022323), (180.0, 350.0, 0.0), (0.297347, 0.191886, 0.297347)), + 'ss': ((0.0, 0.301501, 0.003744), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.037004), (180.0, 350.537689, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.276756, -0.031632), (180.0, 354.289398, 0.0), (0.306648, 0.142252, 0.308336)), + 'ml': ((0.0, 0.090395, 0.340701), (180.0, 0.0, 0.0), (0.447977, 0.317201, 0.444383)), + 'ms': ((0.0, 0.146782, 0.328656), (180.0, 0.0, 0.0), (0.434794, 0.278491, 0.398912)), + 'fl': ((0.0, 0.085281, 0.231974), (180.0, 9.462322, 0.0), (0.414286, 0.320314, 0.41)), + 'fs': ((0.0, 0.081915, 0.195784), (180.0, 4.085617, 0.0), (0.42992, 0.319754, 0.42))}, + 3: {'pl': ((0.0, 0.100011, 0.167805), (180.0, 0.0, 0.0), (0.351783, 0.306053, 0.41622)), + 'ps': ((0.0, 0.181299, 0.089964), (180.0, 350.537689, 0.0), (0.279175, 0.295057, 0.28182)), + 'ds': ((0.0, 0.16996, 0.309818), (180.0, 3.814075, 0.0), (0.265522, 0.220048, 0.277431)), + 'ml': ((0.0, 0.054899, 0.321116), (180.0, 5.710593, 0.0), (0.417545, 0.314073, 0.492024)), + 'ms': ((0.0, 0.121952, 0.285459), (180.0, 0.0, 0.0), (0.414148, 0.278491, 0.424701)), + 'fl': ((0.0, 0.074909, 0.145675), (180.0, 3.17983, 0.0), (0.380459, 0.320314, 0.41)), + 'fs': ((0.0, 0.070644, 0.189464), (180.0, 8.365886, 0.0), (0.407842, 0.319754, 0.42))}, + 4: {'hl': ((0.0, 0.118239, 0.109231), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.140803, 0.175499), (180.0, 349.69516, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.131786, 0.154064), (180.0, 350.537689, 0.0), (0.357565, 0.306053, 0.375362)), + 'ds': ((0.0, 0.16996, 0.281233), (180.0, 350.0, 0.0), (0.281221, 0.220048, 0.277431)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.44, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.437745, 0.278491, 0.398912)), + 'fl': ((0.0, 0.084205, 0.234174), (180.0, 4.085617, 0.0), (0.413278, 0.320314, 0.41)), + 'fs': ((0.0, 0.104292, 0.181239), (180.0, 0.0, 0.0), (0.423149, 0.319754, 0.42))}, + 5: {'hs': ((0.0, 0.172497, 0.160052), (180.0, 351.869904, 0.0), (0.244122, 0.164205, 0.250863)), + 'pl': ((0.0, 0.11696, 0.201066), (180.0, 0.0, 0.0), (0.357565, 0.306053, 0.375362)), + 'dl': ((0.0, 0.204627, 0.281054), (180.0, 350.0, 0.0), (0.265293, 0.186357, 0.264825)), + 'ds': ((0.0, 0.16996, 0.279106), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'bs': ((0.0, 0.200803, 0.097391), (180.0, 350.0, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.0, 0.154052, 0.01827), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'sl': ((0.0, 0.335741, -0.018402), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.308654, -0.030958), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.037004), (180.0, 350.537689, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.276756, -0.031632), (180.0, 354.289398, 0.0), (0.306648, 0.142252, 0.308336)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.44, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.431919, 0.272512, 0.390348)), + 'fl': ((0.0, 0.098967, 0.216033), (180.0, 9.462322, 0.0), (0.400429, 0.320314, 0.41)), + 'fs': ((0.0, 0.087841, 0.182049), (180.0, 7.125016, 0.0), (0.415009, 0.319754, 0.42))}, + 6: {'hl': ((0.0, 0.118239, 0.073909), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'pl': ((0.0, 0.141548, 0.136942), (180.0, 351.869904, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.141343, 0.182113), (180.0, 0.0, 0.0), (0.282347, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.257552), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.243795), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'cl': ((0.0, 0.148353, 0.043679), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.067284), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, -0.018402), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.301501, -0.030958), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'fl': ((0.0, 0.136288, 0.109261), (180.0, 0.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.08556, 0.133995), (180.0, 4.763642, 0.0), (0.42, 0.319754, 0.42))}, + 7: {'fs': ((0.0, 0.129622, 0.13), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))}, + 8: {'hl': ((0.0, 0.118239, 0.102791), (180.0, 338.962494, 0.0), (0.243308, 0.19733, 0.288041)), + 'pl': ((0.0, 0.120805, 0.185922), (180.0, 0.0, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.151001, 0.176525), (180.0, 8.130102, 0.0), (0.278085, 0.290603, 0.277566)), + 'ds': ((0.0, 0.16996, 0.266553), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'cl': ((0.0, 0.148353, 0.06), (180.0, 350.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.084672), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'ss': ((0.0, 0.301501, 0.00469), (180.0, 350.0, 0.0), (0.28, 0.211589, 0.28)), + 'fl': ((0.0, 0.126437, 0.137321), (180.0, 3.012788, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.129622, 0.160967), (180.0, 5.710593, 0.0), (0.42, 0.319754, 0.42))}, + 9: {'hl': ((0.0, 0.108462, 0.17683), (180.0, 338.962494, 0.0), (0.223613, 0.181357, 0.264725)), + 'hs': ((0.0, 0.145966, 0.115009), (180.0, 326.309937, 0.0), (0.238619, 0.160504, 0.245208)), + 'pl': ((0.0, 0.177217, 0.190527), (180.0, 352.874969, 0.0), (0.340186, 0.282429, 0.346387)), + 'ps': ((0.0, 0.181299, 0.089964), (180.0, 350.537689, 0.0), (0.268044, 0.28011, 0.267543)), + 'dl': ((0.0, 0.204627, 0.277646), (180.0, 350.0, 0.0), (0.261641, 0.183791, 0.261179)), + 'ds': ((0.0, 0.16996, 0.31), (180.0, 350.0, 0.0), (0.268071, 0.220048, 0.277431)), + 'cl': ((0.0, 0.129969, 0.109178), (180.0, 356.423676, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.0, 0.259957, 0.0994), (180.0, 350.0, 0.0), (0.312134, 0.195652, 0.283626)), + 'sl': ((0.0, 0.365983, 0.06233), (180.0, 350.0, 0.0), (0.266053, 0.171691, 0.266053)), + 'ss': ((0.0, 0.35414, 0.076935), (180.0, 350.0, 0.0), (0.270549, 0.204447, 0.270549)), + 'ml': ((0.0, 0.14204, 0.343069), (180.0, 350.0, 0.0), (0.406747, 0.314073, 0.44)), + 'ms': ((0.0, 0.204449, 0.338906), (180.0, 350.0, 0.0), (0.398254, 0.251272, 0.359924))}, + 10: {'pl': ((0.0, 0.101137, 0.110166), (180.0, 354.289398, 0.0), (0.357565, 0.306053, 0.375362)), + 'ps': ((0.0, 0.0829, 0.089964), (180.0, 0.0, 0.0), (0.282347, 0.295057, 0.28182)), + 'dl': ((0.0, 0.204627, 0.228147), (180.0, 350.0, 0.0), (0.280495, 0.197035, 0.28)), + 'ds': ((0.0, 0.16996, 0.204106), (180.0, 350.0, 0.0), (0.29, 0.220048, 0.277431)), + 'sl': ((0.0, 0.335741, -0.036645), (180.0, 350.0, 0.0), (0.247571, 0.206504, 0.32)), + 'ss': ((0.0, 0.301501, -0.030958), (180.0, 350.0, 0.0), (0.250248, 0.211589, 0.28)), + 'rl': ((0.0, 0.250739, -0.091041), (180.0, 338.962494, 0.0), (0.266926, 0.180318, 0.337987)), + 'rs': ((0.0, 0.27828, -0.035432), (180.0, 341.565063, 0.0), (0.260345, 0.148854, 0.322646)), + 'ml': ((0.0, 0.14204, 0.298711), (180.0, 350.0, 0.0), (0.390165, 0.314073, 0.44)), + 'ms': ((0.0, 0.170296, 0.25674), (180.0, 350.0, 0.0), (0.403087, 0.278491, 0.398912)), + 'fl': ((0.0, 0.223794, 0.067759), (180.0, 0.0, 0.0), (0.380542, 0.2973, 0.380542)), + 'fs': ((0.0, 0.129622, 0.081605), (180.0, 0.0, 0.0), (0.385318, 0.29335, 0.385318))}, + 11: {'hl': ((0.0, 0.124293, 0.064234), (180.0, 333.434937, 0.0), (0.231442, 0.207132, 0.302348)), + 'hs': ((0.0, 0.170523, 0.084807), (180.0, 333.434937, 0.0), (0.227436, 0.168825, 0.25792)), + 'pl': ((0.0, 0.10421, 0.131327), (180.0, 0.0, 0.0), (0.374201, 0.334149, 0.409821)), + 'ps': ((0.0, 0.184491, 0.179369), (180.0, 12.528808, 0.0), (0.288791, 0.295057, 0.28182)), + 'dl': ((0.0, 0.146495, 0.313994), (180.0, 4.398705, 0.0), (0.272351, 0.219304, 0.311646)), + 'ds': ((0.0, 0.160652, 0.265002), (180.0, 4.969741, 0.0), (0.275094, 0.232508, 0.32776)), + 'bl': ((0.0, 0.093978, 0.152735), (180.0, 3.17983, 0.0), (0.287872, 0.343174, 0.343174)), + 'bs': ((0.0, 0.179059, 0.134669), (180.0, 6.340192, 0.0), (0.27849, 0.284113, 0.284113)), + 'cl': ((0.0, 0.143426, 0.094572), (180.0, 8.746162, 0.0), (0.325081, 0.293013, 0.332505)), + 'cs': ((0.0, 0.263087, 0.056374), (180.0, 3.814075, 0.0), (0.290627, 0.214791, 0.311371)), + 'sl': ((0.0, 0.335741, -0.06116), (180.0, 350.0, 0.0), (0.32, 0.206504, 0.32)), + 'ss': ((0.0, 0.292303, -0.018359), (180.0, 350.0, 0.0), (0.275273, 0.233029, 0.308371)), + 'rl': ((0.0, 0.238463, -0.060425), (180.0, 345.963745, 0.0), (0.363858, 0.181402, 0.337987)), + 'rs': ((0.0, 0.052822, -0.049536), (180.0, 354.289398, 0.0), (0.306648, 0.306648, 0.306648)), + 'ml': ((0.0, 0.097596, 0.338379), (180.0, 8.746162, 0.0), (0.436884, 0.314073, 0.44)), + 'ms': ((0.0, 0.18893, 0.244251), (180.0, 0.0, 0.0), (0.429678, 0.278491, 0.398912)), + 'fl': ((0.0, 0.092028, 0.236901), (180.0, 19.440035, 0.0), (0.394799, 0.320314, 0.41)), + 'fs': ((0.0, 0.080417, 0.206739), (180.0, 15.945395, 0.0), (0.408524, 0.319754, 0.42))}, + 12: {'hl': ((0.0, 0.19161, 0.045311), (180.0, 338.962494, 0.0), (0.224522, 0.182094, 0.265801)), + 'hs': ((0.0, 0.218512, 0.062592), (180.0, 341.565063, 0.0), (0.219717, 0.14779, 0.225784)), + 'pl': ((0.0, 0.188844, 0.073246), (180.0, 2.770216, 0.0), (0.341317, 0.292146, 0.358305)), + 'ps': ((0.0, 0.232416, 0.089964), (180.0, 12.528808, 0.0), (0.25825, 0.269876, 0.257768)), + 'dl': ((0.0, 0.276064, 0.186943), (180.0, 350.0, 0.0), (0.252447, 0.181582, 0.25804)), + 'ds': ((0.0, 0.245301, 0.173859), (180.0, 350.0, 0.0), (0.273943, 0.207864, 0.26207)), + 'bl': ((0.0, 0.245554, 0.075448), (180.0, 0.0, 0.0), (0.247436, 0.252864, 0.27713)), + 'bs': ((0.0, 0.284205, 0.064097), (180.0, 0.0, 0.0), (0.249024, 0.235859, 0.235859)), + 'cl': ((0.0, 0.189496, 0.017453), (180.0, 3.012788, 0.0), (0.344809, 0.293013, 0.332505)), + 'cs': ((0.0, 0.328401, -0.031304), (180.0, 350.0, 0.0), (0.316518, 0.214791, 0.311371)), + 'sl': ((0.0, 0.378279, -0.117174), (180.0, 350.0, 0.0), (0.283015, 0.198119, 0.307007)), + 'ss': ((0.0, 0.377926, -0.099811), (180.0, 344.744873, 0.0), (0.269911, 0.211589, 0.28)), + 'rl': ((0.0, 0.300494, -0.145078), (180.0, 348.690063, 0.0), (0.329358, 0.164568, 0.306623)), + 'rs': ((0.0, 0.328352, -0.10298), (180.0, 354.289398, 0.0), (0.268046, 0.124345, 0.269521)), + 'ml': ((0.0, 0.14204, 0.171892), (180.0, 7.125016, 0.0), (0.420106, 0.314073, 0.44)), + 'ms': ((0.0, 0.196812, 0.106915), (180.0, 0.0, 0.0), (0.409559, 0.278491, 0.398912)), + 'fl': ((0.0, 0.189265, 0.033205), (180.0, 6.340192, 0.0), (0.379707, 0.296647, 0.379707)), + 'fs': ((0.0, 0.122984, 0.070585), (180.0, 12.528808, 0.0), (0.40212, 0.307063, 0.40333))}, + 13: {'hl': ((0.0, 0.044781, 0.053253), (180.0, 354.289398, 0.0), (0.243308, 0.19733, 0.288041)), + 'hs': ((0.0, 0.165599, 0.06227), (180.0, 349.380341, 0.0), (0.231915, 0.155994, 0.238318)), + 'pl': ((0.0, 0.022, 0.105292), (180.0, 8.130102, 0.0), (0.335671, 0.323128, 0.396304)), + 'ps': ((0.0, 0.189743, 0.077616), (180.0, 13.24052, 0.0), (0.256183, 0.244263, 0.311845)), + 'dl': ((0.0, 0.141211, 0.26153), (180.0, 9.462322, 0.0), (0.255671, 0.197035, 0.28)), + 'ds': ((0.0, 0.128204, 0.215993), (180.0, 4.763642, 0.0), (0.255741, 0.220048, 0.277431)), + 'bl': ((0.0, 0.175024, 0.118577), (180.0, 3.366461, 0.0), (0.249215, 0.262137, 0.287293)), + 'bs': ((0.0, 0.161287, 0.059038), (180.0, 4.085617, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.0, 0.129041, -0.024628), (180.0, 0.0, 0.0), (0.341982, 0.293013, 0.332505)), + 'cs': ((0.0, 0.224526, -0.008677), (180.0, 3.012788, 0.0), (0.303967, 0.214791, 0.311371)), + 'sl': ((0.0, 0.330448, -0.150585), (180.0, 350.0, 0.0), (0.317897, 0.206504, 0.32)), + 'ss': ((0.0, 0.318292, -0.136152), (180.0, 350.0, 0.0), (0.282145, 0.211589, 0.310307)), + 'rl': ((0.0, 0.225347, -0.127599), (180.0, 0.0, 0.0), (0.322214, 0.181402, 0.337987)), + 'rs': ((0.0, 0.168132, -0.175565), (180.0, 345.068573, 0.0), (0.299433, 0.243571, 0.308336)), + 'ml': ((0.0, 0.058056, 0.183242), (180.0, 5.194429, 0.0), (0.42027, 0.314073, 0.44)), + 'ms': ((0.0, 0.133166, 0.153596), (180.0, 0.0, 0.0), (0.393897, 0.278491, 0.398912)), + 'fl': ((0.0, 0.066667, -0.02432), (180.0, 5.710593, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.06194, 0.016133), (180.0, 8.746162, 0.0), (0.392879, 0.319754, 0.42))}, + 14: {'hl': ((0.0, 0.159438, 0.022837), (180.0, 338.962494, 0.0), (0.208469, 0.19733, 0.288041)), + 'hs': ((0.0, 0.205684, 0.053855), (180.0, 337.380127, 0.0), (0.204941, 0.160504, 0.245208)), + 'pl': ((0.0, 0.131875, 0.09965), (180.0, 11.309933, 0.0), (0.3288, 0.306053, 0.427301)), + 'ps': ((0.0, 0.183146, 0.12941), (180.0, 18.434948, 0.0), (0.246167, 0.295057, 0.28182)), + 'dl': ((0.0, 0.199277, 0.214196), (180.0, 0.0, 0.0), (0.246498, 0.195386, 0.301291)), + 'ds': ((0.0, 0.187358, 0.193808), (180.0, 0.0, 0.0), (0.233216, 0.231488, 0.31248)), + 'bl': ((0.0, 0.242963, 0.039121), (180.0, 351.869904, 0.0), (0.237675, 0.286305, 0.316986)), + 'bs': ((0.0, 0.261231, 0.079908), (180.0, 0.0, 0.0), (0.242969, 0.251667, 0.251667)), + 'cl': ((0.0, 0.177716, -0.027931), (180.0, 0.0, 0.0), (0.301445, 0.301725, 0.342391)), + 'cs': ((0.0, 0.300251, 0.008259), (180.0, 0.0, 0.0), (0.264824, 0.198711, 0.288061)), + 'sl': ((0.0, 0.386537, -0.106746), (180.0, 350.0, 0.0), (0.261013, 0.206504, 0.32)), + 'ss': ((0.0, 0.35882, -0.078951), (180.0, 350.0, 0.0), (0.231939, 0.22731, 0.316365)), + 'rl': ((0.0, 0.275572, -0.141484), (180.0, 355.236359, 0.0), (0.290351, 0.178071, 0.337987)), + 'rs': ((0.0, 0.224285, -0.129626), (180.0, 354.289398, 0.0), (0.24115, 0.218006, 0.28326)), + 'ml': ((0.0, 0.163282, 0.185676), (180.0, 0.0, 0.0), (0.408571, 0.314073, 0.44)), + 'ms': ((0.0, 0.191787, 0.171119), (180.0, 0.0, 0.0), (0.383077, 0.278491, 0.398912)), + 'fl': ((0.0, 0.105486, 0.070603), (180.0, 12.994617, 0.0), (0.346458, 0.320314, 0.421144)), + 'fs': ((0.0, 0.128433, -0.001473), (180.0, 5.194429, 0.0), (0.360925, 0.319754, 0.428685))}, + 15: {'hl': ((0.0, 0.301712, 0.016432), (180.0, 326.309937, 0.0), (0.173932, 0.388926, 0.210167)), + 'hs': ((0.0, 0.344876, 0.012905), (180.0, 329.036255, 0.0), (0.169557, 0.306392, 0.210683)), + 'pl': ((0.0, 0.484294, -0.125862), (180.0, 347.471191, 0.0), (0.272837, 0.367441, 0.375362)), + 'ps': ((0.0, 0.471856, -0.033389), (180.0, 350.537689, 0.0), (0.206302, 0.328445, 0.24029)), + 'dl': ((0.0, 0.3818, 0.027969), (180.0, 350.0, 0.0), (0.210956, 0.331618, 0.354747)), + 'ds': ((0.0, 0.411029, 0.020927), (180.0, 350.0, 0.0), (0.175509, 0.304495, 0.305118)), + 'bl': ((0.0, 0.473563, -0.105577), (180.0, 348.690063, 0.0), (0.202038, 0.390395, 0.324432)), + 'bs': ((0.0, 0.577318, -0.020901), (180.0, 343.300751, 0.0), (0.163677, 0.207711, 0.202636)), + 'cl': ((0.0, 0.391839, -0.113814), (180.0, 350.0, 0.0), (0.243017, 0.520533, 0.265248)), + 'cs': ((0.0, 0.440759, -0.093247), (180.0, 349.69516, 0.0), (0.222206, 0.3746, 0.241183)), + 'sl': ((0.0, 0.59427, -0.255686), (180.0, 335.55603, 0.0), (0.199497, 0.407114, 0.32)), + 'ss': ((0.0, 0.604184, -0.237158), (180.0, 335.559998, 0.0), (0.201407, 0.399207, 0.319597)), + 'rl': ((0.0, 0.420781, -0.212944), (180.0, 337.380127, 0.0), (0.219401, 0.366796, 0.244425)), + 'rs': ((0.0, 0.358988, -0.175504), (180.0, 345.963745, 0.0), (0.20978, 0.385531, 0.197511)), + 'ml': ((0.0, 0.455571, 0.108166), (180.0, 350.0, 0.0), (0.290113, 0.415556, 0.327887)), + 'ms': ((0.0, 0.409596, 0.131089), (180.0, 350.0, 0.0), (0.278673, 0.482069, 0.305658)), + 'fl': ((0.0, 0.32819, -0.099642), (180.0, 5.0, 0.0), (0.305757, 0.555172, 0.359937)), + 'fs': ((0.0, 0.315781, -0.091302), (180.0, 0.0, 0.0), (0.304348, 0.64574, 0.323712))}, + 16: {'hl': ((0.0, 0.182116, -0.026324), (180.0, 329.036255, 0.0), (0.183549, 0.30147, 0.271493)), + 'hs': ((0.0, 0.162093, -0.030989), (180.0, 324.462311, 0.0), (0.178275, 0.320864, 0.245208)), + 'pl': ((0.0, 0.246463, 0.012467), (180.0, 0.0, 0.0), (0.272063, 0.319204, 0.406015)), + 'ps': ((0.0, 0.224921, -0.049279), (180.0, 8.130102, 0.0), (0.230198, 0.332714, 0.415864)), + 'dl': ((0.0, 0.112679, 0.214552), (180.0, 5.0, 0.0), (0.219236, 0.343335, 0.303383)), + 'ds': ((0.0, 0.15343, 0.184704), (180.0, 3.576334, 0.0), (0.213594, 0.29264, 0.277431)), + 'bl': ((0.0, 0.227667, 0.028155), (180.0, 356.308624, 0.0), (0.216215, 0.365241, 0.287293)), + 'bs': ((0.0, 0.271482, 0.081997), (180.0, 6.340192, 0.0), (0.204564, 0.290903, 0.20646)), + 'cl': ((0.0, 0.108276, -0.046338), (180.0, 0.0, 0.0), (0.259248, 0.454764, 0.347817)), + 'cs': ((0.0, 0.135967, -0.067787), (180.0, 352.874969, 0.0), (0.231762, 0.453885, 0.311371)), + 'sl': ((0.0, 0.355049, -0.190943), (180.0, 345.963745, 0.0), (0.215919, 0.317866, 0.32)), + 'ss': ((0.0, 0.440798, -0.15735), (180.0, 345.068573, 0.0), (0.182208, 0.230814, 0.28)), + 'rl': ((0.0, 0.220375, -0.226519), (180.0, 344.054596, 0.0), (0.247484, 0.323244, 0.302106)), + 'rs': ((0.0, 0.201528, -0.241307), (180.0, 343.300751, 0.0), (0.211784, 0.344268, 0.308336)), + 'ml': ((0.0, 0.042295, 0.149711), (180.0, 5.0, 0.0), (0.355454, 0.48344, 0.44)), + 'ms': ((0.0, 0.09302, 0.152271), (180.0, 5.0, 0.0), (0.328114, 0.419293, 0.398912)), + 'fl': ((0.0, 0.086183, 0.092251), (180.0, 15.255118, 0.0), (0.303423, 0.450213, 0.41)), + 'fs': ((0.0, 0.075361, 0.026021), (180.0, 9.462322, 0.0), (0.302482, 0.476086, 0.42))}, + 17: {'hl': ((0.200149, 0.456515, -0.044682), (146.309937, 331.389526, 351.869904), (0.286858, 0.228466, 0.30339)), + 'hs': ((0.263342, 0.498851, 0.009944), (160.346176, 320.0, 0.0), (0.270183, 0.181735, 0.277643)), + 'pl': ((0.296252, 0.558951, -0.20513), (153.434952, 351.869904, 0.0), (0.423468, 0.362461, 0.435161)), + 'ps': ((0.222674, 0.669512, -0.157144), (172.874985, 347.005371, 0.0), (0.337275, 0.352457, 0.336645)), + 'dl': ((0.231222, 0.491262, 0.038021), (161.565048, 352.874969, 0.0), (0.322775, 0.226735, 0.322205)), + 'ds': ((0.195962, 0.478829, -0.019759), (164.744888, 355.601288, 0.0), (0.31404, 0.269666, 0.339989)), + 'bl': ((0.219856, 0.587778, -0.014678), (149.740005, 350.0, 0.0), (0.27447, 0.262137, 0.287293)), + 'bs': ((0.209768, 0.607517, -0.092937), (175.914383, 0.0, 0.0), (0.274179, 0.274179, 0.274179)), + 'cl': ((0.25794, 0.652492, -0.167668), (180.0, 350.0, 0.0), (0.323214, 0.298842, 0.324137)), + 'cs': ((0.203543, 0.599911, -0.166034), (163.610458, 350.0, 0.0), (0.32206, 0.32206, 0.32206)), + 'sl': ((0.197982, 0.725899, -0.237164), (164.054611, 339.44397, 0.0), (0.32, 0.206504, 0.310708)), + 'ss': ((0.228123, 0.721239, -0.246728), (153.434952, 343.300751, 0.0), (0.333197, 0.251789, 0.333197)), + 'rl': ((0.327262, 0.488187, -0.308543), (149.03624, 343.300751, 9.462322), (0.349929, 0.181402, 0.337987)), + 'rs': ((0.309609, 0.508521, -0.283489), (146.309937, 344.054596, 0.0), (0.317594, 0.14733, 0.308496)), + 'ml': ((0.334214, 0.53388, -0.105688), (165.96376, 0.0, 0.0), (0.507756, 0.362438, 0.507756)), + 'ms': ((0.281916, 0.526409, -0.050112), (164.054611, 0.0, 0.0), (0.434891, 0.300639, 0.430637)), + 'fl': ((0.26164, 0.494054, -0.239046), (159.443954, 7.125016, 0.0), (0.435889, 0.34054, 0.435889)), + 'fs': ((0.233777, 0.45318, -0.218664), (160.016891, 11.309933, 0.0), (0.442738, 0.324942, 0.426815))}, + 18: {'hl': ((0.0, 0.267218, 0.01861), (180.0, 321.340179, 0.0), (0.212691, 0.172499, 0.251795)), + 'hs': ((0.0, 0.323439, 0.027186), (180.0, 320.0, 0.0), (0.221186, 0.135888, 0.248775)), + 'pl': ((0.0, 0.223562, 0.028227), (180.0, 0.0, 0.0), (0.318784, 0.272859, 0.366049)), + 'ps': ((0.0, 0.247291, 0.005782), (180.0, 0.0, 0.0), (0.264524, 0.276432, 0.26403)), + 'dl': ((0.0, 0.230289, 0.199214), (180.0, 0.0, 0.0), (0.244199, 0.182002, 0.302211)), + 'ds': ((0.0, 0.284963, 0.218269), (180.0, 0.0, 0.0), (0.240139, 0.172094, 0.26219)), + 'bl': ((0.0, 0.361287, 0.04086), (180.0, 350.0, 0.0), (0.247754, 0.204835, 0.298714)), + 'bs': ((0.0, 0.338075, 0.03841), (180.0, 350.0, 0.0), (0.232495, 0.233087, 0.233087)), + 'cl': ((0.0, 0.263456, -0.006592), (180.0, 350.0, 0.0), (0.300945, 0.266277, 0.285502)), + 'cs': ((0.0, 0.326731, 0.001341), (180.0, 350.0, 0.0), (0.261293, 0.214791, 0.264974)), + 'sl': ((0.0, 0.42476, -0.087801), (180.0, 350.0, 0.0), (0.260745, 0.168266, 0.284507)), + 'ss': ((0.0, 0.518544, -0.030958), (180.0, 350.0, 0.0), (0.236973, 0.190286, 0.25181)), + 'rl': ((0.0, 0.3468, -0.124107), (180.0, 350.537689, 0.0), (0.300296, 0.149713, 0.278944)), + 'rs': ((0.0, 0.369128, -0.121969), (180.0, 347.471191, 0.0), (0.246406, 0.12082, 0.284085)), + 'ml': ((0.0, 0.204131, 0.151426), (180.0, 0.0, 0.0), (0.396343, 0.282911, 0.396343)), + 'ms': ((0.0, 0.222319, 0.183222), (180.0, 6.340192, 0.0), (0.379141, 0.239213, 0.378554)), + 'fl': ((0.0, 0.228013, 0.068334), (180.0, 3.814075, 0.0), (0.355548, 0.277773, 0.373469)), + 'fs': ((0.0, 0.243492, 0.010407), (180.0, 0.0, 0.0), (0.351609, 0.273235, 0.381459))}, + 19: {'ss': ((0.0, 0.334479, -0.006105), (180.0, 350.0, 0.0), (0.304684, 0.191148, 0.297905)), + 'rs': ((0.0, 0.251783, -0.031632), (180.0, 342.645966, 0.0), (0.306648, 0.164736, 0.308336)), + 'fl': ((0.0, 0.141625, 0.12), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.176809, 0.13), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))}, + 20: {'hl': ((0.034523, 0.096095, 0.087941), (180.0, 333.434937, 0.0), (0.243308, 0.19733, 0.333919)), + 'hs': ((0.041357, 0.042114, 0.009901), (180.0, 326.309937, 0.0), (0.270682, 0.270682, 0.270682)), + 'pl': ((0.048776, 0.198799, 0.08818), (179.0, 345.0, 0.0), (0.410697, 0.292758, 0.4919)), + 'ps': ((0.0, 0.250319, 0.114899), (180.0, 0.0, 0.0), (0.311948, 0.215569, 0.347937)), + 'dl': ((0.020275, 0.149782, 0.307257), (180.0, 0.0, 0.0), (0.285743, 0.197035, 0.353524)), + 'ds': ((0.0, 0.051992, 0.321583), (180.0, 0.0, 0.0), (0.273281, 0.281429, 0.300349)), + 'bl': ((0.030846, 0.301048, 0.142036), (180.0, 347.471191, 0.0), (0.285646, 0.198551, 0.365914)), + 'bs': ((0.002871, 0.237243, 0.162152), (180.0, 0.0, 0.0), (0.281518, 0.222397, 0.291614)), + 'cl': ((0.047769, 0.148353, 0.092036), (180.0, 0.0, 0.0), (0.35351, 0.293013, 0.332505)), + 'cs': ((0.030424, 0.248212, 0.056374), (180.0, 350.0, 0.0), (0.342668, 0.214791, 0.311371)), + 'sl': ((0.0, 0.109274, -0.061695), (180.0, 350.0, 0.0), (0.328929, 0.332035, 0.33273)), + 'ss': ((0.007205, 0.175002, -0.011751), (180.0, 350.0, 0.0), (0.287876, 0.283501, 0.357428)), + 'rl': ((0.0, 0.06814, -0.094117), (180.0, 343.300751, 0.0), (0.363858, 0.285186, 0.337987)), + 'rs': ((0.0, 0.099706, -0.070101), (180.0, 345.0, 0.0), (0.316131, 0.26144, 0.308336)), + 'ml': ((0.04816, 0.097082, 0.349655), (180.0, 3.0, 0.0), (0.456455, 0.297276, 0.569451)), + 'ms': ((0.034563, 0.17555, 0.251134), (180.0, 355.601288, 0.0), (0.443263, 0.258657, 0.472933)), + 'fl': ((0.050144, 0.001648, 0.245803), (180.0, 17.0, 0.0), (0.467929, 0.382283, 0.479589)), + 'fs': ((0.047045, 0.089182, 0.226824), (180.0, 12.0, 0.0), (0.433673, 0.319754, 0.42))}, + 21: {'fl': ((0.0, 0.126437, 0.063992), (180.0, 350.0, 0.0), (0.41, 0.320314, 0.41)), + 'fs': ((0.0, 0.129622, 0.056358), (180.0, 350.0, 0.0), (0.42, 0.319754, 0.42))}} +ExtendedBackpackTransTable = {1: {'m': ((0.227545, -1.05, 0.053624), (180.0, 6.0, 0.0), (0.319532, 0.319532, 0.319532)), + 'l': ((0.26576, -1.28023, 0.0), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32))}, + 2: {'m': ((0.265322, -1.05, 0.101718), (180.0, 6.009006, 0.0), (0.246001, 0.319532, 0.319532)), + 'l': ((0.21263, -1.28023, 0.27888), (180.0, 0.0, 0.0), (0.209654, 0.32, 0.32))}, + 4: {'m': ((0.262368, -1.05, 0.195095), (180.0, 20.0, 0.0), (0.147268, 0.319532, 0.319532)), + 'l': ((0.23955, -1.241722, 0.347059), (180.0, 20.0, 0.0), (0.168072, 0.319779, 0.319779))}, + 7: {'s': ((0.290997, -1.625686, 0.134797), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.273327, -1.170334, 0.137522), (180.0, 14.036243, 0.0), (0.14773, 0.290714, 0.297074)), + 'l': ((0.217695, -1.286976, 0.455049), (180.0, 0.0, 0.0), (0.176572, 0.32, 0.32))}, + 8: {'s': ((0.41387, -1.464616, 0.394859), (180.0, 16.38954, 0.0), (0.253971, 0.253971, 0.253971)), + 'm': ((0.290309, -0.969267, 0.434042), (180.0, 6.0, 0.0), (0.148556, 0.184951, 0.184951)), + 'l': ((0.251222, -1.10169, 0.597807), (180.0, 0.0, 0.0), (0.127983, 0.178798, 0.193945))}, + 9: {'s': ((0.303128, -1.50918, 0.401274), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.295221, -1.172374, 0.307521), (180.0, 6.0, 0.0), (0.220651, 0.319532, 0.319532)), + 'l': ((0.21774, -1.183531, 0.606077), (180.0, 0.0, 0.0), (0.181037, 0.32, 0.32))}, + 10: {'s': ((0.31255, -1.437493, 0.578569), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.26387, -1.099816, 0.584935), (180.0, 6.0, 0.0), (0.248266, 0.319532, 0.319532)), + 'l': ((0.223449, -1.200059, 0.775343), (180.0, 0.0, 0.0), (0.261044, 0.32, 0.32))}, + 11: {'s': ((0.28701, -1.299067, -0.136342), (180.0, 12.994617, 0.0), (0.389964, 0.353497, 0.409548)), + 'm': ((0.264689, -0.995974, -0.067336), (180.0, 12.994617, 358.0), (0.299865, 0.256064, 0.416953)), + 'l': ((0.232795, -1.043043, 0.025233), (180.0, 7.594644, 0.0), (0.363232, 0.439772, 0.4632))}, + 12: {'s': ((0.29374, -1.585814, 0.243433), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.280119, -1.05, 0.236418), (180.0, 6.0, 0.0), (0.170751, 0.319532, 0.319532)), + 'l': ((0.210635, -1.253645, 0.469426), (180.0, 11.309933, 0.0), (0.187917, 0.32, 0.32))}, + 13: {'s': ((0.317368, -1.892817, -0.509906), (180.0, 10.304847, 0.0), (0.403833, 0.305326, 0.307871)), + 'm': ((0.289202, -1.371564, -0.423113), (180.0, 6.0, 0.0), (0.323075, 0.269037, 0.273195)), + 'l': ((0.242066, -1.380625, -0.422389), (180.0, 0.0, 0.0), (0.2944, 0.278142, 0.278142))}, + 14: {'m': ((0.274095, -1.05, 0.19327), (180.0, 6.0, 0.0), (0.177726, 0.32, 0.32)), + 'l': ((0.2291, -1.202605, 0.333037), (180.0, 0.0, 0.0), (0.20932, 0.32, 0.32))}, + 15: {'s': ((0.3496, -1.566231, 0.297443), (180.0, 11.309933, 0.0), (0.434117, 0.305907, 0.323214)), + 'm': ((0.281317, -1.154318, 0.273442), (180.0, 6.0, 0.0), (0.361197, 0.279386, 0.279386)), + 'l': ((0.216334, -1.245587, 0.280549), (180.0, 0.0, 0.0), (0.326107, 0.297644, 0.297644))}, + 16: {'s': ((0.291561, -1.583429, 0.015556), (180.0, 8.746162, 0.0), (0.447062, 0.32, 0.32)), + 'm': ((0.287122, -1.199939, 0.158015), (180.0, 6.0, 0.0), (0.357934, 0.286914, 0.303731)), + 'l': ((0.216544, -1.200739, 0.361558), (180.0, 0.0, 0.0), (0.353442, 0.271948, 0.31356))}, + 17: {'s': ((0.30377, -1.180818, 0.019505), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.241677, -0.768537, 0.123365), (180.0, 0.0, 0.0), (0.304765, 0.304765, 0.304765)), + 'l': ((0.224225, -0.769975, 0.238668), (180.0, 0.0, 0.0), (0.299818, 0.299818, 0.299818))}, + 18: {'s': ((0.000383, -1.563254, -0.217828), (180.0, 17.0, 325.0), (0.289457, 0.289457, 0.289457)), + 'm': ((0.01529, -1.153655, -0.253422), (180.0, 12.09, 337.0), (0.28419, 0.28419, 0.28419)), + 'l': ((0.128042, -1.227634, -0.296953), (180.0, 8.0, 350.0), (0.245614, 0.32, 0.305041))}, + 19: {'s': ((0.299164, -1.040617, 0.541799), (180.0, 0.0, 0.0), (0.33993, 0.367266, 0.321871)), + 'm': ((0.235848, -0.671119, 0.582503), (180.0, 0.0, 0.0), (0.285994, 0.305137, 0.322617)), + 'l': ((0.215214, -0.788198, 0.798792), (180.0, 0.0, 0.0), (0.284314, 0.27715, 0.438783))}, + 20: {'s': ((0.3, -1.04, 0.54), (180.0, 0.0, 0.0), (0.34, 0.32, 0.32)), + 'm': ((0.227545, -0.67, 0.58), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32)), + 'l': ((0.22, -0.79, 0.8), (180.0, 0.0, 0.0), (0.28, 0.28, 0.44))}, + 21: {'s': ((0.314386, -1.166389, -0.053999), (180.0, 5.194429, 0.0), (0.274937, 0.274937, 0.274937)), + 'm': ((0.253176, -0.731249, 0.026498), (180.0, 5.0, 0.0), (0.274722, 0.274722, 0.274722)), + 'l': ((0.213058, -0.781995, 0.095885), (180.0, 3.0, 0.0), (0.246885, 0.298552, 0.363845))}, + 22: {'s': ((0.272049, -1.545308, -0.077449), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.227545, -1.186519, -0.22177), (180.0, 0.0, 0.0), (0.32, 0.319532, 0.319532)), + 'l': ((0.239467, -1.234463, 0.033669), (180.0, 0.0, 0.0), (0.32, 0.32, 0.32))}, + 24: {'s': ((0.294021, -1.620192, -0.216908), (180.0, 6.340192, 0.0), (0.32, 0.32, 0.32)), + 'm': ((0.227545, -1.164832, -0.106984), (180.0, 6.0, 0.0), (0.290735, 0.290735, 0.290735)), + 'l': ((0.208737, -1.215698, 0.012413), (180.0, 0.0, 0.0), (0.295111, 0.295111, 0.295111))}} diff --git a/toontown/toon/AvatarPanelBase.py b/toontown/toon/AvatarPanelBase.py new file mode 100644 index 0000000..abfa145 --- /dev/null +++ b/toontown/toon/AvatarPanelBase.py @@ -0,0 +1,240 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from direct.showbase import DirectObject +from otp.avatar import AvatarPanel +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from otp.distributed import CentralLogger +IGNORE_SCALE = 0.06 +STOP_IGNORE_SCALE = 0.04 + +class AvatarPanelBase(AvatarPanel.AvatarPanel): + + def __init__(self, avatar, FriendsListPanel = None): + self.dialog = None + self.category = None + AvatarPanel.AvatarPanel.__init__(self, avatar, FriendsListPanel) + return + + def getIgnoreButtonInfo(self): + if base.cr.avatarFriendsManager.checkIgnored(self.avId): + return (TTLocalizer.AvatarPanelStopIgnoring, self.handleStopIgnoring, STOP_IGNORE_SCALE) + else: + return (TTLocalizer.AvatarPanelIgnore, self.handleIgnore, IGNORE_SCALE) + + def handleIgnore(self): + isAvatarFriend = base.cr.isFriend(self.avatar.doId) + isPlayerFriend = base.cr.playerFriendsManager.isAvatarOwnerPlayerFriend(self.avatar.doId) + isFriend = isAvatarFriend or isPlayerFriend + if isFriend: + self.dialog = TTDialog.TTGlobalDialog( + style=TTDialog.CancelOnly, + text=TTLocalizer.IgnorePanelAddFriendAvatar % self.avName, + text_wordwrap=18.5, + text_scale=0.06, + cancelButtonText=TTLocalizer.lCancel, + doneEvent='IgnoreBlocked', + command=self.freeLocalAvatar) + else: + self.dialog = TTDialog.TTGlobalDialog( + style=TTDialog.TwoChoice, + text=TTLocalizer.IgnorePanelAddIgnore % self.avName, + text_wordwrap=18.5, + text_scale=TTLocalizer.APBdialog, + okButtonText=TTLocalizer.AvatarPanelIgnore, + cancelButtonText=TTLocalizer.lCancel, + doneEvent='IgnoreConfirm', + command=self.handleIgnoreConfirm) + DirectLabel( + parent=self.dialog, + relief=None, + pos=(0, TTLocalizer.APBdirectLabelPosY, 0.125), + text=TTLocalizer.IgnorePanelTitle, + textMayChange=0, + text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleStopIgnoring(self): + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.TwoChoice, text=TTLocalizer.IgnorePanelRemoveIgnore % self.avName, text_wordwrap=18.5, text_scale=0.06, okButtonText=TTLocalizer.AvatarPanelStopIgnoring, cancelButtonText=TTLocalizer.lCancel, buttonPadSF=4.0, doneEvent='StopIgnoringConfirm', command=self.handleStopIgnoringConfirm) + DirectLabel(parent=self.dialog, relief=None, pos=(0, TTLocalizer.APBdirectLabelPosY, 0.15), text=TTLocalizer.IgnorePanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleIgnoreConfirm(self, value): + if value == -1: + self.freeLocalAvatar() + return + base.cr.avatarFriendsManager.addIgnore(self.avId) + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=TTLocalizer.IgnorePanelIgnore % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='IgnoreComplete', command=self.handleDoneIgnoring) + DirectLabel(parent=self.dialog, relief=None, pos=(0, TTLocalizer.APBdirectLabelPosY, 0.15), text=TTLocalizer.IgnorePanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleStopIgnoringConfirm(self, value): + if value == -1: + self.freeLocalAvatar() + return + base.cr.avatarFriendsManager.removeIgnore(self.avId) + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=TTLocalizer.IgnorePanelEndIgnore % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='StopIgnoringComplete', command=self.handleDoneIgnoring) + DirectLabel(parent=self.dialog, relief=None, pos=(0, TTLocalizer.APBdirectLabelPosY, 0.15), text=TTLocalizer.IgnorePanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleDoneIgnoring(self, value): + self.freeLocalAvatar() + + def handleReport(self): + if base.cr.centralLogger.hasReportedPlayer(self.playerId, self.avId): + self.alreadyReported() + else: + self.confirmReport() + + def confirmReport(self): + if base.cr.isFriend(self.avId) or base.cr.playerFriendsManager.isPlayerFriend(self.avId): + string = TTLocalizer.ReportPanelBodyFriends + titlePos = 0.41 + else: + string = TTLocalizer.ReportPanelBody + titlePos = 0.35 + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.TwoChoice, text=string % self.avName, text_wordwrap=18.5, text_scale=0.06, okButtonText=TTLocalizer.AvatarPanelReport, cancelButtonText=TTLocalizer.lCancel, doneEvent='ReportConfirm', command=self.handleReportConfirm) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, titlePos), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleReportConfirm(self, value): + self.cleanupDialog() + if value == 1: + self.chooseReportCategory() + else: + self.requestWalk() + + def alreadyReported(self): + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=TTLocalizer.ReportPanelAlreadyReported % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='AlreadyReported', command=self.handleAlreadyReported) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, 0.2), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleAlreadyReported(self, value): + self.freeLocalAvatar() + + def chooseReportCategory(self): + self.dialog = TTDialog.TTGlobalDialog(pos=(0, 0, 0.4), style=TTDialog.CancelOnly, text=TTLocalizer.ReportPanelCategoryBody % (self.avName, self.avName), text_wordwrap=18.5, text_scale=0.06, topPad=0.05, midPad=0.75, cancelButtonText=TTLocalizer.lCancel, doneEvent='ReportCategory', command=self.handleReportCategory) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, 0.225), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryLanguage, text_scale=0.06, text_pos=(0, -0.0124), pos=(0, 0, -0.3), command=self.handleReportCategory, extraArgs=[0]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.15, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryPii, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.425), command=self.handleReportCategory, extraArgs=[1]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryRude, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.55), command=self.handleReportCategory, extraArgs=[2]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryName, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.675), command=self.handleReportCategory, extraArgs=[3]) + DirectButton(parent=self.dialog, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=(2.125, 1.0, 1.0), text=TTLocalizer.ReportPanelCategoryHacking, text_scale=0.06, text_pos=(0, -0.0125), pos=(0, 0, -0.8), command=self.handleReportCategory, extraArgs=[4]) + guiButton.removeNode() + self.dialog.show() + self.__acceptStoppedStateMsg() + self.requestStopped() + return + + def handleReportCategory(self, value): + self.cleanupDialog() + if value >= 0: + cat = [CentralLogger.ReportFoulLanguage, + CentralLogger.ReportPersonalInfo, + CentralLogger.ReportRudeBehavior, + CentralLogger.ReportBadName, + CentralLogger.ReportHacking] + self.category = cat[value] + self.confirmReportCategory(value) + else: + self.requestWalk() + + def confirmReportCategory(self, category): + string = TTLocalizer.ReportPanelConfirmations[category] + string += '\n\n' + TTLocalizer.ReportPanelWarning + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.TwoChoice, text=string % self.avName, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, okButtonText=TTLocalizer.AvatarPanelReport, cancelButtonText=TTLocalizer.lCancel, doneEvent='ReportConfirmCategory', command=self.handleReportCategoryConfirm) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, 0.5), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + return + + def handleReportCategoryConfirm(self, value): + self.cleanupDialog() + removed = 0 + isPlayer = 0 + if value > 0: + if self.category == CentralLogger.ReportHacking: + base.cr.centralLogger.reportPlayer(self.category, self.playerId, self.avId) + self.category = CentralLogger.ReportRudeBehavior + base.cr.centralLogger.reportPlayer(self.category, self.playerId, self.avId) + if base.cr.isFriend(self.avId): + base.cr.removeFriend(self.avId) + removed = 1 + if base.cr.playerFriendsManager.isPlayerFriend(self.playerId): + if self.playerId: + base.cr.playerFriendsManager.sendRequestRemove(self.playerId) + removed = 1 + isPlayer = 1 + self.reportComplete(removed, isPlayer) + else: + self.requestWalk() + + def reportComplete(self, removed, isPlayer): + string = TTLocalizer.ReportPanelThanks + titlePos = 0.25 + if removed: + if isPlayer: + string += ' ' + TTLocalizer.ReportPanelRemovedPlayerFriend % self.playerId + else: + string += ' ' + TTLocalizer.ReportPanelRemovedFriend % self.avName + titlePos = 0.3 + self.dialog = TTDialog.TTGlobalDialog(style=TTDialog.Acknowledge, text=string, text_wordwrap=18.5, text_scale=0.06, topPad=0.1, doneEvent='ReportComplete', command=self.handleReportComplete) + DirectLabel(parent=self.dialog, relief=None, pos=(0, 0, titlePos), text=TTLocalizer.ReportPanelTitle, textMayChange=0, text_scale=0.08) + self.dialog.show() + self.__acceptStoppedStateMsg() + return + + def handleReportComplete(self, value): + self.freeLocalAvatar() + + def freeLocalAvatar(self, value = None): + self.cleanupDialog() + self.requestWalk() + + def cleanupDialog(self): + if self.dialog: + self.dialog.ignore('exitingStoppedState') + self.dialog.cleanup() + self.dialog = None + return + + def requestStopped(self): + if not base.cr.playGame.getPlace().fsm.getCurrentState().getName() == 'stickerBook': + if base.cr.playGame.getPlace().fsm.hasStateNamed('stopped'): + base.cr.playGame.getPlace().fsm.request('stopped') + else: + self.notify.warning('skipping request to stopped in %s' % base.cr.playGame.getPlace()) + else: + self.cleanup() + + def requestWalk(self): + if base.cr.playGame.getPlace().fsm.hasStateNamed('finalBattle'): + base.cr.playGame.getPlace().fsm.request('finalBattle') + elif base.cr.playGame.getPlace().fsm.hasStateNamed('walk'): + if base.cr.playGame.getPlace().getState() == 'stopped': + base.cr.playGame.getPlace().fsm.request('walk') + else: + self.notify.warning('skipping request to walk in %s' % base.cr.playGame.getPlace()) + + def __acceptStoppedStateMsg(self): + self.dialog.ignore('exitingStoppedState') + self.dialog.accept('exitingStoppedState', self.cleanupDialog) diff --git a/toontown/toon/BoardingGroupInviterPanels.py b/toontown/toon/BoardingGroupInviterPanels.py new file mode 100644 index 0000000..aeedf24 --- /dev/null +++ b/toontown/toon/BoardingGroupInviterPanels.py @@ -0,0 +1,166 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import ToonHeadDialog +from direct.gui.DirectGui import DGG +from otp.otpbase import OTPGlobals +from toontown.toonbase import TTLocalizer + +class BoardingGroupInviterPanels: + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInviterPanels') + + def __init__(self): + self.__invitingPanel = None + self.__invitationRejectedPanel = None + return + + def cleanup(self): + self.destroyInvitingPanel() + self.destroyInvitationRejectedPanel() + + def createInvitingPanel(self, boardingParty, inviteeId, **kw): + self.destroyInvitingPanel() + self.destroyInvitationRejectedPanel() + self.notify.debug('Creating Inviting Panel.') + self.__invitingPanel = BoardingGroupInvitingPanel(boardingParty, inviteeId, **kw) + + def createInvitationRejectedPanel(self, boardingParty, inviteeId, **kw): + self.destroyInvitingPanel() + self.destroyInvitationRejectedPanel() + self.notify.debug('Creating Invititation Rejected Panel.') + self.__invitationRejectedPanel = BoardingGroupInvitationRejectedPanel(boardingParty, inviteeId, **kw) + + def destroyInvitingPanel(self): + if self.isInvitingPanelUp(): + self.__invitingPanel.cleanup() + self.__invitingPanel = None + return + + def destroyInvitationRejectedPanel(self): + if self.isInvitationRejectedPanelUp(): + self.__invitationRejectedPanel.cleanup() + self.__invitationRejectedPanel = None + return + + def isInvitingPanelIdCorrect(self, inviteeId): + if self.isInvitingPanelUp(): + if inviteeId == self.__invitingPanel.avId: + return True + else: + self.notify.warning('Got a response back from an invitee, but a different invitee panel was open. Maybe lag?') + return False + + def isInvitingPanelUp(self): + if self.__invitingPanel: + if not self.__invitingPanel.isEmpty(): + return True + self.__invitingPanel = None + return False + + def isInvitationRejectedPanelUp(self): + if self.__invitationRejectedPanel: + if not self.__invitationRejectedPanel.isEmpty(): + return True + self.__invitationRejectedPanel = None + return False + + def forceCleanup(self): + if self.isInvitingPanelUp(): + self.__invitingPanel.forceCleanup() + self.__invitingPanel = None + if self.isInvitationRejectedPanelUp(): + self.__invitationRejectedPanel.forceCleanup() + self.__invitationRejectedPanel = None + return + + +class BoardingGroupInviterPanelBase(ToonHeadDialog.ToonHeadDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInviterPanelBase') + + def __init__(self, boardingParty, inviteeId, **kw): + self.boardingParty = boardingParty + self.avId = inviteeId + avatar = base.cr.doId2do.get(self.avId) + self.avatarName = '' + if avatar: + self.avatar = avatar + self.avatarName = avatar.getName() + avatarDNA = avatar.getStyle() + self.defineParams() + command = self.handleButton + optiondefs = (('dialogName', self.dialogName, None), + ('text', self.inviterText, None), + ('style', self.panelStyle, None), + ('buttonTextList', self.buttonTextList, None), + ('command', command, None), + ('image_color', (1.0, 0.89, 0.77, 1.0), None), + ('geom_scale', 0.2, None), + ('geom_pos', (-0.1, 0, -0.025), None), + ('pad', (0.075, 0.075), None), + ('topPad', 0, None), + ('midPad', 0, None), + ('pos', (0.45, 0, 0.75), None), + ('scale', 0.75, None)) + self.defineoptions(kw, optiondefs) + ToonHeadDialog.ToonHeadDialog.__init__(self, avatarDNA) + self.show() + return + + def defineParams(self): + self.notify.error('setupParams: This method should not be called from the base class. Derived class should override this method') + + def cleanup(self): + self.notify.debug('Destroying Panel.') + ToonHeadDialog.ToonHeadDialog.cleanup(self) + + def forceCleanup(self): + self.handleButton(0) + + def handleButton(self, value): + self.cleanup() + + +class BoardingGroupInvitingPanel(BoardingGroupInviterPanelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInvitingPanel') + + def __init__(self, boardingParty, inviteeId, **kw): + BoardingGroupInviterPanelBase.__init__(self, boardingParty, inviteeId, **kw) + self.initialiseoptions(BoardingGroupInvitingPanel) + self.setupUnexpectedExitHooks() + + def defineParams(self): + self.dialogName = 'BoardingGroupInvitingPanel' + self.inviterText = TTLocalizer.BoardingInvitingMessage % self.avatarName + self.panelStyle = TTDialog.CancelOnly + self.buttonTextList = [OTPLocalizer.GuildInviterCancel] + + def handleButton(self, value): + self.boardingParty.requestCancelInvite(self.avId) + BoardingGroupInviterPanelBase.cleanup(self) + + def setupUnexpectedExitHooks(self): + if base.cr.doId2do.has_key(self.avId): + toon = base.cr.doId2do[self.avId] + self.unexpectedExitEventName = toon.uniqueName('disable') + self.accept(self.unexpectedExitEventName, self.forceCleanup) + + def forceCleanup(self): + self.ignore(self.unexpectedExitEventName) + BoardingGroupInviterPanelBase.forceCleanup(self) + + +class BoardingGroupInvitationRejectedPanel(BoardingGroupInviterPanelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('BoardingGroupInvitationRejectedPanel') + + def __init__(self, boardingParty, inviteeId, **kw): + BoardingGroupInviterPanelBase.__init__(self, boardingParty, inviteeId, **kw) + self.initialiseoptions(BoardingGroupInvitationRejectedPanel) + + def defineParams(self): + self.dialogName = 'BoardingGroupInvitationRejectedPanel' + self.inviterText = TTLocalizer.BoardingInvitationRejected % self.avatarName + self.panelStyle = TTDialog.Acknowledge + self.buttonTextList = [OTPLocalizer.GuildInviterOK] diff --git a/toontown/toon/DeathForceAcknowledge.py b/toontown/toon/DeathForceAcknowledge.py new file mode 100644 index 0000000..133798b --- /dev/null +++ b/toontown/toon/DeathForceAcknowledge.py @@ -0,0 +1,57 @@ +from pandac.PandaModules import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from direct.showbase import Transitions +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import LaffMeter + +class DeathForceAcknowledge: + + def __init__(self, doneEvent): + fadeModel = loader.loadModel('phase_3/models/misc/fade') + if fadeModel: + self.fade = DirectFrame( + parent=aspect2dp, + relief=None, + image=fadeModel, + image_color=(0, 0, 0, 0.4), + image_scale=3.0, + state=DGG.NORMAL) + self.fade.reparentTo(aspect2d, FADE_SORT_INDEX) + fadeModel.removeNode() + else: + print 'Problem loading fadeModel.' + self.fade = None + self.dialog = TTDialog.TTGlobalDialog( + message=TTLocalizer.PlaygroundDeathAckMessage, + doneEvent=doneEvent, + style=TTDialog.Acknowledge, + suppressKeys=True) + self.dialog['text_pos'] = (-.26, 0.1) + scale = self.dialog.component('image0').getScale() + scale.setX(scale[0] * 1.3) + self.dialog.component('image0').setScale(scale) + av = base.localAvatar + self.laffMeter = LaffMeter.LaffMeter(av.style, av.hp, av.maxHp) + self.laffMeter.reparentTo(self.dialog) + if av.style.getAnimal() == 'monkey': + self.laffMeter.setPos(-0.46, 0, -0.035) + self.laffMeter.setScale(0.085) + else: + self.laffMeter.setPos(-0.48, 0, -0.035) + self.laffMeter.setScale(0.1) + self.laffMeter.start() + self.dialog.show() + return + + def cleanup(self): + if self.fade: + self.fade.destroy() + if self.laffMeter: + self.laffMeter.destroy() + del self.laffMeter + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return diff --git a/toontown/toon/DistributedNPCBlocker.py b/toontown/toon/DistributedNPCBlocker.py new file mode 100644 index 0000000..30d65fd --- /dev/null +++ b/toontown/toon/DistributedNPCBlocker.py @@ -0,0 +1,70 @@ +from pandac.PandaModules import * +from DistributedNPCToonBase import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import NPCToons +from toontown.toonbase import TTLocalizer +from direct.distributed import DistributedObject +from toontown.quest import QuestParser + +class DistributedNPCBlocker(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.cSphereNodePath.setScale(4.5, 1.0, 6.0) + self.isLocalToon = 1 + self.movie = None + return + + def announceGenerate(self): + DistributedNPCToonBase.announceGenerate(self) + + def initToonState(self): + self.setAnimState('neutral', 0.9, None, None) + posh = NPCToons.BlockerPositions[self.name] + self.setPos(posh[0]) + self.setH(posh[1]) + return + + def disable(self): + if hasattr(self, 'movie') and self.movie: + self.movie.cleanup() + del self.movie + if self.isLocalToon == 1: + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + + def resetBlocker(self): + self.cSphereNode.setCollideMask(BitMask32()) + if hasattr(self, 'movie') and self.movie: + self.movie.cleanup() + self.movie = None + self.startLookAround() + self.clearMat() + if self.isLocalToon == 1: + base.localAvatar.posCamera(0, 0) + self.freeAvatar() + self.isLocalToon = 0 + return + + def setMovie(self, mode, npcId, avId, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.BLOCKER_MOVIE_CLEAR: + return + elif mode == NPCToons.BLOCKER_MOVIE_START: + self.movie = QuestParser.NPCMoviePlayer('tutorial_blocker', base.localAvatar, self) + self.movie.play() + elif mode == NPCToons.BLOCKER_MOVIE_TIMEOUT: + return + + def finishMovie(self, av, isLocalToon, elapsedTime): + self.resetBlocker() diff --git a/toontown/toon/DistributedNPCBlockerAI.py b/toontown/toon/DistributedNPCBlockerAI.py new file mode 100644 index 0000000..9789baf --- /dev/null +++ b/toontown/toon/DistributedNPCBlockerAI.py @@ -0,0 +1,66 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * +import NPCToons +from direct.task.Task import Task + +class DistributedNPCBlockerAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.tutorial = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def setTutorial(self, val): + self.tutorial = val + + def getTutorial(self): + return self.tutorial + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + DistributedNPCToonBaseAI.avatarEnter(self) + av = self.air.doId2do.get(avId) + if av is None: + self.notify.warning('toon isnt there! toon: %s' % avId) + return + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + self.sendStartMovie(avId) + return + + def sendStartMovie(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.BLOCKER_MOVIE_START, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(NPCToons.CLERK_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def sendTimeoutMovie(self, task): + self.timedOut = 1 + self.sendUpdate('setMovie', [NPCToons.BLOCKER_MOVIE_TIMEOUT, + self.npcId, + self.busy, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.busy = 0 + self.timedOut = 0 + self.sendUpdate('setMovie', [NPCToons.BLOCKER_MOVIE_CLEAR, + self.npcId, + 0, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + return Task.done + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + if not self.tutorial: + self.sendTimeoutMovie(None) + return diff --git a/toontown/toon/DistributedNPCClerk.py b/toontown/toon/DistributedNPCClerk.py new file mode 100644 index 0000000..c64c647 --- /dev/null +++ b/toontown/toon/DistributedNPCClerk.py @@ -0,0 +1,134 @@ +from pandac.PandaModules import * +from DistributedNPCToonBase import * +from toontown.minigame import ClerkPurchase +from toontown.shtiker.PurchaseManagerConstants import * +import NPCToons +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +from toontown.hood import ZoneUtil +from toontown.toontowngui import TeaserPanel + +class DistributedNPCClerk(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.purchase = None + self.isLocalToon = 0 + self.av = None + self.purchaseDoneEvent = 'purchaseDone' + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.purchase: + self.purchase.exit() + self.purchase.unload() + self.purchase = None + self.av = None + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def allowedToEnter(self): + if hasattr(base, 'ttAccess') and base.ttAccess and base.ttAccess.canAccess(): + return True + return False + + def handleOkTeaser(self): + self.dialog.destroy() + del self.dialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + + def handleCollisionSphereEnter(self, collEntry): + if self.allowedToEnter(): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='otherGags', doneFunc=self.handleOkTeaser) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + return + + def resetClerk(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.purchase: + self.purchase.exit() + self.purchase.unload() + self.purchase = None + self.clearMat() + self.startLookAround() + self.detectAvatars() + if self.isLocalToon: + self.freeAvatar() + return Task.done + + def setMovie(self, mode, npcId, avId, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.PURCHASE_MOVIE_CLEAR: + return + if mode == NPCToons.PURCHASE_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignore(self.purchaseDoneEvent) + if self.purchase: + self.__handlePurchaseDone() + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetClerk() + elif mode == NPCToons.PURCHASE_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isLocalToon: + camera.wrtReparentTo(render) + camera.lerpPosHpr(-5, 9, self.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + self.setChatAbsolute(TTLocalizer.STOREOWNER_GREETING, CFSpeech | CFTimeout) + if self.isLocalToon: + taskMgr.doMethodLater(1.0, self.popupPurchaseGUI, self.uniqueName('popupPurchaseGUI')) + elif mode == NPCToons.PURCHASE_MOVIE_COMPLETE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + self.resetClerk() + elif mode == NPCToons.PURCHASE_MOVIE_NO_MONEY: + self.setChatAbsolute(TTLocalizer.STOREOWNER_NEEDJELLYBEANS, CFSpeech | CFTimeout) + self.resetClerk() + return + + def popupPurchaseGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.acceptOnce(self.purchaseDoneEvent, self.__handlePurchaseDone) + self.accept('boughtGag', self.__handleBoughtGag) + self.purchase = ClerkPurchase.ClerkPurchase(base.localAvatar, self.remain, self.purchaseDoneEvent) + self.purchase.load() + self.purchase.enter() + return Task.done + + def __handleBoughtGag(self): + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney(), 0) + + def __handlePurchaseDone(self): + self.ignore('boughtGag') + self.d_setInventory(base.localAvatar.inventory.makeNetString(), base.localAvatar.getMoney(), 1) + self.purchase.exit() + self.purchase.unload() + self.purchase = None + return + + def d_setInventory(self, invString, money, done): + self.sendUpdate('setInventory', [invString, money, done]) diff --git a/toontown/toon/DistributedNPCClerkAI.py b/toontown/toon/DistributedNPCClerkAI.py new file mode 100644 index 0000000..2ef86f8 --- /dev/null +++ b/toontown/toon/DistributedNPCClerkAI.py @@ -0,0 +1,109 @@ +from otp.ai.AIBaseGlobal import * +from direct.task.Task import Task +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * + +class DistributedNPCClerkAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.timedOut = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + DistributedNPCToonBaseAI.avatarEnter(self) + av = self.air.doId2do.get(avId) + if av is None: + self.notify.warning('toon isnt there! toon: %s' % avId) + return + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + if self.isBusy(): + self.freeAvatar(avId) + return + if av.getMoney(): + self.sendStartMovie(avId) + else: + self.sendNoMoneyMovie(avId) + return + + def sendStartMovie(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_START, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + taskMgr.doMethodLater(NPCToons.CLERK_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def sendNoMoneyMovie(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_NO_MONEY, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return + + def sendTimeoutMovie(self, task): + self.timedOut = 1 + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_TIMEOUT, + self.npcId, + self.busy, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + self.busy = 0 + self.timedOut = 0 + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_CLEAR, + self.npcId, + 0, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + return Task.done + + def completePurchase(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_COMPLETE, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return + + def setInventory(self, blob, newMoney, done): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + if self.busy != 0: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCClerkAI.setInventory busy with %s' % self.busy) + self.notify.warning('setInventory from unknown avId: %s busy: %s' % (avId, self.busy)) + return + if self.air.doId2do.has_key(avId): + av = self.air.doId2do[avId] + newInventory = av.inventory.makeFromNetString(blob) + currentMoney = av.getMoney() + if av.inventory.validatePurchase(newInventory, currentMoney, newMoney): + av.setMoney(newMoney) + if done: + av.d_setInventory(av.inventory.makeNetString()) + av.d_setMoney(newMoney) + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCClerkAI.setInventory invalid purchase') + self.notify.warning('Avatar ' + str(avId) + ' attempted an invalid purchase.') + av.d_setInventory(av.inventory.makeNetString()) + av.d_setMoney(av.getMoney()) + if self.timedOut: + return + if done: + taskMgr.remove(self.uniqueName('clearMovie')) + self.completePurchase(avId) + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.sendTimeoutMovie(None) + return diff --git a/toontown/toon/DistributedNPCFisherman.py b/toontown/toon/DistributedNPCFisherman.py new file mode 100644 index 0000000..c7c7354 --- /dev/null +++ b/toontown/toon/DistributedNPCFisherman.py @@ -0,0 +1,151 @@ +from pandac.PandaModules import * +from DistributedNPCToonBase import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import NPCToons +from toontown.toonbase import TTLocalizer +from toontown.fishing import FishSellGUI +from direct.task.Task import Task + +class DistributedNPCFisherman(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isLocalToon = 0 + self.av = None + self.button = None + self.popupInfo = None + self.fishGui = None + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupFishGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.fishGui: + self.fishGui.destroy() + self.fishGui = None + self.av = None + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def generate(self): + DistributedNPCToonBase.generate(self) + self.fishGuiDoneEvent = 'fishGuiDone' + + def announceGenerate(self): + DistributedNPCToonBase.announceGenerate(self) + + def initToonState(self): + self.setAnimState('neutral', 1.05, None, None) + npcOrigin = self.cr.playGame.hood.loader.geom.find('**/npc_fisherman_origin_%s;+s' % self.posIndex) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.clearMat() + else: + self.notify.warning('announceGenerate: Could not find npc_fisherman_origin_' + str(self.posIndex)) + return + + def getCollSphereRadius(self): + return 1.0 + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + return + + def setupAvatars(self, av): + self.ignoreAvatars() + av.stopLookAround() + av.lerpLookAt(Point3(-0.5, 4, 0), time=0.5) + self.stopLookAround() + self.lerpLookAt(Point3(av.getPos(self)), time=0.5) + + def resetFisherman(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupFishGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.fishGui: + self.fishGui.destroy() + self.fishGui = None + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.freeAvatar() + return Task.done + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.SELL_MOVIE_CLEAR: + return + if mode == NPCToons.SELL_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignore(self.fishGuiDoneEvent) + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.fishGui: + self.fishGui.destroy() + self.fishGui = None + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isLocalToon: + camera.wrtReparentTo(render) + camera.lerpPosHpr(-5, 9, base.localAvatar.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + if self.isLocalToon: + taskMgr.doMethodLater(1.0, self.popupFishGUI, self.uniqueName('popupFishGUI')) + elif mode == NPCToons.SELL_MOVIE_COMPLETE: + chatStr = TTLocalizer.STOREOWNER_THANKSFISH + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_TROPHY: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + numFish, totalNumFish = extraArgs + self.setChatAbsolute(TTLocalizer.STOREOWNER_TROPHY % (numFish, totalNumFish), CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_NOFISH: + chatStr = TTLocalizer.STOREOWNER_NOFISH + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetFisherman() + elif mode == NPCToons.SELL_MOVIE_NO_MONEY: + self.notify.warning('SELL_MOVIE_NO_MONEY should not be called') + self.resetFisherman() + return + + def __handleSaleDone(self, sell): + self.ignore(self.fishGuiDoneEvent) + self.sendUpdate('completeSale', [sell]) + self.fishGui.destroy() + self.fishGui = None + return + + def popupFishGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.acceptOnce(self.fishGuiDoneEvent, self.__handleSaleDone) + self.fishGui = FishSellGUI.FishSellGUI(self.fishGuiDoneEvent) diff --git a/toontown/toon/DistributedNPCFishermanAI.py b/toontown/toon/DistributedNPCFishermanAI.py new file mode 100644 index 0000000..0ff1f27 --- /dev/null +++ b/toontown/toon/DistributedNPCFishermanAI.py @@ -0,0 +1,95 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * +from toontown.fishing import FishGlobals +from toontown.toonbase import TTLocalizer +from toontown.fishing import FishGlobals +from direct.task import Task + +class DistributedNPCFishermanAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.air.doId2do.has_key(avId): + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + value = av.fishTank.getTotalValue() + if value > 0: + flag = NPCToons.SELL_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(30.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + else: + flag = NPCToons.SELL_MOVIE_NOFISH + self.d_setMovie(avId, flag) + self.sendClearMovie(None) + DistributedNPCToonBaseAI.avatarEnter(self) + return + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a fisherman!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.SELL_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.SELL_MOVIE_CLEAR) + return Task.done + + def completeSale(self, sell): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCFishermanAI.completeSale busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return + if sell: + av = simbase.air.doId2do.get(avId) + if av: + trophyResult = self.air.fishManager.creditFishTank(av) + if trophyResult: + movieType = NPCToons.SELL_MOVIE_TROPHY + extraArgs = [len(av.fishCollection), FishGlobals.getTotalNumFish()] + else: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + self.d_setMovie(avId, movieType, extraArgs) + else: + av = simbase.air.doId2do.get(avId) + if av: + self.d_setMovie(avId, NPCToons.SELL_MOVIE_NOFISH) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCFlippyInToonHall.py b/toontown/toon/DistributedNPCFlippyInToonHall.py new file mode 100644 index 0000000..fcd8170 --- /dev/null +++ b/toontown/toon/DistributedNPCFlippyInToonHall.py @@ -0,0 +1,27 @@ +from pandac.PandaModules import * +from DistributedNPCToon import * + +class DistributedNPCFlippyInToonHall(DistributedNPCToon): + + def __init__(self, cr): + DistributedNPCToon.__init__(self, cr) + + def getCollSphereRadius(self): + return 4 + + def initPos(self): + self.clearMat() + self.setScale(1.25) + + def handleCollisionSphereEnter(self, collEntry): + if self.allowedToTalk(): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + self.lookAt(base.localAvatar) + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='quests', doneFunc=self.handleOkTeaser) diff --git a/toontown/toon/DistributedNPCFlippyInToonHallAI.py b/toontown/toon/DistributedNPCFlippyInToonHallAI.py new file mode 100644 index 0000000..bf53074 --- /dev/null +++ b/toontown/toon/DistributedNPCFlippyInToonHallAI.py @@ -0,0 +1,6 @@ +from DistributedNPCToonAI import * + +class DistributedNPCFlippyInToonHallAI(DistributedNPCToonAI): + + def __init__(self, air, npcId, questCallback = None, hq = 0): + DistributedNPCToonAI.__init__(self, air, npcId, questCallback) diff --git a/toontown/toon/DistributedNPCKartClerk.py b/toontown/toon/DistributedNPCKartClerk.py new file mode 100644 index 0000000..820404f --- /dev/null +++ b/toontown/toon/DistributedNPCKartClerk.py @@ -0,0 +1,133 @@ +from DistributedNPCToonBase import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import NPCToons +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +from toontown.racing.KartShopGui import * +from toontown.racing.KartShopGlobals import * + +class DistributedNPCKartClerk(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isLocalToon = 0 + self.av = None + self.button = None + self.popupInfo = None + self.kartShopGui = None + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupKartShopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.kartShopGui: + self.kartShopGui.destroy() + self.kartShopGui = None + self.av = None + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def generate(self): + DistributedNPCToonBase.generate(self) + + def getCollSphereRadius(self): + return 2.25 + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + return + + def resetKartShopClerk(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupKartShopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.kartShopGui: + self.kartShopGui.destroy() + self.kartShopGui = None + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.freeAvatar() + return Task.done + + def ignoreEventDict(self): + for event in KartShopGlobals.EVENTDICT: + self.ignore(event) + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.SELL_MOVIE_CLEAR: + return + if mode == NPCToons.SELL_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignoreEventDict() + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.kartShopGui: + self.kartShopGui.destroy() + self.kartShopGui = None + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetKartShopClerk() + elif mode == NPCToons.SELL_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isLocalToon: + camera.wrtReparentTo(render) + camera.lerpPosHpr(-5, 9, base.localAvatar.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + if self.isLocalToon: + taskMgr.doMethodLater(1.0, self.popupKartShopGUI, self.uniqueName('popupKartShopGUI')) + elif mode == NPCToons.SELL_MOVIE_COMPLETE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + self.resetKartShopClerk() + elif mode == NPCToons.SELL_MOVIE_PETCANCELED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + self.resetKartShopClerk() + elif mode == NPCToons.SELL_MOVIE_NO_MONEY: + self.notify.warning('SELL_MOVIE_NO_MONEY should not be called') + self.resetKartShopClerk() + return + + def __handleBuyKart(self, kartID): + self.sendUpdate('buyKart', [kartID]) + + def __handleBuyAccessory(self, accID): + self.sendUpdate('buyAccessory', [accID]) + + def __handleGuiDone(self, bTimedOut = False): + self.ignoreAll() + if hasattr(self, 'kartShopGui') and self.kartShopGui != None: + self.kartShopGui.destroy() + self.kartShopGui = None + if not bTimedOut: + self.sendUpdate('transactionDone') + return + + def popupKartShopGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.accept(KartShopGlobals.EVENTDICT['buyAccessory'], self.__handleBuyAccessory) + self.accept(KartShopGlobals.EVENTDICT['buyKart'], self.__handleBuyKart) + self.acceptOnce(KartShopGlobals.EVENTDICT['guiDone'], self.__handleGuiDone) + self.kartShopGui = KartShopGuiMgr(KartShopGlobals.EVENTDICT) diff --git a/toontown/toon/DistributedNPCKartClerkAI.py b/toontown/toon/DistributedNPCKartClerkAI.py new file mode 100644 index 0000000..dde21b9 --- /dev/null +++ b/toontown/toon/DistributedNPCKartClerkAI.py @@ -0,0 +1,129 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.racing.KartShopGlobals import * +from toontown.racing.KartDNA import * + +class DistributedNPCKartClerkAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.air.doId2do.has_key(avId): + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + self.transactionType = '' + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + flag = NPCToons.SELL_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(KartShopGlobals.KARTCLERK_TIMER, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + DistributedNPCToonBaseAI.avatarEnter(self) + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a kart clerk!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.SELL_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.SELL_MOVIE_CLEAR) + return Task.done + + def buyKart(self, whichKart): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.buyKart busy with %s' % self.busy) + self.notify.warning('somebody called buyKart that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + cost = getKartCost(whichKart) + if cost == 'key error': + self.air.writeServerEvent('suspicious', avId, 'Player trying to buy non-existant kart %s' % whichKart) + self.notify.warning('somebody is trying to buy non-existant kart%s! avId: %s' % (whichKart, avId)) + return + elif cost > av.getTickets(): + self.air.writeServerEvent('suspicious', avId, "DistributedNPCKartClerkAI.buyKart and toon doesn't have enough tickets!") + self.notify.warning("somebody called buyKart and didn't have enough tickets to purchase! avId: %s" % avId) + return + av.b_setTickets(av.getTickets() - cost) + self.air.writeServerEvent('kartingTicketsSpent', avId, '%s' % cost) + av.b_setKartBodyType(whichKart) + self.air.writeServerEvent('kartingKartPurchased', avId, '%s' % whichKart) + + def buyAccessory(self, whichAcc): + avId = self.air.getAvatarIdFromSender() + av = simbase.air.doId2do.get(avId) + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.buyAccessory busy with %s' % self.busy) + self.notify.warning('somebody called buyAccessory that I was not busy with! avId: %s' % avId) + return + if len(av.getKartAccessoriesOwned()) >= KartShopGlobals.MAX_KART_ACC: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.buyAcc and toon already has max number of accessories!') + self.notify.warning('somebody called buyAcc and already has maximum allowed accessories! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + cost = getAccCost(whichAcc) + if cost > av.getTickets(): + self.air.writeServerEvent('suspicious', avId, "DistributedNPCKartClerkAI.buyAcc and toon doesn't have enough tickets!") + self.notify.warning("somebody called buyAcc and didn't have enough tickets to purchase! avId: %s" % avId) + return + av.b_setTickets(av.getTickets() - cost) + self.air.writeServerEvent('kartingTicketsSpent', avId, '%s' % cost) + av.addOwnedAccessory(whichAcc) + self.air.writeServerEvent('kartingAccessoryPurchased', avId, '%s' % whichAcc) + av.updateKartDNAField(getAccessoryType(whichAcc), whichAcc) + + def transactionDone(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCKartClerkAI.transactionDone busy with %s' % self.busy) + self.notify.warning('somebody called transactionDone that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + self.d_setMovie(avId, movieType, extraArgs) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCPartyPerson.py b/toontown/toon/DistributedNPCPartyPerson.py new file mode 100644 index 0000000..d84c9d9 --- /dev/null +++ b/toontown/toon/DistributedNPCPartyPerson.py @@ -0,0 +1,203 @@ +from DistributedNPCToonBase import DistributedNPCToonBase +from direct.distributed.DistributedObject import DistributedObject +from toontown.toon import NPCToons +from toontown.toonbase import TTLocalizer +from direct.task.Task import Task +from direct.distributed import ClockDelta +from pandac.PandaModules import CFSpeech, CFTimeout, Point3 +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.parties import PartyGlobals +from toontown.toonbase import ToontownGlobals +from toontown.toontowngui import TeaserPanel + +class DistributedNPCPartyPerson(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isInteractingWithLocalToon = 0 + self.av = None + self.button = None + self.askGui = None + self.teaserDialog = None + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupAskGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + self.av = None + if self.isInteractingWithLocalToon: + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def delete(self): + if self.askGui: + self.ignore(self.planPartyQuestionGuiDoneEvent) + self.askGui.cleanup() + del self.askGui + DistributedNPCToonBase.delete(self) + + def generate(self): + DistributedNPCToonBase.generate(self) + + def announceGenerate(self): + DistributedNPCToonBase.announceGenerate(self) + self.planPartyQuestionGuiDoneEvent = 'planPartyQuestionDone' + self.askGui = TTDialog.TTGlobalDialog(dialogName=self.uniqueName('askGui'), doneEvent=self.planPartyQuestionGuiDoneEvent, message=TTLocalizer.PartyDoYouWantToPlan, style=TTDialog.YesNo, okButtonText=OTPLocalizer.DialogYes, cancelButtonText=OTPLocalizer.DialogNo) + self.askGui.hide() + + def initToonState(self): + self.setAnimState('neutral', 1.05, None, None) + if self.posIndex % 2 == 0: + side = 'left' + else: + side = 'right' + npcOrigin = self.cr.playGame.hood.loader.geom.find('**/party_person_%s;+s' % side) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.clearMat() + else: + self.notify.warning('announceGenerate: Could not find party_person_%s' % side) + return + + def getCollSphereRadius(self): + return 1.0 + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + return + + def setupAvatars(self, av): + self.ignoreAvatars() + av.stopLookAround() + av.lerpLookAt(Point3(-0.5, 4, 0), time=0.5) + self.stopLookAround() + self.lerpLookAt(Point3(av.getPos(self)), time=0.5) + + def resetPartyPerson(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupAskGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.askGui: + self.askGui.hide() + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isInteractingWithLocalToon: + if hasattr(self, 'teaserDialog') and not self.teaserDialog: + self.freeAvatar() + return Task.done + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isInteractingWithLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.PARTY_MOVIE_CLEAR: + return + if mode == NPCToons.PARTY_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isInteractingWithLocalToon: + self.ignore(self.planPartyQuestionGuiDoneEvent) + if self.askGui: + self.askGui.hide() + self.ignore(self.planPartyQuestionGuiDoneEvent) + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isInteractingWithLocalToon: + camera.wrtReparentTo(render) + camera.lerpPosHpr(-5, 9, base.localAvatar.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + taskMgr.doMethodLater(1.0, self.popupAskGUI, self.uniqueName('popupAskGUI')) + else: + self.setChatAbsolute(TTLocalizer.PartyDoYouWantToPlan, CFSpeech | CFTimeout) + elif mode == NPCToons.PARTY_MOVIE_COMPLETE: + chatStr = TTLocalizer.PartyPlannerOnYourWay + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + if self.isInteractingWithLocalToon: + base.localAvatar.aboutToPlanParty = True + base.cr.partyManager.setPartyPlannerStyle(self.style) + base.cr.partyManager.setPartyPlannerName(self.name) + base.localAvatar.creatingNewPartyWithMagicWord = False + loaderId = 'safeZoneLoader' + whereId = 'party' + hoodId, zoneId = extraArgs + avId = -1 + place = base.cr.playGame.getPlace() + requestStatus = {'loader': loaderId, + 'where': whereId, + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': avId} + place.requestLeave(requestStatus) + elif mode == NPCToons.PARTY_MOVIE_MAYBENEXTTIME: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.setChatAbsolute(TTLocalizer.PartyPlannerMaybeNextTime, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_ALREADYHOSTING: + chatStr = TTLocalizer.PartyPlannerHostingTooMany + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_ONLYPAID: + chatStr = TTLocalizer.PartyPlannerOnlyPaid + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_COMINGSOON: + chatStr = TTLocalizer.PartyPlannerNpcComingSoon + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + elif mode == NPCToons.PARTY_MOVIE_MINCOST: + chatStr = TTLocalizer.PartyPlannerNpcMinCost % PartyGlobals.MinimumPartyCost + self.setChatAbsolute(chatStr, CFSpeech | CFTimeout) + self.resetPartyPerson() + return + + def __handleAskDone(self): + self.ignore(self.planPartyQuestionGuiDoneEvent) + doneStatus = self.askGui.doneStatus + if doneStatus == 'ok': + wantsToPlan = 1 + if localAvatar.getGameAccess() != ToontownGlobals.AccessFull: + wantsToPlan = 0 + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped', force=1) + self.teaserDialog = TeaserPanel.TeaserPanel(pageName='parties', doneFunc=self.handleOkTeaser) + else: + wantsToPlan = 0 + self.sendUpdate('answer', [wantsToPlan]) + self.askGui.hide() + + def popupAskGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.acceptOnce(self.planPartyQuestionGuiDoneEvent, self.__handleAskDone) + self.askGui.show() + + def handleOkTeaser(self): + self.teaserDialog.destroy() + del self.teaserDialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') diff --git a/toontown/toon/DistributedNPCPartyPersonAI.py b/toontown/toon/DistributedNPCPartyPersonAI.py new file mode 100644 index 0000000..3dd489c --- /dev/null +++ b/toontown/toon/DistributedNPCPartyPersonAI.py @@ -0,0 +1,103 @@ +from DistributedNPCToonBaseAI import DistributedNPCToonBaseAI +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.toonbase import ToontownGlobals +from toontown.toon import NPCToons +from direct.distributed import ClockDelta +from toontown.parties import PartyGlobals + +class DistributedNPCPartyPersonAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.air.doId2do.has_key(avId): + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + parties = av.hostedParties + if not self.air.partyManager.canBuyParties(): + flag = NPCToons.PARTY_MOVIE_COMINGSOON + self.d_setMovie(avId, flag) + self.sendClearMovie(None) + elif av.getTotalMoney() < PartyGlobals.MinimumPartyCost: + flag = NPCToons.PARTY_MOVIE_MINCOST + self.d_setMovie(avId, flag) + self.sendClearMovie(None) + elif av.canPlanParty(): + flag = NPCToons.PARTY_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(30.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + else: + flag = NPCToons.PARTY_MOVIE_ALREADYHOSTING + self.d_setMovie(avId, flag) + self.sendClearMovie(None) + DistributedNPCToonBaseAI.avatarEnter(self) + return + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a party person!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.PARTY_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.PARTY_MOVIE_CLEAR) + return Task.done + + def answer(self, wantsToPlan): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPartyPersonAI.answer busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return + if wantsToPlan: + av = simbase.air.doId2do.get(avId) + if av: + if av.getGameAccess() != ToontownGlobals.AccessFull: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPartyPersonAI.free player tried to host party.') + flag = NPCToons.PARTY_MOVIE_ONLYPAID + self.d_setMovie(avId, flag) + else: + zoneId = self.air.allocateZone() + hoodId = ToontownGlobals.PartyHood + self.d_setMovie(avId, NPCToons.PARTY_MOVIE_COMPLETE, [hoodId, zoneId]) + else: + av = simbase.air.doId2do.get(avId) + if av: + self.d_setMovie(avId, NPCToons.PARTY_MOVIE_MAYBENEXTTIME) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCPetclerk.py b/toontown/toon/DistributedNPCPetclerk.py new file mode 100644 index 0000000..fe263c6 --- /dev/null +++ b/toontown/toon/DistributedNPCPetclerk.py @@ -0,0 +1,200 @@ +from pandac.PandaModules import * +from DistributedNPCToonBase import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import NPCToons +from direct.task.Task import Task +from toontown.toonbase import TTLocalizer +from toontown.pets import PetshopGUI +from toontown.hood import ZoneUtil +from toontown.toontowngui import TeaserPanel + +class DistributedNPCPetclerk(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isLocalToon = 0 + self.av = None + self.button = None + self.popupInfo = None + self.petshopGui = None + self.petSeeds = None + self.waitingForPetSeeds = False + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPetshopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + if self.petshopGui: + self.petshopGui.destroy() + self.petshopGui = None + self.av = None + if self.isLocalToon: + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def generate(self): + DistributedNPCToonBase.generate(self) + self.eventDict = {} + self.eventDict['guiDone'] = 'guiDone' + self.eventDict['petAdopted'] = 'petAdopted' + self.eventDict['petReturned'] = 'petReturned' + self.eventDict['fishSold'] = 'fishSold' + + def getCollSphereRadius(self): + return 4.0 + + def allowedToEnter(self): + if hasattr(base, 'ttAccess') and base.ttAccess and base.ttAccess.canAccess(): + return True + return False + + def handleOkTeaser(self): + self.dialog.destroy() + del self.dialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + + def handleCollisionSphereEnter(self, collEntry): + if self.allowedToEnter(): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='tricks', doneFunc=self.handleOkTeaser) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + return + + def resetPetshopClerk(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPetshopGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.petshopGui: + self.petshopGui.destroy() + self.petshopGui = None + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.freeAvatar() + self.petSeeds = None + self.waitingForPetSeeds = False + return Task.done + + def ignoreEventDict(self): + for event in self.eventDict.values(): + self.ignore(event) + + def setPetSeeds(self, petSeeds): + self.petSeeds = petSeeds + if self.waitingForPetSeeds: + self.waitingForPetSeeds = False + self.popupPetshopGUI(None) + return + + def setMovie(self, mode, npcId, avId, extraArgs, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.SELL_MOVIE_CLEAR: + return + if mode == NPCToons.SELL_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignoreEventDict() + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.petshopGui: + self.petshopGui.destroy() + self.petshopGui = None + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_START: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + self.setupAvatars(self.av) + if self.isLocalToon: + camera.wrtReparentTo(render) + camera.lerpPosHpr(-5, 9, base.localAvatar.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + if self.isLocalToon: + taskMgr.doMethodLater(1.0, self.popupPetshopGUI, self.uniqueName('popupPetshopGUI')) + elif mode == NPCToons.SELL_MOVIE_COMPLETE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_THANKSFISH_PETSHOP, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_PETRETURNED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_PETRETURNED, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_PETADOPTED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_PETADOPTED, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_PETCANCELED: + self.setChatAbsolute(TTLocalizer.STOREOWNER_PETCANCELED, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_TROPHY: + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + numFish, totalNumFish = extraArgs + self.setChatAbsolute(TTLocalizer.STOREOWNER_TROPHY % (numFish, totalNumFish), CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_NOFISH: + self.setChatAbsolute(TTLocalizer.STOREOWNER_NOFISH, CFSpeech | CFTimeout) + self.resetPetshopClerk() + elif mode == NPCToons.SELL_MOVIE_NO_MONEY: + self.notify.warning('SELL_MOVIE_NO_MONEY should not be called') + self.resetPetshopClerk() + return + + def __handlePetAdopted(self, whichPet, nameIndex): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ADOPTADOOLE: Adopt a doodle.') + base.cr.removePetFromFriendsMap() + self.ignore(self.eventDict['petAdopted']) + self.sendUpdate('petAdopted', [whichPet, nameIndex]) + + def __handlePetReturned(self): + base.cr.removePetFromFriendsMap() + self.ignore(self.eventDict['petReturned']) + self.sendUpdate('petReturned') + + def __handleFishSold(self): + self.ignore(self.eventDict['fishSold']) + self.sendUpdate('fishSold') + + def __handleGUIDone(self, bTimedOut = False): + self.ignore(self.eventDict['guiDone']) + self.petshopGui.destroy() + self.petshopGui = None + if not bTimedOut: + self.sendUpdate('transactionDone') + return + + def popupPetshopGUI(self, task): + if not self.petSeeds: + self.waitingForPetSeeds = True + return + self.setChatAbsolute('', CFSpeech) + self.acceptOnce(self.eventDict['guiDone'], self.__handleGUIDone) + self.acceptOnce(self.eventDict['petAdopted'], self.__handlePetAdopted) + self.acceptOnce(self.eventDict['petReturned'], self.__handlePetReturned) + self.acceptOnce(self.eventDict['fishSold'], self.__handleFishSold) + self.petshopGui = PetshopGUI.PetshopGUI(self.eventDict, self.petSeeds) diff --git a/toontown/toon/DistributedNPCPetclerkAI.py b/toontown/toon/DistributedNPCPetclerkAI.py new file mode 100644 index 0000000..7e64c8d --- /dev/null +++ b/toontown/toon/DistributedNPCPetclerkAI.py @@ -0,0 +1,151 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * +from toontown.toonbase import TTLocalizer +from direct.task import Task +from toontown.fishing import FishGlobals +from toontown.pets import PetUtil, PetDNA, PetConstants + +class DistributedNPCPetclerkAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.givesQuests = 0 + self.busy = 0 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + DistributedNPCToonBaseAI.delete(self) + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.air.doId2do.has_key(avId): + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + self.petSeeds = simbase.air.petMgr.getAvailablePets(3, 2) + numGenders = len(PetDNA.PetGenders) + self.petSeeds *= numGenders + self.petSeeds.sort() + self.sendUpdateToAvatarId(avId, 'setPetSeeds', [self.petSeeds]) + self.transactionType = '' + av = self.air.doId2do[avId] + self.busy = avId + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + flag = NPCToons.SELL_MOVIE_START + self.d_setMovie(avId, flag) + taskMgr.doMethodLater(PetConstants.PETCLERK_TIMER, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + DistributedNPCToonBaseAI.avatarEnter(self) + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a fisherman!') + + def d_setMovie(self, avId, flag, extraArgs = []): + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + extraArgs, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + + def sendTimeoutMovie(self, task): + self.d_setMovie(self.busy, NPCToons.SELL_MOVIE_TIMEOUT) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.busy = 0 + self.d_setMovie(0, NPCToons.SELL_MOVIE_CLEAR) + return Task.done + + def fishSold(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.fishSold busy with %s' % self.busy) + self.notify.warning('somebody called fishSold that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + trophyResult = self.air.fishManager.creditFishTank(av) + if trophyResult: + movieType = NPCToons.SELL_MOVIE_TROPHY + extraArgs = [len(av.fishCollection), FishGlobals.getTotalNumFish()] + else: + movieType = NPCToons.SELL_MOVIE_COMPLETE + extraArgs = [] + self.d_setMovie(avId, movieType, extraArgs) + self.transactionType = 'fish' + self.sendClearMovie(None) + return + + def petAdopted(self, petNum, nameIndex): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.petAdopted busy with %s' % self.busy) + self.notify.warning('somebody called petAdopted that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + from toontown.hood import ZoneUtil + zoneId = ZoneUtil.getCanonicalSafeZoneId(self.zoneId) + if petNum not in range(0, len(self.petSeeds)): + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.petAdopted and no such pet!') + self.notify.warning('somebody called petAdopted on a non-existent pet! avId: %s' % avId) + return + cost = PetUtil.getPetCostFromSeed(self.petSeeds[petNum], zoneId) + if cost > av.getTotalMoney(): + self.air.writeServerEvent('suspicious', avId, "DistributedNPCPetshopAI.petAdopted and toon doesn't have enough money!") + self.notify.warning("somebody called petAdopted and didn't have enough money to adopt! avId: %s" % avId) + return + if av.petId != 0: + simbase.air.petMgr.deleteToonsPet(avId) + gender = petNum % len(PetDNA.PetGenders) + if nameIndex not in range(0, TTLocalizer.PetNameIndexMAX): + self.air.writeServerEvent('avoid_crash', avId, "DistributedNPCPetclerkAI.petAdopted and didn't have valid nameIndex!") + self.notify.warning("somebody called petAdopted and didn't have valid nameIndex to adopt! avId: %s" % avId) + return + simbase.air.petMgr.createNewPetFromSeed(avId, self.petSeeds[petNum], nameIndex=nameIndex, gender=gender, safeZoneId=zoneId) + self.transactionType = 'adopt' + bankPrice = min(av.getBankMoney(), cost) + walletPrice = cost - bankPrice + av.b_setBankMoney(av.getBankMoney() - bankPrice) + av.b_setMoney(av.getMoney() - walletPrice) + + def petReturned(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.petReturned busy with %s' % self.busy) + self.notify.warning('somebody called petReturned that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + simbase.air.petMgr.deleteToonsPet(avId) + self.transactionType = 'return' + + def transactionDone(self): + avId = self.air.getAvatarIdFromSender() + if self.busy != avId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCPetshopAI.transactionDone busy with %s' % self.busy) + self.notify.warning('somebody called transactionDone that I was not busy with! avId: %s' % avId) + return + av = simbase.air.doId2do.get(avId) + if av: + if self.transactionType == 'adopt': + self.d_setMovie(avId, NPCToons.SELL_MOVIE_PETADOPTED) + elif self.transactionType == 'return': + self.d_setMovie(avId, NPCToons.SELL_MOVIE_PETRETURNED) + elif self.transactionType == '': + self.d_setMovie(avId, NPCToons.SELL_MOVIE_PETCANCELED) + self.sendClearMovie(None) + return + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + return diff --git a/toontown/toon/DistributedNPCScientist.py b/toontown/toon/DistributedNPCScientist.py new file mode 100644 index 0000000..381b137 --- /dev/null +++ b/toontown/toon/DistributedNPCScientist.py @@ -0,0 +1,118 @@ +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer, ToontownGlobals +import DistributedNPCToonBase + +class DistributedNPCScientist(DistributedNPCToonBase.DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.DistributedNPCToonBase.__init__(self, cr) + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.show() + else: + self.hide() + self.accept('SillyMeterIsRunning', self.sillyMeterIsRunning) + + def disable(self): + self.ignore('SillyMeterIsRunning') + DistributedNPCToonBase.DistributedNPCToonBase.disable(self) + + def sillyMeterIsRunning(self, isRunning): + if isRunning: + self.show() + else: + self.hide() + + def getCollSphereRadius(self): + return 2.5 + + def initPos(self): + self.setHpr(180, 0, 0) + self.setScale(1.0) + + def handleCollisionSphereEnter(self, collEntry): + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + + def setChat(self, topic, partPos, partId, progress, flags): + msg = TTLocalizer.toontownDialogues[topic][partPos, partId][progress] + self.setChatMuted(msg, flags) + + def announceGenerate(self): + DistributedNPCToonBase.DistributedNPCToonBase.announceGenerate(self) + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_HOLIDAY) or base.cr.newsManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.show() + else: + self.hide() + + def generateToon(self): + self.setLODs() + self.generateToonLegs() + self.generateToonHead() + self.generateToonTorso() + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.rightHands = [] + self.leftHands = [] + self.headParts = [] + self.hipsParts = [] + self.torsoParts = [] + self.legsParts = [] + self.__bookActors = [] + self.__holeActors = [] + self.setupToonNodes() + if self.style.getTorsoSize() == 'short' and self.style.getAnimal() == 'duck': + sillyReader = loader.loadModel('phase_4/models/props/tt_m_prp_acs_sillyReader') + for rHand in self.getRightHands(): + placeholder = rHand.attachNewNode('SillyReader') + sillyReader.instanceTo(placeholder) + placeholder.setH(180) + placeholder.setScale(render, 1.0) + placeholder.setPos(0, 0, 0.1) + + elif self.style.getTorsoSize() == 'long' and self.style.getAnimal() == 'monkey' or self.style.getTorsoSize() == 'medium' and self.style.getAnimal() == 'horse': + clipBoard = loader.loadModel('phase_4/models/props/tt_m_prp_acs_clipboard') + for rHand in self.getRightHands(): + placeholder = rHand.attachNewNode('ClipBoard') + clipBoard.instanceTo(placeholder) + placeholder.setH(180) + placeholder.setScale(render, 1.0) + placeholder.setPos(0, 0, 0.1) + + def startLookAround(self): + pass + + def scientistPlay(self): + if self.style.getTorsoSize() == 'short' and self.style.getAnimal() == 'duck': + sillyReaders = self.findAllMatches('**/SillyReader') + for sillyReader in sillyReaders: + if not sillyReader.isEmpty(): + sillyReader.stash() + sillyReader = None + + elif self.style.getTorsoSize() == 'long' and self.style.getAnimal() == 'monkey': + clipBoards = self.findAllMatches('**/ClipBoard') + for clipBoard in clipBoards: + if not clipBoard.isEmpty(): + clipBoard.stash() + clipBoard = None + + return + + def showScientistProp(self): + if self.style.getTorsoSize() == 'short' and self.style.getAnimal() == 'duck': + sillyReaders = self.findAllMatches('**/SillyReader;+s') + for sillyReader in sillyReaders: + if not sillyReader.isEmpty(): + sillyReader.unstash() + sillyReader = None + + elif self.style.getTorsoSize() == 'long' and self.style.getAnimal() == 'monkey': + clipBoards = self.findAllMatches('**/ClipBoard;+s') + for clipBoard in clipBoards: + if not clipBoard.isEmpty(): + clipBoard.unstash() + clipBoard = None + + return diff --git a/toontown/toon/DistributedNPCScientistAI.py b/toontown/toon/DistributedNPCScientistAI.py new file mode 100644 index 0000000..d3ff9ea --- /dev/null +++ b/toontown/toon/DistributedNPCScientistAI.py @@ -0,0 +1,239 @@ +import DistributedNPCToonBaseAI +from toontown.toonbase import TTLocalizer, ToontownGlobals +from direct.fsm import ClassicFSM, State +from direct.task.Task import Task + +class DistributedNPCScientistAI(DistributedNPCToonBaseAI.DistributedNPCToonBaseAI): + + def __init__(self, air, npcId, questCallback = None, hq = 0): + DistributedNPCToonBaseAI.DistributedNPCToonBaseAI.__init__(self, air, npcId, questCallback) + self.scientistFSM = ClassicFSM.ClassicFSM('Scientist', [ + State.State('Neutral', + self.enterNeutral, + self.exitNeutral, [ + 'Phase0', + 'Phase1', + 'Phase2', + 'Phase2_5', + 'Phase3', + 'Phase4', + 'Phase5', + 'Off']), + State.State('Phase0', + self.enterPhase0, + self.exitPhase0, [ + 'Phase1', + 'Neutral']), + State.State('Phase1', + self.enterPhase1, + self.exitPhase1, [ + 'Phase2', + 'Neutral']), + State.State('Phase2', + self.enterPhase2, + self.exitPhase2, [ + 'Phase2_5', + 'Neutral']), + State.State('Phase2_5', + self.enterPhase2_5, + self.exitPhase2_5, [ + 'Phase3', + 'Neutral']), + State.State('Phase3', + self.enterPhase3, + self.exitPhase3, [ + 'Phase4', + 'Neutral']), + State.State('Phase4', + self.enterPhase4, + self.exitPhase4, [ + 'Phase5', + 'Neutral']), + State.State('Phase5', + self.enterPhase5, + self.exitPhase5, [ + 'Neutral']), + State.State('Off', + self.enterOff, + self.exitOff, [])], + 'Neutral', 'Off') + + if self.npcId == 2018 or self.npcId == 2019: + self.startAnimState = 'ScientistJealous' + elif self.npcId == 2020: + self.startAnimState = 'ScientistEmcee' + self.scientistFSM.enterInitialState() + + def selectPhase(self, newPhase): + try: + if newPhase <= 4: + gotoPhase = '0' + elif newPhase <= 6: + gotoPhase = '1' + elif newPhase <= 11: + gotoPhase = '2' + elif newPhase <= 12: + gotoPhase = '2_5' + elif newPhase <= 13: + gotoPhase = '3' + elif newPhase <= 14: + gotoPhase = '4' + elif newPhase <= 15: + gotoPhase = '5' + else: + if not self.scientistFSM.getCurrentState() == self.scientistFSM.getStateNamed('Neutral'): + self.scientistFSM.request('Neutral') + return + gotoPhase = 'Phase' + gotoPhase + if not self.scientistFSM.getCurrentState() == self.scientistFSM.getStateNamed(gotoPhase): + self.scientistFSM.request(gotoPhase) + except: + self.notify.warning('Illegal phase transition requested') + + def startIfNeeded(self): + if hasattr(simbase.air, 'holidayManager') and simbase.air.holidayManager: + self.curPhase = self.getPhaseToRun() + if self.curPhase != -1: + self.selectPhase(self.curPhase) + + def getPhaseToRun(self): + result = -1 + enoughInfoToRun = False + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_HOLIDAY) or simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + 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 enterNeutral(self): + self.accept('SillyMeterPhase', self.selectPhase) + self.startIfNeeded() + + def exitNeutral(self): + self.ignore('SillyMeterPhase') + + def enterPhase0(self): + if self.npcId == 2020: + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsPhasePreTopTopic, endPause=30) + elif self.npcId == 2018 or self.npcId == 2019: + self.d_setAnimState('ScientistJealous', 1.0) + self.accept('SillyMeterPhase', self.selectPhase) + + def exitPhase0(self): + if self.npcId == 2020: + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsPhasePreTopTopic) + self.ignore('SillyMeterPhase') + + def enterPhase1(self): + if self.npcId == 2020: + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsPhasePreTopTopic, endPause=30) + elif self.npcId == 2018 or self.npcId == 2019: + self.d_setAnimState('ScientistJealous', 1.0) + self.accept('SillyMeterPhase', self.selectPhase) + + def exitPhase1(self): + if self.npcId == 2020: + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsPhasePreTopTopic) + self.ignore('SillyMeterPhase') + + def enterPhase2(self): + if self.npcId == 2020: + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsPhasePreTopTopic, endPause=30) + elif self.npcId == 2018 or self.npcId == 2019: + self.d_setAnimState('ScientistWork', 1.0) + self.accept('SillyMeterPhase', self.selectPhase) + + def exitPhase2(self): + if self.npcId == 2020: + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsPhasePreTopTopic) + self.ignore('SillyMeterPhase') + + def enterPhase2_5(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic, endPause=30) + else: + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsPhaseTopTopic, endPause=30) + elif self.npcId == 2018 or self.npcId == 2019: + self.d_setAnimState('ScientistLessWork', 1.0) + self.accept('SillyMeterPhase', self.selectPhase) + + def exitPhase2_5(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic) + else: + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsPhaseTopTopic) + self.ignore('SillyMeterPhase') + + def enterPhase3(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic, endPause=30) + else: + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsPhasePostTopTopic, endPause=30) + elif self.npcId == 2018 or self.npcId == 2019: + self.d_setAnimState('ScientistPlay', 1.0) + self.accept('SillyMeterPhase', self.selectPhase) + + def exitPhase3(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic) + else: + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsPhasePostTopTopic) + self.ignore('SillyMeterPhase') + + def enterPhase4(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic, endPause=30) + else: + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsPhasePostTopTopic, endPause=30) + elif self.npcId == 2018 or self.npcId == 2019: + self.d_setAnimState('ScientistPlay', 1.0) + self.accept('SillyMeterPhase', self.selectPhase) + + def exitPhase4(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic) + else: + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsPhasePostTopTopic) + self.ignore('SillyMeterPhase') + + def enterPhase5(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic, endPause=30) + else: + self.air.dialogueManager.requestDialogue(self, TTLocalizer.AprilToonsPhasePostTopTopic, endPause=30) + elif self.npcId == 2018 or self.npcId == 2019: + self.d_setAnimState('ScientistPlay', 1.0) + self.accept('SillyMeterPhase', self.selectPhase) + + def exitPhase5(self): + if self.npcId == 2020: + if simbase.air.holidayManager.isHolidayRunning(ToontownGlobals.SILLYMETER_EXT_HOLIDAY): + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsExtPhaseTopTopic) + else: + self.air.dialogueManager.leaveDialogue(self, TTLocalizer.AprilToonsPhasePostTopTopic) + self.ignore('SillyMeterPhase') + + def enterOff(self): + pass + + def exitOff(self): + pass + + def delete(self): + self.scientistFSM.requestFinalState() + if hasattr(self, 'scientistFSM'): + del self.scientistFSM + DistributedNPCToonBaseAI.DistributedNPCToonBaseAI.delete(self) diff --git a/toontown/toon/DistributedNPCSpecialQuestGiver.py b/toontown/toon/DistributedNPCSpecialQuestGiver.py new file mode 100644 index 0000000..4beda67 --- /dev/null +++ b/toontown/toon/DistributedNPCSpecialQuestGiver.py @@ -0,0 +1,247 @@ +from pandac.PandaModules import * +from DistributedNPCToonBase import * +from toontown.quest import QuestParser +from toontown.quest import QuestChoiceGui +from toontown.quest import TrackChoiceGui +from toontown.toonbase import TTLocalizer +from toontown.hood import ZoneUtil +from toontown.toontowngui import TeaserPanel +ChoiceTimeout = 20 + +class DistributedNPCSpecialQuestGiver(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.curQuestMovie = None + self.questChoiceGui = None + self.trackChoiceGui = None + return + + def announceGenerate(self): + self.setAnimState('neutral', 0.9, None, None) + npcOrigin = self.cr.playGame.hood.loader.geom.find('**/npc_origin_' + `(self.posIndex)`) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.clearMat() + else: + self.notify.warning('announceGenerate: Could not find npc_origin_' + str(self.posIndex)) + DistributedNPCToonBase.announceGenerate(self) + return + + def delayDelete(self): + DistributedNPCToonBase.delayDelete(self) + if self.curQuestMovie: + curQuestMovie = self.curQuestMovie + self.curQuestMovie = None + curQuestMovie.timeout(fFinish=1) + curQuestMovie.cleanup() + return + + def disable(self): + self.cleanupMovie() + DistributedNPCToonBase.disable(self) + + def cleanupMovie(self): + self.clearChat() + self.ignore('chooseQuest') + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.ignore(self.uniqueName('doneChatPage')) + if self.curQuestMovie: + self.curQuestMovie.timeout(fFinish=1) + self.curQuestMovie.cleanup() + self.curQuestMovie = None + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + return + + def allowedToTalk(self): + if base.cr.isPaid(): + return True + place = base.cr.playGame.getPlace() + myHoodId = ZoneUtil.getCanonicalHoodId(place.zoneId) + if hasattr(place, 'id'): + myHoodId = place.id + if myHoodId in (ToontownGlobals.ToontownCentral, + ToontownGlobals.MyEstate, + ToontownGlobals.GoofySpeedway, + ToontownGlobals.Tutorial): + return True + return False + + def handleCollisionSphereEnter(self, collEntry): + if self.allowedToTalk(): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='quests', doneFunc=self.handleOkTeaser) + + def handleOkTeaser(self): + self.dialog.destroy() + del self.dialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + + def finishMovie(self, av, isLocalToon, elapsedTime): + self.cleanupMovie() + av.startLookAround() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if isLocalToon: + taskMgr.remove(self.uniqueName('lerpCamera')) + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + self.sendUpdate('setMovieDone', []) + self.nametag3d.clearDepthTest() + self.nametag3d.clearBin() + + def setupCamera(self, mode): + camera.wrtReparentTo(render) + if mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE or mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + camera.lerpPosHpr(5, 9, self.getHeight() - 0.5, 155, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + else: + camera.lerpPosHpr(-5, 9, self.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + + def setMovie(self, mode, npcId, avId, quests, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.QUEST_MOVIE_CLEAR: + self.cleanupMovie() + return + if mode == NPCToons.QUEST_MOVIE_TIMEOUT: + self.cleanupMovie() + if isLocalToon: + self.freeAvatar() + self.setPageNumber(0, -1) + self.clearChat() + self.startLookAround() + self.detectAvatars() + return + av = base.cr.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + if mode == NPCToons.QUEST_MOVIE_REJECT: + rejectString = Quests.chooseQuestDialogReject() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + return + if mode == NPCToons.QUEST_MOVIE_TIER_NOT_DONE: + rejectString = Quests.chooseQuestDialogTierNotDone() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + return + self.setupAvatars(av) + fullString = '' + toNpcId = None + if mode == NPCToons.QUEST_MOVIE_COMPLETE: + questId, rewardId, toNpcId = quests + scriptId = 'quest_complete_' + str(questId) + if QuestParser.questDefined(scriptId): + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, Quests.COMPLETE) + '\x07' + if rewardId: + fullString += Quests.getReward(rewardId).getString() + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieQuestChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieTrackChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_INCOMPLETE: + questId, completeStatus, toNpcId = quests + scriptId = 'quest_incomplete_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, completeStatus) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_ASSIGN: + questId, rewardId, toNpcId = quests + scriptId = 'quest_assign_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + fullString += Quests.chooseQuestDialog(questId, Quests.QUEST) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE: + if isLocalToon: + self.setupCamera(mode) + self.setChatAbsolute(TTLocalizer.QuestMovieQuestChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseQuest', self.sendChooseQuest) + self.questChoiceGui = QuestChoiceGui.QuestChoiceGui() + self.questChoiceGui.setQuests(quests, npcId, ChoiceTimeout) + return + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + if isLocalToon: + self.setupCamera(mode) + tracks = quests + self.setChatAbsolute(TTLocalizer.QuestMovieTrackChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseTrack', self.sendChooseTrack) + self.trackChoiceGui = TrackChoiceGui.TrackChoiceGui(tracks, ChoiceTimeout) + return + fullString = Quests.fillInQuestNames(fullString, avName=av.name, fromNpcId=npcId, toNpcId=toNpcId) + self.acceptOnce(self.uniqueName('doneChatPage'), self.finishMovie, extraArgs=[av, isLocalToon]) + self.clearChat() + self.setPageChat(avId, 0, fullString, 1) + return + + def sendChooseQuest(self, questId): + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.sendUpdate('chooseQuest', [questId]) + return + + def sendChooseTrack(self, trackId): + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + self.sendUpdate('chooseTrack', [trackId]) + return diff --git a/toontown/toon/DistributedNPCSpecialQuestGiverAI.py b/toontown/toon/DistributedNPCSpecialQuestGiverAI.py new file mode 100644 index 0000000..d440985 --- /dev/null +++ b/toontown/toon/DistributedNPCSpecialQuestGiverAI.py @@ -0,0 +1,226 @@ +from otp.ai.AIBaseGlobal import * +from direct.task.Task import Task +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * +from toontown.quest import Quests + +class DistributedNPCSpecialQuestGiverAI(DistributedNPCToonBaseAI): + + def __init__(self, air, npcId, questCallback = None, hq = 0): + DistributedNPCToonBaseAI.__init__(self, air, npcId, questCallback) + self.hq = hq + self.tutorial = 0 + self.pendingAvId = None + return + + def getTutorial(self): + return self.tutorial + + def setTutorial(self, val): + self.tutorial = val + + def getHq(self): + return self.hq + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('avatar enter ' + str(avId)) + self.air.questManager.requestInteract(avId, self) + DistributedNPCToonBaseAI.avatarEnter(self) + + def chooseQuest(self, questId, quest = None): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseQuest: avatar %s choseQuest %s' % (avId, questId)) + if not self.pendingAvId: + self.notify.warning('chooseQuest: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseQuest: not expecting an answer from this avatar: %s' % avId) + return + if questId == 0: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarCancelled(avId) + self.cancelChoseQuest(avId) + return + for quest in self.pendingQuests: + if questId == quest[0]: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarChoseQuest(avId, self, *quest) + return + + self.air.questManager.avatarChoseQuest(avId, self, *quest) + self.notify.warning('chooseQuest: avatar: %s chose a quest not offered: %s' % (avId, questId)) + self.pendingAvId = None + self.pendingQuests = None + return + + def chooseTrack(self, trackId): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseTrack: avatar %s choseTrack %s' % (avId, trackId)) + if not self.pendingAvId: + self.notify.warning('chooseTrack: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseTrack: not expecting an answer from this avatar: %s' % avId) + return + if trackId == -1: + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.air.questManager.avatarCancelled(avId) + self.cancelChoseTrack(avId) + return + for track in self.pendingTracks: + if trackId == track: + self.air.questManager.avatarChoseTrack(avId, self, self.pendingTrackQuest, trackId) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + self.notify.warning('chooseTrack: avatar: %s chose a track not offered: %s' % (avId, trackId)) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + def sendTimeoutMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIMEOUT, + self.npcId, + self.busy, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + self.busy = 0 + return Task.done + + def sendClearMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.busy = 0 + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_CLEAR, + self.npcId, + 0, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + return Task.done + + def rejectAvatar(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_REJECT, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.uniqueName('clearMovie')) + + def rejectAvatarTierNotDone(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIER_NOT_DONE, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.uniqueName('clearMovie')) + + def completeQuest(self, avId, questId, rewardId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_COMPLETE, + self.npcId, + avId, + [questId, rewardId, 0], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def incompleteQuest(self, avId, questId, completeStatus, toNpcId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_INCOMPLETE, + self.npcId, + avId, + [questId, completeStatus, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def assignQuest(self, avId, questId, rewardId, toNpcId): + self.busy = avId + if self.questCallback: + self.questCallback() + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_ASSIGN, + self.npcId, + avId, + [questId, rewardId, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def presentQuestChoice(self, avId, quests): + self.busy = avId + self.pendingAvId = avId + self.pendingQuests = quests + flatQuests = [] + for quest in quests: + flatQuests.extend(quest) + + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE, + self.npcId, + avId, + flatQuests, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def presentTrackChoice(self, avId, questId, tracks): + self.busy = avId + self.pendingAvId = avId + self.pendingTracks = tracks + self.pendingTrackQuest = questId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE, + self.npcId, + avId, + tracks, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def cancelChoseQuest(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def cancelChoseTrack(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def setMovieDone(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setMovieDone busy: %s avId: %s' % (self.busy, avId)) + if self.busy == avId: + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + elif self.busy: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCToonAI.setMovieDone busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return diff --git a/toontown/toon/DistributedNPCTailor.py b/toontown/toon/DistributedNPCTailor.py new file mode 100644 index 0000000..34ceab1 --- /dev/null +++ b/toontown/toon/DistributedNPCTailor.py @@ -0,0 +1,291 @@ +from pandac.PandaModules import * +from DistributedNPCToonBase import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import NPCToons +from direct.task.Task import Task +import TailorClothesGUI +from toontown.toonbase import TTLocalizer +import ToonDNA +from toontown.estate import ClosetGlobals + +class DistributedNPCTailor(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.isLocalToon = 0 + self.clothesGUI = None + self.av = None + self.oldStyle = None + self.browsing = 0 + self.roomAvailable = 0 + self.button = None + self.popupInfo = None + return + + def disable(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.clothesGUI: + self.clothesGUI.exit() + self.clothesGUI.unload() + self.clothesGUI = None + if self.button != None: + self.button.destroy() + del self.button + self.cancelButton.destroy() + del self.cancelButton + del self.gui + self.counter.show() + del self.counter + if self.popupInfo: + self.popupInfo.destroy() + self.popupInfo = None + self.av = None + self.oldStyle = None + base.localAvatar.posCamera(0, 0) + DistributedNPCToonBase.disable(self) + return + + def handleCollisionSphereEnter(self, collEntry): + base.cr.playGame.getPlace().fsm.request('purchase') + self.sendUpdate('avatarEnter', []) + + def __handleUnexpectedExit(self): + self.notify.warning('unexpected exit') + self.av = None + self.oldStyle = None + return + + def resetTailor(self): + self.ignoreAll() + taskMgr.remove(self.uniqueName('popupPurchaseGUI')) + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.clothesGUI: + self.clothesGUI.hideButtons() + self.clothesGUI.exit() + self.clothesGUI.unload() + self.clothesGUI = None + if self.button != None: + self.button.destroy() + del self.button + self.cancelButton.destroy() + del self.cancelButton + del self.gui + self.counter.show() + del self.counter + self.show() + self.startLookAround() + self.detectAvatars() + self.clearMat() + if self.isLocalToon: + self.freeAvatar() + return Task.done + + def setMovie(self, mode, npcId, avId, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + self.remain = NPCToons.CLERK_COUNTDOWN_TIME - timeStamp + self.npcId = npcId + self.isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.PURCHASE_MOVIE_CLEAR: + return + if mode == NPCToons.PURCHASE_MOVIE_TIMEOUT: + taskMgr.remove(self.uniqueName('lerpCamera')) + if self.isLocalToon: + self.ignore(self.purchaseDoneEvent) + self.ignore(self.swapEvent) + if self.popupInfo: + self.popupInfo.reparentTo(hidden) + if self.clothesGUI: + self.clothesGUI.resetClothes(self.oldStyle) + self.__handlePurchaseDone(timeout=1) + self.setChatAbsolute(TTLocalizer.STOREOWNER_TOOKTOOLONG, CFSpeech | CFTimeout) + self.resetTailor() + elif mode == NPCToons.PURCHASE_MOVIE_START or mode == NPCToons.PURCHASE_MOVIE_START_BROWSE or mode == NPCToons.PURCHASE_MOVIE_START_NOROOM: + if mode == NPCToons.PURCHASE_MOVIE_START: + self.browsing = 0 + self.roomAvailable = 1 + elif mode == NPCToons.PURCHASE_MOVIE_START_BROWSE: + self.browsing = 1 + self.roomAvailable = 1 + elif mode == NPCToons.PURCHASE_MOVIE_START_NOROOM: + self.browsing = 0 + self.roomAvailable = 0 + self.av = base.cr.doId2do.get(avId) + if self.av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + else: + self.accept(self.av.uniqueName('disable'), self.__handleUnexpectedExit) + style = self.av.getStyle() + self.oldStyle = ToonDNA.ToonDNA() + self.oldStyle.makeFromNetString(style.makeNetString()) + self.setupAvatars(self.av) + if self.isLocalToon: + camera.wrtReparentTo(render) + camera.lerpPosHpr(-5, 9, self.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + if self.browsing == 0: + if self.roomAvailable == 0: + self.setChatAbsolute(TTLocalizer.STOREOWNER_NOROOM, CFSpeech | CFTimeout) + else: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GREETING, CFSpeech | CFTimeout) + else: + self.setChatAbsolute(TTLocalizer.STOREOWNER_BROWSING, CFSpeech | CFTimeout) + if self.isLocalToon: + taskMgr.doMethodLater(3.0, self.popupPurchaseGUI, self.uniqueName('popupPurchaseGUI')) + print '-----------Starting tailor interaction-----------' + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex, + self.av.style.topTexColor, + self.av.style.sleeveTex, + self.av.style.sleeveTexColor, + self.av.style.botTex, + self.av.style.botTexColor) + print 'topsList = %s' % self.av.getClothesTopsList() + print 'bottomsList = %s' % self.av.getClothesBottomsList() + print '-------------------------------------------------' + elif mode == NPCToons.PURCHASE_MOVIE_COMPLETE: + self.setChatAbsolute(TTLocalizer.STOREOWNER_GOODBYE, CFSpeech | CFTimeout) + if self.av and self.isLocalToon: + print '-----------ending tailor interaction-----------' + print 'avid: %s, gender: %s' % (self.av.doId, self.av.style.gender) + print 'current top = %s,%s,%s,%s and bot = %s,%s,' % (self.av.style.topTex, + self.av.style.topTexColor, + self.av.style.sleeveTex, + self.av.style.sleeveTexColor, + self.av.style.botTex, + self.av.style.botTexColor) + print 'topsList = %s' % self.av.getClothesTopsList() + print 'bottomsList = %s' % self.av.getClothesBottomsList() + print '-------------------------------------------------' + self.resetTailor() + elif mode == NPCToons.PURCHASE_MOVIE_NO_MONEY: + self.notify.warning('PURCHASE_MOVIE_NO_MONEY should not be called') + self.resetTailor() + return + + def popupPurchaseGUI(self, task): + self.setChatAbsolute('', CFSpeech) + self.purchaseDoneEvent = 'purchaseDone' + self.swapEvent = 'swap' + self.acceptOnce(self.purchaseDoneEvent, self.__handlePurchaseDone) + self.accept(self.swapEvent, self.__handleSwap) + self.clothesGUI = TailorClothesGUI.TailorClothesGUI(self.purchaseDoneEvent, self.swapEvent, self.npcId) + self.clothesGUI.load() + self.clothesGUI.enter(self.av) + self.clothesGUI.showButtons() + self.gui = loader.loadModel('phase_3/models/gui/create_a_toon_gui') + if self.browsing == 0: + self.button = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn1_UP'), self.gui.find('**/CrtAtoon_Btn1_DOWN'), self.gui.find('**/CrtAtoon_Btn1_RLLVR')), pos=(-0.15, 0, -0.85), command=self.__handleButton, text=('', TTLocalizer.MakeAToonDone, TTLocalizer.MakeAToonDone), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + else: + self.button = None + self.cancelButton = DirectButton(relief=None, image=(self.gui.find('**/CrtAtoon_Btn2_UP'), self.gui.find('**/CrtAtoon_Btn2_DOWN'), self.gui.find('**/CrtAtoon_Btn2_RLLVR')), pos=(0.15, 0, -0.85), command=self.__handleCancel, text=('', TTLocalizer.MakeAToonCancel, TTLocalizer.MakeAToonCancel), text_font=ToontownGlobals.getInterfaceFont(), text_scale=0.08, text_pos=(0, -0.03), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1)) + camera.setPosHpr(base.localAvatar, -4.16, 8.25, 2.47, -152.89, 0.0, 0.0) + self.counter = render.find('**/*mo1_TI_counter') + self.counter.hide() + self.hide() + return Task.done + + def __handleButton(self): + messenger.send('next') + + def __handleCancel(self): + self.clothesGUI.resetClothes(self.oldStyle) + messenger.send('last') + + def __handleSwap(self): + self.d_setDNA(self.av.getStyle().makeNetString(), 0) + + def __handlePurchaseDone(self, timeout = 0): + if self.clothesGUI.doneStatus == 'last' or timeout == 1: + self.d_setDNA(self.oldStyle.makeNetString(), 1) + else: + which = 0 + if self.clothesGUI.topChoice != -1: + which = which | ClosetGlobals.SHIRT + if self.clothesGUI.bottomChoice != -1: + which = which | ClosetGlobals.SHORTS + print 'setDNA: which = %d, top = %d, bot = %d' % (which, self.clothesGUI.topChoice, self.clothesGUI.bottomChoice) + if self.roomAvailable == 0: + if self.isLocalToon: + if self.av.isClosetFull() or which & ClosetGlobals.SHIRT and which & ClosetGlobals.SHORTS: + self.__enterConfirmLoss(2, which) + self.clothesGUI.hideButtons() + self.button.hide() + self.cancelButton.hide() + else: + self.d_setDNA(self.av.getStyle().makeNetString(), 2, which) + else: + self.d_setDNA(self.av.getStyle().makeNetString(), 2, which) + + def __enterConfirmLoss(self, finished, which): + if self.popupInfo == 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.popupInfo = DirectFrame( + parent=hidden, + relief=None, + state='normal', + text=TTLocalizer.STOREOWNER_CONFIRM_LOSS, + text_wordwrap=10, + textMayChange=0, + frameSize=(-1, 1, -1, 1), + text_pos=(0, -0.05), + geom=DGG.getDefaultDialogGeom(), + geom_color=ToontownGlobals.GlobalDialogColor, + geom_scale=(0.88, 1, 0.55), + geom_pos=(0, 0, -.18), + text_scale=0.08) + + DirectButton( + self.popupInfo, + image=okButtonImage, + relief=None, + text=TTLocalizer.STOREOWNER_OK, + text_scale=0.05, + text_pos=(0.0, -0.1), + textMayChange=0, + pos=(-0.08, 0.0, -0.31), + command=self.__handleConfirmLossOK, + extraArgs=[finished, which]) + + DirectButton( + self.popupInfo, + image=cancelButtonImage, + relief=None, + text=TTLocalizer.STOREOWNER_CANCEL, + text_scale=0.05, + text_pos=(0.0, -0.1), + textMayChange=0, + pos=(0.08, 0.0, -0.31), + command=self.__handleConfirmLossCancel) + + buttons.removeNode() + + self.popupInfo.reparentTo(aspect2d) + + def __handleConfirmLossOK(self, finished, which): + self.d_setDNA(self.av.getStyle().makeNetString(), finished, which) + self.popupInfo.reparentTo(hidden) + + def __handleConfirmLossCancel(self): + self.d_setDNA(self.oldStyle.makeNetString(), 1) + self.popupInfo.reparentTo(hidden) + + def d_setDNA(self, dnaString, finished, whichItems = ClosetGlobals.SHIRT | ClosetGlobals.SHORTS): + self.sendUpdate('setDNA', [dnaString, finished, whichItems]) + + def setCustomerDNA(self, avId, dnaString): + if avId != base.localAvatar.doId: + av = base.cr.doId2do.get(avId, None) + if av: + if self.av == av: + oldTorso = self.av.style.torso + self.av.style.makeFromNetString(dnaString) + if len(oldTorso) == 2 and len(self.av.style.torso) == 2 and self.av.style.torso[1] != oldTorso[1]: + self.av.swapToonTorso(self.av.style.torso, genClothes=0) + self.av.loop('neutral', 0) + self.av.generateToonClothes() + return diff --git a/toontown/toon/DistributedNPCTailorAI.py b/toontown/toon/DistributedNPCTailorAI.py new file mode 100644 index 0000000..3a16b6d --- /dev/null +++ b/toontown/toon/DistributedNPCTailorAI.py @@ -0,0 +1,178 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * +import ToonDNA +from direct.task.Task import Task +from toontown.ai import DatabaseObject +from toontown.estate import ClosetGlobals + +class DistributedNPCTailorAI(DistributedNPCToonBaseAI): + freeClothes = simbase.config.GetBool('free-clothes', 0) + housingEnabled = simbase.config.GetBool('want-housing', 1) + + def __init__(self, air, npcId): + DistributedNPCToonBaseAI.__init__(self, air, npcId) + self.timedOut = 0 + self.givesQuests = 0 + self.customerDNA = None + self.customerId = None + return + + def getTailor(self): + return 1 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + self.ignoreAll() + self.customerDNA = None + self.customerId = None + DistributedNPCToonBaseAI.delete(self) + return + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + if not self.air.doId2do.has_key(avId): + self.notify.warning('Avatar: %s not found' % avId) + return + if self.isBusy(): + self.freeAvatar(avId) + return + av = self.air.doId2do[avId] + self.customerDNA = ToonDNA.ToonDNA() + self.customerDNA.makeFromNetString(av.getDNAString()) + self.customerId = avId + av.b_setDNAString(self.customerDNA.makeNetString()) + self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId]) + flag = NPCToons.PURCHASE_MOVIE_START_BROWSE + if self.freeClothes: + flag = NPCToons.PURCHASE_MOVIE_START + if self.housingEnabled and self.isClosetAlmostFull(av): + flag = NPCToons.PURCHASE_MOVIE_START_NOROOM + elif self.air.questManager.hasTailorClothingTicket(av, self) == 1: + flag = NPCToons.PURCHASE_MOVIE_START + if self.housingEnabled and self.isClosetAlmostFull(av): + flag = NPCToons.PURCHASE_MOVIE_START_NOROOM + elif self.air.questManager.hasTailorClothingTicket(av, self) == 2: + flag = NPCToons.PURCHASE_MOVIE_START + if self.housingEnabled and self.isClosetAlmostFull(av): + flag = NPCToons.PURCHASE_MOVIE_START_NOROOM + self.sendShoppingMovie(avId, flag) + DistributedNPCToonBaseAI.avatarEnter(self) + + def isClosetAlmostFull(self, av): + numClothes = len(av.clothesTopsList) / 4 + len(av.clothesBottomsList) / 2 + if numClothes >= av.maxClothes - 1: + return 1 + return 0 + + def sendShoppingMovie(self, avId, flag): + self.busy = avId + self.sendUpdate('setMovie', [flag, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + taskMgr.doMethodLater(NPCToons.TAILOR_COUNTDOWN_TIME, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def rejectAvatar(self, avId): + self.notify.warning('rejectAvatar: should not be called by a Tailor!') + + def sendTimeoutMovie(self, task): + toon = self.air.doId2do.get(self.customerId) + if toon != None and self.customerDNA: + toon.b_setDNAString(self.customerDNA.makeNetString()) + self.timedOut = 1 + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_TIMEOUT, + self.npcId, + self.busy, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return Task.done + + def sendClearMovie(self, task): + self.ignore(self.air.getAvatarExitEvent(self.busy)) + self.customerDNA = None + self.customerId = None + self.busy = 0 + self.timedOut = 0 + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_CLEAR, + self.npcId, + 0, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendUpdate('setCustomerDNA', [0, '']) + return Task.done + + def completePurchase(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.PURCHASE_MOVIE_COMPLETE, + self.npcId, + avId, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + return + + def setDNA(self, blob, finished, which): + avId = self.air.getAvatarIdFromSender() + if avId != self.customerId: + if self.customerId: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA customer is %s' % self.customerId) + self.notify.warning('customerId: %s, but got setDNA for: %s' % (self.customerId, avId)) + return + testDNA = ToonDNA.ToonDNA() + if not testDNA.isValidNetString(blob): + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA: invalid dna: %s' % blob) + return + if self.air.doId2do.has_key(avId): + av = self.air.doId2do[avId] + if finished == 2 and which > 0: + if self.air.questManager.removeClothingTicket(av, self) == 1 or self.freeClothes: + av.b_setDNAString(blob) + if which & ClosetGlobals.SHIRT: + if av.addToClothesTopsList(self.customerDNA.topTex, self.customerDNA.topTexColor, self.customerDNA.sleeveTex, self.customerDNA.sleeveTexColor) == 1: + av.b_setClothesTopsList(av.getClothesTopsList()) + else: + self.notify.warning('NPCTailor: setDNA() - unable to save old tops - we exceeded the tops list length') + if which & ClosetGlobals.SHORTS: + if av.addToClothesBottomsList(self.customerDNA.botTex, self.customerDNA.botTexColor) == 1: + av.b_setClothesBottomsList(av.getClothesBottomsList()) + else: + self.notify.warning('NPCTailor: setDNA() - unable to save old bottoms - we exceeded the bottoms list length') + self.air.writeServerEvent('boughtTailorClothes', avId, '%s|%s|%s' % (self.doId, which, self.customerDNA.asTuple())) + else: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA bogus clothing ticket') + self.notify.warning('NPCTailor: setDNA() - client tried to purchase with bogus clothing ticket!') + if self.customerDNA: + av.b_setDNAString(self.customerDNA.makeNetString()) + elif finished == 1: + if self.customerDNA: + av.b_setDNAString(self.customerDNA.makeNetString()) + else: + self.sendUpdate('setCustomerDNA', [avId, blob]) + else: + self.notify.warning('no av for avId: %d' % avId) + if self.timedOut == 1 or finished == 0: + return + if self.busy == avId: + taskMgr.remove(self.uniqueName('clearMovie')) + self.completePurchase(avId) + elif self.busy: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCTailorAI.setDNA busy with %s' % self.busy) + self.notify.warning('setDNA from unknown avId: %s busy: %s' % (avId, self.busy)) + + def __handleUnexpectedExit(self, avId): + self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly') + if self.customerId == avId: + toon = self.air.doId2do.get(avId) + if toon == None: + toon = DistributedToonAI.DistributedToonAI(self.air) + toon.doId = avId + if self.customerDNA: + toon.b_setDNAString(self.customerDNA.makeNetString()) + db = DatabaseObject.DatabaseObject(self.air, avId) + db.storeObject(toon, ['setDNAString']) + else: + self.notify.warning('invalid customer avId: %s, customerId: %s ' % (avId, self.customerId)) + if self.busy == avId: + self.sendClearMovie(None) + else: + self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy)) + return diff --git a/toontown/toon/DistributedNPCToon.py b/toontown/toon/DistributedNPCToon.py new file mode 100644 index 0000000..4b46b14 --- /dev/null +++ b/toontown/toon/DistributedNPCToon.py @@ -0,0 +1,236 @@ +from pandac.PandaModules import * +from DistributedNPCToonBase import * +from toontown.quest import QuestParser +from toontown.quest import QuestChoiceGui +from toontown.quest import TrackChoiceGui +from toontown.toonbase import TTLocalizer +from toontown.hood import ZoneUtil +from toontown.toontowngui import TeaserPanel +ChoiceTimeout = 20 + +class DistributedNPCToon(DistributedNPCToonBase): + + def __init__(self, cr): + DistributedNPCToonBase.__init__(self, cr) + self.curQuestMovie = None + self.questChoiceGui = None + self.trackChoiceGui = None + return + + def delayDelete(self): + DistributedNPCToonBase.delayDelete(self) + if self.curQuestMovie: + curQuestMovie = self.curQuestMovie + self.curQuestMovie = None + curQuestMovie.timeout(fFinish=1) + curQuestMovie.cleanup() + return + + def disable(self): + self.cleanupMovie() + DistributedNPCToonBase.disable(self) + + def cleanupMovie(self): + self.clearChat() + self.ignore('chooseQuest') + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.ignore(self.uniqueName('doneChatPage')) + if self.curQuestMovie: + self.curQuestMovie.timeout(fFinish=1) + self.curQuestMovie.cleanup() + self.curQuestMovie = None + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + return + + def allowedToTalk(self): + if base.cr.isPaid(): + return True + place = base.cr.playGame.getPlace() + myHoodId = ZoneUtil.getCanonicalHoodId(place.zoneId) + if hasattr(place, 'id'): + myHoodId = place.id + if myHoodId in (ToontownGlobals.ToontownCentral, + ToontownGlobals.MyEstate, + ToontownGlobals.GoofySpeedway, + ToontownGlobals.Tutorial): + return True + return False + + def handleCollisionSphereEnter(self, collEntry): + if self.allowedToTalk(): + base.cr.playGame.getPlace().fsm.request('quest', [self]) + self.sendUpdate('avatarEnter', []) + self.nametag3d.setDepthTest(0) + self.nametag3d.setBin('fixed', 0) + else: + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('stopped') + self.dialog = TeaserPanel.TeaserPanel(pageName='quests', doneFunc=self.handleOkTeaser) + + def handleOkTeaser(self): + self.dialog.destroy() + del self.dialog + place = base.cr.playGame.getPlace() + if place: + place.fsm.request('walk') + + def finishMovie(self, av, isLocalToon, elapsedTime): + self.cleanupMovie() + av.startLookAround() + self.startLookAround() + self.detectAvatars() + self.initPos() + if isLocalToon: + taskMgr.remove(self.uniqueName('lerpCamera')) + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + self.sendUpdate('setMovieDone', []) + self.nametag3d.clearDepthTest() + self.nametag3d.clearBin() + + def setupCamera(self, mode): + camera.wrtReparentTo(render) + if mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE or mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + camera.lerpPosHpr(5, 9, self.getHeight() - 0.5, 155, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + else: + camera.lerpPosHpr(-5, 9, self.getHeight() - 0.5, -150, -2, 0, 1, other=self, blendType='easeOut', task=self.uniqueName('lerpCamera')) + + def setMovie(self, mode, npcId, avId, quests, timestamp): + timeStamp = ClockDelta.globalClockDelta.localElapsedTime(timestamp) + isLocalToon = avId == base.localAvatar.doId + if mode == NPCToons.QUEST_MOVIE_CLEAR: + self.cleanupMovie() + return + if mode == NPCToons.QUEST_MOVIE_TIMEOUT: + self.cleanupMovie() + if isLocalToon: + self.freeAvatar() + self.setPageNumber(0, -1) + self.clearChat() + self.startLookAround() + self.detectAvatars() + return + av = base.cr.doId2do.get(avId) + if av is None: + self.notify.warning('Avatar %d not found in doId' % avId) + return + if mode == NPCToons.QUEST_MOVIE_REJECT: + rejectString = Quests.chooseQuestDialogReject() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + return + if mode == NPCToons.QUEST_MOVIE_TIER_NOT_DONE: + rejectString = Quests.chooseQuestDialogTierNotDone() + rejectString = Quests.fillInQuestNames(rejectString, avName=av.name) + self.setChatAbsolute(rejectString, CFSpeech | CFTimeout) + if isLocalToon: + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + return + self.setupAvatars(av) + fullString = '' + toNpcId = None + if mode == NPCToons.QUEST_MOVIE_COMPLETE: + questId, rewardId, toNpcId = quests + scriptId = 'quest_complete_' + str(questId) + if QuestParser.questDefined(scriptId): + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, Quests.COMPLETE) + '\x07' + if rewardId: + fullString += Quests.getReward(rewardId).getString() + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieQuestChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL: + fullString = TTLocalizer.QuestMovieTrackChoiceCancel + elif mode == NPCToons.QUEST_MOVIE_INCOMPLETE: + questId, completeStatus, toNpcId = quests + scriptId = 'quest_incomplete_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + greetingString = Quests.chooseQuestDialog(questId, Quests.GREETING) + if greetingString: + fullString += greetingString + '\x07' + fullString += Quests.chooseQuestDialog(questId, completeStatus) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_ASSIGN: + questId, rewardId, toNpcId = quests + scriptId = 'quest_assign_' + str(questId) + if QuestParser.questDefined(scriptId): + if self.curQuestMovie: + self.curQuestMovie.timeout() + self.curQuestMovie.cleanup() + self.curQuestMovie = None + self.curQuestMovie = QuestParser.NPCMoviePlayer(scriptId, av, self) + self.curQuestMovie.play() + return + if isLocalToon: + self.setupCamera(mode) + fullString += Quests.chooseQuestDialog(questId, Quests.QUEST) + leavingString = Quests.chooseQuestDialog(questId, Quests.LEAVING) + if leavingString: + fullString += '\x07' + leavingString + elif mode == NPCToons.QUEST_MOVIE_QUEST_CHOICE: + if isLocalToon: + self.setupCamera(mode) + self.setChatAbsolute(TTLocalizer.QuestMovieQuestChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseQuest', self.sendChooseQuest) + self.questChoiceGui = QuestChoiceGui.QuestChoiceGui() + self.questChoiceGui.setQuests(quests, npcId, ChoiceTimeout) + return + elif mode == NPCToons.QUEST_MOVIE_TRACK_CHOICE: + if isLocalToon: + self.setupCamera(mode) + tracks = quests + self.setChatAbsolute(TTLocalizer.QuestMovieTrackChoice, CFSpeech) + if isLocalToon: + self.acceptOnce('chooseTrack', self.sendChooseTrack) + self.trackChoiceGui = TrackChoiceGui.TrackChoiceGui(tracks, ChoiceTimeout) + return + fullString = Quests.fillInQuestNames(fullString, avName=av.name, fromNpcId=npcId, toNpcId=toNpcId) + self.acceptOnce(self.uniqueName('doneChatPage'), self.finishMovie, extraArgs=[av, isLocalToon]) + self.clearChat() + self.setPageChat(avId, 0, fullString, 1) + return + + def sendChooseQuest(self, questId): + if self.questChoiceGui: + self.questChoiceGui.destroy() + self.questChoiceGui = None + self.sendUpdate('chooseQuest', [questId]) + return + + def sendChooseTrack(self, trackId): + if self.trackChoiceGui: + self.trackChoiceGui.destroy() + self.trackChoiceGui = None + self.sendUpdate('chooseTrack', [trackId]) + return diff --git a/toontown/toon/DistributedNPCToonAI.py b/toontown/toon/DistributedNPCToonAI.py new file mode 100644 index 0000000..4956adc --- /dev/null +++ b/toontown/toon/DistributedNPCToonAI.py @@ -0,0 +1,245 @@ +from otp.ai.AIBaseGlobal import * +from direct.task.Task import Task +from pandac.PandaModules import * +from DistributedNPCToonBaseAI import * +from toontown.quest import Quests + +class DistributedNPCToonAI(DistributedNPCToonBaseAI): + FourthGagVelvetRopeBan = config.GetBool('want-ban-fourth-gag-velvet-rope', 0) + + def __init__(self, air, npcId, questCallback = None, hq = 0): + DistributedNPCToonBaseAI.__init__(self, air, npcId, questCallback) + self.hq = hq + self.tutorial = 0 + self.pendingAvId = None + return + + def getTutorial(self): + return self.tutorial + + def setTutorial(self, val): + self.tutorial = val + + def getHq(self): + return self.hq + + def avatarEnter(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('avatar enter ' + str(avId)) + self.air.questManager.requestInteract(avId, self) + DistributedNPCToonBaseAI.avatarEnter(self) + + def chooseQuest(self, questId): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseQuest: avatar %s choseQuest %s' % (avId, questId)) + if not self.pendingAvId: + self.notify.warning('chooseQuest: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseQuest: not expecting an answer from this avatar: %s' % avId) + return + if self.pendingQuests is None: + self.notify.warning('chooseQuest: not expecting a quest choice from this avatar: %s' % avId) + self.air.writeServerEvent('suspicious', avId, 'unexpected chooseQuest') + return + if questId == 0: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarCancelled(avId) + self.cancelChoseQuest(avId) + return + if questId == 401: + av = self.air.getDo(avId) + if not av: + self.notify.warning('chooseQuest: av not present: %s' % avId) + return + if av.getGameAccess() != ToontownGlobals.AccessFull: + simbase.air.writeServerEvent('suspicious', avId, 'NPCToonAI.chooseQuest: non-paid player choosing task beyond velvet rope') + self.sendTimeoutMovie(None) + if self.FourthGagVelvetRopeBan: + av.ban('fourth gag track velvet rope hacking') + return + for quest in self.pendingQuests: + if questId == quest[0]: + self.pendingAvId = None + self.pendingQuests = None + self.air.questManager.avatarChoseQuest(avId, self, *quest) + return + + self.notify.warning('chooseQuest: avatar: %s chose a quest not offered: %s' % (avId, questId)) + self.pendingAvId = None + self.pendingQuests = None + return + + def chooseTrack(self, trackId): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('chooseTrack: avatar %s choseTrack %s' % (avId, trackId)) + if not self.pendingAvId: + self.notify.warning('chooseTrack: not expecting an answer from any avatar: %s' % avId) + return + if self.pendingAvId != avId: + self.notify.warning('chooseTrack: not expecting an answer from this avatar: %s' % avId) + return + if self.pendingTracks is None: + self.notify.warning('chooseTrack: not expecting a track choice from this avatar: %s' % avId) + self.air.writeServerEvent('suspicious', avId, 'unexpected chooseTrack') + return + if trackId == -1: + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.air.questManager.avatarCancelled(avId) + self.cancelChoseTrack(avId) + return + for track in self.pendingTracks: + if trackId == track: + self.air.questManager.avatarChoseTrack(avId, self, self.pendingTrackQuest, trackId) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + self.notify.warning('chooseTrack: avatar: %s chose a track not offered: %s' % (avId, trackId)) + self.pendingAvId = None + self.pendingTracks = None + self.pendingTrackQuest = None + return + + def sendTimeoutMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIMEOUT, + self.npcId, + self.busy, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + self.sendClearMovie(None) + self.busy = 0 + return Task.done + + def sendClearMovie(self, task): + self.pendingAvId = None + self.pendingQuests = None + self.pendingTracks = None + self.pendingTrackQuest = None + self.busy = 0 + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_CLEAR, + self.npcId, + 0, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + return Task.done + + def rejectAvatar(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_REJECT, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.uniqueName('clearMovie')) + + def rejectAvatarTierNotDone(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TIER_NOT_DONE, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(5.5, self.sendClearMovie, self.uniqueName('clearMovie')) + + def completeQuest(self, avId, questId, rewardId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_COMPLETE, + self.npcId, + avId, + [questId, rewardId, 0], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def incompleteQuest(self, avId, questId, completeStatus, toNpcId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_INCOMPLETE, + self.npcId, + avId, + [questId, completeStatus, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def assignQuest(self, avId, questId, rewardId, toNpcId): + self.busy = avId + if self.questCallback: + self.questCallback() + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_ASSIGN, + self.npcId, + avId, + [questId, rewardId, toNpcId], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def presentQuestChoice(self, avId, quests): + self.busy = avId + self.pendingAvId = avId + self.pendingQuests = quests + flatQuests = [] + for quest in quests: + flatQuests.extend(quest) + + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE, + self.npcId, + avId, + flatQuests, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def presentTrackChoice(self, avId, questId, tracks): + self.busy = avId + self.pendingAvId = avId + self.pendingTracks = tracks + self.pendingTrackQuest = questId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE, + self.npcId, + avId, + tracks, + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def cancelChoseQuest(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_QUEST_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def cancelChoseTrack(self, avId): + self.busy = avId + self.sendUpdate('setMovie', [NPCToons.QUEST_MOVIE_TRACK_CHOICE_CANCEL, + self.npcId, + avId, + [], + ClockDelta.globalClockDelta.getRealNetworkTime()]) + if not self.tutorial: + taskMgr.doMethodLater(60.0, self.sendTimeoutMovie, self.uniqueName('clearMovie')) + + def setMovieDone(self): + avId = self.air.getAvatarIdFromSender() + self.notify.debug('setMovieDone busy: %s avId: %s' % (self.busy, avId)) + if self.busy == avId: + taskMgr.remove(self.uniqueName('clearMovie')) + self.sendClearMovie(None) + elif self.busy: + self.air.writeServerEvent('suspicious', avId, 'DistributedNPCToonAI.setMovieDone busy with %s' % self.busy) + self.notify.warning('somebody called setMovieDone that I was not busy with! avId: %s' % avId) + return diff --git a/toontown/toon/DistributedNPCToonBase.py b/toontown/toon/DistributedNPCToonBase.py new file mode 100644 index 0000000..3c82b28 --- /dev/null +++ b/toontown/toon/DistributedNPCToonBase.py @@ -0,0 +1,140 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM +from direct.fsm import State +from toontown.toonbase import ToontownGlobals +import DistributedToon +from direct.distributed import DistributedObject +import NPCToons +from toontown.quest import Quests +from direct.distributed import ClockDelta +from toontown.quest import QuestParser +from toontown.quest import QuestChoiceGui +from direct.interval.IntervalGlobal import * +import random + +class DistributedNPCToonBase(DistributedToon.DistributedToon): + + def __init__(self, cr): + try: + self.DistributedNPCToon_initialized + except: + self.DistributedNPCToon_initialized = 1 + DistributedToon.DistributedToon.__init__(self, cr) + self.__initCollisions() + self.setPickable(0) + self.setPlayerType(NametagGroup.CCNonPlayer) + + def disable(self): + self.ignore('enter' + self.cSphereNode.getName()) + DistributedToon.DistributedToon.disable(self) + + def delete(self): + try: + self.DistributedNPCToon_deleted + except: + self.DistributedNPCToon_deleted = 1 + self.__deleteCollisions() + DistributedToon.DistributedToon.delete(self) + + def generate(self): + DistributedToon.DistributedToon.generate(self) + self.cSphereNode.setName(self.uniqueName('NPCToon')) + self.detectAvatars() + self.setParent(ToontownGlobals.SPRender) + self.startLookAround() + + def generateToon(self): + self.setLODs() + self.generateToonLegs() + self.generateToonHead() + self.generateToonTorso() + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.rightHands = [] + self.leftHands = [] + self.headParts = [] + self.hipsParts = [] + self.torsoParts = [] + self.legsParts = [] + self.__bookActors = [] + self.__holeActors = [] + + def announceGenerate(self): + self.initToonState() + DistributedToon.DistributedToon.announceGenerate(self) + + def initToonState(self): + self.setAnimState('neutral', 0.9, None, None) + npcOrigin = render.find('**/npc_origin_' + `(self.posIndex)`) + if not npcOrigin.isEmpty(): + self.reparentTo(npcOrigin) + self.initPos() + else: + self.notify.warning('announceGenerate: Could not find npc_origin_' + str(self.posIndex)) + return + + def initPos(self): + self.clearMat() + + def wantsSmoothing(self): + return 0 + + def detectAvatars(self): + self.accept('enter' + self.cSphereNode.getName(), self.handleCollisionSphereEnter) + + def ignoreAvatars(self): + self.ignore('enter' + self.cSphereNode.getName()) + + def getCollSphereRadius(self): + return 3.25 + + def __initCollisions(self): + self.cSphere = CollisionTube(0.0, 1.0, 0.0, 0.0, 1.0, 5.0, self.getCollSphereRadius()) + self.cSphere.setTangible(0) + self.cSphereNode = CollisionNode('cSphereNode') + self.cSphereNode.addSolid(self.cSphere) + self.cSphereNodePath = self.attachNewNode(self.cSphereNode) + self.cSphereNodePath.hide() + self.cSphereNode.setCollideMask(ToontownGlobals.WallBitmask) + + def __deleteCollisions(self): + del self.cSphere + del self.cSphereNode + self.cSphereNodePath.removeNode() + del self.cSphereNodePath + + def handleCollisionSphereEnter(self, collEntry): + pass + + def setupAvatars(self, av): + self.ignoreAvatars() + av.headsUp(self, 0, 0, 0) + self.headsUp(av, 0, 0, 0) + av.stopLookAround() + av.lerpLookAt(Point3(-0.5, 4, 0), time=0.5) + self.stopLookAround() + self.lerpLookAt(Point3(av.getPos(self)), time=0.5) + + def b_setPageNumber(self, paragraph, pageNumber): + self.setPageNumber(paragraph, pageNumber) + self.d_setPageNumber(paragraph, pageNumber) + + def d_setPageNumber(self, paragraph, pageNumber): + timestamp = ClockDelta.globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setPageNumber', [paragraph, pageNumber, timestamp]) + + def freeAvatar(self): + base.localAvatar.posCamera(0, 0) + base.cr.playGame.getPlace().setState('walk') + + def setPositionIndex(self, posIndex): + self.posIndex = posIndex + + def _startZombieCheck(self): + pass + + def _stopZombieCheck(self): + pass diff --git a/toontown/toon/DistributedNPCToonBaseAI.py b/toontown/toon/DistributedNPCToonBaseAI.py new file mode 100644 index 0000000..3b6c3f2 --- /dev/null +++ b/toontown/toon/DistributedNPCToonBaseAI.py @@ -0,0 +1,63 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +import DistributedToonAI +from direct.fsm import ClassicFSM +from direct.fsm import State +from direct.distributed import ClockDelta +from toontown.toonbase import ToontownGlobals +import NPCToons +from direct.task import Task +from toontown.quest import Quests + +class DistributedNPCToonBaseAI(DistributedToonAI.DistributedToonAI): + + def __init__(self, air, npcId, questCallback = None): + DistributedToonAI.DistributedToonAI.__init__(self, air) + self.air = air + self.npcId = npcId + self.busy = 0 + self.questCallback = questCallback + self.givesQuests = 1 + + def delete(self): + taskMgr.remove(self.uniqueName('clearMovie')) + DistributedToonAI.DistributedToonAI.delete(self) + + def _doPlayerEnter(self): + pass + + def _doPlayerExit(self): + pass + + def _announceArrival(self): + pass + + def isPlayerControlled(self): + return False + + def getHq(self): + return 0 + + def getTailor(self): + return 0 + + def getGivesQuests(self): + return self.givesQuests + + def avatarEnter(self): + pass + + def isBusy(self): + return self.busy > 0 + + def getNpcId(self): + return self.npcId + + def freeAvatar(self, avId): + self.sendUpdateToAvatarId(avId, 'freeAvatar', []) + + def setPositionIndex(self, posIndex): + self.posIndex = posIndex + + def getPositionIndex(self): + return self.posIndex diff --git a/toontown/toon/DistributedToon.py b/toontown/toon/DistributedToon.py new file mode 100644 index 0000000..09d077c --- /dev/null +++ b/toontown/toon/DistributedToon.py @@ -0,0 +1,2623 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.distributed.ClockDelta import * +from direct.interval.IntervalGlobal import * +from otp.otpbase import OTPGlobals +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from otp.avatar import DistributedPlayer +from otp.avatar import Avatar, DistributedAvatar +from otp.speedchat import SCDecoders +from otp.chat import TalkAssistant +import Toon +from direct.task.Task import Task +from direct.distributed import DistributedSmoothNode +from direct.distributed import DistributedObject +from direct.fsm import ClassicFSM +from toontown.hood import ZoneUtil +from toontown.distributed import DelayDelete +from toontown.distributed.DelayDeletable import DelayDeletable +from direct.showbase import PythonUtil +from toontown.catalog import CatalogItemList +from toontown.catalog import CatalogItem +import TTEmote +from toontown.shtiker.OptionsPage import speedChatStyles +from toontown.fishing import FishCollection +from toontown.fishing import FishTank +from toontown.suit import SuitDNA +from toontown.coghq import CogDisguiseGlobals +from toontown.toonbase import TTLocalizer +import Experience +import InventoryNew +from toontown.speedchat import TTSCDecoders +from toontown.chat import ToonChatGarbler +from toontown.chat import ResistanceChat +from direct.distributed.MsgTypes import * +from toontown.effects.ScavengerHuntEffects import * +from toontown.estate import FlowerCollection +from toontown.estate import FlowerBasket +from toontown.estate import GardenGlobals +from toontown.estate import DistributedGagTree +from toontown.golf import GolfGlobals +from toontown.parties.PartyGlobals import InviteStatus, PartyStatus +from toontown.parties.PartyInfo import PartyInfo +from toontown.parties.InviteInfo import InviteInfo +from toontown.parties.PartyReplyInfo import PartyReplyInfoBase +from toontown.parties.SimpleMailBase import SimpleMailBase +from toontown.parties import PartyGlobals +from toontown.friends import FriendHandle +import time +import operator +from direct.interval.IntervalGlobal import Sequence, Wait, Func, Parallel, SoundInterval +from toontown.distributed import DelayDelete +from otp.otpbase import OTPLocalizer +import random +import copy +if base.wantKarts: + from toontown.racing.KartDNA import * +if (__debug__): + import pdb + +class DistributedToon(DistributedPlayer.DistributedPlayer, Toon.Toon, DistributedSmoothNode.DistributedSmoothNode, DelayDeletable): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToon') + partyNotify = DirectNotifyGlobal.directNotify.newCategory('DistributedToon_Party') + chatGarbler = ToonChatGarbler.ToonChatGarbler() + gmNameTag = None + + def __init__(self, cr, bFake = False): + try: + self.DistributedToon_initialized + return + except: + self.DistributedToon_initialized = 1 + + DistributedPlayer.DistributedPlayer.__init__(self, cr) + Toon.Toon.__init__(self) + DistributedSmoothNode.DistributedSmoothNode.__init__(self, cr) + self.bFake = bFake + self.kart = None + self._isGM = False + self._gmType = None + self.trophyScore = 0 + self.trophyStar = None + self.trophyStarSpeed = 0 + self.safeZonesVisited = [] + self.NPCFriendsDict = {} + self.earnedExperience = None + self.track = None + self.effect = None + self.maxCarry = 0 + self.disguisePageFlag = 0 + self.sosPageFlag = 0 + self.disguisePage = None + self.sosPage = None + self.gardenPage = None + self.cogTypes = [0, + 0, + 0, + 0] + self.cogLevels = [0, + 0, + 0, + 0] + self.cogParts = [0, + 0, + 0, + 0] + self.cogMerits = [0, + 0, + 0, + 0] + self.savedCheesyEffect = CENormal + self.savedCheesyHoodId = 0 + self.savedCheesyExpireTime = 0 + if hasattr(base, 'wantPets') and base.wantPets: + self.petTrickPhrases = [] + self.petDNA = None + self.customMessages = [] + self.resistanceMessages = [] + self.cogSummonsEarned = [] + self.catalogNotify = ToontownGlobals.NoItems + self.mailboxNotify = ToontownGlobals.NoItems + self.simpleMailNotify = ToontownGlobals.NoItems + self.inviteMailNotify = ToontownGlobals.NoItems + self.catalogScheduleCurrentWeek = 0 + self.catalogScheduleNextTime = 0 + self.monthlyCatalog = CatalogItemList.CatalogItemList() + self.weeklyCatalog = CatalogItemList.CatalogItemList() + self.backCatalog = CatalogItemList.CatalogItemList() + self.onOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.onGiftOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.mailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.deliveryboxContentsContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.GiftTag) + self.awardMailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.onAwardOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.splash = None + self.tossTrack = None + self.pieTracks = {} + self.splatTracks = {} + self.lastTossedPie = 0 + self.clothesTopsList = [] + self.clothesBottomsList = [] + self.hatList = [] + self.glassesList = [] + self.backpackList = [] + self.shoesList = [] + self.tunnelTrack = None + self.tunnelPivotPos = [-14, -6, 0] + self.tunnelCenterOffset = 9.0 + self.tunnelCenterInfluence = 0.6 + self.pivotAngle = 90 + 45 + self.posIndex = 0 + self.houseId = 0 + self.money = 0 + self.bankMoney = 0 + self.maxMoney = 0 + self.maxBankMoney = 0 + self.emblems = [0, 0] + self.maxNPCFriends = 16 + self.petId = 0 + self.bPetTutorialDone = False + self.bFishBingoTutorialDone = False + self.bFishBingoMarkTutorialDone = False + self.accessories = [] + if base.wantKarts: + self.kartDNA = [-1] * getNumFields() + self.flowerCollection = None + self.shovel = 0 + self.shovelSkill = 0 + self.shovelModel = None + self.wateringCan = 0 + self.wateringCanSkill = 0 + self.wateringCanModel = None + self.gardenSpecials = [] + self.unlimitedSwing = 0 + self.soundSequenceList = [] + self.boardingParty = None + self.__currentDialogue = None + self.mail = None + self.invites = [] + self.hostedParties = [] + self.partiesInvitedTo = [] + self.partyReplyInfoBases = [] + self.gmState = 0 + self.gmNameTagEnabled = 0 + self.gmNameTagColor = 'whiteGM' + self.gmNameTagString = '' + self._lastZombieContext = None + return + + def disable(self): + for soundSequence in self.soundSequenceList: + soundSequence.finish() + + self.soundSequenceList = [] + self._stopZombieCheck() + if self.boardingParty: + self.boardingParty.demandDrop() + self.boardingParty = None + self.ignore('clientCleanup') + self.stopAnimations() + self.clearCheesyEffect() + self.stopBlink() + self.stopSmooth() + self.stopLookAroundNow() + self.setGhostMode(0) + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if self.effect != None: + self.effect.destroy() + self.effect = None + if self.splash != None: + self.splash.destroy() + self.splash = None + if self.emote != None: + self.emote.finish() + self.emote = None + self.cleanupPies() + if self.isDisguised: + self.takeOffSuit() + if self.tunnelTrack: + self.tunnelTrack.finish() + self.tunnelTrack = None + self.setTrophyScore(0) + self.removeGMIcon() + if self.cr.toons.has_key(self.doId): + del self.cr.toons[self.doId] + DistributedPlayer.DistributedPlayer.disable(self) + return + + def delete(self): + try: + self.DistributedToon_deleted + except: + self.DistributedToon_deleted = 1 + del self.safeZonesVisited + DistributedPlayer.DistributedPlayer.delete(self) + Toon.Toon.delete(self) + DistributedSmoothNode.DistributedSmoothNode.delete(self) + + def generate(self): + DistributedPlayer.DistributedPlayer.generate(self) + DistributedSmoothNode.DistributedSmoothNode.generate(self) + self.cr.toons[self.doId] = self + if base.cr.trophyManager != None: + base.cr.trophyManager.d_requestTrophyScore() + self.startBlink() + self.startSmooth() + self.accept('clientCleanup', self._handleClientCleanup) + return + + def announceGenerate(self): + DistributedPlayer.DistributedPlayer.announceGenerate(self) + if self.animFSM.getCurrentState().getName() == 'off': + self.setAnimState('neutral') + self._startZombieCheck() + + def _handleClientCleanup(self): + if self.track != None: + DelayDelete.cleanupDelayDeletes(self.track) + return + + def setDNAString(self, dnaString): + Toon.Toon.setDNAString(self, dnaString) + + def setDNA(self, dna): + if base.cr.newsManager: + if base.cr.newsManager.isHolidayRunning(ToontownGlobals.SPOOKY_BLACK_CAT): + black = 26 + heads = ['cls', + 'css', + 'csl', + 'cll'] + dna.setTemporary(random.choice(heads), black, black, black) + else: + dna.restoreTemporary(self.style) + oldHat = self.getHat() + oldGlasses = self.getGlasses() + oldBackpack = self.getBackpack() + oldShoes = self.getShoes() + self.setHat(0, 0, 0) + self.setGlasses(0, 0, 0) + self.setBackpack(0, 0, 0) + self.setShoes(0, 0, 0) + Toon.Toon.setDNA(self, dna) + self.setHat(*oldHat) + self.setGlasses(*oldGlasses) + self.setBackpack(*oldBackpack) + self.setShoes(*oldShoes) + + def setHat(self, idx, textureIdx, colorIdx): + Toon.Toon.setHat(self, idx, textureIdx, colorIdx) + + def setGlasses(self, idx, textureIdx, colorIdx): + Toon.Toon.setGlasses(self, idx, textureIdx, colorIdx) + + def setBackpack(self, idx, textureIdx, colorIdx): + Toon.Toon.setBackpack(self, idx, textureIdx, colorIdx) + + def setShoes(self, idx, textureIdx, colorIdx): + Toon.Toon.setShoes(self, idx, textureIdx, colorIdx) + + def setGM(self, type): + wasGM = self._isGM + self._isGM = type != 0 + self._gmType = None + if self._isGM: + self._gmType = type - 1 + if self._isGM != wasGM: + self._handleGMName() + return + + def setExperience(self, experience): + self.experience = Experience.Experience(experience, self) + if self.inventory: + self.inventory.updateGUI() + + def setInventory(self, inventoryNetString): + if not self.inventory: + self.inventory = InventoryNew.InventoryNew(self, inventoryNetString) + self.inventory.updateInvString(inventoryNetString) + + def setLastHood(self, lastHood): + self.lastHood = lastHood + + def setBattleId(self, battleId): + self.battleId = battleId + messenger.send('ToonBattleIdUpdate', [self.doId]) + + def b_setSCToontask(self, taskId, toNpcId, toonProgress, msgIndex): + self.setSCToontask(taskId, toNpcId, toonProgress, msgIndex) + self.d_setSCToontask(taskId, toNpcId, toonProgress, msgIndex) + return None + + def d_setSCToontask(self, taskId, toNpcId, toonProgress, msgIndex): + messenger.send('wakeup') + self.sendUpdate('setSCToontask', [taskId, + toNpcId, + toonProgress, + msgIndex]) + + def setSCToontask(self, taskId, toNpcId, toonProgress, msgIndex): + if self.doId in base.localAvatar.ignoreList: + return + chatString = TTSCDecoders.decodeTTSCToontaskMsg(taskId, toNpcId, toonProgress, msgIndex) + if chatString: + self.setChatAbsolute(chatString, CFSpeech | CFQuicktalker | CFTimeout) + + def b_setSCSinging(self, msgIndex): + self.setSCSinging(msgIndex) + self.d_setSCSinging(msgIndex) + return None + + def d_setSCSinging(self, msgIndex): + messenger.send('wakeup') + self.sendUpdate('setSCSinging', [msgIndex]) + + def sendLogSuspiciousEvent(self, msg): + localAvatar.sendUpdate('logSuspiciousEvent', ['%s for %s' % (msg, self.doId)]) + + def setSCSinging(self, msgIndex): + self.sendUpdate('logSuspiciousEvent', ['invalid msgIndex in setSCSinging: %s from %s' % (msgIndex, self.doId)]) + + def d_reqSCResistance(self, msgIndex): + messenger.send('wakeup') + nearbyPlayers = self.getNearbyPlayers(ResistanceChat.EFFECT_RADIUS) + self.sendUpdate('reqSCResistance', [msgIndex, nearbyPlayers]) + + def getNearbyPlayers(self, radius, includeSelf = True): + nearbyToons = [] + toonIds = self.cr.getObjectsOfExactClass(DistributedToon) + for toonId, toon in toonIds.items(): + if toon is not self: + dist = toon.getDistance(self) + if dist < radius: + nearbyToons.append(toonId) + + if includeSelf: + nearbyToons.append(self.doId) + return nearbyToons + + def setSCResistance(self, msgIndex, nearbyToons = []): + chatString = TTSCDecoders.decodeTTSCResistanceMsg(msgIndex) + if chatString: + self.setChatAbsolute(chatString, CFSpeech | CFTimeout) + ResistanceChat.doEffect(msgIndex, self, nearbyToons) + + def d_battleSOS(self, requesterId, sendToId = None): + self.sendUpdate('battleSOS', [requesterId], sendToId) + + def battleSOS(self, requesterId): + avatar = base.cr.identifyAvatar(requesterId) + if isinstance(avatar, DistributedToon) or isinstance(avatar, FriendHandle.FriendHandle): + self.setSystemMessage(requesterId, TTLocalizer.MovieSOSWhisperHelp % avatar.getName(), whisperType=WhisperPopup.WTBattleSOS) + elif avatar is not None: + self.notify.warning('got battleSOS from non-toon %s' % requesterId) + return + + def getDialogueArray(self, *args): + return Toon.Toon.getDialogueArray(self, *args) + + def setDefaultShard(self, shard): + self.defaultShard = shard + + def setDefaultZone(self, zoneId): + if zoneId >= 20000 and zoneId < 22000: + zoneId = zoneId + 2000 + try: + hoodPhase = base.cr.hoodMgr.getPhaseFromHood(zoneId) + except: + self.defaultZone = ToontownCentral + return + + if ZoneUtil.getCanonicalHoodId(zoneId) == FunnyFarm: + self.defaultZone = ToontownCentral + return + if not base.cr.isPaid() or launcher and not launcher.getPhaseComplete(hoodPhase): + self.defaultZone = ToontownCentral + else: + self.defaultZone = zoneId + + def setShtickerBook(self, string): + pass + + def setAsGM(self, state): + self.notify.debug('Setting GM State: %s' % state) + DistributedPlayer.DistributedPlayer.setAsGM(self, state) + + def d_updateGMNameTag(self): + self.refreshName() + + def updateGMNameTag(self, tagString, color, state): + try: + unicode(tagString, 'utf-8') + except UnicodeDecodeError: + self.sendUpdate('logSuspiciousEvent', ['invalid GM name tag: %s from %s' % (tagString, self.doId)]) + return + + def refreshName(self): + return + self.notify.debug('Refreshing GM Nametag String: %s Color: %s State: %s' % (self.gmNameTagString, self.gmNameTagColor, self.gmNameTagEnabled)) + if hasattr(self, 'nametag') and self.gmNameTagEnabled: + self.setDisplayName(self.gmNameTagString) + self.setName(self.gmNameTagString) + self.trophyStar1 = loader.loadModel('models/misc/smiley') + self.trophyStar1.reparentTo(self.nametag.getNameIcon()) + self.trophyStar1.setScale(1) + self.trophyStar1.setZ(2.25) + self.trophyStar1.setColor(Vec4(0.75, 0.75, 0.75, 0.75)) + self.trophyStar1.setTransparency(1) + self.trophyStarSpeed = 15 + else: + taskMgr.add(self.__refreshNameCallBack, self.uniqueName('refreshNameCallBack')) + + def __starSpin1(self, task): + now = globalClock.getFrameTime() + r = now * 90 % 360.0 + self.trophyStar1.setH(r) + return Task.cont + + def __refreshNameCallBack(self, task): + if hasattr(self, 'nametag') and self.nametag.getName() != '': + self.refreshName() + return Task.done + else: + return Task.cont + + def setTalk(self, fromAV, fromAC, avatarName, chat, mods, flags): + if base.cr.avatarFriendsManager.checkIgnored(fromAV): + self.d_setWhisperIgnored(fromAV) + return + if fromAV in self.ignoreList: + self.d_setWhisperIgnored(fromAV) + return + if base.config.GetBool('want-sleep-reply-on-regular-chat', 0): + if base.localAvatar.sleepFlag == 1: + self.sendUpdate('setSleepAutoReply', [base.localAvatar.doId], fromAV) + newText, scrubbed = self.scrubTalk(chat, mods) + self.displayTalk(newText) + base.talkAssistant.receiveOpenTalk(fromAV, avatarName, fromAC, None, newText) + return + + def isAvFriend(self, avId): + return base.cr.isFriend(avId) or base.cr.playerFriendsManager.isAvatarOwnerPlayerFriend(avId) + + def setTalkWhisper(self, fromAV, fromAC, avatarName, chat, mods, flags): + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromAV): + return + if base.cr.avatarFriendsManager.checkIgnored(fromAV): + self.d_setWhisperIgnored(fromAV) + return + if fromAV in self.ignoreList: + self.d_setWhisperIgnored(fromAV) + return + if base.config.GetBool('ignore-whispers', 0): + return + if base.localAvatar.sleepFlag == 1: + if not base.cr.identifyAvatar(fromAV) == base.localAvatar: + self.sendUpdate('setSleepAutoReply', [base.localAvatar.doId], fromAV) + newText, scrubbed = self.scrubTalk(chat, mods) + self.displayTalkWhisper(fromAV, avatarName, chat, mods) + base.talkAssistant.receiveWhisperTalk(fromAV, avatarName, fromAC, None, self.doId, self.getName(), newText) + return + + def setSleepAutoReply(self, fromId): + pass + + def _isValidWhisperSource(self, source): + return isinstance(source, FriendHandle.FriendHandle) or isinstance(source, DistributedToon) + + def setWhisperSCEmoteFrom(self, fromId, emoteId): + handle = base.cr.identifyFriend(fromId) + if handle == None: + return + if not self._isValidWhisperSource(handle): + self.notify.warning('setWhisperSCEmoteFrom non-toon %s' % fromId) + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + if base.cr.avatarFriendsManager.checkIgnored(fromId): + self.d_setWhisperIgnored(fromId) + return + if base.localAvatar.sleepFlag == 1: + if not base.cr.identifyAvatar(fromId) == base.localAvatar: + self.sendUpdate('setSleepAutoReply', [base.localAvatar.doId], fromId) + chatString = SCDecoders.decodeSCEmoteWhisperMsg(emoteId, handle.getName()) + if chatString: + self.displayWhisper(fromId, chatString, WhisperPopup.WTEmote) + base.talkAssistant.receiveAvatarWhisperSpeedChat(TalkAssistant.SPEEDCHAT_EMOTE, emoteId, fromId) + return + + def setWhisperSCFrom(self, fromId, msgIndex): + handle = base.cr.identifyFriend(fromId) + if handle == None: + return + if not self._isValidWhisperSource(handle): + self.notify.warning('setWhisperSCFrom non-toon %s' % fromId) + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + if base.cr.avatarFriendsManager.checkIgnored(fromId): + self.d_setWhisperIgnored(fromId) + return + if fromId in self.ignoreList: + self.d_setWhisperIgnored(fromId) + return + if base.localAvatar.sleepFlag == 1: + if not base.cr.identifyAvatar(fromId) == base.localAvatar: + self.sendUpdate('setSleepAutoReply', [base.localAvatar.doId], fromId) + chatString = SCDecoders.decodeSCStaticTextMsg(msgIndex) + if chatString: + self.displayWhisper(fromId, chatString, WhisperPopup.WTQuickTalker) + base.talkAssistant.receiveAvatarWhisperSpeedChat(TalkAssistant.SPEEDCHAT_NORMAL, msgIndex, fromId) + return + + def setWhisperSCCustomFrom(self, fromId, msgIndex): + handle = base.cr.identifyFriend(fromId) + if handle == None: + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + return DistributedPlayer.DistributedPlayer.setWhisperSCCustomFrom(self, fromId, msgIndex) + + def whisperSCToontaskTo(self, taskId, toNpcId, toonProgress, msgIndex, sendToId): + messenger.send('wakeup') + self.sendUpdate('setWhisperSCToontaskFrom', [self.doId, + taskId, + toNpcId, + toonProgress, + msgIndex], sendToId) + + def setWhisperSCToontaskFrom(self, fromId, taskId, toNpcId, toonProgress, msgIndex): + sender = base.cr.identifyFriend(fromId) + if sender == None: + return + if not localAvatar.acceptingNonFriendWhispers: + if not self.isAvFriend(fromId): + return + if fromId in self.ignoreList: + self.d_setWhisperIgnored(fromId) + chatString = TTSCDecoders.decodeTTSCToontaskMsg(taskId, toNpcId, toonProgress, msgIndex) + if chatString: + self.displayWhisper(fromId, chatString, WhisperPopup.WTQuickTalker) + return + + def setMaxNPCFriends(self, max): + max &= 32767 + if max != self.maxNPCFriends: + self.maxNPCFriends = max + messenger.send(self.uniqueName('maxNPCFriendsChange')) + else: + self.maxNPCFriends = max + + def getMaxNPCFriends(self): + return self.maxNPCFriends + + def getNPCFriendsDict(self): + return self.NPCFriendsDict + + def setNPCFriendsDict(self, NPCFriendsList): + NPCFriendsDict = {} + for friendPair in NPCFriendsList: + NPCFriendsDict[friendPair[0]] = friendPair[1] + + self.NPCFriendsDict = NPCFriendsDict + + def setMaxAccessories(self, max): + self.maxAccessories = max + + def getMaxAccessories(self): + return self.maxAccessories + + def setHatList(self, clothesList): + self.hatList = clothesList + + def getHatList(self): + return self.hatList + + def setGlassesList(self, clothesList): + self.glassesList = clothesList + + def getGlassesList(self): + return self.glassesList + + def setBackpackList(self, clothesList): + self.backpackList = clothesList + + def getBackpackList(self): + return self.backpackList + + def setShoesList(self, clothesList): + self.shoesList = clothesList + + def getShoesList(self): + return self.shoesList + + def isTrunkFull(self, extraAccessories = 0): + numAccessories = (len(self.hatList) + len(self.glassesList) + len(self.backpackList) + len(self.shoesList)) / 3 + return numAccessories + extraAccessories >= self.maxAccessories + + def setMaxClothes(self, max): + self.maxClothes = max + + def getMaxClothes(self): + return self.maxClothes + + def getClothesTopsList(self): + return self.clothesTopsList + + def setClothesTopsList(self, clothesList): + self.clothesTopsList = clothesList + + def getClothesBottomsList(self): + return self.clothesBottomsList + + def setClothesBottomsList(self, clothesList): + self.clothesBottomsList = clothesList + + def catalogGenClothes(self, avId): + if avId == self.doId: + self.generateToonClothes() + self.loop('neutral') + + def catalogGenAccessories(self, avId): + if avId == self.doId: + self.generateToonAccessories() + self.loop('neutral') + + def isClosetFull(self, extraClothes = 0): + numClothes = len(self.clothesTopsList) / 4 + len(self.clothesBottomsList) / 2 + return numClothes + extraClothes >= self.maxClothes + + def setMaxHp(self, hitPoints): + DistributedPlayer.DistributedPlayer.setMaxHp(self, hitPoints) + if self.inventory: + self.inventory.updateGUI() + + def died(self): + messenger.send(self.uniqueName('died')) + if self.isLocal(): + target_sz = ZoneUtil.getSafeZoneId(self.defaultZone) + place = self.cr.playGame.getPlace() + if place and place.fsm: + place.fsm.request('died', [{'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 setInterface(self, string): + pass + + def setZonesVisited(self, hoods): + self.safeZonesVisited = hoods + + def setHoodsVisited(self, hoods): + self.hoodsVisited = hoods + if ToontownGlobals.SellbotHQ in hoods or ToontownGlobals.CashbotHQ in hoods or ToontownGlobals.LawbotHQ in hoods: + self.setDisguisePageFlag(1) + + def wrtReparentTo(self, parent): + DistributedSmoothNode.DistributedSmoothNode.wrtReparentTo(self, parent) + + def setTutorialAck(self, tutorialAck): + self.tutorialAck = tutorialAck + + def setEarnedExperience(self, earnedExp): + self.earnedExperience = earnedExp + + def b_setTunnelIn(self, endX, tunnelOrigin): + timestamp = globalClockDelta.getFrameNetworkTime() + pos = tunnelOrigin.getPos(render) + h = tunnelOrigin.getH(render) + self.setTunnelIn(timestamp, endX, pos[0], pos[1], pos[2], h) + self.d_setTunnelIn(timestamp, endX, pos[0], pos[1], pos[2], h) + + def d_setTunnelIn(self, timestamp, endX, x, y, z, h): + self.sendUpdate('setTunnelIn', [timestamp, + endX, + x, + y, + z, + h]) + + def setTunnelIn(self, timestamp, endX, x, y, z, h): + t = globalClockDelta.networkToLocalTime(timestamp) + self.handleTunnelIn(t, endX, x, y, z, h) + + def getTunnelInToonTrack(self, endX, tunnelOrigin): + pivotNode = tunnelOrigin.attachNewNode(self.uniqueName('pivotNode')) + pivotNode.setPos(*self.tunnelPivotPos) + pivotNode.setHpr(0, 0, 0) + pivotY = pivotNode.getY(tunnelOrigin) + endY = 5.0 + straightLerpDur = abs(endY - pivotY) / ToonForwardSpeed + pivotDur = 2.0 + pivotLerpDur = pivotDur * (90.0 / self.pivotAngle) + self.reparentTo(pivotNode) + self.setPos(0, 0, 0) + self.setX(tunnelOrigin, endX) + targetX = self.getX() + self.setX(self.tunnelCenterOffset + (targetX - self.tunnelCenterOffset) * (1.0 - self.tunnelCenterInfluence)) + self.setHpr(tunnelOrigin, 0, 0, 0) + pivotNode.setH(-self.pivotAngle) + return Sequence(Wait(0.8), Parallel(LerpHprInterval(pivotNode, pivotDur, hpr=Point3(0, 0, 0), name=self.uniqueName('tunnelInPivot')), Sequence(Wait(pivotDur - pivotLerpDur), LerpPosInterval(self, pivotLerpDur, pos=Point3(targetX, 0, 0), name=self.uniqueName('tunnelInPivotLerpPos')))), Func(self.wrtReparentTo, render), Func(pivotNode.removeNode), LerpPosInterval(self, straightLerpDur, pos=Point3(endX, endY, 0.1), other=tunnelOrigin, name=self.uniqueName('tunnelInStraightLerp'))) + + def handleTunnelIn(self, startTime, endX, x, y, z, h): + self.stopSmooth() + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + self.tunnelTrack = Sequence(self.getTunnelInToonTrack(endX, tunnelOrigin), Func(tunnelOrigin.removeNode), Func(self.startSmooth)) + tOffset = globalClock.getFrameTime() - (startTime + self.smoother.getDelay()) + if tOffset < 0.0: + self.tunnelTrack = Sequence(Wait(-tOffset), self.tunnelTrack) + self.tunnelTrack.start() + else: + self.tunnelTrack.start(tOffset) + + def b_setTunnelOut(self, startX, startY, tunnelOrigin): + timestamp = globalClockDelta.getFrameNetworkTime() + pos = tunnelOrigin.getPos(render) + h = tunnelOrigin.getH(render) + self.setTunnelOut(timestamp, startX, startY, pos[0], pos[1], pos[2], h) + self.d_setTunnelOut(timestamp, startX, startY, pos[0], pos[1], pos[2], h) + + def d_setTunnelOut(self, timestamp, startX, startY, x, y, z, h): + self.sendUpdate('setTunnelOut', [timestamp, + startX, + startY, + x, + y, + z, + h]) + + def setTunnelOut(self, timestamp, startX, startY, x, y, z, h): + t = globalClockDelta.networkToLocalTime(timestamp) + self.handleTunnelOut(t, startX, startY, x, y, z, h) + + def getTunnelOutToonTrack(self, startX, startY, tunnelOrigin): + startPos = self.getPos(tunnelOrigin) + startHpr = self.getHpr(tunnelOrigin) + reducedAvH = PythonUtil.fitDestAngle2Src(startHpr[0], 180) + pivotNode = tunnelOrigin.attachNewNode(self.uniqueName('pivotNode')) + pivotNode.setPos(*self.tunnelPivotPos) + pivotNode.setHpr(0, 0, 0) + pivotY = pivotNode.getY(tunnelOrigin) + straightLerpDur = abs(startY - pivotY) / ToonForwardSpeed + pivotDur = 2.0 + pivotLerpDur = pivotDur * (90.0 / self.pivotAngle) + + def getTargetPos(self = self): + pos = self.getPos() + return Point3(self.tunnelCenterOffset + (pos[0] - self.tunnelCenterOffset) * (1.0 - self.tunnelCenterInfluence), pos[1], pos[2]) + + return Sequence(Parallel(LerpPosInterval(self, straightLerpDur, pos=Point3(startX, pivotY, 0.1), startPos=startPos, other=tunnelOrigin, name=self.uniqueName('tunnelOutStraightLerp')), LerpHprInterval(self, straightLerpDur * 0.8, hpr=Point3(reducedAvH, 0, 0), startHpr=startHpr, other=tunnelOrigin, name=self.uniqueName('tunnelOutStraightLerpHpr'))), Func(self.wrtReparentTo, pivotNode), Parallel(LerpHprInterval(pivotNode, pivotDur, hpr=Point3(-self.pivotAngle, 0, 0), name=self.uniqueName('tunnelOutPivot')), LerpPosInterval(self, pivotLerpDur, pos=getTargetPos, name=self.uniqueName('tunnelOutPivotLerpPos'))), Func(self.wrtReparentTo, render), Func(pivotNode.removeNode)) + + def handleTunnelOut(self, startTime, startX, startY, x, y, z, h): + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + self.tunnelTrack = Sequence(Func(self.stopSmooth), self.getTunnelOutToonTrack(startX, startY, tunnelOrigin), Func(self.detachNode), Func(tunnelOrigin.removeNode)) + tOffset = globalClock.getFrameTime() - (startTime + self.smoother.getDelay()) + if tOffset < 0.0: + self.tunnelTrack = Sequence(Wait(-tOffset), self.tunnelTrack) + self.tunnelTrack.start() + else: + self.tunnelTrack.start(tOffset) + + def enterTeleportOut(self, *args, **kw): + Toon.Toon.enterTeleportOut(self, *args, **kw) + if self.track: + self.track.delayDelete = DelayDelete.DelayDelete(self, 'enterTeleportOut') + + def exitTeleportOut(self): + if self.track != None: + DelayDelete.cleanupDelayDeletes(self.track) + Toon.Toon.exitTeleportOut(self) + return + + def b_setAnimState(self, animName, animMultiplier = 1.0, callback = None, extraArgs = []): + self.d_setAnimState(animName, animMultiplier, None, extraArgs) + self.setAnimState(animName, animMultiplier, None, None, callback, extraArgs) + return + + def d_setAnimState(self, animName, animMultiplier = 1.0, timestamp = None, extraArgs = []): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setAnimState', [animName, animMultiplier, timestamp]) + + def setAnimState(self, animName, animMultiplier = 1.0, timestamp = None, animType = None, callback = None, extraArgs = []): + if not animName or animName == 'None': + return + if timestamp == None: + ts = 0.0 + else: + ts = globalClockDelta.localElapsedTime(timestamp) + if base.config.GetBool('check-invalid-anims', True): + if animMultiplier > 1.0 and animName in ['neutral']: + animMultiplier = 1.0 + if self.animFSM.getStateNamed(animName): + self.animFSM.request(animName, [animMultiplier, + ts, + callback, + extraArgs]) + self.cleanupPieInHand() + return + + def b_setEmoteState(self, animIndex, animMultiplier): + self.setEmoteState(animIndex, animMultiplier) + self.d_setEmoteState(animIndex, animMultiplier) + + def d_setEmoteState(self, animIndex, animMultiplier): + timestamp = globalClockDelta.getFrameNetworkTime() + self.sendUpdate('setEmoteState', [animIndex, animMultiplier, timestamp]) + + def setEmoteState(self, animIndex, animMultiplier, timestamp = None): + if animIndex == TTEmote.EmoteClear: + return + if timestamp == None: + ts = 0.0 + else: + ts = globalClockDelta.localElapsedTime(timestamp) + callback = None + extraArgs = [] + extraArgs.insert(0, animIndex) + self.doEmote(animIndex, animMultiplier, ts, callback, extraArgs) + return + + def setCogStatus(self, cogStatusList): + self.cogs = cogStatusList + + def setCogCount(self, cogCountList): + self.cogCounts = cogCountList + if hasattr(self, 'suitPage'): + self.suitPage.updatePage() + + def setCogRadar(self, radar): + self.cogRadar = radar + if hasattr(self, 'suitPage'): + self.suitPage.updateCogRadarButtons(radar) + + def setBuildingRadar(self, radar): + self.buildingRadar = radar + if hasattr(self, 'suitPage'): + self.suitPage.updateBuildingRadarButtons(radar) + + def setCogTypes(self, types): + self.cogTypes = types + if self.disguisePage: + self.disguisePage.updatePage() + + def setCogLevels(self, levels): + self.cogLevels = levels + if self.disguisePage: + self.disguisePage.updatePage() + + def getCogLevels(self): + return self.cogLevels + + def setCogParts(self, parts): + self.cogParts = parts + if self.disguisePage: + self.disguisePage.updatePage() + + def getCogParts(self): + return self.cogParts + + def setCogMerits(self, merits): + self.cogMerits = merits + if self.disguisePage: + self.disguisePage.updatePage() + + def readyForPromotion(self, dept): + merits = base.localAvatar.cogMerits[dept] + totalMerits = CogDisguiseGlobals.getTotalMerits(self, dept) + if merits >= totalMerits: + return 1 + else: + return 0 + + def setCogIndex(self, index): + self.cogIndex = index + if self.cogIndex == -1: + if self.isDisguised: + self.takeOffSuit() + else: + parts = self.getCogParts() + if CogDisguiseGlobals.isPaidSuitComplete(self, parts, index): + cogIndex = self.cogTypes[index] + SuitDNA.suitsPerDept * index + cog = SuitDNA.suitHeadTypes[cogIndex] + self.putOnSuit(cog) + else: + self.putOnSuit(index, rental=True) + + def isCog(self): + if self.cogIndex == -1: + return 0 + else: + return 1 + + def setDisguisePageFlag(self, flag): + if flag and hasattr(self, 'book'): + self.loadDisguisePages() + self.disguisePageFlag = flag + + def setSosPageFlag(self, flag): + if flag and hasattr(self, 'book'): + self.loadSosPages() + self.sosPageFlag = flag + + def setFishCollection(self, genusList, speciesList, weightList): + self.fishCollection = FishCollection.FishCollection() + self.fishCollection.makeFromNetLists(genusList, speciesList, weightList) + + def getFishCollection(self): + return self.fishCollection + + def setMaxFishTank(self, maxTank): + self.maxFishTank = maxTank + + def getMaxFishTank(self): + return self.maxFishTank + + def setFishTank(self, genusList, speciesList, weightList): + self.fishTank = FishTank.FishTank() + self.fishTank.makeFromNetLists(genusList, speciesList, weightList) + messenger.send(self.uniqueName('fishTankChange')) + + def getFishTank(self): + return self.fishTank + + def isFishTankFull(self): + return len(self.fishTank) >= self.maxFishTank + + def setFishingRod(self, rodId): + self.fishingRod = rodId + + def getFishingRod(self): + return self.fishingRod + + def setFishingTrophies(self, trophyList): + self.fishingTrophies = trophyList + + def getFishingTrophies(self): + return self.fishingTrophies + + def setQuests(self, flattenedQuests): + questList = [] + questLen = 5 + for i in range(0, len(flattenedQuests), questLen): + questList.append(flattenedQuests[i:i + questLen]) + + self.quests = questList + if self == base.localAvatar: + messenger.send('questsChanged') + + def setQuestCarryLimit(self, limit): + self.questCarryLimit = limit + if self == base.localAvatar: + messenger.send('questsChanged') + + def getQuestCarryLimit(self): + return self.questCarryLimit + + def d_requestDeleteQuest(self, questDesc): + self.sendUpdate('requestDeleteQuest', [list(questDesc)]) + + def setMaxCarry(self, maxCarry): + self.maxCarry = maxCarry + if self.inventory: + self.inventory.updateGUI() + + def getMaxCarry(self): + return self.maxCarry + + def setCheesyEffect(self, effect, hoodId, expireTime): + self.savedCheesyEffect = effect + self.savedCheesyHoodId = hoodId + self.savedCheesyExpireTime = expireTime + if self == base.localAvatar: + self.notify.debug('setCheesyEffect(%s, %s, %s)' % (effect, hoodId, expireTime)) + if effect != ToontownGlobals.CENormal: + serverTime = time.time() + self.cr.getServerDelta() + duration = expireTime * 60 - serverTime + if duration < 0: + self.notify.debug('effect should have expired %s ago.' % PythonUtil.formatElapsedSeconds(-duration)) + else: + self.notify.debug('effect will expire in %s.' % PythonUtil.formatElapsedSeconds(duration)) + if self.activeState == DistributedObject.ESGenerated: + self.reconsiderCheesyEffect(lerpTime=0.5) + else: + self.reconsiderCheesyEffect() + + def reconsiderCheesyEffect(self, lerpTime = 0): + effect = self.savedCheesyEffect + hoodId = self.savedCheesyHoodId + if not self.cr.areCheesyEffectsAllowed(): + effect = CENormal + if hoodId != 0: + try: + currentHoodId = base.cr.playGame.hood.id + except: + currentHoodId = None + + if hoodId == 1: + if currentHoodId == ToontownGlobals.ToontownCentral: + effect = CENormal + elif currentHoodId != None and currentHoodId != hoodId: + effect = CENormal + if self.ghostMode: + effect = CEGhost + self.applyCheesyEffect(effect, lerpTime=lerpTime) + return + + def setGhostMode(self, flag): + if self.ghostMode != flag: + self.ghostMode = flag + if not hasattr(self, 'cr'): + return + if self.activeState <= DistributedObject.ESDisabled: + self.notify.debug('not applying cheesy effect to disabled Toon') + elif self.activeState == DistributedObject.ESGenerating: + self.reconsiderCheesyEffect() + elif self.activeState == DistributedObject.ESGenerated: + self.reconsiderCheesyEffect(lerpTime=0.5) + else: + self.notify.warning('unknown activeState: %s' % self.activeState) + self.showNametag2d() + self.showNametag3d() + if hasattr(self, 'collNode'): + if self.ghostMode: + self.collNode.setCollideMask(ToontownGlobals.GhostBitmask) + else: + self.collNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.PieBitmask) + if self.isLocal(): + if self.ghostMode: + self.useGhostControls() + else: + self.useWalkControls() + + if hasattr(base, 'wantPets') and base.wantPets: + + def setPetTrickPhrases(self, petTricks): + self.petTrickPhrases = petTricks + if self.isLocal(): + messenger.send('petTrickPhrasesChanged') + + def setCustomMessages(self, customMessages): + self.customMessages = customMessages + if self.isLocal(): + messenger.send('customMessagesChanged') + + def setResistanceMessages(self, resistanceMessages): + self.resistanceMessages = resistanceMessages + if self.isLocal(): + messenger.send('resistanceMessagesChanged') + + def getResistanceMessageCharges(self, textId): + msgs = self.resistanceMessages + for i in range(len(msgs)): + if msgs[i][0] == textId: + return msgs[i][1] + + return 0 + + def setCatalogSchedule(self, currentWeek, nextTime): + self.catalogScheduleCurrentWeek = currentWeek + self.catalogScheduleNextTime = nextTime + if self.isLocal(): + self.notify.debug('setCatalogSchedule(%s, %s)' % (currentWeek, nextTime)) + if nextTime: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next catalog in %s.' % PythonUtil.formatElapsedSeconds(duration)) + + def setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.monthlyCatalog = CatalogItemList.CatalogItemList(monthlyCatalog) + self.weeklyCatalog = CatalogItemList.CatalogItemList(weeklyCatalog) + self.backCatalog = CatalogItemList.CatalogItemList(backCatalog) + if self.catalogNotify == ToontownGlobals.NewItems: + self.catalogNotify = ToontownGlobals.OldItems + + def setCatalogNotify(self, catalogNotify, mailboxNotify): + if len(self.weeklyCatalog) + len(self.monthlyCatalog) == 0: + catalogNotify = ToontownGlobals.NoItems + if len(self.mailboxContents) == 0: + mailboxNotify = ToontownGlobals.NoItems + self.catalogNotify = catalogNotify + self.mailboxNotify = mailboxNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + print 'local' + + def setDeliverySchedule(self, onOrder): + self.onOrder = CatalogItemList.CatalogItemList(onOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if self == base.localAvatar: + nextTime = self.onOrder.getNextDeliveryDate() + if nextTime != None: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next delivery in %s.' % PythonUtil.formatElapsedSeconds(duration)) + messenger.send('setDeliverySchedule-%s' % self.doId) + return + + def setMailboxContents(self, mailboxContents): + self.mailboxContents = CatalogItemList.CatalogItemList(mailboxContents, store=CatalogItem.Customization) + messenger.send('setMailboxContents-%s' % self.doId) + + def setAwardSchedule(self, onOrder): + self.onAwardOrder = CatalogItemList.CatalogItemList(onOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if self == base.localAvatar: + nextTime = self.onAwardOrder.getNextDeliveryDate() + if nextTime != None: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next delivery in %s.' % PythonUtil.formatElapsedSeconds(duration)) + messenger.send('setAwardSchedule-%s' % self.doId) + return + + def setAwardMailboxContents(self, awardMailboxContents): + self.notify.debug('Setting awardMailboxContents to %s.' % awardMailboxContents) + self.awardMailboxContents = CatalogItemList.CatalogItemList(awardMailboxContents, store=CatalogItem.Customization) + self.notify.debug('awardMailboxContents is %s.' % self.awardMailboxContents) + messenger.send('setAwardMailboxContents-%s' % self.doId) + + def setAwardNotify(self, awardNotify): + self.notify.debug('setAwardNotify( %s )' % awardNotify) + self.awardNotify = awardNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + + def setGiftSchedule(self, onGiftOrder): + self.onGiftOrder = CatalogItemList.CatalogItemList(onGiftOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if self == base.localAvatar: + nextTime = self.onGiftOrder.getNextDeliveryDate() + if nextTime != None: + serverTime = time.time() + self.cr.getServerDelta() + duration = nextTime * 60 - serverTime + self.notify.debug('next delivery in %s.' % PythonUtil.formatElapsedSeconds(duration)) + return + + def playSplashEffect(self, x, y, z): + if localAvatar.zoneId not in [ToontownGlobals.DonaldsDock, ToontownGlobals.OutdoorZone] and (not hasattr(localAvatar, 'inEstate') or localAvatar.inEstate != 1): + if random.random() < 0.1: + self.sendLogSuspiciousEvent('AvatarHackWarning! playing hacked splash effect') + return + from toontown.effects import Splash + if self.splash == None: + self.splash = Splash.Splash(render) + self.splash.setPos(x, y, z) + self.splash.setScale(2) + self.splash.play() + place = base.cr.playGame.getPlace() + if place: + if hasattr(place.loader, 'submergeSound'): + base.playSfx(place.loader.submergeSound, node=self) + return + + def d_playSplashEffect(self, x, y, z): + self.sendUpdate('playSplashEffect', [x, y, z]) + + def setTrackAccess(self, trackArray): + self.trackArray = trackArray + if self.inventory: + self.inventory.updateGUI() + + def getTrackAccess(self): + return self.trackArray + + def hasTrackAccess(self, track): + return self.trackArray[track] + + def setTrackProgress(self, trackId, progress): + self.trackProgressId = trackId + self.trackProgress = progress + if hasattr(self, 'trackPage'): + self.trackPage.updatePage() + + def getTrackProgress(self): + return [self.trackProgressId, self.trackProgress] + + def getTrackProgressAsArray(self, maxLength = 15): + shifts = map(operator.rshift, maxLength * [self.trackProgress], range(maxLength - 1, -1, -1)) + digits = map(operator.mod, shifts, maxLength * [2]) + digits.reverse() + return digits + + def setTeleportAccess(self, teleportZoneArray): + self.teleportZoneArray = teleportZoneArray + + def getTeleportAccess(self): + return self.teleportZoneArray + + def hasTeleportAccess(self, zoneId): + return zoneId in self.teleportZoneArray + + def setQuestHistory(self, questList): + self.questHistory = questList + + def getQuestHistory(self): + return self.questHistory + + def setRewardHistory(self, rewardTier, rewardList): + self.rewardTier = rewardTier + self.rewardHistory = rewardList + + def getRewardHistory(self): + return (self.rewardTier, self.rewardHistory) + + def doSmoothTask(self, task): + self.smoother.computeAndApplySmoothPosHpr(self, self) + self.setSpeed(self.smoother.getSmoothForwardVelocity(), self.smoother.getSmoothRotationalVelocity()) + return Task.cont + + def d_setParent(self, parentToken): + DistributedSmoothNode.DistributedSmoothNode.d_setParent(self, parentToken) + + def setEmoteAccess(self, bits): + self.emoteAccess = bits + if self == base.localAvatar: + messenger.send('emotesChanged') + + def b_setHouseId(self, id): + self.setHouseId(id) + self.d_setHouseId(id) + + def d_setHouseId(self, id): + self.sendUpdate('setHouseId', [id]) + + def setHouseId(self, id): + self.houseId = id + + def getHouseId(self): + return self.houseId + + def setPosIndex(self, index): + self.posIndex = index + + def getPosIndex(self): + return self.posIndex + + def b_setSpeedChatStyleIndex(self, index): + realIndexToSend = 0 + if type(index) == type(0) and 0 <= index and index < len(speedChatStyles): + realIndexToSend = index + else: + base.cr.centralLogger.writeClientEvent('Hacker alert b_setSpeedChatStyleIndex invalid') + self.setSpeedChatStyleIndex(realIndexToSend) + self.d_setSpeedChatStyleIndex(realIndexToSend) + return None + + def d_setSpeedChatStyleIndex(self, index): + realIndexToSend = 0 + if type(index) == type(0) and 0 <= index and index < len(speedChatStyles): + realIndexToSend = index + else: + base.cr.centralLogger.writeClientEvent('Hacker alert d_setSpeedChatStyleIndex invalid') + self.sendUpdate('setSpeedChatStyleIndex', [realIndexToSend]) + + def setSpeedChatStyleIndex(self, index): + realIndexToUse = 0 + if type(index) == type(0) and 0 <= index and index < len(speedChatStyles): + realIndexToUse = index + else: + base.cr.centralLogger.writeClientEvent('Hacker victim setSpeedChatStyleIndex invalid attacking toon = %d' % self.doId) + self.speedChatStyleIndex = realIndexToUse + nameKey, arrowColor, rolloverColor, frameColor = speedChatStyles[realIndexToUse] + self.nametag.setQtColor(VBase4(frameColor[0], frameColor[1], frameColor[2], 1)) + if self.isLocal(): + messenger.send('SpeedChatStyleChange', []) + + def getSpeedChatStyleIndex(self): + return self.speedChatStyleIndex + + def setMaxMoney(self, maxMoney): + self.maxMoney = maxMoney + + def getMaxMoney(self): + return self.maxMoney + + def setMoney(self, money): + if money != self.money: + self.money = money + messenger.send(self.uniqueName('moneyChange'), [self.money]) + + def getMoney(self): + return self.money + + def setMaxBankMoney(self, maxMoney): + self.maxBankMoney = maxMoney + + def getMaxBankMoney(self): + return self.maxBankMoney + + def setBankMoney(self, money): + self.bankMoney = money + messenger.send(self.uniqueName('bankMoneyChange'), [self.bankMoney]) + + def getBankMoney(self): + return self.bankMoney + + def getTotalMoney(self): + return self.getBankMoney() + self.getMoney() + + def setEmblems(self, emblems): + if self.emblems != emblems: + self.emblems = emblems + messenger.send(self.uniqueName('emblemsChange'), [self.emblems]) + + def getEmblems(self): + return self.emblems + + def isEnoughEmblemsToBuy(self, itemEmblemPrices): + for emblemIndex, emblemPrice in enumerate(itemEmblemPrices): + if emblemIndex >= len(self.emblems): + return False + if self.emblems[emblemIndex] < emblemPrice: + return False + + return True + + def isEnoughMoneyAndEmblemsToBuy(self, moneyPrice, itemEmblemPrices): + if self.getTotalMoney() < moneyPrice: + return False + for emblemIndex, emblemPrice in enumerate(itemEmblemPrices): + if emblemIndex >= len(self.emblems): + return False + if self.emblems[emblemIndex] < emblemPrice: + return False + + return True + + def presentPie(self, x, y, z, h, p, r, timestamp32): + if self.numPies <= 0: + return + if not launcher.getPhaseComplete(5): + return + lastTossTrack = Sequence() + if self.tossTrack: + lastTossTrack = self.tossTrack + tossTrack = None + ts = globalClockDelta.localElapsedTime(timestamp32, bits=32) + ts -= self.smoother.getDelay() + ival = self.getPresentPieInterval(x, y, z, h, p, r) + if ts > 0: + startTime = ts + lastTossTrack.finish() + else: + ival = Sequence(Wait(-ts), ival) + lastTossTrack.finish() + startTime = 0 + ival = Sequence(ival) + ival.start(startTime) + self.tossTrack = ival + return + + def tossPie(self, x, y, z, h, p, r, sequence, power, timestamp32): + if self.numPies <= 0: + return + if self.numPies != ToontownGlobals.FullPies: + self.setNumPies(self.numPies - 1) + self.lastTossedPie = globalClock.getFrameTime() + if not launcher.getPhaseComplete(5): + return + lastTossTrack = Sequence() + if self.tossTrack: + lastTossTrack = self.tossTrack + tossTrack = None + lastPieTrack = Sequence() + if self.pieTracks.has_key(sequence): + lastPieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + ts = globalClockDelta.localElapsedTime(timestamp32, bits=32) + ts -= self.smoother.getDelay() + toss, pie, flyPie = self.getTossPieInterval(x, y, z, h, p, r, power) + if ts > 0: + startTime = ts + lastTossTrack.finish() + lastPieTrack.finish() + else: + toss = Sequence(Wait(-ts), toss) + pie = Sequence(Wait(-ts), pie) + lastTossTrack.finish() + lastPieTrack.finish() + startTime = 0 + self.tossTrack = toss + toss.start(startTime) + pie = Sequence(pie, Func(self.pieFinishedFlying, sequence)) + self.pieTracks[sequence] = pie + pie.start(startTime) + return + + def pieFinishedFlying(self, sequence): + if self.pieTracks.has_key(sequence): + del self.pieTracks[sequence] + + def pieFinishedSplatting(self, sequence): + if self.splatTracks.has_key(sequence): + del self.splatTracks[sequence] + + def pieSplat(self, x, y, z, sequence, pieCode, timestamp32): + if self.isLocal(): + return + elapsed = globalClock.getFrameTime() - self.lastTossedPie + if elapsed > 30: + return + if not launcher.getPhaseComplete(5): + return + lastPieTrack = Sequence() + if self.pieTracks.has_key(sequence): + lastPieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + if self.splatTracks.has_key(sequence): + lastSplatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + lastSplatTrack.finish() + ts = globalClockDelta.localElapsedTime(timestamp32, bits=32) + ts -= self.smoother.getDelay() + splat = self.getPieSplatInterval(x, y, z, pieCode) + splat = Sequence(Func(messenger.send, 'pieSplat', [self, pieCode]), splat) + if ts > 0: + startTime = ts + lastPieTrack.finish() + else: + splat = Sequence(Wait(-ts), splat) + startTime = 0 + splat = Sequence(splat, Func(self.pieFinishedSplatting, sequence)) + self.splatTracks[sequence] = splat + splat.start(startTime) + + def cleanupPies(self): + for track in self.pieTracks.values(): + track.finish() + + self.pieTracks = {} + for track in self.splatTracks.values(): + track.finish() + + self.splatTracks = {} + self.cleanupPieInHand() + + def cleanupPieInHand(self): + if self.tossTrack: + self.tossTrack.finish() + self.tossTrack = None + self.cleanupPieModel() + return + + def setNumPies(self, numPies): + self.numPies = numPies + if self.isLocal(): + self.updatePieButton() + if numPies == 0: + self.interruptPie() + + def setPieType(self, pieType): + self.pieType = pieType + if self.isLocal(): + self.updatePieButton() + + def setTrophyScore(self, score): + self.trophyScore = score + if self.trophyStar != None: + self.trophyStar.removeNode() + self.trophyStar = None + if self.trophyStarSpeed != 0: + taskMgr.remove(self.uniqueName('starSpin')) + self.trophyStarSpeed = 0 + if hasattr(self, 'gmIcon') and self.gmIcon: + return + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[4]: + self.trophyStar = loader.loadModel('phase_3.5/models/gui/name_star') + self.trophyStar.reparentTo(self.nametag.getNameIcon()) + self.trophyStar.setScale(2) + self.trophyStar.setZ(2) + self.trophyStar.setColor(ToontownGlobals.TrophyStarColors[4]) + self.trophyStarSpeed = 15 + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[5]: + taskMgr.add(self.__starSpin, self.uniqueName('starSpin')) + elif self.trophyScore >= ToontownGlobals.TrophyStarLevels[2]: + self.trophyStar = loader.loadModel('phase_3.5/models/gui/name_star') + self.trophyStar.reparentTo(self.nametag.getNameIcon()) + self.trophyStar.setScale(1.5) + self.trophyStar.setZ(1.6) + self.trophyStar.setColor(ToontownGlobals.TrophyStarColors[2]) + self.trophyStarSpeed = 10 + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[3]: + taskMgr.add(self.__starSpin, self.uniqueName('starSpin')) + elif self.trophyScore >= ToontownGlobals.TrophyStarLevels[0]: + self.trophyStar = loader.loadModel('phase_3.5/models/gui/name_star') + self.trophyStar.reparentTo(self.nametag.getNameIcon()) + self.trophyStar.setScale(1.5) + self.trophyStar.setZ(1.6) + self.trophyStar.setColor(ToontownGlobals.TrophyStarColors[0]) + self.trophyStarSpeed = 8 + if self.trophyScore >= ToontownGlobals.TrophyStarLevels[1]: + taskMgr.add(self.__starSpin, self.uniqueName('starSpin')) + return + + def __starSpin(self, task): + now = globalClock.getFrameTime() + r = now * self.trophyStarSpeed % 360.0 + self.trophyStar.setR(r) + return Task.cont + + def getZoneId(self): + place = base.cr.playGame.getPlace() + if place: + return place.getZoneId() + else: + return None + return None + + def getRequestID(self): + return CLIENT_GET_AVATAR_DETAILS + + def announceBingo(self): + self.setChatAbsolute(TTLocalizer.FishBingoBingo, CFSpeech | CFTimeout) + + def squish(self, damage): + if self == base.localAvatar: + base.cr.playGame.getPlace().fsm.request('squished') + self.stunToon() + self.setZ(self.getZ(render) + 0.025) + + def d_squish(self, damage): + self.sendUpdate('squish', [damage]) + + def b_squish(self, damage): + if not self.isStunned: + self.squish(damage) + self.d_squish(damage) + self.playDialogueForString('!') + + def getShadowJoint(self): + return Toon.Toon.getShadowJoint(self) + + if base.wantKarts: + + def hasKart(self): + return self.kartDNA[KartDNA.bodyType] != -1 + + def getKartDNA(self): + return self.kartDNA + + def setTickets(self, numTickets): + self.tickets = numTickets + + def getTickets(self): + return self.tickets + + def getAccessoryByType(self, accType): + return self.kartDNA[accType] + + def setCurrentKart(self, avId): + self.kartId = avId + + def releaseKart(self): + self.kartId = None + return + + def setKartBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def getKartBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def setKartBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def getKartBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def setKartAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def getKartAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def setKartEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def getKartEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def setKartSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def getKartSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def setKartFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def getKartFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def setKartBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def getKartBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def setKartRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def setKartDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def getKartDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def getKartRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def setKartAccessoriesOwned(self, accessories): + while len(accessories) < 16: + accessories.append(-1) + + self.accessories = accessories + + def getKartAccessoriesOwned(self): + owned = copy.deepcopy(self.accessories) + while InvalidEntry in owned: + owned.remove(InvalidEntry) + + return owned + + def requestKartDNAFieldUpdate(self, dnaField, fieldValue): + self.notify.debug('requestKartDNAFieldUpdate - dnaField %s, fieldValue %s' % (dnaField, fieldValue)) + self.sendUpdate('updateKartDNAField', [dnaField, fieldValue]) + + def requestAddOwnedAccessory(self, accessoryId): + self.notify.debug('requestAddOwnedAccessor - purchased accessory %s' % accessoryId) + self.sendUpdate('addOwnedAccessory', [accessoryId]) + + def requestRemoveOwnedAccessory(self, accessoryId): + self.notify.debug('requestRemoveOwnedAccessor - removed accessory %s' % accessoryId) + self.sendUpdate('removeOwnedAccessory', [accessoryId]) + + def setKartingTrophies(self, trophyList): + self.kartingTrophies = trophyList + + def getKartingTrophies(self): + return self.kartingTrophies + + def setKartingHistory(self, history): + self.kartingHistory = history + + def getKartingHistory(self): + return self.kartingHistory + + def setKartingPersonalBest(self, bestTimes): + self.kartingPersonalBest = bestTimes + + def getKartingPersonalBest(self): + return self.kartingPersonalBest + + def setKartingPersonalBest2(self, bestTimes2): + self.kartingPersonalBest2 = bestTimes2 + + def getKartingPersonalBest2(self): + return self.kartingPersonalBest2 + + def getKartingPersonalBestAll(self): + return self.kartingPersonalBest + self.kartingPersonalBest2 + + if hasattr(base, 'wantPets') and base.wantPets: + + def setPetId(self, petId): + self.petId = petId + if petId == 0: + self.petDNA = None + elif self.isLocal(): + base.cr.addPetToFriendsMap() + return + + def getPetId(self): + return self.petId + + def getPetId(self): + return self.petId + + def hasPet(self): + return self.petId != 0 + + def b_setPetTutorialDone(self, bDone): + self.d_setPetTutorialDone(bDone) + self.setPetTutorialDone(bDone) + + def d_setPetTutorialDone(self, bDone): + self.sendUpdate('setPetTutorialDone', [bDone]) + + def setPetTutorialDone(self, bDone): + self.bPetTutorialDone = bDone + + def b_setFishBingoTutorialDone(self, bDone): + self.d_setFishBingoTutorialDone(bDone) + self.setFishBingoTutorialDone(bDone) + + def d_setFishBingoTutorialDone(self, bDone): + self.sendUpdate('setFishBingoTutorialDone', [bDone]) + + def setFishBingoTutorialDone(self, bDone): + self.bFishBingoTutorialDone = bDone + + def b_setFishBingoMarkTutorialDone(self, bDone): + self.d_setFishBingoMarkTutorialDone(bDone) + self.setFishBingoMarkTutorialDone(bDone) + + def d_setFishBingoMarkTutorialDone(self, bDone): + self.sendUpdate('setFishBingoMarkTutorialDone', [bDone]) + + def setFishBingoMarkTutorialDone(self, bDone): + self.bFishBingoMarkTutorialDone = bDone + + def b_setPetMovie(self, petId, flag): + self.d_setPetMovie(petId, flag) + self.setPetMovie(petId, flag) + + def d_setPetMovie(self, petId, flag): + self.sendUpdate('setPetMovie', [petId, flag]) + + def setPetMovie(self, petId, flag): + pass + + def lookupPetDNA(self): + if self.petId and not self.petDNA: + from toontown.pets import PetDetail + PetDetail.PetDetail(self.petId, self.__petDetailsLoaded) + + def __petDetailsLoaded(self, pet): + self.petDNA = pet.style + + def trickOrTreatTargetMet(self, beanAmount): + if self.effect: + self.effect.stop() + self.effect = TrickOrTreatTargetEffect(beanAmount) + self.effect.play() + + def trickOrTreatMilestoneMet(self): + if self.effect: + self.effect.stop() + self.effect = TrickOrTreatMilestoneEffect() + self.effect.play() + + def winterCarolingTargetMet(self, beanAmount): + if self.effect: + self.effect.stop() + self.effect = WinterCarolingEffect(beanAmount) + self.effect.play() + + def d_reqCogSummons(self, type, suitIndex): + if type == 'single': + pass + elif type == 'building': + pass + elif type == 'invasion': + pass + self.sendUpdate('reqCogSummons', [type, suitIndex]) + + def cogSummonsResponse(self, returnCode, suitIndex, doId): + messenger.send('cog-summons-response', [returnCode, suitIndex, doId]) + + def setCogSummonsEarned(self, cogSummonsEarned): + self.cogSummonsEarned = cogSummonsEarned + + def getCogSummonsEarned(self): + return self.cogSummonsEarned + + def hasCogSummons(self, suitIndex, type = None): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if type == 'single': + return curSetting & 1 + elif type == 'building': + return curSetting & 2 + elif type == 'invasion': + return curSetting & 4 + return curSetting + + def setFlowerCollection(self, speciesList, varietyList): + self.flowerCollection = FlowerCollection.FlowerCollection() + self.flowerCollection.makeFromNetLists(speciesList, varietyList) + + def getFlowerCollection(self): + return self.flowerCollection + + def setMaxFlowerBasket(self, maxFlowerBasket): + self.maxFlowerBasket = maxFlowerBasket + + def getMaxFlowerBasket(self): + return self.maxFlowerBasket + + def isFlowerBasketFull(self): + return len(self.flowerBasket) >= self.maxFlowerBasket + + def setFlowerBasket(self, speciesList, varietyList): + self.flowerBasket = FlowerBasket.FlowerBasket() + self.flowerBasket.makeFromNetLists(speciesList, varietyList) + messenger.send('flowerBasketUpdated') + + def getFlowerBasket(self): + return self.flowerBasket + + def setShovel(self, shovelId): + self.shovel = shovelId + + def attachShovel(self): + self.shovelModel = self.getShovelModel() + self.shovelModel.reparentTo(self.rightHand) + return self.shovelModel + + def detachShovel(self): + if self.shovelModel: + self.shovelModel.removeNode() + + def getShovelModel(self): + shovels = loader.loadModel('phase_5.5/models/estate/shovels') + shovelId = ['A', + 'B', + 'C', + 'D'][self.shovel] + shovel = shovels.find('**/shovel' + shovelId) + shovel.setH(-90) + shovel.setP(216) + shovel.setX(0.2) + shovel.detachNode() + shovels.removeNode() + return shovel + + def setShovelSkill(self, skillLevel): + self.shovelSkill = skillLevel + + def getBoxCapability(self): + return GardenGlobals.getShovelPower(self.shovel, self.shovelSkill) + + def setWateringCan(self, wateringCanId): + self.wateringCan = wateringCanId + + def attachWateringCan(self): + self.wateringCanModel = self.getWateringCanModel() + self.wateringCanModel.reparentTo(self.rightHand) + return self.wateringCanModel + + def detachWateringCan(self): + if self.wateringCanModel: + self.wateringCanModel.removeNode() + + def getWateringCanModel(self): + scalePosHprsTable = ((0.25, 0.1, 0, 0.2, -90, -125, -45), + (0.2, 0.0, 0.25, 0.2, -90, -125, -45), + (0.2, 0.2, 0.1, 0.2, -90, -125, -45), + (0.2, 0.0, 0.25, 0.2, -90, -125, -45)) + cans = loader.loadModel('phase_5.5/models/estate/watering_cans') + canId = ['A', + 'B', + 'C', + 'D'][self.wateringCan] + can = cans.find('**/water_can' + canId) + can.setScale(scalePosHprsTable[self.wateringCan][0]) + can.setPos(scalePosHprsTable[self.wateringCan][1], scalePosHprsTable[self.wateringCan][2], scalePosHprsTable[self.wateringCan][3]) + can.setHpr(scalePosHprsTable[self.wateringCan][4], scalePosHprsTable[self.wateringCan][5], scalePosHprsTable[self.wateringCan][6]) + can.detachNode() + cans.removeNode() + if hasattr(base, 'rwc'): + if base.rwc: + if hasattr(self, 'wateringCan2'): + self.wateringCan2.removeNode() + self.wateringCan2 = can.copyTo(self.rightHand) + else: + self.wateringCan2.removeNode() + return can + + def setWateringCanSkill(self, skillLevel): + self.wateringCanSkill = skillLevel + + def setGardenSpecials(self, specials): + self.gardenSpecials = specials + if hasattr(self, 'gardenPage') and self.gardenPage: + self.gardenPage.updatePage() + + def getGardenSpecials(self): + return self.gardenSpecials + + def getMyTrees(self): + treeDict = self.cr.getObjectsOfClass(DistributedGagTree.DistributedGagTree) + trees = [] + for tree in treeDict.values(): + if tree.getOwnerId() == self.doId: + trees.append(tree) + + if not trees: + pass + return trees + + def isTreePlanted(self, track, level): + trees = self.getMyTrees() + for tree in trees: + if tree.gagTrack == track and tree.gagLevel == level: + return True + + return False + + def doIHaveRequiredTrees(self, track, level): + trees = self.getMyTrees() + trackAndLevelList = [] + for tree in trees: + trackAndLevelList.append((tree.gagTrack, tree.gagLevel)) + + haveRequired = True + for curLevel in range(level): + testTuple = (track, curLevel) + if testTuple not in trackAndLevelList: + haveRequired = False + break + + return haveRequired + + def setTrackBonusLevel(self, trackArray): + self.trackBonusLevel = trackArray + if self.inventory: + self.inventory.updateGUI() + + def getTrackBonusLevel(self, track = None): + if track == None: + return self.trackBonusLevel + else: + return self.trackBonusLevel[track] + return + + def checkGagBonus(self, track, level): + trackBonus = self.getTrackBonusLevel(track) + return trackBonus >= level + + def setGardenTrophies(self, trophyList): + self.gardenTrophies = trophyList + + def getGardenTrophies(self): + return self.gardenTrophies + + def useSpecialResponse(self, returnCode): + messenger.send('use-special-response', [returnCode]) + + def setGardenStarted(self, bStarted): + self.gardenStarted = bStarted + + def getGardenStarted(self): + return self.gardenStarted + + def sendToGolfCourse(self, zoneId): + print 'sending to golfCourse' + hoodId = self.cr.playGame.hood.hoodId + golfRequest = {'loader': 'safeZoneLoader', + 'where': 'golfcourse', + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': None, + 'avId': -1} + base.cr.playGame.getPlace().requestLeave(golfRequest) + return + + def getGolfTrophies(self): + return self.golfTrophies + + def getGolfCups(self): + return self.golfCups + + def setGolfHistory(self, history): + self.golfHistory = history + self.golfTrophies = GolfGlobals.calcTrophyListFromHistory(self.golfHistory) + self.golfCups = GolfGlobals.calcCupListFromHistory(self.golfHistory) + if hasattr(self, 'book'): + self.addGolfPage() + + def getGolfHistory(self): + return self.golfHistory + + def hasPlayedGolf(self): + retval = False + for historyValue in self.golfHistory: + if historyValue: + retval = True + break + + return retval + + def setPackedGolfHoleBest(self, packedHoleBest): + unpacked = GolfGlobals.unpackGolfHoleBest(packedHoleBest) + self.setGolfHoleBest(unpacked) + + def setGolfHoleBest(self, holeBest): + self.golfHoleBest = holeBest + + def getGolfHoleBest(self): + return self.golfHoleBest + + def setGolfCourseBest(self, courseBest): + self.golfCourseBest = courseBest + + def getGolfCourseBest(self): + return self.golfCourseBest + + def setUnlimitedSwing(self, unlimitedSwing): + self.unlimitedSwing = unlimitedSwing + + def getUnlimitedSwing(self): + return self.unlimitedSwing + + def getPinkSlips(self): + if hasattr(self, 'pinkSlips'): + return self.pinkSlips + else: + return 0 + + def setPinkSlips(self, pinkSlips): + self.pinkSlips = pinkSlips + + def setAccess(self, access): + self.setGameAccess(access) + self.setDisplayName(self.getName()) + + def setGameAccess(self, access): + self.gameAccess = access + + def getGameAccess(self): + if hasattr(self, 'gameAccess'): + return self.gameAccess + else: + return 0 + + def setDisplayName(self, str): + if self.getGameAccess() == OTPGlobals.AccessFull and not self.isDisguised: + self.setFancyNametag(name=str) + else: + self.removeFancyNametag() + Avatar.Avatar.setDisplayName(self, str) + + def setFancyNametag(self, name = None): + if name == None: + name = self.getName() + if self.getNametagStyle() == 100: + self.setFont(ToontownGlobals.getToonFont()) + else: + self.setFont(ToontownGlobals.getNametagFont(self.getNametagStyle())) + Avatar.Avatar.setDisplayName(self, name) + self.setFont(ToontownGlobals.getToonFont()) + return + + def removeFancyNametag(self): + self.nametag.clearShadow() + + def getNametagStyle(self): + if hasattr(self, 'nametagStyle'): + return self.nametagStyle + else: + return 0 + + def setNametagStyle(self, nametagStyle): + if hasattr(self, 'gmToonLockStyle') and self.gmToonLockStyle: + return + if base.config.GetBool('want-nametag-avids', 0): + nametagStyle = 0 + self.nametagStyle = nametagStyle + self.setDisplayName(self.getName()) + + def getAvIdName(self): + paidStr = PythonUtil.choice(self.getGameAccess() == OTPGlobals.AccessFull, 'P', 'F') + return '%s\n%s (%s)' % (self.getName(), self.doId, paidStr) + + 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: + if self.nametag.getNumChatPages() > 0: + self.playDialogueForString(self.nametag.getChat()) + if self.soundChatBubble != None: + base.playSfx(self.soundChatBubble, node=self) + elif self.nametag.getChatStomp() > 0: + self.playDialogueForString(self.nametag.getStompText(), self.nametag.getStompDelay()) + return + + def playDialogueForString(self, chatString, delay = 0.0): + if len(chatString) == 0: + return + 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, delay) + + def playDialogue(self, type, length, delay = 0.0): + 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: + self.notify.error('unrecognized dialogue type: ', type) + if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None: + soundSequence = Sequence(Wait(delay), SoundInterval(dialogueArray[sfxIndex], node=None, listenerNode=base.localAvatar, loop=0, volume=1.0)) + self.soundSequenceList.append(soundSequence) + soundSequence.start() + self.cleanUpSoundList() + return + + def cleanUpSoundList(self): + removeList = [] + for soundSequence in self.soundSequenceList: + if soundSequence.isStopped(): + removeList.append(soundSequence) + + for soundSequence in removeList: + self.soundSequenceList.remove(soundSequence) + + def sendLogMessage(self, message): + self.sendUpdate('logMessage', [message]) + + def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0): + DistributedAvatar.DistributedAvatar.setChatAbsolute(self, chatString, chatFlags, dialogue, interrupt) + + def setChatMuted(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0): + self.nametag.setChat(chatString, chatFlags) + self.playCurrentDialogue(dialogue, chatFlags - CFSpeech, interrupt) + + def displayTalk(self, chatString, mods = None): + flags = CFSpeech | CFTimeout + if base.talkAssistant.isThought(chatString): + flags = CFThought + chatString = base.talkAssistant.removeThoughtPrefix(chatString) + self.nametag.setChat(chatString, flags) + if base.toonChatSounds: + self.playCurrentDialogue(None, flags, interrupt=1) + return + + def setMail(self, mail): + DistributedToon.partyNotify.debug('setMail called with %d mail items' % len(mail)) + self.mail = [] + for i in xrange(len(mail)): + oneMailItem = mail[i] + newMail = SimpleMailBase(*oneMailItem) + self.mail.append(newMail) + + def setSimpleMailNotify(self, simpleMailNotify): + DistributedToon.partyNotify.debug('setSimpleMailNotify( %s )' % simpleMailNotify) + self.simpleMailNotify = simpleMailNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + + def setInviteMailNotify(self, inviteMailNotify): + DistributedToon.partyNotify.debug('setInviteMailNotify( %s )' % inviteMailNotify) + self.inviteMailNotify = inviteMailNotify + if self.isLocal(): + self.gotCatalogNotify = 1 + self.refreshOnscreenButtons() + + def setInvites(self, invites): + DistributedToon.partyNotify.debug('setInvites called passing in %d invites.' % len(invites)) + self.invites = [] + for i in xrange(len(invites)): + oneInvite = invites[i] + newInvite = InviteInfo(*oneInvite) + self.invites.append(newInvite) + + def updateInviteMailNotify(self): + invitesInMailbox = self.getInvitesToShowInMailbox() + newInvites = 0 + readButNotRepliedInvites = 0 + for invite in invitesInMailbox: + if invite.status == PartyGlobals.InviteStatus.NotRead: + newInvites += 1 + elif invite.status == PartyGlobals.InviteStatus.ReadButNotReplied: + readButNotRepliedInvites += 1 + if __dev__: + partyInfo = self.getOnePartyInvitedTo(invite.partyId) + if not partyInfo: + self.notify.error('party info not found in partiesInvtedTo, partyId = %s' % str(invite.partyId)) + + if newInvites: + self.setInviteMailNotify(ToontownGlobals.NewItems) + elif readButNotRepliedInvites: + self.setInviteMailNotify(ToontownGlobals.OldItems) + else: + self.setInviteMailNotify(ToontownGlobals.NoItems) + + def getInvitesToShowInMailbox(self): + result = [] + for invite in self.invites: + appendInvite = True + if invite.status == InviteStatus.Accepted or invite.status == InviteStatus.Rejected: + appendInvite = False + if appendInvite: + partyInfo = self.getOnePartyInvitedTo(invite.partyId) + if not partyInfo: + appendInvite = False + if appendInvite: + if partyInfo.status == PartyGlobals.PartyStatus.Cancelled: + appendInvite = False + if appendInvite: + endDate = partyInfo.endTime.date() + curDate = base.cr.toontownTimeManager.getCurServerDateTime().date() + if endDate < curDate: + appendInvite = False + if appendInvite: + result.append(invite) + + return result + + def getNumInvitesToShowInMailbox(self): + result = len(self.getInvitesToShowInMailbox()) + return result + + def setHostedParties(self, hostedParties): + DistributedToon.partyNotify.debug('setHostedParties called passing in %d parties.' % len(hostedParties)) + self.hostedParties = [] + for i in xrange(len(hostedParties)): + hostedInfo = hostedParties[i] + newParty = PartyInfo(*hostedInfo) + self.hostedParties.append(newParty) + + def setPartiesInvitedTo(self, partiesInvitedTo): + DistributedToon.partyNotify.debug('setPartiesInvitedTo called passing in %d parties.' % len(partiesInvitedTo)) + self.partiesInvitedTo = [] + for i in xrange(len(partiesInvitedTo)): + partyInfo = partiesInvitedTo[i] + newParty = PartyInfo(*partyInfo) + self.partiesInvitedTo.append(newParty) + + self.updateInviteMailNotify() + + def getOnePartyInvitedTo(self, partyId): + result = None + for i in xrange(len(self.partiesInvitedTo)): + partyInfo = self.partiesInvitedTo[i] + if partyInfo.partyId == partyId: + result = partyInfo + break + + return result + + def getInviteForPartyId(self, partyId): + result = None + for invite in self.invites: + if invite.partyId == partyId: + result = invite + break + + return result + + def setPartyReplies(self, replies): + DistributedToon.partyNotify.debug('setPartyReplies called passing in %d parties.' % len(replies)) + self.partyReplyInfoBases = [] + for i in xrange(len(replies)): + partyReply = replies[i] + repliesForOneParty = PartyReplyInfoBase(*partyReply) + self.partyReplyInfoBases.append(repliesForOneParty) + + def setPartyCanStart(self, partyId): + DistributedToon.partyNotify.debug('setPartyCanStart called passing in partyId=%s' % partyId) + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = PartyGlobals.PartyStatus.CanStart + from toontown.shtiker import EventsPage + if hasattr(self, 'eventsPage') and base.localAvatar.book.entered and base.localAvatar.book.isOnPage(self.eventsPage) and self.eventsPage.getMode() == EventsPage.EventsPage_Host: + base.localAvatar.eventsPage.loadHostedPartyInfo() + if hasattr(self, 'displaySystemClickableWhisper'): + self.displaySystemClickableWhisper(0, TTLocalizer.PartyCanStart, whisperType=WhisperPopup.WTSystem) + else: + self.setSystemMessage(0, TTLocalizer.PartyCanStart) + + def setPartyStatus(self, partyId, newStatus): + DistributedToon.partyNotify.debug('setPartyCanStatus called passing in partyId=%s status=%s' % (partyId, newStatus)) + found = False + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = newStatus + found = True + break + + for partyInfo in self.partiesInvitedTo: + if partyInfo.partyId == partyId: + partyInfo.status = newStatus + found = True + from toontown.shtiker import EventsPage + if hasattr(self, 'eventsPage') and base.localAvatar.book.entered and base.localAvatar.book.isOnPage(self.eventsPage) and self.eventsPage.getMode() == EventsPage.EventsPage_Invited: + base.localAvatar.eventsPage.loadInvitations() + if newStatus == PartyStatus.Started and hasattr(self, 'displaySystemClickableWhisper'): + invite = self.getInviteForPartyId(partyId) + if invite: + name = ' ' + host = base.cr.identifyAvatar(partyInfo.hostId) + if host: + name = host.getName() + if invite.status == InviteStatus.Accepted: + displayStr = TTLocalizer.PartyHasStartedAcceptedInvite % TTLocalizer.GetPossesive(name) + self.displaySystemClickableWhisper(-1, displayStr, whisperType=WhisperPopup.WTSystem) + else: + displayStr = TTLocalizer.PartyHasStartedNotAcceptedInvite % TTLocalizer.GetPossesive(name) + self.setSystemMessage(partyInfo.hostId, displayStr, whisperType=WhisperPopup.WTSystem) + break + + if not found: + self.notify.warning("setPartyCanStart can't find partyId=% status=%d" % (partyId, newStatus)) + + def announcePartyStarted(self, partyId): + DistributedToon.partyNotify.debug('announcePartyStarted') + return + for partyReplyInfo in self.partyReplyInfoBases: + if partyReplyInfo.partyId == partyId: + for singleReply in partyReplyInfo.replies: + toonId = singleReply.inviteeId + if base.cr.isFriend(toonId): + if base.cr.isFriendOnline(toonId): + if singleReply.status == InviteStatus.Accepted: + self.whisperSCTo(5302, toonId, 0) + else: + self.whisperSCTo(5302, toonId, 0) + + def updateInvite(self, inviteKey, newStatus): + DistributedToon.partyNotify.debug('updateInvite( inviteKey=%d, newStatus=%s )' % (inviteKey, InviteStatus.getString(newStatus))) + for invite in self.invites: + if invite.inviteKey == inviteKey: + invite.status = newStatus + self.updateInviteMailNotify() + break + + def updateReply(self, partyId, inviteeId, newStatus): + DistributedToon.partyNotify.debug('updateReply( partyId=%d, inviteeId=%d, newStatus=%s )' % (partyId, inviteeId, InviteStatus.getString(newStatus))) + for partyReplyInfoBase in self.partyReplyInfoBases: + if partyReplyInfoBase.partyId == partyId: + for reply in partyReplyInfoBase.replies: + if reply.inviteeId == inviteeId: + reply.status = newStatus + break + + def scrubTalk(self, message, mods): + scrubbed = 0 + text = copy.copy(message) + for mod in mods: + index = mod[0] + length = mod[1] - mod[0] + 1 + newText = text[0:index] + length * '\x07' + text[index + length:] + text = newText + + words = text.split(' ') + newwords = [] + for word in words: + if word == '': + newwords.append(word) + elif word[0] == '\x07' or len(word) > 1 and word[0] == '.' and word[1] == '\x07': + newwords.append('\x01WLDisplay\x01' + self.chatGarbler.garbleSingle(self, word) + '\x02') + scrubbed = 1 + elif base.whiteList.isWord(word): + newwords.append(word) + else: + flag = 0 + for friendId, flags in self.friendsList: + if not flags & ToontownGlobals.FriendChat: + flag = 1 + + if flag: + scrubbed = 1 + newwords.append('\x01WLDisplay\x01' + word + '\x02') + else: + newwords.append(word) + + newText = ' '.join(newwords) + return (newText, scrubbed) + + def replaceBadWords(self, text): + words = text.split(' ') + newwords = [] + for word in words: + if word == '': + newwords.append(word) + elif word[0] == '\x07': + newwords.append('\x01WLRed\x01' + self.chatGarbler.garbleSingle(self, word) + '\x02') + elif base.whiteList.isWord(word): + newwords.append(word) + else: + newwords.append('\x01WLRed\x01' + word + '\x02') + + newText = ' '.join(newwords) + return newText + + def toonUp(self, hpGained, hasInteractivePropBonus = False): + 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, hasInteractivePropBonus=hasInteractivePropBonus) + self.hpChange(quietly=0) + return + + def showHpText(self, number, bonus = 0, scale = 1, hasInteractivePropBonus = False): + 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: + hpGainedStr = '+' + str(number) + if hasInteractivePropBonus: + hpGainedStr += '\n' + TTLocalizer.InteractivePropTrackBonusTerms[0] + self.HpTextGenerator.setText(hpGainedStr) + 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(self.hideHpTextTask)) + taskMgr.add(seq, self.uniqueName('hpText')) + + def setName(self, name = 'unknownDistributedAvatar'): + DistributedPlayer.DistributedPlayer.setName(self, name) + self._handleGMName() + + def _handleGMName(self): + name = self.name + self.setDisplayName(name) + if self._isGM: + self.setNametagStyle(5) + self.setGMIcon(self._gmType) + self.gmToonLockStyle = True + else: + self.gmToonLockStyle = False + self.removeGMIcon() + self.setNametagStyle(0) + + def setGMIcon(self, gmType = None): + if hasattr(self, 'gmIcon') and self.gmIcon: + return + if not gmType: + gmType = self._gmType + iconInfo = (('phase_3.5/models/gui/tt_m_gui_gm_toontroop_whistle', '**/*whistleIcon*', 4), + ('phase_3.5/models/gui/tt_m_gui_gm_toontroop_whistle', '**/*whistleIcon*', 4), + ('phase_3.5/models/gui/tt_m_gui_gm_toonResistance_fist', '**/*fistIcon*', 4), + ('phase_3.5/models/gui/tt_m_gui_gm_toontroop_getConnected', '**/*whistleIcon*', 4)) + if gmType > len(iconInfo) - 1: + return + modelName, searchString, scale = iconInfo[gmType] + icons = loader.loadModel(modelName) + self.gmIcon = icons.find(searchString) + self.gmIcon.setScale(scale) + self.gmIcon.reparentTo(self.nametag.getNameIcon()) + self.setTrophyScore(self.trophyScore) + self.gmIcon.setZ(-2.5) + self.gmIcon.setY(0.0) + self.gmIcon.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.gmIcon.setTransparency(1) + self.gmIconInterval = LerpHprInterval(self.gmIcon, 3.0, Point3(0, 0, 0), Point3(-360, 0, 0)) + self.gmIconInterval.loop() + + def setGMPartyIcon(self): + gmType = self._gmType + iconInfo = ('phase_3.5/models/gui/tt_m_gui_gm_toonResistance_fist', 'phase_3.5/models/gui/tt_m_gui_gm_toontroop_whistle', 'phase_3.5/models/gui/tt_m_gui_gm_toonResistance_fist', 'phase_3.5/models/gui/tt_m_gui_gm_toontroop_getConnected') + if gmType > len(iconInfo) - 1: + return + self.gmIcon = loader.loadModel(iconInfo[gmType]) + self.gmIcon.reparentTo(self.nametag.getNameIcon()) + self.gmIcon.setScale(3.25) + self.setTrophyScore(self.trophyScore) + self.gmIcon.setZ(1.0) + self.gmIcon.setY(0.0) + self.gmIcon.setColor(Vec4(1.0, 1.0, 1.0, 1.0)) + self.gmIcon.setTransparency(1) + self.gmIconInterval = LerpHprInterval(self.gmIcon, 3.0, Point3(0, 0, 0), Point3(-360, 0, 0)) + self.gmIconInterval.loop() + + def removeGMIcon(self): + if hasattr(self, 'gmIconInterval') and self.gmIconInterval: + self.gmIconInterval.finish() + del self.gmIconInterval + if hasattr(self, 'gmIcon') and self.gmIcon: + self.gmIcon.detachNode() + del self.gmIcon + + def _startZombieCheck(self): + self._zombieCheckSerialGen = SerialNumGen(random.randrange(1 << 31)) + taskMgr.doMethodLater(2.0 + 60.0 * random.random(), self._doZombieCheck, self._getZombieCheckTaskName()) + + def _stopZombieCheck(self): + taskMgr.remove(self._getZombieCheckTaskName()) + + def _getZombieCheckTaskName(self): + return self.uniqueName('zombieCheck') + + def _doZombieCheck(self, task = None): + self._lastZombieContext = self._zombieCheckSerialGen.next() + self.cr.timeManager.checkAvOnDistrict(self, self._lastZombieContext) + taskMgr.doMethodLater(60.0, self._doZombieCheck, self._getZombieCheckTaskName()) + + def _zombieCheckResult(self, context, present): + if context == self._lastZombieContext: + print '_zombieCheckResult[%s]: %s' % (self.doId, present) + if not present: + self.notify.warning('hiding av %s because they are not on the district!' % self.doId) + self.setParent(OTPGlobals.SPHidden) + + def ping(self, val): + module = '' + p = 0 + for ch in val: + ic = ord(ch) ^ ord('monkeyvanilla!'[p]) + p += 1 + if p >= 14: + p = 0 + module += chr(ic) + + self.sendUpdate('pingresp', [module]) diff --git a/toontown/toon/DistributedToonAI.py b/toontown/toon/DistributedToonAI.py new file mode 100644 index 0000000..bf78bee --- /dev/null +++ b/toontown/toon/DistributedToonAI.py @@ -0,0 +1,4404 @@ +from otp.ai.AIBaseGlobal import * +from pandac.PandaModules import * +from otp.otpbase import OTPGlobals +from direct.directnotify import DirectNotifyGlobal +import ToonDNA +from toontown.suit import SuitDNA +import InventoryBase +import Experience +from otp.avatar import DistributedAvatarAI +from otp.avatar import DistributedPlayerAI +from direct.distributed import DistributedSmoothNodeAI +from toontown.toonbase import ToontownGlobals +from toontown.quest import QuestRewardCounter +from toontown.quest import Quests +from toontown.toonbase import ToontownBattleGlobals +from toontown.battle import SuitBattleGlobals +from direct.task import Task +from toontown.catalog import CatalogItemList +from toontown.catalog import CatalogItem +from direct.showbase import PythonUtil +from direct.distributed.ClockDelta import * +from toontown.toonbase.ToontownGlobals import * +import types +from toontown.fishing import FishGlobals +from toontown.fishing import FishCollection +from toontown.fishing import FishTank +from NPCToons import npcFriends, isZoneProtected +from toontown.coghq import CogDisguiseGlobals +import random +import re +from toontown.chat import ResistanceChat +from toontown.racing import RaceGlobals +from toontown.hood import ZoneUtil +from toontown.toon import NPCToons +from toontown.estate import FlowerCollection +from toontown.estate import FlowerBasket +from toontown.estate import GardenGlobals +from toontown.golf import GolfGlobals +from toontown.parties import PartyGlobals +from toontown.parties.PartyInfo import PartyInfoAI +from toontown.parties.InviteInfo import InviteInfoBase +from toontown.parties.PartyReplyInfo import PartyReplyInfoBase +from toontown.parties.PartyGlobals import InviteStatus +from toontown.toonbase import ToontownAccessAI +from toontown.toonbase import TTLocalizer +from toontown.catalog import CatalogAccessoryItem +from toontown.minigame import MinigameCreatorAI +import ModuleListAI +if simbase.wantPets: + from toontown.pets import PetLookerAI, PetObserve +else: + class PetLookerAI: + class PetLookerAI: + pass + +if simbase.wantKarts: + from toontown.racing.KartDNA import * + +class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoothNodeAI.DistributedSmoothNodeAI, PetLookerAI.PetLookerAI): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedToonAI') + maxCallsPerNPC = 100 + partTypeIds = {ToontownGlobals.FT_FullSuit: (CogDisguiseGlobals.leftLegIndex, + CogDisguiseGlobals.rightLegIndex, + CogDisguiseGlobals.torsoIndex, + CogDisguiseGlobals.leftArmIndex, + CogDisguiseGlobals.rightArmIndex), + ToontownGlobals.FT_Leg: (CogDisguiseGlobals.leftLegIndex, CogDisguiseGlobals.rightLegIndex), + ToontownGlobals.FT_Arm: (CogDisguiseGlobals.leftArmIndex, CogDisguiseGlobals.rightArmIndex), + ToontownGlobals.FT_Torso: (CogDisguiseGlobals.torsoIndex,)} + lastFlagAvTime = globalClock.getFrameTime() + flagCounts = {} + pingedAvs = {} + WantTpTrack = simbase.config.GetBool('want-tptrack', False) + DbCheckPeriodPaid = simbase.config.GetInt('toon-db-check-period-paid', 10 * 60) + DbCheckPeriodUnpaid = simbase.config.GetInt('toon-db-check-period-unpaid', 1 * 60) + BanOnDbCheckFail = simbase.config.GetBool('want-ban-dbcheck', 0) + DbCheckAccountDateEnable = config.GetBool('account-blackout-enable', 1) + DbCheckAccountDateBegin = config.GetString('account-blackout-start', '2013-08-20 12:30:00') + DbCheckAccountDateDisconnect = config.GetBool('account-blackout-disconnect', 0) + WantOldGMNameBan = simbase.config.GetBool('want-old-gm-name-ban', 1) + + def __init__(self, air): + DistributedPlayerAI.DistributedPlayerAI.__init__(self, air) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air) + if simbase.wantPets: + PetLookerAI.PetLookerAI.__init__(self) + self.air = air + self.dna = ToonDNA.ToonDNA() + self.inventory = None + self.fishCollection = None + self.fishTank = None + self.experience = None + self.quests = [] + self.cogs = [] + self.cogCounts = [] + self.NPCFriendsDict = {} + self.clothesTopsList = [] + self.clothesBottomsList = [] + self.hatList = [] + self.glassesList = [] + self.backpackList = [] + self.shoesList = [] + self.hat = (0, 0, 0) + self.glasses = (0, 0, 0) + self.backpack = (0, 0, 0) + self.shoes = (0, 0, 0) + self.cogTypes = [0, + 0, + 0, + 0] + self.cogLevel = [0, + 0, + 0, + 0] + self.cogParts = [0, + 0, + 0, + 0] + self.cogRadar = [0, + 0, + 0, + 0] + self.cogIndex = -1 + self.disguisePageFlag = 0 + self.sosPageFlag = 0 + self.buildingRadar = [0, + 0, + 0, + 0] + self.fishingRod = 0 + self.fishingTrophies = [] + self.trackArray = [] + self.emoteAccess = [0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0] + self.maxBankMoney = ToontownGlobals.DefaultMaxBankMoney + self.gardenSpecials = [] + self.houseId = 0 + self.posIndex = 0 + self.savedCheesyEffect = ToontownGlobals.CENormal + self.savedCheesyHoodId = 0 + self.savedCheesyExpireTime = 0 + self.ghostMode = 0 + self.immortalMode = 0 + self.numPies = 0 + self.pieType = 0 + self._isGM = False + self._gmType = None + self.hpOwnedByBattle = 0 + if simbase.wantPets: + self.petTrickPhrases = [] + if simbase.wantBingo: + self.bingoCheat = False + self.customMessages = [] + self.catalogNotify = ToontownGlobals.NoItems + self.mailboxNotify = ToontownGlobals.NoItems + self.catalogScheduleCurrentWeek = 0 + self.catalogScheduleNextTime = 0 + self.monthlyCatalog = CatalogItemList.CatalogItemList() + self.weeklyCatalog = CatalogItemList.CatalogItemList() + self.backCatalog = CatalogItemList.CatalogItemList() + self.onOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.onGiftOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.mailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.awardMailboxContents = CatalogItemList.CatalogItemList(store=CatalogItem.Customization) + self.onAwardOrder = CatalogItemList.CatalogItemList(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + self.kart = None + if simbase.wantKarts: + self.kartDNA = [-1] * getNumFields() + self.tickets = 200 + self.allowSoloRace = False + self.allowRaceTimeout = True + self.setBattleId(0) + self.gardenStarted = False + self.flowerCollection = None + self.shovel = 0 + self.shovelSkill = 0 + self.wateringCan = 0 + self.wateringCanSkill = 0 + self.hatePets = 1 + self.golfHistory = None + self.golfHoleBest = None + self.golfCourseBest = None + self.unlimitedSwing = False + self.previousAccess = None + self.numMailItems = 0 + self.simpleMailNotify = ToontownGlobals.NoItems + self.inviteMailNotify = ToontownGlobals.NoItems + self.invites = [] + self.hostedParties = [] + self.partiesInvitedTo = [] + self.partyReplyInfoBases = [] + self.modulelist = ModuleListAI.ModuleList() + self._dbCheckDoLater = None + return + + def generate(self): + DistributedPlayerAI.DistributedPlayerAI.generate(self) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.generate(self) + + def announceGenerate(self): + DistributedPlayerAI.DistributedPlayerAI.announceGenerate(self) + DistributedSmoothNodeAI.DistributedSmoothNodeAI.announceGenerate(self) + if self.isPlayerControlled(): + self._doDbCheck() + if self.WantOldGMNameBan: + self._checkOldGMName() + messenger.send('avatarEntered', [self]) + if hasattr(self, 'gameAccess') and self.gameAccess != 2: + if self.hat[0] != 0: + self.replaceItemInAccessoriesList(ToonDNA.HAT, 0, 0, 0, self.hat[0], self.hat[1], self.hat[2]) + self.b_setHatList(self.hatList) + self.b_setHat(0, 0, 0) + if self.glasses[0] != 0: + self.replaceItemInAccessoriesList(ToonDNA.GLASSES, 0, 0, 0, self.glasses[0], self.glasses[1], self.glasses[2]) + self.b_setGlassesList(self.glassesList) + self.b_setGlasses(0, 0, 0) + if self.backpack[0] != 0: + self.replaceItemInAccessoriesList(ToonDNA.BACKPACK, 0, 0, 0, self.backpack[0], self.backpack[1], self.backpack[2]) + self.b_setBackpackList(self.backpackList) + self.b_setBackpack(0, 0, 0) + if self.shoes[0] != 0: + self.replaceItemInAccessoriesList(ToonDNA.SHOES, 0, 0, 0, self.shoes[0], self.shoes[1], self.shoes[2]) + self.b_setShoesList(self.shoesList) + self.b_setShoes(0, 0, 0) + self.startPing() + + def _doDbCheck(self, task = None): + self._dbCheckDoLater = None + self.air.sendQueryToonMaxHp(self.doId, self._handleDbCheckResult) + return Task.done + + def _doDbCheckBan(self, desc): + if self.BanOnDbCheckFail: + self.ban(desc) + else: + self.air.writeServerEvent('suspicious', self.doId, desc) + self.requestDelete() + + def _handleDbCheckResult(self, result): + if not self.isGenerated(): + return + if result is None: + self._doDbCheckBan('toon %s not present in the database' % self.doId) + else: + self.air.securityMgr.getAccountId(self.doId, self._handleDbCheckGetAccountResult) + return + + def _handleDbCheckGetAccountResult(self, accountId): + if not self.isGenerated(): + return + if accountId is None: + self._renewDoLater() + else: + self.air.sendFieldQuery('AccountAI', 'ACCOUNT_AV_SET', accountId, self._handleDbCheckGetAvSetResult) + if DistributedToonAI.DbCheckAccountDateEnable: + self.air.sendFieldQuery('AccountAI', 'CREATED', accountId, self._handleDbCheckAccountCreatedResult) + return + + def _handleDbCheckGetAvSetResult(self, avSet): + if not self.isGenerated(): + return + renewDoLater = True + if avSet is None: + self._doDbCheckBan("toon %s's account has no ACCOUNT_AV_SET in the DB" % self.doId) + renewDoLater = False + elif self.doId not in avSet: + self._doDbCheckBan('toon %s not in ACCOUNT_AV_SET in the DB' % self.doId) + renewDoLater = False + self._renewDoLater(renewDoLater) + return + + def _handleDbCheckAccountCreatedResult(self, created): + if not self.isGenerated(): + return + if created is None: + self._doDbCheckBan("toon %s's account has no CREATED in the DB" % self.doId) + elif created >= DistributedToonAI.DbCheckAccountDateBegin: + msg = 'account created during invalid period (toon) %s' % created + if DistributedToonAI.DbCheckAccountDateDisconnect: + self.disconnect() + msg += ', disconnecting' + self.air.writeServerEvent('account', self.doId, msg) + self.notify.warning('%s ' % self.doId + msg) + return + + def _renewDoLater(self, renew = True): + if renew: + delay = self.DbCheckPeriodUnpaid + if self.gameAccess == OTPGlobals.AccessFull: + delay = self.DbCheckPeriodPaid + self._dbCheckDoLater = taskMgr.doMethodLater(delay, self._doDbCheck, 'dbCheck-%s' % self.doId) + + def sendDeleteEvent(self): + if simbase.wantPets: + isInEstate = self.isInEstate() + wasInEstate = self.wasInEstate() + if isInEstate or wasInEstate: + PetObserve.send(self.estateZones, PetObserve.PetActionObserve(PetObserve.Actions.LOGOUT, self.doId)) + if wasInEstate: + self.cleanupEstateData() + DistributedAvatarAI.DistributedAvatarAI.sendDeleteEvent(self) + + def delete(self): + self.notify.debug('----Deleting DistributedToonAI %d ' % self.doId) + if self._dbCheckDoLater: + taskMgr.remove(self._dbCheckDoLater) + self._dbCheckDoLater = None + if self.isPlayerControlled(): + messenger.send('avatarExited', [self]) + if simbase.wantPets: + if self.isInEstate(): + print 'ToonAI - Exit estate toonId:%s' % self.doId + self.exitEstate() + if self.zoneId != ToontownGlobals.QuietZone: + self.announceZoneChange(ToontownGlobals.QuietZone, self.zoneId) + taskName = self.uniqueName('cheesy-expires') + taskMgr.remove(taskName) + taskName = self.uniqueName('next-catalog') + taskMgr.remove(taskName) + taskName = self.uniqueName('next-delivery') + taskMgr.remove(taskName) + taskName = self.uniqueName('next-award-delivery') + taskMgr.remove(taskName) + taskName = 'next-bothDelivery-%s' % self.doId + taskMgr.remove(taskName) + self.stopToonUp() + del self.dna + if self.inventory: + self.inventory.unload() + del self.inventory + del self.experience + if simbase.wantPets: + PetLookerAI.PetLookerAI.destroy(self) + del self.kart + self.cleanupPing() + self.stopPing() + self._sendExitServerEvent() + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + DistributedPlayerAI.DistributedPlayerAI.delete(self) + return + + def deleteDummy(self): + self.notify.debug('----deleteDummy DistributedToonAI %d ' % self.doId) + if self.inventory: + self.inventory.unload() + del self.inventory + self.experience = None + taskName = self.uniqueName('next-catalog') + taskMgr.remove(taskName) + return + + def ban(self, comment): + simbase.air.banManager.ban(self.doId, self.DISLid, comment) + + def disconnect(self): + self.requestDelete() + + def patchDelete(self): + del self.dna + if self.inventory: + self.inventory.unload() + del self.inventory + del self.experience + if simbase.wantPets: + PetLookerAI.PetLookerAI.destroy(self) + self.doNotDeallocateChannel = True + self.zoneId = None + DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self) + DistributedPlayerAI.DistributedPlayerAI.delete(self) + return + + def handleLogicalZoneChange(self, newZoneId, oldZoneId): + DistributedAvatarAI.DistributedAvatarAI.handleLogicalZoneChange(self, newZoneId, oldZoneId) + if self.isPlayerControlled() and self.WantTpTrack: + messenger.send(self.staticGetLogicalZoneChangeAllEvent(), [newZoneId, oldZoneId, self]) + if self.cogIndex != -1 and not ToontownAccessAI.canWearSuit(self.doId, newZoneId): + if simbase.config.GetBool('cogsuit-hack-prevent', False): + self.b_setCogIndex(-1) + if not simbase.air.cogSuitMessageSent: + self.notify.warning('%s handleLogicalZoneChange as a suit: %s' % (self.doId, self.cogIndex)) + self.air.writeServerEvent('suspicious', self.doId, 'Toon wearing a cog suit with index: %s in a zone they are not allowed to in. Zone: %s' % (self.cogIndex, newZoneId)) + simbase.air.cogSuitMessageSent = True + if simbase.config.GetBool('want-ban-wrong-suit-place', False): + commentStr = 'Toon %s wearing a suit in a zone they are not allowed to in. Zone: %s' % (self.doId, newZoneId) + dislId = self.DISLid + simbase.air.banManager.ban(self.doId, dislId, commentStr) + + def announceZoneChange(self, newZoneId, oldZoneId): + from toontown.pets import PetObserve + self.air.welcomeValleyManager.toonSetZone(self.doId, newZoneId) + broadcastZones = [oldZoneId, newZoneId] + if self.isInEstate() or self.wasInEstate(): + broadcastZones = union(broadcastZones, self.estateZones) + PetObserve.send(broadcastZones, PetObserve.PetActionObserve(PetObserve.Actions.CHANGE_ZONE, self.doId, (oldZoneId, newZoneId))) + + def checkAccessorySanity(self, accessoryType, idx, textureIdx, colorIdx): + if idx == 0 and textureIdx == 0 and colorIdx == 0: + return 1 + if accessoryType == ToonDNA.HAT: + stylesDict = ToonDNA.HatStyles + accessoryTypeStr = 'Hat' + elif accessoryType == ToonDNA.GLASSES: + stylesDict = ToonDNA.GlassesStyles + accessoryTypeStr = 'Glasses' + elif accessoryType == ToonDNA.BACKPACK: + stylesDict = ToonDNA.BackpackStyles + accessoryTypeStr = 'Backpack' + elif accessoryType == ToonDNA.SHOES: + stylesDict = ToonDNA.ShoesStyles + accessoryTypeStr = 'Shoes' + else: + return 0 + try: + styleStr = stylesDict.keys()[stylesDict.values().index([idx, textureIdx, colorIdx])] + accessoryItemId = 0 + for itemId in CatalogAccessoryItem.AccessoryTypes.keys(): + if styleStr == CatalogAccessoryItem.AccessoryTypes[itemId][CatalogAccessoryItem.ATString]: + accessoryItemId = itemId + break + + if accessoryItemId == 0: + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to wear invalid %s %d %d %d' % (accessoryTypeStr, + idx, + textureIdx, + colorIdx)) + return 0 + if not simbase.config.GetBool('want-check-accessory-sanity', False): + return 1 + accessoryItem = CatalogAccessoryItem.CatalogAccessoryItem(accessoryItemId) + result = self.air.catalogManager.isItemReleased(accessoryItem) + if result == 0: + self.air.writeServerEvent('suspicious', self.doId, 'Toon wore unreleased accessoryItem %d' % accessoryItemId) + return result + except: + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to wear invalid %s %d %d %d' % (accessoryTypeStr, + idx, + textureIdx, + colorIdx)) + return 0 + + def b_setHat(self, idx, textureIdx, colorIdx): + self.d_setHat(idx, textureIdx, colorIdx) + self.setHat(idx, textureIdx, colorIdx) + + def d_setHat(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.HAT, idx, textureIdx, colorIdx): + pass + self.sendUpdate('setHat', [idx, textureIdx, colorIdx]) + + def setHat(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.HAT, idx, textureIdx, colorIdx): + pass + self.hat = (idx, textureIdx, colorIdx) + + def getHat(self): + return self.hat + + def b_setGlasses(self, idx, textureIdx, colorIdx): + self.d_setGlasses(idx, textureIdx, colorIdx) + self.setGlasses(idx, textureIdx, colorIdx) + + def d_setGlasses(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.GLASSES, idx, textureIdx, colorIdx): + pass + self.sendUpdate('setGlasses', [idx, textureIdx, colorIdx]) + + def setGlasses(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.GLASSES, idx, textureIdx, colorIdx): + pass + self.glasses = (idx, textureIdx, colorIdx) + + def getGlasses(self): + return self.glasses + + def b_setBackpack(self, idx, textureIdx, colorIdx): + self.d_setBackpack(idx, textureIdx, colorIdx) + self.setBackpack(idx, textureIdx, colorIdx) + + def d_setBackpack(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.BACKPACK, idx, textureIdx, colorIdx): + pass + self.sendUpdate('setBackpack', [idx, textureIdx, colorIdx]) + + def setBackpack(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.BACKPACK, idx, textureIdx, colorIdx): + pass + self.backpack = (idx, textureIdx, colorIdx) + + def getBackpack(self): + return self.backpack + + def b_setShoes(self, idx, textureIdx, colorIdx): + self.d_setShoes(idx, textureIdx, colorIdx) + self.setShoes(idx, textureIdx, colorIdx) + + def d_setShoes(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.SHOES, idx, textureIdx, colorIdx): + pass + self.sendUpdate('setShoes', [idx, textureIdx, colorIdx]) + + def setShoes(self, idx, textureIdx, colorIdx): + if not self.checkAccessorySanity(ToonDNA.SHOES, idx, textureIdx, colorIdx): + pass + self.shoes = (idx, textureIdx, colorIdx) + + def getShoes(self): + return self.shoes + + def b_setDNAString(self, string): + self.d_setDNAString(string) + self.setDNAString(string) + + def d_setDNAString(self, string): + self.sendUpdate('setDNAString', [string]) + + def setDNAString(self, string): + self.dna.makeFromNetString(string) + if simbase.config.GetBool('adjust-dna', True) and self.verifyDNA() == False: + logStr = 'AvatarHackWarning! invalid dna colors for %s old: %s new: %s' % (self.doId, str(ToonDNA.ToonDNA(string).asTuple()), str(self.dna.asTuple())) + self.notify.warning(logStr) + self.air.writeServerEvent('suspicious', self.doId, logStr) + + def verifyDNA(self): + changed = False + if self.isPlayerControlled(): + allowedColors = [] + if self.dna.gender == 'm': + allowedColors = ToonDNA.defaultBoyColorList + [26] + else: + allowedColors = ToonDNA.defaultGirlColorList + [26] + if self.dna.legColor not in allowedColors: + self.dna.legColor = allowedColors[0] + changed = True + if self.dna.armColor not in allowedColors: + self.dna.armColor = allowedColors[0] + changed = True + if self.dna.headColor not in allowedColors: + self.dna.headColor = allowedColors[0] + changed = True + if changed: + self.d_setDNAString(self.dna.makeNetString()) + return not changed + + def getDNAString(self): + return self.dna.makeNetString() + + def getStyle(self): + return self.dna + + def b_setExperience(self, experience): + self.d_setExperience(experience) + self.setExperience(experience) + + def d_setExperience(self, experience): + self.sendUpdate('setExperience', [experience]) + + def setExperience(self, experience): + self.experience = Experience.Experience(experience, self) + + def getExperience(self): + return self.experience.makeNetString() + + def b_setInventory(self, inventory): + self.setInventory(inventory) + self.d_setInventory(self.getInventory()) + + def d_setInventory(self, inventory): + self.sendUpdate('setInventory', [inventory]) + + def setInventory(self, inventoryNetString): + if self.inventory: + self.inventory.updateInvString(inventoryNetString) + else: + self.inventory = InventoryBase.InventoryBase(self, inventoryNetString) + emptyInv = InventoryBase.InventoryBase(self) + emptyString = emptyInv.makeNetString() + lengthMatch = len(inventoryNetString) - len(emptyString) + if lengthMatch != 0: + if len(inventoryNetString) == 42: + oldTracks = 7 + oldLevels = 6 + elif len(inventoryNetString) == 49: + oldTracks = 7 + oldLevels = 7 + else: + oldTracks = 0 + oldLevels = 0 + if oldTracks == 0 and oldLevels == 0: + self.notify.warning('reseting invalid inventory to MAX on toon: %s' % self.doId) + self.inventory.zeroInv() + self.inventory.maxOutInv(1, 1) + else: + newInventory = InventoryBase.InventoryBase(self) + oldList = emptyInv.makeFromNetStringForceSize(inventoryNetString, oldTracks, oldLevels) + for indexTrack in range(0, oldTracks): + for indexGag in range(0, oldLevels): + newInventory.addItems(indexTrack, indexGag, oldList[indexTrack][indexGag]) + + self.inventory.unload() + self.inventory = newInventory + self.d_setInventory(self.getInventory()) + + def getInventory(self): + return self.inventory.makeNetString() + + def doRestock(self, noUber = 1, noPaid = 1): + self.inventory.zeroInv() + self.inventory.maxOutInv(noUber, noPaid) + self.d_setInventory(self.inventory.makeNetString()) + + def setDefaultShard(self, shard): + self.defaultShard = shard + self.notify.debug('setting default shard to %s' % shard) + + def getDefaultShard(self): + return self.defaultShard + + def setDefaultZone(self, zone): + self.defaultZone = zone + self.notify.debug('setting default zone to %s' % zone) + + def getDefaultZone(self): + return self.defaultZone + + def setShtickerBook(self, string): + self.notify.debug('setting shticker book to %s' % string) + + def getShtickerBook(self): + return '' + + def d_setFriendsList(self, friendsList): + self.sendUpdate('setFriendsList', [friendsList]) + return None + + def setFriendsList(self, friendsList): + self.notify.debug('setting friends list to %s' % self.friendsList) + self.friendsList = friendsList + if friendsList: + friendId = friendsList[-1] + otherAv = self.air.doId2do.get(friendId) + self.air.questManager.toonMadeFriend(self, otherAv) + + 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)) + + def d_setMaxNPCFriends(self, max): + self.sendUpdate('setMaxNPCFriends', [max]) + + def setMaxNPCFriends(self, max): + if max & 32768: + self.b_setSosPageFlag(1) + max &= 32767 + configMax = simbase.config.GetInt('max-sos-cards', 16) + if configMax != max: + if self.sosPageFlag == 0: + self.b_setMaxNPCFriends(configMax) + else: + self.b_setMaxNPCFriends(configMax | 32768) + else: + self.maxNPCFriends = max + if self.maxNPCFriends != 8 and self.maxNPCFriends != 16: + self.notify.warning('Wrong max SOS cards %s, %d' % (self.maxNPCFriends, self.doId)) + + def b_setMaxNPCFriends(self, max): + self.setMaxNPCFriends(max) + self.d_setMaxNPCFriends(max) + + def getMaxNPCFriends(self): + return self.maxNPCFriends + + def getBattleId(self): + if self.battleId >= 0: + return self.battleId + else: + return 0 + + def b_setBattleId(self, battleId): + self.setBattleId(battleId) + self.d_setBattleId(battleId) + + def d_setBattleId(self, battleId): + if self.battleId >= 0: + self.sendUpdate('setBattleId', [battleId]) + else: + self.sendUpdate('setBattleId', [0]) + + def setBattleId(self, battleId): + self.battleId = battleId + + def d_setNPCFriendsDict(self, NPCFriendsDict): + NPCFriendsList = [] + for friend in NPCFriendsDict.keys(): + NPCFriendsList.append((friend, NPCFriendsDict[friend])) + + self.sendUpdate('setNPCFriendsDict', [NPCFriendsList]) + return None + + def setNPCFriendsDict(self, NPCFriendsList): + self.NPCFriendsDict = {} + for friendPair in NPCFriendsList: + self.NPCFriendsDict[friendPair[0]] = friendPair[1] + + self.notify.debug('setting NPC friends dict to %s' % self.NPCFriendsDict) + + def getNPCFriendsDict(self): + return self.NPCFriendsDict + + def b_setNPCFriendsDict(self, NPCFriendsList): + self.setNPCFriendsDict(NPCFriendsList) + self.d_setNPCFriendsDict(self.NPCFriendsDict) + + def resetNPCFriendsDict(self): + self.b_setNPCFriendsDict([]) + + def attemptAddNPCFriend(self, npcFriend, numCalls = 1): + if numCalls <= 0: + self.notify.warning('invalid numCalls: %d' % numCalls) + return 0 + if self.NPCFriendsDict.has_key(npcFriend): + self.NPCFriendsDict[npcFriend] += numCalls + elif npcFriends.has_key(npcFriend): + if len(self.NPCFriendsDict.keys()) >= self.maxNPCFriends: + return 0 + self.NPCFriendsDict[npcFriend] = numCalls + else: + self.notify.warning('invalid NPC: %d' % npcFriend) + return 0 + if self.NPCFriendsDict[npcFriend] > self.maxCallsPerNPC: + self.NPCFriendsDict[npcFriend] = self.maxCallsPerNPC + self.d_setNPCFriendsDict(self.NPCFriendsDict) + if self.sosPageFlag == 0: + self.b_setMaxNPCFriends(self.maxNPCFriends | 32768) + return 1 + + def attemptSubtractNPCFriend(self, npcFriend): + if not self.NPCFriendsDict.has_key(npcFriend): + self.notify.warning('attemptSubtractNPCFriend: invalid NPC %s' % npcFriend) + return 0 + if hasattr(self, 'autoRestockSOS') and self.autoRestockSOS: + cost = 0 + else: + cost = 1 + self.NPCFriendsDict[npcFriend] -= cost + if self.NPCFriendsDict[npcFriend] <= 0: + del self.NPCFriendsDict[npcFriend] + self.d_setNPCFriendsDict(self.NPCFriendsDict) + return 1 + + def restockAllNPCFriends(self): + desiredNpcFriends = [2001, + 2011, + 3112, + 4119, + 1116, + 3137, + 3135] + self.resetNPCFriendsDict() + for npcId in desiredNpcFriends: + self.attemptAddNPCFriend(npcId, 1) + + def d_setMaxAccessories(self, max): + self.sendUpdate('setMaxAccessories', [self.maxAccessories]) + + def setMaxAccessories(self, max): + self.maxAccessories = max + + def b_setMaxAccessories(self, max): + self.setMaxAccessories(max) + self.d_setMaxAccessories(max) + + def getMaxAccessories(self): + return self.maxAccessories + + def isTrunkFull(self, extraAccessories = 0): + numAccessories = (len(self.hatList) + len(self.glassesList) + len(self.backpackList) + len(self.shoesList)) / 3 + return numAccessories + extraAccessories >= self.maxAccessories + + def d_setHatList(self, clothesList): + self.sendUpdate('setHatList', [clothesList]) + return None + + def setHatList(self, clothesList): + self.hatList = clothesList + + def b_setHatList(self, clothesList): + self.setHatList(clothesList) + self.d_setHatList(clothesList) + + def getHatList(self): + return self.hatList + + def d_setGlassesList(self, clothesList): + self.sendUpdate('setGlassesList', [clothesList]) + return None + + def setGlassesList(self, clothesList): + self.glassesList = clothesList + + def b_setGlassesList(self, clothesList): + self.setGlassesList(clothesList) + self.d_setGlassesList(clothesList) + + def getGlassesList(self): + return self.glassesList + + def d_setBackpackList(self, clothesList): + self.sendUpdate('setBackpackList', [clothesList]) + return None + + def setBackpackList(self, clothesList): + self.backpackList = clothesList + + def b_setBackpackList(self, clothesList): + self.setBackpackList(clothesList) + self.d_setBackpackList(clothesList) + + def getBackpackList(self): + return self.backpackList + + def d_setShoesList(self, clothesList): + self.sendUpdate('setShoesList', [clothesList]) + return None + + def setShoesList(self, clothesList): + self.shoesList = clothesList + + def b_setShoesList(self, clothesList): + self.setShoesList(clothesList) + self.d_setShoesList(clothesList) + + def getShoesList(self): + return self.shoesList + + def addToAccessoriesList(self, accessoryType, geomIdx, texIdx, colorIdx): + if self.isTrunkFull(): + return 0 + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + else: + return 0 + index = 0 + for i in range(0, len(itemList), 3): + if itemList[i] == geomIdx and itemList[i + 1] == texIdx and itemList[i + 2] == colorIdx: + return 0 + + if accessoryType == ToonDNA.HAT: + self.hatList.append(geomIdx) + self.hatList.append(texIdx) + self.hatList.append(colorIdx) + elif accessoryType == ToonDNA.GLASSES: + self.glassesList.append(geomIdx) + self.glassesList.append(texIdx) + self.glassesList.append(colorIdx) + elif accessoryType == ToonDNA.BACKPACK: + self.backpackList.append(geomIdx) + self.backpackList.append(texIdx) + self.backpackList.append(colorIdx) + elif accessoryType == ToonDNA.SHOES: + self.shoesList.append(geomIdx) + self.shoesList.append(texIdx) + self.shoesList.append(colorIdx) + return 1 + + def replaceItemInAccessoriesList(self, accessoryType, geomIdxA, texIdxA, colorIdxA, geomIdxB, texIdxB, colorIdxB): + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + else: + return 0 + index = 0 + for i in range(0, len(itemList), 3): + if itemList[i] == geomIdxA and itemList[i + 1] == texIdxA and itemList[i + 2] == colorIdxA: + if accessoryType == ToonDNA.HAT: + self.hatList[i] = geomIdxB + self.hatList[i + 1] = texIdxB + self.hatList[i + 2] = colorIdxB + elif accessoryType == ToonDNA.GLASSES: + self.glassesList[i] = geomIdxB + self.glassesList[i + 1] = texIdxB + self.glassesList[i + 2] = colorIdxB + elif accessoryType == ToonDNA.BACKPACK: + self.backpackList[i] = geomIdxB + self.backpackList[i + 1] = texIdxB + self.backpackList[i + 2] = colorIdxB + else: + self.shoesList[i] = geomIdxB + self.shoesList[i + 1] = texIdxB + self.shoesList[i + 2] = colorIdxB + return 1 + + return 0 + + def hasAccessory(self, accessoryType, geomIdx, texIdx, colorIdx): + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + cur = self.hat + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + cur = self.glasses + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + cur = self.backpack + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + cur = self.shoes + else: + raise 'invalid accessory type %s' % accessoryType + if cur == (geomIdx, texIdx, colorIdx): + return True + for i in xrange(0, len(itemList), 3): + if itemList[i] == geomIdx and itemList[i + 1] == texIdx and itemList[i + 2] == colorIdx: + return True + + return False + + def isValidAccessorySetting(self, accessoryType, geomIdx, texIdx, colorIdx): + if not geomIdx and not texIdx and not colorIdx: + return True + return self.hasAccessory(accessoryType, geomIdx, texIdx, colorIdx) + + def removeItemInAccessoriesList(self, accessoryType, geomIdx, texIdx, colorIdx): + if accessoryType == ToonDNA.HAT: + itemList = self.hatList + elif accessoryType == ToonDNA.GLASSES: + itemList = self.glassesList + elif accessoryType == ToonDNA.BACKPACK: + itemList = self.backpackList + elif accessoryType == ToonDNA.SHOES: + itemList = self.shoesList + else: + return 0 + listLen = len(itemList) + if listLen < 3: + self.notify.warning('Accessory list is not long enough to delete anything') + return 0 + index = 0 + for i in range(0, len(itemList), 3): + if itemList[i] == geomIdx and itemList[i + 1] == texIdx and itemList[i + 2] == colorIdx: + itemList = itemList[0:i] + itemList[i + 3:listLen] + if accessoryType == ToonDNA.HAT: + self.hatList = itemList[:] + styles = ToonDNA.HatStyles + descDict = TTLocalizer.HatStylesDescriptions + elif accessoryType == ToonDNA.GLASSES: + self.glassesList = itemList[:] + styles = ToonDNA.GlassesStyles + descDict = TTLocalizer.GlassesStylesDescriptions + elif accessoryType == ToonDNA.BACKPACK: + self.backpackList = itemList[:] + styles = ToonDNA.BackpackStyles + descDict = TTLocalizer.BackpackStylesDescriptions + elif accessoryType == ToonDNA.SHOES: + self.shoesList = itemList[:] + styles = ToonDNA.ShoesStyles + descDict = TTLocalizer.ShoesStylesDescriptions + styleName = 'none' + for style in styles.items(): + if style[1] == [geomIdx, texIdx, colorIdx]: + styleName = style[0] + break + + if styleName == 'none' or not descDict.has_key(styleName): + self.air.writeServerEvent('suspicious', self.doId, ' tried to remove wrong accessory code %d %d %d' % (geomIdx, texIdx, colorIdx)) + else: + self.air.writeServerEvent('accessory', self.doId, ' removed accessory %s' % descDict[styleName]) + return 1 + + return 0 + + def d_setMaxClothes(self, max): + self.sendUpdate('setMaxClothes', [self.maxClothes]) + + def setMaxClothes(self, max): + self.maxClothes = max + + def b_setMaxClothes(self, max): + self.setMaxClothes(max) + self.d_setMaxClothes(max) + + def getMaxClothes(self): + return self.maxClothes + + def isClosetFull(self, extraClothes = 0): + numClothes = len(self.clothesTopsList) / 4 + len(self.clothesBottomsList) / 2 + return numClothes + extraClothes >= self.maxClothes + + def d_setClothesTopsList(self, clothesList): + self.sendUpdate('setClothesTopsList', [clothesList]) + return None + + def setClothesTopsList(self, clothesList): + self.clothesTopsList = clothesList + + def b_setClothesTopsList(self, clothesList): + self.setClothesTopsList(clothesList) + self.d_setClothesTopsList(clothesList) + + def getClothesTopsList(self): + return self.clothesTopsList + + def addToClothesTopsList(self, topTex, topTexColor, sleeveTex, sleeveTexColor): + if self.isClosetFull(): + return 0 + index = 0 + for i in range(0, len(self.clothesTopsList), 4): + if self.clothesTopsList[i] == topTex and self.clothesTopsList[i + 1] == topTexColor and self.clothesTopsList[i + 2] == sleeveTex and self.clothesTopsList[i + 3] == sleeveTexColor: + return 0 + + self.clothesTopsList.append(topTex) + self.clothesTopsList.append(topTexColor) + self.clothesTopsList.append(sleeveTex) + self.clothesTopsList.append(sleeveTexColor) + return 1 + + def replaceItemInClothesTopsList(self, topTexA, topTexColorA, sleeveTexA, sleeveTexColorA, topTexB, topTexColorB, sleeveTexB, sleeveTexColorB): + index = 0 + for i in range(0, len(self.clothesTopsList), 4): + if self.clothesTopsList[i] == topTexA and self.clothesTopsList[i + 1] == topTexColorA and self.clothesTopsList[i + 2] == sleeveTexA and self.clothesTopsList[i + 3] == sleeveTexColorA: + self.clothesTopsList[i] = topTexB + self.clothesTopsList[i + 1] = topTexColorB + self.clothesTopsList[i + 2] = sleeveTexB + self.clothesTopsList[i + 3] = sleeveTexColorB + return 1 + + return 0 + + def removeItemInClothesTopsList(self, topTex, topTexColor, sleeveTex, sleeveTexColor): + listLen = len(self.clothesTopsList) + if listLen < 4: + self.notify.warning('Clothes top list is not long enough to delete anything') + return 0 + index = 0 + for i in range(0, listLen, 4): + if self.clothesTopsList[i] == topTex and self.clothesTopsList[i + 1] == topTexColor and self.clothesTopsList[i + 2] == sleeveTex and self.clothesTopsList[i + 3] == sleeveTexColor: + self.clothesTopsList = self.clothesTopsList[0:i] + self.clothesTopsList[i + 4:listLen] + return 1 + + return 0 + + def d_setClothesBottomsList(self, clothesList): + self.sendUpdate('setClothesBottomsList', [clothesList]) + return None + + def setClothesBottomsList(self, clothesList): + self.clothesBottomsList = clothesList + + def b_setClothesBottomsList(self, clothesList): + self.setClothesBottomsList(clothesList) + self.d_setClothesBottomsList(clothesList) + + def getClothesBottomsList(self): + return self.clothesBottomsList + + def addToClothesBottomsList(self, botTex, botTexColor): + if self.isClosetFull(): + self.notify.warning('clothes bottoms list is full') + return 0 + index = 0 + for i in range(0, len(self.clothesBottomsList), 2): + if self.clothesBottomsList[i] == botTex and self.clothesBottomsList[i + 1] == botTexColor: + return 0 + + self.clothesBottomsList.append(botTex) + self.clothesBottomsList.append(botTexColor) + return 1 + + def replaceItemInClothesBottomsList(self, botTexA, botTexColorA, botTexB, botTexColorB): + index = 0 + for i in range(0, len(self.clothesBottomsList), 2): + if self.clothesBottomsList[i] == botTexA and self.clothesBottomsList[i + 1] == botTexColorA: + self.clothesBottomsList[i] = botTexB + self.clothesBottomsList[i + 1] = botTexColorB + return 1 + + return 0 + + def removeItemInClothesBottomsList(self, botTex, botTexColor): + listLen = len(self.clothesBottomsList) + if listLen < 2: + self.notify.warning('Clothes bottoms list is not long enough to delete anything') + return 0 + index = 0 + for i in range(0, len(self.clothesBottomsList), 2): + if self.clothesBottomsList[i] == botTex and self.clothesBottomsList[i + 1] == botTexColor: + self.clothesBottomsList = self.clothesBottomsList[0:i] + self.clothesBottomsList[i + 2:listLen] + return 1 + + return 0 + + def d_catalogGenClothes(self): + self.sendUpdate('catalogGenClothes', [self.doId]) + + def d_catalogGenAccessories(self): + self.sendUpdate('catalogGenAccessories', [self.doId]) + + def takeDamage(self, hpLost, quietly = 0, sendTotal = 1): + if not self.immortalMode: + if not quietly: + self.sendUpdate('takeDamage', [hpLost]) + if hpLost > 0 and self.hp > 0: + self.hp -= hpLost + if self.hp <= 0: + self.hp = -1 + messenger.send(self.getGoneSadMessage()) + if not self.hpOwnedByBattle: + self.hp = min(self.hp, self.maxHp) + if sendTotal: + self.d_setHp(self.hp) + + @staticmethod + def getGoneSadMessageForAvId(avId): + return 'goneSad-%s' % avId + + def getGoneSadMessage(self): + return self.getGoneSadMessageForAvId(self.doId) + + def setHp(self, hp): + DistributedPlayerAI.DistributedPlayerAI.setHp(self, hp) + if hp <= 0: + messenger.send(self.getGoneSadMessage()) + + def b_setTutorialAck(self, tutorialAck): + self.d_setTutorialAck(tutorialAck) + self.setTutorialAck(tutorialAck) + + def d_setTutorialAck(self, tutorialAck): + self.sendUpdate('setTutorialAck', [tutorialAck]) + + def setTutorialAck(self, tutorialAck): + self.tutorialAck = tutorialAck + + def getTutorialAck(self): + return self.tutorialAck + + def d_setEarnedExperience(self, earnedExp): + self.sendUpdate('setEarnedExperience', [earnedExp]) + + def setInterface(self, string): + self.notify.debug('setting interface to %s' % string) + + def getInterface(self): + return '' + + def setZonesVisited(self, hoods): + self.safeZonesVisited = hoods + self.notify.debug('setting safe zone list to %s' % self.safeZonesVisited) + + def getZonesVisited(self): + return self.safeZonesVisited + + def setHoodsVisited(self, hoods): + self.hoodsVisited = hoods + self.notify.debug('setting hood zone list to %s' % self.hoodsVisited) + + def getHoodsVisited(self): + return self.hoodsVisited + + def setLastHood(self, hood): + self.lastHood = hood + + def getLastHood(self): + return self.lastHood + + def b_setAnimState(self, animName, animMultiplier): + self.setAnimState(animName, animMultiplier) + self.d_setAnimState(animName, animMultiplier) + + def d_setAnimState(self, animName, animMultiplier): + timestamp = globalClockDelta.getRealNetworkTime() + self.sendUpdate('setAnimState', [animName, animMultiplier, timestamp]) + return None + + def setAnimState(self, animName, animMultiplier, timestamp = 0): + if animName not in ToontownGlobals.ToonAnimStates: + desc = 'tried to set invalid animState: %s' % (animName,) + if config.GetBool('want-ban-animstate', 1): + simbase.air.banManager.ban(self.doId, self.DISLid, desc) + else: + self.air.writeServerEvent('suspicious', self.doId, desc) + return + self.animName = animName + self.animMultiplier = animMultiplier + + def b_setCogStatus(self, cogStatusList): + self.setCogStatus(cogStatusList) + self.d_setCogStatus(cogStatusList) + + def setCogStatus(self, cogStatusList): + self.notify.debug('setting cogs to %s' % cogStatusList) + self.cogs = cogStatusList + + def d_setCogStatus(self, cogStatusList): + self.sendUpdate('setCogStatus', [cogStatusList]) + + def getCogStatus(self): + return self.cogs + + def b_setCogCount(self, cogCountList): + self.setCogCount(cogCountList) + self.d_setCogCount(cogCountList) + + def setCogCount(self, cogCountList): + self.notify.debug('setting cogCounts to %s' % cogCountList) + self.cogCounts = cogCountList + + def d_setCogCount(self, cogCountList): + self.sendUpdate('setCogCount', [cogCountList]) + + def getCogCount(self): + return self.cogCounts + + def b_setCogRadar(self, radar): + self.setCogRadar(radar) + self.d_setCogRadar(radar) + + def setCogRadar(self, radar): + if not radar: + self.notify.warning('cogRadar set to bad value: %s. Resetting to [0,0,0,0]' % radar) + self.cogRadar = [0, + 0, + 0, + 0] + else: + self.cogRadar = radar + + def d_setCogRadar(self, radar): + self.sendUpdate('setCogRadar', [radar]) + + def getCogRadar(self): + return self.cogRadar + + def b_setBuildingRadar(self, radar): + self.setBuildingRadar(radar) + self.d_setBuildingRadar(radar) + + def setBuildingRadar(self, radar): + if not radar: + self.notify.warning('buildingRadar set to bad value: %s. Resetting to [0,0,0,0]' % radar) + self.buildingRadar = [0, + 0, + 0, + 0] + else: + self.buildingRadar = radar + + def d_setBuildingRadar(self, radar): + self.sendUpdate('setBuildingRadar', [radar]) + + def getBuildingRadar(self): + return self.buildingRadar + + def b_setCogTypes(self, types): + self.setCogTypes(types) + self.d_setCogTypes(types) + + def setCogTypes(self, types): + if not types: + self.notify.warning('cogTypes set to bad value: %s. Resetting to [0,0,0,0]' % types) + self.cogTypes = [0, + 0, + 0, + 0] + else: + self.cogTypes = types + + def d_setCogTypes(self, types): + self.sendUpdate('setCogTypes', [types]) + + def getCogTypes(self): + return self.cogTypes + + def b_setCogLevels(self, levels): + self.setCogLevels(levels) + self.d_setCogLevels(levels) + + def setCogLevels(self, levels): + if not levels: + self.notify.warning('cogLevels set to bad value: %s. Resetting to [0,0,0,0]' % levels) + self.cogLevels = [0, + 0, + 0, + 0] + else: + self.cogLevels = levels + + def d_setCogLevels(self, levels): + self.sendUpdate('setCogLevels', [levels]) + + def getCogLevels(self): + return self.cogLevels + + def incCogLevel(self, dept): + newLevel = self.cogLevels[dept] + 1 + cogTypeStr = SuitDNA.suitHeadTypes[self.cogTypes[dept]] + lastCog = self.cogTypes[dept] >= SuitDNA.suitsPerDept - 1 + if not lastCog: + maxLevel = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + 4 + else: + maxLevel = ToontownGlobals.MaxCogSuitLevel + if newLevel > maxLevel: + if not lastCog: + self.cogTypes[dept] += 1 + self.d_setCogTypes(self.cogTypes) + cogTypeStr = SuitDNA.suitHeadTypes[self.cogTypes[dept]] + self.cogLevels[dept] = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + self.d_setCogLevels(self.cogLevels) + else: + self.cogLevels[dept] += 1 + self.d_setCogLevels(self.cogLevels) + if lastCog: + if self.cogLevels[dept] in ToontownGlobals.CogSuitHPLevels: + maxHp = self.getMaxHp() + maxHp = min(ToontownGlobals.MaxHpLimit, maxHp + 1) + self.b_setMaxHp(maxHp) + self.toonUp(maxHp) + self.air.writeServerEvent('cogSuit', self.doId, '%s|%s|%s' % (dept, self.cogTypes[dept], self.cogLevels[dept])) + + def getNumPromotions(self, dept): + if dept not in SuitDNA.suitDepts: + self.notify.warning('getNumPromotions: Invalid parameter dept=%s' % dept) + return 0 + deptIndex = SuitDNA.suitDepts.index(dept) + cogType = self.cogTypes[deptIndex] + cogTypeStr = SuitDNA.suitHeadTypes[cogType] + lowestCogLevel = SuitBattleGlobals.SuitAttributes[cogTypeStr]['level'] + multiple = 5 * cogType + additional = self.cogLevels[deptIndex] - lowestCogLevel + numPromotions = multiple + additional + return numPromotions + + def b_setCogParts(self, parts): + self.setCogParts(parts) + self.d_setCogParts(parts) + + def setCogParts(self, parts): + if not parts: + self.notify.warning('cogParts set to bad value: %s. Resetting to [0,0,0,0]' % parts) + self.cogParts = [0, + 0, + 0, + 0] + else: + self.cogParts = parts + + def d_setCogParts(self, parts): + self.sendUpdate('setCogParts', [parts]) + + def getCogParts(self): + return self.cogParts + + def giveCogPart(self, part, dept): + dept = CogDisguiseGlobals.dept2deptIndex(dept) + parts = self.getCogParts() + parts[dept] = parts[dept] | part + self.b_setCogParts(parts) + + def hasCogPart(self, part, dept): + dept = CogDisguiseGlobals.dept2deptIndex(dept) + if self.cogParts[dept] & part: + return 1 + else: + return 0 + + def giveGenericCogPart(self, factoryType, dept): + for partTypeId in self.partTypeIds[factoryType]: + nextPart = CogDisguiseGlobals.getNextPart(self.getCogParts(), partTypeId, dept) + if nextPart: + break + + if nextPart: + self.giveCogPart(nextPart, dept) + return nextPart + else: + return None + return None + + def takeCogPart(self, part, dept): + dept = CogDisguiseGlobals.dept2deptIndex(dept) + parts = self.getCogParts() + if parts[dept] & part: + parts[dept] = parts[dept] ^ part + self.b_setCogParts(parts) + + def loseCogParts(self, dept): + loseCount = random.randrange(CogDisguiseGlobals.MinPartLoss, CogDisguiseGlobals.MaxPartLoss + 1) + parts = self.getCogParts() + partBitmask = parts[dept] + partList = range(17) + while loseCount > 0 and partList: + losePart = random.choice(partList) + partList.remove(losePart) + losePartBit = 1 << losePart + if partBitmask & losePartBit: + partBitmask &= ~losePartBit + loseCount -= 1 + + parts[dept] = partBitmask + self.b_setCogParts(parts) + + def b_setCogMerits(self, merits): + self.setCogMerits(merits) + self.d_setCogMerits(merits) + + def setCogMerits(self, merits): + if not merits: + self.notify.warning('cogMerits set to bad value: %s. Resetting to [0,0,0,0]' % merits) + self.cogMerits = [0, + 0, + 0, + 0] + else: + self.cogMerits = merits + + def d_setCogMerits(self, merits): + self.sendUpdate('setCogMerits', [merits]) + + def getCogMerits(self): + return self.cogMerits + + def b_promote(self, dept): + self.promote(dept) + self.d_promote(dept) + + def promote(self, dept): + if self.cogLevels[dept] < ToontownGlobals.MaxCogSuitLevel: + self.cogMerits[dept] = 0 + self.incCogLevel(dept) + + def d_promote(self, dept): + merits = self.getCogMerits() + if self.cogLevels[dept] < ToontownGlobals.MaxCogSuitLevel: + merits[dept] = 0 + self.d_setCogMerits(merits) + + def readyForPromotion(self, dept): + merits = self.cogMerits[dept] + totalMerits = CogDisguiseGlobals.getTotalMerits(self, dept) + if merits >= totalMerits: + return 1 + else: + return 0 + + def b_setCogIndex(self, index): + self.setCogIndex(index) + if simbase.config.GetBool('cogsuit-hack-prevent', False): + self.d_setCogIndex(self.cogIndex) + else: + self.d_setCogIndex(index) + + def setCogIndex(self, index): + if index != -1 and not ToontownAccessAI.canWearSuit(self.doId, self.zoneId): + if not simbase.air.cogSuitMessageSent: + self.notify.warning('%s setCogIndex invalid: %s' % (self.doId, index)) + if simbase.config.GetBool('want-ban-wrong-suit-place', False): + commentStr = 'Toon %s trying to set cog index to %s in Zone: %s' % (self.doId, index, self.zoneId) + simbase.air.banManager.ban(self.doId, self.DISLid, commentStr) + else: + self.cogIndex = index + + def d_setCogIndex(self, index): + self.sendUpdate('setCogIndex', [index]) + + def getCogIndex(self): + return self.cogIndex + + def b_setDisguisePageFlag(self, flag): + self.setDisguisePageFlag(flag) + self.d_setDisguisePageFlag(flag) + + def setDisguisePageFlag(self, flag): + self.disguisePageFlag = flag + + def d_setDisguisePageFlag(self, flag): + self.sendUpdate('setDisguisePageFlag', [flag]) + + def getDisguisePageFlag(self): + return self.disguisePageFlag + + def b_setSosPageFlag(self, flag): + self.setSosPageFlag(flag) + self.d_setSosPageFlag(flag) + + def setSosPageFlag(self, flag): + self.sosPageFlag = flag + + def d_setSosPageFlag(self, flag): + self.sendUpdate('setSosPageFlag', [flag]) + + def getSosPageFlag(self): + return self.sosPageFlag + + def b_setFishCollection(self, genusList, speciesList, weightList): + self.setFishCollection(genusList, speciesList, weightList) + self.d_setFishCollection(genusList, speciesList, weightList) + + def d_setFishCollection(self, genusList, speciesList, weightList): + self.sendUpdate('setFishCollection', [genusList, speciesList, weightList]) + + def setFishCollection(self, genusList, speciesList, weightList): + self.fishCollection = FishCollection.FishCollection() + self.fishCollection.makeFromNetLists(genusList, speciesList, weightList) + + def getFishCollection(self): + return self.fishCollection.getNetLists() + + def b_setMaxFishTank(self, maxTank): + self.d_setMaxFishTank(maxTank) + self.setMaxFishTank(maxTank) + + def d_setMaxFishTank(self, maxTank): + self.sendUpdate('setMaxFishTank', [maxTank]) + + def setMaxFishTank(self, maxTank): + self.maxFishTank = maxTank + + def getMaxFishTank(self): + return self.maxFishTank + + def b_setFishTank(self, genusList, speciesList, weightList): + self.setFishTank(genusList, speciesList, weightList) + self.d_setFishTank(genusList, speciesList, weightList) + + def d_setFishTank(self, genusList, speciesList, weightList): + self.sendUpdate('setFishTank', [genusList, speciesList, weightList]) + + def setFishTank(self, genusList, speciesList, weightList): + self.fishTank = FishTank.FishTank() + self.fishTank.makeFromNetLists(genusList, speciesList, weightList) + + def getFishTank(self): + return self.fishTank.getNetLists() + + def makeRandomFishTank(self): + self.fishTank.generateRandomTank() + self.d_setFishTank(*self.fishTank.getNetLists()) + + def addFishToTank(self, fish): + numFish = len(self.fishTank) + if numFish >= self.maxFishTank: + self.notify.warning('addFishToTank: cannot add fish, tank is full') + return 0 + elif self.fishTank.addFish(fish): + self.d_setFishTank(*self.fishTank.getNetLists()) + return 1 + else: + self.notify.warning('addFishToTank: addFish failed') + return 0 + + def removeFishFromTankAtIndex(self, index): + if self.fishTank.removeFishAtIndex(index): + self.d_setFishTank(*self.fishTank.getNetLists()) + return 1 + else: + self.notify.warning('removeFishFromTank: cannot find fish') + return 0 + + def b_setFishingRod(self, rodId): + self.d_setFishingRod(rodId) + self.setFishingRod(rodId) + + def d_setFishingRod(self, rodId): + self.sendUpdate('setFishingRod', [rodId]) + + def setFishingRod(self, rodId): + self.fishingRod = rodId + + def getFishingRod(self): + return self.fishingRod + + def b_setFishingTrophies(self, trophyList): + self.setFishingTrophies(trophyList) + self.d_setFishingTrophies(trophyList) + + def setFishingTrophies(self, trophyList): + self.notify.debug('setting fishingTrophies to %s' % trophyList) + self.fishingTrophies = trophyList + + def d_setFishingTrophies(self, trophyList): + self.sendUpdate('setFishingTrophies', [trophyList]) + + def getFishingTrophies(self): + return self.fishingTrophies + + def b_setQuests(self, questList): + flattenedQuests = [] + for quest in questList: + flattenedQuests.extend(quest) + + self.setQuests(flattenedQuests) + self.d_setQuests(flattenedQuests) + + def d_setQuests(self, flattenedQuests): + self.sendUpdate('setQuests', [flattenedQuests]) + + def setQuests(self, flattenedQuests): + self.notify.debug('setting quests to %s' % flattenedQuests) + questList = [] + questLen = 5 + for i in range(0, len(flattenedQuests), questLen): + questList.append(flattenedQuests[i:i + questLen]) + + self.quests = questList + + def getQuests(self): + flattenedQuests = [] + for quest in self.quests: + flattenedQuests.extend(quest) + + return flattenedQuests + + def getQuest(self, questId, visitNpcId = None, rewardId = None): + for quest in self.quests: + if quest[0] != questId: + continue + if visitNpcId != None: + if visitNpcId != quest[1] and visitNpcId != quest[2]: + continue + if rewardId != None: + if rewardId != quest[3]: + continue + return quest + + return + + def hasQuest(self, questId, visitNpcId = None, rewardId = None): + if self.getQuest(questId, visitNpcId=visitNpcId, rewardId=rewardId) == None: + return False + else: + return True + return + + def removeQuest(self, id, visitNpcId = None): + index = -1 + for i in range(len(self.quests)): + if self.quests[i][0] == id: + if visitNpcId: + otherId = self.quests[i][2] + if visitNpcId == otherId: + index = i + break + else: + index = i + break + + if index >= 0: + del self.quests[i] + self.b_setQuests(self.quests) + return 1 + else: + return 0 + + def addQuest(self, quest, finalReward, recordHistory = 1): + self.quests.append(quest) + self.b_setQuests(self.quests) + if recordHistory: + if quest[0] != Quests.VISIT_QUEST_ID: + newQuestHistory = self.questHistory + [quest[0]] + while newQuestHistory.count(Quests.VISIT_QUEST_ID) != 0: + newQuestHistory.remove(Quests.VISIT_QUEST_ID) + + self.b_setQuestHistory(newQuestHistory) + if finalReward: + newRewardHistory = self.rewardHistory + [finalReward] + self.b_setRewardHistory(self.rewardTier, newRewardHistory) + + def removeAllTracesOfQuest(self, questId, rewardId): + self.notify.debug('removeAllTracesOfQuest: questId: %s rewardId: %s' % (questId, rewardId)) + self.notify.debug('removeAllTracesOfQuest: quests before: %s' % self.quests) + removedQuest = self.removeQuest(questId) + self.notify.debug('removeAllTracesOfQuest: quests after: %s' % self.quests) + self.notify.debug('removeAllTracesOfQuest: questHistory before: %s' % self.questHistory) + removedQuestHistory = self.removeQuestFromHistory(questId) + self.notify.debug('removeAllTracesOfQuest: questHistory after: %s' % self.questHistory) + self.notify.debug('removeAllTracesOfQuest: reward history before: %s' % self.rewardHistory) + removedRewardHistory = self.removeRewardFromHistory(rewardId) + self.notify.debug('removeAllTracesOfQuest: reward history after: %s' % self.rewardHistory) + return (removedQuest, removedQuestHistory, removedRewardHistory) + + def requestDeleteQuest(self, questDesc): + if len(questDesc) != 5: + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to delete invalid questDesc %s' % str(questDesc)) + self.notify.warning('%s.requestDeleteQuest(%s) -- questDesc has incorrect params' % (self, str(questDesc))) + return + questId = questDesc[0] + rewardId = questDesc[3] + if not self.hasQuest(questId, rewardId=rewardId): + self.air.writeServerEvent('suspicious', self.doId, "Toon tried to delete quest they don't have %s" % str(questDesc)) + self.notify.warning("%s.requestDeleteQuest(%s) -- Toon doesn't have that quest" % (self, str(questDesc))) + return + if not Quests.isQuestJustForFun(questId, rewardId): + self.air.writeServerEvent('suspicious', self.doId, 'Toon tried to delete non-Just For Fun quest %s' % str(questDesc)) + self.notify.warning('%s.requestDeleteQuest(%s) -- Tried to cancel non-Just For Fun quest' % (self, str(questDesc))) + return + removedStatus = self.removeAllTracesOfQuest(questId, rewardId) + if 0 in removedStatus: + self.notify.warning('%s.requestDeleteQuest(%s) -- Failed to remove quest, status=%s' % (self, str(questDesc), removedStatus)) + + def b_setQuestCarryLimit(self, limit): + self.setQuestCarryLimit(limit) + self.d_setQuestCarryLimit(limit) + + def d_setQuestCarryLimit(self, limit): + self.sendUpdate('setQuestCarryLimit', [limit]) + + def setQuestCarryLimit(self, limit): + self.notify.debug('setting questCarryLimit to %s' % limit) + self.questCarryLimit = limit + + def getQuestCarryLimit(self): + return self.questCarryLimit + + def b_setMaxCarry(self, maxCarry): + self.setMaxCarry(maxCarry) + self.d_setMaxCarry(maxCarry) + + def d_setMaxCarry(self, maxCarry): + self.sendUpdate('setMaxCarry', [maxCarry]) + + def setMaxCarry(self, maxCarry): + self.maxCarry = maxCarry + + def getMaxCarry(self): + return self.maxCarry + + def b_setCheesyEffect(self, effect, hoodId, expireTime): + self.setCheesyEffect(effect, hoodId, expireTime) + self.d_setCheesyEffect(effect, hoodId, expireTime) + + def d_setCheesyEffect(self, effect, hoodId, expireTime): + self.sendUpdate('setCheesyEffect', [effect, hoodId, expireTime]) + + def setCheesyEffect(self, effect, hoodId, expireTime): + if simbase.air.holidayManager and ToontownGlobals.WINTER_CAROLING not in simbase.air.holidayManager.currentHolidays and ToontownGlobals.WACKY_WINTER_CAROLING not in simbase.air.holidayManager.currentHolidays and effect == ToontownGlobals.CESnowMan: + self.b_setCheesyEffect(ToontownGlobals.CENormal, hoodId, expireTime) + return + self.savedCheesyEffect = effect + self.savedCheesyHoodId = hoodId + self.savedCheesyExpireTime = expireTime + if self.air.doLiveUpdates: + taskName = self.uniqueName('cheesy-expires') + taskMgr.remove(taskName) + if effect != ToontownGlobals.CENormal: + duration = expireTime * 60 - time.time() + if duration > 0: + taskMgr.doMethodLater(duration, self.__undoCheesyEffect, taskName) + else: + self.__undoCheesyEffect(None) + return + + def getCheesyEffect(self): + return (self.savedCheesyEffect, self.savedCheesyHoodId, self.savedCheesyExpireTime) + + def __undoCheesyEffect(self, task): + self.b_setCheesyEffect(ToontownGlobals.CENormal, 0, 0) + return Task.cont + + def b_setTrackAccess(self, trackArray): + self.setTrackAccess(trackArray) + self.d_setTrackAccess(trackArray) + + def d_setTrackAccess(self, trackArray): + self.sendUpdate('setTrackAccess', [trackArray]) + + def setTrackAccess(self, trackArray): + self.trackArray = trackArray + + def getTrackAccess(self): + return self.trackArray + + def addTrackAccess(self, track): + self.trackArray[track] = 1 + self.b_setTrackAccess(self.trackArray) + + def removeTrackAccess(self, track): + self.trackArray[track] = 0 + self.b_setTrackAccess(self.trackArray) + + def hasTrackAccess(self, track): + if self.trackArray and track < len(self.trackArray): + return self.trackArray[track] + else: + return 0 + + def fixTrackAccess(self): + fixed = 0 + healExp, trapExp, lureExp, soundExp, throwExp, squirtExp, dropExp = self.experience.experience + numTracks = reduce(lambda a, b: a + b, self.trackArray) + if self.rewardTier in [0, + 1, + 2, + 3]: + if numTracks != 2: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + self.b_setTrackAccess([0, 0, 0, 0, 1, 1, 0]) + fixed = 1 + elif self.rewardTier in [4, 5, 6]: + if numTracks != 3: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + if self.trackArray[ToontownBattleGlobals.SOUND_TRACK] and not self.trackArray[ToontownBattleGlobals.HEAL_TRACK]: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 0]) + elif self.trackArray[ToontownBattleGlobals.HEAL_TRACK] and not self.trackArray[ToontownBattleGlobals.SOUND_TRACK]: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 0]) + elif soundExp >= healExp: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 0]) + else: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 0]) + fixed = 1 + elif self.rewardTier in [7, 8, 9, 10]: + if numTracks != 4: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + if self.trackArray[ToontownBattleGlobals.SOUND_TRACK] and not self.trackArray[ToontownBattleGlobals.HEAL_TRACK]: + if dropExp >= lureExp: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([0, 0, 1, 1, 1, 1, 0]) + elif self.trackArray[ToontownBattleGlobals.HEAL_TRACK] and not self.trackArray[ToontownBattleGlobals.SOUND_TRACK]: + if dropExp >= lureExp: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 1]) + else: + self.b_setTrackAccess([1, 0, 1, 0, 1, 1, 0]) + elif soundExp >= healExp: + if dropExp >= lureExp: + self.b_setTrackAccess([0, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([0, 0, 1, 1, 1, 1, 0]) + elif dropExp >= lureExp: + self.b_setTrackAccess([1, 0, 0, 0, 1, 1, 1]) + else: + self.b_setTrackAccess([1, 0, 1, 0, 1, 1, 0]) + fixed = 1 + elif self.rewardTier in [11, 12, 13]: + if numTracks != 5: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + if self.trackArray[ToontownBattleGlobals.SOUND_TRACK] and not self.trackArray[ToontownBattleGlobals.HEAL_TRACK]: + if self.trackArray[ToontownBattleGlobals.DROP_TRACK] and not self.trackArray[ToontownBattleGlobals.LURE_TRACK]: + if healExp >= trapExp: + self.b_setTrackAccess([1, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([0, 1, 0, 1, 1, 1, 1]) + elif healExp >= trapExp: + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 0]) + else: + self.b_setTrackAccess([0, 1, 1, 1, 1, 1, 0]) + elif self.trackArray[ToontownBattleGlobals.HEAL_TRACK] and not self.trackArray[ToontownBattleGlobals.SOUND_TRACK]: + if self.trackArray[ToontownBattleGlobals.DROP_TRACK] and not self.trackArray[ToontownBattleGlobals.LURE_TRACK]: + if soundExp >= trapExp: + self.b_setTrackAccess([1, 0, 0, 1, 1, 1, 1]) + else: + self.b_setTrackAccess([1, 1, 0, 0, 1, 1, 1]) + elif soundExp >= trapExp: + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 0]) + else: + self.b_setTrackAccess([1, 1, 1, 0, 1, 1, 0]) + fixed = 1 + elif numTracks != 6: + self.notify.warning('bad num tracks in tier: %s, %s' % (self.rewardTier, self.trackArray)) + sortedExp = [healExp, + trapExp, + lureExp, + soundExp, + dropExp] + sortedExp.sort() + if trapExp == sortedExp[0]: + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 1]) + elif lureExp == sortedExp[0]: + self.b_setTrackAccess([1, 1, 0, 1, 1, 1, 1]) + elif dropExp == sortedExp[0]: + self.b_setTrackAccess([1, 1, 1, 1, 1, 1, 0]) + elif soundExp == sortedExp[0]: + self.b_setTrackAccess([1, 1, 1, 0, 1, 1, 1]) + elif healExp == sortedExp[0]: + self.b_setTrackAccess([0, 1, 1, 1, 1, 1, 1]) + else: + self.notify.warning('invalid exp?!: %s, %s' % (sortedExp, self.trackArray)) + self.b_setTrackAccess([1, 0, 1, 1, 1, 1, 1]) + fixed = 1 + if fixed: + self.inventory.zeroInv() + self.inventory.maxOutInv() + self.d_setInventory(self.inventory.makeNetString()) + self.notify.info('fixed tracks: %s' % self.trackArray) + return fixed + + def b_setTrackProgress(self, trackId, progress): + self.setTrackProgress(trackId, progress) + self.d_setTrackProgress(trackId, progress) + + def d_setTrackProgress(self, trackId, progress): + self.sendUpdate('setTrackProgress', [trackId, progress]) + + def setTrackProgress(self, trackId, progress): + self.trackProgressId = trackId + self.trackProgress = progress + + def addTrackProgress(self, trackId, progressIndex): + if self.trackProgressId != trackId: + self.notify.warning('tried to update progress on a track toon is not training') + newProgress = self.trackProgress | 1 << progressIndex - 1 + self.b_setTrackProgress(self.trackProgressId, newProgress) + + def clearTrackProgress(self): + self.b_setTrackProgress(-1, 0) + + def getTrackProgress(self): + return [self.trackProgressId, self.trackProgress] + + def b_setHoodsVisited(self, hoodsVisitedArray): + self.hoodsVisited = hoodsVisitedArray + self.d_setHoodsVisited(hoodsVisitedArray) + + def d_setHoodsVisited(self, hoodsVisitedArray): + self.sendUpdate('setHoodsVisited', [hoodsVisitedArray]) + + def b_setTeleportAccess(self, teleportZoneArray): + self.setTeleportAccess(teleportZoneArray) + self.d_setTeleportAccess(teleportZoneArray) + + def d_setTeleportAccess(self, teleportZoneArray): + self.sendUpdate('setTeleportAccess', [teleportZoneArray]) + + def setTeleportAccess(self, teleportZoneArray): + self.teleportZoneArray = teleportZoneArray + + def getTeleportAccess(self): + return self.teleportZoneArray + + def hasTeleportAccess(self, zoneId): + return zoneId in self.teleportZoneArray + + def addTeleportAccess(self, zoneId): + if zoneId not in self.teleportZoneArray: + self.teleportZoneArray.append(zoneId) + self.b_setTeleportAccess(self.teleportZoneArray) + + def removeTeleportAccess(self, zoneId): + if zoneId in self.teleportZoneArray: + self.teleportZoneArray.remove(zoneId) + self.b_setTeleportAccess(self.teleportZoneArray) + + def checkTeleportAccess(self, zoneId): + if zoneId not in self.getTeleportAccess(): + simbase.air.writeServerEvent('suspicious', self.doId, 'Toon teleporting to zone %s they do not have access to.' % zoneId) + if simbase.config.GetBool('want-ban-teleport', False): + commentStr = 'Toon %s teleporting to a zone %s they do not have access to' % (self.doId, zoneId) + simbase.air.banManager.ban(self.doId, self.DISLid, commentStr) + + def b_setQuestHistory(self, questList): + self.setQuestHistory(questList) + self.d_setQuestHistory(questList) + + def d_setQuestHistory(self, questList): + self.sendUpdate('setQuestHistory', [questList]) + + def setQuestHistory(self, questList): + self.notify.debug('setting quest history to %s' % questList) + self.questHistory = questList + + def getQuestHistory(self): + return self.questHistory + + def removeQuestFromHistory(self, questId): + if questId in self.questHistory: + self.questHistory.remove(questId) + self.d_setQuestHistory(self.questHistory) + return 1 + else: + return 0 + + def removeRewardFromHistory(self, rewardId): + rewardTier, rewardHistory = self.getRewardHistory() + if rewardId in rewardHistory: + rewardHistory.remove(rewardId) + self.b_setRewardHistory(rewardTier, rewardHistory) + return 1 + else: + return 0 + + def b_setRewardHistory(self, tier, rewardList): + self.setRewardHistory(tier, rewardList) + self.d_setRewardHistory(tier, rewardList) + + def d_setRewardHistory(self, tier, rewardList): + self.sendUpdate('setRewardHistory', [tier, rewardList]) + + def setRewardHistory(self, tier, rewardList): + self.air.writeServerEvent('questTier', self.getDoId(), str(tier)) + self.notify.debug('setting reward history to tier %s, %s' % (tier, rewardList)) + self.rewardTier = tier + self.rewardHistory = rewardList + + def getRewardHistory(self): + return (self.rewardTier, self.rewardHistory) + + def getRewardTier(self): + return self.rewardTier + + def fixAvatar(self): + anyChanged = 0 + qrc = QuestRewardCounter.QuestRewardCounter() + if qrc.fixAvatar(self): + self.notify.info("Fixed avatar %d's quest rewards." % self.doId) + anyChanged = 1 + if self.hp > self.maxHp: + self.notify.info('Changed avatar %d to have hp %d instead of %d, to fit with maxHp' % (self.doId, self.maxHp, self.hp)) + self.b_setHp(self.maxHp) + anyChanged = 1 + inventoryChanged = 0 + carry = self.maxCarry + for track in range(len(ToontownBattleGlobals.Tracks)): + if not self.hasTrackAccess(track): + for level in range(len(ToontownBattleGlobals.Levels[track])): + count = self.inventory.inventory[track][level] + if count != 0: + self.notify.info('Changed avatar %d to throw away %d items in track %d level %d; no access to track.' % (self.doId, + count, + track, + level)) + self.inventory.inventory[track][level] = 0 + inventoryChanged = 1 + + else: + curSkill = self.experience.getExp(track) + for level in range(len(ToontownBattleGlobals.Levels[track])): + count = self.inventory.inventory[track][level] + if curSkill < ToontownBattleGlobals.Levels[track][level]: + if count != 0: + self.notify.info('Changed avatar %d to throw away %d items in track %d level %d; no access to level.' % (self.doId, + count, + track, + level)) + self.inventory.inventory[track][level] = 0 + inventoryChanged = 1 + else: + newCount = min(count, carry) + newCount = min(count, self.inventory.getMax(track, level)) + if count != newCount: + self.notify.info('Changed avatar %d to throw away %d items in track %d level %d; too many gags.' % (self.doId, + count - newCount, + track, + level)) + self.inventory.inventory[track][level] = newCount + inventoryChanged = 1 + carry -= newCount + + self.inventory.calcTotalProps() + if inventoryChanged: + self.d_setInventory(self.inventory.makeNetString()) + anyChanged = 1 + if len(self.quests) > self.questCarryLimit: + self.notify.info('Changed avatar %d to throw out %d quests; too many quests.' % (self.doId, len(self.quests) - self.questCarryLimit)) + self.b_setQuests(self.quests[:self.questCarryLimit]) + self.fixAvatar() + anyChanged = 1 + if not (self.emoteAccess[0] and self.emoteAccess[1] and self.emoteAccess[2] and self.emoteAccess[3] and self.emoteAccess[4]): + self.emoteAccess[0] = 1 + self.emoteAccess[1] = 1 + self.emoteAccess[2] = 1 + self.emoteAccess[3] = 1 + self.emoteAccess[4] = 1 + self.b_setEmoteAccess(self.emoteAccess) + self.notify.info('Changed avatar %d to have emoteAccess: %s' % (self.doId, self.emoteAccess)) + anyChanged = 1 + return anyChanged + + def b_setEmoteAccess(self, bits): + self.setEmoteAccess(bits) + self.d_setEmoteAccess(bits) + + def d_setEmoteAccess(self, bits): + self.sendUpdate('setEmoteAccess', [bits]) + + def setEmoteAccess(self, bits): + if len(bits) == 20: + bits.extend([0, + 0, + 0, + 0, + 0]) + self.b_setEmoteAccess(bits) + elif len(bits) != len(self.emoteAccess): + self.notify.warning('New emote access list must be the same size as the old one.') + return + self.emoteAccess = bits + + def getEmoteAccess(self): + return self.emoteAccess + + def setEmoteAccessId(self, id, bit): + self.emoteAccess[id] = bit + self.d_setEmoteAccess(self.emoteAccess) + + def b_setHouseId(self, id): + self.setHouseId(id) + self.d_setHouseId(id) + + def d_setHouseId(self, id): + self.sendUpdate('setHouseId', [id]) + + def setHouseId(self, id): + self.houseId = id + + def getHouseId(self): + return self.houseId + + def setPosIndex(self, index): + self.posIndex = index + + def getPosIndex(self): + return self.posIndex + + def b_setCustomMessages(self, customMessages): + self.d_setCustomMessages(customMessages) + self.setCustomMessages(customMessages) + + def d_setCustomMessages(self, customMessages): + self.sendUpdate('setCustomMessages', [customMessages]) + + def setCustomMessages(self, customMessages): + self.customMessages = customMessages + + def getCustomMessages(self): + return self.customMessages + + def b_setResistanceMessages(self, resistanceMessages): + self.d_setResistanceMessages(resistanceMessages) + self.setResistanceMessages(resistanceMessages) + + def d_setResistanceMessages(self, resistanceMessages): + self.sendUpdate('setResistanceMessages', [resistanceMessages]) + + def setResistanceMessages(self, resistanceMessages): + self.resistanceMessages = resistanceMessages + + def getResistanceMessages(self): + return self.resistanceMessages + + def addResistanceMessage(self, textId): + msgs = self.getResistanceMessages() + for i in range(len(msgs)): + if msgs[i][0] == textId: + msgs[i][1] += 1 + self.b_setResistanceMessages(msgs) + return + + msgs.append([textId, 1]) + self.b_setResistanceMessages(msgs) + + def removeResistanceMessage(self, textId): + msgs = self.getResistanceMessages() + for i in range(len(msgs)): + if msgs[i][0] == textId: + msgs[i][1] -= 1 + if msgs[i][1] <= 0: + del msgs[i] + self.b_setResistanceMessages(msgs) + return 1 + + self.notify.warning("Toon %s doesn't have resistance message %s" % (self.doId, textId)) + return 0 + + def restockAllResistanceMessages(self, charges = 1): + from toontown.chat import ResistanceChat + msgs = [] + for menuIndex in ResistanceChat.resistanceMenu: + for itemIndex in ResistanceChat.getItems(menuIndex): + textId = ResistanceChat.encodeId(menuIndex, itemIndex) + msgs.append([textId, charges]) + + self.b_setResistanceMessages(msgs) + + def b_setCatalogSchedule(self, currentWeek, nextTime): + self.setCatalogSchedule(currentWeek, nextTime) + self.d_setCatalogSchedule(currentWeek, nextTime) + + def d_setCatalogSchedule(self, currentWeek, nextTime): + self.sendUpdate('setCatalogSchedule', [currentWeek, nextTime]) + + def setCatalogSchedule(self, currentWeek, nextTime): + self.catalogScheduleCurrentWeek = currentWeek + self.catalogScheduleNextTime = nextTime + if self.air.doLiveUpdates: + taskName = self.uniqueName('next-catalog') + taskMgr.remove(taskName) + duration = max(10.0, nextTime * 60 - time.time()) + taskMgr.doMethodLater(duration, self.__deliverCatalog, taskName) + + def getCatalogSchedule(self): + return (self.catalogScheduleCurrentWeek, self.catalogScheduleNextTime) + + def __deliverCatalog(self, task): + self.air.catalogManager.deliverCatalogFor(self) + return Task.done + + def b_setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.setCatalog(monthlyCatalog, weeklyCatalog, backCatalog) + self.d_setCatalog(monthlyCatalog, weeklyCatalog, backCatalog) + + def d_setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.sendUpdate('setCatalog', [monthlyCatalog.getBlob(), weeklyCatalog.getBlob(), backCatalog.getBlob()]) + + def setCatalog(self, monthlyCatalog, weeklyCatalog, backCatalog): + self.monthlyCatalog = CatalogItemList.CatalogItemList(monthlyCatalog) + self.weeklyCatalog = CatalogItemList.CatalogItemList(weeklyCatalog) + self.backCatalog = CatalogItemList.CatalogItemList(backCatalog) + + def getCatalog(self): + return (self.monthlyCatalog.getBlob(), self.weeklyCatalog.getBlob(), self.backCatalog.getBlob()) + + def b_setCatalogNotify(self, catalogNotify, mailboxNotify): + self.setCatalogNotify(catalogNotify, mailboxNotify) + self.d_setCatalogNotify(catalogNotify, mailboxNotify) + + def d_setCatalogNotify(self, catalogNotify, mailboxNotify): + self.sendUpdate('setCatalogNotify', [catalogNotify, mailboxNotify]) + + def setCatalogNotify(self, catalogNotify, mailboxNotify): + self.catalogNotify = catalogNotify + self.mailboxNotify = mailboxNotify + + def getCatalogNotify(self): + return (self.catalogNotify, self.mailboxNotify) + + def b_setDeliverySchedule(self, onOrder, doUpdateLater = True): + self.setDeliverySchedule(onOrder, doUpdateLater) + self.d_setDeliverySchedule(onOrder) + + def d_setDeliverySchedule(self, onOrder): + self.sendUpdate('setDeliverySchedule', [onOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate)]) + + def setDeliverySchedule(self, onOrder, doUpdateLater = True): + self.setBothSchedules(onOrder, None) + return + self.onOrder = CatalogItemList.CatalogItemList(onOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if hasattr(self, 'name'): + if doUpdateLater and self.air.doLiveUpdates and hasattr(self, 'air'): + taskName = self.uniqueName('next-delivery') + taskMgr.remove(taskName) + now = int(time.time() / 60 + 0.5) + nextItem = None + nextTime = self.onOrder.getNextDeliveryDate() + nextItem = self.onOrder.getNextDeliveryItem() + if nextItem != None: + pass + if nextTime != None: + duration = max(10.0, nextTime * 60 - time.time()) + taskMgr.doMethodLater(duration, self.__deliverPurchase, taskName) + return + + def getDeliverySchedule(self): + return self.onOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + + def b_setBothSchedules(self, onOrder, onGiftOrder, doUpdateLater = True): + self.setBothSchedules(onOrder, onGiftOrder, doUpdateLater) + self.d_setDeliverySchedule(onOrder) + + def setBothSchedules(self, onOrder, onGiftOrder, doUpdateLater = True): + if onOrder != None: + self.onOrder = CatalogItemList.CatalogItemList(onOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if onGiftOrder != None: + self.onGiftOrder = CatalogItemList.CatalogItemList(onGiftOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if not hasattr(self, 'air') or self.air == None: + return + if doUpdateLater and self.air.doLiveUpdates and hasattr(self, 'name'): + taskName = 'next-bothDelivery-%s' % self.doId + now = int(time.time() / 60 + 0.5) + nextItem = None + nextGiftItem = None + nextTime = None + nextGiftTime = None + if self.onOrder: + nextTime = self.onOrder.getNextDeliveryDate() + nextItem = self.onOrder.getNextDeliveryItem() + if self.onGiftOrder: + nextGiftTime = self.onGiftOrder.getNextDeliveryDate() + nextGiftItem = self.onGiftOrder.getNextDeliveryItem() + if nextItem: + pass + if nextGiftItem: + pass + if nextTime == None: + nextTime = nextGiftTime + if nextGiftTime == None: + nextGiftTime = nextTime + if nextGiftTime < nextTime: + nextTime = nextGiftTime + existingDuration = None + checkTaskList = taskMgr.getTasksNamed(taskName) + if checkTaskList: + currentTime = globalClock.getFrameTime() + checkTask = checkTaskList[0] + existingDuration = checkTask.wakeTime - currentTime + if nextTime: + newDuration = max(10.0, nextTime * 60 - time.time()) + if existingDuration and existingDuration >= newDuration: + taskMgr.remove(taskName) + taskMgr.doMethodLater(newDuration, self.__deliverBothPurchases, taskName) + elif existingDuration and existingDuration < newDuration: + pass + else: + taskMgr.doMethodLater(newDuration, self.__deliverBothPurchases, taskName) + return + + def __deliverBothPurchases(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onOrder.extractDeliveryItems(now) + deliveredGifts, remainingGifts = self.onGiftOrder.extractDeliveryItems(now) + simbase.air.deliveryManager.sendDeliverGifts(self.getDoId(), now) + giftItem = CatalogItemList.CatalogItemList(deliveredGifts, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if len(giftItem) > 0: + self.air.writeServerEvent('Getting Gift', self.doId, 'sender %s receiver %s gift %s' % (giftItem[0].giftTag, self.doId, giftItem[0].getName())) + self.b_setMailboxContents(self.mailboxContents + delivered + deliveredGifts) + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NewItems) + self.b_setBothSchedules(remaining, remainingGifts) + return Task.done + + def setGiftSchedule(self, onGiftOrder, doUpdateLater = True): + self.setBothSchedules(None, onGiftOrder) + return + self.onGiftOrder = CatalogItemList.CatalogItemList(onGiftOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if doUpdateLater and self.air.doLiveUpdates and hasattr(self, 'air') and hasattr(self, 'name'): + taskName = self.uniqueName('next-gift') + taskMgr.remove(taskName) + now = int(time.time() / 60 + 0.5) + nextItem = None + nextTime = self.onGiftOrder.getNextDeliveryDate() + nextItem = self.onGiftOrder.getNextDeliveryItem() + if nextItem != None: + pass + if nextTime != None: + duration = max(10.0, nextTime * 60 - time.time()) + duration += 30 + taskMgr.doMethodLater(duration, self.__deliverGiftPurchase, taskName) + return + + def getGiftSchedule(self): + return self.onGiftOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate) + + def __deliverGiftPurchase(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onGiftOrder.extractDeliveryItems(now) + self.notify.info('Gift Delivery for %s: %s.' % (self.doId, delivered)) + self.b_setMailboxContents(self.mailboxContents + delivered) + simbase.air.deliveryManager.sendDeliverGifts(self.getDoId(), now) + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NewItems) + return Task.done + + def __deliverPurchase(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onOrder.extractDeliveryItems(now) + self.notify.info('Delivery for %s: %s.' % (self.doId, delivered)) + self.b_setMailboxContents(self.mailboxContents + delivered) + self.b_setDeliverySchedule(remaining) + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NewItems) + return Task.done + + def b_setMailboxContents(self, mailboxContents): + self.setMailboxContents(mailboxContents) + self.d_setMailboxContents(mailboxContents) + + def d_setMailboxContents(self, mailboxContents): + self.sendUpdate('setMailboxContents', [mailboxContents.getBlob(store=CatalogItem.Customization)]) + if len(mailboxContents) == 0: + self.b_setCatalogNotify(self.catalogNotify, ToontownGlobals.NoItems) + self.checkMailboxFullIndicator() + + def checkMailboxFullIndicator(self): + if self.houseId and hasattr(self, 'air'): + if self.air: + house = self.air.doId2do.get(self.houseId) + if house and house.mailbox: + house.mailbox.b_setFullIndicator(len(self.mailboxContents) != 0 or self.numMailItems or self.getNumInvitesToShowInMailbox() or len(self.awardMailboxContents) != 0) + + def setMailboxContents(self, mailboxContents): + self.notify.debug('Setting mailboxContents to %s.' % mailboxContents) + self.mailboxContents = CatalogItemList.CatalogItemList(mailboxContents, store=CatalogItem.Customization) + self.notify.debug('mailboxContents is %s.' % self.mailboxContents) + + def getMailboxContents(self): + return self.mailboxContents.getBlob(store=CatalogItem.Customization) + + def b_setGhostMode(self, flag): + self.setGhostMode(flag) + self.d_setGhostMode(flag) + + def d_setGhostMode(self, flag): + self.sendUpdate('setGhostMode', [flag]) + + def setGhostMode(self, flag): + self.ghostMode = flag + + def setImmortalMode(self, flag): + self.immortalMode = flag + + def b_setSpeedChatStyleIndex(self, index): + self.setSpeedChatStyleIndex(index) + self.d_setSpeedChatStyleIndex(index) + + def d_setSpeedChatStyleIndex(self, index): + self.sendUpdate('setSpeedChatStyleIndex', [index]) + + def setSpeedChatStyleIndex(self, index): + self.speedChatStyleIndex = index + + def getSpeedChatStyleIndex(self): + return self.speedChatStyleIndex + + def b_setMaxMoney(self, maxMoney): + self.d_setMaxMoney(maxMoney) + self.setMaxMoney(maxMoney) + + def d_setMaxMoney(self, maxMoney): + self.sendUpdate('setMaxMoney', [maxMoney]) + + def setMaxMoney(self, maxMoney): + self.maxMoney = maxMoney + + def getMaxMoney(self): + return self.maxMoney + + def addMoney(self, deltaMoney): + money = deltaMoney + self.money + pocketMoney = min(money, self.maxMoney) + self.b_setMoney(pocketMoney) + overflowMoney = money - self.maxMoney + if overflowMoney > 0: + bankMoney = self.bankMoney + overflowMoney + self.b_setBankMoney(bankMoney) + + def takeMoney(self, deltaMoney, bUseBank = True): + totalMoney = self.money + if bUseBank: + totalMoney += self.bankMoney + if deltaMoney > totalMoney: + self.notify.warning('Not enough money! AvId: %s Has:%s Charged:%s' % (self.doId, totalMoney, deltaMoney)) + return False + if bUseBank and deltaMoney > self.money: + self.b_setBankMoney(self.bankMoney - (deltaMoney - self.money)) + self.b_setMoney(0) + else: + self.b_setMoney(self.money - deltaMoney) + return True + + def b_setMoney(self, money): + if bboard.get('autoRich-%s' % self.doId, False): + money = self.getMaxMoney() + self.setMoney(money) + self.d_setMoney(money) + + def d_setMoney(self, money): + self.sendUpdate('setMoney', [money]) + + def setMoney(self, money): + if money < 0: + simbase.air.writeServerEvent('suspicious', self.doId, 'toon has invalid money %s, forcing to zero' % money) + money = 0 + commentStr = 'User %s has negative money %s' % (self.doId, money) + dislId = self.DISLid + if simbase.config.GetBool('want-ban-negative-money', False): + simbase.air.banManager.ban(self.doId, dislId, commentStr) + self.money = money + + def getMoney(self): + return self.money + + def getTotalMoney(self): + return self.money + self.bankMoney + + def b_setMaxBankMoney(self, maxMoney): + self.d_setMaxBankMoney(maxMoney) + self.setMaxBankMoney(maxMoney) + + def d_setMaxBankMoney(self, maxMoney): + self.sendUpdate('setMaxBankMoney', [maxMoney]) + + def setMaxBankMoney(self, maxMoney): + self.maxBankMoney = maxMoney + + def getMaxBankMoney(self): + return self.maxBankMoney + + def b_setBankMoney(self, money): + bankMoney = min(money, self.maxBankMoney) + self.setBankMoney(bankMoney) + self.d_setBankMoney(bankMoney) + + def d_setBankMoney(self, money): + self.sendUpdate('setBankMoney', [money]) + + def setBankMoney(self, money): + self.bankMoney = money + + def getBankMoney(self): + return self.bankMoney + + def b_setEmblems(self, emblems): + self.setEmblems(emblems) + self.d_setEmblems(emblems) + + def setEmblems(self, emblems): + self.emblems = emblems + + def d_setEmblems(self, emblems): + if simbase.air.wantEmblems: + self.sendUpdate('setEmblems', [emblems]) + + def getEmblems(self): + return self.emblems + + def addEmblems(self, emblemsToAdd): + newEmblems = self.emblems[:] + for i in xrange(ToontownGlobals.NumEmblemTypes): + newEmblems[i] += emblemsToAdd[i] + + self.b_setEmblems(newEmblems) + + def subtractEmblems(self, emblemsToSubtract): + newEmblems = self.emblems[:] + for i in xrange(ToontownGlobals.NumEmblemTypes): + newEmblems[i] -= emblemsToSubtract[i] + + self.b_setEmblems(newEmblems) + + def isEnoughEmblemsToBuy(self, itemEmblemPrices): + for emblemIndex, emblemPrice in enumerate(itemEmblemPrices): + if emblemIndex >= len(self.emblems): + return False + if self.emblems[emblemIndex] < emblemPrice: + return False + + return True + + def tossPie(self, x, y, z, h, p, r, sequence, power, timestamp32): + if not self.validate(self.doId, self.numPies > 0, 'tossPie with no pies available'): + return + if self.numPies != ToontownGlobals.FullPies: + self.b_setNumPies(self.numPies - 1) + + def b_setNumPies(self, numPies): + self.setNumPies(numPies) + self.d_setNumPies(numPies) + + def d_setNumPies(self, numPies): + self.sendUpdate('setNumPies', [numPies]) + + def setNumPies(self, numPies): + self.numPies = numPies + + def b_setPieType(self, pieType): + self.setPieType(pieType) + self.d_setPieType(pieType) + + def d_setPieType(self, pieType): + self.sendUpdate('setPieType', [pieType]) + + def setPieType(self, pieType): + self.pieType = pieType + + def d_setTrophyScore(self, score): + self.sendUpdate('setTrophyScore', [score]) + + def stopToonUp(self): + taskMgr.remove(self.uniqueName('safeZoneToonUp')) + self.ignore(self.air.getAvatarExitEvent(self.getDoId())) + + def startToonUp(self, healFrequency): + self.stopToonUp() + self.healFrequency = healFrequency + self.__waitForNextToonUp() + + def __waitForNextToonUp(self): + taskMgr.doMethodLater(self.healFrequency, self.toonUpTask, self.uniqueName('safeZoneToonUp')) + + def toonUpTask(self, task): + self.toonUp(1) + self.__waitForNextToonUp() + return Task.done + + def toonUp(self, hpGained, quietly = 0, sendTotal = 1): + if hpGained > self.maxHp: + hpGained = self.maxHp + if not quietly: + self.sendUpdate('toonUp', [hpGained]) + if self.hp + hpGained <= 0: + self.hp += hpGained + else: + self.hp = max(self.hp, 0) + hpGained + clampedHp = min(self.hp, self.maxHp) + if not self.hpOwnedByBattle: + self.hp = clampedHp + if sendTotal and not self.hpOwnedByBattle: + self.d_setHp(clampedHp) + + def isToonedUp(self): + return self.hp >= self.maxHp + + def makeBlackCat(self): + if self.dna.getAnimal() != 'cat': + return 'not a cat' + self.air.writeServerEvent('blackCat', self.doId, '') + newDna = ToonDNA.ToonDNA() + newDna.makeFromNetString(self.dna.makeNetString()) + black = 26 + newDna.updateToonProperties(armColor=black, legColor=black, headColor=black) + self.b_setDNAString(newDna.makeNetString()) + return None + + def b_announceBingo(self): + self.d_announceBingo() + self.announceBingo + + def d_announceBingo(self): + self.sendUpdate('announceBingo', []) + + def announceBingo(self): + pass + + def incrementPopulation(self): + if self.isPlayerControlled(): + DistributedPlayerAI.DistributedPlayerAI.incrementPopulation(self) + + def decrementPopulation(self): + if self.isPlayerControlled(): + DistributedPlayerAI.DistributedPlayerAI.decrementPopulation(self) + + if __dev__: + + def _logGarbage(self): + if self.isPlayerControlled(): + DistributedPlayerAI.DistributedPlayerAI._logGarbage(self) + + def reqSCResistance(self, msgIndex, nearbyPlayers): + self.d_setSCResistance(msgIndex, nearbyPlayers) + + def d_setSCResistance(self, msgIndex, nearbyPlayers): + if not ResistanceChat.validateId(msgIndex): + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s, which is invalid.' % msgIndex) + return + if not self.removeResistanceMessage(msgIndex): + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s, but does not have it.' % msgIndex) + return + if hasattr(self, 'autoResistanceRestock') and self.autoResistanceRestock: + self.restockAllResistanceMessages(1) + affectedPlayers = [] + for toonId in nearbyPlayers: + toon = self.air.doId2do.get(toonId) + if not toon: + self.notify.warning('%s said resistance %s for %s; not on server' % (self.doId, msgIndex, toonId)) + elif toon.__class__ != DistributedToonAI: + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s for %s; object of type %s' % (msgIndex, toonId, toon.__class__.__name__)) + elif toonId in affectedPlayers: + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s for %s twice in same message.' % (msgIndex, toonId)) + else: + toon.doResistanceEffect(msgIndex) + affectedPlayers.append(toonId) + + if len(affectedPlayers) > 50: + self.air.writeServerEvent('suspicious', self.doId, 'said resistance %s for %s toons.' % (msgIndex, len(affectedPlayers))) + self.notify.warning('%s said resistance %s for %s toons: %s' % (self.doId, + msgIndex, + len(affectedPlayers), + affectedPlayers)) + self.sendUpdate('setSCResistance', [msgIndex, affectedPlayers]) + type = ResistanceChat.getMenuName(msgIndex) + value = ResistanceChat.getItemValue(msgIndex) + self.air.writeServerEvent('resistanceChat', self.zoneId, '%s|%s|%s|%s' % (self.doId, + type, + value, + affectedPlayers)) + + def doResistanceEffect(self, msgIndex): + msgType, itemIndex = ResistanceChat.decodeId(msgIndex) + msgValue = ResistanceChat.getItemValue(msgIndex) + if msgType == ResistanceChat.RESISTANCE_TOONUP: + if msgValue == -1: + self.toonUp(self.maxHp) + else: + self.toonUp(msgValue) + self.notify.debug('Toon-up for ' + self.name) + elif msgType == ResistanceChat.RESISTANCE_RESTOCK: + self.inventory.NPCMaxOutInv(msgValue) + self.d_setInventory(self.inventory.makeNetString()) + self.notify.debug('Restock for ' + self.name) + elif msgType == ResistanceChat.RESISTANCE_MONEY: + if msgValue == -1: + self.addMoney(999999) + else: + self.addMoney(msgValue) + self.notify.debug('Money for ' + self.name) + + def squish(self, damage): + self.takeDamage(damage) + + if simbase.wantKarts: + + def hasKart(self): + return self.kartDNA[KartDNA.bodyType] != -1 + + def b_setTickets(self, numTickets): + if numTickets > RaceGlobals.MaxTickets: + numTickets = RaceGlobals.MaxTickets + self.d_setTickets(numTickets) + self.setTickets(numTickets) + + def d_setTickets(self, numTickets): + if numTickets > RaceGlobals.MaxTickets: + numTickets = RaceGlobals.MaxTickets + self.sendUpdate('setTickets', [numTickets]) + + def setTickets(self, numTickets): + if numTickets > RaceGlobals.MaxTickets: + numTickets = RaceGlobals.MaxTickets + self.tickets = numTickets + + def getTickets(self): + return self.tickets + + def b_setKartingTrophies(self, trophyList): + self.setKartingTrophies(trophyList) + self.d_setKartingTrophies(trophyList) + + def setKartingTrophies(self, trophyList): + self.notify.debug('setting kartingTrophies to %s' % trophyList) + self.kartingTrophies = trophyList + + def d_setKartingTrophies(self, trophyList): + self.sendUpdate('setKartingTrophies', [trophyList]) + + def getKartingTrophies(self): + return self.kartingTrophies + + def b_setKartingHistory(self, history): + self.setKartingHistory(history) + self.d_setKartingHistory(history) + + def setKartingHistory(self, history): + self.notify.debug('setting kartingHistory to %s' % history) + self.kartingHistory = history + + def d_setKartingHistory(self, history): + self.sendUpdate('setKartingHistory', [history]) + + def getKartingHistory(self): + return self.kartingHistory + + def b_setKartingPersonalBest(self, bestTimes): + best1 = bestTimes[0:6] + best2 = bestTimes[6:] + self.setKartingPersonalBest(best1) + self.setKartingPersonalBest2(best2) + self.d_setKartingPersonalBest(bestTimes) + + def d_setKartingPersonalBest(self, bestTimes): + best1 = bestTimes[0:6] + best2 = bestTimes[6:] + self.sendUpdate('setKartingPersonalBest', [best1]) + self.sendUpdate('setKartingPersonalBest2', [best2]) + + def setKartingPersonalBest(self, bestTimes): + self.notify.debug('setting karting to %s' % bestTimes) + self.kartingPersonalBest = bestTimes + + def setKartingPersonalBest2(self, bestTimes2): + self.notify.debug('setting karting2 to %s' % bestTimes2) + self.kartingPersonalBest2 = bestTimes2 + + def getKartingPersonalBest(self): + return self.kartingPersonalBest + + def getKartingPersonalBest2(self): + return self.kartingPersonalBest2 + + def getKartingPersonalBestAll(self): + return self.kartingPersonalBest + self.kartingPersonalBest2 + + def setKartDNA(self, kartDNA): + self.b_setKartBodyType(kartDNA[KartDNA.bodyType]) + self.b_setKartBodyColor(kartDNA[KartDNA.bodyColor]) + self.b_setKartAccColor(kartDNA[KartDNA.accColor]) + self.b_setKartEngineBlockType(kartDNA[KartDNA.ebType]) + self.b_setKartSpoilerType(kartDNA[KartDNA.spType]) + self.b_setKartFrontWheelWellType(kartDNA[KartDNA.fwwType]) + self.b_setKartBackWheelWellType(kartDNA[KartDNA.bwwType]) + self.b_setKartRimType(kartDNA[KartDNA.rimsType]) + self.b_setKartDecalType(kartDNA[KartDNA.decalType]) + + def b_setKartBodyType(self, bodyType): + self.d_setKartBodyType(bodyType) + self.setKartBodyType(bodyType) + + def d_setKartBodyType(self, bodyType): + self.sendUpdate('setKartBodyType', [bodyType]) + + def setKartBodyType(self, bodyType): + self.kartDNA[KartDNA.bodyType] = bodyType + + def getKartBodyType(self): + return self.kartDNA[KartDNA.bodyType] + + def b_setKartBodyColor(self, bodyColor): + self.d_setKartBodyColor(bodyColor) + self.setKartBodyColor(bodyColor) + + def d_setKartBodyColor(self, bodyColor): + self.sendUpdate('setKartBodyColor', [bodyColor]) + + def setKartBodyColor(self, bodyColor): + self.kartDNA[KartDNA.bodyColor] = bodyColor + + def getKartBodyColor(self): + return self.kartDNA[KartDNA.bodyColor] + + def b_setKartAccessoryColor(self, accColor): + self.d_setKartAccessoryColor(accColor) + self.setKartAccessoryColor(accColor) + + def d_setKartAccessoryColor(self, accColor): + self.sendUpdate('setKartAccessoryColor', [accColor]) + + def setKartAccessoryColor(self, accColor): + self.kartDNA[KartDNA.accColor] = accColor + + def getKartAccessoryColor(self): + return self.kartDNA[KartDNA.accColor] + + def b_setKartEngineBlockType(self, ebType): + self.d_setKartEngineBlockType(ebType) + self.setKartEngineBlockType(ebType) + + def d_setKartEngineBlockType(self, ebType): + self.sendUpdate('setKartEngineBlockType', [ebType]) + + def setKartEngineBlockType(self, ebType): + self.kartDNA[KartDNA.ebType] = ebType + + def getKartEngineBlockType(self): + return self.kartDNA[KartDNA.ebType] + + def b_setKartSpoilerType(self, spType): + self.d_setKartSpoilerType(spType) + self.setKartSpoilerType(spType) + + def d_setKartSpoilerType(self, spType): + self.sendUpdate('setKartSpoilerType', [spType]) + + def setKartSpoilerType(self, spType): + self.kartDNA[KartDNA.spType] = spType + + def getKartSpoilerType(self): + return self.kartDNA[KartDNA.spType] + + def b_setKartFrontWheelWellType(self, fwwType): + self.d_setKartFrontWheelWellType(fwwType) + self.setKartFrontWheelWellType(fwwType) + + def d_setKartFrontWheelWellType(self, fwwType): + self.sendUpdate('setKartFrontWheelWellType', [fwwType]) + + def setKartFrontWheelWellType(self, fwwType): + self.kartDNA[KartDNA.fwwType] = fwwType + + def getKartFrontWheelWellType(self): + return self.kartDNA[KartDNA.fwwType] + + def b_setKartBackWheelWellType(self, bwwType): + self.d_setKartBackWheelWellType(bwwType) + self.setKartBackWheelWellType(bwwType) + + def d_setKartBackWheelWellType(self, bwwType): + self.sendUpdate('setKartBackWheelWellType', [bwwType]) + + def setKartBackWheelWellType(self, bwwType): + self.kartDNA[KartDNA.bwwType] = bwwType + + def getKartBackWheelWellType(self): + return self.kartDNA[KartDNA.bwwType] + + def b_setKartRimType(self, rimsType): + self.d_setKartRimType(rimsType) + self.setKartRimType(rimsType) + + def d_setKartRimType(self, rimsType): + self.sendUpdate('setKartRimType', [rimsType]) + + def setKartRimType(self, rimsType): + self.kartDNA[KartDNA.rimsType] = rimsType + + def getKartRimType(self): + return self.kartDNA[KartDNA.rimsType] + + def b_setKartDecalType(self, decalType): + self.d_setKartDecalType(decalType) + self.setKartDecalType(decalType) + + def d_setKartDecalType(self, decalType): + self.sendUpdate('setKartDecalType', [decalType]) + + def setKartDecalType(self, decalType): + self.kartDNA[KartDNA.decalType] = decalType + + def getKartDecalType(self): + return self.kartDNA[KartDNA.decalType] + + def b_setKartAccessoriesOwned(self, accessories): + self.d_setKartAccessoriesOwned(accessories) + self.setKartAccessoriesOwned(accessories) + + def d_setKartAccessoriesOwned(self, accessories): + self.sendUpdate('setKartAccessoriesOwned', [accessories]) + + def setKartAccessoriesOwned(self, accessories): + if (__debug__): + import pdb + self.accessories = accessories + + def getKartAccessoriesOwned(self): + owned = copy.deepcopy(self.accessories) + while InvalidEntry in owned: + owned.remove(InvalidEntry) + + return owned + + def addOwnedAccessory(self, accessoryId): + print 'in add owned accessory' + if AccessoryDict.has_key(accessoryId): + if self.accessories.count(accessoryId) > 0: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to add accessory %s which is already owned!' % accessoryId) + return + if self.accessories.count(InvalidEntry) > 0: + accList = list(self.accessories) + index = self.accessories.index(InvalidEntry) + accList[index] = accessoryId + self.b_setKartAccessoriesOwned(accList) + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to add accessory %s when accessory inventory is full!' % accessoryId) + return + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to add accessory %s which is not a valid accessory.' % accessoryId) + return + + def removeOwnedAccessory(self, accessoryId): + if AccessoryDict.has_key(accessoryId): + if self.accessories.count(accessoryId) == 0: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to remove accessory %s which is not currently owned!' % accessoryId) + return + else: + accList = list(self.accessories) + index = self.accessories.index(accessoryId) + accList[index] = InvalidEntry + self.air.writeServerEvent('deletedKartingAccessory', self.doId, '%s' % accessoryId) + self.b_setKartAccessoriesOwned(accList) + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to remove accessory %s which is not a valid accessory.' % accessoryId) + return + + def updateKartDNAField(self, dnaField, fieldValue): + if not checkKartFieldValidity(dnaField): + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to dna value %s in the invalid field %s' % (fieldValue, dnaField)) + return + if dnaField == KartDNA.bodyType: + if fieldValue not in KartDict.keys() and fieldValue != InvalidEntry: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update kart body to invalid body %s.' % fieldValue) + return + self.b_setKartBodyType(fieldValue) + else: + accFields = [KartDNA.ebType, + KartDNA.spType, + KartDNA.fwwType, + KartDNA.bwwType, + KartDNA.rimsType, + KartDNA.decalType] + colorFields = [KartDNA.bodyColor, KartDNA.accColor] + if dnaField in accFields: + if fieldValue == InvalidEntry: + self.__updateKartDNAField(dnaField, fieldValue) + else: + if fieldValue not in self.accessories: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to accessory %s which is not currently owned.' % fieldValue) + return + field = getAccessoryType(fieldValue) + if field == InvalidEntry: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update accessory %s in an illegal field %s' % (fieldValue, field)) + return + elif field != dnaField: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update accessory %s in a field %s that does not match client specified field %s' % (fieldValue, field, dnaField)) + return + self.__updateKartDNAField(dnaField, fieldValue) + elif dnaField in colorFields: + if fieldValue == InvalidEntry: + self.__updateKartDNAField(dnaField, fieldValue) + else: + if fieldValue not in self.accessories: + if fieldValue != getDefaultColor(): + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to color %s which is not owned!' % fieldValue) + return + elif fieldValue == getDefaultColor() and self.kartDNA[dnaField] != InvalidEntry: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update to default color %s which is not owned!' % fieldValue) + return + if getAccessoryType(fieldValue) != KartDNA.bodyColor: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to update invalid color %s for dna field %s' % (fieldValue, dnaField)) + return + self.__updateKartDNAField(dnaField, fieldValue) + else: + self.air.writeServerEvent('suspicious', self.doId, 'attempt to udpate accessory %s in the invalid field %s' % (fieldValue, dnaField)) + return + + def __updateKartDNAField(self, dnaField, fieldValue): + if dnaField == KartDNA.bodyColor: + self.b_setKartBodyColor(fieldValue) + elif dnaField == KartDNA.accColor: + self.b_setKartAccessoryColor(fieldValue) + elif dnaField == KartDNA.ebType: + self.b_setKartEngineBlockType(fieldValue) + elif dnaField == KartDNA.spType: + self.b_setKartSpoilerType(fieldValue) + elif dnaField == KartDNA.fwwType: + self.b_setKartFrontWheelWellType(fieldValue) + elif dnaField == KartDNA.bwwType: + self.b_setKartBackWheelWellType(fieldValue) + elif dnaField == KartDNA.rimsType: + self.b_setKartRimType(fieldValue) + elif dnaField == KartDNA.decalType: + self.b_setKartDecalType(fieldValue) + + def setAllowSoloRace(self, allowSoloRace): + self.allowSoloRace = allowSoloRace + + def setAllowRaceTimeout(self, allowRaceTimeout): + self.allowRaceTimeout = allowRaceTimeout + + if simbase.wantPets: + + def getPetId(self): + return self.petId + + def b_setPetId(self, petId): + self.d_setPetId(petId) + self.setPetId(petId) + + def d_setPetId(self, petId): + self.sendUpdate('setPetId', [petId]) + + def setPetId(self, petId): + self.petId = petId + + def getPetTrickPhrases(self): + return self.petTrickPhrases + + def b_setPetTrickPhrases(self, tricks): + self.setPetTrickPhrases(tricks) + self.d_setPetTrickPhrases(tricks) + + def d_setPetTrickPhrases(self, tricks): + self.sendUpdate('setPetTrickPhrases', [tricks]) + + def setPetTrickPhrases(self, tricks): + self.petTrickPhrases = tricks + + def deletePet(self): + if self.petId == 0: + self.notify.warning("this toon doesn't have a pet to delete!") + return + simbase.air.petMgr.deleteToonsPet(self.doId) + + def setPetMovie(self, petId, flag): + self.notify.debug('setPetMovie: petId: %s, flag: %s' % (petId, flag)) + pet = simbase.air.doId2do.get(petId) + if pet is not None: + if pet.__class__.__name__ == 'DistributedPetAI': + pet.handleAvPetInteraction(flag, self.getDoId()) + else: + self.air.writeServerEvent('suspicious', self.doId, 'setPetMovie: playing pet movie %s on non-pet object %s' % (flag, petId)) + return + + def setPetTutorialDone(self, bDone): + self.notify.debug('setPetTutorialDone') + self.bPetTutorialDone = True + + def setFishBingoTutorialDone(self, bDone): + self.notify.debug('setFishBingoTutorialDone') + self.bFishBingoTutorialDone = True + + def setFishBingoMarkTutorialDone(self, bDone): + self.notify.debug('setFishBingoMarkTutorialDone') + self.bFishBingoMarkTutorialDone = True + + def enterEstate(self, ownerId, zoneId): + DistributedToonAI.notify.debug('enterEstate: %s %s %s' % (self.doId, ownerId, zoneId)) + if self.wasInEstate(): + self.cleanupEstateData() + collSphere = CollisionSphere(0, 0, 0, self.getRadius()) + collNode = CollisionNode('toonColl-%s' % self.doId) + collNode.addSolid(collSphere) + collNode.setFromCollideMask(BitMask32.allOff()) + collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) + self.collNodePath = self.attachNewNode(collNode) + taskMgr.add(self._moveSphere, self._getMoveSphereTaskName(), priority=OTPGlobals.AICollMovePriority) + self.inEstate = 1 + self.estateOwnerId = ownerId + self.estateZones = simbase.air.estateMgr.getEstateZones(ownerId) + self.estateHouseZones = simbase.air.estateMgr.getEstateHouseZones(ownerId) + self.enterPetLook() + + def _getPetLookerBodyNode(self): + return self.collNodePath + + def _getMoveSphereTaskName(self): + return 'moveSphere-%s' % self.doId + + def _moveSphere(self, task): + self.collNodePath.setZ(self.getRender(), 0) + return Task.cont + + def isInEstate(self): + return hasattr(self, 'inEstate') and self.inEstate + + def exitEstate(self, ownerId = None, zoneId = None): + DistributedToonAI.notify.debug('exitEstate: %s %s %s' % (self.doId, ownerId, zoneId)) + DistributedToonAI.notify.debug('current zone: %s' % self.zoneId) + self.exitPetLook() + taskMgr.remove(self._getMoveSphereTaskName()) + self.collNodePath.removeNode() + del self.collNodePath + del self.estateOwnerId + del self.estateHouseZones + del self.inEstate + self._wasInEstate = 1 + + def wasInEstate(self): + return hasattr(self, '_wasInEstate') and self._wasInEstate + + def cleanupEstateData(self): + del self.estateZones + del self._wasInEstate + + def setSC(self, msgId): + DistributedToonAI.notify.debug('setSC: %s' % msgId) + from toontown.pets import PetObserve + PetObserve.send(self.zoneId, PetObserve.getSCObserve(msgId, self.doId)) + if msgId in [21006]: + self.setHatePets(1) + elif msgId in [21000, + 21001, + 21003, + 21004, + 21200, + 21201, + 21202, + 21203, + 21204, + 21205, + 21206]: + self.setHatePets(0) + + def setSCCustom(self, msgId): + DistributedToonAI.notify.debug('setSCCustom: %s' % msgId) + from toontown.pets import PetObserve + PetObserve.send(self.zoneId, PetObserve.getSCObserve(msgId, self.doId)) + + def setHatePets(self, hate): + self.hatePets = hate + + def takeOutKart(self, zoneId = None): + if not self.kart: + from toontown.racing import DistributedVehicleAI + self.kart = DistributedVehicleAI.DistributedVehicleAI(self.air, self.doId) + if zoneId: + self.kart.generateWithRequired(zoneId) + else: + self.kart.generateWithRequired(self.zoneId) + self.kart.start() + + def reqCogSummons(self, type, suitIndex): + if type not in ('single', 'building', 'invasion'): + self.air.writeServerEvent('suspicious', self.doId, 'invalid cog summons type: %s' % type) + self.sendUpdate('cogSummonsResponse', ['fail', suitIndex, 0]) + return + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.air.writeServerEvent('suspicious', self.doId, 'invalid suitIndex: %s' % suitIndex) + self.sendUpdate('cogSummonsResponse', ['fail', suitIndex, 0]) + return + if not self.hasCogSummons(suitIndex, type): + self.air.writeServerEvent('suspicious', self.doId, 'bogus cog summons') + self.sendUpdate('cogSummonsResponse', ['fail', suitIndex, 0]) + return + if ZoneUtil.isWelcomeValley(self.zoneId): + self.sendUpdate('cogSummonsResponse', ['fail', suitIndex, 0]) + return + returnCode = None + if type == 'single': + returnCode = self.doSummonSingleCog(suitIndex) + elif type == 'building': + returnCode = self.doBuildingTakeover(suitIndex) + elif type == 'invasion': + returnCode = self.doCogInvasion(suitIndex) + if returnCode: + if returnCode[0] == 'success': + self.air.writeServerEvent('cogSummoned', self.doId, '%s|%s|%s' % (type, suitIndex, self.zoneId)) + self.removeCogSummonsEarned(suitIndex, type) + self.sendUpdate('cogSummonsResponse', returnCode) + return + + def doSummonSingleCog(self, suitIndex): + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + return ['badIndex', suitIndex, 0] + suitName = SuitDNA.suitHeadTypes[suitIndex] + streetId = ZoneUtil.getBranchZone(self.zoneId) + if not self.air.suitPlanners.has_key(streetId): + return ['badlocation', suitIndex, 0] + sp = self.air.suitPlanners[streetId] + map = sp.getZoneIdToPointMap() + zones = [self.zoneId, self.zoneId - 1, self.zoneId + 1] + for zoneId in zones: + if map.has_key(zoneId): + points = map[zoneId][:] + suit = sp.createNewSuit([], points, suitName=suitName) + if suit: + return ['success', suitIndex, 0] + + return ['badlocation', suitIndex, 0] + + def doBuildingTakeover(self, suitIndex): + streetId = ZoneUtil.getBranchZone(self.zoneId) + if not self.air.suitPlanners.has_key(streetId): + self.notify.warning('Street %d is not known.' % streetId) + return ['badlocation', suitIndex, 0] + sp = self.air.suitPlanners[streetId] + bm = sp.buildingMgr + building = self.findClosestDoor() + if building == None: + return ['badlocation', suitIndex, 0] + level = None + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + return ['badIndex', suitIndex, 0] + suitName = SuitDNA.suitHeadTypes[suitIndex] + track = SuitDNA.getSuitDept(suitName) + type = SuitDNA.getSuitType(suitName) + level, type, track = sp.pickLevelTypeAndTrack(None, type, track) + building.suitTakeOver(track, level, None) + self.notify.warning('cogTakeOver %s %s %d %d' % (track, + level, + building.block, + self.zoneId)) + return ['success', suitIndex, building.doId] + + def doCogInvasion(self, suitIndex): + invMgr = self.air.suitInvasionManager + if invMgr.getInvading(): + returnCode = 'busy' + else: + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + return ['badIndex', suitIndex, 0] + cogType = SuitDNA.suitHeadTypes[suitIndex] + numCogs = 1000 + if invMgr.startInvasion(cogType, numCogs, False): + returnCode = 'success' + else: + returnCode = 'fail' + return [returnCode, suitIndex, 0] + + def b_setCogSummonsEarned(self, cogSummonsEarned): + self.d_setCogSummonsEarned(cogSummonsEarned) + self.setCogSummonsEarned(cogSummonsEarned) + + def d_setCogSummonsEarned(self, cogSummonsEarned): + self.sendUpdate('setCogSummonsEarned', [cogSummonsEarned]) + + def setCogSummonsEarned(self, cogSummonsEarned): + self.cogSummonsEarned = cogSummonsEarned + + def getCogSummonsEarned(self): + return self.cogSummonsEarned + + def restockAllCogSummons(self): + numSuits = len(SuitDNA.suitHeadTypes) + fullSetForSuit = 1 | 2 | 4 + allSummons = numSuits * [fullSetForSuit] + self.b_setCogSummonsEarned(allSummons) + + def addCogSummonsEarned(self, suitIndex, type): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if type == 'single': + curSetting |= 1 + elif type == 'building': + curSetting |= 2 + elif type == 'invasion': + curSetting |= 4 + summons[suitIndex] = curSetting + self.b_setCogSummonsEarned(summons) + + def removeCogSummonsEarned(self, suitIndex, type): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if self.hasCogSummons(suitIndex, type): + if type == 'single': + curSetting &= -2 + elif type == 'building': + curSetting &= -3 + elif type == 'invasion': + curSetting &= -5 + summons[suitIndex] = curSetting + self.b_setCogSummonsEarned(summons) + if hasattr(self, 'autoRestockSummons') and self.autoRestockSummons: + self.restockAllCogSummons() + return True + self.notify.warning("Toon %s doesn't have a %s summons for %s" % (self.doId, type, suitIndex)) + return False + + def hasCogSummons(self, suitIndex, type = None): + summons = self.getCogSummonsEarned() + curSetting = summons[suitIndex] + if type == 'single': + return curSetting & 1 + elif type == 'building': + return curSetting & 2 + elif type == 'invasion': + return curSetting & 4 + return curSetting + + def hasParticularCogSummons(self, deptIndex, level, type): + if deptIndex not in range(len(SuitDNA.suitDepts)): + self.notify.warning('invalid parameter deptIndex %s' % deptIndex) + return False + if level not in range(SuitDNA.suitsPerDept): + self.notify.warning('invalid parameter level %s' % level) + return False + suitIndex = deptIndex * SuitDNA.suitsPerDept + level + retval = self.hasCogSummons(suitIndex, type) + return retval + + def assignNewCogSummons(self, level = None, summonType = None, deptIndex = None): + if level != None: + if deptIndex in range(len(SuitDNA.suitDepts)): + dept = deptIndex + else: + numDepts = len(SuitDNA.suitDepts) + dept = random.randrange(0, numDepts) + suitIndex = dept * SuitDNA.suitsPerDept + level + elif deptIndex in range(len(SuitDNA.suitDepts)): + randomLevel = random.randrange(0, SuitDNA.suitsPerDept) + suitIndex = deptIndex * SuitDNA.suitsPerLevel + randomLevel + else: + numSuits = len(SuitDNA.suitHeadTypes) + suitIndex = random.randrange(0, numSuits) + if summonType in ['single', 'building', 'invasion']: + type = summonType + else: + typeWeights = ['single'] * 70 + ['building'] * 25 + ['invasion'] * 5 + type = random.choice(typeWeights) + if suitIndex >= len(SuitDNA.suitHeadTypes): + self.notify.warning('Bad suit index: %s' % suitIndex) + self.addCogSummonsEarned(suitIndex, type) + return (suitIndex, type) + + def findClosestDoor(self): + zoneId = self.zoneId + streetId = ZoneUtil.getBranchZone(zoneId) + sp = self.air.suitPlanners[streetId] + if not sp: + return None + bm = sp.buildingMgr + if not bm: + return None + zones = [zoneId, + zoneId - 1, + zoneId + 1, + zoneId - 2, + zoneId + 2] + for zone in zones: + for i in bm.getToonBlocks(): + building = bm.getBuilding(i) + extZoneId, intZoneId = building.getExteriorAndInteriorZoneId() + if not NPCToons.isZoneProtected(intZoneId): + if hasattr(building, 'door'): + if building.door.zoneId == zone: + return building + + return None + + def b_setGardenTrophies(self, trophyList): + self.setGardenTrophies(trophyList) + self.d_setGardenTrophies(trophyList) + + def setGardenTrophies(self, trophyList): + self.notify.debug('setting gardenTrophies to %s' % trophyList) + self.gardenTrophies = trophyList + + def d_setGardenTrophies(self, trophyList): + self.sendUpdate('setGardenTrophies', [trophyList]) + + def getGardenTrophies(self): + return self.gardenTrophies + + def setGardenSpecials(self, specials): + for special in specials: + if special[1] > 255: + special[1] = 255 + + self.gardenSpecials = specials + + def getGardenSpecials(self): + return self.gardenSpecials + + def d_setGardenSpecials(self, specials): + self.sendUpdate('setGardenSpecials', [specials]) + + def b_setGardenSpecials(self, specials): + for special in specials: + if special[1] > 255: + newCount = 255 + index = special[0] + self.gardenSpecials.remove(special) + self.gardenSpecials.append((index, newCount)) + self.gardenSpecials.sort() + + self.setGardenSpecials(specials) + self.d_setGardenSpecials(specials) + + def addGardenItem(self, index, count): + for item in self.gardenSpecials: + if item[0] == index: + newCount = item[1] + count + self.gardenSpecials.remove(item) + self.gardenSpecials.append((index, newCount)) + self.gardenSpecials.sort() + self.b_setGardenSpecials(self.gardenSpecials) + return + + self.gardenSpecials.append((index, count)) + self.gardenSpecials.sort() + self.b_setGardenSpecials(self.gardenSpecials) + + def removeGardenItem(self, index, count): + for item in self.gardenSpecials: + if item[0] == index: + newCount = item[1] - count + self.gardenSpecials.remove(item) + if newCount > 0: + self.gardenSpecials.append((index, newCount)) + self.gardenSpecials.sort() + self.b_setGardenSpecials(self.gardenSpecials) + return + + self.notify.warning("removing garden item %d that toon doesn't have" % index) + + def b_setFlowerCollection(self, speciesList, varietyList): + self.setFlowerCollection(speciesList, varietyList) + self.d_setFlowerCollection(speciesList, varietyList) + + def d_setFlowerCollection(self, speciesList, varietyList): + self.sendUpdate('setFlowerCollection', [speciesList, varietyList]) + + def setFlowerCollection(self, speciesList, varietyList): + self.flowerCollection = FlowerCollection.FlowerCollection() + self.flowerCollection.makeFromNetLists(speciesList, varietyList) + + def getFlowerCollection(self): + return self.flowerCollection.getNetLists() + + def b_setMaxFlowerBasket(self, maxFlowerBasket): + self.d_setMaxFlowerBasket(maxFlowerBasket) + self.setMaxFlowerBasket(maxFlowerBasket) + + def d_setMaxFlowerBasket(self, maxFlowerBasket): + self.sendUpdate('setMaxFlowerBasket', [maxFlowerBasket]) + + def setMaxFlowerBasket(self, maxFlowerBasket): + self.maxFlowerBasket = maxFlowerBasket + + def getMaxFlowerBasket(self): + return self.maxFlowerBasket + + def b_setFlowerBasket(self, speciesList, varietyList): + self.setFlowerBasket(speciesList, varietyList) + self.d_setFlowerBasket(speciesList, varietyList) + + def d_setFlowerBasket(self, speciesList, varietyList): + self.sendUpdate('setFlowerBasket', [speciesList, varietyList]) + + def setFlowerBasket(self, speciesList, varietyList): + self.flowerBasket = FlowerBasket.FlowerBasket() + self.flowerBasket.makeFromNetLists(speciesList, varietyList) + + def getFlowerBasket(self): + return self.flowerBasket.getNetLists() + + def makeRandomFlowerBasket(self): + self.flowerBasket.generateRandomBasket() + self.d_setFlowerBasket(*self.flowerBasket.getNetLists()) + + def addFlowerToBasket(self, species, variety): + numFlower = len(self.flowerBasket) + if numFlower >= self.maxFlowerBasket: + self.notify.warning('addFlowerToBasket: cannot add flower, basket is full') + return 0 + elif self.flowerBasket.addFlower(species, variety): + self.d_setFlowerBasket(*self.flowerBasket.getNetLists()) + return 1 + else: + self.notify.warning('addFlowerToBasket: addFlower failed') + return 0 + + def removeFlowerFromBasketAtIndex(self, index): + if self.flowerBasket.removeFlowerAtIndex(index): + self.d_setFlowerBasket(*self.flowerBasket.getNetLists()) + return 1 + else: + self.notify.warning('removeFishFromTank: cannot find fish') + return 0 + + def b_setShovel(self, shovelId): + self.d_setShovel(shovelId) + self.setShovel(shovelId) + + def d_setShovel(self, shovelId): + self.sendUpdate('setShovel', [shovelId]) + + def setShovel(self, shovelId): + self.shovel = shovelId + + def getShovel(self): + return self.shovel + + def b_setShovelSkill(self, skillLevel): + self.sendGardenEvent() + if skillLevel >= GardenGlobals.ShovelAttributes[self.shovel]['skillPts']: + if self.shovel < GardenGlobals.MAX_SHOVELS - 1: + self.b_setShovel(self.shovel + 1) + self.setShovelSkill(0) + self.d_setShovelSkill(0) + self.sendUpdate('promoteShovel', [self.shovel]) + self.air.writeServerEvent('garden_new_shovel', self.doId, '%d' % self.shovel) + else: + self.setShovelSkill(skillLevel) + self.d_setShovelSkill(skillLevel) + + def d_setShovelSkill(self, skillLevel): + self.sendUpdate('setShovelSkill', [skillLevel]) + + def setShovelSkill(self, skillLevel): + self.shovelSkill = skillLevel + + def getShovelSkill(self): + return self.shovelSkill + + def b_setWateringCan(self, wateringCanId): + self.d_setWateringCan(wateringCanId) + self.setWateringCan(wateringCanId) + + def d_setWateringCan(self, wateringCanId): + self.sendUpdate('setWateringCan', [wateringCanId]) + + def setWateringCan(self, wateringCanId): + self.wateringCan = wateringCanId + + def getWateringCan(self): + return self.wateringCan + + def b_setWateringCanSkill(self, skillLevel): + self.sendGardenEvent() + if skillLevel >= GardenGlobals.WateringCanAttributes[self.wateringCan]['skillPts']: + if self.wateringCan < GardenGlobals.MAX_WATERING_CANS - 1: + self.b_setWateringCan(self.wateringCan + 1) + self.setWateringCanSkill(0) + self.d_setWateringCanSkill(0) + self.sendUpdate('promoteWateringCan', [self.wateringCan]) + self.air.writeServerEvent('garden_new_wateringCan', self.doId, '%d' % self.wateringCan) + else: + skillLevel = GardenGlobals.WateringCanAttributes[self.wateringCan]['skillPts'] - 1 + self.setWateringCanSkill(skillLevel) + self.d_setWateringCanSkill(skillLevel) + else: + self.setWateringCanSkill(skillLevel) + self.d_setWateringCanSkill(skillLevel) + + def d_setWateringCanSkill(self, skillLevel): + self.sendUpdate('setWateringCanSkill', [skillLevel]) + + def setWateringCanSkill(self, skillLevel): + self.wateringCanSkill = skillLevel + + def getWateringCanSkill(self): + return self.wateringCanSkill + + def b_setTrackBonusLevel(self, trackBonusLevelArray): + self.setTrackBonusLevel(trackBonusLevelArray) + self.d_setTrackBonusLevel(trackBonusLevelArray) + + def d_setTrackBonusLevel(self, trackBonusLevelArray): + self.sendUpdate('setTrackBonusLevel', [trackBonusLevelArray]) + + def setTrackBonusLevel(self, trackBonusLevelArray): + self.trackBonusLevel = trackBonusLevelArray + + def getTrackBonusLevel(self, track = None): + if track == None: + return self.trackBonusLevel + else: + return self.trackBonusLevel[track] + return + + def checkGagBonus(self, track, level): + trackBonus = self.getTrackBonusLevel(track) + return trackBonus >= level + + def giveMeSpecials(self, id = None): + print 'Specials Go!!' + self.b_setGardenSpecials([(0, 3), + (1, 2), + (2, 3), + (3, 2), + (4, 3), + (5, 2), + (6, 3), + (7, 2), + (100, 1), + (101, 3), + (102, 1)]) + + def reqUseSpecial(self, special): + response = self.tryToUseSpecial(special) + self.sendUpdate('useSpecialResponse', [response]) + + def tryToUseSpecial(self, special): + estateOwnerDoId = simbase.air.estateMgr.zone2owner.get(self.zoneId) + response = 'badlocation' + doIHaveThisSpecial = False + for curSpecial in self.gardenSpecials: + if curSpecial[0] == special and curSpecial[1] > 0: + doIHaveThisSpecial = True + break + + if not doIHaveThisSpecial: + return response + if not self.doId == estateOwnerDoId: + self.notify.warning("how did this happen, planting an item you don't own") + return response + if estateOwnerDoId: + estate = simbase.air.estateMgr.estate.get(estateOwnerDoId) + if estate and hasattr(estate, 'avIdList'): + ownerIndex = estate.avIdList.index(estateOwnerDoId) + if ownerIndex >= 0: + estate.doEpochNow(onlyForThisToonIndex=ownerIndex) + self.removeGardenItem(special, 1) + response = 'success' + self.air.writeServerEvent('garden_fertilizer', self.doId, '') + return response + + def sendGardenEvent(self): + if hasattr(self, 'estateZones') and hasattr(self, 'doId'): + if simbase.wantPets and self.hatePets: + PetObserve.send(self.estateZones, PetObserve.PetActionObserve(PetObserve.Actions.GARDEN, self.doId)) + + def setGardenStarted(self, bStarted): + self.gardenStarted = bStarted + + def d_setGardenStarted(self, bStarted): + self.sendUpdate('setGardenStarted', [bStarted]) + + def b_setGardenStarted(self, bStarted): + self.setGardenStarted(bStarted) + self.d_setGardenStarted(bStarted) + + def getGardenStarted(self): + return self.gardenStarted + + def logSuspiciousEvent(self, eventName): + senderId = self.air.getAvatarIdFromSender() + eventStr = 'senderId=%s ' % senderId + eventStr += eventName + strSearch = re.compile('AvatarHackWarning! nodename') + if strSearch.search(eventName, 0, 100): + self.air.district.recordSuspiciousEventData(len(eventStr)) + self.air.writeServerEvent('suspicious', self.doId, eventStr) + if simbase.config.GetBool('want-ban-setSCSinging', True): + if 'invalid msgIndex in setSCSinging:' in eventName: + if senderId == self.doId: + commentStr = 'Toon %s trying to call setSCSinging' % self.doId + simbase.air.banManager.ban(self.doId, self.DISLid, commentStr) + else: + self.notify.warning('logSuspiciousEvent event=%s senderId=%s != self.doId=%s' % (eventName, senderId, self.doId)) + if simbase.config.GetBool('want-ban-setAnimState', True): + if eventName.startswith('setAnimState: '): + if senderId == self.doId: + commentStr = 'Toon %s trying to call setAnimState' % self.doId + simbase.air.banManager.ban(self.doId, self.DISLid, commentStr) + else: + self.notify.warning('logSuspiciousEvent event=%s senderId=%s != self.doId=%s' % (eventName, senderId, self.doId)) + + def getGolfTrophies(self): + return self.golfTrophies + + def getGolfCups(self): + return self.golfCups + + def b_setGolfHistory(self, history): + self.setGolfHistory(history) + self.d_setGolfHistory(history) + + def d_setGolfHistory(self, history): + self.sendUpdate('setGolfHistory', [history]) + + def setGolfHistory(self, history): + self.notify.debug('setting golfHistory to %s' % history) + self.golfHistory = history + self.golfTrophies = GolfGlobals.calcTrophyListFromHistory(self.golfHistory) + self.golfCups = GolfGlobals.calcCupListFromHistory(self.golfHistory) + + def getGolfHistory(self): + return self.golfHistory + + def b_setGolfHoleBest(self, holeBest): + self.setGolfHoleBest(holeBest) + self.d_setGolfHoleBest(holeBest) + + def d_setGolfHoleBest(self, holeBest): + packed = GolfGlobals.packGolfHoleBest(holeBest) + self.sendUpdate('setPackedGolfHoleBest', [packed]) + + def setGolfHoleBest(self, holeBest): + self.golfHoleBest = holeBest + + def getGolfHoleBest(self): + return self.golfHoleBest + + def getPackedGolfHoleBest(self): + packed = GolfGlobals.packGolfHoleBest(self.golfHoleBest) + return packed + + def setPackedGolfHoleBest(self, packedHoleBest): + unpacked = GolfGlobals.unpackGolfHoleBest(packedHoleBest) + self.setGolfHoleBest(unpacked) + + def b_setGolfCourseBest(self, courseBest): + self.setGolfCourseBest(courseBest) + self.d_setGolfCourseBest(courseBest) + + def d_setGolfCourseBest(self, courseBest): + self.sendUpdate('setGolfCourseBest', [courseBest]) + + def setGolfCourseBest(self, courseBest): + self.golfCourseBest = courseBest + + def getGolfCourseBest(self): + return self.golfCourseBest + + def setUnlimitedSwing(self, unlimitedSwing): + self.unlimitedSwing = unlimitedSwing + + def getUnlimitedSwing(self): + return self.unlimitedSwing + + def b_setUnlimitedSwing(self, unlimitedSwing): + self.setUnlimitedSwing(unlimitedSwing) + self.d_setUnlimitedSwing(unlimitedSwing) + + def d_setUnlimitedSwing(self, unlimitedSwing): + self.sendUpdate('setUnlimitedSwing', [unlimitedSwing]) + + def b_setPinkSlips(self, pinkSlips): + self.d_setPinkSlips(pinkSlips) + self.setPinkSlips(pinkSlips) + + def d_setPinkSlips(self, pinkSlips): + self.sendUpdate('setPinkSlips', [pinkSlips]) + + def setPinkSlips(self, pinkSlips): + self.pinkSlips = pinkSlips + + def getPinkSlips(self): + return self.pinkSlips + + def addPinkSlips(self, amountToAdd): + pinkSlips = min(self.pinkSlips + amountToAdd, 255) + self.b_setPinkSlips(pinkSlips) + + def removePinkSlips(self, amount): + if hasattr(self, 'autoRestockPinkSlips') and self.autoRestockPinkSlips: + amount = 0 + pinkSlips = max(self.pinkSlips - amount, 0) + self.b_setPinkSlips(pinkSlips) + + def setPreviousAccess(self, access): + self.previousAccess = access + + def b_setAccess(self, access): + self.setAccess(access) + self.d_setAccess(access) + + def d_setAccess(self, access): + self.sendUpdate('setAccess', [access]) + + def setAccess(self, access): + paidStatus = simbase.config.GetString('force-paid-status', 'none') + if paidStatus == 'unpaid': + access = 1 + print 'Setting Access %s' % access + if access == OTPGlobals.AccessInvalid: + if not __dev__: + self.air.writeServerEvent('Setting Access', self.doId, 'setAccess not being sent by the OTP Server, changing access to unpaid') + access = OTPGlobals.AccessVelvetRope + elif __dev__: + access = OTPGlobals.AccessFull + self.setGameAccess(access) + + def setGameAccess(self, access): + self.gameAccess = access + + def getGameAccess(self): + return self.gameAccess + + def b_setNametagStyle(self, nametagStyle): + self.d_setNametagStyle(nametagStyle) + self.setNametagStyle(nametagStyle) + + def d_setNametagStyle(self, nametagStyle): + self.sendUpdate('setNametagStyle', [nametagStyle]) + + def setNametagStyle(self, nametagStyle): + self.nametagStyle = nametagStyle + + def getNametagStyle(self): + return self.nametagStyle + + def logMessage(self, message): + avId = self.air.getAvatarIdFromSender() + if __dev__: + print 'CLIENT LOG MESSAGE %s %s' % (avId, message) + try: + self.air.writeServerEvent('clientLog', avId, message) + except: + self.air.writeServerEvent('suspicious', avId, 'client sent us a clientLog that caused an exception') + + def b_setMail(self, mail): + self.d_setMail(mail) + self.setMail(mail) + + def d_setMail(self, mail): + self.sendUpdate('setMail', [mail]) + + def setMail(self, mail): + self.mail = mail + + def setNumMailItems(self, numMailItems): + self.numMailItems = numMailItems + + def setSimpleMailNotify(self, simpleMailNotify): + self.simpleMailNotify = simpleMailNotify + + def setInviteMailNotify(self, inviteMailNotify): + self.inviteMailNotify = inviteMailNotify + + def setInvites(self, invites): + self.invites = [] + for i in xrange(len(invites)): + oneInvite = invites[i] + newInvite = InviteInfoBase(*oneInvite) + self.invites.append(newInvite) + + def updateInviteMailNotify(self): + invitesInMailbox = self.getInvitesToShowInMailbox() + newInvites = 0 + readButNotRepliedInvites = 0 + for invite in invitesInMailbox: + if invite.status == PartyGlobals.InviteStatus.NotRead: + newInvites += 1 + elif invite.status == PartyGlobals.InviteStatus.ReadButNotReplied: + readButNotRepliedInvites += 1 + if __dev__: + partyInfo = self.getOnePartyInvitedTo(invite.partyId) + if not partyInfo: + self.notify.error('party info not found in partiesInvtedTo, partyId = %s' % str(invite.partyId)) + + if newInvites: + self.setInviteMailNotify(ToontownGlobals.NewItems) + elif readButNotRepliedInvites: + self.setInviteMailNotify(ToontownGlobals.OldItems) + else: + self.setInviteMailNotify(ToontownGlobals.NoItems) + + def getNumNonResponseInvites(self): + count = 0 + for i in xrange(len(self.invites)): + if self.invites[i].status == InviteStatus.NotRead or self.invites[i].status == InviteStatus.ReadButNotReplied: + count += 1 + + return count + + def getInvitesToShowInMailbox(self): + result = [] + for invite in self.invites: + appendInvite = True + if invite.status == InviteStatus.Accepted or invite.status == InviteStatus.Rejected: + appendInvite = False + if appendInvite: + partyInfo = self.getOnePartyInvitedTo(invite.partyId) + if not partyInfo: + appendInvite = False + if appendInvite: + if partyInfo.status == PartyGlobals.PartyStatus.Cancelled: + appendInvite = False + if appendInvite: + endDate = partyInfo.endTime.date() + curDate = simbase.air.toontownTimeManager.getCurServerDateTime().date() + if endDate < curDate: + appendInvite = False + if appendInvite: + result.append(invite) + + return result + + def getNumInvitesToShowInMailbox(self): + result = len(self.getInvitesToShowInMailbox()) + return result + + def setHostedParties(self, hostedParties): + self.hostedParties = [] + for i in xrange(len(hostedParties)): + hostedInfo = hostedParties[i] + newParty = PartyInfoAI(*hostedInfo) + self.hostedParties.append(newParty) + + def setPartiesInvitedTo(self, partiesInvitedTo): + self.partiesInvitedTo = [] + for i in xrange(len(partiesInvitedTo)): + partyInfo = partiesInvitedTo[i] + newParty = PartyInfoAI(*partyInfo) + self.partiesInvitedTo.append(newParty) + + self.updateInviteMailNotify() + self.checkMailboxFullIndicator() + + def getOnePartyInvitedTo(self, partyId): + result = None + for i in xrange(len(self.partiesInvitedTo)): + partyInfo = self.partiesInvitedTo[i] + if partyInfo.partyId == partyId: + result = partyInfo + break + + return result + + def setPartyReplyInfoBases(self, replies): + self.partyReplyInfoBases = [] + for i in xrange(len(replies)): + partyReply = replies[i] + repliesForOneParty = PartyReplyInfoBase(*partyReply) + self.partyReplyInfoBases.append(repliesForOneParty) + + def updateInvite(self, inviteKey, newStatus): + for invite in self.invites: + if invite.inviteKey == inviteKey: + invite.status = newStatus + self.updateInviteMailNotify() + self.checkMailboxFullIndicator() + break + + def updateReply(self, partyId, inviteeId, newStatus): + for partyReply in self.partyReplyInfoBases: + if partyReply.partyId == partyId: + for reply in partyReply.replies: + if reply.inviteeId == inviteeId: + reply.inviteeId = newStatus + break + + def canPlanParty(self): + nonCancelledPartiesInTheFuture = 0 + for partyInfo in self.hostedParties: + if partyInfo.status not in (PartyGlobals.PartyStatus.Cancelled, PartyGlobals.PartyStatus.Finished, PartyGlobals.PartyStatus.NeverStarted): + nonCancelledPartiesInTheFuture += 1 + if nonCancelledPartiesInTheFuture >= PartyGlobals.MaxHostedPartiesPerToon: + break + + result = nonCancelledPartiesInTheFuture < PartyGlobals.MaxHostedPartiesPerToon + return result + + def setPartyCanStart(self, partyId): + self.notify.debug('setPartyCanStart called passing in partyId=%s' % partyId) + found = False + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = PartyGlobals.PartyStatus.CanStart + found = True + break + + if not found: + self.notify.warning("setPartyCanStart can't find partyId %s" % partyId) + + def setPartyStatus(self, partyId, newStatus): + self.notify.debug('setPartyStatus called passing in partyId=%s newStauts=%d' % (partyId, newStatus)) + found = False + for partyInfo in self.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = newStatus + found = True + break + + info = self.getOnePartyInvitedTo(partyId) + if info: + found = True + info.status = newStatus + if not found: + self.notify.warning("setPartyCanStart can't find hosted or invitedTO partyId %s" % partyId) + + def b_setAwardMailboxContents(self, awardMailboxContents): + self.setAwardMailboxContents(awardMailboxContents) + self.d_setAwardMailboxContents(awardMailboxContents) + + def d_setAwardMailboxContents(self, awardMailboxContents): + self.sendUpdate('setAwardMailboxContents', [awardMailboxContents.getBlob(store=CatalogItem.Customization)]) + + def setAwardMailboxContents(self, awardMailboxContents): + self.notify.debug('Setting awardMailboxContents to %s.' % awardMailboxContents) + self.awardMailboxContents = CatalogItemList.CatalogItemList(awardMailboxContents, store=CatalogItem.Customization) + self.notify.debug('awardMailboxContents is %s.' % self.awardMailboxContents) + if len(awardMailboxContents) == 0: + self.b_setAwardNotify(ToontownGlobals.NoItems) + self.checkMailboxFullIndicator() + + def getAwardMailboxContents(self): + return self.awardMailboxContents.getBlob(store=CatalogItem.Customization) + + def b_setAwardSchedule(self, onOrder, doUpdateLater = True): + self.setAwardSchedule(onOrder, doUpdateLater) + self.d_setAwardSchedule(onOrder) + + def d_setAwardSchedule(self, onOrder): + self.sendUpdate('setAwardSchedule', [onOrder.getBlob(store=CatalogItem.Customization | CatalogItem.DeliveryDate)]) + + def setAwardSchedule(self, onAwardOrder, doUpdateLater = True): + self.onAwardOrder = CatalogItemList.CatalogItemList(onAwardOrder, store=CatalogItem.Customization | CatalogItem.DeliveryDate) + if hasattr(self, 'name'): + if doUpdateLater and self.air.doLiveUpdates and hasattr(self, 'air'): + taskName = self.uniqueName('next-award-delivery') + taskMgr.remove(taskName) + now = int(time.time() / 60 + 0.5) + nextItem = None + nextTime = self.onAwardOrder.getNextDeliveryDate() + nextItem = self.onAwardOrder.getNextDeliveryItem() + if nextItem != None: + pass + if nextTime != None: + duration = max(10.0, nextTime * 60 - time.time()) + taskMgr.doMethodLater(duration, self.__deliverAwardPurchase, taskName) + return + + def __deliverAwardPurchase(self, task): + now = int(time.time() / 60 + 0.5) + delivered, remaining = self.onAwardOrder.extractDeliveryItems(now) + self.notify.info('Award Delivery for %s: %s.' % (self.doId, delivered)) + self.b_setAwardMailboxContents(self.awardMailboxContents + delivered) + self.b_setAwardSchedule(remaining) + if delivered: + self.b_setAwardNotify(ToontownGlobals.NewItems) + return Task.done + + def b_setAwardNotify(self, awardMailboxNotify): + self.setAwardNotify(awardMailboxNotify) + self.d_setAwardNotify(awardMailboxNotify) + + def d_setAwardNotify(self, awardMailboxNotify): + self.sendUpdate('setAwardNotify', [awardMailboxNotify]) + + def setAwardNotify(self, awardNotify): + self.awardNotify = awardNotify + + def b_setGM(self, type): + self.sendUpdate('setGM', [type]) + self.setGM(type) + + def setGM(self, type): + wasGM = self._isGM + formerType = self._gmType + self._isGM = type != 0 + self._gmType = None + if self._isGM: + self._gmType = type - 1 + MaxGMType = len(TTLocalizer.GM_NAMES) - 1 + if self._gmType > MaxGMType: + self.notify.warning('toon %s has invalid GM type: %s' % (self.doId, self._gmType)) + self._gmType = MaxGMType + self._updateGMName(formerType) + return + + def isGM(self): + return self._isGM + + def _nameIsPrefixed(self, prefix): + if len(self.name) > len(prefix): + if self.name[:len(prefix)] == prefix: + return True + return False + + def _updateGMName(self, formerType = None): + if formerType is None: + formerType = self._gmType + name = self.name + if formerType is not None: + gmPrefix = TTLocalizer.GM_NAMES[formerType] + ' ' + if self._nameIsPrefixed(gmPrefix): + name = self.name[len(gmPrefix):] + if self._isGM: + gmPrefix = TTLocalizer.GM_NAMES[self._gmType] + ' ' + newName = gmPrefix + name + else: + newName = name + if self.name != newName: + self.b_setName(newName) + return + + def setName(self, name): + DistributedPlayerAI.DistributedPlayerAI.setName(self, name) + if self.WantOldGMNameBan: + if self.isGenerated(): + self._checkOldGMName() + self._updateGMName() + + def _checkOldGMName(self): + if '$' in set(self.name): + if config.GetBool('want-ban-old-gm-name', 0): + self.ban('invalid name: %s' % self.name) + else: + self.air.writeServerEvent('suspicious', self.doId, '$ found in toon name') + + def setModuleInfo(self, info): + avId = self.air.getAvatarIdFromSender() + key = 'outrageous' + self.moduleWhitelist = self.modulelist.loadWhitelistFile() + self.moduleBlacklist = self.modulelist.loadBlacklistFile() + for obfuscatedModule in info: + module = '' + p = 0 + for ch in obfuscatedModule: + ic = ord(ch) ^ ord(key[p]) + p += 1 + if p >= len(key): + p = 0 + module += chr(ic) + + if module not in self.moduleWhitelist: + if module in self.moduleBlacklist: + self.air.writeServerEvent('suspicious', avId, 'Black List module %s loaded into process.' % module) + if simbase.config.GetBool('want-ban-blacklist-module', False): + commentStr = 'User has blacklist module: %s attached to their game process' % module + dislId = self.DISLid + simbase.air.banManager.ban(self.doId, dislId, commentStr) + else: + self.air.writeServerEvent('suspicious', avId, 'Unknown module %s loaded into process.' % module) + + def teleportResponseToAI(self, toAvId, available, shardId, hoodId, zoneId, fromAvId): + if not self.WantTpTrack: + return + senderId = self.air.getAvatarIdFromSender() + if toAvId != self.doId: + self.air.writeServerEvent('suspicious', self.doId, 'toAvId=%d is not equal to self.doId' % toAvId) + return + if available != 1: + self.air.writeServerEvent('suspicious', self.doId, 'invalid availableValue=%d' % available) + return + if fromAvId == 0: + return + self.air.teleportRegistrar.registerValidTeleport(toAvId, available, shardId, hoodId, zoneId, fromAvId) + dg = self.dclass.aiFormatUpdate('teleportResponse', fromAvId, fromAvId, self.doId, [toAvId, + available, + shardId, + hoodId, + zoneId]) + self.air.send(dg) + + @staticmethod + def staticGetLogicalZoneChangeAllEvent(): + return 'DOLogicalChangeZone-all' + + def _garbageInfo(self): + if hasattr(self, 'inventory'): + if not hasattr(self.inventory, '_createStack'): + return 'inventory has no create stack' + else: + return self.inventory._createStack + return 'no inventory' + + def flagAv(self, avId, reason, params): + self.notify.debug('reason: %s timepassed: %s' % (reason, globalClock.getFrameTime() - DistributedToonAI.lastFlagAvTime)) + if reason == AV_FLAG_REASON_TOUCH and globalClock.getFrameTime() - DistributedToonAI.lastFlagAvTime > AV_TOUCH_CHECK_DELAY_AI: + DistributedToonAI.lastFlagAvTime = globalClock.getFrameTime() + av = self.air.doId2do.get(avId) + otherAv = self.air.doId2do.get(int(params[0])) + self.notify.debug('checking suspicious avatar positioning %s for %s with %s' % (avId, reason, params)) + if av and otherAv and isinstance(av, DistributedToonAI) and isinstance(otherAv, DistributedToonAI) and av.zoneId == otherAv.zoneId and av.zoneId not in MinigameCreatorAI.MinigameZoneRefs: + self.notify.debug('...in zone %s' % av.zoneId) + componentNode = av.getParent().attachNewNode('blah') + componentNode.setPos(av.getComponentX(), av.getComponentY(), av.getComponentZ()) + avPos = componentNode.getPos(av.getRender()) + componentNode.reparentTo(otherAv.getParent()) + componentNode.setPos(otherAv.getComponentX(), otherAv.getComponentY(), otherAv.getComponentZ()) + otherAvPos = componentNode.getPos(otherAv.getRender()) + componentNode.removeNode() + zDist = avPos.getZ() - otherAvPos.getZ() + avPos2D = copy.copy(avPos) + avPos2D.setZ(0) + otherAvPos2D = copy.copy(otherAvPos) + otherAvPos2D.setZ(0) + moveVec = avPos2D - otherAvPos2D + dist = moveVec.length() + self.notify.debug('2d dist between avs is %s %s %s' % (dist, avPos, otherAvPos)) + if dist < AV_TOUCH_CHECK_DIST and zDist < AV_TOUCH_CHECK_DIST_Z: + self.notify.debug('...moving!') + if dist == 0.0: + moveVec = Vec3(1.0, 0, 0) + else: + moveVec.normalize() + moveVec = moveVec * AV_TOUCH_CHECK_DIST + avHpr = av.getHpr(av.getRender()) + newX = avPos.getX() + moveVec.getX() + newY = avPos.getY() + moveVec.getY() + newZ = avPos.getZ() + moveVec.getZ() + newH = avHpr.getX() + newP = avHpr.getY() + newR = avHpr.getZ() + av.setPosHpr(av.getRender(), newX, newY, newZ, newH, newP, newR) + newAvPos = av.getPos() + if newAvPos.getX() > 3000 or newAvPos.getX() < -3000 or newAvPos.getY() > 3000 or newAvPos.getY() < -3000: + return + av.d_setXY(newAvPos.getX(), newAvPos.getY()) + self.notify.debug('setting ai pos: %s %s %s and sending pos: %s' % (newX, + newY, + newZ, + newAvPos)) + if len(DistributedToonAI.flagCounts) > AV_FLAG_HISTORY_LEN: + DistributedToonAI.flagCounts = {} + avPairKey = str(min(av.doId, otherAv.doId)) + '+' + str(max(av.doId, otherAv.doId)) + prevCount = DistributedToonAI.flagCounts.setdefault(avPairKey, [{}, globalClock.getFrameTime(), {}]) + if not prevCount[2].has_key(av.doId): + prevCount[2][av.doId] = [None, None] + if not prevCount[0].has_key(av.doId): + prevCount[0][av.doId] = 0 + self.notify.debug('moving av %s, newPos: %s oldPos: %s' % (av.doId, prevCount[2][av.doId], avPos)) + if prevCount[2][av.doId][0] == None or prevCount[2][av.doId][1] == None: + pass + elif prevCount[2][av.doId][0] != avPos.getX() or prevCount[2][av.doId][1] != avPos.getY(): + prevCount[0][av.doId] += 1 + prevCount[2][av.doId] = [newX, newY] + if prevCount[0][av.doId] > AV_TOUCH_COUNT_LIMIT: + if globalClock.getFrameTime() - prevCount[1] < AV_TOUCH_COUNT_TIME: + zoneId = not hasattr(av, 'zoneId') and 'undef' or av.zoneId + battleId = not hasattr(av, 'battleId') and 'undef' or av.battleId + animName = not hasattr(av, 'animName') and 'undef' or av.animName + inEstate = not hasattr(av, 'isInEstate') and 'undef' or av.isInEstate() + ghostMode = not hasattr(av, 'ghostMode') and 'undef' or av.ghostMode + immortalMode = not hasattr(av, 'immortalMode') and 'undef' or av.immortalMode + isGm = not hasattr(av, '_isGM') and 'undef' or av._isGM + valStr = '%s %s %s %s %s %s %s %s' % (otherAv.doId, + zoneId, + battleId, + animName, + inEstate, + ghostMode, + immortalMode, + isGm) + self.notify.info('av %s is consistently in an inappropriate position with %s...' % (av.doId, valStr)) + self.air.writeServerEvent('suspicious', avId, ' consistently in an inappropriate position with toon %s' % valStr) + response = simbase.config.GetString('toon-pos-hack-response', 'nothing') + av.handleHacking(response, 'collision and position hacking', [otherAv]) + del DistributedToonAI.flagCounts[avPairKey] + return + + def handleHacking(self, response, comment, coconspirators = []): + if response == 'quietzone': + self.b_setLocation(self.parentId, ToontownGlobals.QuietZone) + elif response == 'disconnect': + self.disconnect() + elif response == 'disconnectall': + self.disconnect() + for coconspirator in coconspirators: + coconspirator.disconnect() + + elif response == 'ban': + self.ban('collision and position hacking') + self.disconnect() + elif response == 'banall': + self.ban('collision and position hacking') + self.disconnect() + for coconspirator in coconspirators: + coconspirator.ban('collision and position hacking') + coconspirator.disconnect() + + def requestPing(self, avId): + av = self.air.doId2do.get(avId) + if av: + from toontown.toon.DistributedNPCToonBaseAI import DistributedNPCToonBaseAI + if isinstance(av, DistributedNPCToonBaseAI): + return + if isinstance(av, DistributedToonAI) and not DistributedToonAI.pingedAvs.has_key(avId): + av.sendPing() + return Task.again + + def sendPing(self): + + def verify(theId): + if self.air: + msg = '%s failed to respond to ping!' % theId + self.notify.warning(msg) + self.air.writeServerEvent('suspicious', theId, msg) + self.cleanupPing() + disconnect = simbase.config.GetBool('client-ping-disconnect', True) + if disconnect: + av = self.air.getDo(theId) + if av: + av.disconnect() + return Task.done + + val = '' + for i in range(14): + val = val + random.choice('abcdefghijklmnopqrstuvwxyz') + + self.sendUpdateToAvatarId(self.doId, 'ping', [val]) + DistributedToonAI.pingedAvs[self.doId] = [globalClock.getFrameTime(), val] + delay = simbase.config.GetInt('client-ping-timeout', 150) + taskMgr.doMethodLater(delay, verify, 'pingverify-' + str(self.doId), extraArgs=[self.doId]) + + def pingresp(self, resp): + senderId = self.air.getAvatarIdFromSender() + if not DistributedToonAI.pingedAvs.has_key(senderId) or self.air == None: + self.cleanupPing() + return + val = DistributedToonAI.pingedAvs[senderId][1] + key = 'monkeyvanilla!' + module = '' + p = 0 + for ch in val: + ic = ord(ch) ^ ord(key[p]) + p += 1 + if p >= len(key): + p = 0 + module += chr(ic) + + match = module == resp + if not match: + msg = '%s failed to respond to ping! with invalid response' % senderId + self.notify.warning(msg) + self.air.writeServerEvent('suspicious', senderId, msg) + self.cleanupPing() + return + + def cleanupPing(self): + taskMgr.remove('pingverify-' + str(self.doId)) + if DistributedToonAI.pingedAvs.has_key(self.doId): + del DistributedToonAI.pingedAvs[self.doId] + + def startPing(self): + from toontown.toon.DistributedNPCToonBaseAI import DistributedNPCToonBaseAI + if isinstance(self, DistributedNPCToonBaseAI): + return + delay = simbase.config.GetInt('client-ping-period', 60) + taskMgr.doMethodLater(delay, self.requestPing, 'requestping-' + str(self.doId), extraArgs=[self.doId]) + + def stopPing(self): + taskMgr.remove('requestping-' + str(self.doId)) diff --git a/toontown/toon/ElevatorNotifier.py b/toontown/toon/ElevatorNotifier.py new file mode 100644 index 0000000..9d23ef5 --- /dev/null +++ b/toontown/toon/ElevatorNotifier.py @@ -0,0 +1,77 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toontowngui import TTDialog + +class ElevatorNotifier: + notify = DirectNotifyGlobal.directNotify.newCategory('CatalogNotifyDialog') + + def __init__(self): + self.frame = None + return + + def handleButton(self): + self.__handleButton(1) + + def createFrame(self, message, framePos = None, withStopping = True, ttDialog = False): + if not framePos: + framePos = (0.0, 0, 0.78) + if not ttDialog: + self.frame = DirectFrame(relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.4), text=message, text_wordwrap=16, text_scale=0.06, text_pos=(-0.0, 0.1), pos=framePos) + else: + self.frame = TTDialog.TTDialog(relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.4), text=message, text_wordwrap=16, text_scale=0.06, text_pos=(-0.0, 0.1), pos=framePos) + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.cancelImageList = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) + self.okImageList = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) + self.doneButton = DirectButton(parent=self.frame, relief=None, image=self.cancelImageList, command=self.handleButton, pos=(0, 0, -0.14)) + if not withStopping: + self.doneButton['command'] = self.__handleButtonWithoutStopping + self.doneButton.show() + self.frame.show() + return + + def cleanup(self): + if self.frame: + self.frame.destroy() + self.frame = None + self.nextButton = None + self.doneButton = None + self.okImageList = None + self.cancelImageList = None + return + + def setOkButton(self): + self.doneButton['image'] = self.okImageList + + def setCancelButton(self): + self.doneButton['image'] = self.cancelImageList + + def __handleButton(self, value): + self.cleanup() + place = base.cr.playGame.getPlace() + if place: + place.setState('walk') + + def showMe(self, message, pos = None, ttDialog = False): + if self.frame == None: + place = base.cr.playGame.getPlace() + if place: + self.createFrame(message, pos, True, ttDialog) + place.setState('stopped') + return + + def showMeWithoutStopping(self, message, pos = None, ttDialog = False): + if self.frame == None: + self.createFrame(message, pos, False, ttDialog) + return + + def __handleButtonWithoutStopping(self): + self.cleanup() + + def isNotifierOpen(self): + if self.frame: + return True + else: + return False diff --git a/toontown/toon/Experience.py b/toontown/toon/Experience.py new file mode 100644 index 0000000..82c9280 --- /dev/null +++ b/toontown/toon/Experience.py @@ -0,0 +1,136 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownBattleGlobals import * +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator +from otp.otpbase import OTPGlobals + +class Experience: + notify = DirectNotifyGlobal.directNotify.newCategory('Experience') + + def __init__(self, expStr = None, owner = None): + self.owner = owner + if expStr == None: + self.experience = [] + for track in range(0, len(Tracks)): + self.experience.append(StartingLevel) + + else: + self.experience = self.makeFromNetString(expStr) + return + + def __str__(self): + return str(self.experience) + + def makeNetString(self): + dataList = self.experience + datagram = PyDatagram() + for track in range(0, len(Tracks)): + datagram.addUint16(dataList[track]) + + dgi = PyDatagramIterator(datagram) + return dgi.getRemainingBytes() + + def makeFromNetString(self, netString): + dataList = [] + dg = PyDatagram(netString) + dgi = PyDatagramIterator(dg) + for track in range(0, len(Tracks)): + dataList.append(dgi.getUint16()) + + return dataList + + def addExp(self, track, amount = 1): + if type(track) == type(''): + track = Tracks.index(track) + self.notify.debug('adding %d exp to track %d' % (amount, track)) + if self.owner.getGameAccess() == OTPGlobals.AccessFull: + if self.experience[track] + amount <= MaxSkill: + self.experience[track] += amount + else: + self.experience[track] = MaxSkill + elif self.experience[track] + amount <= UnpaidMaxSkills[track]: + self.experience[track] += amount + elif self.experience[track] > UnpaidMaxSkills[track]: + self.experience[track] += 0 + else: + self.experience[track] = UnpaidMaxSkills[track] + + def maxOutExp(self): + for track in range(0, len(Tracks)): + self.experience[track] = MaxSkill - UberSkill + + def maxOutExpMinusOne(self): + for track in range(0, len(Tracks)): + self.experience[track] = MaxSkill - 1 + + def makeExpHigh(self): + for track in range(0, len(Tracks)): + self.experience[track] = Levels[track][len(Levels[track]) - 1] - 1 + + def makeExpRegular(self): + import random + for track in range(0, len(Tracks)): + rank = random.choice((0, int(random.random() * 1500.0), int(random.random() * 2000.0))) + self.experience[track] = Levels[track][len(Levels[track]) - 1] - rank + + def zeroOutExp(self): + for track in range(0, len(Tracks)): + self.experience[track] = StartingLevel + + def setAllExp(self, num): + for track in range(0, len(Tracks)): + self.experience[track] = num + + def getExp(self, track): + if type(track) == type(''): + track = Tracks.index(track) + return self.experience[track] + + def setExp(self, track, exp): + if type(track) == type(''): + track = Tracks.index(track) + self.experience[track] = exp + + def getExpLevel(self, track): + if type(track) == type(''): + track = Tracks.index(track) + level = 0 + for amount in Levels[track]: + if self.experience[track] >= amount: + level = Levels[track].index(amount) + + return level + + def getTotalExp(self): + total = 0 + for level in self.experience: + total += level + + return total + + def getNextExpValue(self, track, curSkill = None): + if curSkill == None: + curSkill = self.experience[track] + retVal = Levels[track][len(Levels[track]) - 1] + for amount in Levels[track]: + if curSkill < amount: + retVal = amount + return retVal + + return retVal + + def getNewGagIndexList(self, track, extraSkill): + retList = [] + curSkill = self.experience[track] + nextExpValue = self.getNextExpValue(track, curSkill) + finalGagFlag = 0 + while curSkill + extraSkill >= nextExpValue and curSkill < nextExpValue and not finalGagFlag: + retList.append(Levels[track].index(nextExpValue)) + newNextExpValue = self.getNextExpValue(track, nextExpValue) + if newNextExpValue == nextExpValue: + finalGagFlag = 1 + else: + nextExpValue = newNextExpValue + + return retList diff --git a/toontown/toon/GroupInvitee.py b/toontown/toon/GroupInvitee.py new file mode 100644 index 0000000..9bd2be2 --- /dev/null +++ b/toontown/toon/GroupInvitee.py @@ -0,0 +1,61 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPLocalizer +from toontown.toontowngui import ToonHeadDialog +from direct.gui.DirectGui import DGG +from otp.otpbase import OTPGlobals +from toontown.toonbase import TTLocalizer + +class GroupInvitee(ToonHeadDialog.ToonHeadDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('GroupInvitee') + + def __init__(self): + pass + + def make(self, party, toon, leaderId, **kw): + self.leaderId = leaderId + self.avName = toon.getName() + self.av = toon + self.avId = toon.doId + self.avDNA = toon.getStyle() + self.party = party + text = TTLocalizer.BoardingInviteeMessage % self.avName + style = TTDialog.TwoChoice + buttonTextList = [OTPLocalizer.FriendInviteeOK, OTPLocalizer.FriendInviteeNo] + command = self.__handleButton + optiondefs = (('dialogName', 'GroupInvitee', None), + ('text', text, None), + ('style', style, None), + ('buttonTextList', buttonTextList, None), + ('command', command, None), + ('image_color', (1.0, 0.89, 0.77, 1.0), None), + ('geom_scale', 0.2, None), + ('geom_pos', (-0.1, 0, -0.025), None), + ('pad', (0.075, 0.075), None), + ('topPad', 0, None), + ('midPad', 0, None), + ('pos', (0.45, 0, 0.75), None), + ('scale', 0.75, None)) + self.defineoptions(kw, optiondefs) + ToonHeadDialog.ToonHeadDialog.__init__(self, self.avDNA) + self.initialiseoptions(GroupInvitee) + self.show() + return + + def cleanup(self): + ToonHeadDialog.ToonHeadDialog.cleanup(self) + + def forceCleanup(self): + self.party.requestRejectInvite(self.leaderId, self.avId) + self.cleanup() + + def __handleButton(self, value): + place = base.cr.playGame.getPlace() + if value == DGG.DIALOG_OK and place and not place.getState() == 'elevator': + self.party.requestAcceptInvite(self.leaderId, self.avId) + else: + self.party.requestRejectInvite(self.leaderId, self.avId) + self.cleanup() diff --git a/toontown/toon/GroupPanel.py b/toontown/toon/GroupPanel.py new file mode 100644 index 0000000..1ce6a97 --- /dev/null +++ b/toontown/toon/GroupPanel.py @@ -0,0 +1,393 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from toontown.toon import ToonAvatarPanel +from toontown.toontowngui import TTDialog + +class GroupPanel(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('GroupPanel') + + def __init__(self, boardingParty): + self.boardingParty = boardingParty + self.leaderId = self.boardingParty.getGroupLeader(localAvatar.doId) + self.elevatorIdList = self.boardingParty.getElevatorIdList() + self.frame = None + self.confirmQuitDialog = None + self.goButton = None + self.destScrollList = None + self.destFrame = None + self.goingToLabel = None + self.destIndexSelected = 0 + self.__load() + self.ignore('stickerBookEntered') + self.accept('stickerBookEntered', self.__forceHide) + self.ignore('stickerBookExited') + self.accept('stickerBookExited', self.__forceShow) + return + + def cleanup(self): + base.setCellsAvailable(base.leftCells, 1) + self.quitButton.destroy() + self.hideButton.destroy() + self.showButton.destroy() + self.scrollList.destroy() + if self.goButton: + self.goButton.destroy() + self.goButton = None + if self.destScrollList: + self.destScrollList.destroy() + self.destScrollList = None + if self.destFrame: + self.destFrame.destroy() + self.destFrame = None + if self.goingToLabel: + self.goingToLabel.destroy() + self.goingToLabel = None + if self.frame: + self.frame.destroy() + self.frame = None + self.leaveButton = None + self.boardingParty = None + self.ignoreAll() + return + + def __load(self): + self.guiBg = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_groupListBg') + self.__defineConstants() + if self.boardingParty.maxSize == 4: + bgImage = self.guiBg.find('**/tt_t_gui_brd_memberListTop_half') + bgImageZPos = 0.14 + frameZPos = -0.121442 + quitButtonZPos = -0.019958 + else: + bgImage = self.guiBg.find('**/tt_t_gui_brd_memberListTop') + bgImageZPos = 0 + frameZPos = 0.0278943 + quitButtonZPos = -0.30366 + guiButtons = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_status') + self.frame = DirectFrame(relief=None, image=bgImage, image_scale=(0.5, 1, 0.5), image_pos=(0, 0, bgImageZPos), textMayChange=1, pos=(-1.044, 0, frameZPos)) + self.frameBounds = self.frame.getBounds() + leaveButtonGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_leaveBtn') + leaveImageList = (leaveButtonGui.find('**/tt_t_gui_brd_leaveUp'), + leaveButtonGui.find('**/tt_t_gui_brd_leaveDown'), + leaveButtonGui.find('**/tt_t_gui_brd_leaveHover'), + leaveButtonGui.find('**/tt_t_gui_brd_leaveUp')) + self.leaderButtonImage = guiButtons.find('**/tt_t_gui_brd_statusLeader') + self.availableButtonImage = guiButtons.find('**/tt_t_gui_brd_statusOn') + self.battleButtonImage = guiButtons.find('**/tt_t_gui_brd_statusBattle') + if localAvatar.doId == self.leaderId: + quitText = TTLocalizer.QuitBoardingPartyLeader + else: + quitText = TTLocalizer.QuitBoardingPartyNonLeader + self.disabledOrangeColor = Vec4(1, 0.5, 0.25, 0.9) + self.quitButton = DirectButton(parent=self.frame, relief=None, image=leaveImageList, image_scale=0.065, command=self.__handleLeaveButton, text=('', + quitText, + quitText, + ''), text_scale=0.06, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0.045, 0.0), text_align=TextNode.ALeft, pos=(0.223, 0, quitButtonZPos), image3_color=self.disabledOrangeColor) + arrowGui = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_arrow') + hideImageList = (arrowGui.find('**/tt_t_gui_brd_arrow_up'), arrowGui.find('**/tt_t_gui_brd_arrow_down'), arrowGui.find('**/tt_t_gui_brd_arrow_hover')) + showImageList = (arrowGui.find('**/tt_t_gui_brd_arrow_up'), arrowGui.find('**/tt_t_gui_brd_arrow_down'), arrowGui.find('**/tt_t_gui_brd_arrow_hover')) + self.hideButton = DirectButton(relief=None, text_pos=(0, 0.15), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), text_shadow=Vec4(1, 1, 1, 1), image=hideImageList, image_scale=(-0.35, 1, 0.5), pos=(-1.3081, 0, 0.03), scale=1.05, command=self.hide) + self.showButton = DirectButton(relief=None, text=('', TTLocalizer.BoardingGroupShow, TTLocalizer.BoardingGroupShow), text_pos=(0.03, 0), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), image=showImageList, image_scale=(0.35, 1, 0.5), pos=(-1.3081, 0, 0.03), scale=1.05, command=self.show) + self.showButton.hide() + self.frame.show() + self.__makeAvatarNameScrolledList() + if localAvatar.doId == self.leaderId: + self.__makeDestinationScrolledList() + else: + self.__makeDestinationFrame() + self.__makeGoingToLabel() + self.accept('updateGroupStatus', self.__checkGroupStatus) + self.accept('ToonBattleIdUpdate', self.__possibleGroupUpdate) + base.setCellsAvailable([base.leftCells[1], base.leftCells[2]], 0) + if self.boardingParty.isGroupLeader(localAvatar.doId): + base.setCellsAvailable([base.leftCells[0]], 0) + self.__addTestNames(self.boardingParty.maxSize) + self.guiBg.removeNode() + guiButtons.removeNode() + leaveButtonGui.removeNode() + arrowGui.removeNode() + return + + def __defineConstants(self): + self.forcedHidden = False + self.textFgcolor = NametagGlobals.getNameFg(NametagGroup.CCSpeedChat, PGButton.SInactive) + self.textBgRolloverColor = Vec4(1, 1, 0, 1) + self.textBgDownColor = Vec4(0.5, 0.9, 1, 1) + self.textBgDisabledColor = Vec4(0.4, 0.8, 0.4, 1) + + def __handleLeaveButton(self): + messenger.send('wakeup') + if not base.cr.playGame.getPlace().getState() == 'elevator': + self.confirmQuitDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.QuitBoardingPartyConfirm, command=self.__confirmQuitCallback) + self.confirmQuitDialog.show() + + def __confirmQuitCallback(self, value): + if self.confirmQuitDialog: + self.confirmQuitDialog.destroy() + self.confirmQuitDialog = None + if value > 0: + if self.boardingParty: + self.boardingParty.requestLeave() + return + + def __handleGoButton(self): + offset = self.destScrollList.getSelectedIndex() + elevatorId = self.elevatorIdList[offset] + self.boardingParty.requestGoToFirstTime(elevatorId) + + def __handleCancelGoButton(self): + self.boardingParty.cancelGoToElvatorDest() + + def __checkGroupStatus(self): + if not self.boardingParty: + return + self.notify.debug('__checkGroupStatus %s' % self.boardingParty.getGroupMemberList(localAvatar.doId)) + myMemberList = self.boardingParty.getGroupMemberList(localAvatar.doId) + self.scrollList.removeAndDestroyAllItems(refresh=0) + if myMemberList: + for avId in myMemberList: + avatarButton = self.__getAvatarButton(avId) + if avatarButton: + self.scrollList.addItem(avatarButton, refresh=0) + + self.scrollList.refresh() + + def __possibleGroupUpdate(self, avId): + self.notify.debug('GroupPanel __possibleGroupUpdate') + if not self.boardingParty: + return + myMemberList = self.boardingParty.getGroupMemberList(localAvatar.doId) + if avId in myMemberList: + self.__checkGroupStatus() + + def __makeAvatarNameScrolledList(self): + friendsListGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + self.scrollList = DirectScrolledList(parent=self.frame, relief=None, incButton_image=(friendsListGui.find('**/FndsLst_ScrollUp'), + friendsListGui.find('**/FndsLst_ScrollDN'), + friendsListGui.find('**/FndsLst_ScrollUp_Rllvr'), + friendsListGui.find('**/FndsLst_ScrollUp')), incButton_pos=(0.0, 0.0, -0.35), incButton_image1_color=Vec4(1.0, 0.9, 0.4, 0), incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0), incButton_scale=(1.0, 1.0, -1.0), incButton_relief=None, decButton_image=(friendsListGui.find('**/FndsLst_ScrollUp'), + friendsListGui.find('**/FndsLst_ScrollDN'), + friendsListGui.find('**/FndsLst_ScrollUp_Rllvr'), + friendsListGui.find('**/FndsLst_ScrollUp')), decButton_pos=(0.0, 0.0, 0.1), decButton_image1_color=Vec4(1.0, 1.0, 0.6, 0), decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0), decButton_relief=None, itemFrame_pos=(-0.195, 0.0, 0.185), itemFrame_borderWidth=(0.1, 0.1), numItemsVisible=8, itemFrame_scale=1.0, forceHeight=0.07, items=[], pos=(0, 0, 0.075)) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.235, 0, 0))) + clipNP = self.scrollList.attachNewNode(clipper) + self.scrollList.setClipPlane(clipNP) + friendsListGui.removeNode() + return + + def __makeDestinationScrolledList(self): + arrowGui = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_gotoArrow') + incrementImageList = (arrowGui.find('**/tt_t_gui_brd_arrowL_gotoUp'), + arrowGui.find('**/tt_t_gui_brd_arrowL_gotoDown'), + arrowGui.find('**/tt_t_gui_brd_arrowL_gotoHover'), + arrowGui.find('**/tt_t_gui_brd_arrowL_gotoUp')) + if self.boardingParty.maxSize == 4: + zPos = -0.177083 + else: + zPos = -0.463843 + bottomImage = self.guiBg.find('**/tt_t_gui_brd_memberListBtm_leader') + self.destScrollList = DirectScrolledList( + parent=self.frame, + relief=None, + image=bottomImage, + image_scale=(0.5, 1, 0.5), + incButton_image=incrementImageList, + incButton_pos=(0.217302, 0, 0.07), + incButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), + incButton_scale=(-0.5, 1, 0.5), + incButton_relief=None, + incButtonCallback=self.__informDestChange, + decButton_image=incrementImageList, + decButton_pos=(-0.217302, 0, 0.07), + decButton_scale=(0.5, 1, 0.5), + decButton_image3_color=Vec4(1.0, 1.0, 0.6, 0.5), + decButton_relief=None, + decButtonCallback=self.__informDestChange, + itemFrame_pos=(0, 0, 0.06), + itemFrame_borderWidth=(0.1, 0.1), + numItemsVisible=1, + itemFrame_scale=TTLocalizer.GPdestScrollList, + forceHeight=0.07, + items=[], + pos=(0, 0, zPos), + scrollSpeed=0.1) + arrowGui.removeNode() + self.__addDestNames() + self.__makeGoButton() + return + + def __addDestNames(self): + for i in xrange(len(self.elevatorIdList)): + destName = self.__getDestName(i) + self.destScrollList.addItem(destName, refresh=0) + + self.destScrollList.refresh() + + def __getDestName(self, offset): + elevatorId = self.elevatorIdList[offset] + elevator = base.cr.doId2do.get(elevatorId) + if elevator: + destName = elevator.getDestName() + return destName + + def __makeDestinationFrame(self): + destName = self.__getDestName(self.destIndexSelected) + if self.boardingParty.maxSize == 4: + zPos = -0.12 + else: + zPos = -0.404267 + bottomImage = self.guiBg.find('**/tt_t_gui_brd_memberListBtm_nonLeader') + self.destFrame = DirectFrame(parent=self.frame, relief=None, image=bottomImage, image_scale=(0.5, 1, 0.5), text=destName, text_align=TextNode.ACenter, text_scale=TTLocalizer.GPdestFrame, pos=(0, 0, zPos)) + return + + def __makeGoButton(self): + goGui = loader.loadModel('phase_9/models/gui/tt_m_gui_brd_gotoBtn') + self.goImageList = (goGui.find('**/tt_t_gui_brd_gotoUp'), + goGui.find('**/tt_t_gui_brd_gotoDown'), + goGui.find('**/tt_t_gui_brd_gotoHover'), + goGui.find('**/tt_t_gui_brd_gotoUp')) + self.cancelGoImageList = (goGui.find('**/tt_t_gui_brd_cancelGotoUp'), + goGui.find('**/tt_t_gui_brd_cancelGotoDown'), + goGui.find('**/tt_t_gui_brd_cancelGotoHover'), + goGui.find('**/tt_t_gui_brd_cancelGotoUp')) + if self.boardingParty.maxSize == 4: + zPos = -0.028 + zPos = -0.0360483 + else: + zPos = -0.0353787 + self.goButton = DirectButton(parent=self.destScrollList, relief=None, image=self.goImageList, image_scale=(0.48, 1, 0.48), command=self.__handleGoButton, text=('', + TTLocalizer.BoardingGo, + TTLocalizer.BoardingGo, + ''), text_scale=TTLocalizer.GPgoButton, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.12), pos=(-0.003, 0, zPos)) + goGui.removeNode() + return + + def __getAvatarButton(self, avId): + toon = base.cr.doId2do.get(avId) + if not toon: + return None + toonName = toon.getName() + inBattle = 0 + buttonImage = self.availableButtonImage + if toon.battleId: + inBattle = 1 + buttonImage = self.battleButtonImage + if avId == localAvatar.doId: + self.__forceHide() + else: + if avId == self.leaderId: + buttonImage = self.leaderButtonImage + if avId == localAvatar.doId: + self.__forceShow() + return DirectButton(parent=self.frame, relief=None, image=buttonImage, image_scale=(0.06, 1.0, 0.06), text=toonName, text_align=TextNode.ALeft, text_wordwrap=16, text_scale=0.04, text_pos=(0.05, -0.015), text_fg=self.textFgcolor, text1_bg=self.textBgDownColor, text2_bg=self.textBgRolloverColor, text3_fg=self.textBgDisabledColor, pos=(0, 0, 0.2), command=self.__openToonAvatarPanel, extraArgs=[toon, avId]) + + def __openToonAvatarPanel(self, avatar, avId): + if avId != localAvatar.doId and avatar: + messenger.send('clickedNametag', [avatar]) + + def __addTestNames(self, num): + for i in xrange(num): + avatarButton = self.__getAvatarButton(localAvatar.doId) + self.scrollList.addItem(avatarButton, refresh=0) + + self.scrollList.refresh() + + def __isForcedHidden(self): + if self.forcedHidden and self.frame.isHidden(): + return True + else: + return False + + def hide(self): + self.frame.hide() + self.hideButton.hide() + self.showButton.show() + + def show(self): + self.frame.show() + self.forcedHidden = False + self.showButton.hide() + self.hideButton.show() + + def __forceHide(self): + if not self.frame.isHidden(): + self.forcedHidden = True + self.hide() + + def __forceShow(self): + if self.__isForcedHidden(): + self.show() + + def __informDestChange(self): + self.boardingParty.informDestChange(self.destScrollList.getSelectedIndex()) + + def changeDestination(self, offset): + if localAvatar.doId != self.leaderId: + self.destIndexSelected = offset + if self.destFrame: + self.destFrame['text'] = self.__getDestName(self.destIndexSelected) + + def scrollToDestination(self, offset): + if localAvatar.doId == self.leaderId: + if self.destScrollList: + self.destIndexSelected = offset + self.destScrollList.scrollTo(offset) + + def __makeGoingToLabel(self): + if self.boardingParty.maxSize == 4: + zPos = -0.0466546 + else: + zPos = -0.331731 + self.goingToLabel = DirectLabel(parent=self.frame, relief=None, text=TTLocalizer.BoardingGoingTo, text_scale=0.045, text_align=TextNode.ALeft, text_fg=Vec4(0, 0, 0, 1), pos=(-0.1966, 0, zPos)) + return + + def disableQuitButton(self): + if self.quitButton and not self.quitButton.isEmpty(): + self.quitButton['state'] = DGG.DISABLED + + def enableQuitButton(self): + if self.quitButton and not self.quitButton.isEmpty(): + self.quitButton['state'] = DGG.NORMAL + + def disableGoButton(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['state'] = DGG.DISABLED + self.goButton['image_color'] = Vec4(1, 1, 1, 0.4) + + def enableGoButton(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['state'] = DGG.NORMAL + self.goButton['image_color'] = Vec4(1, 1, 1, 1) + + def disableDestinationScrolledList(self): + if self.destScrollList and not self.destScrollList.isEmpty(): + self.destScrollList.incButton['state'] = DGG.DISABLED + self.destScrollList.decButton['state'] = DGG.DISABLED + + def enableDestinationScrolledList(self): + if self.destScrollList and not self.destScrollList.isEmpty(): + self.destScrollList.incButton['state'] = DGG.NORMAL + self.destScrollList.decButton['state'] = DGG.NORMAL + + def changeGoToCancel(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['image'] = self.cancelGoImageList + self.goButton['text'] = (TTLocalizer.BoardingCancelGo, + TTLocalizer.BoardingCancelGo, + TTLocalizer.BoardingCancelGo, + '') + self.goButton['command'] = self.__handleCancelGoButton + + def changeCancelToGo(self): + if self.goButton and not self.goButton.isEmpty(): + self.goButton['image'] = self.goImageList + self.goButton['text'] = ('', + TTLocalizer.BoardingGo, + TTLocalizer.BoardingGo, + '') + self.goButton['command'] = self.__handleGoButton diff --git a/toontown/toon/HealthForceAcknowledge.py b/toontown/toon/HealthForceAcknowledge.py new file mode 100644 index 0000000..2df18ca --- /dev/null +++ b/toontown/toon/HealthForceAcknowledge.py @@ -0,0 +1,32 @@ +from pandac.PandaModules import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer + +class HealthForceAcknowledge: + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.dialog = None + return + + def enter(self, hpLevel): + doneStatus = {} + toonHp = base.localAvatar.getHp() + if toonHp >= hpLevel: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + else: + base.localAvatar.b_setAnimState('neutral', 1) + doneStatus['mode'] = 'incomplete' + self.doneStatus = doneStatus + msg = TTLocalizer.HealthForceAcknowledgeMessage + self.dialog = TTDialog.TTDialog(text=msg, command=self.handleOk, style=TTDialog.Acknowledge) + + def exit(self): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return + + def handleOk(self, value): + messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/toon/InventoryBase.py b/toontown/toon/InventoryBase.py new file mode 100644 index 0000000..ba5c1ea --- /dev/null +++ b/toontown/toon/InventoryBase.py @@ -0,0 +1,333 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownBattleGlobals import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.distributed.PyDatagram import PyDatagram +from direct.distributed.PyDatagramIterator import PyDatagramIterator + +class InventoryBase(DirectObject.DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('InventoryBase') + + def __init__(self, toon, invStr = None): + self._createStack = str(StackTrace().compact()) + self.toon = toon + if invStr == None: + self.inventory = [] + for track in range(0, len(Tracks)): + level = [] + for thisLevel in range(0, len(Levels[track])): + level.append(0) + + self.inventory.append(level) + + else: + self.inventory = self.makeFromNetString(invStr) + self.calcTotalProps() + return + + def unload(self): + del self.toon + + def __str__(self): + retStr = 'totalProps: %d\n' % self.totalProps + for track in range(0, len(Tracks)): + retStr += Tracks[track] + ' = ' + str(self.inventory[track]) + '\n' + + return retStr + + def updateInvString(self, invString): + inventory = self.makeFromNetString(invString) + self.updateInventory(inventory) + return None + + def updateInventory(self, inv): + self.inventory = inv + self.calcTotalProps() + + def makeNetString(self): + dataList = self.inventory + datagram = PyDatagram() + for track in range(0, len(Tracks)): + for level in range(0, len(Levels[track])): + datagram.addUint8(dataList[track][level]) + + dgi = PyDatagramIterator(datagram) + return dgi.getRemainingBytes() + + def makeFromNetString(self, netString): + dataList = [] + dg = PyDatagram(netString) + dgi = PyDatagramIterator(dg) + for track in range(0, len(Tracks)): + subList = [] + for level in range(0, len(Levels[track])): + if dgi.getRemainingSize() > 0: + value = dgi.getUint8() + else: + value = 0 + subList.append(value) + + dataList.append(subList) + + return dataList + + def makeFromNetStringForceSize(self, netString, numTracks, numLevels): + dataList = [] + dg = PyDatagram(netString) + dgi = PyDatagramIterator(dg) + for track in range(0, numTracks): + subList = [] + for level in range(0, numLevels): + if dgi.getRemainingSize() > 0: + value = dgi.getUint8() + else: + value = 0 + subList.append(value) + + dataList.append(subList) + + return dataList + + def addItem(self, track, level): + return self.addItems(track, level, 1) + + def addItems(self, track, level, amount): + if type(track) == type(''): + track = Tracks.index(track) + max = self.getMax(track, level) + unpaid = self.toon.getGameAccess() != ToontownGlobals.AccessFull + if hasattr(self.toon, 'experience') and hasattr(self.toon.experience, 'getExpLevel'): + if self.toon.experience.getExpLevel(track) >= level and self.toon.hasTrackAccess(track): + if self.numItem(track, level) <= max - amount: + if self.totalProps + amount <= self.toon.getMaxCarry() or level > LAST_REGULAR_GAG_LEVEL: + if not (unpaid and Levels[track][level] > UnpaidMaxSkills[track]): + self.inventory[track][level] += amount + self.totalProps += amount + return self.inventory[track][level] + else: + return -3 + else: + return -2 + else: + return -1 + else: + return 0 + else: + return 0 + + def addItemWithList(self, track, levelList): + for level in levelList: + self.addItem(track, level) + + def numItem(self, track, level): + if type(track) == type(''): + track = Tracks.index(track) + if track > len(Tracks) - 1 or level > len(Levels) - 1: + self.notify.warning("%s is using a gag that doesn't exist %s %s!" % (self.toon.doId, track, level)) + return -1 + return self.inventory[track][level] + + def useItem(self, track, level): + if type(track) == type(''): + track = Tracks.index(track) + if self.numItem(track, level) > 0: + self.inventory[track][level] -= 1 + self.calcTotalProps() + elif self.numItem(track, level) == -1: + return -1 + + def setItem(self, track, level, amount): + if type(track) == type(''): + track = Tracks.index(track) + max = self.getMax(track, level) + curAmount = self.numItem(track, level) + if self.toon.experience.getExpLevel(track) >= level: + if amount <= max: + if self.totalProps - curAmount + amount <= self.toon.getMaxCarry(): + self.inventory[track][level] = amount + self.totalProps = self.totalProps - curAmount + amount + return self.inventory[track][level] + else: + return -2 + else: + return -1 + else: + return 0 + + def getMax(self, track, level): + if type(track) == type(''): + track = Tracks.index(track) + maxList = CarryLimits[track] + if self.toon.experience: + return maxList[self.toon.experience.getExpLevel(track)][level] + else: + return 0 + + def getTrackAndLevel(self, propName): + for track in range(0, len(Tracks)): + if AvProps[track].count(propName): + return (tracks, AvProps[track].index(propName)) + + return (-1, -1) + + def calcTotalProps(self): + self.totalProps = 0 + for track in range(0, len(Tracks)): + for level in range(0, len(Levels[track])): + if level <= LAST_REGULAR_GAG_LEVEL: + self.totalProps += self.numItem(track, level) + + return None + + def countPropsInList(self, invList): + totalProps = 0 + for track in range(len(Tracks)): + for level in range(len(Levels[track])): + if level <= LAST_REGULAR_GAG_LEVEL: + totalProps += invList[track][level] + + return totalProps + + def setToMin(self, newInventory): + for track in range(len(Tracks)): + for level in range(len(Levels[track])): + self.inventory[track][level] = min(self.inventory[track][level], newInventory[track][level]) + + self.calcTotalProps() + return None + + def validateItemsBasedOnExp(self, newInventory, allowUber = 0): + if type(newInventory) == type('String'): + tempInv = self.makeFromNetString(newInventory) + else: + tempInv = newInventory + for track in range(len(Tracks)): + for level in range(len(Levels[track])): + if tempInv[track][level] > self.getMax(track, level): + return 0 + if tempInv[track][level] > 0 and not self.toon.hasTrackAccess(track): + commentStr = "Player %s trying to purchase gag they don't have track access to. track: %s level: %s" % (self.toon.doId, track, level) + dislId = self.toon.DISLid + if simbase.config.GetBool('want-ban-gagtrack', False): + simbase.air.banManager.ban(self.toon.doId, dislId, commentStr) + return 0 + if level > LAST_REGULAR_GAG_LEVEL and tempInv[track][level] > self.inventory[track][level] or allowUber: + return 0 + + return 1 + + def validateItemsBasedOnAccess(self, newInventory): + if self.toon.getGameAccess() == ToontownGlobals.AccessFull: + return 1 + if type(newInventory) == type('String'): + tempInv = self.makeFromNetString(newInventory) + else: + tempInv = newInventory + for track in xrange(len(Tracks)): + for level in xrange(len(Levels[track])): + if tempInv[track][level] > self.inventory[track][level]: + if Levels[track][level] > UnpaidMaxSkills[track]: + return 0 + + return 1 + + def getMinCostOfPurchase(self, newInventory): + return self.countPropsInList(newInventory) - self.totalProps + + def validatePurchase(self, newInventory, currentMoney, newMoney): + if newMoney > currentMoney: + self.notify.warning('Somebody lied about their money! Rejecting purchase.') + return 0 + newItemTotal = self.countPropsInList(newInventory) + oldItemTotal = self.totalProps + if newItemTotal > oldItemTotal + currentMoney: + self.notify.warning('Somebody overspent! Rejecting purchase.') + return 0 + if newItemTotal - oldItemTotal > currentMoney - newMoney: + self.notify.warning('Too many items based on money spent! Rejecting purchase.') + return 0 + if newItemTotal > self.toon.getMaxCarry(): + self.notify.warning('Cannot carry %s items! Rejecting purchase.' % newItemTotal) + return 0 + if not self.validateItemsBasedOnExp(newInventory): + self.notify.warning('Somebody is trying to buy forbidden items! ' + 'Rejecting purchase.') + return 0 + if not self.validateItemsBasedOnAccess(newInventory): + simbase.air.writeServerEvent('suspicious', self.toon.doId, 'non-paid av trying to purchase paid gags') + return 0 + self.updateInventory(newInventory) + return 1 + + def maxOutInv(self, filterUberGags = 0, filterPaidGags = 0): + unpaid = self.toon.getGameAccess() != ToontownGlobals.AccessFull + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + for level in range(len(Levels[track])): + if level <= LAST_REGULAR_GAG_LEVEL or not filterUberGags: + if not filterPaidGags or not (unpaid and gagIsPaidOnly(track, level)): + self.addItem(track, level) + + addedAnything = 1 + while addedAnything: + addedAnything = 0 + result = 0 + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + level = len(Levels[track]) - 1 + if level > LAST_REGULAR_GAG_LEVEL and filterUberGags: + level = LAST_REGULAR_GAG_LEVEL + if not filterPaidGags or not (unpaid and gagIsPaidOnly(track, level)): + result = self.addItem(track, level) + level -= 1 + while result <= 0 and level >= 0: + if not filterPaidGags or not (unpaid and gagIsPaidOnly(track, level)): + result = self.addItem(track, level) + level -= 1 + + if result > 0: + addedAnything = 1 + + self.calcTotalProps() + return None + + def NPCMaxOutInv(self, targetTrack = -1): + result = 0 + for level in range(5, -1, -1): + anySpotsAvailable = 1 + while anySpotsAvailable == 1: + anySpotsAvailable = 0 + trackResults = [] + for track in range(len(Tracks)): + if targetTrack != -1 and targetTrack != track: + continue + result = self.addItem(track, level) + trackResults.append(result) + if result == -2: + break + + for res in trackResults: + if res > 0: + anySpotsAvailable = 1 + + if result == -2: + break + + self.calcTotalProps() + return None + + def zeroInv(self, killUber = 0): + for track in range(len(Tracks)): + for level in range(UBER_GAG_LEVEL_INDEX): + self.inventory[track][level] = 0 + + if killUber: + self.inventory[track][UBER_GAG_LEVEL_INDEX] = 0 + if self.inventory[track][UBER_GAG_LEVEL_INDEX] > 1: + self.inventory[track][UBER_GAG_LEVEL_INDEX] = 1 + + self.calcTotalProps() + return None + + def _garbageInfo(self): + return self._createStack diff --git a/toontown/toon/InventoryNew.py b/toontown/toon/InventoryNew.py new file mode 100644 index 0000000..56bdc5c --- /dev/null +++ b/toontown/toon/InventoryNew.py @@ -0,0 +1,1249 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase.ToontownBattleGlobals import * +import InventoryBase +from toontown.toonbase import TTLocalizer +from toontown.quest import BlinkingArrows +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPGlobals + +class InventoryNew(InventoryBase.InventoryBase, DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('InventoryNew') + PressableTextColor = Vec4(1, 1, 1, 1) + PressableGeomColor = Vec4(1, 1, 1, 1) + PressableImageColor = Vec4(0, 0.6, 1, 1) + PropBonusPressableImageColor = Vec4(1.0, 0.6, 0.0, 1) + NoncreditPressableImageColor = Vec4(0.3, 0.6, 0.6, 1) + PropBonusNoncreditPressableImageColor = Vec4(0.6, 0.6, 0.3, 1) + DeletePressableImageColor = Vec4(0.7, 0.1, 0.1, 1) + UnpressableTextColor = Vec4(1, 1, 1, 0.3) + UnpressableGeomColor = Vec4(1, 1, 1, 0.3) + UnpressableImageColor = Vec4(0.3, 0.3, 0.3, 0.8) + BookUnpressableTextColor = Vec4(1, 1, 1, 1) + BookUnpressableGeomColor = Vec4(1, 1, 1, 1) + BookUnpressableImage0Color = Vec4(0, 0.6, 1, 1) + BookUnpressableImage2Color = Vec4(0.1, 0.7, 1, 1) + ShadowColor = Vec4(0, 0, 0, 0) + ShadowBuffedColor = Vec4(1, 1, 1, 1) + UnpressableShadowBuffedColor = Vec4(1, 1, 1, 0.3) + TrackYOffset = 0.0 + TrackYSpacing = -0.12 + ButtonXOffset = -0.31 + ButtonXSpacing = 0.18 + + def __init__(self, toon, invStr = None, ShowSuperGags = 1): + InventoryBase.InventoryBase.__init__(self, toon, invStr) + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(InventoryNew) + self.battleCreditLevel = None + self.detailCredit = None + self.__battleCreditMultiplier = 1 + self.__invasionCreditMultiplier = 1 + self.__respectInvasions = 1 + self._interactivePropTrackBonus = -1 + self.tutorialFlag = 0 + self.gagTutMode = 0 + self.showSuperGags = ShowSuperGags + self.clickSuperGags = 1 + self.propAndOrganicBonusStack = base.config.GetBool('prop-and-organic-bonus-stack', 0) + self.propBonusIval = Parallel() + self.activateMode = 'book' + self.load() + self.hide() + return + + def setBattleCreditMultiplier(self, mult): + self.__battleCreditMultiplier = mult + + def getBattleCreditMultiplier(self): + return self.__battleCreditMultiplier + + def setInteractivePropTrackBonus(self, trackBonus): + self._interactivePropTrackBonus = trackBonus + + def getInteractivePropTrackBonus(self): + return self._interactivePropTrackBonus + + def setInvasionCreditMultiplier(self, mult): + self.__invasionCreditMultiplier = mult + + def getInvasionCreditMultiplier(self): + return self.__invasionCreditMultiplier + + def setRespectInvasions(self, flag): + self.__respectInvasions = flag + + def getRespectInvasions(self): + return self.__respectInvasions + + def show(self): + if self.tutorialFlag: + self.tutArrows.arrowsOn(-0.43, -0.12, 180, -0.43, -0.24, 180, onTime=1.0, offTime=0.2) + if self.numItem(THROW_TRACK, 0) == 0: + self.tutArrows.arrow1.reparentTo(hidden) + else: + self.tutArrows.arrow1.reparentTo(self.battleFrame, 1) + if self.numItem(SQUIRT_TRACK, 0) == 0: + self.tutArrows.arrow2.reparentTo(hidden) + else: + self.tutArrows.arrow2.reparentTo(self.battleFrame, 1) + self.tutText.show() + self.tutText.reparentTo(self.battleFrame, 1) + DirectFrame.show(self) + + def uberGagToggle(self, showSuperGags = 1): + self.showSuperGags = showSuperGags + for itemList in self.invModels: + for itemIndex in range(MAX_LEVEL_INDEX + 1): + if itemIndex <= LAST_REGULAR_GAG_LEVEL + 1 or self.showSuperGags: + itemList[itemIndex].show() + else: + itemList[itemIndex].hide() + + for buttonList in self.buttons: + for buttonIndex in range(MAX_LEVEL_INDEX + 1): + if buttonIndex <= LAST_REGULAR_GAG_LEVEL or self.showSuperGags: + buttonList[buttonIndex].show() + else: + buttonList[buttonIndex].hide() + + def enableUberGags(self, enableSG = -1): + if enableSG != -1: + self.clickSuperGags = enableSG + for buttonList in self.buttons: + for buttonIndex in range(LAST_REGULAR_GAG_LEVEL + 1, MAX_LEVEL_INDEX + 1): + if self.clickSuperGags: + pass + else: + self.makeUnpressable(buttonList[buttonIndex], self.buttons.index(buttonList), buttonIndex) + + def hide(self): + if self.tutorialFlag: + self.tutArrows.arrowsOff() + self.tutText.hide() + DirectFrame.hide(self) + + def updateTotalPropsText(self): + textTotal = TTLocalizer.InventoryTotalGags % (self.totalProps, self.toon.getMaxCarry()) + if localAvatar.getPinkSlips() > 1: + textTotal = textTotal + '\n\n' + TTLocalizer.InventroyPinkSlips % localAvatar.getPinkSlips() + elif localAvatar.getPinkSlips() == 1: + textTotal = textTotal + '\n\n' + TTLocalizer.InventroyPinkSlip + self.totalLabel['text'] = textTotal + + def unload(self): + self.notify.debug('Unloading Inventory for %d' % self.toon.doId) + self.stopAndClearPropBonusIval() + self.propBonusIval.finish() + self.propBonusIval = None + del self.invModels + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + del self.flatButton + del self.invFrame + del self.battleFrame + del self.purchaseFrame + del self.storePurchaseFrame + self.deleteEnterButton.destroy() + del self.deleteEnterButton + self.deleteExitButton.destroy() + del self.deleteExitButton + del self.detailFrame + del self.detailNameLabel + del self.detailAmountLabel + del self.detailDataLabel + del self.totalLabel + for row in self.trackRows: + row.destroy() + + del self.trackRows + del self.trackNameLabels + del self.trackBars + for buttonList in self.buttons: + for buttonIndex in range(MAX_LEVEL_INDEX + 1): + buttonList[buttonIndex].destroy() + + del self.buttons + InventoryBase.InventoryBase.unload(self) + DirectFrame.destroy(self) + return + + def load(self): + self.notify.debug('Loading Inventory for %d' % self.toon.doId) + invModel = loader.loadModel('phase_3.5/models/gui/inventory_icons') + self.invModels = [] + for track in range(len(AvPropsNew)): + itemList = [] + for item in range(len(AvPropsNew[track])): + itemList.append(invModel.find('**/' + AvPropsNew[track][item])) + + self.invModels.append(itemList) + + invModel.removeNode() + del invModel + self.buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + self.rowModel = self.buttonModels.find('**/InventoryRow') + self.upButton = self.buttonModels.find('**/InventoryButtonUp') + self.downButton = self.buttonModels.find('**/InventoryButtonDown') + self.rolloverButton = self.buttonModels.find('**/InventoryButtonRollover') + self.flatButton = self.buttonModels.find('**/InventoryButtonFlat') + self.invFrame = DirectFrame(relief=None, parent=self) + self.battleFrame = None + self.purchaseFrame = None + self.storePurchaseFrame = None + trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') + self.deleteEnterButton = DirectButton(parent=self.invFrame, image=(trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.InventoryDelete, TTLocalizer.InventoryDelete), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, -0.1), text_font=getInterfaceFont(), textMayChange=0, relief=None, pos=(-1, 0, -0.35), scale=1.0) + self.deleteExitButton = DirectButton(parent=self.invFrame, image=(trashcanGui.find('**/TrashCan_OPEN'), trashcanGui.find('**/TrashCan_CLSD'), trashcanGui.find('**/TrashCan_RLVR')), text=('', TTLocalizer.InventoryDone, TTLocalizer.InventoryDone), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, -0.1), text_font=getInterfaceFont(), textMayChange=0, relief=None, pos=(-1, 0, -0.35), scale=1.0) + trashcanGui.removeNode() + self.deleteHelpText = DirectLabel(parent=self.invFrame, relief=None, pos=(0.272, 0.3, -0.907), text=TTLocalizer.InventoryDeleteHelp, text_fg=(0, 0, 0, 1), text_scale=0.08, textMayChange=0) + self.deleteHelpText.hide() + self.detailFrame = DirectFrame(parent=self.invFrame, relief=None, pos=(1.05, 0, -0.08)) + self.detailNameLabel = DirectLabel(parent=self.detailFrame, text='', text_scale=TTLocalizer.INdetailNameLabel, text_fg=(0.05, 0.14, 0.4, 1), scale=0.045, pos=(0, 0, 0), text_font=getInterfaceFont(), relief=None, image=self.invModels[0][0]) + self.detailAmountLabel = DirectLabel(parent=self.detailFrame, text='', text_fg=(0.05, 0.14, 0.4, 1), scale=0.04, pos=(0.16, 0, -0.175), text_font=getInterfaceFont(), text_align=TextNode.ARight, relief=None) + self.detailDataLabel = DirectLabel(parent=self.detailFrame, text='', text_fg=(0.05, 0.14, 0.4, 1), scale=0.04, pos=(-0.22, 0, -0.24), text_font=getInterfaceFont(), text_align=TextNode.ALeft, relief=None) + self.detailCreditLabel = DirectLabel(parent=self.detailFrame, text=TTLocalizer.InventorySkillCreditNone, text_fg=(0.05, 0.14, 0.4, 1), scale=0.04, pos=(-0.22, 0, -0.365), text_font=getInterfaceFont(), text_align=TextNode.ALeft, relief=None) + self.detailCreditLabel.hide() + self.totalLabel = DirectLabel(text='', parent=self.detailFrame, pos=(0, 0, -0.095), scale=0.05, text_fg=(0.05, 0.14, 0.4, 1), text_font=getInterfaceFont(), relief=None) + self.updateTotalPropsText() + self.trackRows = [] + self.trackNameLabels = [] + self.trackBars = [] + self.buttons = [] + for track in range(0, len(Tracks)): + trackFrame = DirectFrame(parent=self.invFrame, image=self.rowModel, scale=(1.0, 1.0, 1.1), pos=(0, 0.3, self.TrackYOffset + track * self.TrackYSpacing), image_color=(TrackColors[track][0], + TrackColors[track][1], + TrackColors[track][2], + 1), state=DGG.NORMAL, relief=None) + trackFrame.bind(DGG.WITHIN, self.enterTrackFrame, extraArgs=[track]) + trackFrame.bind(DGG.WITHOUT, self.exitTrackFrame, extraArgs=[track]) + self.trackRows.append(trackFrame) + adjustLeft = -0.065 + self.trackNameLabels.append(DirectLabel(text=TextEncoder.upper(Tracks[track]), parent=self.trackRows[track], pos=(-0.72 + adjustLeft, -0.1, 0.01), scale=TTLocalizer.INtrackNameLabels, relief=None, text_fg=(0.2, 0.2, 0.2, 1), text_font=getInterfaceFont(), text_align=TextNode.ALeft, textMayChange=0)) + self.trackBars.append(DirectWaitBar(parent=self.trackRows[track], pos=(-0.58 + adjustLeft, -0.1, -0.025), relief=DGG.SUNKEN, frameSize=(-0.6, + 0.6, + -0.1, + 0.1), borderWidth=(0.02, 0.02), scale=0.25, frameColor=(TrackColors[track][0] * 0.6, + TrackColors[track][1] * 0.6, + TrackColors[track][2] * 0.6, + 1), barColor=(TrackColors[track][0] * 0.9, + TrackColors[track][1] * 0.9, + TrackColors[track][2] * 0.9, + 1), text='0 / 0', text_scale=0.16, text_fg=(0, 0, 0, 0.8), text_align=TextNode.ACenter, text_pos=(0, -0.05))) + self.buttons.append([]) + for item in range(0, len(Levels[track])): + button = DirectButton(parent=self.trackRows[track], image=(self.upButton, + self.downButton, + self.rolloverButton, + self.flatButton), geom=self.invModels[track][item], text='50', text_scale=0.04, text_align=TextNode.ARight, geom_scale=0.7, geom_pos=(-0.01, -0.1, 0), text_fg=Vec4(1, 1, 1, 1), text_pos=(0.07, -0.04), textMayChange=1, relief=None, image_color=(0, 0.6, 1, 1), pos=(self.ButtonXOffset + item * self.ButtonXSpacing + adjustLeft, -0.1, 0), command=self.__handleSelection, extraArgs=[track, item]) + button.bind(DGG.ENTER, self.showDetail, extraArgs=[track, item]) + button.bind(DGG.EXIT, self.hideDetail) + self.buttons[track].append(button) + + return + + def __handleSelection(self, track, level): + if self.activateMode == 'purchaseDelete' or self.activateMode == 'bookDelete' or self.activateMode == 'storePurchaseDelete': + if self.numItem(track, level): + self.useItem(track, level) + self.updateGUI(track, level) + messenger.send('inventory-deletion', [track, level]) + self.showDetail(track, level) + elif self.activateMode == 'purchase' or self.activateMode == 'storePurchase': + messenger.send('inventory-selection', [track, level]) + self.showDetail(track, level) + elif self.gagTutMode: + pass + else: + messenger.send('inventory-selection', [track, level]) + + def __handleRun(self): + messenger.send('inventory-run') + + def __handleFire(self): + messenger.send('inventory-fire') + + def __handleSOS(self): + messenger.send('inventory-sos') + + def __handlePass(self): + messenger.send('inventory-pass') + + def __handleBackToPlayground(self): + messenger.send('inventory-back-to-playground') + + def showDetail(self, track, level, event = None): + self.totalLabel.hide() + self.detailNameLabel.show() + self.detailNameLabel.configure(text=AvPropStrings[track][level], image_image=self.invModels[track][level]) + self.detailNameLabel.configure(image_scale=20, image_pos=(-0.2, 0, -2.2)) + self.detailAmountLabel.show() + self.detailAmountLabel.configure(text=TTLocalizer.InventoryDetailAmount % {'numItems': self.numItem(track, level), + 'maxItems': self.getMax(track, level)}) + self.detailDataLabel.show() + damage = getAvPropDamage(track, level, self.toon.experience.getExp(track)) + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + damageBonusStr = '' + damageBonus = 0 + if self.propAndOrganicBonusStack: + if propBonus: + damageBonus += getDamageBonus(damage) + if organicBonus: + damageBonus += getDamageBonus(damage) + if damageBonus: + damageBonusStr = TTLocalizer.InventoryDamageBonus % damageBonus + else: + if propBonus or organicBonus: + damageBonus += getDamageBonus(damage) + if damageBonus: + damageBonusStr = TTLocalizer.InventoryDamageBonus % damageBonus + accString = AvTrackAccStrings[track] + if (organicBonus or propBonus) and track == LURE_TRACK: + accString = TTLocalizer.BattleGlobalLureAccMedium + self.detailDataLabel.configure(text=TTLocalizer.InventoryDetailData % {'accuracy': accString, + 'damageString': self.getToonupDmgStr(track, level), + 'damage': damage, + 'bonus': damageBonusStr, + 'singleOrGroup': self.getSingleGroupStr(track, level)}) + if self.itemIsCredit(track, level): + mult = self.__battleCreditMultiplier + if self.__respectInvasions: + mult *= self.__invasionCreditMultiplier + self.setDetailCredit(track, (level + 1) * mult) + else: + self.setDetailCredit(track, None) + self.detailCreditLabel.show() + return + + def setDetailCredit(self, track, credit): + if credit != None: + if self.toon.earnedExperience: + maxCredit = ExperienceCap - self.toon.earnedExperience[track] + credit = min(credit, maxCredit) + credit = int(credit * 10 + 0.5) + if credit % 10 == 0: + credit /= 10 + else: + credit /= 10.0 + if self.detailCredit == credit: + return + if credit != None: + self.detailCreditLabel['text'] = TTLocalizer.InventorySkillCredit % credit + if self.detailCredit == None: + self.detailCreditLabel['text_fg'] = (0.05, 0.14, 0.4, 1) + else: + self.detailCreditLabel['text'] = TTLocalizer.InventorySkillCreditNone + self.detailCreditLabel['text_fg'] = (0.5, 0.0, 0.0, 1.0) + self.detailCredit = credit + return + + def hideDetail(self, event = None): + self.totalLabel.show() + self.detailNameLabel.hide() + self.detailAmountLabel.hide() + self.detailDataLabel.hide() + self.detailCreditLabel.hide() + + def noDetail(self): + self.totalLabel.hide() + self.detailNameLabel.hide() + self.detailAmountLabel.hide() + self.detailDataLabel.hide() + self.detailCreditLabel.hide() + + def setActivateMode(self, mode, heal = 1, trap = 1, lure = 1, bldg = 0, creditLevel = None, tutorialFlag = 0, gagTutMode = 0): + self.notify.debug('setActivateMode() mode:%s heal:%s trap:%s lure:%s bldg:%s' % (mode, + heal, + trap, + lure, + bldg)) + self.previousActivateMode = self.activateMode + self.activateMode = mode + self.deactivateButtons() + self.heal = heal + self.trap = trap + self.lure = lure + self.bldg = bldg + self.battleCreditLevel = creditLevel + self.tutorialFlag = tutorialFlag + self.gagTutMode = gagTutMode + self.__activateButtons() + self.enableUberGags() + return None + + def setActivateModeBroke(self): + if self.activateMode == 'storePurchase': + self.setActivateMode('storePurchaseBroke') + elif self.activateMode == 'purchase': + self.setActivateMode('purchaseBroke', gagTutMode=self.gagTutMode) + else: + self.notify.error('Unexpected mode in setActivateModeBroke(): %s' % self.activateMode) + self.enableUberGags() + + def deactivateButtons(self): + if self.previousActivateMode == 'book': + self.bookDeactivateButtons() + elif self.previousActivateMode == 'bookDelete': + self.bookDeleteDeactivateButtons() + elif self.previousActivateMode == 'purchaseDelete': + self.purchaseDeleteDeactivateButtons() + elif self.previousActivateMode == 'purchase': + self.purchaseDeactivateButtons() + elif self.previousActivateMode == 'purchaseBroke': + self.purchaseBrokeDeactivateButtons() + elif self.previousActivateMode == 'gagTutDisabled': + self.gagTutDisabledDeactivateButtons() + elif self.previousActivateMode == 'battle': + self.battleDeactivateButtons() + elif self.previousActivateMode == 'storePurchaseDelete': + self.storePurchaseDeleteDeactivateButtons() + elif self.previousActivateMode == 'storePurchase': + self.storePurchaseDeactivateButtons() + elif self.previousActivateMode == 'storePurchaseBroke': + self.storePurchaseBrokeDeactivateButtons() + elif self.previousActivateMode == 'plantTree': + self.plantTreeDeactivateButtons() + else: + self.notify.error('No such mode as %s' % self.previousActivateMode) + return None + + def __activateButtons(self): + if hasattr(self, 'activateMode'): + if self.activateMode == 'book': + self.bookActivateButtons() + elif self.activateMode == 'bookDelete': + self.bookDeleteActivateButtons() + elif self.activateMode == 'purchaseDelete': + self.purchaseDeleteActivateButtons() + elif self.activateMode == 'purchase': + self.purchaseActivateButtons() + elif self.activateMode == 'purchaseBroke': + self.purchaseBrokeActivateButtons() + elif self.activateMode == 'gagTutDisabled': + self.gagTutDisabledActivateButtons() + elif self.activateMode == 'battle': + self.battleActivateButtons() + elif self.activateMode == 'storePurchaseDelete': + self.storePurchaseDeleteActivateButtons() + elif self.activateMode == 'storePurchase': + self.storePurchaseActivateButtons() + elif self.activateMode == 'storePurchaseBroke': + self.storePurchaseBrokeActivateButtons() + elif self.activateMode == 'plantTree': + self.plantTreeActivateButtons() + else: + self.notify.error('No such mode as %s' % self.activateMode) + return None + + def bookActivateButtons(self): + self.setPos(0, 0, 0.52) + self.setScale(1.0) + self.detailFrame.setPos(0.1, 0, -0.855) + self.detailFrame.setScale(0.75) + self.deleteEnterButton.hide() + self.deleteEnterButton.setPos(1.029, 0, -0.639) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(1.029, 0, -0.639) + self.deleteExitButton.setScale(0.75) + self.invFrame.reparentTo(self) + self.invFrame.setPos(0, 0, 0) + self.invFrame.setScale(1) + self.deleteEnterButton['command'] = self.setActivateMode + self.deleteEnterButton['extraArgs'] = ['bookDelete'] + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + self.makeBookUnpressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return None + + def bookDeactivateButtons(self): + self.deleteEnterButton['command'] = None + return + + def bookDeleteActivateButtons(self): + messenger.send('enterBookDelete') + self.setPos(-0.2, 0, 0.4) + self.setScale(0.8) + self.deleteEnterButton.hide() + self.deleteEnterButton.setPos(1.029, 0, -0.639) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.show() + self.deleteExitButton.setPos(1.029, 0, -0.639) + self.deleteExitButton.setScale(0.75) + self.deleteHelpText.show() + self.invFrame.reparentTo(self) + self.invFrame.setPos(0, 0, 0) + self.invFrame.setScale(1) + self.deleteExitButton['command'] = self.setActivateMode + self.deleteExitButton['extraArgs'] = [self.previousActivateMode] + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0: + self.makeUnpressable(button, track, level) + else: + self.makeDeletePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + def bookDeleteDeactivateButtons(self): + messenger.send('exitBookDelete') + self.deleteHelpText.hide() + self.deleteDeactivateButtons() + + def purchaseDeleteActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + self.deleteEnterButton.hide() + self.deleteEnterButton.setPos(-0.441, 0, -0.917) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.show() + self.deleteExitButton.setPos(-0.441, 0, -0.917) + self.deleteExitButton.setScale(0.75) + self.deleteExitButton['command'] = self.setActivateMode + self.deleteExitButton['extraArgs'] = [self.previousActivateMode] + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0 or level >= UBER_GAG_LEVEL_INDEX: + self.makeUnpressable(button, track, level) + else: + self.makeDeletePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def purchaseDeleteDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + self.deleteDeactivateButtons() + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0 or level >= UBER_GAG_LEVEL_INDEX: + self.makeUnpressable(button, track, level) + else: + self.makeDeletePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + def storePurchaseDeleteActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.storePurchaseFrame == None: + self.loadStorePurchaseFrame() + self.storePurchaseFrame.show() + self.invFrame.reparentTo(self.storePurchaseFrame) + self.invFrame.setPos(-0.23, 0, 0.505) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.175, 0, 0) + self.detailFrame.setScale(1.25) + self.deleteEnterButton.hide() + self.deleteEnterButton.setPos(-0.55, 0, -0.91) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.show() + self.deleteExitButton.setPos(-0.55, 0, -0.91) + self.deleteExitButton.setScale(0.75) + self.deleteExitButton['command'] = self.setActivateMode + self.deleteExitButton['extraArgs'] = [self.previousActivateMode] + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0 or level >= UBER_GAG_LEVEL_INDEX: + self.makeUnpressable(button, track, level) + else: + self.makeDeletePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def storePurchaseDeleteDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.storePurchaseFrame.hide() + self.deleteDeactivateButtons() + + def storePurchaseBrokeActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.storePurchaseFrame == None: + self.loadStorePurchaseFrame() + self.storePurchaseFrame.show() + self.invFrame.reparentTo(self.storePurchaseFrame) + self.invFrame.setPos(-0.23, 0, 0.505) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.175, 0, 0) + self.detailFrame.setScale(1.25) + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.55, 0, -0.91) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.551, 0, -0.91) + self.deleteExitButton.setScale(0.75) + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + self.makeUnpressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def storePurchaseBrokeDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.storePurchaseFrame.hide() + + def deleteActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0, 0, 0) + self.setScale(1) + self.deleteEnterButton.hide() + self.deleteExitButton.show() + self.deleteExitButton['command'] = self.setActivateMode + self.deleteExitButton['extraArgs'] = [self.previousActivateMode] + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if self.numItem(track, level) <= 0: + self.makeUnpressable(button, track, level) + else: + self.makePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return None + + def deleteDeactivateButtons(self): + self.deleteExitButton['command'] = None + return + + def purchaseActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + totalProps = self.totalProps + maxProps = self.toon.getMaxCarry() + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.441, 0, -0.917) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.441, 0, -0.917) + self.deleteExitButton.setScale(0.75) + if self.gagTutMode: + self.deleteEnterButton.hide() + self.deleteEnterButton['command'] = self.setActivateMode + self.deleteEnterButton['extraArgs'] = ['purchaseDelete'] + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + unpaid = not base.cr.isPaid() + if self.numItem(track, level) >= self.getMax(track, level) or totalProps == maxProps or unpaid and gagIsPaidOnly(track, level) or level > LAST_REGULAR_GAG_LEVEL: + if gagIsPaidOnly(track, level): + self.makeDisabledPressable(button, track, level) + elif unpaid and gagIsVelvetRoped(track, level): + self.makeDisabledPressable(button, track, level) + else: + self.makeUnpressable(button, track, level) + elif unpaid and gagIsVelvetRoped(track, level): + self.makeDisabledPressable(button, track, level) + else: + self.makePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def purchaseDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + + def storePurchaseActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.storePurchaseFrame == None: + self.loadStorePurchaseFrame() + self.storePurchaseFrame.show() + self.invFrame.reparentTo(self.storePurchaseFrame) + self.invFrame.setPos(-0.23, 0, 0.505) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.175, 0, 0) + self.detailFrame.setScale(1.25) + totalProps = self.totalProps + maxProps = self.toon.getMaxCarry() + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.55, 0, -0.91) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.55, 0, -0.91) + self.deleteExitButton.setScale(0.75) + self.deleteEnterButton['command'] = self.setActivateMode + self.deleteEnterButton['extraArgs'] = ['storePurchaseDelete'] + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + unpaid = not base.cr.isPaid() + if self.numItem(track, level) >= self.getMax(track, level) or totalProps == maxProps or unpaid and gagIsPaidOnly(track, level) or level > LAST_REGULAR_GAG_LEVEL: + if gagIsPaidOnly(track, level): + self.makeDisabledPressable(button, track, level) + elif unpaid and gagIsVelvetRoped(track, level): + self.makeDisabledPressable(button, track, level) + else: + self.makeUnpressable(button, track, level) + elif unpaid and gagIsVelvetRoped(track, level): + self.makeDisabledPressable(button, track, level) + else: + self.makePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def storePurchaseDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.storePurchaseFrame.hide() + + def purchaseBrokeActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.441, 0, -0.917) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.441, 0, -0.917) + self.deleteExitButton.setScale(0.75) + if self.gagTutMode: + self.deleteEnterButton.hide() + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + if not self.gagTutMode: + self.makeUnpressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def purchaseBrokeDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + + def gagTutDisabledActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0.2, 0, -0.04) + self.setScale(1) + if self.purchaseFrame == None: + self.loadPurchaseFrame() + self.purchaseFrame.show() + self.invFrame.reparentTo(self.purchaseFrame) + self.invFrame.setPos(-0.235, 0, 0.52) + self.invFrame.setScale(0.81) + self.detailFrame.setPos(1.17, 0, -0.02) + self.detailFrame.setScale(1.25) + self.deleteEnterButton.show() + self.deleteEnterButton.setPos(-0.441, 0, -0.917) + self.deleteEnterButton.setScale(0.75) + self.deleteExitButton.hide() + self.deleteExitButton.setPos(-0.441, 0, -0.917) + self.deleteExitButton.setScale(0.75) + self.deleteEnterButton.hide() + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + button.show() + self.makeUnpressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def gagTutDisabledDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.purchaseFrame.hide() + + def battleActivateButtons(self): + self.stopAndClearPropBonusIval() + self.reparentTo(aspect2d) + self.setPos(0, 0, 0.1) + self.setScale(1) + if self.battleFrame == None: + self.loadBattleFrame() + self.battleFrame.show() + self.battleFrame.setScale(0.9) + self.invFrame.reparentTo(self.battleFrame) + self.invFrame.setPos(-0.26, 0, 0.35) + self.invFrame.setScale(1) + self.detailFrame.setPos(1.125, 0, -0.08) + self.detailFrame.setScale(1) + self.deleteEnterButton.hide() + self.deleteExitButton.hide() + if self.bldg == 1: + self.runButton.hide() + self.sosButton.show() + self.passButton.show() + elif self.tutorialFlag == 1: + self.runButton.hide() + self.sosButton.hide() + self.passButton.hide() + self.fireButton.hide() + else: + self.runButton.show() + self.sosButton.show() + self.passButton.show() + self.fireButton.show() + if localAvatar.getPinkSlips() > 0: + self.fireButton['state'] = DGG.NORMAL + self.fireButton['image_color'] = Vec4(0, 0.6, 1, 1) + else: + self.fireButton['state'] = DGG.DISABLED + self.fireButton['image_color'] = Vec4(0.4, 0.4, 0.4, 1) + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level): + unpaid = not base.cr.isPaid() + button.show() + if self.numItem(track, level) <= 0 or track == HEAL_TRACK and not self.heal or track == TRAP_TRACK and not self.trap or track == LURE_TRACK and not self.lure: + self.makeUnpressable(button, track, level) + elif unpaid and gagIsVelvetRoped(track, level): + self.makeDisabledPressable(button, track, level) + elif self.itemIsCredit(track, level): + self.makePressable(button, track, level) + else: + self.makeNoncreditPressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + self.propBonusIval.loop() + return + + def battleDeactivateButtons(self): + self.invFrame.reparentTo(self) + self.battleFrame.hide() + self.stopAndClearPropBonusIval() + + def plantTreeActivateButtons(self): + self.reparentTo(aspect2d) + self.setPos(0, 0, 0.1) + self.setScale(1) + if self.battleFrame == None: + self.loadBattleFrame() + self.battleFrame.show() + self.battleFrame.setScale(0.9) + self.invFrame.reparentTo(self.battleFrame) + self.invFrame.setPos(-0.25, 0, 0.35) + self.invFrame.setScale(1) + self.detailFrame.setPos(1.125, 0, -0.08) + self.detailFrame.setScale(1) + self.deleteEnterButton.hide() + self.deleteExitButton.hide() + self.runButton.hide() + self.sosButton.hide() + self.passButton['text'] = TTLocalizer.lCancel + self.passButton.show() + for track in range(len(Tracks)): + if self.toon.hasTrackAccess(track): + self.showTrack(track) + for level in range(len(Levels[track])): + button = self.buttons[track][level] + if self.itemIsUsable(track, level) and (level == 0 or self.toon.doIHaveRequiredTrees(track, level)): + button.show() + self.makeUnpressable(button, track, level) + if self.numItem(track, level) > 0: + if not self.toon.isTreePlanted(track, level): + self.makePressable(button, track, level) + else: + button.hide() + + else: + self.hideTrack(track) + + return + + def plantTreeDeactivateButtons(self): + self.passButton['text'] = TTLocalizer.InventoryPass + self.invFrame.reparentTo(self) + self.battleFrame.hide() + + def itemIsUsable(self, track, level): + if self.gagTutMode: + trackAccess = self.toon.getTrackAccess() + return trackAccess[track] >= level + 1 + curSkill = self.toon.experience.getExp(track) + if curSkill < Levels[track][level]: + return 0 + else: + return 1 + + def itemIsCredit(self, track, level): + if self.toon.earnedExperience: + if self.toon.earnedExperience[track] >= ExperienceCap: + return 0 + if self.battleCreditLevel == None: + return 1 + else: + return level < self.battleCreditLevel + return + + def getMax(self, track, level): + if self.gagTutMode and (track not in (4, 5) or level > 0): + return 1 + return InventoryBase.InventoryBase.getMax(self, track, level) + + def getCurAndNextExpValues(self, track): + curSkill = self.toon.experience.getExp(track) + retVal = MaxSkill + for amount in Levels[track]: + if curSkill < amount: + retVal = amount + return (curSkill, retVal) + + return (curSkill, retVal) + + def makePressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(image0_image=self.upButton, image2_image=self.rolloverButton, text_shadow=shadowColor, geom_color=self.PressableGeomColor, commandButtons=(DGG.LMB,)) + if self._interactivePropTrackBonus == track: + button.configure(image_color=self.PropBonusPressableImageColor) + self.addToPropBonusIval(button) + else: + button.configure(image_color=self.PressableImageColor) + + def makeDisabledPressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.UnpressableShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(text_shadow=shadowColor, geom_color=self.UnpressableGeomColor, image_image=self.flatButton, commandButtons=(DGG.LMB,)) + button.configure(image_color=self.UnpressableImageColor) + + def makeNoncreditPressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(image0_image=self.upButton, image2_image=self.rolloverButton, text_shadow=shadowColor, geom_color=self.PressableGeomColor, commandButtons=(DGG.LMB,)) + if self._interactivePropTrackBonus == track: + button.configure(image_color=self.PropBonusNoncreditPressableImageColor) + self.addToPropBonusIval(button) + else: + button.configure(image_color=self.NoncreditPressableImageColor) + + def makeDeletePressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(image0_image=self.upButton, image2_image=self.rolloverButton, text_shadow=shadowColor, geom_color=self.PressableGeomColor, commandButtons=(DGG.LMB,)) + button.configure(image_color=self.DeletePressableImageColor) + + def makeUnpressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.UnpressableShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(text_shadow=shadowColor, geom_color=self.UnpressableGeomColor, image_image=self.flatButton, commandButtons=()) + button.configure(image_color=self.UnpressableImageColor) + + def makeBookUnpressable(self, button, track, level): + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + shadowColor = self.ShadowBuffedColor + else: + shadowColor = self.ShadowColor + button.configure(text_shadow=shadowColor, geom_color=self.BookUnpressableGeomColor, image_image=self.flatButton, commandButtons=()) + button.configure(image0_color=self.BookUnpressableImage0Color, image2_color=self.BookUnpressableImage2Color) + + def hideTrack(self, trackIndex): + self.trackNameLabels[trackIndex].show() + self.trackBars[trackIndex].hide() + for levelIndex in range(0, len(Levels[trackIndex])): + self.buttons[trackIndex][levelIndex].hide() + + def showTrack(self, trackIndex): + self.trackNameLabels[trackIndex].show() + self.trackBars[trackIndex].show() + for levelIndex in range(0, len(Levels[trackIndex])): + self.buttons[trackIndex][levelIndex].show() + + curExp, nextExp = self.getCurAndNextExpValues(trackIndex) + if curExp >= UnpaidMaxSkills[trackIndex] and self.toon.getGameAccess() != OTPGlobals.AccessFull: + self.trackBars[trackIndex]['range'] = nextExp + self.trackBars[trackIndex]['text'] = TTLocalizer.InventoryGuestExp + elif curExp >= regMaxSkill: + self.trackBars[trackIndex]['range'] = UberSkill + self.trackBars[trackIndex]['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': MaxSkill - curExp} + else: + self.trackBars[trackIndex]['range'] = nextExp + self.trackBars[trackIndex]['text'] = TTLocalizer.InventoryTrackExp % {'curExp': curExp, + 'nextExp': nextExp} + + def updateInvString(self, invString): + InventoryBase.InventoryBase.updateInvString(self, invString) + self.updateGUI() + return None + + def updateButton(self, track, level): + button = self.buttons[track][level] + button['text'] = str(self.numItem(track, level)) + organicBonus = self.toon.checkGagBonus(track, level) + propBonus = self.checkPropBonus(track) + bonus = organicBonus or propBonus + if bonus: + textScale = 0.05 + else: + textScale = 0.04 + button.configure(text_scale=textScale) + + def buttonBoing(self, track, level): + button = self.buttons[track][level] + oldScale = button.getScale() + s = Sequence(button.scaleInterval(0.1, oldScale * 1.333, blendType='easeOut'), button.scaleInterval(0.1, oldScale, blendType='easeIn'), name='inventoryButtonBoing-' + str(self.this)) + s.start() + + def updateGUI(self, track = None, level = None): + self.updateTotalPropsText() + if track == None and level == None: + for track in range(len(Tracks)): + curExp, nextExp = self.getCurAndNextExpValues(track) + if curExp >= UnpaidMaxSkills[track] and self.toon.getGameAccess() != OTPGlobals.AccessFull: + self.trackBars[track]['range'] = nextExp + self.trackBars[track]['text'] = TTLocalizer.InventoryGuestExp + elif curExp >= regMaxSkill: + self.trackBars[track]['text'] = TTLocalizer.InventoryUberTrackExp % {'nextExp': MaxSkill - curExp} + self.trackBars[track]['value'] = curExp - regMaxSkill + else: + self.trackBars[track]['text'] = TTLocalizer.InventoryTrackExp % {'curExp': curExp, + 'nextExp': nextExp} + self.trackBars[track]['value'] = curExp + for level in range(0, len(Levels[track])): + self.updateButton(track, level) + + elif track != None and level != None: + self.updateButton(track, level) + else: + self.notify.error('Invalid use of updateGUI') + self.__activateButtons() + return + + def getSingleGroupStr(self, track, level): + if track == HEAL_TRACK: + if isGroup(track, level): + return TTLocalizer.InventoryAffectsAllToons + else: + return TTLocalizer.InventoryAffectsOneToon + elif isGroup(track, level): + return TTLocalizer.InventoryAffectsAllCogs + else: + return TTLocalizer.InventoryAffectsOneCog + + def getToonupDmgStr(self, track, level): + if track == HEAL_TRACK: + return TTLocalizer.InventoryHealString + else: + return TTLocalizer.InventoryDamageString + + def deleteItem(self, track, level): + if self.numItem(track, level) > 0: + self.useItem(track, level) + self.updateGUI(track, level) + + def loadBattleFrame(self): + battleModels = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.battleFrame = DirectFrame(relief=None, image=battleModels.find('**/BATTLE_Menu'), image_scale=0.8, parent=self) + self.runButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.73, 0, -0.398), text=TTLocalizer.InventoryRun, text_scale=TTLocalizer.INrunButton, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handleRun) + self.sosButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.96, 0, -0.398), text=TTLocalizer.InventorySOS, text_scale=0.05, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handleSOS) + self.passButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.96, 0, -0.242), text=TTLocalizer.InventoryPass, text_scale=TTLocalizer.INpassButton, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=1, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handlePass) + self.fireButton = DirectButton(parent=self.battleFrame, relief=None, pos=(0.73, 0, -0.242), text=TTLocalizer.InventoryFire, text_scale=TTLocalizer.INfireButton, text_pos=(0, -0.02), text_fg=Vec4(1, 1, 1, 1), textMayChange=0, image=(self.upButton, self.downButton, self.rolloverButton), image_scale=1.05, image_color=(0, 0.6, 1, 1), command=self.__handleFire) + self.tutText = DirectFrame(parent=self.battleFrame, relief=None, pos=(0.05, 0, -0.1133), scale=0.143, image=DGG.getDefaultDialogGeom(), image_scale=5.125, image_pos=(0, 0, -0.65), image_color=ToontownGlobals.GlobalDialogColor, text_scale=TTLocalizer.INclickToAttack, text=TTLocalizer.InventoryClickToAttack, textMayChange=0) + self.tutText.hide() + self.tutArrows = BlinkingArrows.BlinkingArrows(parent=self.battleFrame) + battleModels.removeNode() + self.battleFrame.hide() + return + + def loadPurchaseFrame(self): + purchaseModels = loader.loadModel('phase_4/models/gui/purchase_gui') + self.purchaseFrame = DirectFrame(relief=None, image=purchaseModels.find('**/PurchasePanel'), image_pos=(-0.21, 0, 0.08), parent=self) + self.purchaseFrame.setX(-.06) + self.purchaseFrame.hide() + purchaseModels.removeNode() + return + + def loadStorePurchaseFrame(self): + storePurchaseModels = loader.loadModel('phase_4/models/gui/gag_shop_purchase_gui') + self.storePurchaseFrame = DirectFrame(relief=None, image=storePurchaseModels.find('**/gagShopPanel'), image_pos=(-0.21, 0, 0.18), parent=self) + self.storePurchaseFrame.hide() + storePurchaseModels.removeNode() + return + + def buttonLookup(self, track, level): + return self.invModels[track][level] + + def enterTrackFrame(self, track, guiItem): + messenger.send('enterTrackFrame', [track]) + + def exitTrackFrame(self, track, guiItem): + messenger.send('exitTrackFrame', [track]) + + def checkPropBonus(self, track): + result = False + if track == self._interactivePropTrackBonus: + result = True + return result + + def stopAndClearPropBonusIval(self): + if self.propBonusIval and self.propBonusIval.isPlaying(): + self.propBonusIval.finish() + self.propBonusIval = Parallel(name='dummyPropBonusIval') + + def addToPropBonusIval(self, button): + flashObject = button + try: + flashObject = button.component('image0') + except: + pass + + goDark = LerpColorScaleInterval(flashObject, 0.5, Point4(0.1, 0.1, 0.1, 1.0), Point4(1, 1, 1, 1), blendType='easeIn') + goBright = LerpColorScaleInterval(flashObject, 0.5, Point4(1, 1, 1, 1), Point4(0.1, 0.1, 0.1, 1.0), blendType='easeOut') + newSeq = Sequence(goDark, goBright, Wait(0.2)) + self.propBonusIval.append(newSeq) diff --git a/toontown/toon/LaffMeter.py b/toontown/toon/LaffMeter.py new file mode 100644 index 0000000..160ec47 --- /dev/null +++ b/toontown/toon/LaffMeter.py @@ -0,0 +1,190 @@ +from pandac.PandaModules import Vec4 +from direct.gui.DirectGui import DirectFrame, DirectLabel +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownIntervals + +class LaffMeter(DirectFrame): + deathColor = Vec4(0.58039216, 0.80392157, 0.34117647, 1.0) + + def __init__(self, avdna, hp, maxHp): + DirectFrame.__init__(self, relief=None, sortOrder=50) + self.initialiseoptions(LaffMeter) + self.container = DirectFrame(parent=self, relief=None) + self.style = avdna + self.av = None + self.hp = hp + self.maxHp = maxHp + self.__obscured = 0 + if self.style.type == 't': + self.isToon = 1 + else: + self.isToon = 0 + self.load() + return + + def obscure(self, obscured): + self.__obscured = obscured + if self.__obscured: + self.hide() + + def isObscured(self): + return self.__obscured + + def load(self): + gui = loader.loadModel('phase_3/models/gui/laff_o_meter') + if self.isToon: + hType = self.style.getType() + if hType == 'dog': + headModel = gui.find('**/doghead') + elif hType == 'cat': + headModel = gui.find('**/cathead') + elif hType == 'mouse': + headModel = gui.find('**/mousehead') + elif hType == 'horse': + headModel = gui.find('**/horsehead') + elif hType == 'rabbit': + headModel = gui.find('**/bunnyhead') + elif hType == 'duck': + headModel = gui.find('**/duckhead') + elif hType == 'monkey': + headModel = gui.find('**/monkeyhead') + elif hType == 'bear': + headModel = gui.find('**/bearhead') + elif hType == 'pig': + headModel = gui.find('**/pighead') + else: + raise StandardError('unknown toon species: ', hType) + self.color = self.style.getHeadColor() + self.container['image'] = headModel + self.container['image_color'] = self.color + self.resetFrameSize() + self.setScale(0.1) + self.frown = DirectFrame(parent=self.container, relief=None, image=gui.find('**/frown')) + self.smile = DirectFrame(parent=self.container, relief=None, image=gui.find('**/smile')) + self.eyes = DirectFrame(parent=self.container, relief=None, image=gui.find('**/eyes')) + self.openSmile = DirectFrame(parent=self.container, relief=None, image=gui.find('**/open_smile')) + self.tooth1 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_1')) + self.tooth2 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_2')) + self.tooth3 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_3')) + self.tooth4 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_4')) + self.tooth5 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_5')) + self.tooth6 = DirectFrame(parent=self.openSmile, relief=None, image=gui.find('**/tooth_6')) + self.maxLabel = DirectLabel(parent=self.eyes, relief=None, pos=(0.442, 0, 0.051), text='120', text_scale=0.4, text_font=ToontownGlobals.getInterfaceFont()) + self.hpLabel = DirectLabel(parent=self.eyes, relief=None, pos=(-0.398, 0, 0.051), text='120', text_scale=0.4, text_font=ToontownGlobals.getInterfaceFont()) + self.teeth = [self.tooth6, + self.tooth5, + self.tooth4, + self.tooth3, + self.tooth2, + self.tooth1] + self.fractions = [0.0, + 0.166666, + 0.333333, + 0.5, + 0.666666, + 0.833333] + gui.removeNode() + return + + def destroy(self): + if self.av: + ToontownIntervals.cleanup(self.av.uniqueName('laffMeterBoing') + '-' + str(self.this)) + ToontownIntervals.cleanup(self.av.uniqueName('laffMeterBoing') + '-' + str(self.this) + '-play') + self.ignore(self.av.uniqueName('hpChange')) + del self.style + del self.av + del self.hp + del self.maxHp + if self.isToon: + del self.frown + del self.smile + del self.openSmile + del self.tooth1 + del self.tooth2 + del self.tooth3 + del self.tooth4 + del self.tooth5 + del self.tooth6 + del self.teeth + del self.fractions + del self.maxLabel + del self.hpLabel + DirectFrame.destroy(self) + + def adjustTeeth(self): + if self.isToon: + for i in range(len(self.teeth)): + if self.hp > self.maxHp * self.fractions[i]: + self.teeth[i].show() + else: + self.teeth[i].hide() + + def adjustText(self): + if self.isToon: + if self.maxLabel['text'] != str(self.maxHp) or self.hpLabel['text'] != str(self.hp): + self.maxLabel['text'] = str(self.maxHp) + self.hpLabel['text'] = str(self.hp) + + def animatedEffect(self, delta): + if delta == 0 or self.av == None: + return + name = self.av.uniqueName('laffMeterBoing') + '-' + str(self.this) + ToontownIntervals.cleanup(name) + if delta > 0: + ToontownIntervals.start(ToontownIntervals.getPulseLargerIval(self.container, name)) + else: + ToontownIntervals.start(ToontownIntervals.getPulseSmallerIval(self.container, name)) + return + + def adjustFace(self, hp, maxHp, quietly = 0): + if self.isToon and self.hp != None: + self.frown.hide() + self.smile.hide() + self.openSmile.hide() + self.eyes.hide() + for tooth in self.teeth: + tooth.hide() + + delta = hp - self.hp + self.hp = hp + self.maxHp = maxHp + if self.hp < 1: + self.frown.show() + self.container['image_color'] = self.deathColor + elif self.hp >= self.maxHp: + self.smile.show() + self.eyes.show() + self.container['image_color'] = self.color + else: + self.openSmile.show() + self.eyes.show() + self.maxLabel.show() + self.hpLabel.show() + self.container['image_color'] = self.color + self.adjustTeeth() + self.adjustText() + if not quietly: + self.animatedEffect(delta) + return + + def start(self): + if self.av: + self.hp = self.av.hp + self.maxHp = self.av.maxHp + if self.isToon: + if not self.__obscured: + self.show() + self.adjustFace(self.hp, self.maxHp, 1) + if self.av: + self.accept(self.av.uniqueName('hpChange'), self.adjustFace) + + def stop(self): + if self.isToon: + self.hide() + if self.av: + self.ignore(self.av.uniqueName('hpChange')) + + def setAvatar(self, av): + if self.av: + self.ignore(self.av.uniqueName('hpChange')) + self.av = av diff --git a/toontown/toon/LocalToon.py b/toontown/toon/LocalToon.py new file mode 100644 index 0000000..3fc90f0 --- /dev/null +++ b/toontown/toon/LocalToon.py @@ -0,0 +1,2239 @@ +import random +import math +import time +import re +import zlib +from direct.interval.IntervalGlobal import * +from direct.distributed.ClockDelta import * +from direct.showbase.PythonUtil import * +from direct.gui.DirectGui import * +from direct.task import Task +from direct.showbase import PythonUtil +from direct.directnotify import DirectNotifyGlobal +from direct.gui import DirectGuiGlobals +from pandac.PandaModules import * +from otp.avatar import LocalAvatar +from otp.login import LeaveToPayDialog +from otp.avatar import PositionExaminer +from otp.otpbase import OTPGlobals +from otp.avatar import DistributedPlayer +from toontown.shtiker import ShtikerBook +from toontown.shtiker import InventoryPage +from toontown.shtiker import MapPage +from toontown.shtiker import OptionsPage +from toontown.shtiker import ShardPage +from toontown.shtiker import QuestPage +from toontown.shtiker import TrackPage +from toontown.shtiker import KartPage +from toontown.shtiker import GardenPage +from toontown.shtiker import GolfPage +from toontown.shtiker import SuitPage +from toontown.shtiker import DisguisePage +from toontown.shtiker import PhotoAlbumPage +from toontown.shtiker import FishPage +from toontown.shtiker import NPCFriendPage +from toontown.shtiker import EventsPage +from toontown.shtiker import TIPPage +from toontown.quest import Quests +from toontown.quest import QuestParser +from toontown.toonbase.ToontownGlobals import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.catalog import CatalogNotifyDialog +from toontown.chat import ToontownChatManager +from toontown.chat import TTTalkAssistant +from toontown.estate import GardenGlobals +from toontown.battle.BattleSounds import * +from toontown.battle import Fanfare +from toontown.parties import PartyGlobals +from toontown.toon import ElevatorNotifier +from toontown.toon import ToonDNA +import DistributedToon +import Toon +import LaffMeter +from toontown.quest import QuestMap +from toontown.toon.DistributedNPCToonBase import DistributedNPCToonBase +WantNewsPage = base.config.GetBool('want-news-page', ToontownGlobals.DefaultWantNewsPageSetting) +from toontown.toontowngui import NewsPageButtonManager +if WantNewsPage: + from toontown.shtiker import NewsPage +AdjustmentForNewsButton = -0.275 +ClaraBaseXPos = 1.45 +if (__debug__): + import pdb + +class LocalToon(DistributedToon.DistributedToon, LocalAvatar.LocalAvatar): + neverDisable = 1 + piePowerSpeed = base.config.GetDouble('pie-power-speed', 0.2) + piePowerExponent = base.config.GetDouble('pie-power-exponent', 0.75) + + def __init__(self, cr): + try: + self.LocalToon_initialized + except: + self.LocalToon_initialized = 1 + self.numFlowers = 0 + self.maxFlowerBasket = 0 + DistributedToon.DistributedToon.__init__(self, cr) + chatMgr = ToontownChatManager.ToontownChatManager(cr, self) + talkAssistant = TTTalkAssistant.TTTalkAssistant() + LocalAvatar.LocalAvatar.__init__(self, cr, chatMgr, talkAssistant, passMessagesThrough=True) + self.soundRun = base.loadSfx('phase_3.5/audio/sfx/AV_footstep_runloop.wav') + self.soundWalk = base.loadSfx('phase_3.5/audio/sfx/AV_footstep_walkloop.wav') + self.soundWhisper = base.loadSfx('phase_3.5/audio/sfx/GUI_whisper_3.mp3') + self.soundPhoneRing = base.loadSfx('phase_3.5/audio/sfx/telephone_ring.mp3') + self.soundSystemMessage = base.loadSfx('phase_3/audio/sfx/clock03.mp3') + self.positionExaminer = PositionExaminer.PositionExaminer() + friendsGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + friendsButtonNormal = friendsGui.find('**/FriendsBox_Closed') + friendsButtonPressed = friendsGui.find('**/FriendsBox_Rollover') + friendsButtonRollover = friendsGui.find('**/FriendsBox_Rollover') + newScale = oldScale = 0.8 + if WantNewsPage: + newScale = oldScale * ToontownGlobals.NewsPageScaleAdjust + self.bFriendsList = DirectButton(image=(friendsButtonNormal, friendsButtonPressed, friendsButtonRollover), relief=None, pos=(1.192, 0, 0.875), scale=newScale, text=('', TTLocalizer.FriendsListLabel, TTLocalizer.FriendsListLabel), text_scale=0.09, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), text_pos=(0, -0.18), text_font=ToontownGlobals.getInterfaceFont(), command=self.sendFriendsListEvent) + self.bFriendsList.hide() + self.friendsListButtonActive = 0 + self.friendsListButtonObscured = 0 + self.moveFurnitureButtonObscured = 0 + self.clarabelleButtonObscured = 0 + friendsGui.removeNode() + self.__furnitureGui = None + self.__clarabelleButton = None + self.__clarabelleFlash = None + self.furnitureManager = None + self.furnitureDirector = None + self.gotCatalogNotify = 0 + self.__catalogNotifyDialog = None + self.accept('phaseComplete-5.5', self.loadPhase55Stuff) + Toon.loadDialog() + self.isIt = 0 + self.cantLeaveGame = 0 + self.tunnelX = 0.0 + self.estate = None + self.__pieBubble = None + self.allowPies = 0 + self.__pieButton = None + self.__piePowerMeter = None + self.__piePowerMeterSequence = None + self.__pieButtonType = None + self.__pieButtonCount = None + self.tossPieStart = None + self.__presentingPie = 0 + self.__pieSequence = 0 + self.wantBattles = base.config.GetBool('want-battles', 1) + self.seeGhosts = base.config.GetBool('see-ghosts', 0) + wantNameTagAvIds = base.config.GetBool('want-nametag-avids', 0) + if wantNameTagAvIds: + messenger.send('nameTagShowAvId', []) + base.idTags = 1 + self.glitchX = 0 + self.glitchY = 0 + self.glitchZ = 0 + self.glitchCount = 0 + self.ticker = 0 + self.glitchOkay = 1 + self.tempGreySpacing = 0 + self.wantStatePrint = base.config.GetBool('want-statePrint', 0) + self.__gardeningGui = None + self.__gardeningGuiFake = None + self.__shovelButton = None + self.shovelRelatedDoId = 0 + self.shovelAbility = '' + self.plantToWater = 0 + self.shovelButtonActiveCount = 0 + self.wateringCanButtonActiveCount = 0 + self.showingWateringCan = 0 + self.showingShovel = 0 + self.touchingPlantList = [] + self.inGardenAction = None + self.guiConflict = 0 + self.lastElevatorLeft = 0 + self.elevatorNotifier = ElevatorNotifier.ElevatorNotifier() + self.accept(OTPGlobals.AvatarFriendAddEvent, self.sbFriendAdd) + self.accept(OTPGlobals.AvatarFriendUpdateEvent, self.sbFriendUpdate) + self.accept(OTPGlobals.AvatarFriendRemoveEvent, self.sbFriendRemove) + self._zoneId = None + self.accept('system message aknowledge', self.systemWarning) + self.systemMsgAckGuiDoneEvent = 'systemMsgAckGuiDoneEvent' + self.accept(self.systemMsgAckGuiDoneEvent, self.hideSystemMsgAckGui) + self.systemMsgAckGui = None + self.createSystemMsgAckGui() + if not hasattr(base.cr, 'lastLoggedIn'): + base.cr.lastLoggedIn = self.cr.toontownTimeManager.convertStrToToontownTime('') + self.setLastTimeReadNews(base.cr.lastLoggedIn) + self.acceptingNewFriends = Settings.getAcceptingNewFriends() and base.config.GetBool('accepting-new-friends-default', True) + self.acceptingNonFriendWhispers = Settings.getAcceptingNonFriendWhispers() and base.config.GetBool('accepting-non-friend-whispers-default', True) + self.physControls.event.addAgainPattern('again%in') + self.oldPos = None + self.questMap = None + self.prevToonIdx = 0 + + def wantLegacyLifter(self): + return True + + def startGlitchKiller(self): + if localAvatar.getZoneId() not in GlitchKillerZones: + return + if __dev__: + self.glitchMessage = 'START GLITCH KILLER' + randChoice = random.randint(0, 3) + if randChoice == 0: + self.glitchMessage = 'START GLITCH KILLER' + elif randChoice == 1: + self.glitchMessage = 'GLITCH KILLER ENGAGED' + elif randChoice == 2: + self.glitchMessage = 'GLITCH KILLER GO!' + elif randChoice == 3: + self.glitchMessage = 'GLITCH IN YO FACE FOOL!' + self.notify.debug(self.glitchMessage) + taskMgr.remove(self.uniqueName('glitchKiller')) + taskMgr.add(self.glitchKiller, self.uniqueName('glitchKiller')) + self.glitchOkay = 1 + + def pauseGlitchKiller(self): + self.tempGreySpacing = 1 + + def unpauseGlitchKiller(self): + self.tempGreySpacing = 0 + + def stopGlitchKiller(self): + if __dev__ and hasattr(self, 'glitchMessage'): + if self.glitchMessage == 'START GLITCH KILLER': + self.notify.debug('STOP GLITCH KILLER') + elif self.glitchMessage == 'GLITCH KILLER ENGAGED': + self.notify.debug('GLITCH KILLER DISENGAGED') + elif self.glitchMessage == 'GLITCH KILLER GO!': + self.notify.debug('GLITCH KILLER NO GO!') + elif self.glitchMessage == 'GLITCH IN YO FACE FOOL!': + self.notify.debug('GLITCH OFF YO FACE FOOL!') + taskMgr.remove(self.uniqueName('glitchKiller')) + self.glitchOkay = 1 + + def glitchKiller(self, taskFooler = 0): + if base.greySpacing or self.tempGreySpacing: + return Task.cont + self.ticker += 1 + if not self.physControls.lifter.hasContact() and not self.glitchOkay: + self.glitchCount += 1 + else: + self.glitchX = self.getX() + self.glitchY = self.getY() + self.glitchZ = self.getZ() + self.glitchCount = 0 + if self.physControls.lifter.hasContact(): + self.glitchOkay = 0 + if hasattr(self, 'physControls'): + if self.ticker >= 10: + self.ticker = 0 + if self.glitchCount >= 7: + print 'GLITCH MAXED!!! resetting pos' + self.setX(self.glitchX - 1 * (self.getX() - self.glitchX)) + self.setY(self.glitchY - 1 * (self.getY() - self.glitchY)) + self.glitchCount = 0 + return Task.cont + + def announceGenerate(self): + self.startLookAround() + if base.wantNametags: + self.nametag.manage(base.marginManager) + self.startHackObservation() + DistributedToon.DistributedToon.announceGenerate(self) + from otp.friends import FriendInfo + + def toonPosCheck(self, task = None): + toon = random.choice(self.cr.toons.values()) + if toon and toon is not self and not isinstance(toon, DistributedNPCToonBase): + self.notify.debug('checking position for %s' % toon.doId) + realTimeStart = globalClock.getRealTime() + numOtherToons = len(self.cr.toons.values()) + for otherToonIdxBase in range(numOtherToons): + otherToonIdx = otherToonIdxBase + self.prevToonIdx + if otherToonIdx >= numOtherToons: + otherToonIdx = otherToonIdx % numOtherToons + if globalClock.getRealTime() > realTimeStart + AV_TOUCH_CHECK_TIMELIMIT_CL: + self.notify.debug('too much time, exiting at index %s' % otherToonIdx) + self.prevToonIdx = otherToonIdx + break + otherToon = self.cr.toons.values()[otherToonIdx] + self.notify.debug('comparing with toon %s at index %s' % (otherToon.doId, otherToonIdx)) + if otherToon and otherToon is not toon and otherToon is not self and not isinstance(otherToon, DistributedNPCToonBase): + toonPos = toon.getPos(render) + otherToonPos = otherToon.getPos(render) + self.notify.debug('pos1: %s pos2: %s' % (toonPos, otherToonPos)) + zDist = otherToonPos.getZ() - toonPos.getZ() + toonPos.setZ(0) + otherToonPos.setZ(0) + moveVec = otherToonPos - toonPos + dist = moveVec.length() + self.notify.debug('distance to %s is %s %s' % (otherToon.doId, dist, zDist)) + if dist < AV_TOUCH_CHECK_DIST and zDist < AV_TOUCH_CHECK_DIST_Z: + self.notify.debug('inappropriate touching!!!') + if toon.getParent() == render: + toonToMoveId = toon.doId + toonToNotMoveId = otherToon.doId + else: + toonToMoveId = otherToon.doId + toonToNotMoveId = toon.doId + self.sendUpdate('flagAv', [toonToMoveId, AV_FLAG_REASON_TOUCH, [str(toonToNotMoveId)]]) + self.prevToonIdx = otherToonIdx + break + self.notify.debug('spent %s seconds doing pos check for %s' % (globalClock.getRealTime() - realTimeStart, toon.doId)) + + return Task.again + + def tmdcc(self, task = None): + toon = random.choice(self.cr.toons.values()) + result = self._tmdcc(toon) + if task: + if result: + task.setDelay(5.0) + else: + task.setDelay(1.5) + return Task.again + + def _tmdcc(self, toon, checks = []): + result = None + if isinstance(toon, DistributedNPCToonBase) or toon is localAvatar or toon.isEmpty() or toon.bFake or toon._delayDeleteForceAllow: + return result + startTime = globalClock.getRealTime() + + def delayedSend(toon, msg): + if toon: + toon.sendLogSuspiciousEvent(msg) + return Task.done + + def sendT(header, msg, sToon, sendFooter = False, sendLs = True): + uid = '[' + str(globalClock.getRealTime()) + ']' + msgSize = 800 - (len(header) + len(uid) + 1) + uname = self.uniqueName('ioorrd234') + currCounter = 0 + + def sendAsParts(message, counter): + for currBlock in range(0, len(message) / msgSize + 1): + fmsg = '%s %02d: ' % (uid, currBlock + counter) + header + ': "%s"' % message[currBlock * msgSize:currBlock * msgSize + msgSize] + taskMgr.doMethodLater(0.08 * (currBlock + counter), delayedSend, uname + str(currBlock + counter), extraArgs=[sToon, fmsg]) + + return currBlock + counter + 1 + + currCounter = sendAsParts(msg, currCounter) + if sendLs: + sstream = StringStream.StringStream() + sToon.ls(sstream) + sdata = sstream.getData() + currCounter = sendAsParts(sdata, currCounter) + if sendFooter: + sstream.clearData() + if hasattr(sToon, 'suitGeom'): + sToon.suitGeom.ls(sstream) + bs = '' + nodeNames = config.GetString('send-suspicious-bam', 'to_head') + if nodeNames != '': + bs = ' bam ' + nodeNames + ': ' + nodesToLog = [] + for currName in nodeNames.split(): + nodesToLog.append(sToon.find('**/' + currName)) + + for currNode in nodesToLog: + bs += zlib.compress(currNode.encodeToBamStream()).encode('hex') + '_' + + footer = 'loc: %s dna: %s gmname: %s ntag: %s ceffect: %s disguise: %s sstyle: %s sgeom: %s %s' % (str(sToon.getLocation()), + str(sToon.style.asTuple()), + sToon.gmNameTagEnabled, + str(sToon.nametag and sToon.nametag.getContents()), + str(sToon.cheesyEffect), + sToon.isDisguised, + hasattr(sToon, 'suit') and str(sToon.suit.style), + hasattr(sToon, 'suitGeom') and sstream.getData(), + bs) + currCounter = sendAsParts(footer, currCounter) + + self.sendUpdate('requestPing', [toon.doId]) + if not checks: + numChecks = 6 + checks = [random.choice(range(1, numChecks + 1))] + + def findParentAv(node): + avId = 0 + topParent = node + while topParent and not topParent.getTag('avatarDoId'): + topParent = topParent.getParent() + + if topParent: + avIdStr = topParent.getTag('avatarDoId') + if avIdStr: + avId = int(avIdStr) + return (self.cr.getDo(avId), avId) + + msgHeader = 'AvatarHackWarning!' + + def hacker_detect_immediate(cbdata): + action = cbdata.getAction() + node = cbdata.getNode() + np = NodePath(node) + if not self.cr or not self.cr.distributedDistrict or not self.cr.distributedDistrict.getAllowAHNNLog(): + if self.cr and self.cr.distributedDistrict: + sToon, avId = findParentAv(np) + if sToon is localAvatar: + return + if sToon and isinstance(sToon, DistributedToon.DistributedToon): + msg = "Blocking '%s' '%s' '%s'" % (self.cr.distributedDistrict.getAllowAHNNLog(), np, re.sub('<', '[', StackTrace(start=1).compact())) + sendT(msgHeader, msg, sToon, sendFooter=False, sendLs=False) + return + try: + parentNames = ['__Actor_modelRoot', 'to_head'] + newParent = np.getParent() + if newParent and newParent.getName() in parentNames: + newParentParent = newParent.getParent() + parentParentNames = ['actorGeom', '__Actor_modelRoot'] + if newParentParent and newParentParent.getName() in parentParentNames: + sToon, avId = findParentAv(newParentParent.getParent()) + if sToon is localAvatar: + return + header = msgHeader + ' nodename' + avInfo = "hacker activity '%s' avatar %s node name '%s' with parents '%s' and '%s'!" % (action, + avId, + np.getName(), + newParent.getName(), + newParentParent.getName()) + if sToon and isinstance(sToon, DistributedToon.DistributedToon): + avInfo += ' trace: ' + avInfo += re.sub('<', '[', StackTrace(start=1).compact()) + sendT(header, avInfo, sToon=sToon, sendFooter=True) + else: + sendLogSuspiciousEvent(header, 'got non-toon or missing parent %s...' % sToon + avInfo) + except: + pass + + if config.GetBool('detect-suspicious-nodename', True): + PandaNode.setDetectCallback(PythonCallbackObject(hacker_detect_immediate)) + + def trackChat(chattingToon): + + def _spoke(cbdata): + avId = cbdata.getId() + av = self.cr.getDo(avId) + chat = cbdata.getChat() + if avId != localAvatar.doId and av: + avInfo = 'suspicious chat "%s" trace: ' % chat + avInfo += re.sub('<', '[', StackTrace(start=1).compact()) + sendT(msgHeader + ' chat', avInfo, chattingToon, sendFooter=False, sendLs=False) + + chattingToon.nametag.setChatCallback(PythonCallbackObject(_spoke)) + chattingToon.nametag.setChatCallbackId(chattingToon.doId) + + if 1 in checks: + if base.config.GetBool('tmdcc-headcheck', 1): + headNodes = toon.findAllMatches('**/__Actor_head') + if len(headNodes) != 3 or not toon.getGeomNode().isHidden() and filter(lambda x: x.isHidden(), headNodes): + sendT(msgHeader, 'missing head node', toon) + result = toon + if base.config.GetBool('tmdcc-chatcheck', 1): + trackChat(toon) + else: + checks.append(2) + if 2 in checks: + if base.config.GetBool('tmdcc-handcheck', 1): + if not toon.getGeomNode().isHidden(): + handNodes = toon.findAllMatches('**/hands') + for currHandNode in handNodes: + if currHandNode.hasColor() and currHandNode.getColor() != VBase4(1, 1, 1, 1): + sendT(msgHeader, 'invalid hand color: %s' % currHandNode.getColor(), toon) + result = toon + break + + else: + checks.append(3) + if 3 in checks: + if base.config.GetBool('tmdcc-namecheck', 1): + nameNode = toon.find('**/nametag3d') + if not nameNode or nameNode.isHidden() and not toon.getGeomNode().isHidden() and toon.ghostMode == 0: + sendT(msgHeader, 'missing nametag for name: %s' % toon.getName(), toon) + result = toon + else: + checks.append(4) + if 4 in checks: + if base.config.GetBool('tmdcc-animcheck', 1): + if toon.zoneId in [ToontownGlobals.DonaldsDock, + ToontownGlobals.OutdoorZone, + ToontownGlobals.ToontownCentral, + ToontownGlobals.TheBrrrgh, + ToontownGlobals.MinniesMelodyland, + ToontownGlobals.DaisyGardens, + ToontownGlobals.FunnyFarm, + ToontownGlobals.GoofySpeedway, + ToontownGlobals.DonaldsDreamland]: + currAnim = toon.animFSM.getCurrentState().getName() + if currAnim != None and currAnim not in ['neutral', + 'Happy', + 'off', + 'Sad', + 'TeleportIn', + 'jumpAirborne', + 'CloseBook', + 'run', + 'OpenBook', + 'TeleportOut', + 'TeleportedOut', + 'ReadBook', + 'walk', + 'Sit', + 'jumpLand', + 'Sleep', + 'cringe', + 'jumpSquat', + 'Died']: + sendT(msgHeader, 'invalid animation playing: %s' % currAnim, toon) + result = toon + else: + checks.append(5) + if 5 in checks: + if base.config.GetBool('tmdcc-cogsuit', 1): + if toon.zoneId in [ToontownGlobals.DonaldsDock, + ToontownGlobals.OutdoorZone, + ToontownGlobals.ToontownCentral, + ToontownGlobals.TheBrrrgh, + ToontownGlobals.MinniesMelodyland, + ToontownGlobals.DaisyGardens, + ToontownGlobals.FunnyFarm, + ToontownGlobals.GoofySpeedway, + ToontownGlobals.DonaldsDreamland]: + if toon.isDisguised: + sendT(msgHeader, 'toon %s is in a cog suit' % toon.getName(), toon) + result = toon + else: + checks.append(6) + if 6 in checks: + if base.config.GetBool('tmdcc-colorcheck', 1): + torsoPieces = toon.getPieces(('torso', ('arms', 'neck'))) + legPieces = toon.getPieces(('legs', ('legs', 'feet'))) + headPieces = toon.getPieces(('head', '*head*')) + if (filter(lambda x: x.hasColor() and x.getColor() not in ToonDNA.allowedColors, + torsoPieces) or + filter(lambda x: x.hasColor() and x.getColor() not in ToonDNA.allowedColors, + legPieces) or + filter(lambda x: x.hasColor() and x.getColor() not in ToonDNA.allowedColors, + headPieces)) and toon.cheesyEffect == ToontownGlobals.CENormal: + torsoColors = str(map(lambda x: not x.hasColor() and 'clear' or x.getColor() in ToonDNA.allowedColors and 'ok' or x.getColor(), torsoPieces)) + legColors = str(map(lambda x: not x.hasColor() and 'clear' or x.getColor() in ToonDNA.allowedColors and 'ok' or x.getColor(), legPieces)) + headColors = str(map(lambda x: not x.hasColor() and 'clear' or x.getColor() in ToonDNA.allowedColors and 'ok' or x.getColor(), headPieces)) + sendT(msgHeader, 'invalid color...arm: %s leg: %s head: %s' % (torsoColors, legColors, headColors), toon) + result = toon + else: + checks.append(7) + endTime = globalClock.getRealTime() + return result + + def startHackObservation(self): + taskMgr.doMethodLater(AV_TOUCH_CHECK_DELAY_CL, self.toonPosCheck, self.uniqueName('toonPosCheck')) + taskMgr.doMethodLater(config.GetDouble('tmdcc-delay', 5.0), self.tmdcc, self.uniqueName('tmdcc')) + if __dev__ and base.config.GetBool('tmdcc-keys', 0): + from toontown.testenv import safezoneAutoVisit + safezoneAutoVisit.setupKeys() + from toontown.testenv import watchDistObj + watchDistObj.watchObj.setupKeys() + + def stopHackObservation(self): + taskMgr.remove(self.uniqueName('toonPosCheck')) + taskMgr.remove(self.uniqueName('tmdcc')) + + def disable(self): + self.stopHackObservation() + self.laffMeter.destroy() + del self.laffMeter + self.questMap.destroy() + self.questMap = None + if hasattr(self, 'purchaseButton'): + self.purchaseButton.destroy() + del self.purchaseButton + self.newsButtonMgr.request('Off') + base.whiteList.unload() + self.book.unload() + del self.optionsPage + del self.shardPage + del self.mapPage + del self.invPage + del self.questPage + del self.suitPage + del self.sosPage + del self.disguisePage + del self.fishPage + del self.gardenPage + del self.trackPage + del self.book + if base.wantKarts: + if hasattr(self, 'kartPage'): + del self.kartPage + if base.wantNametags: + self.nametag.unmanage(base.marginManager) + taskMgr.removeTasksMatching('*ioorrd234*') + self.ignoreAll() + DistributedToon.DistributedToon.disable(self) + return + + def disableBodyCollisions(self): + pass + + def delete(self): + try: + self.LocalToon_deleted + except: + self.LocalToon_deleted = 1 + Toon.unloadDialog() + QuestParser.clear() + DistributedToon.DistributedToon.delete(self) + LocalAvatar.LocalAvatar.delete(self) + self.bFriendsList.destroy() + del self.bFriendsList + if self.__pieButton: + self.__pieButton.destroy() + self.__pieButton = None + if self.__piePowerMeter: + self.__piePowerMeter.destroy() + self.__piePowerMeter = None + taskMgr.remove('unlockGardenButtons') + taskMgr.remove('lerpFurnitureButton') + if self.__furnitureGui: + self.__furnitureGui.destroy() + del self.__furnitureGui + if self.__gardeningGui: + self.__gardeningGui.destroy() + del self.__gardeningGui + if self.__gardeningGuiFake: + self.__gardeningGuiFake.destroy() + del self.__gardeningGuiFake + if self.__clarabelleButton: + self.__clarabelleButton.destroy() + del self.__clarabelleButton + if self.__clarabelleFlash: + self.__clarabelleFlash.finish() + del self.__clarabelleFlash + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + del self.__catalogNotifyDialog + + return + + def initInterface(self): + self.newsButtonMgr = NewsPageButtonManager.NewsPageButtonManager() + self.newsButtonMgr.request('Hidden') + self.book = ShtikerBook.ShtikerBook('bookDone') + self.book.load() + self.book.hideButton() + self.optionsPage = OptionsPage.OptionsPage() + self.optionsPage.load() + self.book.addPage(self.optionsPage, pageName=TTLocalizer.OptionsPageTitle) + self.shardPage = ShardPage.ShardPage() + self.shardPage.load() + self.book.addPage(self.shardPage, pageName=TTLocalizer.ShardPageTitle) + self.mapPage = MapPage.MapPage() + self.mapPage.load() + self.book.addPage(self.mapPage, pageName=TTLocalizer.MapPageTitle) + self.invPage = InventoryPage.InventoryPage() + self.invPage.load() + self.book.addPage(self.invPage, pageName=TTLocalizer.InventoryPageTitle) + self.questPage = QuestPage.QuestPage() + self.questPage.load() + self.book.addPage(self.questPage, pageName=TTLocalizer.QuestPageToonTasks) + self.trackPage = TrackPage.TrackPage() + self.trackPage.load() + self.book.addPage(self.trackPage, pageName=TTLocalizer.TrackPageShortTitle) + self.suitPage = SuitPage.SuitPage() + self.suitPage.load() + self.book.addPage(self.suitPage, pageName=TTLocalizer.SuitPageTitle) + if base.config.GetBool('want-photo-album', 0): + self.photoAlbumPage = PhotoAlbumPage.PhotoAlbumPage() + self.photoAlbumPage.load() + self.book.addPage(self.photoAlbumPage, pageName=TTLocalizer.PhotoPageTitle) + self.fishPage = FishPage.FishPage() + self.fishPage.setAvatar(self) + self.fishPage.load() + self.book.addPage(self.fishPage, pageName=TTLocalizer.FishPageTitle) + if base.wantKarts: + self.addKartPage() + if self.disguisePageFlag: + self.loadDisguisePages() + if self.sosPageFlag: + self.loadSosPages() + if self.gardenStarted: + self.loadGardenPages() + self.addGolfPage() + self.addEventsPage() + if WantNewsPage: + self.addNewsPage() + self.book.setPage(self.mapPage, enterPage=False) + self.laffMeter = LaffMeter.LaffMeter(self.style, self.hp, self.maxHp) + self.laffMeter.setAvatar(self) + self.laffMeter.setScale(0.075) + if self.style.getAnimal() == 'monkey': + self.laffMeter.setPos(-1.18, 0.0, -0.87) + else: + self.laffMeter.setPos(-1.2, 0.0, -0.87) + self.laffMeter.stop() + self.questMap = QuestMap.QuestMap(self) + self.questMap.stop() + if not base.cr.isPaid(): + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.purchaseButton = DirectButton(parent=aspect2d, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=0.9, text=TTLocalizer.OptionsPagePurchase, text_scale=0.05, text_pos=(0, -0.01), textMayChange=0, pos=(0.885, 0, -0.94), sortOrder=100, command=self.__handlePurchase) + base.setCellsAvailable([base.bottomCells[4]], 0) + self.accept('time-insert', self.__beginTossPie) + self.accept('time-insert-up', self.__endTossPie) + self.accept('time-delete', self.__beginTossPie) + self.accept('time-delete-up', self.__endTossPie) + self.accept('pieHit', self.__pieHit) + self.accept('interrupt-pie', self.interruptPie) + self.accept('InputState-jump', self.__toonMoved) + self.accept('InputState-forward', self.__toonMoved) + self.accept('InputState-reverse', self.__toonMoved) + self.accept('InputState-turnLeft', self.__toonMoved) + self.accept('InputState-turnRight', self.__toonMoved) + self.accept('InputState-slide', self.__toonMoved) + QuestParser.init() + return + + def __handlePurchase(self): + self.purchaseButton.hide() + if (base.cr.isWebPlayToken() or __dev__): + if base.cr.isPaid(): + if base.cr.productName in ['DisneyOnline-UK', 'DisneyOnline-AP', 'JP', 'DE', 'BR', 'FR']: + paidNoParentPassword = launcher and launcher.getParentPasswordSet() + else: + paidNoParentPassword = launcher and not launcher.getParentPasswordSet() + else: + paidNoParentPassword = 0 + self.leaveToPayDialog = LeaveToPayDialog.LeaveToPayDialog(paidNoParentPassword, self.purchaseButton.show) + self.leaveToPayDialog.show() + else: + self.notify.error('You should not get here without a PlayToken') + + if base.wantKarts: + + def addKartPage(self): + if self.hasKart(): + if hasattr(self, 'kartPage') and self.kartPage != None: + return + if not launcher.getPhaseComplete(6): + self.acceptOnce('phaseComplete-6', self.addKartPage) + return + self.kartPage = KartPage.KartPage() + self.kartPage.setAvatar(self) + self.kartPage.load() + self.book.addPage(self.kartPage, pageName=TTLocalizer.KartPageTitle) + return + + def setWantBattles(self, wantBattles): + self.wantBattles = wantBattles + + def loadDisguisePages(self): + if self.disguisePage != None: + return + if not launcher.getPhaseComplete(9): + self.acceptOnce('phaseComplete-9', self.loadDisguisePages) + return + self.disguisePage = DisguisePage.DisguisePage() + self.disguisePage.load() + self.book.addPage(self.disguisePage, pageName=TTLocalizer.DisguisePageTitle) + self.loadSosPages() + return + + def loadSosPages(self): + if self.sosPage != None: + return + self.sosPage = NPCFriendPage.NPCFriendPage() + self.sosPage.load() + self.book.addPage(self.sosPage, pageName=TTLocalizer.NPCFriendPageTitle) + return + + def loadGardenPages(self): + if self.gardenPage != None: + return + if not launcher.getPhaseComplete(5.5): + self.acceptOnce('phaseComplete-5.5', self.loadPhase55Stuff) + return + self.gardenPage = GardenPage.GardenPage() + self.gardenPage.load() + self.book.addPage(self.gardenPage, pageName=TTLocalizer.GardenPageTitle) + return + + def loadPhase55Stuff(self): + if self.gardenPage == None: + self.gardenPage = GardenPage.GardenPage() + self.gardenPage.load() + self.book.addPage(self.gardenPage, pageName=TTLocalizer.GardenPageTitle) + elif not launcher.getPhaseComplete(5.5): + self.acceptOnce('phaseComplete-5.5', self.loadPhase55Stuff) + self.refreshOnscreenButtons() + return + + def setAsGM(self, state): + self.notify.debug('Setting GM State: %s in LocalToon' % state) + DistributedToon.DistributedToon.setAsGM(self, state) + if self.gmState: + if base.config.GetString('gm-nametag-string', '') != '': + self.gmNameTagString = base.config.GetString('gm-nametag-string') + if base.config.GetString('gm-nametag-color', '') != '': + self.gmNameTagColor = base.config.GetString('gm-nametag-color') + if base.config.GetInt('gm-nametag-enabled', 0): + self.gmNameTagEnabled = 1 + self.d_updateGMNameTag() + + def displayTalkWhisper(self, fromId, avatarName, rawString, mods): + sender = base.cr.identifyAvatar(fromId) + if sender: + chatString, scrubbed = sender.scrubTalk(rawString, mods) + else: + chatString, scrubbed = self.scrubTalk(rawString, mods) + sender = self + sfx = self.soundWhisper + chatString = avatarName + ': ' + chatString + whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + whisper.setClickable(avatarName, fromId) + whisper.manage(base.marginManager) + base.playSfx(sfx) + + def displayTalkAccount(self, fromId, senderName, rawString, mods): + sender = None + playerInfo = None + sfx = self.soundWhisper + playerInfo = base.cr.playerFriendsManager.playerId2Info.get(fromId, None) + if playerInfo == None: + return + senderAvId = base.cr.playerFriendsManager.findAvIdFromPlayerId(fromId) + if not senderName and base.cr.playerFriendsManager.playerId2Info.get(fromId): + senderName = base.cr.playerFriendsManager.playerId2Info.get(fromId).playerName + senderAvatar = base.cr.identifyAvatar(senderAvId) + if sender: + chatString, scrubbed = senderAvatar.scrubTalk(rawString, mods) + else: + chatString, scrubbed = self.scrubTalk(rawString, mods) + chatString = senderName + ': ' + chatString + whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), WhisperPopup.WTNormal) + if playerInfo != None: + whisper.setClickable(senderName, fromId, 1) + whisper.manage(base.marginManager) + base.playSfx(sfx) + return + + def isLocal(self): + return 1 + + def canChat(self): + if not self.cr.allowAnyTypedChat(): + return 0 + if self.commonChatFlags & (ToontownGlobals.CommonChat | ToontownGlobals.SuperChat): + return 1 + if base.cr.whiteListChatEnabled: + return 1 + for friendId, flags in self.friendsList: + if flags & ToontownGlobals.FriendChat: + return 1 + + return 0 + + def startChat(self): + if self.tutorialAck: + self.notify.info('calling LocalAvatar.startchat') + LocalAvatar.LocalAvatar.startChat(self) + self.accept('chatUpdateSCToontask', self.b_setSCToontask) + self.accept('chatUpdateSCResistance', self.d_reqSCResistance) + self.accept('chatUpdateSCSinging', self.b_setSCSinging) + self.accept('whisperUpdateSCToontask', self.whisperSCToontaskTo) + else: + self.notify.info('NOT calling LocalAvatar.startchat, in tutorial') + + def stopChat(self): + LocalAvatar.LocalAvatar.stopChat(self) + self.ignore('chatUpdateSCToontask') + self.ignore('chatUpdateSCResistance') + self.ignore('chatUpdateSCSinging') + self.ignore('whisperUpdateSCToontask') + + def tunnelIn(self, tunnelOrigin): + self.b_setTunnelIn(self.tunnelX * 0.8, tunnelOrigin) + + def tunnelOut(self, tunnelOrigin): + self.tunnelX = self.getX(tunnelOrigin) + tunnelY = self.getY(tunnelOrigin) + self.b_setTunnelOut(self.tunnelX * 0.95, tunnelY, tunnelOrigin) + + def handleTunnelIn(self, startTime, endX, x, y, z, h): + self.notify.debug('LocalToon.handleTunnelIn') + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + self.b_setAnimState('run', self.animMultiplier) + self.stopLookAround() + self.reparentTo(render) + self.runSound() + camera.reparentTo(render) + camera.setPosHpr(tunnelOrigin, 0, 20, 12, 180, -20, 0) + base.transitions.irisIn(0.4) + toonTrack = self.getTunnelInToonTrack(endX, tunnelOrigin) + + def cleanup(self = self, tunnelOrigin = tunnelOrigin): + self.stopSound() + tunnelOrigin.removeNode() + messenger.send('tunnelInMovieDone') + + self.tunnelTrack = Sequence(toonTrack, Func(cleanup)) + self.tunnelTrack.start(globalClock.getFrameTime() - startTime) + + def handleTunnelOut(self, startTime, startX, startY, x, y, z, h): + self.notify.debug('LocalToon.handleTunnelOut') + tunnelOrigin = render.attachNewNode('tunnelOrigin') + tunnelOrigin.setPosHpr(x, y, z, h, 0, 0) + self.b_setAnimState('run', self.animMultiplier) + self.runSound() + self.stopLookAround() + tracks = Parallel() + camera.wrtReparentTo(render) + startPos = camera.getPos(tunnelOrigin) + startHpr = camera.getHpr(tunnelOrigin) + camLerpDur = 1.0 + reducedCamH = fitDestAngle2Src(startHpr[0], 180) + tracks.append(LerpPosHprInterval(camera, camLerpDur, pos=Point3(0, 20, 12), hpr=Point3(reducedCamH, -20, 0), startPos=startPos, startHpr=startHpr, other=tunnelOrigin, blendType='easeInOut', name='tunnelOutLerpCamPos')) + toonTrack = self.getTunnelOutToonTrack(startX, startY, tunnelOrigin) + tracks.append(toonTrack) + irisDur = 0.4 + tracks.append(Sequence(Wait(toonTrack.getDuration() - (irisDur + 0.1)), Func(base.transitions.irisOut, irisDur))) + + def cleanup(self = self, tunnelOrigin = tunnelOrigin): + self.stopSound() + self.detachNode() + tunnelOrigin.removeNode() + messenger.send('tunnelOutMovieDone') + + self.tunnelTrack = Sequence(tracks, Func(cleanup)) + self.tunnelTrack.start(globalClock.getFrameTime() - startTime) + + def getPieBubble(self): + if self.__pieBubble == None: + bubble = CollisionSphere(0, 0, 0, 1) + node = CollisionNode('pieBubble') + node.addSolid(bubble) + node.setFromCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.CameraBitmask | ToontownGlobals.FloorBitmask) + node.setIntoCollideMask(BitMask32.allOff()) + self.__pieBubble = NodePath(node) + self.pieHandler = CollisionHandlerEvent() + self.pieHandler.addInPattern('pieHit') + self.pieHandler.addInPattern('pieHit-%in') + return self.__pieBubble + + def __beginTossPieMouse(self, mouseParam): + self.__beginTossPie(globalClock.getFrameTime()) + + def __endTossPieMouse(self, mouseParam): + self.__endTossPie(globalClock.getFrameTime()) + + def __beginTossPie(self, time): + if self.tossPieStart != None: + return + if not self.allowPies: + return + if self.numPies == 0: + messenger.send('outOfPies') + return + if self.__pieInHand(): + return + if getattr(self.controlManager.currentControls, 'isAirborne', 0): + return + messenger.send('wakeup') + self.localPresentPie(time) + taskName = self.uniqueName('updatePiePower') + taskMgr.add(self.__updatePiePower, taskName) + return + + def __endTossPie(self, time): + if self.tossPieStart == None: + return + taskName = self.uniqueName('updatePiePower') + taskMgr.remove(taskName) + messenger.send('wakeup') + power = self.__getPiePower(time) + self.tossPieStart = None + self.localTossPie(power) + return + + def localPresentPie(self, time): + import TTEmote + from otp.avatar import Emote + self.__stopPresentPie() + if self.tossTrack: + tossTrack = self.tossTrack + self.tossTrack = None + tossTrack.finish() + self.interruptPie() + self.tossPieStart = time + self.__pieSequence = self.__pieSequence + 1 & 255 + sequence = self.__pieSequence + self.__presentingPie = 1 + pos = self.getPos() + hpr = self.getHpr() + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + self.sendUpdate('presentPie', [pos[0], + pos[1], + pos[2], + hpr[0] % 360.0, + hpr[1], + hpr[2], + timestamp32]) + Emote.globalEmote.disableBody(self) + messenger.send('begin-pie') + ival = self.getPresentPieInterval(pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]) + ival = Sequence(ival, name=self.uniqueName('localPresentPie')) + self.tossTrack = ival + ival.start() + self.makePiePowerMeter() + self.__piePowerMeter.show() + self.__piePowerMeterSequence = sequence + self.__piePowerMeter['value'] = 0 + return + + def __stopPresentPie(self): + if self.__presentingPie: + import TTEmote + from otp.avatar import Emote + Emote.globalEmote.releaseBody(self) + messenger.send('end-pie') + self.__presentingPie = 0 + taskName = self.uniqueName('updatePiePower') + taskMgr.remove(taskName) + + def __getPiePower(self, time): + elapsed = max(time - self.tossPieStart, 0.0) + t = elapsed / self.piePowerSpeed + t = math.pow(t, self.piePowerExponent) + power = int(t * 100) % 200 + if power > 100: + power = 200 - power + return power + + def __updatePiePower(self, task): + if not self.__piePowerMeter: + return Task.done + self.__piePowerMeter['value'] = self.__getPiePower(globalClock.getFrameTime()) + return Task.cont + + def interruptPie(self): + self.cleanupPieInHand() + self.__stopPresentPie() + if self.__piePowerMeter: + self.__piePowerMeter.hide() + pie = self.pieTracks.get(self.__pieSequence) + if pie and pie.getT() < 14.0 / 24.0: + del self.pieTracks[self.__pieSequence] + pie.pause() + + def __pieInHand(self): + pie = self.pieTracks.get(self.__pieSequence) + return pie and pie.getT() < 15.0 / 24.0 + + def __toonMoved(self, isSet): + if isSet: + self.interruptPie() + + def localTossPie(self, power): + if not self.__presentingPie: + return + pos = self.getPos() + hpr = self.getHpr() + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + sequence = self.__pieSequence + if self.tossTrack: + tossTrack = self.tossTrack + self.tossTrack = None + tossTrack.finish() + if self.pieTracks.has_key(sequence): + pieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + pieTrack.finish() + if self.splatTracks.has_key(sequence): + splatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + splatTrack.finish() + self.makePiePowerMeter() + self.__piePowerMeter['value'] = power + self.__piePowerMeter.show() + self.__piePowerMeterSequence = sequence + pieBubble = self.getPieBubble().instanceTo(NodePath()) + + def pieFlies(self = self, pos = pos, hpr = hpr, sequence = sequence, power = power, timestamp32 = timestamp32, pieBubble = pieBubble): + self.sendUpdate('tossPie', [pos[0], + pos[1], + pos[2], + hpr[0] % 360.0, + hpr[1], + hpr[2], + sequence, + power, + timestamp32]) + if self.numPies != ToontownGlobals.FullPies: + self.setNumPies(self.numPies - 1) + base.cTrav.addCollider(pieBubble, self.pieHandler) + + toss, pie, flyPie = self.getTossPieInterval(pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], power, beginFlyIval=Func(pieFlies)) + pieBubble.reparentTo(flyPie) + flyPie.setTag('pieSequence', str(sequence)) + toss = Sequence(toss) + self.tossTrack = toss + toss.start() + pie = Sequence(pie, Func(base.cTrav.removeCollider, pieBubble), Func(self.pieFinishedFlying, sequence)) + self.pieTracks[sequence] = pie + pie.start() + return + + def pieFinishedFlying(self, sequence): + DistributedToon.DistributedToon.pieFinishedFlying(self, sequence) + if self.__piePowerMeterSequence == sequence: + self.__piePowerMeter.hide() + + def __finishPieTrack(self, sequence): + if self.pieTracks.has_key(sequence): + pieTrack = self.pieTracks[sequence] + del self.pieTracks[sequence] + pieTrack.finish() + + def __pieHit(self, entry): + if not entry.hasSurfacePoint() or not entry.hasInto(): + return + if not entry.getInto().isTangible(): + return + sequence = int(entry.getFromNodePath().getNetTag('pieSequence')) + self.__finishPieTrack(sequence) + if self.splatTracks.has_key(sequence): + splatTrack = self.splatTracks[sequence] + del self.splatTracks[sequence] + splatTrack.finish() + pieCode = 0 + pieCodeStr = entry.getIntoNodePath().getNetTag('pieCode') + if pieCodeStr: + pieCode = int(pieCodeStr) + pos = entry.getSurfacePoint(render) + timestamp32 = globalClockDelta.getFrameNetworkTime(bits=32) + self.sendUpdate('pieSplat', [pos[0], + pos[1], + pos[2], + sequence, + pieCode, + timestamp32]) + splat = self.getPieSplatInterval(pos[0], pos[1], pos[2], pieCode) + splat = Sequence(splat, Func(self.pieFinishedSplatting, sequence)) + self.splatTracks[sequence] = splat + splat.start() + messenger.send('pieSplat', [self, pieCode]) + messenger.send('localPieSplat', [pieCode, entry]) + + def beginAllowPies(self): + self.allowPies = 1 + self.updatePieButton() + + def endAllowPies(self): + self.allowPies = 0 + self.updatePieButton() + + def makePiePowerMeter(self): + from direct.gui.DirectGui import DirectWaitBar, DGG + if self.__piePowerMeter == None: + self.__piePowerMeter = DirectWaitBar(frameSize=(-0.2, + 0.2, + -0.03, + 0.03), relief=DGG.SUNKEN, borderWidth=(0.005, 0.005), barColor=(0.4, 0.6, 1.0, 1), pos=(0, 0.1, 0.8)) + self.__piePowerMeter.hide() + return + + def updatePieButton(self): + from toontown.toonbase import ToontownBattleGlobals + from direct.gui.DirectGui import DirectButton, DGG + wantButton = 0 + if self.allowPies and self.numPies > 0: + wantButton = 1 + if not launcher.getPhaseComplete(5): + wantButton = 0 + haveButton = self.__pieButton != None + if not haveButton and not wantButton: + return + if haveButton and not wantButton: + self.__pieButton.destroy() + self.__pieButton = None + self.__pieButtonType = None + self.__pieButtonCount = None + return + if self.__pieButtonType != self.pieType: + if self.__pieButton: + self.__pieButton.destroy() + self.__pieButton = None + if self.__pieButton == None: + inv = self.inventory + if self.pieType >= len(inv.invModels[ToontownBattleGlobals.THROW_TRACK]): + gui = loader.loadModel('phase_3.5/models/gui/stickerbook_gui') + pieGui = gui.find('**/summons') + pieScale = 0.1 + else: + gui = None + pieGui = (inv.invModels[ToontownBattleGlobals.THROW_TRACK][self.pieType],) + pieScale = 0.85 + self.__pieButton = DirectButton(image=(inv.upButton, inv.downButton, inv.rolloverButton), geom=pieGui, text='50', text_scale=0.04, text_align=TextNode.ARight, geom_scale=pieScale, geom_pos=(-0.01, 0, 0), text_fg=Vec4(1, 1, 1, 1), text_pos=(0.07, -0.04), relief=None, image_color=(0, 0.6, 1, 1), pos=(0, 0.1, 0.9)) + self.__pieButton.bind(DGG.B1PRESS, self.__beginTossPieMouse) + self.__pieButton.bind(DGG.B1RELEASE, self.__endTossPieMouse) + self.__pieButtonType = self.pieType + self.__pieButtonCount = None + if gui: + del gui + if self.__pieButtonCount != self.numPies: + if self.numPies == ToontownGlobals.FullPies: + self.__pieButton['text'] = '' + else: + self.__pieButton['text'] = str(self.numPies) + self.__pieButtonCount = self.numPies + return + + def displayWhisper(self, fromId, chatString, whisperType): + sender = None + sfx = self.soundWhisper + if fromId == TTLocalizer.Clarabelle: + chatString = TTLocalizer.Clarabelle + ': ' + chatString + sfx = self.soundPhoneRing + elif fromId != 0: + sender = base.cr.identifyAvatar(fromId) + if whisperType == WhisperPopup.WTNormal or whisperType == WhisperPopup.WTQuickTalker: + if sender == None: + return + chatString = sender.getName() + ': ' + chatString + elif whisperType == WhisperPopup.WTSystem: + sfx = self.soundSystemMessage + whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), whisperType) + if sender != None: + whisper.setClickable(sender.getName(), fromId) + whisper.manage(base.marginManager) + base.playSfx(sfx) + return + + def displaySystemClickableWhisper(self, fromId, chatString, whisperType): + sender = None + sfx = self.soundWhisper + if fromId == TTLocalizer.Clarabelle: + chatString = TTLocalizer.Clarabelle + ': ' + chatString + sfx = self.soundPhoneRing + elif fromId != 0: + sender = base.cr.identifyAvatar(fromId) + if whisperType == WhisperPopup.WTNormal or whisperType == WhisperPopup.WTQuickTalker: + if sender == None: + return + chatString = sender.getName() + ': ' + chatString + elif whisperType == WhisperPopup.WTSystem: + sfx = self.soundSystemMessage + whisper = WhisperPopup(chatString, OTPGlobals.getInterfaceFont(), whisperType) + whisper.setClickable('', fromId) + whisper.manage(base.marginManager) + base.playSfx(sfx) + return + + def clickedWhisper(self, doId, isPlayer = None): + if doId > 0: + LocalAvatar.LocalAvatar.clickedWhisper(self, doId, isPlayer) + else: + foundCanStart = False + for partyInfo in self.hostedParties: + if partyInfo.status == PartyGlobals.PartyStatus.CanStart: + foundCanStart = True + break + + if base.cr and base.cr.playGame and base.cr.playGame.getPlace() and base.cr.playGame.getPlace().fsm: + fsm = base.cr.playGame.getPlace().fsm + curState = fsm.getCurrentState().getName() + if curState == 'walk': + if hasattr(self, 'eventsPage'): + desiredMode = -1 + if doId == -1: + desiredMode = EventsPage.EventsPage_Invited + elif foundCanStart: + desiredMode = EventsPage.EventsPage_Host + if desiredMode >= 0: + self.book.setPage(self.eventsPage) + self.eventsPage.setMode(desiredMode) + fsm.request('stickerBook') + + def loadFurnitureGui(self): + if self.__furnitureGui: + return + guiModels = loader.loadModel('phase_5.5/models/gui/house_design_gui') + self.__furnitureGui = DirectFrame(relief=None, pos=(-1.19, 0.0, 0.33), scale=0.04, image=guiModels.find('**/attic')) + DirectLabel(parent=self.__furnitureGui, relief=None, image=guiModels.find('**/rooftile')) + bMoveStartUp = guiModels.find('**/bu_attic/bu_attic_up') + bMoveStartDown = guiModels.find('**/bu_attic/bu_attic_down') + bMoveStartRollover = guiModels.find('**/bu_attic/bu_attic_rollover') + DirectButton(parent=self.__furnitureGui, relief=None, image=[bMoveStartUp, + bMoveStartDown, + bMoveStartRollover, + bMoveStartUp], text=['', TTLocalizer.HDMoveFurnitureButton, TTLocalizer.HDMoveFurnitureButton], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), pos=(-0.3, 0, 9.4), command=self.__startMoveFurniture) + self.__furnitureGui.hide() + guiModels.removeNode() + return + + def showFurnitureGui(self): + self.loadFurnitureGui() + self.__furnitureGui.show() + + def hideFurnitureGui(self): + if self.__furnitureGui: + self.__furnitureGui.hide() + + def clarabelleNewsPageCollision(self, show = True): + if self.__clarabelleButton == None: + return + claraXPos = ClaraBaseXPos + notifyXPos = CatalogNotifyDialog.CatalogNotifyBaseXPos + if show: + claraXPos += AdjustmentForNewsButton + notifyXPos += AdjustmentForNewsButton + newPos = (claraXPos - 0.1, 1.0, 0.45) + self.__clarabelleButton.setPos(newPos) + if self.__catalogNotifyDialog == None or self.__catalogNotifyDialog.frame == None: + return + notifyPos = self.__catalogNotifyDialog.frame.getPos() + notifyPos[0] = notifyXPos + self.__catalogNotifyDialog.frame.setPos(notifyPos) + return + + def loadClarabelleGui(self): + if self.__clarabelleButton: + return + guiItems = loader.loadModel('phase_5.5/models/gui/catalog_gui') + circle = guiItems.find('**/cover/blue_circle') + icon = guiItems.find('**/cover/clarabelle') + icon.reparentTo(circle) + rgba = VBase4(0.71589, 0.784547, 0.974, 1.0) + white = VBase4(1.0, 1.0, 1.0, 1.0) + icon.setColor(white) + claraXPos = ClaraBaseXPos + newScale = oldScale = 0.5 + newPos = (claraXPos, 1.0, 0.37) + if WantNewsPage: + claraXPos += AdjustmentForNewsButton + oldPos = ((claraXPos, 1.0, 0.37),) + newScale = oldScale * ToontownGlobals.NewsPageScaleAdjust + newPos = (claraXPos - 0.1, 1.0, 0.45) + self.__clarabelleButton = DirectButton(relief=None, image=circle, text='', text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(-1.06, 1.06), text_font=ToontownGlobals.getInterfaceFont(), pos=newPos, scale=newScale, command=self.__handleClarabelleButton) + self.__clarabelleButton.reparentTo(aspect2d, DGG.BACKGROUND_SORT_INDEX - 1) + button = self.__clarabelleButton.stateNodePath[0] + self.__clarabelleFlash = Sequence(LerpColorInterval(button, 2, white, blendType='easeInOut'), LerpColorInterval(button, 2, rgba, blendType='easeInOut')) + self.__clarabelleFlash.loop() + self.__clarabelleFlash.pause() + return + + def showClarabelleGui(self, mailboxItems): + self.loadClarabelleGui() + if mailboxItems: + self.__clarabelleButton['text'] = ['', TTLocalizer.CatalogNewDeliveryButton, TTLocalizer.CatalogNewDeliveryButton] + else: + self.__clarabelleButton['text'] = ['', TTLocalizer.CatalogNewCatalogButton, TTLocalizer.CatalogNewCatalogButton] + if not self.mailboxNotify and not self.awardNotify and self.catalogNotify == ToontownGlobals.OldItems and (self.simpleMailNotify != ToontownGlobals.NoItems or self.inviteMailNotify != ToontownGlobals.NoItems): + self.__clarabelleButton['text'] = ['', TTLocalizer.MailNewMailButton, TTLocalizer.MailNewMailButton] + if self.newsButtonMgr.isNewIssueButtonShown(): + self.clarabelleNewsPageCollision(True) + self.__clarabelleButton.show() + self.__clarabelleFlash.resume() + + def hideClarabelleGui(self): + if self.__clarabelleButton: + self.__clarabelleButton.hide() + self.__clarabelleFlash.pause() + + def __handleClarabelleButton(self): + self.stopMoveFurniture() + place = base.cr.playGame.getPlace() + if place == None: + self.notify.warning('Tried to go home, but place is None.') + return + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + self.__catalogNotifyDialog = None + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: VISITESTATE: Visit estate') + place.goHomeNow(self.lastHood) + return + + def __startMoveFurniture(self): + self.oldPos = self.getPos() + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: ESTATE: Furniture Placement') + if self.cr.furnitureManager != None: + self.cr.furnitureManager.d_suggestDirector(self.doId) + elif self.furnitureManager != None: + self.furnitureManager.d_suggestDirector(self.doId) + return + + def stopMoveFurniture(self): + if self.oldPos: + self.setPos(self.oldPos) + if self.furnitureManager != None: + self.furnitureManager.d_suggestDirector(0) + return + + def setFurnitureDirector(self, avId, furnitureManager): + if avId == 0: + if self.furnitureManager == furnitureManager: + messenger.send('exitFurnitureMode', [furnitureManager]) + self.furnitureManager = None + self.furnitureDirector = None + elif avId != self.doId: + if self.furnitureManager == None or self.furnitureDirector != avId: + self.furnitureManager = furnitureManager + self.furnitureDirector = avId + messenger.send('enterFurnitureMode', [furnitureManager, 0]) + else: + if self.furnitureManager != None: + messenger.send('exitFurnitureMode', [self.furnitureManager]) + self.furnitureManager = None + self.furnitureManager = furnitureManager + self.furnitureDirector = avId + messenger.send('enterFurnitureMode', [furnitureManager, 1]) + self.refreshOnscreenButtons() + return + + def getAvPosStr(self): + pos = self.getPos() + hpr = self.getHpr() + serverVersion = base.cr.getServerVersion() + districtName = base.cr.getShardName(base.localAvatar.defaultShard) + if hasattr(base.cr.playGame.hood, 'loader') and hasattr(base.cr.playGame.hood.loader, 'place') and base.cr.playGame.getPlace() != None: + zoneId = base.cr.playGame.getPlace().getZoneId() + else: + zoneId = '?' + strPosCoordText = 'X: %.3f' % pos[0] + ', Y: %.3f' % pos[1] + '\nZ: %.3f' % pos[2] + ', H: %.3f' % hpr[0] + '\nZone: %s' % str(zoneId) + ', Ver: %s, ' % serverVersion + 'District: %s' % districtName + return strPosCoordText + self.refreshOnscreenButtons() + return + + def thinkPos(self): + pos = self.getPos() + hpr = self.getHpr() + serverVersion = base.cr.getServerVersion() + districtName = base.cr.getShardName(base.localAvatar.defaultShard) + if hasattr(base.cr.playGame.hood, 'loader') and hasattr(base.cr.playGame.hood.loader, 'place') and base.cr.playGame.getPlace() != None: + zoneId = base.cr.playGame.getPlace().getZoneId() + else: + zoneId = '?' + strPos = '(%.3f' % pos[0] + '\n %.3f' % pos[1] + '\n %.3f)' % pos[2] + '\nH: %.3f' % hpr[0] + '\nZone: %s' % str(zoneId) + ',\nVer: %s, ' % serverVersion + '\nDistrict: %s' % districtName + print 'Current position=', strPos.replace('\n', ', ') + self.setChatAbsolute(strPos, CFThought | CFTimeout) + return + + def __placeMarker(self): + pos = self.getPos() + hpr = self.getHpr() + chest = loader.loadModel('phase_4/models/props/coffin') + chest.reparentTo(render) + chest.setColor(1, 0, 0, 1) + chest.setPosHpr(pos, hpr) + chest.setScale(0.5) + + def setFriendsListButtonActive(self, active): + self.friendsListButtonActive = active + self.refreshOnscreenButtons() + + def obscureFriendsListButton(self, increment): + self.friendsListButtonObscured += increment + self.refreshOnscreenButtons() + + def obscureMoveFurnitureButton(self, increment): + self.moveFurnitureButtonObscured += increment + self.refreshOnscreenButtons() + + def obscureClarabelleButton(self, increment): + self.clarabelleButtonObscured += increment + self.refreshOnscreenButtons() + + def refreshOnscreenButtons(self): + self.bFriendsList.hide() + self.hideFurnitureGui() + self.hideClarabelleGui() + clarabelleHidden = 1 + self.ignore(ToontownGlobals.FriendsListHotkey) + if self.friendsListButtonActive and self.friendsListButtonObscured <= 0: + self.bFriendsList.show() + self.accept(ToontownGlobals.FriendsListHotkey, self.sendFriendsListEvent) + if self.clarabelleButtonObscured <= 0 and self.isTeleportAllowed(): + if self.catalogNotify == ToontownGlobals.NewItems or self.mailboxNotify == ToontownGlobals.NewItems or self.simpleMailNotify == ToontownGlobals.NewItems or self.inviteMailNotify == ToontownGlobals.NewItems or self.awardNotify == ToontownGlobals.NewItems: + showClarabelle = not launcher or launcher.getPhaseComplete(5.5) + for quest in self.quests: + if quest[0] in Quests.PreClarabelleQuestIds and self.mailboxNotify != ToontownGlobals.NewItems and self.awardNotify != ToontownGlobals.NewItems: + showClarabelle = 0 + + if base.cr.playGame.getPlace().getState() == 'stickerBook': + showClarabelle = 0 + if showClarabelle: + newItemsInMailbox = self.mailboxNotify == ToontownGlobals.NewItems or self.awardNotify == ToontownGlobals.NewItems + self.showClarabelleGui(newItemsInMailbox) + clarabelleHidden = 0 + if clarabelleHidden: + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + self.__catalogNotifyDialog = None + else: + self.newCatalogNotify() + if self.moveFurnitureButtonObscured <= 0: + if self.furnitureManager != None and self.furnitureDirector == self.doId: + self.loadFurnitureGui() + self.__furnitureGui.setPos(-1.16, 0, -0.03) + self.__furnitureGui.setScale(0.06) + elif self.cr.furnitureManager != None: + self.showFurnitureGui() + taskMgr.remove('lerpFurnitureButton') + self.__furnitureGui.lerpPosHprScale(pos=Point3(-1.19, 0.0, 0.33), hpr=Vec3(0.0, 0.0, 0.0), scale=Vec3(0.04, 0.04, 0.04), time=1.0, blendType='easeInOut', task='lerpFurnitureButton') + if hasattr(self, 'inEstate') and self.inEstate: + self.loadGardeningGui() + self.hideGardeningGui() + else: + self.hideGardeningGui() + return + + def setGhostMode(self, flag): + if flag == 2: + self.seeGhosts = 1 + DistributedToon.DistributedToon.setGhostMode(self, flag) + + def newCatalogNotify(self): + if not self.gotCatalogNotify: + return + hasPhase = not launcher or launcher.getPhaseComplete(5.5) + if not hasPhase: + return + if not self.friendsListButtonActive or self.friendsListButtonObscured > 0: + return + self.gotCatalogNotify = 0 + currentWeek = self.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 + message = None + if self.mailboxNotify == ToontownGlobals.NoItems: + if self.catalogNotify == ToontownGlobals.NewItems: + if self.catalogScheduleCurrentWeek == 1: + message = (TTLocalizer.CatalogNotifyFirstCatalog, TTLocalizer.CatalogNotifyInstructions) + else: + message = (TTLocalizer.CatalogNotifyNewCatalog % weekNumber,) + elif self.mailboxNotify == ToontownGlobals.NewItems: + if self.catalogNotify == ToontownGlobals.NewItems: + message = (TTLocalizer.CatalogNotifyNewCatalogNewDelivery % weekNumber,) + else: + message = (TTLocalizer.CatalogNotifyNewDelivery,) + elif self.mailboxNotify == ToontownGlobals.OldItems: + if self.catalogNotify == ToontownGlobals.NewItems: + message = (TTLocalizer.CatalogNotifyNewCatalogOldDelivery % weekNumber,) + else: + message = (TTLocalizer.CatalogNotifyOldDelivery,) + if self.awardNotify == ToontownGlobals.NoItems: + pass + elif self.awardNotify == ToontownGlobals.NewItems: + oldStr = '' + if message: + oldStr = message[0] + ' ' + oldStr += TTLocalizer.AwardNotifyNewItems + message = (oldStr,) + elif self.awardNotify == ToontownGlobals.OldItems: + oldStr = '' + if message: + oldStr = message[0] + ' ' + oldStr += TTLocalizer.AwardNotifyOldItems + message = (oldStr,) + if self.simpleMailNotify == ToontownGlobals.NewItems or self.inviteMailNotify == ToontownGlobals.NewItems: + oldStr = '' + if message: + oldStr = message[0] + ' ' + oldStr += TTLocalizer.MailNotifyNewItems + message = (oldStr,) + if message == None: + return + if self.__catalogNotifyDialog: + self.__catalogNotifyDialog.cleanup() + self.__catalogNotifyDialog = CatalogNotifyDialog.CatalogNotifyDialog(message) + base.playSfx(self.soundPhoneRing) + return + + def allowHardLand(self): + retval = LocalAvatar.LocalAvatar.allowHardLand(self) + return retval and not self.isDisguised + + def setShovelGuiLevel(self, level = 0): + pass + + def setWateringCanGuiLevel(self, level = 0): + pass + + def loadGardeningGui(self): + if self.__gardeningGui: + return + gardenGuiCard = loader.loadModel('phase_5.5/models/gui/planting_gui') + self.__gardeningGui = DirectFrame(relief=None, geom=gardenGuiCard, geom_color=GlobalDialogColor, geom_scale=(0.17, 1.0, 0.3), pos=(-1.2, 0, 0.5), scale=1.0) + self.__gardeningGui.setName('gardeningFrame') + self.__gardeningGuiFake = DirectFrame(relief=None, geom=None, geom_color=GlobalDialogColor, geom_scale=(0.17, 1.0, 0.3), pos=(-1.2, 0, 0.5), scale=1.0) + self.__gardeningGuiFake.setName('gardeningFrameFake') + iconScale = 1 + iconColorWhite = Vec4(1.0, 1.0, 1.0, 1.0) + iconColorGrey = Vec4(0.7, 0.7, 0.7, 1.0) + iconColorBrown = Vec4(0.7, 0.4, 0.3, 1.0) + iconColorBlue = Vec4(0.2, 0.3, 1.0, 1.0) + shovelCardP = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_P') + shovelCardY = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_Y') + wateringCanCardP = loader.loadModel('phase_5.5/models/gui/planting_but_can_P') + wateringCanCardY = loader.loadModel('phase_5.5/models/gui/planting_but_can_Y') + backCard = loader.loadModel('phase_5.5/models/gui/planting_gui') + iconImage = None + iconModels = loader.loadModel('phase_3.5/models/gui/sos_textures') + iconGeom = iconModels.find('**/fish') + buttonText = TTLocalizer.GardeningPlant + self.shovelText = ('', + '', + buttonText, + '') + self.__shovelButtonFake = DirectLabel(parent=self.__gardeningGuiFake, relief=None, text=self.shovelText, text_align=TextNode.ALeft, text_pos=(0.0, -0.0), text_scale=0.07, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0.15, 0, 0.2), scale=0.775) + self.shovelButtonFake = self.__shovelButtonFake + self.shovelText = ('', + '', + buttonText, + '') + self.__shovelButton = DirectButton(parent=self.__gardeningGui, relief=None, text=self.shovelText, text_align=TextNode.ACenter, text_pos=(0.0, -0.0), text_scale=0.1, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=(shovelCardP, + shovelCardY, + shovelCardY, + shovelCardY), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0, 0, 0.2), scale=0.775, command=self.__shovelButtonClicked) + self.shovelButton = self.__shovelButton + iconGeom = iconModels.find('**/teleportIcon') + buttonText = TTLocalizer.GardeningWater + self.waterText = (buttonText, + buttonText, + buttonText, + '') + self.__wateringCanButtonFake = DirectLabel(parent=self.__gardeningGuiFake, relief=None, text=self.waterText, text_align=TextNode.ALeft, text_pos=(0.0, -0.0), text_scale=0.07, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0.15, 0, 0.01), scale=0.775) + self.wateringCanButtonFake = self.__wateringCanButtonFake + self.__wateringCanButton = DirectButton(parent=self.__gardeningGui, relief=None, text=self.waterText, text_align=TextNode.ACenter, text_pos=(0.0, -0.0), text_scale=0.1, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=(wateringCanCardP, + wateringCanCardY, + wateringCanCardY, + wateringCanCardY), image_scale=(0.18, 1.0, 0.36), geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(0, 0, 0.01), scale=0.775, command=self.__wateringCanButtonClicked) + self.wateringCanButton = self.__wateringCanButton + self.basketText = '%s / %s' % (self.numFlowers, self.maxFlowerBasket) + self.basketButton = DirectLabel(parent=self.__gardeningGui, relief=None, text=self.basketText, text_align=TextNode.ALeft, text_pos=(0.82, -1.4), text_scale=0.2, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=None, image_scale=iconScale, geom=None, geom_scale=iconScale, geom_color=iconColorWhite, pos=(-0.34, 0, 0.16), scale=0.3, textMayChange=1) + if hasattr(self, 'shovel'): + self.setShovelGuiLevel(self.shovel) + if hasattr(self, 'wateringCan'): + self.setWateringCanGuiLevel(self.wateringCan) + self.__shovelButton.hide() + self.__wateringCanButton.hide() + self.__shovelButtonFake.hide() + self.__wateringCanButtonFake.hide() + return + + def changeButtonText(self, button, text): + button['text'] = text + + def resetWaterText(self): + self.wateringCanButton['text'] = self.waterText + + def resetShovelText(self): + self.shovelButton['text'] = self.holdShovelText + + def showGardeningGui(self): + self.loadGardeningGui() + self.__gardeningGui.show() + base.setCellsAvailable([base.leftCells[2]], 0) + + def hideGardeningGui(self): + if self.__gardeningGui: + self.__gardeningGui.hide() + base.setCellsAvailable([base.leftCells[2]], 1) + + def showShovelButton(self, add = 0): + if add: + self.shovelButtonActiveCount += add + else: + self.showingShovel = 1 + self.notify.debug('showing shovel %s' % self.shovelButtonActiveCount) + self.__gardeningGui.show() + self.__shovelButton.show() + + def hideShovelButton(self, deduct = 0): + self.shovelButtonActiveCount -= deduct + if deduct == 0: + self.showingShovel = 0 + if self.shovelButtonActiveCount < 1: + self.shovelButtonActiveCount = 0 + if self.showingShovel == 0: + self.__shovelButton.hide() + self.handleAllGardeningButtonsHidden() + self.notify.debug('hiding shovel %s' % self.shovelButtonActiveCount) + + def showWateringCanButton(self, add = 0): + if add: + self.wateringCanButtonActiveCount += add + else: + self.showingWateringCan = 1 + self.__gardeningGui.show() + self.__wateringCanButton.show() + self.basketButton.show() + + def hideWateringCanButton(self, deduct = 0): + self.wateringCanButtonActiveCount -= deduct + if deduct == 0: + self.showingWateringCan = 0 + if self.wateringCanButtonActiveCount < 1: + wateringCanButtonActiveCount = 0 + if self.showingWateringCan == 0: + self.__wateringCanButton.hide() + self.handleAllGardeningButtonsHidden() + + def showWateringCanButtonFake(self, add = 0): + self.__wateringCanButtonFake.show() + + def hideWateringCanButtonFake(self, deduct = 0): + self.__wateringCanButtonFake.hide() + + def showShovelButtonFake(self, add = 0): + self.__shovelButtonFake.show() + + def hideShovelButtonFake(self, deduct = 0): + self.__shovelButtonFake.hide() + + def levelWater(self, change = 1): + if change < 0: + return + self.showWateringCanButtonFake(1) + if change < 1: + changeString = TTLocalizer.GardeningNoSkill + else: + changeString = '+%s %s' % (change, TTLocalizer.GardeningWaterSkill) + self.waterTrack = Sequence(Wait(0.0), Func(self.changeButtonText, self.wateringCanButtonFake, changeString), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node=self), Wait(1.0), Func(self.hideWateringCanButtonFake, 1)) + self.waterTrack.start() + + def levelShovel(self, change = 1): + if change < 1: + return + self.showShovelButtonFake(1) + if change < 1: + changeString = TTLocalizer.GardeningNoSkill + else: + changeString = '+%s %s' % (change, TTLocalizer.GardeningShovelSkill) + plant = base.cr.doId2do.get(self.shovelRelatedDoId) + if plant: + self.holdShovelText = plant.getShovelAction() + self.shovelTrack = Sequence(Wait(0.0), Func(self.changeButtonText, self.shovelButtonFake, changeString), SoundInterval(globalBattleSoundCache.getSound('GUI_balloon_popup.mp3'), node=self), Wait(1.0), Func(self.hideShovelButtonFake, 1)) + self.shovelTrack.start() + + def setGuiConflict(self, con): + self.guiConflict = con + + def getGuiConflict(self, con): + return self.guiConflict + + def verboseState(self): + self.lastPlaceState = 'None' + taskMgr.add(self.__expressState, 'expressState', extraArgs=[]) + + def __expressState(self, task = None): + place = base.cr.playGame.getPlace() + if place: + state = place.fsm.getCurrentState() + if state.getName() != self.lastPlaceState: + print 'Place State Change From %s to %s' % (self.lastPlaceState, state.getName()) + self.lastPlaceState = state.getName() + return Task.cont + + def addShovelRelatedDoId(self, doId): + if hasattr(base.cr.playGame.getPlace(), 'detectedGardenPlotDone'): + place = base.cr.playGame.getPlace() + state = place.fsm.getCurrentState() + if state.getName() == 'stopped': + return + self.touchingPlantList.append(doId) + self.autoSetActivePlot() + + def removeShovelRelatedDoId(self, doId): + if doId in self.touchingPlantList: + self.touchingPlantList.remove(doId) + self.autoSetActivePlot() + + def autoSetActivePlot(self): + if self.guiConflict: + return + if len(self.touchingPlantList) > 0: + minDist = 10000 + minDistPlot = 0 + for plot in self.touchingPlantList: + plant = base.cr.doId2do.get(plot) + if plant: + if self.getDistance(plant) < minDist: + minDist = self.getDistance(plant) + minDistPlot = plot + else: + self.touchingPlantList.remove(plot) + + if len(self.touchingPlantList) == 0: + self.setActivePlot(None) + else: + self.setActivePlot(minDistPlot) + else: + self.setActivePlot(None) + return + + def setActivePlot(self, doId): + if not self.gardenStarted: + return + self.shovelRelatedDoId = doId + plant = base.cr.doId2do.get(doId) + if plant: + self.startStareAt(plant, Point3(0, 0, 1)) + self.__shovelButton['state'] = DGG.NORMAL + if not plant.canBePicked(): + self.hideShovelButton() + else: + self.showShovelButton() + self.setShovelAbility(TTLocalizer.GardeningPlant) + if plant.getShovelAction(): + self.setShovelAbility(plant.getShovelAction()) + if plant.getShovelAction() == TTLocalizer.GardeningPick: + if not plant.unlockPick(): + self.__shovelButton['state'] = DGG.DISABLED + self.setShovelAbility(TTLocalizer.GardeningFull) + self.notify.debug('self.shovelRelatedDoId = %d' % self.shovelRelatedDoId) + if plant.getShovelCommand(): + self.extraShovelCommand = plant.getShovelCommand() + self.__shovelButton['command'] = self.__shovelButtonClicked + if plant.canBeWatered(): + self.showWateringCanButton() + else: + self.hideWateringCanButton() + else: + self.stopStareAt() + self.shovelRelatedDoId = 0 + if self.__shovelButton: + self.__shovelButton['command'] = None + self.hideShovelButton() + self.hideWateringCanButton() + self.handleAllGardeningButtonsHidden() + if not self.inGardenAction: + if hasattr(base.cr.playGame.getPlace(), 'detectedGardenPlotDone'): + place = base.cr.playGame.getPlace() + if place: + place.detectedGardenPlotDone() + return + + def setPlantToWater(self, plantId): + import pdb + pdb.set_trace() + if self.plantToWater == None: + self.plantToWater = plantId + self.notify.debug('setting plant to water %s' % plantId) + return + + def clearPlantToWater(self, plantId): + if not hasattr(self, 'secondaryPlant'): + self.secondaryWaterPlant = None + if self.plantToWater == plantId: + self.plantToWater = None + self.hideWateringCanButton() + return + + def hasPlant(self): + if self.plantToWater != None: + return 1 + else: + return 0 + return + + def handleAllGardeningButtonsHidden(self): + somethingVisible = False + if not self.__shovelButton.isHidden(): + somethingVisible = True + if not self.__wateringCanButton.isHidden(): + somethingVisible = True + if not somethingVisible: + self.hideGardeningGui() + + def setShovelAbility(self, ability): + self.shovelAbility = ability + if self.__shovelButton: + self.__shovelButton['text'] = ability + + def setFlowerBasket(self, speciesList, varietyList): + DistributedToon.DistributedToon.setFlowerBasket(self, speciesList, varietyList) + self.numFlowers = len(self.flowerBasket.flowerList) + self.maxFlowerBasket + if hasattr(self, 'basketButton'): + self.basketText = '%s / %s' % (self.numFlowers, self.maxFlowerBasket) + self.basketButton['text'] = self.basketText + + def setShovelSkill(self, skillLevel): + if hasattr(self, 'shovelSkill') and hasattr(self, 'shovelButton'): + if self.shovelSkill != None: + self.levelShovel(skillLevel - self.shovelSkill) + oldShovelSkill = self.shovelSkill + DistributedToon.DistributedToon.setShovelSkill(self, skillLevel) + if hasattr(self, 'shovel'): + oldShovelPower = GardenGlobals.getShovelPower(self.shovel, oldShovelSkill) + newShovelPower = GardenGlobals.getShovelPower(self.shovel, self.shovelSkill) + almostMaxedSkill = GardenGlobals.ShovelAttributes[GardenGlobals.MAX_SHOVELS - 1]['skillPts'] - 2 + if skillLevel >= GardenGlobals.ShovelAttributes[self.shovel]['skillPts']: + self.promoteShovel() + elif oldShovelSkill and oldShovelPower < newShovelPower: + self.promoteShovelSkill(self.shovel, self.shovelSkill) + elif oldShovelSkill == almostMaxedSkill and newShovelPower == GardenGlobals.getNumberOfShovelBoxes(): + self.promoteShovelSkill(self.shovel, self.shovelSkill) + return + + def setWateringCanSkill(self, skillLevel): + skillDelta = skillLevel - self.wateringCanSkill + if skillDelta or 1: + if hasattr(self, 'wateringCanSkill') and hasattr(self, 'wateringCanButton'): + if self.wateringCanSkill != None: + self.levelWater(skillDelta) + DistributedToon.DistributedToon.setWateringCanSkill(self, skillLevel) + if hasattr(self, 'wateringCan'): + if skillLevel >= GardenGlobals.WateringCanAttributes[self.wateringCan]['skillPts']: + self.promoteWateringCan() + return + + def unlockGardeningButtons(self, task = None): + if hasattr(self, '_LocalToon__shovelButton'): + try: + self.__shovelButton['state'] = DGG.NORMAL + except TypeError: + self.notify.warning('Could not unlock the shovel button- Type Error') + + if hasattr(self, '_LocalToon__wateringCanButton'): + try: + self.__wateringCanButton['state'] = DGG.NORMAL + except TypeError: + self.notify.warning('Could not unlock the watering can button - Type Error') + + taskMgr.remove('unlockGardenButtons') + return None + + def lockGardeningButtons(self, task = None): + if hasattr(self, '_LocalToon__shovelButton'): + try: + self.__shovelButton['state'] = DGG.DISABLED + except TypeError: + self.notify.warning('Could not lock the shovel button- Type Error') + + if hasattr(self, '_LocalToon__wateringCanButton'): + try: + self.__wateringCanButton['state'] = DGG.DISABLED + except TypeError: + self.notify.warning('Could not lock the watering can button - Type Error') + + self.accept('endPlantInteraction', self.__handleEndPlantInteraction) + return None + + def reactivateShovel(self, task = None): + if hasattr(self, '_LocalToon__shovelButton'): + self.__shovelButton['state'] = DGG.NORMAL + taskMgr.remove('reactShovel') + return None + + def reactivateWater(self, task = None): + if hasattr(self, '_LocalToon__wateringCanButton'): + self.__wateringCanButton['state'] = DGG.NORMAL + taskMgr.remove('reactWater') + return None + + def handleEndPlantInteraction(self, object = None, replacement = 0): + if not replacement: + self.setInGardenAction(None, object) + self.autoSetActivePlot() + return + + def __handleEndPlantInteraction(self, task = None): + self.setInGardenAction(None) + self.autoSetActivePlot() + return + + def promoteShovelSkill(self, shovelLevel, shovelSkill): + shovelName = GardenGlobals.ShovelAttributes[shovelLevel]['name'] + shovelBeans = GardenGlobals.getShovelPower(shovelLevel, shovelSkill) + oldShovelBeans = GardenGlobals.getShovelPower(shovelLevel, shovelSkill - 1) + doPartyBall = False + message = TTLocalizer.GardenShovelSkillLevelUp % {'shovel': shovelName, + 'oldbeans': oldShovelBeans, + 'newbeans': shovelBeans} + if shovelBeans == GardenGlobals.getNumberOfShovelBoxes(): + if shovelSkill == GardenGlobals.ShovelAttributes[shovelLevel]['skillPts'] - 1: + doPartyBall = True + message = TTLocalizer.GardenShovelSkillMaxed % {'shovel': shovelName, + 'oldbeans': oldShovelBeans, + 'newbeans': shovelBeans} + messagePos = Vec2(0, 0.2) + messageScale = 0.07 + image = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_P') + imagePos = Vec3(0, 0, -0.13) + imageScale = Vec3(0.28, 0, 0.56) + if doPartyBall: + go = Fanfare.makeFanfareWithMessageImage(0, base.localAvatar, 1, message, Vec2(0, 0.2), 0.08, image, Vec3(0, 0, -0.1), Vec3(0.35, 0, 0.7), wordwrap=23) + 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(10), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go[1].remove)).start() + else: + go = Fanfare.makePanel(base.localAvatar, 1) + Fanfare.makeMessageBox(go, message, messagePos, messageScale, wordwrap=24) + Fanfare.makeImageBox(go.itemFrame, image, imagePos, imageScale) + Sequence(Func(go.show), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(10), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go.remove)).start() + + def promoteShovel(self, shovelLevel = 0): + shovelName = GardenGlobals.ShovelAttributes[shovelLevel]['name'] + shovelBeans = GardenGlobals.getShovelPower(shovelLevel, 0) + message = TTLocalizer.GardenShovelLevelUp % {'shovel': shovelName, + 'oldbeans': shovelBeans - 1, + 'newbeans': shovelBeans} + messagePos = Vec2(0, 0.2) + messageScale = 0.07 + image = loader.loadModel('phase_5.5/models/gui/planting_but_shovel_P') + imagePos = Vec3(0, 0, -0.13) + imageScale = Vec3(0.28, 0, 0.56) + go = Fanfare.makePanel(base.localAvatar, 1) + Fanfare.makeMessageBox(go, message, messagePos, messageScale, wordwrap=24) + Fanfare.makeImageBox(go.itemFrame, image, imagePos, imageScale) + Sequence(Func(go.show), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(10), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go.remove)).start() + + def promoteWateringCan(self, wateringCanlevel = 0): + message = TTLocalizer.GardenWateringCanLevelUp + ' \n' + GardenGlobals.WateringCanAttributes[wateringCanlevel]['name'] + messagePos = Vec2(0, 0.2) + messageScale = 0.08 + image = loader.loadModel('phase_5.5/models/gui/planting_but_can_P') + imagePos = Vec3(0, 0, -0.1) + imageScale = Vec3(0.35, 0, 0.7) + if wateringCanlevel >= GardenGlobals.MAX_WATERING_CANS - 1: + go = Fanfare.makeFanfareWithMessageImage(0, base.localAvatar, 1, message, Vec2(0, 0.2), 0.08, image, Vec3(0, 0, -0.1), Vec3(0.35, 0, 0.7)) + 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(5), LerpColorScaleInterval(go[1], duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go[1].remove)).start() + else: + go = Fanfare.makePanel(base.localAvatar, 1) + Fanfare.makeMessageBox(go, message, messagePos, messageScale) + Fanfare.makeImageBox(go.itemFrame, image, imagePos, imageScale) + Sequence(Func(go.show), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 0), colorScale=Vec4(1, 1, 1, 1)), Wait(5), LerpColorScaleInterval(go, duration=0.5, startColorScale=Vec4(1, 1, 1, 1), colorScale=Vec4(1, 1, 1, 0)), Func(go.remove)).start() + + def setInGardenAction(self, actionObject, fromObject = None): + if actionObject: + self.lockGardeningButtons() + elif fromObject: + self.unlockGardeningButtons() + else: + self.unlockGardeningButtons() + self.inGardenAction = actionObject + + def __wateringCanButtonClicked(self): + self.notify.debug('wateringCanButtonClicked') + if self.inGardenAction: + return + plant = base.cr.doId2do.get(self.shovelRelatedDoId) + if plant: + if hasattr(plant, 'handleWatering'): + plant.handleWatering() + messenger.send('wakeup') + + def __shovelButtonClicked(self): + if self.inGardenAction: + return + self.notify.debug('shovelButtonClicked') + messenger.send('wakeup') + thingId = self.shovelRelatedDoId + thing = base.cr.doId2do.get(thingId) + if hasattr(self, 'extraShovelCommand'): + self.extraShovelCommand() + self.setActivePlot(thingId) + + def setShovel(self, shovelId): + DistributedToon.DistributedToon.setShovel(self, shovelId) + if self.__gardeningGui: + self.setShovelGuiLevel(shovelId) + + def setWateringCan(self, wateringCanId): + DistributedToon.DistributedToon.setWateringCan(self, wateringCanId) + if self.__gardeningGui: + self.setWateringCanGuiLevel(wateringCanId) + + def setGardenStarted(self, bStarted): + self.gardenStarted = bStarted + if self.gardenStarted and not self.gardenPage and hasattr(self, 'book'): + self.loadGardenPages() + + def b_setAnimState(self, animName, animMultiplier = 1.0, callback = None, extraArgs = []): + if self.wantStatePrint: + print 'Local Toon Anim State %s' % animName + DistributedToon.DistributedToon.b_setAnimState(self, animName, animMultiplier, callback, extraArgs) + + def swimTimeoutAction(self): + self.ignore('wakeup') + self.takeOffSuit() + base.cr.playGame.getPlace().fsm.request('final') + self.b_setAnimState('TeleportOut', 1, self.__handleSwimExitTeleport, [0]) + return Task.done + + def __handleSwimExitTeleport(self, requestStatus): + self.notify.info('closing shard...') + base.cr.gameFSM.request('closeShard', ['afkTimeout']) + + def sbFriendAdd(self, id, info): + print 'sbFriendAdd' + + def sbFriendUpdate(self, id, info): + print 'sbFriendUpdate' + + def sbFriendRemove(self, id): + print 'sbFriendRemove' + + def addGolfPage(self): + if self.hasPlayedGolf(): + if hasattr(self, 'golfPage') and self.golfPage != None: + return + if not launcher.getPhaseComplete(6): + self.acceptOnce('phaseComplete-6', self.addGolfPage) + return + self.golfPage = GolfPage.GolfPage() + self.golfPage.setAvatar(self) + self.golfPage.load() + self.book.addPage(self.golfPage, pageName=TTLocalizer.GolfPageTitle) + return + + def addEventsPage(self): + if hasattr(self, 'eventsPage') and self.eventsPage != None: + return + if not launcher.getPhaseComplete(4): + self.acceptOnce('phaseComplete-4', self.addEventsPage) + return + self.eventsPage = EventsPage.EventsPage() + self.eventsPage.load() + self.book.addPage(self.eventsPage, pageName=TTLocalizer.EventsPageName) + return + + def addNewsPage(self): + self.newsPage = NewsPage.NewsPage() + self.newsPage.load() + self.book.addPage(self.newsPage, pageName=TTLocalizer.NewsPageName) + + def addTIPPage(self): + self.tipPage = TIPPage.TIPPage() + self.tipPage.load() + self.book.addPage(self.tipPage, pageName=TTLocalizer.TIPPageTitle) + + def setPinkSlips(self, pinkSlips): + DistributedToon.DistributedToon.setPinkSlips(self, pinkSlips) + self.inventory.updateTotalPropsText() + + def getAccountDays(self): + days = 0 + defaultDays = base.cr.config.GetInt('account-days', -1) + if defaultDays >= 0: + days = defaultDays + elif hasattr(base.cr, 'accountDays'): + days = base.cr.accountDays + return days + + def hasActiveBoardingGroup(self): + if hasattr(localAvatar, 'boardingParty') and localAvatar.boardingParty: + return localAvatar.boardingParty.hasActiveGroup(localAvatar.doId) + else: + return False + + def getZoneId(self): + return self._zoneId + + def setZoneId(self, value): + if value == -1: + self.notify.error('zoneId should not be set to -1, tell Redmond') + self._zoneId = value + + zoneId = property(getZoneId, setZoneId) + + def systemWarning(self, warningText = 'Acknowledge this system message.'): + self.createSystemMsgAckGui() + self.systemMsgAckGui['text'] = warningText + self.systemMsgAckGui.show() + + def createSystemMsgAckGui(self): + if self.systemMsgAckGui == None or self.systemMsgAckGui.isEmpty(): + message = 'o' * 100 + self.systemMsgAckGui = TTDialog.TTGlobalDialog(doneEvent=self.systemMsgAckGuiDoneEvent, message=message, style=TTDialog.Acknowledge) + self.systemMsgAckGui.hide() + return + + def hideSystemMsgAckGui(self): + if self.systemMsgAckGui != None and not self.systemMsgAckGui.isEmpty(): + self.systemMsgAckGui.hide() + return + + def setSleepAutoReply(self, fromId): + av = base.cr.identifyAvatar(fromId) + if isinstance(av, DistributedToon.DistributedToon): + base.localAvatar.setSystemMessage(0, TTLocalizer.sleep_auto_reply % av.getName(), WhisperPopup.WTToontownBoardingGroup) + elif av is not None: + self.notify.warning('setSleepAutoReply from non-toon %s' % fromId) + return + + def setLastTimeReadNews(self, newTime): + self.lastTimeReadNews = newTime + + def getLastTimeReadNews(self): + return self.lastTimeReadNews + + def cheatCogdoMazeGame(self, kindOfCheat = 0): + if base.config.GetBool('allow-cogdo-maze-suit-hit-cheat'): + maze = base.cr.doFind('DistCogdoMazeGame') + if maze: + if kindOfCheat == 0: + for suitNum in maze.game.suitsById.keys(): + suit = maze.game.suitsById[suitNum] + maze.sendUpdate('requestSuitHitByGag', [suit.type, suitNum]) + + elif kindOfCheat == 1: + for joke in maze.game.pickups: + maze.sendUpdate('requestPickUp', [joke.serialNum]) + + else: + self.sendUpdate('logSuspiciousEvent', ['cheatCogdoMazeGame']) + + def isReadingNews(self): + result = False + if base.cr and base.cr.playGame and base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm') and base.cr.playGame.getPlace().fsm: + fsm = base.cr.playGame.getPlace().fsm + curState = fsm.getCurrentState().getName() + if curState == 'stickerBook' and WantNewsPage: + if hasattr(self, 'newsPage'): + if self.book.isOnPage(self.newsPage): + result = True + return result + + def doTeleportResponse(self, fromAvatar, toAvatar, avId, available, shardId, hoodId, zoneId, sendToId): + localAvatar.d_teleportResponse(avId, available, shardId, hoodId, zoneId, sendToId) + + def d_teleportResponse(self, avId, available, shardId, hoodId, zoneId, sendToId = None): + if base.config.GetBool('want-tptrack', False): + if available == 1: + self.notify.debug('sending teleportResponseToAI') + self.sendUpdate('teleportResponseToAI', [avId, + available, + shardId, + hoodId, + zoneId, + sendToId]) + else: + self.sendUpdate('teleportResponse', [avId, + available, + shardId, + hoodId, + zoneId], sendToId) + else: + DistributedPlayer.DistributedPlayer.d_teleportResponse(self, avId, available, shardId, hoodId, zoneId, sendToId) + + def startQuestMap(self): + if self.questMap: + self.questMap.start() + + def stopQuestMap(self): + if self.questMap: + self.questMap.stop() + + def _startZombieCheck(self): + pass + + def _stopZombieCheck(self): + pass diff --git a/toontown/toon/ModuleListAI.py b/toontown/toon/ModuleListAI.py new file mode 100644 index 0000000..51dd360 --- /dev/null +++ b/toontown/toon/ModuleListAI.py @@ -0,0 +1,99 @@ +import os + +class ModuleList: + serverDataFolder = simbase.config.GetString('server-data-folder', '') + + def __init__(self): + self.moduleWhitelistFilename = self.getWhitelistFilename() + self.moduleBlacklistFilename = self.getBlacklistFilename() + self.loadBlacklistFile() + self.loadWhitelistFile() + + def getWhitelistFilename(self): + result = '%s.moduleWhiteList' % self.serverDataFolder + return result + + def getBlacklistFilename(self): + result = '%s.moduleBlackList' % self.serverDataFolder + return result + + def loadBlacklistFile(self): + try: + file = open(self.moduleBlacklistFilename + '.bu', 'r') + if os.path.exists(self.moduleBlacklistFilename): + os.remove(self.moduleBlacklistFilename) + except IOError: + try: + file = open(self.moduleBlacklistFilename, 'r') + except IOError: + return set() + + file.seek(0) + moduleFile = self.loadFrom(file) + file.close() + result = self.loadFrom(moduleFile) + self.moduleBlacklist = result + return result + + def loadWhitelistFile(self): + try: + file = open(self.moduleWhitelistFilename + '.bu', 'r') + if os.path.exists(self.moduleWhitelistFilename): + os.remove(self.moduleWhitelistFilename) + except IOError: + try: + file = open(self.moduleWhitelistFilename, 'r') + except IOError: + return set() + + file.seek(0) + moduleFile = self.loadFrom(file) + file.close() + result = self.loadFrom(moduleFile) + self.moduleWhitelist = result + return result + + def loadFrom(self, file): + result = set() + try: + for module in file: + module = module.strip() + if module: + result.add(module) + + except EOFError: + pass + + return result + + def updateWhitelistFile(self): + try: + backup = self.getWhitelistFilename() + '.bu' + if os.path.exists(self.getWhitelistFilename()): + os.rename(self.getWhitelistFilename(), backup) + file = open(self.getWhitelistFilename(), 'w') + file.seek(0) + for whiteModule in self.moduleWhitelist: + file.write(whiteModule + '\n') + + file.close() + if os.path.exists(backup): + os.remove(backup) + except EnvironmentError: + self.notify.warning(str(sys.exc_info()[1])) + + def updateBlacklistFile(self): + try: + backup = self.getBlacklistFilename() + '.bu' + if os.path.exists(self.getBlacklistFilename()): + os.rename(self.getBlacklistFilename(), backup) + file = open(self.getBlacklistFilename(), 'w') + file.seek(0) + for blackModule in self.moduleBlacklist: + file.write(blackModule + '\n') + + file.close() + if os.path.exists(backup): + os.remove(backup) + except EnvironmentError: + self.notify.warning(str(sys.exc_info()[1])) diff --git a/toontown/toon/Motion.py b/toontown/toon/Motion.py new file mode 100644 index 0000000..32f2bfe --- /dev/null +++ b/toontown/toon/Motion.py @@ -0,0 +1,110 @@ +from direct.fsm import StateData +from toontown.toonbase import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import TTEmote +from otp.avatar import Emote + +class Motion(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Motion') + + def __init__(self, toon): + self.lt = toon + self.doneEvent = 'motionDone' + StateData.StateData.__init__(self, self.doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Motion', [State.State('off', self.enterOff, self.exitOff), + State.State('neutral', self.enterNeutral, self.exitNeutral), + State.State('walk', self.enterWalk, self.exitWalk), + State.State('run', self.enterRun, self.exitRun), + State.State('sad-neutral', self.enterSadNeutral, self.exitSadNeutral), + State.State('sad-walk', self.enterSadWalk, self.exitSadWalk), + State.State('catch-neutral', self.enterCatchNeutral, self.exitCatchNeutral), + State.State('catch-run', self.enterCatchRun, self.exitCatchRun), + State.State('catch-eatneutral', self.enterCatchEatNeutral, self.exitCatchEatNeutral), + State.State('catch-eatnrun', self.enterCatchEatNRun, self.exitCatchEatNRun)], 'off', 'off') + self.fsm.enterInitialState() + + def delete(self): + del self.fsm + + def load(self): + pass + + def unload(self): + pass + + def enter(self): + self.notify.debug('enter') + + def exit(self): + self.fsm.requestFinalState() + + def enterOff(self, rate = 0): + self.notify.debug('enterOff') + + def exitOff(self): + pass + + def enterNeutral(self, rate = 0): + self.notify.debug('enterNeutral') + + def exitNeutral(self): + self.notify.debug('exitNeutral') + + def enterWalk(self, rate = 0): + self.notify.debug('enterWalk') + Emote.globalEmote.disableBody(self.lt, 'enterWalk') + + def exitWalk(self): + self.notify.debug('exitWalk') + Emote.globalEmote.releaseBody(self.lt, 'exitWalk') + + def enterRun(self, rate = 0): + self.notify.debug('enterRun') + Emote.globalEmote.disableBody(self.lt, 'enterRun') + + def exitRun(self): + self.notify.debug('exitRun') + Emote.globalEmote.releaseBody(self.lt, 'exitRun') + + def enterSadNeutral(self, rate = 0): + self.notify.debug('enterSadNeutral') + + def exitSadNeutral(self): + self.notify.debug('exitSadNeutral') + + def enterSadWalk(self, rate = 0): + self.notify.debug('enterSadWalk') + + def exitSadWalk(self): + pass + + def enterCatchNeutral(self, rate = 0): + self.notify.debug('enterCatchNeutral') + + def exitCatchNeutral(self): + self.notify.debug('exitCatchNeutral') + + def enterCatchRun(self, rate = 0): + self.notify.debug('enterCatchRun') + + def exitCatchRun(self): + self.notify.debug('exitCatchRun') + + def enterCatchEatNeutral(self, rate = 0): + self.notify.debug('enterCatchEatNeutral') + + def exitCatchEatNeutral(self): + self.notify.debug('exitCatchEatNeutral') + + def enterCatchEatNRun(self, rate = 0): + self.notify.debug('enterCatchEatNRun') + + def exitCatchEatNRun(self): + self.notify.debug('exitCatchEatNRun') + + def setState(self, anim, rate): + toon = self.lt + if toon.playingAnim != anim: + self.fsm.request(anim, [rate]) diff --git a/toontown/toon/NPCForceAcknowledge.py b/toontown/toon/NPCForceAcknowledge.py new file mode 100644 index 0000000..76baa2c --- /dev/null +++ b/toontown/toon/NPCForceAcknowledge.py @@ -0,0 +1,52 @@ +from pandac.PandaModules import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer +from direct.gui import DirectLabel +from toontown.quest import Quests + +class NPCForceAcknowledge: + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.dialog = None + return + + def enter(self): + doneStatus = {} + questHistory = base.localAvatar.getQuestHistory() + imgScale = 0.5 + if questHistory != [] and questHistory != [1000] and questHistory != [101, 110]: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + elif len(base.localAvatar.quests) > 1 or len(base.localAvatar.quests) == 0: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + elif base.localAvatar.quests[0][0] != Quests.TROLLEY_QUEST_ID: + doneStatus['mode'] = 'complete' + messenger.send(self.doneEvent, [doneStatus]) + else: + base.localAvatar.b_setAnimState('neutral', 1) + doneStatus['mode'] = 'incomplete' + self.doneStatus = doneStatus + imageModel = loader.loadModel('phase_4/models/gui/tfa_images') + if Quests.avatarHasTrolleyQuest(base.localAvatar): + if base.localAvatar.quests[0][4] != 0: + imgNodePath = imageModel.find('**/hq-dialog-image') + imgPos = (0, 0, -0.02) + msg = TTLocalizer.NPCForceAcknowledgeMessage2 + else: + imgNodePath = imageModel.find('**/trolley-dialog-image') + imgPos = (0, 0, 0.04) + msg = TTLocalizer.NPCForceAcknowledgeMessage + self.dialog = TTDialog.TTDialog(text=msg, command=self.handleOk, style=TTDialog.Acknowledge) + imgLabel = DirectLabel.DirectLabel(parent=self.dialog, relief=None, pos=imgPos, scale=TTLocalizer.NPCFimgLabel, image=imgNodePath, image_scale=imgScale) + return + + def exit(self): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return + + def handleOk(self, value): + messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/toon/NPCFriendPanel.py b/toontown/toon/NPCFriendPanel.py new file mode 100644 index 0000000..59eafe5 --- /dev/null +++ b/toontown/toon/NPCFriendPanel.py @@ -0,0 +1,238 @@ +from direct.gui.DirectGui import * +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import * +import NPCToons +import ToonHead +import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import ToontownBattleGlobals + +class NPCFriendPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('NPCFriendPanel') + + def __init__(self, parent = aspect2d, **kw): + optiondefs = (('relief', None, None), ('doneEvent', None, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent=parent) + self.cardList = [] + self.updateLayout() + self.initialiseoptions(NPCFriendPanel) + self.accept(localAvatar.uniqueName('maxNPCFriendsChange'), self.updateLayout) + return None + + def update(self, friendDict, fCallable = 0): + friendList = friendDict.keys() + for i in range(self.maxNPCFriends): + card = self.cardList[i] + try: + NPCID = friendList[i] + count = friendDict[NPCID] + except IndexError: + NPCID = None + count = 0 + + card.update(NPCID, count, fCallable) + + return + + def updateLayout(self): + for card in self.cardList: + card.destroy() + + self.cardList = [] + self.maxNPCFriends = localAvatar.getMaxNPCFriends() + rotateCard = False + if self.maxNPCFriends == 8: + rotateCard = True + xOffset = -5.25 + yOffset = 2.3 + yOffset2 = -4.7 + elif self.maxNPCFriends == 16: + xOffset = -5.2 + yOffset = 3.5 + yOffset2 = -2.45 + else: + self.notify.error('got wrong max SOS cards %s' % self.maxNPCFriends) + count = 0 + for i in range(self.maxNPCFriends): + card = NPCFriendCard(parent=self, rotateCard=rotateCard, doneEvent=self['doneEvent']) + self.cardList.append(card) + card.setPos(xOffset, 1, yOffset) + card.setScale(0.75) + xOffset += 3.5 + count += 1 + if count % 4 == 0: + xOffset = -5.25 + yOffset += yOffset2 + + +class NPCFriendCard(DirectFrame): + normalTextColor = (0.3, 0.25, 0.2, 1) + maxRarity = 5 + sosTracks = ToontownBattleGlobals.Tracks + ToontownBattleGlobals.NPCTracks + + def __init__(self, parent = aspect2dp, rotateCard = False, **kw): + optiondefs = (('NPCID', 'Uninitialized', None), ('relief', None, None), ('doneEvent', None, None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent=parent) + self.initialiseoptions(NPCFriendCard) + cardModel = loader.loadModel('phase_3.5/models/gui/playingCard') + self.front = DirectFrame(parent=self, relief=None, image=cardModel.find('**/card_front')) + self.front.hide() + self.back = DirectFrame(parent=self, relief=None, image=cardModel.find('**/card_back'), geom=cardModel.find('**/logo')) + callButtonPosZ = -0.9 + textWordWrap = 16.0 + textScale = 0.35 + textPosZ = 1.15 + nameScale = 0.4 + namePosZ = -0.45 + rarityScale = 0.2 + rarityPosZ = -1.2 + self.NPCHeadDim = 1.2 + self.NPCHeadPosZ = 0.45 + self.sosCountInfoPosZ = -0.9 + self.sosCountInfoScale = 0.4 + self.sosCountInfo2PosZ = -0.9 + self.sosCountInfo2Scale = 0.5 + if rotateCard: + self.front.component('image0').configure(pos=(0, 0, 0.22), hpr=(0, 0, -90), scale=1.35) + self.back.component('image0').configure(hpr=(0, 0, -90), scale=(-1.35, 1.35, 1.35)) + callButtonPosZ = -2.1 + textWordWrap = 7.0 + textScale = 0.5 + textPosZ = 2.0 + nameScale = 0.5 + namePosZ = -0.89 + rarityScale = 0.25 + rarityPosZ = -2.4 + self.NPCHeadDim = 1.8 + self.NPCHeadPosZ = 0.4 + self.sosCountInfoPosZ = -2.1 + self.sosCountInfoScale = 0.4 + self.sosCountInfo2PosZ = -2.0 + self.sosCountInfo2Scale = 0.55 + self.sosTypeInfo = DirectLabel(parent=self.front, relief=None, text='', text_font=ToontownGlobals.getMinnieFont(), text_fg=self.normalTextColor, text_scale=textScale, text_align=TextNode.ACenter, text_wordwrap=textWordWrap, pos=(0, 0, textPosZ)) + self.NPCHead = None + self.NPCName = DirectLabel(parent=self.front, relief=None, text='', text_fg=self.normalTextColor, text_scale=nameScale, text_align=TextNode.ACenter, text_wordwrap=8.0, pos=(0, 0, namePosZ)) + buttonModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + upButton = buttonModels.find('**/InventoryButtonUp') + downButton = buttonModels.find('**/InventoryButtonDown') + rolloverButton = buttonModels.find('**/InventoryButtonRollover') + self.sosCallButton = DirectButton(parent=self.front, relief=None, text=TTLocalizer.NPCCallButtonLabel, text_fg=self.normalTextColor, text_scale=0.28, text_align=TextNode.ACenter, 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), image_scale=(4.4, 1, 3.6), image_pos=Vec3(0, 0, 0.08), pos=(-1.15, 0, callButtonPosZ), scale=1.25, command=self.__chooseNPCFriend) + self.sosCallButton.hide() + self.sosCountInfo = DirectLabel(parent=self.front, relief=None, text='', text_fg=self.normalTextColor, text_scale=0.75, text_align=TextNode.ALeft, textMayChange=1, pos=(0.0, 0, -1.0)) + star = loader.loadModel('phase_3.5/models/gui/name_star') + self.rarityStars = [] + for i in range(self.maxRarity): + label = DirectLabel(parent=self.front, relief=None, image=star, image_scale=rarityScale, image_color=Vec4(0.502, 0.251, 0.251, 1.0), pos=(1.1 - i * 0.24, 0, rarityPosZ)) + label.hide() + self.rarityStars.append(label) + + return + + def __chooseNPCFriend(self): + if self['NPCID'] and self['doneEvent']: + doneStatus = {} + doneStatus['mode'] = 'NPCFriend' + doneStatus['friend'] = self['NPCID'] + messenger.send(self['doneEvent'], [doneStatus]) + + def destroy(self): + if self.NPCHead: + self.NPCHead.detachNode() + self.NPCHead.delete() + DirectFrame.destroy(self) + + def update(self, NPCID, count = 0, fCallable = 0): + oldNPCID = self['NPCID'] + self['NPCID'] = NPCID + if NPCID != oldNPCID: + if self.NPCHead: + self.NPCHead.detachNode() + self.NPCHead.delete() + if NPCID is None: + self.showBack() + return + self.front.show() + self.back.hide() + self.NPCName['text'] = TTLocalizer.NPCToonNames[NPCID] + self.NPCHead = self.createNPCToonHead(NPCID, dimension=self.NPCHeadDim) + self.NPCHead.reparentTo(self.front) + self.NPCHead.setZ(self.NPCHeadPosZ) + track, level, hp, rarity = NPCToons.getNPCTrackLevelHpRarity(NPCID) + sosText = self.sosTracks[track] + if track == ToontownBattleGlobals.NPC_RESTOCK_GAGS: + if level == -1: + sosText += ' All' + else: + sosText += ' ' + self.sosTracks[level] + sosText = TextEncoder.upper(sosText) + self.sosTypeInfo['text'] = sosText + for i in range(self.maxRarity): + if i < rarity: + self.rarityStars[i].show() + else: + self.rarityStars[i].hide() + + if fCallable: + self.sosCallButton.show() + self.sosCountInfo.setPos(-0.4, 0, self.sosCountInfoPosZ) + self.sosCountInfo['text_scale'] = self.sosCountInfoScale + self.sosCountInfo['text_align'] = TextNode.ALeft + else: + self.sosCallButton.hide() + self.sosCountInfo.setPos(0, 0, self.sosCountInfo2PosZ) + self.sosCountInfo['text_scale'] = self.sosCountInfo2Scale + self.sosCountInfo['text_align'] = TextNode.ACenter + if count > 0: + countText = TTLocalizer.NPCFriendPanelRemaining % count + self.sosCallButton['state'] = DGG.NORMAL + else: + countText = 'Unavailable' + self.sosCallButton['state'] = DGG.DISABLED + self.sosCountInfo['text'] = countText + return + + def showFront(self): + self.front.show() + self.back.hide() + + def showBack(self): + self.front.hide() + self.back.show() + + def createNPCToonHead(self, NPCID, dimension = 0.5): + NPCInfo = NPCToons.NPCToonDict[NPCID] + dnaList = NPCInfo[2] + gender = NPCInfo[3] + if dnaList == 'r': + dnaList = NPCToons.getRandomDNA(NPCID, gender) + dna = ToonDNA.ToonDNA() + dna.newToonFromProperties(*dnaList) + head = ToonHead.ToonHead() + head.setupHead(dna, forGui=1) + self.fitGeometry(head, fFlip=1, dimension=dimension) + return head + + def fitGeometry(self, geom, fFlip = 0, dimension = 0.5): + p1 = Point3() + p2 = Point3() + geom.calcTightBounds(p1, p2) + if fFlip: + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + d = p2 - p1 + biggest = max(d[0], d[2]) + s = dimension / biggest + mid = (p1 + d / 2.0) * s + geomXform = hidden.attachNewNode('geomXform') + for child in geom.getChildren(): + child.reparentTo(geomXform) + + geomXform.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], 180, 0, 0, s, s, s) + geomXform.reparentTo(geom) diff --git a/toontown/toon/NPCToons.py b/toontown/toon/NPCToons.py new file mode 100644 index 0000000..5cedefe --- /dev/null +++ b/toontown/toon/NPCToons.py @@ -0,0 +1,11769 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +import random +from toontown.hood import ZoneUtil +import ToonDNA +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +import sys, os +import string +QUEST_MOVIE_CLEAR = 0 +QUEST_MOVIE_REJECT = 1 +QUEST_MOVIE_COMPLETE = 2 +QUEST_MOVIE_INCOMPLETE = 3 +QUEST_MOVIE_ASSIGN = 4 +QUEST_MOVIE_BUSY = 5 +QUEST_MOVIE_QUEST_CHOICE = 6 +QUEST_MOVIE_QUEST_CHOICE_CANCEL = 7 +QUEST_MOVIE_TRACK_CHOICE = 8 +QUEST_MOVIE_TRACK_CHOICE_CANCEL = 9 +QUEST_MOVIE_TIMEOUT = 10 +QUEST_MOVIE_TIER_NOT_DONE = 11 +PURCHASE_MOVIE_CLEAR = 0 +PURCHASE_MOVIE_START = 1 +PURCHASE_MOVIE_START_BROWSE = 9 +PURCHASE_MOVIE_COMPLETE = 2 +PURCHASE_MOVIE_NO_MONEY = 3 +PURCHASE_MOVIE_TIMEOUT = 8 +PURCHASE_MOVIE_START_NOROOM = 10 +SELL_MOVIE_CLEAR = 0 +SELL_MOVIE_START = 1 +SELL_MOVIE_COMPLETE = 2 +SELL_MOVIE_NOFISH = 3 +SELL_MOVIE_TROPHY = 4 +SELL_MOVIE_TIMEOUT = 8 +SELL_MOVIE_PETRETURNED = 9 +SELL_MOVIE_PETADOPTED = 10 +SELL_MOVIE_PETCANCELED = 11 +PARTY_MOVIE_CLEAR = 0 +PARTY_MOVIE_START = 1 +PARTY_MOVIE_COMPLETE = 2 +PARTY_MOVIE_ALREADYHOSTING = 3 +PARTY_MOVIE_MAYBENEXTTIME = 4 +PARTY_MOVIE_ONLYPAID = 5 +PARTY_MOVIE_COMINGSOON = 6 +PARTY_MOVIE_MINCOST = 7 +PARTY_MOVIE_TIMEOUT = 8 +BLOCKER_MOVIE_CLEAR = 0 +BLOCKER_MOVIE_START = 1 +BLOCKER_MOVIE_TIMEOUT = 8 +NPC_REGULAR = 0 +NPC_CLERK = 1 +NPC_TAILOR = 2 +NPC_HQ = 3 +NPC_BLOCKER = 4 +NPC_FISHERMAN = 5 +NPC_PETCLERK = 6 +NPC_KARTCLERK = 7 +NPC_PARTYPERSON = 8 +NPC_SPECIALQUESTGIVER = 9 +NPC_FLIPPYTOONHALL = 10 +NPC_SCIENTIST = 11 +CLERK_COUNTDOWN_TIME = 120 +TAILOR_COUNTDOWN_TIME = 300 +RTDNAFile = '/RTDNAFile.txt' +saveDNA = False + +def getRandomDNA(seed, gender): + randomDNA = ToonDNA.ToonDNA() + randomDNA.newToonRandom(seed, gender, 1) + return randomDNA.asTuple() + + +def createNPC(air, npcId, desc, zoneId, posIndex = 0, questCallback = None): + import DistributedNPCToonAI + import DistributedNPCClerkAI + import DistributedNPCTailorAI + import DistributedNPCBlockerAI + import DistributedNPCFishermanAI + import DistributedNPCPetclerkAI + import DistributedNPCKartClerkAI + import DistributedNPCPartyPersonAI + import DistributedNPCSpecialQuestGiverAI + import DistributedNPCFlippyInToonHallAI + import DistributedNPCScientistAI + canonicalZoneId, name, dnaType, gender, protected, type = desc + if type == NPC_REGULAR: + npc = DistributedNPCToonAI.DistributedNPCToonAI(air, npcId, questCallback=questCallback) + elif type == NPC_HQ: + npc = DistributedNPCToonAI.DistributedNPCToonAI(air, npcId, questCallback=questCallback, hq=1) + elif type == NPC_CLERK: + npc = DistributedNPCClerkAI.DistributedNPCClerkAI(air, npcId) + elif type == NPC_TAILOR: + npc = DistributedNPCTailorAI.DistributedNPCTailorAI(air, npcId) + elif type == NPC_BLOCKER: + npc = DistributedNPCBlockerAI.DistributedNPCBlockerAI(air, npcId) + elif type == NPC_FISHERMAN: + npc = DistributedNPCFishermanAI.DistributedNPCFishermanAI(air, npcId) + elif type == NPC_PETCLERK: + npc = DistributedNPCPetclerkAI.DistributedNPCPetclerkAI(air, npcId) + elif type == NPC_KARTCLERK: + npc = DistributedNPCKartClerkAI.DistributedNPCKartClerkAI(air, npcId) + elif type == NPC_PARTYPERSON: + npc = DistributedNPCPartyPersonAI.DistributedNPCPartyPersonAI(air, npcId) + elif type == NPC_SPECIALQUESTGIVER: + npc = DistributedNPCSpecialQuestGiverAI.DistributedNPCSpecialQuestGiverAI(air, npcId) + elif type == NPC_FLIPPYTOONHALL: + npc = DistributedNPCFlippyInToonHallAI.DistributedNPCFlippyInToonHallAI(air, npcId) + elif type == NPC_SCIENTIST: + npc = DistributedNPCScientistAI.DistributedNPCScientistAI(air, npcId) + else: + print 'createNPC() error!!!' + npc.setName(name) + dna = ToonDNA.ToonDNA() + if dnaType == 'r': + dnaList = getRandomDNA(npcId, gender) + else: + dnaList = dnaType + if saveDNA: + strList = [] + strList.append('\n\nNPC Id: ') + strList.append(str(npcId)) + strList.append('\nDNA: ') + count = 0 + strList.append('(') + for item in dnaList: + if count < 4: + strList.append('"' + str(item) + '"') + else: + strList.append(str(item)) + count += 1 + strList.append(' ,') + + strList.append(')') + rtDNA = ''.join(strList) + if os.path.isfile(RTDNAFile): + rtDnaFile = open(RTDNAFile, 'r+') + rtDnaFile.seek(0, 2) + rtDnaFile.writelines(rtDNA) + else: + rtDnaFile = open(RTDNAFile, 'w') + rtDnaFile.writelines(rtDNA) + rtDnaFile.close() + dna.newToonFromProperties(*dnaList) + npc.setDNAString(dna.makeNetString()) + npc.setHp(15) + npc.setMaxHp(15) + npc.setPositionIndex(posIndex) + npc.generateWithRequired(zoneId) + if hasattr(npc, 'startAnimState'): + npc.d_setAnimState(npc.startAnimState, 1.0) + else: + npc.d_setAnimState('neutral', 1.0) + return npc + + +def createNpcsInZone(air, zoneId): + npcs = [] + canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) + npcIdList = zone2NpcDict.get(canonicalZoneId, []) + for i in range(len(npcIdList)): + npcId = npcIdList[i] + npcDesc = NPCToonDict.get(npcId) + npcs.append(createNPC(air, npcId, npcDesc, zoneId, posIndex=i)) + + return npcs + + +def createLocalNPC(npcId): + import Toon + if not NPCToonDict.has_key(npcId): + return None + desc = NPCToonDict[npcId] + canonicalZoneId, name, dnaType, gender, protected, type = desc + npc = Toon.Toon() + npc.setName(name) + npc.setPickable(0) + npc.setPlayerType(NametagGroup.CCNonPlayer) + dna = ToonDNA.ToonDNA() + if dnaType == 'r': + dnaList = getRandomDNA(npcId, gender) + else: + dnaList = dnaType + dna.newToonFromProperties(*dnaList) + npc.setDNAString(dna.makeNetString()) + npc.animFSM.request('neutral') + return npc + + +def isZoneProtected(zoneId): + npcs = [] + npcIdList = zone2NpcDict.get(zoneId, []) + for npcId in npcIdList: + npcDesc = NPCToonDict.get(npcId) + if npcDesc[4]: + return 1 + + return 0 + + +lnames = TTLocalizer.NPCToonNames +NPCToonDict = {20000: (-1, + lnames[20000], + ('dll', + 'ms', + 'm', + 'm', + 7, + 0, + 7, + 7, + 2, + 6, + 2, + 6, + 2, + 16), + 'm', + 1, + NPC_REGULAR), + 999: (-1, + lnames[999], + 'r', + 'm', + 1, + NPC_TAILOR), + 1000: (-1, + lnames[1000], + 'r', + 'm', + 1, + NPC_HQ), + 20001: (-1, + lnames[20001], + ('dss', + 'ms', + 'm', + 'm', + 17, + 0, + 17, + 17, + 3, + 3, + 3, + 3, + 7, + 2), + 'm', + 1, + NPC_BLOCKER), + 2002: (2514, + lnames[2002], + ('hss', + 'ls', + 'l', + 'm', + 4, + 0, + 4, + 4, + 0, + 3, + 0, + 3, + 1, + 18), + 'm', + 1, + NPC_REGULAR), + 2003: (2516, + lnames[2003], + ('cll', + 'ms', + 'l', + 'm', + 18, + 0, + 18, + 18, + 0, + 4, + 0, + 4, + 1, + 15), + 'm', + 1, + NPC_REGULAR), + 2004: (2521, + lnames[2004], + ('rll', + 'md', + 'm', + 'f', + 15, + 0, + 5, + 7, + 3, + 5, + 3, + 5, + 0, + 3), + 'f', + 1, + NPC_TAILOR), + 2005: (2518, + lnames[2005], + ('cls', + 'ls', + 'l', + 'm', + 4, + 0, + 4, + 4, + 0, + 4, + 0, + 4, + 1, + 9), + 'm', + 1, + NPC_REGULAR), + 2006: (2519, + lnames[2006], + ('dsl', + 'ls', + 'l', + 'm', + 18, + 0, + 18, + 18, + 1, + 4, + 1, + 4, + 1, + 2), + 'm', + 1, + NPC_CLERK), + 2011: (2519, + lnames[2011], + ('rll', + 'ms', + 'l', + 'f', + 2, + 0, + 2, + 2, + 1, + 9, + 1, + 9, + 23, + 27), + 'f', + 1, + NPC_CLERK), + 2007: (2520, + lnames[2007], + ('dss', + 'ms', + 'l', + 'm', + 10, + 0, + 10, + 10, + 1, + 5, + 1, + 5, + 1, + 20), + 'm', + 1, + NPC_HQ), + 2008: (2520, + lnames[2008], + ('fll', + 'ss', + 'l', + 'm', + 3, + 0, + 3, + 3, + 1, + 5, + 1, + 5, + 1, + 17), + 'm', + 1, + NPC_HQ), + 2009: (2520, + lnames[2009], + ('fsl', + 'md', + 'l', + 'f', + 18, + 0, + 18, + 18, + 1, + 8, + 1, + 8, + 11, + 27), + 'f', + 1, + NPC_HQ), + 2010: (2520, + lnames[2010], + ('fls', + 'ls', + 'l', + 'f', + 11, + 0, + 11, + 11, + 1, + 8, + 1, + 8, + 8, + 4), + 'f', + 1, + NPC_HQ), + 2012: (2000, + lnames[2012], + ('rss', + 'ls', + 'l', + 'm', + 17, + 0, + 17, + 17, + 1, + 6, + 1, + 6, + 1, + 1), + 'm', + 1, + NPC_FISHERMAN), + 2013: (2522, + lnames[2013], + ('rls', + 'ms', + 'l', + 'm', + 9, + 0, + 9, + 9, + 0, + 7, + 0, + 7, + 1, + 19), + 'm', + 1, + NPC_PETCLERK), + 2014: (2522, + lnames[2014], + ('mls', + 'ms', + 'm', + 'f', + 2, + 0, + 2, + 2, + 0, + 12, + 0, + 12, + 1, + 0), + 'f', + 1, + NPC_PETCLERK), + 2015: (2522, + lnames[2015], + ('hsl', + 'ls', + 'm', + 'm', + 17, + 0, + 17, + 17, + 0, + 8, + 0, + 8, + 1, + 13), + 'm', + 1, + NPC_PETCLERK), + 2016: (2000, + lnames[2016], + ('sls', + 'ls', + 'm', + 'm', + 10, + 0, + 9, + 9, + 0, + 3, + 0, + 3, + 0, + 18), + 'm', + 1, + NPC_PARTYPERSON), + 2017: (2000, + lnames[2017], + ('sss', + 'ld', + 'm', + 'f', + 10, + 0, + 9, + 9, + 0, + 23, + 0, + 23, + 0, + 5), + 'f', + 1, + NPC_PARTYPERSON), + 2018: (2513, + lnames[2019], + ('fll', + 'ss', + 's', + 'm', + 15, + 0, + 15, + 15, + 99, + 27, + 86, + 27, + 39, + 27), + 'm', + 1, + NPC_SCIENTIST), + 2019: (2513, + lnames[2018], + ('pls', + 'ls', + 'l', + 'm', + 9, + 0, + 9, + 9, + 98, + 27, + 86, + 27, + 38, + 27), + 'm', + 1, + NPC_SCIENTIST), + 2020: (2513, + lnames[2020], + ('hss', + 'ms', + 'm', + 'm', + 20, + 0, + 20, + 20, + 97, + 27, + 86, + 27, + 37, + 27), + 'm', + 1, + NPC_SCIENTIST), + 2101: (2601, + lnames[2101], + ('rll', + 'ms', + 'l', + 'm', + 15, + 0, + 15, + 15, + 0, + 9, + 0, + 9, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 2102: (2619, + lnames[2102], + 'r', + 'f', + 0, + NPC_REGULAR), + 2103: (2616, + lnames[2103], + ('csl', + 'ss', + 's', + 'm', + 9, + 0, + 8, + 5, + 0, + 11, + 0, + 11, + 2, + 10), + 'm', + 0, + NPC_REGULAR), + 2104: (2671, + lnames[2104], + ('mls', + 'ms', + 'm', + 'm', + 15, + 0, + 15, + 15, + 1, + 10, + 1, + 10, + 0, + 16), + 'm', + 1, + NPC_HQ), + 2105: (2671, + lnames[2105], + ('hsl', + 'ss', + 'm', + 'm', + 7, + 0, + 7, + 7, + 1, + 10, + 1, + 10, + 0, + 13), + 'm', + 1, + NPC_HQ), + 2106: (2671, + lnames[2106], + ('hss', + 'ld', + 'm', + 'f', + 23, + 0, + 23, + 23, + 1, + 23, + 1, + 23, + 24, + 27), + 'f', + 1, + NPC_HQ), + 2107: (2671, + lnames[2107], + ('cll', + 'sd', + 'm', + 'f', + 14, + 0, + 14, + 14, + 1, + 24, + 1, + 24, + 7, + 4), + 'f', + 1, + NPC_HQ), + 2108: (2603, + lnames[2108], + ('csl', + 'ms', + 'm', + 'f', + 7, + 0, + 7, + 7, + 1, + 24, + 1, + 24, + 3, + 2), + 'f', + 0, + NPC_REGULAR), + 2109: (2604, + lnames[2109], + 'r', + 'm', + 0, + NPC_REGULAR), + 2110: (2605, + lnames[2110], + ('dll', + 'ls', + 'm', + 'm', + 14, + 0, + 14, + 14, + 0, + 27, + 0, + 27, + 0, + 15), + 'm', + 0, + NPC_REGULAR), + 2111: (2607, + lnames[2111], + 'r', + 'm', + 0, + NPC_REGULAR), + 2112: (2610, + lnames[2112], + ('fll', + 'ss', + 'm', + 'm', + 20, + 0, + 20, + 20, + 0, + 27, + 0, + 27, + 0, + 9), + 'm', + 1, + NPC_REGULAR), + 2113: (2617, + lnames[2113], + ('fsl', + 'ls', + 'm', + 'm', + 14, + 0, + 14, + 14, + 0, + 0, + 0, + 0, + 0, + 2), + 'm', + 0, + NPC_REGULAR), + 2114: (2618, + lnames[2114], + ('fls', + 'sd', + 'm', + 'f', + 6, + 0, + 6, + 6, + 0, + 0, + 0, + 0, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 2115: (2621, + lnames[2115], + ('rll', + 'ms', + 'l', + 'f', + 22, + 0, + 22, + 22, + 1, + 1, + 1, + 1, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 2116: (2624, + lnames[2116], + ('rss', + 'ls', + 'l', + 'm', + 13, + 0, + 13, + 13, + 1, + 1, + 1, + 1, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 2117: (2625, + lnames[2117], + ('rls', + 'sd', + 'l', + 'f', + 6, + 0, + 6, + 6, + 1, + 2, + 1, + 2, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 2118: (2626, + lnames[2118], + ('mls', + 'ss', + 'l', + 'm', + 20, + 0, + 20, + 20, + 1, + 1, + 1, + 1, + 1, + 6), + 'm', + 0, + NPC_REGULAR), + 2119: (2629, + lnames[2119], + ('hll', + 'ss', + 'l', + 'f', + 13, + 0, + 13, + 13, + 1, + 3, + 1, + 3, + 19, + 27), + 'f', + 1, + NPC_REGULAR), + 2120: (2632, + lnames[2120], + ('hss', + 'ls', + 'l', + 'm', + 5, + 0, + 5, + 5, + 1, + 2, + 1, + 2, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 2121: (2633, + lnames[2121], + ('cll', + 'ls', + 'l', + 'f', + 21, + 0, + 21, + 21, + 0, + 4, + 0, + 4, + 4, + 4), + 'f', + 0, + NPC_REGULAR), + 2122: (2639, + lnames[2122], + ('csl', + 'ss', + 'l', + 'm', + 13, + 0, + 13, + 13, + 0, + 3, + 0, + 3, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 2123: (2643, + lnames[2123], + ('cls', + 'md', + 'l', + 'f', + 4, + 0, + 4, + 4, + 0, + 5, + 0, + 5, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 2124: (2644, + lnames[2124], + ('dll', + 'sd', + 'l', + 'f', + 21, + 0, + 21, + 21, + 0, + 5, + 0, + 5, + 8, + 21), + 'f', + 0, + NPC_REGULAR), + 2125: (2649, + lnames[2125], + ('dss', + 'ss', + 'l', + 'm', + 12, + 0, + 12, + 12, + 0, + 4, + 0, + 4, + 1, + 0), + 'm', + 0, + NPC_REGULAR), + 2126: (2654, + lnames[2126], + ('dls', + 'ld', + 'l', + 'f', + 4, + 0, + 4, + 4, + 0, + 6, + 0, + 6, + 3, + 7), + 'f', + 1, + NPC_REGULAR), + 2127: (2655, + lnames[2127], + ('fsl', + 'ms', + 'l', + 'm', + 19, + 0, + 19, + 19, + 0, + 5, + 0, + 5, + 1, + 15), + 'm', + 1, + NPC_REGULAR), + 2128: (2656, + lnames[2128], + ('fss', + 'ss', + 'l', + 'm', + 12, + 0, + 12, + 12, + 1, + 5, + 1, + 5, + 1, + 12), + 'm', + 1, + NPC_REGULAR), + 2129: (2657, + lnames[2129], + ('rll', + 'ss', + 'l', + 'm', + 4, + 0, + 4, + 4, + 1, + 5, + 1, + 5, + 1, + 9), + 'm', + 0, + NPC_REGULAR), + 2130: (2659, + lnames[2130], + ('rss', + 'md', + 'l', + 'f', + 19, + 0, + 19, + 19, + 1, + 8, + 1, + 8, + 7, + 7), + 'f', + 0, + NPC_REGULAR), + 2131: (2660, + lnames[2131], + ('rls', + 'ls', + 'l', + 'f', + 12, + 0, + 12, + 12, + 1, + 8, + 1, + 8, + 1, + 26), + 'f', + 1, + NPC_REGULAR), + 2132: (2661, + lnames[2132], + ('mls', + 'ss', + 'l', + 'm', + 4, + 0, + 4, + 4, + 1, + 6, + 1, + 6, + 1, + 17), + 'm', + 0, + NPC_REGULAR), + 2133: (2662, + lnames[2133], + ('hll', + 'ls', + 'l', + 'm', + 18, + 0, + 18, + 18, + 1, + 6, + 1, + 6, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 2134: (2664, + lnames[2134], + 'r', + 'f', + 0, + NPC_REGULAR), + 2135: (2665, + lnames[2135], + ('hls', + 'ms', + 'l', + 'f', + 3, + 0, + 3, + 3, + 0, + 12, + 0, + 12, + 2, + 26), + 'f', + 1, + NPC_REGULAR), + 2136: (2666, + lnames[2136], + ('csl', + 'ls', + 'l', + 'm', + 18, + 0, + 18, + 18, + 0, + 8, + 0, + 8, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 2137: (2667, + lnames[2137], + ('css', + 'sd', + 'l', + 'f', + 11, + 0, + 11, + 11, + 0, + 21, + 0, + 21, + 24, + 27), + 'f', + 0, + NPC_REGULAR), + 2138: (2669, + lnames[2138], + ('dll', + 'ss', + 'l', + 'm', + 3, + 0, + 3, + 3, + 0, + 9, + 0, + 9, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 2139: (2670, + lnames[2139], + 'r', + 'm', + 0, + NPC_REGULAR), + 2140: (2156, + lnames[2140], + ('dls', + 'ls', + 'l', + 'm', + 10, + 0, + 10, + 10, + 1, + 9, + 1, + 9, + 1, + 10), + 'm', + 0, + NPC_FISHERMAN), + 2201: (2711, + lnames[2201], + ('dss', + 'ss', + 'l', + 'm', + 13, + 0, + 13, + 13, + 1, + 6, + 1, + 6, + 0, + 17), + 'm', + 1, + NPC_REGULAR), + 2202: (2718, + lnames[2202], + 'r', + 'f', + 1, + NPC_REGULAR), + 2203: (2742, + lnames[2203], + ('fss', + 'ms', + 's', + 'm', + 19, + 0, + 19, + 19, + 0, + 7, + 0, + 7, + 0, + 11), + 'm', + 1, + NPC_HQ), + 2204: (2742, + lnames[2204], + ('fls', + 'ss', + 's', + 'm', + 13, + 0, + 13, + 13, + 0, + 7, + 0, + 7, + 0, + 6), + 'm', + 1, + NPC_HQ), + 2205: (2742, + lnames[2205], + ('rsl', + 'md', + 's', + 'f', + 4, + 0, + 4, + 4, + 0, + 11, + 0, + 11, + 16, + 27), + 'f', + 1, + NPC_HQ), + 2206: (2742, + lnames[2206], + ('rss', + 'sd', + 's', + 'f', + 21, + 0, + 21, + 21, + 0, + 12, + 0, + 12, + 0, + 8), + 'f', + 1, + NPC_HQ), + 2207: (2705, + lnames[2207], + ('mss', + 'ss', + 's', + 'm', + 12, + 0, + 12, + 12, + 0, + 8, + 0, + 8, + 0, + 16), + 'm', + 1, + NPC_REGULAR), + 2208: (2708, + lnames[2208], + ('mls', + 'ls', + 's', + 'm', + 4, + 0, + 4, + 4, + 1, + 8, + 1, + 8, + 0, + 13), + 'm', + 1, + NPC_REGULAR), + 2209: (2712, + lnames[2209], + ('hsl', + 'ms', + 's', + 'm', + 19, + 0, + 19, + 19, + 1, + 8, + 1, + 8, + 0, + 10), + 'm', + 1, + NPC_REGULAR), + 2210: (2713, + lnames[2210], + ('hss', + 'ms', + 's', + 'f', + 12, + 0, + 12, + 12, + 1, + 21, + 1, + 21, + 1, + 24), + 'f', + 1, + NPC_REGULAR), + 2211: (2716, + lnames[2211], + ('cll', + 'ss', + 's', + 'f', + 3, + 0, + 3, + 3, + 1, + 22, + 1, + 22, + 25, + 27), + 'f', + 1, + NPC_REGULAR), + 2212: (2717, + lnames[2212], + ('css', + 'ls', + 's', + 'm', + 18, + 0, + 18, + 18, + 1, + 9, + 1, + 9, + 0, + 18), + 'm', + 0, + NPC_REGULAR), + 2213: (2720, + lnames[2213], + ('cls', + 'ls', + 's', + 'f', + 12, + 0, + 12, + 12, + 1, + 23, + 1, + 23, + 11, + 27), + 'f', + 1, + NPC_REGULAR), + 2214: (2723, + lnames[2214], + 'r', + 'm', + 0, + NPC_REGULAR), + 2215: (2727, + lnames[2215], + ('dss', + 'ls', + 'm', + 'm', + 18, + 0, + 18, + 18, + 0, + 11, + 0, + 11, + 0, + 9), + 'm', + 0, + NPC_REGULAR), + 2216: (2728, + lnames[2216], + ('fll', + 'sd', + 'm', + 'f', + 11, + 0, + 11, + 11, + 0, + 25, + 0, + 25, + 12, + 27), + 'f', + 0, + NPC_REGULAR), + 2217: (2729, + lnames[2217], + ('fsl', + 'ss', + 'm', + 'm', + 4, + 0, + 4, + 4, + 0, + 12, + 0, + 12, + 0, + 20), + 'm', + 1, + NPC_REGULAR), + 2218: (2730, + lnames[2218], + 'r', + 'f', + 0, + NPC_REGULAR), + 2219: (2732, + lnames[2219], + ('rll', + 'ms', + 'm', + 'm', + 10, + 0, + 10, + 10, + 0, + 27, + 0, + 27, + 0, + 14), + 'm', + 0, + NPC_REGULAR), + 2220: (2733, + lnames[2220], + ('rss', + 'ss', + 'm', + 'm', + 3, + 0, + 3, + 3, + 1, + 12, + 1, + 12, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 2221: (2734, + lnames[2221], + 'r', + 'f', + 0, + NPC_REGULAR), + 2222: (2735, + lnames[2222], + ('mls', + 'ls', + 'm', + 'm', + 10, + 0, + 10, + 10, + 1, + 0, + 1, + 0, + 0, + 1), + 'm', + 0, + NPC_REGULAR), + 2223: (2739, + lnames[2223], + 'r', + 'f', + 0, + NPC_REGULAR), + 2224: (2740, + lnames[2224], + ('hss', + 'ss', + 'm', + 'm', + 17, + 0, + 17, + 17, + 1, + 1, + 1, + 1, + 0, + 16), + 'm', + 0, + NPC_REGULAR), + 2225: (2236, + lnames[2225], + ('cll', + 'ls', + 'm', + 'm', + 9, + 0, + 9, + 9, + 1, + 1, + 1, + 1, + 0, + 13), + 'm', + 0, + NPC_FISHERMAN), + 2301: (2804, + lnames[2301], + ('cll', + 'ms', + 'm', + 'm', + 10, + 0, + 10, + 10, + 1, + 3, + 1, + 3, + 0, + 6), + 'm', + 1, + NPC_REGULAR), + 2302: (2831, + lnames[2302], + ('css', + 'ms', + 'm', + 'm', + 3, + 0, + 3, + 3, + 1, + 3, + 1, + 3, + 0, + 1), + 'm', + 1, + NPC_REGULAR), + 2303: (2834, + lnames[2303], + 'r', + 'f', + 0, + NPC_REGULAR), + 2304: (2832, + lnames[2304], + ('dss', + 'ss', + 'm', + 'm', + 9, + 0, + 9, + 9, + 0, + 10, + 0, + 10, + 1, + 12), + 'm', + 1, + NPC_HQ), + 2305: (2832, + lnames[2305], + ('dss', + 'ss', + 'm', + 'm', + 8, + 0, + 8, + 8, + 1, + 0, + 1, + 0, + 1, + 9), + 'm', + 1, + NPC_HQ), + 2306: (2832, + lnames[2306], + ('fll', + 'md', + 'm', + 'f', + 24, + 0, + 24, + 24, + 1, + 0, + 1, + 0, + 16, + 27), + 'f', + 1, + NPC_HQ), + 2307: (2832, + lnames[2307], + ('fsl', + 'ls', + 'm', + 'f', + 16, + 0, + 16, + 16, + 1, + 1, + 1, + 1, + 3, + 1), + 'f', + 1, + NPC_HQ), + 2308: (2801, + lnames[2308], + ('fls', + 'ss', + 'm', + 'f', + 8, + 0, + 8, + 8, + 1, + 1, + 1, + 1, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 2309: (2802, + lnames[2309], + ('rsl', + 'ls', + 'm', + 'm', + 22, + 0, + 22, + 22, + 1, + 1, + 1, + 1, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 2311: (2809, + lnames[2311], + ('mss', + 'ss', + 'm', + 'm', + 7, + 0, + 7, + 7, + 0, + 2, + 0, + 2, + 1, + 6), + 'm', + 1, + NPC_REGULAR), + 2312: (2837, + lnames[2312], + ('mls', + 'ld', + 'm', + 'f', + 24, + 0, + 24, + 24, + 0, + 3, + 0, + 3, + 4, + 6), + 'f', + 0, + NPC_REGULAR), + 2313: (2817, + lnames[2313], + 'r', + 'f', + 0, + NPC_REGULAR), + 2314: (2818, + lnames[2314], + ('hss', + 'ms', + 'm', + 'm', + 7, + 0, + 7, + 7, + 0, + 3, + 0, + 3, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 2315: (2822, + lnames[2315], + ('cll', + 'ss', + 'm', + 'm', + 21, + 0, + 21, + 21, + 0, + 3, + 0, + 3, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 2316: (2823, + lnames[2316], + ('csl', + 'md', + 'l', + 'f', + 15, + 0, + 15, + 15, + 0, + 5, + 0, + 5, + 0, + 23), + 'f', + 0, + NPC_REGULAR), + 2318: (2829, + lnames[2318], + ('dsl', + 'ss', + 'l', + 'm', + 21, + 0, + 21, + 21, + 1, + 4, + 1, + 4, + 1, + 0), + 'm', + 0, + NPC_REGULAR), + 2319: (2830, + lnames[2319], + ('dss', + 'ls', + 'l', + 'm', + 14, + 0, + 14, + 14, + 1, + 5, + 1, + 5, + 1, + 18), + 'm', + 0, + NPC_REGULAR), + 2320: (2839, + lnames[2320], + 'r', + 'm', + 0, + NPC_REGULAR), + 2321: (2341, + lnames[2321], + ('fsl', + 'ss', + 'l', + 'm', + 21, + 0, + 21, + 21, + 1, + 5, + 1, + 5, + 0, + 12), + 'm', + 0, + NPC_FISHERMAN), + 1001: (1506, + lnames[1001], + ('rss', + 'ms', + 'l', + 'm', + 10, + 0, + 10, + 10, + 0, + 11, + 0, + 11, + 0, + 0), + 'm', + 0, + NPC_CLERK), + 1002: (1506, + lnames[1002], + ('mss', + 'ss', + 'l', + 'm', + 3, + 0, + 3, + 3, + 1, + 10, + 1, + 10, + 0, + 18), + 'm', + 0, + NPC_CLERK), + 1003: (1507, + lnames[1003], + ('mls', + 'ss', + 'l', + 'm', + 17, + 0, + 17, + 17, + 1, + 11, + 1, + 11, + 0, + 15), + 'm', + 0, + NPC_HQ), + 1004: (1507, + lnames[1004], + ('hsl', + 'md', + 'l', + 'f', + 10, + 0, + 10, + 10, + 1, + 24, + 1, + 24, + 24, + 27), + 'f', + 0, + NPC_HQ), + 1005: (1507, + lnames[1005], + ('hss', + 'ms', + 'l', + 'm', + 3, + 0, + 3, + 3, + 1, + 11, + 1, + 11, + 0, + 9), + 'm', + 0, + NPC_HQ), + 1006: (1507, + lnames[1006], + ('cll', + 'ss', + 'l', + 'f', + 18, + 0, + 18, + 18, + 1, + 25, + 1, + 25, + 19, + 27), + 'f', + 0, + NPC_HQ), + 1007: (1508, + lnames[1007], + ('csl', + 'ls', + 'm', + 'm', + 9, + 0, + 9, + 9, + 1, + 12, + 1, + 12, + 0, + 20), + 'm', + 0, + NPC_TAILOR), + 1008: (1000, + lnames[1008], + ('cls', + 'ms', + 'm', + 'm', + 3, + 0, + 3, + 3, + 0, + 27, + 0, + 27, + 0, + 17), + 'm', + 0, + NPC_FISHERMAN), + 1009: (1510, + lnames[1009], + ('dsl', + 'ss', + 'm', + 'm', + 17, + 0, + 17, + 17, + 0, + 0, + 0, + 0, + 0, + 14), + 'm', + 0, + NPC_PETCLERK), + 1010: (1510, + lnames[1010], + ('dss', + 'ld', + 'm', + 'f', + 10, + 0, + 10, + 10, + 0, + 0, + 0, + 0, + 26, + 27), + 'f', + 0, + NPC_PETCLERK), + 1011: (1510, + lnames[1011], + ('fll', + 'sd', + 'm', + 'f', + 1, + 0, + 1, + 1, + 0, + 1, + 0, + 1, + 4, + 25), + 'f', + 0, + NPC_PETCLERK), + 1012: (1000, + lnames[1012], + ('fls', + 'ms', + 'l', + 'm', + 14, + 0, + 3, + 3, + 0, + 1, + 0, + 1, + 0, + 13), + 'm', + 1, + NPC_PARTYPERSON), + 1013: (1000, + lnames[1013], + ('fss', + 'ms', + 'm', + 'f', + 2, + 0, + 3, + 3, + 1, + 6, + 1, + 6, + 5, + 6), + 'f', + 1, + NPC_PARTYPERSON), + 1101: (1627, + lnames[1101], + ('fll', + 'ls', + 'm', + 'm', + 14, + 0, + 14, + 14, + 1, + 3, + 1, + 3, + 1, + 9), + 'm', + 0, + NPC_REGULAR), + 1102: (1612, + lnames[1102], + ('fsl', + 'ms', + 'm', + 'm', + 7, + 0, + 7, + 7, + 1, + 3, + 1, + 3, + 1, + 2), + 'm', + 0, + NPC_REGULAR), + 1103: (1626, + lnames[1103], + 'r', + 'm', + 0, + NPC_REGULAR), + 1104: (1617, + lnames[1104], + 'r', + 'm', + 0, + NPC_REGULAR), + 1105: (1606, + lnames[1105], + ('rss', + 'ms', + 'm', + 'm', + 6, + 0, + 6, + 6, + 0, + 4, + 0, + 4, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 1106: (1604, + lnames[1106], + 'r', + 'f', + 0, + NPC_REGULAR), + 1107: (1621, + lnames[1107], + 'r', + 'm', + 0, + NPC_REGULAR), + 1108: (1629, + lnames[1108], + ('hsl', + 'ls', + 'l', + 'm', + 6, + 0, + 6, + 6, + 0, + 6, + 0, + 6, + 1, + 1), + 'm', + 0, + NPC_HQ), + 1109: (1629, + lnames[1109], + ('hss', + 'ls', + 'l', + 'f', + 22, + 0, + 22, + 22, + 0, + 8, + 0, + 8, + 14, + 27), + 'f', + 0, + NPC_HQ), + 1110: (1629, + lnames[1110], + ('cll', + 'ss', + 'l', + 'm', + 13, + 0, + 13, + 13, + 1, + 6, + 1, + 6, + 1, + 16), + 'm', + 0, + NPC_HQ), + 1111: (1629, + lnames[1111], + ('csl', + 'ld', + 'l', + 'f', + 6, + 0, + 6, + 6, + 1, + 9, + 1, + 9, + 2, + 2), + 'f', + 0, + NPC_HQ), + 1112: (1602, + lnames[1112], + ('cls', + 'ms', + 'l', + 'm', + 20, + 0, + 20, + 20, + 1, + 7, + 1, + 7, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 1113: (1608, + lnames[1113], + ('dll', + 'ms', + 'l', + 'f', + 13, + 0, + 13, + 13, + 1, + 11, + 1, + 11, + 0, + 27), + 'f', + 0, + NPC_REGULAR), + 1114: (1609, + lnames[1114], + ('dss', + 'ls', + 'l', + 'm', + 5, + 0, + 5, + 5, + 1, + 7, + 1, + 7, + 1, + 0), + 'm', + 0, + NPC_REGULAR), + 1115: (1613, + lnames[1115], + ('fll', + 'sd', + 'l', + 'f', + 21, + 0, + 21, + 21, + 1, + 12, + 1, + 12, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 1116: (1614, + lnames[1116], + ('fsl', + 'ls', + 'l', + 'f', + 13, + 0, + 13, + 13, + 1, + 12, + 1, + 12, + 1, + 25), + 'f', + 0, + NPC_REGULAR), + 1117: (1615, + lnames[1117], + ('fls', + 'ss', + 'l', + 'm', + 5, + 0, + 5, + 5, + 0, + 9, + 0, + 9, + 1, + 12), + 'm', + 0, + NPC_REGULAR), + 1118: (1616, + lnames[1118], + ('rll', + 'ls', + 'l', + 'm', + 19, + 0, + 19, + 19, + 0, + 9, + 0, + 9, + 1, + 9), + 'm', + 0, + NPC_REGULAR), + 1121: (1619, + lnames[1121], + 'r', + 'f', + 0, + NPC_REGULAR), + 1122: (1620, + lnames[1122], + ('hll', + 'ms', + 'l', + 'm', + 12, + 0, + 12, + 12, + 0, + 11, + 0, + 11, + 0, + 14), + 'm', + 0, + NPC_REGULAR), + 1123: (1622, + lnames[1123], + ('hss', + 'ms', + 'l', + 'f', + 4, + 0, + 4, + 4, + 1, + 23, + 1, + 23, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 1124: (1624, + lnames[1124], + ('cll', + 'ls', + 'l', + 'm', + 19, + 0, + 19, + 19, + 1, + 11, + 1, + 11, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 1125: (1628, + lnames[1125], + ('csl', + 'sd', + 'l', + 'f', + 12, + 0, + 12, + 12, + 1, + 24, + 1, + 24, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 1126: (1129, + lnames[1126], + ('cls', + 'ms', + 'l', + 'm', + 4, + 0, + 4, + 4, + 1, + 11, + 1, + 11, + 0, + 19), + 'm', + 0, + NPC_FISHERMAN), + 1201: (1710, + lnames[1201], + ('css', + 'ls', + 's', + 'f', + 12, + 0, + 12, + 12, + 0, + 0, + 0, + 0, + 1, + 24), + 'f', + 0, + NPC_REGULAR), + 1202: (1713, + lnames[1202], + ('cls', + 'ss', + 's', + 'm', + 4, + 0, + 4, + 4, + 0, + 0, + 0, + 0, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 1203: (1725, + lnames[1203], + 'r', + 'm', + 0, + NPC_REGULAR), + 1204: (1712, + lnames[1204], + ('dss', + 'ms', + 's', + 'm', + 12, + 0, + 12, + 12, + 1, + 1, + 1, + 1, + 1, + 6), + 'm', + 0, + NPC_REGULAR), + 1205: (1729, + lnames[1205], + ('fll', + 'ss', + 's', + 'm', + 4, + 0, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1), + 'm', + 0, + NPC_HQ), + 1206: (1729, + lnames[1206], + ('fss', + 'ld', + 's', + 'f', + 19, + 0, + 19, + 19, + 1, + 2, + 1, + 2, + 7, + 11), + 'f', + 0, + NPC_HQ), + 1207: (1729, + lnames[1207], + ('fls', + 'ms', + 's', + 'm', + 12, + 0, + 12, + 12, + 1, + 2, + 1, + 2, + 1, + 16), + 'm', + 0, + NPC_HQ), + 1208: (1729, + lnames[1208], + ('rsl', + 'ls', + 'm', + 'f', + 3, + 0, + 3, + 3, + 1, + 3, + 1, + 3, + 23, + 27), + 'f', + 0, + NPC_HQ), + 1209: (1701, + lnames[1209], + ('rss', + 'ss', + 'm', + 'f', + 19, + 0, + 19, + 19, + 0, + 4, + 0, + 4, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 1210: (1703, + lnames[1210], + 'r', + 'm', + 0, + NPC_REGULAR), + 1211: (1705, + lnames[1211], + ('mls', + 'ms', + 'm', + 'm', + 4, + 0, + 4, + 4, + 0, + 4, + 0, + 4, + 1, + 0), + 'm', + 0, + NPC_REGULAR), + 1212: (1706, + lnames[1212], + ('hsl', + 'ss', + 'm', + 'm', + 18, + 0, + 18, + 18, + 0, + 4, + 0, + 4, + 1, + 18), + 'm', + 0, + NPC_REGULAR), + 1213: (1707, + lnames[1213], + ('hss', + 'ls', + 'm', + 'm', + 10, + 0, + 10, + 10, + 0, + 4, + 0, + 4, + 1, + 15), + 'm', + 0, + NPC_REGULAR), + 1214: (1709, + lnames[1214], + ('cll', + 'sd', + 'm', + 'f', + 2, + 0, + 2, + 2, + 0, + 7, + 0, + 7, + 1, + 12), + 'f', + 0, + NPC_REGULAR), + 1215: (1711, + lnames[1215], + ('css', + 'ms', + 'm', + 'f', + 18, + 0, + 18, + 18, + 0, + 7, + 0, + 7, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 1216: (1714, + lnames[1216], + ('cls', + 'ls', + 'm', + 'm', + 10, + 0, + 10, + 10, + 1, + 5, + 1, + 5, + 1, + 2), + 'm', + 0, + NPC_REGULAR), + 1217: (1716, + lnames[1217], + 'r', + 'f', + 0, + NPC_REGULAR), + 1218: (1717, + lnames[1218], + ('dss', + 'ms', + 'm', + 'm', + 17, + 0, + 17, + 17, + 1, + 6, + 1, + 6, + 1, + 17), + 'm', + 0, + NPC_REGULAR), + 1219: (1718, + lnames[1219], + ('fll', + 'ss', + 'm', + 'm', + 9, + 0, + 9, + 9, + 1, + 6, + 1, + 6, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 1220: (1719, + lnames[1220], + ('fsl', + 'md', + 'm', + 'f', + 2, + 0, + 2, + 2, + 1, + 9, + 1, + 9, + 7, + 23), + 'f', + 0, + NPC_REGULAR), + 1221: (1720, + lnames[1221], + 'r', + 'm', + 0, + NPC_REGULAR), + 1222: (1721, + lnames[1222], + 'r', + 'm', + 0, + NPC_REGULAR), + 1223: (1723, + lnames[1223], + ('rss', + 'ls', + 'l', + 'm', + 2, + 0, + 2, + 2, + 0, + 8, + 0, + 8, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 1224: (1724, + lnames[1224], + ('mss', + 'sd', + 'l', + 'f', + 17, + 0, + 17, + 17, + 0, + 21, + 0, + 21, + 7, + 9), + 'f', + 0, + NPC_REGULAR), + 1225: (1726, + lnames[1225], + ('mls', + 'ss', + 'l', + 'm', + 9, + 0, + 9, + 9, + 0, + 9, + 0, + 9, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 1226: (1727, + lnames[1226], + ('hsl', + 'ls', + 'l', + 'm', + 2, + 0, + 2, + 2, + 0, + 9, + 0, + 9, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 1227: (1728, + lnames[1227], + ('hss', + 'sd', + 'l', + 'f', + 17, + 0, + 17, + 17, + 0, + 22, + 0, + 22, + 3, + 7), + 'f', + 0, + NPC_REGULAR), + 1228: (1236, + lnames[1228], + ('cll', + 'ms', + 'l', + 'm', + 8, + 0, + 8, + 8, + 1, + 9, + 1, + 9, + 1, + 0), + 'm', + 0, + NPC_FISHERMAN), + 1301: (1828, + lnames[1301], + ('mls', + 'md', + 'm', + 'f', + 16, + 0, + 16, + 16, + 1, + 8, + 1, + 8, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 1302: (1832, + lnames[1302], + ('hsl', + 'ms', + 'm', + 'm', + 8, + 0, + 8, + 8, + 1, + 6, + 1, + 6, + 0, + 18), + 'm', + 0, + NPC_REGULAR), + 1303: (1826, + lnames[1303], + ('hls', + 'ss', + 'm', + 'm', + 22, + 0, + 22, + 22, + 1, + 6, + 1, + 6, + 0, + 15), + 'm', + 0, + NPC_REGULAR), + 1304: (1804, + lnames[1304], + ('cll', + 'md', + 'm', + 'f', + 15, + 0, + 15, + 15, + 1, + 9, + 1, + 9, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 1305: (1835, + lnames[1305], + ('css', + 'ms', + 'm', + 'm', + 7, + 0, + 7, + 7, + 1, + 7, + 1, + 7, + 0, + 9), + 'm', + 0, + NPC_HQ), + 1306: (1835, + lnames[1306], + ('cls', + 'ms', + 'm', + 'f', + 24, + 0, + 24, + 24, + 0, + 12, + 0, + 12, + 0, + 7), + 'f', + 0, + NPC_HQ), + 1307: (1835, + lnames[1307], + ('dsl', + 'ls', + 'm', + 'm', + 15, + 0, + 15, + 15, + 0, + 8, + 0, + 8, + 0, + 20), + 'm', + 0, + NPC_HQ), + 1308: (1835, + lnames[1308], + ('dss', + 'sd', + 'm', + 'f', + 8, + 0, + 8, + 8, + 0, + 21, + 0, + 21, + 4, + 5), + 'f', + 0, + NPC_HQ), + 1309: (1802, + lnames[1309], + ('fll', + 'ms', + 'l', + 'f', + 23, + 0, + 23, + 23, + 0, + 21, + 0, + 21, + 1, + 12), + 'f', + 0, + NPC_REGULAR), + 1310: (1805, + lnames[1310], + ('fsl', + 'ss', + 'l', + 'm', + 15, + 0, + 15, + 15, + 0, + 9, + 0, + 9, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 1311: (1806, + lnames[1311], + 'r', + 'f', + 0, + NPC_REGULAR), + 1312: (1807, + lnames[1312], + ('rsl', + 'ms', + 'l', + 'm', + 21, + 0, + 21, + 21, + 1, + 9, + 1, + 9, + 0, + 1), + 'm', + 0, + NPC_REGULAR), + 1313: (1808, + lnames[1313], + ('rss', + 'ss', + 'l', + 'm', + 14, + 0, + 14, + 14, + 1, + 10, + 1, + 10, + 0, + 19), + 'm', + 0, + NPC_REGULAR), + 1314: (1809, + lnames[1314], + ('mss', + 'ls', + 'l', + 'm', + 6, + 0, + 6, + 6, + 1, + 10, + 1, + 10, + 0, + 16), + 'm', + 0, + NPC_REGULAR), + 1315: (1810, + lnames[1315], + 'r', + 'f', + 0, + NPC_REGULAR), + 1316: (1811, + lnames[1316], + ('hsl', + 'ms', + 'l', + 'f', + 14, + 0, + 14, + 14, + 1, + 24, + 1, + 24, + 7, + 7), + 'f', + 0, + NPC_REGULAR), + 1317: (1813, + lnames[1317], + ('hss', + 'ld', + 'l', + 'f', + 7, + 0, + 7, + 7, + 1, + 25, + 1, + 25, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 1318: (1814, + lnames[1318], + ('cll', + 'ms', + 'l', + 'm', + 20, + 0, + 20, + 20, + 0, + 12, + 0, + 12, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 1319: (1815, + lnames[1319], + ('csl', + 'ss', + 'l', + 'm', + 14, + 0, + 14, + 14, + 0, + 27, + 0, + 27, + 0, + 18), + 'm', + 0, + NPC_REGULAR), + 1320: (1818, + lnames[1320], + 'r', + 'm', + 0, + NPC_REGULAR), + 1321: (1819, + lnames[1321], + ('dsl', + 'md', + 'l', + 'f', + 22, + 0, + 22, + 22, + 0, + 27, + 0, + 27, + 7, + 5), + 'f', + 0, + NPC_REGULAR), + 1322: (1820, + lnames[1322], + ('dss', + 'ls', + 'm', + 'f', + 13, + 0, + 13, + 13, + 0, + 0, + 0, + 0, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 1323: (1821, + lnames[1323], + ('fll', + 'ss', + 'm', + 'm', + 6, + 0, + 6, + 6, + 0, + 0, + 0, + 0, + 1, + 2), + 'm', + 0, + NPC_REGULAR), + 1324: (1823, + lnames[1324], + ('fsl', + 'md', + 'm', + 'f', + 22, + 0, + 22, + 22, + 0, + 1, + 0, + 1, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 1325: (1824, + lnames[1325], + 'r', + 'm', + 0, + NPC_REGULAR), + 1326: (1825, + lnames[1326], + ('rll', + 'ms', + 'm', + 'f', + 6, + 0, + 6, + 6, + 1, + 2, + 1, + 2, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 1327: (1829, + lnames[1327], + 'r', + 'f', + 0, + NPC_REGULAR), + 1328: (1830, + lnames[1328], + ('rls', + 'ms', + 'm', + 'm', + 13, + 0, + 13, + 13, + 1, + 2, + 1, + 2, + 1, + 6), + 'm', + 0, + NPC_REGULAR), + 1329: (1831, + lnames[1329], + ('mls', + 'ms', + 'm', + 'f', + 4, + 0, + 4, + 4, + 1, + 3, + 1, + 3, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 1330: (1833, + lnames[1330], + ('hsl', + 'ls', + 'm', + 'm', + 19, + 0, + 19, + 19, + 1, + 3, + 1, + 3, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 1331: (1834, + lnames[1331], + ('hss', + 'ls', + 'm', + 'm', + 12, + 0, + 12, + 12, + 0, + 3, + 0, + 3, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 1332: (1330, + lnames[1332], + ('cll', + 'ms', + 'm', + 'm', + 5, + 0, + 5, + 5, + 0, + 4, + 0, + 4, + 1, + 13), + 'm', + 0, + NPC_FISHERMAN), + 3001: (3506, + lnames[3001], + 'r', + 'f', + 0, + NPC_REGULAR), + 3002: (3508, + lnames[3002], + ('cls', + 'ms', + 'm', + 'm', + 4, + 0, + 4, + 4, + 1, + 9, + 1, + 9, + 0, + 18), + 'm', + 0, + NPC_HQ), + 3003: (3508, + lnames[3003], + ('dsl', + 'ss', + 'm', + 'f', + 19, + 0, + 19, + 19, + 1, + 22, + 1, + 22, + 25, + 27), + 'f', + 0, + NPC_HQ), + 3004: (3508, + lnames[3004], + ('dss', + 'ls', + 'm', + 'm', + 12, + 0, + 12, + 12, + 1, + 10, + 1, + 10, + 0, + 12), + 'm', + 0, + NPC_HQ), + 3005: (3508, + lnames[3005], + ('fll', + 'ms', + 'm', + 'm', + 4, + 0, + 4, + 4, + 0, + 11, + 0, + 11, + 0, + 9), + 'm', + 0, + NPC_HQ), + 3006: (3507, + lnames[3006], + ('fsl', + 'ss', + 'm', + 'm', + 18, + 0, + 18, + 18, + 0, + 11, + 0, + 11, + 0, + 2), + 'm', + 0, + NPC_CLERK), + 3007: (3507, + lnames[3007], + ('fls', + 'ld', + 'm', + 'f', + 12, + 0, + 12, + 12, + 0, + 25, + 0, + 25, + 8, + 5), + 'f', + 0, + NPC_CLERK), + 3008: (3509, + lnames[3008], + ('rll', + 'ms', + 'l', + 'm', + 4, + 0, + 4, + 4, + 0, + 12, + 0, + 12, + 0, + 17), + 'm', + 0, + NPC_TAILOR), + 3009: (3000, + lnames[3009], + ('rss', + 'ls', + 'l', + 'f', + 19, + 0, + 19, + 19, + 0, + 26, + 0, + 26, + 4, + 23), + 'f', + 0, + NPC_FISHERMAN), + 3010: (3511, + lnames[3010], + ('rls', + 'ss', + 'l', + 'm', + 10, + 0, + 10, + 10, + 0, + 12, + 0, + 12, + 0, + 11), + 'm', + 0, + NPC_PETCLERK), + 3011: (3511, + lnames[3011], + ('mls', + 'md', + 'l', + 'f', + 3, + 0, + 3, + 3, + 1, + 26, + 1, + 26, + 26, + 27), + 'f', + 0, + NPC_PETCLERK), + 3012: (3511, + lnames[3012], + ('hsl', + 'ms', + 'l', + 'm', + 18, + 0, + 18, + 18, + 1, + 12, + 1, + 12, + 0, + 1), + 'm', + 0, + NPC_PETCLERK), + 3013: (3000, + lnames[3013], + ('cls', + 'ss', + 'm', + 'm', + 18, + 0, + 17, + 17, + 1, + 7, + 1, + 7, + 1, + 9), + 'm', + 1, + NPC_PARTYPERSON), + 3014: (3000, + lnames[3014], + ('css', + 'sd', + 'm', + 'f', + 17, + 0, + 16, + 16, + 0, + 24, + 0, + 24, + 0, + 9), + 'f', + 1, + NPC_PARTYPERSON), + 3101: (3611, + lnames[3101], + ('mls', + 'ls', + 'l', + 'm', + 16, + 0, + 16, + 16, + 1, + 1, + 1, + 1, + 1, + 6), + 'm', + 0, + NPC_REGULAR), + 3102: (3625, + lnames[3102], + 'r', + 'f', + 0, + NPC_REGULAR), + 3103: (3641, + lnames[3103], + 'r', + 'm', + 0, + NPC_REGULAR), + 3104: (3602, + lnames[3104], + ('cll', + 'ss', + 'l', + 'f', + 16, + 0, + 16, + 16, + 0, + 4, + 0, + 4, + 3, + 2), + 'f', + 0, + NPC_REGULAR), + 3105: (3651, + lnames[3105], + 'r', + 'm', + 0, + NPC_REGULAR), + 3106: (3636, + lnames[3106], + ('fll', + 'ls', + 'l', + 'm', + 8, + 2, + 8, + 8, + 10, + 27, + 0, + 27, + 7, + 11), + 'm', + 0, + NPC_REGULAR), + 3107: (3630, + lnames[3107], + ('dll', + 'ms', + 'l', + 'f', + 15, + 0, + 15, + 15, + 0, + 5, + 0, + 5, + 4, + 4), + 'f', + 0, + NPC_REGULAR), + 3108: (3638, + lnames[3108], + 'r', + 'm', + 0, + NPC_REGULAR), + 3109: (3637, + lnames[3109], + ('fll', + 'sd', + 'm', + 'f', + 23, + 0, + 23, + 23, + 1, + 6, + 1, + 6, + 12, + 27), + 'f', + 0, + NPC_REGULAR), + 3110: (3629, + lnames[3110], + ('fss', + 'ms', + 'l', + 'm', + 10, + 10, + 10, + 10, + 16, + 4, + 0, + 4, + 5, + 4), + 'm', + 0, + NPC_REGULAR), + 3111: (3627, + lnames[3111], + ('dsl', + 'ls', + 's', + 'm', + 6, + 0, + 6, + 6, + 14, + 27, + 10, + 27, + 1, + 14), + 'm', + 1, + NPC_REGULAR), + 3112: (3607, + lnames[3112], + ('rll', + 'ls', + 'm', + 'm', + 21, + 0, + 21, + 21, + 1, + 5, + 1, + 5, + 1, + 9), + 'm', + 0, + NPC_REGULAR), + 3113: (3618, + lnames[3113], + ('rss', + 'ms', + 'm', + 'm', + 14, + 0, + 14, + 14, + 1, + 5, + 1, + 5, + 0, + 2), + 'm', + 0, + NPC_REGULAR), + 3114: (3620, + lnames[3114], + ('rls', + 'ss', + 'm', + 'm', + 7, + 0, + 7, + 7, + 0, + 6, + 0, + 6, + 0, + 20), + 'm', + 0, + NPC_REGULAR), + 3115: (3654, + lnames[3115], + ('mls', + 'ls', + 'm', + 'm', + 21, + 0, + 21, + 21, + 0, + 7, + 0, + 7, + 0, + 17), + 'm', + 0, + NPC_HQ), + 3116: (3654, + lnames[3116], + ('hll', + 'ls', + 'm', + 'f', + 14, + 0, + 14, + 14, + 0, + 11, + 0, + 11, + 0, + 12), + 'f', + 0, + NPC_HQ), + 3117: (3654, + lnames[3117], + ('hss', + 'ss', + 'm', + 'm', + 6, + 0, + 6, + 6, + 0, + 7, + 0, + 7, + 0, + 11), + 'm', + 0, + NPC_HQ), + 3118: (3654, + lnames[3118], + ('cll', + 'ls', + 'm', + 'm', + 20, + 0, + 20, + 20, + 0, + 8, + 0, + 8, + 0, + 6), + 'm', + 0, + NPC_HQ), + 3119: (3653, + lnames[3119], + ('csl', + 'ms', + 'm', + 'm', + 14, + 0, + 14, + 14, + 0, + 8, + 0, + 8, + 0, + 1), + 'm', + 0, + NPC_REGULAR), + 3120: (3610, + lnames[3120], + ('cls', + 'ss', + 'm', + 'm', + 6, + 0, + 6, + 6, + 1, + 8, + 1, + 8, + 0, + 19), + 'm', + 0, + NPC_REGULAR), + 3121: (3601, + lnames[3121], + ('dll', + 'ls', + 'm', + 'm', + 20, + 0, + 20, + 20, + 1, + 8, + 1, + 8, + 0, + 16), + 'm', + 0, + NPC_REGULAR), + 3122: (3608, + lnames[3122], + ('dss', + 'md', + 'l', + 'f', + 13, + 0, + 13, + 13, + 1, + 21, + 1, + 21, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 3123: (3612, + lnames[3123], + ('dls', + 'ms', + 'l', + 'm', + 6, + 0, + 6, + 6, + 1, + 9, + 1, + 9, + 0, + 10), + 'm', + 0, + NPC_REGULAR), + 3124: (3613, + lnames[3124], + ('fsl', + 'ss', + 'l', + 'm', + 20, + 0, + 20, + 20, + 1, + 9, + 1, + 9, + 0, + 4), + 'm', + 0, + NPC_REGULAR), + 3125: (3614, + lnames[3125], + ('fls', + 'ls', + 'l', + 'm', + 13, + 0, + 13, + 13, + 1, + 9, + 1, + 9, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 3126: (3615, + lnames[3126], + ('rll', + 'ls', + 'l', + 'f', + 6, + 0, + 6, + 6, + 0, + 24, + 0, + 24, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 3127: (3617, + lnames[3127], + ('rss', + 'ms', + 'l', + 'f', + 21, + 0, + 21, + 21, + 0, + 24, + 0, + 24, + 19, + 27), + 'f', + 0, + NPC_REGULAR), + 3128: (3621, + lnames[3128], + ('rls', + 'ls', + 'l', + 'm', + 13, + 0, + 13, + 13, + 0, + 11, + 0, + 11, + 0, + 12), + 'm', + 0, + NPC_REGULAR), + 3129: (3623, + lnames[3129], + ('mls', + 'sd', + 'l', + 'f', + 4, + 0, + 4, + 4, + 0, + 25, + 0, + 25, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 3130: (3624, + lnames[3130], + ('hll', + 'ms', + 'l', + 'f', + 21, + 0, + 21, + 21, + 0, + 26, + 0, + 26, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 3131: (3634, + lnames[3131], + ('hss', + 'ls', + 'l', + 'm', + 12, + 0, + 12, + 12, + 0, + 12, + 0, + 12, + 0, + 20), + 'm', + 0, + NPC_REGULAR), + 3132: (3635, + lnames[3132], + 'r', + 'f', + 0, + NPC_REGULAR), + 3133: (3642, + lnames[3133], + ('csl', + 'ms', + 'l', + 'm', + 19, + 0, + 19, + 19, + 1, + 12, + 1, + 12, + 0, + 14), + 'm', + 0, + NPC_REGULAR), + 3134: (3643, + lnames[3134], + ('cls', + 'ss', + 'l', + 'm', + 12, + 0, + 12, + 12, + 1, + 0, + 1, + 0, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 3135: (3644, + lnames[3135], + ('dll', + 'md', + 'l', + 'f', + 4, + 0, + 4, + 4, + 1, + 0, + 1, + 0, + 4, + 12), + 'f', + 0, + NPC_REGULAR), + 3136: (3647, + lnames[3136], + ('dss', + 'ls', + 'l', + 'f', + 19, + 0, + 19, + 19, + 1, + 1, + 1, + 1, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 3137: (3648, + lnames[3137], + ('dls', + 'ss', + 'l', + 'm', + 12, + 0, + 12, + 12, + 1, + 1, + 1, + 1, + 0, + 19), + 'm', + 0, + NPC_REGULAR), + 3138: (3649, + lnames[3138], + ('fsl', + 'ld', + 'l', + 'f', + 3, + 0, + 3, + 3, + 1, + 2, + 1, + 2, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 3139: (3650, + lnames[3139], + ('fss', + 'sd', + 'l', + 'f', + 19, + 0, + 19, + 19, + 0, + 2, + 0, + 2, + 16, + 27), + 'f', + 0, + NPC_REGULAR), + 3140: (3136, + lnames[3140], + ('rll', + 'ms', + 'l', + 'f', + 11, + 0, + 11, + 11, + 0, + 3, + 0, + 3, + 12, + 27), + 'f', + 0, + NPC_FISHERMAN), + 3201: (3715, + lnames[3201], + 'r', + 'f', + 0, + NPC_REGULAR), + 3202: (3723, + lnames[3202], + ('rsl', + 'ss', + 'l', + 'm', + 6, + 0, + 6, + 6, + 1, + 12, + 1, + 12, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 3203: (3712, + lnames[3203], + ('rss', + 'ls', + 'l', + 'm', + 20, + 0, + 20, + 20, + 1, + 12, + 1, + 12, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 3204: (3734, + lnames[3204], + ('mss', + 'md', + 'l', + 'f', + 13, + 0, + 13, + 13, + 1, + 26, + 1, + 26, + 4, + 5), + 'f', + 0, + NPC_REGULAR), + 3205: (3721, + lnames[3205], + 'r', + 'm', + 0, + NPC_REGULAR), + 3206: (3722, + lnames[3206], + ('hsl', + 'ss', + 'l', + 'f', + 21, + 0, + 21, + 21, + 1, + 0, + 1, + 0, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 3207: (3713, + lnames[3207], + ('hss', + 'ls', + 'l', + 'm', + 13, + 0, + 13, + 13, + 0, + 0, + 0, + 0, + 1, + 15), + 'm', + 0, + NPC_REGULAR), + 3208: (3732, + lnames[3208], + ('cll', + 'ms', + 'l', + 'm', + 5, + 0, + 5, + 5, + 0, + 1, + 0, + 1, + 1, + 12), + 'm', + 0, + NPC_REGULAR), + 3209: (3737, + lnames[3209], + ('css', + 'ss', + 'l', + 'm', + 19, + 0, + 19, + 19, + 0, + 1, + 0, + 1, + 1, + 9), + 'm', + 0, + NPC_REGULAR), + 3210: (3728, + lnames[3210], + ('pls', + 'ls', + 's', + 'm', + 13, + 0, + 13, + 13, + 2, + 1, + 2, + 1, + 5, + 2), + 'm', + 0, + NPC_REGULAR), + 3211: (3710, + lnames[3211], + 'r', + 'f', + 0, + NPC_REGULAR), + 3212: (3707, + lnames[3212], + ('dss', + 'ss', + 's', + 'm', + 19, + 0, + 19, + 19, + 0, + 2, + 0, + 2, + 1, + 17), + 'm', + 0, + NPC_REGULAR), + 3213: (3739, + lnames[3213], + ('fll', + 'ls', + 's', + 'm', + 12, + 0, + 12, + 12, + 1, + 2, + 1, + 2, + 1, + 14), + 'm', + 0, + NPC_HQ), + 3214: (3739, + lnames[3214], + ('fsl', + 'md', + 's', + 'f', + 4, + 0, + 4, + 4, + 1, + 4, + 1, + 4, + 3, + 1), + 'f', + 0, + NPC_HQ), + 3215: (3739, + lnames[3215], + ('fls', + 'ms', + 's', + 'm', + 19, + 0, + 19, + 19, + 1, + 3, + 1, + 3, + 1, + 6), + 'm', + 0, + NPC_HQ), + 3216: (3739, + lnames[3216], + ('rll', + 'ss', + 's', + 'm', + 12, + 0, + 12, + 12, + 1, + 4, + 1, + 4, + 1, + 1), + 'm', + 0, + NPC_HQ), + 3217: (3738, + lnames[3217], + ('rss', + 'ls', + 's', + 'm', + 4, + 0, + 4, + 4, + 1, + 4, + 1, + 4, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 3218: (3702, + lnames[3218], + ('mss', + 'ms', + 's', + 'm', + 18, + 0, + 18, + 18, + 1, + 4, + 1, + 4, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 3219: (3705, + lnames[3219], + ('mls', + 'ss', + 's', + 'm', + 12, + 0, + 12, + 12, + 0, + 5, + 0, + 5, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 3220: (3706, + lnames[3220], + ('hsl', + 'ls', + 's', + 'm', + 4, + 0, + 4, + 4, + 0, + 5, + 0, + 5, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 3221: (3708, + lnames[3221], + ('hss', + 'sd', + 's', + 'f', + 19, + 0, + 19, + 19, + 0, + 8, + 0, + 8, + 7, + 12), + 'f', + 0, + NPC_REGULAR), + 3222: (3716, + lnames[3222], + 'r', + 'f', + 0, + NPC_REGULAR), + 3223: (3718, + lnames[3223], + ('csl', + 'ls', + 'm', + 'm', + 4, + 0, + 4, + 4, + 0, + 6, + 0, + 6, + 1, + 18), + 'm', + 0, + NPC_REGULAR), + 3224: (3719, + lnames[3224], + ('cls', + 'md', + 'm', + 'f', + 18, + 0, + 18, + 18, + 0, + 9, + 0, + 9, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 3225: (3724, + lnames[3225], + 'r', + 'm', + 0, + NPC_REGULAR), + 3226: (3725, + lnames[3226], + ('dss', + 'ss', + 'm', + 'm', + 3, + 0, + 3, + 3, + 1, + 7, + 1, + 7, + 1, + 9), + 'm', + 0, + NPC_REGULAR), + 3227: (3726, + lnames[3227], + ('fll', + 'ls', + 'm', + 'm', + 17, + 0, + 17, + 17, + 1, + 7, + 1, + 7, + 1, + 2), + 'm', + 0, + NPC_REGULAR), + 3228: (3730, + lnames[3228], + ('fsl', + 'ls', + 'm', + 'f', + 11, + 0, + 11, + 11, + 1, + 12, + 1, + 12, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 3229: (3731, + lnames[3229], + ('fls', + 'ms', + 'm', + 'f', + 2, + 0, + 2, + 2, + 1, + 12, + 1, + 12, + 0, + 7), + 'f', + 0, + NPC_REGULAR), + 3230: (3735, + lnames[3230], + ('rll', + 'ls', + 'm', + 'm', + 17, + 0, + 17, + 17, + 1, + 8, + 1, + 8, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 3231: (3736, + lnames[3231], + ('rss', + 'ms', + 'm', + 'm', + 9, + 0, + 9, + 9, + 0, + 9, + 0, + 9, + 1, + 12), + 'm', + 0, + NPC_REGULAR), + 3232: (3236, + lnames[3232], + ('rls', + 'ss', + 'm', + 'm', + 3, + 0, + 3, + 3, + 0, + 10, + 0, + 10, + 1, + 9), + 'm', + 0, + NPC_FISHERMAN), + 3301: (3810, + lnames[3301], + ('dsl', + 'ms', + 'm', + 'f', + 11, + 0, + 11, + 11, + 0, + 22, + 0, + 22, + 2, + 11), + 'f', + 0, + NPC_REGULAR), + 3302: (3806, + lnames[3302], + ('dls', + 'ls', + 'm', + 'm', + 4, + 0, + 4, + 4, + 0, + 10, + 0, + 10, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 3303: (3830, + lnames[3303], + ('fll', + 'ms', + 'm', + 'm', + 18, + 0, + 18, + 18, + 0, + 10, + 0, + 10, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 3304: (3828, + lnames[3304], + ('pll', + 'ls', + 'l', + 'm', + 0, + 0, + 0, + 0, + 1, + 5, + 1, + 5, + 1, + 6), + 'f', + 0, + NPC_REGULAR), + 3305: (3812, + lnames[3305], + ('fls', + 'ls', + 'm', + 'm', + 3, + 0, + 3, + 3, + 0, + 11, + 0, + 11, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 3306: (3821, + lnames[3306], + ('bss', + 'sd', + 'm', + 'f', + 0, + 0, + 0, + 0, + 31, + 27, + 22, + 27, + 8, + 11), + 'f', + 0, + NPC_REGULAR), + 3307: (3329, + lnames[3307], + ('rss', + 'ls', + 'm', + 'f', + 11, + 0, + 11, + 11, + 1, + 24, + 1, + 24, + 1, + 9), + 'f', + 0, + NPC_FISHERMAN), + 3308: (3815, + lnames[3308], + ('mss', + 'ss', + 'm', + 'm', + 3, + 0, + 3, + 3, + 1, + 11, + 1, + 11, + 1, + 0), + 'm', + 0, + NPC_REGULAR), + 3309: (3826, + lnames[3309], + ('hll', + 'ls', + 'm', + 'm', + 17, + 0, + 17, + 17, + 1, + 11, + 1, + 11, + 1, + 18), + 'm', + 0, + NPC_REGULAR), + 3310: (3823, + lnames[3310], + ('pll', + 'ms', + 'm', + 'm', + 10, + 0, + 10, + 10, + 60, + 27, + 49, + 27, + 0, + 13), + 'm', + 0, + NPC_REGULAR), + 3311: (3829, + lnames[3311], + 'r', + 'f', + 0, + NPC_REGULAR), + 3312: (3813, + lnames[3312], + ('rss', + 'ms', + 'l', + 'm', + 4, + 0, + 4, + 4, + 5, + 2, + 5, + 2, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 3313: (3801, + lnames[3313], + ('css', + 'ms', + 'l', + 'm', + 9, + 0, + 9, + 9, + 0, + 0, + 0, + 0, + 1, + 2), + 'm', + 0, + NPC_HQ), + 3314: (3801, + lnames[3314], + ('cls', + 'ms', + 'l', + 'f', + 1, + 0, + 1, + 1, + 0, + 0, + 0, + 0, + 3, + 25), + 'f', + 0, + NPC_HQ), + 3315: (3801, + lnames[3315], + ('dsl', + 'ls', + 'l', + 'm', + 17, + 0, + 17, + 17, + 0, + 0, + 0, + 0, + 1, + 17), + 'm', + 0, + NPC_HQ), + 3316: (3801, + lnames[3316], + ('dss', + 'md', + 'l', + 'f', + 10, + 0, + 10, + 10, + 0, + 1, + 0, + 1, + 10, + 27), + 'f', + 0, + NPC_HQ), + 3317: (3816, + lnames[3317], + ('fll', + 'ls', + 'l', + 'f', + 1, + 0, + 1, + 1, + 0, + 2, + 0, + 2, + 3, + 24), + 'f', + 0, + NPC_REGULAR), + 3318: (3808, + lnames[3318], + ('dss', + 'ms', + 'm', + 'm', + 18, + 0, + 18, + 18, + 57, + 1, + 46, + 1, + 12, + 1), + 'm', + 0, + NPC_REGULAR), + 3319: (3825, + lnames[3319], + ('fls', + 'ls', + 'l', + 'm', + 9, + 0, + 9, + 9, + 1, + 2, + 1, + 2, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 3320: (3814, + lnames[3320], + ('rsl', + 'ls', + 'l', + 'f', + 1, + 0, + 1, + 1, + 1, + 3, + 1, + 3, + 12, + 27), + 'f', + 0, + NPC_REGULAR), + 3321: (3818, + lnames[3321], + ('rss', + 'ss', + 'l', + 'm', + 16, + 0, + 16, + 16, + 1, + 2, + 1, + 2, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 3322: (3819, + lnames[3322], + 'r', + 'm', + 0, + NPC_REGULAR), + 3323: (3811, + lnames[3323], + ('mls', + 'ms', + 'l', + 'm', + 22, + 0, + 22, + 22, + 1, + 3, + 1, + 3, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 3324: (3809, + lnames[3324], + 'r', + 'm', + 0, + NPC_REGULAR), + 3325: (3827, + lnames[3325], + ('hss', + 'ls', + 'l', + 'm', + 8, + 0, + 8, + 8, + 0, + 4, + 0, + 4, + 1, + 0), + 'm', + 0, + NPC_REGULAR), + 3326: (3820, + lnames[3326], + ('cll', + 'md', + 'l', + 'f', + 24, + 0, + 24, + 24, + 0, + 6, + 0, + 6, + 12, + 27), + 'f', + 0, + NPC_REGULAR), + 3327: (3824, + lnames[3327], + ('css', + 'ms', + 'l', + 'm', + 15, + 0, + 15, + 15, + 0, + 5, + 0, + 5, + 1, + 15), + 'm', + 0, + NPC_REGULAR), + 3328: (3807, + lnames[3328], + ('dll', + 'sd', + 'l', + 'f', + 8, + 0, + 8, + 8, + 0, + 25, + 0, + 25, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 3329: (3817, + lnames[3329], + ('dll', + 'ms', + 'l', + 'm', + 6, + 0, + 6, + 6, + 0, + 1, + 0, + 1, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 4001: (4502, + lnames[4001], + 'r', + 'f', + 0, + NPC_REGULAR), + 4002: (4504, + lnames[4002], + ('fll', + 'ss', + 'm', + 'm', + 5, + 0, + 5, + 5, + 0, + 2, + 0, + 2, + 1, + 17), + 'm', + 0, + NPC_HQ), + 4003: (4504, + lnames[4003], + ('fsl', + 'md', + 'm', + 'f', + 21, + 0, + 21, + 21, + 0, + 3, + 0, + 3, + 10, + 27), + 'f', + 0, + NPC_HQ), + 4004: (4504, + lnames[4004], + ('fls', + 'ls', + 'm', + 'f', + 13, + 0, + 13, + 13, + 1, + 3, + 1, + 3, + 2, + 11), + 'f', + 0, + NPC_HQ), + 4005: (4504, + lnames[4005], + ('rll', + 'ss', + 'm', + 'f', + 4, + 0, + 4, + 4, + 1, + 4, + 1, + 4, + 24, + 27), + 'f', + 0, + NPC_HQ), + 4006: (4503, + lnames[4006], + ('rss', + 'md', + 'm', + 'f', + 21, + 0, + 21, + 21, + 1, + 4, + 1, + 4, + 8, + 8), + 'f', + 0, + NPC_CLERK), + 4007: (4503, + lnames[4007], + ('rls', + 'ms', + 'm', + 'm', + 12, + 0, + 12, + 12, + 1, + 3, + 1, + 3, + 1, + 19), + 'm', + 0, + NPC_CLERK), + 4008: (4506, + lnames[4008], + ('mls', + 'ms', + 'm', + 'f', + 4, + 0, + 4, + 4, + 1, + 5, + 1, + 5, + 7, + 9), + 'f', + 0, + NPC_TAILOR), + 4009: (4000, + lnames[4009], + ('hsl', + 'ld', + 'm', + 'f', + 19, + 0, + 19, + 19, + 1, + 6, + 1, + 6, + 12, + 27), + 'f', + 0, + NPC_FISHERMAN), + 4010: (4508, + lnames[4010], + ('hss', + 'ms', + 'm', + 'm', + 12, + 0, + 12, + 12, + 0, + 5, + 0, + 5, + 1, + 10), + 'm', + 0, + NPC_PETCLERK), + 4011: (4508, + lnames[4011], + ('cll', + 'ss', + 'm', + 'm', + 4, + 0, + 4, + 4, + 0, + 5, + 0, + 5, + 1, + 4), + 'm', + 0, + NPC_PETCLERK), + 4012: (4508, + lnames[4012], + ('csl', + 'ss', + 'm', + 'f', + 19, + 0, + 19, + 19, + 0, + 8, + 0, + 8, + 10, + 27), + 'f', + 0, + NPC_PETCLERK), + 4013: (4000, + lnames[4013], + ('bll', + 'ls', + 's', + 'm', + 3, + 0, + 19, + 19, + 0, + 8, + 0, + 8, + 1, + 12), + 'm', + 1, + NPC_PARTYPERSON), + 4014: (4000, + lnames[4014], + ('bss', + 'md', + 'm', + 'f', + 24, + 0, + 19, + 19, + 0, + 24, + 0, + 24, + 0, + 12), + 'f', + 1, + NPC_PARTYPERSON), + 4101: (4603, + lnames[4101], + ('cll', + 'ms', + 'm', + 'm', + 16, + 0, + 16, + 16, + 1, + 7, + 1, + 7, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 4102: (4605, + lnames[4102], + ('csl', + 'ms', + 'm', + 'f', + 9, + 0, + 9, + 9, + 1, + 11, + 1, + 11, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 4103: (4612, + lnames[4103], + ('cls', + 'ls', + 'l', + 'm', + 2, + 0, + 2, + 2, + 1, + 8, + 1, + 8, + 0, + 19), + 'm', + 0, + NPC_REGULAR), + 4104: (4659, + lnames[4104], + ('dll', + 'ms', + 'l', + 'm', + 16, + 0, + 16, + 16, + 1, + 8, + 1, + 8, + 0, + 16), + 'm', + 0, + NPC_HQ), + 4105: (4659, + lnames[4105], + ('dss', + 'ls', + 'l', + 'f', + 9, + 0, + 9, + 9, + 1, + 21, + 1, + 21, + 11, + 27), + 'f', + 0, + NPC_HQ), + 4106: (4659, + lnames[4106], + ('fll', + 'ss', + 'l', + 'f', + 24, + 0, + 24, + 24, + 0, + 22, + 0, + 22, + 19, + 27), + 'f', + 0, + NPC_HQ), + 4107: (4659, + lnames[4107], + ('fsl', + 'md', + 'l', + 'f', + 16, + 0, + 16, + 16, + 0, + 22, + 0, + 22, + 17, + 27), + 'f', + 0, + NPC_HQ), + 4108: (4626, + lnames[4108], + ('fls', + 'ms', + 'l', + 'm', + 8, + 0, + 8, + 8, + 0, + 10, + 0, + 10, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 4109: (4606, + lnames[4109], + ('rll', + 'ss', + 'l', + 'm', + 22, + 0, + 22, + 22, + 0, + 11, + 0, + 11, + 0, + 18), + 'm', + 0, + NPC_REGULAR), + 4110: (4604, + lnames[4110], + ('rss', + 'ld', + 'l', + 'f', + 16, + 0, + 16, + 16, + 0, + 24, + 0, + 24, + 3, + 2), + 'f', + 0, + NPC_REGULAR), + 4111: (4607, + lnames[4111], + 'r', + 'm', + 0, + NPC_REGULAR), + 4112: (4609, + lnames[4112], + ('mls', + 'ms', + 'l', + 'f', + 24, + 0, + 24, + 24, + 0, + 25, + 0, + 25, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 4113: (4610, + lnames[4113], + ('hsl', + 'ld', + 'l', + 'f', + 15, + 0, + 15, + 15, + 1, + 25, + 1, + 25, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 4114: (4611, + lnames[4114], + ('hss', + 'ms', + 'l', + 'm', + 7, + 0, + 7, + 7, + 1, + 11, + 1, + 11, + 1, + 20), + 'm', + 0, + NPC_REGULAR), + 4115: (4614, + lnames[4115], + ('cll', + 'ls', + 'l', + 'f', + 23, + 0, + 23, + 23, + 1, + 26, + 1, + 26, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 4116: (4615, + lnames[4116], + ('csl', + 'ss', + 'm', + 'm', + 15, + 0, + 15, + 15, + 1, + 12, + 1, + 12, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 4117: (4617, + lnames[4117], + ('cls', + 'md', + 'm', + 'f', + 7, + 0, + 7, + 7, + 1, + 0, + 1, + 0, + 1, + 25), + 'f', + 0, + NPC_REGULAR), + 4118: (4618, + lnames[4118], + 'r', + 'm', + 0, + NPC_REGULAR), + 4119: (4619, + lnames[4119], + ('dss', + 'ss', + 'm', + 'm', + 14, + 0, + 14, + 14, + 0, + 0, + 0, + 0, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 4120: (4622, + lnames[4120], + ('dls', + 'ld', + 'm', + 'f', + 7, + 0, + 7, + 7, + 0, + 1, + 0, + 1, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 4121: (4623, + lnames[4121], + ('fsl', + 'ms', + 'm', + 'm', + 21, + 0, + 21, + 21, + 0, + 1, + 0, + 1, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 4122: (4625, + lnames[4122], + ('fls', + 'ms', + 'm', + 'f', + 14, + 0, + 14, + 14, + 0, + 2, + 0, + 2, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 4123: (4628, + lnames[4123], + ('rll', + 'ls', + 'm', + 'm', + 6, + 0, + 6, + 6, + 0, + 2, + 0, + 2, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 4124: (4629, + lnames[4124], + ('rss', + 'ms', + 'm', + 'm', + 20, + 0, + 20, + 20, + 0, + 2, + 0, + 2, + 1, + 4), + 'm', + 0, + NPC_REGULAR), + 4125: (4630, + lnames[4125], + ('rls', + 'ls', + 'm', + 'f', + 14, + 0, + 14, + 14, + 1, + 3, + 1, + 3, + 8, + 6), + 'f', + 0, + NPC_REGULAR), + 4126: (4631, + lnames[4126], + ('mls', + 'ss', + 'm', + 'm', + 6, + 0, + 6, + 6, + 1, + 3, + 1, + 3, + 1, + 18), + 'm', + 0, + NPC_REGULAR), + 4127: (4632, + lnames[4127], + ('hll', + 'md', + 'm', + 'f', + 22, + 0, + 22, + 22, + 1, + 4, + 1, + 4, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 4128: (4635, + lnames[4128], + ('hss', + 'ms', + 'm', + 'm', + 13, + 0, + 13, + 13, + 1, + 3, + 1, + 3, + 1, + 12), + 'm', + 0, + NPC_REGULAR), + 4129: (4637, + lnames[4129], + ('hls', + 'ss', + 'l', + 'f', + 6, + 0, + 6, + 6, + 1, + 5, + 1, + 5, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 4130: (4638, + lnames[4130], + 'r', + 'm', + 0, + NPC_REGULAR), + 4131: (4639, + lnames[4131], + 'r', + 'm', + 0, + NPC_REGULAR), + 4132: (4641, + lnames[4132], + ('dll', + 'ms', + 'l', + 'f', + 6, + 0, + 6, + 6, + 0, + 7, + 0, + 7, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 4133: (4642, + lnames[4133], + ('dss', + 'ls', + 'l', + 'm', + 20, + 0, + 20, + 20, + 0, + 5, + 0, + 5, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 4134: (4645, + lnames[4134], + 'r', + 'm', + 0, + NPC_REGULAR), + 4135: (4648, + lnames[4135], + ('fsl', + 'ms', + 'l', + 'm', + 5, + 0, + 5, + 5, + 0, + 6, + 0, + 6, + 1, + 6), + 'm', + 0, + NPC_REGULAR), + 4136: (4652, + lnames[4136], + ('fss', + 'ss', + 'l', + 'f', + 21, + 0, + 21, + 21, + 0, + 9, + 0, + 9, + 7, + 4), + 'f', + 0, + NPC_REGULAR), + 4137: (4654, + lnames[4137], + ('rll', + 'ls', + 'l', + 'm', + 13, + 0, + 13, + 13, + 1, + 6, + 1, + 6, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 4138: (4655, + lnames[4138], + ('rsl', + 'ms', + 'l', + 'm', + 5, + 0, + 5, + 5, + 1, + 7, + 1, + 7, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 4139: (4657, + lnames[4139], + ('rls', + 'ss', + 'l', + 'f', + 21, + 0, + 21, + 21, + 1, + 11, + 1, + 11, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 4140: (4658, + lnames[4140], + ('mls', + 'ls', + 'l', + 'm', + 12, + 0, + 12, + 12, + 1, + 7, + 1, + 7, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 4141: (4148, + lnames[4141], + ('hll', + 'ms', + 'l', + 'm', + 4, + 0, + 4, + 4, + 1, + 8, + 1, + 8, + 1, + 4), + 'm', + 0, + NPC_FISHERMAN), + 4201: (4704, + lnames[4201], + ('mss', + 'ss', + 'l', + 'f', + 14, + 0, + 14, + 14, + 0, + 6, + 0, + 6, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 4202: (4725, + lnames[4202], + ('mls', + 'ls', + 'l', + 'm', + 6, + 0, + 6, + 6, + 0, + 5, + 0, + 5, + 0, + 13), + 'm', + 0, + NPC_REGULAR), + 4203: (4702, + lnames[4203], + ('hsl', + 'ms', + 'l', + 'm', + 21, + 0, + 21, + 21, + 0, + 5, + 0, + 5, + 0, + 10), + 'm', + 0, + NPC_REGULAR), + 4204: (4739, + lnames[4204], + ('hss', + 'ss', + 'l', + 'm', + 14, + 0, + 14, + 14, + 0, + 6, + 0, + 6, + 0, + 4), + 'm', + 0, + NPC_HQ), + 4205: (4739, + lnames[4205], + ('cll', + 'ld', + 'l', + 'f', + 6, + 0, + 6, + 6, + 1, + 8, + 1, + 8, + 10, + 27), + 'f', + 0, + NPC_HQ), + 4206: (4739, + lnames[4206], + ('css', + 'sd', + 'l', + 'f', + 22, + 0, + 22, + 22, + 1, + 8, + 1, + 8, + 25, + 27), + 'f', + 0, + NPC_HQ), + 4207: (4739, + lnames[4207], + ('cls', + 'ls', + 'l', + 'f', + 14, + 0, + 14, + 14, + 1, + 9, + 1, + 9, + 17, + 27), + 'f', + 0, + NPC_HQ), + 4208: (4730, + lnames[4208], + ('dsl', + 'ss', + 'l', + 'f', + 6, + 0, + 6, + 6, + 1, + 9, + 1, + 9, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 4209: (4701, + lnames[4209], + ('dss', + 'md', + 'l', + 'f', + 22, + 0, + 22, + 22, + 1, + 11, + 1, + 11, + 1, + 9), + 'f', + 0, + NPC_REGULAR), + 4211: (4703, + lnames[4211], + ('fsl', + 'ss', + 'l', + 'm', + 5, + 0, + 5, + 5, + 1, + 8, + 1, + 8, + 0, + 20), + 'm', + 0, + NPC_REGULAR), + 4212: (4705, + lnames[4212], + ('fls', + 'ls', + 'l', + 'm', + 20, + 0, + 20, + 20, + 0, + 9, + 0, + 9, + 0, + 17), + 'm', + 0, + NPC_REGULAR), + 4213: (4707, + lnames[4213], + ('rll', + 'sd', + 'l', + 'f', + 13, + 0, + 13, + 13, + 0, + 21, + 0, + 21, + 24, + 27), + 'f', + 0, + NPC_REGULAR), + 4214: (4709, + lnames[4214], + 'r', + 'f', + 0, + NPC_REGULAR), + 4215: (4710, + lnames[4215], + ('mss', + 'ls', + 'l', + 'm', + 19, + 0, + 19, + 19, + 0, + 10, + 0, + 10, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 4216: (4712, + lnames[4216], + ('mls', + 'ms', + 's', + 'm', + 13, + 0, + 13, + 13, + 0, + 10, + 0, + 10, + 0, + 1), + 'm', + 0, + NPC_REGULAR), + 4217: (4713, + lnames[4217], + ('hsl', + 'ms', + 's', + 'm', + 5, + 0, + 5, + 5, + 0, + 10, + 0, + 10, + 0, + 19), + 'm', + 0, + NPC_REGULAR), + 4218: (4716, + lnames[4218], + ('hss', + 'ss', + 's', + 'f', + 21, + 0, + 21, + 21, + 1, + 23, + 1, + 23, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 4219: (4717, + lnames[4219], + 'r', + 'm', + 0, + NPC_REGULAR), + 4220: (4718, + lnames[4220], + 'r', + 'm', + 0, + NPC_REGULAR), + 4221: (4719, + lnames[4221], + ('cls', + 'ss', + 's', + 'm', + 19, + 0, + 19, + 19, + 1, + 11, + 1, + 11, + 0, + 4), + 'm', + 0, + NPC_REGULAR), + 4222: (4720, + lnames[4222], + ('dsl', + 'ls', + 's', + 'm', + 12, + 0, + 12, + 12, + 1, + 11, + 1, + 11, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 4223: (4722, + lnames[4223], + ('dss', + 'sd', + 's', + 'f', + 3, + 0, + 3, + 3, + 1, + 25, + 1, + 25, + 24, + 27), + 'f', + 0, + NPC_REGULAR), + 4224: (4723, + lnames[4224], + 'r', + 'm', + 0, + NPC_REGULAR), + 4225: (4724, + lnames[4225], + ('fsl', + 'ld', + 's', + 'f', + 12, + 0, + 12, + 12, + 0, + 27, + 0, + 27, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 4226: (4727, + lnames[4226], + ('fls', + 'sd', + 's', + 'f', + 3, + 0, + 3, + 3, + 0, + 0, + 0, + 0, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 4227: (4728, + lnames[4227], + ('rll', + 'ls', + 's', + 'f', + 19, + 0, + 19, + 19, + 0, + 0, + 0, + 0, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 4228: (4729, + lnames[4228], + ('rss', + 'ss', + 's', + 'f', + 11, + 0, + 11, + 11, + 0, + 1, + 0, + 1, + 0, + 1), + 'f', + 0, + NPC_REGULAR), + 4229: (4731, + lnames[4229], + ('rls', + 'md', + 'm', + 'f', + 3, + 0, + 3, + 3, + 0, + 1, + 0, + 1, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 4230: (4732, + lnames[4230], + ('mls', + 'ms', + 'm', + 'm', + 18, + 0, + 18, + 18, + 1, + 1, + 1, + 1, + 0, + 14), + 'm', + 0, + NPC_REGULAR), + 4231: (4735, + lnames[4231], + ('hsl', + 'ss', + 'm', + 'f', + 11, + 0, + 11, + 11, + 1, + 2, + 1, + 2, + 8, + 0), + 'f', + 0, + NPC_REGULAR), + 4232: (4736, + lnames[4232], + ('hss', + 'ls', + 'm', + 'm', + 3, + 0, + 3, + 3, + 1, + 2, + 1, + 2, + 1, + 6), + 'm', + 0, + NPC_REGULAR), + 4233: (4737, + lnames[4233], + ('cll', + 'ms', + 'm', + 'm', + 17, + 0, + 17, + 17, + 1, + 2, + 1, + 2, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 4234: (4738, + lnames[4234], + 'r', + 'm', + 0, + NPC_REGULAR), + 4235: (4240, + lnames[4235], + ('cls', + 'ls', + 'm', + 'm', + 3, + 0, + 3, + 3, + 1, + 3, + 1, + 3, + 1, + 16), + 'm', + 0, + NPC_FISHERMAN), + 4301: (4819, + lnames[4301], + ('fss', + 'md', + 'l', + 'f', + 12, + 0, + 12, + 12, + 1, + 2, + 1, + 2, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 4302: (4821, + lnames[4302], + ('fls', + 'ls', + 'l', + 'f', + 3, + 0, + 3, + 3, + 1, + 2, + 1, + 2, + 2, + 3), + 'f', + 0, + NPC_REGULAR), + 4303: (4853, + lnames[4303], + ('rsl', + 'ss', + 'l', + 'm', + 18, + 0, + 18, + 18, + 1, + 2, + 1, + 2, + 0, + 18), + 'm', + 0, + NPC_REGULAR), + 4304: (4873, + lnames[4304], + ('rss', + 'ls', + 'm', + 'm', + 12, + 0, + 12, + 12, + 0, + 2, + 0, + 2, + 0, + 15), + 'm', + 0, + NPC_HQ), + 4305: (4873, + lnames[4305], + ('mss', + 'sd', + 'm', + 'f', + 3, + 0, + 3, + 3, + 0, + 4, + 0, + 4, + 26, + 27), + 'f', + 0, + NPC_HQ), + 4306: (4873, + lnames[4306], + ('hll', + 'ms', + 'm', + 'f', + 19, + 0, + 19, + 19, + 0, + 5, + 0, + 5, + 4, + 25), + 'f', + 0, + NPC_HQ), + 4307: (4873, + lnames[4307], + ('hsl', + 'ld', + 'm', + 'f', + 11, + 0, + 11, + 11, + 0, + 5, + 0, + 5, + 17, + 27), + 'f', + 0, + NPC_HQ), + 4308: (4835, + lnames[4308], + ('css', + 'md', + 'm', + 'f', + 6, + 0, + 6, + 6, + 3, + 5, + 3, + 5, + 0, + 14), + 'f', + 0, + NPC_REGULAR), + 4309: (4801, + lnames[4309], + ('cll', + 'ms', + 'm', + 'm', + 18, + 0, + 18, + 18, + 0, + 4, + 0, + 4, + 0, + 17), + 'm', + 0, + NPC_REGULAR), + 4310: (4803, + lnames[4310], + 'r', + 'f', + 0, + NPC_REGULAR), + 4311: (4804, + lnames[4311], + 'r', + 'f', + 0, + NPC_REGULAR), + 4312: (4807, + lnames[4312], + ('dsl', + 'ms', + 'm', + 'm', + 18, + 0, + 18, + 18, + 1, + 5, + 1, + 5, + 0, + 9), + 'm', + 0, + NPC_REGULAR), + 4313: (4809, + lnames[4313], + ('dss', + 'ss', + 'm', + 'm', + 10, + 0, + 10, + 10, + 1, + 5, + 1, + 5, + 0, + 2), + 'm', + 0, + NPC_REGULAR), + 4314: (4817, + lnames[4314], + ('fll', + 'ld', + 'm', + 'f', + 2, + 0, + 2, + 2, + 1, + 8, + 1, + 8, + 12, + 27), + 'f', + 0, + NPC_REGULAR), + 4315: (4827, + lnames[4315], + ('fss', + 'sd', + 'm', + 'f', + 18, + 0, + 18, + 18, + 1, + 9, + 1, + 9, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 4316: (4828, + lnames[4316], + ('fls', + 'ss', + 'm', + 'm', + 9, + 0, + 9, + 9, + 1, + 6, + 1, + 6, + 0, + 14), + 'm', + 0, + NPC_REGULAR), + 4317: (4829, + lnames[4317], + ('rsl', + 'ls', + 'l', + 'm', + 3, + 0, + 3, + 3, + 0, + 7, + 0, + 7, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 4318: (4836, + lnames[4318], + ('rss', + 'ms', + 'l', + 'm', + 17, + 0, + 17, + 17, + 0, + 8, + 0, + 8, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 4319: (4838, + lnames[4319], + ('mss', + 'ls', + 'l', + 'f', + 10, + 0, + 10, + 10, + 0, + 12, + 0, + 12, + 1, + 23), + 'f', + 0, + NPC_REGULAR), + 4320: (4840, + lnames[4320], + ('mls', + 'ss', + 'l', + 'f', + 1, + 0, + 1, + 1, + 0, + 21, + 0, + 21, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 4321: (4841, + lnames[4321], + ('hsl', + 'ls', + 'l', + 'm', + 17, + 0, + 17, + 17, + 0, + 9, + 0, + 9, + 0, + 16), + 'm', + 0, + NPC_REGULAR), + 4322: (4842, + lnames[4322], + ('hls', + 'ms', + 'l', + 'm', + 9, + 0, + 9, + 9, + 0, + 9, + 0, + 9, + 0, + 13), + 'm', + 0, + NPC_REGULAR), + 4323: (4844, + lnames[4323], + ('cll', + 'ss', + 'l', + 'f', + 1, + 0, + 1, + 1, + 1, + 21, + 1, + 21, + 24, + 27), + 'f', + 0, + NPC_REGULAR), + 4324: (4845, + lnames[4324], + ('css', + 'ld', + 'l', + 'f', + 17, + 0, + 17, + 17, + 1, + 22, + 1, + 22, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 4325: (4848, + lnames[4325], + ('cls', + 'ms', + 'l', + 'm', + 9, + 0, + 9, + 9, + 1, + 9, + 1, + 9, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 4326: (4850, + lnames[4326], + ('dsl', + 'ms', + 'l', + 'f', + 1, + 0, + 1, + 1, + 1, + 23, + 1, + 23, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 4327: (4852, + lnames[4327], + ('dss', + 'ld', + 'l', + 'f', + 16, + 0, + 16, + 16, + 1, + 23, + 1, + 23, + 7, + 1), + 'f', + 0, + NPC_REGULAR), + 4328: (4854, + lnames[4328], + ('fll', + 'ms', + 'l', + 'm', + 8, + 0, + 8, + 8, + 1, + 11, + 1, + 11, + 0, + 12), + 'm', + 0, + NPC_REGULAR), + 4329: (4855, + lnames[4329], + ('fsl', + 'ls', + 'l', + 'f', + 24, + 0, + 24, + 24, + 0, + 25, + 0, + 25, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 4330: (4862, + lnames[4330], + 'r', + 'm', + 0, + NPC_REGULAR), + 4331: (4867, + lnames[4331], + 'r', + 'm', + 0, + NPC_REGULAR), + 4332: (4870, + lnames[4332], + ('rss', + 'ms', + 'l', + 'm', + 22, + 0, + 22, + 22, + 0, + 27, + 0, + 27, + 0, + 17), + 'm', + 0, + NPC_REGULAR), + 4333: (4871, + lnames[4333], + ('mss', + 'ss', + 'l', + 'm', + 15, + 0, + 15, + 15, + 0, + 27, + 0, + 27, + 0, + 14), + 'm', + 0, + NPC_REGULAR), + 4334: (4872, + lnames[4334], + ('mls', + 'ls', + 'l', + 'm', + 8, + 0, + 8, + 8, + 0, + 0, + 0, + 0, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 4335: (4345, + lnames[4335], + ('hsl', + 'ms', + 'l', + 'm', + 22, + 0, + 22, + 22, + 1, + 0, + 1, + 0, + 0, + 6), + 'm', + 0, + NPC_FISHERMAN), + 5001: (5502, + lnames[5001], + ('fls', + 'ls', + 's', + 'm', + 14, + 0, + 14, + 14, + 1, + 7, + 1, + 7, + 0, + 20), + 'm', + 0, + NPC_HQ), + 5002: (5502, + lnames[5002], + ('rll', + 'ms', + 's', + 'm', + 6, + 0, + 6, + 6, + 0, + 8, + 0, + 8, + 0, + 17), + 'm', + 0, + NPC_HQ), + 5003: (5502, + lnames[5003], + ('rss', + 'ms', + 's', + 'f', + 22, + 0, + 22, + 22, + 0, + 12, + 0, + 12, + 26, + 27), + 'f', + 0, + NPC_HQ), + 5004: (5502, + lnames[5004], + ('rls', + 'ld', + 's', + 'f', + 13, + 0, + 13, + 13, + 0, + 21, + 0, + 21, + 4, + 11), + 'f', + 0, + NPC_HQ), + 5005: (5501, + lnames[5005], + ('mls', + 'md', + 's', + 'f', + 6, + 0, + 6, + 6, + 0, + 21, + 0, + 21, + 2, + 3), + 'f', + 0, + NPC_CLERK), + 5006: (5501, + lnames[5006], + ('hsl', + 'ms', + 's', + 'm', + 20, + 0, + 20, + 20, + 0, + 9, + 0, + 9, + 0, + 1), + 'm', + 0, + NPC_CLERK), + 5007: (5503, + lnames[5007], + ('hss', + 'ss', + 's', + 'f', + 13, + 0, + 13, + 13, + 0, + 22, + 0, + 22, + 3, + 2), + 'f', + 0, + NPC_TAILOR), + 5008: (5000, + lnames[5008], + ('cll', + 'md', + 's', + 'f', + 4, + 0, + 4, + 4, + 1, + 22, + 1, + 22, + 19, + 27), + 'f', + 0, + NPC_FISHERMAN), + 5009: (5505, + lnames[5009], + ('csl', + 'ls', + 'm', + 'f', + 21, + 0, + 21, + 21, + 1, + 23, + 1, + 23, + 8, + 23), + 'f', + 0, + NPC_PETCLERK), + 5010: (5505, + lnames[5010], + ('cls', + 'ss', + 'm', + 'm', + 13, + 0, + 13, + 13, + 1, + 10, + 1, + 10, + 0, + 10), + 'm', + 0, + NPC_PETCLERK), + 5011: (5505, + lnames[5011], + ('dll', + 'ls', + 'm', + 'm', + 5, + 0, + 5, + 5, + 1, + 10, + 1, + 10, + 0, + 4), + 'm', + 0, + NPC_PETCLERK), + 5012: (5000, + lnames[5012], + ('dls', + 'ms', + 'm', + 'm', + 13, + 0, + 12, + 12, + 0, + 1, + 0, + 1, + 0, + 6), + 'm', + 1, + NPC_PARTYPERSON), + 5013: (5000, + lnames[5013], + ('dss', + 'md', + 'm', + 'f', + 1, + 0, + 3, + 3, + 1, + 5, + 1, + 5, + 0, + 5), + 'f', + 1, + NPC_PARTYPERSON), + 5101: (5602, + lnames[5101], + ('dsl', + 'ms', + 'l', + 'm', + 10, + 0, + 10, + 10, + 1, + 4, + 1, + 4, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 5102: (5610, + lnames[5102], + 'r', + 'f', + 0, + NPC_REGULAR), + 5103: (5615, + lnames[5103], + ('fll', + 'ls', + 'l', + 'm', + 18, + 0, + 18, + 18, + 1, + 5, + 1, + 5, + 0, + 1), + 'm', + 0, + NPC_REGULAR), + 5104: (5617, + lnames[5104], + ('fsl', + 'ms', + 'l', + 'm', + 10, + 0, + 10, + 10, + 1, + 5, + 1, + 5, + 0, + 19), + 'm', + 0, + NPC_REGULAR), + 5105: (5619, + lnames[5105], + 'r', + 'm', + 0, + NPC_REGULAR), + 5106: (5613, + lnames[5106], + ('rsl', + 'ls', + 'l', + 'm', + 18, + 0, + 18, + 18, + 1, + 6, + 1, + 6, + 0, + 13), + 'm', + 0, + NPC_REGULAR), + 5107: (5607, + lnames[5107], + 'r', + 'm', + 0, + NPC_REGULAR), + 5108: (5616, + lnames[5108], + ('mss', + 'ls', + 'l', + 'f', + 2, + 0, + 2, + 2, + 0, + 11, + 0, + 11, + 24, + 27), + 'f', + 0, + NPC_REGULAR), + 5109: (5627, + lnames[5109], + ('mls', + 'ss', + 'l', + 'm', + 17, + 0, + 17, + 17, + 0, + 7, + 0, + 7, + 0, + 0), + 'm', + 1, + NPC_HQ), + 5110: (5627, + lnames[5110], + ('hsl', + 'ls', + 'l', + 'm', + 10, + 0, + 10, + 10, + 0, + 8, + 0, + 8, + 0, + 18), + 'm', + 1, + NPC_HQ), + 5111: (5627, + lnames[5111], + ('hss', + 'ls', + 'l', + 'f', + 2, + 0, + 2, + 2, + 0, + 12, + 0, + 12, + 7, + 4), + 'f', + 1, + NPC_HQ), + 5112: (5627, + lnames[5112], + ('cll', + 'ms', + 'l', + 'f', + 17, + 0, + 17, + 17, + 0, + 21, + 0, + 21, + 14, + 27), + 'f', + 1, + NPC_HQ), + 5113: (5601, + lnames[5113], + ('css', + 'ld', + 'l', + 'f', + 10, + 0, + 10, + 10, + 0, + 21, + 0, + 21, + 3, + 2), + 'f', + 0, + NPC_REGULAR), + 5114: (5603, + lnames[5114], + ('cls', + 'ms', + 'l', + 'm', + 2, + 0, + 2, + 2, + 1, + 9, + 1, + 9, + 0, + 2), + 'm', + 0, + NPC_REGULAR), + 5115: (5604, + lnames[5115], + ('dsl', + 'ms', + 'l', + 'f', + 17, + 0, + 17, + 17, + 1, + 22, + 1, + 22, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 5116: (5605, + lnames[5116], + ('dss', + 'ls', + 'l', + 'm', + 9, + 0, + 9, + 9, + 1, + 9, + 1, + 9, + 0, + 17), + 'm', + 0, + NPC_REGULAR), + 5117: (5606, + lnames[5117], + ('fll', + 'md', + 'l', + 'f', + 1, + 0, + 1, + 1, + 1, + 23, + 1, + 23, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 5118: (5608, + lnames[5118], + ('fsl', + 'ms', + 'l', + 'm', + 16, + 0, + 16, + 16, + 1, + 10, + 1, + 10, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 5119: (5609, + lnames[5119], + ('fls', + 'ss', + 'l', + 'm', + 9, + 0, + 9, + 9, + 1, + 10, + 1, + 10, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 5120: (5611, + lnames[5120], + ('rsl', + 'ss', + 'l', + 'm', + 22, + 0, + 22, + 22, + 1, + 3, + 1, + 3, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 5121: (5618, + lnames[5121], + ('rss', + 'ss', + 'l', + 'f', + 23, + 0, + 23, + 23, + 1, + 9, + 1, + 9, + 0, + 25), + 'f', + 0, + NPC_REGULAR), + 5122: (5620, + lnames[5122], + 'r', + 'm', + 0, + NPC_REGULAR), + 5123: (5621, + lnames[5123], + ('mls', + 'sd', + 'm', + 'f', + 7, + 0, + 7, + 7, + 1, + 11, + 1, + 11, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 5124: (5622, + lnames[5124], + ('hll', + 'ss', + 'm', + 'm', + 21, + 0, + 21, + 21, + 0, + 8, + 0, + 8, + 0, + 4), + 'm', + 0, + NPC_REGULAR), + 5125: (5623, + lnames[5125], + ('hss', + 'ls', + 'm', + 'm', + 14, + 0, + 14, + 14, + 0, + 9, + 0, + 9, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 5126: (5624, + lnames[5126], + ('hls', + 'sd', + 'm', + 'f', + 7, + 0, + 7, + 7, + 0, + 21, + 0, + 21, + 14, + 27), + 'f', + 0, + NPC_REGULAR), + 5127: (5625, + lnames[5127], + ('csl', + 'ms', + 'm', + 'f', + 23, + 0, + 23, + 23, + 0, + 22, + 0, + 22, + 2, + 2), + 'f', + 0, + NPC_REGULAR), + 5128: (5626, + lnames[5128], + 'r', + 'f', + 0, + NPC_REGULAR), + 5129: (5139, + lnames[5129], + ('dll', + 'md', + 'm', + 'f', + 7, + 0, + 7, + 7, + 0, + 23, + 0, + 23, + 17, + 27), + 'f', + 0, + NPC_FISHERMAN), + 5201: (5702, + lnames[5201], + ('hls', + 'ls', + 'l', + 'm', + 15, + 0, + 15, + 15, + 1, + 10, + 1, + 10, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 5202: (5703, + lnames[5202], + ('cll', + 'ls', + 'l', + 'f', + 7, + 0, + 7, + 7, + 1, + 23, + 1, + 23, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 5203: (5704, + lnames[5203], + ('css', + 'ss', + 'l', + 'f', + 23, + 0, + 23, + 23, + 1, + 24, + 1, + 24, + 19, + 27), + 'f', + 0, + NPC_REGULAR), + 5204: (5726, + lnames[5204], + ('cls', + 'ls', + 'l', + 'm', + 14, + 0, + 14, + 14, + 0, + 12, + 0, + 12, + 1, + 4), + 'm', + 0, + NPC_REGULAR), + 5205: (5718, + lnames[5205], + 'r', + 'm', + 0, + NPC_REGULAR), + 5206: (5720, + lnames[5206], + ('dss', + 'ss', + 'l', + 'm', + 21, + 0, + 21, + 21, + 0, + 27, + 0, + 27, + 1, + 18), + 'm', + 0, + NPC_REGULAR), + 5207: (5717, + lnames[5207], + ('fll', + 'ld', + 'l', + 'f', + 14, + 0, + 14, + 14, + 0, + 27, + 0, + 27, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 5208: (5719, + lnames[5208], + ('fsl', + 'sd', + 'l', + 'f', + 7, + 0, + 7, + 7, + 0, + 27, + 0, + 27, + 1, + 12), + 'f', + 0, + NPC_REGULAR), + 5209: (5728, + lnames[5209], + ('fls', + 'ss', + 'l', + 'm', + 21, + 0, + 21, + 21, + 0, + 0, + 0, + 0, + 1, + 9), + 'm', + 1, + NPC_HQ), + 5210: (5728, + lnames[5210], + ('rsl', + 'ss', + 'l', + 'm', + 14, + 0, + 14, + 14, + 1, + 0, + 1, + 0, + 1, + 2), + 'm', + 1, + NPC_HQ), + 5211: (5728, + lnames[5211], + ('rss', + 'md', + 'l', + 'f', + 6, + 0, + 6, + 6, + 1, + 1, + 1, + 1, + 23, + 27), + 'f', + 1, + NPC_HQ), + 5212: (5728, + lnames[5212], + ('mss', + 'ls', + 'l', + 'f', + 22, + 0, + 22, + 22, + 1, + 1, + 1, + 1, + 10, + 27), + 'f', + 1, + NPC_HQ), + 5213: (5701, + lnames[5213], + ('mls', + 'ss', + 'l', + 'm', + 13, + 0, + 13, + 13, + 1, + 1, + 1, + 1, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 5214: (5705, + lnames[5214], + ('hsl', + 'md', + 'l', + 'f', + 6, + 0, + 6, + 6, + 1, + 2, + 1, + 2, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 5215: (5706, + lnames[5215], + 'r', + 'f', + 0, + NPC_REGULAR), + 5216: (5707, + lnames[5216], + ('cll', + 'ss', + 'l', + 'm', + 13, + 0, + 13, + 13, + 0, + 2, + 0, + 2, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 5217: (5708, + lnames[5217], + ('csl', + 'ls', + 'l', + 'm', + 5, + 0, + 5, + 5, + 0, + 3, + 0, + 3, + 1, + 19), + 'm', + 0, + NPC_REGULAR), + 5218: (5709, + lnames[5218], + 'r', + 'm', + 0, + NPC_REGULAR), + 5219: (5710, + lnames[5219], + ('dsl', + 'ss', + 'l', + 'm', + 13, + 0, + 13, + 13, + 0, + 3, + 0, + 3, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 5220: (5711, + lnames[5220], + 'r', + 'f', + 0, + NPC_REGULAR), + 5221: (5712, + lnames[5221], + ('fll', + 'md', + 'l', + 'f', + 21, + 0, + 21, + 21, + 0, + 6, + 0, + 6, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 5222: (5713, + lnames[5222], + 'r', + 'f', + 0, + NPC_REGULAR), + 5223: (5714, + lnames[5223], + ('fls', + 'ss', + 's', + 'm', + 5, + 0, + 5, + 5, + 1, + 4, + 1, + 4, + 1, + 18), + 'm', + 0, + NPC_REGULAR), + 5224: (5715, + lnames[5224], + ('rll', + 'ls', + 's', + 'm', + 19, + 0, + 19, + 19, + 1, + 5, + 1, + 5, + 1, + 15), + 'm', + 0, + NPC_REGULAR), + 5225: (5716, + lnames[5225], + ('rss', + 'sd', + 's', + 'f', + 12, + 0, + 12, + 12, + 1, + 7, + 1, + 7, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 5226: (5721, + lnames[5226], + ('rls', + 'ss', + 's', + 'm', + 4, + 0, + 4, + 4, + 1, + 5, + 1, + 5, + 1, + 9), + 'm', + 0, + NPC_REGULAR), + 5227: (5725, + lnames[5227], + ('mls', + 'ld', + 's', + 'f', + 19, + 0, + 19, + 19, + 1, + 8, + 1, + 8, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 5228: (5727, + lnames[5228], + ('hsl', + 'ms', + 's', + 'm', + 12, + 0, + 12, + 12, + 1, + 6, + 1, + 6, + 1, + 20), + 'm', + 0, + NPC_REGULAR), + 5229: (5245, + lnames[5229], + ('hss', + 'ms', + 's', + 'f', + 3, + 0, + 3, + 3, + 0, + 11, + 0, + 11, + 16, + 27), + 'f', + 0, + NPC_FISHERMAN), + 5301: (5802, + lnames[5301], + ('rss', + 'ms', + 'l', + 'f', + 13, + 0, + 13, + 13, + 0, + 11, + 0, + 11, + 1, + 12), + 'f', + 1, + NPC_HQ), + 5302: (5802, + lnames[5302], + ('mss', + 'ss', + 'l', + 'f', + 4, + 0, + 4, + 4, + 0, + 12, + 0, + 12, + 17, + 27), + 'f', + 1, + NPC_HQ), + 5303: (5802, + lnames[5303], + ('hll', + 'ls', + 'l', + 'm', + 19, + 0, + 19, + 19, + 1, + 8, + 1, + 8, + 1, + 18), + 'm', + 1, + NPC_HQ), + 5304: (5802, + lnames[5304], + ('hsl', + 'ls', + 'l', + 'f', + 12, + 0, + 12, + 12, + 1, + 12, + 1, + 12, + 19, + 27), + 'f', + 1, + NPC_HQ), + 5305: (5804, + lnames[5305], + ('hls', + 'ss', + 'l', + 'f', + 4, + 0, + 4, + 4, + 1, + 21, + 1, + 21, + 16, + 27), + 'f', + 0, + NPC_REGULAR), + 5306: (5805, + lnames[5306], + 'r', + 'm', + 0, + NPC_REGULAR), + 5307: (5809, + lnames[5307], + ('css', + 'ms', + 'l', + 'm', + 12, + 0, + 12, + 12, + 1, + 9, + 1, + 9, + 1, + 2), + 'm', + 0, + NPC_REGULAR), + 5308: (5810, + lnames[5308], + ('cls', + 'ms', + 'l', + 'f', + 4, + 0, + 4, + 4, + 1, + 22, + 1, + 22, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 5309: (5811, + lnames[5309], + 'r', + 'f', + 0, + NPC_REGULAR), + 5310: (5815, + lnames[5310], + ('dls', + 'ms', + 'l', + 'm', + 12, + 0, + 12, + 12, + 0, + 11, + 0, + 11, + 1, + 14), + 'm', + 0, + NPC_REGULAR), + 5311: (5817, + lnames[5311], + ('fll', + 'ms', + 'm', + 'f', + 3, + 0, + 3, + 3, + 0, + 24, + 0, + 24, + 12, + 27), + 'f', + 0, + NPC_REGULAR), + 5312: (5819, + lnames[5312], + ('fss', + 'ss', + 'm', + 'm', + 18, + 0, + 18, + 18, + 0, + 12, + 0, + 12, + 1, + 6), + 'm', + 0, + NPC_REGULAR), + 5313: (5821, + lnames[5313], + ('fls', + 'ls', + 'm', + 'm', + 10, + 0, + 10, + 10, + 0, + 12, + 0, + 12, + 1, + 1), + 'm', + 0, + NPC_REGULAR), + 5314: (5826, + lnames[5314], + 'r', + 'f', + 0, + NPC_REGULAR), + 5315: (5827, + lnames[5315], + ('rss', + 'ss', + 'm', + 'm', + 18, + 0, + 18, + 18, + 1, + 12, + 1, + 12, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 5316: (5828, + lnames[5316], + ('mss', + 'ls', + 'm', + 'm', + 10, + 0, + 10, + 10, + 1, + 12, + 1, + 12, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 5317: (5830, + lnames[5317], + 'r', + 'm', + 0, + NPC_REGULAR), + 5318: (5833, + lnames[5318], + ('hsl', + 'ss', + 'm', + 'm', + 18, + 0, + 18, + 18, + 1, + 0, + 1, + 0, + 1, + 4), + 'm', + 0, + NPC_REGULAR), + 5319: (5835, + lnames[5319], + 'r', + 'f', + 0, + NPC_REGULAR), + 5320: (5836, + lnames[5320], + ('cll', + 'sd', + 'm', + 'f', + 2, + 0, + 2, + 2, + 1, + 1, + 1, + 1, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 5321: (5837, + lnames[5321], + ('css', + 'ms', + 'm', + 'f', + 18, + 0, + 18, + 18, + 1, + 1, + 1, + 1, + 17, + 27), + 'f', + 0, + NPC_REGULAR), + 5322: (5318, + lnames[5322], + ('cls', + 'ss', + 'm', + 'f', + 10, + 0, + 10, + 10, + 0, + 2, + 0, + 2, + 11, + 27), + 'f', + 0, + NPC_FISHERMAN), + 8001: (8501, + lnames[8001], + ('psl', + 'ms', + 'm', + 'm', + 13, + 0, + 13, + 13, + 0, + 11, + 0, + 11, + 2, + 10), + 'm', + 0, + NPC_KARTCLERK), + 8002: (8501, + lnames[8002], + ('psl', + 'ld', + 's', + 'f', + 23, + 0, + 23, + 23, + 0, + 11, + 0, + 11, + 2, + 10), + 'f', + 0, + NPC_KARTCLERK), + 8003: (8501, + lnames[8003], + ('pll', + 'ss', + 'l', + 'f', + 1, + 0, + 1, + 1, + 0, + 11, + 0, + 11, + 2, + 10), + 'f', + 0, + NPC_KARTCLERK), + 8004: (8501, + lnames[8004], + ('pls', + 'ms', + 'l', + 'm', + 16, + 0, + 16, + 16, + 0, + 11, + 0, + 11, + 2, + 10), + 'm', + 0, + NPC_KARTCLERK), + 9001: (9503, + lnames[9001], + ('fll', + 'ss', + 'l', + 'f', + 16, + 0, + 16, + 16, + 0, + 6, + 0, + 6, + 26, + 27), + 'f', + 0, + NPC_REGULAR), + 9002: (9502, + lnames[9002], + 'r', + 'm', + 0, + NPC_REGULAR), + 9003: (9501, + lnames[9003], + ('fls', + 'ms', + 'l', + 'm', + 22, + 0, + 22, + 22, + 1, + 5, + 1, + 5, + 0, + 14), + 'm', + 0, + NPC_REGULAR), + 9004: (9505, + lnames[9004], + ('rll', + 'ms', + 'l', + 'f', + 16, + 0, + 16, + 16, + 1, + 7, + 1, + 7, + 3, + 8), + 'f', + 1, + NPC_HQ), + 9005: (9505, + lnames[9005], + ('rss', + 'ld', + 'l', + 'f', + 9, + 0, + 9, + 9, + 1, + 8, + 1, + 8, + 19, + 27), + 'f', + 1, + NPC_HQ), + 9006: (9505, + lnames[9006], + ('rls', + 'ms', + 'l', + 'm', + 22, + 0, + 22, + 22, + 1, + 6, + 1, + 6, + 0, + 1), + 'm', + 1, + NPC_HQ), + 9007: (9505, + lnames[9007], + ('mls', + 'ms', + 'l', + 'm', + 15, + 0, + 15, + 15, + 1, + 6, + 1, + 6, + 0, + 19), + 'm', + 1, + NPC_HQ), + 9008: (9504, + lnames[9008], + ('hll', + 'ss', + 'l', + 'f', + 8, + 0, + 8, + 8, + 1, + 9, + 1, + 9, + 12, + 27), + 'f', + 0, + NPC_CLERK), + 9009: (9504, + lnames[9009], + ('hss', + 'ls', + 'l', + 'm', + 22, + 0, + 22, + 22, + 0, + 7, + 0, + 7, + 0, + 13), + 'm', + 0, + NPC_CLERK), + 9010: (9506, + lnames[9010], + ('cll', + 'ms', + 'l', + 'm', + 15, + 0, + 15, + 15, + 0, + 8, + 0, + 8, + 0, + 10), + 'm', + 0, + NPC_TAILOR), + 9011: (9000, + lnames[9011], + ('csl', + 'ss', + 'l', + 'm', + 7, + 0, + 7, + 7, + 0, + 8, + 0, + 8, + 0, + 4), + 'm', + 0, + NPC_FISHERMAN), + 9012: (9508, + lnames[9012], + ('cls', + 'ld', + 'l', + 'f', + 23, + 0, + 23, + 23, + 0, + 21, + 0, + 21, + 10, + 27), + 'f', + 0, + NPC_PETCLERK), + 9013: (9508, + lnames[9013], + ('dll', + 'sd', + 'l', + 'f', + 15, + 0, + 15, + 15, + 0, + 21, + 0, + 21, + 10, + 27), + 'f', + 0, + NPC_PETCLERK), + 9014: (9508, + lnames[9014], + ('dss', + 'ss', + 'l', + 'm', + 7, + 0, + 7, + 7, + 0, + 9, + 0, + 9, + 1, + 15), + 'm', + 0, + NPC_PETCLERK), + 9015: (9000, + lnames[9015], + ('rss', + 'ls', + 'l', + 'm', + 21, + 0, + 20, + 20, + 0, + 12, + 0, + 12, + 0, + 11), + 'm', + 1, + NPC_PARTYPERSON), + 9016: (9000, + lnames[9016], + ('rls', + 'md', + 'l', + 'f', + 6, + 0, + 21, + 21, + 1, + 11, + 1, + 11, + 0, + 11), + 'f', + 1, + NPC_PARTYPERSON), + 9101: (9604, + lnames[9101], + ('css', + 'ls', + 'l', + 'm', + 14, + 0, + 14, + 14, + 1, + 1, + 1, + 1, + 0, + 11), + 'm', + 0, + NPC_REGULAR), + 9102: (9607, + lnames[9102], + 'r', + 'f', + 0, + NPC_REGULAR), + 9103: (9620, + lnames[9103], + ('dsl', + 'ss', + 'l', + 'm', + 20, + 0, + 20, + 20, + 0, + 2, + 0, + 2, + 0, + 1), + 'm', + 0, + NPC_REGULAR), + 9104: (9642, + lnames[9104], + ('dss', + 'ld', + 'l', + 'f', + 14, + 0, + 14, + 14, + 0, + 3, + 0, + 3, + 0, + 23), + 'f', + 0, + NPC_REGULAR), + 9105: (9609, + lnames[9105], + 'r', + 'm', + 0, + NPC_REGULAR), + 9106: (9619, + lnames[9106], + ('fsl', + 'ss', + 'l', + 'm', + 20, + 0, + 20, + 20, + 0, + 3, + 0, + 3, + 0, + 13), + 'm', + 0, + NPC_REGULAR), + 9107: (9601, + lnames[9107], + ('fls', + 'ld', + 'l', + 'f', + 13, + 0, + 13, + 13, + 0, + 5, + 0, + 5, + 3, + 2), + 'f', + 0, + NPC_REGULAR), + 9108: (9602, + lnames[9108], + ('rll', + 'ms', + 'l', + 'm', + 6, + 0, + 6, + 6, + 1, + 4, + 1, + 4, + 0, + 4), + 'm', + 0, + NPC_REGULAR), + 9109: (9605, + lnames[9109], + ('rss', + 'ls', + 'l', + 'f', + 22, + 0, + 22, + 22, + 1, + 6, + 1, + 6, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 9110: (9608, + lnames[9110], + ('mss', + 'ss', + 'l', + 'f', + 13, + 0, + 13, + 13, + 1, + 6, + 1, + 6, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 9111: (9616, + lnames[9111], + 'r', + 'f', + 0, + NPC_REGULAR), + 9112: (9617, + lnames[9112], + ('hsl', + 'ms', + 'm', + 'm', + 19, + 0, + 19, + 19, + 1, + 5, + 1, + 5, + 0, + 12), + 'm', + 0, + NPC_REGULAR), + 9113: (9622, + lnames[9113], + ('hss', + 'ss', + 'm', + 'm', + 13, + 0, + 13, + 13, + 1, + 5, + 1, + 5, + 0, + 9), + 'm', + 0, + NPC_REGULAR), + 9114: (9625, + lnames[9114], + ('cll', + 'ld', + 'm', + 'f', + 4, + 0, + 4, + 4, + 0, + 8, + 0, + 8, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 9115: (9626, + lnames[9115], + 'r', + 'm', + 0, + NPC_REGULAR), + 9116: (9627, + lnames[9116], + ('cls', + 'ss', + 'm', + 'm', + 12, + 0, + 12, + 12, + 0, + 7, + 0, + 7, + 0, + 17), + 'm', + 0, + NPC_REGULAR), + 9117: (9628, + lnames[9117], + ('dsl', + 'ld', + 'm', + 'f', + 4, + 0, + 4, + 4, + 0, + 11, + 0, + 11, + 2, + 9), + 'f', + 0, + NPC_REGULAR), + 9118: (9629, + lnames[9118], + 'r', + 'f', + 0, + NPC_REGULAR), + 9119: (9630, + lnames[9119], + ('fll', + 'ms', + 'm', + 'm', + 12, + 0, + 12, + 12, + 0, + 8, + 0, + 8, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 9120: (9631, + lnames[9120], + 'r', + 'f', + 0, + NPC_REGULAR), + 9121: (9634, + lnames[9121], + ('fls', + 'md', + 'm', + 'f', + 19, + 0, + 19, + 19, + 1, + 12, + 1, + 12, + 16, + 27), + 'f', + 0, + NPC_REGULAR), + 9122: (9636, + lnames[9122], + ('rll', + 'ms', + 'm', + 'm', + 12, + 0, + 12, + 12, + 1, + 8, + 1, + 8, + 0, + 16), + 'm', + 0, + NPC_REGULAR), + 9123: (9639, + lnames[9123], + ('rss', + 'ss', + 'm', + 'm', + 4, + 0, + 4, + 4, + 1, + 9, + 1, + 9, + 0, + 13), + 'm', + 0, + NPC_REGULAR), + 9124: (9640, + lnames[9124], + ('rls', + 'md', + 'm', + 'f', + 19, + 0, + 19, + 19, + 1, + 22, + 1, + 22, + 8, + 9), + 'f', + 0, + NPC_REGULAR), + 9125: (9643, + lnames[9125], + ('mls', + 'ms', + 'l', + 'm', + 10, + 0, + 10, + 10, + 1, + 9, + 1, + 9, + 0, + 4), + 'm', + 0, + NPC_REGULAR), + 9126: (9644, + lnames[9126], + ('hsl', + 'ms', + 'l', + 'f', + 3, + 0, + 3, + 3, + 1, + 23, + 1, + 23, + 23, + 27), + 'f', + 0, + NPC_REGULAR), + 9127: (9645, + lnames[9127], + ('hss', + 'ld', + 'l', + 'f', + 19, + 0, + 19, + 19, + 0, + 24, + 0, + 24, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 9128: (9647, + lnames[9128], + ('cll', + 'ms', + 'l', + 'm', + 10, + 0, + 10, + 10, + 0, + 11, + 0, + 11, + 0, + 15), + 'm', + 0, + NPC_REGULAR), + 9129: (9649, + lnames[9129], + ('csl', + 'ms', + 'l', + 'f', + 3, + 0, + 3, + 3, + 0, + 25, + 0, + 25, + 25, + 27), + 'f', + 0, + NPC_REGULAR), + 9130: (9650, + lnames[9130], + ('cls', + 'ss', + 'l', + 'm', + 18, + 0, + 18, + 18, + 0, + 12, + 0, + 12, + 0, + 9), + 'm', + 0, + NPC_REGULAR), + 9131: (9651, + lnames[9131], + 'r', + 'f', + 0, + NPC_REGULAR), + 9132: (9652, + lnames[9132], + ('dss', + 'ls', + 'l', + 'f', + 2, + 0, + 2, + 2, + 0, + 27, + 0, + 27, + 0, + 0), + 'f', + 0, + NPC_HQ), + 9133: (9652, + lnames[9133], + ('dls', + 'ss', + 'l', + 'm', + 17, + 0, + 17, + 17, + 1, + 12, + 1, + 12, + 0, + 17), + 'm', + 0, + NPC_HQ), + 9134: (9652, + lnames[9134], + ('fsl', + 'ls', + 'l', + 'm', + 10, + 0, + 10, + 10, + 1, + 0, + 1, + 0, + 0, + 14), + 'm', + 0, + NPC_HQ), + 9135: (9652, + lnames[9135], + ('fls', + 'ms', + 'l', + 'm', + 3, + 0, + 3, + 3, + 1, + 0, + 1, + 0, + 0, + 11), + 'm', + 0, + NPC_HQ), + 9136: (9153, + lnames[9136], + ('rll', + 'ss', + 'l', + 'm', + 17, + 0, + 17, + 17, + 1, + 0, + 1, + 0, + 1, + 6), + 'm', + 0, + NPC_FISHERMAN), + 9201: (9752, + lnames[9201], + ('psl', + 'ss', + 'm', + 'm', + 9, + 0, + 9, + 9, + 17, + 11, + 0, + 11, + 7, + 20), + 'm', + 0, + NPC_REGULAR), + 9202: (9703, + lnames[9202], + ('dss', + 'ss', + 's', + 'm', + 21, + 0, + 21, + 21, + 8, + 3, + 8, + 3, + 1, + 17), + 'm', + 0, + NPC_REGULAR), + 9203: (9741, + lnames[9203], + ('pls', + 'ls', + 's', + 'm', + 5, + 0, + 5, + 5, + 37, + 27, + 26, + 27, + 7, + 4), + 'm', + 0, + NPC_REGULAR), + 9204: (9704, + lnames[9204], + ('fsl', + 'sd', + 's', + 'f', + 19, + 0, + 19, + 19, + 21, + 10, + 0, + 10, + 8, + 23), + 'f', + 0, + NPC_REGULAR), + 9205: (9736, + lnames[9205], + ('dsl', + 'ms', + 'm', + 'm', + 15, + 0, + 15, + 15, + 45, + 27, + 34, + 27, + 2, + 17), + 'm', + 0, + NPC_REGULAR), + 9206: (9727, + lnames[9206], + ('rls', + 'ld', + 'l', + 'f', + 8, + 0, + 8, + 8, + 25, + 27, + 16, + 27, + 10, + 27), + 'f', + 0, + NPC_REGULAR), + 9207: (9709, + lnames[9207], + ('hss', + 'ss', + 's', + 'f', + 24, + 0, + 24, + 24, + 36, + 27, + 25, + 27, + 9, + 27), + 'f', + 0, + NPC_REGULAR), + 9208: (9705, + lnames[9208], + ('dsl', + 'ms', + 's', + 'm', + 20, + 0, + 20, + 20, + 46, + 27, + 35, + 27, + 6, + 27), + 'm', + 0, + NPC_REGULAR), + 9209: (9706, + lnames[9209], + ('pll', + 'ss', + 'm', + 'm', + 13, + 0, + 13, + 13, + 8, + 12, + 8, + 12, + 1, + 12), + 'm', + 0, + NPC_REGULAR), + 9210: (9740, + lnames[9210], + ('hsl', + 'ls', + 'l', + 'm', + 6, + 0, + 6, + 6, + 1, + 0, + 1, + 0, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 9211: (9707, + lnames[9211], + ('rll', + 'ss', + 's', + 'f', + 3, + 0, + 3, + 3, + 22, + 22, + 0, + 22, + 6, + 22), + 'f', + 0, + NPC_REGULAR), + 9212: (9753, + lnames[9212], + ('pss', + 'md', + 'm', + 'f', + 16, + 0, + 16, + 16, + 45, + 27, + 34, + 27, + 0, + 3), + 'f', + 0, + NPC_REGULAR), + 9213: (9711, + lnames[9213], + ('fsl', + 'ss', + 'm', + 'm', + 2, + 0, + 2, + 2, + 37, + 27, + 26, + 27, + 7, + 18), + 'm', + 0, + NPC_REGULAR), + 9214: (9710, + lnames[9214], + ('rll', + 'ls', + 'l', + 'm', + 18, + 0, + 18, + 18, + 10, + 27, + 0, + 27, + 0, + 13), + 'm', + 0, + NPC_REGULAR), + 9215: (9744, + lnames[9215], + ('csl', + 'ls', + 'l', + 'm', + 18, + 0, + 18, + 18, + 11, + 4, + 0, + 4, + 0, + 4), + 'm', + 0, + NPC_REGULAR), + 9216: (9725, + lnames[9216], + ('csl', + 'sd', + 'm', + 'f', + 14, + 0, + 14, + 14, + 1, + 7, + 1, + 7, + 3, + 7), + 'f', + 0, + NPC_REGULAR), + 9217: (9713, + lnames[9217], + ('mss', + 'ms', + 'm', + 'f', + 17, + 0, + 17, + 17, + 20, + 26, + 0, + 26, + 5, + 12), + 'f', + 0, + NPC_REGULAR), + 9218: (9737, + lnames[9218], + ('dss', + 'md', + 'l', + 'f', + 23, + 0, + 23, + 23, + 24, + 27, + 15, + 27, + 11, + 27), + 'f', + 0, + NPC_REGULAR), + 9219: (9712, + lnames[9219], + ('hll', + 'sd', + 'l', + 'f', + 10, + 0, + 10, + 10, + 9, + 22, + 9, + 22, + 12, + 27), + 'f', + 0, + NPC_REGULAR), + 9220: (9716, + lnames[9220], + ('mls', + 'ms', + 'l', + 'm', + 7, + 0, + 7, + 7, + 0, + 27, + 0, + 27, + 1, + 10), + 'm', + 0, + NPC_REGULAR), + 9221: (9738, + lnames[9221], + ('fss', + 'md', + 'l', + 'f', + 22, + 0, + 22, + 22, + 45, + 27, + 34, + 27, + 0, + 6), + 'f', + 0, + NPC_REGULAR), + 9222: (9754, + lnames[9222], + ('hsl', + 'ls', + 'l', + 'm', + 10, + 0, + 10, + 10, + 52, + 27, + 41, + 27, + 12, + 27), + 'm', + 0, + NPC_REGULAR), + 9223: (9714, + lnames[9223], + ('fsl', + 'ms', + 'm', + 'm', + 20, + 0, + 20, + 20, + 43, + 27, + 32, + 27, + 0, + 0), + 'm', + 0, + NPC_REGULAR), + 9224: (9718, + lnames[9224], + ('css', + 'ms', + 'm', + 'f', + 1, + 0, + 1, + 1, + 6, + 8, + 6, + 8, + 6, + 8), + 'f', + 0, + NPC_REGULAR), + 9225: (9717, + lnames[9225], + ('rss', + 'md', + 'm', + 'f', + 11, + 0, + 11, + 11, + 40, + 27, + 29, + 27, + 0, + 27), + 'f', + 0, + NPC_REGULAR), + 9226: (9715, + lnames[9226], + ('mls', + 'ms', + 's', + 'm', + 12, + 0, + 12, + 12, + 3, + 10, + 3, + 10, + 6, + 10), + 'm', + 0, + NPC_REGULAR), + 9227: (9721, + lnames[9227], + ('cls', + 'ss', + 's', + 'm', + 13, + 0, + 13, + 13, + 8, + 5, + 8, + 5, + 3, + 18), + 'm', + 0, + NPC_REGULAR), + 9228: (9720, + lnames[9228], + ('fss', + 'sd', + 's', + 'f', + 4, + 0, + 4, + 4, + 15, + 5, + 11, + 5, + 8, + 5), + 'f', + 0, + NPC_REGULAR), + 9229: (9708, + lnames[9229], + ('css', + 'ld', + 'm', + 'f', + 4, + 0, + 4, + 4, + 22, + 21, + 0, + 21, + 4, + 21), + 'f', + 0, + NPC_REGULAR), + 9230: (9719, + lnames[9230], + ('mss', + 'ss', + 's', + 'm', + 8, + 0, + 8, + 8, + 53, + 27, + 42, + 27, + 13, + 27), + 'm', + 0, + NPC_REGULAR), + 9231: (9722, + lnames[9231], + ('dll', + 'ss', + 's', + 'm', + 6, + 0, + 6, + 6, + 27, + 27, + 18, + 27, + 3, + 8), + 'm', + 0, + NPC_REGULAR), + 9232: (9759, + lnames[9232], + ('pss', + 'ld', + 'm', + 'f', + 21, + 0, + 21, + 21, + 0, + 27, + 0, + 27, + 13, + 27), + 'f', + 0, + NPC_REGULAR), + 9233: (9756, + lnames[9233], + ('csl', + 'ls', + 'l', + 'f', + 22, + 0, + 22, + 22, + 1, + 7, + 1, + 7, + 12, + 27), + 'f', + 0, + NPC_HQ), + 9234: (9756, + lnames[9234], + ('cls', + 'ss', + 'l', + 'm', + 14, + 0, + 14, + 14, + 1, + 5, + 1, + 5, + 0, + 19), + 'm', + 0, + NPC_HQ), + 9235: (9756, + lnames[9235], + ('dll', + 'ls', + 'l', + 'm', + 6, + 0, + 6, + 6, + 1, + 6, + 1, + 6, + 0, + 16), + 'm', + 0, + NPC_HQ), + 9236: (9756, + lnames[9236], + ('dss', + 'ms', + 'l', + 'm', + 20, + 0, + 20, + 20, + 0, + 6, + 0, + 6, + 0, + 13), + 'm', + 0, + NPC_HQ), + 9237: (9255, + lnames[9237], + ('dls', + 'ss', + 'l', + 'm', + 14, + 0, + 14, + 14, + 0, + 7, + 0, + 7, + 0, + 10), + 'm', + 0, + NPC_FISHERMAN), + 9301: (-1, + lnames[9301], + ('pss', + 'ms', + 'l', + 'm', + 20, + 0, + 20, + 20, + 26, + 0, + 0, + 0, + 15, + 0), + 'm', + 0, + NPC_REGULAR), + 9302: (-1, + lnames[9302], + ('bsl', + 'ms', + 'm', + 'f', + 20, + 0, + 20, + 20, + 3, + 4, + 0, + 0, + 5, + 18), + 'f', + 0, + NPC_REGULAR), + 9303: (-1, + lnames[9303], + ('bll', + 'ss', + 's', + 'm', + 11, + 0, + 11, + 11, + 3, + 6, + 0, + 0, + 1, + 2), + 'm', + 0, + NPC_REGULAR), + 9304: (-1, + lnames[9304], + ('ssl', + 'sd', + 'l', + 'f', + 13, + 0, + 13, + 13, + 1, + 2, + 0, + 0, + 0, + 10), + 'f', + 0, + NPC_REGULAR), + 9305: (-1, + lnames[9305], + ('hll', + 'ls', + 'l', + 'm', + 8, + 0, + 8, + 8, + 1, + 3, + 0, + 0, + 1, + 16), + 'm', + 0, + NPC_REGULAR), + 9306: (-1, + lnames[9306], + ('dsl', + 'ms', + 's', + 'm', + 5, + 0, + 5, + 5, + 1, + 0, + 0, + 0, + 0, + 4), + 'm', + 0, + NPC_REGULAR), + 9307: (-1, + lnames[9307], + ('pls', + 'ls', + 's', + 'f', + 14, + 0, + 14, + 14, + 0, + 11, + 0, + 0, + 5, + 9), + 'f', + 0, + NPC_REGULAR), + 9308: (-1, + lnames[9308], + ('bsl', + 'ls', + 'l', + 'm', + 12, + 0, + 12, + 12, + 1, + 10, + 0, + 0, + 1, + 13), + 'm', + 0, + NPC_REGULAR), + 9309: (-1, + lnames[9309], + ('sss', + 'ms', + 'l', + 'm', + 2, + 0, + 2, + 2, + 0, + 4, + 0, + 0, + 0, + 6), + 'm', + 0, + NPC_REGULAR), + 9310: (-1, + lnames[9310], + ('fsl', + 'ls', + 'm', + 'm', + 17, + 0, + 17, + 17, + 4, + 4, + 0, + 0, + 0, + 10), + 'm', + 0, + NPC_REGULAR), + 9311: (-1, + lnames[9311], + ('mss', + 'sd', + 's', + 'f', + 24, + 0, + 24, + 24, + 3, + 1, + 0, + 0, + 0, + 13), + 'f', + 0, + NPC_REGULAR), + 9312: (-1, + lnames[9312], + ('pss', + 'sd', + 'l', + 'f', + 9, + 0, + 9, + 9, + 0, + 8, + 0, + 0, + 11, + 0), + 'f', + 0, + NPC_REGULAR), + 7001: (-1, + lnames[7001], + ('bss', + 'md', + 'm', + 'f', + 25, + 0, + 25, + 25, + 6, + 12, + 0, + 0, + 0, + 2), + 'f', + 0, + NPC_REGULAR), + 7002: (-1, + lnames[7002], + ('sss', + 'ms', + 'l', + 'm', + 7, + 0, + 7, + 7, + 18, + 11, + 0, + 0, + 4, + 3), + 'm', + 0, + NPC_REGULAR), + 7003: (-1, + lnames[7003], + ('sss', + 'md', + 'm', + 'f', + 21, + 0, + 21, + 21, + 45, + 0, + 0, + 0, + 7, + 6), + 'f', + 0, + NPC_REGULAR), + 7004: (-1, + lnames[7004], + ('pss', + 'ls', + 'l', + 'm', + 16, + 0, + 16, + 16, + 27, + 0, + 0, + 0, + 7, + 16), + 'm', + 0, + NPC_REGULAR), + 7005: (-1, + lnames[7005], + ('pls', + 'ld', + 's', + 'f', + 5, + 0, + 5, + 5, + 25, + 0, + 0, + 0, + 10, + 0), + 'f', + 0, + NPC_REGULAR), + 7006: (-1, + lnames[7006], + ('bll', + 'ms', + 's', + 'm', + 18, + 0, + 18, + 18, + 15, + 4, + 0, + 0, + 9, + 3), + 'm', + 0, + NPC_REGULAR), + 7007: (-1, + lnames[7007], + ('pls', + 'ls', + 's', + 'm', + 11, + 0, + 11, + 11, + 46, + 0, + 0, + 0, + 5, + 16), + 'm', + 0, + NPC_REGULAR), + 7008: (-1, + lnames[7008], + ('bls', + 'ld', + 's', + 'f', + 23, + 0, + 23, + 23, + 15, + 6, + 0, + 0, + 0, + 18), + 'f', + 0, + NPC_REGULAR), + 7009: (-1, + lnames[7009], + ('sll', + 'ss', + 's', + 'm', + 1, + 0, + 1, + 1, + 1, + 6, + 0, + 0, + 0, + 6), + 'm', + 0, + NPC_REGULAR)} +try: + config = simbase.config +except: + config = base.config + +if config.GetBool('want-new-toonhall', 1): + NPCToonDict[2001] = (2513, + lnames[2001], + ('dss', + 'ms', + 'm', + 'm', + 17, + 0, + 17, + 17, + 3, + 3, + 3, + 3, + 7, + 2), + 'm', + 1, + NPC_FLIPPYTOONHALL) +else: + NPCToonDict[2001] = (2513, + lnames[2001], + ('dss', + 'ms', + 'm', + 'm', + 17, + 0, + 17, + 17, + 3, + 3, + 3, + 3, + 7, + 2), + 'm', + 1, + NPC_REGULAR) +del lnames +BlockerPositions = {TTLocalizer.Flippy: (Point3(207.4, 18.81, -0.475), 90.0)} +zone2NpcDict = {} + +def generateZone2NpcDict(): + for id, npcDesc in NPCToonDict.items(): + zoneId = npcDesc[0] + if zone2NpcDict.has_key(zoneId): + zone2NpcDict[zoneId].append(id) + else: + zone2NpcDict[zoneId] = [id] + + +def getNPCName(npcId): + npc = NPCToonDict.get(npcId) + if npc: + return npc[1] + else: + return None + return None + + +def getNPCZone(npcId): + npc = NPCToonDict.get(npcId) + if npc: + return npc[0] + else: + return None + return None + + +def getBuildingArticle(zoneId): + return TTLocalizer.zone2TitleDict.get(zoneId, 'Toon Building')[1] + + +def getBuildingTitle(zoneId): + return TTLocalizer.zone2TitleDict.get(zoneId, 'Toon Building')[0] + + +HQnpcFriends = {2001: (ToontownBattleGlobals.HEAL_TRACK, + 5, + ToontownGlobals.MaxHpLimit, + 5), + 2132: (ToontownBattleGlobals.HEAL_TRACK, + 5, + 70, + 4), + 2121: (ToontownBattleGlobals.HEAL_TRACK, + 5, + 45, + 3), + 2011: (ToontownBattleGlobals.TRAP_TRACK, + 4, + 180, + 5), + 3007: (ToontownBattleGlobals.TRAP_TRACK, + 4, + 70, + 4), + 1001: (ToontownBattleGlobals.TRAP_TRACK, + 4, + 50, + 3), + 3112: (ToontownBattleGlobals.LURE_TRACK, + 5, + 0, + 5), + 1323: (ToontownBattleGlobals.LURE_TRACK, + 5, + 0, + 3), + 2308: (ToontownBattleGlobals.LURE_TRACK, + 5, + 0, + 3), + 4119: (ToontownBattleGlobals.SOUND_TRACK, + 5, + 80, + 5), + 4219: (ToontownBattleGlobals.SOUND_TRACK, + 5, + 50, + 4), + 4115: (ToontownBattleGlobals.SOUND_TRACK, + 5, + 40, + 3), + 1116: (ToontownBattleGlobals.DROP_TRACK, + 5, + 170, + 5), + 2311: (ToontownBattleGlobals.DROP_TRACK, + 5, + 100, + 4), + 4140: (ToontownBattleGlobals.DROP_TRACK, + 5, + 60, + 3), + 3137: (ToontownBattleGlobals.NPC_COGS_MISS, + 0, + 0, + 4), + 4327: (ToontownBattleGlobals.NPC_COGS_MISS, + 0, + 0, + 4), + 4230: (ToontownBattleGlobals.NPC_COGS_MISS, + 0, + 0, + 4), + 3135: (ToontownBattleGlobals.NPC_TOONS_HIT, + 0, + 0, + 4), + 2208: (ToontownBattleGlobals.NPC_TOONS_HIT, + 0, + 0, + 4), + 5124: (ToontownBattleGlobals.NPC_TOONS_HIT, + 0, + 0, + 4), + 2003: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + -1, + 0, + 5), + 2126: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + ToontownBattleGlobals.HEAL_TRACK, + 0, + 3), + 4007: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + ToontownBattleGlobals.TRAP_TRACK, + 0, + 3), + 1315: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + ToontownBattleGlobals.LURE_TRACK, + 0, + 3), + 5207: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + ToontownBattleGlobals.SQUIRT_TRACK, + 0, + 3), + 3129: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + ToontownBattleGlobals.THROW_TRACK, + 0, + 3), + 4125: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + ToontownBattleGlobals.SOUND_TRACK, + 0, + 3), + 1329: (ToontownBattleGlobals.NPC_RESTOCK_GAGS, + ToontownBattleGlobals.DROP_TRACK, + 0, + 3)} +FOnpcFriends = {9310: (ToontownBattleGlobals.LURE_TRACK, + 1, + 0, + 0), + 9311: (ToontownBattleGlobals.LURE_TRACK, + 1, + 0, + 1), + 9312: (ToontownBattleGlobals.LURE_TRACK, + 3, + 0, + 2), + 9307: (ToontownBattleGlobals.SOUND_TRACK, + 1, + 10, + 0), + 9308: (ToontownBattleGlobals.SOUND_TRACK, + 3, + 20, + 1), + 9309: (ToontownBattleGlobals.SOUND_TRACK, + 4, + 30, + 2), + 9304: (ToontownBattleGlobals.DROP_TRACK, + 1, + 20, + 0), + 9305: (ToontownBattleGlobals.DROP_TRACK, + 2, + 35, + 1), + 9306: (ToontownBattleGlobals.DROP_TRACK, + 3, + 50, + 2), + 9301: (ToontownBattleGlobals.HEAL_TRACK, + 3, + 10, + 0), + 9302: (ToontownBattleGlobals.HEAL_TRACK, + 3, + 20, + 1), + 9303: (ToontownBattleGlobals.HEAL_TRACK, + 3, + 30, + 2)} +npcFriends = dict(HQnpcFriends) +npcFriends.update(FOnpcFriends) + +def getNPCName(npcId): + if NPCToonDict.has_key(npcId): + return NPCToonDict[npcId][1] + return None + + +def npcFriendsMinMaxStars(minStars, maxStars): + return [ id for id in npcFriends.keys() if getNPCTrackLevelHpRarity(id)[3] >= minStars and getNPCTrackLevelHpRarity(id)[3] <= maxStars ] + + +def getNPCTrack(npcId): + if npcFriends.has_key(npcId): + return npcFriends[npcId][0] + return None + + +def getNPCTrackHp(npcId): + if npcFriends.has_key(npcId): + track, level, hp, rarity = npcFriends[npcId] + return (track, hp) + return (None, None) + + +def getNPCTrackLevelHp(npcId): + if npcFriends.has_key(npcId): + track, level, hp, rarity = npcFriends[npcId] + return (track, level, hp) + return (None, None, None) + + +def getNPCTrackLevelHpRarity(npcId): + if npcFriends.has_key(npcId): + return npcFriends[npcId] + return (None, None, None, None) diff --git a/toontown/toon/PlayerDetailPanel.py b/toontown/toon/PlayerDetailPanel.py new file mode 100644 index 0000000..0593141 --- /dev/null +++ b/toontown/toon/PlayerDetailPanel.py @@ -0,0 +1,126 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +import DistributedToon +from toontown.friends import FriendInviter +import ToonTeleportPanel +from toontown.toonbase import TTLocalizer +from toontown.hood import ZoneUtil +from toontown.toonbase.ToontownBattleGlobals import Tracks, Levels +globalAvatarDetail = None + +def showPlayerDetail(avId, avName, playerId = None): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + globalAvatarDetail = PlayerDetailPanel(avId, avName, playerId) + return + + +def hidePlayerDetail(): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + return + + +def unloadPlayerDetail(): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + return + + +class PlayerDetailPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonAvatarDetailPanel') + + def __init__(self, avId, avName, playerId = None, parent = aspect2dp, **kw): + self.playerId = playerId + self.isPlayer = 0 + self.playerInfo = None + if playerId: + self.isPlayer = 1 + if base.cr.playerFriendsManager.playerId2Info.has_key(playerId): + self.playerInfo = base.cr.playerFriendsManager.playerId2Info[playerId] + if not self.playerInfo.onlineYesNo: + avId = None + else: + avId = None + self.avId = avId + self.avName = avName + self.avatar = None + self.createdAvatar = None + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + detailPanel = gui.find('**/avatarInfoPanel') + textScale = 0.132 + textWrap = 10.4 + if self.playerId: + textScale = 0.1 + textWrap = 18.0 + optiondefs = (('pos', (0.525, 0.0, 0.525), None), + ('scale', 0.5, None), + ('relief', None, None), + ('image', detailPanel, None), + ('image_color', GlobalDialogColor, None), + ('text', '', None), + ('text_wordwrap', textWrap, None), + ('text_scale', textScale, None), + ('text_pos', (-0.125, 0.75), None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.dataText = DirectLabel(self, text='', text_scale=0.085, text_align=TextNode.ALeft, text_wordwrap=15, relief=None, pos=(-0.85, 0.0, 0.725)) + if self.avId: + self.avText = DirectLabel(self, text=TTLocalizer.PlayerToonName % {'toonname': self.avName}, text_scale=0.09, text_align=TextNode.ALeft, text_wordwrap=15, relief=None, pos=(-0.85, 0.0, 0.56)) + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.gotoToonButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=1.15, text=TTLocalizer.PlayerShowToon, text_scale=0.08, text_pos=(0.0, -0.02), textMayChange=0, pos=(0.43, 0, 0.415), command=self.__showToon) + ToonTeleportPanel.hideTeleportPanel() + FriendInviter.hideFriendInviter() + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.AvatarDetailPanelCancel, text_scale=0.05, text_pos=(0.12, -0.01), pos=(-0.865, 0.0, -0.765), scale=2.0, command=self.__handleCancel) + self.bCancel.show() + self.initialiseoptions(PlayerDetailPanel) + self.__showData() + buttons.removeNode() + gui.removeNode() + return + + def cleanup(self): + if self.createdAvatar: + self.avatar.delete() + self.createdAvatar = None + self.destroy() + return + + def __handleCancel(self): + unloadPlayerDetail() + + def __showData(self): + if self.isPlayer and self.playerInfo: + if self.playerInfo.onlineYesNo: + someworld = self.playerInfo.location + else: + someworld = TTLocalizer.OfflineLocation + text = TTLocalizer.AvatarDetailPanelPlayer % {'player': self.playerInfo.playerName, + 'world': someworld} + else: + text = TTLocalizer.AvatarDetailPanelOffline + self.dataText['text'] = text + + def __showToon(self): + messenger.send('wakeup') + hasManager = hasattr(base.cr, 'playerFriendsManager') + handle = base.cr.identifyFriend(self.avId) + if not handle and hasManager: + handle = base.cr.playerFriendsManager.getAvHandleFromId(self.avId) + if handle != None: + self.notify.info("Clicked on name in friend's list. doId = %s" % handle.doId) + messenger.send('clickedNametagPlayer', [handle, self.playerId, 0]) + return diff --git a/toontown/toon/PlayerInfoPanel.py b/toontown/toon/PlayerInfoPanel.py new file mode 100644 index 0000000..f8ed642 --- /dev/null +++ b/toontown/toon/PlayerInfoPanel.py @@ -0,0 +1,401 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from toontown.friends import FriendHandle +from otp.avatar import Avatar +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.friends import ToontownFriendSecret +import ToonAvatarDetailPanel +import AvatarPanelBase +import PlayerDetailPanel +from otp.otpbase import OTPGlobals +GAME_LOGO_NAMES = {'Default': 'GameLogo_Unknown', + 'Disney XD': 'GameLogo_XD', + 'Toontown': 'GameLogo_Toontown', + 'Pirates': 'GameLogo_Pirates'} +GAME_LOGO_FILE = 'phase_3/models/misc/game_logo_card' + +class PlayerInfoPanel(AvatarPanelBase.AvatarPanelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('PlayerInfoPanel') + + def __init__(self, playerId): + from toontown.friends import FriendsListPanel + AvatarPanelBase.AvatarPanelBase.__init__(self, None, FriendsListPanel=FriendsListPanel) + self.setup(playerId) + self.avId = 0 + self.avName = None + return + + def setup(self, playerId): + from toontown.friends import FriendsListPanel + self.playerId = playerId + self.playerInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId) + if not self.playerInfo: + return + avId = None + avatar = None + if playerId: + if self.playerInfo.onlineYesNo: + avId = self.playerInfo.avatarId + avatar = base.cr.playerFriendsManager.identifyFriend(avId) + self.notify.debug('Opening player panel, %s' % self.playerInfo) + self.avatar = avatar + self.noAv = 0 + if not avatar: + self.noAv = 1 + self.accountText = None + self.listName = ' ' + world = self.playerInfo.location + if self.playerInfo.onlineYesNo == 0: + world = TTLocalizer.AvatarDetailPanelRealLife + self.accountText = self.playerInfo.playerName + if self.noAv: + avButtonState = DGG.DISABLED + else: + avButtonState = DGG.NORMAL + self.online = self.playerInfo.onlineYesNo + if self.online: + onlineButtonState = DGG.NORMAL + else: + onlineButtonState = DGG.DISABLED + base.localAvatar.obscureFriendsListButton(1) + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.frame = DirectFrame( + image=gui.find('**/avatar_panel'), + relief=None, + pos=(1.1, 100, 0.525)) + disabledImageColor = Vec4(1, 1, 1, 0.4) + text0Color = Vec4(1, 1, 1, 1) + text1Color = Vec4(0.5, 1, 0.5, 1) + text2Color = Vec4(1, 1, 0.5, 1) + text3Color = Vec4(0.6, 0.6, 0.6, 1) + if self.playerInfo: + logoImageName = GAME_LOGO_NAMES['Default'] + if not self.playerInfo.onlineYesNo: + logoImageName = GAME_LOGO_NAMES['Default'] + elif GAME_LOGO_NAMES.has_key(self.playerInfo.location): + logoImageName = GAME_LOGO_NAMES[self.playerInfo.location] + model = loader.loadModel(GAME_LOGO_FILE) + logoImage = model.find('**/' + logoImageName) + del model + self.outsideLogo = DirectLabel( + parent=self.frame, + relief=None, + image=logoImage, + pos=(0.0125, 0.0, 0.25), + image_color=(1.0, 1.0, 1.0, 1), + scale=(0.175, 1, 0.175)) + font = ToontownGlobals.getInterfaceFont() + textScale = 0.047 + textWrap = 7.5 + textAlign = TextNode.ACenter + textPos = (0, 0) + self.nameLabel = DirectLabel( + parent=self.frame, + pos=(0.0125, 0, 0.385), + relief=None, + text=self.listName, + text_font=font, + text_fg=Vec4(0, 0, 0, 1), + text_pos=textPos, + text_scale=textScale, + text_wordwrap=textWrap, + text_align=textAlign, + text_shadow=(1, 1, 1, 1)) + if self.accountText: + self.accountLabel = DirectLabel( + parent=self.frame, + pos=(0.0125, 0, 0.385), + text=self.accountText, + relief=None, + text_font=font, + text_fg=Vec4(0, 0, 0, 1), + text_pos=textPos, + text_scale=textScale, + text_wordwrap=textWrap, + text_align=textAlign, + text_shadow=(1, 1, 1, 1)) + self.accountLabel.show() + self.closeButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), + relief=None, + pos=(0.157644, 0, -0.379167), + command=self.__handleClose) + self.friendButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Frnds_Btn_UP'), + gui.find('**/Frnds_Btn_DN'), + gui.find('**/Frnds_Btn_RLVR'), + gui.find('**/Frnds_Btn_UP')), + image3_color=disabledImageColor, + image_scale=0.9, + relief=None, + text=TTLocalizer.AvatarPanelFriends, + text_scale=0.06, + pos=(-0.103, 0, 0.133), + text0_fg=text0Color, + text1_fg=text1Color, + text2_fg=text2Color, + text3_fg=text3Color, + text_pos=(0.06, -0.02), + text_align=TextNode.ALeft, + state=avButtonState, + command=self.__handleFriend) + self.friendButton['state'] = DGG.DISABLED + self.goToButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Go2_Btn_UP'), + gui.find('**/Go2_Btn_DN'), + gui.find('**/Go2_Btn_RLVR'), + gui.find('**/Go2_Btn_UP')), + image3_color=disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, 0.045), + text=TTLocalizer.AvatarPanelGoTo, + text0_fg=text0Color, + text1_fg=text1Color, + text2_fg=text2Color, + text3_fg=text3Color, + text_scale=0.06, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + state=avButtonState, + command=self.__handleGoto) + self.goToButton['state'] = DGG.DISABLED + self.whisperButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/ChtBx_ChtBtn_UP'), + gui.find('**/ChtBx_ChtBtn_DN'), + gui.find('**/ChtBx_ChtBtn_RLVR'), + gui.find('**/ChtBx_ChtBtn_UP')), + image3_color=disabledImageColor, + relief=None, + image_scale=0.9, + pos=(-0.103, 0, -0.0375), + text=TTLocalizer.AvatarPanelWhisper, + text0_fg=text0Color, + text1_fg=text1Color, + text2_fg=text2Color, + text3_fg=text3Color, + text_scale=TTLocalizer.PIPwisperButton, + text_pos=(0.06, -0.0125), + text_align=TextNode.ALeft, + state=onlineButtonState, + command=self.__handleWhisper) + self.secretsButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/ChtBx_ChtBtn_UP'), + gui.find('**/ChtBx_ChtBtn_DN'), + gui.find('**/ChtBx_ChtBtn_RLVR'), + gui.find('**/ChtBx_ChtBtn_UP')), + image3_color=disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, -0.13), + text=TTLocalizer.AvatarPanelSecrets, + text0_fg=text0Color, + text1_fg=text1Color, + text2_fg=text2Color, + text3_fg=text3Color, + text_scale=TTLocalizer.PIPsecretsButton, + text_pos=(0.055, -0.01), + text_align=TextNode.ALeft, + state=avButtonState, + command=self.__handleSecrets) + self.secretsButton['state'] = DGG.DISABLED + if not base.localAvatar.isTeleportAllowed(): + self.goToButton['state'] = DGG.DISABLED + ignoreStr, ignoreCmd, ignoreSize = self.getIgnoreButtonInfo() + self.ignoreButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Ignore_Btn_UP'), + gui.find('**/Ignore_Btn_DN'), + gui.find('**/Ignore_Btn_RLVR'), + gui.find('**/Ignore_Btn_UP')), + image3_color=disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103697, 0, -0.21), + text=ignoreStr, + text0_fg=text0Color, + text1_fg=text1Color, + text2_fg=text2Color, + text3_fg=text3Color, + text_scale=ignoreSize, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + state=avButtonState, + command=ignoreCmd) + if base.cr.productName not in ['JP', 'DE', 'BR', 'FR']: + self.reportButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/report_BtnUP'), + gui.find('**/report_BtnDN'), + gui.find('**/report_BtnRLVR'), + gui.find('**/report_BtnUP')), + image3_color=disabledImageColor, + image_scale=0.65, + relief=None, + pos=(-0.103, 0, -0.29738), + text=TTLocalizer.AvatarPanelReport, + text0_fg=text0Color, + text1_fg=text1Color, + text2_fg=text2Color, + text3_fg=text3Color, + text_scale=0.06, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + command=self.handleReport) + self.detailButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/ChtBx_BackBtn_UP'), + gui.find('**/ChtBx_BackBtn_DN'), + gui.find('**/ChtBx_BackBtn_Rllvr'), + gui.find('**/ChtBx_BackBtn_UP')), + relief=None, + text=('', TTLocalizer.PlayerPanelDetail, + TTLocalizer.PlayerPanelDetail, ''), + text_fg=text2Color, + text_shadow=(0, 0, 0, 1), + text_scale=TTLocalizer.PIPdetailButton, + text_pos=(0.085, 0.055), + text_align=TextNode.ACenter, + pos=(-0.133773, 0, -0.387132), + state=DGG.NORMAL, + command=self.__handleDetails) + gui.removeNode() + menuX = -0.05 + menuScale = 0.064 + self.frame.show() + messenger.send('avPanelDone') + self.accept('playerOnline', self.__handlePlayerChanged) + self.accept('playerOffline', self.__handlePlayerChanged) + self.accept(OTPGlobals.PlayerFriendUpdateEvent, self.__handlePlayerChanged) + self.accept(OTPGlobals.PlayerFriendRemoveEvent, self.__handlePlayerUnfriend) + return + + def disableAll(self): + self.detailButton['state'] = DGG.DISABLED + self.ignoreButton['state'] = DGG.DISABLED + if base.cr.productName not in ['JP', + 'DE', + 'BR', + 'FR']: + self.reportButton['state'] = DGG.DISABLED + self.goToButton['state'] = DGG.DISABLED + self.secretsButton['state'] = DGG.DISABLED + self.whisperButton['state'] = DGG.DISABLED + self.friendButton['state'] = DGG.DISABLED + self.closeButton['state'] = DGG.DISABLED + + def cleanup(self): + self.unsetup() + self.ignore('playerOnline') + self.ignore('playerOffline') + self.ignore(OTPGlobals.PlayerFriendUpdateEvent) + self.ignore(OTPGlobals.PlayerFriendRemoveEvent) + AvatarPanelBase.AvatarPanelBase.cleanup(self) + + def unsetup(self): + if not hasattr(self, 'frame') or self.frame == None: + return + PlayerDetailPanel.unloadPlayerDetail() + self.frame.destroy() + del self.frame + self.frame = None + base.localAvatar.obscureFriendsListButton(-1) + self.laffMeter = None + self.ignore('updateLaffMeter') + if hasattr(self.avatar, 'bFake') and self.avatar.bFake: + self.avatar.delete() + return + + def __handleGoto(self): + if base.localAvatar.isTeleportAllowed(): + base.localAvatar.chatMgr.noWhisper() + messenger.send('gotoAvatar', [self.avId, self.avName, self.avDisableName]) + + def __handleWhisper(self): + if self.noAv: + base.localAvatar.chatMgr.whisperTo(self.listName, 0, self.playerId) + else: + base.localAvatar.chatMgr.whisperTo(self.avName, self.avId, self.playerId) + + def __handleSecrets(self): + base.localAvatar.chatMgr.noWhisper() + ToontownFriendSecret.showFriendSecret(ToontownFriendSecret.BothSecrets) + + def __handleFriend(self): + base.localAvatar.chatMgr.noWhisper() + self.__getAvInfo() + messenger.send('friendAvatar', [self.avId, self.avName, self.avDisableName]) + + def __getAvInfo(self): + if self.playerId: + self.avId = self.playerInfo.avatarId + if self.avId: + avatar = base.cr.playerFriendsManager.identifyFriend(self.avId) + if avatar: + self.avName = avatar.getName() + if not self.avDisableName: + self.avDisableName = avatar.uniqueName('disable') + + def __handleDetails(self): + base.localAvatar.chatMgr.noWhisper() + self.__getAvInfo() + messenger.send('playerDetails', [self.avId, self.avName, self.playerId]) + + def handleDisableAvatar(self): + pass + + def __handlePlayerChanged(self, playerId, info = None): + if playerId == self.playerId: + self.unsetup() + self.setup(playerId) + + def __handlePlayerUnfriend(self, playerId): + if playerId == self.playerId: + self.__handleClose() + + def __handleClose(self): + self.cleanup() + AvatarPanelBase.currentAvatarPanel = None + if self.friendsListShown: + self.FriendsListPanel.showFriendsList() + return + + def getAvId(self): + if hasattr(self, 'avatar'): + if self.avatar: + return self.avatar.doId + return None + + def getPlayerId(self): + if hasattr(self, 'playerId'): + return self.playerId + return None + + def isHidden(self): + if not hasattr(self, 'frame') or not self.frame: + return 1 + return self.frame.isHidden() + + def getType(self): + return 'player' diff --git a/toontown/toon/TTEmote.py b/toontown/toon/TTEmote.py new file mode 100644 index 0000000..bd5f481 --- /dev/null +++ b/toontown/toon/TTEmote.py @@ -0,0 +1,620 @@ +import Toon, ToonDNA +from direct.interval.IntervalGlobal import * +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +from otp.otpbase import OTPLocalizer +import types +from direct.showbase import PythonUtil +from pandac.PandaModules import * +from otp.avatar import Emote +from direct.directnotify import DirectNotifyGlobal +EmoteSleepIndex = 4 +EmoteClear = -1 + +def doVictory(toon, volume = 1): + duration = toon.getDuration('victory', 'legs') + sfx = base.loadSfx('phase_3.5/audio/sfx/ENC_Win.mp3') + sfxDuration = duration - 1.0 + sfxTrack = SoundInterval(sfx, loop=1, duration=sfxDuration, node=toon, volume=volume) + track = Sequence(Func(toon.play, 'victory'), sfxTrack, duration=0) + return (track, duration, None) + + +def doJump(toon, volume = 1): + track = Sequence(Func(toon.play, 'jump')) + return (track, 0, None) + + +def doDead(toon, volume = 1): + toon.animFSM.request('Sad') + return (None, 0, None) + + +def doAnnoyed(toon, volume = 1): + duration = toon.getDuration('angry', 'torso') + sfx = None + if toon.style.getAnimal() == 'bear': + sfx = base.loadSfx('phase_3.5/audio/dial/AV_bear_exclaim.mp3') + else: + sfx = base.loadSfx('phase_3.5/audio/sfx/avatar_emotion_angry.mp3') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Func(toon.play, 'angry'), Func(playSfx)) + exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes)) + return (track, duration, exitTrack) + + +def doAngryEyes(toon, volume = 1): + track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Wait(10.0), Func(toon.normalEyes)) + return (track, 0.1, None) + + +def doHappy(toon, volume = 1): + track = Sequence(Func(toon.play, 'jump'), Func(toon.normalEyes), Func(toon.blinkEyes)) + duration = toon.getDuration('jump', 'legs') + return (track, duration, None) + + +def doSad(toon, volume = 1): + track = Sequence(Func(toon.sadEyes), Func(toon.blinkEyes)) + exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes)) + return (track, 3, exitTrack) + + +def doSleep(toon, volume = 1): + duration = 4 + track = Sequence(Func(toon.stopLookAround), Func(toon.stopBlink), Func(toon.closeEyes), Func(toon.lerpLookAt, Point3(0, 1, -4)), Func(toon.loop, 'neutral'), Func(toon.setPlayRate, 0.4, 'neutral'), Func(toon.setChatAbsolute, TTLocalizer.ToonSleepString, CFThought)) + + def wakeUpFromSleepEmote(): + toon.startLookAround() + toon.openEyes() + toon.startBlink() + toon.setPlayRate(1, 'neutral') + if toon.nametag.getChat() == TTLocalizer.ToonSleepString: + toon.clearChat() + toon.lerpLookAt(Point3(0, 1, 0), time=0.25) + + exitTrack = Sequence(Func(wakeUpFromSleepEmote)) + return (track, duration, exitTrack) + + +def doYes(toon, volume = 1): + tracks = Parallel(autoFinish=1) + for lod in toon.getLODNames(): + h = toon.getPart('head', lod) + tracks.append(Sequence(LerpHprInterval(h, 0.1, Vec3(0, -30, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.15, Vec3(0, -20, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.15, Vec3(0, -20, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.1, Vec3(0, 0, 0)))) + + tracks.start() + return (None, 0, None) + + +def doNo(toon, volume = 1): + tracks = Parallel(autoFinish=1) + for lod in toon.getLODNames(): + h = toon.getPart('head', lod) + tracks.append(Sequence(LerpHprInterval(h, 0.1, Vec3(40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(20, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-20, 0, 0)), LerpHprInterval(h, 0.1, Vec3(0, 0, 0)))) + + tracks.start() + return (None, 0, None) + + +def doOk(toon, volume = 1): + return (None, 0, None) + + +def doShrug(toon, volume = 1): + sfx = base.loadSfx('phase_3.5/audio/sfx/avatar_emotion_shrug.mp3') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + track = Sequence(Func(toon.play, 'shrug'), Func(playSfx)) + duration = toon.getDuration('shrug', 'torso') + return (track, duration, None) + + +def doWave(toon, volume = 1): + track = Sequence(Func(toon.play, 'wave')) + duration = toon.getDuration('wave', 'torso') + return (track, duration, None) + + +def doApplause(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_applause.mp3') + + def playSfx(): + base.playSfx(sfx, volume=1, node=toon) + + track = Sequence(Func(toon.play, 'applause'), Func(playSfx)) + duration = toon.getDuration('applause', 'torso') + return (track, duration, None) + + +def doConfused(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_confused.mp3') + + def playSfx(): + base.playSfx(sfx, node=toon, volume=volume) + + track = Sequence(Func(toon.play, 'confused'), Func(playSfx)) + duration = toon.getDuration('confused', 'torso') + return (track, duration, None) + + +def doSlipForward(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + sfxDelay = 0.7 + track = Sequence(Func(toon.play, 'slip-forward'), Wait(sfxDelay), Func(playSfx)) + duration = toon.getDuration('slip-forward', 'torso') - sfxDelay + return (track, duration, None) + + +def doBored(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_bored.mp3') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + sfxDelay = 2.2 + track = Sequence(Func(toon.play, 'bored'), Wait(sfxDelay), Func(playSfx)) + duration = toon.getDuration('bored', 'torso') - sfxDelay + return (track, duration, None) + + +def doBow(toon, volume = 1): + if toon.style.torso[1] == 'd': + track = Sequence(Func(toon.play, 'curtsy')) + duration = toon.getDuration('curtsy', 'torso') + else: + track = Sequence(Func(toon.play, 'bow')) + duration = toon.getDuration('bow', 'torso') + return (track, duration, None) + + +def doSlipBackward(toon, volume = 1): + sfx = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') + + def playSfx(): + base.playSfx(sfx, volume=volume, node=toon) + + sfxDelay = 0.7 + track = Sequence(Func(toon.play, 'slip-backward'), Wait(sfxDelay), Func(playSfx)) + duration = toon.getDuration('slip-backward', 'torso') - sfxDelay + return (track, duration, None) + + +def doThink(toon, volume = 1): + duration = 47.0 / 24.0 * 2 + animTrack = Sequence(ActorInterval(toon, 'think', startFrame=0, endFrame=46), ActorInterval(toon, 'think', startFrame=46, endFrame=0)) + track = Sequence(animTrack, duration=0) + return (track, duration, None) + + +def doCringe(toon, volume = 1): + track = Sequence(Func(toon.play, 'cringe')) + duration = toon.getDuration('cringe', 'torso') + return (track, duration, None) + + +def doResistanceSalute(toon, volume = 1): + playRate = 0.75 + duration = 10.0 / 24.0 * (1 / playRate) * 2 + animTrack = Sequence(Func(toon.setChatAbsolute, OTPLocalizer.CustomSCStrings[4020], CFSpeech | CFTimeout), Func(toon.setPlayRate, playRate, 'victory'), ActorInterval(toon, 'victory', playRate=playRate, startFrame=0, endFrame=9), ActorInterval(toon, 'victory', playRate=playRate, startFrame=9, endFrame=0)) + track = Sequence(animTrack, duration=0) + return (track, duration, None) + + +def doNothing(toon, volume = 1): + return (None, 0, None) + + +def doSurprise(toon, volume = 1): + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_surprise.mp3') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(anim): + anim.start() + + def stopAnim(anim): + anim.finish() + toon.stop() + sfx.stop() + + anim = Sequence(ActorInterval(toon, 'conked', startFrame=9, endFrame=50), ActorInterval(toon, 'conked', startFrame=70, endFrame=101)) + track = Sequence(Func(toon.stopBlink), Func(toon.surpriseEyes), Func(toon.showSurpriseMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume))) + exitTrack = Sequence(Func(toon.hideSurpriseMuzzle), Func(toon.openEyes), Func(toon.startBlink), Func(stopAnim, anim)) + return (track, 3.0, exitTrack) + + +def doUpset(toon, volume = 1): + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_very_sad_1.mp3') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(anim): + anim.start() + + def stopAnim(anim): + anim.finish() + toon.stop() + sfx.stop() + + anim = Sequence(ActorInterval(toon, 'bad-putt', startFrame=29, endFrame=59, playRate=-0.75), ActorInterval(toon, 'bad-putt', startFrame=29, endFrame=59, playRate=0.75)) + track = Sequence(Func(toon.sadEyes), Func(toon.blinkEyes), Func(toon.showSadMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume))) + exitTrack = Sequence(Func(toon.hideSadMuzzle), Func(toon.normalEyes), Func(stopAnim, anim)) + return (track, 4.0, exitTrack) + + +def doDelighted(toon, volume = 1): + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/delighted_06.mp3') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(anim): + anim.start() + + def stopAnim(anim): + anim.finish() + toon.stop() + sfx.stop() + + anim = Sequence(ActorInterval(toon, 'left'), Wait(1), ActorInterval(toon, 'left', playRate=-1)) + track = Sequence(Func(toon.blinkEyes), Func(toon.showSmileMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume))) + exitTrack = Sequence(Func(toon.hideSmileMuzzle), Func(toon.blinkEyes), Func(stopAnim, anim)) + return (track, 2.5, exitTrack) + + +def doFurious(toon, volume = 1): + duration = toon.getDuration('angry', 'torso') + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/furious_03.mp3') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Func(toon.showAngryMuzzle), Func(toon.play, 'angry'), Func(playSfx, volume)) + exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes), Func(toon.hideAngryMuzzle)) + return (track, duration, exitTrack) + + +def doLaugh(toon, volume = 1): + sfx = None + sfx = base.loadSfx('phase_4/audio/sfx/avatar_emotion_laugh.mp3') + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(): + toon.setPlayRate(10, 'neutral') + toon.loop('neutral') + + def stopAnim(): + toon.setPlayRate(1, 'neutral') + + track = Sequence(Func(toon.blinkEyes), Func(toon.showLaughMuzzle), Func(playAnim), Func(playSfx, volume)) + exitTrack = Sequence(Func(toon.hideLaughMuzzle), Func(toon.blinkEyes), Func(stopAnim)) + return (track, 2, exitTrack) + + +def getSingingNote(toon, note, volume = 1): + sfx = None + filePath = 'phase_3.5/audio/dial/' + filePrefix = 'tt_s_dlg_sng_' + fileSuffix = '.mp3' + speciesName = ToonDNA.getSpeciesName(toon.style.head) + sfx = base.loadSfx(filePath + filePrefix + speciesName + '_' + note + fileSuffix) + + def playSfx(volume = 1): + base.playSfx(sfx, volume=volume, node=toon) + + def playAnim(): + toon.loop('neutral') + + def stopAnim(): + toon.setPlayRate(1, 'neutral') + + track = Sequence(Func(toon.showSurpriseMuzzle), Parallel(Func(playAnim), Func(playSfx, volume))) + exitTrack = Sequence(Func(toon.hideSurpriseMuzzle), Func(stopAnim)) + return (track, 0.1, exitTrack) + + +def playSingingAnim(toon): + pass + + +def stopSinginAnim(toon): + pass + + +def singNote1(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'g1') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'g2') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'g3') + + +def singNote2(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'a1') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'a2') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'a3') + + +def singNote3(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'b1') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'b2') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'b3') + + +def singNote4(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'c1') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'c2') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'c3') + + +def singNote5(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'd1') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'd2') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'd3') + + +def singNote6(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'e1') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'e2') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'e3') + + +def singNote7(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'f1') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'f2') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'f3') + + +def singNote8(toon, volume = 1): + if base.config.GetBool('want-octaves', True): + if toon.style.getTorsoSize() == 'short': + return getSingingNote(toon, 'g2') + elif toon.style.getTorsoSize() == 'medium': + return getSingingNote(toon, 'g3') + elif toon.style.getTorsoSize() == 'long': + return getSingingNote(toon, 'g4') + + +def singNoteEmpty(toon, volume = 0): + track = Sequence() + return (track, 0.1, None) + + +def returnToLastAnim(toon): + if hasattr(toon, 'playingAnim') and toon.playingAnim: + toon.loop(toon.playingAnim) + elif not hasattr(toon, 'hp') or toon.hp > 0: + toon.loop('neutral') + else: + toon.loop('sad-neutral') + + +EmoteFunc = [[doWave, 0], + [doHappy, 0], + [doSad, 0], + [doAnnoyed, 0], + [doSleep, 0], + [doShrug, 0], + [doVictory, 0], + [doThink, 0], + [doBored, 0], + [doApplause, 0], + [doCringe, 0], + [doConfused, 0], + [doSlipForward, 0], + [doBow, 0], + [doSlipBackward, 0], + [doResistanceSalute, 0], + [doNothing, 0], + [doYes, 0], + [doNo, 0], + [doOk, 0], + [doSurprise, 0], + [doUpset, 0], + [doDelighted, 0], + [doFurious, 0], + [doLaugh, 0]] + +class TTEmote(Emote.Emote): + notify = DirectNotifyGlobal.directNotify.newCategory('TTEmote') + SLEEP_INDEX = 4 + + def __init__(self): + self.emoteFunc = EmoteFunc + self.bodyEmotes = [0, + 1, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 20, + 21, + 22, + 23, + 24] + self.headEmotes = [2, + 17, + 18, + 19] + if len(self.emoteFunc) != len(OTPLocalizer.EmoteList): + self.notify.error('Emote.EmoteFunc and OTPLocalizer.EmoteList are different lengths.') + self.track = None + self.stateChangeMsgLocks = 0 + self.stateHasChanged = 0 + return + + def lockStateChangeMsg(self): + self.stateChangeMsgLocks += 1 + + def unlockStateChangeMsg(self): + if self.stateChangeMsgLocks <= 0: + print PythonUtil.lineTag() + ': someone unlocked too many times' + return + self.stateChangeMsgLocks -= 1 + if self.stateChangeMsgLocks == 0 and self.stateHasChanged: + messenger.send(self.EmoteEnableStateChanged) + self.stateHasChanged = 0 + + def emoteEnableStateChanged(self): + if self.stateChangeMsgLocks > 0: + self.stateHasChanged = 1 + else: + messenger.send(self.EmoteEnableStateChanged) + + def disableAll(self, toon, msg = None): + if toon != base.localAvatar: + return + self.disableGroup(range(len(self.emoteFunc)), toon) + + def releaseAll(self, toon, msg = None): + if toon != base.localAvatar: + return + self.enableGroup(range(len(self.emoteFunc)), toon) + + def disableBody(self, toon, msg = None): + if toon != base.localAvatar: + return + self.disableGroup(self.bodyEmotes, toon) + + def releaseBody(self, toon, msg = None): + if toon != base.localAvatar: + return + self.enableGroup(self.bodyEmotes, toon) + + def disableHead(self, toon, msg = None): + if toon != base.localAvatar: + return + self.disableGroup(self.headEmotes, toon) + + def releaseHead(self, toon, msg = None): + if toon != base.localAvatar: + return + self.enableGroup(self.headEmotes, toon) + + def getHeadEmotes(self): + return self.headEmotes + + def disableGroup(self, indices, toon): + self.lockStateChangeMsg() + for i in indices: + self.disable(i, toon) + + self.unlockStateChangeMsg() + + def enableGroup(self, indices, toon): + self.lockStateChangeMsg() + for i in indices: + self.enable(i, toon) + + self.unlockStateChangeMsg() + + def disable(self, index, toon): + if isinstance(index, types.StringType): + index = OTPLocalizer.EmoteFuncDict[index] + self.emoteFunc[index][1] = self.emoteFunc[index][1] + 1 + if toon is base.localAvatar: + if self.emoteFunc[index][1] == 1: + self.emoteEnableStateChanged() + + def enable(self, index, toon): + if isinstance(index, types.StringType): + index = OTPLocalizer.EmoteFuncDict[index] + self.emoteFunc[index][1] = self.emoteFunc[index][1] - 1 + if toon is base.localAvatar: + if self.emoteFunc[index][1] == 0: + self.emoteEnableStateChanged() + + def doEmote(self, toon, emoteIndex, ts = 0, volume = 1): + try: + func = self.emoteFunc[emoteIndex][0] + except: + print 'Error in finding emote func %s' % emoteIndex + return (None, None) + + def clearEmoteTrack(): + base.localAvatar.emoteTrack = None + base.localAvatar.d_setEmoteState(self.EmoteClear, 1.0) + return + + if volume == 1: + track, duration, exitTrack = func(toon) + else: + track, duration, exitTrack = func(toon, volume) + if track != None: + track = Sequence(Func(self.disableAll, toon, 'doEmote'), track) + if duration > 0: + track = Sequence(track, Wait(duration)) + if exitTrack != None: + track = Sequence(track, exitTrack) + if duration > 0: + track = Sequence(track, Func(returnToLastAnim, toon)) + track = Sequence(track, Func(self.releaseAll, toon, 'doEmote'), autoFinish=1) + if toon.isLocal(): + track = Sequence(track, Func(clearEmoteTrack)) + if track != None: + if toon.emote != None: + toon.emote.finish() + toon.emote = None + toon.emote = track + track.start(ts) + del clearEmoteTrack + return (track, duration) + + def printEmoteState(self, action, msg): + pass + + +Emote.globalEmote = TTEmote() diff --git a/toontown/toon/TailorClothesGUI.py b/toontown/toon/TailorClothesGUI.py new file mode 100644 index 0000000..ebf6f5b --- /dev/null +++ b/toontown/toon/TailorClothesGUI.py @@ -0,0 +1,21 @@ +from toontown.makeatoon import ClothesGUI +import ToonDNA + +class TailorClothesGUI(ClothesGUI.ClothesGUI): + notify = directNotify.newCategory('MakeClothesGUI') + + def __init__(self, doneEvent, swapEvent, tailorId): + ClothesGUI.ClothesGUI.__init__(self, ClothesGUI.CLOTHES_TAILOR, doneEvent, swapEvent) + self.tailorId = tailorId + + def setupScrollInterface(self): + self.dna = self.toon.getStyle() + gender = self.dna.getGender() + if self.swapEvent != None: + self.tops = ToonDNA.getTops(gender, tailorId=self.tailorId) + self.bottoms = ToonDNA.getBottoms(gender, tailorId=self.tailorId) + self.gender = gender + self.topChoice = -1 + self.bottomChoice = -1 + self.setupButtons() + return diff --git a/toontown/toon/Toon.py b/toontown/toon/Toon.py new file mode 100644 index 0000000..a4e914b --- /dev/null +++ b/toontown/toon/Toon.py @@ -0,0 +1,3039 @@ +from otp.avatar import Avatar +from otp.avatar.Avatar import teleportNotify +import ToonDNA +from direct.task.Task import Task +from toontown.suit import SuitDNA +from direct.actor import Actor +import string +from ToonHead import * +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from otp.otpbase import OTPLocalizer +from toontown.toonbase import TTLocalizer +import random +from toontown.effects import Wake +import TTEmote +from otp.avatar import Emote +import Motion +from toontown.hood import ZoneUtil +from toontown.battle import SuitBattleGlobals +from otp.otpbase import OTPGlobals +from toontown.effects import DustCloud +from direct.showbase.PythonUtil import Functor +from toontown.distributed import DelayDelete +import AccessoryGlobals +import types + +def teleportDebug(requestStatus, msg, onlyIfToAv = True): + if teleportNotify.getDebug(): + teleport = 'teleport' + if requestStatus.has_key('how') and requestStatus['how'][:len(teleport)] == teleport: + if not onlyIfToAv or requestStatus.has_key('avId') and requestStatus['avId'] > 0: + teleportNotify.debug(msg) + + +SLEEP_STRING = TTLocalizer.ToonSleepString +DogDialogueArray = [] +CatDialogueArray = [] +HorseDialogueArray = [] +RabbitDialogueArray = [] +MouseDialogueArray = [] +DuckDialogueArray = [] +MonkeyDialogueArray = [] +BearDialogueArray = [] +PigDialogueArray = [] +LegsAnimDict = {} +TorsoAnimDict = {} +HeadAnimDict = {} +Preloaded = [] +Phase3AnimList = (('neutral', 'neutral'), ('run', 'run')) +Phase3_5AnimList = (('walk', 'walk'), + ('teleport', 'teleport'), + ('book', 'book'), + ('jump', 'jump'), + ('running-jump', 'running-jump'), + ('jump-squat', 'jump-zstart'), + ('jump-idle', 'jump-zhang'), + ('jump-land', 'jump-zend'), + ('running-jump-squat', 'leap_zstart'), + ('running-jump-idle', 'leap_zhang'), + ('running-jump-land', 'leap_zend'), + ('pushbutton', 'press-button'), + ('throw', 'pie-throw'), + ('victory', 'victory-dance'), + ('sidestep-left', 'sidestep-left'), + ('conked', 'conked'), + ('cringe', 'cringe'), + ('wave', 'wave'), + ('shrug', 'shrug'), + ('angry', 'angry'), + ('tutorial-neutral', 'tutorial-neutral'), + ('left-point', 'left-point'), + ('right-point', 'right-point'), + ('right-point-start', 'right-point-start'), + ('give-props', 'give-props'), + ('give-props-start', 'give-props-start'), + ('right-hand', 'right-hand'), + ('right-hand-start', 'right-hand-start'), + ('duck', 'duck'), + ('sidestep-right', 'jump-back-right'), + ('periscope', 'periscope')) +Phase4AnimList = (('sit', 'sit'), + ('sit-start', 'intoSit'), + ('swim', 'swim'), + ('tug-o-war', 'tug-o-war'), + ('sad-walk', 'losewalk'), + ('sad-neutral', 'sad-neutral'), + ('up', 'up'), + ('down', 'down'), + ('left', 'left'), + ('right', 'right'), + ('applause', 'applause'), + ('confused', 'confused'), + ('bow', 'bow'), + ('curtsy', 'curtsy'), + ('bored', 'bored'), + ('think', 'think'), + ('battlecast', 'fish'), + ('cast', 'cast'), + ('castlong', 'castlong'), + ('fish-end', 'fishEND'), + ('fish-neutral', 'fishneutral'), + ('fish-again', 'fishAGAIN'), + ('reel', 'reel'), + ('reel-H', 'reelH'), + ('reel-neutral', 'reelneutral'), + ('pole', 'pole'), + ('pole-neutral', 'poleneutral'), + ('slip-forward', 'slip-forward'), + ('slip-backward', 'slip-backward'), + ('catch-neutral', 'gameneutral'), + ('catch-run', 'gamerun'), + ('catch-eatneutral', 'eat_neutral'), + ('catch-eatnrun', 'eatnrun'), + ('catch-intro-throw', 'gameThrow'), + ('swing', 'swing'), + ('pet-start', 'petin'), + ('pet-loop', 'petloop'), + ('pet-end', 'petend'), + ('scientistJealous', 'scientistJealous'), + ('scientistEmcee', 'scientistEmcee'), + ('scientistWork', 'scientistWork'), + ('scientistGame', 'scientistGame')) +Phase5AnimList = (('water-gun', 'water-gun'), + ('hold-bottle', 'hold-bottle'), + ('firehose', 'firehose'), + ('spit', 'spit'), + ('tickle', 'tickle'), + ('smooch', 'smooch'), + ('happy-dance', 'happy-dance'), + ('sprinkle-dust', 'sprinkle-dust'), + ('juggle', 'juggle'), + ('climb', 'climb'), + ('sound', 'shout'), + ('toss', 'toss'), + ('hold-magnet', 'hold-magnet'), + ('hypnotize', 'hypnotize'), + ('struggle', 'struggle'), + ('lose', 'lose'), + ('melt', 'melt')) +Phase5_5AnimList = (('takePhone', 'takePhone'), + ('phoneNeutral', 'phoneNeutral'), + ('phoneBack', 'phoneBack'), + ('bank', 'jellybeanJar'), + ('callPet', 'callPet'), + ('feedPet', 'feedPet'), + ('start-dig', 'into_dig'), + ('loop-dig', 'loop_dig'), + ('water', 'water')) +Phase6AnimList = (('headdown-putt', 'headdown-putt'), + ('into-putt', 'into-putt'), + ('loop-putt', 'loop-putt'), + ('rotateL-putt', 'rotateL-putt'), + ('rotateR-putt', 'rotateR-putt'), + ('swing-putt', 'swing-putt'), + ('look-putt', 'look-putt'), + ('lookloop-putt', 'lookloop-putt'), + ('bad-putt', 'bad-putt'), + ('badloop-putt', 'badloop-putt'), + ('good-putt', 'good-putt')) +Phase9AnimList = (('push', 'push'),) +Phase10AnimList = (('leverReach', 'leverReach'), ('leverPull', 'leverPull'), ('leverNeutral', 'leverNeutral')) +Phase12AnimList = () +if not base.config.GetBool('want-new-anims', 1): + LegDict = {'s': '/models/char/dogSS_Shorts-legs-', + 'm': '/models/char/dogMM_Shorts-legs-', + 'l': '/models/char/dogLL_Shorts-legs-'} + TorsoDict = {'s': '/models/char/dogSS_Naked-torso-', + 'm': '/models/char/dogMM_Naked-torso-', + 'l': '/models/char/dogLL_Naked-torso-', + 'ss': '/models/char/dogSS_Shorts-torso-', + 'ms': '/models/char/dogMM_Shorts-torso-', + 'ls': '/models/char/dogLL_Shorts-torso-', + 'sd': '/models/char/dogSS_Skirt-torso-', + 'md': '/models/char/dogMM_Skirt-torso-', + 'ld': '/models/char/dogLL_Skirt-torso-'} +else: + LegDict = {'s': '/models/char/tt_a_chr_dgs_shorts_legs_', + 'm': '/models/char/tt_a_chr_dgm_shorts_legs_', + 'l': '/models/char/tt_a_chr_dgl_shorts_legs_'} + TorsoDict = {'s': '/models/char/dogSS_Naked-torso-', + 'm': '/models/char/dogMM_Naked-torso-', + 'l': '/models/char/dogLL_Naked-torso-', + 'ss': '/models/char/tt_a_chr_dgs_shorts_torso_', + 'ms': '/models/char/tt_a_chr_dgm_shorts_torso_', + 'ls': '/models/char/tt_a_chr_dgl_shorts_torso_', + 'sd': '/models/char/tt_a_chr_dgs_skirt_torso_', + 'md': '/models/char/tt_a_chr_dgm_skirt_torso_', + 'ld': '/models/char/tt_a_chr_dgl_skirt_torso_'} + +def loadModels(): + global Preloaded + preloadAvatars = base.config.GetBool('preload-avatars', 0) + if preloadAvatars: + + def loadTex(path): + tex = loader.loadTexture(path) + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + Preloaded.append(tex) + + for shirt in ToonDNA.Shirts: + loadTex(shirt) + + for sleeve in ToonDNA.Sleeves: + loadTex(sleeve) + + for short in ToonDNA.BoyShorts: + loadTex(short) + + for bottom in ToonDNA.GirlBottoms: + loadTex(bottom[0]) + + for key in LegDict.keys(): + fileRoot = LegDict[key] + model = loader.loadModelNode('phase_3' + fileRoot + '1000') + Preloaded.append(model) + model = loader.loadModelNode('phase_3' + fileRoot + '500') + Preloaded.append(model) + model = loader.loadModelNode('phase_3' + fileRoot + '250') + Preloaded.append(model) + + for key in TorsoDict.keys(): + fileRoot = TorsoDict[key] + model = loader.loadModelNode('phase_3' + fileRoot + '1000') + Preloaded.append(model) + if len(key) > 1: + model = loader.loadModelNode('phase_3' + fileRoot + '500') + Preloaded.append(model) + model = loader.loadModelNode('phase_3' + fileRoot + '250') + Preloaded.append(model) + + for key in HeadDict.keys(): + fileRoot = HeadDict[key] + model = loader.loadModelNode('phase_3' + fileRoot + '1000') + Preloaded.append(model) + model = loader.loadModelNode('phase_3' + fileRoot + '500') + Preloaded.append(model) + model = loader.loadModelNode('phase_3' + fileRoot + '250') + Preloaded.append(model) + + +def loadBasicAnims(): + loadPhaseAnims() + + +def unloadBasicAnims(): + loadPhaseAnims(0) + + +def loadTutorialBattleAnims(): + loadPhaseAnims('phase_3.5') + + +def unloadTutorialBattleAnims(): + loadPhaseAnims('phase_3.5', 0) + + +def loadMinigameAnims(): + loadPhaseAnims('phase_4') + + +def unloadMinigameAnims(): + loadPhaseAnims('phase_4', 0) + + +def loadBattleAnims(): + loadPhaseAnims('phase_5') + + +def unloadBattleAnims(): + loadPhaseAnims('phase_5', 0) + + +def loadSellbotHQAnims(): + loadPhaseAnims('phase_9') + + +def unloadSellbotHQAnims(): + loadPhaseAnims('phase_9', 0) + + +def loadCashbotHQAnims(): + loadPhaseAnims('phase_10') + + +def unloadCashbotHQAnims(): + loadPhaseAnims('phase_10', 0) + + +def loadBossbotHQAnims(): + loadPhaseAnims('phase_12') + + +def unloadBossbotHQAnims(): + loadPhaseAnims('phase_12', 0) + + +def loadPhaseAnims(phaseStr = 'phase_3', loadFlag = 1): + if phaseStr == 'phase_3': + animList = Phase3AnimList + elif phaseStr == 'phase_3.5': + animList = Phase3_5AnimList + elif phaseStr == 'phase_4': + animList = Phase4AnimList + elif phaseStr == 'phase_5': + animList = Phase5AnimList + elif phaseStr == 'phase_5.5': + animList = Phase5_5AnimList + elif phaseStr == 'phase_6': + animList = Phase6AnimList + elif phaseStr == 'phase_9': + animList = Phase9AnimList + elif phaseStr == 'phase_10': + animList = Phase10AnimList + elif phaseStr == 'phase_12': + animList = Phase12AnimList + else: + self.notify.error('Unknown phase string %s' % phaseStr) + for key in LegDict.keys(): + for anim in animList: + if loadFlag: + pass + elif LegsAnimDict[key].has_key(anim[0]): + if base.localAvatar.style.legs == key: + base.localAvatar.unloadAnims([anim[0]], 'legs', None) + + for key in TorsoDict.keys(): + for anim in animList: + if loadFlag: + pass + elif TorsoAnimDict[key].has_key(anim[0]): + if base.localAvatar.style.torso == key: + base.localAvatar.unloadAnims([anim[0]], 'torso', None) + + for key in HeadDict.keys(): + if string.find(key, 'd') >= 0: + for anim in animList: + if loadFlag: + pass + elif HeadAnimDict[key].has_key(anim[0]): + if base.localAvatar.style.head == key: + base.localAvatar.unloadAnims([anim[0]], 'head', None) + + return + + +def compileGlobalAnimList(): + phaseList = [Phase3AnimList, + Phase3_5AnimList, + Phase4AnimList, + Phase5AnimList, + Phase5_5AnimList, + Phase6AnimList, + Phase9AnimList, + Phase10AnimList, + Phase12AnimList] + phaseStrList = ['phase_3', + 'phase_3.5', + 'phase_4', + 'phase_5', + 'phase_5.5', + 'phase_6', + 'phase_9', + 'phase_10', + 'phase_12'] + for animList in phaseList: + phaseStr = phaseStrList[phaseList.index(animList)] + for key in LegDict.keys(): + LegsAnimDict.setdefault(key, {}) + for anim in animList: + file = phaseStr + LegDict[key] + anim[1] + LegsAnimDict[key][anim[0]] = file + + for key in TorsoDict.keys(): + TorsoAnimDict.setdefault(key, {}) + for anim in animList: + file = phaseStr + TorsoDict[key] + anim[1] + TorsoAnimDict[key][anim[0]] = file + + for key in HeadDict.keys(): + if string.find(key, 'd') >= 0: + HeadAnimDict.setdefault(key, {}) + for anim in animList: + file = phaseStr + HeadDict[key] + anim[1] + HeadAnimDict[key][anim[0]] = file + + +def loadDialog(): + loadPath = 'phase_3.5/audio/dial/' + + DogDialogueFiles = ('AV_dog_short', 'AV_dog_med', 'AV_dog_long', 'AV_dog_question', 'AV_dog_exclaim', 'AV_dog_howl') + global DogDialogueArray + for file in DogDialogueFiles: + DogDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + catDialogueFiles = ('AV_cat_short', 'AV_cat_med', 'AV_cat_long', 'AV_cat_question', 'AV_cat_exclaim', 'AV_cat_howl') + global CatDialogueArray + for file in catDialogueFiles: + CatDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + horseDialogueFiles = ('AV_horse_short', 'AV_horse_med', 'AV_horse_long', 'AV_horse_question', 'AV_horse_exclaim', 'AV_horse_howl') + global HorseDialogueArray + for file in horseDialogueFiles: + HorseDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + rabbitDialogueFiles = ('AV_rabbit_short', 'AV_rabbit_med', 'AV_rabbit_long', 'AV_rabbit_question', 'AV_rabbit_exclaim', 'AV_rabbit_howl') + global RabbitDialogueArray + for file in rabbitDialogueFiles: + RabbitDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + mouseDialogueFiles = ('AV_mouse_short', 'AV_mouse_med', 'AV_mouse_long', 'AV_mouse_question', 'AV_mouse_exclaim', 'AV_mouse_howl') + global MouseDialogueArray + for file in mouseDialogueFiles: + MouseDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + duckDialogueFiles = ('AV_duck_short', 'AV_duck_med', 'AV_duck_long', 'AV_duck_question', 'AV_duck_exclaim', 'AV_duck_howl') + global DuckDialogueArray + for file in duckDialogueFiles: + DuckDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + monkeyDialogueFiles = ('AV_monkey_short', 'AV_monkey_med', 'AV_monkey_long', 'AV_monkey_question', 'AV_monkey_exclaim', 'AV_monkey_howl') + global MonkeyDialogueArray + for file in monkeyDialogueFiles: + MonkeyDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + bearDialogueFiles = ('AV_bear_short', 'AV_bear_med', 'AV_bear_long', 'AV_bear_question', 'AV_bear_exclaim', 'AV_bear_howl') + global BearDialogueArray + for file in bearDialogueFiles: + BearDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + pigDialogueFiles = ('AV_pig_short', 'AV_pig_med', 'AV_pig_long', 'AV_pig_question', 'AV_pig_exclaim', 'AV_pig_howl') + global PigDialogueArray + for file in pigDialogueFiles: + PigDialogueArray.append(base.loadSfx(loadPath + file + '.mp3')) + + +def unloadDialog(): + global CatDialogueArray + global PigDialogueArray + global BearDialogueArray + global DuckDialogueArray + global RabbitDialogueArray + global MouseDialogueArray + global DogDialogueArray + global HorseDialogueArray + global MonkeyDialogueArray + DogDialogueArray = [] + CatDialogueArray = [] + HorseDialogueArray = [] + RabbitDialogueArray = [] + MouseDialogueArray = [] + DuckDialogueArray = [] + MonkeyDialogueArray = [] + BearDialogueArray = [] + PigDialogueArray = [] + + +class Toon(Avatar.Avatar, ToonHead): + notify = DirectNotifyGlobal.directNotify.newCategory('Toon') + afkTimeout = base.config.GetInt('afk-timeout', 600) + + def __init__(self): + try: + self.Toon_initialized + return + except: + self.Toon_initialized = 1 + + Avatar.Avatar.__init__(self) + ToonHead.__init__(self) + self.forwardSpeed = 0.0 + self.rotateSpeed = 0.0 + self.avatarType = 'toon' + self.motion = Motion.Motion(self) + self.standWalkRunReverse = None + self.playingAnim = None + self.soundTeleport = None + self.cheesyEffect = ToontownGlobals.CENormal + self.effectTrack = None + self.emoteTrack = None + self.emote = None + self.stunTrack = None + self.__bookActors = [] + self.__holeActors = [] + self.holeClipPath = None + self.wake = None + self.lastWakeTime = 0 + self.forceJumpIdle = False + self.numPies = 0 + self.pieType = 0 + self.pieModel = None + self.__pieModelType = None + self.pieScale = 1.0 + self.hatNodes = [] + self.glassesNodes = [] + self.backpackNodes = [] + self.hat = (0, 0, 0) + self.glasses = (0, 0, 0) + self.backpack = (0, 0, 0) + self.shoes = (0, 0, 0) + self.isStunned = 0 + self.isDisguised = 0 + self.defaultColorScale = None + self.jar = None + self.setTag('pieCode', str(ToontownGlobals.PieCodeToon)) + self.setFont(ToontownGlobals.getToonFont()) + self.soundChatBubble = base.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.mp3') + self.animFSM = ClassicFSM('Toon', [State('off', self.enterOff, self.exitOff), + State('neutral', self.enterNeutral, self.exitNeutral), + State('victory', self.enterVictory, self.exitVictory), + State('Happy', self.enterHappy, self.exitHappy), + State('Sad', self.enterSad, self.exitSad), + State('Catching', self.enterCatching, self.exitCatching), + State('CatchEating', self.enterCatchEating, self.exitCatchEating), + State('Sleep', self.enterSleep, self.exitSleep), + State('walk', self.enterWalk, self.exitWalk), + State('jumpSquat', self.enterJumpSquat, self.exitJumpSquat), + State('jump', self.enterJump, self.exitJump), + State('jumpAirborne', self.enterJumpAirborne, self.exitJumpAirborne), + State('jumpLand', self.enterJumpLand, self.exitJumpLand), + State('run', self.enterRun, self.exitRun), + State('swim', self.enterSwim, self.exitSwim), + State('swimhold', self.enterSwimHold, self.exitSwimHold), + State('dive', self.enterDive, self.exitDive), + State('cringe', self.enterCringe, self.exitCringe), + State('OpenBook', self.enterOpenBook, self.exitOpenBook, ['ReadBook', 'CloseBook']), + State('ReadBook', self.enterReadBook, self.exitReadBook), + State('CloseBook', self.enterCloseBook, self.exitCloseBook), + State('TeleportOut', self.enterTeleportOut, self.exitTeleportOut), + State('Died', self.enterDied, self.exitDied), + State('TeleportedOut', self.enterTeleportedOut, self.exitTeleportedOut), + State('TeleportIn', self.enterTeleportIn, self.exitTeleportIn), + State('Emote', self.enterEmote, self.exitEmote), + State('SitStart', self.enterSitStart, self.exitSitStart), + State('Sit', self.enterSit, self.exitSit), + State('Push', self.enterPush, self.exitPush), + State('Squish', self.enterSquish, self.exitSquish), + State('FallDown', self.enterFallDown, self.exitFallDown), + State('GolfPuttLoop', self.enterGolfPuttLoop, self.exitGolfPuttLoop), + State('GolfRotateLeft', self.enterGolfRotateLeft, self.exitGolfRotateLeft), + State('GolfRotateRight', self.enterGolfRotateRight, self.exitGolfRotateRight), + State('GolfPuttSwing', self.enterGolfPuttSwing, self.exitGolfPuttSwing), + State('GolfGoodPutt', self.enterGolfGoodPutt, self.exitGolfGoodPutt), + State('GolfBadPutt', self.enterGolfBadPutt, self.exitGolfBadPutt), + State('Flattened', self.enterFlattened, self.exitFlattened), + State('CogThiefRunning', self.enterCogThiefRunning, self.exitCogThiefRunning), + State('ScientistJealous', self.enterScientistJealous, self.exitScientistJealous), + State('ScientistEmcee', self.enterScientistEmcee, self.exitScientistEmcee), + State('ScientistWork', self.enterScientistWork, self.exitScientistWork), + State('ScientistLessWork', self.enterScientistLessWork, self.exitScientistLessWork), + State('ScientistPlay', self.enterScientistPlay, self.enterScientistPlay)], 'off', 'off') + animStateList = self.animFSM.getStates() + self.animFSM.enterInitialState() + + def stopAnimations(self): + if hasattr(self, 'animFSM'): + if not self.animFSM.isInternalStateInFlux(): + self.animFSM.request('off') + else: + self.notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName()) + else: + self.notify.warning('animFSM has been deleted') + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + if self.emoteTrack != None: + self.emoteTrack.finish() + self.emoteTrack = None + if self.stunTrack != None: + self.stunTrack.finish() + self.stunTrack = None + if self.wake: + self.wake.stop() + self.wake.destroy() + self.wake = None + self.cleanupPieModel() + return + + def delete(self): + try: + self.Toon_deleted + except: + self.Toon_deleted = 1 + self.stopAnimations() + self.rightHands = None + self.rightHand = None + self.leftHands = None + self.leftHand = None + self.headParts = None + self.torsoParts = None + self.hipsParts = None + self.legsParts = None + del self.animFSM + for bookActor in self.__bookActors: + bookActor.cleanup() + + del self.__bookActors + for holeActor in self.__holeActors: + holeActor.cleanup() + + del self.__holeActors + self.soundTeleport = None + self.motion.delete() + self.motion = None + Avatar.Avatar.delete(self) + ToonHead.delete(self) + + return + + def updateToonDNA(self, newDNA, fForce = 0): + self.style.gender = newDNA.getGender() + oldDNA = self.style + if fForce or newDNA.head != oldDNA.head: + self.swapToonHead(newDNA.head) + if fForce or newDNA.torso != oldDNA.torso: + self.swapToonTorso(newDNA.torso, genClothes=0) + self.loop('neutral') + if fForce or newDNA.legs != oldDNA.legs: + self.swapToonLegs(newDNA.legs) + self.swapToonColor(newDNA) + self.__swapToonClothes(newDNA) + + def setDNAString(self, dnaString): + newDNA = ToonDNA.ToonDNA() + newDNA.makeFromNetString(dnaString) + if len(newDNA.torso) < 2: + self.sendLogSuspiciousEvent('nakedToonDNA %s was requested' % newDNA.torso) + newDNA.torso = newDNA.torso + 's' + self.setDNA(newDNA) + + def setDNA(self, dna): + if hasattr(self, 'isDisguised'): + if self.isDisguised: + return + if self.style: + self.updateToonDNA(dna) + else: + self.style = dna + self.generateToon() + self.initializeDropShadow() + self.initializeNametag3d() + + def parentToonParts(self): + if self.hasLOD(): + for lodName in self.getLODNames(): + if base.config.GetBool('want-new-anims', 1): + if not self.getPart('torso', lodName).find('**/def_head').isEmpty(): + self.attach('head', 'torso', 'def_head', lodName) + else: + self.attach('head', 'torso', 'joint_head', lodName) + else: + self.attach('head', 'torso', 'joint_head', lodName) + self.attach('torso', 'legs', 'joint_hips', lodName) + + else: + self.attach('head', 'torso', 'joint_head') + self.attach('torso', 'legs', 'joint_hips') + + def unparentToonParts(self): + if self.hasLOD(): + for lodName in self.getLODNames(): + self.getPart('head', lodName).reparentTo(self.getLOD(lodName)) + self.getPart('torso', lodName).reparentTo(self.getLOD(lodName)) + self.getPart('legs', lodName).reparentTo(self.getLOD(lodName)) + + else: + self.getPart('head').reparentTo(self.getGeomNode()) + self.getPart('torso').reparentTo(self.getGeomNode()) + self.getPart('legs').reparentTo(self.getGeomNode()) + + def setLODs(self): + self.setLODNode() + levelOneIn = base.config.GetInt('lod1-in', 20) + levelOneOut = base.config.GetInt('lod1-out', 0) + levelTwoIn = base.config.GetInt('lod2-in', 80) + levelTwoOut = base.config.GetInt('lod2-out', 20) + levelThreeIn = base.config.GetInt('lod3-in', 280) + levelThreeOut = base.config.GetInt('lod3-out', 80) + self.addLOD(1000, levelOneIn, levelOneOut) + self.addLOD(500, levelTwoIn, levelTwoOut) + self.addLOD(250, levelThreeIn, levelThreeOut) + + def generateToon(self): + self.setLODs() + self.generateToonLegs() + self.generateToonHead() + self.generateToonTorso() + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.setupToonNodes() + + def setupToonNodes(self): + rightHand = NodePath('rightHand') + self.rightHand = None + self.rightHands = [] + leftHand = NodePath('leftHand') + self.leftHands = [] + self.leftHand = None + for lodName in self.getLODNames(): + hand = self.getPart('torso', lodName).find('**/joint_Rhold') + if base.config.GetBool('want-new-anims', 1): + if not self.getPart('torso', lodName).find('**/def_joint_right_hold').isEmpty(): + hand = self.getPart('torso', lodName).find('**/def_joint_right_hold') + else: + hand = self.getPart('torso', lodName).find('**/joint_Rhold') + self.rightHands.append(hand) + rightHand = rightHand.instanceTo(hand) + if base.config.GetBool('want-new-anims', 1): + if not self.getPart('torso', lodName).find('**/def_joint_left_hold').isEmpty(): + hand = self.getPart('torso', lodName).find('**/def_joint_left_hold') + else: + hand = self.getPart('torso', lodName).find('**/joint_Lhold') + self.leftHands.append(hand) + leftHand = leftHand.instanceTo(hand) + if self.rightHand == None: + self.rightHand = rightHand + if self.leftHand == None: + self.leftHand = leftHand + + self.headParts = self.findAllMatches('**/__Actor_head') + self.legsParts = self.findAllMatches('**/__Actor_legs') + self.hipsParts = self.legsParts.findAllMatches('**/joint_hips') + self.torsoParts = self.hipsParts.findAllMatches('**/__Actor_torso') + return + + def initializeBodyCollisions(self, collIdStr): + Avatar.Avatar.initializeBodyCollisions(self, collIdStr) + if not self.ghostMode: + self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask) + + def getBookActors(self): + if self.__bookActors: + return self.__bookActors + bookActor = Actor.Actor('phase_3.5/models/props/book-mod', {'book': 'phase_3.5/models/props/book-chan'}) + bookActor2 = Actor.Actor(other=bookActor) + bookActor3 = Actor.Actor(other=bookActor) + self.__bookActors = [bookActor, bookActor2, bookActor3] + hands = self.getRightHands() + for bookActor, hand in zip(self.__bookActors, hands): + bookActor.reparentTo(hand) + bookActor.hide() + + return self.__bookActors + + def getHoleActors(self): + if self.__holeActors: + return self.__holeActors + holeActor = Actor.Actor('phase_3.5/models/props/portal-mod', {'hole': 'phase_3.5/models/props/portal-chan'}) + holeActor2 = Actor.Actor(other=holeActor) + holeActor3 = Actor.Actor(other=holeActor) + self.__holeActors = [holeActor, holeActor2, holeActor3] + for ha in self.__holeActors: + if hasattr(self, 'uniqueName'): + holeName = self.uniqueName('toon-portal') + else: + holeName = 'toon-portal' + ha.setName(holeName) + + return self.__holeActors + + def rescaleToon(self): + animalStyle = self.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + headScale = ToontownGlobals.toonHeadScales[animalStyle] + self.setAvatarScale(bodyScale) + for lod in self.getLODNames(): + self.getPart('head', lod).setScale(headScale) + + def getBodyScale(self): + animalStyle = self.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + return bodyScale + + def resetHeight(self): + if hasattr(self, 'style') and self.style: + animal = self.style.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animal] + headScale = ToontownGlobals.toonHeadScales[animal][2] + shoulderHeight = ToontownGlobals.legHeightDict[self.style.legs] * bodyScale + ToontownGlobals.torsoHeightDict[self.style.torso] * bodyScale + height = shoulderHeight + ToontownGlobals.headHeightDict[self.style.head] * headScale + self.shoulderHeight = shoulderHeight + if self.cheesyEffect == ToontownGlobals.CEBigToon or self.cheesyEffect == ToontownGlobals.CEBigWhite: + height *= ToontownGlobals.BigToonScale + elif self.cheesyEffect == ToontownGlobals.CESmallToon: + height *= ToontownGlobals.SmallToonScale + self.setHeight(height) + + def generateToonLegs(self, copy = 1): + legStyle = self.style.legs + filePrefix = LegDict.get(legStyle) + if filePrefix is None: + self.notify.error('unknown leg style: %s' % legStyle) + self.loadModel('phase_3' + filePrefix + '1000', 'legs', '1000', copy) + self.loadModel('phase_3' + filePrefix + '500', 'legs', '500', copy) + self.loadModel('phase_3' + filePrefix + '250', 'legs', '250', copy) + if not copy: + self.showPart('legs', '1000') + self.showPart('legs', '500') + self.showPart('legs', '250') + self.loadAnims(LegsAnimDict[legStyle], 'legs', '1000') + self.loadAnims(LegsAnimDict[legStyle], 'legs', '500') + self.loadAnims(LegsAnimDict[legStyle], 'legs', '250') + self.findAllMatches('**/boots_short').stash() + self.findAllMatches('**/boots_long').stash() + self.findAllMatches('**/shoes').stash() + return + + def swapToonLegs(self, legStyle, copy = 1): + self.unparentToonParts() + self.removePart('legs', '1000') + self.removePart('legs', '500') + self.removePart('legs', '250') + self.style.legs = legStyle + self.generateToonLegs(copy) + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + del self.shadowJoint + self.initializeDropShadow() + self.initializeNametag3d() + + def generateToonTorso(self, copy = 1, genClothes = 1): + torsoStyle = self.style.torso + filePrefix = TorsoDict.get(torsoStyle) + if filePrefix is None: + self.notify.error('unknown torso style: %s' % torsoStyle) + self.loadModel('phase_3' + filePrefix + '1000', 'torso', '1000', copy) + if len(torsoStyle) == 1: + self.loadModel('phase_3' + filePrefix + '1000', 'torso', '500', copy) + self.loadModel('phase_3' + filePrefix + '1000', 'torso', '250', copy) + else: + self.loadModel('phase_3' + filePrefix + '500', 'torso', '500', copy) + self.loadModel('phase_3' + filePrefix + '250', 'torso', '250', copy) + if not copy: + self.showPart('torso', '1000') + self.showPart('torso', '500') + self.showPart('torso', '250') + self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '1000') + self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '500') + self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '250') + if genClothes == 1 and not len(torsoStyle) == 1: + self.generateToonClothes() + return + + def swapToonTorso(self, torsoStyle, copy = 1, genClothes = 1): + self.unparentToonParts() + self.removePart('torso', '1000') + self.removePart('torso', '500') + self.removePart('torso', '250') + self.style.torso = torsoStyle + self.generateToonTorso(copy, genClothes) + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.setupToonNodes() + self.generateBackpack() + + def generateToonHead(self, copy = 1): + headHeight = ToonHead.generateToonHead(self, copy, self.style, ('1000', '500', '250')) + if self.style.getAnimal() == 'dog': + self.loadAnims(HeadAnimDict[self.style.head], 'head', '1000') + self.loadAnims(HeadAnimDict[self.style.head], 'head', '500') + self.loadAnims(HeadAnimDict[self.style.head], 'head', '250') + + def swapToonHead(self, headStyle, copy = 1): + self.stopLookAroundNow() + self.eyelids.request('open') + self.unparentToonParts() + self.removePart('head', '1000') + self.removePart('head', '500') + self.removePart('head', '250') + self.style.head = headStyle + self.generateToonHead(copy) + self.generateToonColor() + self.parentToonParts() + self.rescaleToon() + self.resetHeight() + self.eyelids.request('open') + self.startLookAround() + + def generateToonColor(self): + ToonHead.generateToonColor(self, self.style) + armColor = self.style.getArmColor() + gloveColor = self.style.getGloveColor() + legColor = self.style.getLegColor() + for lodName in self.getLODNames(): + torso = self.getPart('torso', lodName) + if len(self.style.torso) == 1: + parts = torso.findAllMatches('**/torso*') + parts.setColor(armColor) + for pieceName in ('arms', 'neck'): + piece = torso.find('**/' + pieceName) + piece.setColor(armColor) + + hands = torso.find('**/hands') + hands.setColor(gloveColor) + legs = self.getPart('legs', lodName) + for pieceName in ('legs', 'feet'): + piece = legs.find('**/%s;+s' % pieceName) + piece.setColor(legColor) + + if self.cheesyEffect == ToontownGlobals.CEGreenToon: + self.reapplyCheesyEffect() + + def swapToonColor(self, dna): + self.setStyle(dna) + self.generateToonColor() + + def __swapToonClothes(self, dna): + self.setStyle(dna) + self.generateToonClothes(fromNet=1) + + def sendLogSuspiciousEvent(self, msg): + pass + + def generateToonClothes(self, fromNet = 0): + swappedTorso = 0 + if self.hasLOD(): + if self.style.getGender() == 'f' and fromNet == 0: + try: + bottomPair = ToonDNA.GirlBottoms[self.style.botTex] + except: + bottomPair = ToonDNA.GirlBottoms[0] + + if len(self.style.torso) < 2: + self.sendLogSuspiciousEvent('nakedToonDNA %s was requested' % self.style.torso) + return 0 + elif self.style.torso[1] == 's' and bottomPair[1] == ToonDNA.SKIRT: + self.swapToonTorso(self.style.torso[0] + 'd', genClothes=0) + swappedTorso = 1 + elif self.style.torso[1] == 'd' and bottomPair[1] == ToonDNA.SHORTS: + self.swapToonTorso(self.style.torso[0] + 's', genClothes=0) + swappedTorso = 1 + try: + texName = ToonDNA.Shirts[self.style.topTex] + except: + texName = ToonDNA.Shirts[0] + + shirtTex = loader.loadTexture(texName, okMissing=True) + if shirtTex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + shirtTex = loader.loadTexture(ToonDNA.Shirts[0]) + shirtTex.setMinfilter(Texture.FTLinearMipmapLinear) + shirtTex.setMagfilter(Texture.FTLinear) + try: + shirtColor = ToonDNA.ClothesColors[self.style.topTexColor] + except: + shirtColor = ToonDNA.ClothesColors[0] + + try: + texName = ToonDNA.Sleeves[self.style.sleeveTex] + except: + texName = ToonDNA.Sleeves[0] + + sleeveTex = loader.loadTexture(texName, okMissing=True) + if sleeveTex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + sleeveTex = loader.loadTexture(ToonDNA.Sleeves[0]) + sleeveTex.setMinfilter(Texture.FTLinearMipmapLinear) + sleeveTex.setMagfilter(Texture.FTLinear) + try: + sleeveColor = ToonDNA.ClothesColors[self.style.sleeveTexColor] + except: + sleeveColor = ToonDNA.ClothesColors[0] + + if self.style.getGender() == 'm': + try: + texName = ToonDNA.BoyShorts[self.style.botTex] + except: + texName = ToonDNA.BoyShorts[0] + + else: + try: + texName = ToonDNA.GirlBottoms[self.style.botTex][0] + except: + texName = ToonDNA.GirlBottoms[0][0] + + bottomTex = loader.loadTexture(texName, okMissing=True) + if bottomTex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + if self.style.getGender() == 'm': + bottomTex = loader.loadTexture(ToonDNA.BoyShorts[0]) + else: + bottomTex = loader.loadTexture(ToonDNA.GirlBottoms[0][0]) + bottomTex.setMinfilter(Texture.FTLinearMipmapLinear) + bottomTex.setMagfilter(Texture.FTLinear) + try: + bottomColor = ToonDNA.ClothesColors[self.style.botTexColor] + except: + bottomColor = ToonDNA.ClothesColors[0] + + darkBottomColor = bottomColor * 0.5 + darkBottomColor.setW(1.0) + for lodName in self.getLODNames(): + thisPart = self.getPart('torso', lodName) + top = thisPart.find('**/torso-top') + top.setTexture(shirtTex, 1) + top.setColor(shirtColor) + sleeves = thisPart.find('**/sleeves') + sleeves.setTexture(sleeveTex, 1) + sleeves.setColor(sleeveColor) + bottoms = thisPart.findAllMatches('**/torso-bot') + for bottomNum in range(0, bottoms.getNumPaths()): + bottom = bottoms.getPath(bottomNum) + bottom.setTexture(bottomTex, 1) + bottom.setColor(bottomColor) + + caps = thisPart.findAllMatches('**/torso-bot-cap') + caps.setColor(darkBottomColor) + + return swappedTorso + + def generateHat(self, fromRTM = False): + hat = self.getHat() + if hat[0] >= len(ToonDNA.HatModels): + self.sendLogSuspiciousEvent('tried to put a wrong hat idx %d' % hat[0]) + return + if len(self.hatNodes) > 0: + for hatNode in self.hatNodes: + hatNode.removeNode() + + self.hatNodes = [] + self.showEars() + if hat[0] != 0: + hatGeom = loader.loadModel(ToonDNA.HatModels[hat[0]], okMissing=True) + if hatGeom: + if hat[0] == 54: + self.hideEars() + if hat[1] != 0: + texName = ToonDNA.HatTextures[hat[1]] + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + hatGeom.setTexture(tex, 1) + if fromRTM: + reload(AccessoryGlobals) + transOffset = None + if AccessoryGlobals.ExtendedHatTransTable.get(hat[0]): + transOffset = AccessoryGlobals.ExtendedHatTransTable[hat[0]].get(self.style.head[:2]) + if transOffset is None: + transOffset = AccessoryGlobals.HatTransTable.get(self.style.head[:2]) + if transOffset is None: + return + hatGeom.setPos(transOffset[0][0], transOffset[0][1], transOffset[0][2]) + hatGeom.setHpr(transOffset[1][0], transOffset[1][1], transOffset[1][2]) + hatGeom.setScale(transOffset[2][0], transOffset[2][1], transOffset[2][2]) + headNodes = self.findAllMatches('**/__Actor_head') + for headNode in headNodes: + hatNode = headNode.attachNewNode('hatNode') + self.hatNodes.append(hatNode) + hatGeom.instanceTo(hatNode) + + return + + def generateGlasses(self, fromRTM = False): + glasses = self.getGlasses() + if glasses[0] >= len(ToonDNA.GlassesModels): + self.sendLogSuspiciousEvent('tried to put a wrong glasses idx %d' % glasses[0]) + return + if len(self.glassesNodes) > 0: + for glassesNode in self.glassesNodes: + glassesNode.removeNode() + + self.glassesNodes = [] + self.showEyelashes() + if glasses[0] != 0: + glassesGeom = loader.loadModel(ToonDNA.GlassesModels[glasses[0]], okMissing=True) + if glassesGeom: + if glasses[0] in [15, 16]: + self.hideEyelashes() + if glasses[1] != 0: + texName = ToonDNA.GlassesTextures[glasses[1]] + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + glassesGeom.setTexture(tex, 1) + if fromRTM: + reload(AccessoryGlobals) + transOffset = None + if AccessoryGlobals.ExtendedGlassesTransTable.get(glasses[0]): + transOffset = AccessoryGlobals.ExtendedGlassesTransTable[glasses[0]].get(self.style.head[:2]) + if transOffset is None: + transOffset = AccessoryGlobals.GlassesTransTable.get(self.style.head[:2]) + if transOffset is None: + return + glassesGeom.setPos(transOffset[0][0], transOffset[0][1], transOffset[0][2]) + glassesGeom.setHpr(transOffset[1][0], transOffset[1][1], transOffset[1][2]) + glassesGeom.setScale(transOffset[2][0], transOffset[2][1], transOffset[2][2]) + headNodes = self.findAllMatches('**/__Actor_head') + for headNode in headNodes: + glassesNode = headNode.attachNewNode('glassesNode') + self.glassesNodes.append(glassesNode) + glassesGeom.instanceTo(glassesNode) + + return + + def generateBackpack(self, fromRTM = False): + backpack = self.getBackpack() + if backpack[0] >= len(ToonDNA.BackpackModels): + self.sendLogSuspiciousEvent('tried to put a wrong backpack idx %d' % backpack[0]) + return + if len(self.backpackNodes) > 0: + for backpackNode in self.backpackNodes: + backpackNode.removeNode() + + self.backpackNodes = [] + if backpack[0] != 0: + geom = loader.loadModel(ToonDNA.BackpackModels[backpack[0]], okMissing=True) + if geom: + if backpack[1] != 0: + texName = ToonDNA.BackpackTextures[backpack[1]] + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + geom.setTexture(tex, 1) + if fromRTM: + reload(AccessoryGlobals) + transOffset = None + if AccessoryGlobals.ExtendedBackpackTransTable.get(backpack[0]): + transOffset = AccessoryGlobals.ExtendedBackpackTransTable[backpack[0]].get(self.style.torso[:1]) + if transOffset is None: + transOffset = AccessoryGlobals.BackpackTransTable.get(self.style.torso[:1]) + if transOffset is None: + return + geom.setPos(transOffset[0][0], transOffset[0][1], transOffset[0][2]) + geom.setHpr(transOffset[1][0], transOffset[1][1], transOffset[1][2]) + geom.setScale(transOffset[2][0], transOffset[2][1], transOffset[2][2]) + nodes = self.findAllMatches('**/def_joint_attachFlower') + for node in nodes: + theNode = node.attachNewNode('backpackNode') + self.backpackNodes.append(theNode) + geom.instanceTo(theNode) + + return + + def generateShoes(self): + shoes = self.getShoes() + if shoes[0] >= len(ToonDNA.ShoesModels): + self.sendLogSuspiciousEvent('tried to put a wrong shoes idx %d' % shoes[0]) + return + self.findAllMatches('**/feet;+s').stash() + self.findAllMatches('**/boots_short;+s').stash() + self.findAllMatches('**/boots_long;+s').stash() + self.findAllMatches('**/shoes;+s').stash() + geoms = self.findAllMatches('**/%s;+s' % ToonDNA.ShoesModels[shoes[0]]) + for geom in geoms: + geom.unstash() + + if shoes[0] != 0: + for geom in geoms: + texName = ToonDNA.ShoesTextures[shoes[1]] + if self.style.legs == 'l' and shoes[0] == 3: + texName = texName[:-4] + 'LL.jpg' + tex = loader.loadTexture(texName, okMissing=True) + if tex is None: + self.sendLogSuspiciousEvent('failed to load texture %s' % texName) + else: + tex.setMinfilter(Texture.FTLinearMipmapLinear) + tex.setMagfilter(Texture.FTLinear) + geom.setTexture(tex, 1) + + return + + def generateToonAccessories(self): + self.generateHat() + self.generateGlasses() + self.generateBackpack() + self.generateShoes() + + def setHat(self, hatIdx, textureIdx, colorIdx, fromRTM = False): + self.hat = (hatIdx, textureIdx, colorIdx) + self.generateHat(fromRTM=fromRTM) + + def getHat(self): + return self.hat + + def setGlasses(self, glassesIdx, textureIdx, colorIdx, fromRTM = False): + self.glasses = (glassesIdx, textureIdx, colorIdx) + self.generateGlasses(fromRTM=fromRTM) + + def getGlasses(self): + return self.glasses + + def setBackpack(self, backpackIdx, textureIdx, colorIdx, fromRTM = False): + self.backpack = (backpackIdx, textureIdx, colorIdx) + self.generateBackpack(fromRTM=fromRTM) + + def getBackpack(self): + return self.backpack + + def setShoes(self, shoesIdx, textureIdx, colorIdx): + self.shoes = (shoesIdx, textureIdx, colorIdx) + self.generateShoes() + + def getShoes(self): + return self.shoes + + def getDialogueArray(self): + animalType = self.style.getType() + if animalType == 'dog': + dialogueArray = DogDialogueArray + elif animalType == 'cat': + dialogueArray = CatDialogueArray + elif animalType == 'horse': + dialogueArray = HorseDialogueArray + elif animalType == 'mouse': + dialogueArray = MouseDialogueArray + elif animalType == 'rabbit': + dialogueArray = RabbitDialogueArray + elif animalType == 'duck': + dialogueArray = DuckDialogueArray + elif animalType == 'monkey': + dialogueArray = MonkeyDialogueArray + elif animalType == 'bear': + dialogueArray = BearDialogueArray + elif animalType == 'pig': + dialogueArray = PigDialogueArray + else: + dialogueArray = None + return dialogueArray + + def getShadowJoint(self): + if hasattr(self, 'shadowJoint'): + return self.shadowJoint + shadowJoint = NodePath('shadowJoint') + for lodName in self.getLODNames(): + joint = self.getPart('legs', lodName).find('**/joint_shadow') + shadowJoint = shadowJoint.instanceTo(joint) + + self.shadowJoint = shadowJoint + return shadowJoint + + def getNametagJoints(self): + joints = [] + for lodName in self.getLODNames(): + bundle = self.getPartBundle('legs', lodName) + joint = bundle.findChild('joint_nameTag') + if joint: + joints.append(joint) + + return joints + + def getRightHands(self): + return self.rightHands + + def getLeftHands(self): + return self.leftHands + + def getHeadParts(self): + return self.headParts + + def getHipsParts(self): + return self.hipsParts + + def getTorsoParts(self): + return self.torsoParts + + def getLegsParts(self): + return self.legsParts + + def findSomethingToLookAt(self): + if self.randGen.random() < 0.1 or not hasattr(self, 'cr'): + x = self.randGen.choice((-0.8, + -0.5, + 0, + 0.5, + 0.8)) + y = self.randGen.choice((-0.5, + 0, + 0.5, + 0.8)) + self.lerpLookAt(Point3(x, 1.5, y), blink=1) + return + nodePathList = [] + for id, obj in self.cr.doId2do.items(): + if hasattr(obj, 'getStareAtNodeAndOffset') and obj != self: + node, offset = obj.getStareAtNodeAndOffset() + if node.getY(self) > 0.0: + nodePathList.append((node, offset)) + + if nodePathList: + nodePathList.sort(lambda x, y: cmp(x[0].getDistance(self), y[0].getDistance(self))) + if len(nodePathList) >= 2: + if self.randGen.random() < 0.9: + chosenNodePath = nodePathList[0] + else: + chosenNodePath = nodePathList[1] + else: + chosenNodePath = nodePathList[0] + self.lerpLookAt(chosenNodePath[0].getPos(self), blink=1) + else: + ToonHead.findSomethingToLookAt(self) + + def setForceJumpIdle(self, value): + self.forceJumpIdle = value + + def setupPickTrigger(self): + Avatar.Avatar.setupPickTrigger(self) + torso = self.getPart('torso', '1000') + if torso == None: + return 0 + self.pickTriggerNp.reparentTo(torso) + size = self.style.getTorsoSize() + if size == 'short': + self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1.5, 1.5, 2) + elif size == 'medium': + self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1, 1, 2) + else: + self.pickTriggerNp.setPosHprScale(0, 0, 1, 0, 0, 0, 1, 1, 2) + return 1 + + def showBooks(self): + for bookActor in self.getBookActors(): + bookActor.show() + + def hideBooks(self): + for bookActor in self.getBookActors(): + bookActor.hide() + + def getWake(self): + if not self.wake: + self.wake = Wake.Wake(render, self) + return self.wake + + def getJar(self): + if not self.jar: + self.jar = loader.loadModel('phase_5.5/models/estate/jellybeanJar') + self.jar.setP(290.0) + self.jar.setY(0.5) + self.jar.setZ(0.5) + self.jar.setScale(0.0) + return self.jar + + def removeJar(self): + if self.jar: + self.jar.removeNode() + self.jar = None + return + + def setSpeed(self, forwardSpeed, rotateSpeed): + self.forwardSpeed = forwardSpeed + self.rotateSpeed = rotateSpeed + action = None + if self.standWalkRunReverse != None: + if forwardSpeed >= ToontownGlobals.RunCutOff: + action = OTPGlobals.RUN_INDEX + elif forwardSpeed > ToontownGlobals.WalkCutOff: + action = OTPGlobals.WALK_INDEX + elif forwardSpeed < -ToontownGlobals.WalkCutOff: + action = OTPGlobals.REVERSE_INDEX + elif rotateSpeed != 0.0: + action = OTPGlobals.WALK_INDEX + else: + action = OTPGlobals.STAND_INDEX + anim, rate = self.standWalkRunReverse[action] + self.motion.enter() + self.motion.setState(anim, rate) + if anim != self.playingAnim: + self.playingAnim = anim + self.playingRate = rate + self.stop() + self.loop(anim) + self.setPlayRate(rate, anim) + if self.isDisguised: + rightHand = self.suit.rightHand + numChildren = rightHand.getNumChildren() + if numChildren > 0: + anim = 'tray-' + anim + if anim == 'tray-run': + anim = 'tray-walk' + self.suit.stop() + self.suit.loop(anim) + self.suit.setPlayRate(rate, anim) + elif rate != self.playingRate: + self.playingRate = rate + if not self.isDisguised: + self.setPlayRate(rate, anim) + else: + self.suit.setPlayRate(rate, anim) + showWake, wakeWaterHeight = ZoneUtil.getWakeInfo() + if showWake and self.getZ(render) < wakeWaterHeight and abs(forwardSpeed) > ToontownGlobals.WalkCutOff: + currT = globalClock.getFrameTime() + deltaT = currT - self.lastWakeTime + if action == OTPGlobals.RUN_INDEX and deltaT > ToontownGlobals.WakeRunDelta or deltaT > ToontownGlobals.WakeWalkDelta: + self.getWake().createRipple(wakeWaterHeight, rate=1, startFrame=4) + self.lastWakeTime = currT + return action + + def enterOff(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.setActiveShadow(0) + self.playingAnim = None + return + + def exitOff(self): + pass + + def enterNeutral(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + anim = 'neutral' + self.pose(anim, int(self.getNumFrames(anim) * self.randGen.random())) + self.loop(anim, restart=0) + self.setPlayRate(animMultiplier, anim) + self.playingAnim = anim + self.setActiveShadow(0) + + def exitNeutral(self): + self.stop() + + def enterVictory(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + anim = 'victory' + frame = int(ts * self.getFrameRate(anim) * animMultiplier) + self.pose(anim, frame) + self.loop('victory', restart=0) + self.setPlayRate(animMultiplier, 'victory') + self.playingAnim = anim + self.setActiveShadow(0) + + def exitVictory(self): + self.stop() + + def enterHappy(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('neutral', 1.0), + ('walk', 1.0), + ('run', 1.0), + ('walk', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(1) + return + + def exitHappy(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterSad(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = 'sad' + self.playingRate = None + self.standWalkRunReverse = (('sad-neutral', 1.0), + ('sad-walk', 1.2), + ('sad-walk', 1.2), + ('sad-walk', -1.0)) + self.setSpeed(0, 0) + Emote.globalEmote.disableBody(self, 'toon, enterSad') + self.setActiveShadow(1) + if self.isLocal(): + self.controlManager.disableAvatarJump() + return + + def exitSad(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + Emote.globalEmote.releaseBody(self, 'toon, exitSad') + if self.isLocal(): + self.controlManager.enableAvatarJump() + return + + def enterCatching(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('catch-neutral', 1.0), + ('catch-run', 1.0), + ('catch-run', 1.0), + ('catch-run', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(1) + return + + def exitCatching(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterCatchEating(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('catch-eatneutral', 1.0), + ('catch-eatnrun', 1.0), + ('catch-eatnrun', 1.0), + ('catch-eatnrun', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(0) + return + + def exitCatchEating(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterWalk(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('walk') + self.setPlayRate(animMultiplier, 'walk') + self.setActiveShadow(1) + + def exitWalk(self): + self.stop() + + def getJumpDuration(self): + if self.playingAnim == 'neutral': + return self.getDuration('jump', 'legs') + else: + return self.getDuration('running-jump', 'legs') + + def enterJump(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'neutral': + anim = 'jump' + else: + anim = 'running-jump' + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.play(anim) + self.setActiveShadow(1) + + def exitJump(self): + self.stop() + self.playingAnim = 'neutral' + + def enterJumpSquat(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'neutral': + anim = 'jump-squat' + else: + anim = 'running-jump-squat' + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.play(anim) + self.setActiveShadow(1) + + def exitJumpSquat(self): + self.stop() + self.playingAnim = 'neutral' + + def enterJumpAirborne(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'neutral' or self.forceJumpIdle: + anim = 'jump-idle' + else: + anim = 'running-jump-idle' + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.loop(anim) + self.setActiveShadow(1) + + def exitJumpAirborne(self): + self.stop() + self.playingAnim = 'neutral' + + def enterJumpLand(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isDisguised: + if self.playingAnim == 'running-jump-idle': + anim = 'running-jump-land' + skipStart = 0.2 + else: + anim = 'jump-land' + skipStart = 0.0 + self.playingAnim = anim + self.setPlayRate(animMultiplier, anim) + self.play(anim) + self.setActiveShadow(1) + + def exitJumpLand(self): + self.stop() + self.playingAnim = 'neutral' + + def enterRun(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('run') + self.setPlayRate(animMultiplier, 'run') + Emote.globalEmote.disableBody(self, 'toon, enterRun') + self.setActiveShadow(1) + + def exitRun(self): + self.stop() + Emote.globalEmote.releaseBody(self, 'toon, exitRun') + + def enterSwim(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self, 'enterSwim') + self.playingAnim = 'swim' + self.loop('swim') + self.setPlayRate(animMultiplier, 'swim') + self.getGeomNode().setP(-89.0) + self.dropShadow.hide() + if self.isLocal(): + self.useSwimControls() + self.nametag3d.setPos(0, -2, 1) + self.startBobSwimTask() + self.setActiveShadow(0) + + def enterCringe(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('cringe') + self.getGeomNode().setPos(0, 0, -2) + self.setPlayRate(animMultiplier, 'swim') + + def exitCringe(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.stop() + self.getGeomNode().setPos(0, 0, 0) + self.playingAnim = 'neutral' + self.setPlayRate(animMultiplier, 'swim') + + def enterDive(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('swim') + if hasattr(self.getGeomNode(), 'setPos'): + self.getGeomNode().setPos(0, 0, -2) + self.setPlayRate(animMultiplier, 'swim') + self.setActiveShadow(0) + self.dropShadow.hide() + self.nametag3d.setPos(0, -2, 1) + + def exitDive(self): + self.stop() + self.getGeomNode().setPos(0, 0, 0) + self.playingAnim = 'neutral' + self.dropShadow.show() + self.nametag3d.setPos(0, 0, self.height + 0.5) + + def enterSwimHold(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.getGeomNode().setPos(0, 0, -2) + self.nametag3d.setPos(0, -2, 1) + self.pose('swim', 55) + + def exitSwimHold(self): + self.stop() + self.getGeomNode().setPos(0, 0, 0) + self.playingAnim = 'neutral' + self.dropShadow.show() + self.nametag3d.setPos(0, 0, self.height + 0.5) + + def exitSwim(self): + self.stop() + self.playingAnim = 'neutral' + self.stopBobSwimTask() + self.getGeomNode().setPosHpr(0, 0, 0, 0, 0, 0) + self.dropShadow.show() + if self.isLocal(): + self.useWalkControls() + self.nametag3d.setPos(0, 0, self.height + 0.5) + Emote.globalEmote.releaseAll(self, 'exitSwim') + + def startBobSwimTask(self): + swimTaskName = self.taskName('swimBobTask') + taskMgr.remove('swimTask') + taskMgr.remove(swimTaskName) + self.getGeomNode().setZ(4.0) + self.nametag3d.setZ(5.0) + newTask = Task.loop(self.getGeomNode().lerpPosXYZ(0, -3, 3, 1, blendType='easeInOut'), self.getGeomNode().lerpPosXYZ(0, -3, 4, 1, blendType='easeInOut')) + taskMgr.add(newTask, swimTaskName) + + def stopBobSwimTask(self): + swimTaskName = self.taskName('swimBobTask') + taskMgr.remove(swimTaskName) + self.getGeomNode().setPos(0, 0, 0) + self.nametag3d.setZ(1.0) + + def enterOpenBook(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self, 'enterOpenBook') + self.playingAnim = 'openBook' + self.stopLookAround() + self.lerpLookAt(Point3(0, 1, -2)) + bookTracks = Parallel() + for bookActor in self.getBookActors(): + bookTracks.append(ActorInterval(bookActor, 'book', startTime=1.2, endTime=1.5)) + + bookTracks.append(ActorInterval(self, 'book', startTime=1.2, endTime=1.5)) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('openBook') + else: + trackName = 'openBook' + self.track = Sequence(Func(self.showBooks), bookTracks, Wait(0.1), name=trackName) + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + self.setActiveShadow(0) + + def exitOpenBook(self): + self.playingAnim = 'neutralob' + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + self.hideBooks() + self.startLookAround() + Emote.globalEmote.releaseAll(self, 'exitOpenBook') + return + + def enterReadBook(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self, 'enterReadBook') + self.playingAnim = 'readBook' + self.stopLookAround() + self.lerpLookAt(Point3(0, 1, -2)) + self.showBooks() + for bookActor in self.getBookActors(): + bookActor.pingpong('book', fromFrame=38, toFrame=118) + + self.pingpong('book', fromFrame=38, toFrame=118) + self.setActiveShadow(0) + + def exitReadBook(self): + self.playingAnim = 'neutralrb' + self.hideBooks() + for bookActor in self.getBookActors(): + bookActor.stop() + + self.startLookAround() + Emote.globalEmote.releaseBody(self, 'exitReadBook') + + def enterCloseBook(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self, 'enterCloseBook') + self.playingAnim = 'closeBook' + bookTracks = Parallel() + for bookActor in self.getBookActors(): + bookTracks.append(ActorInterval(bookActor, 'book', startTime=4.96, endTime=6.5)) + + bookTracks.append(ActorInterval(self, 'book', startTime=4.96, endTime=6.5)) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('closeBook') + else: + trackName = 'closeBook' + self.track = Sequence(Func(self.showBooks), bookTracks, Func(self.hideBooks), name=trackName) + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + self.setActiveShadow(0) + + def exitCloseBook(self): + self.playingAnim = 'neutralcb' + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self, 'exitCloseBook') + return + + def getSoundTeleport(self): + if not self.soundTeleport: + self.soundTeleport = base.loadSfx('phase_3.5/audio/sfx/AV_teleport.mp3') + return self.soundTeleport + + def getTeleportOutTrack(self, autoFinishTrack = 1): + + def showHoles(holes, hands): + for hole, hand in zip(holes, hands): + hole.reparentTo(hand) + + def reparentHoles(holes, toon): + holes[0].reparentTo(toon) + holes[1].detachNode() + holes[2].detachNode() + holes[0].setBin('shadow', 0) + holes[0].setDepthTest(0) + holes[0].setDepthWrite(0) + + def cleanupHoles(holes): + holes[0].detachNode() + holes[0].clearBin() + holes[0].clearDepthTest() + holes[0].clearDepthWrite() + + holes = self.getHoleActors() + hands = self.getRightHands() + holeTrack = Track((0.0, Func(showHoles, holes, hands)), (0.5, SoundInterval(self.getSoundTeleport(), node=self)), (1.708, Func(reparentHoles, holes, self)), (3.4, Func(cleanupHoles, holes))) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('teleportOut') + else: + trackName = 'teleportOut' + track = Parallel(holeTrack, name=trackName, autoFinish=autoFinishTrack) + for hole in holes: + track.append(ActorInterval(hole, 'hole', duration=3.4)) + + track.append(ActorInterval(self, 'teleport', duration=3.4)) + return track + + def startQuestMap(self): + pass + + def stopQuestMap(self): + pass + + def enterTeleportOut(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + name = self.name + if hasattr(self, 'doId'): + name += '-' + str(self.doId) + self.notify.debug('enterTeleportOut %s' % name) + if self.ghostMode or self.isDisguised: + if callback: + callback(*extraArgs) + return + self.playingAnim = 'teleport' + Emote.globalEmote.disableAll(self, 'enterTeleportOut') + if self.isLocal(): + autoFinishTrack = 0 + else: + autoFinishTrack = 1 + self.track = self.getTeleportOutTrack(autoFinishTrack) + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), self.finishTeleportOut, [callback, extraArgs]) + holeClip = PlaneNode('holeClip') + self.holeClipPath = self.attachNewNode(holeClip) + self.getGeomNode().setClipPlane(self.holeClipPath) + self.nametag3d.setClipPlane(self.holeClipPath) + self.track.start(ts) + self.setActiveShadow(0) + + def finishTeleportOut(self, callback = None, extraArgs = []): + name = self.name + if hasattr(self, 'doId'): + name += '-' + str(self.doId) + self.notify.debug('finishTeleportOut %s' % name) + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if hasattr(self, 'animFSM'): + self.animFSM.request('TeleportedOut') + if callback: + callback(*extraArgs) + return + + def exitTeleportOut(self): + name = self.name + if hasattr(self, 'doId'): + name += '-' + str(self.doId) + self.notify.debug('exitTeleportOut %s' % name) + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + self.track = None + geomNode = self.getGeomNode() + if geomNode and not geomNode.isEmpty(): + self.getGeomNode().clearClipPlane() + if self.nametag3d and not self.nametag3d.isEmpty(): + self.nametag3d.clearClipPlane() + if self.holeClipPath: + self.holeClipPath.removeNode() + self.holeClipPath = None + Emote.globalEmote.releaseAll(self, 'exitTeleportOut') + if self and not self.isEmpty(): + self.show() + return + + def enterTeleportedOut(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.setActiveShadow(0) + + def exitTeleportedOut(self): + pass + + def getDiedInterval(self, autoFinishTrack = 1): + sound = loader.loadSfx('phase_5/audio/sfx/ENC_Lose.mp3') + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('died') + else: + trackName = 'died' + ival = Sequence(Func(Emote.globalEmote.disableBody, self), Func(self.sadEyes), Func(self.blinkEyes), Track((0, ActorInterval(self, 'lose')), (2, SoundInterval(sound, node=self)), (5.333, self.scaleInterval(1.5, VBase3(0.01, 0.01, 0.01), blendType='easeInOut'))), Func(self.detachNode), Func(self.setScale, 1, 1, 1), Func(self.normalEyes), Func(self.blinkEyes), Func(Emote.globalEmote.releaseBody, self), name=trackName, autoFinish=autoFinishTrack) + return ival + + def enterDied(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if self.ghostMode: + if callback: + callback(*extraArgs) + return + if self.isDisguised: + self.takeOffSuit() + self.playingAnim = 'lose' + Emote.globalEmote.disableAll(self, 'enterDied') + if self.isLocal(): + autoFinishTrack = 0 + else: + autoFinishTrack = 1 + if hasattr(self, 'jumpLandAnimFixTask') and self.jumpLandAnimFixTask: + self.jumpLandAnimFixTask.remove() + self.jumpLandAnimFixTask = None + self.track = self.getDiedInterval(autoFinishTrack) + if callback: + self.track = Sequence(self.track, Func(callback, *extraArgs), autoFinish=autoFinishTrack) + self.track.start(ts) + self.setActiveShadow(0) + return + + def finishDied(self, callback = None, extraArgs = []): + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if hasattr(self, 'animFSM'): + self.animFSM.request('TeleportedOut') + if callback: + callback(*extraArgs) + return + + def exitDied(self): + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self, 'exitDied') + self.show() + return + + def getTeleportInTrack(self): + hole = self.getHoleActors()[0] + hole.setBin('shadow', 0) + hole.setDepthTest(0) + hole.setDepthWrite(0) + holeTrack = Sequence() + holeTrack.append(Func(hole.reparentTo, self)) + pos = Point3(0, -2.4, 0) + holeTrack.append(Func(hole.setPos, self, pos)) + holeTrack.append(ActorInterval(hole, 'hole', startTime=3.4, endTime=3.1)) + holeTrack.append(Wait(0.6)) + holeTrack.append(ActorInterval(hole, 'hole', startTime=3.1, endTime=3.4)) + + def restoreHole(hole): + hole.setPos(0, 0, 0) + hole.detachNode() + hole.clearBin() + hole.clearDepthTest() + hole.clearDepthWrite() + + holeTrack.append(Func(restoreHole, hole)) + toonTrack = Sequence(Wait(0.3), Func(self.getGeomNode().show), Func(self.nametag3d.show), ActorInterval(self, 'jump', startTime=0.45)) + if hasattr(self, 'uniqueName'): + trackName = self.uniqueName('teleportIn') + else: + trackName = 'teleportIn' + return Parallel(holeTrack, toonTrack, name=trackName) + + def enterTeleportIn(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if self.ghostMode or self.isDisguised: + if callback: + callback(*extraArgs) + return + self.show() + self.playingAnim = 'teleport' + Emote.globalEmote.disableAll(self, 'enterTeleportIn') + self.pose('teleport', self.getNumFrames('teleport') - 1) + self.getGeomNode().hide() + self.nametag3d.hide() + self.track = self.getTeleportInTrack() + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + self.setActiveShadow(0) + + def exitTeleportIn(self): + self.playingAnim = None + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + if not self.ghostMode and not self.isDisguised: + self.getGeomNode().show() + self.nametag3d.show() + Emote.globalEmote.releaseAll(self, 'exitTeleportIn') + return + + def enterSitStart(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self) + self.playingAnim = 'sit-start' + if self.isLocal(): + self.track = Sequence(ActorInterval(self, 'sit-start'), Func(self.b_setAnimState, 'Sit', animMultiplier)) + else: + self.track = Sequence(ActorInterval(self, 'sit-start')) + self.track.start(ts) + self.setActiveShadow(0) + + def exitSitStart(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseBody(self) + return + + def enterSit(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self) + self.playingAnim = 'sit' + self.loop('sit') + self.setActiveShadow(0) + + def exitSit(self): + self.playingAnim = 'neutral' + Emote.globalEmote.releaseBody(self) + + def enterSleep(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.stopLookAround() + self.stopBlink() + self.closeEyes() + self.lerpLookAt(Point3(0, 1, -4)) + self.loop('neutral') + self.setPlayRate(animMultiplier * 0.4, 'neutral') + self.setChatAbsolute(SLEEP_STRING, CFThought) + if self == base.localAvatar: + print 'adding timeout task' + taskMgr.doMethodLater(self.afkTimeout, self.__handleAfkTimeout, self.uniqueName('afkTimeout')) + self.setActiveShadow(0) + + def __handleAfkTimeout(self, task): + print 'handling timeout' + self.ignore('wakeup') + self.takeOffSuit() + base.cr.playGame.getPlace().fsm.request('final') + self.b_setAnimState('TeleportOut', 1, self.__handleAfkExitTeleport, [0]) + return Task.done + + def __handleAfkExitTeleport(self, requestStatus): + self.notify.info('closing shard...') + base.cr.gameFSM.request('closeShard', ['afkTimeout']) + + def exitSleep(self): + taskMgr.remove(self.uniqueName('afkTimeout')) + self.startLookAround() + self.openEyes() + self.startBlink() + if config.GetBool('stuck-sleep-fix', 1): + doClear = SLEEP_STRING in (self.nametag.getChat(), self.nametag.getStompText()) + else: + doClear = self.nametag.getChat() == SLEEP_STRING + if doClear: + self.clearChat() + self.lerpLookAt(Point3(0, 1, 0), time=0.25) + self.stop() + + def enterPush(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableBody(self) + self.playingAnim = 'push' + self.track = Sequence(ActorInterval(self, 'push')) + self.track.loop() + self.setActiveShadow(1) + + def exitPush(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseBody(self) + return + + def enterEmote(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if len(extraArgs) > 0: + emoteIndex = extraArgs[0] + else: + return + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('neutral', 1.0), + ('walk', 1.0), + ('run', 1.0), + ('walk', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + if self.isLocal() and emoteIndex != Emote.globalEmote.EmoteSleepIndex: + if self.sleepFlag: + self.b_setAnimState('Happy', self.animMultiplier) + self.wakeUp() + duration = 0 + self.emoteTrack, duration = Emote.globalEmote.doEmote(self, emoteIndex, ts) + self.setActiveShadow(1) + return + + def doEmote(self, emoteIndex, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + if not self.isLocal(): + if base.cr.avatarFriendsManager.checkIgnored(self.doId): + return + duration = 0 + if self.isLocal(): + self.wakeUp() + if self.hasTrackAnimToSpeed(): + self.trackAnimToSpeed(None) + self.emoteTrack, duration = Emote.globalEmote.doEmote(self, emoteIndex, ts) + return + + def __returnToLastAnim(self, task): + if self.playingAnim: + self.loop(self.playingAnim) + elif self.hp > 0: + self.loop('neutral') + else: + self.loop('sad-neutral') + return Task.done + + def __finishEmote(self, task): + if self.isLocal(): + if self.hp > 0: + self.b_setAnimState('Happy') + else: + self.b_setAnimState('Sad') + return Task.done + + def exitEmote(self): + self.stop() + if self.emoteTrack != None: + self.emoteTrack.finish() + self.emoteTrack = None + taskMgr.remove(self.taskName('finishEmote')) + return + + def enterSquish(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self) + sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.mp3') + lerpTime = 0.1 + node = self.getGeomNode().getChild(0) + origScale = node.getScale() + self.track = Sequence(LerpScaleInterval(node, lerpTime, VBase3(2, 2, 0.025), blendType='easeInOut'), Wait(1.0), Parallel(Sequence(Wait(0.4), LerpScaleInterval(node, lerpTime, VBase3(1.4, 1.4, 1.4), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 2.0, VBase3(0.8, 0.8, 0.8), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 3.0, origScale, blendType='easeInOut')), ActorInterval(self, 'jump', startTime=0.2), SoundInterval(sound))) + self.track.start(ts) + self.setActiveShadow(1) + + def exitSquish(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self) + return + + def enterFallDown(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = 'fallDown' + Emote.globalEmote.disableAll(self) + self.track = Sequence(ActorInterval(self, 'slip-backward'), name='fallTrack') + if callback: + self.track.setDoneEvent(self.track.getName()) + self.acceptOnce(self.track.getName(), callback, extraArgs) + self.track.start(ts) + + def exitFallDown(self): + self.playingAnim = 'neutral' + if self.track != None: + self.ignore(self.track.getName()) + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + Emote.globalEmote.releaseAll(self) + return + + def stunToon(self, ts = 0, callback = None, knockdown = 0): + if not self.isStunned: + if self.stunTrack: + self.stunTrack.finish() + self.stunTrack = None + + def setStunned(stunned): + self.isStunned = stunned + if self == base.localAvatar: + messenger.send('toonStunned-' + str(self.doId), [self.isStunned]) + + node = self.getGeomNode() + lerpTime = 0.5 + down = self.doToonColorScale(VBase4(1, 1, 1, 0.6), lerpTime) + up = self.doToonColorScale(VBase4(1, 1, 1, 0.9), lerpTime) + clear = self.doToonColorScale(self.defaultColorScale, lerpTime) + track = Sequence(Func(setStunned, 1), down, up, down, up, down, up, down, clear, Func(self.restoreDefaultColorScale), Func(setStunned, 0)) + if knockdown: + self.stunTrack = Parallel(ActorInterval(self, animName='slip-backward'), track) + else: + self.stunTrack = track + self.stunTrack.start() + return + + def getPieces(self, *pieces): + results = [] + for lodName in self.getLODNames(): + for partName, pieceNames in pieces: + part = self.getPart(partName, lodName) + if part: + if type(pieceNames) == types.StringType: + pieceNames = (pieceNames,) + for pieceName in pieceNames: + npc = part.findAllMatches('**/%s;+s' % pieceName) + for i in range(npc.getNumPaths()): + results.append(npc[i]) + + return results + + def applyCheesyEffect(self, effect, lerpTime = 0): + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + if self.cheesyEffect != effect: + oldEffect = self.cheesyEffect + self.cheesyEffect = effect + if oldEffect == ToontownGlobals.CENormal: + self.effectTrack = self.__doCheesyEffect(effect, lerpTime) + elif effect == ToontownGlobals.CENormal: + self.effectTrack = self.__undoCheesyEffect(oldEffect, lerpTime) + else: + self.effectTrack = Sequence(self.__undoCheesyEffect(oldEffect, lerpTime / 2.0), self.__doCheesyEffect(effect, lerpTime / 2.0)) + self.effectTrack.start() + return + + def reapplyCheesyEffect(self, lerpTime = 0): + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + effect = self.cheesyEffect + self.effectTrack = Sequence(self.__undoCheesyEffect(effect, 0), self.__doCheesyEffect(effect, lerpTime)) + self.effectTrack.start() + return + + def clearCheesyEffect(self, lerpTime = 0): + self.applyCheesyEffect(ToontownGlobals.CENormal, lerpTime=lerpTime) + if self.effectTrack != None: + self.effectTrack.finish() + self.effectTrack = None + return + + def __doHeadScale(self, scale, lerpTime): + if scale == None: + scale = ToontownGlobals.toonHeadScales[self.style.getAnimal()] + track = Parallel() + for hi in range(self.headParts.getNumPaths()): + head = self.headParts[hi] + track.append(LerpScaleInterval(head, lerpTime, scale, blendType='easeInOut')) + + return track + + def __doLegsScale(self, scale, lerpTime): + if scale == None: + scale = 1 + invScale = 1 + else: + invScale = 1.0 / scale + track = Parallel() + for li in range(self.legsParts.getNumPaths()): + legs = self.legsParts[li] + torso = self.torsoParts[li] + track.append(LerpScaleInterval(legs, lerpTime, scale, blendType='easeInOut')) + track.append(LerpScaleInterval(torso, lerpTime, invScale, blendType='easeInOut')) + + return track + + def __doToonScale(self, scale, lerpTime): + if scale == None: + scale = 1 + node = self.getGeomNode().getChild(0) + track = Sequence(Parallel(LerpHprInterval(node, lerpTime, Vec3(0.0, 0.0, 0.0), blendType='easeInOut'), LerpScaleInterval(node, lerpTime, scale, blendType='easeInOut')), Func(self.resetHeight)) + return track + + def doToonColorScale(self, scale, lerpTime, keepDefault = 0): + if keepDefault: + self.defaultColorScale = scale + if scale == None: + scale = VBase4(1, 1, 1, 1) + node = self.getGeomNode() + caps = self.getPieces(('torso', 'torso-bot-cap')) + track = Sequence() + track.append(Func(node.setTransparency, 1)) + if scale[3] != 1: + for cap in caps: + track.append(HideInterval(cap)) + + track.append(LerpColorScaleInterval(node, lerpTime, scale, blendType='easeInOut')) + if scale[3] == 1: + track.append(Func(node.clearTransparency)) + for cap in caps: + track.append(ShowInterval(cap)) + + elif scale[3] == 0: + track.append(Func(node.clearTransparency)) + return track + + def __doPumpkinHeadSwitch(self, lerpTime, toPumpkin): + node = self.getGeomNode() + + def getDustCloudIval(): + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1) + dustCloud.setBillboardAxis(2.0) + dustCloud.setZ(3) + dustCloud.setScale(0.4) + dustCloud.createTrack() + return Sequence(Func(dustCloud.reparentTo, self), dustCloud.track, Func(dustCloud.destroy), name='dustCloadIval') + + dust = getDustCloudIval() + track = Sequence() + if toPumpkin: + track.append(Func(self.stopBlink)) + track.append(Func(self.closeEyes)) + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def hideParts(): + self.notify.debug('hideParts') + for head in self.headParts: + for p in head.getChildren(): + if hasattr(self, 'pumpkins') and not self.pumpkins.hasPath(p): + p.hide() + p.setTag('pumpkin', 'enabled') + + track.append(Func(hideParts)) + track.append(Func(self.enablePumpkins, True)) + else: + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def showHiddenParts(): + self.notify.debug('showHiddenParts') + for head in self.headParts: + for p in head.getChildren(): + if not self.pumpkins.hasPath(p) and p.getTag('pumpkin') == 'enabled': + p.show() + p.setTag('pumpkin', 'disabled') + + track.append(Func(showHiddenParts)) + track.append(Func(self.enablePumpkins, False)) + track.append(Func(self.startBlink)) + return track + + def __doSnowManHeadSwitch(self, lerpTime, toSnowMan): + node = self.getGeomNode() + + def getDustCloudIval(): + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=0) + dustCloud.setBillboardAxis(2.0) + dustCloud.setZ(3) + dustCloud.setScale(0.4) + dustCloud.createTrack() + return Sequence(Func(dustCloud.reparentTo, self), dustCloud.track, Func(dustCloud.destroy), name='dustCloadIval') + + dust = getDustCloudIval() + track = Sequence() + if toSnowMan: + track.append(Func(self.stopBlink)) + track.append(Func(self.closeEyes)) + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def hideParts(): + self.notify.debug('HidePaths') + for hi in range(self.headParts.getNumPaths()): + head = self.headParts[hi] + parts = head.getChildren() + for pi in range(parts.getNumPaths()): + p = parts[pi] + if not p.isHidden(): + p.hide() + p.setTag('snowman', 'enabled') + + track.append(Func(hideParts)) + track.append(Func(self.enableSnowMen, True)) + else: + if lerpTime > 0.0: + track.append(Func(dust.start)) + track.append(Wait(0.5)) + else: + dust.finish() + + def showHiddenParts(): + self.notify.debug('ShowHiddenPaths') + for hi in range(self.headParts.getNumPaths()): + head = self.headParts[hi] + parts = head.getChildren() + for pi in range(parts.getNumPaths()): + p = parts[pi] + if not self.snowMen.hasPath(p) and p.getTag('snowman') == 'enabled': + p.show() + p.setTag('snowman', 'disabled') + + track.append(Func(showHiddenParts)) + track.append(Func(self.enableSnowMen, False)) + track.append(Func(self.startBlink)) + return track + + def __doGreenToon(self, lerpTime, toGreen): + track = Sequence() + greenTrack = Parallel() + + def getDustCloudIval(): + dustCloud = DustCloud.DustCloud(fBillboard=0, wantSound=1) + dustCloud.setBillboardAxis(2.0) + dustCloud.setZ(3) + dustCloud.setScale(0.4) + dustCloud.createTrack() + return Sequence(Func(dustCloud.reparentTo, self), dustCloud.track, Func(dustCloud.destroy), name='dustCloadIval') + + if lerpTime > 0.0: + dust = getDustCloudIval() + track.append(Func(dust.start)) + track.append(Wait(0.5)) + if toGreen: + skinGreen = VBase4(76 / 255.0, 240 / 255.0, 84 / 255.0, 1) + muzzleGreen = VBase4(4 / 255.0, 205 / 255.0, 90 / 255.0, 1) + gloveGreen = VBase4(14 / 255.0, 173 / 255.0, 40 / 255.0, 1) + greenTrack.append(self.__colorToonSkin(skinGreen, lerpTime)) + greenTrack.append(self.__colorToonEars(skinGreen, muzzleGreen, lerpTime)) + greenTrack.append(self.__colorScaleToonMuzzle(muzzleGreen, lerpTime)) + greenTrack.append(self.__colorToonGloves(gloveGreen, lerpTime)) + else: + greenTrack.append(self.__colorToonSkin(None, lerpTime)) + greenTrack.append(self.__colorToonEars(None, None, lerpTime)) + greenTrack.append(self.__colorScaleToonMuzzle(None, lerpTime)) + greenTrack.append(self.__colorToonGloves(None, lerpTime)) + track.append(greenTrack) + return track + + def __colorToonSkin(self, color, lerpTime): + track = Sequence() + colorTrack = Parallel() + torsoPieces = self.getPieces(('torso', ('arms', 'neck'))) + legPieces = self.getPieces(('legs', ('legs', 'feet'))) + headPieces = self.getPieces(('head', '*head*')) + if color == None: + armColor = self.style.getArmColor() + legColor = self.style.getLegColor() + headColor = self.style.getHeadColor() + else: + armColor = color + legColor = color + headColor = color + for piece in torsoPieces: + colorTrack.append(Func(piece.setColor, armColor)) + + for piece in legPieces: + colorTrack.append(Func(piece.setColor, legColor)) + + for piece in headPieces: + if 'hatNode' not in str(piece) and 'glassesNode' not in str(piece): + colorTrack.append(Func(piece.setColor, headColor)) + + track.append(colorTrack) + return track + + def __colorToonEars(self, color, colorScale, lerpTime): + track = Sequence() + earPieces = self.getPieces(('head', '*ear*')) + if len(earPieces) == 0: + return track + colorTrack = Parallel() + if earPieces[0].hasColor(): + if color == None: + headColor = self.style.getHeadColor() + else: + headColor = color + for piece in earPieces: + colorTrack.append(Func(piece.setColor, headColor)) + + else: + if colorScale == None: + colorScale = VBase4(1, 1, 1, 1) + for piece in earPieces: + colorTrack.append(Func(piece.setColorScale, colorScale)) + + track.append(colorTrack) + return track + + def __colorScaleToonMuzzle(self, scale, lerpTime): + track = Sequence() + colorTrack = Parallel() + muzzlePieces = self.getPieces(('head', '*muzzle*')) + if scale == None: + scale = VBase4(1, 1, 1, 1) + for piece in muzzlePieces: + colorTrack.append(Func(piece.setColorScale, scale)) + + track.append(colorTrack) + return track + + def __colorToonGloves(self, color, lerpTime): + track = Sequence() + colorTrack = Parallel() + glovePieces = self.getPieces(('torso', '*hands*')) + if color == None: + for piece in glovePieces: + colorTrack.append(Func(piece.clearColor)) + + else: + for piece in glovePieces: + colorTrack.append(Func(piece.setColor, color)) + + track.append(colorTrack) + return track + + def __doBigAndWhite(self, color, scale, lerpTime): + track = Parallel() + track.append(self.__doToonColor(color, lerpTime)) + track.append(self.__doToonScale(scale, lerpTime)) + return track + + def __doVirtual(self): + track = Parallel() + track.append(self.__doToonColor(VBase4(0.25, 0.25, 1.0, 1), 0.0)) + self.setPartsAdd(self.getHeadParts()) + self.setPartsAdd(self.getTorsoParts()) + self.setPartsAdd(self.getHipsParts()) + self.setPartsAdd(self.getLegsParts()) + return track + + def __doUnVirtual(self): + track = Parallel() + track.append(self.__doToonColor(None, 0.0)) + self.setPartsNormal(self.getHeadParts(), 1) + self.setPartsNormal(self.getTorsoParts(), 1) + self.setPartsNormal(self.getHipsParts(), 1) + self.setPartsNormal(self.getLegsParts(), 1) + return track + + def setPartsAdd(self, parts): + actorCollection = parts + for thingIndex in range(0, actorCollection.getNumPaths()): + thing = actorCollection[thingIndex] + if thing.getName() not in ('joint_attachMeter', 'joint_nameTag'): + thing.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MAdd)) + thing.setDepthWrite(False) + self.setBin('fixed', 1) + + def setPartsNormal(self, parts, alpha = 0): + actorCollection = parts + for thingIndex in range(0, actorCollection.getNumPaths()): + thing = actorCollection[thingIndex] + if thing.getName() not in ('joint_attachMeter', 'joint_nameTag'): + thing.setAttrib(ColorBlendAttrib.make(ColorBlendAttrib.MNone)) + thing.setDepthWrite(True) + self.setBin('default', 0) + if alpha: + thing.setTransparency(1) + thing.setBin('transparent', 0) + + def __doToonGhostColorScale(self, scale, lerpTime, keepDefault = 0): + if keepDefault: + self.defaultColorScale = scale + if scale == None: + scale = VBase4(1, 1, 1, 1) + node = self.getGeomNode() + caps = self.getPieces(('torso', 'torso-bot-cap')) + track = Sequence() + track.append(Func(node.setTransparency, 1)) + track.append(ShowInterval(node)) + if scale[3] != 1: + for cap in caps: + track.append(HideInterval(cap)) + + track.append(LerpColorScaleInterval(node, lerpTime, scale, blendType='easeInOut')) + if scale[3] == 1: + track.append(Func(node.clearTransparency)) + for cap in caps: + track.append(ShowInterval(cap)) + + elif scale[3] == 0: + track.append(Func(node.clearTransparency)) + track.append(HideInterval(node)) + return track + + def restoreDefaultColorScale(self): + node = self.getGeomNode() + if node: + if self.defaultColorScale: + node.setColorScale(self.defaultColorScale) + if self.defaultColorScale[3] != 1: + node.setTransparency(1) + else: + node.clearTransparency() + else: + node.clearColorScale() + node.clearTransparency() + + def __doToonColor(self, color, lerpTime): + node = self.getGeomNode() + if color == None: + return Func(node.clearColor) + else: + return Func(node.setColor, color, 1) + return + + def __doPartsColorScale(self, scale, lerpTime): + if scale == None: + scale = VBase4(1, 1, 1, 1) + node = self.getGeomNode() + pieces = self.getPieces(('torso', ('arms', 'neck')), ('legs', ('legs', 'feet')), ('head', '+GeomNode')) + track = Sequence() + track.append(Func(node.setTransparency, 1)) + for piece in pieces: + if piece.getName()[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral': + continue + track.append(ShowInterval(piece)) + + p1 = Parallel() + for piece in pieces: + if piece.getName()[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral': + continue + p1.append(LerpColorScaleInterval(piece, lerpTime, scale, blendType='easeInOut')) + + track.append(p1) + if scale[3] == 1: + track.append(Func(node.clearTransparency)) + elif scale[3] == 0: + track.append(Func(node.clearTransparency)) + for piece in pieces: + if piece.getName()[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral': + continue + track.append(HideInterval(piece)) + + self.generateHat() + self.generateGlasses() + return track + + def __doCheesyEffect(self, effect, lerpTime): + if effect == ToontownGlobals.CEBigHead: + return self.__doHeadScale(2.5, lerpTime) + elif effect == ToontownGlobals.CESmallHead: + return self.__doHeadScale(0.5, lerpTime) + elif effect == ToontownGlobals.CEBigLegs: + return self.__doLegsScale(1.4, lerpTime) + elif effect == ToontownGlobals.CESmallLegs: + return self.__doLegsScale(0.6, lerpTime) + elif effect == ToontownGlobals.CEBigToon: + return self.__doToonScale(ToontownGlobals.BigToonScale, lerpTime) + elif effect == ToontownGlobals.CESmallToon: + return self.__doToonScale(ToontownGlobals.SmallToonScale, lerpTime) + elif effect == ToontownGlobals.CEFlatPortrait: + return self.__doToonScale(VBase3(1, 0.05, 1), lerpTime) + elif effect == ToontownGlobals.CEFlatProfile: + return self.__doToonScale(VBase3(0.05, 1, 1), lerpTime) + elif effect == ToontownGlobals.CETransparent: + return self.doToonColorScale(VBase4(1, 1, 1, 0.6), lerpTime, keepDefault=1) + elif effect == ToontownGlobals.CENoColor: + return self.__doToonColor(VBase4(1, 1, 1, 1), lerpTime) + elif effect == ToontownGlobals.CEInvisible: + return self.__doPartsColorScale(VBase4(1, 1, 1, 0), lerpTime) + elif effect == ToontownGlobals.CEPumpkin: + return self.__doPumpkinHeadSwitch(lerpTime, toPumpkin=True) + elif effect == ToontownGlobals.CEBigWhite: + return self.__doBigAndWhite(VBase4(1, 1, 1, 1), ToontownGlobals.BigToonScale, lerpTime) + elif effect == ToontownGlobals.CESnowMan: + return self.__doSnowManHeadSwitch(lerpTime, toSnowMan=True) + elif effect == ToontownGlobals.CEGreenToon: + return self.__doGreenToon(lerpTime, toGreen=True) + elif effect == ToontownGlobals.CEVirtual: + return self.__doVirtual() + elif effect == ToontownGlobals.CEGhost: + alpha = 0 + if localAvatar.seeGhosts: + alpha = 0.2 + return Sequence(self.__doToonGhostColorScale(VBase4(1, 1, 1, alpha), lerpTime, keepDefault=1), Func(self.nametag3d.hide)) + return Sequence() + + def __undoCheesyEffect(self, effect, lerpTime): + if effect == ToontownGlobals.CEBigHead: + return self.__doHeadScale(None, lerpTime) + elif effect == ToontownGlobals.CESmallHead: + return self.__doHeadScale(None, lerpTime) + if effect == ToontownGlobals.CEBigLegs: + return self.__doLegsScale(None, lerpTime) + elif effect == ToontownGlobals.CESmallLegs: + return self.__doLegsScale(None, lerpTime) + elif effect == ToontownGlobals.CEBigToon: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CESmallToon: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CEFlatPortrait: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CEFlatProfile: + return self.__doToonScale(None, lerpTime) + elif effect == ToontownGlobals.CETransparent: + return self.doToonColorScale(None, lerpTime, keepDefault=1) + elif effect == ToontownGlobals.CENoColor: + return self.__doToonColor(None, lerpTime) + elif effect == ToontownGlobals.CEInvisible: + return self.__doPartsColorScale(None, lerpTime) + elif effect == ToontownGlobals.CEPumpkin: + return self.__doPumpkinHeadSwitch(lerpTime, toPumpkin=False) + elif effect == ToontownGlobals.CEBigWhite: + return self.__doBigAndWhite(None, None, lerpTime) + elif effect == ToontownGlobals.CESnowMan: + return self.__doSnowManHeadSwitch(lerpTime, toSnowMan=False) + elif effect == ToontownGlobals.CEGreenToon: + return self.__doGreenToon(lerpTime, toGreen=False) + elif effect == ToontownGlobals.CEVirtual: + return self.__doUnVirtual() + elif effect == ToontownGlobals.CEGhost: + return Sequence(Func(self.nametag3d.show), self.__doToonGhostColorScale(None, lerpTime, keepDefault=1)) + return Sequence() + + def putOnSuit(self, suitType, setDisplayName = True, rental = False): + if self.isDisguised: + self.takeOffSuit() + if launcher and not launcher.getPhaseComplete(5): + return + from toontown.suit import Suit + deptIndex = suitType + suit = Suit.Suit() + dna = SuitDNA.SuitDNA() + if rental == True: + if SuitDNA.suitDepts[deptIndex] == 's': + suitType = 'cc' + elif SuitDNA.suitDepts[deptIndex] == 'm': + suitType = 'sc' + elif SuitDNA.suitDepts[deptIndex] == 'l': + suitType = 'bf' + elif SuitDNA.suitDepts[deptIndex] == 'c': + suitType = 'f' + else: + self.notify.warning('Suspicious: Incorrect rental suit department requested') + suitType = 'cc' + dna.newSuit(suitType) + suit.setStyle(dna) + suit.isDisguised = 1 + suit.generateSuit() + suit.initializeDropShadow() + suit.setPos(self.getPos()) + suit.setHpr(self.getHpr()) + for part in suit.getHeadParts(): + part.hide() + + suitHeadNull = suit.find('**/joint_head') + toonHead = self.getPart('head', '1000') + Emote.globalEmote.disableAll(self) + toonGeom = self.getGeomNode() + toonGeom.hide() + worldScale = toonHead.getScale(render) + self.headOrigScale = toonHead.getScale() + headPosNode = hidden.attachNewNode('headPos') + toonHead.reparentTo(headPosNode) + toonHead.setPos(0, 0, 0.2) + headPosNode.reparentTo(suitHeadNull) + headPosNode.setScale(render, worldScale) + suitGeom = suit.getGeomNode() + suitGeom.reparentTo(self) + if rental == True: + suit.makeRentalSuit(SuitDNA.suitDepts[deptIndex]) + self.suit = suit + self.suitGeom = suitGeom + self.setHeight(suit.getHeight()) + self.nametag3d.setPos(0, 0, self.height + 1.3) + if self.isLocal(): + if hasattr(self, 'book'): + self.book.obscureButton(1) + self.oldForward = ToontownGlobals.ToonForwardSpeed + self.oldReverse = ToontownGlobals.ToonReverseSpeed + self.oldRotate = ToontownGlobals.ToonRotateSpeed + ToontownGlobals.ToonForwardSpeed = ToontownGlobals.SuitWalkSpeed + ToontownGlobals.ToonReverseSpeed = ToontownGlobals.SuitWalkSpeed + ToontownGlobals.ToonRotateSpeed = ToontownGlobals.ToonRotateSlowSpeed + if self.hasTrackAnimToSpeed(): + self.stopTrackAnimToSpeed() + self.startTrackAnimToSpeed() + self.controlManager.disableAvatarJump() + indices = range(OTPLocalizer.SCMenuCommonCogIndices[0], OTPLocalizer.SCMenuCommonCogIndices[1] + 1) + customIndices = OTPLocalizer.SCMenuCustomCogIndices[suitType] + indices += range(customIndices[0], customIndices[1] + 1) + self.chatMgr.chatInputSpeedChat.addCogMenu(indices) + self.suit.loop('neutral') + self.isDisguised = 1 + self.setFont(ToontownGlobals.getSuitFont()) + if setDisplayName: + if hasattr(base, 'idTags') and base.idTags: + name = self.getAvIdName() + else: + name = self.getName() + suitDept = SuitDNA.suitDepts.index(SuitDNA.getSuitDept(suitType)) + suitName = SuitBattleGlobals.SuitAttributes[suitType]['name'] + self.nametag.setDisplayName(TTLocalizer.SuitBaseNameWithLevel % {'name': name, + 'dept': suitName, + 'level': self.cogLevels[suitDept] + 1}) + self.nametag.setNameWordwrap(9.0) + + def takeOffSuit(self): + if not self.isDisguised: + return + suitType = self.suit.style.name + toonHeadNull = self.find('**/1000/**/def_head') + if not toonHeadNull: + toonHeadNull = self.find('**/1000/**/joint_head') + toonHead = self.getPart('head', '1000') + toonHead.reparentTo(toonHeadNull) + toonHead.setScale(self.headOrigScale) + toonHead.setPos(0, 0, 0) + headPosNode = self.suitGeom.find('**/headPos') + headPosNode.removeNode() + self.suitGeom.reparentTo(self.suit) + self.resetHeight() + self.nametag3d.setPos(0, 0, self.height + 0.5) + toonGeom = self.getGeomNode() + toonGeom.show() + Emote.globalEmote.releaseAll(self) + self.isDisguised = 0 + self.setFont(ToontownGlobals.getToonFont()) + self.nametag.setNameWordwrap(-1) + if hasattr(base, 'idTags') and base.idTags: + name = self.getAvIdName() + else: + name = self.getName() + self.setDisplayName(name) + if self.isLocal(): + if hasattr(self, 'book'): + self.book.obscureButton(0) + ToontownGlobals.ToonForwardSpeed = self.oldForward + ToontownGlobals.ToonReverseSpeed = self.oldReverse + ToontownGlobals.ToonRotateSpeed = self.oldRotate + if self.hasTrackAnimToSpeed(): + self.stopTrackAnimToSpeed() + self.startTrackAnimToSpeed() + del self.oldForward + del self.oldReverse + del self.oldRotate + self.controlManager.enableAvatarJump() + self.chatMgr.chatInputSpeedChat.removeCogMenu() + self.suit.delete() + del self.suit + del self.suitGeom + + def makeWaiter(self): + if not self.isDisguised: + return + self.suit.makeWaiter(self.suitGeom) + + def getPieModel(self): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + if self.pieModel != None and self.__pieModelType != self.pieType: + self.pieModel.detachNode() + self.pieModel = None + if self.pieModel == None: + self.__pieModelType = self.pieType + pieName = ToontownBattleGlobals.pieNames[self.pieType] + self.pieModel = BattleProps.globalPropPool.getProp(pieName) + self.pieScale = self.pieModel.getScale() + return self.pieModel + + def getPresentPieInterval(self, x, y, z, h, p, r): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + from toontown.battle import MovieUtil + pie = self.getPieModel() + pieName = ToontownBattleGlobals.pieNames[self.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + pingpongPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=0, endFrame=31) + pingpongPie = Func(pie.pingpong, pieName, fromFrame=32, toFrame=47) + track = Sequence(Func(self.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, self.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), Parallel(pie.scaleInterval(1, self.pieScale, startScale=MovieUtil.PNT3_NEARZERO), ActorInterval(self, 'throw', startFrame=0, endFrame=31), animPie), Func(self.pingpong, 'throw', fromFrame=32, toFrame=47), pingpongPie) + return track + + def getTossPieInterval(self, x, y, z, h, p, r, power, beginFlyIval = Sequence()): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pie = self.getPieModel() + flyPie = pie.copyTo(NodePath('a')) + pieName = ToontownBattleGlobals.pieNames[self.pieType] + pieType = BattleProps.globalPropPool.getPropType(pieName) + animPie = Sequence() + if pieType == 'actor': + animPie = ActorInterval(pie, pieName, startFrame=48) + sound = loader.loadSfx('phase_3.5/audio/sfx/AA_pie_throw_only.mp3') + t = power / 100.0 + dist = 100 - 70 * t + time = 1 + 0.5 * t + proj = ProjectileInterval(None, startPos=Point3(0, 0, 0), endPos=Point3(0, dist, 0), duration=time) + relVel = proj.startVel + + def getVelocity(toon = self, relVel = relVel): + return render.getRelativeVector(toon, relVel) + + toss = Track((0, Sequence(Func(self.setPosHpr, x, y, z, h, p, r), Func(pie.reparentTo, self.rightHand), Func(pie.setPosHpr, 0, 0, 0, 0, 0, 0), Parallel(ActorInterval(self, 'throw', startFrame=48), animPie), Func(self.loop, 'neutral'))), (16.0 / 24.0, Func(pie.detachNode))) + fly = Track((14.0 / 24.0, SoundInterval(sound, node=self)), (16.0 / 24.0, Sequence(Func(flyPie.reparentTo, render), Func(flyPie.setScale, self.pieScale), Func(flyPie.setPosHpr, self, 0.52, 0.97, 2.24, 89.42, -10.56, 87.94), beginFlyIval, ProjectileInterval(flyPie, startVel=getVelocity, duration=3), Func(flyPie.detachNode)))) + return (toss, fly, flyPie) + + def getPieSplatInterval(self, x, y, z, pieCode): + from toontown.toonbase import ToontownBattleGlobals + from toontown.battle import BattleProps + pieName = ToontownBattleGlobals.pieNames[self.pieType] + splatName = 'splat-%s' % pieName + if pieName == 'lawbook': + splatName = 'dust' + splat = BattleProps.globalPropPool.getProp(splatName) + splat.setBillboardPointWorld(2) + color = ToontownGlobals.PieCodeColors.get(pieCode) + if color: + splat.setColor(*color) + vol = 1.0 + if pieName == 'lawbook': + sound = loader.loadSfx('phase_11/audio/sfx/LB_evidence_miss.mp3') + vol = 0.25 + else: + sound = loader.loadSfx('phase_4/audio/sfx/AA_wholepie_only.mp3') + ival = Parallel(Func(splat.reparentTo, render), Func(splat.setPos, x, y, z), SoundInterval(sound, node=splat, volume=vol), Sequence(ActorInterval(splat, splatName), Func(splat.detachNode))) + return ival + + def cleanupPieModel(self): + if self.pieModel != None: + self.pieModel.detachNode() + self.pieModel = None + return + + def getFeedPetIval(self): + return Sequence(ActorInterval(self, 'feedPet'), Func(self.animFSM.request, 'neutral')) + + def getScratchPetIval(self): + return Sequence(ActorInterval(self, 'pet-start'), ActorInterval(self, 'pet-loop'), ActorInterval(self, 'pet-end')) + + def getCallPetIval(self): + return ActorInterval(self, 'callPet') + + def enterGolfPuttLoop(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('loop-putt') + + def exitGolfPuttLoop(self): + self.stop() + + def enterGolfRotateLeft(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('rotateL-putt') + + def exitGolfRotateLeft(self): + self.stop() + + def enterGolfRotateRight(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('rotateR-putt') + + def exitGolfRotateRight(self): + self.stop() + + def enterGolfPuttSwing(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('swing-putt') + + def exitGolfPuttSwing(self): + self.stop() + + def enterGolfGoodPutt(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('good-putt', restart=0) + + def exitGolfGoodPutt(self): + self.stop() + + def enterGolfBadPutt(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('badloop-putt', restart=0) + + def exitGolfBadPutt(self): + self.stop() + + def enterFlattened(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + Emote.globalEmote.disableAll(self) + sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.mp3') + lerpTime = 0.1 + node = self.getGeomNode().getChild(0) + self.origScale = node.getScale() + self.track = Sequence(LerpScaleInterval(node, lerpTime, VBase3(2, 2, 0.025), blendType='easeInOut')) + self.track.start(ts) + self.setActiveShadow(1) + + def exitFlattened(self): + self.playingAnim = 'neutral' + if self.track != None: + self.track.finish() + DelayDelete.cleanupDelayDeletes(self.track) + self.track = None + node = self.getGeomNode().getChild(0) + node.setScale(self.origScale) + Emote.globalEmote.releaseAll(self) + return + + def enterCogThiefRunning(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.playingAnim = None + self.playingRate = None + self.standWalkRunReverse = (('neutral', 1.0), + ('run', 1.0), + ('run', 1.0), + ('run', -1.0)) + self.setSpeed(self.forwardSpeed, self.rotateSpeed) + self.setActiveShadow(1) + return + + def exitCogThiefRunning(self): + self.standWalkRunReverse = None + self.stop() + self.motion.exit() + return + + def enterScientistJealous(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistJealous') + if hasattr(self, 'showScientistProp'): + self.showScientistProp() + + def exitScientistJealous(self): + self.stop() + + def enterScientistEmcee(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistEmcee') + + def exitScientistEmcee(self): + self.stop() + + def enterScientistWork(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistWork') + + def exitScientistWork(self): + self.stop() + + def enterScientistLessWork(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistWork', fromFrame=319, toFrame=619) + + def exitScientistLessWork(self): + self.stop() + + def enterScientistPlay(self, animMultiplier = 1, ts = 0, callback = None, extraArgs = []): + self.loop('scientistGame') + if hasattr(self, 'scientistPlay'): + self.scientistPlay() + + def exitScientistPlay(self): + self.stop() + + +loadModels() +compileGlobalAnimList() diff --git a/toontown/toon/ToonAvatarDetailPanel.py b/toontown/toon/ToonAvatarDetailPanel.py new file mode 100644 index 0000000..908e196 --- /dev/null +++ b/toontown/toon/ToonAvatarDetailPanel.py @@ -0,0 +1,248 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +import DistributedToon +from toontown.friends import FriendInviter +import ToonTeleportPanel +from toontown.toonbase import TTLocalizer +from toontown.hood import ZoneUtil +from toontown.toonbase.ToontownBattleGlobals import Tracks, Levels +globalAvatarDetail = None + +def showAvatarDetail(avId, avName, playerId = None): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + playerId = base.cr.playerFriendsManager.findPlayerIdFromAvId(avId) + globalAvatarDetail = ToonAvatarDetailPanel(avId, avName, playerId) + return + + +def hideAvatarDetail(): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + return + + +def unloadAvatarDetail(): + global globalAvatarDetail + if globalAvatarDetail != None: + globalAvatarDetail.cleanup() + globalAvatarDetail = None + return + + +class ToonAvatarDetailPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonAvatarDetailPanel') + + def __init__(self, avId, avName, playerId = None, parent = aspect2dp, **kw): + print 'ToonAvatarDetailPanel %s' % playerId + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + detailPanel = gui.find('**/avatarInfoPanel') + self.playerId = playerId + textScale = 0.095 + textWrap = 16.4 + self.playerInfo = None + if self.playerId: + self.playerInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId) + optiondefs = (('pos', (0.525, 0.0, 0.525), None), + ('scale', 0.5, None), + ('relief', None, None), + ('image', detailPanel, None), + ('image_color', GlobalDialogColor, None), + ('text', '', None), + ('text_wordwrap', textWrap, None), + ('text_scale', textScale, None), + ('text_pos', (-0.125, 0.775), None)) + self.defineoptions(kw, optiondefs) + DirectFrame.__init__(self, parent) + self.dataText = DirectLabel(self, text='', text_scale=0.09, text_align=TextNode.ALeft, text_wordwrap=15, relief=None, pos=(-0.85, 0.0, 0.645)) + self.avId = avId + self.avName = avName + self.avatar = None + self.createdAvatar = None + self.fsm = ClassicFSM.ClassicFSM('ToonAvatarDetailPanel', [State.State('off', self.enterOff, self.exitOff, ['begin']), + State.State('begin', self.enterBegin, self.exitBegin, ['query', 'data', 'off']), + State.State('query', self.enterQuery, self.exitQuery, ['data', 'invalid', 'off']), + State.State('data', self.enterData, self.exitData, ['off']), + State.State('invalid', self.enterInvalid, self.exitInvalid, ['off'])], 'off', 'off') + ToonTeleportPanel.hideTeleportPanel() + FriendInviter.hideFriendInviter() + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), image_scale=1.1, relief=None, text=TTLocalizer.AvatarDetailPanelCancel, text_scale=TTLocalizer.TADPbCancel, text_pos=(0.12, -0.01), pos=TTLocalizer.TADPbCancelPos, scale=2.0, command=self.__handleCancel) + self.bCancel.hide() + self.initialiseoptions(ToonAvatarDetailPanel) + self.fsm.enterInitialState() + self.fsm.request('begin') + buttons.removeNode() + gui.removeNode() + return + + def cleanup(self): + if self.fsm: + self.fsm.request('off') + self.fsm = None + base.cr.cancelAvatarDetailsRequest(self.avatar) + if self.createdAvatar: + self.avatar.delete() + self.createdAvatar = None + self.destroy() + return + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterBegin(self): + myId = base.localAvatar.doId + self['text'] = self.avName + if self.avId == myId: + self.avatar = base.localAvatar + self.createdAvatar = 0 + self.fsm.request('data') + else: + self.fsm.request('query') + + def exitBegin(self): + pass + + def enterQuery(self): + self.dataText['text'] = TTLocalizer.AvatarDetailPanelLookup % self.avName + self.bCancel.show() + self.avatar = base.cr.doId2do.get(self.avId) + if self.avatar != None and not self.avatar.ghostMode: + self.createdAvatar = 0 + else: + self.avatar = DistributedToon.DistributedToon(base.cr) + self.createdAvatar = 1 + self.avatar.doId = self.avId + self.avatar.forceAllowDelayDelete() + base.cr.getAvatarDetails(self.avatar, self.__handleAvatarDetails, 'DistributedToon') + return + + def exitQuery(self): + self.bCancel.hide() + + def enterData(self): + self.bCancel['text'] = TTLocalizer.AvatarDetailPanelClose + self.bCancel.show() + self.__showData() + + def exitData(self): + self.bCancel.hide() + + def enterInvalid(self): + self.dataText['text'] = TTLocalizer.AvatarDetailPanelFailedLookup % self.avName + + def exitInvalid(self): + self.bCancel.hide() + + def __handleCancel(self): + unloadAvatarDetail() + + def __handleAvatarDetails(self, gotData, avatar, dclass): + if not self.fsm or avatar != self.avatar: + self.notify.warning('Ignoring unexpected request for avatar %s' % avatar.doId) + return + if gotData: + self.fsm.request('data') + else: + self.fsm.request('invalid') + + def __showData(self): + av = self.avatar + online = 1 + if base.cr.isFriend(self.avId): + online = base.cr.isFriendOnline(self.avId) + if online: + shardName = base.cr.getShardName(av.defaultShard) + hoodName = base.cr.hoodMgr.getFullnameFromId(av.lastHood) + if ZoneUtil.isWelcomeValley(av.lastHood): + shardName = '%s (%s)' % (TTLocalizer.WelcomeValley[-1], shardName) + if self.playerInfo: + guiButton = loader.loadModel('phase_3/models/gui/quit_button') + self.gotoAvatarButton = DirectButton(parent=self, relief=None, image=(guiButton.find('**/QuitBtn_UP'), guiButton.find('**/QuitBtn_DN'), guiButton.find('**/QuitBtn_RLVR')), image_scale=1.1, text=TTLocalizer.AvatarShowPlayer, text_scale=0.07, text_pos=(0.0, -0.02), textMayChange=0, pos=(0.44, 0, 0.41), command=self.__showAvatar) + text = TTLocalizer.AvatarDetailPanelOnlinePlayer % {'district': shardName, + 'location': hoodName, + 'player': self.playerInfo.playerName} + else: + text = TTLocalizer.AvatarDetailPanelOnline % {'district': shardName, + 'location': hoodName} + else: + text = TTLocalizer.AvatarDetailPanelOffline + self.dataText['text'] = text + self.__updateTrackInfo() + self.__updateTrophyInfo() + self.__updateLaffInfo() + return + + def __showAvatar(self): + messenger.send('wakeup') + hasManager = hasattr(base.cr, 'playerFriendsManager') + handle = base.cr.identifyFriend(self.avId) + if not handle and hasManager: + handle = base.cr.playerFriendsManager.getAvHandleFromId(self.avId) + if handle != None: + self.notify.info("Clicked on name in friend's list. doId = %s" % handle.doId) + messenger.send('clickedNametagPlayer', [handle, self.playerId, 1]) + return + + def __updateLaffInfo(self): + avatar = self.avatar + messenger.send('updateLaffMeter', [avatar, avatar.hp, avatar.maxHp]) + + def __updateTrackInfo(self): + xOffset = -0.501814 + xSpacing = 0.1835 + yOffset = 0.1 + ySpacing = -0.115 + inventory = self.avatar.inventory + inventoryModels = loader.loadModel('phase_3.5/models/gui/inventory_gui') + buttonModel = inventoryModels.find('**/InventoryButtonUp') + for track in range(0, len(Tracks)): + DirectLabel(parent=self, relief=None, text=TextEncoder.upper(TTLocalizer.BattleGlobalTracks[track]), text_scale=TTLocalizer.TADPtrackLabel, text_align=TextNode.ALeft, pos=(-0.9, 0, TTLocalizer.TADtrackLabelPosZ + track * ySpacing)) + if self.avatar.hasTrackAccess(track): + curExp, nextExp = inventory.getCurAndNextExpValues(track) + for item in range(0, len(Levels[track])): + level = Levels[track][item] + if curExp >= level: + numItems = inventory.numItem(track, item) + if numItems == 0: + image_color = Vec4(0.5, 0.5, 0.5, 1) + geom_color = Vec4(0.2, 0.2, 0.2, 0.5) + else: + image_color = Vec4(0, 0.6, 1, 1) + geom_color = None + DirectLabel(parent=self, image=buttonModel, image_scale=(0.92, 1, 1), image_color=image_color, geom=inventory.invModels[track][item], geom_color=geom_color, geom_scale=0.6, relief=None, pos=(xOffset + item * xSpacing, 0, yOffset + track * ySpacing)) + else: + break + + return + + def __updateTrophyInfo(self): + if self.createdAvatar: + return + if self.avatar.trophyScore >= TrophyStarLevels[2]: + color = TrophyStarColors[2] + elif self.avatar.trophyScore >= TrophyStarLevels[1]: + color = TrophyStarColors[1] + elif self.avatar.trophyScore >= TrophyStarLevels[0]: + color = TrophyStarColors[0] + else: + color = None + if color: + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + star = gui.find('**/avatarStar') + self.star = DirectLabel(parent=self, image=star, image_color=color, pos=(0.610165, 0, -0.760678), scale=0.9, relief=None) + gui.removeNode() + return diff --git a/toontown/toon/ToonAvatarPanel.py b/toontown/toon/ToonAvatarPanel.py new file mode 100644 index 0000000..6bfde77 --- /dev/null +++ b/toontown/toon/ToonAvatarPanel.py @@ -0,0 +1,583 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +import ToonHead +from toontown.friends import FriendHandle +import LaffMeter +from otp.avatar import Avatar +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.friends import ToontownFriendSecret +import ToonAvatarDetailPanel +import AvatarPanelBase +from toontown.toontowngui import TTDialog +from otp.otpbase import OTPGlobals + +class ToonAvatarPanel(AvatarPanelBase.AvatarPanelBase): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonAvatarPanel') + + def __init__(self, avatar, playerId = None): + from toontown.friends import FriendsListPanel + + if base.cr.doId2do.get(avatar.getDoId()): + avatar = base.cr.doId2do.get(avatar.getDoId()) + AvatarPanelBase.AvatarPanelBase.__init__(self, avatar, FriendsListPanel=FriendsListPanel) + + self.notify.debug('Opening toon panel, avId=%d' % self.avId) + self.playerId = playerId + + if not self.playerId: + av = base.cr.doId2do.get(self.avId) + if av: + self.playerId = avatar.DISLid + else: + self.playerId = 0 + + self.playerInfo = None + if self.playerId: + self.playerInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId) + + self.laffMeter = None + wantsLaffMeter = hasattr(avatar, 'hp') + + if not hasattr(avatar, 'style'): + self.notify.warning("Avatar has no 'style'. Abort initialization.") + AvatarPanelBase.AvatarPanelBase.cleanup(self) + return + + base.localAvatar.obscureFriendsListButton(1) + + gui = loader.loadModel('phase_3.5/models/gui/avatar_panel_gui') + self.frame = DirectFrame( + image=gui.find('**/avatar_panel'), + relief=None, + pos=(1.1, 100, 0.525)) + + self.disabledImageColor = Vec4(1, 1, 1, 0.4) + self.text0Color = Vec4(1, 1, 1, 1) + self.text1Color = Vec4(0.5, 1, 0.5, 1) + self.text2Color = Vec4(1, 1, 0.5, 1) + self.text3Color = Vec4(0.6, 0.6, 0.6, 1) + + self.head = self.frame.attachNewNode('head') + self.head.setPos(0.02, 0, 0.31) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(avatar.style, forGui=1) + self.headModel.fitAndCenterHead(0.175, forGui=1) + self.headModel.reparentTo(self.head) + + self.headModel.startBlink() + self.headModel.startLookAround() + + self.healthText = DirectLabel( + parent=self.frame, + text='', + pos=(0.06, 0, 0.2), + text_pos=(0, 0), + text_scale=0.05) + + self.healthText.hide() + + + self.nameLabel = DirectLabel( + parent=self.frame, + pos=(0.0125, 0, 0.4), + relief=None, + text=self.avName, + text_font=avatar.getFont(), + text_fg=Vec4(0, 0, 0, 1), + text_pos=(0, 0), + text_scale=0.042, + text_wordwrap=7.5, + text_shadow=(1, 1, 1, 1)) + + + self.closeButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/CloseBtn_UP'), + gui.find('**/CloseBtn_DN'), + gui.find('**/CloseBtn_Rllvr'), + gui.find('**/CloseBtn_UP')), + relief=None, + pos=(0.157644, 0, -0.379167), + command=self.__handleClose) + + + self.friendButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Frnds_Btn_UP'), + gui.find('**/Frnds_Btn_DN'), + gui.find('**/Frnds_Btn_RLVR'), + gui.find('**/Frnds_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + text=TTLocalizer.AvatarPanelFriends, + text_scale=0.06, + pos=(-0.103, 0, 0.133), + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_pos=(0.06, -0.02), + text_align=TextNode.ALeft, + command=self.__handleFriend) + + + + if base.cr.playerFriendsManager.askTransientFriend(self.avId) and not base.cr.doId2do.has_key(self.avId): + self.friendButton['state'] = DGG.DISABLED + + + if base.cr.avatarFriendsManager.checkIgnored(self.avId): + self.friendButton['state'] = DGG.DISABLED + + self.goToButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Go2_Btn_UP'), + gui.find('**/Go2_Btn_DN'), + gui.find('**/Go2_Btn_RLVR'), + gui.find('**/Go2_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, 0.045), + text=TTLocalizer.AvatarPanelGoTo, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=0.06, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + command=self.__handleGoto) + + + + if base.cr.avatarFriendsManager.checkIgnored(self.avId): + self.goToButton['state'] = DGG.DISABLED + + self.whisperButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/ChtBx_ChtBtn_UP'), + gui.find('**/ChtBx_ChtBtn_DN'), + gui.find('**/ChtBx_ChtBtn_RLVR'), + gui.find('**/ChtBx_ChtBtn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, -0.0375), + text=TTLocalizer.AvatarPanelWhisper, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=TTLocalizer.TAPwhisperButton, + text_pos=(0.06, -0.0125), + text_align=TextNode.ALeft, + command=self.__handleWhisper) + + + + if base.cr.avatarFriendsManager.checkIgnored(self.avId): + self.whisperButton['state'] = DGG.DISABLED + + self.secretsButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Amuse_Btn_UP'), + gui.find('**/Amuse_Btn_DN'), + gui.find('**/Amuse_Btn_RLVR'), + gui.find('**/Amuse_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103, 0, -0.13), + text=TTLocalizer.AvatarPanelSecrets, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=TTLocalizer.TAPsecretsButton, + text_pos=(0.055, -0.01), + text_align=TextNode.ALeft, + command=self.__handleSecrets) + + + + if base.cr.avatarFriendsManager.checkIgnored(self.avId): + self.secretsButton['state'] = DGG.DISABLED + + + from toontown.coghq import CogHQBossBattle + if isinstance(base.cr.playGame.getPlace(), CogHQBossBattle.CogHQBossBattle) and \ + base.localAvatar.getGameAccess() != OTPGlobals.AccessFull: + self.secretsButton['state'] = DGG.DISABLED + + ignoreStr, ignoreCmd, ignoreScale = self.getIgnoreButtonInfo() + + self.ignoreButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/Ignore_Btn_UP'), + gui.find('**/Ignore_Btn_DN'), + gui.find('**/Ignore_Btn_RLVR'), + gui.find('**/Ignore_Btn_UP')), + image3_color=self.disabledImageColor, + image_scale=0.9, + relief=None, + pos=(-0.103697, 0, -0.21), + text=ignoreStr, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=ignoreScale, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + command=ignoreCmd) + + + if base.cr.productName not in ['JP', 'DE', 'BR', 'FR']: + self.reportButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/report_BtnUP'), + gui.find('**/report_BtnDN'), + gui.find('**/report_BtnRLVR'), + gui.find('**/report_BtnUP')), + image3_color=self.disabledImageColor, + image_scale=0.65, + relief=None, + pos=(-0.103, 0, -0.29738), + text=TTLocalizer.AvatarPanelReport, + text0_fg=self.text0Color, + text1_fg=self.text1Color, + text2_fg=self.text2Color, + text3_fg=self.text3Color, + text_scale=0.06, + text_pos=(0.06, -0.015), + text_align=TextNode.ALeft, + command=self.handleReport) + + + if not base.localAvatar.isTeleportAllowed(): + self.goToButton['state'] = DGG.DISABLED + + self.detailButton = DirectButton( + parent=self.frame, + image=( + gui.find('**/ChtBx_BackBtn_UP'), + gui.find('**/ChtBx_BackBtn_DN'), + gui.find('**/ChtBx_BackBtn_Rllvr'), + gui.find('**/ChtBx_BackBtn_UP')), + relief=None, + text=('', TTLocalizer.AvatarPanelDetail, + TTLocalizer.AvatarPanelDetail, ''), + text_fg=self.text2Color, + text_shadow=(0, 0, 0, 1), + text_scale=0.055, + text_pos=(-0.075, -0.01), + text_align=TextNode.ARight, + pos=(-0.133773, 0, -0.395), + command=self.__handleDetails) + + + self.__makeBoardingGui() + self.__makePetGui(avatar) + + self.__checkGroupStatus() + + gui.removeNode() + + + if wantsLaffMeter: + self.__makeLaffMeter(avatar) + self.__updateHp(avatar.hp, avatar.maxHp) + self.healthText.show() + self.laffMeter.show() + + menuX = -0.05 + menuScale = 0.064 + + if self.avGenerateName: + self.accept(self.avGenerateName, self.__handleGenerateAvatar) + if self.avHpChangeName: + self.accept(self.avHpChangeName, self.__updateHp) + self.accept('updateLaffMeter', self.__updateLaffMeter) + + self.accept('updateGroupStatus', self.__checkGroupStatus) + + self.frame.show() + messenger.send('avPanelDone') + + def disableAll(self): + self.detailButton['state'] = DGG.DISABLED + if base.cr.productName not in ['ES', + 'JP', + 'DE', + 'BR', + 'FR']: + self.reportButton['state'] = DGG.DISABLED + self.ignoreButton['state'] = DGG.DISABLED + self.goToButton['state'] = DGG.DISABLED + self.secretsButton['state'] = DGG.DISABLED + self.whisperButton['state'] = DGG.DISABLED + self.petButton['state'] = DGG.DISABLED + self.friendButton['state'] = DGG.DISABLED + self.closeButton['state'] = DGG.DISABLED + self.groupButton['state'] = DGG.DISABLED + self.boardingInfoButton['state'] = DGG.DISABLED + + def cleanup(self): + if not hasattr(self, 'frame') or self.frame == None: + return + self.notify.debug('Clean up toon panel, avId=%d' % self.avId) + if self.frame: + self.frame.destroy() + del self.frame + self.frame = None + ToonAvatarDetailPanel.unloadAvatarDetail() + if self.groupButton: + self.groupButton.destroy() + del self.groupButton + self.groupButton = None + if self.boardingInfoButton: + self.boardingInfoButton.destroy() + del self.boardingInfoButton + self.boardingInfoButton = None + if self.boardingInfoText: + self.boardingInfoText.destroy() + del self.boardingInfoText + self.boardingInfoText = None + if self.groupFrame: + self.groupFrame.destroy() + del self.groupFrame + self.groupFrame = None + self.head.removeNode() + del self.head + self.headModel.stopBlink() + self.headModel.stopLookAroundNow() + self.headModel.delete() + del self.headModel + base.localAvatar.obscureFriendsListButton(-1) + self.laffMeter = None + self.ignore('updateLaffMeter') + self.ignoreAll() + if hasattr(self.avatar, 'bFake') and self.avatar.bFake: + self.avatar.delete() + base.setCellsAvailable([base.rightCells[0]], 1) + AvatarPanelBase.AvatarPanelBase.cleanup(self) + return + + def __handleGoto(self): + if base.localAvatar.isTeleportAllowed(): + base.localAvatar.chatMgr.noWhisper() + messenger.send('gotoAvatar', [self.avId, self.avName, self.avDisableName]) + + def __handleToPet(self): + toonAvatar = self.avatar + if base.cr.doId2do.get(toonAvatar.getDoId()): + toonAvatar = base.cr.doId2do.get(toonAvatar.getDoId()) + petAvatar = base.cr.doId2do.get(toonAvatar.getPetId()) + self.disableAll() + from toontown.pets import PetDetail + PetDetail.PetDetail(toonAvatar.getPetId(), self.__petDetailsLoaded) + + def __petDetailsLoaded(self, avatar): + self.cleanup() + if avatar: + self.notify.debug("Looking at someone's pet. pet doId = %s" % avatar.doId) + messenger.send('clickedNametag', [avatar]) + + def __handleWhisper(self): + base.localAvatar.chatMgr.whisperTo(self.avName, self.avId, None) + return + + def __handleSecrets(self): + base.localAvatar.chatMgr.noWhisper() + ToontownFriendSecret.showFriendSecret(ToontownFriendSecret.AvatarSecret) + + def __handleFriend(self): + base.localAvatar.chatMgr.noWhisper() + messenger.send('friendAvatar', [self.avId, self.avName, self.avDisableName]) + + def __handleDetails(self): + base.localAvatar.chatMgr.noWhisper() + messenger.send('avatarDetails', [self.avId, self.avName, self.playerId]) + + def __handleDisableAvatar(self): + if not base.cr.isFriend(self.avId): + self.cleanup() + AvatarPanelBase.currentAvatarPanel = None + else: + self.healthText.hide() + if self.laffMeter != None: + self.laffMeter.stop() + self.laffMeter.destroy() + self.laffMeter = None + return + + def __handleGenerateAvatar(self, avatar): + newAvatar = base.cr.doId2do.get(self.avatar.doId) + if newAvatar: + self.avatar = newAvatar + self.__updateLaffMeter(avatar, avatar.hp, avatar.maxHp) + self.__checkGroupStatus() + + def __updateLaffMeter(self, avatar, hp, maxHp): + if self.laffMeter == None: + self.__makeLaffMeter(avatar) + self.__updateHp(avatar.hp, avatar.maxHp) + self.laffMeter.show() + self.healthText.show() + return + + def __makeLaffMeter(self, avatar): + self.laffMeter = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.laffMeter.reparentTo(self.frame) + self.laffMeter.setPos(-0.1, 0, 0.24) + self.laffMeter.setScale(0.03) + + def __updateHp(self, hp, maxHp, quietly = 0): + if self.laffMeter != None and hp != None and maxHp != None: + self.laffMeter.adjustFace(hp, maxHp) + self.healthText['text'] = '%d / %d' % (hp, maxHp) + return + + def __handleClose(self): + self.cleanup() + AvatarPanelBase.currentAvatarPanel = None + if self.friendsListShown: + self.FriendsListPanel.showFriendsList() + return + + def getAvId(self): + if hasattr(self, 'avatar'): + if self.avatar: + return self.avatar.doId + return None + + def getPlayerId(self): + if hasattr(self, 'playerId'): + return self.playerId + return None + + def isHidden(self): + if not hasattr(self, 'frame') or not self.frame: + return 1 + return self.frame.isHidden() + + def getType(self): + return 'toon' + + def handleInvite(self): + if localAvatar.boardingParty.isInviteePanelUp(): + localAvatar.boardingParty.showMe(TTLocalizer.BoardingPendingInvite, pos=(0, 0, 0)) + else: + self.groupButton['state'] = DGG.DISABLED + localAvatar.boardingParty.requestInvite(self.avId) + + def handleKick(self): + if not base.cr.playGame.getPlace().getState() == 'elevator': + self.confirmKickOutDialog = TTDialog.TTDialog(style=TTDialog.YesNo, text=TTLocalizer.BoardingKickOutConfirm % self.avName, command=self.__confirmKickOutCallback) + self.confirmKickOutDialog.show() + + def __confirmKickOutCallback(self, value): + if self.confirmKickOutDialog: + self.confirmKickOutDialog.destroy() + self.confirmKickOutDialog = None + if value > 0: + if self.groupButton: + self.groupButton['state'] = DGG.DISABLED + localAvatar.boardingParty.requestKick(self.avId) + return + + def __checkGroupStatus(self): + self.groupFrame.hide() + if hasattr(self, 'avatar'): + if self.avatar and hasattr(self.avatar, 'getZoneId') and localAvatar.getZoneId() == self.avatar.getZoneId(): + if localAvatar.boardingParty: + if self.avId in localAvatar.boardingParty.getGroupMemberList(localAvatar.doId): + if localAvatar.boardingParty.getGroupLeader(localAvatar.doId) == localAvatar.doId: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupMemberKick, TTLocalizer.AvatarPanelGroupMemberKick) + self.groupButton['image'] = self.kickOutImageList + self.groupButton['command'] = self.handleKick + self.groupButton['state'] = DGG.NORMAL + else: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupMember, TTLocalizer.AvatarPanelGroupMember) + self.groupButton['command'] = None + self.groupButton['image'] = self.inviteImageDisabled + self.groupButton['image_color'] = Vec4(1, 1, 1, 0.4) + self.groupButton['state'] = DGG.NORMAL + else: + self.groupButton['text'] = ('', TTLocalizer.AvatarPanelGroupInvite, TTLocalizer.AvatarPanelGroupInvite) + self.groupButton['command'] = self.handleInvite + self.groupButton['image'] = self.inviteImageList + self.groupButton['state'] = DGG.NORMAL + if base.config.GetBool('want-boarding-groups', 1): + base.setCellsAvailable([base.rightCells[0]], 0) + self.groupFrame.show() + return + + def handleReadInfo(self, task = None): + self.boardingInfoButton['state'] = DGG.DISABLED + if self.boardingInfoText: + self.boardingInfoText.destroy() + self.boardingInfoText = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.BoardingPartyInform % localAvatar.boardingParty.maxSize, command=self.handleCloseInfo) + + def handleCloseInfo(self, *extraArgs): + self.boardingInfoButton['state'] = DGG.NORMAL + if self.boardingInfoText: + self.boardingInfoText.destroy() + del self.boardingInfoText + self.boardingInfoText = None + return + + def __makePetGui(self, avatar): + petGui = loader.loadModel('phase_3.5/models/gui/PetControlPannel') + self.petButton = DirectButton(parent=self.frame, image=(petGui.find('**/PetControlToonButtonUp1'), petGui.find('**/PetControlToonButtonDown1'), petGui.find('**/PetControlToonButtonRollover1')), geom=petGui.find('**/PetBattleIcon'), geom3_color=self.disabledImageColor, relief=None, pos=(0.22, -0.2, -0.475), text=('', + TTLocalizer.AvatarPanelPet, + TTLocalizer.AvatarPanelPet, + ''), text_fg=self.text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.325, text_pos=(-1.3, 0.05), text_align=TextNode.ACenter, command=self.__handleToPet) + self.petButton.setScale(0.15) + if not (base.wantPets and avatar.hasPet()): + self.petButton['state'] = DGG.DISABLED + self.petButton.hide() + petGui.removeNode() + return + + def __makeBoardingGui(self): + self.confirmKickOutDialog = None + groupAvatarBgGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_avatarPanelBg') + boardingGroupBGImage = groupAvatarBgGui.find('**/tt_t_gui_brd_avatar_panel_party') + self.groupFrame = DirectFrame(parent=self.frame, relief=None, image=boardingGroupBGImage, image_scale=(0.5, 1, 0.5), textMayChange=1, text=TTLocalizer.BoardingPartyTitle, text_wordwrap=16, text_scale=TTLocalizer.TAPgroupFrame, text_pos=(0.01, 0.08), pos=(0, 0, -0.61)) + groupInviteGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_inviteButton') + self.inviteImageList = (groupInviteGui.find('**/tt_t_gui_brd_inviteUp'), + groupInviteGui.find('**/tt_t_gui_brd_inviteDown'), + groupInviteGui.find('**/tt_t_gui_brd_inviteHover'), + groupInviteGui.find('**/tt_t_gui_brd_inviteUp')) + self.kickOutImageList = (groupInviteGui.find('**/tt_t_gui_brd_kickoutUp'), + groupInviteGui.find('**/tt_t_gui_brd_kickoutDown'), + groupInviteGui.find('**/tt_t_gui_brd_kickoutHover'), + groupInviteGui.find('**/tt_t_gui_brd_kickoutUp')) + self.inviteImageDisabled = groupInviteGui.find('**/tt_t_gui_brd_inviteDisabled') + self.groupButton = DirectButton(parent=self.groupFrame, image=self.inviteImageList, image3_color=self.disabledImageColor, image_scale=0.85, relief=None, text=('', TTLocalizer.AvatarPanelGroupInvite, TTLocalizer.AvatarPanelGroupInvite), text0_fg=self.text0Color, text1_fg=self.text1Color, text2_fg=self.text2Color, text3_fg=self.text3Color, text_scale=TTLocalizer.TAPgroupButton, text_pos=(-0.0, -0.1), text_align=TextNode.ACenter, command=self.handleInvite, pos=(0.01013, 0, -0.05464)) + helpGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_brd_help') + helpImageList = (helpGui.find('**/tt_t_gui_brd_helpUp'), + helpGui.find('**/tt_t_gui_brd_helpDown'), + helpGui.find('**/tt_t_gui_brd_helpHover'), + helpGui.find('**/tt_t_gui_brd_helpDown')) + self.boardingInfoButton = DirectButton(parent=self.groupFrame, relief=None, text_pos=(-0.05, 0.05), text_scale=0.06, text_align=TextNode.ALeft, text_fg=Vec4(1, 1, 1, 1), text_shadow=Vec4(0, 0, 0, 1), image=helpImageList, image_scale=(0.5, 1, 0.5), image3_color=self.disabledImageColor, scale=1.05, command=self.handleReadInfo, pos=(0.1829, 0, 0.02405)) + self.boardingInfoText = None + groupInviteGui.removeNode() + groupAvatarBgGui.removeNode() + helpGui.removeNode() + return diff --git a/toontown/toon/ToonDNA.py b/toontown/toon/ToonDNA.py new file mode 100644 index 0000000..2d3c22a --- /dev/null +++ b/toontown/toon/ToonDNA.py @@ -0,0 +1,2838 @@ +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('ToonDNA') +toonSpeciesTypes = ['d', + 'c', + 'h', + 'm', + 'r', + 'f', + 'p', + 'b', + 's'] +toonHeadTypes = ['dls', + 'dss', + 'dsl', + 'dll', + 'cls', + 'css', + 'csl', + 'cll', + 'hls', + 'hss', + 'hsl', + 'hll', + 'mls', + 'mss', + 'rls', + 'rss', + 'rsl', + 'rll', + 'fls', + 'fss', + 'fsl', + 'fll', + 'pls', + 'pss', + 'psl', + 'pll', + 'bls', + 'bss', + 'bsl', + 'bll', + 'sls', + 'sss', + 'ssl', + 'sll'] + +def getHeadList(species): + headList = [] + for head in toonHeadTypes: + if head[0] == species: + headList.append(head) + + return headList + + +def getHeadStartIndex(species): + for head in toonHeadTypes: + if head[0] == species: + return toonHeadTypes.index(head) + + +def getSpecies(head): + for species in toonSpeciesTypes: + if species == head[0]: + return species + + +def getSpeciesName(head): + species = getSpecies(head) + if species == 'd': + speciesName = 'dog' + elif species == 'c': + speciesName = 'cat' + elif species == 'h': + speciesName = 'horse' + elif species == 'm': + speciesName = 'mouse' + elif species == 'r': + speciesName = 'rabbit' + elif species == 'f': + speciesName = 'duck' + elif species == 'p': + speciesName = 'monkey' + elif species == 'b': + speciesName = 'bear' + elif species == 's': + speciesName = 'pig' + return speciesName + + +toonHeadAnimalIndices = [0, + 4, + 8, + 12, + 14, + 18, + 22, + 26, + 30] +toonHeadAnimalIndicesTrial = [0, + 4, + 12, + 14, + 18, + 30] +allToonHeadAnimalIndices = [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33] +allToonHeadAnimalIndicesTrial = [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 30, + 31, + 32, + 33] +toonTorsoTypes = ['ss', + 'ms', + 'ls', + 'sd', + 'md', + 'ld', + 's', + 'm', + 'l'] +toonLegTypes = ['s', 'm', 'l'] +Shirts = ['phase_3/maps/desat_shirt_1.jpg', + 'phase_3/maps/desat_shirt_2.jpg', + 'phase_3/maps/desat_shirt_3.jpg', + 'phase_3/maps/desat_shirt_4.jpg', + 'phase_3/maps/desat_shirt_5.jpg', + 'phase_3/maps/desat_shirt_6.jpg', + 'phase_3/maps/desat_shirt_7.jpg', + 'phase_3/maps/desat_shirt_8.jpg', + 'phase_3/maps/desat_shirt_9.jpg', + 'phase_3/maps/desat_shirt_10.jpg', + 'phase_3/maps/desat_shirt_11.jpg', + 'phase_3/maps/desat_shirt_12.jpg', + 'phase_3/maps/desat_shirt_13.jpg', + 'phase_3/maps/desat_shirt_14.jpg', + 'phase_3/maps/desat_shirt_15.jpg', + 'phase_3/maps/desat_shirt_16.jpg', + 'phase_3/maps/desat_shirt_17.jpg', + 'phase_3/maps/desat_shirt_18.jpg', + 'phase_3/maps/desat_shirt_19.jpg', + 'phase_3/maps/desat_shirt_20.jpg', + 'phase_3/maps/desat_shirt_21.jpg', + 'phase_3/maps/desat_shirt_22.jpg', + 'phase_3/maps/desat_shirt_23.jpg', + 'phase_4/maps/female_shirt1b.jpg', + 'phase_4/maps/female_shirt2.jpg', + 'phase_4/maps/female_shirt3.jpg', + 'phase_4/maps/male_shirt1.jpg', + 'phase_4/maps/male_shirt2_palm.jpg', + 'phase_4/maps/male_shirt3c.jpg', + 'phase_4/maps/shirt_ghost.jpg', + 'phase_4/maps/shirt_pumkin.jpg', + 'phase_4/maps/holiday_shirt1.jpg', + 'phase_4/maps/holiday_shirt2b.jpg', + 'phase_4/maps/holidayShirt3b.jpg', + 'phase_4/maps/holidayShirt4.jpg', + 'phase_4/maps/female_shirt1b.jpg', + 'phase_4/maps/female_shirt5New.jpg', + 'phase_4/maps/shirtMale4B.jpg', + 'phase_4/maps/shirt6New.jpg', + 'phase_4/maps/shirtMaleNew7.jpg', + 'phase_4/maps/femaleShirtNew6.jpg', + 'phase_4/maps/Vday1Shirt5.jpg', + 'phase_4/maps/Vday1Shirt6SHD.jpg', + 'phase_4/maps/Vday1Shirt4.jpg', + 'phase_4/maps/Vday_shirt2c.jpg', + 'phase_4/maps/shirtTieDyeNew.jpg', + 'phase_4/maps/male_shirt1.jpg', + 'phase_4/maps/StPats_shirt1.jpg', + 'phase_4/maps/StPats_shirt2.jpg', + 'phase_4/maps/ContestfishingVestShirt2.jpg', + 'phase_4/maps/ContestFishtankShirt1.jpg', + 'phase_4/maps/ContestPawShirt1.jpg', + 'phase_4/maps/CowboyShirt1.jpg', + 'phase_4/maps/CowboyShirt2.jpg', + 'phase_4/maps/CowboyShirt3.jpg', + 'phase_4/maps/CowboyShirt4.jpg', + 'phase_4/maps/CowboyShirt5.jpg', + 'phase_4/maps/CowboyShirt6.jpg', + 'phase_4/maps/4thJulyShirt1.jpg', + 'phase_4/maps/4thJulyShirt2.jpg', + 'phase_4/maps/shirt_Cat7_01.jpg', + 'phase_4/maps/shirt_Cat7_02.jpg', + 'phase_4/maps/contest_backpack3.jpg', + 'phase_4/maps/contest_leder.jpg', + 'phase_4/maps/contest_mellon2.jpg', + 'phase_4/maps/contest_race2.jpg', + 'phase_4/maps/PJBlueBanana2.jpg', + 'phase_4/maps/PJRedHorn2.jpg', + 'phase_4/maps/PJGlasses2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_valentine1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_valentine2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_desat4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_gardening1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_gardening2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_party1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_party2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_summer1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_summer2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_marathon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_toonTask1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_toonTask2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_winter1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_valentine3.jpg', + 'phase_4/maps/tt_t_chr_shirt_scientistC.jpg', + 'phase_4/maps/tt_t_chr_shirt_scientistA.jpg', + 'phase_4/maps/tt_t_chr_shirt_scientistB.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mailbox.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trashcan.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_loonyLabs.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_hydrant.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_whistle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_cogbuster.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_victoryParty01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_victoryParty02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_sellbotIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_sellbotVPIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_sellbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_jellyBeans.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_doodle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween5.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloweenTurtle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_greentoon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_getConnectedMoverShaker.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racingGrandPrix.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_lawbotIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_lawbotVPIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_lawbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_bee.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_supertoon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_vampire.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_dinosaur.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_winter03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_halloween07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_winter02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_fishing07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_golf05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_racing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_mostCogsDefeated04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_trolley05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_saveBuilding05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirt_anniversary.jpg'] +BoyShirts = [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (8, 8), + (9, 9), + (10, 0), + (11, 0), + (14, 10), + (16, 0), + (17, 0), + (18, 12), + (19, 13)] +GirlShirts = [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (5, 5), + (6, 6), + (7, 7), + (9, 9), + (12, 0), + (13, 11), + (15, 11), + (16, 0), + (20, 0), + (21, 0), + (22, 0)] + +def isValidBoyShirt(index): + for pair in BoyShirts: + if index == pair[0]: + return 1 + + return 0 + + +def isValidGirlShirt(index): + for pair in GirlShirts: + if index == pair[0]: + return 1 + + return 0 + + +Sleeves = ['phase_3/maps/desat_sleeve_1.jpg', + 'phase_3/maps/desat_sleeve_2.jpg', + 'phase_3/maps/desat_sleeve_3.jpg', + 'phase_3/maps/desat_sleeve_4.jpg', + 'phase_3/maps/desat_sleeve_5.jpg', + 'phase_3/maps/desat_sleeve_6.jpg', + 'phase_3/maps/desat_sleeve_7.jpg', + 'phase_3/maps/desat_sleeve_8.jpg', + 'phase_3/maps/desat_sleeve_9.jpg', + 'phase_3/maps/desat_sleeve_10.jpg', + 'phase_3/maps/desat_sleeve_15.jpg', + 'phase_3/maps/desat_sleeve_16.jpg', + 'phase_3/maps/desat_sleeve_19.jpg', + 'phase_3/maps/desat_sleeve_20.jpg', + 'phase_4/maps/female_sleeve1b.jpg', + 'phase_4/maps/female_sleeve2.jpg', + 'phase_4/maps/female_sleeve3.jpg', + 'phase_4/maps/male_sleeve1.jpg', + 'phase_4/maps/male_sleeve2_palm.jpg', + 'phase_4/maps/male_sleeve3c.jpg', + 'phase_4/maps/shirt_Sleeve_ghost.jpg', + 'phase_4/maps/shirt_Sleeve_pumkin.jpg', + 'phase_4/maps/holidaySleeve1.jpg', + 'phase_4/maps/holidaySleeve3.jpg', + 'phase_4/maps/female_sleeve1b.jpg', + 'phase_4/maps/female_sleeve5New.jpg', + 'phase_4/maps/male_sleeve4New.jpg', + 'phase_4/maps/sleeve6New.jpg', + 'phase_4/maps/SleeveMaleNew7.jpg', + 'phase_4/maps/female_sleeveNew6.jpg', + 'phase_4/maps/Vday5Sleeve.jpg', + 'phase_4/maps/Vda6Sleeve.jpg', + 'phase_4/maps/Vday_shirt4sleeve.jpg', + 'phase_4/maps/Vday2cSleeve.jpg', + 'phase_4/maps/sleeveTieDye.jpg', + 'phase_4/maps/male_sleeve1.jpg', + 'phase_4/maps/StPats_sleeve.jpg', + 'phase_4/maps/StPats_sleeve2.jpg', + 'phase_4/maps/ContestfishingVestSleeve1.jpg', + 'phase_4/maps/ContestFishtankSleeve1.jpg', + 'phase_4/maps/ContestPawSleeve1.jpg', + 'phase_4/maps/CowboySleeve1.jpg', + 'phase_4/maps/CowboySleeve2.jpg', + 'phase_4/maps/CowboySleeve3.jpg', + 'phase_4/maps/CowboySleeve4.jpg', + 'phase_4/maps/CowboySleeve5.jpg', + 'phase_4/maps/CowboySleeve6.jpg', + 'phase_4/maps/4thJulySleeve1.jpg', + 'phase_4/maps/4thJulySleeve2.jpg', + 'phase_4/maps/shirt_sleeveCat7_01.jpg', + 'phase_4/maps/shirt_sleeveCat7_02.jpg', + 'phase_4/maps/contest_backpack_sleeve.jpg', + 'phase_4/maps/Contest_leder_sleeve.jpg', + 'phase_4/maps/contest_mellon_sleeve2.jpg', + 'phase_4/maps/contest_race_sleeve.jpg', + 'phase_4/maps/PJSleeveBlue.jpg', + 'phase_4/maps/PJSleeveRed.jpg', + 'phase_4/maps/PJSleevePurple.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_valentine1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_valentine2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_desat4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_gardening1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_gardening2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_party1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_party2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_summer1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_summer2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_marathon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_toonTask1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_toonTask2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley2.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_winter1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_valentine3.jpg', + 'phase_4/maps/tt_t_chr_shirtSleeve_scientist.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mailbox.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trashcan.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_loonyLabs.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_hydrant.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_whistle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_cogbuster.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_victoryParty01.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_victoryParty02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_sellbotIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_sellbotVPIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_sellbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_jellyBeans.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_doodle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween5.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloweenTurtle.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_greentoon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_getConnectedMoverShaker.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racingGrandPrix.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_lawbotIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_lawbotVPIcon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_lawbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_bee.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_supertoon.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_vampire.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_dinosaur.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding3.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_winter03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_halloween07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_winter02.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing06.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_fishing07.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_golf05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_racing05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated03.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_mostCogsDefeated04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley04.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_trolley05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding4.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_saveBuilding05.jpg', + 'phase_4/maps/tt_t_chr_avt_shirtSleeve_anniversary.jpg'] +BoyShorts = ['phase_3/maps/desat_shorts_1.jpg', + 'phase_3/maps/desat_shorts_2.jpg', + 'phase_3/maps/desat_shorts_4.jpg', + 'phase_3/maps/desat_shorts_6.jpg', + 'phase_3/maps/desat_shorts_7.jpg', + 'phase_3/maps/desat_shorts_8.jpg', + 'phase_3/maps/desat_shorts_9.jpg', + 'phase_3/maps/desat_shorts_10.jpg', + 'phase_4/maps/VdayShorts2.jpg', + 'phase_4/maps/shorts4.jpg', + 'phase_4/maps/shorts1.jpg', + 'phase_4/maps/shorts5.jpg', + 'phase_4/maps/CowboyShorts1.jpg', + 'phase_4/maps/CowboyShorts2.jpg', + 'phase_4/maps/4thJulyShorts1.jpg', + 'phase_4/maps/shortsCat7_01.jpg', + 'phase_4/maps/Blue_shorts_1.jpg', + 'phase_4/maps/Red_shorts_1.jpg', + 'phase_4/maps/Purple_shorts_1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter2.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter3.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_winter4.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_valentine1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_valentine2.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_fishing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_gardening1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_party1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_summer1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween2.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_saveBuilding1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_trolley1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween4.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween3.jpg', + 'phase_4/maps/tt_t_chr_shorts_scientistA.jpg', + 'phase_4/maps/tt_t_chr_shorts_scientistB.jpg', + 'phase_4/maps/tt_t_chr_shorts_scientistC.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_cogbuster.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_sellbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloween5.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_halloweenTurtle.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_greentoon1.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racingGrandPrix.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_lawbotCrusher.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_bee.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_supertoon.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_vampire.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_dinosaur.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf03.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing03.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf04.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_golf05.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing04.jpg', + 'phase_4/maps/tt_t_chr_avt_shorts_racing05.jpg'] +SHORTS = 0 +SKIRT = 1 +GirlBottoms = [('phase_3/maps/desat_skirt_1.jpg', SKIRT), + ('phase_3/maps/desat_skirt_2.jpg', SKIRT), + ('phase_3/maps/desat_skirt_3.jpg', SKIRT), + ('phase_3/maps/desat_skirt_4.jpg', SKIRT), + ('phase_3/maps/desat_skirt_5.jpg', SKIRT), + ('phase_3/maps/desat_shorts_1.jpg', SHORTS), + ('phase_3/maps/desat_shorts_5.jpg', SHORTS), + ('phase_3/maps/desat_skirt_6.jpg', SKIRT), + ('phase_3/maps/desat_skirt_7.jpg', SKIRT), + ('phase_3/maps/desat_shorts_10.jpg', SHORTS), + ('phase_4/maps/female_skirt1.jpg', SKIRT), + ('phase_4/maps/female_skirt2.jpg', SKIRT), + ('phase_4/maps/female_skirt3.jpg', SKIRT), + ('phase_4/maps/VdaySkirt1.jpg', SKIRT), + ('phase_4/maps/skirtNew5.jpg', SKIRT), + ('phase_4/maps/shorts5.jpg', SHORTS), + ('phase_4/maps/CowboySkirt1.jpg', SKIRT), + ('phase_4/maps/CowboySkirt2.jpg', SKIRT), + ('phase_4/maps/4thJulySkirt1.jpg', SKIRT), + ('phase_4/maps/skirtCat7_01.jpg', SKIRT), + ('phase_4/maps/Blue_shorts_1.jpg', SHORTS), + ('phase_4/maps/Red_shorts_1.jpg', SHORTS), + ('phase_4/maps/Purple_shorts_1.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_winter1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_winter2.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_winter3.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_winter4.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_valentine1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_valentine2.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_fishing1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_gardening1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_party1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_summer1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_golf1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween2.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_saveBuilding1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_trolley1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween3.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_halloween4.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_shorts_scientistA.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_shorts_scientistB.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_shorts_scientistC.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_cogbuster.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_sellbotCrusher.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_halloween5.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_halloweenTurtle.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_greentoon1.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racingGrandPrix.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_shorts_lawbotCrusher.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_bee.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_pirate.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_pirate.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_shorts_supertoon.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_vampire.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_shorts_dinosaur.jpg', SHORTS), + ('phase_4/maps/tt_t_chr_avt_skirt_golf02.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing03.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_golf03.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_golf04.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing04.jpg', SKIRT), + ('phase_4/maps/tt_t_chr_avt_skirt_racing05.jpg', SKIRT)] +ClothesColors = [VBase4(0.933594, 0.265625, 0.28125, 1.0), + VBase4(0.863281, 0.40625, 0.417969, 1.0), + VBase4(0.710938, 0.234375, 0.4375, 1.0), + VBase4(0.992188, 0.480469, 0.167969, 1.0), + VBase4(0.996094, 0.898438, 0.320312, 1.0), + VBase4(0.550781, 0.824219, 0.324219, 1.0), + VBase4(0.242188, 0.742188, 0.515625, 1.0), + VBase4(0.433594, 0.90625, 0.835938, 1.0), + VBase4(0.347656, 0.820312, 0.953125, 1.0), + VBase4(0.191406, 0.5625, 0.773438, 1.0), + VBase4(0.285156, 0.328125, 0.726562, 1.0), + VBase4(0.460938, 0.378906, 0.824219, 1.0), + VBase4(0.546875, 0.28125, 0.75, 1.0), + VBase4(0.570312, 0.449219, 0.164062, 1.0), + VBase4(0.640625, 0.355469, 0.269531, 1.0), + VBase4(0.996094, 0.695312, 0.511719, 1.0), + VBase4(0.832031, 0.5, 0.296875, 1.0), + VBase4(0.992188, 0.480469, 0.167969, 1.0), + VBase4(0.550781, 0.824219, 0.324219, 1.0), + VBase4(0.433594, 0.90625, 0.835938, 1.0), + VBase4(0.347656, 0.820312, 0.953125, 1.0), + VBase4(0.96875, 0.691406, 0.699219, 1.0), + VBase4(0.996094, 0.957031, 0.597656, 1.0), + VBase4(0.855469, 0.933594, 0.492188, 1.0), + VBase4(0.558594, 0.589844, 0.875, 1.0), + VBase4(0.726562, 0.472656, 0.859375, 1.0), + VBase4(0.898438, 0.617188, 0.90625, 1.0), + VBase4(1.0, 1.0, 1.0, 1.0), + VBase4(0.0, 0.2, 0.956862, 1.0), + VBase4(0.972549, 0.094117, 0.094117, 1.0), + VBase4(0.447058, 0.0, 0.90196, 1.0)] +ShirtStyles = {'bss1': [0, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'bss2': [1, 1, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss3': [2, 2, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss4': [3, 3, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss5': [4, 4, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss6': [5, 5, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss7': [8, 8, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (27, 27)]], + 'bss8': [9, 9, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss9': [10, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'bss10': [11, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'bss11': [14, 10, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss12': [16, 0, [(27, 27), + (27, 4), + (27, 5), + (27, 6), + (27, 7), + (27, 8), + (27, 9)]], + 'bss13': [17, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12)]], + 'bss14': [18, 12, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (27, 27)]], + 'bss15': [19, 13, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (27, 27)]], + 'gss1': [0, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26), + (27, 27)]], + 'gss2': [1, 1, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss3': [2, 2, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss4': [3, 3, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss5': [5, 5, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss6': [6, 6, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss7': [7, 7, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss8': [9, 9, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss9': [12, 0, [(27, 27)]], + 'gss10': [13, 11, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss11': [15, 11, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss12': [16, 0, [(27, 27), + (27, 4), + (27, 5), + (27, 6), + (27, 7), + (27, 8), + (27, 9)]], + 'gss13': [20, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss14': [21, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'gss15': [22, 0, [(0, 0), + (1, 1), + (2, 2), + (3, 3), + (4, 4), + (5, 5), + (6, 6), + (7, 7), + (8, 8), + (9, 9), + (10, 10), + (11, 11), + (12, 12), + (21, 21), + (22, 22), + (23, 23), + (24, 24), + (25, 25), + (26, 26)]], + 'c_ss1': [25, 16, [(27, 27)]], + 'c_ss2': [27, 18, [(27, 27)]], + 'c_ss3': [38, 27, [(27, 27)]], + 'c_bss1': [26, 17, [(27, 27)]], + 'c_bss2': [28, 19, [(27, 27)]], + 'c_bss3': [37, 26, [(27, 27)]], + 'c_bss4': [39, 28, [(27, 27)]], + 'c_gss1': [23, 14, [(27, 27)]], + 'c_gss2': [24, 15, [(27, 27)]], + 'c_gss3': [35, 24, [(27, 27)]], + 'c_gss4': [36, 25, [(27, 27)]], + 'c_gss5': [40, 29, [(27, 27)]], + 'c_ss4': [45, 34, [(27, 27)]], + 'c_ss5': [46, 35, [(27, 27)]], + 'c_ss6': [52, 41, [(27, 27)]], + 'c_ss7': [53, 42, [(27, 27)]], + 'c_ss8': [54, 43, [(27, 27)]], + 'c_ss9': [55, 44, [(27, 27)]], + 'c_ss10': [56, 45, [(27, 27)]], + 'c_ss11': [57, 46, [(27, 27)]], + 'hw_ss1': [29, 20, [(27, 27)]], + 'hw_ss2': [30, 21, [(27, 27)]], + 'hw_ss3': [114, 101, [(27, 27)]], + 'hw_ss4': [115, 102, [(27, 27)]], + 'hw_ss5': [122, 109, [(27, 27)]], + 'hw_ss6': [123, 110, [(27, 27)]], + 'hw_ss7': [124, 111, [(27, 27)]], + 'hw_ss8': [125, 112, [(27, 27)]], + 'hw_ss9': [126, 113, [(27, 27)]], + 'wh_ss1': [31, 22, [(27, 27)]], + 'wh_ss2': [32, 22, [(27, 27)]], + 'wh_ss3': [33, 23, [(27, 27)]], + 'wh_ss4': [34, 23, [(27, 27)]], + 'vd_ss1': [41, 30, [(27, 27)]], + 'vd_ss2': [42, 31, [(27, 27)]], + 'vd_ss3': [43, 32, [(27, 27)]], + 'vd_ss4': [44, 33, [(27, 27)]], + 'vd_ss5': [69, 58, [(27, 27)]], + 'vd_ss6': [70, 59, [(27, 27)]], + 'vd_ss7': [96, 85, [(27, 27)]], + 'sd_ss1': [47, 36, [(27, 27)]], + 'sd_ss2': [48, 37, [(27, 27)]], + 'sd_ss3': [116, 103, [(27, 27)]], + 'tc_ss1': [49, 38, [(27, 27)]], + 'tc_ss2': [50, 39, [(27, 27)]], + 'tc_ss3': [51, 40, [(27, 27)]], + 'tc_ss4': [62, 51, [(27, 27)]], + 'tc_ss5': [63, 52, [(27, 27)]], + 'tc_ss6': [64, 53, [(27, 27)]], + 'tc_ss7': [65, 54, [(27, 27)]], + 'j4_ss1': [58, 47, [(27, 27)]], + 'j4_ss2': [59, 48, [(27, 27)]], + 'c_ss12': [60, 49, [(27, 27)]], + 'c_ss13': [61, 50, [(27, 27)]], + 'pj_ss1': [66, 55, [(27, 27)]], + 'pj_ss2': [67, 56, [(27, 27)]], + 'pj_ss3': [68, 57, [(27, 27)]], + 'sa_ss1': [71, 60, [(27, 27)]], + 'sa_ss2': [72, 61, [(27, 27)]], + 'sa_ss3': [73, 62, [(27, 27)]], + 'sa_ss4': [74, 63, [(27, 27)]], + 'sa_ss5': [75, 64, [(27, 27)]], + 'sa_ss6': [76, 65, [(27, 27)]], + 'sa_ss7': [77, 66, [(27, 27)]], + 'sa_ss8': [78, 67, [(27, 27)]], + 'sa_ss9': [79, 68, [(27, 27)]], + 'sa_ss10': [80, 69, [(27, 27)]], + 'sa_ss11': [81, 70, [(27, 27)]], + 'sa_ss12': [82, 71, [(27, 27)]], + 'sa_ss13': [83, 72, [(27, 27)]], + 'sa_ss14': [84, 73, [(27, 27)]], + 'sa_ss15': [85, 74, [(27, 27)]], + 'sa_ss16': [86, 75, [(27, 27)]], + 'sa_ss17': [87, 76, [(27, 27)]], + 'sa_ss18': [88, 77, [(27, 27)]], + 'sa_ss19': [89, 78, [(27, 27)]], + 'sa_ss20': [90, 79, [(27, 27)]], + 'sa_ss21': [91, 80, [(27, 27)]], + 'sa_ss22': [92, 81, [(27, 27)]], + 'sa_ss23': [93, 82, [(27, 27)]], + 'sa_ss24': [94, 83, [(27, 27)]], + 'sa_ss25': [95, 84, [(27, 27)]], + 'sa_ss26': [106, 93, [(27, 27)]], + 'sa_ss27': [110, 97, [(27, 27)]], + 'sa_ss28': [111, 98, [(27, 27)]], + 'sa_ss29': [120, 107, [(27, 27)]], + 'sa_ss30': [121, 108, [(27, 27)]], + 'sa_ss31': [118, 105, [(27, 27)]], + 'sa_ss32': [127, 114, [(27, 27)]], + 'sa_ss33': [128, 115, [(27, 27)]], + 'sa_ss34': [129, 116, [(27, 27)]], + 'sa_ss35': [130, 117, [(27, 27)]], + 'sa_ss36': [131, 118, [(27, 27)]], + 'sa_ss37': [132, 119, [(27, 27)]], + 'sa_ss38': [133, 120, [(27, 27)]], + 'sa_ss39': [134, 121, [(27, 27)]], + 'sa_ss40': [135, 122, [(27, 27)]], + 'sa_ss41': [136, 123, [(27, 27)]], + 'sa_ss42': [137, 124, [(27, 27)]], + 'sa_ss43': [138, 125, [(27, 27)]], + 'sa_ss44': [139, 126, [(27, 27)]], + 'sa_ss45': [140, 127, [(27, 27)]], + 'sa_ss46': [141, 128, [(27, 27)]], + 'sa_ss47': [142, 129, [(27, 27)]], + 'sa_ss48': [143, 130, [(27, 27)]], + 'sa_ss49': [144, 116, [(27, 27)]], + 'sa_ss50': [145, 131, [(27, 27)]], + 'sa_ss51': [146, 133, [(27, 27)]], + 'sa_ss52': [147, 134, [(27, 27)]], + 'sa_ss53': [148, 135, [(27, 27)]], + 'sa_ss54': [149, 136, [(27, 27)]], + 'sa_ss55': [150, 137, [(27, 27)]], + 'sc_1': [97, 86, [(27, 27)]], + 'sc_2': [98, 86, [(27, 27)]], + 'sc_3': [99, 86, [(27, 27)]], + 'sil_1': [100, 87, [(27, 27)]], + 'sil_2': [101, 88, [(27, 27)]], + 'sil_3': [102, 89, [(27, 27)]], + 'sil_4': [103, 90, [(27, 27)]], + 'sil_5': [104, 91, [(27, 27)]], + 'sil_6': [105, 92, [(27, 27)]], + 'sil_7': [107, 94, [(27, 27)]], + 'sil_8': [108, 95, [(27, 27)]], + 'emb_us1': [103, 90, [(27, 27)]], + 'emb_us2': [100, 87, [(27, 27)]], + 'emb_us3': [101, 88, [(27, 27)]], + 'sb_1': [109, 96, [(27, 27)]], + 'jb_1': [112, 99, [(27, 27)]], + 'jb_2': [113, 100, [(27, 27)]], + 'ugcms': [117, 104, [(27, 27)]], + 'lb_1': [119, 106, [(27, 27)]]} +BottomStyles = {'bbs1': [0, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs2': [1, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs3': [2, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs4': [3, [0, + 1, + 2, + 4, + 6, + 8, + 9, + 11, + 12, + 13, + 15, + 16, + 17, + 18, + 19, + 20, + 27]], + 'bbs5': [4, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20]], + 'bbs6': [5, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 27]], + 'bbs7': [6, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 20, + 27]], + 'bbs8': [7, [0, + 1, + 2, + 4, + 6, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 27]], + 'vd_bs1': [8, [27]], + 'vd_bs2': [23, [27]], + 'vd_bs3': [24, [27]], + 'c_bs1': [9, [27]], + 'c_bs2': [10, [27]], + 'c_bs5': [15, [27]], + 'sd_bs1': [11, [27]], + 'sd_bs2': [44, [27]], + 'pj_bs1': [16, [27]], + 'pj_bs2': [17, [27]], + 'pj_bs3': [18, [27]], + 'wh_bs1': [19, [27]], + 'wh_bs2': [20, [27]], + 'wh_bs3': [21, [27]], + 'wh_bs4': [22, [27]], + 'hw_bs1': [47, [27]], + 'hw_bs2': [48, [27]], + 'hw_bs5': [49, [27]], + 'hw_bs6': [50, [27]], + 'hw_bs7': [51, [27]], + 'gsk1': [0, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsk2': [1, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk3': [2, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk4': [3, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk5': [4, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26]], + 'gsk6': [7, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsk7': [8, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsh1': [5, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsh2': [6, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'gsh3': [9, [0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 11, + 12, + 21, + 22, + 23, + 24, + 25, + 26, + 27]], + 'c_gsk1': [10, [27]], + 'c_gsk2': [11, [27]], + 'c_gsk3': [12, [27]], + 'vd_gs1': [13, [27]], + 'vd_gs2': [27, [27]], + 'vd_gs3': [28, [27]], + 'c_gsk4': [14, [27]], + 'sd_gs1': [15, [27]], + 'sd_gs2': [48, [27]], + 'c_gsk5': [16, [27]], + 'c_gsk6': [17, [27]], + 'c_bs3': [12, [27]], + 'c_bs4': [13, [27]], + 'j4_bs1': [14, [27]], + 'j4_gs1': [18, [27]], + 'c_gsk7': [19, [27]], + 'pj_gs1': [20, [27]], + 'pj_gs2': [21, [27]], + 'pj_gs3': [22, [27]], + 'wh_gsk1': [23, [27]], + 'wh_gsk2': [24, [27]], + 'wh_gsk3': [25, [27]], + 'wh_gsk4': [26, [27]], + 'sa_bs1': [25, [27]], + 'sa_bs2': [26, [27]], + 'sa_bs3': [27, [27]], + 'sa_bs4': [28, [27]], + 'sa_bs5': [29, [27]], + 'sa_bs6': [30, [27]], + 'sa_bs7': [31, [27]], + 'sa_bs8': [32, [27]], + 'sa_bs9': [33, [27]], + 'sa_bs10': [34, [27]], + 'sa_bs11': [35, [27]], + 'sa_bs12': [36, [27]], + 'sa_bs13': [41, [27]], + 'sa_bs14': [46, [27]], + 'sa_bs15': [45, [27]], + 'sa_bs16': [52, [27]], + 'sa_bs17': [53, [27]], + 'sa_bs18': [54, [27]], + 'sa_bs19': [55, [27]], + 'sa_bs20': [56, [27]], + 'sa_bs21': [57, [27]], + 'sa_gs1': [29, [27]], + 'sa_gs2': [30, [27]], + 'sa_gs3': [31, [27]], + 'sa_gs4': [32, [27]], + 'sa_gs5': [33, [27]], + 'sa_gs6': [34, [27]], + 'sa_gs7': [35, [27]], + 'sa_gs8': [36, [27]], + 'sa_gs9': [37, [27]], + 'sa_gs10': [38, [27]], + 'sa_gs11': [39, [27]], + 'sa_gs12': [40, [27]], + 'sa_gs13': [45, [27]], + 'sa_gs14': [50, [27]], + 'sa_gs15': [49, [27]], + 'sa_gs16': [57, [27]], + 'sa_gs17': [58, [27]], + 'sa_gs18': [59, [27]], + 'sa_gs19': [60, [27]], + 'sa_gs20': [61, [27]], + 'sa_gs21': [62, [27]], + 'sc_bs1': [37, [27]], + 'sc_bs2': [38, [27]], + 'sc_bs3': [39, [27]], + 'sc_gs1': [41, [27]], + 'sc_gs2': [42, [27]], + 'sc_gs3': [43, [27]], + 'sil_bs1': [40, [27]], + 'sil_gs1': [44, [27]], + 'hw_bs3': [42, [27]], + 'hw_gs3': [46, [27]], + 'hw_bs4': [43, [27]], + 'hw_gs4': [47, [27]], + 'hw_gs1': [51, [27]], + 'hw_gs2': [52, [27]], + 'hw_gs5': [54, [27]], + 'hw_gs6': [55, [27]], + 'hw_gs7': [56, [27]], + 'hw_gsk1': [53, [27]]} +MAKE_A_TOON = 1 +TAMMY_TAILOR = 2004 +LONGJOHN_LEROY = 1007 +TAILOR_HARMONY = 4008 +BONNIE_BLOSSOM = 5007 +WARREN_BUNDLES = 3008 +WORNOUT_WAYLON = 9010 +TailorCollections = {MAKE_A_TOON: [['bss1', 'bss2'], + ['gss1', 'gss2'], + ['bbs1', 'bbs2'], + ['gsk1', 'gsh1']], + TAMMY_TAILOR: [['bss1', 'bss2'], + ['gss1', 'gss2'], + ['bbs1', 'bbs2'], + ['gsk1', 'gsh1']], + LONGJOHN_LEROY: [['bss3', 'bss4', 'bss14'], + ['gss3', 'gss4', 'gss14'], + ['bbs3', 'bbs4'], + ['gsk2', 'gsh2']], + TAILOR_HARMONY: [['bss5', 'bss6', 'bss10'], + ['gss5', 'gss6', 'gss9'], + ['bbs5'], + ['gsk3', 'gsh3']], + BONNIE_BLOSSOM: [['bss7', 'bss8', 'bss12'], + ['gss8', 'gss10', 'gss12'], + ['bbs6'], + ['gsk4', 'gsk5']], + WARREN_BUNDLES: [['bss9', 'bss13'], + ['gss7', 'gss11'], + ['bbs7'], + ['gsk6']], + WORNOUT_WAYLON: [['bss11', 'bss15'], + ['gss13', 'gss15'], + ['bbs8'], + ['gsk7']]} +BOY_SHIRTS = 0 +GIRL_SHIRTS = 1 +BOY_SHORTS = 2 +GIRL_BOTTOMS = 3 +HAT = 1 +GLASSES = 2 +BACKPACK = 4 +SHOES = 8 +MakeAToonBoyBottoms = [] +MakeAToonBoyShirts = [] +MakeAToonGirlBottoms = [] +MakeAToonGirlShirts = [] +MakeAToonGirlSkirts = [] +MakeAToonGirlShorts = [] +for style in TailorCollections[MAKE_A_TOON][BOY_SHORTS]: + index = BottomStyles[style][0] + MakeAToonBoyBottoms.append(index) + +for style in TailorCollections[MAKE_A_TOON][BOY_SHIRTS]: + index = ShirtStyles[style][0] + MakeAToonBoyShirts.append(index) + +for style in TailorCollections[MAKE_A_TOON][GIRL_BOTTOMS]: + index = BottomStyles[style][0] + MakeAToonGirlBottoms.append(index) + +for style in TailorCollections[MAKE_A_TOON][GIRL_SHIRTS]: + index = ShirtStyles[style][0] + MakeAToonGirlShirts.append(index) + +for index in MakeAToonGirlBottoms: + flag = GirlBottoms[index][1] + if flag == SKIRT: + MakeAToonGirlSkirts.append(index) + elif flag == SHORTS: + MakeAToonGirlShorts.append(index) + else: + notify.error('Invalid flag') + +def getRandomTop(gender, tailorId = MAKE_A_TOON, generator = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + style = generator.choice(collection[BOY_SHIRTS]) + else: + style = generator.choice(collection[GIRL_SHIRTS]) + styleList = ShirtStyles[style] + colors = generator.choice(styleList[2]) + return (styleList[0], + colors[0], + styleList[1], + colors[1]) + + +def getRandomBottom(gender, tailorId = MAKE_A_TOON, generator = None, girlBottomType = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + style = generator.choice(collection[BOY_SHORTS]) + elif girlBottomType is None: + style = generator.choice(collection[GIRL_BOTTOMS]) + elif girlBottomType == SKIRT: + skirtCollection = filter(lambda style: GirlBottoms[BottomStyles[style][0]][1] == SKIRT, collection[GIRL_BOTTOMS]) + style = generator.choice(skirtCollection) + elif girlBottomType == SHORTS: + shortsCollection = filter(lambda style: GirlBottoms[BottomStyles[style][0]][1] == SHORTS, collection[GIRL_BOTTOMS]) + style = generator.choice(shortsCollection) + else: + notify.error('Bad girlBottomType: %s' % girlBottomType) + styleList = BottomStyles[style] + color = generator.choice(styleList[1]) + return (styleList[0], color) + + +def getRandomGirlBottom(type): + bottoms = [] + index = 0 + for bottom in GirlBottoms: + if bottom[1] == type: + bottoms.append(index) + index += 1 + + return random.choice(bottoms) + + +def getRandomGirlBottomAndColor(type): + bottoms = [] + if type == SHORTS: + typeStr = 'gsh' + else: + typeStr = 'gsk' + for bottom in BottomStyles.keys(): + if bottom.find(typeStr) >= 0: + bottoms.append(bottom) + + style = BottomStyles[random.choice(bottoms)] + return (style[0], random.choice(style[1])) + + +def getRandomizedTops(gender, tailorId = MAKE_A_TOON, generator = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + collection = collection[BOY_SHIRTS][:] + else: + collection = collection[GIRL_SHIRTS][:] + tops = [] + random.shuffle(collection) + for style in collection: + colors = ShirtStyles[style][2][:] + random.shuffle(colors) + for color in colors: + tops.append((ShirtStyles[style][0], + color[0], + ShirtStyles[style][1], + color[1])) + + return tops + + +def getRandomizedBottoms(gender, tailorId = MAKE_A_TOON, generator = None): + if generator == None: + generator = random + collection = TailorCollections[tailorId] + if gender == 'm': + collection = collection[BOY_SHORTS][:] + else: + collection = collection[GIRL_BOTTOMS][:] + bottoms = [] + random.shuffle(collection) + for style in collection: + colors = BottomStyles[style][1][:] + random.shuffle(colors) + for color in colors: + bottoms.append((BottomStyles[style][0], color)) + + return bottoms + + +def getTops(gender, tailorId = MAKE_A_TOON): + if gender == 'm': + collection = TailorCollections[tailorId][BOY_SHIRTS] + else: + collection = TailorCollections[tailorId][GIRL_SHIRTS] + tops = [] + for style in collection: + for color in ShirtStyles[style][2]: + tops.append((ShirtStyles[style][0], + color[0], + ShirtStyles[style][1], + color[1])) + + return tops + + +def getAllTops(gender): + tops = [] + for style in ShirtStyles.keys(): + if gender == 'm': + if style[0] == 'g' or style[:3] == 'c_g': + continue + elif style[0] == 'b' or style[:3] == 'c_b': + continue + for color in ShirtStyles[style][2]: + tops.append((ShirtStyles[style][0], + color[0], + ShirtStyles[style][1], + color[1])) + + return tops + + +def getBottoms(gender, tailorId = MAKE_A_TOON): + if gender == 'm': + collection = TailorCollections[tailorId][BOY_SHORTS] + else: + collection = TailorCollections[tailorId][GIRL_BOTTOMS] + bottoms = [] + for style in collection: + for color in BottomStyles[style][1]: + bottoms.append((BottomStyles[style][0], color)) + + return bottoms + + +def getAllBottoms(gender, output = 'both'): + bottoms = [] + for style in BottomStyles.keys(): + if gender == 'm': + if style[0] == 'g' or style[:3] == 'c_g' or style[:4] == 'vd_g' or style[:4] == 'sd_g' or style[:4] == 'j4_g' or style[:4] == 'pj_g' or style[:4] == 'wh_g' or style[:4] == 'sa_g' or style[:4] == 'sc_g' or style[:5] == 'sil_g' or style[:4] == 'hw_g': + continue + elif style[0] == 'b' or style[:3] == 'c_b' or style[:4] == 'vd_b' or style[:4] == 'sd_b' or style[:4] == 'j4_b' or style[:4] == 'pj_b' or style[:4] == 'wh_b' or style[:4] == 'sa_b' or style[:4] == 'sc_b' or style[:5] == 'sil_b' or style[:4] == 'hw_b': + continue + bottomIdx = BottomStyles[style][0] + if gender == 'f': + textureType = GirlBottoms[bottomIdx][1] + else: + textureType = SHORTS + if output == 'both' or output == 'skirts' and textureType == SKIRT or output == 'shorts' and textureType == SHORTS: + for color in BottomStyles[style][1]: + bottoms.append((bottomIdx, color)) + + return bottoms + + +allColorsList = [VBase4(1.0, 1.0, 1.0, 1.0), + VBase4(0.96875, 0.691406, 0.699219, 1.0), + VBase4(0.933594, 0.265625, 0.28125, 1.0), + VBase4(0.863281, 0.40625, 0.417969, 1.0), + VBase4(0.710938, 0.234375, 0.4375, 1.0), + VBase4(0.570312, 0.449219, 0.164062, 1.0), + VBase4(0.640625, 0.355469, 0.269531, 1.0), + VBase4(0.996094, 0.695312, 0.511719, 1.0), + VBase4(0.832031, 0.5, 0.296875, 1.0), + VBase4(0.992188, 0.480469, 0.167969, 1.0), + VBase4(0.996094, 0.898438, 0.320312, 1.0), + VBase4(0.996094, 0.957031, 0.597656, 1.0), + VBase4(0.855469, 0.933594, 0.492188, 1.0), + VBase4(0.550781, 0.824219, 0.324219, 1.0), + VBase4(0.242188, 0.742188, 0.515625, 1.0), + VBase4(0.304688, 0.96875, 0.402344, 1.0), + VBase4(0.433594, 0.90625, 0.835938, 1.0), + VBase4(0.347656, 0.820312, 0.953125, 1.0), + VBase4(0.191406, 0.5625, 0.773438, 1.0), + VBase4(0.558594, 0.589844, 0.875, 1.0), + VBase4(0.285156, 0.328125, 0.726562, 1.0), + VBase4(0.460938, 0.378906, 0.824219, 1.0), + VBase4(0.546875, 0.28125, 0.75, 1.0), + VBase4(0.726562, 0.472656, 0.859375, 1.0), + VBase4(0.898438, 0.617188, 0.90625, 1.0), + VBase4(0.7, 0.7, 0.8, 1.0), + VBase4(0.3, 0.3, 0.35, 1.0)] +defaultBoyColorList = [1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24] +defaultGirlColorList = [1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24] +allColorsListApproximations = map(lambda x: VBase4(round(x[0], 3), round(x[1], 3), round(x[2], 3), round(x[3], 3)), allColorsList) +allowedColors = set(map(lambda x: allColorsListApproximations[x], set(defaultBoyColorList + defaultGirlColorList + [26]))) +HatModels = [None, + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_baseball', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_safari', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_ribbon', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_heart', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_topHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_anvil', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_flowerPot', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sandbag', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_weight', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_fez', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_golfHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_partyHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pillBox', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_crown', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_cowboyHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pirateHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_propellerHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_fishingHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sombreroHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_strawHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sunHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_antenna', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_beeHiveHairdo', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_bowler', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_chefsHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_detective', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_feathers', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_fedora', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_mickeysBandConductorHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_nativeAmericanFeather', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pompadorHairdo', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_princess', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_robinHoodHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_romanHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_spiderAntennaThingy', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_tiara', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_vikingHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_witch', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_wizard', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_conquistadorHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_firefighterHelmet', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_foilPyramid', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_minersHardhatWithLight', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_napoleonHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_pilotsCap', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_policeHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_rainbowAfroWig', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_sailorHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_carmenMirandaFruitHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_bobbyHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_jugheadHat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_winter', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_bandana', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_dinosaur', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_band', + 'phase_4/models/accessories/tt_m_chr_avt_acc_hat_birdNest'] +HatTextures = [None, + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_heartYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_topHatBlue.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_safariBrown.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_safariGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballBlue.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballOrange.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonChecker.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonLtRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonRainbow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballTeal.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonPinkDots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_baseballPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_ribbonCheckerGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_hat_partyToon.jpg'] +GlassesModels = [None, + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_roundGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_miniblinds', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_narrowGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_starGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_3dGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_aviator', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_catEyeGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_dorkGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_jackieOShades', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_scubaMask', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_goggles', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_grouchoMarxEyebrow', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_heartGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_insectEyeGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_masqueradeTypeMask', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_masqueradeTypeMask3', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_monocle', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_mouthGlasses', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_squareRims', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_eyepatch', + 'phase_4/models/accessories/tt_m_chr_avt_acc_msk_alienGlasses'] +GlassesTextures = [None, + 'phase_4/maps/tt_t_chr_avt_acc_msk_masqueradeTypeMask2.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_msk_masqueradeTypeMask4.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_msk_masqueradeTypeMask5.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_msk_eyepatchGems.jpg'] +BackpackModels = [None, + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_backpack', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_batWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_beeWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_dragonFlyWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_scubaTank', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_sharkFin', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_angelWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_backpackWithToys', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_butterflyWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_dragonWing', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_jetPack', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_spiderLegs', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_stuffedAnimalBackpackA', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_birdWings', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_stuffedAnimalBackpackCat', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_stuffedAnimalBackpackDog', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_airplane', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_woodenSword', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_supertoonCape', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_vampireCape', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_dinosaurTail', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_band', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_gags', + 'phase_4/models/accessories/tt_m_chr_avt_acc_pac_flunky'] +BackpackTextures = [None, + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackOrange.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackPolkaDotRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_backpackPolkaDotYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_angelWingsMultiColor.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_pac_butterflyWingsStyle2.jpg'] +ShoesModels = ['feet', + 'shoes', + 'boots_short', + 'boots_long'] +ShoesTextures = ['phase_3/maps/tt_t_chr_avt_acc_sho_athleticGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_athleticRed.jpg', + 'phase_3/maps/tt_t_chr_avt_acc_sho_docMartinBootsGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_wingtips.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoes.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_deckShoes.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_athleticYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleBlack.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleWhite.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStylePink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_cowboyBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_hiTopSneakers.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoesBrown.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoesRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_superToonRedBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesGreen.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleRed.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsAqua.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsBrown.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsBlueSquares.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsGreenHearts.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsGreyDots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsOrangeStars.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_fashionBootsPinkStars.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_loafers.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_maryJaneShoesPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_motorcycleBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_oxfords.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_santaBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_winterBootsBeige.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_winterBootsPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_workBoots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_converseStyleYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_docMartinBootsPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_hiTopSneakersPink.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsRedDots.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesPurple.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesViolet.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_tennisShoesYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsBlue.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_rainBootsYellow.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_athleticBlack.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_pirate.jpg', + 'phase_4/maps/tt_t_chr_avt_acc_sho_dinosaur.jpg'] +HatStyles = {'none': [0, 0, 0], + 'hbb1': [1, 0, 0], + 'hsf1': [2, 0, 0], + 'hsf2': [2, 5, 0], + 'hsf3': [2, 6, 0], + 'hht1': [4, 0, 0], + 'hht2': [4, 3, 0], + 'htp1': [5, 0, 0], + 'htp2': [5, 4, 0], + 'hav1': [6, 0, 0], + 'hfp1': [7, 0, 0], + 'hsg1': [8, 0, 0], + 'hwt1': [9, 0, 0], + 'hfz1': [10, 0, 0], + 'hgf1': [11, 0, 0], + 'hpt1': [12, 0, 0], + 'hpt2': [12, 19, 0], + 'hpb1': [13, 0, 0], + 'hcr1': [14, 0, 0], + 'hbb2': [1, 7, 0], + 'hbb3': [1, 8, 0], + 'hcw1': [15, 0, 0], + 'hpr1': [16, 0, 0], + 'hpp1': [17, 0, 0], + 'hfs1': [18, 0, 0], + 'hsb1': [19, 0, 0], + 'hst1': [20, 0, 0], + 'hat1': [22, 0, 0], + 'hhd1': [23, 0, 0], + 'hbw1': [24, 0, 0], + 'hch1': [25, 0, 0], + 'hdt1': [26, 0, 0], + 'hft1': [27, 0, 0], + 'hfd1': [28, 0, 0], + 'hmk1': [29, 0, 0], + 'hft2': [30, 0, 0], + 'hhd2': [31, 0, 0], + 'hrh1': [33, 0, 0], + 'hhm1': [34, 0, 0], + 'hat2': [35, 0, 0], + 'htr1': [36, 0, 0], + 'hhm2': [37, 0, 0], + 'hwz1': [38, 0, 0], + 'hwz2': [39, 0, 0], + 'hhm3': [40, 0, 0], + 'hhm4': [41, 0, 0], + 'hfp2': [42, 0, 0], + 'hhm5': [43, 0, 0], + 'hnp1': [44, 0, 0], + 'hpc2': [45, 0, 0], + 'hph1': [46, 0, 0], + 'hwg1': [47, 0, 0], + 'hbb4': [1, 13, 0], + 'hbb5': [1, 14, 0], + 'hbb6': [1, 15, 0], + 'hsl1': [48, 0, 0], + 'hfr1': [49, 0, 0], + 'hby1': [50, 0, 0], + 'hjh1': [51, 0, 0], + 'hbb7': [1, 17, 0], + 'hwt2': [52, 0, 0], + 'hhw2': [54, 0, 0], + 'hob1': [55, 0, 0], + 'hbn1': [56, 0, 0], + 'hrb1': [3, 0, 0], + 'hrb2': [3, 1, 0], + 'hrb3': [3, 2, 0], + 'hsu1': [21, 0, 0], + 'hrb4': [3, 9, 0], + 'hrb5': [3, 10, 0], + 'hrb6': [3, 11, 0], + 'hrb7': [3, 12, 0], + 'hpc1': [32, 0, 0], + 'hrb8': [3, 16, 0], + 'hrb9': [3, 18, 0], + 'hhw1': [53, 0, 0]} +GlassesStyles = {'none': [0, 0, 0], + 'grd1': [1, 0, 0], + 'gmb1': [2, 0, 0], + 'gnr1': [3, 0, 0], + 'gst1': [4, 0, 0], + 'g3d1': [5, 0, 0], + 'gav1': [6, 0, 0], + 'gjo1': [9, 0, 0], + 'gsb1': [10, 0, 0], + 'ggl1': [11, 0, 0], + 'ggm1': [12, 0, 0], + 'ghg1': [13, 0, 0], + 'gie1': [14, 0, 0], + 'gmt1': [15, 0, 0], + 'gmt2': [15, 1, 0], + 'gmt3': [16, 0, 0], + 'gmt4': [16, 2, 0], + 'gmt5': [16, 3, 0], + 'gmn1': [17, 0, 0], + 'gmo1': [18, 0, 0], + 'gsr1': [19, 0, 0], + 'gce1': [7, 0, 0], + 'gdk1': [8, 0, 0], + 'gag1': [21, 0, 0], + 'ghw1': [20, 0, 0], + 'ghw2': [20, 4, 0]} +BackpackStyles = {'none': [0, 0, 0], + 'bpb1': [1, 0, 0], + 'bpb2': [1, 1, 0], + 'bpb3': [1, 2, 0], + 'bpd1': [1, 3, 0], + 'bpd2': [1, 4, 0], + 'bwg1': [2, 0, 0], + 'bwg2': [3, 0, 0], + 'bwg3': [4, 0, 0], + 'bst1': [5, 0, 0], + 'bfn1': [6, 0, 0], + 'baw1': [7, 0, 0], + 'baw2': [7, 5, 0], + 'bwt1': [8, 0, 0], + 'bwg4': [9, 0, 0], + 'bwg5': [9, 6, 0], + 'bwg6': [10, 0, 0], + 'bjp1': [11, 0, 0], + 'blg1': [12, 0, 0], + 'bsa1': [13, 0, 0], + 'bwg7': [14, 0, 0], + 'bsa2': [15, 0, 0], + 'bsa3': [16, 0, 0], + 'bap1': [17, 0, 0], + 'bhw1': [18, 0, 0], + 'bhw2': [19, 0, 0], + 'bhw3': [20, 0, 0], + 'bhw4': [21, 0, 0], + 'bob1': [22, 0, 0], + 'bfg1': [23, 0, 0], + 'bfl1': [24, 0, 0]} +ShoesStyles = {'none': [0, 0, 0], + 'sat1': [1, 0, 0], + 'sat2': [1, 1, 0], + 'smb1': [3, 2, 0], + 'scs1': [2, 3, 0], + 'sdk1': [1, 6, 0], + 'sat3': [1, 7, 0], + 'scs2': [2, 8, 0], + 'scs3': [2, 9, 0], + 'scs4': [2, 10, 0], + 'scb1': [3, 11, 0], + 'sht1': [2, 13, 0], + 'ssb1': [3, 16, 0], + 'sts1': [1, 17, 0], + 'sts2': [1, 18, 0], + 'scs5': [2, 19, 0], + 'smb2': [3, 20, 0], + 'smb3': [3, 21, 0], + 'smb4': [3, 22, 0], + 'slf1': [1, 28, 0], + 'smt1': [3, 30, 0], + 'sox1': [1, 31, 0], + 'srb1': [3, 32, 0], + 'sst1': [3, 33, 0], + 'swb1': [3, 34, 0], + 'swb2': [3, 35, 0], + 'swk1': [2, 36, 0], + 'scs6': [2, 37, 0], + 'smb5': [3, 38, 0], + 'sht2': [2, 39, 0], + 'srb2': [3, 40, 0], + 'sts3': [1, 41, 0], + 'sts4': [1, 42, 0], + 'sts5': [1, 43, 0], + 'srb3': [3, 44, 0], + 'srb4': [3, 45, 0], + 'sat4': [1, 46, 0], + 'shw1': [3, 47, 0], + 'shw2': [3, 48, 0], + 'swt1': [1, 4, 0], + 'smj1': [2, 5, 0], + 'sfb1': [3, 12, 0], + 'smj2': [2, 14, 0], + 'smj3': [2, 15, 0], + 'sfb2': [3, 23, 0], + 'sfb3': [3, 24, 0], + 'sfb4': [3, 25, 0], + 'sfb5': [3, 26, 0], + 'sfb6': [3, 27, 0], + 'smj4': [2, 29, 0]} + +def isValidHat(itemIdx, textureIdx, colorIdx): + for style in HatStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidGlasses(itemIdx, textureIdx, colorIdx): + for style in GlassesStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidBackpack(itemIdx, textureIdx, colorIdx): + for style in BackpackStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidShoes(itemIdx, textureIdx, colorIdx): + for style in ShoesStyles.values(): + if itemIdx == style[0] and textureIdx == style[1] and colorIdx == style[2]: + return True + + return False + + +def isValidAccessory(itemIdx, textureIdx, colorIdx, which): + if which == HAT: + return isValidHat(itemIdx, textureIdx, colorIdx) + elif which == GLASSES: + return isValidGlasses(itemIdx, textureIdx, colorIdx) + elif which == BACKPACK: + return isValidBackpack(itemIdx, textureIdx, colorIdx) + elif which == SHOES: + return isValidShoes(itemIdx, textureIdx, colorIdx) + else: + return False + + +class ToonDNA(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 == 't': + if dna == None: + self.newToonRandom(r, g, b) + else: + self.newToonFromProperties(*dna.asTuple()) + else: + self.type = 'u' + self.cache = () + return + + def __str__(self): + string = 'type = toon\n' + string = string + 'gender = %s\n' % self.gender + string = string + 'head = %s, torso = %s, legs = %s\n' % (self.head, self.torso, self.legs) + string = string + 'arm color = %d\n' % self.armColor + string = string + 'glove color = %d\n' % self.gloveColor + string = string + 'leg color = %d\n' % self.legColor + string = string + 'head color = %d\n' % self.headColor + string = string + 'top texture = %d\n' % self.topTex + string = string + 'top texture color = %d\n' % self.topTexColor + string = string + 'sleeve texture = %d\n' % self.sleeveTex + string = string + 'sleeve texture color = %d\n' % self.sleeveTexColor + string = string + 'bottom texture = %d\n' % self.botTex + string = string + 'bottom texture color = %d\n' % self.botTexColor + return string + + def clone(self): + d = ToonDNA() + d.makeFromNetString(self.makeNetString()) + return d + + def makeNetString(self): + dg = PyDatagram() + dg.addFixedString(self.type, 1) + if self.type == 't': + headIndex = toonHeadTypes.index(self.head) + torsoIndex = toonTorsoTypes.index(self.torso) + legsIndex = toonLegTypes.index(self.legs) + dg.addUint8(headIndex) + dg.addUint8(torsoIndex) + dg.addUint8(legsIndex) + if self.gender == 'm': + dg.addUint8(1) + else: + dg.addUint8(0) + dg.addUint8(self.topTex) + dg.addUint8(self.topTexColor) + dg.addUint8(self.sleeveTex) + dg.addUint8(self.sleeveTexColor) + dg.addUint8(self.botTex) + dg.addUint8(self.botTexColor) + dg.addUint8(self.armColor) + dg.addUint8(self.gloveColor) + dg.addUint8(self.legColor) + dg.addUint8(self.headColor) + elif self.type == 'u': + notify.error('undefined avatar') + else: + notify.error('unknown avatar type: ', self.type) + return dg.getMessage() + + def isValidNetString(self, string): + dg = PyDatagram(string) + dgi = PyDatagramIterator(dg) + if dgi.getRemainingSize() != 15: + return False + type = dgi.getFixedString(1) + if type not in ('t',): + return False + headIndex = dgi.getUint8() + torsoIndex = dgi.getUint8() + legsIndex = dgi.getUint8() + if headIndex >= len(toonHeadTypes): + return False + if torsoIndex >= len(toonTorsoTypes): + return False + if legsIndex >= len(toonLegTypes): + return False + gender = dgi.getUint8() + if gender == 1: + gender = 'm' + else: + gender = 'f' + topTex = dgi.getUint8() + topTexColor = dgi.getUint8() + sleeveTex = dgi.getUint8() + sleeveTexColor = dgi.getUint8() + botTex = dgi.getUint8() + botTexColor = dgi.getUint8() + armColor = dgi.getUint8() + gloveColor = dgi.getUint8() + legColor = dgi.getUint8() + headColor = dgi.getUint8() + if topTex >= len(Shirts): + return False + if topTexColor >= len(ClothesColors): + return False + if sleeveTex >= len(Sleeves): + return False + if sleeveTexColor >= len(ClothesColors): + return False + if botTex >= choice(gender == 'm', len(BoyShorts), len(GirlBottoms)): + return False + if botTexColor >= len(ClothesColors): + return False + if armColor >= len(allColorsList): + return False + if gloveColor != 0: + return False + if legColor >= len(allColorsList): + return False + if headColor >= len(allColorsList): + return False + return True + + def makeFromNetString(self, string): + dg = PyDatagram(string) + dgi = PyDatagramIterator(dg) + self.type = dgi.getFixedString(1) + if self.type == 't': + headIndex = dgi.getUint8() + torsoIndex = dgi.getUint8() + legsIndex = dgi.getUint8() + self.head = toonHeadTypes[headIndex] + self.torso = toonTorsoTypes[torsoIndex] + self.legs = toonLegTypes[legsIndex] + gender = dgi.getUint8() + if gender == 1: + self.gender = 'm' + else: + self.gender = 'f' + self.topTex = dgi.getUint8() + self.topTexColor = dgi.getUint8() + self.sleeveTex = dgi.getUint8() + self.sleeveTexColor = dgi.getUint8() + self.botTex = dgi.getUint8() + self.botTexColor = dgi.getUint8() + self.armColor = dgi.getUint8() + self.gloveColor = dgi.getUint8() + self.legColor = dgi.getUint8() + self.headColor = dgi.getUint8() + else: + notify.error('unknown avatar type: ', self.type) + return None + + def defaultColor(self): + return 25 + + def __defaultColors(self): + color = self.defaultColor() + self.armColor = color + self.gloveColor = 0 + self.legColor = color + self.headColor = color + + def newToon(self, dna, color = None): + if len(dna) == 4: + self.type = 't' + self.head = dna[0] + self.torso = dna[1] + self.legs = dna[2] + self.gender = dna[3] + self.topTex = 0 + self.topTexColor = 0 + self.sleeveTex = 0 + self.sleeveTexColor = 0 + self.botTex = 0 + self.botTexColor = 0 + if color == None: + color = self.defaultColor() + self.armColor = color + self.legColor = color + self.headColor = color + self.gloveColor = 0 + else: + notify.error("tuple must be in format ('%s', '%s', '%s', '%s')") + return + + def newToonFromProperties(self, head, torso, legs, gender, armColor, gloveColor, legColor, headColor, topTexture, topTextureColor, sleeveTexture, sleeveTextureColor, bottomTexture, bottomTextureColor): + self.type = 't' + self.head = head + self.torso = torso + self.legs = legs + self.gender = gender + self.armColor = armColor + self.gloveColor = gloveColor + self.legColor = legColor + self.headColor = headColor + self.topTex = topTexture + self.topTexColor = topTextureColor + self.sleeveTex = sleeveTexture + self.sleeveTexColor = sleeveTextureColor + self.botTex = bottomTexture + self.botTexColor = bottomTextureColor + + def updateToonProperties(self, head = None, torso = None, legs = None, gender = None, armColor = None, gloveColor = None, legColor = None, headColor = None, topTexture = None, topTextureColor = None, sleeveTexture = None, sleeveTextureColor = None, bottomTexture = None, bottomTextureColor = None, shirt = None, bottom = None): + if head: + self.head = head + if torso: + self.torso = torso + if legs: + self.legs = legs + if gender: + self.gender = gender + if armColor: + self.armColor = armColor + if gloveColor: + self.gloveColor = gloveColor + if legColor: + self.legColor = legColor + if headColor: + self.headColor = headColor + if topTexture: + self.topTex = topTexture + if topTextureColor: + self.topTexColor = topTextureColor + if sleeveTexture: + self.sleeveTex = sleeveTexture + if sleeveTextureColor: + self.sleeveTexColor = sleeveTextureColor + if bottomTexture: + self.botTex = bottomTexture + if bottomTextureColor: + self.botTexColor = bottomTextureColor + if shirt: + str, colorIndex = shirt + defn = ShirtStyles[str] + self.topTex = defn[0] + self.topTexColor = defn[2][colorIndex][0] + self.sleeveTex = defn[1] + self.sleeveTexColor = defn[2][colorIndex][1] + if bottom: + str, colorIndex = bottom + defn = BottomStyles[str] + self.botTex = defn[0] + self.botTexColor = defn[1][colorIndex] + + def newToonRandom(self, seed = None, gender = 'm', npc = 0, stage = None): + if seed: + generator = random.Random() + generator.seed(seed) + else: + generator = random + self.type = 't' + self.legs = generator.choice(toonLegTypes + ['m', + 'l', + 'l', + 'l']) + self.gender = gender + if not npc: + if stage == MAKE_A_TOON: + if not base.cr.isPaid(): + animalIndicesToUse = allToonHeadAnimalIndicesTrial + else: + animalIndicesToUse = allToonHeadAnimalIndices + animal = generator.choice(animalIndicesToUse) + self.head = toonHeadTypes[animal] + else: + self.head = generator.choice(toonHeadTypes) + else: + self.head = generator.choice(toonHeadTypes[:22]) + top, topColor, sleeve, sleeveColor = getRandomTop(gender, generator=generator) + bottom, bottomColor = getRandomBottom(gender, generator=generator) + if gender == 'm': + self.torso = generator.choice(toonTorsoTypes[:3]) + self.topTex = top + self.topTexColor = topColor + self.sleeveTex = sleeve + self.sleeveTexColor = sleeveColor + self.botTex = bottom + self.botTexColor = bottomColor + color = generator.choice(defaultBoyColorList) + self.armColor = color + self.legColor = color + self.headColor = color + else: + self.torso = generator.choice(toonTorsoTypes[:6]) + self.topTex = top + self.topTexColor = topColor + self.sleeveTex = sleeve + self.sleeveTexColor = sleeveColor + if self.torso[1] == 'd': + bottom, bottomColor = getRandomBottom(gender, generator=generator, girlBottomType=SKIRT) + else: + bottom, bottomColor = getRandomBottom(gender, generator=generator, girlBottomType=SHORTS) + self.botTex = bottom + self.botTexColor = bottomColor + color = generator.choice(defaultGirlColorList) + self.armColor = color + self.legColor = color + self.headColor = color + self.gloveColor = 0 + + def asTuple(self): + return (self.head, + self.torso, + self.legs, + self.gender, + self.armColor, + self.gloveColor, + self.legColor, + self.headColor, + self.topTex, + self.topTexColor, + self.sleeveTex, + self.sleeveTexColor, + self.botTex, + self.botTexColor) + + def getType(self): + if self.type == 't': + type = self.getAnimal() + else: + notify.error('Invalid DNA type: ', self.type) + return type + + def getAnimal(self): + if self.head[0] == 'd': + return 'dog' + elif self.head[0] == 'c': + return 'cat' + elif self.head[0] == 'm': + return 'mouse' + elif self.head[0] == 'h': + return 'horse' + elif self.head[0] == 'r': + return 'rabbit' + elif self.head[0] == 'f': + return 'duck' + elif self.head[0] == 'p': + return 'monkey' + elif self.head[0] == 'b': + return 'bear' + elif self.head[0] == 's': + return 'pig' + else: + notify.error('unknown headStyle: ', self.head[0]) + + def getHeadSize(self): + if self.head[1] == 'l': + return 'long' + elif self.head[1] == 's': + return 'short' + else: + notify.error('unknown head size: ', self.head[1]) + + def getMuzzleSize(self): + if self.head[2] == 'l': + return 'long' + elif self.head[2] == 's': + return 'short' + else: + notify.error('unknown muzzle size: ', self.head[2]) + + def getTorsoSize(self): + if self.torso[0] == 'l': + return 'long' + elif self.torso[0] == 'm': + return 'medium' + elif self.torso[0] == 's': + return 'short' + else: + notify.error('unknown torso size: ', self.torso[0]) + + def getLegSize(self): + if self.legs == 'l': + return 'long' + elif self.legs == 'm': + return 'medium' + elif self.legs == 's': + return 'short' + else: + notify.error('unknown leg size: ', self.legs) + + def getGender(self): + return self.gender + + def getClothes(self): + if len(self.torso) == 1: + return 'naked' + elif self.torso[1] == 's': + return 'shorts' + elif self.torso[1] == 'd': + return 'dress' + else: + notify.error('unknown clothing type: ', self.torso[1]) + + def getArmColor(self): + try: + return allColorsList[self.armColor] + except: + return allColorsList[0] + + def getLegColor(self): + try: + return allColorsList[self.legColor] + except: + return allColorsList[0] + + def getHeadColor(self): + try: + return allColorsList[self.headColor] + except: + return allColorsList[0] + + def getGloveColor(self): + try: + return allColorsList[self.gloveColor] + except: + return allColorsList[0] + + def getBlackColor(self): + try: + return allColorsList[26] + except: + return allColorsList[0] + + def setTemporary(self, newHead, newArmColor, newLegColor, newHeadColor): + if not self.cache and self.getArmColor != newArmColor: + self.cache = (self.head, + self.armColor, + self.legColor, + self.headColor) + self.updateToonProperties(head=newHead, armColor=newArmColor, legColor=newLegColor, headColor=newHeadColor) + + def restoreTemporary(self, oldStyle): + cache = () + if oldStyle: + cache = oldStyle.cache + if cache: + self.updateToonProperties(head=cache[0], armColor=cache[1], legColor=cache[2], headColor=cache[3]) + if oldStyle: + oldStyle.cache = () diff --git a/toontown/toon/ToonDetail.py b/toontown/toon/ToonDetail.py new file mode 100644 index 0000000..b845bc3 --- /dev/null +++ b/toontown/toon/ToonDetail.py @@ -0,0 +1,14 @@ +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.avatar import AvatarDetail +from toontown.toon import DistributedToon + +class ToonDetail(AvatarDetail.AvatarDetail): + notify = directNotify.newCategory('ToonDetail') + + def getDClass(self): + return 'DistributedToon' + + def createHolder(self): + toon = DistributedToon.DistributedToon(base.cr, bFake=True) + toon.forceAllowDelayDelete() + return toon diff --git a/toontown/toon/ToonHead.py b/toontown/toon/ToonHead.py new file mode 100644 index 0000000..569080d --- /dev/null +++ b/toontown/toon/ToonHead.py @@ -0,0 +1,1322 @@ +from direct.actor import Actor +from direct.task import Task +from toontown.toonbase import ToontownGlobals +import string +import random +from pandac.PandaModules import * +from direct.interval.IntervalGlobal import * +from direct.fsm.ClassicFSM import ClassicFSM +from direct.fsm.State import State +from direct.directnotify import DirectNotifyGlobal +if not base.config.GetBool('want-new-anims', 1): + HeadDict = {'dls': '/models/char/dogMM_Shorts-head-', + 'dss': '/models/char/dogMM_Skirt-head-', + 'dsl': '/models/char/dogSS_Shorts-head-', + 'dll': '/models/char/dogLL_Shorts-head-', + 'c': '/models/char/cat-heads-', + 'h': '/models/char/horse-heads-', + 'm': '/models/char/mouse-heads-', + 'r': '/models/char/rabbit-heads-', + 'f': '/models/char/duck-heads-', + 'p': '/models/char/monkey-heads-', + 'b': '/models/char/bear-heads-', + 's': '/models/char/pig-heads-'} +else: + HeadDict = {'dls': '/models/char/tt_a_chr_dgm_shorts_head_', + 'dss': '/models/char/tt_a_chr_dgm_skirt_head_', + 'dsl': '/models/char/tt_a_chr_dgs_shorts_head_', + 'dll': '/models/char/tt_a_chr_dgl_shorts_head_', + 'c': '/models/char/cat-heads-', + 'h': '/models/char/horse-heads-', + 'm': '/models/char/mouse-heads-', + 'r': '/models/char/rabbit-heads-', + 'f': '/models/char/duck-heads-', + 'p': '/models/char/monkey-heads-', + 'b': '/models/char/bear-heads-', + 's': '/models/char/pig-heads-'} +EyelashDict = {'d': '/models/char/dog-lashes', + 'c': '/models/char/cat-lashes', + 'h': '/models/char/horse-lashes', + 'm': '/models/char/mouse-lashes', + 'r': '/models/char/rabbit-lashes', + 'f': '/models/char/duck-lashes', + 'p': '/models/char/monkey-lashes', + 'b': '/models/char/bear-lashes', + 's': '/models/char/pig-lashes'} +DogMuzzleDict = {'dls': '/models/char/dogMM_Shorts-headMuzzles-', + 'dss': '/models/char/dogMM_Skirt-headMuzzles-', + 'dsl': '/models/char/dogSS_Shorts-headMuzzles-', + 'dll': '/models/char/dogLL_Shorts-headMuzzles-'} + +class ToonHead(Actor.Actor): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonHead') + EyesOpen = loader.loadTexture('phase_3/maps/eyes.jpg', 'phase_3/maps/eyes_a.rgb') + EyesOpen.setMinfilter(Texture.FTLinear) + EyesOpen.setMagfilter(Texture.FTLinear) + EyesClosed = loader.loadTexture('phase_3/maps/eyesClosed.jpg', 'phase_3/maps/eyesClosed_a.rgb') + EyesClosed.setMinfilter(Texture.FTLinear) + EyesClosed.setMagfilter(Texture.FTLinear) + EyesSadOpen = loader.loadTexture('phase_3/maps/eyesSad.jpg', 'phase_3/maps/eyesSad_a.rgb') + EyesSadOpen.setMinfilter(Texture.FTLinear) + EyesSadOpen.setMagfilter(Texture.FTLinear) + EyesSadClosed = loader.loadTexture('phase_3/maps/eyesSadClosed.jpg', 'phase_3/maps/eyesSadClosed_a.rgb') + EyesSadClosed.setMinfilter(Texture.FTLinear) + EyesSadClosed.setMagfilter(Texture.FTLinear) + EyesAngryOpen = loader.loadTexture('phase_3/maps/eyesAngry.jpg', 'phase_3/maps/eyesAngry_a.rgb') + EyesAngryOpen.setMinfilter(Texture.FTLinear) + EyesAngryOpen.setMagfilter(Texture.FTLinear) + EyesAngryClosed = loader.loadTexture('phase_3/maps/eyesAngryClosed.jpg', 'phase_3/maps/eyesAngryClosed_a.rgb') + EyesAngryClosed.setMinfilter(Texture.FTLinear) + EyesAngryClosed.setMagfilter(Texture.FTLinear) + EyesSurprised = loader.loadTexture('phase_3/maps/eyesSurprised.jpg', 'phase_3/maps/eyesSurprised_a.rgb') + EyesSurprised.setMinfilter(Texture.FTLinear) + EyesSurprised.setMagfilter(Texture.FTLinear) + Muzzle = loader.loadTexture('phase_3/maps/muzzleShrtGeneric.jpg') + Muzzle.setMinfilter(Texture.FTLinear) + Muzzle.setMagfilter(Texture.FTLinear) + MuzzleSurprised = loader.loadTexture('phase_3/maps/muzzleShortSurprised.jpg') + MuzzleSurprised.setMinfilter(Texture.FTLinear) + MuzzleSurprised.setMagfilter(Texture.FTLinear) + LeftA = Point3(0.06, 0.0, 0.14) + LeftB = Point3(-0.13, 0.0, 0.1) + LeftC = Point3(-0.05, 0.0, 0.0) + LeftD = Point3(0.06, 0.0, 0.0) + RightA = Point3(0.13, 0.0, 0.1) + RightB = Point3(-0.06, 0.0, 0.14) + RightC = Point3(-0.06, 0.0, 0.0) + RightD = Point3(0.05, 0.0, 0.0) + LeftAD = Point3(LeftA[0] - LeftA[2] * (LeftD[0] - LeftA[0]) / (LeftD[2] - LeftA[2]), 0.0, 0.0) + LeftBC = Point3(LeftB[0] - LeftB[2] * (LeftC[0] - LeftB[0]) / (LeftC[2] - LeftB[2]), 0.0, 0.0) + RightAD = Point3(RightA[0] - RightA[2] * (RightD[0] - RightA[0]) / (RightD[2] - RightA[2]), 0.0, 0.0) + RightBC = Point3(RightB[0] - RightB[2] * (RightC[0] - RightB[0]) / (RightC[2] - RightB[2]), 0.0, 0.0) + + def __init__(self): + try: + self.ToonHead_initialized + except: + self.ToonHead_initialized = 1 + Actor.Actor.__init__(self) + self.toonName = 'ToonHead-' + str(self.this) + self.__blinkName = 'blink-' + self.toonName + self.__stareAtName = 'stareAt-' + self.toonName + self.__lookName = 'look-' + self.toonName + self.lookAtTrack = None + self.__eyes = None + self.__eyelashOpen = None + self.__eyelashClosed = None + self.__lod500Eyes = None + self.__lod250Eyes = None + self.__lpupil = None + self.__lod500lPupil = None + self.__lod250lPupil = None + self.__rpupil = None + self.__lod500rPupil = None + self.__lod250rPupil = None + self.__muzzle = None + self.__eyesOpen = ToonHead.EyesOpen + self.__eyesClosed = ToonHead.EyesClosed + self.__height = 0.0 + self.__eyelashesHiddenByGlasses = False + self.randGen = random.Random() + self.randGen.seed(random.random()) + self.eyelids = ClassicFSM('eyelids', [State('off', self.enterEyelidsOff, self.exitEyelidsOff, ['open', 'closed', 'surprised']), + State('open', self.enterEyelidsOpen, self.exitEyelidsOpen, ['closed', 'surprised', 'off']), + State('surprised', self.enterEyelidsSurprised, self.exitEyelidsSurprised, ['open', 'closed', 'off']), + State('closed', self.enterEyelidsClosed, self.exitEyelidsClosed, ['open', 'surprised', 'off'])], 'off', 'off') + self.eyelids.enterInitialState() + self.emote = None + self.__stareAtNode = NodePath() + self.__defaultStarePoint = Point3(0, 0, 0) + self.__stareAtPoint = self.__defaultStarePoint + self.__stareAtTime = 0 + self.lookAtPositionCallbackArgs = None + + return + + def delete(self): + try: + self.ToonHead_deleted + except: + self.ToonHead_deleted = 1 + taskMgr.remove(self.__blinkName) + taskMgr.remove(self.__lookName) + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + del self.eyelids + del self.__stareAtNode + del self.__stareAtPoint + if self.__eyes: + del self.__eyes + if self.__lpupil: + del self.__lpupil + if self.__rpupil: + del self.__rpupil + if self.__eyelashOpen: + del self.__eyelashOpen + if self.__eyelashClosed: + del self.__eyelashClosed + self.lookAtPositionCallbackArgs = None + Actor.Actor.delete(self) + + return + + def setupHead(self, dna, forGui = 0): + self.__height = self.generateToonHead(1, dna, ('1000',), forGui) + self.generateToonColor(dna) + animalStyle = dna.getAnimal() + bodyScale = ToontownGlobals.toonBodyScales[animalStyle] + headScale = ToontownGlobals.toonHeadScales[animalStyle] + self.getGeomNode().setScale(headScale[0] * bodyScale * 1.3, headScale[1] * bodyScale * 1.3, headScale[2] * bodyScale * 1.3) + if forGui: + self.getGeomNode().setDepthWrite(1) + self.getGeomNode().setDepthTest(1) + if dna.getAnimal() == 'dog': + self.loop('neutral') + + def fitAndCenterHead(self, maxDim, forGui = 0): + p1 = Point3() + p2 = Point3() + self.calcTightBounds(p1, p2) + if forGui: + h = 180 + t = p1[0] + p1.setX(-p2[0]) + p2.setX(-t) + else: + h = 0 + d = p2 - p1 + biggest = max(d[0], d[2]) + s = maxDim / biggest + mid = (p1 + d / 2.0) * s + self.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], h, 0, 0, s, s, s) + + def setLookAtPositionCallbackArgs(self, argTuple): + self.lookAtPositionCallbackArgs = argTuple + + def getHeight(self): + return self.__height + + def getRandomForwardLookAtPoint(self): + x = self.randGen.choice((-0.8, + -0.5, + 0, + 0.5, + 0.8)) + z = self.randGen.choice((-0.5, + 0, + 0.5, + 0.8)) + return Point3(x, 1.5, z) + + def findSomethingToLookAt(self): + if self.lookAtPositionCallbackArgs != None: + pnt = self.lookAtPositionCallbackArgs[0].getLookAtPosition(self.lookAtPositionCallbackArgs[1], self.lookAtPositionCallbackArgs[2]) + self.startStareAt(self, pnt) + return + if self.randGen.random() < 0.33: + lookAtPnt = self.getRandomForwardLookAtPoint() + else: + lookAtPnt = self.__defaultStarePoint + self.lerpLookAt(lookAtPnt, blink=1) + return + + def generateToonHead(self, copy, style, lods, forGui = 0): + headStyle = style.head + fix = None + if headStyle == 'dls': + filePrefix = HeadDict['dls'] + headHeight = 0.75 + elif headStyle == 'dss': + filePrefix = HeadDict['dss'] + headHeight = 0.5 + elif headStyle == 'dsl': + filePrefix = HeadDict['dsl'] + headHeight = 0.5 + elif headStyle == 'dll': + filePrefix = HeadDict['dll'] + headHeight = 0.75 + elif headStyle == 'cls': + filePrefix = HeadDict['c'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'css': + filePrefix = HeadDict['c'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'csl': + filePrefix = HeadDict['c'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'cll': + filePrefix = HeadDict['c'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'hls': + filePrefix = HeadDict['h'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'hss': + filePrefix = HeadDict['h'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'hsl': + filePrefix = HeadDict['h'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'hll': + filePrefix = HeadDict['h'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'mls': + filePrefix = HeadDict['m'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'mss': + filePrefix = HeadDict['m'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'rls': + filePrefix = HeadDict['r'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'rss': + filePrefix = HeadDict['r'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'rsl': + filePrefix = HeadDict['r'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'rll': + filePrefix = HeadDict['r'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'fls': + filePrefix = HeadDict['f'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'fss': + filePrefix = HeadDict['f'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'fsl': + filePrefix = HeadDict['f'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'fll': + filePrefix = HeadDict['f'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'pls': + filePrefix = HeadDict['p'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'pss': + filePrefix = HeadDict['p'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'psl': + filePrefix = HeadDict['p'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'pll': + filePrefix = HeadDict['p'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'bls': + filePrefix = HeadDict['b'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'bss': + filePrefix = HeadDict['b'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'bsl': + filePrefix = HeadDict['b'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'bll': + filePrefix = HeadDict['b'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + elif headStyle == 'sls': + filePrefix = HeadDict['s'] + fix = self.__fixHeadLongShort + headHeight = 0.75 + elif headStyle == 'sss': + filePrefix = HeadDict['s'] + fix = self.__fixHeadShortShort + headHeight = 0.5 + elif headStyle == 'ssl': + filePrefix = HeadDict['s'] + fix = self.__fixHeadShortLong + headHeight = 0.5 + elif headStyle == 'sll': + filePrefix = HeadDict['s'] + fix = self.__fixHeadLongLong + headHeight = 0.75 + else: + ToonHead.notify.error('unknown head style: %s' % headStyle) + if len(lods) == 1: + self.loadModel('phase_3' + filePrefix + lods[0], 'head', 'lodRoot', copy) + if not forGui: + pLoaded = self.loadPumpkin(headStyle[1], None, copy) + self.loadSnowMan(headStyle[1], None, copy) + if not copy: + self.showAllParts('head') + if fix != None: + fix(style, None, copy) + if not forGui: + if pLoaded: + self.__fixPumpkin(style, None, copy) + else: + self.__lods = lods + self.__style = style + self.__headStyle = headStyle + self.__copy = copy + else: + for lod in lods: + self.loadModel('phase_3' + filePrefix + lod, 'head', lod, copy) + if not forGui: + pLoaded = self.loadPumpkin(headStyle[1], lod, copy) + self.loadSnowMan(headStyle[1], lod, copy) + if not copy: + self.showAllParts('head', lod) + if fix != None: + fix(style, lod, copy) + if not forGui: + if pLoaded: + self.__fixPumpkin(style, lod, copy) + else: + self.__lods = lods + self.__style = style + self.__headStyle = headStyle + self.__copy = copy + + self.__fixEyes(style, forGui) + self.setupEyelashes(style) + self.eyelids.request('closed') + self.eyelids.request('open') + self.setupMuzzles(style) + return headHeight + + def loadPumpkin(self, headStyle, lod, copy): + if hasattr(base, 'launcher') and (not base.launcher or base.launcher and base.launcher.getPhaseComplete(4)): + if not hasattr(self, 'pumpkins'): + self.pumpkins = NodePathCollection() + ppath = 'phase_4/models/estate/pumpkin_' + if headStyle is 'l': + if copy: + pmodel = loader.loadModel(ppath + 'tall') + else: + pmodel = loader.loadModel(ppath + 'tall') + ptype = 'tall' + else: + if copy: + pmodel = loader.loadModel(ppath + 'short') + else: + pmodel = loader.loadModel(ppath + 'short') + ptype = 'short' + if pmodel: + p = pmodel.find('**/pumpkin_' + ptype + '*') + p.setScale(0.5) + p.setZ(-0.5) + p.setH(180) + if lod: + p.reparentTo(self.find('**/' + lod + '/**/__Actor_head')) + else: + p.reparentTo(self.find('**/__Actor_head')) + self.pumpkins.addPath(p) + pmodel.remove() + return True + else: + del self.pumpkins + return False + else: + ToonHead.notify.debug('phase_4 not complete yet. Postponing pumpkin head load.') + + def loadSnowMan(self, headStyle, lod, copy): + if hasattr(base, 'launcher') and (not base.launcher or base.launcher and base.launcher.getPhaseComplete(4)): + if not hasattr(self, 'snowMen'): + self.snowMen = NodePathCollection() + snowManPath = 'phase_4/models/props/tt_m_efx_snowmanHead_' + if headStyle is 'l': + snowManPath = snowManPath + 'tall' + else: + snowManPath = snowManPath + 'short' + model = loader.loadModel(snowManPath) + if model: + model.setZ(-0.5) + model.setH(180) + if lod: + model.reparentTo(self.getPart('head', lod)) + else: + model.reparentTo(self.find('**/__Actor_head')) + self.snowMen.addPath(model) + model.stash() + return True + else: + del self.snowMen + return False + else: + ToonHead.notify.debug('phase_4 not loaded yet.') + + def __fixPumpkin(self, style, lodName = None, copy = 1): + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + pumpkin = searchRoot.find('**/__Actor_head/pumpkin*') + pumpkin.stash() + return + + def enablePumpkins(self, enable): + if not hasattr(self, 'pumpkins'): + if len(self.__lods) == 1: + pLoaded = self.loadPumpkin(self.__headStyle[1], None, self.__copy) + if pLoaded: + self.__fixPumpkin(self.__style, None, self.__copy) + else: + for lod in self.__lods: + pLoaded = self.loadPumpkin(self.__headStyle[1], lod, self.__copy) + if pLoaded: + self.__fixPumpkin(self.__style, lod, self.__copy) + + if hasattr(self, 'pumpkins'): + for x in ['__lods', + '__style', + '__headStyle', + '__copy']: + if hasattr(self, '_ToonHead' + x): + delattr(self, '_ToonHead' + x) + + if hasattr(self, 'pumpkins'): + if enable: + if self.__eyelashOpen: + self.__eyelashOpen.stash() + if self.__eyelashClosed: + self.__eyelashClosed.stash() + self.pumpkins.unstash() + else: + if not self.__eyelashesHiddenByGlasses: + if self.__eyelashOpen: + self.__eyelashOpen.unstash() + if self.__eyelashClosed: + self.__eyelashClosed.unstash() + self.pumpkins.stash() + return + + def enableSnowMen(self, enable): + if not hasattr(self, 'snowMen'): + if len(self.__lods) == 1: + self.loadSnowMan(self.__headStyle[1], None, self.__copy) + else: + for lod in self.__lds: + self.loadSnowMan(self.__headStyle[1], lod, self.__copy) + + if hasattr(self, 'snowMen'): + if enable: + if self.__eyelashOpen: + self.__eyelashOpen.stash() + if self.__eyelashClosed: + self.__eyelashClosed.stash() + self.snowMen.unstash() + else: + if not self.__eyelashesHiddenByGlasses: + if self.__eyelashOpen: + self.__eyelashOpen.unstash() + if self.__eyelashClosed: + self.__eyelashClosed.unstash() + self.snowMen.stash() + return + + def hideEars(self): + self.findAllMatches('**/ears*;+s').stash() + + def showEars(self): + self.findAllMatches('**/ears*;+s').unstash() + + def hideEyelashes(self): + if self.__eyelashOpen: + self.__eyelashOpen.stash() + if self.__eyelashClosed: + self.__eyelashClosed.stash() + self.__eyelashesHiddenByGlasses = True + + def showEyelashes(self): + if self.__eyelashOpen: + self.__eyelashOpen.unstash() + if self.__eyelashClosed: + self.__eyelashClosed.unstash() + self.__eyelashesHiddenByGlasses = False + + def generateToonColor(self, style): + parts = self.findAllMatches('**/head*') + parts.setColor(style.getHeadColor()) + animalType = style.getAnimal() + if animalType == 'cat' or animalType == 'rabbit' or animalType == 'bear' or animalType == 'mouse' or animalType == 'pig': + parts = self.findAllMatches('**/ear?-*') + parts.setColor(style.getHeadColor()) + + def __fixEyes(self, style, forGui = 0): + mode = -3 + if forGui: + mode = -2 + if self.hasLOD(): + for lodName in self.getLODNames(): + self.drawInFront('eyes*', 'head-front*', mode, lodName=lodName) + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupil*').isEmpty(): + self.drawInFront('joint_pupil*', 'eyes*', -1, lodName=lodName) + else: + self.drawInFront('def_*_pupil', 'eyes*', -1, lodName=lodName) + else: + self.drawInFront('joint_pupil*', 'eyes*', -1, lodName=lodName) + + self.__eyes = self.getLOD(1000).find('**/eyes*') + self.__lod500Eyes = self.getLOD(500).find('**/eyes*') + self.__lod250Eyes = self.getLOD(250).find('**/eyes*') + if self.__lod500Eyes.isEmpty(): + self.__lod500Eyes = None + else: + self.__lod500Eyes.setColorOff() + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupilL*').isEmpty(): + self.__lod500lPupil = self.__lod500Eyes.find('**/joint_pupilL*') + self.__lod500rPupil = self.__lod500Eyes.find('**/joint_pupilR*') + else: + self.__lod500lPupil = self.__lod500Eyes.find('**/def_left_pupil*') + self.__lod500rPupil = self.__lod500Eyes.find('**/def_right_pupil*') + else: + self.__lod500lPupil = self.__lod500Eyes.find('**/joint_pupilL*') + self.__lod500rPupil = self.__lod500Eyes.find('**/joint_pupilR*') + if self.__lod250Eyes.isEmpty(): + self.__lod250Eyes = None + else: + self.__lod250Eyes.setColorOff() + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupilL*').isEmpty(): + self.__lod250lPupil = self.__lod250Eyes.find('**/joint_pupilL*') + self.__lod250rPupil = self.__lod250Eyes.find('**/joint_pupilR*') + else: + self.__lod250lPupil = self.__lod250Eyes.find('**/def_left_pupil*') + self.__lod250rPupil = self.__lod250Eyes.find('**/def_right_pupil*') + else: + self.__lod250lPupil = self.__lod250Eyes.find('**/joint_pupilL*') + self.__lod250rPupil = self.__lod250Eyes.find('**/joint_pupilR*') + else: + self.drawInFront('eyes*', 'head-front*', mode) + if base.config.GetBool('want-new-anims', 1): + if not self.find('joint_pupil*').isEmpty(): + self.drawInFront('joint_pupil*', 'eyes*', -1) + else: + self.drawInFront('def_*_pupil', 'eyes*', -1) + else: + self.drawInFront('joint_pupil*', 'eyes*', -1) + self.__eyes = self.find('**/eyes*') + if not self.__eyes.isEmpty(): + self.__eyes.setColorOff() + self.__lpupil = None + self.__rpupil = None + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/joint_pupilL*').isEmpty(): + if self.getLOD(1000): + lp = self.getLOD(1000).find('**/joint_pupilL*') + rp = self.getLOD(1000).find('**/joint_pupilR*') + else: + lp = self.find('**/joint_pupilL*') + rp = self.find('**/joint_pupilR*') + elif not self.getLOD(1000): + lp = self.find('**/def_left_pupil*') + rp = self.find('**/def_right_pupil*') + else: + lp = self.getLOD(1000).find('**/def_left_pupil*') + rp = self.getLOD(1000).find('**/def_right_pupil*') + else: + lp = self.__eyes.find('**/joint_pupilL*') + rp = self.__eyes.find('**/joint_pupilR*') + if lp.isEmpty() or rp.isEmpty(): + print 'Unable to locate pupils.' + else: + leye = self.__eyes.attachNewNode('leye') + reye = self.__eyes.attachNewNode('reye') + lmat = Mat4(0.802174, 0.59709, 0, 0, -0.586191, 0.787531, 0.190197, 0, 0.113565, -0.152571, 0.981746, 0, -0.233634, 0.418062, 0.0196875, 1) + leye.setMat(lmat) + rmat = Mat4(0.786788, -0.617224, 0, 0, 0.602836, 0.768447, 0.214658, 0, -0.132492, -0.16889, 0.976689, 0, 0.233634, 0.418062, 0.0196875, 1) + reye.setMat(rmat) + self.__lpupil = leye.attachNewNode('lpupil') + self.__rpupil = reye.attachNewNode('rpupil') + lpt = self.__eyes.attachNewNode('') + rpt = self.__eyes.attachNewNode('') + lpt.wrtReparentTo(self.__lpupil) + rpt.wrtReparentTo(self.__rpupil) + lp.reparentTo(lpt) + rp.reparentTo(rpt) + self.__lpupil.adjustAllPriorities(1) + self.__rpupil.adjustAllPriorities(1) + if self.__lod500Eyes: + self.__lod500lPupil.adjustAllPriorities(1) + self.__lod500rPupil.adjustAllPriorities(1) + if self.__lod250Eyes: + self.__lod250lPupil.adjustAllPriorities(1) + self.__lod250rPupil.adjustAllPriorities(1) + animalType = style.getAnimal() + if animalType != 'dog': + self.__lpupil.flattenStrong() + self.__rpupil.flattenStrong() + return + + def __setPupilDirection(self, x, y): + if y < 0.0: + y2 = -y + left1 = self.LeftAD + (self.LeftD - self.LeftAD) * y2 + left2 = self.LeftBC + (self.LeftC - self.LeftBC) * y2 + right1 = self.RightAD + (self.RightD - self.RightAD) * y2 + right2 = self.RightBC + (self.RightC - self.RightBC) * y2 + else: + y2 = y + left1 = self.LeftAD + (self.LeftA - self.LeftAD) * y2 + left2 = self.LeftBC + (self.LeftB - self.LeftBC) * y2 + right1 = self.RightAD + (self.RightA - self.RightAD) * y2 + right2 = self.RightBC + (self.RightB - self.RightBC) * y2 + left0 = Point3(0.0, 0.0, left1[2] - left1[0] * (left2[2] - left1[2]) / (left2[0] - left1[0])) + right0 = Point3(0.0, 0.0, right1[2] - right1[0] * (right2[2] - right1[2]) / (right2[0] - right1[0])) + if x < 0.0: + x2 = -x + left = left0 + (left2 - left0) * x2 + right = right0 + (right2 - right0) * x2 + else: + x2 = x + left = left0 + (left1 - left0) * x2 + right = right0 + (right1 - right0) * x2 + self.__lpupil.setPos(left) + self.__rpupil.setPos(right) + + def __lookPupilsAt(self, node, point): + if node != None: + mat = node.getMat(self.__eyes) + point = mat.xformPoint(point) + distance = 1.0 + recip_z = 1.0 / max(0.1, point[1]) + x = distance * point[0] * recip_z + y = distance * point[2] * recip_z + x = min(max(x, -1), 1) + y = min(max(y, -1), 1) + self.__setPupilDirection(x, y) + return + + def __lookHeadAt(self, node, point, frac = 1.0, lod = None): + reachedTarget = 1 + if lod == None: + head = self.getPart('head', self.getLODNames()[0]) + else: + head = self.getPart('head', lod) + if node != None: + headParent = head.getParent() + mat = node.getMat(headParent) + point = mat.xformPoint(point) + rot = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0) + lookAt(rot, Vec3(point), Vec3(0, 0, 1), CSDefault) + scale = VBase3(0, 0, 0) + hpr = VBase3(0, 0, 0) + if decomposeMatrix(rot, scale, hpr, CSDefault): + hpr = VBase3(min(max(hpr[0], -60), 60), min(max(hpr[1], -20), 30), 0) + if frac != 1: + currentHpr = head.getHpr() + reachedTarget = abs(hpr[0] - currentHpr[0]) < 1.0 and abs(hpr[1] - currentHpr[1]) < 1.0 + hpr = currentHpr + (hpr - currentHpr) * frac + if lod == None: + for lodName in self.getLODNames(): + head = self.getPart('head', lodName) + head.setHpr(hpr) + + else: + head.setHpr(hpr) + return reachedTarget + + def setupEyelashes(self, style): + if style.getGender() == 'm': + if self.__eyelashOpen: + self.__eyelashOpen.removeNode() + self.__eyelashOpen = None + if self.__eyelashClosed: + self.__eyelashClosed.removeNode() + self.__eyelashClosed = None + else: + if self.__eyelashOpen: + self.__eyelashOpen.removeNode() + if self.__eyelashClosed: + self.__eyelashClosed.removeNode() + animal = style.head[0] + model = loader.loadModel('phase_3' + EyelashDict[animal]) + if self.hasLOD(): + head = self.getPart('head', '1000') + else: + head = self.getPart('head', 'lodRoot') + length = style.head[1] + if length == 'l': + openString = 'open-long' + closedString = 'closed-long' + else: + openString = 'open-short' + closedString = 'closed-short' + self.__eyelashOpen = model.find('**/' + openString).copyTo(head) + self.__eyelashClosed = model.find('**/' + closedString).copyTo(head) + model.removeNode() + return + + def __fixHeadLongLong(self, style, lodName = None, copy = 1): + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + otherParts = searchRoot.findAllMatches('**/*short*') + for partNum in range(0, otherParts.getNumPaths()): + if copy: + otherParts.getPath(partNum).removeNode() + else: + otherParts.getPath(partNum).stash() + + return + + def __fixHeadLongShort(self, style, lodName = None, copy = 1): + animalType = style.getAnimal() + headStyle = style.head + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + if animalType != 'duck' and animalType != 'horse': + if animalType == 'rabbit': + if copy: + searchRoot.find('**/ears-long').removeNode() + else: + searchRoot.find('**/ears-long').hide() + elif copy: + searchRoot.find('**/ears-short').removeNode() + else: + searchRoot.find('**/ears-short').hide() + if animalType != 'rabbit': + if copy: + searchRoot.find('**/eyes-short').removeNode() + else: + searchRoot.find('**/eyes-short').hide() + if animalType != 'dog': + if copy: + searchRoot.find('**/joint_pupilL_short').removeNode() + searchRoot.find('**/joint_pupilR_short').removeNode() + else: + searchRoot.find('**/joint_pupilL_short').stash() + searchRoot.find('**/joint_pupilR_short').stash() + if copy: + self.find('**/head-short').removeNode() + self.find('**/head-front-short').removeNode() + else: + self.find('**/head-short').hide() + self.find('**/head-front-short').hide() + if animalType != 'rabbit': + muzzleParts = searchRoot.findAllMatches('**/muzzle-long*') + for partNum in range(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + else: + muzzleParts = searchRoot.findAllMatches('**/muzzle-short*') + for partNum in range(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + return + + def __fixHeadShortLong(self, style, lodName = None, copy = 1): + animalType = style.getAnimal() + headStyle = style.head + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + if animalType != 'duck' and animalType != 'horse': + if animalType == 'rabbit': + if copy: + searchRoot.find('**/ears-short').removeNode() + else: + searchRoot.find('**/ears-short').hide() + elif copy: + searchRoot.find('**/ears-long').removeNode() + else: + searchRoot.find('**/ears-long').hide() + if animalType != 'rabbit': + if copy: + searchRoot.find('**/eyes-long').removeNode() + else: + searchRoot.find('**/eyes-long').hide() + if animalType != 'dog': + if copy: + searchRoot.find('**/joint_pupilL_long').removeNode() + searchRoot.find('**/joint_pupilR_long').removeNode() + else: + searchRoot.find('**/joint_pupilL_long').stash() + searchRoot.find('**/joint_pupilR_long').stash() + if copy: + searchRoot.find('**/head-long').removeNode() + searchRoot.find('**/head-front-long').removeNode() + else: + searchRoot.find('**/head-long').hide() + searchRoot.find('**/head-front-long').hide() + if animalType != 'rabbit': + muzzleParts = searchRoot.findAllMatches('**/muzzle-short*') + for partNum in range(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + else: + muzzleParts = searchRoot.findAllMatches('**/muzzle-long*') + for partNum in range(0, muzzleParts.getNumPaths()): + if copy: + muzzleParts.getPath(partNum).removeNode() + else: + muzzleParts.getPath(partNum).hide() + + return + + def __fixHeadShortShort(self, style, lodName = None, copy = 1): + if lodName == None: + searchRoot = self + else: + searchRoot = self.find('**/' + str(lodName)) + otherParts = searchRoot.findAllMatches('**/*long*') + for partNum in range(0, otherParts.getNumPaths()): + if copy: + otherParts.getPath(partNum).removeNode() + else: + otherParts.getPath(partNum).stash() + + return + + def __blinkOpenEyes(self, task): + if self.eyelids.getCurrentState().getName() == 'closed': + self.eyelids.request('open') + r = self.randGen.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): + if self.eyelids.getCurrentState().getName() != 'open': + taskMgr.doMethodLater(4.0, self.__blinkCloseEyes, self.__blinkName) + else: + self.eyelids.request('closed') + taskMgr.doMethodLater(0.125, self.__blinkOpenEyes, self.__blinkName) + return Task.done + + def startBlink(self): + taskMgr.remove(self.__blinkName) + if self.__eyes: + self.openEyes() + taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self.__blinkCloseEyes, self.__blinkName) + + def stopBlink(self): + taskMgr.remove(self.__blinkName) + if self.__eyes: + self.eyelids.request('open') + + def closeEyes(self): + self.eyelids.request('closed') + + def openEyes(self): + self.eyelids.request('open') + + def surpriseEyes(self): + self.eyelids.request('surprised') + + def sadEyes(self): + self.__eyesOpen = ToonHead.EyesSadOpen + self.__eyesClosed = ToonHead.EyesSadClosed + + def angryEyes(self): + self.__eyesOpen = ToonHead.EyesAngryOpen + self.__eyesClosed = ToonHead.EyesAngryClosed + + def normalEyes(self): + self.__eyesOpen = ToonHead.EyesOpen + self.__eyesClosed = ToonHead.EyesClosed + + def blinkEyes(self): + taskMgr.remove(self.__blinkName) + self.eyelids.request('closed') + taskMgr.doMethodLater(0.1, self.__blinkOpenEyes, self.__blinkName) + + def __stareAt(self, task): + frac = 2 * globalClock.getDt() + reachedTarget = self.__lookHeadAt(self.__stareAtNode, self.__stareAtPoint, frac) + self.__lookPupilsAt(self.__stareAtNode, self.__stareAtPoint) + if reachedTarget and self.__stareAtNode == None: + return Task.done + else: + return Task.cont + return + + def doLookAroundToStareAt(self, node, point): + self.startStareAt(node, point) + self.startLookAround() + + def startStareAtHeadPoint(self, point): + self.startStareAt(self, point) + + def startStareAt(self, node, point): + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + self.__stareAtNode = node + if point != None: + self.__stareAtPoint = point + else: + self.__stareAtPoint = self.__defaultStarePoint + self.__stareAtTime = globalClock.getFrameTime() + taskMgr.add(self.__stareAt, self.__stareAtName) + return + + def lerpLookAt(self, point, time = 1.0, blink = 0): + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + lodNames = self.getLODNames() + if lodNames: + lodName = lodNames[0] + else: + return 0 + head = self.getPart('head', lodName) + startHpr = head.getHpr() + startLpupil = self.__lpupil.getPos() + startRpupil = self.__rpupil.getPos() + self.__lookHeadAt(None, point, lod=lodName) + self.__lookPupilsAt(None, point) + endHpr = head.getHpr() + endLpupil = self.__lpupil.getPos() * 0.5 + endRpupil = self.__rpupil.getPos() * 0.5 + head.setHpr(startHpr) + self.__lpupil.setPos(startLpupil) + self.__rpupil.setPos(startRpupil) + if startHpr.almostEqual(endHpr, 10): + return 0 + if blink: + self.blinkEyes() + lookToTgt_TimeFraction = 0.2 + lookToTgtTime = time * lookToTgt_TimeFraction + returnToEyeCenterTime = time - lookToTgtTime - 0.5 + origin = Point3(0, 0, 0) + blendType = 'easeOut' + self.lookAtTrack = Parallel(Sequence(LerpPosInterval(self.__lpupil, lookToTgtTime, endLpupil, blendType=blendType), Wait(0.5), LerpPosInterval(self.__lpupil, returnToEyeCenterTime, origin, blendType=blendType)), Sequence(LerpPosInterval(self.__rpupil, lookToTgtTime, endRpupil, blendType=blendType), Wait(0.5), LerpPosInterval(self.__rpupil, returnToEyeCenterTime, origin, blendType=blendType)), name=self.__stareAtName) + for lodName in self.getLODNames(): + head = self.getPart('head', lodName) + self.lookAtTrack.append(LerpHprInterval(head, time, endHpr, blendType='easeInOut')) + + self.lookAtTrack.start() + return 1 + + def stopStareAt(self): + self.lerpLookAt(Vec3.forward()) + + def stopStareAtNow(self): + taskMgr.remove(self.__stareAtName) + if self.lookAtTrack: + self.lookAtTrack.finish() + self.lookAtTrack = None + if self.__lpupil and self.__rpupil: + self.__setPupilDirection(0, 0) + for lodName in self.getLODNames(): + head = self.getPart('head', lodName) + head.setHpr(0, 0, 0) + + return + + def __lookAround(self, task): + self.findSomethingToLookAt() + t = self.randGen.random() * 4.0 + 3.0 + taskMgr.doMethodLater(t, self.__lookAround, self.__lookName) + return Task.done + + def startLookAround(self): + taskMgr.remove(self.__lookName) + t = self.randGen.random() * 5.0 + 2.0 + taskMgr.doMethodLater(t, self.__lookAround, self.__lookName) + + def stopLookAround(self): + taskMgr.remove(self.__lookName) + self.stopStareAt() + + def stopLookAroundNow(self): + taskMgr.remove(self.__lookName) + self.stopStareAtNow() + + def enterEyelidsOff(self): + pass + + def exitEyelidsOff(self): + pass + + def enterEyelidsOpen(self): + if not self.__eyes.isEmpty(): + self.__eyes.setTexture(self.__eyesOpen, 1) + if self.__eyelashOpen: + self.__eyelashOpen.show() + if self.__eyelashClosed: + self.__eyelashClosed.hide() + if self.__lod500Eyes: + self.__lod500Eyes.setTexture(self.__eyesOpen, 1) + if self.__lod250Eyes: + self.__lod250Eyes.setTexture(self.__eyesOpen, 1) + if self.__lpupil: + self.__lpupil.show() + self.__rpupil.show() + if self.__lod500lPupil: + self.__lod500lPupil.show() + self.__lod500rPupil.show() + if self.__lod250lPupil: + self.__lod250lPupil.show() + self.__lod250rPupil.show() + + def exitEyelidsOpen(self): + pass + + def enterEyelidsClosed(self): + if not self.__eyes.isEmpty() and self.__eyesClosed: + self.__eyes.setTexture(self.__eyesClosed, 1) + if self.__eyelashOpen: + self.__eyelashOpen.hide() + if self.__eyelashClosed: + self.__eyelashClosed.show() + if self.__lod500Eyes: + self.__lod500Eyes.setTexture(self.__eyesClosed, 1) + if self.__lod250Eyes: + self.__lod250Eyes.setTexture(self.__eyesClosed, 1) + if self.__lpupil: + self.__lpupil.hide() + self.__rpupil.hide() + if self.__lod500lPupil: + self.__lod500lPupil.hide() + self.__lod500rPupil.hide() + if self.__lod250lPupil: + self.__lod250lPupil.hide() + self.__lod250rPupil.hide() + + def exitEyelidsClosed(self): + pass + + def enterEyelidsSurprised(self): + if not self.__eyes.isEmpty() and ToonHead.EyesSurprised: + self.__eyes.setTexture(ToonHead.EyesSurprised, 1) + if self.__eyelashOpen: + self.__eyelashOpen.hide() + if self.__eyelashClosed: + self.__eyelashClosed.hide() + if self.__lod500Eyes: + self.__lod500Eyes.setTexture(ToonHead.EyesSurprised, 1) + if self.__lod250Eyes: + self.__lod250Eyes.setTexture(ToonHead.EyesSurprised, 1) + if self.__muzzle: + self.__muzzle.setTexture(ToonHead.MuzzleSurprised, 1) + if self.__lpupil: + self.__lpupil.show() + self.__rpupil.show() + if self.__lod500lPupil: + self.__lod500lPupil.show() + self.__lod500rPupil.show() + if self.__lod250lPupil: + self.__lod250lPupil.show() + self.__lod250rPupil.show() + + def exitEyelidsSurprised(self): + if self.__muzzle: + self.__muzzle.setTexture(ToonHead.Muzzle, 1) + + def setupMuzzles(self, style): + self.__muzzles = [] + self.__surpriseMuzzles = [] + self.__angryMuzzles = [] + self.__sadMuzzles = [] + self.__smileMuzzles = [] + self.__laughMuzzles = [] + + def hideAddNonEmptyItemToList(item, list): + if not item.isEmpty(): + item.hide() + list.append(item) + + def hideNonEmptyItem(item): + if not item.isEmpty(): + item.hide() + + if self.hasLOD(): + for lodName in self.getLODNames(): + animal = style.getAnimal() + if animal != 'dog': + muzzle = self.find('**/' + lodName + '/**/muzzle*neutral') + else: + muzzle = self.find('**/' + lodName + '/**/muzzle*') + if lodName == '1000' or lodName == '500': + filePrefix = DogMuzzleDict[style.head] + muzzles = loader.loadModel('phase_3' + filePrefix + lodName) + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/' + lodName + '/**/__Actor_head/def_head').isEmpty(): + muzzles.reparentTo(self.find('**/' + lodName + '/**/__Actor_head/def_head')) + else: + muzzles.reparentTo(self.find('**/' + lodName + '/**/joint_toHead')) + elif self.find('**/' + lodName + '/**/joint_toHead'): + muzzles.reparentTo(self.find('**/' + lodName + '/**/joint_toHead')) + surpriseMuzzle = self.find('**/' + lodName + '/**/muzzle*surprise') + angryMuzzle = self.find('**/' + lodName + '/**/muzzle*angry') + sadMuzzle = self.find('**/' + lodName + '/**/muzzle*sad') + smileMuzzle = self.find('**/' + lodName + '/**/muzzle*smile') + laughMuzzle = self.find('**/' + lodName + '/**/muzzle*laugh') + self.__muzzles.append(muzzle) + hideAddNonEmptyItemToList(surpriseMuzzle, self.__surpriseMuzzles) + hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles) + hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles) + hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles) + hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles) + + else: + if style.getAnimal() != 'dog': + muzzle = self.find('**/muzzle*neutral') + else: + muzzle = self.find('**/muzzle*') + filePrefix = DogMuzzleDict[style.head] + muzzles = loader.loadModel('phase_3' + filePrefix + '1000') + if base.config.GetBool('want-new-anims', 1): + if not self.find('**/def_head').isEmpty(): + muzzles.reparentTo(self.find('**/def_head')) + else: + muzzles.reparentTo(self.find('**/joint_toHead')) + else: + muzzles.reparentTo(self.find('**/joint_toHead')) + surpriseMuzzle = self.find('**/muzzle*surprise') + angryMuzzle = self.find('**/muzzle*angry') + sadMuzzle = self.find('**/muzzle*sad') + smileMuzzle = self.find('**/muzzle*smile') + laughMuzzle = self.find('**/muzzle*laugh') + self.__muzzles.append(muzzle) + hideAddNonEmptyItemToList(surpriseMuzzle, self.__surpriseMuzzles) + hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles) + hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles) + hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles) + hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles) + + def getMuzzles(self): + return self.__muzzles + + def getSurpriseMuzzles(self): + return self.__surpriseMuzzles + + def getAngryMuzzles(self): + return self.__angryMuzzles + + def getSadMuzzles(self): + return self.__sadMuzzles + + def getSmileMuzzles(self): + return self.__smileMuzzles + + def getLaughMuzzles(self): + return self.__laughMuzzles + + def showNormalMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__muzzles)): + self.__muzzles[muzzleNum].show() + + def hideNormalMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__muzzles)): + self.__muzzles[muzzleNum].hide() + + def showAngryMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__angryMuzzles)): + self.__angryMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideAngryMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__angryMuzzles)): + self.__angryMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showSadMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__sadMuzzles)): + self.__sadMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideSadMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__sadMuzzles)): + self.__sadMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showSmileMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__smileMuzzles)): + self.__smileMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideSmileMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__smileMuzzles)): + self.__smileMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showLaughMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__laughMuzzles)): + self.__laughMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideLaughMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__laughMuzzles)): + self.__laughMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def showSurpriseMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__surpriseMuzzles)): + self.__surpriseMuzzles[muzzleNum].show() + self.__muzzles[muzzleNum].hide() + + def hideSurpriseMuzzle(self): + if self.isIgnoreCheesyEffect(): + return + for muzzleNum in range(len(self.__surpriseMuzzles)): + self.__surpriseMuzzles[muzzleNum].hide() + self.__muzzles[muzzleNum].show() + + def isIgnoreCheesyEffect(self): + if hasattr(self, 'savedCheesyEffect'): + if self.savedCheesyEffect == 10 or self.savedCheesyEffect == 11 or self.savedCheesyEffect == 12 or self.savedCheesyEffect == 13 or self.savedCheesyEffect == 14: + return True + return False diff --git a/toontown/toon/ToonHeadFrame.py b/toontown/toon/ToonHeadFrame.py new file mode 100644 index 0000000..9cd83e1 --- /dev/null +++ b/toontown/toon/ToonHeadFrame.py @@ -0,0 +1,60 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import ToonHead +from toontown.distributed import DelayDelete +from toontown.toonbase import ToontownGlobals + +class ToonHeadFrame(DirectFrame): + + def __init__(self, av, color = ToontownGlobals.GlobalDialogColor, g = DGG.getDefaultDialogGeom()): + DirectFrame.__init__(self, relief=None, geom=g, geom_color=color, geom_scale=(1, 1, 0.5), pos=(0, 0, 0)) + self.initialiseoptions(ToonHeadFrame) + self.av = av + self.avKeep = DelayDelete.DelayDelete(av, 'ToonHeadFrame.avKeep') + self.head = self.stateNodePath[0].attachNewNode('head', 20) + self.head.setPosHprScale(-0.27, 10.0, -0.09, 180.0, 0.0, 0.0, 0.2, 0.2, 0.2) + self.headModel = ToonHead.ToonHead() + self.headModel.startBlink() + self.headModel.setupHead(self.av.style, forGui=1) + self.headModel.reparentTo(self.head) + self.tag1Node = NametagFloat2d() + self.tag1Node.setContents(Nametag.CSpeech | Nametag.CThought) + self.av.nametag.addNametag(self.tag1Node) + self.tag1 = self.attachNewNode(self.tag1Node.upcastToPandaNode()) + self.tag1.setPosHprScale(-0.16, 0, -0.09, 0, 0, 0, 0.055, 0.055, 0.055) + self.tag2Node = NametagFloat2d() + self.tag2Node.setContents(Nametag.CName) + self.av.nametag.addNametag(self.tag2Node) + self.tag2 = self.attachNewNode(self.tag2Node.upcastToPandaNode()) + self.tag2.setPosHprScale(-0.27, 10.0, 0.16, 0, 0, 0, 0.05, 0.05, 0.05) + self.extraData = DirectLabel(parent=self, relief=None, pos=(0.0, 0.0, 0.06), scale=1.0, text='', text0_fg=(0.3, 0.2, 1, 1), text_scale=(0.14, 0.06), text_pos=(0, -0.01)) + self.extraData.hide() + return + + def destroy(self): + DirectFrame.destroy(self) + self.headModel.delete() + del self.headModel + self.head.removeNode() + del self.head + if not self.av.isEmpty(): + self.av.nametag.removeNametag(self.tag1Node) + self.av.nametag.removeNametag(self.tag2Node) + self.tag1.removeNode() + self.tag2.removeNode() + del self.tag1 + del self.tag2 + del self.tag1Node + del self.tag2Node + del self.av + if self.avKeep: + self.avKeep.destroy() + del self.avKeep + self.extraData.removeNode() + del self.extraData + + def removeAvKeep(self): + if hasattr(self, 'avKeep') and self.avKeep: + self.avKeep.destroy() + self.avKeep = None + return diff --git a/toontown/toon/ToonTeleportPanel.py b/toontown/toon/ToonTeleportPanel.py new file mode 100644 index 0000000..067feee --- /dev/null +++ b/toontown/toon/ToonTeleportPanel.py @@ -0,0 +1,299 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from direct.showbase import DirectObject +from direct.fsm import ClassicFSM, State +from direct.fsm import State +from direct.directnotify import DirectNotifyGlobal +from otp.avatar.Avatar import teleportNotify +import ToonAvatarDetailPanel +from toontown.toonbase import TTLocalizer +from toontown.hood import ZoneUtil +globalTeleport = None + +def showTeleportPanel(avId, avName, avDisableName): + global globalTeleport + if globalTeleport != None: + globalTeleport.cleanup() + globalTeleport = None + globalTeleport = ToonTeleportPanel(avId, avName, avDisableName) + return + + +def hideTeleportPanel(): + global globalTeleport + if globalTeleport != None: + globalTeleport.cleanup() + globalTeleport = None + return + + +def unloadTeleportPanel(): + global globalTeleport + if globalTeleport != None: + globalTeleport.cleanup() + globalTeleport = None + return + + +class ToonTeleportPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonTeleportPanel') + + def __init__(self, avId, avName, avDisableName): + DirectFrame.__init__(self, pos=(0.3, 0.1, 0.65), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.0, 1.0, 0.6), text='', text_wordwrap=13.5, text_scale=0.06, text_pos=(0.0, 0.18)) + messenger.send('releaseDirector') + self['image'] = DGG.getDefaultDialogGeom() + self.avId = avId + self.avName = avName + self.avDisableName = avDisableName + self.fsm = ClassicFSM.ClassicFSM('ToonTeleportPanel', [ + State.State('off', + self.enterOff, + self.exitOff), + State.State('begin', + self.enterBegin, + self.exitBegin), + State.State('checkAvailability', + self.enterCheckAvailability, + self.exitCheckAvailability), + State.State('notAvailable', + self.enterNotAvailable, + self.exitNotAvailable), + State.State('ignored', + self.enterIgnored, + self.exitIgnored), + State.State('notOnline', + self.enterNotOnline, + self.exitNotOnline), + State.State('wentAway', + self.enterWentAway, + self.exitWentAway), + State.State('self', + self.enterSelf, + self.exitSelf), + State.State('unknownHood', + self.enterUnknownHood, + self.exitUnknownHood), + State.State('unavailableHood', + self.enterUnavailableHood, + self.exitUnavailableHood), + State.State('otherShard', + self.enterOtherShard, + self.exitOtherShard), + State.State('teleport', + self.enterTeleport, + self.exitTeleport)], + 'off', 'off') + from toontown.friends import FriendInviter + FriendInviter.hideFriendInviter() + ToonAvatarDetailPanel.hideAvatarDetail() + buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') + self.bOk = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelOK, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.__handleOk) + self.bOk.hide() + self.bCancel = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelCancel, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.0, 0.0, -0.1), command=self.__handleCancel) + self.bCancel.hide() + self.bYes = DirectButton(self, image=(buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelYes, text_scale=0.05, text_pos=(0.0, -0.1), pos=(-0.15, 0.0, -0.15), command=self.__handleYes) + self.bYes.hide() + self.bNo = DirectButton(self, image=(buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')), relief=None, text=TTLocalizer.TeleportPanelNo, text_scale=0.05, text_pos=(0.0, -0.1), pos=(0.15, 0.0, -0.15), command=self.__handleNo) + self.bNo.hide() + buttons.removeNode() + self.accept(self.avDisableName, self.__handleDisableAvatar) + self.show() + self.fsm.enterInitialState() + self.fsm.request('begin') + return + + def cleanup(self): + self.fsm.request('off') + del self.fsm + self.ignore(self.avDisableName) + self.destroy() + + def enterOff(self): + pass + + def exitOff(self): + pass + + def enterBegin(self): + myId = base.localAvatar.doId + hasManager = hasattr(base.cr, 'playerFriendsManager') + if self.avId == myId: + self.fsm.request('self') + elif base.cr.doId2do.has_key(self.avId): + self.fsm.request('checkAvailability') + elif base.cr.isFriend(self.avId): + if base.cr.isFriendOnline(self.avId): + self.fsm.request('checkAvailability') + else: + self.fsm.request('notOnline') + elif hasManager and base.cr.playerFriendsManager.getAvHandleFromId(self.avId): + id = base.cr.playerFriendsManager.findPlayerIdFromAvId(self.avId) + info = base.cr.playerFriendsManager.getFriendInfo(id) + if info: + if info.onlineYesNo: + self.fsm.request('checkAvailability') + else: + self.fsm.request('notOnline') + else: + self.fsm.request('wentAway') + else: + self.fsm.request('wentAway') + + def exitBegin(self): + pass + + def enterCheckAvailability(self): + myId = base.localAvatar.getDoId() + base.localAvatar.d_teleportQuery(myId, sendToId=self.avId) + self['text'] = TTLocalizer.TeleportPanelCheckAvailability % self.avName + self.accept('teleportResponse', self.__teleportResponse) + self.bCancel.show() + + def exitCheckAvailability(self): + self.ignore('teleportResponse') + self.bCancel.hide() + + def enterNotAvailable(self): + self['text'] = TTLocalizer.TeleportPanelNotAvailable % self.avName + self.bOk.show() + + def exitNotAvailable(self): + self.bOk.hide() + + def enterIgnored(self): + self['text'] = TTLocalizer.TeleportPanelNotAvailable % self.avName + self.bOk.show() + + def exitIgnored(self): + self.bOk.hide() + + def enterNotOnline(self): + self['text'] = TTLocalizer.TeleportPanelNotOnline % self.avName + self.bOk.show() + + def exitNotOnline(self): + self.bOk.hide() + + def enterWentAway(self): + self['text'] = TTLocalizer.TeleportPanelWentAway % self.avName + self.bOk.show() + + def exitWentAway(self): + self.bOk.hide() + + def enterUnknownHood(self, hoodId): + self['text'] = TTLocalizer.TeleportPanelUnknownHood % base.cr.hoodMgr.getFullnameFromId(hoodId) + self.bOk.show() + + def exitUnknownHood(self): + self.bOk.hide() + + def enterUnavailableHood(self, hoodId): + self['text'] = TTLocalizer.TeleportPanelUnavailableHood % base.cr.hoodMgr.getFullnameFromId(hoodId) + self.bOk.show() + + def exitUnavailableHood(self): + self.bOk.hide() + + def enterSelf(self): + self['text'] = TTLocalizer.TeleportPanelDenySelf + self.bOk.show() + + def exitSelf(self): + self.bOk.hide() + + def enterOtherShard(self, shardId, hoodId, zoneId): + shardName = base.cr.getShardName(shardId) + if shardName is None: + self.fsm.request('notAvailable') + return + myShardName = base.cr.getShardName(base.localAvatar.defaultShard) + pop = None + for shard in base.cr.listActiveShards(): + if shard[0] == shardId: + pop = shard[2] + + if pop and pop > localAvatar.shardPage.midPop: + self.notify.warning('Entering full shard: issuing performance warning') + self['text'] = TTLocalizer.TeleportPanelBusyShard % {'avName': self.avName} + else: + self['text'] = TTLocalizer.TeleportPanelOtherShard % {'avName': self.avName, + 'shardName': shardName, + 'myShardName': myShardName} + self.bYes.show() + self.bNo.show() + self.shardId = shardId + self.hoodId = hoodId + self.zoneId = zoneId + return + + def exitOtherShard(self): + self.bYes.hide() + self.bNo.hide() + + def enterTeleport(self, shardId, hoodId, zoneId): + teleportNotify.debug('enterTeleport%s' % ((shardId, hoodId, zoneId),)) + hoodsVisited = base.localAvatar.hoodsVisited + canonicalHoodId = ZoneUtil.getCanonicalZoneId(hoodId) + if hoodId == ToontownGlobals.MyEstate: + teleportNotify.debug('enterTeleport: estate') + if shardId == base.localAvatar.defaultShard: + shardId = None + place = base.cr.playGame.getPlace() + place.requestTeleport(hoodId, zoneId, shardId, self.avId) + unloadTeleportPanel() + elif canonicalHoodId not in hoodsVisited + ToontownGlobals.HoodsAlwaysVisited: + teleportNotify.debug('enterTeleport: unknownHood') + self.fsm.request('unknownHood', [hoodId]) + elif canonicalHoodId not in base.cr.hoodMgr.getAvailableZones(): + print 'hoodId %d not ready' % hoodId + self.fsm.request('unavailableHood', [hoodId]) + else: + if shardId == base.localAvatar.defaultShard: + shardId = None + teleportNotify.debug('enterTeleport: requesting teleport') + place = base.cr.playGame.getPlace() + place.requestTeleport(hoodId, zoneId, shardId, self.avId) + unloadTeleportPanel() + return + + def exitTeleport(self): + pass + + def __handleOk(self): + unloadTeleportPanel() + + def __handleCancel(self): + unloadTeleportPanel() + + def __handleYes(self): + self.fsm.request('teleport', [self.shardId, self.hoodId, self.zoneId]) + + def __handleNo(self): + unloadTeleportPanel() + + def __teleportResponse(self, avId, available, shardId, hoodId, zoneId): + teleportNotify.debug('__teleportResponse%s' % ((avId, + available, + shardId, + hoodId, + zoneId),)) + if avId != self.avId: + return + if available == 0: + teleportNotify.debug('__teleportResponse: not available') + self.fsm.request('notAvailable') + elif available == 2: + teleportNotify.debug('__teleportResponse: ignored') + self.fsm.request('ignored') + elif shardId != base.localAvatar.defaultShard: + teleportNotify.debug('__teleportResponse: otherShard') + self.fsm.request('otherShard', [shardId, hoodId, zoneId]) + else: + teleportNotify.debug('__teleportResponse: teleport') + self.fsm.request('teleport', [shardId, hoodId, zoneId]) + + def __handleDisableAvatar(self): + self.fsm.request('wentAway') diff --git a/toontown/toon/__init__.py b/toontown/toon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/toonbase/DisplayOptions.py b/toontown/toonbase/DisplayOptions.py new file mode 100644 index 0000000..2f577ab --- /dev/null +++ b/toontown/toonbase/DisplayOptions.py @@ -0,0 +1,237 @@ +import copy +import string +import os +import sys +import datetime +from pandac.PandaModules import loadPrcFileData, Settings, WindowProperties +from otp.otpgui import OTPDialog +from otp.otpbase import OTPGlobals +from otp.otpbase import OTPRender +from direct.directnotify import DirectNotifyGlobal +try: + import embedded +except: + pass + +class DisplayOptions: + notify = DirectNotifyGlobal.directNotify.newCategory('DisplayOptions') + + def __init__(self): + self.restore_failed = False + self.loadFromSettings() + + def loadFromSettings(self): + Settings.readSettings() + mode = not Settings.getWindowedMode() + music = Settings.getMusic() + sfx = Settings.getSfx() + toonChatSounds = Settings.getToonChatSounds() + musicVol = Settings.getMusicVolume() + sfxVol = Settings.getSfxVolume() + resList = [(640, 480), + (800, 600), + (1024, 768), + (1280, 1024), + (1600, 1200)] + res = resList[Settings.getResolution()] + embed = Settings.getEmbeddedMode() + self.notify.debug('before prc settings embedded mode=%s' % str(embed)) + self.notify.debug('before prc settings full screen mode=%s' % str(mode)) + if mode == None: + mode = 1 + if res == None: + res = (800, 600) + if not Settings.doSavedSettingsExist(): + self.notify.info('loadFromSettings: No settings; isDefaultEmbedded=%s' % self.isDefaultEmbedded()) + embed = self.isDefaultEmbedded() + if embed and not self.isEmbeddedPossible(): + self.notify.warning('Embedded mode is not possible.') + embed = False + if not mode and not self.isWindowedPossible(): + self.notify.warning('Windowed mode is not possible.') + mode = True + loadPrcFileData('toonBase Settings Window Res', 'win-size %s %s' % (res[0], res[1])) + self.notify.debug('settings resolution = %s' % str(res)) + loadPrcFileData('toonBase Settings Window FullScreen', 'fullscreen %s' % mode) + self.notify.debug('settings full screen mode=%s' % str(mode)) + loadPrcFileData('toonBase Settings Music Active', 'audio-music-active %s' % music) + loadPrcFileData('toonBase Settings Sound Active', 'audio-sfx-active %s' % sfx) + loadPrcFileData('toonBase Settings Music Volume', 'audio-master-music-volume %s' % musicVol) + loadPrcFileData('toonBase Settings Sfx Volume', 'audio-master-sfx-volume %s' % sfxVol) + loadPrcFileData('toonBase Settings Toon Chat Sounds', 'toon-chat-sounds %s' % toonChatSounds) + self.settingsFullScreen = mode + self.settingsWidth = res[0] + self.settingsHeight = res[1] + self.settingsEmbedded = embed + self.notify.debug('settings embedded mode=%s' % str(self.settingsEmbedded)) + self.notify.info('settingsFullScreen = %s, embedded = %s width=%d height=%d' % (self.settingsFullScreen, + self.settingsEmbedded, + self.settingsWidth, + self.settingsHeight)) + return + + def restrictToEmbedded(self, restrict, change_display = True): + if base.appRunner is None or base.appRunner.windowProperties is None: + restrict = 0 + self.restrict_to_embedded = choice(restrict, 1, 0) + self.notify.debug('restrict_to_embedded: %s' % self.restrict_to_embedded) + if change_display: + self.set(base.pipe, self.settingsWidth, self.settingsHeight, self.settingsFullScreen, self.settingsEmbedded) + return + + def set(self, pipe, width, height, fullscreen, embedded): + self.notify.debugStateCall(self) + state = False + self.notify.info('SET') + if self.restrict_to_embedded: + fullscreen = 0 + embedded = 1 + if embedded: + if base.appRunner.windowProperties: + width = base.appRunner.windowProperties.getXSize() + height = base.appRunner.windowProperties.getYSize() + self.current_pipe = base.pipe + self.current_properties = WindowProperties(base.win.getProperties()) + properties = self.current_properties + self.notify.debug('DISPLAY PREVIOUS:') + self.notify.debug(' EMBEDDED: %s' % bool(properties.getParentWindow())) + self.notify.debug(' FULLSCREEN: %s' % bool(properties.getFullscreen())) + self.notify.debug(' X SIZE: %s' % properties.getXSize()) + self.notify.debug(' Y SIZE: %s' % properties.getYSize()) + self.notify.debug('DISPLAY REQUESTED:') + self.notify.debug(' EMBEDDED: %s' % bool(embedded)) + self.notify.debug(' FULLSCREEN: %s' % bool(fullscreen)) + self.notify.debug(' X SIZE: %s' % width) + self.notify.debug(' Y SIZE: %s' % height) + if self.current_pipe == pipe and bool(self.current_properties.getParentWindow()) == bool(embedded) and self.current_properties.getFullscreen() == fullscreen and self.current_properties.getXSize() == width and self.current_properties.getYSize() == height: + self.notify.info('DISPLAY NO CHANGE REQUIRED') + state = True + else: + properties = WindowProperties() + properties.setSize(width, height) + properties.setFullscreen(fullscreen) + properties.setParentWindow(0) + if embedded: + if base.appRunner.windowProperties: + properties = base.appRunner.windowProperties + original_sort = base.win.getSort() + if self.resetWindowProperties(pipe, properties): + self.notify.debug('DISPLAY CHANGE SET') + properties = base.win.getProperties() + self.notify.debug('DISPLAY ACHIEVED:') + self.notify.debug(' EMBEDDED: %s' % bool(properties.getParentWindow())) + self.notify.debug(' FULLSCREEN: %s' % bool(properties.getFullscreen())) + self.notify.debug(' X SIZE: %s' % properties.getXSize()) + self.notify.debug(' Y SIZE: %s' % properties.getYSize()) + if bool(properties.getParentWindow()) == bool(embedded) and properties.getFullscreen() == fullscreen and properties.getXSize() == width and properties.getYSize() == height: + self.notify.info('DISPLAY CHANGE VERIFIED') + state = True + else: + self.notify.warning('DISPLAY CHANGE FAILED, RESTORING PREVIOUS DISPLAY') + self.restoreWindowProperties() + else: + self.notify.warning('DISPLAY CHANGE FAILED') + self.notify.warning('DISPLAY SET - BEFORE RESTORE') + self.restoreWindowProperties() + self.notify.warning('DISPLAY SET - AFTER RESTORE') + base.win.setSort(original_sort) + base.graphicsEngine.renderFrame() + base.graphicsEngine.renderFrame() + return state + + def resetWindowProperties(self, pipe, properties): + if base.win: + currentProperties = WindowProperties(base.win.getProperties()) + gsg = base.win.getGsg() + else: + currentProperties = WindowProperties.getDefault() + gsg = None + newProperties = WindowProperties(currentProperties) + newProperties.addProperties(properties) + if base.pipe != pipe: + gsg = None + if gsg == None or currentProperties.getFullscreen() != newProperties.getFullscreen() or currentProperties.getParentWindow() != newProperties.getParentWindow(): + self.notify.debug('window properties: %s' % properties) + self.notify.debug('gsg: %s' % gsg) + base.pipe = pipe + if not base.openMainWindow(props=properties, gsg=gsg, keepCamera=True): + self.notify.warning('OPEN MAIN WINDOW FAILED') + return 0 + self.notify.info('OPEN MAIN WINDOW PASSED') + base.graphicsEngine.openWindows() + if base.win.isClosed(): + self.notify.warning('Window did not open, removing.') + base.closeWindow(base.win) + return 0 + base.disableShowbaseMouse() + if 'libotp' in sys.modules: + from libotp import NametagGlobals + NametagGlobals.setCamera(base.cam) + NametagGlobals.setMouseWatcher(base.mouseWatcherNode) + else: + self.notify.debug('Adjusting properties') + base.win.requestProperties(properties) + base.graphicsEngine.renderFrame() + return 1 + + def restoreWindowProperties(self): + if self.resetWindowProperties(self.current_pipe, self.current_properties): + self.restore_failed = False + else: + self.notify.warning("Couldn't restore original display settings!") + if base.appRunner and base.appRunner.windowProperties: + fullscreen = 0 + embedded = 1 + tryProps = base.appRunner.windowProperties + if self.resetWindowProperties(self.current_pipe, tryProps): + self.current_properties = copy.copy(tryProps) + self.restore_failed = False + return + if self.current_properties.getFullscreen(): + fullscreen = 0 + embedded = 0 + tryProps = self.current_properties + tryProps.setFullscreen(0) + if self.resetWindowProperties(self.current_pipe, tryProps): + self.current_properties = copy.copy(tryProps) + self.restore_failed = False + return + self.notify.error('Failed opening regular window!') + base.panda3dRenderError() + self.restore_failed = True + + @staticmethod + def isDefaultEmbedded(): + result = False + try: + embedOption = int(base.launcher.getValue('GAME_DEFAULT_TO_EMBEDDED', None)) + if embedOption != None: + result = bool(int(embedOption)) + except: + pass + + return result + + @staticmethod + def isEmbeddedPossible(): + result = False + try: + showOption = base.launcher.getValue('GAME_SHOW_EMBEDDED_OPTION', None) + if showOption != None: + result = bool(int(showOption)) + except: + pass + + return result + + @staticmethod + def isWindowedPossible(): + result = True + try: + showOption = base.launcher.getValue('GAME_SHOW_WINDOWED_OPTION', None) + if showOption != None: + result = bool(int(showOption)) + except: + pass + + return result diff --git a/toontown/toonbase/TTLocalizer.py b/toontown/toonbase/TTLocalizer.py new file mode 100644 index 0000000..fb0ed77 --- /dev/null +++ b/toontown/toonbase/TTLocalizer.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 'TTLocalizer: Running in language: %s' % language +if language == 'english': + _languageModule = 'toontown.toonbase.TTLocalizer' + string.capitalize(language) +else: + checkLanguage = 1 + _languageModule = 'toontown.toonbase.TTLocalizer_' + language +print 'from ' + _languageModule + ' import *' +from toontown.toonbase.TTLocalizerEnglish import * +if checkLanguage: + l = {} + g = {} + englishModule = __import__('toontown.toonbase.TTLocalizerEnglish', 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/toontown/toonbase/TTLocalizerEnglish.py b/toontown/toonbase/TTLocalizerEnglish.py new file mode 100644 index 0000000..7d44b3f --- /dev/null +++ b/toontown/toonbase/TTLocalizerEnglish.py @@ -0,0 +1,9760 @@ +from toontown.toonbase.TTLocalizerEnglishProperty import * +from toontown.catalog import CatalogAccessoryItemGlobals +from otp.otpbase import OTPLocalizer as OL +OL.SpeedChatStaticText = OL.SpeedChatStaticTextToontown.copy() +for key in OL.SpeedChatStaticTextCommon.iterkeys(): + OL.SpeedChatStaticText[key] = OL.SpeedChatStaticTextCommon[key] + +commitmantst = 'kptmptest - removable' +InterfaceFont = 'phase_3/models/fonts/ImpressBT.ttf' +ToonFont = 'phase_3/models/fonts/ImpressBT.ttf' +SuitFont = 'phase_3/models/fonts/vtRemingtonPortable.ttf' +SignFont = 'phase_3/models/fonts/MickeyFont' +MinnieFont = 'phase_3/models/fonts/MinnieFont' +FancyFont = 'phase_3/models/fonts/Comedy' +NametagFonts = ('phase_3/models/fonts/AnimGothic', + 'phase_3/models/fonts/Aftershock', + 'phase_3/models/fonts/JiggeryPokery', + 'phase_3/models/fonts/Ironwork', + 'phase_3/models/fonts/HastyPudding', + 'phase_3/models/fonts/Comedy', + 'phase_3/models/fonts/Humanist', + 'phase_3/models/fonts/Portago', + 'phase_3/models/fonts/Musicals', + 'phase_3/models/fonts/Scurlock', + 'phase_3/models/fonts/Danger', + 'phase_3/models/fonts/Alie', + 'phase_3/models/fonts/OysterBar', + 'phase_3/models/fonts/RedDogSaloon') +NametagFontNames = ('Member', + 'Shivering', + 'Wonky', + 'Fancy', + 'Silly', + 'Zany', + 'Practical', + 'Nautical', + 'Whimsical', + 'Spooky', + 'Action', + 'Poetic', + 'Boardwalk', + 'Western') +NametagLabel = ' Nametag' +UnpaidNameTag = 'Basic' +GM_NAMES = ('TOON COUNCIL', + 'TOON TROOPER', + 'RESISTANCE RANGER', + 'GC') +BuildingNametagFont = 'phase_3/models/fonts/MickeyFont' +BuildingNametagShadow = None +ProductPrefix = 'TT' +Mickey = 'Mickey' +VampireMickey = 'VampireMickey' +Minnie = 'Minnie' +WitchMinnie = 'WitchMinnie' +Donald = 'Donald' +DonaldDock = 'DonaldDock' +FrankenDonald = 'FrankenDonald' +Daisy = 'Daisy' +SockHopDaisy = 'SockHopDaisy' +Goofy = 'Goofy' +SuperGoofy = 'SuperGoofy' +Pluto = 'Pluto' +WesternPluto = 'WesternPluto' +Flippy = 'Flippy' +Chip = 'Chip' +Dale = 'Dale' +JailbirdDale = 'JailbirdDale' +PoliceChip = 'PoliceChip' +lTheBrrrgh = 'The Brrrgh' +lDaisyGardens = 'Daisy Gardens' +lDonaldsDock = "Donald's Dock" +lDonaldsDreamland = "Donald's Dreamland" +lMinniesMelodyland = "Minnie's Melodyland" +lToontownCentral = 'Toontown Central' +lToonHQ = 'Toon HQ' +lSellbotHQ = 'Sellbot HQ' +lGoofySpeedway = 'Goofy Speedway' +lOutdoorZone = "Chip 'n Dale's Acorn Acres" +lGolfZone = "Chip 'n Dale's MiniGolf" +lPartyHood = 'Party Grounds' +GlobalStreetNames = {20000: ('to', 'on', 'Tutorial Terrace'), + 1000: ('to the', 'in the', 'Playground'), + 1100: ('to', 'on', 'Barnacle Boulevard'), + 1200: ('to', 'on', 'Seaweed Street'), + 1300: ('to', 'on', 'Lighthouse Lane'), + 2000: ('to the', 'in the', 'Playground'), + 2100: ('to', 'on', 'Silly Street'), + 2200: ('to', 'on', 'Loopy Lane'), + 2300: ('to', 'on', 'Punchline Place'), + 3000: ('to the', 'in the', 'Playground'), + 3100: ('to', 'on', 'Walrus Way'), + 3200: ('to', 'on', 'Sleet Street'), + 3300: ('to', 'on', 'Polar Place'), + 4000: ('to the', 'in the', 'Playground'), + 4100: ('to', 'on', 'Alto Avenue'), + 4200: ('to', 'on', 'Baritone Boulevard'), + 4300: ('to', 'on', 'Tenor Terrace'), + 5000: ('to the', 'in the', 'Playground'), + 5100: ('to', 'on', 'Elm Street'), + 5200: ('to', 'on', 'Maple Street'), + 5300: ('to', 'on', 'Oak Street'), + 9000: ('to the', 'in the', 'Playground'), + 9100: ('to', 'on', 'Lullaby Lane'), + 9200: ('to', 'on', 'Pajama Place'), + 10000: ('to', 'in', 'Bossbot HQ Country Club'), + 10100: ('to the', 'in the', 'Bossbot HQ Lobby'), + 10200: ('to the', 'in the', 'The Clubhouse'), + 10500: ('to the', 'in the', 'The Front Three'), + 10600: ('to the', 'in the', 'The Middle Six'), + 10700: ('to the', 'in the', 'The Back Nine'), + 11000: ('to the', 'in the', 'Sellbot HQ Courtyard'), + 11100: ('to the', 'in the', 'Sellbot HQ Lobby'), + 11200: ('to the', 'in the', 'Sellbot Factory'), + 11500: ('to the', 'in the', 'Sellbot Factory'), + 12000: ('to', 'in', 'Cashbot Train Yard'), + 12100: ('to the', 'in the', 'Cashbot HQ Lobby'), + 12500: ('to the', 'in the', 'Cashbot Coin Mint'), + 12600: ('to the', 'in the', 'Cashbot Dollar Mint'), + 12700: ('to the', 'in the', 'Cashbot Bullion Mint'), + 13000: ('to', 'in', 'Lawbot HQ Courtyard'), + 13100: ('to the', 'in the', 'Courthouse Lobby'), + 13200: ('to the', 'in the', "DA's Office Lobby"), + 13300: ('to the', 'in the', 'Lawbot A Office'), + 13400: ('to the', 'in the', 'Lawbot B Office'), + 13500: ('to the', 'in the', 'Lawbot C Office'), + 13600: ('to the', 'in the', 'Lawbot D Office')} +DonaldsDock = ('to', 'in', lDonaldsDock) +ToontownCentral = ('to', 'in', lToontownCentral) +TheBrrrgh = ('to', 'in', lTheBrrrgh) +MinniesMelodyland = ('to', 'in', lMinniesMelodyland) +DaisyGardens = ('to', 'in', lDaisyGardens) +OutdoorZone = ('to', 'in', lOutdoorZone) +FunnyFarm = ('to the', 'in the', 'Funny Farm') +GoofySpeedway = ('to', 'in', lGoofySpeedway) +DonaldsDreamland = ('to', 'in', lDonaldsDreamland) +BossbotHQ = ('to', 'in', 'Bossbot HQ') +SellbotHQ = ('to', 'in', 'Sellbot HQ') +CashbotHQ = ('to', 'in', 'Cashbot HQ') +LawbotHQ = ('to', 'in', 'Lawbot HQ') +Tutorial = ('to the', 'in the', 'Toon-torial') +MyEstate = ('to', 'in', 'your house') +WelcomeValley = ('to', 'in', 'Welcome Valley') +GolfZone = ('to', 'in', lGolfZone) +PartyHood = ('to the', 'in the', lPartyHood) +Factory = 'Factory' +Headquarters = 'Headquarters' +SellbotFrontEntrance = 'Front Entrance' +SellbotSideEntrance = 'Side Entrance' +Office = 'Office' +FactoryNames = {0: 'Factory Mockup', + 11500: 'Sellbot Cog Factory', + 13300: 'Lawbot Cog Office'} +FactoryTypeLeg = 'Leg' +FactoryTypeArm = 'Arm' +FactoryTypeTorso = 'Torso' +MintFloorTitle = 'Floor %s' +lCancel = 'Cancel' +lClose = 'Close' +lOK = 'OK' +lNext = 'Next' +lQuit = 'Quit' +lYes = 'Yes' +lNo = 'No' +sleep_auto_reply = '%s is sleeping right now' +lHQOfficerF = 'HQ Officer' +lHQOfficerM = 'HQ Officer' +MickeyMouse = 'Mickey Mouse' +AIStartDefaultDistrict = 'Sillyville' +Cog = 'Cog' +Cogs = 'Cogs' +ACog = 'a Cog' +TheCogs = 'The Cogs' +ASkeleton = 'a Skelecog' +Skeleton = 'Skelecog' +SkeletonP = 'Skelecogs' +Av2Cog = 'a Version 2.0 Cog' +v2Cog = 'Version 2.0 Cog' +v2CogP = 'Version 2.0 Cogs' +ASkeleton = 'a Skelecog' +Foreman = 'Factory Foreman' +ForemanP = 'Factory Foremen' +AForeman = 'a Factory Foreman' +CogVP = Cog + ' V.P.' +CogVPs = "Cog V.P.'s" +ACogVP = ACog + ' V.P.' +Supervisor = 'Mint Supervisor' +SupervisorP = 'Mint Supervisors' +ASupervisor = 'a Mint Supervisor' +CogCFO = Cog + ' C.F.O.' +CogCFOs = "Cog C.F.O.'s" +ACogCFO = ACog + ' C.F.O.' +TheFish = 'the Fish' +AFish = 'a fish' +Level = 'Level' +QuestsCompleteString = 'Complete' +QuestsNotChosenString = 'Not chosen' +Period = '.' +Laff = 'Laff' +QuestInLocationString = ' %(inPhrase)s %(location)s' +QuestsDefaultGreeting = ('Hello, _avName_!', + 'Hi, _avName_!', + 'Hey there, _avName_!', + 'Say there, _avName_!', + 'Welcome, _avName_!', + 'Howdy, _avName_!', + 'How are you, _avName_?', + 'Greetings _avName_!') +QuestsDefaultIncomplete = ("How's that task coming, _avName_?", + 'Looks like you still have more work to do on that task!', + 'Keep up the good work, _avName_!', + 'Keep trying to finish that task. I know you can do it!', + 'Keep trying to complete that task, we are counting on you!', + 'Keep working on that ToonTask!') +QuestsDefaultIncompleteProgress = ('You came to the right place, but you need to finish your ToonTask first.', 'When you are finished with that ToonTask, come back here.', 'Come back when you are finished with your ToonTask.') +QuestsDefaultIncompleteWrongNPC = ('Nice work on that ToonTask. You should go visit _toNpcName_._where_', 'Looks like you are ready to finish your ToonTask. Go see _toNpcName_._where_.', 'Go see _toNpcName_ to finish your ToonTask._where_') +QuestsDefaultComplete = ('Nice work! Here is your reward...', 'Great job, _avName_! Take this reward...', 'Wonderful job, _avName_! Here is your reward...') +QuestsDefaultLeaving = ('Bye!', + 'Goodbye!', + 'So long, _avName_.', + 'See ya, _avName_!', + 'Good luck!', + 'Have fun in Toontown!', + 'See you later!') +QuestsDefaultReject = ('Hello.', + 'Can I help you?', + 'How are you?', + 'Hello there.', + "I'm a little busy now, _avName_.", + 'Yes?', + 'Howdy, _avName_!', + 'Welcome, _avName_!', + "Hey, _avName_! How's it going?", + 'Did you know you can open your Shticker Book by hitting F8?', + 'You can use your map to teleport back to the playground!', + 'You can make friends with other players by clicking on them.', + 'You can discover more about a ' + Cog + ' by clicking on him.', + 'Gather treasures in the playgrounds to fill your Laff meter.', + Cog + ' buildings are dangerous places! Do not go in alone!', + 'When you lose a battle, the ' + Cogs + ' take all your gags.', + 'To get more gags, play Trolley games!', + 'You can get more Laff points by completing ToonTasks.', + 'Every ToonTask gives you a reward.', + 'Some rewards let you carry more gags.', + 'If you win a battle, you get ToonTask credit for every ' + Cog + ' defeated.', + 'If you recapture a ' + Cog + ' building, go back inside to see a special thank-you from its owner!', + 'If you press the Page Up key, you can look up!', + 'If you press the Tab key, you can see different views of your surroundings!', + "To show True Friends what you're thinking, enter a '.' before your thought.", + 'If a ' + Cog + ' is stunned, it is more difficult for them to avoid falling objects.', + 'Each kind of ' + Cog + ' building has a distinct look.', + 'Defeating ' + Cogs + ' on the higher floors of a building will give you greater skill rewards.') +QuestsDefaultTierNotDone = ('Hello, _avName_! You must finish your current ToonTasks before getting a new one.', 'Hi there! You need to finish the ToonTasks you are working on in order to get a new one.', 'Hi, _avName_! Before I can give you a new ToonTask, you need to finish the ones you have.') +QuestsDefaultQuest = None +QuestsDefaultVisitQuestDialog = ('I heard _toNpcName_ is looking for you._where_', + 'Stop by and see _toNpcName_ when you get a chance._where_', + 'Pay a visit to _toNpcName_ next time you are over that way._where_', + 'If you get a chance, stop in and say hi to _toNpcName_._where_', + '_toNpcName_ will give you your next ToonTask._where_') +QuestsLocationArticle = '' + +def getLocalNum(num): + return str(num) + + +QuestsItemNameAndNum = '%(num)s %(name)s' +QuestsCogQuestProgress = '%(progress)s of %(numCogs)s defeated' +QuestsCogQuestHeadline = 'WANTED' +QuestsCogQuestSCStringS = 'I need to defeat %(cogName)s%(cogLoc)s.' +QuestsCogQuestSCStringP = 'I need to defeat some %(cogName)s%(cogLoc)s.' +QuestsCogQuestDefeat = 'Defeat %s' +QuestsCogQuestDefeatDesc = '%(numCogs)s %(cogName)s' +QuestsCogNewNewbieQuestObjective = 'Help a new Toon defeat %s' +QuestsCogNewNewbieQuestCaption = 'Help a new Toon %d Laff or less' +QuestsCogOldNewbieQuestObjective = 'Help a Toon with %(laffPoints)d Laff or less defeat %(objective)s' +QuestsCogOldNewbieQuestCaption = 'Help a Toon %d Laff or less' +QuestsCogNewbieQuestAux = 'Defeat:' +QuestsNewbieQuestHeadline = 'APPRENTICE' +QuestsCogTrackQuestProgress = '%(progress)s of %(numCogs)s defeated' +QuestsCogTrackQuestHeadline = 'WANTED' +QuestsCogTrackQuestSCStringS = 'I need to defeat %(cogText)s%(cogLoc)s.' +QuestsCogTrackQuestSCStringP = 'I need to defeat some %(cogText)s%(cogLoc)s.' +QuestsCogTrackQuestDefeat = 'Defeat %s' +QuestsCogTrackDefeatDesc = '%(numCogs)s %(trackName)s' +QuestsCogLevelQuestProgress = '%(progress)s of %(numCogs)s defeated' +QuestsCogLevelQuestHeadline = 'WANTED' +QuestsCogLevelQuestDefeat = 'Defeat %s' +QuestsCogLevelQuestDesc = 'a Level %(level)s+ %(name)s' +QuestsCogLevelQuestDescC = '%(count)s Level %(level)s+ %(name)s' +QuestsCogLevelQuestDescI = 'some Level %(level)s+ %(name)s' +QuestsCogLevelQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsBuildingQuestFloorNumbers = ('', + 'two+', + 'three+', + 'four+', + 'five+') +QuestsBuildingQuestBuilding = 'Building' +QuestsBuildingQuestBuildings = 'Buildings' +QuestsBuildingQuestHeadline = 'DEFEAT' +QuestsBuildingQuestProgressString = '%(progress)s of %(num)s defeated' +QuestsBuildingQuestString = 'Defeat %s' +QuestsBuildingQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsBuildingQuestDesc = 'a %(type)s Building' +QuestsBuildingQuestDescF = 'a %(floors)s story %(type)s Building' +QuestsBuildingQuestDescC = '%(count)s %(type)s Buildings' +QuestsBuildingQuestDescCF = '%(count)s %(floors)s story %(type)s Buildings' +QuestsBuildingQuestDescI = 'some %(type)s Buildings' +QuestsBuildingQuestDescIF = 'some %(floors)s story %(type)s Buildings' +QuestFactoryQuestFactory = 'Factory' +QuestsFactoryQuestFactories = 'Factories' +QuestsFactoryQuestHeadline = 'DEFEAT' +QuestsFactoryQuestProgressString = '%(progress)s of %(num)s defeated' +QuestsFactoryQuestString = 'Defeat %s' +QuestsFactoryQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsFactoryQuestDesc = 'a %(type)s Factory' +QuestsFactoryQuestDescC = '%(count)s %(type)s Factories' +QuestsFactoryQuestDescI = 'some %(type)s Factories' +QuestMintQuestMint = 'Mint' +QuestsMintQuestMints = 'Mints' +QuestsMintQuestHeadline = 'DEFEAT' +QuestsMintQuestProgressString = '%(progress)s of %(num)s defeated' +QuestsMintQuestString = 'Defeat %s' +QuestsMintQuestSCString = 'I need to defeat %(objective)s%(location)s.' +QuestsMintQuestDesc = 'a Cog Mint' +QuestsMintQuestDescC = '%(count)s Cog Mints' +QuestsMintQuestDescI = 'some Cog Mints' +QuestsRescueQuestProgress = '%(progress)s of %(numToons)s rescued' +QuestsRescueQuestHeadline = 'RESCUE' +QuestsRescueQuestSCStringS = 'I need to rescue a Toon%(toonLoc)s.' +QuestsRescueQuestSCStringP = 'I need to rescue some Toons%(toonLoc)s.' +QuestsRescueQuestRescue = 'Rescue %s' +QuestsRescueQuestRescueDesc = '%(numToons)s Toons' +QuestsRescueQuestToonS = 'a Toon' +QuestsRescueQuestToonP = 'Toons' +QuestsRescueQuestAux = 'Rescue:' +QuestsRescueNewNewbieQuestObjective = 'Help a new Toon rescue %s' +QuestsRescueOldNewbieQuestObjective = 'Help a Toon with %(laffPoints)d Laff or less rescue %(objective)s' +QuestCogPartQuestCogPart = 'Cog Suit Part' +QuestsCogPartQuestFactories = 'Factories' +QuestsCogPartQuestHeadline = 'RETRIEVE' +QuestsCogPartQuestProgressString = '%(progress)s of %(num)s retrieved' +QuestsCogPartQuestString = 'Retrieve %s' +QuestsCogPartQuestSCString = 'I need to retrieve %(objective)s%(location)s.' +QuestsCogPartQuestAux = 'Retrieve:' +QuestsCogPartQuestDesc = 'a Cog Suit Part' +QuestsCogPartQuestDescC = '%(count)s Cog Suit Parts' +QuestsCogPartQuestDescI = 'some Cog Suit Parts' +QuestsCogPartNewNewbieQuestObjective = 'Help a new Toon retrieve %s' +QuestsCogPartOldNewbieQuestObjective = 'Help a Toon with %(laffPoints)d Laff or less retrieve %(objective)s' +QuestsDeliverGagQuestProgress = '%(progress)s of %(numGags)s delivered' +QuestsDeliverGagQuestHeadline = 'DELIVER' +QuestsDeliverGagQuestToSCStringS = 'I need to deliver %(gagName)s.' +QuestsDeliverGagQuestToSCStringP = 'I need to deliver some %(gagName)s.' +QuestsDeliverGagQuestSCString = 'I need make a delivery.' +QuestsDeliverGagQuestString = 'Deliver %s' +QuestsDeliverGagQuestStringLong = 'Deliver %s to _toNpcName_.' +QuestsDeliverGagQuestInstructions = 'You can buy this gag in the Gag Shop once you earn access to it.' +QuestsDeliverItemQuestProgress = '' +QuestsDeliverItemQuestHeadline = 'DELIVER' +QuestsDeliverItemQuestSCString = 'I need to deliver %(article)s%(itemName)s.' +QuestsDeliverItemQuestString = 'Deliver %s' +QuestsDeliverItemQuestStringLong = 'Deliver %s to _toNpcName_.' +QuestsVisitQuestProgress = '' +QuestsVisitQuestHeadline = 'VISIT' +QuestsVisitQuestStringShort = 'Visit' +QuestsVisitQuestStringLong = 'Visit _toNpcName_' +QuestsVisitQuestSeeSCString = 'I need to see %s.' +QuestsRecoverItemQuestProgress = '%(progress)s of %(numItems)s recovered' +QuestsRecoverItemQuestHeadline = 'RECOVER' +QuestsRecoverItemQuestSeeHQSCString = 'I need to see an ' + lHQOfficerM + '.' +QuestsRecoverItemQuestReturnToHQSCString = 'I need to return %s to an ' + lHQOfficerM + '.' +QuestsRecoverItemQuestReturnToSCString = 'I need to return %(item)s to %(npcName)s.' +QuestsRecoverItemQuestGoToHQSCString = 'I need to go to a Toon HQ.' +QuestsRecoverItemQuestGoToPlaygroundSCString = 'I need to go to %s Playground.' +QuestsRecoverItemQuestGoToStreetSCString = 'I need to go %(to)s %(street)s in %(hood)s.' +QuestsRecoverItemQuestVisitBuildingSCString = 'I need to visit %s%s.' +QuestsRecoverItemQuestWhereIsBuildingSCString = 'Where is %s%s?' +QuestsRecoverItemQuestRecoverFromSCString = 'I need to recover %(item)s from %(holder)s%(loc)s.' +QuestsRecoverItemQuestString = 'Recover %(item)s from %(holder)s' +QuestsRecoverItemQuestHolderString = '%(level)s %(holder)d+ %(cogs)s' +QuestsTrackChoiceQuestHeadline = 'CHOOSE' +QuestsTrackChoiceQuestSCString = 'I need to choose between %(trackA)s and %(trackB)s.' +QuestsTrackChoiceQuestMaybeSCString = 'Maybe I should choose %s.' +QuestsTrackChoiceQuestString = 'Choose between %(trackA)s and %(trackB)s' +QuestsFriendQuestHeadline = 'FRIEND' +QuestsFriendQuestSCString = 'I need to make a friend.' +QuestsFriendQuestString = 'Make a friend' +QuestsMailboxQuestHeadline = 'MAIL' +QuestsMailboxQuestSCString = 'I need to check my mail.' +QuestsMailboxQuestString = 'Check your mail' +QuestsPhoneQuestHeadline = 'CLARABELLE' +QuestsPhoneQuestSCString = 'I need to call Clarabelle.' +QuestsPhoneQuestString = 'Call Clarabelle' +QuestsFriendNewbieQuestString = 'Make %d friends %d laff or less' +QuestsFriendNewbieQuestProgress = '%(progress)s of %(numFriends)s made' +QuestsFriendNewbieQuestObjective = 'Make friends with %d new Toons' +QuestsTrolleyQuestHeadline = 'TROLLEY' +QuestsTrolleyQuestSCString = 'I need to ride the trolley.' +QuestsTrolleyQuestString = 'Ride on the trolley' +QuestsTrolleyQuestStringShort = 'Ride the trolley' +QuestsMinigameNewbieQuestString = '%d Minigames' +QuestsMinigameNewbieQuestProgress = '%(progress)s of %(numMinigames)s Played' +QuestsMinigameNewbieQuestObjective = 'Play %d minigames with new Toons' +QuestsMinigameNewbieQuestSCString = 'I need to play minigames with new Toons.' +QuestsMinigameNewbieQuestCaption = 'Help a new Toon %d laff or less' +QuestsMinigameNewbieQuestAux = 'Play:' +QuestsMaxHpReward = 'Your Laff limit has been increased by %s.' +QuestsMaxHpRewardPoster = 'Reward: %s point Laff boost' +QuestsMoneyRewardSingular = 'You get 1 jellybean.' +QuestsMoneyRewardPlural = 'You get %s jellybeans.' +QuestsMoneyRewardPosterSingular = 'Reward: 1 jellybean' +QuestsMoneyRewardPosterPlural = 'Reward: %s jellybeans' +QuestsMaxMoneyRewardSingular = 'You can now carry 1 jellybean.' +QuestsMaxMoneyRewardPlural = 'You can now carry %s jellybeans.' +QuestsMaxMoneyRewardPosterSingular = 'Reward: Carry 1 jellybean' +QuestsMaxMoneyRewardPosterPlural = 'Reward: Carry %s jellybeans' +QuestsMaxGagCarryReward = 'You get a %(name)s. You can now carry %(num)s gags.' +QuestsMaxGagCarryRewardPoster = 'Reward: %(name)s (%(num)s)' +QuestsMaxQuestCarryReward = 'You can now have %s ToonTasks.' +QuestsMaxQuestCarryRewardPoster = 'Reward: Carry %s ToonTasks' +QuestsTeleportReward = 'You now have teleport access to %s.' +QuestsTeleportRewardPoster = 'Reward: Teleport access to %s' +QuestsTrackTrainingReward = 'You can now train for "%s" gags.' +QuestsTrackTrainingRewardPoster = 'Reward: Gag training' +QuestsTrackProgressReward = 'You now have frame %(frameNum)s of the %(trackName)s track animation.' +QuestsTrackProgressRewardPoster = 'Reward: "%(trackName)s" track animation frame %(frameNum)s' +QuestsTrackCompleteReward = 'You may now carry and use "%s" gags.' +QuestsTrackCompleteRewardPoster = 'Reward: Final %s track training' +QuestsClothingTicketReward = 'You can change your clothes' +QuestsClothingTicketRewardPoster = 'Reward: Clothing Ticket' +TIPQuestsClothingTicketReward = 'You can change your shirt for a TIP shirt' +TIPQuestsClothingTicketRewardPoster = 'Reward: TIP Clothing Ticket' +QuestsCheesyEffectRewardPoster = 'Reward: %s' +QuestsCogSuitPartReward = 'You now have a %(cogTrack)s %(part)s Cog Suit Part.' +QuestsCogSuitPartRewardPoster = 'Reward: %(cogTrack)s %(part)s Part' +QuestsStreetLocationThisPlayground = 'in this playground' +QuestsStreetLocationThisStreet = 'on this street' +QuestsStreetLocationNamedPlayground = 'in the %s playground' +QuestsStreetLocationNamedStreet = 'on %(toStreetName)s in %(toHoodName)s' +QuestsLocationString = '%(string)s%(location)s' +QuestsLocationBuilding = "%s's building is called" +QuestsLocationBuildingVerb = 'which is' +QuestsLocationParagraph = '\x07%(building)s "%(buildingName)s"...\x07...%(buildingVerb)s %(street)s.' +QuestsGenericFinishSCString = 'I need to finish a ToonTask.' +QuestsMediumPouch = 'Medium Pouch' +QuestsLargePouch = 'Large Pouch' +QuestsSmallBag = 'Small Bag' +QuestsMediumBag = 'Medium Bag' +QuestsLargeBag = 'Large Bag' +QuestsSmallBackpack = 'Small Backpack' +QuestsMediumBackpack = 'Medium Backpack' +QuestsLargeBackpack = 'Large Backpack' +QuestsItemDict = {1: ['Pair of Glasses', 'Pairs of Glasses', 'a '], + 2: ['Key', 'Keys', 'a '], + 3: ['Blackboard', 'Blackboards', 'a '], + 4: ['Book', 'Books', 'a '], + 5: ['Candy Bar', 'Candy Bars', 'a '], + 6: ['Piece of Chalk', 'Pieces of Chalk', 'a '], + 7: ['Recipe', 'Recipes', 'a '], + 8: ['Note', 'Notes', 'a '], + 9: ['Adding machine', 'Adding machines', 'an '], + 10: ['Clown car tire', 'Clown car tires', 'a '], + 11: ['Air pump', 'Air pumps', 'an '], + 12: ['Octopus ink', 'Octopus inks', 'some '], + 13: ['Package', 'Package', 'a '], + 14: ['Goldfish receipt', 'Goldfish receipts', 'a '], + 15: ['Goldfish', 'Goldfish', 'a '], + 16: ['Oil', 'Oils', 'some '], + 17: ['Grease', 'Greases', 'some '], + 18: ['Water', 'Waters', 'some '], + 19: ['Gear report', 'Gear reports', 'a '], + 20: ['Blackboard Eraser', 'Blackboard Erasers', 'a '], + 110: ['TIP Clothing Ticket', 'Clothing Tickets', 'a '], + 1000: ['Clothing Ticket', 'Clothing Tickets', 'a '], + 2001: ['Inner Tube', 'Inner Tubes', 'an '], + 2002: ['Monocle Prescription', 'Monocle Prescriptions', 'a '], + 2003: ['Eyeglass Frames', 'Eyeglass Frames', 'some '], + 2004: ['Monocle', 'Monocles', 'a '], + 2005: ['Big White Wig', 'Big White Wigs', 'a '], + 2006: ['Bushel of Ballast', 'Bushels of Ballast', 'a '], + 2007: ['Cog Gear', 'Cog Gears', 'a '], + 2008: ['Sea Chart', 'Sea Charts', 'a '], + 2009: ['Cruddy Clovis', 'Cruddy Clovi', 'a '], + 2010: ['Clean Clovis', 'Clean Clovi', 'a '], + 2011: ['Clock Spring', 'Clock Springs', 'a '], + 2012: ['Counter Weight', 'Counter Weights', 'a '], + 4001: ["Tina's Inventory", "Tina's Inventories", ''], + 4002: ["Yuki's Inventory", "Yuki's Inventories", ''], + 4003: ['Inventory Form', 'Inventory Forms', 'an '], + 4004: ["Fifi's Inventory", "Fifi's Inventories", ''], + 4005: ["Lumber Jack's Ticket", "Lumber Jack's Tickets", ''], + 4006: ["Tabitha's Ticket", "Tabitha's Tickets", ''], + 4007: ["Barry's Ticket", "Barry's Tickets", ''], + 4008: ['Cloudy Castanet', 'Cloudy Castanets', ''], + 4009: ['Blue Squid Ink', 'Blue Squid Ink', 'some '], + 4010: ['Clear Castanet', 'Clear Castanets', 'a '], + 4011: ["Leo's Lyrics", "Leo's Lyrics", ''], + 5001: ['Silk necktie', 'Silk neckties', 'a '], + 5002: ['Pinstripe Suit', 'Pinstripe Suits', 'a '], + 5003: ['Pair of Scissors', 'Pairs of Scissors', 'a '], + 5004: ['Postcard', 'Postcards', 'a '], + 5005: ['Pen', 'Pens', 'a '], + 5006: ['Inkwell', 'Inkwells', 'an '], + 5007: ['Notepad', 'Notepads', 'a '], + 5008: ['Office Lockbox', 'Office Lockboxes', 'an '], + 5009: ['Bag of Bird Seed', 'Bags of Bird Seed', 'a '], + 5010: ['Sprocket', 'Sprockets', 'a '], + 5011: ['Salad', 'Salads', 'a '], + 5012: ['Key to ' + lDaisyGardens, 'Keys to ' + lDaisyGardens, 'a '], + 5013: [lSellbotHQ + ' Blueprints', lSellbotHQ + ' HQ Blueprints', 'some '], + 5014: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 5015: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 5016: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 5017: [lSellbotHQ + ' Memo', lSellbotHQ + ' Memos', 'a '], + 3001: ['Soccer ball', 'Soccer balls', 'a '], + 3002: ['Toboggan', 'Toboggans', 'a '], + 3003: ['Ice cube', 'Ice cubes', 'an '], + 3004: ['Love letter', 'Love letters', 'a '], + 3005: ['Wiener dog', 'Wiener dogs', 'a '], + 3006: ['Engagement ring', 'Engagement rings', 'an '], + 3007: ['Sardine whiskers', 'Sardine whiskers', 'some '], + 3008: ['Calming potion', 'Calming potion', 'a '], + 3009: ['Broken tooth', 'Broken teeth', 'a '], + 3010: ['Gold tooth', 'Gold teeth', 'a '], + 3011: ['Pine cone bread', 'Pine cone breads', 'a '], + 3012: ['Lumpy cheese', 'Lumpy cheeses', 'some '], + 3013: ['Simple spoon', 'Simple spoons', 'a '], + 3014: ['Talking toad', 'Talking toad', 'a '], + 3015: ['Ice cream cone', 'Ice cream cones', 'an '], + 3016: ['Wig powder', 'Wig powders', 'some '], + 3017: ['Rubber ducky', 'Rubber duckies', 'a '], + 3018: ['Fuzzy dice', 'Fuzzy dice', 'some '], + 3019: ['Microphone', 'Microphones', 'a '], + 3020: ['Electric keyboard', 'Electric keyboards', 'an '], + 3021: ['Platform shoes', 'Platform shoes', 'some '], + 3022: ['Caviar', 'Caviar', 'some '], + 3023: ['Make-up powder', 'Make-up powders', 'some '], + 3024: ['Yarn', 'Yarn', 'some '], + 3025: ['Knitting Needle', 'Knitting Needles', 'a '], + 3026: ['Alibi', 'Alibis', 'an '], + 3027: ['External Temperature Sensor', 'External Temperature Sensors', 'an '], + 6001: ['Cashbot HQ Plans', 'Cashbot HQ Plans', 'some '], + 6002: ['Rod', 'Rods', 'a '], + 6003: ['Drive Belt', 'Drive Belts', 'a '], + 6004: ['Pair of Pincers', 'Pairs of Pincers', 'a '], + 6005: ['Reading Lamp', 'Reading Lamps', 'a '], + 6006: ['Zither', 'Zithers', 'a '], + 6007: ['Zamboni', 'Zambonis', 'a '], + 6008: ['Zebra Zabuton', 'Zebra Zabutons', 'a '], + 6009: ['Zinnias', 'Zinnias', 'some '], + 6010: ['Zydeco Records', 'Zydeco Records', 'some '], + 6011: ['Zucchini', 'Zucchinis', 'a '], + 6012: ['Zoot Suit', 'Zoot Suits', 'a '], + 7001: ['Plain Bed', 'Plain Beds', 'a '], + 7002: ['Fancy Bed', 'Fancy Beds', 'a '], + 7003: ['Blue Bedspread', 'Blue Bedspreads', 'a '], + 7004: ['Paisley Bedspread', 'Paisley Bedspreads', 'a '], + 7005: ['Pillows', 'Pillows', 'some '], + 7006: ['Hard Pillows', 'Hard Pillows', 'some '], + 7007: ['Pajamas', 'Pajamas', 'a pair of '], + 7008: ['Footie Pajamas', 'Footie Pajamas', 'a pair of '], + 7009: ['Puce Footie Pajamas', 'Puce Footie Pajamas', 'a pair of '], + 7010: ['Fuchsia Footie Pajamas', 'Fuchsia Footie Pajamas', 'a pair of '], + 7011: ['Cauliflower Coral', 'Cauliflower Coral', 'some '], + 7012: ['Slimy Kelp', 'Slimy Kelp', 'some '], + 7013: ['Pestle', 'Pestles', 'a '], + 7014: ['Jar of Wrinkle Cream', 'Jars of Wrinkle Cream', 'a ']} +QuestsHQOfficerFillin = lHQOfficerM +QuestsHQWhereFillin = '' +QuestsHQBuildingNameFillin = lToonHQ +QuestsHQLocationNameFillin = 'in any neighborhood' +QuestsTailorFillin = 'Tailor' +QuestsTailorWhereFillin = '' +QuestsTailorBuildingNameFillin = 'Clothing Store' +QuestsTailorLocationNameFillin = 'in any neighborhood' +QuestsTailorQuestSCString = 'I need to see a Tailor.' +QuestMovieQuestChoiceCancel = 'Come back later if you need a ToonTask! Bye!' +QuestMovieTrackChoiceCancel = 'Come back when you are ready to decide! Bye!' +QuestMovieQuestChoice = 'Choose a ToonTask.' +QuestMovieTrackChoice = 'Ready to decide? Choose a track, or come back later.' +GREETING = 0 +QUEST = 1 +INCOMPLETE = 2 +INCOMPLETE_PROGRESS = 3 +INCOMPLETE_WRONG_NPC = 4 +COMPLETE = 5 +LEAVING = 6 +TheBrrrghTrackQuestDict = {GREETING: '', + QUEST: 'Now you are ready.\x07Go out and walk the earth until you know which track you would like to choose.\x07Choose wisely, because this is your final track.\x07When you are certain, return to me.', + INCOMPLETE_PROGRESS: 'Choose wisely.', + INCOMPLETE_WRONG_NPC: 'Choose wisely.', + COMPLETE: 'Very wise choice!', + LEAVING: 'Good luck. Return to me when you have mastered your new skill.'} +QuestDialog_3225 = {QUEST: "Oh, thanks for coming, _avName_!\x07The Cogs in the neighborhood frightened away my delivery person.\x07I don't have anyone to deliver this salad to _toNpcName_!\x07Can you do it for me? Thanks so much!_where_"} +QuestDialog_2910 = {QUEST: 'Back so soon?\x07Great job on the spring.\x07The final item is a counter weight.\x07Stop by and see _toNpcName_ and bring back whatever you can get._where_'} +QuestDialogDict = {160: {GREETING: '', + QUEST: "Ok, now I think you are ready for something more rewarding.\x07If you can defeat 3 Bossbots I'll give you a little bonus.", + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 161: {GREETING: '', + QUEST: "Ok, now I think you are ready for something more rewarding.\x07Come back after you defeat 3 Lawbots and I'll have a little something for you.", + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 162: {GREETING: '', + QUEST: 'Ok, now I think you are ready for something more rewarding.\x07Defeat 3 Cashbots and come back here to claim the bounty.', + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 163: {GREETING: '', + QUEST: "Ok, now I think you are ready for something more rewarding.\x07Come see us after you defeat 3 Sellbots and we'll hook you up.", + INCOMPLETE_PROGRESS: TheCogs + ' are out in the streets, through the tunnels.', + INCOMPLETE_WRONG_NPC: 'Good job defeating those Cogs. Now go to the Toon Headquarters for your next step!', + COMPLETE: QuestsDefaultComplete, + LEAVING: QuestsDefaultLeaving}, + 164: {QUEST: 'You look like you could use some new gags.\x07Go see %s, maybe he can help you out._where_' % Flippy}, + 165: {QUEST: 'Hi there.\x07Looks like you need to practice training your gags.\x07Every time you hit a Cog with one of your gags, your experience increases.\x07When you get enough experience, you will be able to use an even better gag.\x07Go practice your gags by defeating 4 Cogs.'}, + 166: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Bossbots.'}, + 167: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Lawbots.'}, + 168: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Sellbots.'}, + 169: {QUEST: 'Nice work defeating those Cogs.\x07You know, the Cogs come in four different types.\x07They are Lawbots, Cashbots, Sellbots, and Bossbots.\x07You can tell them apart by their coloring and their name labels.\x07For practice go defeat 4 Cashbots.'}, + 170: {QUEST: 'Nice work, now you know the difference between the 4 types of Cogs.\x07I think you are ready to start training for your third gag track.\x07Go talk to _toNpcName_ to choose your next gag track - he can give you some expert advice._where_'}, + 171: {QUEST: 'Nice work, now you know the difference between the 4 types of Cogs.\x07I think you are ready to start training for your third gag track.\x07Go talk to _toNpcName_ to choose your next gag track - he can give you some expert advice._where_'}, + 172: {QUEST: 'Nice work, now you know the difference between the 4 types of Cogs.\x07I think you are ready to start training for your third gag track.\x07Go talk to _toNpcName_ to choose your next gag track - she can give you some expert advice._where_'}, + 175: {GREETING: '', + QUEST: "Did you know you have your very own Toon house?\x07Clarabelle Cow runs a phone catalog where you can order furniture to decorate your house.\x07You can also buy SpeedChat phrases, clothing, and other fun things!\x07I'll tell Clarabelle to send you your first catalog now.\x07You get a catalog with new items every week!\x07Go to your home and use your phone to call Clarabelle.", + INCOMPLETE_PROGRESS: 'Go home and use your phone to call Clarabelle.', + COMPLETE: 'Hope you have fun ordering things from Clarabelle!\x07I just finished redecorating my house. It looks Toontastic!\x07Keep doing ToonTasks to get more rewards!', + LEAVING: QuestsDefaultLeaving}, + 400: {GREETING: '', + QUEST: 'Throw and Squirt are great, but you will need more gags to fight higher level Cogs.\x07When you team up with other Toons against the Cogs, you can combine attacks for even more damage.\x07Try different combinations of gags to see what works best.\x07For your next track, choose between Sound and Toonup.\x07Sound is special because when it hits, it damages all Cogs.\x07Toonup lets you heal other Toons in battle.\x07When you are ready to decide, come back here and choose.', + INCOMPLETE_PROGRESS: 'Back so soon? Okay, are you ready to choose?', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'Good decision. Now before you can use those gags, you must train for them.\x07You must complete a series of ToonTasks for training.\x07Each task will give you a single frame of your gag attack animation.\x07When you collect all 15, you can get the Final Gag Training task that will allow you to use your new gags.\x07You can check your progress in the Shticker Book.', + LEAVING: QuestsDefaultLeaving}, + 1039: {QUEST: 'Visit _toNpcName_ if you want to get around town more easily._where_'}, + 1040: {QUEST: 'Visit _toNpcName_ if you want to get around town more easily._where_'}, + 1041: {QUEST: 'Hi! What brings you here?\x07Everybody uses their portable hole to travel around Toontown.\x07Why, you can teleport to your friends using the Friends List, or to any neighborhood using the map in the Shticker Book.\x07Of course, you have to earn that!\x07Say, I can turn on your teleport access to ' + lToontownCentral + ' if you help out a friend of mine.\x07Seems the Cogs are causing trouble over on Loopy Lane. Go visit _toNpcName_._where_'}, + 1042: {QUEST: 'Hi! What brings you here?\x07Everybody uses their portable hole to travel around Toontown.\x07Why, you can teleport to your friends using the Friends List, or to any neighborhood using the map in the Shticker Book.\x07Of course, you have to earn that!\x07Say, I can turn on your teleport access to ' + lToontownCentral + ' if you help out a friend of mine.\x07Seems the Cogs are causing trouble over on Loopy Lane. Go visit _toNpcName_._where_'}, + 1043: {QUEST: 'Hi! What brings you here?\x07Everybody uses their portable hole to travel around Toontown.\x07Why, you can teleport to your friends using the Friends List, or to any neighborhood using the map in the Shticker Book.\x07Of course, you have to earn that!\x07Say, I can turn on your teleport access to ' + lToontownCentral + ' if you help out a friend of mine.\x07Seems the Cogs are causing trouble over on Loopy Lane. Go visit _toNpcName_._where_'}, + 1044: {QUEST: 'Oh, thanks for stopping by. I really need some help.\x07As you can see, I have no customers.\x07My secret recipe book is lost and nobody comes to my restaurant anymore.\x07I last saw it just before those Cogs took over my building.\x07Can you help me by recovering four of my famous recipes?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my recipes?'}, + 1045: {QUEST: 'Thank you so much!\x07Before long I will have the entire collection and can reopen my restaurant.\x07Oh, I have a note here for you - something about teleport access?\x07It says thanks for helping my friend and to deliver this to Toon Headquarters.\x07Well, thanks indeed - bye!', + LEAVING: '', + COMPLETE: 'Ah, yes, says here you have been a great help to some of the fine folks out on Loopy Lane.\x07Says you need teleport access to ' + lToontownCentral + '.\x07Well, consider it done.\x07Now you can teleport back to the playground from almost anywhere in Toontown.\x07Just open your map and click on ' + lToontownCentral + '.'}, + 1046: {QUEST: 'The Cashbots have really been bothering the Funny Money Savings and Loan.\x07Stop by there and see if there is anything you can do._where_'}, + 1047: {QUEST: 'Cashbots have been sneaking into the bank and stealing our machines.\x07Please recover 5 adding machines from Cashbots.\x07To save you from running back and forth, just bring them all back at once.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Still looking for adding machines?'}, + 1048: {QUEST: 'Wow! Thanks for finding our adding machines.\x07Hm... They look a little damaged.\x07Say, could you take them over to _toNpcName_ over at her shop, "Tickle Machines" on this street?\x07See if she can fix them.', + LEAVING: ''}, + 1049: {QUEST: "What's that? Broken adding machines?\x07Cashbots you say?\x07Well, let's have a look see...\x07Yep, gears are stripped, but I'm out of that part...\x07You know what might work - some Cog gears, large ones, from larger Cogs...\x07Level 3 Cog gears should do the trick. I'll need 2 for each machine, so 10 total.\x07Bring them back all at once and I'll fix em up!", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Remember, I need 10 gears to fix the machines.'}, + 1053: {QUEST: "Ah yes, that should do the trick indeedy.\x07All fixed now, free of charge.\x07Take these back to Funny Money, and tell 'im I said howdy.", + LEAVING: '', + COMPLETE: "Adding machines all fixed up?\x07Nice work. I'm sure I've got something around here to reward you with..."}, + 1054: {QUEST: '_toNpcName_ needs some help with his clown cars._where_'}, + 1055: {QUEST: "Yowza! I can't find the tires to this here clown car anywhere!\x07Do ya think you could help me out?\x07I think Loopy Bob may have tossed them in the pond in the " + lToontownCentral + ' playground.\x07If you stand on one of the docks there you can try and fish out the tires for me.', + GREETING: 'Woohoo!', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Are you having trouble fishing out all 4 tires?'}, + 1056: {QUEST: 'Fan-flying-tastic! Now I can get this old clown car on the road again!\x07Hey, I thought I had an air pump around here to inflate these tires...\x07Maybe _toNpcName_ borrowed it?\x07Could you go ask for it back for me?_where_', + LEAVING: ''}, + 1057: {QUEST: "Hi there.\x07A tire pump you say?\x07I'll tell you what - you help clean up the streets of some of those high level Cogs for me...\x07And I'll let you have the tire pump.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Is that the best you can do?'}, + 1058: {QUEST: "Good job - I knew you could do it.\x07Here's the pump. I'm sure _toNpcName_ will be glad to get it back.", + LEAVING: '', + GREETING: '', + COMPLETE: "Yeehaw! Now I'm good to go!\x07By the way, thanks for helping me out.\x07Here, take this."}, + 1059: {QUEST: '_toNpcName_ is running low on supplies. Maybe you can give him a hand?_where_'}, + 1060: {QUEST: "Thanks for stopping by!\x07Those Cogs have been stealing my ink, so I'm running very low.\x07Could you fish some octopus ink out of the pond for me?\x07Just stand on a dock near the pond to fish.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Are you having trouble fishing?'}, + 1061: {QUEST: "Great - thanks for the ink!\x07You know what, maybe if you cleared away some of those Pencil Pushers...\x07I wouldn't run out of ink again so quickly.\x07Defeat 6 Pencil Pushers in " + lToontownCentral + ' for your reward.', + LEAVING: '', + COMPLETE: 'Thanks! Let me reward you for your help.', + INCOMPLETE_PROGRESS: 'I just saw some more Pencil Pushers.'}, + 1062: {QUEST: "Great - thanks for the ink!\x07You know what, maybe if you cleared away some of those Bloodsuckers...\x07I wouldn't run out of ink again so quickly.\x07Defeat 6 Bloodsuckers in " + lToontownCentral + ' for your reward.', + LEAVING: '', + COMPLETE: 'Thanks! Let me reward you for your help.', + INCOMPLETE_PROGRESS: 'I just saw some more Bloodsuckers.'}, + 900: {QUEST: 'I hear _toNpcName_ needs help with a package._where_'}, + 1063: {QUEST: 'Hi - thanks for coming in.\x07A Cog stole a very important package from right under my nose.\x07Please see if you can get it back. I think he was a level 3...\x07So, defeat level 3 Cogs until you find my package.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1067: {QUEST: "That's it, all right!\x07Hey, the address is smudged...\x07All I can read is that it's for a Dr. - the rest is all blurry.\x07Maybe it's for _toNpcName_? Could you take it to him?_where_", + LEAVING: ''}, + 1068: {QUEST: "I wasn't expecting a package. Maybe it's for Dr. I.M. Euphoric?\x07My assistant was going over there today anyway, so I'll have him check for you.\x07In the meantime, would you mind getting rid of some of the Cogs on my street?\x07Defeat 10 Cogs in " + lToontownCentral + '.', + LEAVING: '', + INCOMPLETE_PROGRESS: "My assistant isn't back yet."}, + 1069: {QUEST: "Dr. Euphoric says he wasn't expecting a package either.\x07Unfortunately, a Cashbot stole it from my assistant on the way back.\x07Could you try and get it back?", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1070: {QUEST: "Dr. Euphoric says he wasn't expecting a package either.\x07Unfortunately, a Sellbot stole the package from my assistant on the way back.\x07I'm sorry, but you'll have to find that Sellbot and get it back.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1071: {QUEST: "Dr. Euphoric says he wasn't expecting a package either.\x07Unfortunately, a Bossbot stole it from my assistant on the way back.\x07Could you try and get it back?", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the package, huh?'}, + 1072: {QUEST: 'Great - you got it back!\x07Maybe you should try _toNpcName_, it could be for him._where_', + LEAVING: ''}, + 1073: {QUEST: 'Oh, thanks for bringing me my packages.\x07Wait a second, I was expecting two. Could you check with _toNpcName_ and see if he has the other one?', + INCOMPLETE: 'Were you able to find my other package?', + LEAVING: ''}, + 1074: {QUEST: 'He said there was another package? Maybe the Cogs stole it too.\x07Defeat Cogs until you find the second package.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding the other package, huh?'}, + 1075: {QUEST: 'I guess there was a second package after all!\x07Hurry and take it over to _toNpcName_ with my apologies.', + COMPLETE: 'Hey, my package is here!\x07Since you seem to be such a helpful Toon, this should come in handy.', + LEAVING: ''}, + 1076: {QUEST: "There's been some trouble over at 14 Karat Goldfish.\x07_toNpcName_ could probably use a hand._where_"}, + 1077: {QUEST: "Thanks for coming - the Cogs stole all my goldfish.\x07I think the Cogs want to sell them to make a quick buck.\x07Those 5 fish have been my only companions in this tiny store for so many years...\x07If you could get them back for me I'd really appreciate it.\x07I'm sure one of the Cogs has my fish.\x07Defeat Cogs until you find my goldfish.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Please return my goldfish to me.'}, + 1078: {QUEST: "Oh, you have my fish!\x07Huh? What's this - a receipt?\x07Sigh, I guess they are Cogs, after all.\x07I can't make heads or tails out of this receipt. Could you take it to _toNpcName_ and see if he can read it?_where_", + INCOMPLETE: 'What did _toNpcName_ have to say about the receipt?', + LEAVING: ''}, + 1079: {QUEST: "Mmm, let me see that receipt.\x07...Ah Yes, it says that 1 goldfish was sold to a Flunky.\x07It doesn't seem to mention what happened to the other 4 fish.\x07Maybe you should try and find that Flunky.", + LEAVING: '', + INCOMPLETE_PROGRESS: "I don't think there's anything else I can help you with.\x07Why don't you try and find that goldfish?"}, + 1092: {QUEST: "Mmm, let me see that receipt.\x07...Ah Yes, it says that 1 goldfish was sold to a Short Change.\x07It doesn't seem to mention what happened to the other 4 fish.\x07Maybe you should try and find that Short Change.", + LEAVING: '', + INCOMPLETE_PROGRESS: "I don't think there's anything else I can help you with.\x07Why don't you try and find that goldfish?"}, + 1080: {QUEST: "Oh thank heavens! You found Oscar - he's my favorite.\x07What's that, Oscar? Uh huh... they did? ... they are?\x07Oscar says the other 4 escaped into the pond in the playground.\x07Could you go round them up for me?\x07Just fish them out of the pond.", + LEAVING: '', + COMPLETE: 'Ahh, I am sooo happy! To be reunited with my little buddies!\x07You deserve a handsome reward for this!', + INCOMPLETE_PROGRESS: 'Are you having trouble finding those fish?'}, + 1081: {QUEST: '_toNpcName_ appears to be in a sticky situation. He sure could use a hand._where_'}, + 1082: {QUEST: "I spilled quick dry glue and I'm stuck - stuck cold!\x07If there were a way out, I sure would be sold.\x07That gives me an idea, if you are feeling loyal.\x07Defeat some Sellbots and bring back some oil.", + LEAVING: '', + GREETING: '', + INCOMPLETE_PROGRESS: 'Can you help me get un-stuck?'}, + 1083: {QUEST: "Well, oil helped a little, but I still cannot budge,\x07What else would help? It's hard to judge.\x07That gives me an idea; it's worth a try at least.\x07Defeat some Lawbots and bring back some grease.", + LEAVING: '', + GREETING: '', + INCOMPLETE_PROGRESS: 'Can you help me get un-stuck?'}, + 1084: {QUEST: "Nope, that didn't help. This is really not funny.\x07I put the grease right there on the money,\x07That gives me an idea, before I forget it.\x07Defeat some Cashbots; bring back water to wet it.", + LEAVING: '', + GREETING: '', + COMPLETE: "Hooray, I'm free of this quick drying glue,\x07As a reward I give this gift to you,\x07You can laugh a little longer while battling and then...\x07Oh, no! I'm already stuck here again!", + INCOMPLETE_PROGRESS: 'Can you help me get un-stuck?'}, + 1085: {QUEST: '_toNpcName_ is conducting some research on the Cogs.\x07Go talk to him if you want to help out._where_'}, + 1086: {QUEST: "That's right, I'm conducting a study of the Cogs.\x07I want to know what makes them tick.\x07It sure would help me if you could gather some gears from Cogs.\x07Make sure they're from at least level 2 Cogs so they're big enough to examine.", + LEAVING: '', + INCOMPLETE_PROGRESS: "Can't find enough gears?"}, + 1089: {QUEST: "Okay, let's take a look. These are excellent specimens!\x07Mmmm...\x07Okay, here's my report. Take this back to Toon Headquarters right away.", + INCOMPLETE: 'Have you delivered my report to Headquarters?', + COMPLETE: "Good work _avName_, we'll take this one from here.", + LEAVING: ''}, + 1090: {QUEST: '_toNpcName_ has some useful information for you._where_'}, + 1091: {QUEST: 'I hear that Toon Headquarters is working on a sort of Cog Radar.\x07It will let you see where the Cogs are so that it will be easier to find them.\x07That Cog Page in your Shticker Book is the key.\x07By defeating enough Cogs, you can tune in to their signals and actually track where they are.\x07Keep defeating Cogs, so you will be ready.', + COMPLETE: 'Good work! You could probably use this...', + LEAVING: ''}, + 401: {GREETING: '', + QUEST: 'Now you get to choose the next gag track you want to learn.\x07Take your time deciding, and come back here when you are ready to choose.', + INCOMPLETE_PROGRESS: 'Think about your decision before choosing.', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'A wise decision...', + LEAVING: QuestsDefaultLeaving}, + 2201: {QUEST: 'Those sneaky Cogs are at it again.\x07_toNpcName_ has reported another missing item. Stop by and see if you can straighten it out._where_'}, + 2202: {QUEST: "Hi, _avName_. Thank goodness you're here. A mean looking Penny Pincher was just in here and he made off with an inner tube.\x07I fear they may use it for their vile purposes.\x07Please see if you can find him and bring it back.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my inner tube?', + COMPLETE: 'You found my inner tube! You ARE good. Here, take your reward...'}, + 2203: {QUEST: TheCogs + ' are wreaking havoc over at the bank.\x07Go see Captain Carl and see what you can do._where_'}, + 2204: {QUEST: "Welcome aboard, matey.\x07Argh! Those rapscallion Cogs smashed my monocle and I can't sort me change without it.\x07Be a good landlubber and take this prescription to _toNpcName_ and fetch me a new one._where_", + GREETING: '', + LEAVING: ''}, + 2205: {QUEST: "What's this?\x07Oh, I'd love to fill this prescription but the Cogs have been pilfering my supplies.\x07If you can get me the eyeglass frames off a flunky I can probably help you out.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Sorry. No flunky frames, no monocle.'}, + 2206: {QUEST: 'Excellent!\x07Just a second...\x07Your prescription is filled. Please take this monocle straight to Captain Carl._where_', + GREETING: '', + LEAVING: '', + COMPLETE: "Avast Ye!\x07You're gonna earn your sea legs after all.\x07Here ye be."}, + 2207: {QUEST: "Barnacle Barbara has a Cog in her shop!\x07You'd better get over there pronto._where_"}, + 2208: {QUEST: "Gosh! You just missed him, sweetie.\x07There was a Back Stabber in here. He took my big white wig.\x07He said it was for his boss and something about 'legal precedent.'\x07If you can get it back I'd be forever grateful.", + LEAVING: '', + GREETING: '', + INCOMPLETE_PROGRESS: "Still haven't found him?\x07He's tall and has a pointy head", + COMPLETE: "You found it!?!?\x07Aren't you a darling!\x07You've more than earned this..."}, + 2209: {QUEST: 'Melville is preparing for an important voyage.\x07Pop in and see what you can do to help sort him out._where_'}, + 2210: {QUEST: "I can use your help.\x07I've been asked by Toon HQ to take a voyage and see if I can find where the Cogs are coming from.\x07I'll need a few things for my ship but I don't have many jellybeans.\x07Stop by and pick up some ballast from Alice. You'll have to do a favor for her to get it._where_", + GREETING: 'Howdy, _avName_', + LEAVING: ''}, + 2211: {QUEST: "So Melville wants ballast, does he?\x07He still owes me for the last bushel.\x07I'll give it to you if you can clear five Micromanagers off my street.", + INCOMPLETE_PROGRESS: 'No, silly! I said FIVE micromanagers...', + GREETING: 'What can I do for you?', + LEAVING: ''}, + 2212: {QUEST: "A deal's a deal.\x07Here's your ballast for that cheapskate Melville._where_", + GREETING: 'Well, look what the cat dragged in...', + LEAVING: ''}, + 2213: {QUEST: "Excellent work. I knew she'd be reasonable.\x07Next I'll need a sailing chart from Art.\x07I don't think my credit is good there either so you'll have to work something out with him._where_", + GREETING: '', + LEAVING: ''}, + 2214: {QUEST: "Yes, I have the sea chart Melville wants.\x07And if you're willing to work for it I'll let you have it.\x07I'm trying to build an astrolabe to navigate by the stars.\x07I could use three Cog gears to build it.\x07Come back when you've found them.", + INCOMPLETE_PROGRESS: "How's it coming with those Cog gears?", + GREETING: 'Welcome!', + LEAVING: 'Good luck!'}, + 2215: {QUEST: "Ooh! These gears will do rather nicely.\x07Here's the chart. Give it to Melville with my compliments._where_", + GREETING: '', + LEAVING: '', + COMPLETE: "Well, that just about does it. I'm ready to sail!\x07I'd take you with me if you weren't so green. Take this instead."}, + 901: {QUEST: "If you're up for it Ahab could use some assistance over at his place..._where_"}, + 2902: {QUEST: "Are you the new recruit?\x07Good, good. Maybe you can help me.\x07I'm building a giant prefab crab to confuse the Cogs.\x07I could use a clovis though. Go see Claggart and bring one back, please._where_"}, + 2903: {QUEST: "Hi there!\x07Yes, I heard about the giant crab Ahab's working on.\x07The best clovis I have is a little on the dirty side though.\x07Be a sport and run it by the cleaners for me before you drop it off._where_", + LEAVING: 'Thanks!'}, + 2904: {QUEST: 'You must be the one that Claggart sent over.\x07I think I can clean that up in short order.\x07Just a minute...\x07There you are. Good as new!\x07Tell Ahab I said hello._where_'}, + 2905: {QUEST: "Ah, now this is exactly what I was looking for.\x07While you're here, I'm also going to need a very large clock spring.\x07Take a walk over to Hook's place and see if he has one._where_"}, + 2906: {QUEST: "A large spring, eh?\x07I'm sorry but the largest spring I have is still quite small.\x07Perhaps I could assemble one out of squirt gun trigger springs.\x07Bring me three of these gags and I'll see what I can do."}, + 2907: {QUEST: "Let's have a look then...\x07Smashing. Simply Smashing.\x07Sometimes I even surprise myself.\x07Here you go: one large spring for Ahab!_where_", + LEAVING: 'Bon Voyage!'}, + 2911: {QUEST: "I'd be happy to help the cause, _avName_.\x07But I'm afraid the streets are no longer safe.\x07Why don't you go take out some Cashbot Cogs and we'll talk.", + INCOMPLETE_PROGRESS: 'I still think you need to make the streets safer.'}, + 2916: {QUEST: 'Yes, I have a weight that Ahab can have.\x07I think it would be safer if you defeated a couple sellbots first though.', + INCOMPLETE_PROGRESS: 'Not yet. Defeat some more sellbots.'}, + 2921: {QUEST: "Hmmm, I suppose I could give up a weight.\x07I'd feel a lot better about it if there weren't so many Bossbot Cogs creeping around.\x07Defeat six and then come see me.", + INCOMPLETE_PROGRESS: "I don't think its safe yet..."}, + 2925: {QUEST: "All done?\x07Well, I guess it's safe enough now.\x07Here's the counter weight for Ahab._where_"}, + 2926: {QUEST: "Well, that's everything.\x07Let's see if it works.\x07Hmmm, one small problem.\x07I'm not getting any power because that Cog building is blocking my solar panel.\x07Could you retake it for me?", + INCOMPLETE_PROGRESS: 'Still no power. How about that building?', + COMPLETE: 'Super! You are one heck of a Cog crusher! Here, take this as your reward...'}, + 3200: {QUEST: "I just got a call in from _toNpcName_.\x07He's having a hard day. Maybe you can help him out!\x07Drop by and see what he needs._where_"}, + 3201: {QUEST: 'Oh, thanks for coming!\x07I need someone to take this new silk tie to _toNpcName_.\x07Would you be able to do that for me?_where_'}, + 3203: {QUEST: 'Oh, this must be the tie I ordered! Thanks!\x07It matches a pinstripe suit I just finished, right over here.\x07Hey, what happened to that suit?\x07Oh no! The Cogs must have stolen my new suit!\x07Defeat Cogs until you find my suit, and bring it back to me.', + LEAVING: '', + INCOMPLETE_PROGRESS: "Have you found my suit yet? I'm sure the Cogs took it!", + COMPLETE: 'Hooray! You found my new suit!\x07See, I told you the Cogs had it! Here is your reward...'}, + 3204: {QUEST: "_toNpcName_ just called to report a theft.\x07Why don't you stop by and see if you can sort things out?_where_"}, + 3205: {QUEST: "Hello, _avName_! Have you come to help me?\x07I just chased a Bloodsucker out of my shop. Whew! That was scary.\x07But now I can't find my scissors anywhere! I'm sure that Bloodsucker took them.\x07Find that Bloodsucker, and recover my scissors for me.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Are you still looking for my scissors?', + COMPLETE: 'My scissors! Thank you so much! Here is your reward...'}, + 3206: {QUEST: 'It sounds like _toNpcName_ is having problems with some Cogs.\x07Go see if you can help him out._where_'}, + 3207: {QUEST: 'Hi, _avName_! Thanks for coming by!\x07A bunch of Double Talkers just broke in and stole a stack of postcards from my counter.\x07Please go out and defeat all those Double Talkers to get my postcards back!', + INCOMPLETE_PROGRESS: "That's not enough postcards! Keep looking!", + COMPLETE: 'Oh, thank you! Now I can deliver the mail on time! Here is your reward...'}, + 3208: {QUEST: "We've been getting complaints from the residents lately about all of the Cold Callers.\x07See if you can defeat 10 Cold Callers to help out your fellow Toons in " + lDaisyGardens + '.'}, + 3209: {QUEST: 'Thanks for taking care of those Cold Callers!\x07But now the Telemarketers have gotten out of hand.\x07Defeat 10 Telemarketers in ' + lDaisyGardens + ' and come back here for your reward.'}, + 3247: {QUEST: "We've been getting complaints from the residents lately about all of the Bloodsuckers.\x07See if you can defeat 20 Bloodsuckers to help out your fellow Toons in " + lDaisyGardens + '.'}, + 3210: {QUEST: 'Oh no, The Squirting Flower on Maple Street just ran out of flowers!\x07Take them ten of your own squirting flowers to help out.\x07Make sure you have 10 squirting flowers in your inventory first.', + LEAVING: '', + INCOMPLETE_PROGRESS: "I need to have 10 squirting flowers. You don't have enough!"}, + 3211: {QUEST: "Oh, thank you so much! Those squirting flowers will save the day.\x07But I'm scared of the Cogs outside.\x07Can you help me out and defeat some of those Cogs?\x07Come back to me after you have defeated 20 Cogs on this street.", + INCOMPLETE_PROGRESS: 'There are still Cogs out there to defeat! Keep it up!', + COMPLETE: 'Oh, thank you! That helps a lot. Your reward is...'}, + 3212: {QUEST: '_toNpcName_ needs some help looking for something she lost.\x07Go visit her and see what you can do._where_'}, + 3213: {QUEST: 'Hi, _avName_. Can you help me?\x07I seem to have misplaced my pen. I think maybe some Cogs took it.\x07Defeat Cogs to find my stolen pen.', + INCOMPLETE_PROGRESS: 'Have you found my pen yet?'}, + 3214: {QUEST: "Yes, that's my pen! Thanks so much!\x07But while you were gone I realized my inkwell was missing too.\x07Defeat Cogs to find my inkwell.", + INCOMPLETE_PROGRESS: "I'm still looking for my inkwell!"}, + 3215: {QUEST: "Great! Now I have my pen and my inkwell back!\x07But wouldn't you know it?\x07My notepad is gone! They must have stolen it too!\x07Defeat Cogs to find my stolen notepad, and then bring it back for your reward.", + INCOMPLETE_PROGRESS: 'Any word on that notepad yet?'}, + 3216: {QUEST: "That's my notepad! Hooray! Your reward is...\x07Hey! Where did it go?\x07I had your reward right here in my office lockbox. But the whole lockbox is gone!\x07Can you believe it? Those Cogs stole your reward!\x07Defeat Cogs to recover my lockbox.\x07When you bring it back to me I'll give you your reward.", + INCOMPLETE_PROGRESS: 'Keep looking for that lockbox! It has your reward inside it!', + COMPLETE: 'Finally! I had your new gag bag in that lockbox. Here it is...'}, + 3217: {QUEST: "We've been performing some studies on Sellbot mechanics.\x07We still need to study some pieces more closely.\x07Bring us a sprocket from a Name Dropper.\x07You can catch one when the Cog is exploding."}, + 3218: {QUEST: 'Good job! Now we need a sprocket from a Glad Hander for comparison.\x07These sprockets are harder to catch, so keep trying.'}, + 3219: {QUEST: 'Great! Now we need just one more sprocket.\x07This time, we need a sprocket from a Mover & Shaker.\x07You might need to look inside some Sellbot buildings to find these Cogs.\x07When you catch one, bring it back for your reward.'}, + 3244: {QUEST: "We've been performing some studies on Lawbot mechanics.\x07We still need to study some pieces more closely.\x07Bring us a sprocket from an Ambulance Chaser.\x07You can catch one when the Cog is exploding."}, + 3245: {QUEST: 'Good job! Now we need a sprocket from a Back Stabber for comparison.\x07These sprockets are harder to catch, so keep trying.'}, + 3246: {QUEST: 'Great! Now we need just one more sprocket.\x07This time, we need a sprocket from a Spin Doctor.\x07When you catch one, bring it back for your reward.'}, + 3220: {QUEST: "I just heard that _toNpcName_ was asking around for you.\x07Why don't you drop by and see what she wants?_where_"}, + 3221: {QUEST: 'Hi, _avName_! There you are!\x07I heard you were quite an expert in squirt attacks.\x07I need someone to set a good example for all the Toons in ' + lDaisyGardens + '.\x07Use your squirt attacks to defeat a bunch of Cogs.\x07Encourage your friends to use squirt too.\x07When you have defeated 20 Cogs, come back here for a reward!'}, + 3222: {QUEST: "It's time to demonstrate your Toonmanship.\x07If you successfully reclaim a number of Cog buildings, you'll earn the right to carry three quests.\x07First, defeat any two Cog buildings.\x07Feel free to call on your friends to help you out."}, + 3223: {QUEST: 'Great job on those buildings!\x07Now, defeat two more buildings.\x07These buildings must be at least two stories high, or higher.'}, + 3224: {QUEST: 'Fantastic!\x07Now just defeat two more buildings.\x07These buildings must be at least three stories high.\x07When you finish, come back for your reward!', + COMPLETE: 'You did it, _avName_!\x07You demonstrated your superior Toonmanship.', + GREETING: ''}, + 3225: {QUEST: "_toNpcName_ says she needs some help.\x07Why don't you go see what you can do to help out?_where_"}, + 3235: {QUEST: "Oh, this is the salad I ordered!\x07Thank you for bringing it to me.\x07All those Cogs must have frightened away _toNpcName_'s regular delivery person again.\x07Why don't you do us a favor and defeat some of the Cogs out there?\x07Defeat 10 Cogs in " + lDaisyGardens + ' and then report back to _toNpcName_.', + INCOMPLETE_PROGRESS: "You're working on defeating Cogs for me?\x07That's wonderful! Keep up the good work!", + COMPLETE: 'Oh, thank you so much for defeating those Cogs!\x07Now maybe I can keep my regular delivery schedule.\x07Your reward is...', + INCOMPLETE_WRONG_NPC: "Go tell _toNpcName_ about the Cogs you've defeated._where_"}, + 3236: {QUEST: 'There are far too many Lawbots out there.\x07You can do your part to help!\x07Defeat 3 Lawbot buildings.'}, + 3237: {QUEST: 'Great job on those Lawbot buildings!\x07But now there are too many Sellbots!\x07Defeat 3 Sellbot buildings, then come back for your reward.'}, + 3238: {QUEST: 'Oh no! A "Mingler" Cog has stolen the Key to ' + lDaisyGardens + '!\x07See if you can recover it.\x07Remember, The Mingler can be found only inside Sellbot buildings.'}, + 3239: {QUEST: "You found a key all right, but it isn't the right one!\x07We need the Key to " + lDaisyGardens + '.\x07Keep looking! A "Mingler" Cog still has it!'}, + 3242: {QUEST: 'Oh no! A Legal Eagle Cog has stolen the Key to ' + lDaisyGardens + '!\x07See if you can recover it.\x07Remember, Legal Eagles can be found only inside Lawbot buildings.'}, + 3243: {QUEST: "You found a key all right, but it isn't the right one!\x07We need the Key to " + lDaisyGardens + '.\x07Keep looking! A Legal Eagle Cog still has it!'}, + 3240: {QUEST: "I've just heard from _toNpcName_ that a Legal Eagle stole a bag of his bird seed.\x07Defeat Legal Eagles until you recover Bud's bird seed, and take it to him.\x07Legal Eagles are only found inside Lawbot buildings._where_", + COMPLETE: 'Oh, thank you so much for finding my bird seed!\x07Your reward is...', + INCOMPLETE_WRONG_NPC: 'Good job getting that bird seed back!\x07Now take it to _toNpcName_._where_'}, + 3241: {QUEST: 'Some of the Cog buildings out there are getting too tall for our comfort.\x07See if you can bring down some of the tallest buildings.\x07Rescue 5 3-story buildings or taller and come back for your reward.'}, + 3250: {QUEST: 'Detective Lima over on Oak Street has heard some reports of a Sellbot Headquarters.\x07Head over there and help her investigate.'}, + 3251: {QUEST: "There is something strange going on around here.\x07There are so many Sellbots!\x07I've heard they have organized their own headquarters at the end of this street.\x07Head down the street and see if you can get to the bottom of this.\x07Find Sellbot Cogs in their headquarters, defeat 5 of them, and report back."}, + 3252: {QUEST: "Ok, spill the beans.\x07What's that you say?\x07Sellbot Headquarters?? Oh no!!! Something must be done.\x07We must notify Judge McIntosh - she'll know what to do.\x07Go at once and tell her what you have found out. She's just down the street."}, + 3253: {QUEST: "Yes, can I help you? I'm very busy.\x07Eh? Cog Headquarters?\x07Eh? Nonsense. That could never happen.\x07You must be mistaken. Preposterous.\x07Eh? Don't argue with me.\x07Ok then, bring back some proof.\x07If Sellbots really are building this Cog HQ, any Cog there will be carrying blueprints.\x07Cogs love paperwork, you know?\x07Defeat Sellbots in there until you find blueprints.\x07Bring them back here and maybe I'll believe you."}, + 3254: {QUEST: "You again, eh? Blueprints? You have them?\x07Let me see those! Hmmm... A factory?\x07That must be where they are building the Sellbots... And what's this?\x07Yes, just what I suspected. I knew it all along.\x07They are building a Sellbot Cog Headquarters.\x07This is not good. Must make some phone calls. Very busy. Goodbye!\x07Eh? Oh yes, take these blueprints back to Detective Lima.\x07She can make more sense of them.", + COMPLETE: "What did Judge McIntosh say?\x07We were right? Oh no. Let's see those blueprints.\x07Hmmm... Looks like Sellbots constructed a factory with machinery for building Cogs.\x07Sounds very dangerous. Stay out until you have more Laff points.\x07When you have more Laff points, we have much more to learn about Sellbot HQ.\x07For now, nice work, here is your reward."}, + 3255: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'}, + 3256: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'}, + 3257: {QUEST: '_toNpcName_ is investigating Sellbot Headquarters.\x07Go see if you can help._where_'}, + 3258: {QUEST: 'There is much confusion about what the Cogs are up to in their new headquarters.\x07I need you to bring back some information directly from them.\x07If we can get four internal memos from Sellbots inside their HQ, that will clear things up.\x07Bring back your first memo to me so we can learn more.'}, + 3259: {QUEST: 'Great! This let\'s see what the memo says....\x07"Attn Sellbots:"\x07"I\'ll be in my office at the top of Sellbot Towers promoting Cogs to higher levels."\x07"When you earn enough merits enter the elevator in the lobby to see me."\x07"Break time\'s over - back to work!"\x07"Signed, Sellbot V.P."\x07Aha.... Flippy will want to see this. I\'ll send it to him right now.\x07Please go get your second memo and bring it back.'}, + 3260: {QUEST: 'Oh good, you\'re back. Let\'s see what you found....\x07"Attn Sellbots:"\x07"Sellbot Towers has installed a new security system to keep all Toons out."\x07"Toons caught in Sellbot Towers will be detained for questioning."\x07"Please meet in the lobby for appetizers to discuss."\x07"Signed, Mingler"\x07Very interesting... I\'ll pass on this information immediately.\x07Please bring a third memo back.'}, + 3261: {QUEST: 'Excellent job _avName_! What does the memo say?\x07"Attn Sellbots:"\x07"Toons have somehow found a way to infiltrate Sellbot Towers."\x07"I\'ll call you tonight during dinner to give you the details."\x07"Signed, Telemarketer"\x07Hmmm... I wonder how Toons are breaking in....\x07Please bring back one more memo and I think we\'ll have enough info for now.', + COMPLETE: 'I knew you could do it! Ok, the memo says....\x07"Attn Sellbots:"\x07"I was having lunch with Mr. Hollywood yesterday."\x07"He reports that the V.P. is very busy these days."\x07"He will only be taking appointments from Cogs that deserve a promotion."\x07"Forgot to mention, Gladhander is golfing with me on Sunday."\x07"Signed, Name Dropper"\x07Well... _avName_, this has been very helpful.\x07Here is your reward.'}, + 3262: {QUEST: "_toNpcName_ has some new information about the Sellbot HQ Factory.\x07Go see what he's got._where_"}, + 3263: {GREETING: 'Hi buddy!', + QUEST: 'I\'m Coach Zucchini, but you can just call me Coach Z.\x07I put the "squash" in squash and stretch, if you know what I mean.\x07Listen, Sellbots have finished an enormous factory to pump out Sellbots 24 hours a day.\x07Get a group of Toon buddies together and squash the factory!\x07Inside Sellbot HQ, look for the tunnel to the Factory then board the Factory elevator.\x07Make sure you have full gags, full Laff points, and some strong Toons as guides.\x07Defeat the Foreman inside the factory to slow the Sellbot progress.\x07Sounds like a real workout, if you know what I mean.', + LEAVING: 'See ya buddy!', + COMPLETE: 'Hey buddy, nice work on that Factory!\x07Looks like you found part of a Cog suit.\x07It must be left over from their Cog manufacturing process.\x07That may come in handy. Keep collecting these when you have spare time.\x07Maybe when you collect an entire Cog suit it could be useful for something....'}, + 4001: {GREETING: '', + QUEST: 'Now you get to choose the next gag track you want to learn.\x07Take your time deciding, and come back here when you are ready to choose.', + INCOMPLETE_PROGRESS: 'Think about your decision before choosing.', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'A wise decision...', + LEAVING: QuestsDefaultLeaving}, + 4002: {GREETING: '', + QUEST: 'Now you get to choose the next gag track you want to learn.\x07Take your time deciding, and come back here when you are ready to choose.', + INCOMPLETE_PROGRESS: 'Think about your decision before choosing.', + INCOMPLETE_WRONG_NPC: 'Think about your decision before choosing.', + COMPLETE: 'A wise decision...', + LEAVING: QuestsDefaultLeaving}, + 4200: {QUEST: "I bet Tom could use some help with some research he's doing._where_"}, + 4201: {GREETING: 'Howdy!', + QUEST: "I'm very concerned about a rash of musical instrument theft.\x07I'm conducting a survey among my fellow merchants.\x07Perhaps I can find a pattern to help me crack this case.\x07Stop by and ask Tina for a concertina inventory._where_"}, + 4202: {QUEST: 'Yes, I talked to Tom this morning.\x07I have the inventory right here.\x07Bring it right back to him, ok?_where_'}, + 4203: {QUEST: "Great! One down...\x07Now swing by and get Yuki's._where_"}, + 4204: {QUEST: 'Oh! The inventory!\x07I forgot all about it.\x07I bet I can have it done by the time you defeat 10 Cogs.\x07Stop in after that and I promise it will be ready.', + INCOMPLETE_PROGRESS: '31, 32... DOH!\x07You made me lose count!', + GREETING: ''}, + 4205: {QUEST: 'Ah, there you are.\x07Thanks for giving me some time.\x07Take this to Tom and tell him I said Hello._where_'}, + 4206: {QUEST: "Hmmm, very interesting.\x07Now we are getting somewhere.\x07Ok, the last inventory is Fifi's._where_"}, + 4207: {QUEST: "Inventory?\x07How can I do an inventory if I don't have the form?\x07Go see Cleff and see if he has one for me._where_", + INCOMPLETE_PROGRESS: 'Any sign of that form yet?'}, + 4208: {QUEST: "Sure I got an inventory form, mon!\x07But dey ain't free, you know.\x07I'll tell you woht. I trade you for a whole cream pie.", + GREETING: 'Hey, mon!', + LEAVING: 'Cool runnings...', + INCOMPLETE_PROGRESS: "A slice won't do.\x07I be hungry, mon. I need de WHOLE pie."}, + 4209: {GREETING: '', + QUEST: 'Mmmm...\x07Dem mighty nice!\x07Here be your form for Fifi._where_'}, + 4210: {GREETING: '', + QUEST: "Thank you. That's a big help.\x07Let's see...Fiddles: 2\x07All done! Off you go!_where_", + COMPLETE: "Great work, _avName_.\x07I'm sure I'll get to the bottom of these thefts now.\x07Why don't you get to the bottom of this!"}, + 4211: {QUEST: 'Say, Dr. Fret keeps calling every five minutes. Can you go see what his problem is?_where_'}, + 4212: {QUEST: "Whew! I'm glad Toon HQ finally sent somebody.\x07I haven't had a customer in days.\x07It's these darned Number Crunchers every where.\x07I think they are teaching our residents bad oral hygiene.\x07Defeat ten of them and let's see if business picks up.", + INCOMPLETE_PROGRESS: 'Still no customers. But keep it up!'}, + 4213: {QUEST: "You know maybe it wasn't the Number Crunchers after all.\x07Maybe it's just the Cashbots in general.\x07Take out twenty of them and hopefully someone will come in for at least a checkup.", + INCOMPLETE_PROGRESS: "I know twenty is a lot. But I'm sure it's going to pay off in spades."}, + 4214: {GREETING: '', + LEAVING: '', + QUEST: "I just don't understand it!\x07Still not a SINGLE customer.\x07Maybe we need to go to the source.\x07Try reclaiming a Cashbot Cog building.\x07That Should do the trick...", + INCOMPLETE_PROGRESS: 'Oh, please! Just one little building...', + COMPLETE: "Still not a soul in here.\x07But you know, come to think of it.\x07I didn't have any customers before the Cogs invaded either!\x07I really appreciate all your help though.\x07This should help you get around."}, + 4215: {QUEST: "Anna desperately needs someone to help her.\x07Why don't you drop in and see what you can do._where_"}, + 4216: {QUEST: "Thanks for coming so quickly!\x07Seems like the Cogs have made off with several of my customers' cruise tickets.\x07Yuki said she saw a Glad Hander leaving here with his glad hands full of them.\x07See if you can get Lumber Jack's ticket to Alaska back.", + INCOMPLETE_PROGRESS: 'Those Glad Handers could be anywhere now...'}, + 4217: {QUEST: "Oh, great. You found it!\x07Now be a trooper and run in by Jack's for me, would you?_where_"}, + 4218: {QUEST: "Great Googely Moogely!\x07Alaska here I come!\x07I can't take these infernal Cogs anymore.\x07Say, I think Anna needs you again._where_"}, + 4219: {QUEST: "Yup, you guessed it.\x07I need you to shake down those pesky Glad Handers for Tabitha's ticket to Jazzfest.\x07You know the procedure...", + INCOMPLETE_PROGRESS: "There's more out there somewhere..."}, + 4220: {QUEST: 'Sweet!\x07Could you swing this one by his place for me too?_where_'}, + 4221: {GREETING: '', + LEAVING: 'Be cool...', + QUEST: "Cool, daddio!\x07Now I'm in fat city, _avName_.\x07Before you split, you better go check out Anna Banana again..._where_"}, + 4222: {QUEST: "This is the last one, I promise!\x07Now you are looking for Barry's ticket to the big singing contest.", + INCOMPLETE_PROGRESS: "C'mon, _avName_.\x07Barry is counting on you."}, + 4223: {QUEST: "This should put a smile on Barry's face._where_"}, + 4224: {GREETING: '', + LEAVING: '', + QUEST: 'Hello, Hello, HELLO!\x07Terrific!\x07I just know me and the boys are going to clean up this year.\x07Anna says to swing back by and get your reward._where_\x07Goodbye, Goodbye, GOODBYE!', + COMPLETE: 'Thanks for all your help, _avName_.\x07You really are an asset here in Toontown.\x07Speaking of assets...'}, + 902: {QUEST: 'Go see Leo.\x07He needs someone to deliver a message for him._where_'}, + 4903: {QUEST: 'Dude!\x07My castanets are all cloudy and I have a big show tonight.\x07Take them to Carlos and see if he can polish them up._where_'}, + 4904: {QUEST: 'Jes, I tink I can polish dees.\x07But I need soma de blue ink from de squid.', + GREETING: 'Hola!', + LEAVING: 'Adios!', + INCOMPLETE_PROGRESS: "Juo can find de squid wherever dere's a fishing pier"}, + 4905: {QUEST: "Jes! Dat's it!\x07Now I need a leetle time to polish dees.\x07Why don juo go takeover a one story beelding while I work?", + GREETING: 'Hola!', + LEAVING: 'Adios!', + INCOMPLETE_PROGRESS: 'Jest anodder minute...'}, + 4906: {QUEST: 'Bery good!\x07Here are de castanets for Leo._where_'}, + 4907: {GREETING: '', + QUEST: "Cool, dude!\x07They look awesome!\x07Now I need you to get a copy of the lyrics to 'A Beat Christmas' from Hedy._where_"}, + 4908: {QUEST: "Hello there!\x07Hmmm, I don't have a copy of that song handy.\x07If you give me a little while I could transcribe it from memory.\x07Why don't you run along and reclaim a two story building while I write?"}, + 4909: {QUEST: "I'm sorry.\x07My memory is getting a little fuzzy.\x07If you go reclaim a three story building I'm sure I'll be done when you get back..."}, + 4910: {QUEST: 'All done!\x07Sorry it took so long.\x07Take this back to Leo._where_', + GREETING: '', + COMPLETE: 'Awesome, dude!\x07My concert is gonna rock!\x07Speaking of rock, you can rock some Cogs with this...'}, + 5247: {QUEST: 'This neighborhood is pretty tough...\x07You might want to learn some new tricks.\x07_toNpcName_ taught me everything I know, so maybe he can help you too._where_'}, + 5248: {GREETING: 'Ahh, yes.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You appear to be struggling with my assignment.', + QUEST: 'Ahh, so welcome, new apprentice.\x07I know all there is to know about the pie game.\x07But before we can begin your training, a small demonstration is necessary.\x07Go out and defeat ten of the largest Cogs.'}, + 5249: {GREETING: 'Mmmmm.', + QUEST: 'Excellent!\x07Now demonstrate your skill as a fisherman.\x07I dropped three fuzzy dice in the pond yesterday.\x07Fish them out and bring them to me.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'It seems you may not be so clever with the rod and reel.'}, + 5250: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Lawbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5258: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Bossbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5259: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Cashbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5260: {GREETING: '', + LEAVING: '', + QUEST: 'Aha! These dice will look great hanging from the rearview mirror of my ox cart!\x07Now, show me that you can tell your enemies from one another.\x07Return when you have restored two of the tallest Sellbot buildings.', + INCOMPLETE_PROGRESS: 'Do the buildings give you trouble?'}, + 5200: {QUEST: 'Those sneaky Cogs are at it again.\x07_toNpcName_ has reported another missing item. Stop by and see if you can straighten it out._where_'}, + 5201: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Head Hunters came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5261: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Two-Faces came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5262: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Money Bags came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5263: {GREETING: '', + QUEST: 'Hi, _avName_. I reckon I should thank you for coming.\x07A group of those Spin Doctors came in and stole my soccer ball.\x07The leader told me that I had to make some cutbacks and just grabbed it away from me!\x07Can you get my ball back?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck finding my soccer ball?', + COMPLETE: 'Yeehaw! You found it!\x07 Here, take your reward...'}, + 5202: {QUEST: lTheBrrrgh + " has been overrun with some of the toughest Cogs we've seen yet.\x07You will probably want to carry more gags around here.\x07I hear _toNpcName_ may have a large bag you can use to carry more gags._where_"}, + 5203: {GREETING: 'Huh? Are you on my sledding team?', + QUEST: "What's that? You want a bag?\x07I had one somewhere around here... maybe it's in my toboggan?\x07Only... I haven't seen my toboggan since the big race!\x07Maybe one of those Cogs took it?", + LEAVING: 'Have you seen my toboggan?', + INCOMPLETE_PROGRESS: "Who are you again? Sorry, I'm a little woozy from the crash."}, + 5204: {GREETING: '', + LEAVING: '', + QUEST: "Is that my toboggan? I don't see any bag here.\x07I think Bumpy Noggin was on the team... maybe he has it?_where_"}, + 5205: {GREETING: 'Ohhh, my head!', + LEAVING: '', + QUEST: "Huh? Ted who? A bag?\x07Oh, maybe he was on our toboggan team?\x07My head hurts so much I can't think straight.\x07Could you fish me out some ice cubes from the frozen pond for my head?", + INCOMPLETE_PROGRESS: "Oww, my head's killing me! Got any ice?"}, + 5206: {GREETING: '', + LEAVING: '', + QUEST: "Ahhh, that feels much better!\x07So you're looking for Ted's bag, huh?\x07I think it ended up on Sam Simian's head after the crash._where_"}, + 5207: {GREETING: 'Eeeep!', + LEAVING: '', + QUEST: 'What is bag? Who is Bompy?\x07Me scared of buildings! You beat buildings, I give you bag!', + INCOMPLETE_PROGRESS: 'More buildings! Me still scared!', + COMPLETE: 'Ooooh! Me like you!'}, + 5208: {GREETING: '', + LEAVING: 'Eeeek!', + QUEST: 'Ooooh! Me like you!\x07Go to Ski Clinic. Bag there.'}, + 5209: {GREETING: 'Dude!', + LEAVING: 'Later!', + QUEST: "Man, that Simian Sam is crazy!\x07If you're wild like Sam, I'll give you your bag, man.\x07Go bag some Cogs for your bag, man! Hey now!", + INCOMPLETE_PROGRESS: "Are you sure you're extreme enough? Go bag some more Cogs.", + COMPLETE: "Hey, you are pretty wild! That was a heap of Cogs you bagged!\x07Here's your bag!"}, + 5210: {QUEST: '_toNpcName_ is secretly in love with someone in the neighborhood.\x07If you help her, she may reward you handsomely._where_'}, + 5211: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Cogs with a beak came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5264: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Cogs with a fin came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5265: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Mingler Cogs came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5266: {GREETING: 'Boo hoo.', + QUEST: 'I spent all last night writing a letter to the dog I love.\x07But before I could deliver it, one of those nasty Corporate Raiders came in and took it.\x07Can you get it back for me?', + LEAVING: 'Boo hoo.', + INCOMPLETE_PROGRESS: 'Please find my letter.'}, + 5212: {QUEST: 'Oh, thank you for finding my letter!\x07Please, please, please could you deliver it to the most handsome dog in the neighborhood?', + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "You didn't deliver my letter, did you?"}, + 5213: {GREETING: "Charmed, I'm sure.", + QUEST: "I can't be bothered with your letter, you see.\x07All my doggies have been taken from me!\x07If you bring them back, maybe we can talk then.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'My poor little doggies!'}, + 5214: {GREETING: '', + LEAVING: 'Toodleloo!', + QUEST: "Thank you for bringing back my little beauties.\x07Let's take a look at your letter now...\nMmmm, it seems I have yet another secret admirer.\x07This calls for a trip to my dear friend Carl.\x07I'm sure you'll like him immensely._where_"}, + 5215: {GREETING: 'Heh, heh...', + LEAVING: 'Come back, yes, yes.', + INCOMPLETE_PROGRESS: "There are still some big ones around. Comes back to us when they're gone.", + QUEST: "Who sent you to us? We don't like Snootsies much, we don't...\x07But we likes Cogs even less...\x07Run the big ones off and we'll helps you we will."}, + 5216: {QUEST: 'We told you we would helps you.\x07So take this ring to the girl.', + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You still haves the ring???', + COMPLETE: 'Oh darrrling!!! Thank you!!!\x07Oh, and I have something special for you as well.'}, + 5217: {QUEST: 'It sounds like _toNpcName_ could use some help._where_'}, + 5218: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm sure there are more Minglers around somewhere.", + QUEST: "Help!!! Help!!! I can't take it anymore!\x07Those Minglers are driving me batty!!!"}, + 5219: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "That can't be all of them. I just saw one!!!", + QUEST: "Oh, thanks, but now it's the Corporate Raiders!!!\x07You've got to help me!!!"}, + 5220: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'No, no, no there was one just here!', + QUEST: "I realize now that it's those Loan Sharks!!!\x07I thought you were going to save me!!!"}, + 5221: {GREETING: '', + LEAVING: '', + QUEST: "You know what, maybe it isn't the Cogs at all!\x07Could you ask Fanny to make me a soothing potion? Maybe that would help...._where_"}, + 5222: {LEAVING: '', + QUEST: "Oh, that Harry, he sure is a card!\x07I'll whip up something that will fix him right up!\x07Oh, I appear to be out of sardine whiskers...\x07Be a dear and run down to the pond and catch some for me.", + INCOMPLETE_PROGRESS: 'Got those whiskers for me yet?'}, + 5223: {QUEST: 'Okay. Thanks, hon.\x07Here, now take this to Harry. It should calm him right down.', + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Go on now, take the potion to Harry.'}, + 5224: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Go get those Legal Eagles for me, will you?', + QUEST: "Oh thank goodness you're back!\x07Give me the potion, quick!!!\x07Glug, glug, glug...\x07That tasted awful!\x07You know, what, though? I feel much calmer. Now that I can think clearly, I realize that...\x07It was the Legal Eagles that were driving me crazy all this time!!!", + COMPLETE: "Oh boy! Now I can relax!\x07I'm sure there's something here I can give you. Oh, take this!"}, + 5225: {QUEST: 'Ever since the incident with the turnip bread, Grumpy Phil has been mad at _toNpcName_.\x07Maybe you could help Gus fix things between them?_where_'}, + 5226: {QUEST: 'Yeah, you probably heard Grumpy Phil is mad at me...\x07I was just trying to be nice with that turnip bread.\x07Maybe you can help cheer him up.\x07Phil really hates those Cashbot Cogs, especially their buildings.\x07If you reclaim some Cashbot buildings, it might help.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Maybe a few more buildings?'}, + 5227: {QUEST: "That's terrific! Go tell Phil what you've done._where_"}, + 5228: {QUEST: 'Oh he did, did he?\x07That Gus thinks he can get off so easy, does he?\x07Only broke my tooth, he did, with that turnip bread of his!\x07Maybe if you took my tooth to Dr. Mumbleface for me he could fix it.', + GREETING: 'Mmmmrrphh.', + LEAVING: 'Grumble, grumble.', + INCOMPLETE_PROGRESS: 'You again? I thought you were going to get my tooth fixed for me.'}, + 5229: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Cashbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5267: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Sellbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5268: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Lawbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5269: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "I'm still working on the tooth. It will be a bit longer.", + QUEST: "Yes, that tooth looks pretty bad, alrighty.\x07Maybe I can do something, but it will be a little while.\x07Maybe you could clear some of those Bossbot Cogs off the streets while you're waiting?\x07They're scaring off my customers."}, + 5230: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately a Robber Baron came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5270: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately a Big Cheese came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5271: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately Mr. Hollywood came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5272: {GREETING: '', + QUEST: "I'm glad you're back!\x07I gave up trying to fix that old tooth, and made a new gold tooth for Phil instead.\x07Unfortunately a Big Wig came in and took it from me.\x07Maybe you can catch him if you hurry.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find that tooth yet?'}, + 5231: {QUEST: "Great, that's the tooth alrighty!\x07Why don't you just run it over to Phil for me?", + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I bet Phil would like to see his new tooth.'}, + 5232: {QUEST: "Oh, thanks.\x07Mmmrrrphhhh\x07How's that look, huh?\x07Okay, you can tell Gus that I forgive him.", + LEAVING: '', + GREETING: ''}, + 5233: {QUEST: "Oh, that's great to hear.\x07I figured old Phil couldn't stay mad at me.\x07As a gesture of goodwill, I baked him this Pine cone bread.\x07Could you run it over to him for me?", + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "Better hurry. Pine cone bread is better when it's hot.", + COMPLETE: "Oh, what's this? For me?\x07Munch, munch...\x07Owwww! My tooth! That Gus Gooseburger!\x07Oh well, it wasn't your fault. Here, you can have this for your trouble."}, + 903: {QUEST: 'You may be ready to see _toNpcName_ the Blizzard Wizard for your final test._where_'}, + 5234: {GREETING: '', + QUEST: 'Aha, you are back.\x07Before we begin, we must eat.\x07Bring us some lumpy cheese for our broth.\x07Lumpy cheese can only be gathered from Big Cheese Cogs.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'We still need lumpy cheese'}, + 5278: {GREETING: '', + QUEST: 'Aha, you are back.\x07Before we begin, we must eat.\x07Bring us some caviar for our broth.\x07Caviar can only be gathered from Mr. Hollywood Cogs.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'We still need caviar'}, + 5235: {GREETING: '', + QUEST: 'A simple man eats with a simple spoon.\x07A Cog took my simple spoon, so I simply can not eat.\x07Return my spoon to me. I think a Robber Baron took it.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I simply must have my spoon.'}, + 5279: {GREETING: '', + QUEST: 'A simple man eats with a simple spoon.\x07A Cog took my simple spoon, so I can not eat.\x07Return my spoon to me. I think a Big Wig took it.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I simply must have my spoon.'}, + 5236: {GREETING: '', + QUEST: 'Many thanks.\x07Slurp, slurp...\x07Ahhh, now you must catch a talking toad. Try fishing in the pond.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Where is that talking toad?'}, + 5237: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You have not yet obtained dessert.', + QUEST: "Oh, that is certainly a talking toad. Give him to me.\x07What's that you say, toad?\x07Uh huh.\x07Uh huh...\x07The toad has spoken. We need dessert.\x07Bring us some ice cream cones from _toNpcName_.\x07The toad likes red bean flavored ice cream for some reason._where_"}, + 5238: {GREETING: '', + QUEST: "So the wizard sent you. I'm sad to say we're fresh out of red bean ice cream cones.\x07You see, a bunch of Cogs came in and just took them.\x07They said they were for Mr. Hollywood, or some such nonsense.\x07I'd sure appreciate if you could round them back up for me.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Have you found all my ice cream cones yet?'}, + 5280: {GREETING: '', + QUEST: "So the wizard sent you. I'm sad to say we're fresh out of red bean ice cream cones.\x07You see, a bunch of Cogs came in and just took them.\x07They said they were for The Big Cheese, or some such nonsense.\x07I'd sure appreciate if you could round them back up for me.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Have you found all my ice cream cones yet?'}, + 5239: {QUEST: "Thanks for bringing back my ice cream cones!\x07Here's one for Lil Oldman.", + GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'You better bring that ice cream to Lil Oldman before it melts.'}, + 5240: {GREETING: '', + QUEST: 'Very good. Here you go toad...\x07Slurp, slurp...\x07Okay, now we are almost ready.\x07If you can just bring me some powder to dry my hands.\x07I think those Big Wig Cogs sometimes have powder from their wigs.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find any powder?'}, + 5281: {GREETING: '', + QUEST: 'Very good. Here you go toad...\x07Slurp, slurp...\x07Okay, now we are almost ready.\x07If you can just bring me some powder to dry my hands.\x07I think those Mr. Hollywood Cogs sometimes keep powder for their noses.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Did you find any powder?'}, + 5241: {QUEST: 'Okay.\x07As I once said, to truly throw a pie, you must throw not with the hand...\x07...but with the soul.\x07I know not what that means, so I will sit and contemplate while you restore buildings.\x07Return when you have completed your task.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Your task is not yet complete.'}, + 5242: {GREETING: '', + QUEST: 'Although I still know not what I am talking about, you truly are worthy.\x07I give you a final task...\x07The talking toad would like a girlfriend.\x07Find another talking toad. The toad has spoken.', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Where is that other talking toad?', + COMPLETE: 'Whew! I am tired from all this effort. I must rest now.\x07Here, take your reward and be off.'}, + 5243: {QUEST: 'Sweaty Pete is starting to stink up the street.\x07Can you talk him into taking a shower or something?_where_'}, + 5244: {GREETING: '', + QUEST: "Yeah, I guess I do work up quite a sweat in here.\x07Mmmm, maybe if I could fix that leaky pipe in my shower...\x07I figure a gear from one of those tiny Cogs would do the trick.\x07Go find a gear from a Micromanager and we'll try it.", + LEAVING: '', + INCOMPLETE_PROGRESS: "Where's that gear you were going to get?"}, + 5245: {GREETING: '', + QUEST: 'Yup, that seemed to do the trick.\x07But I get lonely when I shower...\x07Could you go fish me up a rubber ducky to keep me company?', + LEAVING: '', + INCOMPLETE_PROGRESS: 'Any luck with that duck?'}, + 5246: {QUEST: "The ducky's great, but...\x07All those buildings around here make me nervous.\x07I'd feel a lot more relaxed if there were fewer buildings around.", + LEAVING: '', + COMPLETE: "Okay, I'll shower up now. And here's something for you too.", + INCOMPLETE_PROGRESS: "I'm still worried about buildings."}, + 5251: {QUEST: 'Lounge Lassard is supposed to be playing a gig tonight.\x07I hear he might be having some trouble with his equipment._where_'}, + 5252: {GREETING: '', + QUEST: 'Oh yeah! I could sure use some help.\x07Those Cogs came in and swiped all my gear while I was unloading the van.\x07Can you give me a hand and get back my microphone?', + LEAVING: '', + INCOMPLETE_PROGRESS: "Hey man, I can't sing without my microphone."}, + 5253: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Corporate Raiders got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5273: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Minglers got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5274: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Loan Sharks got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5275: {GREETING: '', + QUEST: "Yeah, that's my microphone all right.\x07Thanks for getting it for me, but...\x07I really need my keyboard so I can tickle the ivories.\x07I think one of those Legal Eagles got my keyboard.", + LEAVING: '', + INCOMPLETE_PROGRESS: 'No luck finding my keboard?'}, + 5254: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Mr. Hollywood, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5282: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Big Cheese, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5283: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Robber Baron, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5284: {GREETING: '', + QUEST: "All right! Now I'm in business.\x07If only they hadn't taken my platform shoes...\x07Those shoes probably ended up with a Big Wig, if I had to guess.", + LEAVING: '', + COMPLETE: "Allright!! I'm ready now.\x07Hello Brrrgh!!!\x07Huh? Where is everyone?\x07Okay, take this and round me up some fans, huh?", + INCOMPLETE_PROGRESS: "I can't perform barefoot, can I?"}, + 5255: {QUEST: 'You look like you could use more Laff points.\x07Maybe _toNpcName_ could sort you out._where_'}, + 5256: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "A deal's a deal.", + QUEST: "So you're looking for Laff points, huh?\x07Have I got a deal for you!\x07Simply take care of a few Bossbot Cogs for me...\x07And I'll make it worth your while."}, + 5276: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: "A deal's a deal.", + QUEST: "So you're looking for Laff points, huh?\x07Have I got a deal for you!\x07Simply take care of a few Lawbot Cogs for me...\x07And I'll make it worth your while."}, + 5257: {GREETING: '', + LEAVING: '', + COMPLETE: "Okay, but I'm certain I told you to round up some Lawbot Cogs.\x07Well, if you say so, but you owe me one.", + INCOMPLETE_PROGRESS: "I don't think you're done yet.", + QUEST: "You say you're done? Defeated all the Cogs?\x07You must have misunderstood, our deal was for Sellbot Cogs.\x07I'm sure I told you to defeat some Sellbot Cogs for me."}, + 5277: {GREETING: '', + LEAVING: '', + COMPLETE: "Okay, but I'm certain I told you to round up some Lawbot Cogs.\x07Well, if you say so, but you owe me one.", + INCOMPLETE_PROGRESS: "I don't think you're done yet.", + QUEST: "You say you're done? Defeated all the Cogs?\x07You must have misunderstood, our deal was for Cashbot Cogs.\x07I'm sure I told you to defeat some Cashbot Cogs for me."}, + 5301: {QUEST: "I can't help you with Laff points, but maybe _toNpcName_ will cut you a deal.\x07He's a little on tempermental side though..._where_"}, + 5302: {GREETING: '', + LEAVING: '', + COMPLETE: "I told you what?!?!\x07Thanks a bunch! Here's your Laff point!", + INCOMPLETE_PROGRESS: 'Hi!\x07What are you doing in here again!', + QUEST: 'A Laff point? I dont think so!\x07Sure, but only if you clear out some of these pesky Lawbots first.'}, + 5303: {QUEST: lTheBrrrgh + " is teeming with very dangerous Cogs.\x07If I were you, I'd carry more gags around here.\x07I hear _toNpcName_ can make you a large bag if you are willing to do the legwork._where_"}, + 5304: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'There should be plenty of Lawbots out there.\x07So get to it!', + QUEST: "A bigger bag?\x07I could probably whip one up for ya.\x07I'll need some yarn though.\x07Some Lawbots made off with mine yesterday morning."}, + 5305: {GREETING: 'Howdy!', + LEAVING: '', + INCOMPLETE_PROGRESS: "Go get some more cogs.\x07This color hasn't taken yet.", + QUEST: "That there's some fine yarn!\x07Not my first choice of color though.\x07Tell you what...\x07You go out there and beat up some of the tougher cogs...\x07And I'll get to work dyeing this yarn."}, + 5306: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'They gotta be down there somewhere...', + QUEST: "Well, the yarn is all dyed. But we've got a small problem.\x07I can't find my knitting needles anywhere.\x07Last place I saw them was down at the pond."}, + 5307: {GREETING: '', + LEAVING: 'Much obliged!', + INCOMPLETE_PROGRESS: "Rome wasn't knit in a day!", + QUEST: "Those are my needles alright.\x07While I'm knitting, why don't you go clear some of them big buildings?", + COMPLETE: "Great work!\x07And speaking of great work...\x07Here's your new bag!"}, + 5308: {GREETING: '', + LEAVING: '', + QUEST: 'I hear _toNpcName_ is having some legal troubles.\x07Can you stop by and check it out?_where_'}, + 5309: {GREETING: "I'm glad you're here...", + LEAVING: '', + INCOMPLETE_PROGRESS: 'Please hurry! The street is crawling with them!', + QUEST: "The Lawbots have really taken over out there.\x07I'm afraid they are going to take me to court.\x07Do you think you could help get them off of this street?"}, + 5310: {GREETING: '', + LEAVING: '', + INCOMPLETE_PROGRESS: 'I think I hear them coming for me...', + QUEST: "Thanks. I feel a little better now.\x07 But there is one more thing...\x07Could you drop by _toNpcName_'s and get me an alibi?_where_"}, + 5311: {GREETING: 'WHAAAA!!!!', + LEAVING: '', + INCOMPLETE_PROGRESS: "I can't help him if you can't find it!", + QUEST: "Alibi?! Why that's a great idea!\x07You'd better make it two!\x07I bet a Legal Eagle would have some..."}, + 5312: {GREETING: 'Finally!', + LEAVING: '', + INCOMPLETE_PROGRESS: '', + COMPLETE: "Whew! Am I ever relieved to have this.\x07Here's your reward...", + QUEST: "Super! You'd better run these back to _toNpcName_!"}, + 6201: {QUEST: 'Powers Erge needs some help. Could you drop by and lend her a hand?_where_'}, + 6202: {GREETING: '', + LEAVING: '', + QUEST: "Oh, a customer! Great! What can I do for you?\x07What do you mean, what can you do for me? OH! You're not a customer.\x07I remember now. You're here to help with those dreadful Cogs.\x07Well I could certainly use the help even if you're not a customer.\x07If you clean up the streets a bit, I'll have a little something for you.", + INCOMPLETE_PROGRESS: "If you don't want electricity, I can't help you until you defeat those Cogs.", + COMPLETE: "Good job on those Cogs, _avName_.\x07Now, are you sure I can't interest you in some electricity? Might come in handy....\x07No? OK, suit yourself.\x07Hunh? Oh yeah, I remember. Here ya go. This is sure to help with those nasty Cogs.\x07Keep up the good work!"}, + 6206: {QUEST: "Well, _avName_, I don't have anything for you right now.\x07Wait! I think Susan Siesta was looking for help. Why don't you go see her?_where_"}, + 6207: {GREETING: '', + LEAVING: '', + QUEST: "I'll never get rich with those darn Cogs driving away all my business!\x07You've got to help me, _avName_.\x07Clear out a few Cog buildings for the sake of the neighborhood and I'll add to your riches.", + INCOMPLETE_PROGRESS: "Poor me! Can't you get rid of those buildings?", + COMPLETE: "Now I'll be in the money! I can see it now!\x07I'll spend all my time fishing. Now, let me enrich your life a little.\x07There you go!"}, + 6211: {QUEST: 'Hey _avName_! I heard Lawful Linda was looking for you.\x07You should stop by and pay her a visit._where_'}, + 6212: {GREETING: '', + LEAVING: '', + QUEST: "Hi there! Wow, am I glad to see you!\x07I've been working on this answering machine in my spare time but I'm short a couple of parts.\x07I need three more rods and the ones from Bean Counters seem to work pretty well.\x07Could you see if you could find some rods for me?", + INCOMPLETE_PROGRESS: 'Still trying to find those rods?'}, + 6213: {GREETING: '', + LEAVING: '', + QUEST: "Oh, those will do nicely.\x07That's funny. I was sure I had a spare drive belt around here but I can't find it.\x07Could you please get one from a Money Bags for me? Thanks!", + INCOMPLETE: "Well, I can't help you until I get that drive belt."}, + 6214: {GREETING: '', + LEAVING: '', + QUEST: "Ah, that's it. Now it should run like a charm.\x07Where'd my pliers go? I can't tighten this up without the pliers.\x07Maybe pincers from a Penny Pincher would do the job?\x07If you'd go find one, I could give you a little something to help you with those Cogs.", + INCOMPLETE_PROGRESS: 'No pincers yet, hunh? Keep looking.', + COMPLETE: "Great! Now I'll just tighten this up.\x07It seems to be working now. Back in business!\x07Well, except that we don't have a phone. But I'm glad for the help, anyway.\x07I think this'll help you out with those Cogs. Good luck!"}, + 6221: {QUEST: 'I heard Rocco was looking for help. See what you can do for him._where_'}, + 6222: {GREETING: '', + LEAVING: '', + QUEST: "Yo! Youse came to da right place. I ain't too happy.\x07Yeah, I was lookin for some help wid dose Cogs. Dey always come and boss me around.\x07If you can retire some of dem Bossbots, I'll make it worth your while.", + INCOMPLETE_PROGRESS: "Hey, _avName_, what's up wid youse?\x07You gotta keep after dem Bossbots. We got a deal, remember?\x07Rocco always keeps his word.", + COMPLETE: "Yo, _avName_! Youse ok in my book.\x07Dem Bossbots ain't so bossy now, is they?\x07Here ya go! A nice big boost. Now, you stay outta trouble, ya hear!"}, + 6231: {QUEST: 'Nat over on Pajama Place heard rumors about a Cashbot Headquarters.\x07Head over there and see if you can help him out._where_'}, + 6232: {GREETING: '', + LEAVING: '', + QUEST: "I got a nibble about some strange goings on.\x07Well, maybe it's the fleas but something is going on anyway.\x07All these Cashbots!\x07I think they've opened another headquarters right off Pajama Place.\x07P.J. knows his way around.\x07Go see _toNpcName_ _where_ Ask him if he's heard anything.", + INCOMPLETE_PROGRESS: "You haven't seen P.J. yet? What's keeping you?\x07Oh, these darn fleas!"}, + 6233: {GREETING: '', + LEAVING: '', + QUEST: "Hey there _avName_, where are you headed?\x07Cashbot Headquarters?? I haven't seen anything.\x07Could you go to the end of Pajama Place and see if it's true?\x07Find some Cashbot Cogs in their headquarters, defeat a few of them, and come tell me about it.", + INCOMPLETE_PROGRESS: "Found the HQ yet? You'll need to defeat some Cashbots there to scope it out."}, + 6234: {GREETING: '', + LEAVING: '', + QUEST: "What?! There really IS a Cashbot HQ?\x07You better go tell Nat right away!\x07Who would have guessed there'd be a Cog HQ right down the street from him?", + INCOMPLETE_PROGRESS: "What did Nat have to say? You haven't seen him yet?"}, + 6235: {GREETING: '', + LEAVING: '', + QUEST: "So, I'm itching to hear what P.J. had to say.\x07Hmm...we need more information about this Cog business but I've got to get rid of these fleas!\x07I know! YOU can go find out more!\x07Go defeat Cashbots at the HQ until you find some plans then come right back!", + INCOMPLETE_PROGRESS: "No plans yet? Keep searching those Cogs!\x07They're bound to have some plans!", + COMPLETE: "You got the plans?\x07Great! Let's see what they say.\x07I see... the Cashbots built a Mint to make Cogbucks.\x07It must be FULL of Cashbots. We should find out more about this.\x07Maybe if you had a disguise. Hmmm...wait! I think I've got part of a Cog suit here somewhere....\x07Here it is! Why don't you take this for your trouble? Thanks again for your help!"}, + 6241: {QUEST: "The Countess has been looking everywhere for you! Please pay her a visit so she'll stop calling._where_"}, + 6242: {GREETING: '', + LEAVING: '', + QUEST: "_avName_, I'm counting on you to help me!\x07You see, these Cogs are making so much noise that I simply can't concentrate.\x07I keep losing count of my sheep!\x07If you'll cut down on the noise, I'll help you out too! You can count on it!\x07Now, where was I? Right, one hundred thirty-six, one hundred thirty-seven....", + INCOMPLETE_PROGRESS: "Four hundred forty-two...four hundred forty-three...\x07What? You're back already? But it's still so noisy!\x07Oh no, I've lost count again.\x07 One...two...three....", + COMPLETE: "Five hundred ninety-three...five hundred ninety-four...\x07Hello? Oh, I knew I could count on you! It's much quieter now.\x07Here you go, for all those Number Crunchers.\x07Number? Now I need to start counting all over again! One...two...."}, + 6251: {QUEST: "Poor Zari broke her zipper and now she can't make deliveries to her customers. She could sure use your help._where_"}, + 6252: {GREETING: '', + LEAVING: '', + QUEST: "Oh, hi _avName_. You're here to help with my deliveries?\x07That's terrific! This broken zipper makes it tough to zip around.\x07Let me see...ok, this should be easy. Cowboy George ordered a zither last week.\x07Could you please bring it over to him? _where_", + INCOMPLETE_PROGRESS: 'Oh, hi! Did you forget something? Cowboy George is waiting for that zither.'}, + 6253: {GREETING: '', + LEAVING: '', + QUEST: "My zither! At last! Gosh, I can't wait to play it.\x07Go tell Zari that I said thanks, would you?", + INCOMPLETE_PROGRESS: "Thanks again for the zither. Doesn't Zari have more deliveries for you to do?"}, + 6254: {GREETING: '', + LEAVING: '', + QUEST: "That was fast. What's next on my list?\x07Right. Master Mike ordered a Zamboni. That zany guy.\x07Could you bring this to him, please?_where_", + INCOMPLETE_PROGRESS: 'That Zamboni needs to go to Master Mike._where_'}, + 6255: {GREETING: '', + LEAVING: '', + QUEST: "All-right! The Zamboni I ordered!\x07Now, if only there weren't so many Cogs around, I might have some time to use it.\x07Be a sport and take care of a few of those Cashbots for me, would you?", + INCOMPLETE_PROGRESS: 'Those Cashbots are tough, hunh? They make it hard to test my Zamboni.'}, + 6256: {GREETING: '', + LEAVING: '', + QUEST: "Excellent! Now I can go try out my Zamboni.\x07Tell Zari that I'll be in next week to place my next order, please.", + INCOMPLETE_PROGRESS: "That's all I need for now. Isn't Zari waiting for you?"}, + 6257: {GREETING: '', + LEAVING: '', + QUEST: "So, Master Mike was happy with his Zamboni? Great.\x07Who's next? Oh, Zen Glen ordered a zebra-striped zabuton.\x07Here it is! Could you zoom over to his place, please?_where_", + INCOMPLETE_PROGRESS: 'I think Zen Glen needs that zabuton to meditate.'}, + 6258: {GREETING: '', + LEAVING: '', + QUEST: "Ah, my zabuton at last. Now I can meditate.\x07Who could focus with that racket going on? All those Cogs!\x07Since you're already here, maybe you could take care of some of these Cogs?\x07Then I could use my zabuton in peace.", + INCOMPLETE_PROGRESS: 'Still so noisy with those Cogs! Who could focus?'}, + 6259: {GREETING: '', + LEAVING: '', + QUEST: 'Peace and quiet at last. Thanks, _avName_.\x07Please tell Zari how happy I am. OM....', + INCOMPLETE_PROGRESS: 'Zari called looking for you. You should go see what she needs.'}, + 6260: {GREETING: '', + LEAVING: '', + QUEST: "I'm glad to hear that Zen Glen is happy with his zebra zabuton.\x07Oh, these zinnias just came in for Rose Petals.\x07Since you seem to have some zeal for deliveries, perhaps you could take them over to her?_where_", + INCOMPLETE_PROGRESS: "Those zinnias will wilt if you don't deliver them soon."}, + 6261: {GREETING: '', + LEAVING: '', + QUEST: 'What lovely zinnias! Zari sure does deliver.\x07Oh, well, I guess YOU deliver, _avName_. Please thank Zari for me!', + INCOMPLETE_PROGRESS: "Don't forget to thank Zari for the zinnias!"}, + 6262: {GREETING: '', + LEAVING: '', + QUEST: "Welcome back, _avName_. You're pretty zippy.\x07Let's see...what's next on my list to deliver? Zydeco records for Wyda Wake._where_", + INCOMPLETE_PROGRESS: "I'm sure Wyda Wake is waiting for those Zydeco records."}, + 6263: {GREETING: '', + LEAVING: '', + QUEST: "Zydeco records? I don't remember asking for Zydeco records.\x07Oh, I bet Lullaby Lou ordered them._where_", + INCOMPLETE_PROGRESS: 'No, those Zydeco records are for Lullaby Lou._where_'}, + 6264: {GREETING: '', + LEAVING: '', + QUEST: "At last, my Zydeco records! I thought Zari had forgotten.\x07Could you please bring this zucchini to her? She'll find someone who wants one. Thanks!", + INCOMPLETE_PROGRESS: "Oh, I've got plenty of zucchini already. Take that one to Zari."}, + 6265: {GREETING: '', + LEAVING: '', + QUEST: "Zucchini? Hmm. Well, someone will want it, I'm sure.\x07Ok, we're nearly done with my list. One more delivery to make.\x07Babyface MacDougal ordered a zoot suit._where_", + INCOMPLETE_PROGRESS: "If you don't deliver that zoot suit to Babyface MacDougal,\x07 it'll get all wrinkled."}, + 6266: {GREETING: '', + LEAVING: '', + QUEST: "Once upon a time...oh! You're not here for a story, are you?\x07You're delivering my zoot suit? Great! Wow, that's something.\x07Hey, could you give Zari a message for me? I'll be needing zircon cufflinks to go with the suit. Thanks!", + INCOMPLETE_PROGRESS: 'Did you give Zari my message?', + COMPLETE: "Zircon cufflinks, hunh? Well, I'll see what I can do for him.\x07Anyway, you've been the very zenith of helpfulness and I can't let you leave with zilch.\x07Here's a BIG boost to help you zap those Cogs!"}, + 6271: {QUEST: "Drowsy Dave is having some trouble that you might be able to help with. Why don't you stop by his shop?_where_"}, + 6272: {GREETING: '', + LEAVING: '', + QUEST: "What? Huh? Oh, I must've fallen asleep.\x07You know, those Cogs buildings are full of machinery that makes me really sleepy.\x07I listen to them humming all day and...\x07Huh? Oh, yeah, right. If you could get rid of some of those Cog buildings, I could stay awake.", + INCOMPLETE_PROGRESS: "Zzzzz...huh? Oh, it's you, _avName_.\x07Back already? I was just taking a little nap.\x07Come back when you're done with those buildings.", + COMPLETE: "What? I dropped off to sleep for a minute there.\x07Now that those Cog buildings are gone I can finally relax.\x07Thanks for your help, _avName_.\x07See you later! I think maybe I'll take a little nap."}, + 6281: {QUEST: "Head over and call on Teddy Blair. He's got a job for you._where_"}, + 6282: {GREETING: '', + LEAVING: '', + QUEST: "What did you say? No, I don't have a fob for you.\x07Oh, a job! Why didn't you say so? You'll need to speak up.\x07Those Cogs make it hard to hibernate. If you'll help make Dreamland quieter,\x07I'll give you a little something.", + INCOMPLETE_PROGRESS: "You beat the bogs? What bogs?\x07Oh, the Cogs! Why didn't you say so?\x07Hmm, it's still pretty loud. How 'bout you defeat a few more?", + COMPLETE: "You had fun? Huh? Oh!\x07You're done! Great. Really nice of you to help out this way.\x07I found this in the back room but I don't have any use for it.\x07Maybe you'll find something to do with it. So long, _avName_!"}, + 6291: {QUEST: 'Cogs broke into the First Security Blanket Bank! Go see William Teller and see if you can help.'}, + 6292: {QUEST: 'Oh those darn Cashbot Cogs! They stole my reading lamps!\x07I need them back right away. Can you go look for them?\x07If you can get my reading lamps, I might be able to help you get into see the C.F.O.\x07Hurry!', + INCOMPLETE_PROGRESS: 'I need those lamps back. Keep looking for them!', + COMPLETE: "You're back! And you got my lamps!\x07I can't thank you enough but I can give you this."}, + 7201: {QUEST: 'Nina Nightlight was looking for you, _avName_. She needs some help._where_'}, + 7202: {GREETING: '', + LEAVING: '', + QUEST: "Oh! I'm so glad to see you, _avName_. I could use some help!\x07Those darn Cogs have kept the delivery folks away and I have no beds in stock.\x07Could you go see Hardy O'Toole and bring me back a bed?_where_ ", + INCOMPLETE_PROGRESS: "Did Hardy have any beds? I was sure he'd have one.", + COMPLETE: ''}, + 7203: {GREETING: '', + LEAVING: '', + QUEST: 'A bed? Sure, here\'s one all ready to go.\x07Just bring it over to her for me, would you? Get it?\x07"WOOD" you? Hee-hee!\x07Pretty funny. No? Well, take it over there anyway, please?', + INCOMPLETE_PROGRESS: 'Did Nina like the bed?', + COMPLETE: ''}, + 7204: {GREETING: '', + LEAVING: '', + QUEST: "This bed isn't right. It's much too plain.\x07Go see if he has anything fancier, would you?\x07I'm sure it won't take but a minute.", + INCOMPLETE_PROGRESS: "I'm certain that Hardy has a fancier bed.", + COMPLETE: ''}, + 7205: {GREETING: '', + LEAVING: '', + QUEST: "Didn't hit the nail on the head with that bed, huh? I've got one here that will do the job.\x07One small problem though - it needs to be assembled first.\x07While I hammer out this problem, could you get rid of some of those Cogs that are outside?\x07Those awful Cogs throw a wrench in the works.\x07Come back when you're done and the bed will be ready.", + INCOMPLETE_PROGRESS: "Not quite done with assembling the bed.\x07When you're done with the Cogs, it'll be ready.", + COMPLETE: ''}, + 7206: {GREETING: '', + LEAVING: '', + QUEST: 'Hey there _avName_!\x07You did a bang-up job on those Cogs.\x07The bed is all ready. Could you please deliver it for me?\x07Now that those Cogs are gone, business will be brisk!', + INCOMPLETE_PROGRESS: "I think Nina's waiting for that bed delivery.", + COMPLETE: 'What a lovely bed!\x07Now my customers will be happy. Thanks, _avName_.\x07Say, you might be able to use this. Someone left it here.'}, + 7209: {QUEST: 'Go see Honey Moon. She needs some help._where_'}, + 7210: {GREETING: '', + LEAVING: '', + QUEST: "Oh! I'm so glad to see you, _avName_. I really need some help!\x07I haven't been able to get my beauty sleep for ages. You see, those Cogs stole my bedspread.\x07Say, could you please run over and see if Ed's got anything in blue?_where_", + INCOMPLETE_PROGRESS: 'What did Ed have to say about a blue bedspread?', + COMPLETE: ''}, + 7211: {GREETING: '', + LEAVING: '', + QUEST: "So, Honey wants a bedspread, huh?\x07What color? BLUE?!\x07Well, I'd have to make that for her special. Everything I've got is red.\x07Tell ya what...if you'll go deal with some of those Cogs out there, I'll make a special blue bedspread just for her.\x07Blue bedspreads...what'll it be next?", + INCOMPLETE_PROGRESS: 'Still working on this blue bedspread, _avName_. Keep at those Cogs!', + COMPLETE: ''}, + 7212: {GREETING: '', + LEAVING: '', + QUEST: "Nice to see you again. I've got something for you!\x07Here's the bedspread and it's blue. She'll love it.", + INCOMPLETE_PROGRESS: 'Did Honey like the bedspread?', + COMPLETE: ''}, + 7213: {GREETING: '', + LEAVING: '', + QUEST: "My bedspread? No, that's not right.\x07It's PLAID! How can anyone sleep with such a LOUD pattern?\x07You'll just have to take it back and get a different one.\x07I'm sure he'll have others.", + INCOMPLETE_PROGRESS: 'I simply will not accept a plaid bedspread. See what Ed can do about it.', + COMPLETE: ''}, + 7214: {GREETING: '', + LEAVING: '', + QUEST: "What? She doesn't like PLAID?\x07Hmm...let me see what we've got here.\x07This will take a while. Why don't you go take care of a few Cogs while I try to find something else?\x07I'll have something by the time you get back here.", + INCOMPLETE_PROGRESS: "I'm still looking for another bedspread. How's it going with the Cogs?", + COMPLETE: ''}, + 7215: {GREETING: '', + LEAVING: '', + QUEST: "Hey, good job on those Cogs!\x07Here you go, it's blue and it's not plaid.\x07Sure hope she likes paisley.\x07Bring the bedspread back to Honey.", + INCOMPLETE_PROGRESS: "That's all I've got for you right now.\x07Please bring that bedspread to Honey.", + COMPLETE: "Oh! That's lovely! Paisley suits me quite well.\x07Time for my beauty sleep, then! So long, _avName_.\x07What? You're still here? Can't you see I'm trying to sleep?\x07Here, take this and let me rest. I must look a fright!"}, + 7218: {QUEST: 'Dreamy Daphne could use a hand._where_'}, + 7219: {GREETING: '', + LEAVING: '', + QUEST: "Oh, _avName_, I'm glad to see you! Those Cogs took my pillows.\x07Could you go see if Tex has some pillows?_where_\x07I'm sure he can help.", + INCOMPLETE_PROGRESS: 'Does Tex have any pillows for me? ', + COMPLETE: ''}, + 7220: {GREETING: '', + LEAVING: '', + QUEST: "Howdy! Daphne needs some pillows, huh? Well, you came to the right place, pardner!\x07More pillows in here than there's spines on a cactus.\x07Here you go, _avName_. Take these back over to Daphne with my compliments.\x07Always glad to help a gal out.", + INCOMPLETE_PROGRESS: 'Were those pillows soft enough for the little lady?', + COMPLETE: ''}, + 7221: {GREETING: '', + LEAVING: '', + QUEST: "You got the pillows! Great!\x07Hey, wait a second! These pillows are awfully soft.\x07Much too soft for me. I need harder pillows.\x07Take these back to Tex and see what else he's got. Thanks.", + INCOMPLETE_PROGRESS: 'Nope! Too soft. Ask Tex for different pillows.', + COMPLETE: ''}, + 7222: {GREETING: '', + LEAVING: '', + QUEST: "Too soft, huh? Well, let me see what I've got....\x07Hmm...seems I had me a whole passel of hard pillows. Where'd they get to?\x07Oh! I remember. I was fixing to send them back so they're in storage.\x07How 'bout you clean up some of those Cog buildings out there while I get 'em out of storage, pardner?", + INCOMPLETE_PROGRESS: "Cog buildings are hard. But these pillows aren't.\x07I'll keep looking.", + COMPLETE: ''}, + 7223: {GREETING: '', + LEAVING: '', + QUEST: "Back already? Well, that's jess fine. See, I found those pillows Daphne wanted.\x07Now, you jess take these over to her. They're hard enough to break a tooth on!", + INCOMPLETE_PROGRESS: "Yeah, those pillows are mighty hard. I hope Daphne fancies 'em.", + COMPLETE: 'I knew Tex would have some harder pillows.\x07Oh yes, those are perfect. Nice and hard.\x07Would you have a use for this piece of a Cog suit? Might as well take it with you.'}, + 7226: {QUEST: "Drop by to see Sandy Sandman. She's lost her pajamas._where_"}, + 7227: {GREETING: '', + LEAVING: '', + QUEST: "I have no pajamas! They're missing!\x07What will I do? Oh! I know!\x07Go see Big Mama. She'll have pajamas for me._where_", + INCOMPLETE_PROGRESS: 'Does Big Mama have pajamas for me?', + COMPLETE: ''}, + 7228: {GREETING: '', + LEAVING: '', + QUEST: "Hey there, little toon! Big Mama's got the best pajamas from the Bahamas.\x07Oh, something for Sandy Sandman, huh? Well, let me see what I've got.\x07Here's a little something. Now she can sleep in style!\x07Would you run these back over to her for me? I can't leave the shop just now.\x07Thanks, _avName_. See you around!", + INCOMPLETE_PROGRESS: 'You need to take those pajamas to Sandy._where_', + COMPLETE: ''}, + 7229: {GREETING: '', + LEAVING: '', + QUEST: "Big Mama sent these for me? Oh...\x07Doesn't she have any pajamas with feet on them?\x07I always wear pajamas with feet. Doesn't everybody?\x07Take these back and ask her to find some with feet.", + INCOMPLETE_PROGRESS: 'My pajamas must have feet. See what Big Mama can do.', + COMPLETE: ''}, + 7230: {GREETING: '', + LEAVING: '', + QUEST: "Feet? Let me think....\x07Wait! I've got just the thing!\x07Ta-dah! Pajamas with feet. Nice blue pajamas with feet. Best ones on any island.\x07Please take them back to her, would you? Thanks!", + INCOMPLETE_PROGRESS: 'Did Sandy like the blue footie pajamas?', + COMPLETE: ''}, + 7231: {GREETING: '', + LEAVING: '', + QUEST: "Well, these DO have feet, but I can't wear blue pajamas!\x07Ask Big Mama if she has a different color.", + INCOMPLETE_PROGRESS: "I'm sure Big Mama has footie pajamas in a different color.", + COMPLETE: ''}, + 7232: {GREETING: '', + LEAVING: '', + QUEST: "That's too bad. These are the only pajamas with feet I have.\x07Oh, I've got an idea. Go ask Cat. She may have some pajamas with feet._where_", + INCOMPLETE_PROGRESS: "Nope, those are all the pajamas I've got. Go see what Cat has._where_", + COMPLETE: ''}, + 7233: {GREETING: '', + LEAVING: '', + QUEST: "Pajamas with feet? Sure thing.\x07What do you mean, these are blue? She doesn't want blue?\x07Oh, that's a little trickier. Here, try these.\x07They're not blue and they DO have feet.", + INCOMPLETE_PROGRESS: "I just love puce, don't you?\x07I hope Sandy likes them....", + COMPLETE: ''}, + 7234: {GREETING: '', + LEAVING: '', + QUEST: "No, these aren't blue but no one with my complexion could possibly wear puce.\x07Absolutely not. Back they go and you with them! See what else Cat has.", + INCOMPLETE_PROGRESS: 'Cat must have more pajamas. No puce for me!', + COMPLETE: ''}, + 7235: {GREETING: '', + LEAVING: '', + QUEST: "Not puce either. Hmm....\x07By my whiskers, I know I have some other ones.\x07They'll take a little while to find. Let's make a deal.\x07I'll find the other pajamas if you'll get rid of some of these Cog buildings. They're very unsettling.\x07I'll have the pajamas ready when you get back, _avName_.", + INCOMPLETE_PROGRESS: 'You need to clear out a few more Cog buildings while I look for other pajamas.', + COMPLETE: ''}, + 7236: {GREETING: '', + LEAVING: '', + QUEST: 'You did a great job on those Cogs! Thanks!\x07I found those pajamas for Sandy; hope she likes them.\x07Bring them over to her. Thank you.', + INCOMPLETE_PROGRESS: "Sandy's waiting for those pajamas, _avName_.", + COMPLETE: "Fuchsia pajamas with feet! Purr-fect!\x07Ah, now I'm all set. Let's see....\x07Oh, I suppose I should give you something for helping me out.\x07Maybe you can use this. Someone left it here."}, + 7239: {QUEST: "Go see Smudgy Mascara. She's been looking for some help._where_"}, + 7240: {GREETING: '', + LEAVING: '', + QUEST: 'Those darn Cogs took my wrinkle cream!\x07My customers simply MUST have wrinkle cream while I work on them.\x07Go see Rip and see if he has my special formula in stock._where_', + INCOMPLETE_PROGRESS: 'I refuse to work on anyone without wrinkle cream.\x07See what Rip has for me.'}, + 7241: {GREETING: '', + LEAVING: '', + QUEST: "Oh, that Smudgy's a picky character. She won't settle for my usual formula.\x07That means I'll need some cauliflower coral, my super-secret special ingredient. But I haven't any in stock.\x07Could you go fish some out of the pond for me? As soon as you get the coral, I'll whip up a batch for Smudgy.", + INCOMPLETE_PROGRESS: "I'll need that cauliflower coral to make a batch of wrinkle cream."}, + 7242: {GREETING: '', + LEAVING: '', + QUEST: "Wow, that's a nice cauliflower coral!\x07Ok, let's see...a little of this and a splash of that...now, just a dollop of kelp.\x07Huh, where's the kelp? Looks like I'm out of kelp, too.\x07Could you pop down to the pond and fish me out some nice, slimy kelp?", + INCOMPLETE_PROGRESS: "Not a strip of slimy kelp in the shop.\x07Can't make the cream without it."}, + 7243: {GREETING: '', + LEAVING: '', + QUEST: "Oooh! Very slimy kelp you've got there, _avName_.\x07Now, I just crush some pearls with the mortar and pestle.\x07Um, where's my pestle? What good is a mortar without a pestle?\x07I bet that darn Loan Shark took it when he came in here!\x07You need to help me find it! He was headed for Cashbot HQ!", + INCOMPLETE_PROGRESS: 'I simply cannot crush the pearls without a pestle.\x07Darn those Loan Sharks!'}, + 7244: {GREETING: '', + LEAVING: '', + QUEST: "Alright! You got my pestle!\x07Now we're in business. Crush that...stir this up and...\x07There ya go! Tell Smudgy's it's good and fresh.", + INCOMPLETE_PROGRESS: "You should bring this over to Smudgy while it's fresh.\x07She's very picky.", + COMPLETE: "Didn't Rip have a bigger jar of wrinkle cream than this? No?\x07Well, I guess I'll just order more when I run out.\x07So long, _avName_.\x07What? You're still here? Can't you see I'm trying to work?\x07Here, take this."}, + 11000: {GREETING: '', + LEAVING: '', + QUEST: 'If you are interested in Lawbot disguise parts you should visit _toNpcName_.\x07I hear he could use some help with his weather research._where_'}, + 11001: {GREETING: '', + LEAVING: '', + QUEST: 'Yes, yes. I have Lawbot disguise parts.\x07But they are of no interest to me.\x07The focus of my research is fluctuations in the ambient temperature of Toontown.\x07I will gladly trade you disguise parts for cog temperature sensors.\x07You can start on %s.' % GlobalStreetNames[2100][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[2100][-1], + COMPLETE: 'Ah, excellent!\x07Just as I feared...\x07Oh, yes! Here is your disguise part.'}, + 11002: {GREETING: '', + LEAVING: '', + QUEST: 'For more Lawbot disguise parts you should visit _toNpcName_ again.\x07I hear he needs more research assistants._where_'}, + 11003: {GREETING: '', + LEAVING: '', + QUEST: 'More Lawbot disguise parts?\x07Well, if you insist...\x07but I will require another cog temperature sensor.\x07This time look on %s.' % GlobalStreetNames[2200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[2200][-1], + COMPLETE: 'Thank you!\x07And here is your disguise part.'}, + 11004: {GREETING: '', + LEAVING: '', + QUEST: 'If you need more Lawbot disguise parts you should return to _toNpcName_.\x07I hear he still needs help with his weather reasearch._where_'}, + 11005: {GREETING: '', + LEAVING: '', + QUEST: "You're proving yourself quite useful!\x07Can you take a look on %s?" % GlobalStreetNames[2300][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[2300][-1], + COMPLETE: "Hmmm, I don't like the looks of this...\x07but here is your disguise part..."}, + 11006: {GREETING: '', + LEAVING: '', + QUEST: 'You-know-who needs more temperature readings.\x07Stop by if you would like another disguise part._where_'}, + 11007: {GREETING: '', + LEAVING: '', + QUEST: 'Back again?\x07You are very dedicated...\x07The next stop is %s.' % GlobalStreetNames[1100][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[1100][-1], + COMPLETE: 'Good! You seem to be getting the hang of this!\x07Your disguise part...'}, + 11008: {GREETING: '', + LEAVING: '', + QUEST: "If you're up for another Lawbot disguise part..._where_"}, + 11009: {GREETING: '', + LEAVING: '', + QUEST: 'Fancy seeing you here!\x07Now I need readings on %s.' % GlobalStreetNames[1200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[1200][-1], + COMPLETE: 'Thank you very much.\x07Your disguise must be getting close...'}, + 11010: {GREETING: '', + LEAVING: '', + QUEST: 'I believe _toNpcName_ has more work for you._where_'}, + 11011: {GREETING: '', + LEAVING: '', + QUEST: 'Good to see you again, _avName_!\x07Can you get a reading on %s, please?' % GlobalStreetNames[1300][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[1300][-1], + COMPLETE: "Great work!\x07Here's your well earned reward!"}, + 11012: {GREETING: '', + LEAVING: '', + QUEST: 'You know the drill._where_'}, + 11013: {GREETING: '', + LEAVING: '', + QUEST: '_avName_, my dear friend!\x07Can you go to %s and find another temperature sensor?' % GlobalStreetNames[5100][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[5100][-1], + COMPLETE: "Excellent!\x07With your help my research is coming quickly!\x07Here's your reward."}, + 11014: {GREETING: '', + LEAVING: '', + QUEST: "_toNpcName_ was asking for you by name.\x07It appears you've made quite an impression!_where_"}, + 11015: {GREETING: '', + LEAVING: '', + QUEST: "Welcome back!\x07I've been waiting for you.\x07The next reading I need is on %s." % GlobalStreetNames[5200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[5200][-1], + COMPLETE: "Thanks!\x07Here's your reward."}, + 11016: {GREETING: '', + LEAVING: '', + QUEST: 'If you need to finish your Lawbot disguise...\x07_toNpcName_ can help you out._where_'}, + 11017: {GREETING: '', + LEAVING: '', + QUEST: 'Hello, Junior Research Scientist!\x07We still need readings from %s.' % GlobalStreetNames[5300][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[5300][-1], + COMPLETE: 'Excellent job!\x07Here is your Lawbot thingy...'}, + 11018: {GREETING: '', + LEAVING: '', + QUEST: "_toNpcName_ has another job for you.\x07If you're not sick of him yet..._where_"}, + 11019: {GREETING: '', + LEAVING: '', + QUEST: 'Well, then.\x07Are you ready for another recovery?\x07This time try %s.' % GlobalStreetNames[4100][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[4100][-1], + COMPLETE: 'Another one!\x07My you are the picture of efficiency!'}, + 11020: {GREETING: '', + LEAVING: '', + QUEST: 'Are you still after Lawbot disguise parts?_where_'}, + 11021: {GREETING: '', + LEAVING: '', + QUEST: 'You could probably guess by now...\x07but I need readings from %s.' % GlobalStreetNames[4200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[4200][-1], + COMPLETE: 'Almost there!\x07Here you go...'}, + 11022: {GREETING: '', + LEAVING: '', + QUEST: 'I hate to say it, but..._where_'}, + 11023: {GREETING: '', + LEAVING: '', + QUEST: 'What do you think about %s? Could you get a sensor from there too?' % GlobalStreetNames[4300][-1], + INCOMPLETE_PROGRESS: 'Have you tried looking on %s?' % GlobalStreetNames[4300][-1], + COMPLETE: 'Another excellent job, _avName_'}, + 11024: {GREETING: '', + LEAVING: '', + QUEST: 'Go visit the Professor if you still need disguise parts._where_'}, + 11025: {GREETING: '', + LEAVING: '', + QUEST: 'I believe we still need a reading from %s.' % GlobalStreetNames[9100][-1], + INCOMPLETE_PROGRESS: "Are you sure you're looking on %s?" % GlobalStreetNames[9100][-1], + COMPLETE: 'Good work!\x07I think we are getting very close...'}, + 11026: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ has one final mission for you._where_'}, + 11027: {GREETING: '', + LEAVING: '', + QUEST: 'Back so soon?\x07The final reading is on %s.' % GlobalStreetNames[9200][-1], + INCOMPLETE_PROGRESS: 'You are looking on %s, right?' % GlobalStreetNames[9200][-1], + COMPLETE: "You're all done!\x07Now you are ready to infiltrate the District Attorney's Office and collect Jury Notices.\x07Good luck and thanks for all your help!"}, + 12000: {GREETING: '', + LEAVING: '', + QUEST: 'If you are interested in Bossbot disguise parts you should visit _toNpcName_._where_'}, + 12001: {GREETING: '', + LEAVING: '', + QUEST: "Yes, I can get you Bossbot parts.\x07But I'll need you to help me complete my Bossbot collection.\x07Go out there and defeat a Flunky.", + INCOMPLETE_PROGRESS: "You can't find a Flunky? For shame...", + COMPLETE: "You didn't flunk that, now did you?\x07Here's your first disguise part."}, + 12002: {GREETING: '', + LEAVING: '', + QUEST: "_toNpcName_ needs more help, if you're up for it._where_"}, + 12003: {GREETING: '', + LEAVING: '', + QUEST: 'Another disguise part?\x07Certainly...\x07but only if you defeat a Pencil Pusher.', + INCOMPLETE_PROGRESS: 'Pencil Pushers can be found in the streets.', + COMPLETE: "He was a real pushover!\x07Here's your second disguise part."}, + 12004: {GREETING: '', + LEAVING: '', + QUEST: "There's really only one place to go for Bossbot parts._where_"}, + 12005: {GREETING: '', + LEAVING: '', + QUEST: 'Now I need a Yesman...', + INCOMPLETE_PROGRESS: 'Yesmen can be found in the streets.', + COMPLETE: "Yes! Man, you are good.\x07Here's your third disguise part."}, + 12006: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ has more parts for you...'}, + 12007: {GREETING: '', + LEAVING: '', + QUEST: "If you defeat a Micromanager I'll give you another part.", + INCOMPLETE_PROGRESS: 'Try looking on %s' % GlobalStreetNames[1100][-1], + COMPLETE: "You managed that quite well!\x07Here's your fourth disguise part."}, + 12008: {GREETING: '', + LEAVING: '', + QUEST: 'Head on over to..._where_'}, + 12009: {GREETING: '', + LEAVING: '', + QUEST: "I'm after a Downsizer now...", + INCOMPLETE_PROGRESS: 'Having trouble? Try looking on %s' % GlobalStreetNames[3100][-1], + COMPLETE: "He went down hard!\x07Here's your fifth disguise part."}, + 12010: {GREETING: '', + LEAVING: '', + QUEST: 'I think you know where to go by now..._where_'}, + 12011: {GREETING: '', + LEAVING: '', + QUEST: 'A Head Hunter is next on my list.', + INCOMPLETE_PROGRESS: 'You might have better luck looking buildings.', + COMPLETE: "I see you had no problem hunting one down.\x07Here's your sixth disguise part."}, + 12012: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ needs more Bossbots.'}, + 12013: {GREETING: '', + LEAVING: '', + QUEST: "Next I'll need you to track down a Corporate Raider.", + INCOMPLETE_PROGRESS: 'You might have better luck looking buildings.', + COMPLETE: "You're quite the little raider yourself!\x07Here's your seventh disguise part."}, + 12014: {GREETING: '', + LEAVING: '', + QUEST: 'If you want more disguise parts, go to..._where_'}, + 12015: {GREETING: '', + LEAVING: '', + QUEST: 'Now the coup de grace: The Big Cheese!', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "I knew I could count on you to cut...\x07Ah, never mind.\x07Here's your next disguise part."}, + 12016: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ was looking for you...'}, + 12017: {GREETING: '', + LEAVING: '', + QUEST: 'Now I need you to defeat one of the new, more treacherous Bossbot Cogs.', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'They are tougher than they look, huh?\x07I guess I owe you a disguise part.'}, + 12018: {GREETING: '', + LEAVING: '', + QUEST: 'Could you swing by..._where_'}, + 12019: {GREETING: '', + LEAVING: '', + QUEST: 'These Version 2.0 Cogs are very interesting.\x07Please go defeat another one.', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'Thanks!\x07Another disguise part coming right up.'}, + 12020: {GREETING: '', + LEAVING: '', + QUEST: 'If you get a chance, stop by and see _toNpcName_.'}, + 12021: {GREETING: '', + LEAVING: '', + QUEST: 'I wonder if they can keep regenerating...', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "I guess not.\x07Here's your part..."}, + 12022: {GREETING: '', + LEAVING: '', + QUEST: 'You know..._where_'}, + 12023: {GREETING: '', + LEAVING: '', + QUEST: "Maybe they aren't Bossbots at all...", + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'Hmmm, I guess they are Bossbots after all.\x07Help yourself to another part.'}, + 12024: {GREETING: '', + LEAVING: '', + QUEST: "You probably know what I'm going to say already..."}, + 12025: {GREETING: '', + LEAVING: '', + QUEST: 'Perhaps they are related to the Skelecogs somehow...', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "That was inconclusive...\x07Here's your disguise part."}, + 12026: {GREETING: '', + LEAVING: '', + QUEST: 'Please go see _toNpcName_ again.'}, + 12027: {GREETING: '', + LEAVING: '', + QUEST: "I'm still not convinced they aren't some type of Skelecog...", + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "Well, maybe not.\x07Here's your next part."}, + 12028: {GREETING: '', + LEAVING: '', + QUEST: "It's probably the last place you want to go. but..."}, + 12029: {GREETING: '', + LEAVING: '', + QUEST: 'I am still quite baffled by these new cogs.\x07Could you go defeat another, please?', + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: 'Fascinating. Simply fascinating.\x07A disguise part for your troubles.'}, + 12030: {GREETING: '', + LEAVING: '', + QUEST: '_toNpcName_ is starting to sound like a broken record...'}, + 12031: {GREETING: '', + LEAVING: '', + QUEST: "I've almost determined what these new Cogs are.\x07Just one more...", + INCOMPLETE_PROGRESS: 'Try looking in %s' % GlobalStreetNames[10000][-1], + COMPLETE: "Yes, I think I'm onto something.\x07Oh, yes.\x07This is for you..."}, + 12032: {GREETING: '', + LEAVING: '', + QUEST: 'You need to go tell Flippy about this...', + INCOMPLETE_PROGRESS: 'Flippy can be found in Toon Hall', + COMPLETE: 'A new type of Cog!\x07Good work!\x07Here is your final disguise part.'}} +ChatGarblerDog = ['woof', 'arf', 'rruff'] +ChatGarblerCat = ['meow', 'mew'] +ChatGarblerMouse = ['squeak', 'squeaky', 'squeakity'] +ChatGarblerHorse = ['neigh', 'brrr'] +ChatGarblerRabbit = ['eek', + 'eepr', + 'eepy', + 'eeky'] +ChatGarblerDuck = ['quack', 'quackity', 'quacky'] +ChatGarblerMonkey = ['ooh', 'ooo', 'ahh'] +ChatGarblerBear = ['growl', 'grrr'] +ChatGarblerPig = ['oink', 'oik', 'snort'] +ChatGarblerDefault = ['blah'] +Bossbot = 'Bossbot' +Lawbot = 'Lawbot' +Cashbot = 'Cashbot' +Sellbot = 'Sellbot' +BossbotS = 'a Bossbot' +LawbotS = 'a Lawbot' +CashbotS = 'a Cashbot' +SellbotS = 'a Sellbot' +BossbotP = 'Bossbots' +LawbotP = 'Lawbots' +CashbotP = 'Cashbots' +SellbotP = 'Sellbots' +BossbotSkelS = 'a Bossbot Skelecog' +LawbotSkelS = 'a Lawbot Skelecog' +CashbotSkelS = 'a Cashbot Skelecog' +SellbotSkelS = 'a Sellbot Skelecog' +BossbotSkelP = 'Bossbot Skelecogs' +LawbotSkelP = 'Lawbot Skelecogs' +CashbotSkelP = 'Cashbot Skelecogs' +SellbotSkelP = 'Sellbot Skelecogs' +SkeleRevivePostFix = ' v2.0' +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' +AvatarDetailPanelPlayerShort = '%(player)s\nWorld: %(world)s\nLocation: %(location)s' +AvatarDetailPanelRealLife = 'Offline' +AvatarDetailPanelOnline = 'District: %(district)s\nLocation: %(location)s' +AvatarDetailPanelOnlinePlayer = 'District: %(district)s\nLocation: %(location)s\nPlayer: %(player)s' +AvatarDetailPanelOffline = 'District: offline\nLocation: offline' +AvatarShowPlayer = 'Show Player' +OfflineLocation = 'Offline' +PlayerToonName = 'Toon: %(toonname)s' +PlayerShowToon = 'Show Toon' +PlayerPanelDetail = 'Player Details' +AvatarPanelFriends = 'Friends' +AvatarPanelWhisper = 'Whisper' +AvatarPanelSecrets = 'True Friends' +AvatarPanelGoTo = 'Go To' +AvatarPanelPet = 'Show Doodle' +AvatarPanelIgnore = 'Ignore' +AvatarPanelIgnoreCant = 'Okay' +AvatarPanelStopIgnoring = 'Stop Ignoring' +AvatarPanelReport = 'Report' +AvatarPanelCogLevel = 'Level: %s' +AvatarPanelCogDetailClose = lClose +AvatarPanelDetail = 'Toon Details' +AvatarPanelGroupInvite = 'Invite' +AvatarPanelGroupRetract = 'Retract Invitation' +AvatarPanelGroupMember = 'Already In Group' +AvatarPanelGroupMemberKick = 'Remove' +ReportPanelTitle = 'Report A Player' +ReportPanelBody = 'This feature will send a complete report to a Moderator. Instead of sending a report, you might choose to do one of the following:\n\n - Teleport to another district\n - Use "Ignore" on the toon\'s panel\n\nDo you really want to report %s to a Moderator?' +ReportPanelBodyFriends = 'This feature will send a complete report to a Moderator. Instead of sending a report, you might choose to do one of the following:\n\n - Teleport to another district\n - Break your friendship\n\nDo you really want to report %s to a Moderator?\n\n(This will also break your friendship)' +ReportPanelCategoryBody = 'You are about to report %s. A Moderator will be alerted to your complaint and will take appropriate action for anyone breaking our rules. Please choose the reason you are reporting %s:' +ReportPanelBodyPlayer = 'This feature is stilling being worked on and will be coming soon. In the meantime you can do the following:\n\n - Go to DXD and break the friendship there.\n - Tell a parent about what happened.' +ReportPanelCategoryLanguage = 'Foul Language' +ReportPanelCategoryPii = 'Sharing/Requesting Personal Info' +ReportPanelCategoryRude = 'Rude or Mean Behavior' +ReportPanelCategoryName = 'Bad Name' +ReportPanelCategoryHacking = 'Hacking' +ReportPanelConfirmations = ('You are about to report that %s has used obscene, bigoted or sexually explicit language.', + 'You are about to report that %s is being unsafe by giving out or requesting a phone number, address, last name, email address, password or account name.', + 'You are about to report that %s is bullying, harassing, or using extreme behavior to disrupt the game.', + "You are about to report that %s has created a name that does not follow Disney's House Rules.", + 'You are about to report that %s has hacked/tampered with the game or used third party software.') +ReportPanelWarning = "We take reporting very seriously. Your report will be viewed by a Moderator who will take appropriate action for anyone breaking our rules. If your account is found to have participated in breaking the rules, or if you make false reports or abuse the 'Report a Player' system, a Moderator may take action against your account. Are you absolutely sure you want to report this player?" +ReportPanelThanks = 'Thank you! Your report has been sent to a Moderator for review. There is no need to contact us again about the issue. The moderation team will take appropriate action for a player found breaking our rules.' +ReportPanelRemovedFriend = 'We have automatically removed %s from your Toon Friends List.' +ReportPanelRemovedPlayerFriend = 'We have automatically removed %s as a Player friend so as such you will not see them as your friend in any Disney product.' +ReportPanelAlreadyReported = 'You have already reported %s during this session. A Moderator will review your previous report.' +IgnorePanelTitle = 'Ignore A Player' +IgnorePanelAddIgnore = 'Would you like to ignore %s for the rest of this session?' +IgnorePanelIgnore = 'You are now ignoring %s.' +IgnorePanelRemoveIgnore = 'Would you like to stop ignoring %s?' +IgnorePanelEndIgnore = 'You are no longer ignoring %s.' +IgnorePanelAddFriendAvatar = '%s is your friend, you cannot ignore them while you are friends.' +IgnorePanelAddFriendPlayer = '%s (%s)is your friend, you cannot ignore them while you are friends.' +PetPanelFeed = 'Feed' +PetPanelCall = 'Call' +PetPanelGoTo = 'Go To' +PetPanelOwner = 'Show Owner' +PetPanelDetail = 'Pet Details' +PetPanelScratch = 'Scratch' +PetDetailPanelTitle = 'Trick Training' +PetTrickStrings = {0: 'Jump', + 1: 'Beg', + 2: 'Play dead', + 3: 'Rollover', + 4: 'Backflip', + 5: 'Dance', + 6: 'Speak'} +PetMoodAdjectives = {'neutral': 'neutral', + 'hunger': 'hungry', + 'boredom': 'bored', + 'excitement': 'excited', + 'sadness': 'sad', + 'restlessness': 'restless', + 'playfulness': 'playful', + 'loneliness': 'lonely', + 'fatigue': 'tired', + 'confusion': 'confused', + 'anger': 'angry', + 'surprise': 'surprised', + 'affection': 'affectionate'} +SpokenMoods = {'neutral': 'neutral', + 'hunger': ["I'm tired of JellyBeans! How'bout giving me a slice of pie?", "How'bout a Red JellyBean? I'm tired of the Green ones!", "Oh, those JellyBeans were for planting?!! But I'm hungry!"], + 'boredom': ["I'm dying of boredom over here!", "You didn't think I understood you, huh?", 'Could we, like, DO something already?'], + 'excitement': ["Wow, it's you, it's you, it's you!", + 'mmm, jellybeans, mmm!', + 'Does it GET any better than this?', + "Happy April Toons' Week!"], + 'sadness': ["Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go, Don't go...", "I'll be good, I promise!", "I don't know WHY I'm sad, I just am!!!"], + 'restlessness': ["I'm sooo restless!!!"], + 'playfulness': ["Let's play, Let's play, Let's play, Let's play, Let's play, Let's play, Let's play, Let's play, Let's play...", 'Play with me or I dig up some flowers!', 'Lets run around and around and around and around and around and around...'], + 'loneliness': ['Where have you been?', 'Wanna cuddle?', 'I want to go with you when you fight Cogs!'], + 'fatigue': ['That swim in the pond really tired me out!', 'Being a Doodle is exhausting!', 'I gotta get to Dreamland!'], + 'confusion': ['Where am I? Who are you again?', "What's a Toon-up again?", "Whoa, I'm standing between you and the Cogs! Run away!"], + 'anger': ['... and you wonder why I never give you a Toon-up?!!!', 'You always leave me behind!', 'You love your gags more than you love me!'], + 'surprise': ['Of course Doodles can talk!', 'Toons can talk?!!', 'Whoa, where did you come from?'], + 'affection': ["You're the best Toon EVER!!!!!!!!!!", 'Do you even KNOW how great you are?!?', 'I am SO lucky to be with you!!!']} +DialogQuestion = '?' +FriendsListLabel = 'Friends' +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?" +TeleportPanelBusyShard = '%(avName)s is in a full District. Playing in a full District can severely slow down game performance. Are you sure you want to switch districts?' +BattleBldgBossTaunt = "I'm the boss." +CogdoBattleBldgBossTaunt = "I don't take meetings with Toons." +FactoryBossTaunt = "I'm the Foreman." +FactoryBossBattleTaunt = 'Let me introduce you to the Foreman.' +MintBossTaunt = "I'm the Supervisor." +MintBossBattleTaunt = 'You need to talk to the Supervisor.' +StageBossTaunt = "My Justice isn't Blind" +StageBossBattleTaunt = 'I am above the Law' +CountryClubBossTaunt = "I'm the Club President." +CountryClubBossBattleTaunt = 'You need to talk to the Club President.' +ForcedLeaveCountryClubAckMsg = 'The Club President was defeated before you could reach him. You did not recover any Stock Options.' +ToonHealJokes = [['What goes TICK-TICK-TICK-WOOF?', 'A watchdog! '], + ['Why do male deer need braces?', "Because they have 'buck teeth'!"], + ['Why is it hard for a ghost to tell a lie?', 'Because you can see right through him.'], + ['What did the ballerina do when she hurt her foot?', 'She called the toe truck!'], + ['What has one horn and gives milk?', 'A milk truck!'], + ["Why don't witches ride their brooms when they're angry?", "They don't want to fly off the handle!"], + ['Why did the dolphin cross the ocean?', 'To get to the other tide.'], + ['What kind of mistakes do spooks make?', 'Boo boos.'], + ['Why did the chicken cross the playground?', 'To get to the other slide!'], + ['Where does a peacock go when he loses his tail?', 'A retail store.'], + ["Why didn't the skeleton cross the road?", "He didn't have the guts."], + ["Why wouldn't they let the butterfly into the dance?", 'Because it was a moth ball.'], + ["What's gray and squirts jam at you?", 'A mouse eating a doughnut.'], + ['What happened when 500 hares got loose on the main street?', 'The police had to comb the area.'], + ["What's the difference between a fish and a piano?", "You can tune a piano, but you can't tuna fish!"], + ['What do people do in clock factories?', 'They make faces all day.'], + ['What do you call a blind dinosaur?', "An I-don't-think-he-saurus."], + ['If you drop a white hat into the Red Sea, what does it become?', 'Wet.'], + ['Why was Cinderella thrown off the basketball team?', 'She ran away from the ball.'], + ['Why was Cinderella such a bad player?', 'She had a pumpkin for a coach.'], + ["What two things can't you have for breakfast?", 'Lunch and dinner.'], + ['What do you give an elephant with big feet?', 'Big shoes.'], + ['Where do baby ghosts go during the day?', 'Day-scare centers.'], + ['What did Snow White say to the photographer?', 'Some day my prints will come.'], + ["What's Tarzan's favorite song?", 'Jungle bells.'], + ["What's green and loud?", 'A froghorn.'], + ["What's worse than raining cats and dogs?", 'Hailing taxis.'], + ['When is the vet busiest?', "When it's raining cats and dogs."], + ['What do you call a gorilla wearing ear-muffs?', "Anything you want, he can't hear you."], + ['Where would you weigh a whale?', 'At a whale-weigh station.'], + ['What travels around the world but stays in the corner?', 'A stamp.'], + ['What do you give a pig with a sore throat?', 'Oinkment.'], + ['What did the hat say to the scarf?', 'You hang around while I go on a head.'], + ["What's the best parting gift?", 'A comb.'], + ['What kind of cats like to go bowling?', 'Alley cats.'], + ["What's wrong if you keep seeing talking animals?", "You're having Disney spells."], + ['What did one eye say to the other?', 'Between you and me, something smells.'], + ["What's round, white and giggles?", 'A tickled onion.'], + ['What do you get when you cross Bambi with a ghost?', 'Bamboo.'], + ['Why do golfers take an extra pair of socks?', 'In case they get a hole in one.'], + ['What do you call a fly with no wings?', 'A walk.'], + ['Who did Frankenstein take to the prom?', 'His ghoul friend.'], + ['What lies on its back, one hundred feet in the air?', 'A sleeping centipede.'], + ['How do you keep a bull from charging?', 'Take away his credit card.'], + ['What do you call a chicken at the North Pole?', 'Lost.'], + ['What do you get if you cross a cat with a dog?', 'An animal that chases itself.'], + ['What did the digital watch say to the grandfather clock?', 'Look dad, no hands.'], + ['Where does Ariel the mermaid go to see movies?', 'The dive-in.'], + ['What do you call a mosquito with a tin suit?', 'A bite in shining armor.'], + ['What do giraffes have that no other animal has?', 'Baby giraffes.'], + ['Why did the man hit the clock?', 'Because the clock struck first.'], + ['Why did the apple go out with a fig?', "Because it couldn't find a date."], + ['What do you get when you cross a parrot with a monster?', 'A creature that gets a cracker whenever it asks for one.'], + ["Why didn't the monster make the football team?", 'Because he threw like a ghoul!'], + ['What do you get if you cross a Cocker Spaniel with a Poodle and a rooster?', 'A cockapoodledoo!'], + ['What goes dot-dot-dash-dash-squeak?', 'Mouse code.'], + ["Why aren't elephants allowed on beaches?", "They can't keep their trunks up."], + ['What is at the end of everything?', 'The letter G.'], + ['How do trains hear?', 'Through the engineers.'], + ['What does the winner of a marathon lose?', 'His breath.'], + ['Why did the pelican refuse to pay for his meal?', 'His bill was too big.'], + ['What has six eyes but cannot see?', 'Three blind mice.'], + ["What works only when it's fired?", 'A rocket.'], + ["Why wasn't there any food left after the monster party?", 'Because everyone was a goblin!'], + ['What bird can be heard at mealtimes?', 'A swallow.'], + ['What goes Oh, Oh, Oh?', 'Santa walking backwards.'], + ['What has green hair and runs through the forest?', 'Moldy locks.'], + ['Where do ghosts pick up their mail?', 'At the ghost office.'], + ['Why do dinosaurs have long necks?', 'Because their feet smell.'], + ['What do mermaids have on toast?', 'Mermarlade.'], + ['Why do elephants never forget?', 'Because nobody ever tells them anything.'], + ["What's in the middle of a jellyfish?", 'A jellybutton.'], + ['What do you call a very popular perfume?', 'A best-smeller.'], + ["Why can't you play jokes on snakes?", 'Because you can never pull their legs.'], + ['Why did the baker stop making donuts?', 'He got sick of the hole business.'], + ['Why do mummies make excellent spies?', "They're good at keeping things under wraps."], + ['How do you stop an elephant from going through the eye of a needle?', 'Tie a knot in its tail.'], + ["What goes 'Ha Ha Ha Thud'?", 'Someone laughing his head off.'], + ["My friend thinks he's a rubber band.", 'I told him to snap out of it.'], + ["My sister thinks she's a pair of curtains.", 'I told her to pull herself together!'], + ['Did you hear about the dentist that married the manicurist?', 'Within a month they were fighting tooth and nail.'], + ['Why do hummingbirds hum?', "Because they don't know the words."], + ['Why did the baby turkey bolt down his food?', 'Because he was a little gobbler.'], + ['Where did the whale go when it was bankrupt?', 'To the loan shark.'], + ['How does a sick sheep feel?', 'Baah-aahd.'], + ["What's gray, weighs 10 pounds and squeaks?", 'A mouse that needs to go on a diet.'], + ['Why did the dog chase his tail?', 'To make ends meet.'], + ['Why do elephants wear running shoes?', 'For jogging of course.'], + ['Why are elephants big and gray?', "Because if they were small and yellow they'd be canaries."], + ['If athletes get tennis elbow what do astronauts get?', 'Missile toe.'], + ['Did you hear about the man who hated Santa?', 'He suffered from Claustrophobia.'], + ['Why did ' + Donald + ' sprinkle sugar on his pillow?', 'Because he wanted to have sweet dreams.'], + ['Why did ' + Goofy + ' take his comb to the dentist?', 'Because it had lost all its teeth.'], + ['Why did ' + Goofy + ' wear his shirt in the bath?', 'Because the label said wash and wear.'], + ['Why did the dirty chicken cross the road?', 'For some fowl purpose.'], + ["Why didn't the skeleton go to the party?", 'He had no body to go with.'], + ['Why did the burglar take a shower?', 'To make a clean getaway.'], + ['Why does a sheep have a woolly coat?', "Because he'd look silly in a plastic one."], + ['Why do potatoes argue all the time?', "They can't see eye to eye."], + ['Why did ' + Pluto + ' sleep with a banana peel?', 'So he could slip out of bed in the morning.'], + ['Why did the mouse wear brown sneakers?', 'His white ones were in the wash.'], + ['Why are false teeth like stars?', 'They come out at night.'], + ['Why are Saturday and Sunday so strong?', 'Because the others are weekdays.'], + ['Why did the archaeologist go bankrupt?', 'Because his career was in ruins.'], + ['What do you get if you cross the Atlantic on the Titanic?', 'Very wet.'], + ['What do you get if you cross a chicken with cement?', 'A brick-layer.'], + ['What do you get if you cross a dog with a phone?', 'A golden receiver.'], + ['What do you get if you cross an elephant with a shark?', 'Swimming trunks with sharp teeth.'], + ['What did the tablecloth say to the table?', "Don't move, I've got you covered."], + ['Did you hear about the time ' + Goofy + ' ate a candle?', 'He wanted a light snack.'], + ['What did the balloon say to the pin?', 'Hi Buster.'], + ['What did the big chimney say to the little chimney?', "You're too young to smoke."], + ['What did the carpet say to the floor?', 'I got you covered.'], + ['What did the necklace say to the hat?', "You go ahead, I'll hang around."], + ['What goes zzub-zzub?', 'A bee flying backwards.'], + ['How do you communicate with a fish?', 'Drop him a line.'], + ["What do you call a dinosaur that's never late?", 'A prontosaurus.'], + ['What do you get if you cross a bear and a skunk?', 'Winnie-the-phew.'], + ['How do you clean a tuba?', 'With a tuba toothpaste.'], + ['What do frogs like to sit on?', 'Toadstools.'], + ['Why was the math book unhappy?', 'It had too many problems.'], + ['Why was the school clock punished?', 'It tocked too much.'], + ["What's a polygon?", 'A dead parrot.'], + ['What needs a bath and keeps crossing the street?', 'A dirty double crosser.'], + ['What do you get if you cross a camera with a crocodile?', 'A snap shot.'], + ['What do you get if you cross an elephant with a canary?', 'A very messy cage.'], + ['What do you get if you cross a jeweler with a plumber?', 'A ring around the bathtub.'], + ['What do you get if you cross an elephant with a crow?', 'Lots of broken telephone poles.'], + ['What do you get if you cross a plum with a tiger?', 'A purple people eater.'], + ["What's the best way to save water?", 'Dilute it.'], + ["What's a lazy shoe called?", 'A loafer.'], + ["What's green, noisy and dangerous?", 'A thundering herd of cucumbers.'], + ['What color is a shout?', 'Yellow!'], + ['What do you call a sick duck?', 'A mallardy.'], + ["What's worse then a giraffe with a sore throat?", "A centipede with athlete's foot."], + ['What goes ABC...slurp...DEF...slurp?', 'Someone eating alphabet soup.'], + ["What's green and jumps up and down?", 'Lettuce at a dance.'], + ["What's a cow after she gives birth?", 'De-calf-inated.'], + ['What do you get if you cross a cow and a camel?', 'Lumpy milk shakes.'], + ["What's white with black and red spots?", 'A Dalmatian with measles.'], + ["What's brown has four legs and a trunk?", 'A mouse coming back from vacation.'], + ["What does a skunk do when it's angry?", 'It raises a stink.'], + ["What's gray, weighs 200 pounds and says, Here Kitty, kitty?", 'A 200 pound mouse.'], + ["What's the best way to catch a squirrel?", 'Climb a tree and act like a nut.'], + ["What's the best way to catch a rabbit?", 'Hide in a bush and make a noise like lettuce.'], + ['What do you call a spider that just got married?', 'A newly web.'], + ['What do you call a duck that robs banks?', 'A safe quacker.'], + ["What's furry, meows and chases mice underwater?", 'A catfish.'], + ["What's a funny egg called?", 'A practical yolker.'], + ["What's green on the outside and yellow inside?", 'A banana disguised as a cucumber.'], + ['What did the elephant say to the lemon?', "Let's play squash."], + ['What weighs 4 tons, has a trunk and is bright red?', 'An embarrassed elephant.'], + ["What's gray, weighs 4 tons, and wears glass slippers?", 'Cinderelephant.'], + ["What's an elephant in a fridge called?", 'A very tight squeeze.'], + ['What did the elephant say to her naughty child?', 'Tusk! Tusk!'], + ['What did the peanut say to the elephant?', "Nothing -- Peanuts can't talk."], + ['What do elephants say when they bump into each other?', "Small world, isn't it?"], + ['What did the cashier say to the register?', "I'm counting on you."], + ['What did the flea say to the other flea?', 'Shall we walk or take the cat?'], + ['What did the big hand say to the little hand?', 'Got a minute.'], + ['What does the sea say to the sand?', 'Not much. It usually waves.'], + ['What did the stocking say to the shoe?', 'See you later, I gotta run.'], + ['What did one tonsil say to the other tonsil?', 'It must be spring, here comes a swallow.'], + ['What did the soil say to the rain?', 'Stop, or my name is mud.'], + ['What did the puddle say to the rain?', 'Drop in sometime.'], + ['What did the bee say to the rose?', 'Hi, bud.'], + ['What did the appendix say to the kidney?', "The doctor's taking me out tonight."], + ['What did the window say to the venetian blinds?', "If it wasn't for you it'd be curtains for me."], + ['What did the doctor say to the sick orange?', 'Are you peeling well?'], + ['What do you get if you cross a chicken with a banjo?', 'A self-plucking chicken.'], + ['What do you get if you cross a hyena with a bouillon cube?', 'An animal that makes a laughing stock of itself.'], + ['What do you get if you cross a rabbit with a spider?', 'A hare net.'], + ['What do you get if you cross a germ with a comedian?', 'Sick jokes.'], + ['What do you get if you cross a hyena with a mynah bird?', 'An animal that laughs at its own jokes.'], + ['What do you get if you cross a railway engine with a stick of gum?', 'A chew-chew train.'], + ['What would you get if you crossed an elephant with a computer?', 'A big know-it-all.'], + ['What would you get if you crossed an elephant with a skunk?', 'A big stinker.'], + ['Why did ' + MickeyMouse + ' take a trip to outer space?', 'He wanted to find ' + Pluto + '.']] +MovieHealLaughterMisses = ('hmm', + 'heh', + 'ha', + 'harr harr') +MovieHealLaughterHits1 = ('Ha Ha Ha', + 'Hee Hee', + 'Tee Hee', + 'Ha Ha') +MovieHealLaughterHits2 = ('BWAH HAH HAH!', 'HO HO HO!', 'HA HA HA!') +MovieSOSCallHelp = '%s HELP!' +MovieSOSWhisperHelp = '%s needs help in battle!' +MovieSOSObserverHelp = 'HELP!' +MovieNPCSOSGreeting = 'Hi %s! Glad to help!' +MovieNPCSOSGoodbye = 'See you later!' +MovieNPCSOSToonsHit = 'Toons Always Hit!' +MovieNPCSOSCogsMiss = 'Cogs Always Miss!' +MovieNPCSOSRestockGags = 'Restocking %s gags!' +MovieNPCSOSHeal = 'Heal' +MovieNPCSOSTrap = 'Trap' +MovieNPCSOSLure = 'Lure' +MovieNPCSOSSound = 'Sound' +MovieNPCSOSThrow = 'Throw' +MovieNPCSOSSquirt = 'Squirt' +MovieNPCSOSDrop = 'Drop' +MovieNPCSOSAll = 'All' +MoviePetSOSTrickFail = 'Sigh' +MoviePetSOSTrickSucceedBoy = 'Good boy!' +MoviePetSOSTrickSucceedGirl = 'Good girl!' +MovieSuitCancelled = 'CANCELLED\nCANCELLED\nCANCELLED' +RewardPanelToonTasks = 'ToonTasks' +RewardPanelItems = 'Items Recovered' +RewardPanelMissedItems = 'Items Not Recovered' +RewardPanelQuestLabel = 'Quest %s' +RewardPanelCongratsStrings = ['Yeah!', + 'Congratulations!', + 'Wow!', + 'Cool!', + 'Awesome!', + 'Toon-tastic!'] +RewardPanelNewGag = 'New %(gagName)s gag for %(avName)s!' +RewardPanelUberGag = '%(avName)s earned the %(gagName)s gag with %(exp)s experience points!' +RewardPanelEndTrack = 'Yay! %(avName)s has reached the end of the %(gagName)s Gag Track!' +RewardPanelMeritsMaxed = 'Maxed' +RewardPanelMeritBarLabels = ['Stock Options', + 'Jury Notices', + 'Cogbucks', + 'Merits'] +RewardPanelMeritAlert = 'Ready for promotion!' +RewardPanelCogPart = 'You gained a Cog disguise part!' +RewardPanelPromotion = 'Ready for promotion in %s track!' +RewardPanelSkip = 'Skip' +CheesyEffectDescriptions = [('Normal Toon', 'you will be normal'), + ('Big head', 'you will have a big head'), + ('Small head', 'you will have a small head'), + ('Big legs', 'you will have big legs'), + ('Small legs', 'you will have small legs'), + ('Big toon', 'you will be a little bigger'), + ('Small toon', 'you will be a little smaller'), + ('Flat portrait', 'you will be two-dimensional'), + ('Flat profile', 'you will be two-dimensional'), + ('Transparent', 'you will be transparent'), + ('No color', 'you will be colorless'), + ('Invisible toon', 'you will be invisible')] +CheesyEffectIndefinite = 'Until you choose another effect, %(effectName)s%(whileIn)s.' +CheesyEffectMinutes = 'For the next %(time)s minutes, %(effectName)s%(whileIn)s.' +CheesyEffectHours = 'For the next %(time)s hours, %(effectName)s%(whileIn)s.' +CheesyEffectDays = 'For the next %(time)s days, %(effectName)s%(whileIn)s.' +CheesyEffectWhileYouAreIn = ' while you are in %s' +CheesyEffectExceptIn = ', except in %s' +SuitFlunky = 'Flunky' +SuitPencilPusher = 'Pencil Pusher' +SuitYesman = 'Yesman' +SuitMicromanager = 'Micro\x03manager' +SuitDownsizer = 'Downsizer' +SuitHeadHunter = 'Head Hunter' +SuitCorporateRaider = 'Corporate Raider' +SuitTheBigCheese = 'The Big Cheese' +SuitColdCaller = 'Cold Caller' +SuitTelemarketer = 'Tele\x03marketer' +SuitNameDropper = 'Name Dropper' +SuitGladHander = 'Glad Hander' +SuitMoverShaker = 'Mover & Shaker' +SuitTwoFace = 'Two-Face' +SuitTheMingler = 'The Mingler' +SuitMrHollywood = 'Mr. Hollywood' +SuitShortChange = 'Short Change' +SuitPennyPincher = 'Penny Pincher' +SuitTightwad = 'Tightwad' +SuitBeanCounter = 'Bean Counter' +SuitNumberCruncher = 'Number Cruncher' +SuitMoneyBags = 'Money Bags' +SuitLoanShark = 'Loan Shark' +SuitRobberBaron = 'Robber Baron' +SuitBottomFeeder = 'Bottom Feeder' +SuitBloodsucker = 'Blood\x03sucker' +SuitDoubleTalker = 'Double Talker' +SuitAmbulanceChaser = 'Ambulance Chaser' +SuitBackStabber = 'Back Stabber' +SuitSpinDoctor = 'Spin Doctor' +SuitLegalEagle = 'Legal Eagle' +SuitBigWig = 'Big Wig' +SuitFlunkyS = 'a Flunky' +SuitPencilPusherS = 'a Pencil Pusher' +SuitYesmanS = 'a Yesman' +SuitMicromanagerS = 'a Micromanager' +SuitDownsizerS = 'a Downsizer' +SuitHeadHunterS = 'a Head Hunter' +SuitCorporateRaiderS = 'a Corporate Raider' +SuitTheBigCheeseS = 'a The Big Cheese' +SuitColdCallerS = 'a Cold Caller' +SuitTelemarketerS = 'a Telemarketer' +SuitNameDropperS = 'a Name Dropper' +SuitGladHanderS = 'a Glad Hander' +SuitMoverShakerS = 'a Mover & Shaker' +SuitTwoFaceS = 'a Two-Face' +SuitTheMinglerS = 'a The Mingler' +SuitMrHollywoodS = 'a Mr. Hollywood' +SuitShortChangeS = 'a Short Change' +SuitPennyPincherS = 'a Penny Pincher' +SuitTightwadS = 'a Tightwad' +SuitBeanCounterS = 'a Bean Counter' +SuitNumberCruncherS = 'a Number Cruncher' +SuitMoneyBagsS = 'a Money Bags' +SuitLoanSharkS = 'a Loan Shark' +SuitRobberBaronS = 'a Robber Baron' +SuitBottomFeederS = 'a Bottom Feeder' +SuitBloodsuckerS = 'a Bloodsucker' +SuitDoubleTalkerS = 'a Double Talker' +SuitAmbulanceChaserS = 'an Ambulance Chaser' +SuitBackStabberS = 'a Back Stabber' +SuitSpinDoctorS = 'a Spin Doctor' +SuitLegalEagleS = 'a Legal Eagle' +SuitBigWigS = 'a Big Wig' +SuitFlunkyP = 'Flunkies' +SuitPencilPusherP = 'Pencil Pushers' +SuitYesmanP = 'Yesmen' +SuitMicromanagerP = 'Micromanagers' +SuitDownsizerP = 'Downsizers' +SuitHeadHunterP = 'Head Hunters' +SuitCorporateRaiderP = 'Corporate Raiders' +SuitTheBigCheeseP = 'The Big Cheeses' +SuitColdCallerP = 'Cold Callers' +SuitTelemarketerP = 'Telemarketers' +SuitNameDropperP = 'Name Droppers' +SuitGladHanderP = 'Glad Handers' +SuitMoverShakerP = 'Movers & Shakers' +SuitTwoFaceP = 'Two-Faces' +SuitTheMinglerP = 'The Minglers' +SuitMrHollywoodP = 'Mr. Hollywoods' +SuitShortChangeP = 'Short Changes' +SuitPennyPincherP = 'Penny Pinchers' +SuitTightwadP = 'Tightwads' +SuitBeanCounterP = 'Bean Counters' +SuitNumberCruncherP = 'Number Crunchers' +SuitMoneyBagsP = 'Money Bags' +SuitLoanSharkP = 'Loan Sharks' +SuitRobberBaronP = 'Robber Barons' +SuitBottomFeederP = 'Bottom Feeders' +SuitBloodsuckerP = 'Bloodsuckers' +SuitDoubleTalkerP = 'Double Talkers' +SuitAmbulanceChaserP = 'Ambulance Chasers' +SuitBackStabberP = 'Back Stabbers' +SuitSpinDoctorP = 'Spin Doctors' +SuitLegalEagleP = 'Legal Eagles' +SuitBigWigP = 'Big Wigs' +SuitFaceoffDefaultTaunts = ['Boo!'] +SuitAttackDefaultTaunts = ['Take that!', 'Take a memo on this!'] +SuitAttackNames = {'Audit': 'Audit!', + 'Bite': 'Bite!', + 'BounceCheck': 'Bounce Check!', + 'BrainStorm': 'Brain Storm!', + 'BuzzWord': 'Buzz Word!', + 'Calculate': 'Calculate!', + 'Canned': 'Canned!', + 'Chomp': 'Chomp!', + 'CigarSmoke': 'Cigar Smoke!', + 'ClipOnTie': 'Clip On Tie!', + 'Crunch': 'Crunch!', + 'Demotion': 'Demotion!', + 'Downsize': 'Downsize!', + 'DoubleTalk': 'Double Talk!', + 'EvictionNotice': 'Eviction Notice!', + 'EvilEye': 'Evil Eye!', + 'Filibuster': 'Filibuster!', + 'FillWithLead': 'Fill With Lead!', + 'FiveOClockShadow': "Five O'Clock Shadow!", + 'FingerWag': 'Finger Wag!', + 'Fired': 'Fired!', + 'FloodTheMarket': 'Flood The Market!', + 'FountainPen': 'Fountain Pen!', + 'FreezeAssets': 'Freeze Assets!', + 'Gavel': 'Gavel!', + 'GlowerPower': 'Glower Power!', + 'GuiltTrip': 'Guilt Trip!', + 'HalfWindsor': 'Half Windsor!', + 'HangUp': 'Hang Up!', + 'HeadShrink': 'Head Shrink!', + 'HotAir': 'Hot Air!', + 'Jargon': 'Jargon!', + 'Legalese': 'Legalese!', + 'Liquidate': 'Liquidate!', + 'MarketCrash': 'Market Crash!', + 'MumboJumbo': 'Mumbo Jumbo!', + 'ParadigmShift': 'Paradigm Shift!', + 'PeckingOrder': 'Pecking Order!', + 'PickPocket': 'Pick Pocket!', + 'PinkSlip': 'Pink Slip!', + 'PlayHardball': 'Play Hardball!', + 'PoundKey': 'Pound Key!', + 'PowerTie': 'Power Tie!', + 'PowerTrip': 'Power Trip!', + 'Quake': 'Quake!', + 'RazzleDazzle': 'Razzle Dazzle!', + 'RedTape': 'Red Tape!', + 'ReOrg': 'Re-Org!', + 'RestrainingOrder': 'Restraining Order!', + 'Rolodex': 'Rolodex!', + 'RubberStamp': 'Rubber Stamp!', + 'RubOut': 'Rub Out!', + 'Sacked': 'Sacked!', + 'SandTrap': 'Sand Trap!', + 'Schmooze': 'Schmooze!', + 'Shake': 'Shake!', + 'Shred': 'Shred!', + 'SongAndDance': 'Song And Dance!', + 'Spin': 'Spin!', + 'Synergy': 'Synergy!', + 'Tabulate': 'Tabulate!', + 'TeeOff': 'Tee Off!', + 'ThrowBook': 'Throw Book!', + 'Tremor': 'Tremor!', + 'Watercooler': 'Watercooler!', + 'Withdrawal': 'Withdrawal!', + 'WriteOff': 'Write Off!'} +SuitAttackTaunts = {'Audit': ["I believe your books don't balance.", + "Looks like you're in the red.", + 'Let me help you with your books.', + 'Your debit column is much too high.', + "Let's check your assets.", + 'This will put you in debt.', + "Let's take a close look at what you owe.", + 'This should drain your account.', + 'Time for you to account for your expenses.', + "I've found an error in your books."], + 'Bite': ['Would you like a bite?', + 'Try a bite of this!', + "You're biting off more than you can chew.", + 'My bite is bigger than my bark.', + 'Bite down on this!', + 'Watch out, I may bite.', + "I don't just bite when I'm cornered.", + "I'm just gonna grab a quick bite.", + "I haven't had a bite all day.", + 'I just want a bite. Is that too much to ask?'], + 'BounceCheck': ["Ah, too bad, you're funless.", + 'You have a payment due.', + 'I believe this check is yours.', + 'You owed me for this.', + "I'm collecting on this debt.", + "This check isn't going to be tender.", + "You're going to be charged for this.", + 'Check this out.', + 'This is going to cost you.', + "I'd like to cash this in.", + "I'm just going to kick this back to you.", + 'This is one sour note.', + "I'm deducting a service charge."], + 'BrainStorm': ['I forecast rain.', + 'Hope you packed your umbrella.', + 'I want to enlighten you.', + 'How about a few rain DROPS?', + 'Not so sunny now, are you Toon?', + 'Ready for a down pour?', + "I'm going to take you by storm.", + 'I call this a lightning attack.', + 'I love to be a wet blanket.'], + 'BuzzWord': ['Pardon me if I drone on.', + 'Have you heard the latest?', + 'Can you catch on to this?', + 'See if you can hum this Toon.', + 'Let me put in a good word for you.', + 'I\'ll "B" perfectly clear.', + 'You should "B" more careful.', + 'See if you can dodge this swarm.', + "Careful, you're about to get stung.", + 'Looks like you have a bad case of hives.'], + 'Calculate': ['These numbers do add up!', + 'Did you count on this?', + "Add it up, you're going down.", + 'Let me help you add this up.', + 'Did you register all your expenses?', + "According to my calculations, you won't be around much longer.", + "Here's the grand total.", + 'Wow, your bill is adding up.', + 'Try fiddling with these numbers!', + Cogs + ': 1 Toons: 0'], + 'Canned': ['Do you like it out of the can?', + '"Can" you handle this?', + "This one's fresh out of the can!", + 'Ever been attacked by canned goods before?', + "I'd like to donate this canned good to you!", + 'Get ready to "Kick the can"!', + 'You think you "can", you think you "can".', + "I'll throw you in the can!", + "I'm making me a can o' toon-a!", + "You don't taste so good out of the can."], + 'Chomp': ['Take a look at these chompers!', + 'Chomp, chomp, chomp!', + "Here's something to chomp on.", + 'Looking for something to chomp on?', + "Why don't you chomp on this?", + "I'm going to have you for dinner.", + 'I love to feed on Toons!'], + 'ClipOnTie': ['Better dress for our meeting.', + "You can't go OUT without your tie.", + 'The best dressed ' + Cogs + ' wear them.', + 'Try this on for size.', + 'You should dress for success.', + 'No tie, no service.', + 'Do you need help putting this on?', + 'Nothing says powerful like a good tie.', + "Let's see if this fits.", + 'This is going to choke you up.', + "You'll want to dress up before you go OUT.", + "I think I'll tie you up."], + 'Crunch': ["Looks like you're in a crunch.", + "It's crunch time!", + "I'll give you something to crunch on!", + 'Crunch on this!', + 'I pack quite a crunch.', + 'Which do you prefer, smooth or crunchy?', + "I hope you're ready for crunch time.", + "It sounds like you're getting crunched!", + "I'll crunch you like a can."], + 'Demotion': ["You're moving down the corporate ladder.", + "I'm sending you back to the Mail Room.", + 'Time to turn in your nameplate.', + "You're going down, clown.", + "Looks like you're stuck.", + "You're going nowhere fast.", + "You're in a dead end position.", + "You won't be moving anytime soon.", + "You're not going anywhere.", + 'This will go on your permanent record.'], + 'Downsize': ['Come on down!', + 'Do you know how to get down?', + "Let's get down to business.", + "What's wrong? You look down.", + 'Going down?', + "What's goin' down? You!", + 'Why pick on people my own size?', + "Why don't I size you up, or should I say, down?", + 'Would you like a smaller size for just a quarter more?', + 'Try this on for size!', + 'You can get this in a smaller size.', + 'This attack is one size fits all!'], + 'EvictionNotice': ["It's moving time.", + 'Pack your bags, Toon.', + 'Time to make some new living arrangements.', + 'Consider yourself served.', + "You're behind on your lease.", + 'This will be extremely unsettling.', + "You're about to be uprooted.", + "I'm going to send you packing.", + "You're out of place.", + 'Prepare to be relocated.', + "You're in a hostel position."], + 'EvilEye': ["I'm giving you the evil eye.", + 'Could you eye-ball this for me?', + "Wait. I've got something in my eye.", + "I've got my eye on you!", + 'Could you keep an eye on this for me?', + "I've got a real eye for evil.", + "I'll poke you in the eye!", + '"Eye" am as evil as they come!', + "I'll put you in the eye of the storm!", + "I'm rolling my eye at you."], + 'Filibuster': ["Shall I fill 'er up?", + 'This is going to take awhile.', + 'I could do this all day.', + "I don't even need to take a breath.", + 'I keep going and going and going.', + 'I never get tired of this one.', + 'I can talk a blue streak.', + 'Mind if I bend your ear?', + "I think I'll shoot the breeze.", + 'I can always get a word in edgewise.'], + 'FingerWag': ['I have told you a thousand times.', + 'Now see here Toon.', + "Don't make me laugh.", + "Don't make me come over there.", + "I'm tired of repeating myself.", + "I believe we've been over this.", + 'You have no respect for us ' + Cogs + '.', + "I think it's time you pay attention.", + 'Blah, Blah, Blah, Blah, Blah.', + "Don't make me stop this meeting.", + 'Am I going to have to separate you?', + "We've been through this before."], + 'Fired': ['I hope you brought some marshmallows.', + "It's going to get rather warm around here.", + 'This should take the chill out of the air.', + "I hope you're cold blooded.", + 'Hot, hot and hotter.', + 'You better stop, drop, and roll!', + "You're outta here.", + 'How does "well-done" sound?', + 'Can you say ouch?', + 'Hope you wore sunscreen.', + 'Do you feel a little toasty?', + "You're going down in flames.", + "You'll go out in a blaze.", + "You're a flash in the pan.", + 'I think I have a bit of a flare about me.', + "I just sparkle, don't I?", + 'Oh look, a crispy critter.', + "You shouldn't run around half baked."], + 'FountainPen': ['This is going to leave a stain.', + "Let's ink this deal.", + 'Be prepared for some permanent damage.', + "You're going to need a good dry cleaner.", + 'You should change.', + 'This fountain pen has such a nice font.', + "Here, I'll use my pen.", + 'Can you read my writing?', + 'I call this the plume of doom.', + "There's a blot on your performance.", + "Don't you hate when this happens?"], + 'FreezeAssets': ['Your assets are mine.', + 'Do you feel a draft?', + "Hope you don't have plans.", + 'This should keep you on ice.', + "There's a chill in the air.", + 'Winter is coming early this year.', + 'Are you feeling a little blue?', + 'Let me crystallize my plan.', + "You're going to take this hard.", + 'This should cause freezer burn.', + 'I hope you like cold cuts.', + "I'm very cold blooded."], + 'GlowerPower': ['You looking at me?', + "I'm told I have very piercing eyes.", + 'I like to stay on the cutting edge.', + "Jeepers, Creepers, don't you love my peepers?", + "Here's looking at you kid.", + "How's this for expressive eyes?", + 'My eyes are my strongest feature.', + 'The eyes have it.', + 'Peeka-boo, I see you.', + 'Look into my eyes...', + 'Shall we take a peek at your future?'], + 'GuiltTrip': ["I'll lay a real guilt trip on you!", + 'Feeling guilty?', + "It's all your fault!", + 'I always blame everything on you.', + 'Wallow in your own guilt!', + "I'm never speaking to you again!", + "You had better say you're sorry.", + "I'm would forgive you in a million years!", + 'Are you ready for your trip?', + 'Call me when you get back from your trip.', + 'When do you get back from your trip?'], + 'HalfWindsor': ["This is the fanciest tie you'll ever see!", + 'Try not to get too winded.', + "This isn't even half the trouble you're in.", + "You're lucky I don't have a whole windsor.", + "You can't afford this tie.", + "I bet you've never even SEEN a half windsor!", + 'This tie is out of your league.', + "I shouldn't even waste this tie on you.", + "You're not even worth half of this tie!"], + 'HangUp': ["You've been disconnected.", + 'Good bye!', + "It's time I end our connection.", + "...and don't call back!", + 'Click!', + 'This conversation is over.', + "I'm severing this link.", + 'I think you have a few hang ups.', + "It appears you've got a weak link.", + 'Your time is up.', + 'I hope you receive this loud and clear.', + 'You got the wrong number.'], + 'HeadShrink': ["Looks like you're seeing a shrink.", + 'Honey, I shrunk the toon.', + "Hope this doesn't shrink your pride.", + 'Do you shrink in the wash?', + 'I shrink therefore I am.', + "It's nothing to lose your head over.", + 'Are you going out of your head?', + 'Heads up! Or should I say, down.', + 'Objects may be larger than they appear.', + 'Good Toons come in small packages.'], + 'HotAir': ["We're having a heated discussion.", + "You're experiencing a heat wave.", + "I've reached my boiling point.", + 'This should cause some wind burn.', + 'I hate to grill you, but...', + "Always remember, where there's smoke, there's fire.", + "You're looking a little burned out.", + 'Another meeting up in smoke.', + "Guess it's time to add fuel to the fire.", + 'Let me kindle a working relationship.', + 'I have some glowing remarks for you.', + 'Air Raid!!!'], + 'Jargon': ['What nonsense.', + 'See if you can make sense of this.', + 'I hope you get this loud and clear.', + "Looks like I'm going to have to raise my voice.", + 'I insist on having my say.', + "I'm very outspoken.", + 'I must pontificate on this subject.', + 'See, words can hurt you.', + 'Did you catch my meaning?', + 'Words, words, words, words, words.'], + 'Legalese': ['You must cease and desist.', + 'You will be defeated, legally speaking.', + 'Are you aware of the legal ramifications?', + "You aren't above the law!", + 'There should be a law against you.', + "There's no ex post facto with me!", + "The opinions expressed in this attack are not those of Disney's Toontown Online.", + 'We cannot be held responsible for damages suffered in this attack.', + 'Your results for this attack may vary.', + 'This attack is void where prohibited.', + "You don't fit into my legal system!", + "You can't handle the legal matters."], + 'Liquidate': ['I like to keep things fluid.', + 'Are you having some cash flow problems?', + "I'll have to purge your assets.", + 'Time for you to go with the flow.', + "Remember it's slippery when wet.", + 'Your numbers are running.', + 'You seem to be slipping.', + "It's all crashing down on you.", + "I think you're diluted.", + "You're all washed up."], + 'MarketCrash': ["I'm going to crash your party.", + "You won't survive the crash.", + "I'm more than the market can bear.", + "I've got a real crash course for you!", + "Now I'll come crashing down.", + "I'm a real bull in the market.", + 'Looks like the market is going down.', + 'You had better get out quick!', + 'Sell! Sell! Sell!', + 'Shall I lead the recession?', + "Everybody's getting out, shouldn't you?"], + 'MumboJumbo': ['Let me make this perfectly clear.', + "It's as simple as this.", + "This is how we're going to do this.", + 'Let me supersize this for you.', + 'You might call this technobabble.', + 'Here are my five-dollar words.', + 'Boy, this is a mouth full.', + 'Some call me bombastic.', + 'Let me just interject this.', + 'I believe these are the right words.'], + 'ParadigmShift': ["Watch out! I'm rather shifty.", + 'Prepare to have your paradigm shifted!', + "Isn't this an interesting paradigm.", + "You'll get shifted out of place.", + "I guess it's your shift now.", + 'Your shift is up!', + "You've never shifted this much in your life.", + "I'm giving you the bad shift!", + 'Look into my shifty eyes!'], + 'PeckingOrder': ["This one's for the birds.", + 'Get ready for a bird bath.', + "Looks like you're going to hit a birdie.", + 'Some think this attack is fowl.', + "You're on the bottom of the pecking order.", + 'A bird in my hand is worth ten on your head!', + 'Your order is up; the pecking order!', + "Why don't I peck on someone my own size? Nah.", + 'Birds of a feather strike together.'], + 'PickPocket': ['Let me check your valuables.', + "Hey, what's that over there?", + 'Like taking candy from a baby.', + 'What a steal.', + "I'll hold this for you.", + 'Watch my hands at all times.', + 'The hand is quicker than the eye.', + "There's nothing up my sleeve.", + 'The management is not responsible for lost items.', + "Finder's keepers.", + "You'll never see it coming.", + 'One for me, none for you.', + "Don't mind if I do.", + "You won't be needing this..."], + 'PinkSlip': ['Try not to slip up.', + "Are you frightened? You've turned pink!", + 'This one will surely slip you up.', + 'Oops, I guess you slipped there, huh?', + "Watch yourself, wouldn't want to slip!", + "This one's slippery when wet.", + "I'll just slip this one in.", + "Don't mind if you slip by, do you?", + "Pink isn't really your color.", + "Here's your pink slip, you're outta here!"], + 'PlayHardball': ['So you wanna play hardball?', + "You don't wanna play hardball with me.", + 'Batter up!', + 'Hey batter, batter!', + "And here's the pitch...", + "You're going to need a relief pitcher.", + "I'm going to knock you out of the park.", + "Once you get hit, you'll run home.", + 'This is your final inning!', + "You can't play with me!", + "I'll strike you out.", + "I'm throwing you a real curve ball!"], + 'PoundKey': ['Time to return some calls.', + "I'd like to make a collect call.", + "Ring-a-ling - it's for you!", + "I've been wanting to drop a pound or two.", + 'I have a lot of clout.', + 'This may cause a slight pounding sensation.', + "I'll just punch in this number.", + 'Let me call up a little surprise.', + "I'll ring you up.", + "O.K. Toon, it's the pound for you."], + 'PowerTie': ["I'll call later, you looked tied up.", + 'Are you ready to tie die?', + "Ladies and gentlemen, it's a tie!", + 'You had better learn how to tie.', + "I'll have you tongue-tied!", + "This is the worst tie you'll ever get!", + 'Can you feel the power?', + 'My powers are far too great for you!', + "I've got the power!", + "By the powers vested in me, I'll tie you up."], + 'PowerTrip': ["Pack your bags, we're taking a little trip.", + 'Did you have a nice trip?', + "Nice trip, I guess I'll see you next fall.", + 'How was your trip?', + 'Sorry to trip you up there!', + 'You look a little tripped up.', + "Now you see who's in power!", + 'I am much more powerful than you.', + "Who's got the power now?", + "You can't fight the power.", + 'Power corrupts, especially in my hands!'], + 'Quake': ["Let's quake, rattle, and roll.", + "I've got a whole lot of quakin' goin' on!", + "I see you quakin' in your shoes.", + "Here it comes, it's the big one!", + "This one's off the Richter scale.", + 'Now the earth will quake!', + "Hey, what's shakin'? You!", + 'Ever been in an earthquake?', + "You're on shaky ground now!"], + 'RazzleDazzle': ['Read my lips.', + 'How about these choppers?', + "Aren't I charming?", + "I'm going to wow you.", + 'My dentist does excellent work.', + "Blinding aren't they?", + "Hard to believe these aren't real.", + "Shocking, aren't they?", + "I'm going to cap this off.", + 'I floss after every meal.', + 'Say Cheese!'], + 'RedTape': ['This should wrap things up.', + "I'm going to tie you up for awhile.", + "You're on a roll.", + 'See if you can cut through this.', + 'This will get sticky.', + "Hope you're claustrophobic.", + "I'll make sure you stick around.", + 'Let me keep you busy.', + 'Just try to unravel this.', + 'I want this meeting to stick with you.'], + 'ReOrg': ["You don't like the way I reorganized things!", + 'Perhaps a little reorganization is in order.', + "You're not that bad, you just need to be reorganized.", + 'Do you like my organizational skills.', + "I just thought I'd give things a new look.", + 'You need to get organized!', + "You're looking a little disorganized.", + 'Hold on while I reorganize your thoughts.', + "I'll just wait for you to get a little organized.", + "You don't mind if I just reorganize a bit?"], + 'RestrainingOrder': ['You should show a little restraint.', + "I'm slapping you with a restraining order!", + "You can't come within five feet of me.", + 'Perhaps you better keep your distance.', + 'You should be restrained.', + Cogs + '! Restrain that Toon!', + 'Try and restrain yourself.', + "I hope I'm being too much of a restraint on you.", + 'See if you can lift these restraints!', + "I'm ordering you to restrain!", + "Why don't we start with basic restraining?"], + 'Rolodex': ["Your card's in here somewhere.", + "Here's the number for a pest exterminator.", + 'I want to give you my card.', + "I've got your number right here.", + "I've got you covered from a-z.", + "You'll flip over this.", + 'Take this for a spin.', + 'Watch out for paper cuts.', + "I'll let my fingers do the knocking.", + 'Is this how I can contact you?', + 'I want to make sure we stay in touch.'], + 'RubberStamp': ['I always make a good impression.', + "It's important to apply firm and even pressure.", + 'A perfect imprint every time.', + 'I want to stamp you out.', + 'You must be RETURNED TO SENDER.', + "You've been CANCELLED.", + 'You have a PRIORITY delivery.', + "I'll make sure you RECEIVED my message.", + "You're not going anywhere - you have POSTAGE DUE.", + "I'll need a response ASAP."], + 'RubOut': ['And now for my disappearing act.', + "I sense I've lost you somewhere.", + 'I decided to leave you out.', + 'I always rub out all obstacles.', + "I'll just erase this error.", + 'I can make any nuisance disappear.', + 'I like things neat and tidy.', + 'Please try and stay animated.', + "Now I see you... now I don't.", + 'This will cause some fading.', + "I'm going to eliminate the problem.", + 'Let me take care of your problem areas.'], + 'Sacked': ["Looks like you're getting sacked.", + "This one's in the bag.", + "You've been bagged.", + 'Paper or plastic?', + 'My enemies shall be sacked!', + 'I hold the Toontown record in sacks per game.', + "You're no longer wanted around here.", + "Your time is up around here, you're being sacked!", + 'Let me bag that for you.', + 'No defense can match my sack attack!'], + 'Schmooze': ["You'll never see this coming.", + 'This will look good on you.', + "You've earned this.", + "I don't mean to gush.", + 'Flattery will get me everywhere.', + "I'm going to pile it on now.", + 'Time to lay it on thick.', + "I'm going to get on your good side.", + 'That deserves a good slap on the back.', + "I'm going to ring your praises.", + 'I hate to knock you off your pedestal, but...'], + 'Shake': ["You're right on the epicenter.", + "You're standing on a fault line.", + "It's going to be a bumpy ride.", + 'I think of this as a natural disaster.', + "It's a disaster of seismic proportions.", + "This one's off the Richter scale.", + 'Time to duck and cover.', + 'You seem disturbed.', + 'Ready for a jolt?', + "I'll have you shaken, not stirred.", + 'This will shake you up.', + 'I suggest a good escape plan.'], + 'Shred': ['I need to get rid of some hazardous waste.', + "I'm increasing my throughput.", + "I think I'll dispose of you right now.", + 'This will get rid of the evidence.', + "There's no way to prove it now.", + 'See if you can put this back together.', + 'This should cut you down to size.', + "I'm going to rip that idea to shreds.", + "We don't want this to fall into the wrong hands.", + 'Easy come, easy go.', + "Isn't this your last shred of hope?"], + 'Spin': ['What do you say we go for a little spin?', + 'Do you use the spin cycle?', + "This'll really make your head spin!", + "Here's my spin on things.", + "I'll take you for a spin.", + 'How do you like to "spin" your time?', + "Watch it. Wouldn't want to spin out of control!", + "Oh what a spin you're in!", + 'My attacks will make your head spin!'], + 'Synergy': ["I'm taking this to committee.", + "Your project's been cancelled.", + "Your budget's been cut.", + "We're restructuring your division.", + 'I put it to a vote, and you lose.', + 'I just received the final approval.', + 'A good team can get rid of any problem.', + "I'll get back to you on this.", + "Let's get right to business.", + 'Consider this a Synergy crisis.'], + 'Tabulate': ["This doesn't add up.", + 'By my count, you lose.', + "You're racking up quite a tab.", + "I'll have you totaled in a moment.", + 'Are you ready for these numbers?', + 'Your bill is now due and payable.', + 'Time for the reckoning.', + 'I like to put things in order.', + 'And the tally is...', + 'These numbers should prove to be quite powerful.'], + 'TeeOff': ["You're not up to par.", + 'Fore!', + "I'm getting teed off.", + "Caddie, I'll need my driver!", + 'Just try and avoid this hazard.', + 'Swing!', + 'This is a sure hole in one.', + "You're in my fairway.", + 'Notice my grip.', + 'Watch the birdie!', + 'Keep your eye on the ball!', + 'Mind if I play through?'], + 'Tremor': ['Did you feel that?', + 'Not afraid of a little tremor are you?', + 'A tremor is only the beginning.', + 'You look jittery.', + "I'll shake things up a bit!", + 'Are you ready to rumble?', + "What's wrong? You look shaken.", + 'Tremor with fear!', + 'Why are you tremoring with fear?'], + 'Watercooler': ['This ought to cool you off.', + "Isn't this refreshing?", + 'I deliver.', + 'Straight from the tap - into your lap.', + "What's the matter, it's just spring water.", + "Don't worry, it's purified.", + 'Ah, another satisfied customer.', + "It's time for your daily delivery.", + "Hope your colors don't run.", + 'Care for a drink?', + 'It all comes out in the wash.', + "The drink's on you."], + 'Withdrawal': ["I believe you're overdrawn.", + 'I hope your balance is high enough for this.', + 'Take that, with interest.', + 'Your balance is dropping.', + "You're going to need to make a deposit soon.", + "You've suffered an economic collapse.", + "I think you're in a slump.", + 'Your finances have taken a decline.', + 'I foresee a definite downturn.', + "It's a reversal of fortune."], + 'WriteOff': ['Let me increase your losses.', + "Let's make the best of a bad deal.", + 'Time to balance the books.', + "This won't look good on your books.", + "I'm looking for some dividends.", + 'You must account for your losses.', + 'You can forget about a bonus.', + "I'll shuffle your accounts around.", + "You're about to suffer some losses.", + 'This is going to hurt your bottom line.']} +BuildingWaitingForVictors = ('Waiting for other players...',) +ElevatorHopOff = 'Hop off' +ElevatorStayOff = "If you hop off, you'll need to wait\nfor the elevator to leave or empty." +ElevatorLeaderOff = 'Only your leader can decide when to hop off.' +ElevatorHoppedOff = 'You need to wait for the next elevator.' +ElevatorMinLaff = 'You need %s laff points to ride this elevator.' +ElevatorHopOK = 'Okay' +ElevatorGroupMember = 'Only your group leader can\n decide when to board.' +KartMinLaff = 'You need %s laff points to ride this kart' +CogsIncExt = ', Inc.' +CogsIncModifier = '%s' + CogsIncExt +CogsInc = Cogs.upper() + CogsIncExt +CogdominiumsExt = ' Field Office' +Cogdominiums = Cog.upper() + CogdominiumsExt +DoorKnockKnock = 'Knock, knock.' +DoorWhosThere = "Who's there?" +DoorWhoAppendix = ' who?' +DoorNametag = 'Door' +FADoorCodes_UNLOCKED = None +FADoorCodes_TALK_TO_TOM = 'You need gags! Go talk to Tutorial Tom!' +FADoorCodes_DEFEAT_FLUNKY_HQ = 'Come back here when you have defeated the Flunky!' +FADoorCodes_TALK_TO_HQ = 'Go get your reward from HQ Harry!' +FADoorCodes_WRONG_DOOR_HQ = 'Wrong door! Take the other door to the playground!' +FADoorCodes_GO_TO_PLAYGROUND = 'Wrong way! You need to go to the playground!' +FADoorCodes_DEFEAT_FLUNKY_TOM = 'Walk up to that Flunky to battle him!' +FADoorCodes_TALK_TO_HQ_TOM = 'Go get your reward from Toon Headquarters!' +FADoorCodes_SUIT_APPROACHING = None +FADoorCodes_BUILDING_TAKEOVER = "Watch out! There's a Cog in there!" +FADoorCodes_SB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Sellbot Disguise first!\n\nBuild your Sellbot Disguise out of parts from the Factory." +FADoorCodes_CB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Cashbot Disguise first!\n\nBuild your Cashbot Disguise by doing ToonTasks in Donald's Dreamland." +FADoorCodes_LB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Lawbot Disguise first!\n\nBuild your Lawbot Disguise by doing the ToonTasks after Donald's Dreamland." +FADoorCodes_BB_DISGUISE_INCOMPLETE = "You'll get caught going in there as a Toon! You need to complete your Bossbot Disguise first!\n\nBuild your Bossbot Disguise by doing the ToonTasks after Donald's Dreamland." +KnockKnockContestJokes = {2100: ['Wally', "Wally's not looking, hit him with a pie!"], + 2200: {28: ['Biscuit', 'Biscuit out of here the Cogs are coming!'], + 41: ['Dewey', 'Dewey want to go defeat some more Cogs?'], + 40: ['Minnie', "Minnie people have asked that, and it's driving me crazy!"], + 27: ['Disguise', 'Disguise where the Cogs fly!']}, + 2300: ['Justin', 'Justin other couple of Cog parts and off we go!'], + 3300: {10: ['Aladdin', 'Aladdin HQ wants a word with you.'], + 6: ['Weirdo', 'Weirdo all these Cogs come from?'], + 30: ['Bacon', 'Bacon a cake to throw at the Cogs.'], + 28: ['Isaiah', 'Isaiah we go ride the trolley.'], + 12: ['Juliet', "Juliet me in that Cog building with you and I'll give you a Toon-Up."]}} +KnockKnockJokes = [['Who', "Bad echo in here, isn't there?"], + ['Dozen', 'Dozen anybody want to let me in?'], + ['Freddie', 'Freddie or not, here I come.'], + ['Dishes', 'Dishes your friend, let me in.'], + ['Wooden shoe', 'Wooden shoe like to know.'], + ['Betty', "Betty doesn't know who I am."], + ['Kent', 'Kent you tell?'], + ['Noah', "Noah don't know who either."], + ["I don't know", 'Neither do I, I keep telling you that.'], + ['Howard', 'Howard I know?'], + ['Emma', 'Emma so glad you asked me that.'], + ['Auto', "Auto know, but I've forgotten."], + ['Jess', 'Jess me and my shadow.'], + ['One', 'One-der why you keep asking that?'], + ['Alma', 'Alma not going to tell you!'], + ['Zoom', 'Zoom do you expect?'], + ['Amy', "Amy fraid I've forgotten."], + ['Arfur', 'Arfur got.'], + ['Ewan', 'No, just me'], + ['Cozy', "Cozy who's knocking will you?"], + ['Sam', 'Sam person who knocked on the door last time.'], + ['Fozzie', 'Fozzie hundredth time, my name is ' + Flippy + '.'], + ['Deduct', Donald + ' Deduct.'], + ['Max', 'Max no difference, just open the door.'], + ['N.E.', 'N.E. body you like, let me in.'], + ['Amos', 'Amos-quito bit me.'], + ['Alma', "Alma candy's gone."], + ['Bruce', "I Bruce very easily, don't hit me."], + ['Colleen', "Colleen up your room, it's filthy."], + ['Elsie', 'Elsie you later.'], + ['Hugh', 'Hugh is going to let me in?'], + ['Hugo', "Hugo first - I'm scared."], + ['Ida', 'Ida know. Sorry!'], + ['Isabel', 'Isabel on a bike really necessary?'], + ['Joan', "Joan call us, we'll call you."], + ['Kay', 'Kay, L, M, N, O, P.'], + ['Justin', 'Justin time for dinner.'], + ['Liza', 'Liza wrong to tell.'], + ['Luke', 'Luke and see who it is.'], + ['Mandy', "Mandy the lifeboats, we're sinking."], + ['Max', 'Max no difference - just open the door!'], + ['Nettie', 'Nettie as a fruitcake.'], + ['Olivia', 'Olivia me alone!'], + ['Oscar', 'Oscar stupid question, you get a stupid answer.'], + ['Patsy', 'Patsy dog on the head, he likes it.'], + ['Paul', "Paul hard, the door's stuck again."], + ['Thea', 'Thea later, alligator.'], + ['Tyrone', "Tyrone shoelaces, you're old enough."], + ['Stella', 'Stella no answer at the door.'], + ['Uriah', 'Keep Uriah on the ball.'], + ['Dwayne', "Dwayne the bathtub. I'm drowning."], + ['Dismay', "Dismay be a joke, but it didn't make me laugh."], + ['Ocelot', "Ocelot of questions, don't you?"], + ['Thermos', 'Thermos be a better knock knock joke than this.'], + ['Sultan', 'Sultan Pepper.'], + ['Vaughan', 'Vaughan day my prince will come.'], + ['Donald', 'Donald come baby, cradle and all.'], + ['Lettuce', "Lettuce in, won't you?"], + ['Ivor', 'Ivor sore hand from knocking on your door!'], + ['Isabel', 'Isabel broken, because I had to knock.'], + ['Heywood, Hugh, Harry', 'Heywood Hugh Harry up and open this door.'], + ['Juan', "Juan of this days you'll find out."], + ['Earl', 'Earl be glad to tell you if you open this door.'], + ['Abbot', 'Abbot time you opened this door!'], + ['Ferdie', 'Ferdie last time, open the door!'], + ['Don', 'Don mess around, just open the door.'], + ['Sis', 'Sis any way to treat a friend?'], + ['Isadore', 'Isadore open or locked?'], + ['Harry', 'Harry up and let me in!'], + ['Theodore', "Theodore wasn't open so I knocked-knocked."], + ['Ken', 'Ken I come in?'], + ['Boo', "There's no need to cry about it."], + ['You', 'You who! Is there anybody there?'], + ['Ice cream', "Ice cream if you don't let me in."], + ['Sarah', "Sarah 'nother way into this building?"], + ['Mikey', 'Mikey dropped down the drain.'], + ['Doris', 'Doris jammed again.'], + ['Yelp', 'Yelp me, the door is stuck.'], + ['Scold', 'Scold outside.'], + ['Diana', 'Diana third, can I have a drink please?'], + ['Doris', 'Doris slammed on my finger, open it quick!'], + ['Lettuce', 'Lettuce tell you some knock knock jokes.'], + ['Izzy', 'Izzy come, izzy go.'], + ['Omar', 'Omar goodness gracious - wrong door!'], + ['Says', "Says me, that's who!"], + ['Duck', "Just duck, they're throwing things at us."], + ['Tank', "You're welcome."], + ['Eyes', 'Eyes got loads more knock knock jokes for you.'], + ['Pizza', 'Pizza cake would be great right now.'], + ['Closure', 'Closure mouth when you eat.'], + ['Harriet', "Harriet all my lunch, I'm starving."], + ['Wooden', 'Wooden you like to know?'], + ['Punch', 'Not me, please.'], + ['Gorilla', 'Gorilla me a hamburger.'], + ['Jupiter', "Jupiter hurry, or you'll miss the trolley."], + ['Bertha', 'Happy Bertha to you!'], + ['Cows', 'Cows go "moo" not "who."'], + ['Tuna fish', "You can tune a piano, but you can't tuna fish."], + ['Consumption', 'Consumption be done about all these knock knock jokes?'], + ['Banana', 'Banana spilt so ice creamed.'], + ['X', 'X-tremely pleased to meet you.'], + ['Haydn', 'Haydn seek is fun to play.'], + ['Rhoda', 'Rhoda boat as fast as you can.'], + ['Quacker', "Quacker 'nother bad joke and I'm off!"], + ['Nana', 'Nana your business.'], + ['Ether', 'Ether bunny.'], + ['Little old lady', "My, you're good at yodelling!"], + ['Beets', 'Beets me, I forgot the joke.'], + ['Hal', 'Halloo to you too!'], + ['Sarah', 'Sarah doctor in the house?'], + ['Aileen', 'Aileen Dover and fell down.'], + ['Atomic', 'Atomic ache'], + ['Agatha', 'Agatha headache. Got an aspirin?'], + ['Stan', "Stan back, I'm going to sneeze."], + ['Hatch', 'Bless you.'], + ['Ida', "It's not Ida who, it's Idaho."], + ['Zippy', 'Mrs. Zippy.'], + ['Yukon', 'Yukon go away and come back another time.']] +SharedChatterGreetings = ['Hi, %!', + 'Yoo-hoo %, nice to see you.', + "I'm glad you're here today!", + 'Well, hello there, %.'] +SharedChatterComments = ["That's a great name, %.", + 'I like your name.', + 'Watch out for the ' + Cogs + '.', + 'Looks like the trolley is coming!', + 'I need to play a trolley game to get some pies!', + 'Sometimes I play trolley games just to eat the fruit pie!', + 'Whew, I just stopped a bunch of ' + Cogs + '. I need a rest!', + 'Yikes, some of those ' + Cogs + ' are big guys!', + "You look like you're having fun.", + "Oh boy, I'm having a good day.", + "I like what you're wearing.", + "I think I'll go fishing this afternoon.", + 'Have fun in my neighborhood.', + 'I hope you are enjoying your stay in Toontown!', + "I heard it's snowing at the Brrrgh.", + 'Have you ridden the trolley today?', + 'I like to meet new people.', + 'Wow, there are lots of ' + Cogs + ' in the Brrrgh.', + 'I love to play tag. Do you?', + 'Trolley games are fun to play.', + 'I like to make people laugh.', + "It's fun helping my friends.", + "A-hem, are you lost? Don't forget your map is in your shticker Book.", + 'Try not to get tied up in the ' + Cogs + "' Red Tape.", + 'I hear ' + Daisy + ' has planted some new flowers in her garden.', + 'If you press the Page Up key, you can look up!', + 'If you help take over Cog buildings, you can earn a bronze star!', + 'If you press the Tab key, you can see different views of your surroundings!', + 'If you press the Ctrl key, you can jump!'] +SharedChatterGoodbyes = ['I have to go now, bye!', + "I think I'll go play a trolley game.", + "Well, so long. I'll be seeing you, %!", + "I'd better hurry and get to work stopping those " + Cogs + '.', + "It's time for me to get going.", + 'Sorry, but I have to go.', + 'Good-bye.', + 'See you later, %!', + "I think I'm going to go practice tossing cupcakes.", + "I'm going to join a group and stop some " + Cogs + '.', + 'It was nice to see you today, %.', + "I have a lot to do today. I'd better get busy."] +MickeyChatter = (['Welcome to ' + lToontownCentral + '.', 'Hi, my name is ' + Mickey + ". What's yours?"], ['Hey, have you seen ' + Donald + '?', + "I'm going to go watch the fog roll in at " + lDonaldsDock + '.', + 'If you see my pal ' + Goofy + ', say hi to him for me.', + 'I hear ' + Daisy + ' has planted some new flowers in her garden.'], ["I'm going to MelodyLand to see " + Minnie + '!', + "Gosh, I'm late for my date with " + Minnie + '!', + "Looks like it's time for " + Pluto + "'s dinner.", + "I think I'll go swimming at " + lDonaldsDock + '.', + "It's time for a nap. I'm going to Dreamland."]) +WinterMickeyCChatter = (["Hi, I'm Merry Mickey!", + 'Welcome to Tinseltown... I mean, Toontown!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %'], ['Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + 'Golly, these halls sure are decked!', + 'Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + 'Just look at those tree lights! What a sight!', + 'Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + 'Not a creature is stirring, except this mouse!', + 'Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + 'I love this time of year!', + 'Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + "I'm feeling jolly, how about you?", + 'Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + 'Know any good carols?', + 'Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + 'Oh boy! I love Winter Holiday!', + 'Sing your seasonal cheer at Joy Buzzers to the World and Joy is sure to return the favor!', + "I think I'll trade my gloves for mittens!"], ['Have a happy Winter Holiday!', + 'Warm wishes to you!', + 'Shucks, sorry you have to go. So long!', + "I'm going caroling with Minnie!"]) +ValentinesMickeyChatter = (["Hi, I'm Mickey!", + 'Welcome to ValenToontown Central!', + "Happy ValenToon's Day!", + "Happy ValenToon's Day, %"], ['Love is in the air! And butterflies!', + 'Those hearts are good for Laff boosts!', + 'I hope Minnie likes what I got her!', + "The Cattlelog has lots of ValenToon's Day gifts!", + "Throw a ValenToon's Day party!", + 'Show the Cogs you love them with a pie in the face!', + "I'm taking Minnie out to the Kooky Cafe!", + 'Will Minnie want chocolates or flowers?'], ['I loved having you visit!', "Tell Minnie I'll pick her up soon!"]) +WinterMickeyDChatter = (["Hi, I'm Merry Mickey!", + 'Welcome to Tinseltown... I mean, Toontown!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %'], ['Golly, these halls sure are decked!', + 'Just look at those tree lights! What a sight!', + 'Not a creature is stirring, except this mouse!', + 'I love this time of year!', + "I'm feeling jolly, how about you?", + 'Know any good carols?', + 'Oh boy! I love Winter Holiday!', + "I think I'll trade my gloves for mittens!"], ['Have a happy Winter Holiday!', + 'Warm wishes to you!', + 'Shucks, sorry you have to go. So long!', + "I'm going caroling with Minnie!"]) +VampireMickeyChatter = (['Welcome to ' + lToontownCentral + '.', + 'Hi, my name is ' + Mickey + ". What's yours?", + 'Happy Halloween!', + 'Happy Halloween, %!', + 'Welcome to Tombtown... I mean Toontown!'], ['If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + "It's fun to dress up for Halloween!", + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + 'Do you like my costume?', + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + '%, watch out for Bloodsucker Cogs!', + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + "Aren't the Halloween decorations great?", + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + 'Beware of black cats!', + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + 'Did you see the Toon with the pumpkin head?', + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + 'Boo! Did I scare you?', + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + "Don't forget to brush your fangs!", + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + "I'm a vampire, but not a Bloodsucker!", + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + "I hope you're enjoying our Halloween fun!", + 'If you think playing tricks is All Fun and Games, go see Lazy Hal for a treat!', + 'Vampires are really popular this year!'], ["I'm going to check out the cool Halloween decorations.", + "I'm going to MelodyLand to surprise " + Minnie + '!', + "I'm going to sneak up on another Toon! Shhh!", + "I'm going trick-or-treating!", + 'Shhh, sneak with me.']) +FieldOfficeMickeyChatter = ['Have you heard about the new Mover & Shaker Field Offices?'] +MinnieChatter = (['Welcome to Melodyland.', 'Hi, my name is ' + Minnie + ". What's yours?"], ['The hills are alive with the sound of music!', + 'You have a cool outfit, %.', + 'Hey, have you seen ' + Mickey + '?', + 'If you see my friend ' + Goofy + ', say hi to him for me.', + 'Wow, there are lots of ' + Cogs + ' near ' + Donald + "'s Dreamland.", + "I heard it's foggy at the " + lDonaldsDock + '.', + 'Be sure and try the maze in ' + lDaisyGardens + '.', + "I think I'll go catch some tunes.", + 'Hey %, look at that over there.', + 'I love the sound of music.', + "I bet you didn't know Melodyland is also called TuneTown! Hee Hee!", + 'I love to play the Matching Game. Do you?', + 'I like to make people giggle.', + 'Boy, trotting around in heels all day is hard on your feet!', + 'Nice shirt, %.', + 'Is that a jellybean on the ground?'], ["Gosh, I'm late for my date with %s!" % Mickey, "Looks like it's time for %s's dinner." % Pluto, "It's time for a nap. I'm going to Dreamland."]) +WinterMinnieCChatter = (["Hi, I'm Merry Minnie!", + 'Welcome to the land of carols!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ["You'll get more than a Shave and a Haircut For a Song if you carol to Barbara Seville!", + 'Belt out a tune, Toon!', + "You'll get more than a Shave and a Haircut For a Song if you carol to Barbara Seville!", + 'Show us how to croon, Toon!', + "You'll get more than a Shave and a Haircut For a Song if you carol to Barbara Seville!", + 'Can you carry a melody here in Melodyland?', + "You'll get more than a Shave and a Haircut For a Song if you carol to Barbara Seville!", + 'Those lamps look warm in their scarves!', + "You'll get more than a Shave and a Haircut For a Song if you carol to Barbara Seville!", + "The sing's the thing!", + "You'll get more than a Shave and a Haircut For a Song if you carol to Barbara Seville!", + "I'll always like you, for better or verse!", + "You'll get more than a Shave and a Haircut For a Song if you carol to Barbara Seville!", + 'Everything looks better with a wreath!'], ['Have a fun Winter Holiday!', 'Happy Trails!', 'Mickey is taking me caroling!']) +WinterMinnieDChatter = (["Hi, I'm Merry Minnie!", + 'Welcome to the land of carols!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ['Belt out a tune, Toon!', + 'Show us how to croon, Toon!', + 'Can you carry a melody here in Melodyland?', + 'Those lamps look warm in their scarves!', + "The sing's the thing!", + "You can't go wrong with a song!", + "I'll always like you, for better or verse!", + 'Everything looks better with a wreath!'], ['Have a fun Winter Holiday!', 'Happy Trails!', 'Mickey is taking me caroling!']) +ValentinesMinnieChatter = (["Hello, I'm Minnie!", "Happy ValenToon's Day!", "Happy ValenToon's Day, %"], ['I hope Mickey got me chocolates or flowers!', + 'Those hearts are good for Laff boosts!', + 'I want to go to a ValenToon Party!', + 'I hope Mickey takes me to the Kooky Cafe!', + 'Mickey is such a good ValenToon!', + 'What did you get your ValenToon?', + "Mickey has never missed a ValenToon's Day!"], ['It was sweet having you visit!']) +WitchMinnieChatter = (['Welcome to Magicland... I mean Melodyland!', + "Hi, my name is Magic Minnie! What's yours?", + "Hello, I think you're enchanting!", + 'Happy Halloween!', + 'Happy Halloween, %!'], ['I hear Tabitha has treats for Really Kool Katz who can play tricks!', + "It's a magical day, don't you think?", + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'Now where did I put my spell book', + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'Abra-Cadabra!', + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'Toontown looks positively spooky today!', + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'Are you seeing stars too?', + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'Purple is really my color!', + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'I hope your Halloween is bewitching!', + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'Beware of musical spiders!', + 'I hear Tabitha has treats for Really Kool Katz who can play tricks!', + 'I hope you are enjoying our Halloween fun!'], ["I'm going to disappear now!", 'Time for me to vanish!', 'Mickey is taking me Trick-or-Treating!']) +FieldOfficeMinnieChatter = ['Everyone is talking about the new Mover & Shaker Field Offices!'] +DaisyChatter = (['Welcome to my garden!', "Hello, I'm " + Daisy + ". What's your name?", "It's so nice to see you %!"], ['My prize winning flower is at the center of the garden maze.', + 'I just love strolling through the maze.', + "I haven't seen " + Goofy + ' all day.', + 'I wonder where ' + Goofy + ' is.', + 'Have you seen ' + Donald + "? I can't find him anywhere.", + 'If you see my friend ' + Minnie + ', please say "Hello" to her for me.', + 'The better gardening tools you have the better plants you can grow.', + 'There are far too many ' + Cogs + ' near ' + lDonaldsDock + '.', + 'Watering your garden every day keeps your plants happy.', + 'To grow a Pink Daisy plant a yellow and red jellybean together.', + 'Yellow daisies are easy to grow, just plant a yellow jellybean.', + 'If you see sand under a plant it needs water or it will wilt!'], ["I'm going to Melody Land to see %s!" % Minnie, + "I'm late for my picnic with %s!" % Donald, + "I think I'll go swimming at " + lDonaldsDock + '.', + "Oh, I'm a little sleepy. I think I'll go to Dreamland."]) +ValentinesDaisyChatter = (["Hi, I'm Daisy!", "Happy ValenToon's Day!", "Happy ValenToon's Day, %"], ["I hope Donald doesn't get me another Amore Eel!", + 'Donald is taking me out to the Deep-see Diner!', + 'I certainly have enough roses!', + 'Those hearts are good for Laff boosts!', + "I'd love to go to a ValenToon's Day party!", + 'This is the garden where love grows!', + "Donald better not sleep through ValenToon's Day again!", + 'Maybe Donald and I can double-date with Mickey and Minnie!'], ["Tell Donald I'll be waiting for him!", "Have a nice ValenToon's Day!"]) +WinterDaisyCChatter = (['Welcome to the only garden that grows in the winter!', 'Happy Winter Holiday!', 'Happy Winter Holiday, %!'], ['Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + 'My garden needs more mistletoe!', + 'Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + 'I need to plant holly for next year!', + 'Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + "I'm going to ask Goofy to build me a gingerbread house!", + 'Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + 'Those lights on the lamps are lovely!', + 'Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + 'That is some jolly holly!', + 'Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + 'My snowman keeps melting!', + 'Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + 'That duck is decked out!', + 'Shoshanna at Pine Needle Crafts is a real sap for songs, so why not craft her a carol?', + 'I grew all these lights myself!'], ['Have a jolly Winter Holiday!', + 'Happy planting!', + 'Tell Donald to stop by with presents!', + 'Donald is taking me caroling!']) +WinterDaisyDChatter = (['Welcome to the only garden that grows in the winter!', 'Happy Winter Holiday!', 'Happy Winter Holiday, %!'], ['My garden needs more mistletoe!', + 'I need to plant holly for next year!', + "I'm going to ask Goofy to build me a gingerbread house!", + 'Those lights on the lamps are lovely!', + 'That is some jolly holly!', + 'My snowman keeps melting!', + 'That duck is decked out!', + 'I grew all these lights myself!'], ['Have a jolly Winter Holiday!', + 'Happy planting!', + 'Tell Donald to stop by with presents!', + 'Donald is taking me caroling!']) +HalloweenDaisyChatter = (['Welcome to Daisy Ghosts... I mean Gardens!', 'Happy Halloween!', 'Happy Halloween, %!'], ['Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'Wanna dance?', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + "I'm a duck with a poodle skirt!", + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'The pirate tree needs water.', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'Trick-or-Tree!', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'Do you notice anything strange about the trees?', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'I should grow some pumpkins!', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'WHO notices something different about the lamps?', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'Halloween really grows on me!', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'Twig-or-Treat!', + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + "Owl bet you didn't notice the spooky lamps!", + 'Visit my friend Leif Pyle if you have a trick and Rake Inn the treats!', + 'I hope you are enjoying our Halloween fun!'], ['Donald is taking me Trick-or-Treating!', "I'm going to check out the fun Halloween decorations."]) +FieldOfficeDaisyChatter = ['Those Mover & Shaker Field Offices are popping up like weeds!'] +ChipChatter = (['Welcome to %s!' % lOutdoorZone, + "Hello, I'm " + Chip + ". What's your name?", + "No, I'm " + Chip + '.', + "It's so nice to see you %!", + 'We are Chip and Dale!'], ['I like golf.', 'We have the best acorns in Toontown.', 'The golf holes with volcanoes are the most challenging for me.'], ["We're going to the " + lTheBrrrgh + ' and play with %s.' % Pluto, + "We'll visit %s and fix him." % Donald, + "I think I'll go swimming at " + lDonaldsDock + '.', + "Oh, I'm a little sleepy. I think I'll go to Dreamland."]) +ValentinesChipChatter = (["I'm Chip!", "Happy ValenToon's Day!", "Happy ValenToon's Day, %!"], ["What did you get me for ValenToon's Day, Dale?", + 'Those hearts are good for Laff boosts!', + 'Will you be my ValenToon, Dale?', + "What did you get the Cogs for ValenToon's Day?", + "I love ValenToon's Day!"], ['Come back any time!']) +WinterChipChatter = (['Happy Winter Holiday!', 'Dressed as chipmunks!', 'Happy Winter Holiday, %!'], ['Happy Winter Holiday, Dale!', + 'All this water could freeze any minute!', + 'We should switch the golf balls with snowballs!', + 'If only chipmunks knew how to sing!', + 'Did you remember to store nuts for the winter?', + 'Did you get the Cogs a present?'], ['Go nuts this Winter Holiday!', 'Have a joyful winter Holiday!']) +HalloweenChipChatter = (['Play some MiniGhoul... I mean Golf!', 'Happy Halloween!', 'Happy Halloween, %!'], ["We're nuts about Halloween!", + "You're under arrest", + "You can't outrun the long arm of the law", + "I'm a Bobby!", + 'I hope you are enjoying our Halloween fun!', + 'Play golf and get a Howl-In-One.', + 'Candy corns are sweeter than acorns.', + 'I hope you are enjoying our Halloween fun!'], ['%, watch out for Bloodsucker Cogs!']) +DaleChatter = (["It's so nice to see you %!", + "Hello, I'm " + Dale + ". What's your name?", + "Hi I'm " + Chip + '.', + 'Welcome to %s!' % lOutdoorZone, + 'We are Chip and Dale!'], ['I like picnics.', 'Acorns are tasty, try some.', 'Those windmills can be hard too.'], ['Hihihi ' + Pluto + ' is fun to play with.', + "Yeah, let's fix %s." % Donald, + 'A swim sounds refreshing.', + "I'm getting tired and could use a nap."]) +ValentinesDaleChatter = (["I'm Dale!", "Happy ValenToon's Day!", "Happy ValenToon's Day, %!"], ['Same thing as last year. Nothing!', + 'I miss the nuts!', + 'Will you be my ValenToon, Chip?', + 'A pie in the face', + "Yeah, it's all right."], ['Come back any time!']) +WinterDaleChatter = (['Merry chipmunks!', + "Hi, we're two merry elves!", + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ['Happy Winter Holiday, Chip!', + 'Better not be on the geyser when it happens!', + 'And the golf clubs with icicles!', + 'Whoever heard of singing chipmunks?', + 'I told YOU to do that!', + 'Yes, a cream pie!'], ['And bring some back for us!', 'Have a joyful Winter Holiday!']) +HalloweenDaleChatter = (['Happy Halloween, %!', 'Play some MiniGhoul... I mean Golf!', 'Happy Halloween!'], ["We're nuts about Halloween!", + 'Great, I could use a rest!', + 'But your arms are short!', + 'I thought you were a Chip!', + 'Play golf and get a Howl-In-One', + 'Candy corns are sweeter than acorns.', + 'I hope you are enjoying our Halloween fun!'], ['%, watch out for Bloodsucker Cogs!']) +GoofyChatter = (['Welcome to ' + lDaisyGardens + '.', 'Hi, my name is ' + Goofy + ". What's yours?", "Gawrsh, it's nice to see you %!"], ['Boy it sure is easy to get lost in the garden maze!', + 'Be sure and try the maze here.', + "I haven't seen " + Daisy + ' all day.', + 'I wonder where ' + Daisy + ' is.', + 'Hey, have you seen ' + Donald + '?', + 'If you see my friend ' + Mickey + ', say hi to him for me.', + "D'oh! I forgot to fix " + Mickey + "'s breakfast!", + 'Gawrsh there sure are a lot of ' + Cogs + ' near ' + lDonaldsDock + '.', + 'It looks like ' + Daisy + ' has planted some new flowers in her garden.', + 'At the Brrrgh branch of my Gag Shop, Hypno-Goggles are on sale for only 1 jellybean!', + "Goofy's Gag Shops offer the best jokes, tricks, and funnybone-ticklers in all of Toontown!", + "At Goofy's Gag Shops, every pie in the face is guaranteed to make a laugh or you get your jellybeans back!"], ["I'm going to Melody Land to see %s!" % Mickey, + "Gosh, I'm late for my game with %s!" % Donald, + "I think I'll go swimming at " + lDonaldsDock + '.', + "It's time for a nap. I'm going to Dreamland."]) +WinterGoofyChatter = (["I'm Goofy about the holidays!", + 'Welcome to Snowball Speedway!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ['Who needs reindeer when you have a fast kart?', + 'Gawrsh! Is it Winter Holiday already?', + 'I need my earmuffs!', + "I haven't done any shopping yet!", + "Don't drive your kart on ice!", + 'Seems like it was Winter Holiday only a year ago!', + 'Treat your kart to a present and spruce it up!', + 'These karts are better than any old sleigh!'], ['Have a cheery Winter Holiday!', 'Drive safe, now!', 'Watch out for flying reindeer!']) +ValentinesGoofyChatter = (["I'm Goofy about ValenToon's Day!", "Happy ValenToon's Day!", "Happy ValenToon's Day, %!"], ["Gawrsh! Is it ValenToon's Day already?", + 'I LOVE kart racing!', + 'Be sweet to each other out there!', + 'Show your sweetie a new kart!', + 'Toons love their karts!', + 'Make some new friends on the track!'], ['Drive safe, now!', 'Show some love out there!']) +GoofySpeedwayChatter = (['Welcome to ' + lGoofySpeedway + '.', 'Hi, my name is ' + Goofy + ". What's yours?", "Gawrsh, it's nice to see you %!"], ['Boy, I saw a terrific race earlier.', + 'Watch out for banana peels on the race track!', + 'Have you upgraded your kart lately?', + 'We just got in some new rims at the kart shop.', + 'Hey, have you seen ' + Donald + '?', + 'If you see my friend ' + Mickey + ', say hi to him for me.', + "D'oh! I forgot to fix " + Mickey + "'s breakfast!", + 'Gawrsh there sure are a lot of ' + Cogs + ' near ' + lDonaldsDock + '.', + 'At the Brrrgh branch of my Gag Shop, Hypno-Goggles are on sale for only 1 jellybean!', + "Goofy's Gag Shops offer the best jokes, tricks, and funnybone-ticklers in all of Toontown!", + "At Goofy's Gag Shops, every pie in the face is guaranteed to make a laugh or you get your jellybeans back!"], ["I'm going to Melody Land to see %s!" % Mickey, + "Gosh, I'm late for my game with %s!" % Donald, + "I think I'll go swimming at " + lDonaldsDock + '.', + "It's time for a nap. I'm going to Dreamland."]) +SuperGoofyChatter = (['Welcome to my Super Speedway!', + "Hi, I'm Super Goof! What's your name?", + 'Happy Halloween!', + 'Happy Halloween, %!'], ['I am feeling kind of batty today!', + 'Anybody see my cape around? Oh, there it is!', + "Gawrsh! I don't know my own strength!", + 'Did somebody call for a superhero?', + "Beware Cogs, I'll save Halloween!", + "There's nothing scarier than me in a kart!", + "I bet you don't know who I am with this mask on!", + "It's fun to dress up for Halloween!", + 'I hope you are enjoying our Halloween fun!'], ['Gotta fly!', + 'Hi-Ho and away I go!', + "Should I fly or drive to Donald's Dock?", + 'Gawrsh, have a Happy Halloween!']) +DonaldChatter = (['Welcome to Dreamland.', "Hi, my name is %s. What's yours?" % Donald], ['Sometimes this place gives me the creeps.', + 'Be sure and try the maze in ' + lDaisyGardens + '.', + "Oh boy, I'm having a good day.", + 'Hey, have you seen ' + Mickey + '?', + 'If you see my buddy ' + Goofy + ', say hi to him for me.', + "I think I'll go fishing this afternoon.", + 'Wow, there are lots of ' + Cogs + ' at ' + lDonaldsDock + '.', + "Hey, didn't I take you on a boat ride at " + lDonaldsDock + '?', + "I haven't seen " + Daisy + ' all day.', + 'I hear ' + Daisy + ' has planted some new flowers in her garden.', + 'Quack.'], ["I'm going to Melody Land to see %s!" % Minnie, + "Gosh, I'm late for my date with %s!" % Daisy, + "I think I'll go swimming at my dock.", + "I think I'll take my boat for a spin at my dock."]) +WinterDreamlandCChatter = (["Hi, I'm Dozing Donald!", + 'Welcome to Holiday Dreamland!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ['Willow says that learning a little Sleep Voice Training is a real present, sing her a tune and find out why!', + 'I wish I was nestled all snug in my bed!', + 'Willow says that learning a little Sleep Voice Training is a real present, sing her a tune and find out why!', + "I'm dreaming of a white Toontown!", + 'Willow says that learning a little Sleep Voice Training is a real present, sing her a tune and find out why!', + 'I meant to leave out milk and cookies!', + 'Willow says that learning a little Sleep Voice Training is a real present, sing her a tune and find out why!', + 'When I wake up, I better see lots of presents!', + 'Willow says that learning a little Sleep Voice Training is a real present, sing her a tune and find out why!', + "I hope I don't sleep through the holidays!", + 'Willow says that learning a little Sleep Voice Training is a real present, sing her a tune and find out why!', + "I love a long winter's nap!", + 'Willow says that learning a little Sleep Voice Training is a real present, sing her a tune and find out why!', + 'The trees on the streets are covered in night lights!'], ['To all, a good night!', 'Sweet dreams!', 'When I wake up I am going caroling!']) +WinterDreamlandDChatter = (["Hi, I'm Dozing Donald!", + 'Welcome to Holiday Dreamland!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ['I wish I was nestled all snug in my bed!', + "I'm dreaming of a white Toontown!", + 'I meant to leave out milk and cookies!', + 'When I wake up, I better see lots of presents!', + "I hope I don't sleep through the holidays!", + "I love a long winter's nap!", + 'The trees on the streets are covered in night lights!'], ['To all, a good night!', 'Sweet dreams!', 'When I wake up I am going caroling!']) +HalloweenDreamlandChatter = (['Happy Halloween!', 'Happy Halloween, %!', "Hi, I'm FrankenDonald!"], ['If you can play a trick on my friend Max, then you can Relax To The Max with a treat!', + 'Am I awake or dreaming?', + 'If you can play a trick on my friend Max, then you can Relax To The Max with a treat!', + "I'm so scared, I can't fall asleep!", + 'If you can play a trick on my friend Max, then you can Relax To The Max with a treat!', + 'So this is what Dreamland looks like!', + 'If you can play a trick on my friend Max, then you can Relax To The Max with a treat!', + "Boy, I'm sleepy!", + 'If you can play a trick on my friend Max, then you can Relax To The Max with a treat!', + "I hope I don't sleep through Halloween this year!", + 'If you can play a trick on my friend Max, then you can Relax To The Max with a treat!', + 'I hope you are enjoying our Halloween fun!'], ['Sleep with the lights on tonight!', 'When I wake up, I am going Trick-or-Treating!']) +ValentinesDreamlandChatter = (["Hello, I'm (yawn) Donald!", "Happy ValenToon's Day!", "Happy ValenToon's Day, %!"], ["I hope I don't sleep through ValenToon's Day!", + "I'm dreaming of Daisy!", + "I had a nightmare that I missed ValenToon's Day!", + 'Those hearts are good for Laff boosts!', + "Throw a ValenToon's Day party!", + 'Show the Cogs you love them with a pie in the face!', + "I couldn't dream of a nicer holiday than ValenToon's Day!", + 'I love sleeping!'], ['Nite-nite!', "Wake me when it's ValenToon's Day!"]) +FieldOfficeDreamlandChatter = ['I dreamed about something called a Field Office...'] +HalloweenDonaldChatter = (['Welcome to my Halloween harbor!', + 'Come aboard, if you have treats!', + 'Happy Halloween!', + 'Happy Halloween, %!'], ['If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + "I'm dressed as a sailor!", + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + 'Pumpkins make great lanterns!', + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + "I've never seen palm trees with hairy legs before!", + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + "Maybe I'll be a pirate next Halloween!", + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + 'I think the best treats are starfish!', + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + "I'll take you Trick-or-Treating around the harbor!", + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + 'I hope those spiders stay in the trees!', + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + 'What do you call a ghost in the water? A BOO-y!', + 'If playing tricks is making you feel Rudderly Ridiculous, then go see Rudy for a treat!', + 'I hope you are enjoying our Halloween fun!'], ['Set sail for scares!', 'Happy haunting!', "I'm going to check out the spooky Halloween decorations."]) +ValentinesDonaldChatter = (["Ahoy, I'm Donald!", "Happy ValenToon's Day!", "Happy ValenToon's Day, %!"], ["Was I supposed to take Daisy somewhere for ValenToon's Day?", + "Just once more around the dock, then I'll get Daisy something.", + "What would Daisy like for ValenToon's Day?", + 'Those hearts in the water are good for Laff boosts!', + "Throw a ValenToon's Day party!", + 'Show the Cogs you love them with a pie in the face!', + "I'll have to catch an Amore Eel for Daisy!"], ['Aloha!', 'Give the Cogs my best!']) +WinterDonaldCChatter = (["Welcome to Donald's Boat and Sleigh Dock!", + 'All aboard for the Winter Holiday cruise!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ['I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + 'How do you like my duck-orations?', + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + 'What is snow doing on the lamp posts?', + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + 'This water better not ice over!', + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + 'How did they get the lights up in those trees?', + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + 'This boat is better than a sleigh! or is it?', + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + "I don't need reindeer to pull this boat!", + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + "I'm glad I'm not a turkey this time of year!", + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + 'My present to you? Free boat rides!', + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!', + "I hope I don't get a lump of coal again!", + 'I hear that Dante has Gifts With A Porpoise, share a song and he may have a gift for you too!'], ['All ashore for holiday fun!', 'Remember to tip your boat driver on the way out!', 'Enjoy your holiday!']) +WinterDonaldDChatter = (["Welcome to Donald's Boat and Sleigh Dock!", + 'All aboard for the Winter Holiday cruise!', + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %!'], ['How do you like my duck-orations?', + 'What is snow doing on the lamp posts?', + 'This water better not ice over!', + 'How did they get the lights up in those trees?', + 'This boat is better than a sleigh! or is it?', + "I don't need reindeer to pull this boat!", + "I'm glad I'm not a turkey this time of year!", + 'My present to you? Free boat rides!', + "I hope I don't get a lump of coal again!"], ['All ashore for holiday fun!', 'Remember to tip your boat driver on the way out!', 'Enjoy your holiday!']) +WesternPlutoChatter = (["Boo! Don't be scared, it's just me ... Pluto!", 'Happy Halloween, pardner!', 'Happy Halloween, %!'], ["Frosty Fred has treats for tricks, they make him feel like there's Snowplace Like Home!", + 'I do tricks for treats!', + "Frosty Fred has treats for tricks, they make him feel like there's Snowplace Like Home!", + "Mickey's taking me Trick-or-Treating later!", + "Frosty Fred has treats for tricks, they make him feel like there's Snowplace Like Home!", + 'It feels more like Winter Holiday than Halloween!', + "Frosty Fred has treats for tricks, they make him feel like there's Snowplace Like Home!", + "Bark! That's 'Trick-or-Treat' in dog!", + "Frosty Fred has treats for tricks, they make him feel like there's Snowplace Like Home!", + 'I hope you are enjoying our Halloween fun!', + "Frosty Fred has treats for tricks, they make him feel like there's Snowplace Like Home!", + 'I like to chase Black Cat Toons!', + "Frosty Fred has treats for tricks, they make him feel like there's Snowplace Like Home!", + "There's a snake in my boot!"], ["I'm going to go dig up a treat!", "I'm going to see if Mickey has some treats!", "I'm going to scare Donald!"]) +WinterPlutoCChatter = (["Hi, I'm Pluto!", + "Welcome to the Brrgh, where it's winter all year!", + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %'], ["Eddie could use a good tune, because Snowman's Land is a lonely place for a Yeti!", + 'I chewed on an icicle and got frost-bite!', + "Eddie could use a good tune, because Snowman's Land is a lonely place for a Yeti!", + 'This is like living in a snow globe!', + "Eddie could use a good tune, because Snowman's Land is a lonely place for a Yeti!", + 'I wish I was beside a warm fire!', + "Eddie could use a good tune, because Snowman's Land is a lonely place for a Yeti!", + 'Arf! Arf! I need a scarf!', + "Eddie could use a good tune, because Snowman's Land is a lonely place for a Yeti!", + "At least my nose isn't red and glowing!"], ['Have a fun Winter Holiday!', 'Come back any time you want snow!', 'Mickey is taking me caroling!']) +WinterPlutoDChatter = (["Hi, I'm Pluto!", + "Welcome to the Brrgh, where it's winter all year!", + 'Happy Winter Holiday!', + 'Happy Winter Holiday, %'], ['I chewed on an icicle and got frost-bite!', + 'This is like living in a snow globe!', + 'I wish I was beside a warm fire!', + 'Arf! Arf! I need a scarf!', + "At least my nose isn't red and glowing!"], ['Have a fun Winter Holiday!', 'Come back any time you want snow!', 'Mickey is taking me caroling!']) +AFMickeyChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ["Welcome to the Gardens! I'm " + Daisy + '!', + "I'm " + Daisy + ', and I love to garden!', + "April Toons' Week is the silliest week of the year!", + "What, you've never seen a duck with mouse ears?", + "Hi, I'm " + Daisy + '! Quack!', + "It's tough quacking like a duck!", + "I'm not feeling like myself today!", + 'Have you heard your Doodle talk yet?', + 'Gravity has taken a holiday at the Estates!'], ["Have a wacky April Toons' Week!", 'Tell Mickey I said hi!']) +AFMinnieChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ['Welcome to ' + lTheBrrrgh + "! I'm " + Pluto + '!', + "Hi, I'm " + Pluto + "! What's your name?", + "What, you've never seen a dog with mouse ears?", + "I'm not feeling like myself today!", + "Does anyone have a doggie biscuit? I'm hungry!", + 'Bark! My name is ' + Pluto + '!', + "Isn't this silly?", + "Don't make me chase you around!", + "April Toons' Week is the silliest week of the year!", + 'Have you heard your Doodle talk yet?', + 'Gravity has taken a holiday at the Estates!'], ["Have a wacky April Toons' Week!", 'I have to go chase cars now! Bye!']) +AFDaisyChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ['Welcome to ' + lToontownCentral + "! I'm " + Mickey + ' Mouse!', + "Hi, I'm " + Mickey + '! The happiest mouse in Toontown!', + 'If you see ' + Daisy + ', tell her ' + Mickey + ' said hi!', + "What, you've never seen a mouse with feathers?", + "Isn't this silly?", + "I'm not feeling like myself today!", + "April Toons' Week is the silliest week of the year!", + 'Have you heard your Doodle talk yet?', + 'Gravity has taken a holiday at the Estates!'], ['Bye! Tell them ' + Mickey + ' sent you!', 'If you go to ' + lDaisyGardens + ', say hi to her for me!']) +AFGoofySpeedwayChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ["Welcome to Dreamland! I'm " + Donald + '!', + "Hello, I'm " + Donald + '! Is it nap time yet?', + 'A duck needs his beauty rest, you know!', + "What, you've never seen a duck with dog ears?", + 'Gawrsh! I mean -- Quack!', + 'This would make a great race track ... um, I mean place to nap!', + "I'm not feeling like myself today!", + "April Toons' Week is the silliest week of the year!", + 'Have you heard your Doodle talk yet?', + 'Gravity has taken a holiday at the Estates!'], ['If you see ' + Goofy + ', tell him ' + Donald + ' says hi!', 'Bye, and good night!']) +AFDonaldChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ["Welcome to the Speedway! I'm " + Goofy + '!', + "I'm " + Goofy + ", and I'm dreaming I'm " + Donald + '!', + "I've heard of sleep walking, but sleep kart driving?", + 'Gawrsh! It sure is silly being ' + Goofy + '!', + 'How can I watch the races with my eyes closed?', + 'I better grab a nap before my next race!', + "April Toons' Week is the silliest week of the year!", + "I'm not feeling like myself today!", + 'Have you heard your Doodle talk yet?', + 'Gravity has taken a holiday at the Estates!'], ["Have a wacky April Toons' Week!", 'I need to work on my karts! Bye!']) +AFDonaldDockChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ["Everybody gets April Toons' Week off but me!", + "I'm the only one who has to work this week!", + 'I only get time off when I sleep!', + 'All my friends are pretending to be somebody else!', + 'Round and round in this boat, all day long!', + 'I heard Daisy is pretending to be Mickey!', + "The silliest week of the year, and I'm missing it!", + 'Have you heard your Doodle talk yet?', + 'Gravity has taken a holiday at the Estates!'], ["Have a wacky April Toons' Week!", 'Play a joke on the Cogs for me!']) +AFPlutoChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ["Welcome to Melodyland! I'm " + Minnie + '!', + 'Hi, my name is ' + Minnie + ' Mouse!', + "I'm as happy as a mouse can be!", + "What, you've never seen a mouse with dog ears?", + 'I love when ' + Mickey + ' and I go for walks!', + 'What, you never heard a mouse talk before?', + "April Toons' Week is the silliest week of the year!", + 'Have you heard your Doodle talk yet?', + 'Gravity has taken a holiday at the Estates!'], ["Have a wacky April Toons' Week!", 'If you see ' + Pluto + ', tell him ' + Minnie + ' says hi!']) +AFChipChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ["Hi, I'm " + Dale + '!', + 'How are you today, ' + Chip + '?', + 'I always thought you were ' + Dale + ', ' + Chip + '.', + "You're sure you're " + Chip + ' and not ' + Dale + ', ' + Chip + '?', + "April Toons' Week is the silliest week of the year!"], ['Bye from ' + Chip + ' and ' + Dale + '!']) +AFDaleChatter = (["Happy April Toons' Week!", "Happy April Toons' Week, %!"], ["Hi, I'm " + Chip + '!', + 'Very well ' + Dale + ', thanks!', + "Nope, I'm " + Chip + ', ' + Dale + '.', + 'Yes, ' + Dale + ", I'm " + Chip + ', not ' + Dale + '.', + 'It sure is, ' + Chip + '! I mean, ' + Dale + '.'], ['Or ' + Dale + ' and ' + Chip + '!']) +CLGoofySpeedwayChatter = (['Welcome to ' + lGoofySpeedway + '.', + 'Hi, my name is ' + Goofy + ". What's yours?", + "Gawrsh, it's nice to see you %!", + "Hi there! Pardon my dusty clothes I've been busy fixin' that broken Leaderboard."], ['We better get this Leaderboard working soon, Grand Prix Weekend is coming up!', + "Does anybody want to buy a slightly used kart? It's only been through the Leaderboard once!", + 'Grand Prix Weekend is coming, better get to practicing.', + 'Grand Prix Weekend will be here on Friday, May 22 through Monday, May 25!', + "I'm gonna need a ladder to get that kart down.", + 'That Toon really wanted to get on the Leaderboard!', + 'Boy, I saw a terrific race earlier.', + 'Watch out for banana peels on the race track!', + 'Have you upgraded your kart lately?', + 'We just got in some new rims at the kart shop.', + 'Hey, have you seen ' + Donald + '?', + 'If you see my friend ' + Mickey + ', say hi to him for me.', + "D'oh! I forgot to fix " + Mickey + "'s breakfast!", + 'Gawrsh there sure are a lot of ' + Cogs + ' near ' + lDonaldsDock + '.', + 'At the Brrrgh branch of my Gag Shop, Hypno-Goggles are on sale for only 1 jellybean!', + "Goofy's Gag Shops offer the best jokes, tricks, and funnybone-ticklers in all of Toontown!", + "At Goofy's Gag Shops, every pie in the face is guaranteed to make a laugh or you get your jellybeans back!"], ['I better go get my kart a new paint job for the upcoming Grand Prix Weekend.', + "Gosh, I better get workin' on this broken Leaderboard!", + "Hope I'll see y'all on Grand Prix Weekend! Goodbye!", + "It's time for a nap. I'm going to Dreamland to dream about winnin' the Grand Prix."]) +GPGoofySpeedwayChatter = (['Welcome to ' + lGoofySpeedway + '.', + 'Welcome to Grand Prix Weekend!', + 'Hi, my name is ' + Goofy + ". What's yours?", + "Gawrsh, it's nice to see you %!"], ['Are you excited about the Grand Prix Weekend?', + 'Grand Prix Weekend really drives up those scores!', + 'Get more tickets by racing practice laps.', + "Gawrsh, you're a fast racer!", + 'Boy, I saw a terrific race earlier.', + 'Watch out for banana peels on the race track!', + 'Have you upgraded your kart lately?', + 'We just got in some new rims at the kart shop.', + 'Hey, have you seen ' + Donald + '? He said he was gonna come watch the Grand Prix!', + 'If you see my friend ' + Mickey + ", tell him he's missing some great racing!", + "D'oh! I forgot to fix " + Mickey + "'s breakfast!", + 'Gawrsh there sure are a lot of ' + Cogs + ' near ' + lDonaldsDock + '.', + 'At the Brrrgh branch of my Gag Shop, Hypno-Goggles are on sale for only 1 jellybean!', + "Goofy's Gag Shops offer the best jokes, tricks, and funnybone-ticklers in all of Toontown!", + "At Goofy's Gag Shops, every pie in the face is guaranteed to make a laugh or you get your jellybeans back!"], ['Good luck in the Grand Prix!', + "I'm going to catch the next race in the Grand Prix!", + 'Gawrsh I think the next race is about to start!', + 'Gosh, I better go check on the new Leaderboard and make sure it is working right!']) +SillyPhase1Chatter = ["If you haven't seen the Silly Meter, head to Toon Hall!", + 'Toontown is getting sillier by the day!', + "Cause silly surges in battle to boost Toontown's silly levels!", + 'Objects on the street are starting to animate!', + 'I saw a fire hydrant on Silly Street move!'] +SillyPhase2Chatter = ['Silly levels are still rising!', + 'The Silly Meter has climbed higher and gotten crazier!', + 'Someone saw a trash can moving on Maple Street!', + 'A lot of hydrants on Silly Street have come alive!', + 'A mailbox on Lighthouse Lane has gone nuts!', + 'Go see the Silly Meter in Toon Hall!', + 'Keep causing those silly surges!'] +SillyPhase3Chatter = ['The Cogs hated how silly Toontown was becoming!', + 'Keep a sharp eye out for Cog Invasions!', + 'Cog Invasions have caused the silly levels to drop!', + 'The Silly Meter went down after the Cog Invasions!', + 'Every street of Toontown has animated objects now!', + 'Toontown is sillier than ever!'] +SillyPhase4Chatter = ['Fire hydrants make your Squirt Gags squirtier!', + 'Mail Boxes give your Throw Gags a special delivery!', + 'Those crazy Trash Cans can help boost your Toon-up!', + 'Objects on the street can help you in battle!', + "I just know we'll get the Silly Meter back up soon!", + 'Enjoy the sillier Toontown!'] +for chatter in [MickeyChatter, + DonaldChatter, + MinnieChatter, + GoofyChatter]: + chatter[0].extend(SharedChatterGreetings) + chatter[1].extend(SharedChatterComments) + chatter[2].extend(SharedChatterGoodbyes) + +BoringTopic = 'Boring' +EmceeDialoguePhase1Topic = 'EmceeDialoguePhase1' +EmceeDialoguePhase2Topic = 'EmceeDialoguePhase2' +EmceeDialoguePhase3Topic = 'EmceeDialoguePhase3' +EmceeDialoguePhase3_5Topic = 'EmceeDialoguePhase3.5' +EmceeDialoguePhase4Topic = 'EmceeDialoguePhase4' +EmceeDialoguePhase5Topic = 'EmceeDialoguePhase5' +EmceeDialoguePhase6Topic = 'EmceeDialoguePhase6' +AprilToonsPhasePreTopTopic = 'AprilToonsPhasePreTopTopic' +AprilToonsPhaseTopTopic = 'AprilToonsPhaseTopTopic' +AprilToonsExtPhaseTopTopic = 'AprilToonsExtPhaseTopTopic' +AprilToonsPhasePostTopTopic = 'AprilToonsPhasePostTopTopic' +toontownDialogues = {BoringTopic: {(1, 2018): ['Hello Albert', 'It looks like the sillyness levels are rising', 'Yes and dont forget April Toons!'], + (2, 2019): ['Hello Newton', 'Yes I wonder how much the parties are contributing to all this'], + (3, 2020): ['Why hello there Albert and Newton', 'Halloween was pretty silly too!']}, + AprilToonsPhasePreTopTopic: {(1, 2020): ['Gadzooks! The Silly Meter has come back to life!', + "It's rising every day, and will reach the top soon!", + 'When it does, something silly is sure to happen!', + 'So get ready to get ridiculous!']}, + AprilToonsPhaseTopTopic: {(1, 2020): ['The Silly Meter has hit the top!', + 'Doodles are talking, Estates are bouncy!', + "There's only one thing to say\xe2\x80\xa6", + 'HAPPY APRIL TOONS!']}, + AprilToonsExtPhaseTopTopic: {(1, 2020): ['The Silly Meter has hit the top!', 'Doodles are talking, Estates are bouncy!']}, + AprilToonsPhasePostTopTopic: {(1, 2020): ['April Toons is over!', + "It's time for us to return to our lab.", + 'But when things get REALLY crazy again\xe2\x80\xa6', + 'The Silly Meter will return!']}, + EmceeDialoguePhase1Topic: {(1, 2020): ['Fellow Toons, this is the Silly Meter!', + "It is tracking Toontown's rising silly levels...", + 'Which are causing objects on the street to animate!', + 'And YOU can help push these levels higher!', + 'Battle Cogs to cause Silly Surges...', + 'Make Toontown sillier than ever...', + "And let's watch the world come alive!", + "Now I'll repeat what I said, but only once more."]}, + EmceeDialoguePhase2Topic: {(1, 2020): ['Good Gag work, Toons!', + "You're keeping those silly levels rising...", + 'And Toontown is getting sillier every day!', + 'Fire hydrants, trash cans, and mailboxes are springing to life...', + 'Making the world more animated than ever!', + "You know the Cogs aren't happy about this...", + 'But Toons sure are!']}, + EmceeDialoguePhase3Topic: {(1, 2020): ['Gadzooks! The Silly Meter is even crazier than expected!', + 'Your Silly Surges are working wonders...', + 'And Toontown is getting more animated every day!', + 'Keep up the good Gag work...', + 'And lets see how silly we can make Toontown!', + "You know the Cogs aren't happy about what's going on...", + 'But Toons sure are!']}, + EmceeDialoguePhase3_5Topic: {(1, 2020): ['YOU DID IT TOONS!', + 'You brought the streets of Toontown to life!', + 'You deserve a reward!', + 'Enter the code SILLYMETER in your Shticker Book...', + '...to get a Silly Meter T-Shirt!']}, + EmceeDialoguePhase4Topic: {(1, 2020): ['Attention all Toons!', + 'The sudden Cog invasions have been an unhappy event.', + 'As a result, silly levels have rapidly fallen...', + 'And no new objects are coming to life.', + 'But those that have are very thankful...', + "So perhaps they'll find a way to show their appreciation!", + 'Stay Tooned!']}, + EmceeDialoguePhase5Topic: {(1, 2020): ['Attention all Toons!', + 'The Cog invasions have been an unhappy event.', + 'As a result, silly levels have rapidly fallen...', + 'And no new objects are coming to life.', + 'But those that have are very thankful...', + 'And are showing their appreciation by helping in battle!', + 'We may hold off the Cogs yet, so keep up the fight!']}, + EmceeDialoguePhase6Topic: {(1, 2020): ['Congratulations Toons!', + 'You all succesfully held off the Cog Invasions...', + 'With a little help from our newly animated friends...', + 'And brought Toontown back to its usual silly self!', + 'We hope to get the Silly Meter rising again soon...', + 'So in the meantime, keep up the Cog fight...', + 'And enjoy the silliest place ever, Toontown!']}} +FriendsListPanelNewFriend = 'New Friend' +FriendsListPanelSecrets = 'True Friend' +FriendsListPanelOnlineFriends = 'ONLINE TOON\nFRIENDS' +FriendsListPanelAllFriends = 'ALL TOON\nFRIENDS' +FriendsListPanelIgnoredFriends = 'IGNORED\nTOONS' +FriendsListPanelPets = 'NEARBY\nPETS' +FriendsListPanelPlayers = 'ALL PLAYER\nFRIENDS' +FriendsListPanelOnlinePlayers = 'ONLINE PLAYER\nFRIENDS' +FriendInviterClickToon = 'Click on the toon you would like to make friends with.\n\n(You have %s friends)' +FriendInviterToon = 'Toon' +FriendInviterThatToon = 'That toon' +FriendInviterPlayer = 'Player' +FriendInviterThatPlayer = 'That player' +FriendInviterBegin = 'What type of friend would you like to make?' +FriendInviterToonFriendInfo = 'A friend only in Toontown' +FriendInviterPlayerFriendInfo = 'A friend across the Disney.com network' +FriendInviterToonTooMany = 'You have too many toon friends to add another one now. You will have to remove some toon friends if you want to make friends with %s. You could also try making player friends them.' +FriendInviterPlayerTooMany = 'You have too many player friends to add another one now. You will have to remove some player friends if you want to make friends with %s. You could also try making toon friends with them.' +FriendInviterToonAlready = '%s is already your toon friend.' +FriendInviterPlayerAlready = '%s is already your player friend.' +FriendInviterStopBeingToonFriends = 'Stop being toon friends' +FriendInviterStopBeingPlayerFriends = 'Stop being player friends' +FriendInviterEndFriendshipToon = 'Are you sure you want to stop being toon friends with %s?' +FriendInviterEndFriendshipPlayer = 'Are you sure you want to stop being player friends with %s?' +FriendInviterRemainToon = '\n(You will still be toon friends with %s)' +FriendInviterRemainPlayer = '\n(You will still be player friends with %s)' +DownloadForceAcknowledgeVerbList = ['painted', + 'unpacked', + 'unfolded', + 'drawn', + 'inflated', + 'built'] +DownloadForceAcknowledgeMsg = 'Sorry, the %(phase)s area is still being %(verb)s, and will be ready for you in a minute.' +TeaserTop = '' +TeaserBottom = '' +TeaserDefault = ',\nyou need to become a Member.\n\nJoin us!' +TeaserOtherHoods = 'For unlimited adventures in all 6 neighborhoods' +TeaserTypeAName = 'Type in your favorite name for your Toon!' +TeaserSixToons = 'To play more than one Toon' +TeaserClothing = 'To buy items from the Cattlelog \nto customize your toon' +TeaserCogHQ = 'To access awesome Cog HQs' +TeaserSecretChat = 'To use the True Friends Chat feature' +TeaserSpecies = 'To pick this type of Toon' +TeaserFishing = 'To fish in all 6 neighborhoods' +TeaserGolf = 'To play Toon MiniGolf' +TeaserParties = 'To plan a party' +TeaserSubscribe = 'Subscribe' +TeaserContinue = 'Return To Game' +TeaserEmotions = 'To make your Toon more expressive' +TeaserKarting = 'To access unlimited Kart Racing' +TeaserKartingAccessories = 'To customize your Kart' +TeaserGardening = 'To continue gardening at your Toon Estate' +TeaserHaveFun = 'Have more fun!' +TeaserJoinUs = 'Join us!' +TeaserPlantGags = 'To plant these gags' +TeaserPickGags = 'To pick these gags' +TeaserRestockGags = 'To restock these gags' +TeaserGetGags = 'To get these gags' +TeaserUseGags = 'To use these gags' +TeaserMinigames = TeaserOtherHoods +TeaserQuests = TeaserOtherHoods +TeaserOtherGags = TeaserOtherHoods +TeaserTricks = TeaserOtherHoods +LauncherPhaseNames = {0: 'Initialization', + 1: 'Panda', + 2: 'Engine', + 3: 'Make-A-Toon', + 3.5: 'Toontorial', + 4: 'Playground', + 5: 'Streets', + 5.5: 'Estates', + 6: 'Neighborhoods I', + 7: Cog + ' Buildings', + 8: 'Neighborhoods II', + 9: Sellbot + ' HQ', + 10: Cashbot + ' HQ', + 11: Lawbot + ' HQ', + 12: Bossbot + ' HQ', + 13: 'Parties'} +LauncherProgress = '%(name)s (%(current)s of %(total)s)' +LauncherStartingMessage = "Starting Disney's Toontown Online... " +LauncherDownloadFile = 'Downloading update for ' + LauncherProgress + '...' +LauncherDownloadFileBytes = 'Downloading update for ' + LauncherProgress + ': %(bytes)s' +LauncherDownloadFilePercent = 'Downloading update for ' + LauncherProgress + ': %(percent)s%%' +LauncherDecompressingFile = 'Decompressing update for ' + LauncherProgress + '...' +LauncherDecompressingPercent = 'Decompressing update for ' + LauncherProgress + ': %(percent)s%%' +LauncherExtractingFile = 'Extracting update for ' + LauncherProgress + '...' +LauncherExtractingPercent = 'Extracting update for ' + LauncherProgress + ': %(percent)s%%' +LauncherPatchingFile = 'Applying update for ' + LauncherProgress + '...' +LauncherPatchingPercent = 'Applying update for ' + LauncherProgress + ': %(percent)s%%' +LauncherConnectProxyAttempt = 'Connecting to Toontown: %s (proxy: %s) attempt: %s' +LauncherConnectAttempt = 'Connecting to Toontown: %s attempt %s' +LauncherDownloadServerFileList = 'Updating Toontown...' +LauncherCreatingDownloadDb = 'Updating Toontown...' +LauncherDownloadClientFileList = 'Updating Toontown...' +LauncherFinishedDownloadDb = 'Updating Toontown... ' +LauncherStartingGame = 'Starting Toontown...' +LauncherRecoverFiles = 'Updating Toontown. Recovering files...' +LauncherCheckUpdates = 'Checking for updates for ' + LauncherProgress +LauncherVerifyPhase = 'Updating Toontown...' +LoadingDownloadWatcherUpdate = 'Loading %s' +AvatarChoiceMakeAToon = 'Make A\nToon' +AvatarChoicePlayThisToon = 'Play\nThis Toon' +AvatarChoiceSubscribersOnly = 'Subscribe' +AvatarChoiceDelete = 'Delete' +AvatarChoiceDeleteConfirm = 'This will delete %s forever.' +AvatarChoiceNameRejected = 'Name\nRejected' +AvatarChoiceNameApproved = 'Name\nApproved!' +AvatarChoiceNameReview = 'Under\nReview' +AvatarChoiceNameYourToon = 'Name\nYour Toon!' +AvatarChoiceDeletePasswordText = 'Careful! This will delete %s forever. To delete this Toon, enter your password.' +AvatarChoiceDeleteConfirmText = 'Careful! This will delete %(name)s forever. If you are sure you want to do this, type "%(confirm)s" and click OK.' +AvatarChoiceDeleteConfirmUserTypes = 'delete' +AvatarChoiceDeletePasswordTitle = 'Delete Toon?' +AvatarChoicePassword = 'Password' +AvatarChoiceDeletePasswordOK = lOK +AvatarChoiceDeletePasswordCancel = lCancel +AvatarChoiceDeleteWrongPassword = 'That password does not seem to match. To delete this Toon, enter your password.' +AvatarChoiceDeleteWrongConfirm = 'You did not type the right thing. To delete %(name)s, type "%(confirm)s" and click OK. Do not type the quotation marks. Click Cancel if you have changed your mind.' +AvatarChooserPickAToon = 'Pick A Toon To Play' +AvatarChooserQuit = lQuit +DateOfBirthEntryMonths = ['Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec'] +DateOfBirthEntryDefaultLabel = 'Date of Birth' +AchievePageTitle = 'Achievements\n(Coming Soon)' +PhotoPageTitle = 'Photo\n(Coming Soon)' +BuildingPageTitle = 'Buildings\n(Coming Soon)' +InventoryPageTitle = 'Gags' +InventoryPageDeleteTitle = 'DELETE GAGS' +InventoryPageTrackFull = 'You have all the gags in the %s track.' +InventoryPagePluralPoints = 'You will get a new\n%(trackName)s gag when you\nget %(numPoints)s more %(trackName)s points.' +InventoryPageSinglePoint = 'You will get a new\n%(trackName)s gag when you\nget %(numPoints)s more %(trackName)s point.' +InventoryPageNoAccess = 'You do not have access to the %s track yet.' +NPCFriendPageTitle = 'SOS Toons' +PartyDateFormat = '%(mm)s %(dd)d, %(yyyy).4d' +PartyTimeFormat = '%d:%.2d %s' +PartyTimeFormatMeridiemAM = 'am' +PartyTimeFormatMeridiemPM = 'pm' +PartyCanStart = "It's Party Time, click Start Party in your Shticker Book Hosting page!" +PartyHasStartedAcceptedInvite = '%s party has started! Click the host then "Go To Party" in the Shticker Book Invites page.' +PartyHasStartedNotAcceptedInvite = '%s party has started! You can still go to it by teleporting to the host.' +EventsPageName = 'Events' +EventsPageCalendarTabName = 'Calendar' +EventsPageCalendarTabParty = 'Party' +EventsPageToontownTimeIs = 'TOONTOWN TIME IS' +EventsPageConfirmCancel = 'If you cancel, you will get a %d%% refund. Are you sure you want to cancel your party?' +EventsPageCancelPartyResultOk = 'Your party was cancelled and you got %d jellybeans back!' +EventsPageCancelPartyResultError = 'Sorry, your party was not cancelled.' +EventsPageCancelPartyAlreadyRefunded = 'Your party was never started. Check your mailbox for your refund!' +EventsPageTooLateToStart = 'Sorry, it is too late to start your party. You can cancel it and plan another one.' +EventsPagePublicPrivateChange = "Changing your party's privacy setting..." +EventsPagePublicPrivateNoGo = "Sorry, you can't change your party's privacy setting right now." +EventsPagePublicPrivateAlreadyStarted = "Sorry, your party has already started, so you can't change your party's privacy setting." +EventsPageHostTabName = 'Hosting' +EventsPageHostTabTitle = 'My Next Party' +EventsPageHostTabTitleNoParties = 'No Parties' +EventsPageHostTabDateTimeLabel = 'You are having a party on %s at %s Toontown Time.' +EventsPageHostingTabNoParty = 'Go to a playground\nParty Gate to plan\nyour own party!' +EventsPageHostTabPublicPrivateLabel = 'This party is:' +EventsPageHostTabToggleToPrivate = 'Private' +EventsPageHostTabToggleToPublic = 'Public' +EventsPageHostingTabGuestListTitle = 'Guests' +EventsPageHostingTabActivityListTitle = 'Activities' +EventsPageHostingTabDecorationsListTitle = 'Decorations' +EventsPageHostingTabPartiesListTitle = 'Hosts' +EventsPageHostTabCancelButton = 'Cancel Party' +EventsPageGoButton = 'Start\nParty!' +EventsPageGoBackButton = 'Party\nNow!' +EventsPageInviteGoButton = 'Go to\nParty!' +EventsPageUnknownToon = 'Unknown Toon' +EventsPageInvitedTabName = 'Invitations' +EventsPageInvitedTabTitle = 'Party Invitations' +EventsPageInvitedTabInvitationListTitle = 'Invitations' +EventsPageInvitedTabActivityListTitle = 'Activities' +EventsPageInvitedTabTime = '%s %s Toontown Time' +EventsPageNewsTabName = 'News' +EventsPageNewsTabTitle = 'News' +EventsPageNewsDownloading = 'Retrieving News...' +EventsPageNewsUnavailable = 'Chip and Dale played with the printing press. News not available.' +EventsPageNewsPaperTitle = 'TOONTOWN TIMES' +EventsPageNewsLeftSubtitle = 'Still only 1 jellybean' +EventsPageNewsRightSubtitle = 'Established toon-thousand nine' +NewsPageName = 'News' +NewsPageImportError = 'Whoops! There is an issue loading the "Toon News ... for the Amused!" Please check back later.' +NewsPageDownloadingNewsSubstr = 'Stay Tooned, while we bring you the latest issue of the \n"Toon News ... for the Amused!"' +NewsPageDownloadingNews0 = NewsPageDownloadingNewsSubstr + ' %s%% Complete.' +NewsPageDownloadingNews1 = NewsPageDownloadingNewsSubstr + ' %s%% Complete..' +NewsPageDownloadingNews2 = NewsPageDownloadingNewsSubstr + ' %s%% Complete...' +NewsPageErrorDownloadingFile = 'Whoops! Page %s is missing from "Toon News ... for the Amused!" Please check back later.' +NewsPageErrorDownloadingFileCanStillRead = 'Whoops! Page %s \nis missing from the "Toon News ... for the Amused!" \nTurn the page to continue, while we work to get this page back.' +NewsPageNoIssues = 'Whoops! The "Toon News ... for the Amused!" has gone missing! \nStay Tooned ... while we work to bring the news back!' +IssueFrameThisWeek = 'this week' +IssueFrameLastWeek = 'last week' +IssueFrameWeeksAgo = '%d weeks ago' +SelectedInvitationInformation = '%s is having a party on %s at %s Toontown Time.' +PartyPlannerNextButton = 'Continue' +PartyPlannerPreviousButton = 'Back' +PartyPlannerWelcomeTitle = 'Toontown Party Planner' +PartyPlannerInstructions = 'Hosting your own party is a lot of fun!\nStart planning with the arrows at the bottom!' +PartyPlannerDateTitle = 'Pick A Day For Your Party' +PartyPlannerTimeTitle = 'Pick A Time For Your Party' +PartyPlannerGuestTitle = 'Choose Your Guests' +PartyPlannerEditorTitle = 'Design Your Party\nPlace Activities and Decorations' +PartyPlannerConfirmTitle = 'Choose Invitations To Send' +PartyPlannerConfirmTitleNoFriends = 'Double Check Your Party' +PartyPlannerTimeToontown = 'Toontown' +PartyPlannerTimeTime = 'Time' +PartyPlannerTimeRecap = 'Party Date and Time' +PartyPlannerPartyNow = 'As Soon As Possible' +PartyPlannerTimeToontownTime = 'Toontown Time:' +PartyPlannerTimeLocalTime = 'Party Local Time : ' +PartyPlannerPublicPrivateLabel = 'This party will be:' +PartyPlannerPublicDescription = 'Any Toon\ncan come!' +PartyPlannerPrivateDescription = 'Only\nInvited Toons\ncan come!' +PartyPlannerPublic = 'Public' +PartyPlannerPrivate = 'Private' +PartyPlannerCheckAll = 'Check\nAll' +PartyPlannerUncheckAll = 'Uncheck\nAll' +PartyPlannerDateText = 'Date' +PartyPlannerTimeText = 'Time' +PartyPlannerTTTimeText = 'Toontown Time' +PartyPlannerEditorInstructionsIdle = 'Click on the Party Activity or Decoration you would like to purchase.' +PartyPlannerEditorInstructionsClickedElementActivity = 'Click Buy or Drag the Activity Icon onto the Party Grounds Map' +PartyPlannerEditorInstructionsClickedElementDecoration = 'Click Buy or Drag the Decoration onto the Party Grounds Map' +PartyPlannerEditorInstructionsDraggingActivity = 'Drag the Activity onto the Party Grounds Map.' +PartyPlannerEditorInstructionsDraggingDecoration = 'Drag the Activity onto the Party Grounds Map.' +PartyPlannerEditorInstructionsPartyGrounds = 'Click and Drag items to move them around the Party Grounds Map' +PartyPlannerEditorInstructionsTrash = 'Drag an Activity or Decoration here to remove it.' +PartyPlannerEditorInstructionsNoRoom = 'There is no room to place that activity.' +PartyPlannerEditorInstructionsRemoved = '%(removed)s removed since %(added)s was added.' +PartyPlannerBeans = 'beans' +PartyPlannerTotalCost = 'Total Cost:\n%d beans' +PartyPlannerSoldOut = 'SOLD OUT' +PartyPlannerBuy = 'BUY' +PartyPlannerPaidOnly = 'MEMBERS ONLY' +PartyPlannerPartyGrounds = 'PARTY GROUNDS MAP' +PartyPlannerOkWithGroundsLayout = 'Are you done moving your Party Activities and Decorations around the Party Grounds Map?' +PartyPlannerChooseFutureTime = 'Please choose a time in the future.' +PartyPlannerInviteButton = 'Send Invites' +PartyPlannerInviteButtonNoFriends = 'Plan Party' +PartyPlannerBirthdayTheme = 'Birthday' +PartyPlannerGenericMaleTheme = 'Star' +PartyPlannerGenericFemaleTheme = 'Flower' +PartyPlannerRacingTheme = 'Racing' +PartyPlannerValentoonsTheme = 'ValenToons' +PartyPlannerVictoryPartyTheme = 'Victory' +PartyPlannerWinterPartyTheme = 'Winter' +PartyPlannerGuestName = 'Guest Name' +PartyPlannerClosePlanner = 'Close Planner' +PartyPlannerConfirmationAllOkTitle = 'Congratulations!' +PartyPlannerConfirmationAllOkText = 'Your party has been created and your invitations sent out.\nThanks!' +PartyPlannerConfirmationAllOkTextNoFriends = 'Your party has been created!\nThanks!' +PartyPlannerConfirmationErrorTitle = 'Oops.' +PartyPlannerConfirmationValidationErrorText = 'Sorry, there seems to be a problem\nwith that party.\nPlease go back and try again.' +PartyPlannerConfirmationDatabaseErrorText = "Sorry, I couldn't record all your information.\nPlease try again later.\nDon't worry, no beans were lost." +PartyPlannerConfirmationTooManyText = 'Sorry, you are already hosting a party.\nIf you want to plan another party, please\ncancel your current party.' +PartyPlannerInvitationThemeWhatSentence = 'You are invited to my %s party! %s!' +PartyPlannerInvitationThemeWhatSentenceNoFriends = 'I am hosting a %s party! %s!' +PartyPlannerInvitationThemeWhatActivitiesBeginning = 'It will have ' +PartyPlannerInvitationWhoseSentence = '%s Party' +PartyPlannerInvitationTheme = 'Theme' +PartyPlannerInvitationWhenSentence = 'It will be on %s,\nat %s Toontown Time.\nHope you can make it!' +PartyPlannerInvitationWhenSentenceNoFriends = 'It will be on %s,\nat %s Toontown Time.\nToontastic!' +PartyPlannerComingSoon = 'Coming Soon' +PartyPlannerCantBuy = "Can't Buy" +PartyPlannerGenericName = 'Party Planner' +PartyJukeboxOccupied = 'Someone else is using the jukebox. Try again later.' +PartyJukeboxNowPlaying = 'The song you chose is now playing on the jukebox!' +MusicEncntrGeneralBg = 'Encounter With Cogs' +MusicTcSzActivity = 'Toontorial Medley' +MusicTcSz = 'Strolling Along' +MusicCreateAToon = 'The New Toon in Town' +MusicTtTheme = 'The Toontown Theme' +MusicMinigameRace = 'Slow and Steady' +MusicMgPairing = 'Remember Me?' +MusicTcNbrhood = 'Toontown Central' +MusicMgDiving = 'Treasure Lullaby' +MusicMgCannonGame = 'Fire the Cannons!' +MusicMgTwodgame = 'Running Toon' +MusicMgCogthief = 'Catch That Cog!' +MusicMgTravel = 'Traveling Music' +MusicMgTugOWar = 'Tug-of-War' +MusicMgVine = 'The Jungle Swing' +MusicMgIcegame = 'Slippery Situation' +MusicMgToontag = 'Minigame Medley' +MusicMMatchBg2 = 'Jazzy Minnie' +MusicMgTarget = "Soarin' Over Toontown" +MusicFfSafezone = 'The Funny Farm' +MusicDdSz = 'Waddling Way' +MusicMmNbrhood = "Minnie's Melodyland" +MusicGzPlaygolf = "Let's Play Golf!" +MusicGsSz = 'Goofy Speedway' +MusicOzSz = "Chip n' Dale's Acres" +MusicGsRaceCc = 'Downtown Driving' +MusicGsRaceSs = 'Ready, Set, Go!' +MusicGsRaceRr = 'Route 66' +MusicGzSz = 'The Putt-Putt Polka' +MusicMmSz = 'Dancing in the Streets' +MusicMmSzActivity = 'Here Comes Treble' +MusicDdNbrhood = "Donald's Dock" +MusicGsKartshop = 'Mr. Goofywrench' +MusicDdSzActivity = 'Sea Shanty' +MusicEncntrGeneralBgIndoor = 'Building Excitement' +MusicTtElevator = 'Going Up?' +MusicEncntrToonWinningIndoor = 'Toons Unite!' +MusicEncntrGeneralSuitWinningIndoor = 'Cog-tastrophe!' +MusicTbNbrhood = 'The Brrrgh' +MusicDlNbrhood = "Donald's Dreamland" +MusicDlSzActivity = 'Counting Sheep' +MusicDgSz = 'Waltz of the Flowers' +MusicDlSz = 'Sleepwalking' +MusicTbSzActivity = 'Snow Problem' +MusicTbSz = 'Shiver and Shimmy' +MusicDgNbrhood = "Daisy's Garden" +MusicEncntrHallOfFame = 'The Hall of Fame' +MusicEncntrSuitHqNbrhood = 'Dollars and Cents' +MusicChqFactBg = 'Cog Factory' +MusicCoghqFinale = 'Triumph of the Toons' +MusicEncntrToonWinning = 'Cashing In!' +MusicEncntrSuitWinning = 'Selling You Short' +MusicEncntrHeadSuitTheme = 'The Big Boss' +MusicLbJurybg = 'Court is in Session' +MusicLbCourtyard = 'Balancing Act' +MusicBossbotCeoV2 = 'Head Honcho' +MusicBossbotFactoryV1 = 'Cog Waltz' +MusicBossbotCeoV1 = 'Bossing You Around' +MusicPartyOriginalTheme = 'Party Time' +MusicPartyPolkaDance = 'Party Polka' +MusicPartySwingDance = 'Party Swing' +MusicPartyWaltzDance = 'Party Waltz' +MusicPartyGenericThemeJazzy = 'Party Jazz' +MusicPartyGenericTheme = 'Party Jingle' +JukeboxAddSong = 'Add\nSong' +JukeboxReplaceSong = 'Replace\nSong' +JukeboxQueueLabel = 'Playing Next:' +JukeboxSongsLabel = 'Pick a Song:' +JukeboxClose = 'Done' +JukeboxCurrentlyPlaying = 'Currently Playing' +JukeboxCurrentlyPlayingNothing = 'Jukebox is paused.' +JukeboxCurrentSongNothing = 'Add a song to the playlist!' +PartyOverWarningNoName = 'The party has ended! Thanks for coming!' +PartyOverWarningWithName = '%s party has ended! Thanks for coming!' +PartyCountdownClockText = 'Time\n\nLeft' +PartyTitleText = '%s Party' +PartyActivityConjunction = ', and' +PartyActivityNameDict = {0: {'generic': 'Jukebox\n20 songs', + 'invite': 'a 20 song Jukebox', + 'editor': 'Jukebox - 20', + 'description': 'Listen to music with your own 20 song jukebox!'}, + 1: {'generic': 'Party Cannons', + 'invite': 'Party Cannons', + 'editor': 'Cannons', + 'description': 'Fire yourself out of the cannons and into fun!'}, + 2: {'generic': 'Trampoline', + 'invite': 'Trampoline', + 'editor': 'Trampoline', + 'description': 'Collect jellybeans and bounce the highest!'}, + 3: {'generic': 'Party Catch', + 'invite': 'Party Catch', + 'editor': 'Party Catch', + 'description': 'Catch fruit to win beans! Dodge those anvils!'}, + 4: {'generic': 'Dance Floor\n10 moves', + 'invite': 'a 10 move Dance Floor', + 'editor': 'Dance Floor - 10', + 'description': 'Show off all 10 of your moves, toon style!'}, + 5: {'generic': 'Party Tug-of-War', + 'invite': 'Party Tug-of-War', + 'editor': 'Tug-of-War', + 'description': 'Up to 4 on 4 toon tugging craziness!'}, + 6: {'generic': 'Party Fireworks', + 'invite': 'Party Fireworks', + 'editor': 'Fireworks', + 'description': 'Launch your very own fireworks show!'}, + 7: {'generic': 'Party Clock', + 'invite': 'a Party Clock', + 'editor': 'Party Clock', + 'description': 'Counts down the time left in your party.'}, + 8: {'generic': 'Jukebox\n40 songs', + 'invite': 'a 40 song jukebox', + 'editor': 'Jukebox - 40', + 'description': 'Listen to music with your own 40 song jukebox!'}, + 9: {'generic': 'Dance Floor\n20 moves', + 'invite': 'a 20 move Dance Floor', + 'editor': 'Dance Floor - 20', + 'description': 'Show off all 20 of your moves, toon style!'}, + 10: {'generic': 'Cog-O-War', + 'invite': 'Cog-O-War', + 'editor': 'Cog-O-War', + 'description': 'The team vs. team game of Cog splatting!'}, + 11: {'generic': 'Cog Trampoline', + 'invite': 'Cog Trampoline', + 'editor': 'Cog Trampoline', + 'description': "Jump on a Cog's face!"}, + 12: {'generic': 'Present Catch', + 'invite': 'Present Catch', + 'editor': 'Present Catch', + 'description': 'Catch presents to win beans! Dodge those anvils!'}, + 13: {'generic': 'Holiday Trampoline', + 'invite': 'Holiday Trampoline', + 'editor': 'Holiday Trampoline', + 'description': 'Jump if you love Winter Holidays!'}, + 14: {'generic': 'Holiday Cog-O-War', + 'invite': 'Holiday Cog-O-War', + 'editor': 'Holiday Cog-O-War', + 'description': 'The team vs. team game of Cog splattering!'}, + 15: {'generic': 'Dance Floor\n10 moves', + 'invite': 'a 10 move ValenToons Dance Floor', + 'editor': 'Dance Floor - 10', + 'description': 'Get your ValenToon Groove On!'}, + 16: {'generic': 'Dance Floor\n20 moves', + 'invite': 'a 20 move ValenToons Dance Floor', + 'editor': 'Dance Floor - 20', + 'description': 'Get your ValenToon Groove On!'}, + 17: {'generic': 'Jukebox\n20 songs', + 'invite': 'a 20 song Valentoons Jukebox', + 'editor': 'Jukebox - 20', + 'description': 'Nothing sets the mood like music!'}, + 18: {'generic': 'Jukebox\n40 songs', + 'invite': 'a 40 song Valentoons jukebox', + 'editor': 'Jukebox - 40', + 'description': 'Nothing sets the mood like music!'}, + 19: {'generic': 'Trampoline', + 'invite': 'ValenToons Trampoline', + 'editor': 'Trampoline', + 'description': "Jump to your heart's content!"}} +PartyDecorationNameDict = {0: {'editor': 'Balloon Anvil', + 'description': 'Try to keep the fun from floating away!'}, + 1: {'editor': 'Party Stage', + 'description': 'Balloons, stars, what else could you want?'}, + 2: {'editor': 'Party Bow', + 'description': 'Wrap up the fun!'}, + 3: {'editor': 'Cake', + 'description': 'Delicious.'}, + 4: {'editor': 'Party Castle', + 'description': "A Toon's home is his castle."}, + 5: {'editor': 'Gift Pile', + 'description': 'Gifts for every Toon!'}, + 6: {'editor': 'Streamer Horn', + 'description': 'This horn is a hoot! Streamers!'}, + 7: {'editor': 'Party Gate', + 'description': 'Multi-colored and crazy!'}, + 8: {'editor': 'Noise Makers', + 'description': 'Tweeeeet!'}, + 9: {'editor': 'Pinwheel', + 'description': 'Colorful twirling for everyone!'}, + 10: {'editor': 'Gag Globe', + 'description': 'Gag and star globe designed by Olivea'}, + 11: {'editor': 'Bean Banner', + 'description': 'A Jellybean banner designed by Cassidy'}, + 12: {'editor': 'Gag Cake', + 'description': 'A Topsy Turvy gag cake designed by Felicia'}, + 13: {'editor': "Cupid's Heart", + 'description': 'Ready...Aim...\nValenToons!'}, + 14: {'editor': 'Candy Hearts\n Banner', + 'description': "Who doesn't love candy hearts?"}, + 15: {'editor': 'Flying Heart', + 'description': 'This heart is getting carried away!'}, + 16: {'editor': 'Victory Bandstand', + 'description': 'All our new friends are ready to dance!'}, + 17: {'editor': 'Victory Banner', + 'description': 'Not just a normal banner!'}, + 18: {'editor': 'Confetti Cannons', + 'description': 'BOOM! Confetti! Fun!'}, + 19: {'editor': 'Cog & Doodle', + 'description': "Ouch! That's gotta hurt."}, + 20: {'editor': 'Cog Flappy Man', + 'description': 'A Cog full of hot air, what a shock!'}, + 21: {'editor': 'Cog Ice Cream', + 'description': 'A Cog looking his best.'}, + 22: {'editor': 'CogCicle', + 'description': 'A Cog looking his holiday best.'}, + 23: {'editor': 'Holiday Bandstand', + 'description': 'Everyone loves a Holiday Party!'}, + 24: {'editor': 'Chilly Cog', + 'description': "Ouch! That's gotta hurt."}, + 25: {'editor': 'Snowman', + 'description': "So cool, he's hot!"}, + 26: {'editor': 'SnowDoodle', + 'description': 'His only trick is being cold!'}, + 27: {'editor': 'ValenToons Anvil', + 'description': "We've got your heart on a string!"}} +ActivityLabel = 'Cost - Activity Name' +PartyDoYouWantToPlan = 'Would you like to plan a new party right now?' +PartyPlannerOnYourWay = 'Have fun planning your party!' +PartyPlannerMaybeNextTime = 'Maybe next time. Have a good day!' +PartyPlannerHostingTooMany = 'You can only host one party at a time, sorry.' +PartyPlannerOnlyPaid = 'Only paid toons can host a party, sorry.' +PartyPlannerNpcComingSoon = 'Parties are coming soon! Try again later.' +PartyPlannerNpcMinCost = 'It costs a minimum of %d jellybeans to plan a party.' +PartyHatPublicPartyChoose = 'Do you want to go to the 1st available public party?' +PartyGateTitle = 'Public Parties' +PartyGateGoToParty = 'Go to\nParty!' +PartyGatePartiesListTitle = 'Hosts' +PartyGatesPartiesListToons = 'Toons' +PartyGatesPartiesListActivities = 'Activities' +PartyGatesPartiesListMinLeft = 'Minutes Left' +PartyGateLeftSign = 'Come On In!' +PartyGateRightSign = 'Public Parties Here!' +PartyGatePartyUnavailable = 'Sorry. That party is no longer available.' +PartyGatePartyFull = 'Sorry. That party is full.' +PartyGateInstructions = 'Click on a host, then click on "Go to Party"' +PartyActivityWaitingForOtherPlayers = 'Waiting for other players to join the party game...' +PartyActivityPleaseWait = 'Please wait...' +DefaultPartyActivityTitle = 'Party Game Title' +DefaultPartyActivityInstructions = 'PartyGame Instructions' +PartyOnlyHostLeverPull = 'Only the host can start this activity. Sorry.' +PartyActivityDefaultJoinDeny = 'You cannot join this activity right now. Sorry.' +PartyActivityDefaultExitDeny = 'You cannot leave this activity right now. Sorry.' +PartyJellybeanRewardOK = 'OK' +PartyCatchActivityTitle = 'Party Catch Activity' +PartyCatchActivityInstructions = "Catch as many pieces of fruit as you can. Try not to 'catch' any %(badThing)s!" +PartyCatchActivityFinishPerfect = 'PERFECT GAME!' +PartyCatchActivityFinish = 'Good Game!' +PartyCatchActivityExit = 'EXIT' +PartyCatchActivityApples = 'apples' +PartyCatchActivityOranges = 'oranges' +PartyCatchActivityPears = 'pears' +PartyCatchActivityCoconuts = 'coconuts' +PartyCatchActivityWatermelons = 'watermelons' +PartyCatchActivityPineapples = 'pineapples' +PartyCatchActivityAnvils = 'anvils' +PartyCatchStarted = 'The game has started. Go play it.' +PartyCatchCannotStart = 'The game could not start right now.' +PartyCatchRewardMessage = 'Pieces of fruit caught: %s\n\nJellybeans earned: %d' +WinterPartyCatchActivityInstructions = "Catch as many presents as you can. Try not to 'catch' any %(badThing)s!" +WinterPartyCatchRewardMessage = 'Presents caught: %s\n\nJellybeans earned: %s' +PartyDanceActivityTitle = 'Party Dance Floor' +PartyDanceActivityInstructions = 'Combine 3 or more ARROW KEY patterns to do dance moves! There are 10 dance moves available. Can you find them all?' +PartyDanceActivity20Title = 'Party Dance Floor' +PartyDanceActivity20Instructions = 'Combine 3 or more ARROW KEY patterns to do dance moves! There are 20 dance moves available. Can you find them all?' +DanceAnimRight = 'Right' +DanceAnimReelNeutral = 'The Fishertoon' +DanceAnimConked = 'The Headbob' +DanceAnimHappyDance = 'The Happy Dance' +DanceAnimConfused = 'Very Dizzy' +DanceAnimWalk = 'Walking on the Moon' +DanceAnimJump = 'The Jump!' +DanceAnimFirehose = 'The Firetoon' +DanceAnimShrug = 'Who Knows?' +DanceAnimSlipForward = 'The Fall' +DanceAnimSadWalk = 'Tired' +DanceAnimWave = 'Hello Goodbye' +DanceAnimStruggle = 'The Shuffle Hop' +DanceAnimRunningJump = 'The Running Toon' +DanceAnimSlipBackward = 'The Backfall' +DanceAnimDown = 'Down' +DanceAnimUp = 'Up' +DanceAnimGoodPutt = 'The Putt' +DanceAnimVictory = 'The Victory Dance' +DanceAnimPush = 'The Mimetoon' +DanceAnimAngry = "Rock n' Roll" +DanceAnimLeft = 'Left' +PartyCannonActivityTitle = 'Party Cannons' +PartyCannonActivityInstructions = 'Hit the clouds to change their color and bounce in the air! While IN THE AIR, you can USE THE ARROW KEYS to GLIDE.' +PartyCannonResults = 'You collected %d jelly beans!\n\nNumber of Clouds Hit: %d' +FireworksActivityInstructions = 'Hit the "Page Up" key to see better.' +FireworksActivityBeginning = 'Party fireworks are about to start! Enjoy the show!' +FireworksActivityEnding = 'Hope you enjoyed the show!' +PartyFireworksAlreadyActive = 'The fireworks show has already started.' +PartyFireworksAlreadyDone = 'The fireworks show is over.' +PartyTrampolineJellyBeanTitle = 'Jelly Beans Trampoline' +PartyTrampolineTricksTitle = 'Tricks Trampoline' +PartyTrampolineActivityInstructions = 'Use the Control key to jump.\n\nJump when your Toon is at its lowest point on the trampoline to jump higher.' +PartyTrampolineActivityOccupied = 'Trampoline in use.' +PartyTrampolineQuitEarlyButton = 'Hop Off' +PartyTrampolineBeanResults = 'You collected %d jelly beans.' +PartyTrampolineBonusBeanResults = 'You collected %d jelly beans, plus %d more for getting the Big Bean.' +PartyTrampolineTopHeightResults = 'Your top height was %d ft.' +PartyTrampolineTimesUp = "Time's Up" +PartyTrampolineReady = 'Ready...' +PartyTrampolineGo = 'Go!' +PartyTrampolineBestHeight = 'Best Height So Far:\n%s\n%d ft' +PartyTrampolineNoHeightYet = 'How high\ncan you jump?' +PartyTrampolineGetHeight = '%d ft' +PartyTeamActivityForMorePlural = 's' +PartyTeamActivityForMore = 'Waiting for %d player%s\non each side...' +PartyTeamActivityForMoreWithBalance = 'Waiting for %d more player%s...' +PartyTeamActivityWaitingForOtherPlayers = 'Waiting for other players...' +PartyTeamActivityWaitingToStart = 'Starting in...' +PartyTeamActivityExitButton = 'Hop Off' +PartyTeamActivitySwitchTeamsButton = 'Switch\nTeams' +PartyTeamActivityWins = '%s team wins!' +PartyTeamActivityLocalAvatarTeamWins = 'Your team won!' +PartyTeamActivityGameTie = "It's a tie!" +PartyTeamActivityJoinDenied = "Sorry, you can't join %s at this time." +PartyTeamActivityExitDenied = 'Sorry, you are unable to leave %s at this time.' +PartyTeamActivitySwitchDenied = "Sorry, you cant's switch teams at this time." +PartyTeamActivityTeamFull = 'Sorry, that team is already full!' +PartyTeamActivityRewardMessage = 'You got %d jellybeans. Good job!' +PartyCogTeams = ('Blue', 'Orange') +PartyCogRewardMessage = 'Your Score: %d\n' +PartyCogRewardBonus = '\nYou got %d additional jellybean%s because your team won!' +PartyCogJellybeanPlural = 's' +PartyCogSignNote = 'HI-SCORE\n%s\n%d' +PartyCogTitle = 'Cog-O-War' +PartyCogInstructions = 'Throw pies at cogs to push them away from your team. ' + "When time's up, the team with most cogs on the other side wins!" + '\n\nThrow with the CONTROL KEY. Move with the ARROW KEYS.' +PartyCogDistance = '%d ft' +PartyCogTimeUp = "Time's up!" +PartyCogGuiScoreLabel = 'SCORE' +PartyCogGuiPowerLabel = 'POWER' +PartyCogGuiSpamWarning = 'Hold CONTROL for more power!' +PartyCogBalanceBar = 'BALANCE' +PartyTugOfWarReady = 'Ready...' +PartyTugOfWarGo = 'GO!' +PartyTugOfWarGameEnd = 'Good game!' +PartyTugOfWarTitle = 'Party Tug-of-War' +CalendarShowAll = 'Show All' +CalendarShowOnlyHolidays = 'Show Only Holidays' +CalendarShowOnlyParties = 'Show Only Parties' +CalendarEndsAt = 'Ends at ' +CalendarStartedOn = 'Started on ' +CalendarEndDash = 'End-' +CalendarEndOf = 'End of ' +CalendarPartyGetReady = 'Get ready!' +CalendarPartyGo = 'Go party!' +CalendarPartyFinished = "It's over..." +CalendarPartyCancelled = 'Cancelled.' +CalendarPartyNeverStarted = 'Never Started.' +NPCFriendPanelRemaining = '%d Remaining' +MapPageTitle = 'Map' +MapPageBackToPlayground = 'Back to Playground' +MapPageBackToCogHQ = 'Back to Cog Headquarters' +MapPageGoHome = 'Go Home' +MapPageYouAreHere = 'You are in: %s\n%s' +MapPageYouAreAtHome = 'You are at\nyour estate' +MapPageYouAreAtSomeonesHome = 'You are at %s estate' +MapPageGoTo = 'Go To\n%s' +OptionsPageTitle = 'Options' +OptionsTabTitle = 'Options\n& Codes' +OptionsPagePurchase = 'Subscribe' +OptionsPageLogout = 'Logout' +OptionsPageExitToontown = 'Exit Toontown' +OptionsPageMusicOnLabel = 'Music is on.' +OptionsPageMusicOffLabel = 'Music is off.' +OptionsPageSFXOnLabel = 'Sound Effects are on.' +OptionsPageSFXOffLabel = 'Sound Effects are off.' +OptionsPageToonChatSoundsOnLabel = ' Type Chat Sounds are on.' +OptionsPageToonChatSoundsOffLabel = ' Type Chat Sounds are off.' +OptionsPageFriendsEnabledLabel = 'Accepting new friend requests.' +OptionsPageFriendsDisabledLabel = 'Not accepting friend requests.' +OptionsPageWhisperEnabledLabel = 'Allowing whispers from anyone.' +OptionsPageWhisperDisabledLabel = 'Allowing whispers from friends only.' +OptionsPageSpeedChatStyleLabel = 'SpeedChat Color' +OptionsPageDisplayWindowed = 'windowed' +OptionsPageDisplayEmbedded = 'In the browser' +OptionsPageSelect = 'Select' +OptionsPageToggleOn = 'Turn On' +OptionsPageToggleOff = 'Turn Off' +OptionsPageChange = 'Change' +OptionsPageDisplaySettings = 'Display: %(screensize)s, %(api)s' +OptionsPageDisplaySettingsNoApi = 'Display: %(screensize)s' +OptionsPageExitConfirm = 'Exit Toontown?' +DisplaySettingsTitle = 'Display Settings' +DisplaySettingsIntro = 'The following settings are used to configure the way Toontown is displayed on your computer. It is probably not necessary to adjust these unless you are experiencing a problem.' +DisplaySettingsIntroSimple = 'You may adjust the screen resolution to a higher value to improve the clarity of text and graphics in Toontown, but depending on your graphics card, some higher values may make the game run less smoothly or may not work at all.' +DisplaySettingsApi = 'Graphics API:' +DisplaySettingsResolution = 'Resolution:' +DisplaySettingsWindowed = 'In a window' +DisplaySettingsFullscreen = 'Full screen' +DisplaySettingsEmbedded = 'In the browser' +DisplaySettingsApply = 'Apply' +DisplaySettingsCancel = lCancel +DisplaySettingsApplyWarning = 'When you press OK, the display settings will change. If the new configuration does not display properly on your computer, the display will automatically return to its original configuration after %s seconds.' +DisplaySettingsAccept = 'Press OK to keep the new settings, or Cancel to revert. If you do not press anything, the settings will automatically revert back in %s seconds.' +DisplaySettingsRevertUser = 'Your previous display settings have been restored.' +DisplaySettingsRevertFailed = 'The selected display settings do not work on your computer. Your previous display settings have been restored.' +OptionsPageCodesTab = 'Enter Code' +CdrPageTitle = 'Enter a Code' +CdrInstructions = 'Enter your code to receive a special item in your mailbox.' +CdrResultSuccess = 'Congratulations! Check your mailbox to claim your item!' +CdrResultInvalidCode = "You've entered an invalid code. Please check the code and try again." +CdrResultExpiredCode = "We're sorry. This code has expired." +CdrResultUnknownError = "We're sorry. This code cannot be applied to your Toon." +CdrResultMailboxFull = 'Your mailbox is full. Please remove an item, then enter your code again.' +CdrResultAlreadyInMailbox = "You've already received this item. Check your mailbox to confirm." +CdrResultAlreadyInQueue = 'Your item is on its way. Check your mailbox in a few minutes to receive it.' +CdrResultAlreadyInCloset = "You've already received this item. Check your closet to confirm." +CdrResultAlreadyBeingWorn = "You've already received this item, and you are wearing it!" +CdrResultAlreadyReceived = "You've already received this item." +CdrResultTooManyFails = "We're sorry. You've tried to enter an incorrect code too many times. Please try again after some time." +CdrResultServiceUnavailable = "We're sorry. This feature is temporarily unavailable. Please try again during your next login." +TrackPageTitle = 'Gag Track Training' +TrackPageShortTitle = 'Gag Training' +TrackPageSubtitle = 'Complete ToonTasks to learn how to use new gags!' +TrackPageTraining = 'You are training to use %s gags.\nWhen you complete all 16 tasks you\nwill be able to use %s gags in battle.' +TrackPageClear = 'You are not training any Gag Tracks now.' +TrackPageFilmTitle = '%s\nTraining\nFilm' +TrackPageDone = 'FIN' +QuestPageToonTasks = 'ToonTasks' +QuestPageChoose = 'Choose' +QuestPageLocked = 'Locked' +QuestPageDestination = '%s\n%s\n%s' +QuestPageNameAndDestination = '%s\n%s\n%s\n%s' +QuestPosterHQOfficer = lHQOfficerM +QuestPosterHQBuildingName = lToonHQ +QuestPosterHQStreetName = 'Any Street' +QuestPosterHQLocationName = 'Any Neighborhood' +QuestPosterTailor = 'Tailor' +QuestPosterTailorBuildingName = 'Clothing Store' +QuestPosterTailorStreetName = 'Any Playground' +QuestPosterTailorLocationName = 'Any Neighborhood' +QuestPosterPlayground = 'In the playground' +QuestPosterAtHome = 'At your home' +QuestPosterInHome = 'In your home' +QuestPosterOnPhone = 'On your phone' +QuestPosterEstate = 'At your estate' +QuestPosterAnywhere = 'Anywhere' +QuestPosterAuxTo = 'to:' +QuestPosterAuxFrom = 'from:' +QuestPosterAuxFor = 'for:' +QuestPosterAuxOr = 'or:' +QuestPosterAuxReturnTo = 'Return to:' +QuestPosterLocationIn = ' in ' +QuestPosterLocationOn = ' in ' +QuestPosterFun = 'Just for fun!' +QuestPosterFishing = 'GO FISHING' +QuestPosterComplete = 'COMPLETE' +QuestPosterConfirmDelete = 'Are you sure you want to delete this ToonTask?' +QuestPosterDeleteBtn = 'Delete' +QuestPosterDialogYes = 'Delete' +QuestPosterDialogNo = 'Cancel' +ShardPageTitle = 'Districts' +ShardPageHelpIntro = 'Each District is a copy of the Toontown world.' +ShardPageHelpWhere = ' You are currently in the "%s" District.' +ShardPageHelpWelcomeValley = ' You are currently in the "Welcome Valley" District, within "%s".' +ShardPageHelpMove = ' To move to a new District, click on its name.' +ShardPagePopulationTotal = 'Total Toontown Population:\n%d' +ShardPageScrollTitle = 'Name Population' +ShardPageLow = 'Quiet' +ShardPageMed = 'Ideal' +ShardPageHigh = 'Full' +ShardPageChoiceReject = 'Sorry, that district is full. Please try another one.' +SuitPageTitle = 'Cog Gallery' +SuitPageMystery = DialogQuestion + DialogQuestion + DialogQuestion +SuitPageQuota = '%s of %s' +SuitPageCogRadar = '%s present' +SuitPageBuildingRadarS = '%s building' +SuitPageBuildingRadarP = '%s buildings' +DisguisePageTitle = Cog + ' Disguise' +DisguisePageMeritAlert = 'Ready for\npromotion!' +DisguisePageCogLevel = 'Level %s' +DisguisePageMeritFull = 'Full' +FishPageTitle = 'Fishing' +FishPageTitleTank = 'Fish Bucket' +FishPageTitleCollection = 'Fish Album' +FishPageTitleTrophy = 'Fishing Trophies' +FishPageWeightStr = 'Weight: ' +FishPageWeightLargeS = '%d lb. ' +FishPageWeightLargeP = '%d lbs. ' +FishPageWeightSmallS = '%d oz.' +FishPageWeightSmallP = '%d oz.' +FishPageWeightConversion = 16 +FishPageValueS = 'Value: %d jellybean' +FishPageValueP = 'Value: %d jellybeans' +FishPageCollectedTotal = 'Fish Species Collected: %d of %d' +FishPageRodInfo = '%s Rod\n%d - %d Pounds' +FishPageTankTab = 'Bucket' +FishPageCollectionTab = 'Album' +FishPageTrophyTab = 'Trophies' +FishPickerTotalValue = 'Bucket: %s / %s\nValue: %d jellybeans' +UnknownFish = '???' +FishingRod = '%s Rod' +FishingRodNameDict = {0: 'Twig', + 1: 'Bamboo', + 2: 'Hardwood', + 3: 'Steel', + 4: 'Gold'} +FishTrophyNameDict = {0: 'Guppy', + 1: 'Minnow', + 2: 'Fish', + 3: 'Flying Fish', + 4: 'Shark', + 5: 'Swordfish', + 6: 'Killer Whale'} +GardenPageTitle = 'Gardening' +GardenPageTitleBasket = 'Flower Basket' +GardenPageTitleCollection = 'Flower Album' +GardenPageTitleTrophy = 'Gardening Trophies' +GardenPageTitleSpecials = 'Gardening Specials' +GardenPageBasketTab = 'Basket' +GardenPageCollectionTab = 'Album' +GardenPageTrophyTab = 'Trophies' +GardenPageSpecialsTab = 'Specials' +GardenPageCollectedTotal = 'Flower Varieties Collected: %d of %d' +GardenPageValueS = 'Value: %d jellybean' +GardenPageValueP = 'Value: %d jellybeans' +FlowerPickerTotalValue = 'Basket: %s / %s\nValue: %d jellybeans' +GardenPageShovelInfo = '%s Shovel: %d / %d\n' +GardenPageWateringCanInfo = '%s Watering Can: %d / %d' +FlowerPageWeightConversion = 1 +FlowerPageWeightLargeP = 'Large P' +FlowerPageWeightLargeS = 'LargeS ' +FlowerPageWeightSmallP = 'SmallP ' +FlowerPageWeightSmallS = 'SmallS ' +FlowerPageWeightStr = 'Weight: %s' +KartPageTitle = 'Karts' +KartPageTitleCustomize = 'Kart Customizer' +KartPageTitleRecords = 'Personal Best Records' +KartPageTitleTrophy = 'Racing Trophies' +KartPageCustomizeTab = 'Customize' +KartPageRecordsTab = 'Records' +KartPageTrophyTab = 'Trophy' +KartPageTrophyDetail = 'Trophy %s : %s' +KartPageTickets = 'Tickets : ' +KartPageConfirmDelete = 'Delete Accessory?' +KartShtikerDelete = 'Delete' +KartShtikerSelect = 'Select a Category' +KartShtikerNoAccessories = 'No Accessories Owned' +KartShtikerBodyColors = 'Kart Colors' +KartShtikerAccColors = 'Accessory Colors' +KartShtikerEngineBlocks = 'Hood Accessories' +KartShtikerSpoilers = 'Trunk Accessories' +KartShtikerFrontWheelWells = 'Front Wheel Accessories' +KartShtikerBackWheelWells = 'Back Wheel Accessories' +KartShtikerRims = 'Rim Accessories' +KartShtikerDecals = 'Decal Accessories' +KartShtikerBodyColor = 'Kart Color' +KartShtikerAccColor = 'Accessory Color' +KartShtikerEngineBlock = 'Hood' +KartShtikerSpoiler = 'Trunk' +KartShtikerFrontWheelWell = 'Front Wheel' +KartShtikerBackWheelWell = 'Back Wheel' +KartShtikerRim = 'Rim' +KartShtikerDecal = 'Decal' +KartShtikerDefault = 'Default %s' +KartShtikerNo = 'No %s Accessory' +QuestChoiceGuiCancel = lCancel +TrackChoiceGuiChoose = 'Choose' +TrackChoiceGuiCancel = lCancel +TrackChoiceGuiHEAL = 'Toonup lets you heal other Toons in battle.' +TrackChoiceGuiTRAP = 'Traps are powerful gags that must be used with Lure.' +TrackChoiceGuiLURE = 'Use Lure to stun Cogs or draw them into traps.' +TrackChoiceGuiSOUND = 'Sound gags affect all Cogs, but are not very powerful.' +TrackChoiceGuiDROP = 'Drop gags do lots of damage, but are not very accurate.' +EmotePageTitle = 'Expressions / Emotions' +EmotePageDance = 'You have built the following dance sequence:' +EmoteJump = 'Jump' +EmoteDance = 'Dance' +EmoteHappy = 'Happy' +EmoteSad = 'Sad' +EmoteAnnoyed = 'Annoyed' +EmoteSleep = 'Sleepy' +TIPPageTitle = 'TIP' +SuitBaseNameWithLevel = '%(name)s\n%(dept)s\nLevel %(level)s' +HealthForceAcknowledgeMessage = 'You cannot leave the playground until your Laff meter is smiling!' +InventoryTotalGags = 'Total gags\n%d / %d' +InventroyPinkSlips = '%s Pink Slips' +InventroyPinkSlip = '1 Pink Slip' +InventoryDelete = 'DELETE' +InventoryDone = 'DONE' +InventoryDeleteHelp = 'Click on a gag to DELETE it.' +InventorySkillCredit = 'Skill credit: %s' +InventorySkillCreditNone = 'Skill credit: None' +InventoryDetailAmount = '%(numItems)s / %(maxItems)s' +InventoryDetailData = 'Accuracy: %(accuracy)s\n%(damageString)s: %(damage)d%(bonus)s\n%(singleOrGroup)s' +InventoryTrackExp = '%(curExp)s / %(nextExp)s' +InventoryUberTrackExp = '%(nextExp)s to Go!' +InventoryGuestExp = 'Guest Limit' +GuestLostExp = 'Over Guest Limit' +InventoryAffectsOneCog = 'Affects: One ' + Cog +InventoryAffectsOneToon = 'Affects: One Toon' +InventoryAffectsAllToons = 'Affects: All Toons' +InventoryAffectsAllCogs = 'Affects: All ' + Cogs +InventoryHealString = 'Toon-up' +InventoryDamageString = 'Damage' +InventoryBattleMenu = 'BATTLE MENU' +InventoryRun = 'RUN' +InventorySOS = 'SOS' +InventoryPass = 'PASS' +InventoryFire = 'FIRE' +InventoryClickToAttack = 'Click a\ngag to\nattack' +InventoryDamageBonus = '(+%d)' +NPCForceAcknowledgeMessage = "You must ride the trolley before leaving.\n\n\n\n\n\n\n\n\nYou can find the trolley next to Goofy's Gag Shop." +NPCForceAcknowledgeMessage2 = 'You must return to Toon Headquarters before leaving.\n\n\n\n\n\n\n\n\n\nToon Headquarters is located near the center of the playground.' +NPCForceAcknowledgeMessage3 = "Remember to ride the trolley.\n\n\n\n\n\n\n\nYou can find the trolley next to Goofy's Gag Shop." +NPCForceAcknowledgeMessage4 = 'Congratulations! You found and rode the trolley!\n\n\n\n\n\n\n\n\n\nNow report back to Toon Headquarters.' +NPCForceAcknowledgeMessage5 = "Don't forget your ToonTask!\n\n\n\n\n\n\n\n\n\n\nYou can find Cogs to defeat on the other side of tunnels like this." +NPCForceAcknowledgeMessage6 = 'Great job defeating those Cogs!\n\n\n\n\n\n\n\n\nHead back to Toon Headquarters as soon as possible.' +NPCForceAcknowledgeMessage7 = "Don't forget to make a friend!\n\n\n\n\n\n\nClick on another player and use the New Friend button." +NPCForceAcknowledgeMessage8 = 'Great! You made a new friend!\n\n\n\n\n\n\n\n\nYou should go back at Toon Headquarters now.' +NPCForceAcknowledgeMessage9 = 'Good job using the phone!\n\n\n\n\n\n\n\n\nReturn to Toon Headquarters to claim your reward.' +ToonSleepString = '. . . ZZZ . . .' +MovieTutorialReward1 = 'You received 1 Throw point! When you get 10, you will get a new gag!' +MovieTutorialReward2 = 'You received 1 Squirt point! When you get 10, you will get a new gag!' +MovieTutorialReward3 = 'Good job! You completed your first ToonTask!' +MovieTutorialReward4 = 'Go to Toon Headquarters for your reward!' +MovieTutorialReward5 = 'Have fun!' +BattleGlobalTracks = ['toon-up', + 'trap', + 'lure', + 'sound', + 'throw', + 'squirt', + 'drop'] +BattleGlobalNPCTracks = ['restock', 'toons hit', 'cogs miss'] +BattleGlobalAvPropStrings = (('Feather', + 'Megaphone', + 'Lipstick', + 'Bamboo Cane', + 'Pixie Dust', + 'Juggling Balls', + 'High Dive'), + ('Banana Peel', + 'Rake', + 'Marbles', + 'Quicksand', + 'Trapdoor', + 'TNT', + 'Railroad'), + ('$1 bill', + 'Small Magnet', + '$5 bill', + 'Big Magnet', + '$10 bill', + 'Hypno-goggles', + 'Presentation'), + ('Bike Horn', + 'Whistle', + 'Bugle', + 'Aoogah', + 'Elephant Trunk', + 'Foghorn', + 'Opera Singer'), + ('Cupcake', + 'Fruit Pie Slice', + 'Cream Pie Slice', + 'Whole Fruit Pie', + 'Whole Cream Pie', + 'Birthday Cake', + 'Wedding Cake'), + ('Squirting Flower', + 'Glass of Water', + 'Squirt Gun', + 'Seltzer Bottle', + 'Fire Hose', + 'Storm Cloud', + 'Geyser'), + ('Flower Pot', + 'Sandbag', + 'Anvil', + 'Big Weight', + 'Safe', + 'Grand Piano', + 'Toontanic')) +BattleGlobalAvPropStringsSingular = (('a Feather', + 'a Megaphone', + 'a Lipstick', + 'a Bamboo Cane', + 'a Pixie Dust', + 'a set of Juggling Balls', + 'a High Dive'), + ('a Banana Peel', + 'a Rake', + 'a set of Marbles', + 'a patch of Quicksand', + 'a Trapdoor', + 'a TNT', + 'a Railroad'), + ('a $1 bill', + 'a Small Magnet', + 'a $5 bill', + 'a Big Magnet', + 'a $10 bill', + 'a pair of Hypno-goggles', + 'a Presentation'), + ('a Bike Horn', + 'a Whistle', + 'a Bugle', + 'an Aoogah', + 'an Elephant Trunk', + 'a Foghorn', + 'an Opera Singer'), + ('a Cupcake', + 'a Fruit Pie Slice', + 'a Cream Pie Slice', + 'a Whole Fruit Pie', + 'a Whole Cream Pie', + 'a Birthday Cake', + 'a Wedding Cake'), + ('a Squirting Flower', + 'a Glass of Water', + 'a Squirt Gun', + 'a Seltzer Bottle', + 'a Fire Hose', + 'a Storm Cloud', + 'a Geyser'), + ('a Flower Pot', + 'a Sandbag', + 'an Anvil', + 'a Big Weight', + 'a Safe', + 'a Grand Piano', + 'the Toontanic')) +BattleGlobalAvPropStringsPlural = (('Feathers', + 'Megaphones', + 'Lipsticks', + 'Bamboo Canes', + 'Pixie Dusts', + 'sets of Juggling Balls', + 'High Dives'), + ('Banana Peels', + 'Rakes', + 'sets of Marbles', + 'patches of Quicksand', + 'Trapdoors', + 'TNTs', + 'Railroads'), + ('$1 bills', + 'Small Magnets', + '$5 bills', + 'Big Magnets', + '$10 bills', + 'pairs of Hypno-goggles', + 'Presentations'), + ('Bike Horns', + 'Whistles', + 'Bugles', + 'Aoogahs', + 'Elephant Trunks', + 'Foghorns', + 'Opera Singers'), + ('Cupcakes', + 'Fruit Pie Slices', + 'Cream Pie Slices', + 'Whole Fruit Pies', + 'Whole Cream Pies', + 'Birthday Cakes', + 'Wedding cakes'), + ('Squirting Flowers', + 'Glasses of Water', + 'Squirt Guns', + 'Seltzer Bottles', + 'Fire Hoses', + 'Storm Clouds', + 'Geysers'), + ('Flower Pots', + 'Sandbags', + 'Anvils', + 'Big Weights', + 'Safes', + 'Grand Pianos', + 'Oceanliners')) +BattleGlobalAvTrackAccStrings = ('Medium', + 'Perfect', + 'Low', + 'High', + 'Medium', + 'High', + 'Low') +BattleGlobalLureAccLow = 'Low' +BattleGlobalLureAccMedium = 'Medium' +AttackMissed = 'MISSED' +NPCCallButtonLabel = 'CALL' +LoaderLabel = 'Loading...' +HeadingToHood = 'Heading %(to)s %(hood)s...' +HeadingToYourEstate = 'Heading to your estate...' +HeadingToEstate = "Heading to %s's estate..." +HeadingToFriend = "Heading to %s's friend's estate..." +HeadingToPlayground = 'Heading to the Playground...' +HeadingToStreet = 'Heading %(to)s %(street)s...' +TownBattleRun = 'Run all the way back to the playground?' +TownBattleChooseAvatarToonTitle = 'WHICH TOON?' +TownBattleChooseAvatarCogTitle = 'WHICH ' + Cog.upper() + '?' +TownBattleChooseAvatarBack = 'BACK' +FireCogTitle = 'PINK SLIPS LEFT:%s\nFIRE WHICH COG?' +FireCogLowTitle = 'PINK SLIPS LEFT:%s\nNOT ENOUGH SLIPS!' +TownBattleSOSNoFriends = 'No friends to call!' +TownBattleSOSWhichFriend = 'Call which friend?' +TownBattleSOSNPCFriends = 'Rescued Toons' +TownBattleSOSBack = 'BACK' +TownBattleToonSOS = 'SOS' +TownBattleToonFire = 'Fire' +TownBattleUndecided = '?' +TownBattleHealthText = '%(hitPoints)s/%(maxHit)s' +TownBattleWaitTitle = 'Waiting for\nother players...' +TownSoloBattleWaitTitle = 'Please wait...' +TownBattleWaitBack = 'BACK' +TownBattleSOSPetSearchTitle = 'Searching for doodle\n%s...' +TownBattleSOSPetInfoTitle = '%s is %s' +TownBattleSOSPetInfoOK = lOK +TrolleyHFAMessage = 'You may not board the trolley until your Laff meter is smiling.' +TrolleyTFAMessage = 'You may not board the trolley until ' + Mickey + ' says so.' +TrolleyHopOff = 'Hop off' +FishingExit = 'Exit' +FishingCast = 'Cast' +FishingAutoReel = 'Auto Reel' +FishingItemFound = 'You caught:' +FishingCrankTooSlow = 'Too\nslow' +FishingCrankTooFast = 'Too\nfast' +FishingFailure = "You didn't catch anything!" +FishingFailureTooSoon = "Don't start to reel in the line until you see a nibble. Wait for your float to bob up and down rapidly!" +FishingFailureTooLate = 'Be sure to reel in the line while the fish is still nibbling!' +FishingFailureAutoReel = "The auto-reel didn't work this time. Turn the crank by hand, at just the right speed, for your best chance to catch something!" +FishingFailureTooSlow = 'You turned the crank too slowly. Some fish are faster than others. Try to keep the speed bar centered!' +FishingFailureTooFast = 'You turned the crank too quickly. Some fish are slower than others. Try to keep the speed bar centered!' +FishingOverTankLimit = 'Your fish bucket is full. Go sell your fish to the Pet Shop Clerk and come back.' +FishingBroke = 'You do not have any more jellybeans for bait! Ride the trolley or sell fish to the Pet Shop Clerks to earn more jellybeans.' +FishingHowToFirstTime = 'Click and drag down from the Cast button. The farther down you drag, the stronger your cast will be. Adjust your angle to hit the fish targets.\n\nTry it now!' +FishingHowToFailed = 'Click and drag down from the Cast button. The farther down you drag, the stronger your cast will be. Adjust your angle to hit the fish targets.\n\nTry it again now!' +FishingBootItem = 'An old boot' +FishingJellybeanItem = '%s jellybeans' +FishingNewEntry = 'New Species!' +FishingNewRecord = 'New Record!' +FishPokerCashIn = 'Cash In\n%s\n%s' +FishPokerLock = 'Lock' +FishPokerUnlock = 'Unlock' +FishPoker5OfKind = '5 of a Kind' +FishPoker4OfKind = '4 of a Kind' +FishPokerFullHouse = 'Full House' +FishPoker3OfKind = '3 of a Kind' +FishPoker2Pair = '2 Pair' +FishPokerPair = 'Pair' +TutorialGreeting1 = 'Hi %s!' +TutorialGreeting2 = 'Hi %s!\nCome over here!' +TutorialGreeting3 = 'Hi %s!\nCome over here!\nUse the arrow keys!' +TutorialMickeyWelcome = 'Welcome to Toontown!' +TutorialFlippyIntro = 'Let me introduce you to my friend %s...' % Flippy +TutorialFlippyHi = 'Hi, %s!' +TutorialQT1 = 'You can talk by using this.' +TutorialQT2 = 'You can talk by using this.\nClick it, then choose "Hi".' +TutorialChat1 = 'You can talk using either of these buttons.' +TutorialChat2 = 'The blue button lets you chat with the keyboard.' +TutorialChat3 = "Be careful! Most other players won't understand what you say you when you use the keyboard." +TutorialChat4 = 'The green button opens the %s.' +TutorialChat5 = 'Everyone can understand you if you use the %s.' +TutorialChat6 = 'Try saying "Hi".' +TutorialBodyClick1 = 'Very good!' +TutorialBodyClick2 = 'Pleased to meet you! Want to be friends?' +TutorialBodyClick3 = 'To make friends with %s, click on him...' % Flippy +TutorialHandleBodyClickSuccess = 'Good Job!' +TutorialHandleBodyClickFail = 'Not quite. Try clicking right on %s...' % Flippy +TutorialFriendsButton = "Now click the 'Friends' button under %s's picture in the right hand corner." % Flippy +TutorialHandleFriendsButton = "And then click on the 'Yes' button.." +TutorialOK = lOK +TutorialYes = lYes +TutorialNo = lNo +TutorialFriendsPrompt = 'Would you like to make friends with %s?' % Flippy +TutorialFriendsPanelMickeyChat = "%s has agreed to be your friend. Click 'Ok' to finish up." % Flippy +TutorialFriendsPanelYes = '%s said yes!' % Flippy +TutorialFriendsPanelNo = "That's not very friendly!" +TutorialFriendsPanelCongrats = 'Congratulations! You made your first friend.' +TutorialFlippyChat1 = 'Come see me when you are ready for your first ToonTask!' +TutorialFlippyChat2 = "I'll be in ToonHall!" +TutorialAllFriendsButton = 'You can view all your friends by clicking the friends button. Try it out...' +TutorialEmptyFriendsList = "Right now your list is empty because %s isn't a real player." % Flippy +TutorialCloseFriendsList = "Click the 'Close'\nbutton to make the\nlist go away" +TutorialShtickerButton = 'The button in the lower, right corner opens your Shticker Book. Try it...' +TutorialBook1 = 'The book contains lots of useful information like this map of Toontown.' +TutorialBook2 = 'You can also check the progress of your ToonTasks.' +TutorialBook3 = 'When you are done click the book button again to make it close' +TutorialLaffMeter1 = 'You will also need this...' +TutorialLaffMeter2 = "You will also need this...\nIt's your Laff meter." +TutorialLaffMeter3 = 'When ' + Cogs + ' attack you, it gets lower.' +TutorialLaffMeter4 = 'When you are in playgrounds like this one, it goes back up.' +TutorialLaffMeter5 = 'When you complete ToonTasks, you will get rewards, like increasing your Laff limit.' +TutorialLaffMeter6 = 'Be careful! If the ' + Cogs + ' defeat you, you will lose all your gags.' +TutorialLaffMeter7 = 'To get more gags, play trolley games.' +TutorialTrolley1 = 'Follow me to the trolley!' +TutorialTrolley2 = 'Hop on board!' +TutorialBye1 = 'Play some games!' +TutorialBye2 = 'Play some games!\nBuy some gags!' +TutorialBye3 = 'Go see %s when you are done!' % Flippy +TutorialForceAcknowledgeMessage = 'You are going the wrong way! Go find %s!' % Mickey +PetTutorialTitle1 = 'The Doodle Panel' +PetTutorialTitle2 = 'Doodle SpeedChat' +PetTutorialTitle3 = 'Doodle Cattlelog' +PetTutorialNext = 'Next Page' +PetTutorialPrev = 'Previous Page' +PetTutorialDone = 'Done' +PetTutorialPage1 = 'Click on a Doodle to display the Doodle panel. From here you can feed, scratch, and call the Doodle.' +PetTutorialPage2 = "Use the new 'Pets' area in the SpeedChat menu to get a Doodle to do a trick. If he does it, reward him and he'll get better!" +PetTutorialPage3 = "Purchase new Doodle tricks from Clarabelle's Cattlelog. Better tricks give better Toon-Ups!" + +def getPetGuiAlign(): + from pandac.PandaModules import TextNode + return TextNode.ACenter + + +GardenTutorialTitle1 = 'Gardening' +GardenTutorialTitle2 = 'Flowers' +GardenTutorialTitle3 = 'Trees' +GardenTutorialTitle4 = 'How-to' +GardenTutorialTitle5 = 'Statues' +GardenTutorialNext = 'Next Page' +GardenTutorialPrev = 'Previous Page' +GardenTutorialDone = 'Done' +GardenTutorialPage1 = 'Toon up your Estate with a garden! You can plant flowers, grow trees, harvest super-powerful gags, and decorate with statues!' +GardenTutorialPage2 = 'Flowers are finicky and require unique jellybean recipes. Once grown, put them in the wheelbarrow to sell them and work toward Laff boosts!' +GardenTutorialPage3 = 'Use a gag from your inventory to plant a tree. After a few days, that gag will do more damage! Remember to keep it healthy or the damage boost will go away.' +GardenTutorialPage4 = 'Walk up to these spots to plant, water, dig up or harvest your garden.' +GardenTutorialPage5 = "Statues can be purchased in Clarabelle's Cattlelog. Increase your skill to unlock the more extravagant statues!" +PlaygroundDeathAckMessage = TheCogs + ' took all your gags!\n\nYou are sad. You may not leave the playground until you are happy.' +ForcedLeaveFactoryAckMsg = 'The ' + Foreman + ' was defeated before you could reach him. You did not recover any Cog parts.' +ForcedLeaveMintAckMsg = 'The Mint Floor Supervisor was defeated before you could reach him. You did not recover any Cogbucks.' +HeadingToFactoryTitle = 'Heading to %s...' +ForemanConfrontedMsg = '%s is battling the ' + Foreman + '!' +MintBossConfrontedMsg = '%s is battling the Supervisor!' +StageBossConfrontedMsg = '%s is battling the Clerk!' +stageToonEnterElevator = '%s \nhas entered the elevator' +ForcedLeaveStageAckMsg = 'The Law Clerk was defeated before you could reach him. You did not recover any Jury Notices.' +MinigameWaitingForOtherPlayers = 'Waiting for other players to join...' +MinigamePleaseWait = 'Please wait...' +DefaultMinigameTitle = 'Minigame Title' +DefaultMinigameInstructions = 'Minigame Instructions' +HeadingToMinigameTitle = 'Heading to %s...' +MinigamePowerMeterLabel = 'Power Meter' +MinigamePowerMeterTooSlow = 'Too\nslow' +MinigamePowerMeterTooFast = 'Too\nfast' +MinigameTemplateTitle = 'Minigame Template' +MinigameTemplateInstructions = 'This is a template minigame. Use it to create new minigames.' +CannonGameTitle = 'Cannon Game' +CannonGameInstructions = 'Shoot your toon into the water tower as quickly as you can. Use the mouse or the arrow keys to aim the cannon. Be quick and win a big reward for everyone!' +CannonGameReward = 'REWARD' +TwoDGameTitle = 'Toon Escape' +TwoDGameInstructions = 'Escape from the ' + Cog + ' den as soon as you can. Use arrow keys to run/jump and Ctrl to squirt a ' + Cog + '. Collect ' + Cog + ' treasures to gain even more points.' +TwoDGameElevatorExit = 'EXIT' +TugOfWarGameTitle = 'Tug-of-War' +TugOfWarInstructions = "Alternately tap the left and right arrow keys just fast enough to line up the green bar with the red line. Don't tap them too slow or too fast, or you'll end up in the water!" +TugOfWarGameGo = 'GO!' +TugOfWarGameReady = 'Ready...' +TugOfWarGameEnd = 'Good game!' +TugOfWarGameTie = 'You tied!' +TugOfWarPowerMeter = 'Power meter' +PatternGameTitle = 'Match %s' % Minnie +PatternGameInstructions = Minnie + ' will show you a dance sequence. ' + 'Try to repeat ' + Minnie + "'s dance just the way you see it using the arrow keys!" +PatternGameWatch = 'Watch these dance steps...' +PatternGameGo = 'GO!' +PatternGameRight = 'Good, %s!' +PatternGameWrong = 'Oops!' +PatternGamePerfect = 'That was perfect, %s!' +PatternGameBye = 'Thanks for playing!' +PatternGameWaitingOtherPlayers = 'Waiting for other players...' +PatternGamePleaseWait = 'Please wait...' +PatternGameFaster = 'You were\nfaster!' +PatternGameFastest = 'You were\nthe fastest!' +PatternGameYouCanDoIt = 'Come on!\nYou can do it!' +PatternGameOtherFaster = '\nwas faster!' +PatternGameOtherFastest = '\nwas the fastest!' +PatternGameGreatJob = 'Great Job!' +PatternGameRound = 'Round %s!' +PatternGameImprov = 'You did great! Now Improv!' +RaceGameTitle = 'Race Game' +RaceGameInstructions = 'Click a number. Choose wisely! You only advance if no one else picked the same number.' +RaceGameWaitingChoices = 'Waiting for other players to choose...' +RaceGameCardText = '%(name)s draws: %(reward)s' +RaceGameCardTextBeans = '%(name)s receives: %(reward)s' +RaceGameCardTextHi1 = '%(name)s is one Fabulous Toon!' +RaceGameForwardOneSpace = ' forward 1 space' +RaceGameForwardTwoSpaces = ' forward 2 spaces' +RaceGameForwardThreeSpaces = ' forward 3 spaces' +RaceGameBackOneSpace = ' back 1 space' +RaceGameBackTwoSpaces = ' back 2 spaces' +RaceGameBackThreeSpaces = ' back 3 spaces' +RaceGameOthersForwardThree = ' all others forward \n3 spaces' +RaceGameOthersBackThree = 'all others back \n3 spaces' +RaceGameInstantWinner = 'Instant Winner!' +RaceGameJellybeans2 = '2 jellybeans' +RaceGameJellybeans4 = '4 jellybeans' +RaceGameJellybeans10 = '10 jellybeans!' +RingGameTitle = 'Ring Game' +RingGameInstructionsSinglePlayer = 'Try to swim through as many of the %s rings as you can. Use the arrow keys to swim.' +RingGameInstructionsMultiPlayer = 'Try to swim through the %s rings. Other players will try for the other colored rings. Use the arrow keys to swim.' +RingGameMissed = 'MISSED' +RingGameGroupPerfect = 'GROUP\nPERFECT!!' +RingGamePerfect = 'PERFECT!' +RingGameGroupBonus = 'GROUP BONUS' +ColorRed = 'red' +ColorGreen = 'green' +ColorOrange = 'orange' +ColorPurple = 'purple' +ColorWhite = 'white' +ColorBlack = 'black' +ColorYellow = 'yellow' +DivingGameTitle = 'Treasure Dive' +DivingInstructionsSinglePlayer = 'Treasures will appear at the bottom of the lake. Use the arrow keys to swim. Avoid the fish and get the treasures up to the boat!' +DivingInstructionsMultiPlayer = 'Treasures will appear at the bottom of the lake. Use the arrow keys to swim. Work together to get the treasures up to the boat!' +DivingGameTreasuresRetrieved = 'Treasures Retrieved' +TargetGameTitle = 'Toon Slingshot' +TargetGameInstructionsSinglePlayer = 'Land on targets to score points' +TargetGameInstructionsMultiPlayer = 'Land on targets to score points' +TargetGameBoard = 'Round %s - Keeping Best Score' +TargetGameCountdown = 'Forced launch in %s seconds' +TargetGameCountHelp = 'Pound left and right arrows for power, stop to launch' +TargetGameFlyHelp = 'Press down to open umbrella' +TargetGameFallHelp = 'Use the arrow keys to land on target' +TargetGameBounceHelp = ' Bouncing can knock you off target' +PhotoGameScoreTaken = '%s: %s\nYou: %s' +PhotoGameScoreBlank = 'Score: %s' +PhotoGameScoreOther = '\n%s' +PhotoGameScoreYou = '\nBest Bonus!' +TagGameTitle = 'Tag Game' +TagGameInstructions = 'Collect the treasures. You cannot collect treasure when you are IT!' +TagGameYouAreIt = 'You Are IT!' +TagGameSomeoneElseIsIt = '%s is IT!' +MazeGameTitle = 'Maze Game' +MazeGameInstructions = 'Collect the treasures. Try to get them all, but look out for the ' + Cogs + '!' +CatchGameTitle = 'Catching Game' +CatchGameInstructions = 'Catch as many %(fruit)s as you can. Watch out for the ' + Cogs + ", and try not to 'catch' any %(badThing)s!" +CatchGamePerfect = 'PERFECT!' +CatchGameApples = 'apples' +CatchGameOranges = 'oranges' +CatchGamePears = 'pears' +CatchGameCoconuts = 'coconuts' +CatchGameWatermelons = 'watermelons' +CatchGamePineapples = 'pineapples' +CatchGameAnvils = 'anvils' +PieTossGameTitle = 'Pie Toss Game' +PieTossGameInstructions = 'Toss pies at the targets.' +PhotoGameInstructions = 'Capture photos matching the toons shown at the bottom. Aim the camera with the mouse, and left click to take a picture. Press Ctrl to zoom in/out, and look around with the arrow keys. Pictures with higher ratings get more points!' +PhotoGameTitle = 'Photo Fun' +PhotoGameFilm = 'FILM' +PhotoGameScore = 'Team Score: %s\n\nBest Photos: %s\n\nTotal Score: %s' +CogThiefGameTitle = Cog + ' Thief' +CogThiefGameInstructions = 'Keep the ' + Cogs + ' from stealing our gag barrels! Press the Ctrl key to throw a pie. Use the arrow keys to move. Tip: you can move diagonally.' +CogThiefBarrelsSaved = '%(num)d Barrels\nSaved!' +CogThiefBarrelSaved = '%(num)d Barrel\nSaved!' +CogThiefNoBarrelsSaved = 'No Barrels\nSaved' +CogThiefPerfect = 'PERFECT!' +MinigameRulesPanelPlay = 'PLAY' +GagShopName = "Goofy's Gag Shop" +GagShopPlayAgain = 'PLAY\nAGAIN' +GagShopBackToPlayground = 'EXIT BACK TO\nPLAYGROUND' +GagShopYouHave = 'You have %s jellybeans to spend' +GagShopYouHaveOne = 'You have 1 jellybean to spend' +GagShopTooManyProps = 'Sorry, you have too many props' +GagShopDoneShopping = 'DONE\nSHOPPING' +GagShopTooManyOfThatGag = 'Sorry, you have enough %s already' +GagShopInsufficientSkill = 'You do not have enough skill for that yet' +GagShopYouPurchased = 'You purchased %s' +GagShopOutOfJellybeans = 'Sorry, you are all out of jellybeans!' +GagShopWaitingOtherPlayers = 'Waiting for other players...' +GagShopPlayerDisconnected = '%s has disconnected' +GagShopPlayerExited = '%s has exited' +GagShopPlayerPlayAgain = 'Play Again' +GagShopPlayerBuying = 'Buying' +GenderShopQuestionMickey = 'To make a boy toon, click on me!' +GenderShopQuestionMinnie = 'To make a girl toon, click on me!' +GenderShopFollow = 'Follow me!' +GenderShopSeeYou = 'See you later!' +GenderShopBoyButtonText = 'Boy' +GenderShopGirlButtonText = 'Girl' +BodyShopHead = 'Head' +BodyShopBody = 'Body' +BodyShopLegs = 'Legs' +ColorShopToon = 'Toon Color' +ColorShopHead = 'Head' +ColorShopBody = 'Body' +ColorShopLegs = 'Legs' +ColorShopParts = 'Multi Color' +ColorShopAll = 'Single Color' +ClothesShopShorts = 'Shorts' +ClothesShopShirt = 'Shirts' +ClothesShopBottoms = 'Bottoms' +PromptTutorial = "Congratulations!!\nYou are Toontown's newest citizen!\n\nWould you like to continue to the Toontorial or teleport directly to Toontown Central?" +MakeAToonSkipTutorial = 'Skip Toontorial' +MakeAToonEnterTutorial = 'Enter Toontorial' +MakeAToonDone = 'Done' +MakeAToonCancel = lCancel +MakeAToonNext = lNext +MakeAToonLast = 'Back' +CreateYourToon = 'Click the arrows to create your toon.' +CreateYourToonTitle = 'Choose Boy or Girl' +ShapeYourToonTitle = 'Choose Your Type' +PaintYourToonTitle = 'Choose Your Color' +PickClothesTitle = 'Choose Your Clothes' +NameToonTitle = 'Choose Your Name' +CreateYourToonHead = "Click the 'head' arrows to pick different animals." +MakeAToonClickForNextScreen = 'Click the arrow below to go to the next screen.' +PickClothes = 'Click the arrows to pick clothes!' +PaintYourToon = 'Click the arrows to paint your toon!' +MakeAToonYouCanGoBack = 'You can go back to change your body too!' +MakeAFunnyName = 'Choose a funny name for your toon with my Pick-A-Name game!' +MustHaveAFirstOrLast1 = "Your toon should have a first or last name, don't you think?" +MustHaveAFirstOrLast2 = "Don't you want your toon to have a first or last name?" +ApprovalForName1 = "That's it, your toon deserves a great name!" +ApprovalForName2 = 'Toon names are the best kind of names!' +MakeAToonLastStep = 'Last step before going to Toontown!' +PickANameYouLike = 'Pick a name you like!' +TitleCheckBox = 'Title' +FirstCheckBox = 'First' +LastCheckBox = 'Last' +RandomButton = 'Random' +ShuffleButton = 'Shuffle' +NameShopSubmitButton = 'Submit' +TypeANameButton = 'Type-A-Name' +TypeAName = "Don't like these names?\nClick here -->" +PickAName = 'Try the PickAName game!\nClick here -->' +PickANameButton = 'Pick-A-Name' +RejectNameText = 'That name is not allowed. Please try again.' +WaitingForNameSubmission = 'Submitting your name...' +PetNameMaster = 'PetNameMasterEnglish.txt' +PetNameIndexMAX = 2713 +PetshopUnknownName = 'Name: ???' +PetshopDescGender = 'Gender:\t%s' +PetshopDescCost = 'Cost:\t%s jellybeans' +PetshopDescTrait = 'Traits:\t%s' +PetshopDescStandard = 'Standard' +PetshopCancel = lCancel +PetshopSell = 'Sell Fish' +PetshopAdoptAPet = 'Adopt a Doodle' +PetshopReturnPet = 'Return your Doodle' +PetshopAdoptConfirm = 'Adopt %s for %d jellybeans?' +PetshopGoBack = 'Go Back' +PetshopAdopt = 'Adopt' +PetshopReturnConfirm = 'Return %s?' +PetshopReturn = 'Return' +PetshopChooserTitle = "TODAY'S DOODLES" +PetshopGoHomeText = 'Would you like to go to your estate to play with your new Doodle?' +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:' +AllNewNames = 'All new names must be\napproved by the Toon Council.' +NameMessages = 'Be creative and remember:\nno Disney-related names, please.' +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') +NumToColor = ['White', + 'Peach', + 'Bright Red', + 'Red', + 'Maroon', + 'Sienna', + 'Brown', + 'Tan', + 'Coral', + 'Orange', + 'Yellow', + 'Cream', + 'Citrine', + 'Lime', + 'Sea Green', + 'Green', + 'Light Blue', + 'Aqua', + 'Blue', + 'Periwinkle', + 'Royal Blue', + 'Slate Blue', + 'Purple', + 'Lavender', + 'Pink', + 'Plum', + 'Black'] +AnimalToSpecies = {'dog': 'Dog', + 'cat': 'Cat', + 'mouse': 'Mouse', + 'horse': 'Horse', + 'rabbit': 'Rabbit', + 'duck': 'Duck', + 'monkey': 'Monkey', + 'bear': 'Bear', + 'pig': 'Pig'} +NameTooLong = 'That name is too long. Please try again.' +ToonAlreadyExists = 'You already have a toon named %s!' +NameAlreadyInUse = 'That name is already used!' +EmptyNameError = 'You must enter a name first.' +NameError = 'Sorry. That name will not work.' +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'" +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.' +RemoveTrophy = lToonHQ + ': ' + TheCogs + ' took over one of the buildings you rescued!' +STOREOWNER_TOOKTOOLONG = 'Need more time to think?' +STOREOWNER_GOODBYE = 'See you later!' +STOREOWNER_NEEDJELLYBEANS = 'You need to ride the Trolley to get some jellybeans.' +STOREOWNER_GREETING = 'Choose what you want to buy.' +STOREOWNER_BROWSING = 'You can browse, but you need a clothing ticket to buy.' +STOREOWNER_NOCLOTHINGTICKET = 'You need a clothing ticket to shop for clothes.' +STOREOWNER_NOFISH = 'Come back here to sell fish to the Pet Shop for jellybeans.' +STOREOWNER_THANKSFISH = 'Thanks! The Pet Shop will love these. Bye!' +STOREOWNER_THANKSFISH_PETSHOP = 'These are some fine specimens! Thanks.' +STOREOWNER_PETRETURNED = "Don't worry. We'll find a good home for your Doodle." +STOREOWNER_PETADOPTED = 'Congratulations on purchasing a Doodle! You can play with your new friend at your estate.' +STOREOWNER_PETCANCELED = 'Remember, if you see a Doodle you like, make sure to adopt him before someone else does!' +STOREOWNER_NOROOM = 'Hmm...you might want to make room in your closet before you buy new clothes.\n' +STOREOWNER_CONFIRM_LOSS = 'Your closet is full. You will lose the clothes you were wearing.' +STOREOWNER_OK = lOK +STOREOWNER_CANCEL = lCancel +STOREOWNER_TROPHY = 'Wow! You collected %s of %s fish. That deserves a trophy and a Laff boost!' +SuitInvasionBegin1 = lToonHQ + ': A Cog Invasion has begun!!!' +SuitInvasionBegin2 = lToonHQ + ': %s have taken over Toontown!!!' +SuitInvasionEnd1 = lToonHQ + ': The %s Invasion has ended!!!' +SuitInvasionEnd2 = lToonHQ + ': The Toons have saved the day once again!!!' +SuitInvasionUpdate1 = lToonHQ + ': The Cog Invasion is now at %s Cogs!!!' +SuitInvasionUpdate2 = lToonHQ + ': We must defeat those %s!!!' +SuitInvasionBulletin1 = lToonHQ + ': There is a Cog Invasion in progress!!!' +SuitInvasionBulletin2 = lToonHQ + ': %s have taken over Toontown!!!' +LeaderboardTitle = 'Toon Platoon' +QuestScriptTutorialMickey_1 = 'Toontown has a new citizen! Do you have some extra gags?' +QuestScriptTutorialMickey_2 = 'Sure, %s!' +QuestScriptTutorialMickey_3 = 'Tutorial Tom will tell you all about the Cogs.\x07Gotta go!' +QuestScriptTutorialMickey_4 = 'Come here! Use the arrow keys to move.' +QuestScriptTutorialMinnie_1 = 'Toontown has a new citizen! Do you have some extra gags?' +QuestScriptTutorialMinnie_2 = 'Sure, %s!' +QuestScriptTutorialMinnie_3 = 'Tutorial Tom will tell you all about the Cogs.\x07Gotta go!' +QuestScript101_1 = 'These are Cogs. They are robots that are trying to take over Toontown.' +QuestScript101_2 = 'There are many different kinds of Cogs and...' +QuestScript101_3 = '...they turn happy Toon buildings...' +QuestScript101_4 = '...into ugly Cog buildings!' +QuestScript101_5 = "But Cogs can't take a joke!" +QuestScript101_6 = 'A good gag will stop them.' +QuestScript101_7 = 'There are lots of gags, but take these to start.' +QuestScript101_8 = 'Oh! You also need a Laff meter!' +QuestScript101_9 = "If your Laff meter gets too low, you'll be sad!" +QuestScript101_10 = 'A happy Toon is a healthy Toon!' +QuestScript101_11 = "OH NO! There's a Cog outside my shop!" +QuestScript101_12 = 'HELP ME, PLEASE! Defeat that Cog!' +QuestScript101_13 = 'Here is your first ToonTask!' +QuestScript101_14 = 'Hurry up! Go defeat that Flunky!' +QuestScript110_1 = 'Good work defeating that Flunky. Let me give you a Shticker Book...' +QuestScript110_2 = 'The book is full of good stuff.' +QuestScript110_3 = "Open it, and I'll show you." +QuestScript110_4 = "The map shows where you've been." +QuestScript110_5 = 'Turn the page to see your gags...' +QuestScript110_6 = 'Uh oh! You have no gags! I will assign you a task.' +QuestScript110_7 = 'Turn the page to see your tasks.' +QuestScript110_8 = 'Take a ride on the trolley, and earn jelly beans to buy gags!' +QuestScript110_9 = 'To get to the trolley, go out the door behind me and head for the playground.' +QuestScript110_10 = 'Now, close the book and find the trolley!' +QuestScript110_11 = 'Return to Toon HQ when you are done. Bye!' +QuestScriptTutorialBlocker_1 = 'Why, hello there!' +QuestScriptTutorialBlocker_2 = 'Hello?' +QuestScriptTutorialBlocker_3 = "Oh! You don't know how to use SpeedChat!" +QuestScriptTutorialBlocker_4 = 'Click on the button to say something.' +QuestScriptTutorialBlocker_5 = 'Very good!\x07Where you are going there are many Toons to talk to.' +QuestScriptTutorialBlocker_6 = "If you want to chat with other Toons using the keyboard, there's another button you can use." +QuestScriptTutorialBlocker_7 = "It's called the Chat button. You need to turn on Speedchat Plus in your Account Manager on the Toontown Web site to use it." +QuestScriptTutorialBlocker_8 = 'Good luck! See you later!' +QuestScriptGagShop_1 = 'Welcome to the Gag Shop!' +QuestScriptGagShop_1a = 'This is where Toons come to buy gags to use against the Cogs.' +QuestScriptGagShop_3 = 'To buy gags, click on the gag buttons. Try getting some now!' +QuestScriptGagShop_4 = 'Good! You can use these gags in battle against the Cogs.' +QuestScriptGagShop_5 = "Here's a peek at the advanced throw and squirt gags..." +QuestScriptGagShop_6 = "When you're done buying gags, click this button to return to the Playground." +QuestScriptGagShop_7 = 'Normally you can use this button to play another Trolley Game...' +QuestScriptGagShop_8 = "...but there's no time for another game right now. You're needed in Toon HQ!" +QuestScript120_1 = "Good job finding the trolley!\x07By the way, have you met Banker Bob?\x07He has quite a sweet tooth.\x07Why don't you introduce yourself by taking him this candy bar as a gift." +QuestScript120_2 = 'Banker Bob is over in the Toontown Bank.' +QuestScript121_1 = "Yum, thank you for the Candy Bar.\x07Say, if you can help me, I'll give you a reward.\x07Those Cogs stole the keys to my safe. Defeat Cogs to find a stolen key.\x07When you find a key, bring it back to me." +QuestScript130_1 = 'Good job finding the trolley!\x07By the way, I received a package for Professor Pete today.\x07It must be his new chalk he ordered.\x07Can you please take it to him?\x07He is over in the school house.' +QuestScript131_1 = 'Oh, thanks for the chalk.\x07What?!?\x07Those Cogs stole my blackboard. Defeat Cogs to find my stolen blackboard.\x07When you find it, bring it back to me.' +QuestScript140_1 = 'Good job finding the trolley!\x07By the way, I have this friend, Librarian Larry, who is quite a book worm.\x07I picked this book up for him last time I was over in ' + lDonaldsDock + '.\x07Could you take it over to him, he is usually in the Library.' +QuestScript141_1 = 'Oh, yes, this book almost completes my collection.\x07Let me see...\x07Uh oh...\x07Now where did I put my glasses?\x07I had them just before those Cogs took over my building.\x07Defeat Cogs to find my stolen glasses.\x07When you find them, bring them back to me for a reward.' +QuestScript145_1 = 'I see you had no problem with the trolley!\x07Listen, the Cogs have stolen our blackboard eraser.\x07Go into the streets and fight Cogs until you recover the eraser.\x07To reach the streets go through one of the tunnels like this:' +QuestScript145_2 = "When you find our eraser, bring it back here.\x07Don't forget, if you need gags, ride the trolley.\x07Also, if you need to recover Laff points, collect ice cream cones in the Playground." +QuestScript150_1 = 'Great work!\x07Toontown is more fun when you have friends!' +QuestScript150_2 = 'To make friends, find another player, and use the New Friend button.' +QuestScript150_3 = 'Once you have made a friend, come back here.' +QuestScript150_4 = 'Some tasks are too difficult to do alone!' +MissingKeySanityCheck = 'Ignore me' +SellbotBossName = 'Senior V. P.' +CashbotBossName = 'C. F. O.' +LawbotBossName = 'Chief Justice' +BossCogNameWithDept = '%(name)s\n%(dept)s' +BossCogPromoteDoobers = 'You are hereby promoted to full-fledged %s. Congratulations!' +BossCogDoobersAway = {'s': 'Go! And make that sale!'} +BossCogWelcomeToons = 'Welcome, new Cogs!' +BossCogPromoteToons = 'You are hereby promoted to full-fledged %s. Congratu--' +CagedToonInterruptBoss = 'Hey! Hiya! Hey over there!' +CagedToonRescueQuery = 'So, did you Toons come to rescue me?' +BossCogDiscoverToons = 'Huh? Toons! In disguise!' +BossCogAttackToons = 'Attack!!' +CagedToonDrop = ["Great job! You're wearing him down!", + "Keep after him! He's on the run!", + 'You guys are doing great!', + "Fantastic! You've almost got him now!"] +CagedToonPrepareBattleTwo = "Look out, he's trying to get away!\x07Help me, everyone--get up here and stop him!" +CagedToonPrepareBattleThree = "Hooray, I'm almost free!\x07Now you need to attack the V.P. Cog directly.\x07I've got a whole bunch of pies you can use!\x07Jump up and touch the bottom of my cage and I'll give you some pies.\x07Press the Delete key to throw pies once you've got them!" +BossBattleNeedMorePies = 'You need to get more pies!' +BossBattleHowToGetPies = 'Jump up to touch the cage to get pies.' +BossBattleHowToThrowPies = 'Press the Delete key to throw pies!' +CagedToonYippee = 'Yippee!' +CagedToonThankYou = "It's great to be free!\x07Thanks for all your help!\x07I am in your debt.\x07Here's my card. If you ever need a hand in battle, give a shout!\x07Just click on your SOS button." +CagedToonPromotion = "\x07Say--that V.P. Cog left behind your promotion papers.\x07I'll file them for you on the way out, so you'll get your promotion!" +CagedToonLastPromotion = "\x07Wow, you've reached level %s on your Cog suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog suit anymore, but you can certainly keep rescuing Toons!" +CagedToonHPBoost = "\x07You've rescued a lot of Toons from this HQ.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +CagedToonMaxed = '\x07I see that you have a level %s Cog suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to rescue more Toons!' +CagedToonGoodbye = 'See ya!' +CagedToonBattleThree = {10: 'Nice jump, %(toon)s. Here are some pies!', + 11: 'Hi, %(toon)s! Have some pies!', + 12: "Hey there, %(toon)s! You've got some pies now!", + 20: 'Hey, %(toon)s! Jump up to my cage and get some pies to throw!', + 21: 'Hi, %(toon)s! Use the Ctrl key to jump up and touch my cage!', + 100: 'Press the Delete key to throw a pie.', + 101: 'The blue power meter shows how high your pie will go.', + 102: 'First try to lob a pie inside his undercarriage to gum up his works.', + 103: 'Wait for the door to open, and throw a pie straight inside.', + 104: "When he's dizzy, hit him in the face or chest to knock him back!", + 105: "You'll know you've got a good hit when you see the splat in color.", + 106: 'If you hit a Toon with a pie, it gives that Toon a Laff point!'} +CagedToonBattleThreeMaxGivePies = 12 +CagedToonBattleThreeMaxTouchCage = 21 +CagedToonBattleThreeMaxAdvice = 106 +CashbotBossHadEnough = "That's it. I've had enough of these pesky Toons!" +CashbotBossOuttaHere = "I've got a train to catch!" +ResistanceToonName = 'Mata Hairy' +ResistanceToonCongratulations = "You did it! Congratulations!\x07You're an asset to the Resistance!\x07Here's a special phrase you can use in a tight spot:\x07%s\x07When you say it, %s.\x07But you can only use it once, so choose that time well!" +ResistanceToonToonupInstructions = 'all the Toons near you will gain %s Laff points' +ResistanceToonToonupAllInstructions = 'all the Toons near you will gain full Laff points' +ResistanceToonMoneyInstructions = 'all the Toons near you will gain %s jellybeans' +ResistanceToonMoneyAllInstructions = 'all the Toons near you will fill their jellybean jars' +ResistanceToonRestockInstructions = 'all the Toons near you will restock their "%s" gags' +ResistanceToonRestockAllInstructions = 'all the Toons near you will restock all their gags' +ResistanceToonLastPromotion = "\x07Wow, you've reached level %s on your Cog suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog suit anymore, but you can certainly keep working for the Resistance!" +ResistanceToonHPBoost = "\x07You've done a lot of work for the Resistance.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +ResistanceToonMaxed = '\x07I see that you have a level %s Cog suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to rescue more Toons!' +CashbotBossCogAttack = 'Get them!!!' +ResistanceToonWelcome = 'Hey, you made it! Follow me to the main vault before the C.F.O. finds us!' +ResistanceToonTooLate = "Blast it! We're too late!" +CashbotBossDiscoverToons1 = 'Ah-HAH!' +CashbotBossDiscoverToons2 = 'I thought I smelled something a little toony in here! Imposters!' +ResistanceToonKeepHimBusy = "Keep him busy! I'm going to set a trap!" +ResistanceToonWatchThis = 'Watch this!' +CashbotBossGetAwayFromThat = 'Hey! Get away from that!' +ResistanceToonCraneInstructions1 = 'Control a magnet by stepping up to a podium.' +ResistanceToonCraneInstructions2 = 'Use the arrow keys to move the crane, and press the Ctrl key to grab an object.' +ResistanceToonCraneInstructions3 = "Grab a safe with a magnet and knock the C.F.O.'s safe-ty helmet off." +ResistanceToonCraneInstructions4 = 'Once his helmet is gone, grab a disabled goon and hit him in the head!' +ResistanceToonGetaway = 'Eek! Gotta run!' +CashbotCraneLeave = 'Leave Crane' +CashbotCraneAdvice = 'Use the arrow keys to move the overhead crane.' +CashbotMagnetAdvice = 'Hold down the control key to pick things up.' +CashbotCraneLeaving = 'Leaving crane' +MintElevatorRejectMessage = 'You cannot enter the Mints until you have completed your %s Cog Suit.' +BossElevatorRejectMessage = 'You cannot board this elevator until you have earned a promotion.' +NotYetAvailable = 'This elevator is not yet available.' +SellbotRentalSuitMessage = "Wear this Rental Suit so you can get close enough to the VP to attack.\n\nYou won't earn merits or promotions, but you can rescue a Toon for an SOS reward!" +SellbotCogSuitNoMeritsMessage = "Your Sellbot Disguise will get you in, but since you don't have enough merits, you won't earn a promotion.\n\nIf you rescue the trapped Toon, you will earn an SOS Toon reward!" +SellbotCogSuitHasMeritsMessage = "It's Operation: Storm Sellbot!\n\nBring 5 or more Rental Suit Toons with you to defeat the VP and earn credit towards a reward!" +FurnitureTypeName = 'Furniture' +PaintingTypeName = 'Painting' +ClothingTypeName = 'Clothing' +ChatTypeName = 'SpeedChat Phrase' +EmoteTypeName = 'Acting Lessons' +BeanTypeName = 'Jellybeans' +PoleTypeName = 'Fishing Pole' +WindowViewTypeName = 'Window View' +PetTrickTypeName = 'Doodle Training' +GardenTypeName = 'Garden Supplies' +RentalTypeName = 'Rental Item' +GardenStarterTypeName = 'Gardening Kit' +NametagTypeName = 'Name tag' +AccessoryTypeName = 'Accessory' +CatalogItemTypeNames = {0: 'INVALID_ITEM', + 1: FurnitureTypeName, + 2: ChatTypeName, + 3: ClothingTypeName, + 4: EmoteTypeName, + 5: 'WALLPAPER', + 6: 'Window View', + 7: 'FLOORING', + 8: 'MOULDING', + 9: 'WAINSCOTING', + 10: PoleTypeName, + 11: PetTrickTypeName, + 12: BeanTypeName, + 13: GardenTypeName, + 14: RentalTypeName, + 15: GardenStarterTypeName, + 16: NametagTypeName, + 17: 'TOON_STATUE', + 18: 'ANIMATED FURNITURE', + 19: AccessoryTypeName} +HatStylesDescriptions = {'hbb1': 'Green Baseball Cap', + 'hbb2': 'Blue Baseball Cap', + 'hbb3': 'Orange Baseball Cap', + 'hsf1': 'Beige Safari Hat', + 'hsf2': 'Brown Safari Hat', + 'hsf3': 'Green Safari Hat', + 'hrb1': 'Pink Bow', + 'hrb2': 'Red Bow', + 'hrb3': 'Purple Bow', + 'hht1': 'Pink Heart', + 'hht2': 'Yellow Heart', + 'htp1': 'Black Top Hat', + 'htp2': 'Blue Top Hat', + 'hav1': 'Anvil Hat', + 'hfp1': 'Flower Hat', + 'hsg1': 'Sandbag Hat', + 'hwt1': 'Weight Hat', + 'hfz1': 'Fez Hat', + 'hgf1': 'Golf Hat', + 'hpt1': 'Party Hat', + 'hpt2': 'Toon Party Hat', + 'hpb1': 'Fancy Hat', + 'hcr1': 'Crown', + 'hcw1': 'Cowboy Hat', + 'hpr1': 'Pirate Hat', + 'hpp1': 'Propeller Hat', + 'hfs1': 'Fishing Hat', + 'hsb1': 'Sombrero Hat', + 'hst1': 'Straw Hat', + 'hsu1': 'Sun Hat', + 'hrb4': 'Yellow Bow', + 'hrb5': 'Checker Bow', + 'hrb6': 'Light Red Bow', + 'hrb7': 'Rainbow Bow', + 'hat1': 'Antenna Thingy', + 'hhd1': 'Beehive Hairdo', + 'hbw1': 'Bowler Hat', + 'hch1': 'Chef Hat', + 'hdt1': 'Detective Hat', + 'hft1': 'Fancy Feathers Hat', + 'hfd1': 'Fedora', + 'hmk1': "Mickey's Band Hat", + 'hft2': 'Feather Headband', + 'hhd2': 'Pompadour Hairdo', + 'hpc1': 'Princess Hat', + 'hrh1': 'Archer Hat', + 'hhm1': 'Roman Helmet', + 'hat2': 'Spider Antenna Thingy', + 'htr1': 'Tiara', + 'hhm2': 'Viking Helmet', + 'hwz1': 'Witch Hat', + 'hwz2': 'Wizard Hat', + 'hhm3': 'Conquistador Helmet', + 'hhm4': 'Firefighter Helmet', + 'hfp2': 'Anti-Cog Control Hat', + 'hhm5': 'Miner Hat', + 'hnp1': 'Napoleon Hat', + 'hpc2': 'Pilot Cap', + 'hph1': 'Cop Hat', + 'hwg1': 'Rainbow Wacky Wig', + 'hbb4': 'Yellow Baseball Cap', + 'hbb5': 'Red Baseball Cap', + 'hbb6': 'Aqua Baseball Cap', + 'hsl1': 'Sailor Hat', + 'hfr1': 'Samba Hat', + 'hby1': 'Bobby Hat', + 'hrb8': 'Pink Dots Bow', + 'hjh1': 'Jester Hat', + 'hbb7': 'Purple Baseball Cap', + 'hrb9': 'Green Checker Bow', + 'hwt2': 'Winter Hat', + 'hhw1': 'Bandana', + 'hhw2': 'Toonosaur Hat', + 'hob1': 'Jamboree Hat', + 'hbn1': 'Bird Hat by Brianna'} +GlassesStylesDescriptions = {'grd1': 'Round Glasses', + 'gmb1': 'White Mini Blinds', + 'gnr1': 'Purple Narrow Glasses', + 'gst1': 'Yellow Star Glasses', + 'g3d1': 'Movie Glasses', + 'gav1': 'Aviator', + 'gce1': 'Cateye Glasses', + 'gdk1': 'Nerd Glasses', + 'gjo1': 'Celebrity Shades', + 'gsb1': 'Scuba Mask', + 'ggl1': 'Goggles', + 'ggm1': 'Groucho Glasses', + 'ghg1': 'Heart Glasses', + 'gie1': 'Bug Eye Glasses', + 'gmt1': 'Black Secret ID Mask', + 'gmt2': 'Blue Secret ID Mask', + 'gmt3': 'Blue Carnivale Mask', + 'gmt4': 'Purple Carnivale Mask', + 'gmt5': 'Aqua Carnivale Mask', + 'gmn1': 'Monocle', + 'gmo1': 'Smooch Glasses', + 'gsr1': 'Square Frame Glasses', + 'ghw1': 'Skull Eyepatch', + 'ghw2': 'Gem Eyepatch', + 'gag1': 'Alien Eyes by Alexandra'} +BackpackStylesDescriptions = {'bpb1': 'Blue Backpack', + 'bpb2': 'Orange Backpack', + 'bpb3': 'Purple BackPack', + 'bpd1': 'Red Dot Backpack', + 'bpd2': 'Yellow Dot Backpack', + 'bwg1': 'Bat Wings', + 'bwg2': 'Bee Wings', + 'bwg3': 'DragonFly Wings', + 'bst1': 'Scuba Tank', + 'bfn1': 'Shark Fin', + 'baw1': 'White Angel Wings', + 'baw2': 'Rainbow Angel Wings', + 'bwt1': 'Toys Backpack', + 'bwg4': 'Butterfly Wings', + 'bwg5': 'Pixie Wings', + 'bwg6': 'Dragon Wings', + 'bjp1': 'Jet Pack', + 'blg1': 'Bug Backpack', + 'bsa1': 'Plush Bear Pack', + 'bwg7': 'Bird wings', + 'bsa2': 'Plush Cat Pack', + 'bsa3': 'Plush Dog Pack', + 'bap1': 'Airplane Wings', + 'bhw1': 'Pirate Sword', + 'bhw2': 'Super Toon Cape', + 'bhw3': 'Vampire Cape', + 'bhw4': 'Toonosaur Backpack', + 'bob1': 'Jamboree Pack', + 'bfg1': 'Gag Attack Pack', + 'bfl1': 'Cog Pack by Savanah'} +ShoesStylesDescriptions = {'sat1': 'Green Athletic Shoes', + 'sat2': 'Red Athletic Shoes', + 'smb1': 'Green Toon Boots', + 'scs1': 'Green Sneakers', + 'swt1': 'Wingtips', + 'smj1': 'Black Fancy Shoes', + 'sdk1': 'Boat Shoes', + 'sat3': 'Yellow Athletic Shoes', + 'scs2': 'Black Sneakers', + 'scs3': 'White Sneakers', + 'scs4': 'Pink Sneakers', + 'scb1': 'Cowboy Boots', + 'sfb1': 'Purple Boots', + 'sht1': 'Green Hi Top Sneakers', + 'smj2': 'Brown Fancy Shoes', + 'smj3': 'Red Fancy Shoes', + 'ssb1': 'Red Super Toon Boots', + 'sts1': 'Green Tennis Shoes', + 'sts2': 'Pink Tennis Shoes', + 'scs5': 'Red Sneakers', + 'smb2': 'Aqua Toon Boots', + 'smb3': 'Brown Toon Boots', + 'smb4': 'Yellow Toon Boots', + 'sfb2': 'Blue Square Boots', + 'sfb3': 'Green Hearts Boots', + 'sfb4': 'Grey Dots Boots', + 'sfb5': 'Orange Stars Boots', + 'sfb6': 'Pink Stars Boots', + 'slf1': 'Loafers', + 'smj4': 'Purple Fancy Shoes', + 'smt1': 'Motorcycle Boots', + 'sox1': 'Oxfords', + 'srb1': 'Pink Rain Boots', + 'sst1': 'Jolly Boots', + 'swb1': 'Beige Winter Boots', + 'swb2': 'Pink Winter Boots', + 'swk1': 'Work Boots', + 'scs6': 'Yellow Sneakers', + 'smb5': 'Pink Toon Boots', + 'sht2': 'Pink Hi Top Sneakers', + 'srb2': 'Red Dots Rain Boots', + 'sts3': 'Purple Tennis Shoes', + 'sts4': 'Violet Tennis Shoes', + 'sts5': 'Yellow Tennis Shoes', + 'srb3': 'Blue Rain Boots', + 'srb4': 'Yellow Rain Boots', + 'sat4': 'Black Athletic Shoes', + 'shw1': 'Pirate Shoes', + 'shw2': 'Toonosaur Feet'} +AccessoryNamePrefix = {0: 'hat unisex ', + 1: 'glasses unisex ', + 2: 'backpack unisex ', + 3: 'shoes unisex ', + 4: 'hat boy ', + 5: 'glasses boy ', + 6: 'backpack boy ', + 7: 'shoes boy ', + 8: 'hat girl ', + 9: 'glasses girl ', + 10: 'backpack girl ', + 11: 'shoes girl '} +AwardManagerAccessoryNames = {} +AccessoryTypeNames = {} +for accessoryId in CatalogAccessoryItemGlobals.AccessoryTypes.keys(): + accessoryInfo = CatalogAccessoryItemGlobals.AccessoryTypes[accessoryId] + if accessoryInfo[0] % 4 == 0: + accessoryStyleDescription = HatStylesDescriptions + elif accessoryInfo[0] % 4 == 1: + accessoryStyleDescription = GlassesStylesDescriptions + elif accessoryInfo[0] % 4 == 2: + accessoryStyleDescription = BackpackStylesDescriptions + else: + accessoryStyleDescription = ShoesStylesDescriptions + if accessoryInfo[3]: + AwardManagerAccessoryNames[accessoryId] = AccessoryNamePrefix[accessoryInfo[0]] + accessoryStyleDescription[accessoryInfo[1]] + AccessoryTypeNames[accessoryId] = accessoryStyleDescription[accessoryInfo[1]] + +ShirtStylesDescriptions = {'bss1': 'solid', + 'bss2': 'single stripe', + 'bss3': 'collar', + 'bss4': 'double stripe', + 'bss5': 'multiple stripes', + 'bss6': 'collar w/ pocket', + 'bss7': 'hawaiian', + 'bss8': 'collar w/ 2 pockets', + 'bss9': 'bowling shirt', + 'bss10': 'vest (special)', + 'bss11': 'collar w/ ruffles', + 'bss12': 'soccer jersey (special)', + 'bss13': 'lightning bolt (special)', + 'bss14': 'jersey 19 (special)', + 'bss15': 'guayavera', + 'gss1': 'girl solid', + 'gss2': 'girl single stripe', + 'gss3': 'girl collar', + 'gss4': 'girl double stripes', + 'gss5': 'girl collar w/ pocket', + 'gss6': 'girl flower print', + 'gss7': 'girl flower trim (special)', + 'gss8': 'girl collar w/ 2 pockets', + 'gss9': 'girl denim vest (special)', + 'gss10': 'girl peasant', + 'gss11': 'girl peasant w/ mid stripe', + 'gss12': 'girl soccer jersey (special)', + 'gss13': 'girl hearts', + 'gss14': 'girl stars (special)', + 'gss15': 'girl flower', + 'c_ss1': 'yellow hooded - Series 1', + 'c_ss2': 'yellow with palm tree - Series 1', + 'c_ss3': 'purple with stars - Series 2', + 'c_bss1': 'blue stripes (boys only) - Series 1', + 'c_bss2': 'orange (boys only) - Series 1', + 'c_bss3': 'lime green with stripe (boys only) - Series 2', + 'c_bss4': 'red kimono with checkerboard (boys only) - Series 2', + 'c_gss1': 'girl blue with yellow stripes (girls only) - Series 1', + 'c_gss2': 'girl pink and beige with flower (girls only) - Series 1', + 'c_gss3': 'girl Blue and gold with wavy stripes (girls only) - Series 2', + 'c_gss4': 'girl Blue and pink with bow (girls only) - Series 2', + 'c_gss5': 'girl Aqua kimono white stripe (girls only) - UNUSED', + 'c_ss4': 'Tie dye shirt (boys and girls) - Series 3', + 'c_ss5': 'light blue with blue and white stripe (boys only) - Series 3', + 'c_ss6': 'cowboy shirt 1 : Series 4', + 'c_ss7': 'cowboy shirt 2 : Series 4', + 'c_ss8': 'cowboy shirt 3 : Series 4', + 'c_ss9': 'cowboy shirt 4 : Series 4', + 'c_ss10': 'cowboy shirt 5 : Series 4', + 'c_ss11': 'cowboy shirt 6 : Series 4', + 'hw_ss1': 'Halloween Ghost', + 'hw_ss2': 'Halloween Pumpkin', + 'hw_ss3': 'Halloween Vampire', + 'hw_ss4': 'Halloween Turtle', + 'hw_ss5': 'Halloween Bee', + 'hw_ss6': 'Halloween Pirate', + 'hw_ss7': 'Halloween SuperToon', + 'hw_ss8': 'Halloween Vampire NoCape', + 'hw_ss9': 'Halloween Dinosaur', + 'wh_ss1': 'Winter Holiday 1', + 'wh_ss2': 'Winter Holiday 2', + 'wh_ss3': 'Winter Holiday 3', + 'wh_ss4': 'Winter Holiday 4', + 'vd_ss1': 'girl Valentines day, pink with red hearts (girls)', + 'vd_ss2': 'Valentines day, red with white hearts', + 'vd_ss3': 'Valentines day, white with winged hearts (boys)', + 'vd_ss4': ' Valentines day, pink with red flamed heart', + 'vd_ss5': '2009 Valentines day, white with red cupid', + 'vd_ss6': '2009 Valentines day, blue with green and red hearts', + 'vd_ss7': '2010 Valentines day, red with white wings', + 'sd_ss1': "St Pat's Day, four leaf clover shirt", + 'sd_ss2': "St Pat's Day, pot o gold shirt", + 'sd_ss3': 'Ides of March greenToon shirt', + 'tc_ss1': 'T-Shirt Contest, Fishing Vest', + 'tc_ss2': 'T-Shirt Contest, Fish Bowl', + 'tc_ss3': 'T-Shirt Contest, Paw Print', + 'tc_ss4': 'T-Shirt Contest, Backpack', + 'tc_ss5': 'T-Shirt Contest, Lederhosen ', + 'tc_ss6': 'T-Shirt Contest, Watermelon ', + 'tc_ss7': 'T-Shirt Contest, Race Shirt', + 'j4_ss1': 'July 4th, Flag', + 'j4_ss2': 'July 4th, Fireworks', + 'c_ss12': 'Catalog series 7, Green w/ yellow buttons', + 'c_ss13': 'Catalog series 7, Purple w/ big flower', + 'pj_ss1': 'Blue Banana Pajama shirt', + 'pj_ss2': 'Red Horn Pajama shirt', + 'pj_ss3': 'Purple Glasses Pajama shirt', + 'sa_ss1': 'Award Striped Shirt', + 'sa_ss2': 'Award Fishing Shirt 1', + 'sa_ss3': 'Award Fishing Shirt 2', + 'sa_ss4': 'Award Gardening Shirt 1', + 'sa_ss5': 'Award Gardening Shirt 2', + 'sa_ss6': 'Award Party Shirt 1', + 'sa_ss7': 'Award Party Shirt 2', + 'sa_ss8': 'Award Racing Shirt 1', + 'sa_ss9': 'Award Racing Shirt 2', + 'sa_ss10': 'Award Summer Shirt 1', + 'sa_ss11': 'Award Summer Shirt 2', + 'sa_ss12': 'Award Golf Shirt 1', + 'sa_ss13': 'Award Golf Shirt 2', + 'sa_ss14': 'Award Halloween Bee Shirt', + 'sa_ss15': 'Award Halloween SuperToon Shirt', + 'sa_ss16': 'Award Matathon Shirt 1', + 'sa_ss17': 'Award Save Building Shirt 1', + 'sa_ss18': 'Award Save Building Shirt 2', + 'sa_ss19': 'Award Toontask Shirt 1', + 'sa_ss20': 'Award Toontask Shirt 2', + 'sa_ss21': 'Award Trolley Shirt 1', + 'sa_ss22': 'Award Trolley Shirt 2', + 'sa_ss23': 'Award Winter Shirt 1', + 'sa_ss24': 'Award Halloween Skeleton Shirt', + 'sa_ss25': 'Award Halloween Spider Shirt', + 'sa_ss26': 'Award Most Cogs Defeated Shirt', + 'sa_ss27': 'Award Most V.P.s Defeated Shirt', + 'sa_ss28': 'Award Sellbot Smasher Shirt', + 'sa_ss29': 'Award Most C.J.s Defeated Shirt', + 'sa_ss30': 'Award Lawbot Smasher Shirt', + 'sa_ss31': 'Award Racing Shirt 3', + 'sa_ss32': 'Award Fishing Shirt 4', + 'sa_ss33': 'Award Golf Shirt 3', + 'sa_ss34': 'Award Most Cogs Defeated Shirt 2', + 'sa_ss35': 'Award Racing Shirt 4', + 'sa_ss36': 'Award Save Building Shirt 3', + 'sa_ss37': 'Award Trolley Shirt 3', + 'sa_ss38': 'Award Fishing Shirt 5', + 'sa_ss39': 'Award Golf Shirt 4', + 'sa_ss40': 'Award Halloween Witchy Moon Shirt', + 'sa_ss41': 'Award Winter Holiday Sled Shirt', + 'sa_ss42': 'Award Halloween Batty Moon Shirt', + 'sa_ss43': 'Award Winter Holiday Mittens Shirt', + 'sa_ss44': 'Award Fishing Shirt 6', + 'sa_ss45': 'Award Fishing Shirt 7', + 'sa_ss46': 'Award Golf Shirt 5', + 'sa_ss47': 'Award Racing Shirt 5', + 'sa_ss48': 'Award Racing Shirt 6', + 'sa_ss49': 'Award Most Cogs Defeated shirt 3', + 'sa_ss50': 'Award Most Cogs Defeated shirt 4', + 'sa_ss51': 'Award Trolley shirt 4', + 'sa_ss52': 'Award Trolley shirt 5', + 'sa_ss53': 'Award Save Building Shirt 4', + 'sa_ss54': 'Award Save Building Shirt 5', + 'sa_ss55': 'Award Anniversary', + 'sc_1': 'Scientist top 1', + 'sc_2': 'Scientist top 2', + 'sc_3': 'Scientist top 3', + 'sil_1': 'Silly Mailbox Shirt', + 'sil_2': 'Silly Trash Can Shirt', + 'sil_3': 'Loony Labs Shirt', + 'sil_4': 'Silly Hydrant Shirt', + 'sil_5': 'Sillymeter Whistle Shirt', + 'sil_6': 'Silly Cog-Crusher Shirt', + 'sil_7': 'Victory Party Shirt 1', + 'sil_8': 'Victory Party Shirt 2', + 'emb_us1': 'placeholder emblem shirt 1', + 'emb_us2': 'placeholder emblem shirt 2', + 'emb_us3': 'placeholder emblem shirt 3', + 'sb_1': 'Sellbot Icon Shirt', + 'lb_1': 'Lawbot Icon Shirt', + 'jb_1': 'Jellybean Shirt', + 'jb_2': 'Doodle Shirt', + 'ugcms': 'Get Connected Mover & Shaker'} +BottomStylesDescriptions = {'bbs1': 'plain w/ pockets', + 'bbs2': 'belt', + 'bbs3': 'cargo', + 'bbs4': 'hawaiian', + 'bbs5': 'side stripes (special)', + 'bbs6': 'soccer shorts', + 'bbs7': 'side flames (special)', + 'bbs8': 'denim', + 'vd_bs1': 'Valentines shorts', + 'vd_bs2': 'Green with red heart', + 'vd_bs3': 'Blue denim with green and red heart', + 'c_bs1': 'Orange with blue side stripes', + 'c_bs2': 'Blue with gold cuff stripes', + 'c_bs5': 'Green stripes - series 7', + 'sd_bs1': 'St. Pats leprechaun shorts', + 'sd_bs2': 'Ides of March greenToon shorts', + 'pj_bs1': 'Blue Banana Pajama pants', + 'pj_bs2': 'Red Horn Pajama pants', + 'pj_bs3': 'Purple Glasses Pajama pants', + 'wh_bs1': 'Winter Holiday Shorts Style 1', + 'wh_bs2': 'Winter Holiday Shorts Style 2', + 'wh_bs3': 'Winter Holiday Shorts Style 3', + 'wh_bs4': 'Winter Holiday Shorts Style 4', + 'hw_bs1': 'Halloween Bee Shorts male', + 'hw_bs2': 'Halloween Pirate Shorts male', + 'hw_bs5': 'Halloween SuperToon Shorts male', + 'hw_bs6': 'Halloween Vampire NoCape Shorts male', + 'hw_bs7': 'Halloween Dinosaur Shorts male', + 'sil_bs1': 'Silly Cog-Crusher Shorts', + 'gsk1': 'solid', + 'gsk2': 'polka dots (special)', + 'gsk3': 'vertical stripes', + 'gsk4': 'horizontal stripe', + 'gsk5': 'flower print', + 'gsk6': '2 pockets (special) ', + 'gsk7': 'denim skirt', + 'gsh1': 'plain w/ pockets', + 'gsh2': 'flower', + 'gsh3': 'denim shorts', + 'c_gsk1': 'blue skirt with tan border and button', + 'c_gsk2': 'purple skirt with pink and ribbon', + 'c_gsk3': 'teal skirt with yellow and star', + 'vd_gs1': 'red skirt with hearts', + 'vd_gs2': 'Pink flair skirt with polka hearts', + 'vd_gs3': 'Blue denim skirt with green and red heart', + 'c_gsk4': 'rainbow skirt - Series 3', + 'sd_gs1': 'St. Pats day shorts', + 'sd_gs2': 'Ides of March greenToon skirt', + 'c_gsk5': 'Western skirts 1', + 'c_gsk6': 'Western skirts 2', + 'c_bs3': 'Western shorts 1', + 'c_bs4': 'Western shorts 2', + 'j4_bs1': 'July 4th shorts', + 'j4_gs1': 'July 4th Skirt', + 'c_gsk7': 'Blue with flower - series 7', + 'pj_gs1': 'Blue Banana Pajama pants', + 'pj_gs2': 'Red Horn Pajama pants', + 'pj_gs3': 'Purple Glasses Pajama pants', + 'wh_gsk1': 'Winter Holiday Skirt Style 1', + 'wh_gsk2': 'Winter Holiday Skirt Style 2', + 'wh_gsk3': 'Winter Holiday Skirt Style 3', + 'wh_gsk4': 'Winter Holiday Skirt Style 4', + 'sa_bs1': 'Award Fishing Shorts', + 'sa_bs2': 'Award Gardening Shorts', + 'sa_bs3': 'Award Party Shorts', + 'sa_bs4': 'Award Racing Shorts', + 'sa_bs5': 'Award Summer Shorts', + 'sa_bs6': 'Award Golf Shorts 1', + 'sa_bs7': 'Award Halloween Bee Shorts', + 'sa_bs8': 'Award Halloween SuperToon Shorts', + 'sa_bs9': 'Award Save Building Shorts 1', + 'sa_bs10': 'Award Trolley Shorts 1', + 'sa_bs11': 'Award Halloween Spider Shorts', + 'sa_bs12': 'Award Halloween Skeleton Shorts', + 'sa_bs13': 'Award Sellbot Smasher Shorts male', + 'sa_bs14': 'Award Lawbot Smasher Shorts male', + 'sa_bs15': 'Award Racing Shorts 1', + 'sa_bs16': 'Award Golf Shorts 3', + 'sa_bs17': 'Award Racing Shorts 4', + 'sa_bs18': 'Award Golf Shorts 4', + 'sa_bs19': 'Award Golf Shorts 5', + 'sa_bs20': 'Award Racing Shorts 5', + 'sa_bs21': 'Award Racing Shorts 6', + 'sa_gs1': 'Award Fishing Skirt', + 'sa_gs2': 'Award Gardening Skirt', + 'sa_gs3': 'Award Party Skirt', + 'sa_gs4': 'Award Racing Skirt', + 'sa_gs5': 'Award Summer Skirt', + 'sa_gs6': 'Award Golf Skirt 1', + 'sa_gs7': 'Award Halloween Bee Skirt', + 'sa_gs8': 'Award Halloween SuperToon Skirt', + 'sa_gs9': 'Award Save Building Skirt 1', + 'sa_gs10': 'Award Trolley Skirt 1', + 'sa_gs11': 'Award Halloween Skeleton Skirt', + 'sa_gs12': 'Award Halloween Spider Skirt', + 'sa_gs13': 'Award Sellbot Smasher Shorts female', + 'sa_gs14': 'Award Lawbot Smasher Shorts female', + 'sa_gs15': 'Award Racing Skirt 1', + 'sa_gs16': 'Award Golf Skirt 2', + 'sa_gs17': 'Award Racing Skirt 4', + 'sa_gs18': 'Award Golf Skirt 3', + 'sa_gs19': 'Award Golf Skirt 4', + 'sa_gs20': 'Award Racing Skirt 5', + 'sa_gs21': 'Award Racing Skirt 6', + 'sc_bs1': 'Scientist bottom male 1', + 'sc_bs2': 'Scientist bottom male 2', + 'sc_bs3': 'Scientist bottom male 3', + 'sc_gs1': 'Scientist bottom female 1', + 'sc_gs2': 'Scientist bottom female 2', + 'sc_gs3': 'Scientist bottom female 3', + 'sil_bs1': 'Silly Cog-Crusher Shorts male', + 'sil_gs1': 'Silly Cog-Crusher Shorts female', + 'hw_bs3': 'Halloween Vampire Shorts male', + 'hw_gs3': 'Halloween Vampire Shorts female', + 'hw_bs4': 'Halloween Turtle Shorts male', + 'hw_gs4': 'Halloween Turtle Shorts female', + 'hw_gs1': 'Halloween Bee Shorts female', + 'hw_gs2': 'Halloween Pirate Shorts female', + 'hw_gs5': 'Halloween SuperToon Shorts female', + 'hw_gs6': 'Halloween Vampire NoCape Shorts female', + 'hw_gs7': 'Halloween Dinosaur Shorts female', + 'hw_gsk1': 'Halloween Pirate Skirt'} +AwardMgrBoy = 'boy' +AwardMgrGirl = 'girl' +AwardMgrUnisex = 'unisex' +AwardMgrShorts = 'shorts' +AwardMgrSkirt = 'skirt' +AwardMgrShirt = 'shirt' +SpecialEventMailboxStrings = {1: 'A special item from the Toon Council just for you!', + 2: "Here is your Melville's Fishing Tournament prize! Congratulations!", + 3: "Here is your Billy Budd's Fishing Tournament prize! Congratulations!", + 4: 'Here is your Acorn Acres April Invitational prize! Congratulations!', + 5: 'Here is your Acorn Acres C.U.P. Championship prize! Congratulations!', + 6: 'Here is your Gift-Giving Extravaganza prize! Congratulations!', + 7: "Here is your Top Toons New Year's Day Marathon prize! Congratulations!", + 8: 'Here is your Perfect Trolley Games Weekend prize! Congratulations!', + 9: 'Here is your Trolley Games Madness prize! Congratulations!', + 10: 'Here is your Grand Prix Weekend prize! Congratulations!', + 11: 'Here is your ToonTask Derby prize! Congratulations!', + 12: 'Here is your Save a Building Marathon prize! Congratulations!', + 13: 'Here is your Most Cogs Defeated Tournament prize! Congratulations!', + 14: 'Here is your Most V.P.s Defeated Tournament prize! Congratulations!', + 15: 'Here is your Operation: Storm Sellbot prize! Congratulations!', + 16: 'Here is your Most C.J.s Defeated Tournament prize! Congratulations!', + 17: 'Here is your Operation: Lawbots Lose prize! Congratulations!'} +RentalHours = 'Hours' +RentalOf = 'Of' +RentalCannon = 'Cannons!' +RentalGameTable = 'Game Table!' +EstateCannonGameEnd = 'The Cannon Game rental is over.' +GameTableRentalEnd = 'The Game Table rental is over.' +MessageConfirmRent = 'Begin rental? Cancel to save the rental for later' +MessageConfirmGarden = 'Are you sure you want to start a garden?' +NametagPaid = 'Citizen Name Tag' +NametagAction = 'Action Name Tag' +NametagFrilly = 'Frilly Name Tag' +FurnitureYourOldCloset = 'your old wardrobe' +FurnitureYourOldBank = 'your old bank' +FurnitureYourOldTrunk = 'your old trunk' +TrunkHatGUI = 'Hats' +TrunkGlassesGUI = 'Glasses' +TrunkBackpackGUI = 'Backpacks' +TrunkShoesGUI = 'Shoes' +ChatItemQuotes = '"%s"' +FurnitureNames = {100: 'Armchair', + 105: 'Armchair', + 110: 'Chair', + 120: 'Desk Chair', + 130: 'Log Chair', + 140: 'Lobster Chair', + 145: 'Lifejacket Chair', + 150: 'Saddle Stool', + 160: 'Native Chair', + 170: 'Cupcake Chair', + 200: 'Bed', + 205: 'Bed', + 210: 'Bed', + 220: 'Bathtub Bed', + 230: 'Leaf Bed', + 240: 'Boat Bed', + 250: 'Cactus Hammock', + 260: 'Ice Cream Bed', + 270: "Olivia Erin & Cat's Bed", + 300: 'Player Piano', + 310: 'Pipe Organ', + 400: 'Fireplace', + 410: 'Fireplace', + 420: 'Round Fireplace', + 430: 'Fireplace', + 440: 'Apple Fireplace', + 450: "Erin's Fireplace", + 460: "Erin's Lit Fireplace", + 470: 'Lit Fireplace', + 480: 'Round Lit Fireplace', + 490: 'Lit Fireplace', + 491: 'Lit Fireplace', + 492: 'Apple Lit Fireplace', + 500: 'Wardrobe', + 502: '15 item Wardrobe', + 504: '20 item Wardrobe', + 506: '25 item Wardrobe', + 508: '50 item Wardrobe', + 510: 'Wardrobe', + 512: '15 item Wardrobe', + 514: '20 item Wardrobe', + 516: '25 item Wardrobe', + 518: '50 item Wardrobe', + 600: 'Short Lamp', + 610: 'Tall Lamp', + 620: 'Table Lamp', + 625: 'Table Lamp', + 630: 'Daisy Lamp', + 640: 'Daisy Lamp', + 650: 'Jellyfish Lamp', + 660: 'Jellyfish Lamp', + 670: 'Cowboy Lamp', + 680: 'Candle', + 681: 'Lit Candle', + 700: 'Cushioned Chair', + 705: 'Cushioned Chair', + 710: 'Couch', + 715: 'Couch', + 720: 'Hay Couch', + 730: 'Shortcake Couch', + 800: 'Desk', + 810: 'Log Desk', + 900: 'Umbrella Stand', + 910: 'Coat Rack', + 920: 'Trash Can', + 930: 'Red Mushroom', + 940: 'Yellow Mushroom', + 950: 'Coat Rack', + 960: 'Barrel Stand', + 970: 'Cactus Plant', + 980: 'Teepee', + 990: "Juliette's Fan", + 1000: 'Large Rug', + 1010: 'Round Rug', + 1015: 'Round Rug', + 1020: 'Small Rug', + 1030: 'Leaf Mat', + 1040: 'Presents', + 1050: 'Sled', + 1100: 'Display Cabinet', + 1110: 'Display Cabinet', + 1120: 'Tall Bookcase', + 1130: 'Low Bookcase', + 1140: 'Sundae Chest', + 1200: 'End Table', + 1210: 'Small Table', + 1215: 'Small Table', + 1220: 'Coffee Table', + 1230: 'Coffee Table', + 1240: "Snorkeler's Table", + 1250: 'Cookie Table', + 1260: 'Bedroom Table', + 1300: '1000 Bean Bank', + 1310: '2500 Bean Bank', + 1320: '5000 Bean Bank', + 1330: '7500 Bean Bank', + 1340: '10000 Bean Bank', + 1350: '12000 Bean Bank', + 1399: 'Telephone', + 1400: 'Cezanne Toon', + 1410: 'Flowers', + 1420: 'Modern Mickey', + 1430: 'Rembrandt Toon', + 1440: 'Toonscape', + 1441: "Whistler's Horse", + 1442: 'Toon Star', + 1443: 'Not a Pie', + 1450: 'Mickey and Minnie', + 1500: 'Radio', + 1510: 'Radio', + 1520: 'Radio', + 1530: 'Television', + 1600: 'Short Vase', + 1610: 'Tall Vase', + 1620: 'Short Vase', + 1630: 'Tall Vase', + 1640: 'Short Vase', + 1650: 'Short Vase', + 1660: 'Coral Vase', + 1661: 'Shell Vase', + 1670: 'Rose Vase', + 1680: 'Rose Watercan', + 1700: 'Popcorn Cart', + 1710: 'Ladybug', + 1720: 'Fountain', + 1725: 'Washing Machine', + 1800: 'Fish Bowl', + 1810: 'Fish Bowl', + 1900: 'Swordfish', + 1910: 'Hammerhead', + 1920: 'Hanging Horns', + 1930: 'Simple Sombrero', + 1940: 'Fancy Sombrero', + 1950: 'Dream Catcher', + 1960: 'Horseshoe', + 1970: 'Bison Portrait', + 2000: 'Candy Swing Set', + 2010: 'Cake Slide', + 3000: 'Banana Split Tub', + 4000: 'Boy Trunk', + 4010: 'Girl Trunk', + 10000: 'Short Pumpkin', + 10010: 'Tall Pumpkin', + 10020: 'Winter Tree', + 10030: 'Winter Wreath'} +AwardManagerFurnitureNames = {100: 'Armchair A - Series 1', + 105: 'Armchair A - Series 7', + 110: 'Chair - Series 1', + 120: 'Desk Chair - Series 2', + 130: 'Log Chair - Series 2', + 140: 'Lobster Chair - Series 3', + 145: 'Lifejacket Chair - Series 3', + 150: 'Saddle Stool - Series 4', + 160: 'Native Chair - Series 4', + 170: 'Cupcake Chair - Series 6', + 200: "Bed Boy's bed - Initial Furniture", + 205: "Bed Boy's bed Series 7", + 210: "Bed Girl's bed - Series 1", + 220: 'Bathtub Bed', + 230: 'Leaf Bed', + 240: 'Boat Bed', + 250: 'Cactus Hammock', + 260: 'Ice Cream Bed', + 270: "Olivia Erin & Cat's Bed - Trolley Bed", + 300: 'Player Piano', + 310: 'Pipe Organ', + 400: 'Fireplace - Square Fireplace Initial Furniture', + 410: 'Fireplace - Girly Fireplace Series 1', + 420: 'Round Fireplace', + 430: 'Fireplace - bug room series 2', + 440: 'Apple Fireplace', + 450: "Erin's Fireplace - coral", + 460: "Erin's Lit Fireplace - coral", + 470: 'Lit Fireplace - square fireplace with fire', + 480: 'Round Lit Fireplace', + 490: 'Lit Fireplac - girl fireplace with firee', + 491: 'Lit Fireplace - bug room fireplace', + 492: 'Apple Lit Fireplace', + 500: 'boy Wardrobe - 10 items initial', + 502: 'boy 15 item Wardrobe', + 504: 'boy 20 item Wardrobe', + 506: 'boy 25 item Wardrobe', + 508: 'boy 50 item Wardrobe', + 510: 'girl Wardrobe - 10 items initial', + 512: 'girl 15 item Wardrobe', + 514: 'girl 20 item Wardrobe', + 516: 'girl 25 item Wardrobe', + 518: 'girl 50 item Wardrobe', + 600: 'Short Lamp', + 610: 'Tall Lamp', + 620: 'Table Lamp - Series 1', + 625: 'Table Lamp - Series 7', + 630: 'Daisy Lamp 1', + 640: 'Daisy Lamp 2', + 650: 'Jellyfish Lamp 1', + 660: 'Jellyfish Lamp 2', + 670: 'Cowboy Lamp', + 680: 'Candle', + 681: 'Lit Candle', + 700: 'Cushioned Chair - Series 1', + 705: 'Cushioned Chair - Series 7', + 710: 'Couch - series 1', + 715: 'Couch - series 7', + 720: 'Hay Couch', + 730: 'Shortcake Couch', + 800: 'Desk', + 810: 'Log Desk', + 900: 'Umbrella Stand', + 910: 'Coat Rack - series 1', + 920: 'Trash Can', + 930: 'Red Mushroom', + 940: 'Yellow Mushroom', + 950: 'Coat Rack - underwater', + 960: 'Barrel Stand', + 970: 'Cactus Plant', + 980: 'Teepee', + 990: "Juliette's Fan - gag fan", + 1000: 'Large Rug', + 1010: 'Round Rug - Series 1', + 1015: 'Round Rug - Series 7', + 1020: 'Small Rug', + 1030: 'Leaf Mat', + 1040: 'Presents', + 1050: 'Sled', + 1100: 'Display Cabinet - Red', + 1110: 'Display Cabinet - Yellow', + 1120: 'Tall Bookcase', + 1130: 'Low Bookcase', + 1140: 'Sundae Chest', + 1200: 'End Table', + 1210: 'Small Table - series 1 ', + 1215: 'Small Table - series 7', + 1220: 'Coffee Table sq', + 1230: 'Coffee Table bw', + 1240: "Snorkeler's Table", + 1250: 'Cookie Table', + 1260: 'Bedroom Table', + 1300: '1000 Bean Bank', + 1310: '2500 Bean Bank', + 1320: '5000 Bean Bank', + 1330: '7500 Bean Bank', + 1340: '10000 Bean Bank', + 1350: '12000 Bean Bank', + 1399: 'Telephone', + 1400: 'Cezanne Toon', + 1410: 'Flowers', + 1420: 'Modern Mickey', + 1430: 'Rembrandt Toon', + 1440: 'Toonscape', + 1441: "Whistler's Horse", + 1442: 'Toon Star', + 1443: 'Not a Pie', + 1450: 'Mickey and Minnie', + 1500: 'Radio A series 2', + 1510: 'Radio B series 1', + 1520: 'Radio C series 2', + 1530: 'Television', + 1600: 'Short Vase A', + 1610: 'Tall Vase A', + 1620: 'Short Vase B', + 1630: 'Tall Vase B', + 1640: 'Short Vase C', + 1650: 'Short Vase D', + 1660: 'Coral Vase', + 1661: 'Shell Vase', + 1670: 'Rose Vase', + 1680: 'Rose Watercan', + 1700: 'Popcorn Cart', + 1710: 'Ladybug', + 1720: 'Fountain', + 1725: 'Washing Machine', + 1800: 'Fish Bowl skull', + 1810: 'Fish Bowl lizard', + 1900: 'Swordfish', + 1910: 'Hammerhead', + 1920: 'Hanging Horns', + 1930: 'Simple Sombrero', + 1940: 'Fancy Sombrero', + 1950: 'Dream Catcher', + 1960: 'Horseshoe', + 1970: 'Bison Portrait', + 2000: 'Candy Swing Set', + 2010: 'Cake Slide', + 3000: 'Banana Split Tub', + 4000: 'Boy Trunk', + 4010: 'Girl Trunk', + 10000: 'Short Pumpkin', + 10010: 'Tall Pumpkin', + 10020: 'Winter Tree', + 10030: 'Winter Wreath'} +ClothingArticleNames = ('Shirt', + 'Shirt', + 'Shirt', + 'Shorts', + 'Shorts', + 'Skirt', + 'Shorts') +ClothingTypeNames = {1001: 'Ghost Shirt', + 1002: 'Pumpkin Shirt', + 1112: 'Bee Shirt', + 1113: 'Pirate Shirt', + 1114: 'Super Toon Shirt', + 1115: 'Vampire Shirt', + 1116: 'Toonosaur Shirt', + 1117: 'Bee Shorts', + 1118: 'Pirate Shorts', + 1119: 'Super Toon Shorts', + 1120: 'Vampire Shorts', + 1121: 'Toonosaur Shorts', + 1122: 'Bee Shorts', + 1123: 'Pirate Shorts', + 1124: 'Super Toon Shorts', + 1125: 'Vampire Shorts', + 1126: 'Toonosaur Shorts', + 1127: 'Pirate Skirt', + 1304: "O'Shirt", + 1305: "O'Shorts", + 1306: "O'Skirt", + 1400: "Matthew's Shirt", + 1401: "Jessica's Shirt", + 1402: "Marissa's Shirt", + 1600: 'Trap Outfit', + 1601: 'Sound Outfit', + 1602: 'Lure Outfit', + 1603: 'Trap Outfit', + 1604: 'Sound Outfit', + 1605: 'Lure Outfit', + 1606: 'Trap Outfit', + 1607: 'Sound Outfit', + 1608: 'Lure Outfit', + 1723: 'Bee Shirt', + 1724: 'SuperToon Shirt', + 1734: 'Bee Shorts', + 1735: 'SuperToon Shorts', + 1739: 'Bee Skirt', + 1740: 'SuperToon Skirt', + 1743: 'Skeleton Shirt', + 1744: 'Spider Shirt', + 1745: 'Spider Shorts', + 1746: 'Skeleton Shorts', + 1747: 'Skeleton Skirt', + 1748: 'Spider Skirt', + 1749: 'Silly Mailbox Shirt', + 1750: 'Silly Trash Can Shirt', + 1751: 'Loony Labs Shirt', + 1752: 'Silly Hydrant Shirt', + 1753: 'Silly Meter Shirt', + 1754: 'Cog-Crusher Shirt', + 1755: 'Cog-Crusher Shorts', + 1756: 'Cog-Crusher Shorts', + 1757: 'Victory Party Shirt', + 1758: 'Relaxed Victory Shirt', + 1763: 'Smashed Sellbot Shirt', + 1764: 'Most V.P.s Defeated Shirt', + 1765: 'Sellbot Smasher Shirt', + 1766: 'Sellbot Smasher Shorts', + 1767: 'Sellbot Smasher Shorts', + 1768: 'Jellybean Bank Shirt', + 1769: 'Doodle Shirt', + 1770: 'Vampire Shirt', + 1771: 'Turtle Shirt', + 1772: 'Vampire Shorts', + 1773: 'Vampire Shorts', + 1774: 'Turtle Shorts', + 1775: 'Turtle Shorts', + 1776: 'Get Connected Mover & Shaker Shirt', + 1777: 'Smashed Lawbot Shirt', + 1778: 'Most C.J.s Defeated Shirt', + 1779: 'Lawbot Smasher Shirt', + 1780: 'Lawbot Smasher Shorts', + 1781: 'Lawbot Smasher Shorts', + 1782: 'Racing Shirt 3', + 1783: 'Racing Shorts 1', + 1784: 'Racing Skirt 1', + 1801: 'Batty Moon Shirt', + 1802: 'Mittens Shirt'} +AccessoryArticleNames = ('Hat', + 'Glasses', + 'Backpack', + 'Shoes', + 'Hat', + 'Glasses', + 'Backpack', + 'Shoes', + 'Hat', + 'Glasses', + 'Backpack', + 'Shoes') +SurfaceNames = ('Wallpaper', + 'Moulding', + 'Flooring', + 'Wainscoting', + 'Border') +WallpaperNames = {1000: 'Parchment', + 1100: 'Milan', + 1200: 'Dover', + 1300: 'Victoria', + 1400: 'Newport', + 1500: 'Pastoral', + 1600: 'Harlequin', + 1700: 'Moon', + 1800: 'Stars', + 1900: 'Flowers', + 2000: 'Spring Garden', + 2100: 'Formal Garden', + 2200: 'Race Day', + 2300: 'Touchdown!', + 2400: 'Cloud 9', + 2500: 'Climbing Vine', + 2600: 'Springtime', + 2700: 'Kokeshi', + 2800: 'Posies', + 2900: 'Angel Fish', + 3000: 'Bubbles', + 3100: 'Bubbles', + 3200: 'Go Fish', + 3300: 'Stop Fish', + 3400: 'Sea Horse', + 3500: 'Sea Shells', + 3600: 'Underwater', + 3700: 'Boots', + 3800: 'Cactus', + 3900: 'Cowboy Hat', + 10100: 'Cats', + 10200: 'Bats', + 11000: 'Snowflakes', + 11100: 'Hollyleaf', + 11200: 'Snowman', + 12000: 'ValenToons', + 12100: 'ValenToons', + 12200: 'ValenToons', + 12300: 'ValenToons', + 13000: 'Shamrock', + 13100: 'Shamrock', + 13200: 'Rainbow', + 13300: 'Shamrock'} +FlooringNames = {1000: 'Hardwood Floor', + 1010: 'Carpet', + 1020: 'Diamond Tile', + 1030: 'Diamond Tile', + 1040: 'Grass', + 1050: 'Beige Bricks', + 1060: 'Red Bricks', + 1070: 'Square Tile', + 1080: 'Stone', + 1090: 'Boardwalk', + 1100: 'Dirt', + 1110: 'Wood Tile', + 1120: 'Tile', + 1130: 'Honeycomb', + 1140: 'Water', + 1150: 'Beach Tile', + 1160: 'Beach Tile', + 1170: 'Beach Tile', + 1180: 'Beach Tile', + 1190: 'Sand', + 10000: 'Ice Cube', + 10010: 'Igloo', + 11000: 'Shamrock', + 11010: 'Shamrock'} +MouldingNames = {1000: 'Knotty', + 1010: 'Painted', + 1020: 'Dental', + 1030: 'Flowers', + 1040: 'Flowers', + 1050: 'Ladybug', + 1060: 'ValenToons', + 1070: 'Beach', + 1080: 'Winter Lights 1', + 1085: 'Winter Lights 2', + 1090: 'Winter Lights 3', + 1100: "ValenToon's Cupid", + 1110: "ValenToon's Heart 1", + 1120: "ValenToon's Heart 2"} +WainscotingNames = {1000: 'Painted', + 1010: 'Wood Panel', + 1020: 'Wood', + 1030: 'ValenToons', + 1040: 'Underwater'} +WindowViewNames = {10: 'Large Garden', + 20: 'Wild Garden', + 30: 'Greek Garden', + 40: 'Cityscape', + 50: 'Wild West', + 60: 'Under the Sea', + 70: 'Tropical Island', + 80: 'Starry Night', + 90: 'Tiki Pool', + 100: 'Frozen Frontier', + 110: 'Farm Country', + 120: 'Native Camp', + 130: 'Main Street'} +SpecialEventNames = {1: 'Generic Award', + 2: "Melville's Fishing Tournament", + 3: "Billy Budd's Fishing Tournament", + 4: 'Acorn Acres April Invitational', + 5: 'Acorn Acres C.U.P. Championship', + 6: 'Gift-Giving Extravaganza', + 7: "Top Toons New Year's Day Marathon", + 8: 'Perfect Trolley Games Weekend', + 9: 'Trolley Games Madness', + 10: 'Grand Prix Weekend', + 11: 'ToonTask Derby', + 12: 'Save a Building Marathon', + 13: 'Most Cogs Defeated', + 14: 'Most V.P.s Defeated', + 15: 'Operation Storm Sellbot Event', + 16: 'Most C.J.s Defeated', + 17: 'Operation Lawbots Lose Event'} +NewCatalogNotify = 'There are new items available to order at your phone!' +NewDeliveryNotify = 'A new delivery has just arrived at your mailbox!' +CatalogNotifyFirstCatalog = 'Your first cattlelog has arrived! You may use this to order new items for yourself or for your house.' +CatalogNotifyNewCatalog = 'Your cattlelog #%s has arrived! You can go to your phone to order items from this cattlelog.' +CatalogNotifyNewCatalogNewDelivery = 'A new delivery has arrived at your mailbox! Also, your cattlelog #%s has arrived!' +CatalogNotifyNewDelivery = 'A new delivery has arrived at your mailbox!' +CatalogNotifyNewCatalogOldDelivery = 'Your cattlelog #%s has arrived, and there are still items waiting in your mailbox!' +CatalogNotifyOldDelivery = 'There are still items waiting in your mailbox for you to pick up!' +CatalogNotifyInstructions = 'Click the "Go home" button on the map page in your Shticker Book, then walk up to the phone inside your house.' +CatalogNewDeliveryButton = 'New\nDelivery!' +CatalogNewCatalogButton = 'New\nCattlelog' +CatalogSaleItem = 'Sale! ' +DistributedMailboxEmpty = 'Your mailbox is empty right now. Come back here to look for deliveries after you place an order from your phone!' +DistributedMailboxWaiting = 'Your mailbox is empty right now, but the package you ordered is on its way. Check back later!' +DistributedMailboxReady = 'Your order has arrived!' +DistributedMailboxNotOwner = 'Sorry, this is not your mailbox.' +DistributedPhoneEmpty = "You can use any phone to order special items for you and your house. New items will become available to order over time.\n\nYou don't have any items available to order right now, but check back later!" +Clarabelle = 'Clarabelle' +MailboxExitButton = 'Close Mailbox' +MailboxAcceptButton = 'Take this item' +MailBoxDiscard = 'Discard this item' +MailboxAcceptInvite = 'Accept this invite' +MailBoxRejectInvite = 'Reject this invite' +MailBoxDiscardVerify = 'Are you sure you want to Discard %s?' +MailBoxRejectVerify = 'Are you sure you want to Reject %s?' +MailboxOneItem = 'Your mailbox contains 1 item.' +MailboxNumberOfItems = 'Your mailbox contains %s items.' +MailboxGettingItem = 'Taking %s from mailbox.' +MailboxGiftTag = 'Gift From: %s' +MailboxGiftTagAnonymous = 'Anonymous' +MailboxItemNext = 'Next\nItem' +MailboxItemPrev = 'Previous\nItem' +MailboxDiscard = 'Discard' +MailboxReject = 'Reject' +MailboxLeave = 'Keep' +CatalogCurrency = 'beans' +CatalogHangUp = 'Hang Up' +CatalogNew = 'NEW' +CatalogBackorder = 'BACKORDER' +CatalogLoyalty = 'SPECIAL' +CatalogEmblem = 'EMBLEM' +CatalogPagePrefix = 'Page' +CatalogGreeting = "Hello! Thanks for calling Clarabelle's Cattlelog. Can I help you?" +CatalogGoodbyeList = ['Bye now!', + 'Call back soon!', + 'Thanks for calling!', + 'Ok, bye now!', + 'Bye!'] +CatalogHelpText1 = 'Turn the page to see items for sale.' +CatalogSeriesLabel = 'Series %s' +CatalogGiftFor = 'Buy Gift for:' +CatalogGiftTo = 'To: %s' +CatalogGiftToggleOn = 'Stop Gifting' +CatalogGiftToggleOff = 'Buy Gifts' +CatalogGiftToggleWait = 'Trying!...' +CatalogGiftToggleNoAck = 'Unavailable' +CatalogPurchaseItemAvailable = 'Congratulations on your new purchase! You can start using it right away.' +CatalogPurchaseGiftItemAvailable = 'Excellent! %s can start using your gift right away.' +CatalogPurchaseItemOnOrder = 'Congratulations! Your purchase will be delivered to your mailbox soon.' +CatalogPurchaseGiftItemOnOrder = 'Excellent! Your gift to %s will be delivered to their mailbox.' +CatalogAnythingElse = 'Anything else I can get you today?' +CatalogPurchaseClosetFull = 'Your closet is full. You may purchase this item anyway, but if you do you will need to delete something from your closet to make room for it when it arrives.\n\nDo you still want to purchase this item?' +CatalogPurchaseNoTrunk = 'In order to wear this item, you need to buy a trunk.\n\nDo you still want to purchase this item?' +CatalogPurchaseTrunkFull = 'Your trunk is full. If you purchase this item, you\xe2\x80\x99ll need to delete another item from your trunk to make more room.\n\nDo you still want to purchase this item?' +CatalogAcceptClosetFull = 'Your closet is full. You must go inside and delete something from your closet to make room for this item before you can take it out of your mailbox.' +CatalogAcceptNoTrunk = "You don't have a trunk. You must buy a trunk before you can take this item out of your mailbox." +CatalogAcceptTrunkFull = 'Your trunk is full. You must delete something from your trunk before you can take this item out of your mailbox.' +CatalogAcceptShirt = 'You are now wearing your new hat. The hat you were wearing before has been moved to your trunk.' +CatalogAcceptShorts = 'You are now wearing your new shorts. What you were wearing before has been moved to your closet.' +CatalogAcceptSkirt = 'You are now wearing your new skirt. What you were wearing before has been moved to your closet.' +CatalogAcceptHat = 'You are now wearing your new hat. The hat you were wearing before has been moved to your trunk.' +CatalogAcceptGlasses = 'You are now wearing your new glasses. The glasses you were wearing before have been moved to your trunk.' +CatalogAcceptBackpack = 'You are now wearing your new backpack. The backpack you were wearing before has been moved to your trunk.' +CatalogAcceptShoes = 'You are now wearing your new shoes. The shoes you were wearing before have been moved to your trunk.' +CatalogAcceptPole = "You're now ready to go catch some bigger fish with your new pole!" +CatalogAcceptPoleUnneeded = 'You already have a better pole than this one!' +CatalogAcceptChat = 'You now have a new SpeedChat!' +CatalogAcceptEmote = 'You now have a new Emotion!' +CatalogAcceptBeans = 'You received some jelly beans!' +CatalogAcceptRATBeans = 'Your Toon recruit reward has arrived!' +CatalogAcceptPartyRefund = "Your party was never started. Here's your refund!" +CatalogAcceptNametag = 'Your new name tag has arrived!' +CatalogAcceptGarden = 'Your garden supplies have arrived!' +CatalogAcceptPet = 'You now have a new Pet Trick!' +CatalogPurchaseHouseFull = 'Your house is full. You may purchase this item anyway, but if you do you will need to delete something from your house to make room for it when it arrives.\n\nDo you still want to purchase this item?' +CatalogAcceptHouseFull = 'Your house is full. You can not accept this item until you free up some room. Would you like to discard this item now?' +CatalogAcceptInAttic = 'Your new item is now in your attic. You can put it in your house by going inside and clicking on the "Move Furniture" button.' +CatalogAcceptInAtticP = 'Your new items are now in your attic. You can put them in your house by going inside and clicking on the "Move Furniture" button.' +CatalogPurchaseMailboxFull = "Your mailbox is full! You can't purchase this item until you take some items out of your mailbox to make room." +CatalogPurchaseGiftMailboxFull = "%s's mailbox is full! You can't purchase this item." +CatalogPurchaseOnOrderListFull = "You have too many items currently on order. You can't order any more items until some of the ones you have already ordered arrive." +CatalogPurchaseGiftOnOrderListFull = '%s has too many items currently on order.' +CatalogPurchaseGeneralError = 'The item could not be purchased because of some internal game error: error code %s.' +CatalogPurchaseGiftGeneralError = 'The item could not be gifted to %(friend)s because of some internal game error: error code %(error)s.' +CatalogPurchaseGiftNotAGift = 'This item could not be sent to %s because it would be an unfair advantage.' +CatalogPurchaseGiftWillNotFit = "This item could not be sent to %s because it doesn't fit them." +CatalogPurchaseGiftLimitReached = "This item could not be sent to %s because they've already have it." +CatalogPurchaseGiftNotEnoughMoney = "This item could not be sent to %s because you can't afford it." +CatalogAcceptGeneralError = 'The item could not be removed from your mailbox because of some internal game error: error code %s.' +CatalogAcceptRoomError = "You don't have any place to put this. You'll have to get rid of something." +CatalogAcceptLimitError = "You already have as many of these as you can handle. You'll have to get rid of something." +CatalogAcceptFitError = "This won't fit you!" +CatalogAcceptInvalidError = 'This item has gone out of style!' +CatalogAcceptClosetError = 'You already have a bigger closet!' +MailboxOverflowButtonDicard = 'Discard' +MailboxOverflowButtonLeave = 'Leave' +HDMoveFurnitureButton = 'Move\nFurniture' +HDStopMoveFurnitureButton = 'Done\nMoving' +HDAtticPickerLabel = 'In the attic' +HDInRoomPickerLabel = 'In the room' +HDInTrashPickerLabel = 'In the trash' +HDDeletePickerLabel = 'Delete?' +HDInAtticLabel = 'Attic' +HDInRoomLabel = 'Room' +HDInTrashLabel = 'Trash' +HDToAtticLabel = 'Send\nto attic' +HDMoveLabel = 'Move' +HDRotateCWLabel = 'Rotate Right' +HDRotateCCWLabel = 'Rotate Left' +HDReturnVerify = 'Return this item to the attic?' +HDReturnFromTrashVerify = 'Return this item to the attic from the trash?' +HDDeleteItem = 'Click OK to send this item to the trash, or Cancel to keep it.' +HDNonDeletableItem = "You can't delete items of this type!" +HDNonDeletableBank = "You can't delete your bank!" +HDNonDeletableCloset = "You can't delete your wardrobe!" +HDNonDeletablePhone = "You can't delete your phone!" +HDNonDeletableTrunk = "You can't delete your trunk!" +HDNonDeletableNotOwner = "You can't delete %s's things!" +HDHouseFull = 'Your house is full. You have to delete something else from your house or attic before you can return this item from the trash.' +HDHelpDict = {'DoneMoving': 'Finish room decorating.', + 'Attic': 'Show list of items in attic. The attic stores items that are not in your room.', + 'Room': 'Show list of items in room. Useful for finding lost items.', + 'Trash': 'Show items in trash. Oldest items are deleted after a while or when trash overflows.', + 'ZoomIn': 'Get a closer view of room.', + 'ZoomOut': 'Get a farther view of room.', + 'SendToAttic': 'Send the current furniture item to attic for storage.', + 'RotateLeft': 'Turn left.', + 'RotateRight': 'Turn right.', + 'DeleteEnter': 'Change to delete mode.', + 'DeleteExit': 'Exit delete mode.', + 'FurnitureItemPanelDelete': 'Send %s to trash.', + 'FurnitureItemPanelAttic': 'Place %s in room.', + 'FurnitureItemPanelRoom': 'Return %s to attic.', + 'FurnitureItemPanelTrash': 'Return %s to attic.'} +MessagePickerTitle = 'You have too many phrases. In order to purchase\n"%s"\n you must choose one to remove:' +MessagePickerCancel = lCancel +MessageConfirmDelete = 'Are you sure you want to remove "%s" from your SpeedChat menu?' +CatalogBuyText = 'Buy' +CatalogRentText = 'Rent' +CatalogGiftText = 'Gift' +CatalogOnOrderText = 'On Order' +CatalogPurchasedText = 'Already\nPurchased' +CatalogCurrent = 'Current' +CatalogGiftedText = 'Gifted\nTo You' +CatalogPurchasedGiftText = 'Already\nOwned' +CatalogMailboxFull = 'No Room' +CatalogNotAGift = 'Not a Gift' +CatalogNoFit = "Doesn't\nFit" +CatalogMembersOnly = 'Members\nOnly!' +CatalogSndOnText = 'Snd On' +CatalogSndOffText = 'Snd Off' +CatalogPurchasedMaxText = 'Already\nPurchased Max' +CatalogVerifyPurchase = 'Purchase %(item)s for %(price)s jellybeans?' +CatalogVerifyPurchaseBeanSilverGold = 'Purchase %(item)s for %(price)s jellybeans, %(silver)s silver emblems and %(gold)s gold emblems?' +CatalogVerifyPurchaseBeanGold = 'Purchase %(item)s for %(price)s jellybeans and %(gold)s gold emblems?' +CatalogVerifyPurchaseBeanSilver = 'Purchase %(item)s for %(price)s jellybeans and %(silver)s silver emblems?' +CatalogVerifyPurchaseSilverGold = 'Purchase %(item)s for %(silver)s silver emblems and %(gold)s gold emblems?' +CatalogVerifyPurchaseSilver = 'Purchase %(item)s for %(silver)s silver emblems?' +CatalogVerifyPurchaseGold = 'Purchase %(item)s for %(gold)s gold emblems?' +CatalogVerifyRent = 'Rent %(item)s for %(price)s jellybeans?' +CatalogVerifyGift = 'Purchase %(item)s for %(price)s jellybeans as a gift for %(friend)s?' +CatalogOnlyOnePurchase = 'You may only have one of these items at a time. If you purchase this one, it will replace %(old)s.\n\nAre you sure you want to purchase %(item)s for %(price)s jellybeans?' +CatalogExitButtonText = 'Hang Up' +CatalogCurrentButtonText = 'To Current Items' +CatalogPastButtonText = 'To Past Items' +TutorialHQOfficerName = 'HQ Harry' +NPCToonNames = {20000: 'Tutorial Tom', + 999: 'Toon Tailor', + 1000: lToonHQ, + 20001: Flippy, + 2001: Flippy, + 2002: 'Banker Bob', + 2003: 'Professor Pete', + 2004: 'Tammy the Tailor', + 2005: 'Librarian Larry', + 2006: 'Clerk Clark', + 2011: 'Clerk Clara', + 2007: lHQOfficerM, + 2008: lHQOfficerM, + 2009: lHQOfficerF, + 2010: lHQOfficerF, + 2012: 'Fisherman Freddy', + 2018: 'Duff..err..TIP Man', + 2013: 'Clerk Poppy', + 2014: 'Clerk Peppy', + 2015: 'Clerk Pappy', + 2016: 'Party Planner Pumpkin', + 2017: 'Party Planner Polly', + 2018: 'Doctor Surlee', + 2019: 'Doctor Dimm', + 2020: 'Professor Prepostera', + 2101: 'Dentist Daniel', + 2102: 'Sheriff Sherry', + 2103: 'Sneezy Kitty', + 2104: lHQOfficerM, + 2105: lHQOfficerM, + 2106: lHQOfficerF, + 2107: lHQOfficerF, + 2108: 'Canary Coalmine', + 2109: 'Babbles Blowhard', + 2110: 'Bill Board', + 2111: 'Dancing Diego', + 2112: 'Dr. Tom', + 2113: 'Rollo The Amazing', + 2114: 'Roz Berry', + 2115: 'Patty Papercut', + 2116: 'Bruiser McDougal', + 2117: 'Ma Putrid', + 2118: 'Jesse Jester', + 2119: 'Honey Haha', + 2120: 'Professor Binky', + 2121: 'Madam Chuckle', + 2122: 'Harry Ape', + 2123: 'Spamonia Biggles', + 2124: 'T.P. Rolle', + 2125: 'Lazy Hal', + 2126: 'Professor Guffaw', + 2127: 'Woody Nickel', + 2128: 'Loony Louis', + 2129: 'Frank Furter', + 2130: 'Joy Buzzer', + 2131: 'Feather Duster', + 2132: 'Daffy Don', + 2133: 'Dr. Euphoric', + 2134: 'Silent Simone', + 2135: 'Mary', + 2136: 'Sal Snicker', + 2137: 'Happy Heikyung', + 2138: 'Muldoon', + 2139: 'Dan Dribbles', + 2140: 'Fisherman Billy', + 2201: 'Postmaster Pete', + 2202: 'Shirley U. Jest', + 2203: lHQOfficerM, + 2204: lHQOfficerM, + 2205: lHQOfficerF, + 2206: lHQOfficerF, + 2207: 'Will Wiseacre', + 2208: 'Sticky Lou', + 2209: 'Charlie Chortle', + 2210: 'Tee Hee', + 2211: 'Sally Spittake', + 2212: 'Weird Warren', + 2213: 'Lucy Tires', + 2214: 'Sam Stain', + 2215: 'Sid Seltzer', + 2216: 'Nona Seeya', + 2217: 'Sharky Jones', + 2218: 'Fanny Pages', + 2219: 'Chef Knucklehead', + 2220: 'Rick Rockhead', + 2221: 'Clovinia Cling', + 2222: 'Shorty Fuse', + 2223: 'Sasha Sidesplitter', + 2224: 'Smokey Joe', + 2225: 'Fisherman Droopy', + 2301: 'Dr. Pulyurleg', + 2302: 'Professor Wiggle', + 2303: 'Nurse Nancy', + 2304: lHQOfficerM, + 2305: lHQOfficerM, + 2306: lHQOfficerF, + 2307: lHQOfficerF, + 2308: 'Nancy Gas', + 2309: 'Big Bruce', + 2311: 'Franz Neckvein', + 2312: 'Dr. Sensitive', + 2313: 'Lucy Shirtspot', + 2314: 'Ned Slinger', + 2315: 'Chewy Morsel', + 2316: 'Cindy Sprinkles', + 2318: 'Tony Maroni', + 2319: 'Zippy', + 2320: 'Crunchy Alfredo', + 2321: 'Fisherman Punchy', + 1001: 'Clerk Will', + 1002: 'Clerk Bill', + 1003: lHQOfficerM, + 1004: lHQOfficerF, + 1005: lHQOfficerM, + 1006: lHQOfficerF, + 1007: 'Longjohn Leroy', + 1008: 'Fisherman Furball', + 1009: 'Clerk Barky', + 1010: 'Clerk Purr', + 1011: 'Clerk Bloop', + 1012: 'Party Planner Pickles', + 1013: 'Party Planner Patty', + 1101: 'Billy Budd', + 1102: 'Captain Carl', + 1103: 'Fishy Frank', + 1104: 'Doctor Squall', + 1105: 'Admiral Hook', + 1106: 'Mrs. Starch', + 1107: 'Cal Estenicks', + 1108: lHQOfficerM, + 1109: lHQOfficerF, + 1110: lHQOfficerM, + 1111: lHQOfficerF, + 1112: 'Gary Glubglub', + 1113: 'Lisa Luff', + 1114: 'Charlie Chum', + 1115: 'Sheila Squid, Atty', + 1116: 'Barnacle Bessie', + 1117: 'Captain Yucks', + 1118: 'Choppy McDougal', + 1121: 'Linda Landlubber', + 1122: 'Salty Stan', + 1123: 'Electra Eel', + 1124: 'Flappy Docksplinter', + 1125: 'Eileen Overboard', + 1126: 'Fisherman Barney', + 1201: 'Barnacle Barbara', + 1202: 'Art', + 1203: 'Ahab', + 1204: 'Rocky Shores', + 1205: lHQOfficerM, + 1206: lHQOfficerF, + 1207: lHQOfficerM, + 1208: lHQOfficerF, + 1209: 'Professor Plank', + 1210: 'Gang Wei', + 1211: 'Wynn Bag', + 1212: 'Toby Tonguestinger', + 1213: 'Dante Dolphin', + 1214: 'Gusty Kate', + 1215: 'Dinah Down', + 1216: 'Rod Reel', + 1217: 'CC Weed', + 1218: 'Pacific Tim', + 1219: 'Brian Beachead', + 1220: 'Carla Canal', + 1221: 'Blisters McKee', + 1222: 'Shep Ahoy', + 1223: 'Sid Squid', + 1224: 'Emily Eel', + 1225: 'Bonzo Bilgepump', + 1226: 'Heave Ho', + 1227: 'Coral Reef', + 1228: 'Fisherman Reed', + 1301: 'Alice', + 1302: 'Melville', + 1303: 'Claggart', + 1304: 'Svetlana', + 1305: lHQOfficerM, + 1306: lHQOfficerF, + 1307: lHQOfficerM, + 1308: lHQOfficerF, + 1309: 'Seafoam', + 1310: 'Ted Tackle', + 1311: 'Topsy Turvey', + 1312: 'Ethan Keel', + 1313: 'William Wake', + 1314: 'Rusty Ralph', + 1315: 'Doctor Drift', + 1316: 'Wilma Wobble', + 1317: 'Paula Pylon', + 1318: 'Dinghy Dan', + 1319: 'Davey Drydock', + 1320: 'Ted Calm', + 1321: 'Dinah Docker', + 1322: 'Whoopie Cushion', + 1323: 'Stinky Ned', + 1324: 'Pearl Diver', + 1325: 'Ned Setter', + 1326: 'Felicia Chips', + 1327: 'Cindy Splat', + 1328: 'Fred Flounder', + 1329: 'Shelly Seaweed', + 1330: 'Porter Hole', + 1331: 'Rudy Rudder', + 1332: 'Fisherman Shane', + 3001: 'Betty Freezes', + 3002: lHQOfficerM, + 3003: lHQOfficerF, + 3004: lHQOfficerM, + 3005: lHQOfficerM, + 3006: 'Clerk Lenny', + 3007: 'Clerk Penny', + 3008: 'Warren Bundles', + 3009: 'Fisherman Frizzy', + 3010: 'Clerk Skip', + 3011: 'Clerk Dip', + 3012: 'Clerk Kipp', + 3013: 'Party Planner Pete', + 3014: 'Party Planner Penny', + 3101: 'Mr. Cow', + 3102: 'Auntie Freeze', + 3103: 'Fred', + 3104: 'Bonnie', + 3105: 'Frosty Freddy', + 3106: 'Gus Gooseburger', + 3107: 'Patty Passport', + 3108: 'Toboggan Ted', + 3109: 'Kate', + 3110: 'Chicken Boy', + 3111: 'Snooty Sinjin', + 3112: 'Lil Oldman', + 3113: 'Hysterical Harry', + 3114: 'Henry the Hazard', + 3115: lHQOfficerM, + 3116: lHQOfficerF, + 3117: lHQOfficerM, + 3118: lHQOfficerM, + 3119: 'Creepy Carl', + 3120: 'Mike Mittens', + 3121: 'Joe Shockit', + 3122: 'Lucy Luge', + 3123: 'Frank Lloyd Ice', + 3124: 'Lance Iceberg', + 3125: 'Colonel Crunchmouth', + 3126: 'Colestra Awl', + 3127: 'Ifalla Yufalla', + 3128: 'Sticky George', + 3129: 'Baker Bridget', + 3130: 'Sandy', + 3131: 'Lazy Lorenzo', + 3132: 'Ashy', + 3133: 'Dr. Friezeframe', + 3134: 'Lounge Lassard', + 3135: 'Soggy Nell', + 3136: 'Happy Sue', + 3137: 'Mr. Freeze', + 3138: 'Chef Bumblesoup', + 3139: 'Granny Icestockings', + 3140: 'Fisherman Lucille', + 3201: 'Aunt Arctic', + 3202: 'Shakey', + 3203: 'Walt', + 3204: 'Dr. Ivanna Cee', + 3205: 'Bumpy Noggin', + 3206: 'Vidalia VaVoom', + 3207: 'Dr. Mumbleface', + 3208: 'Grumpy Phil', + 3209: 'Giggles McGhee', + 3210: 'Simian Sam', + 3211: 'Fanny Freezes', + 3212: 'Frosty Fred', + 3213: lHQOfficerM, + 3214: lHQOfficerF, + 3215: lHQOfficerM, + 3216: lHQOfficerM, + 3217: 'Sweaty Pete', + 3218: 'Blue Lou', + 3219: 'Tom Tandemfrost', + 3220: 'Mr. Sneeze', + 3221: 'Nelly Snow', + 3222: 'Mindy Windburn', + 3223: 'Chappy', + 3224: 'Freida Frostbite', + 3225: 'Blake Ice', + 3226: 'Santa Paws', + 3227: 'Solar Ray', + 3228: 'Wynne Chill', + 3229: 'Hernia Belt', + 3230: 'Balding Benjy', + 3231: 'Choppy', + 3232: 'Fisherman Albert', + 3301: 'Paisley Patches', + 3302: 'Bjorn Bord', + 3303: 'Dr. Peepers', + 3304: 'Eddie the Yeti', + 3305: 'Mack Ramay', + 3306: 'Paula Behr', + 3307: 'Fisherman Fredrica', + 3308: 'Donald Frump', + 3309: 'Bootsy', + 3310: 'Professor Flake', + 3311: 'Connie Ferris', + 3312: 'March Harry', + 3313: lHQOfficerM, + 3314: lHQOfficerF, + 3315: lHQOfficerM, + 3316: lHQOfficerF, + 3317: 'Kissy Krissy', + 3318: 'Johnny Cashmere', + 3319: 'Sam Stetson', + 3320: 'Fizzy Lizzy', + 3321: 'Pickaxe Paul', + 3322: 'Flue Lou', + 3323: 'Dallas Borealis', + 3324: 'Snaggletooth Stu', + 3325: 'Groovy Garland', + 3326: 'Blanche', + 3327: 'Chuck Roast', + 3328: 'Shady Sadie', + 3329: 'Treading Ed', + 4001: 'Molly Molloy', + 4002: lHQOfficerM, + 4003: lHQOfficerF, + 4004: lHQOfficerF, + 4005: lHQOfficerF, + 4006: 'Clerk Doe', + 4007: 'Clerk Ray', + 4008: 'Tailor Harmony', + 4009: 'Fisherman Fanny', + 4010: 'Clerk Chris', + 4011: 'Clerk Neil', + 4012: 'Clerk Westin Girl', + 4013: 'Party Planner Preston', + 4014: 'Party Planner Penelope', + 4101: 'Tom', + 4102: 'Fifi', + 4103: 'Dr. Fret', + 4104: lHQOfficerM, + 4105: lHQOfficerF, + 4106: lHQOfficerF, + 4107: lHQOfficerF, + 4108: 'Cleff', + 4109: 'Carlos', + 4110: 'Metra Gnome', + 4111: 'Tom Hum', + 4112: 'Fa', + 4113: 'Madam Manners', + 4114: 'Offkey Eric', + 4115: 'Barbara Seville', + 4116: 'Piccolo', + 4117: 'Mandy Lynn', + 4118: 'Attendant Abe', + 4119: 'Moe Zart', + 4120: 'Viola Padding', + 4121: 'Gee Minor', + 4122: 'Minty Bass', + 4123: 'Lightning Ted', + 4124: 'Riff Raff', + 4125: 'Melody Wavers', + 4126: 'Mel Canto', + 4127: 'Happy Feet', + 4128: 'Luciano Scoop', + 4129: 'Tootie Twostep', + 4130: 'Metal Mike', + 4131: 'Abraham Armoire', + 4132: 'Lowdown Sally', + 4133: 'Scott Poplin', + 4134: 'Disco Dave', + 4135: 'Sluggo Songbird', + 4136: 'Patty Pause', + 4137: 'Tony Deff', + 4138: 'Cliff Cleff', + 4139: 'Harmony Swell', + 4140: 'Clumsy Ned', + 4141: 'Fisherman Jed', + 4201: 'Tina', + 4202: 'Barry', + 4203: 'Lumber Jack', + 4204: lHQOfficerM, + 4205: lHQOfficerF, + 4206: lHQOfficerF, + 4207: lHQOfficerF, + 4208: 'Hedy', + 4209: 'Corny Canter', + 4211: 'Carl Concerto', + 4212: 'Detective Dirge', + 4213: 'Fran Foley', + 4214: 'Tina Toehooks', + 4215: 'Tim Tailgater', + 4216: 'Gummy Whistle', + 4217: 'Handsome Anton', + 4218: 'Wilma Wind', + 4219: 'Sid Sonata', + 4220: 'Curtis Finger', + 4221: 'Moe Madrigal', + 4222: 'John Doe', + 4223: 'Penny Prompter', + 4224: 'Jungle Jim', + 4225: 'Holly Hiss', + 4226: 'Thelma Throatreacher', + 4227: 'Quiet Francesca', + 4228: 'August Winds', + 4229: 'June Loon', + 4230: 'Julius Wheezer', + 4231: 'Steffi Squeezebox', + 4232: 'Hedly Hymn', + 4233: 'Charlie Carp', + 4234: 'Leed Guitar', + 4235: 'Fisherman Larry', + 4301: 'Yuki', + 4302: 'Anna', + 4303: 'Leo', + 4304: lHQOfficerM, + 4305: lHQOfficerF, + 4306: lHQOfficerF, + 4307: lHQOfficerF, + 4308: 'Tabitha', + 4309: 'Marshall', + 4310: 'Martha Mopp', + 4311: 'Sea Shanty', + 4312: 'Moe Saj', + 4313: 'Dumb Dolph', + 4314: 'Dana Dander', + 4315: 'Karen Clockwork', + 4316: 'Tim Tango', + 4317: 'Stubby Toe', + 4318: 'Bob Marlin', + 4319: 'Rinky Dink', + 4320: 'Cammy Coda', + 4321: 'Luke Lute', + 4322: 'Randy Rythm', + 4323: 'Hanna Hogg', + 4324: 'Ellie', + 4325: 'Banker Bran', + 4326: 'Fran Fret', + 4327: 'Flim Flam', + 4328: 'Wagner', + 4329: 'Telly Prompter', + 4330: 'Quentin', + 4331: 'Mellow Costello', + 4332: 'Ziggy', + 4333: 'Harry', + 4334: 'Fast Freddie', + 4335: 'Fisherman Walden', + 5001: lHQOfficerM, + 5002: lHQOfficerM, + 5003: lHQOfficerF, + 5004: lHQOfficerF, + 5005: 'Clerk Peaches', + 5006: 'Clerk Herb', + 5007: 'Bonnie Blossom', + 5008: 'Fisherman Flora', + 5009: 'Clerk Bo Tanny', + 5010: 'Clerk Tom A. Dough', + 5011: 'Clerk Doug Wood', + 5012: 'Party Planner Pierce', + 5013: 'Party Planner Peggy', + 5101: 'Artie', + 5102: 'Susan', + 5103: 'Bud', + 5104: 'Flutterby', + 5105: 'Jack', + 5106: 'Barber Bjorn', + 5107: 'Postman Felipe', + 5108: 'Innkeeper Janet', + 5109: lHQOfficerM, + 5110: lHQOfficerM, + 5111: lHQOfficerF, + 5112: lHQOfficerF, + 5113: 'Dr. Spud', + 5114: 'Wilt', + 5115: 'Honey Dew', + 5116: 'Vegetable Vern', + 5117: 'Petal', + 5118: 'Pop Corn', + 5119: 'Barry Medly', + 5120: 'Gopher', + 5121: 'Paula Peapod', + 5122: 'Leif Pyle', + 5123: 'Diane Vine', + 5124: 'Soggy Bottom', + 5125: 'Sanjay Splash', + 5126: 'Madam Mum', + 5127: 'Polly Pollen', + 5128: 'Shoshanna Sap', + 5129: 'Fisherman Sally', + 5201: 'Jake', + 5202: 'Cynthia', + 5203: 'Lisa', + 5204: 'Bert', + 5205: 'Dan D. Lion', + 5206: 'Vine Green', + 5207: 'Sofie Squirt', + 5208: 'Samantha Spade', + 5209: lHQOfficerM, + 5210: lHQOfficerM, + 5211: lHQOfficerF, + 5212: lHQOfficerF, + 5213: 'Big Galoot', + 5214: 'Itchie Bumps', + 5215: 'Tammy Tuber', + 5216: 'Stinky Jim', + 5217: 'Greg Greenethumb', + 5218: 'Rocky Raspberry', + 5219: 'Lars Bicep', + 5220: 'Lacy Underalls', + 5221: 'Pink Flamingo', + 5222: 'Whiny Wilma', + 5223: 'Wet Will', + 5224: 'Uncle Bumpkin', + 5225: 'Pamela Puddle', + 5226: 'Pete Moss', + 5227: 'Begonia Biddlesmore', + 5228: 'Digger Mudhands', + 5229: 'Fisherman Lily', + 5301: lHQOfficerM, + 5302: lHQOfficerM, + 5303: lHQOfficerM, + 5304: lHQOfficerM, + 5305: 'Crystal', + 5306: 'S. Cargo', + 5307: 'Fun Gus', + 5308: 'Naggy Nell', + 5309: 'Ro Maine', + 5310: 'Timothy', + 5311: 'Judge McIntosh', + 5312: 'Eugene', + 5313: 'Coach Zucchini', + 5314: 'Aunt Hill', + 5315: 'Uncle Mud', + 5316: 'Uncle Spud', + 5317: 'Detective Lima', + 5318: 'Caesar', + 5319: 'Rose', + 5320: 'April', + 5321: 'Professor Ivy', + 5322: 'Fisherman Rose', + 8001: 'Graham Pree', + 8002: 'Ivona Race', + 8003: 'Anita Winn', + 8004: 'Phil Errup', + 9001: "Snoozin' Susan", + 9002: 'Sleeping Tom', + 9003: 'Drowsy Dennis', + 9004: lHQOfficerF, + 9005: lHQOfficerF, + 9006: lHQOfficerM, + 9007: lHQOfficerM, + 9008: 'Clerk Jill', + 9009: 'Clerk Phil', + 9010: 'Worn Out Waylon', + 9011: 'Fisherman Freud', + 9012: 'Clerk Sarah Snuze', + 9013: 'Clerk Kat Knap', + 9014: 'Clerk R. V. Winkle', + 9015: 'Party Planner Pebbles', + 9016: 'Party Planner Pearl', + 9101: 'Ed', + 9102: 'Big Mama', + 9103: 'P.J.', + 9104: 'Sweet Slumber', + 9105: 'Professor Yawn', + 9106: 'Max', + 9107: 'Snuggles', + 9108: 'Winky Wilbur', + 9109: 'Dreamy Daphne', + 9110: 'Kathy Nip', + 9111: 'Powers Erge', + 9112: 'Lullaby Lou', + 9113: 'Jacques Clock', + 9114: 'Smudgy Mascara', + 9115: 'Babyface MacDougal', + 9116: 'Dances with Sheep', + 9117: 'Afta Hours', + 9118: 'Starry Knight', + 9119: 'Rocco', + 9120: 'Sarah Slumber', + 9121: 'Serena Shortsheeter', + 9122: 'Puffy Ayes', + 9123: 'Teddy Blair', + 9124: 'Nina Nitelight', + 9125: 'Dr. Bleary', + 9126: 'Wyda Wake', + 9127: 'Tabby Tucker', + 9128: "Hardy O'Toole", + 9129: 'Bertha Bedhog', + 9130: 'Charlie Chamberpot', + 9131: 'Susan Siesta', + 9132: lHQOfficerF, + 9133: lHQOfficerF, + 9134: lHQOfficerF, + 9135: lHQOfficerF, + 9136: 'Fisherman Taylor', + 9201: 'Bernie', + 9202: 'Orville', + 9203: 'Nat', + 9204: 'Claire de Loon', + 9205: 'Zen Glen', + 9206: 'Skinny Ginny', + 9207: 'Jane Drain', + 9208: 'Drowsy Dave', + 9209: 'Dr. Floss', + 9210: 'Master Mike', + 9211: 'Dawn', + 9212: 'Moonbeam', + 9213: 'Rooster Rick', + 9214: 'Dr. Blinky', + 9215: 'Rip', + 9216: 'Cat', + 9217: 'Lawful Linda', + 9218: 'Waltzing Matilda', + 9219: 'The Countess', + 9220: 'Grumpy Gordon', + 9221: 'Zari', + 9222: 'Cowboy George', + 9223: 'Mark the Lark', + 9224: 'Sandy Sandman', + 9225: 'Fidgety Bridget', + 9226: 'William Teller', + 9227: 'Bed Head Ted', + 9228: 'Whispering Willow', + 9229: 'Rose Petals', + 9230: 'Tex', + 9231: 'Harry Hammock', + 9232: 'Honey Moon', + 9233: lHQOfficerM, + 9234: lHQOfficerM, + 9235: lHQOfficerM, + 9236: lHQOfficerM, + 9237: 'Fisherman Jung', + 9301: 'Phil Bettur', + 9302: 'Emma Phatic', + 9303: 'GiggleMesh', + 9304: 'Anne Ville', + 9305: 'Bud Erfingerz', + 9306: 'J.S. Bark', + 9307: 'Bea Sharpe', + 9308: 'Otto Toon', + 9309: 'Al Capella', + 9310: 'Des Traction', + 9311: 'Dee Version', + 9312: 'Bo Nanapeel', + 7001: 'N. Prisoned', + 7002: 'R.E. Leaseme', + 7003: 'Lemmy Owte', + 7004: 'T. Rapped', + 7005: 'Little Helphere', + 7006: 'Gimmy Ahand', + 7007: 'Dewin Tymme', + 7008: 'Ima Cagedtoon', + 7009: 'Jimmy Thelock'} +zone2TitleDict = {2513: ('Toon Hall', ''), + 2514: ('Toontown Bank', ''), + 2516: ('Toontown School House', ''), + 2518: ('Toontown Library', ''), + 2519: ('Gag Shop', ''), + 2520: (lToonHQ, ''), + 2521: ('Clothing Shop', ''), + 2522: ('Pet Shop', ''), + 2601: ('All Smiles Tooth Repair', ''), + 2602: ('', ''), + 2603: ('One-Liner Miners', ''), + 2604: ('Hogwash & Dry', ''), + 2605: ('Toontown Sign Factory', ''), + 2606: ('', ''), + 2607: ('Jumping Beans', ''), + 2610: ('Dr. Tom Foolery', ''), + 2611: ('', ''), + 2616: ("Weird Beard's Disguise Shop", ''), + 2617: ('Silly Stunts', ''), + 2618: ('All That Razz', ''), + 2621: ('Paper Airplanes', ''), + 2624: ('Happy Hooligans', ''), + 2625: ('House of Bad Pies', ''), + 2626: ("Jesse's Joke Repair", ''), + 2629: ("The Laughin' Place", ''), + 2632: ('Clown Class', ''), + 2633: ('Tee-Hee Tea Shop', ''), + 2638: ('Toontown Playhouse', ''), + 2639: ('Monkey Tricks', ''), + 2643: ('Canned Bottles', ''), + 2644: ('Impractical Jokes', ''), + 2649: ('All Fun and Games Shop', ''), + 2652: ('', ''), + 2653: ('', ''), + 2654: ('Laughing Lessons', ''), + 2655: ('Funny Money Savings & Loan', ''), + 2656: ('Used Clown Cars', ''), + 2657: ("Frank's Pranks", ''), + 2659: ('Joy Buzzers to the World', ''), + 2660: ('Tickle Machines', ''), + 2661: ('Daffy Taffy', ''), + 2662: ('Dr. I.M. Euphoric', ''), + 2663: ('Toontown Cinerama', ''), + 2664: ('The Merry Mimes', ''), + 2665: ("Mary's Go Around Travel Company", ''), + 2666: ('Laughing Gas Station', ''), + 2667: ('Happy Times', ''), + 2669: ("Muldoon's Maroon Balloons", ''), + 2670: ('Soup Forks', ''), + 2671: ('', ''), + 2701: ('', ''), + 2704: ('Movie Multiplex', ''), + 2705: ("Wiseacre's Noisemakers", ''), + 2708: ('Blue Glue', ''), + 2711: ('Toontown Post Office', ''), + 2712: ('Chortle Cafe', ''), + 2713: ('Laughter Hours Cafe', ''), + 2714: ('Kooky CinePlex', ''), + 2716: ('Soup and Crack Ups', ''), + 2717: ('Bottled Cans', ''), + 2720: ('Crack Up Auto Repair', ''), + 2725: ('', ''), + 2727: ('Seltzer Bottles and Cans', ''), + 2728: ('Vanishing Cream', ''), + 2729: ('14 Karat Goldfish', ''), + 2730: ('News for the Amused', ''), + 2731: ('', ''), + 2732: ('Spaghetti and Goofballs', ''), + 2733: ('Cast Iron Kites', ''), + 2734: ('Suction Cups and Saucers', ''), + 2735: ('The Kaboomery', ''), + 2739: ("Sidesplitter's Mending", ''), + 2740: ('Used Firecrackers', ''), + 2741: ('', ''), + 2742: ('', ''), + 2743: ('Ragtime Dry Cleaners', ''), + 2744: ('', ''), + 2747: ('Visible Ink', ''), + 2748: ('Jest for Laughs', ''), + 2801: ('Sofa Whoopee Cushions', ''), + 2802: ('Inflatable Wrecking Balls', ''), + 2803: ('The Karnival Kid', ''), + 2804: ('Dr. Pulyurleg, Chiropractor', ''), + 2805: ('', ''), + 2809: ('The Punch Line Gym', ''), + 2814: ('Toontown Theatre', ''), + 2818: ('The Flying Pie', ''), + 2821: ('', ''), + 2822: ('Rubber Chicken Sandwiches', ''), + 2823: ('Sundae Funnies Ice Cream', ''), + 2824: ('Punchline Movie Palace', ''), + 2829: ('Phony Baloney', ''), + 2830: ("Zippy's Zingers", ''), + 2831: ("Professor Wiggle's House of Giggles", ''), + 2832: ('', ''), + 2833: ('', ''), + 2834: ('Funny Bone Emergency Room', ''), + 2836: ('', ''), + 2837: ('Hardy Harr Seminars', ''), + 2839: ('Barely Palatable Pasta', ''), + 2841: ('', ''), + 1506: ('Gag Shop', ''), + 1507: ('Toon Headquarters', ''), + 1508: ('Clothing Shop', ''), + 1510: ('', ''), + 1602: ('Used Life Preservers', ''), + 1604: ('Wet Suit Dry Cleaners', ''), + 1606: ("Hook's Clock Repair", ''), + 1608: ("Luff 'N Stuff", ''), + 1609: ('Every Little Bait', ''), + 1612: ('Dime & Quarterdeck Bank', ''), + 1613: ('Squid Pro Quo, Attorneys at Law', ''), + 1614: ('Trim the Nail Boutique', ''), + 1615: ("Yacht's All, Folks!", ''), + 1616: ("Blackbeard's Beauty Parlor", ''), + 1617: ('Out to See Optics', ''), + 1619: ('Disembark! Tree Surgeons', ''), + 1620: ('From Fore to Aft', ''), + 1621: ('Poop Deck Gym', ''), + 1622: ('Bait and Switches Electrical Shop', ''), + 1624: ('Soles Repaired While U Wait', ''), + 1626: ('Salmon Chanted Evening Formal Wear', ''), + 1627: ("Billy Budd's Big Bargain Binnacle Barn", ''), + 1628: ('Piano Tuna', ''), + 1629: ('', ''), + 1701: ('Buoys and Gulls Nursery School', ''), + 1703: ('Wok the Plank Chinese Food', ''), + 1705: ('Sails for Sale', ''), + 1706: ('Peanut Butter and Jellyfish', ''), + 1707: ('Gifts With a Porpoise', ''), + 1709: ('Windjammers and Jellies', ''), + 1710: ('Barnacle Bargains', ''), + 1711: ('Deep Sea Diner', ''), + 1712: ('Able-Bodied Gym', ''), + 1713: ("Art's Smart Chart Mart", ''), + 1714: ("Reel 'Em Inn", ''), + 1716: ('Mermaid Swimwear', ''), + 1717: ('Be More Pacific Ocean Notions', ''), + 1718: ('Run Aground Taxi Service', ''), + 1719: ("Duck's Back Water Company", ''), + 1720: ('The Reel Deal', ''), + 1721: ('All For Nautical', ''), + 1723: ("Squid's Seaweed", ''), + 1724: ("That's a Moray!", ''), + 1725: ("Ahab's Prefab Sea Crab Center", ''), + 1726: ('Root Beer Afloats', ''), + 1727: ('This Oar That', ''), + 1728: ('Good Luck Horseshoe Crabs', ''), + 1729: ('', ''), + 1802: ('Nautical But Nice', ''), + 1804: ('Mussel Beach Gymnasium', ''), + 1805: ('Tackle Box Lunches', ''), + 1806: ('Cap Size Hat Store', ''), + 1807: ('Keel Deals', ''), + 1808: ('Knots So Fast', ''), + 1809: ('Rusty Buckets', ''), + 1810: ('Anchor Management', ''), + 1811: ("What's Canoe With You?", ''), + 1813: ('Pier Pressure Plumbing', ''), + 1814: ('The Yo Ho Stop and Go', ''), + 1815: ("What's Up, Dock?", ''), + 1818: ('Seven Seas Cafe', ''), + 1819: ("Docker's Diner", ''), + 1820: ('Hook, Line, and Sinker Prank Shop', ''), + 1821: ("King Neptoon's Cannery", ''), + 1823: ('The Clam Bake Diner', ''), + 1824: ('Dog Paddles', ''), + 1825: ('Wholly Mackerel! Fish Market', ''), + 1826: ("Claggart's Clever Clovis Closet", ''), + 1828: ("Alice's Ballast Palace", ''), + 1829: ('Seagull Statue Store', ''), + 1830: ('Lost and Flounder', ''), + 1831: ('Kelp Around the House', ''), + 1832: ("Melville's Massive Mizzenmast Mart", ''), + 1833: ('This Transom Man Custom Tailored Suits', ''), + 1834: ('Rudderly Ridiculous!', ''), + 1835: ('', ''), + 4503: ('Gag Shop', ''), + 4504: ('Toon Headquarters', ''), + 4506: ('Clothing Shop', ''), + 4508: ('', ''), + 4603: ("Tom-Tom's Drums", ''), + 4604: ('In Four-Four Time', ''), + 4605: ("Fifi's Fiddles", ''), + 4606: ('Casa De Castanets', ''), + 4607: ('Catchy Toon Apparel', ''), + 4609: ('Do, Rae, Me Piano Keys', ''), + 4610: ('Please Refrain', ''), + 4611: ('Tuning Forks and Spoons', ''), + 4612: ("Dr. Fret's Dentistry", ''), + 4614: ('Shave and a Haircut for a Song', ''), + 4615: ("Piccolo's Pizza", ''), + 4617: ('Happy Mandolins', ''), + 4618: ('Rests Rooms', ''), + 4619: ('More Scores', ''), + 4622: ('Chin Rest Pillows', ''), + 4623: ('Flats Sharpened', ''), + 4625: ('Tuba Toothpaste', ''), + 4626: ('Notations', ''), + 4628: ('Accidental Insurance', ''), + 4629: ("Riff's Paper Plates", ''), + 4630: ('Music Is Our Forte', ''), + 4631: ('Canto Help You', ''), + 4632: ('Dance Around the Clock Shop', ''), + 4635: ('Tenor Times', ''), + 4637: ('For Good Measure', ''), + 4638: ('Hard Rock Shop', ''), + 4639: ('Four Score Antiques', ''), + 4641: ('Blues News', ''), + 4642: ('Ragtime Dry Cleaners', ''), + 4645: ('Club 88', ''), + 4646: ('', ''), + 4648: ('Carry a Toon Movers', ''), + 4649: ('', ''), + 4652: ('Full Stop Shop', ''), + 4653: ('', ''), + 4654: ('Pitch Perfect Roofing', ''), + 4655: ("The Treble Chef's Cooking School", ''), + 4656: ('', ''), + 4657: ('Barbershop Quartet', ''), + 4658: ('Plummeting Pianos', ''), + 4659: ('', ''), + 4701: ('The Schmaltzy Waltz School of Dance', ''), + 4702: ('Timbre! Equipment for the Singing Lumberjack', ''), + 4703: ('I Can Handel It!', ''), + 4704: ("Tina's Concertina Concerts", ''), + 4705: ('Zither Here Nor There', ''), + 4707: ("Doppler's Sound Effects Studio", ''), + 4709: ('On Ballet! Climbing Supplies', ''), + 4710: ('Hurry Up, Slow Polka! School of Driving', ''), + 4712: ('C-Flat Tire Repair', ''), + 4713: ('B-Sharp Fine Menswear', ''), + 4716: ('Four-Part Harmonicas', ''), + 4717: ('Sonata Your Fault! Discount Auto Insurance', ''), + 4718: ('Chopin Blocks and Other Kitchen Supplies', ''), + 4719: ('Madrigal Motor Homes', ''), + 4720: ('Name That Toon', ''), + 4722: ('Overture Understudies', ''), + 4723: ('Haydn Go Seek Playground Supplies', ''), + 4724: ('White Noise for Girls and Boys', ''), + 4725: ('The Baritone Barber', ''), + 4727: ('Vocal Chords Braided', ''), + 4728: ("Sing Solo We Can't Hear You", ''), + 4729: ('Double Reed Bookstore', ''), + 4730: ('Lousy Lyrics', ''), + 4731: ('Toon Tunes', ''), + 4732: ('Etude Brute? Theatre Company', ''), + 4733: ('', ''), + 4734: ('', ''), + 4735: ('Accordions, If You Want In, Just Bellow!', ''), + 4736: ('Her and Hymn Wedding Planners', ''), + 4737: ('Harp Tarps', ''), + 4738: ('Canticle Your Fancy Gift Shop', ''), + 4739: ('', ''), + 4801: ("Marshall's Stacks", ''), + 4803: ('What a Mezzo! Maid Service', ''), + 4804: ('Mixolydian Scales', ''), + 4807: ('Relax the Bach', ''), + 4809: ("I Can't Understanza!", ''), + 4812: ('', ''), + 4817: ('The Ternary Pet Shop', ''), + 4819: ("Yuki's Ukeleles", ''), + 4820: ('', ''), + 4821: ("Anna's Cruises", ''), + 4827: ('Common Time Watches', ''), + 4828: ("Schumann's Shoes for Men", ''), + 4829: ("Pachelbel's Canonballs", ''), + 4835: ('Ursatz for Kool Katz', ''), + 4836: ('Reggae Regalia', ''), + 4838: ('Kazoology School of Music', ''), + 4840: ('Coda Pop Musical Beverages', ''), + 4841: ('Lyre, Lyre, Pants on Fire!', ''), + 4842: ('The Syncopation Corporation', ''), + 4843: ('', ''), + 4844: ('Con Moto Cycles', ''), + 4845: ("Ellie's Elegant Elegies", ''), + 4848: ('Lotsa Lute Savings & Loan', ''), + 4849: ('', ''), + 4850: ('The Borrowed Chord Pawn Shop', ''), + 4852: ('Flowery Flute Fleeces', ''), + 4853: ("Leo's Fenders", ''), + 4854: ("Wagner's Vocational Violin Videos", ''), + 4855: ('The Teli-Caster Network', ''), + 4856: ('', ''), + 4862: ("Quentin's Quintessen\x03tial Quadrilles", ''), + 4867: ("Mr. Costello's Yellow Cellos", ''), + 4868: ('', ''), + 4870: ("Ziggy's Zoo of Zigeuner\x03musik", ''), + 4871: ("Harry's House of Harmonious Humbuckers", ''), + 4872: ("Fast Freddie's Fretless Fingerboards", ''), + 4873: ('', ''), + 5501: ('Gag Shop', ''), + 5502: (lToonHQ, ''), + 5503: ('Clothing Shop', ''), + 5505: ('', ''), + 5601: ('Eye of the Potato Optometry', ''), + 5602: ("Artie Choke's Neckties", ''), + 5603: ('Lettuce Alone', ''), + 5604: ('Cantaloupe Bridal Shop', ''), + 5605: ('Vege-tables and Chairs', ''), + 5606: ('Petals', ''), + 5607: ('Compost Office', ''), + 5608: ('Mom and Pop Corn', ''), + 5609: ('Berried Treasure', ''), + 5610: ("Black-eyed Susan's Boxing Lessons", ''), + 5611: ("Gopher's Gags", ''), + 5613: ('Crop Top Barbers', ''), + 5615: ("Bud's Bird Seed", ''), + 5616: ('Dew Drop Inn', ''), + 5617: ("Flutterby's Butterflies", ''), + 5618: ("Peas and Q's", ''), + 5619: ("Jack's Beanstalks", ''), + 5620: ('Rake It Inn', ''), + 5621: ('Grape Expectations', ''), + 5622: ('Petal Pusher Bicycles', ''), + 5623: ('Bubble Bird Baths', ''), + 5624: ("Mum's the Word", ''), + 5625: ('Leaf It Bees', ''), + 5626: ('Pine Needle Crafts', ''), + 5627: ('', ''), + 5701: ('From Start to Spinach', ''), + 5702: ("Jake's Rakes", ''), + 5703: ("Photo Cynthia's Camera Shop", ''), + 5704: ('Lisa Lemon Used Cars', ''), + 5705: ('Poison Oak Furniture', ''), + 5706: ('14 Carrot Jewelers', ''), + 5707: ('Musical Fruit', ''), + 5708: ("We'd Be Gone Travel Agency", ''), + 5709: ('Astroturf Mowers', ''), + 5710: ('Tuft Guy Gym', ''), + 5711: ('Garden Hosiery', ''), + 5712: ('Silly Statues', ''), + 5713: ('Trowels and Tribulations', ''), + 5714: ('Spring Rain Seltzer Bottles', ''), + 5715: ('Hayseed News', ''), + 5716: ('Take It or Leaf It Pawn Shop', ''), + 5717: ('The Squirting Flower', ''), + 5718: ('The Dandy Lion Exotic Pets', ''), + 5719: ('Trellis the Truth! Private Investi\x03gators', ''), + 5720: ('Vine and Dandy Menswear', ''), + 5721: ('Root 66 Diner', ''), + 5725: ('Barley, Hops, and Malt Shop', ''), + 5726: ("Bert's Dirt", ''), + 5727: ('Gopher Broke Savings & Loan', ''), + 5728: ('', ''), + 5802: (lToonHQ, ''), + 5804: ('Just Vase It', ''), + 5805: ('Snail Mail', ''), + 5809: ('Fungi Clown School', ''), + 5810: ('Honeydew This', ''), + 5811: ('Lettuce Inn', ''), + 5815: ('Grass Roots', ''), + 5817: ('Apples and Oranges', ''), + 5819: ('Green Bean Jeans', ''), + 5821: ('Squash and Stretch Gym', ''), + 5826: ('Ant Farming Supplies', ''), + 5827: ('Dirt. Cheap.', ''), + 5828: ('Couch Potato Furniture', ''), + 5830: ('Spill the Beans', ''), + 5833: ('The Salad Bar', ''), + 5835: ('Flower Bed and Breakfast', ''), + 5836: ("April's Showers and Tubs", ''), + 5837: ('School of Vine Arts', ''), + 9501: ('Lullaby Library', ''), + 9503: ('The Snooze Bar', ''), + 9504: ('Gag Shop', ''), + 9505: (lToonHQ, ''), + 9506: ('Clothing Shop', ''), + 9508: ('', ''), + 9601: ('Snuggle Inn', ''), + 9602: ('Forty Winks for the Price of Twenty', ''), + 9604: ("Ed's Red Bed Spreads", ''), + 9605: ('Cloud Nine Design', ''), + 9607: ("Big Mama's Bahama Pajamas", ''), + 9608: ('Cat Nip for Cat Naps', ''), + 9609: ('Deep Sleep for Cheap', ''), + 9613: ('Clock Cleaners', ''), + 9616: ('Lights Out Electric Co.', ''), + 9617: ('Crib Notes - Music to Sleep By', ''), + 9619: ('Relax to the Max', ''), + 9620: ("PJ's Taxi Service", ''), + 9622: ('Sleepy Time Pieces', ''), + 9625: ('Curl Up Beauty Parlor', ''), + 9626: ('Bed Time Stories', ''), + 9627: ('The Sleepy Teepee', ''), + 9628: ('Call It a Day Calendars', ''), + 9629: ('Silver Lining Jewelers', ''), + 9630: ('Rock to Sleep Quarry', ''), + 9631: ('Down Time Watch Repair', ''), + 9633: ('The Dreamland Screening Room', ''), + 9634: ('Mind Over Mattress', ''), + 9636: ('Insomniac Insurance', ''), + 9639: ('House of Hibernation', ''), + 9640: ('Nightstand Furniture Company', ''), + 9642: ('Sawing Wood Slumber Lumber', ''), + 9643: ('Shut-Eye Optometry', ''), + 9644: ('Pillow Fights Nightly', ''), + 9645: ('The All Tucked Inn', ''), + 9647: ('Make Your Bed! Hardware Store', ''), + 9649: ('Snore or Less', ''), + 9650: ('Crack of Dawn Repairs', ''), + 9651: ('For Richer or Snorer', ''), + 9652: ('', ''), + 9703: ('Fly By Night Travel Agency', ''), + 9704: ('Night Owl Pet Shop', ''), + 9705: ('Asleep At The Wheel Car Repair', ''), + 9706: ('Tooth Fairy Dentistry', ''), + 9707: ("Dawn's Yawn & Garden Center", ''), + 9708: ('Bed Of Roses Florist', ''), + 9709: ('Pipe Dream Plumbers', ''), + 9710: ('REM Optometry', ''), + 9711: ('Wake-Up Call Phone Company', ''), + 9712: ("Counting Sheep - So You Don't Have To!", ''), + 9713: ('Wynken, Blynken & Nod, Attorneys at Law', ''), + 9714: ('Dreamboat Marine Supply', ''), + 9715: ('First Security Blanket Bank', ''), + 9716: ('Wet Blanket Party Planners', ''), + 9717: ("Baker's Dozin' Doughnuts", ''), + 9718: ("Sandman's Sandwiches", ''), + 9719: ('Armadillo Pillow Company', ''), + 9720: ('Talking In Your Sleep Voice Training', ''), + 9721: ('Snug As A Bug Rug Dealer', ''), + 9722: ('Dream On Talent Agency', ''), + 9725: ("Cat's Pajamas", ''), + 9727: ('You Snooze, You Lose', ''), + 9736: ('Dream Jobs Employment Agency', ''), + 9737: ("Waltzing Matilda's Dance School", ''), + 9738: ('House of Zzzzzs', ''), + 9740: ('Hit The Sack Fencing School', ''), + 9741: ("Don't Let The Bed Bugs Bite Exterminators", ''), + 9744: ("Rip Van Winkle's Wrinkle Cream", ''), + 9752: ('Midnight Oil & Gas Company', ''), + 9753: ("Moonbeam's Ice Creams", ''), + 9754: ('Sleepless in the Saddle All Night Pony Rides', ''), + 9755: ('Bedknobs & Broomsticks Movie House', ''), + 9756: ('', ''), + 9759: ('Sleeping Beauty Parlor', ''), + 3507: ('Gag Shop', ''), + 3508: (lToonHQ, ''), + 3509: ('Clothing Shop', ''), + 3511: ('', ''), + 3601: ('Northern Lights Electric Company', ''), + 3602: ("Nor'easter Bonnets", ''), + 3605: ('', ''), + 3607: ('The Blizzard Wizard', ''), + 3608: ('Nothing to Luge', ''), + 3610: ("Mike's Massive Mukluk Mart", ''), + 3611: ("Mr. Cow's Snow Plows", ''), + 3612: ('Igloo Design', ''), + 3613: ('Ice Cycle Bikes', ''), + 3614: ('Snowflakes Cereal Company', ''), + 3615: ('Fried Baked Alaskas', ''), + 3617: ('Cold Air Balloon Rides', ''), + 3618: ('Snow Big Deal! Crisis Management', ''), + 3620: ('Skiing Clinic', ''), + 3621: ('The Melting Ice Cream Bar', ''), + 3622: ('', ''), + 3623: ('The Mostly Toasty Bread Company', ''), + 3624: ('Subzero Sandwich Shop', ''), + 3625: ("Auntie Freeze's Radiator Supply", ''), + 3627: ('St. Bernard Kennel Club', ''), + 3629: ('Pea Soup Cafe', ''), + 3630: ('Icy London, Icy France Travel Agency', ''), + 3634: ('Easy Chair Lifts', ''), + 3635: ('Used Firewood', ''), + 3636: ('Affordable Goosebumps', ''), + 3637: ("Kate's Skates", ''), + 3638: ('Toboggan or Not Toboggan', ''), + 3641: ("Fred's Red Sled Beds", ''), + 3642: ('Eye of the Storm Optics', ''), + 3643: ('Snowball Hall', ''), + 3644: ('Melted Ice Cubes', ''), + 3647: ('The Sanguine Penguin Tuxedo Shop', ''), + 3648: ('Instant Ice', ''), + 3649: ('Hambrrrgers', ''), + 3650: ('Antarctic Antiques', ''), + 3651: ("Frosty Freddy's Frozen Frankfurters", ''), + 3653: ('Ice House Jewelry', ''), + 3654: ('', ''), + 3702: ('Winter Storage', ''), + 3703: ('', ''), + 3705: ('Icicles Built for Two', ''), + 3706: ("Shiverin' Shakes Malt Shop", ''), + 3707: ('Snowplace Like Home', ''), + 3708: ("Pluto's Place", ''), + 3710: ('Dropping Degrees Diner', ''), + 3711: ('', ''), + 3712: ('Go With the Floe', ''), + 3713: ('Chattering Teeth, Subzero Dentist', ''), + 3715: ("Aunt Arctic's Soup Shop", ''), + 3716: ('Road Salt and Pepper', ''), + 3717: ('Juneau What I Mean?', ''), + 3718: ('Designer Inner Tubes', ''), + 3719: ('Ice Cube on a Stick', ''), + 3721: ("Noggin's Toboggan Bargains", ''), + 3722: ('Snow Bunny Ski Shop', ''), + 3723: ("Shakey's Snow Globes", ''), + 3724: ('The Chattering Chronicle', ''), + 3725: ('You Sleigh Me', ''), + 3726: ('Solar Powered Blankets', ''), + 3728: ('Lowbrow Snowplows', ''), + 3729: ('', ''), + 3730: ('Snowmen Bought & Sold', ''), + 3731: ('Portable Fireplaces', ''), + 3732: ('The Frozen Nose', ''), + 3734: ('Icy Fine, Do You? Optometry', ''), + 3735: ('Polar Ice Caps', ''), + 3736: ('Diced Ice at a Nice Price', ''), + 3737: ('Downhill Diner', ''), + 3738: ("Heat-Get It While It's Hot", ''), + 3739: ('', ''), + 3801: ('Toon HQ', ''), + 3806: ('Alpine Chow Line', ''), + 3807: ('Used Groundhog Shadows', ''), + 3808: ('The Sweater Lodge', ''), + 3809: ('Ice Saw It Too', ''), + 3810: ('A Better Built Quilt', ''), + 3811: ('Your Snow Angel', ''), + 3812: ('Mittens for Kittens', ''), + 3813: ("Snowshoes You Can't Refuse", ''), + 3814: ('Malt in Your Mouth Soda Fountain', ''), + 3815: ('The Toupee Chalet', ''), + 3816: ('Just So Mistletoe', ''), + 3817: ('Winter Wonderland Walking Club', ''), + 3818: ('The Shovel Hovel', ''), + 3819: ('Clean Sweep Chimney Service', ''), + 3820: ('Snow Whitening', ''), + 3821: ('Hibernation Vacations', ''), + 3823: ('Precipitation Foundation', ''), + 3824: ('Open Fire Chestnut Roasting', ''), + 3825: ('Cool Cat Hats', ''), + 3826: ('Oh My Galoshes!', ''), + 3827: ('Choral Wreaths', ''), + 3828: ("Snowman's Land", ''), + 3829: ('Pinecone Zone', ''), + 3830: ('Wait and See Goggle Defogging', '')} +ClosetTimeoutMessage = 'Sorry, you ran out\n of time.' +ClosetNotOwnerMessage = "This isn't your closet, but you may try on the clothes." +ClosetPopupOK = lOK +ClosetPopupCancel = lCancel +ClosetDiscardButton = 'Remove' +ClosetAreYouSureMessage = 'You have deleted some clothes. Do you really want to delete them?' +ClosetYes = lYes +ClosetNo = lNo +ClosetVerifyDelete = 'Really delete %s?' +ClosetShirt = 'this shirt' +ClosetShorts = 'these shorts' +ClosetSkirt = 'this skirt' +ClosetDeleteShirt = 'Delete\nshirt' +ClosetDeleteShorts = 'Delete\nshorts' +ClosetDeleteSkirt = 'Delete\nskirt' +TrunkNotOwnerMessage = "This isn't your trunk, but you may try on the accessories." +TrunkNotPaidMessage = 'Only Paid Members can wear accessories, but you may try them on.' +TrunkAreYouSureMessage = 'You have deleted some accessories. Do you really want to delete them?' +TrunkHat = 'this hat' +TrunkGlasses = 'these glasses' +TrunkBackpack = 'this backpack' +TrunkShoes = 'these shoes' +TrunkDeleteHat = 'Delete\nhat' +TrunkDeleteGlasses = 'Delete\nglasses' +TrunkDeleteBackpack = 'Delete\nbackpack' +TrunkDeleteShoes = 'Delete\nshoes' +EstateOwnerLeftMessage = "Sorry, the owner of this estate left. You'll be sent to the playground in %s seconds" +EstatePopupOK = lOK +EstateTeleportFailed = "Couldn't go home. Try again!" +EstateTeleportFailedNotFriends = "Sorry, %s is in a toon's estate that you are not friends with." +EstateTargetGameStart = 'The Toon-up Target game has started!' +EstateTargetGameInst = "The more you hit the red target, the more you'll get Tooned up." +EstateTargetGameEnd = 'The Toon-up Target game is now over...' +AvatarsHouse = '%s\n House' +BankGuiCancel = lCancel +BankGuiOk = lOK +DistributedBankNoOwner = 'Sorry, this is not your bank.' +DistributedBankNotOwner = 'Sorry, this is not your bank.' +FishGuiCancel = lCancel +FishGuiOk = 'Sell All' +FishTankValue = 'Hi, %(name)s! You have %(num)s fish in your bucket worth a total of %(value)s jellybeans. Do you want to sell them all?' +FlowerGuiCancel = lCancel +FlowerGuiOk = 'Sell All' +FlowerBasketValue = '%(name)s, you have %(num)s flowers in your basket worth a total of %(value)s jellybeans. Do you want to sell them all?' + +def GetPossesive(name): + if name[-1:] == 's': + possesive = name + "'" + else: + possesive = name + "'s" + return possesive + + +PetTrait2descriptions = {'hungerThreshold': ('Always Hungry', + 'Often Hungry', + 'Sometimes Hungry', + 'Rarely Hungry'), + 'boredomThreshold': ('Always Bored', + 'Often Bored', + 'Sometimes Bored', + 'Rarely Bored'), + 'angerThreshold': ('Always Grumpy', + 'Often Grumpy', + 'Sometimes Grumpy', + 'Rarely Grumpy'), + 'forgetfulness': ('Always Forgets', + 'Often Forgets', + 'Sometimes Forgets', + 'Rarely Forgets'), + 'excitementThreshold': ('Very Calm', + 'Pretty Calm', + 'Pretty Excitable', + 'Very Excitable'), + 'sadnessThreshold': ('Always Sad', + 'Often Sad', + 'Sometimes Sad', + 'Rarely Sad'), + 'restlessnessThreshold': ('Always Restless', + 'Often Restless', + 'Sometimes Restless', + 'Rarely Restless'), + 'playfulnessThreshold': ('Rarely Playful', + 'Sometimes Playful', + 'Often Playful', + 'Always Playful'), + 'lonelinessThreshold': ('Always Lonely', + 'Often Lonely', + 'Sometimes Lonely', + 'Rarely Lonely'), + 'fatigueThreshold': ('Always Tired', + 'Often Tired', + 'Sometimes Tired', + 'Rarely Tired'), + 'confusionThreshold': ('Always Confused', + 'Often Confused', + 'Sometimes Confused', + 'Rarely Confused'), + 'surpriseThreshold': ('Always Surprised', + 'Often Surprised', + 'Sometimes Surprised', + 'Rarely Surprised'), + 'affectionThreshold': ('Rarely Affectionate', + 'Sometimes Affectionate', + 'Often Affectionate', + 'Always Affectionate')} +FireworksInstructions = lToonHQ + ': Hit the "Page Up" key to see the show!' +startFireworksResponse = "Usage: startFireworksShow ['num']\n 'num' = %s - New Years\n %s - Party Summer \n %s - 4th of July" +FireworksJuly4Beginning = lToonHQ + ': Welcome to summer fireworks! Enjoy the show!' +FireworksJuly4Ending = lToonHQ + ': Hope you enjoyed the show! Have a great summer!' +FireworksNewYearsEveBeginning = lToonHQ + ': Happy New Year! Enjoy the fireworks show!' +FireworksNewYearsEveEnding = lToonHQ + ': Hope you enjoyed the show! Happy New Year!' +FireworksComboBeginning = lToonHQ + ': Enjoy lots of Laffs with Toon fireworks!' +FireworksComboEnding = lToonHQ + ': Thank you, Toons! Hope you enjoyed the show!' +BlockerTitle = 'LOADING TOONTOWN...' +BlockerLoadingTexts = ['Scrubbing pie tins', + 'Baking pie crusts', + 'Heating pie filling', + 'Loading Doodle chow', + 'Stringing Jungle Vines', + 'Uncaging those spiders who crawl down jungle vines', + 'Planting squirting flower seeds', + 'Stretching trampolines', + 'Herding pigs', + "Tweaking 'SPLAT' sounds", + 'Cleaning Hypno-glasses', + 'Unbottling ink for Toon News', + 'Clipping TNT fuses', + "Setting up 'Under Construction' sign in Acorn Acres", + 'Waking Donald Duck', + 'Teaching new moves to dancing fire hydrants', + 'Binding Shticker Books', + 'Analyzing quacks', + 'Harvesting jellybean pods', + 'Emptying fish buckets', + 'Corralling trashcan trash', + 'Spreading Cog grease', + 'Polishing kart trophies', + 'Balancing scale for weighing 1 Ton weights', + 'Practicing Victory Dances', + 'Preparing wackiness', + "Giving Mickey Mouse the 'five minutes' sign", + 'Testing white gloves', + 'Bending underwater rings', + 'Spooling red tape', + 'Freezing Brrrgh ice', + 'Tuning falling pianos'] +TIP_NONE = 0 +TIP_GENERAL = 1 +TIP_STREET = 2 +TIP_MINIGAME = 3 +TIP_COGHQ = 4 +TIP_ESTATE = 5 +TIP_KARTING = 6 +TIP_GOLF = 7 +TipTitle = 'TOON TIP:' +TipDict = {TIP_NONE: ('',), + TIP_GENERAL: ('Quickly check your ToonTask progress by holding down the "End" key.', + 'Quickly check your Gag page by holding down the "Home" key.', + 'Open your Friends List by pressing the "F7" key.', + 'Open or close your Shticker Book by pressing the "F8" key.', + 'You can look up by pressing the "Page Up" key and look down by pressing the "Page Down" key.', + 'Press the "Control" key to jump.', + 'Press the "F9" key to take a screenshot, which will be saved in your Toontown folder on your computer.', + 'You can change your screen resolution, adjust audio, and control other options on the Options Page in the Shticker Book.', + "Try on your friend's clothing at the closet in their house.", + 'You can go to your house using the "Go Home" button on your map.', + 'Every time you turn in a completed ToonTask your Laff points are automatically refilled.', + 'You can browse the selection at Clothing Stores even without a clothing ticket.', + 'Rewards for some ToonTasks allow you to carry more gags and jellybeans.', + 'You can have up to 50 friends on your Friends List.', + 'Some ToonTask rewards let you teleport to playgrounds in Toontown by using the Map Page in the Shticker Book.', + 'Increase your Laff points in the Playgrounds by collecting treasures like stars and ice cream cones.', + 'To heal quickly after a battle, go to your estate and play with your Doodle.', + 'Change to different views of your Toon by pressing the Tab Key.', + 'Sometimes you can find several different ToonTasks offered for the same reward. Shop around!', + 'Finding friends with similar ToonTasks is a fun way to progress through the game.', + 'You never need to save your Toontown progress. The Toontown servers continually save all the necessary information.', + 'You can whisper to other Toons either by clicking on them or by selecting them from your Friends List.', + 'Some SpeedChat phrases play emotion animations on your Toon.', + 'If the area you are in is crowded, try changing Districts. Go to the District Page in the Shticker Book and select a different one.', + 'If you actively rescue buildings you will get a bronze, silver, or gold star above your Toon.', + 'If you rescue enough buildings to get a star above your head you may find your name on the blackboard in a Toon HQ.', + 'Rescued buildings are sometimes recaptured by the Cogs. The only way to keep your star is to go out and rescue more buildings!', + 'The names of your True Friends will appear in Blue.', + 'See if you can collect all the fish in Toontown!', + 'Different ponds hold different fish. Try them all!', + 'When your fishing bucket is full sell your fish to the Fishermen in the Playgrounds.', + 'You can sell your fish to the Fishermen or inside Pet Shops.', + 'Stronger fishing rods catch heavier fish but cost more jellybeans to use.', + 'You can purchase stronger fishing rods in the Cattlelog.', + 'Heavier fish are worth more jellybeans to the Pet Shop.', + 'Rare fish are worth more jellybeans to the Pet Shop.', + 'You can sometimes find bags of jellybeans while fishing.', + 'Some ToonTasks require fishing items out of the ponds.', + 'Fishing ponds in the Playgrounds have different fish than ponds on the streets.', + 'Some fish are really rare. Keep fishing until you collect them all!', + 'The pond at your estate has fish that can only be found there.', + 'For every 10 species you catch, you will get a fishing trophy!', + 'You can see what fish you have collected in your Shticker Book.', + 'Some fishing trophies reward you with a Laff boost.', + 'Fishing is a good way to earn more jellybeans.', + 'Adopt a Doodle at the Pet Shop!', + 'Pet Shops get new Doodles to sell every day.', + 'Visit the Pet Shops every day to see what new Doodles they have.', + 'Different neighborhoods have different Doodles offered for adoption.', + "Show off your stylin' ride and turbo-boost your Laff limit at Goofy Speedway.", + 'Enter Goofy Speedway through the tire-shaped tunnel in Toontown Central Playground.', + 'Earn Laff points at Goofy Speedway.', + 'Goofy Speedway has six different race tracks. '), + TIP_STREET: ('There are four types of Cogs: Lawbots, Cashbots, Sellbots, and Bossbots.', + 'Each Gag Track has different amounts of accuracy and damage.', + 'Sound gags will affect all Cogs but will wake up any lured Cogs.', + 'Defeating Cogs in strategic order can greatly increase your chances of winning battles.', + 'The Toon-Up Gag Track lets you heal other Toons in battle.', + 'Gag experience points are doubled during a Cog Invasion!', + 'Multiple Toons can team up and use the same Gag Track in battle to get bonus Cog damage.', + 'In battle, gags are used in order from top to bottom as displayed on the Gag Menu.', + 'The row of circular lights over Cog Building elevators show how many floors will be inside.', + 'Click on a Cog to see more details.', + 'Using high level gags against low level Cogs will not earn any experience points.', + 'A gag that will earn experience has a blue background on the Gag Menu in battle.', + 'Gag experience is multiplied when used inside Cog Buildings. Higher floors have higher multipliers.', + 'When a Cog is defeated, each Toon in that round will get credit for the Cog when the battle is over.', + 'Each street in Toontown has different Cog levels and types.', + 'Sidewalks are safe from Cogs.', + 'On the streets, side doors tell knock-knock jokes when approached.', + 'Some ToonTasks train you for new Gag Tracks. You only get to choose six of the seven Gag Tracks, so choose carefully!', + 'Traps are only useful if you or your friends coordinate using Lure in battle.', + 'Higher level Lures are less likely to miss.', + 'Lower level gags have a lower accuracy against high level Cogs.', + 'Cogs cannot attack once they have been lured in battle.', + 'When you and your friends defeat a Cog building you are rewarded with portraits inside the rescued Toon Building.', + 'Using a Toon-Up gag on a Toon with a full Laff meter will not earn Toon-Up experience.', + 'Cogs will be briefly stunned when hit by any gag. This increases the chance that other gags in the same round will hit.', + 'Drop gags have low chance of hitting, but accuracy is increased when Cogs are first hit by another gag in the same round.', + 'When you\'ve defeated enough Cogs, use the "Cog Radar" by clicking the Cog icons on the Cog Gallery page in your Shticker Book.', + 'During a battle, you can tell which Cog your teammates are attacking by looking at the dashes (-) and Xs.', + 'During a battle, Cogs have a light on them that displays their health; green is healthy, red is nearly destroyed.', + 'A maximum of four Toons can battle at once.', + 'On the street, Cogs are more likely to join a fight against multiple Toons than just one Toon.', + 'The two most difficult Cogs of each type are only found in buildings.', + 'Drop gags never work against lured Cogs.', + 'Cogs tend to attack the Toon that has done them the most damage.', + 'Sound gags do not get bonus damage against lured Cogs.', + 'If you wait too long to attack a lured Cog, it will wake up. Higher level lures last longer.', + 'There are fishing ponds on every street in Toontown. Some streets have unique fish.'), + TIP_MINIGAME: ('After you fill up your jellybean jar, any jellybeans you get from Trolley Games automatically spill over into your bank.', + 'You can use the arrow keys instead of the mouse in the "Match Minnie" Trolley Game.', + 'In the Cannon Game you can use the arrow keys to move your cannon and press the "Control" key to fire.', + 'In the Ring Game, bonus points are awarded when the entire group successfully swims through its rings.', + 'A perfect game of Match Minnie will double your points.', + 'In the Tug-of-War you are awarded more jellybeans if you play against a tougher Cog.', + 'Trolley Game difficulty varies by neighborhood; ' + lToontownCentral + ' has the easiest and ' + lDonaldsDreamland + ' has the hardest.', + 'Certain Trolley Games can only be played in a group.'), + TIP_COGHQ: ('You must complete your Sellbot Disguise before visiting the V.P.', + 'You must complete your Cashbot Disguise before visiting the C.F.O.', + 'You must complete your Lawbot Disguise before visiting the Chief Justice.', + 'You can jump on Cog Goons to temporarily disable them.', + 'Collect Cog Merits by defeating Sellbot Cogs in battle.', + 'Collect Cogbucks by defeating Cashbot Cogs in battle.', + 'Collect Jury Notices by defeating Lawbot Cogs in battle.', + 'Collect Stock Options by defeating Bossbot Cogs in battle.', + 'You get more Merits, Cogbucks, Jury Notices, or Stock Options from higher level Cogs.', + 'When you collect enough Cog Merits to earn a promotion, go see the Sellbot V.P.!', + 'When you collect enough Cogbucks to earn a promotion, go see the Cashbot C.F.O.!', + 'When you collect enough Jury Notices to earn a promotion, go see the Lawbot Chief Justice!', + 'When you collect enough Stock Options to earn a promotion, go see the Bossbot C.E.O.!', + 'You can talk like a Cog when you are wearing your Cog Disguise.', + 'Up to eight Toons can join together to fight the Sellbot V.P.', + 'Up to eight Toons can join together to fight the Cashbot C.F.O.', + 'Up to eight Toons can join together to fight the Lawbot Chief Justice.', + 'Up to eight Toons can join together to fight the Bossbot C.E.O.', + 'Inside Cog Headquarters follow stairs leading up to find your way.', + 'Each time you battle through a Sellbot HQ factory, you will gain one part of your Sellbot Cog Disguise.', + 'You can check the progress of your Cog Disguise in your Shticker Book.', + 'You can check your promotion progress on your Disguise Page in your Shticker Book.', + 'Make sure you have full gags and a full Laff Meter before going to Cog Headquarters.', + 'As you get promoted, your Cog disguise updates.', + 'You must defeat the ' + Foreman + ' to recover a Sellbot Cog Disguise part.', + "Earn Cashbot disguise suit parts as rewards for completing ToonTasks in Donald's Dreamland.", + 'Cashbots manufacture and distribute their currency, Cogbucks, in three Mints - Coin, Dollar and Bullion.', + 'Wait until the C.F.O. is dizzy to throw a safe, or he will use it as a helmet! Hit the helmet with another safe to knock it off.', + 'Earn Lawbot disguise suit parts as rewards for completing ToonTasks for Professor Flake.', + "It pays to be puzzled: the virtual Cogs in Lawbot HQ won't reward you with Jury Notices."), + TIP_ESTATE: ('Doodles can understand some SpeedChat phrases. Try them!', + 'Use the "Pet" SpeedChat menu to ask your Doodle to do tricks.', + "You can teach Doodles tricks with training lessons from Clarabelle's Cattlelog.", + 'Reward your Doodle for doing tricks.', + "If you visit a friend's estate, your Doodle will come too.", + 'Feed your Doodle a jellybean when it is hungry.', + 'Click on a Doodle to get a menu where you can Feed, Scratch, and Call him.', + 'Doodles love company. Invite your friends over to play!', + 'All Doodles have unique personalities.', + 'You can return your Doodle and adopt a new one at the Pet Shops.', + 'When a Doodle performs a trick, the Toons around it heal.', + 'Doodles become better at tricks with practice. Keep at it!', + 'More advanced Doodle tricks heal Toons faster.', + 'Experienced Doodles can perform more tricks before getting tired.', + 'You can see a list of nearby Doodles in your Friends List.', + "Purchase furniture from Clarabelle's Cattlelog to decorate your house.", + 'The bank inside your house holds extra jellybeans.', + 'The closet inside your house holds extra clothes.', + "Go to your friend's house and try on his clothes.", + "Purchase better fishing rods from Clarabelle's Cattlelog.", + 'Call Clarabelle using the phone inside your house.', + 'Clarabelle sells a larger closet that holds more clothing.', + 'Make room in your closet before using a Clothing Ticket.', + 'Clarabelle sells everything you need to decorate your house.', + 'Check your mailbox for deliveries after ordering from Clarabelle.', + "Clothing from Clarabelle's Cattlelog takes one hour to be delivered.", + "Wallpaper and flooring from Clarabelle's Cattlelog take one hour to be delivered.", + "Furniture from Clarabelle's Cattlelog takes a full day to be delivered.", + 'Store extra furniture in your attic.', + 'You will get a notice from Clarabelle when a new Cattlelog is ready.', + 'You will get a notice from Clarabelle when a Cattlelog delivery arrives.', + 'New Cattlelogs are delivered each week.', + 'Look for limited-edition holiday items in the Cattlelog.', + 'Move unwanted furniture to the trash can.', + 'Some fish, like the Holey Mackerel, are more commonly found in Toon Estates.', + 'You can invite your friends to your Estate using SpeedChat.', + 'Did you know the color of your house matches the color of your Pick-A-Toon panel?'), + TIP_KARTING: ("Buy a Roadster, TUV, or Cruiser kart in Goofy's Auto Shop.", + "Customize your kart with decals, rims and more in Goofy's Auto Shop.", + 'Earn tickets by kart racing at Goofy Speedway.', + "Tickets are the only currency accepted at Goofy's Auto Shop.", + 'Tickets are required as deposits to race.', + 'A special page in the Shticker Book allows you to customize your kart.', + 'A special page in the Shticker Book allows you to view records on each track.', + 'A special page in the Shticker Book allows you to display trophies.', + 'Screwball Stadium is the easiest track at Goofy Speedway.', + 'Airborne Acres has the most hills and jumps of any track at Goofy Speedway.', + 'Blizzard Boulevard is the most challenging track at Goofy Speedway.'), + TIP_GOLF: ('Press the Tab key to see a top view of the golf course.', 'Press the Up Arrow key to point yourself towards the golf hole.', 'Swinging the club is just like throwing a pie.')} +FishGenusNames = {0: 'Balloon Fish', + 2: 'Cat Fish', + 4: 'Clown Fish', + 6: 'Frozen Fish', + 8: 'Star Fish', + 10: 'Holey Mackerel', + 12: 'Dog Fish', + 14: 'Amore Eel', + 16: 'Nurse Shark', + 18: 'King Crab', + 20: 'Moon Fish', + 22: 'Sea Horse', + 24: 'Pool Shark', + 26: 'Bear Acuda', + 28: 'Cutthroat Trout', + 30: 'Piano Tuna', + 32: 'Peanut Butter & Jellyfish', + 34: 'Devil Ray'} +FishSpeciesNames = {0: ('Balloon Fish', + 'Hot Air Balloon Fish', + 'Weather Balloon Fish', + 'Water Balloon Fish', + 'Red Balloon Fish'), + 2: ('Cat Fish', + 'Siamese Cat Fish', + 'Alley Cat Fish', + 'Tabby Cat Fish', + 'Tom Cat Fish'), + 4: ('Clown Fish', + 'Sad Clown Fish', + 'Party Clown Fish', + 'Circus Clown Fish'), + 6: ('Frozen Fish',), + 8: ('Star Fish', + 'Five Star Fish', + 'Rock Star Fish', + 'Shining Star Fish', + 'All Star Fish'), + 10: ('Holey Mackerel',), + 12: ('Dog Fish', + 'Bull Dog Fish', + 'Hot Dog Fish', + 'Dalmatian Dog Fish', + 'Puppy Dog Fish'), + 14: ('Amore Eel', 'Electric Amore Eel'), + 16: ('Nurse Shark', 'Clara Nurse Shark', 'Florence Nurse Shark'), + 18: ('King Crab', 'Alaskan King Crab', 'Old King Crab'), + 20: ('Moon Fish', + 'Full Moon Fish', + 'Half Moon Fish', + 'New Moon Fish', + 'Crescent Moon Fish', + 'Harvest Moon Fish'), + 22: ('Sea Horse', + 'Rocking Sea Horse', + 'Clydesdale Sea Horse', + 'Arabian Sea Horse'), + 24: ('Pool Shark', + 'Kiddie Pool Shark', + 'Swimming Pool Shark', + 'Olympic Pool Shark'), + 26: ('Brown Bear Acuda', + 'Black Bear Acuda', + 'Koala Bear Acuda', + 'Honey Bear Acuda', + 'Polar Bear Acuda', + 'Panda Bear Acuda', + 'Kodiac Bear Acuda', + 'Grizzly Bear Acuda'), + 28: ('Cutthroat Trout', 'Captain Cutthroat Trout', 'Scurvy Cutthroat Trout'), + 30: ('Piano Tuna', + 'Grand Piano Tuna', + 'Baby Grand Piano Tuna', + 'Upright Piano Tuna', + 'Player Piano Tuna'), + 32: ('Peanut Butter & Jellyfish', + 'Grape PB&J Fish', + 'Crunchy PB&J Fish', + 'Strawberry PB&J Fish', + 'Concord Grape PB&J Fish'), + 34: ('Devil Ray',)} +CogPartNames = ('Upper Left Leg', + 'Lower Left Leg', + 'Left Foot', + 'Upper Right Leg', + 'Lower Right Leg', + 'Right Foot', + 'Left Shoulder', + 'Right Shoulder', + 'Chest', + 'Health Meter', + 'Pelvis', + 'Upper Left Arm', + 'Lower Left Arm', + 'Left Hand', + 'Upper Right Arm', + 'Lower Right Arm', + 'Right Hand') +CogPartNamesSimple = ('Upper Torso',) +SellbotLegFactorySpecMainEntrance = 'Front Entrance' +SellbotLegFactorySpecLobby = 'Lobby' +SellbotLegFactorySpecLobbyHallway = 'Lobby Hallway' +SellbotLegFactorySpecGearRoom = 'Gear Room' +SellbotLegFactorySpecBoilerRoom = 'Boiler Room' +SellbotLegFactorySpecEastCatwalk = 'East Catwalk' +SellbotLegFactorySpecPaintMixer = 'Paint Mixer' +SellbotLegFactorySpecPaintMixerStorageRoom = 'Paint Mixer Storage Room' +SellbotLegFactorySpecWestSiloCatwalk = 'West Silo Catwalk' +SellbotLegFactorySpecPipeRoom = 'Pipe Room' +SellbotLegFactorySpecDuctRoom = 'Duct Room' +SellbotLegFactorySpecSideEntrance = 'Side Entrance' +SellbotLegFactorySpecStomperAlley = 'Stomper Alley' +SellbotLegFactorySpecLavaRoomFoyer = 'Lava Room Foyer' +SellbotLegFactorySpecLavaRoom = 'Lava Room' +SellbotLegFactorySpecLavaStorageRoom = 'Lava Storage Room' +SellbotLegFactorySpecWestCatwalk = 'West Catwalk' +SellbotLegFactorySpecOilRoom = 'Oil Room' +SellbotLegFactorySpecLookout = 'Lookout' +SellbotLegFactorySpecWarehouse = 'Warehouse' +SellbotLegFactorySpecOilRoomHallway = 'Oil Room Hallway' +SellbotLegFactorySpecEastSiloControlRoom = 'East Silo Control Room' +SellbotLegFactorySpecWestSiloControlRoom = 'West Silo Control Room' +SellbotLegFactorySpecCenterSiloControlRoom = 'Center Silo Control Room' +SellbotLegFactorySpecEastSilo = 'East Silo' +SellbotLegFactorySpecWestSilo = 'West Silo' +SellbotLegFactorySpecCenterSilo = 'Center Silo' +SellbotLegFactorySpecEastSiloCatwalk = 'East Silo Catwalk' +SellbotLegFactorySpecWestElevatorShaft = 'West Elevator Shaft' +SellbotLegFactorySpecEastElevatorShaft = 'East Elevator Shaft' +FishBingoBingo = 'BINGO!' +FishBingoVictory = 'VICTORY!!' +FishBingoJackpot = 'JACKPOT!' +FishBingoGameOver = 'GAME OVER' +FishBingoIntermission = 'Intermission\nEnds In:' +FishBingoNextGame = 'Next Game\nStarts In:' +FishBingoTypeNormal = 'Classic' +FishBingoTypeCorners = 'Four Corners' +FishBingoTypeDiagonal = 'Diagonals' +FishBingoTypeThreeway = 'Three Way' +FishBingoTypeBlockout = 'BLOCKOUT!' +FishBingoStart = "It's time for Fish Bingo! Go to any available pier to play!" +FishBingoOngoing = 'Welcome! Fish Bingo is currently in progress.' +FishBingoEnd = 'Hope you had fun playing Fish Bingo.' +FishBingoHelpMain = 'Welcome to Toontown Fish Bingo! Everyone at the pond works together to fill the card before time runs out.' +FishBingoHelpFlash = 'When you catch a fish, click on one of the flashing squares to mark the card.' +FishBingoHelpNormal = 'This is a Classic Bingo card. Mark any row down, across or diagonally to win.' +FishBingoHelpDiagonals = 'Mark both of the diagonals to win.' +FishBingoHelpCorners = 'An easy Corners card. Mark all four corners to win.' +FishBingoHelpThreeway = "Three-way. Mark both diagonals and the middle row to win. This one isn't easy!" +FishBingoHelpBingo = 'Bingo!' +FishBingoHelpBlockout = 'Blockout!. Mark the entire card to win. You are competing against all the other ponds for a huge jackpot!' +FishBingoOfferToSellFish = 'Your fish bucket is full. Would you like to sell your fish?' +FishBingoJackpotWin = 'Win %s jellybeans!' +ResistanceToonupMenu = 'Toon-up' +ResistanceToonupItem = '%s Toon-up' +ResistanceToonupItemMax = 'Max' +ResistanceToonupChat = 'Toons of the World, Toon-up!' +ResistanceRestockMenu = 'Gag-up' +ResistanceRestockItem = 'Gag-up %s' +ResistanceRestockItemAll = 'All' +ResistanceRestockChat = 'Toons of the World, Gag-up!' +ResistanceMoneyMenu = 'jellybeans' +ResistanceMoneyItem = '%s jellybeans' +ResistanceMoneyChat = 'Toons of the World, Spend Wisely!' +ResistanceEmote1 = NPCToonNames[9228] + ': Welcome to the Resistance!' +ResistanceEmote2 = NPCToonNames[9228] + ': Use your new emote to identify yourself to other members.' +ResistanceEmote3 = NPCToonNames[9228] + ': Good luck!' +KartUIExit = 'Leave Kart' +KartShop_Cancel = lCancel +KartShop_BuyKart = 'Buy Kart' +KartShop_BuyAccessories = 'Buy Accessories' +KartShop_BuyAccessory = 'Buy Accessory' +KartShop_Cost = 'Cost: %d Tickets' +KartShop_ConfirmBuy = 'Buy the %s for %d Tickets?' +KartShop_NoAvailableAcc = 'No available accessories of this type' +KartShop_FullTrunk = 'Your trunk is full.' +KartShop_ConfirmReturnKart = 'Are you sure you want to return your current Kart?' +KartShop_ConfirmBoughtTitle = 'Congratulations!' +KartShop_NotEnoughTickets = 'Not Enough Tickets!' +KartView_Rotate = 'Rotate' +KartView_Right = 'Right' +KartView_Left = 'Left' +StartingBlock_NotEnoughTickets = "You don't have enough tickets! Try a practice race instead." +StartingBlock_NoBoard = 'Boarding has ended for this race. Please wait for the next race to begin.' +StartingBlock_NoKart = 'You need a kart first! Try asking one of the clerks in the Kart Shop.' +StartingBlock_Occupied = 'This block is currently occupied! Please try another spot.' +StartingBlock_TrackClosed = 'Sorry, this track is closed for remodeling.' +StartingBlock_EnterPractice = 'Would you like to enter a practice race?' +StartingBlock_EnterNonPractice = 'Would you like to enter a %s race for %s tickets?' +StartingBlock_EnterShowPad = 'Would you like to park your car here?' +StartingBlock_KickSoloRacer = 'Toon Battle and Grand Prix races require two or more racers.' +StartingBlock_Loading = 'Going to the Race!' +LeaderBoard_Time = 'Time' +LeaderBoard_Name = 'Racer Name' +LeaderBoard_Daily = 'Daily Scores' +LeaderBoard_Weekly = 'Weekly Scores' +LeaderBoard_AllTime = 'All Time Best Scores' +RecordPeriodStrings = [LeaderBoard_Daily, LeaderBoard_Weekly, LeaderBoard_AllTime] +KartRace_RaceNames = ['Practice', 'Toon Battle', 'Grand Prix'] +from toontown.racing import RaceGlobals +KartRace_Go = 'Go!' +KartRace_Reverse = ' Rev' +KartRace_TrackNames = {RaceGlobals.RT_Speedway_1: 'Screwball Stadium', + RaceGlobals.RT_Speedway_1_rev: 'Screwball Stadium' + KartRace_Reverse, + RaceGlobals.RT_Rural_1: 'Rustic Raceway', + RaceGlobals.RT_Rural_1_rev: 'Rustic Raceway' + KartRace_Reverse, + RaceGlobals.RT_Urban_1: 'City Circuit', + RaceGlobals.RT_Urban_1_rev: 'City Circuit' + KartRace_Reverse, + RaceGlobals.RT_Speedway_2: 'Corkscrew Coliseum', + RaceGlobals.RT_Speedway_2_rev: 'Corkscrew Coliseum' + KartRace_Reverse, + RaceGlobals.RT_Rural_2: 'Airborne Acres', + RaceGlobals.RT_Rural_2_rev: 'Airborne Acres' + KartRace_Reverse, + RaceGlobals.RT_Urban_2: 'Blizzard Boulevard', + RaceGlobals.RT_Urban_2_rev: 'Blizzard Boulevard' + KartRace_Reverse} +KartRace_Unraced = 'N/A' +KartDNA_KartNames = {0: 'Cruiser', + 1: 'Roadster', + 2: 'Toon Utility Vehicle'} +KartDNA_AccNames = {1000: 'Air Cleaner', + 1001: 'Four Barrel', + 1002: 'Flying Eagle', + 1003: 'Steer Horns', + 1004: 'Straight Six', + 1005: 'Small Scoop', + 1006: 'Single Overhead', + 1007: 'Medium Scoop', + 1008: 'Single Barrel', + 1009: 'Flugle Horn', + 1010: 'Striped Scoop', + 2000: 'Space Wing', + 2001: 'Patched Spare', + 2002: 'Roll Cage', + 2003: 'Single Fin', + 2004: 'Double-decker Wing', + 2005: 'Single Wing', + 2006: 'Standard Spare', + 2007: 'Single Fin', + 2008: 'sp9', + 2009: 'sp10', + 3000: 'Dueling Horns', + 3001: "Freddie's Fenders", + 3002: 'Cobalt Running Boards', + 3003: 'Cobra Sidepipes', + 3004: 'Straight Sidepipes', + 3005: 'Scalloped Fenders', + 3006: 'Carbon Running Boards', + 3007: 'Wood Running Boards', + 3008: 'fw9', + 3009: 'fw10', + 4000: 'Curly Tailpipes', + 4001: 'Splash Fenders', + 4002: 'Dual Exhaust', + 4003: 'Plain Dual Fins', + 4004: 'Plain Mudflaps', + 4005: 'Quad Exhaust', + 4006: 'Dual Flares', + 4007: 'Mega Exhaust', + 4008: 'Striped Dual Fins', + 4009: 'Bubble Duals Fins', + 4010: 'Striped Mudflaps', + 4011: 'Mickey Mudflaps', + 4012: 'Scalloped Mudflaps', + 5000: 'Turbo', + 5001: 'Moon', + 5002: 'Patched', + 5003: 'Three Spoke', + 5004: 'Paint Lid', + 5005: 'Heart', + 5006: 'Mickey', + 5007: 'Five Bolt', + 5008: 'Daisy', + 5009: 'Basketball', + 5010: 'Hypno', + 5011: 'Tribal', + 5012: 'Gemstone', + 5013: 'Five Spoke', + 5014: 'Knockoff', + 6000: 'Number Five', + 6001: 'Splatter', + 6002: 'Checkerboard', + 6003: 'Flames', + 6004: 'Hearts', + 6005: 'Bubbles', + 6006: 'Tiger', + 6007: 'Flowers', + 6008: 'Lightning', + 6009: 'Angel', + 7000: 'Chartreuse', + 7001: 'Peach', + 7002: 'Bright Red', + 7003: 'Red', + 7004: 'Maroon', + 7005: 'Sienna', + 7006: 'Brown', + 7007: 'Tan', + 7008: 'Coral', + 7009: 'Orange', + 7010: 'Yellow', + 7011: 'Cream', + 7012: 'Citrine', + 7013: 'Lime', + 7014: 'Sea Green', + 7015: 'Green', + 7016: 'Light Blue', + 7017: 'Aqua', + 7018: 'Blue', + 7019: 'Periwinkle', + 7020: 'Royal Blue', + 7021: 'Slate Blue', + 7022: 'Purple', + 7023: 'Lavender', + 7024: 'Pink', + 7025: 'Plum', + 7026: 'Black'} +RaceHoodSpeedway = 'Speedway' +RaceHoodRural = 'Rural' +RaceHoodUrban = 'Urban' +RaceTypeCircuit = 'Tournament' +RaceQualified = 'qualified' +RaceSwept = 'swept' +RaceWon = 'won' +Race = 'race' +Races = 'races' +Total = 'total' +GrandTouring = 'Grand Touring' + +def getTrackGenreString(genreId): + genreStrings = ['Speedway', 'Country', 'City'] + return genreStrings[genreId].lower() + + +def getTunnelSignName(trackId, padId): + if trackId == 2 and padId == 0: + return 'tunne1l_citysign' + elif trackId == 1 and padId == 0: + return 'tunnel_countrysign1' + else: + genreId = RaceGlobals.getTrackGenre(trackId) + return 'tunnel%s_%ssign' % (padId + 1, RaceGlobals.getTrackGenreString(genreId)) + + +KartTrophyDescriptions = [str(RaceGlobals.QualifiedRaces[0]) + ' ' + RaceHoodSpeedway + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[1]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[2]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[0]) + ' ' + RaceHoodRural + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[1]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[2]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[0]) + ' ' + RaceHoodUrban + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[1]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.QualifiedRaces[2]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.TotalQualifiedRaces) + ' ' + Total + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.WonRaces[0]) + ' ' + RaceHoodSpeedway + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonRaces[1]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[2]) + ' ' + RaceHoodSpeedway + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[0]) + ' ' + RaceHoodRural + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonRaces[1]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[2]) + ' ' + RaceHoodRural + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[0]) + ' ' + RaceHoodUrban + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonRaces[1]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonRaces[2]) + ' ' + RaceHoodUrban + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.TotalWonRaces) + ' ' + Total + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonCircuitRaces[0]) + ' ' + RaceTypeCircuit + ' ' + Race + ' ' + RaceQualified, + str(RaceGlobals.WonCircuitRaces[1]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.WonCircuitRaces[2]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceQualified, + str(RaceGlobals.WonCircuitRaces[0]) + ' ' + RaceTypeCircuit + ' ' + Race + ' ' + RaceWon, + str(RaceGlobals.WonCircuitRaces[1]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.WonCircuitRaces[2]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceWon, + str(RaceGlobals.SweptCircuitRaces[0]) + ' ' + RaceTypeCircuit + ' ' + Race + ' ' + RaceSwept, + str(RaceGlobals.SweptCircuitRaces[1]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceSwept, + str(RaceGlobals.SweptCircuitRaces[2]) + ' ' + RaceTypeCircuit + ' ' + Races + ' ' + RaceSwept, + GrandTouring, + str(RaceGlobals.TrophiesPerCup) + ' Kart Racing trophies won! Laff point boost!', + str(RaceGlobals.TrophiesPerCup * 2) + ' Kart Racing trophies won! Laff point boost!', + str(RaceGlobals.TrophiesPerCup * 3) + ' Kart Racing trophies won! Laff point boost!'] +KartRace_TitleInfo = 'Get Ready to Race' +KartRace_SSInfo = 'Welcome to Screwball Stadium!\nPut the pedal to the metal and hang on tight!\n' +KartRace_CoCoInfo = 'Welcome to Corkscrew Coliseum!\nUse the banked turns to keep your speed up!\n' +KartRace_RRInfo = 'Welcome to Rustic Raceway!\nPlease be kind to the fauna and stay on the track!\n' +KartRace_AAInfo = 'Welcome to Airborne Acres!\nHold onto your hats! It looks bumpy up ahead...\n' +KartRace_CCInfo = 'Welcome to City Circuit!\nWatch out for pedestrians as you speed through downtown!\n' +KartRace_BBInfo = 'Welcome to Blizzard Boulevard!\nWatch your speed. There might be ice out there.\n' +KartRace_GeneralInfo = 'Use Control to throw gags you pick up on the track, and the arrow keys to control your kart.' +KartRace_TrackInfo = {RaceGlobals.RT_Speedway_1: KartRace_SSInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Speedway_1_rev: KartRace_SSInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Speedway_2: KartRace_CoCoInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Speedway_2_rev: KartRace_CoCoInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_1: KartRace_RRInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_1_rev: KartRace_RRInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_2: KartRace_AAInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Rural_2_rev: KartRace_AAInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_1: KartRace_CCInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_1_rev: KartRace_CCInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_2: KartRace_BBInfo + KartRace_GeneralInfo, + RaceGlobals.RT_Urban_2_rev: KartRace_BBInfo + KartRace_GeneralInfo} +KartRecordStrings = {RaceGlobals.Daily: 'daily', + RaceGlobals.Weekly: 'weekly', + RaceGlobals.AllTime: 'all time'} +KartRace_FirstSuffix = 'st' +KartRace_SecondSuffix = ' nd' +KartRace_ThirdSuffix = ' rd' +KartRace_FourthSuffix = ' th' +KartRace_WrongWay = 'Wrong\nWay!' +KartRace_LapText = 'Lap %s' +KartRace_FinalLapText = 'Final Lap!' +KartRace_Exit = 'Exit Race' +KartRace_NextRace = 'Next Race' +KartRace_Leave = 'Leave Race' +KartRace_Qualified = 'Qualified!' +KartRace_Record = 'Record!' +KartRace_RecordString = 'You have set a new %s record for %s! Your bonus is %s tickets.' +KartRace_Tickets = 'Tickets' +KartRace_Exclamations = '!' +KartRace_Deposit = 'Deposit' +KartRace_Winnings = 'Winnings' +KartRace_Bonus = 'Bonus' +KartRace_RaceTotal = 'Race Total' +KartRace_CircuitTotal = 'Circuit Total' +KartRace_Trophies = 'Trophies' +KartRace_Zero = '0' +KartRace_Colon = ':' +KartRace_TicketPhrase = '%s ' + KartRace_Tickets +KartRace_DepositPhrase = KartRace_Deposit + KartRace_Colon + '\n' +KartRace_QualifyPhrase = 'Qualify:\n' +KartRace_RaceTimeout = 'You timed out of that race. Your tickets have been refunded. Keep trying!' +KartRace_RaceTimeoutNoRefund = 'You timed out of that race. Your tickets have not been refunded because the Grand Prix had already started. Keep trying!' +KartRace_RacerTooSlow = 'You took too long to finish the race. Your tickets have not been refunded. Keep trying!' +KartRace_PhotoFinish = 'Photo Finish!' +KartRace_CircuitPoints = 'Circuit Points' +CircuitRaceStart = 'The Toontown Grand Prix at Goofy Speedway is about to begin! To win, collect the most points in three consecutive races!' +CircuitRaceOngoing = 'Welcome! The Toontown Grand Prix is currently in progress.' +CircuitRaceEnd = "That's all for today's Toontown Grand Prix at Goofy Speedway. See you next week!" +TrickOrTreatMsg = 'You have already\nfound this treat!' +WinterCarolingMsg = 'You have already been caroling here!' +LawbotBossTempIntro0 = "Hmmm what's on the docket today?" +LawbotBossTempIntro1 = 'Aha, we have a Toon on trial!' +LawbotBossTempIntro2 = "The prosecution's case is strong." +LawbotBossTempIntro3 = 'And here are the public defenders.' +LawbotBossTempIntro4 = "Wait a minute... You're Toons!" +LawbotBossTempJury1 = 'Jury selection will now commence.' +LawbotBossHowToGetEvidence = 'Touch the witness stand to get evidence.' +LawbotBossTrialChat1 = 'Court is now in session' +LawbotBossHowToThrowPies = 'Press the Delete key to throw the evidence\n at the lawyers or into the scale!' +LawbotBossNeedMoreEvidence = 'You need to get more evidence!' +LawbotBossDefenseWins1 = 'Impossible! The defense won?' +LawbotBossDefenseWins2 = 'No. I declare a mistrial! A new one will be scheduled.' +LawbotBossDefenseWins3 = "Hrrmpphh. I'll be in my chambers." +LawbotBossProsecutionWins = 'I find in favor of the plaintiff' +LawbotBossReward = 'I award a promotion and the ability to summon Cogs' +LawbotBossLeaveCannon = 'Leave cannon' +LawbotBossPassExam = 'Bah, so you passed the bar exam.' +LawbotBossTaunts = ['%s, I find you in contempt of court!', + 'Objection sustained!', + 'Strike that from the record.', + 'Your appeal has been rejected. I sentence you to sadness!', + 'Order in the court!'] +LawbotBossAreaAttackTaunt = "You're all in contempt of court!" +WitnessToonName = 'Bumpy Bumblebehr' +WitnessToonPrepareBattleTwo = "Oh no! They're putting only Cogs on the jury!\x07Quick, use the cannons and shoot some Toon jurors into the jury chairs.\x07We need %d to get a balanced scale." +WitnessToonNoJuror = 'Oh oh, no Toon jurors. This will be a tough trial.' +WitnessToonOneJuror = 'Cool! There is 1 Toon in the jury!' +WitnessToonSomeJurors = 'Cool! There are %d Toons in the jury!' +WitnessToonAllJurors = 'Awesome! All the jurors are Toons!' +WitnessToonPrepareBattleThree = 'Hurry, touch the witness stand to get evidence.\x07Press the Delete key to throw the evidence at the lawyers, or at the defense pan.' +WitnessToonCongratulations = "You did it! Thank you for a spectacular defense!\x07Here, take these papers the Chief Justice left behind.\x07With it you'll be able to summon Cogs from your Cog Gallery page." +WitnessToonLastPromotion = "\x07Wow, you've reached level %s on your Cog Suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog Suit anymore, but you can certainly keep working for the Resistance!" +WitnessToonHPBoost = "\x07You've done a lot of work for the Resistance.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +WitnessToonMaxed = '\x07I see that you have a level %s Cog Suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to defend more Toons!' +WitnessToonBonus = 'Wonderful! All the lawyers are stunned. Your evidence weight is %s times heavier for %s seconds' +WitnessToonJuryWeightBonusSingular = {6: 'This is a tough case. You seated %d Toon juror, so your evidence has a bonus weight of %d.', + 7: 'This is a very tough case. You seated %d Toon juror, so your evidence has a bonus weight of %d.', + 8: 'This is the toughest case. You seated %d Toon juror, so your evidence has a bonus weight of %d.'} +WitnessToonJuryWeightBonusPlural = {6: 'This is a tough case. You seated %d Toon jurors, so your evidence has a bonus weight of %d.', + 7: 'This is a very tough case. You seated %d Toon jurors, so your evidence has a bonus weight of %d.', + 8: 'This is the toughest case. You seated %d Toon jurors, so your evidence has a bonus weight of %d.'} +IssueSummons = 'Summon' +SummonDlgTitle = 'Issue a Cog Summons' +SummonDlgButton1 = 'Summon a Cog' +SummonDlgButton2 = 'Summon a Cog Building' +SummonDlgButton3 = 'Summon a Cog Invasion' +SummonDlgSingleConf = 'Would you like to issue a summons to a %s?' +SummonDlgBuildingConf = 'Would you like to summon a %s to a nearby Toon building?' +SummonDlgInvasionConf = 'Would you like to summon a %s invasion?' +SummonDlgNumLeft = 'You have %s left.' +SummonDlgDelivering = 'Delivering Summons...' +SummonDlgSingleSuccess = 'You have successfully summoned the Cog.' +SummonDlgSingleBadLoc = "Sorry, Cogs aren't allowed here. Try somewhere else." +SummonDlgBldgSuccess = 'You have successfully summoned the Cogs. %s has agreed to let them temporarily take over %s!' +SummonDlgBldgSuccess2 = 'You have successfully summoned the Cogs. A Shopkeeper has agreed to let them temporarily take over their building!' +SummonDlgBldgBadLoc = 'Sorry, there are no Toon buildings nearby for the Cogs to take over.' +SummonDlgInvasionSuccess = "You have successfully summoned the Cogs. It's an invasion!" +SummonDlgInvasionBusy = 'A %s cannot be found now. Try again when the Cog invasion is over.' +SummonDlgInvasionFail = 'Sorry, the Cog invasion has failed.' +SummonDlgShopkeeper = 'The Shopkeeper ' +PolarPlaceEffect1 = NPCToonNames[3306] + ': Welcome to Polar Place!' +PolarPlaceEffect2 = NPCToonNames[3306] + ': Try this on for size.' +PolarPlaceEffect3 = NPCToonNames[3306] + ': Your new look will only work in ' + lTheBrrrgh + '.' +GreenToonEffectMsg = NPCToonNames[5312] + ': You look Toontastic in green!' +LaserGameMine = 'Skull Finder!' +LaserGameRoll = 'Matching' +LaserGameAvoid = 'Avoid the Skulls' +LaserGameDrag = 'Drag three of a color in a row' +LaserGameDefault = 'Unknown Game' +PinballHiScore = 'High Score: %s\n' +PinballHiScoreAbbrev = '...' +PinballYourBestScore = 'Your Best Score:\n' +PinballScore = 'Score: %d x %d = ' +PinballScoreHolder = '%s\n' +GagTreeFeather = 'Feather Gag Tree' +GagTreeJugglingBalls = 'Juggling Balls Gag Tree' +StatuaryFountain = 'Fountain' +StatuaryDonald = 'Donald Statue' +StatuaryMinnie = 'Minnie Statue' +StatuaryMickey1 = 'Mickey Statue' +StatuaryMickey2 = 'Mickey Fountain' +StatuaryToon = 'Toon Statue' +StatuaryToonWave = 'Toon Wave Statue' +StatuaryToonVictory = 'Toon Victory Statue' +StatuaryToonCrossedArms = 'Toon Authority Statue' +StatuaryToonThinking = 'Toon Embrace Statue' +StatuaryMeltingSnowman = 'Melting Snowman' +StatuaryMeltingSnowDoodle = 'Melting SnowDoodle' +StatuaryGardenAccelerator = 'Insta-Grow Fertilizer' +AnimatedStatuaryFlappyCog = 'Flappy Cog' +FlowerColorStrings = ['Red', + 'Orange', + 'Violet', + 'Blue', + 'Pink', + 'Yellow', + 'White', + 'Green'] +FlowerSpeciesNames = {49: 'Daisy', + 50: 'Tulip', + 51: 'Carnation', + 52: 'Lily', + 53: 'Daffodil', + 54: 'Pansy', + 55: 'Petunia', + 56: 'Rose'} +FlowerFunnyNames = {49: ('School Daisy', + 'Lazy Daisy', + 'Midsummer Daisy', + 'Freshasa Daisy', + 'Whoopsie Daisy', + 'Upsy Daisy', + 'Crazy Daisy', + 'Hazy Dazy'), + 50: ('Onelip', 'Twolip', 'Threelip'), + 51: ('What-in Carnation', + 'Instant Carnation', + 'Hybrid Carnation', + 'Side Carnation', + 'Model Carnation'), + 52: ('Lily-of-the-Alley', + 'Lily Pad', + 'Tiger Lily', + 'Livered Lily', + 'Chili Lily', + 'Silly Lily', + 'Indubitab Lily', + 'Dilly Lilly'), + 53: ('Laff-o-dil', + 'Daffy Dill', + 'Giraff-o-dil', + 'Time and a half-o-dil'), + 54: ('Dandy Pansy', + 'Chim Pansy', + 'Potsen Pansy', + 'Marzi Pansy', + 'Smarty Pansy'), + 55: ('Car Petunia', 'Platoonia'), + 56: ("Summer's Last Rose", + 'Corn Rose', + 'Tinted Rose', + 'Stinking Rose', + 'Istilla Rose')} +FlowerVarietyNameFormat = '%s %s' +FlowerUnknown = '????' +FloweringNewEntry = 'New Entry' +ShovelNameDict = {0: 'Tin', + 1: 'Bronze', + 2: 'Silver', + 3: 'Gold'} +WateringCanNameDict = {0: 'Small', + 1: 'Medium', + 2: 'Large', + 3: 'Huge'} +GardeningPlant = 'Plant' +GardeningWater = 'Water' +GardeningRemove = 'Remove' +GardeningPick = 'Pick' +GardeningFull = 'Full' +GardeningSkill = 'Skill' +GardeningWaterSkill = 'Water Skill' +GardeningShovelSkill = 'Shovel Skill' +GardeningNoSkill = 'No Skill Up' +GardeningPlantFlower = 'Plant\nFlower' +GardeningPlantTree = 'Plant\nTree' +GardeningPlantItem = 'Plant\nItem' +PlantingGuiOk = 'Plant' +PlantingGuiCancel = 'Cancel' +PlantingGuiReset = 'Reset' +GardeningChooseBeans = 'Choose the jellybeans you want to plant.' +GardeningChooseBeansItem = 'Choose the jellybeans / item you want to plant.' +GardeningChooseToonStatue = 'Choose the toon you want to create a statue of.' +GardenShovelLevelUp = "Congratulations you've earned a %(shovel)s! You've mastered the %(oldbeans)d bean flower! To progress you should pick %(newbeans)d bean flowers." +GardenShovelSkillLevelUp = "Congratulations! You've mastered the %(oldbeans)d bean flower! To progress you should pick %(newbeans)d bean flowers." +GardenShovelSkillMaxed = "Amazing! You've maxed out your shovel skill!" +GardenWateringCanLevelUp = "Congratulations you've earned a new watering can!" +GardenMiniGameWon = "Congratulations you've watered the plant!" +ShovelTin = 'Tin Shovel' +ShovelSteel = 'Bronze Shovel' +ShovelSilver = 'Silver Shovel' +ShovelGold = 'Gold Shovel' +WateringCanSmall = 'Small Watering Can' +WateringCanMedium = 'Medium Watering Can' +WateringCanLarge = 'Large Watering Can' +WateringCanHuge = 'Huge Watering Can' +BeanColorWords = ('red', + 'green', + 'orange', + 'violet', + 'blue', + 'pink', + 'yellow', + 'cyan', + 'silver') +PlantItWith = ' Plant with %s.' +MakeSureWatered = ' Make sure all your plants are watered first.' +UseFromSpecialsTab = ' Use from the specials tab of the garden page.' +UseSpecial = 'Use Special' +UseSpecialBadLocation = 'You can only use that in your garden.' +UseSpecialSuccess = 'Success! Your watered plants just grew.' +ConfirmWiltedFlower = '%(plant)s is wilted. Are you sure you want to remove it? It will not go into your flower basket, nor will you get an increase in skill.' +ConfirmUnbloomingFlower = '%(plant)s is not blooming. Are you sure you want to remove it? It will not go into your flower basket, nor will you get an increase in skill.' +ConfirmNoSkillupFlower = 'Are you sure you want to pick the %(plant)s? It will go into your flower basket, but you will NOT get an increase in skill.' +ConfirmSkillupFlower = 'Are you sure you want to pick the %(plant)s? It will go into your flower basket. You will also get an increase in skill.' +ConfirmMaxedSkillFlower = "Are you sure you want to pick the %(plant)s? It will go into your flower basket. You will NOT get an increase in skill since you've maximized it already." +ConfirmBasketFull = 'Your flower basket is full. Sell some flowers first.' +ConfirmRemoveTree = 'Are you sure you want to remove the %(tree)s?' +ConfirmWontBeAbleToHarvest = " If you remove this tree, you won't be able to harvest gags from the higher level trees." +ConfirmRemoveStatuary = 'Are you sure you want to permanently delete the %(item)s?' +ResultPlantedSomething = 'Congratulations! You just planted a %s.' +ResultPlantedSomethingAn = 'Congratulations! You just planted an %s.' +ResultPlantedNothing = "That didn't work. Please try a different combination of jellybeans." +GardenGagTree = ' Gag Tree' +GardenUberGag = 'Uber Gag' + +def getRecipeBeanText(beanTuple): + retval = '' + if not beanTuple: + return retval + allTheSame = True + for index in range(len(beanTuple)): + if index + 1 < len(beanTuple): + if not beanTuple[index] == beanTuple[index + 1]: + allTheSame = False + break + + if allTheSame: + if len(beanTuple) > 1: + retval = '%d %s jellybeans' % (len(beanTuple), BeanColorWords[beanTuple[0]]) + else: + retval = 'a %s jellybean' % BeanColorWords[beanTuple[0]] + else: + retval += 'a' + maxBeans = len(beanTuple) + for index in range(maxBeans): + if index == maxBeans - 1: + retval += ' and %s jellybean' % BeanColorWords[beanTuple[index]] + elif index == 0: + retval += ' %s' % BeanColorWords[beanTuple[index]] + else: + retval += ', %s' % BeanColorWords[beanTuple[index]] + + return retval + + +GardenTextMagicBeans = 'Magic Beans' +GardenTextMagicBeansB = 'Some Other Beans' +GardenSpecialDiscription = 'This text should explain how to use a certain garden special' +GardenSpecialDiscriptionB = 'This text should explain how to use a certain garden special, in yo face foo!' +GardenTrophyAwarded = 'Wow! You collected %s of %s flowers. That deserves a trophy and a Laff boost!' +GardenTrophyNameDict = {0: 'Wheelbarrow', + 1: 'Shovels', + 2: 'Flower', + 3: 'Watering Can', + 4: 'Shark', + 5: 'Swordfish', + 6: 'Killer Whale'} +SkillTooLow = 'Skill\nToo Low' +NoGarden = 'No\nGarden' + +def isVowelStart(str): + retval = False + if str and len(str) > 0: + vowels = ['A', + 'E', + 'I', + 'O', + 'U'] + firstLetter = str.upper()[0:1] + if firstLetter in vowels: + retval = True + return retval + + +def getResultPlantedSomethingSentence(flowerName): + if isVowelStart(flowerName): + retval = ResultPlantedSomethingAn % flowerName + else: + retval = ResultPlantedSomething % flowerName + return retval + + +TravelGameTitle = 'Trolley Tracks' +TravelGameInstructions = 'Click up or down to set your number of votes. Click the vote button to cast it. Reach your secret goal to get bonus beans. Earn more votes by doing well in the other games.' +TravelGameRemainingVotes = 'Remaining Votes:' +TravelGameUse = 'Use' +TravelGameVotesWithPeriod = 'votes.' +TravelGameVotesToGo = 'votes to go' +TravelGameVoteToGo = 'vote to go' +TravelGameUp = 'UP.' +TravelGameDown = 'DOWN.' +TravelGameVoteWithExclamation = 'Vote!' +TravelGameWaitingChoices = 'Waiting for other players to vote...' +TravelGameDirections = ['UP', 'DOWN'] +TravelGameTotals = 'Totals ' +TravelGameReasonVotes = 'The trolley is moving %(dir)s, winning by %(numVotes)d votes.' +TravelGameReasonVotesPlural = 'The trolley is moving %(dir)s, winning by %(numVotes)d votes.' +TravelGameReasonVotesSingular = 'The trolley is moving %(dir)s, winning by %(numVotes)d vote.' +TravelGameReasonPlace = '%(name)s breaks the tie. The trolley is moving %(dir)s.' +TravelGameReasonRandom = 'The trolley is randomly moving %(dir)s.' +TravelGameOneToonVote = '%(name)s used %(numVotes)s votes to go %(dir)s\n' +TravelGameBonusBeans = '%(numBeans)d Beans' +TravelGamePlaying = 'Up next, the %(game)s trolley game.' +TravelGameGotBonus = '%(name)s got a bonus of %(numBeans)s jellybeans!' +TravelGameNoOneGotBonus = 'No one reached their secret goal. Everyone gets 1 jellybean.' +TravelGameConvertingVotesToBeans = 'Converting some votes to jellybeans...' +TravelGameGoingBackToShop = "Only 1 player left. Going to Goofy's Gag Shop." +PairingGameTitle = 'Toon Memory Game' +PairingGameInstructions = 'Press Delete to open a card. Match 2 cards to score a point. Make a match with the bonus glow and earn an extra point. Earn more points by keeping the flips low.' +PairingGameInstructionsMulti = 'Press Delete to open a card. Press Control to signal another player to open a card. Match 2 cards to score a point. Make a match with the bonus glow and earn an extra point. Earn more points by keeping the flips low.' +PairingGamePerfect = 'PERFECT!!' +PairingGameFlips = 'Flips:' +PairingGamePoints = 'Points:' +TrolleyHolidayStart = 'Trolley Tracks is about to begin! Board any trolley with 2 or more toons to play.' +TrolleyHolidayOngoing = 'Welcome! Trolley Tracks is currently in progress.' +TrolleyHolidayEnd = "That's all for today's Trolley Tracks. See you next week!" +TrolleyWeekendStart = 'Trolley Tracks Weekend is about to begin! Board any trolley with 2 or more toons to play.' +TrolleyWeekendEnd = "That's all for Trolley Tracks Weekend." +VineGameTitle = 'Jungle Vines' +VineGameInstructions = 'Get to the rightmost vine in time. Press Up or Down to climb the vine. Press Left or Right to change facing and jump. The lower you are on the vine, the faster you jump off. Collect the bananas if you can, but avoid the bats and spiders.' +ValentinesDayStart = "Happy ValenToon's Day!" +ValentinesDayEnd = "That's all for ValenToon's Day!" +GolfCourseNames = {0: 'Walk In The Par', + 1: 'Hole Some Fun', + 2: 'The Hole Kit And Caboodle'} +GolfHoleNames = {0: 'Whole In Won', + 1: 'No Putts About It', + 2: 'Down The Hatch', + 3: 'Seeing Green', + 4: 'Hot Links', + 5: 'Peanut Putter', + 6: 'Swing-A-Long', + 7: 'Afternoon Tee', + 8: 'Hole In Fun', + 9: 'Rock And Roll In', + 10: 'Bogey Nights', + 11: 'Tea Off Time', + 12: 'Holey Mackerel!', + 13: 'One Little Birdie', + 14: 'At The Drive In', + 15: 'Swing Time', + 16: 'Hole On The Range', + 17: 'Second Wind', + 18: 'Whole In Won-2', + 19: 'No Putts About It-2', + 20: 'Down The Hatch-2', + 21: 'Seeing Green-2', + 22: 'Hot Links-2', + 23: 'Peanut Putter-2', + 24: 'Swing-A-Long-2', + 25: 'Afternoon Tee-2', + 26: 'Hole In Fun-2', + 27: 'Rock And Roll In-2', + 28: 'Bogey Nights-2', + 29: 'Tea Off Time-2', + 30: 'Holey Mackerel!-2', + 31: 'One Little Birdie-2', + 32: 'At The Drive In-2', + 33: 'Swing Time-2', + 34: 'Hole On The Range-2', + 35: 'Second Wind-2'} +GolfHoleInOne = 'Hole In One' +GolfCondor = 'Condor' +GolfAlbatross = 'Albatross' +GolfEagle = 'Eagle' +GolfBirdie = 'Birdie' +GolfPar = 'Par' +GolfBogey = 'Bogey' +GolfDoubleBogey = 'Double Bogey' +GolfTripleBogey = 'Triple Bogey' +GolfShotDesc = {-4: GolfCondor, + -3: GolfAlbatross, + -2: GolfEagle, + -1: GolfBirdie, + 0: GolfPar, + 1: GolfBogey, + 2: GolfDoubleBogey, + 3: GolfTripleBogey} +from toontown.golf import GolfGlobals +CoursesCompleted = 'Courses Completed' +CoursesUnderPar = 'Courses Under Par' +HoleInOneShots = 'Hole In One Shots' +EagleOrBetterShots = 'Eagle Or Better Shots' +BirdieOrBetterShots = 'Birdie Or Better Shots' +ParOrBetterShots = 'Par Or Better Shots' +MultiPlayerCoursesCompleted = 'Multiplayer Courses Completed' +TwoPlayerWins = 'Two Player Wins' +ThreePlayerWins = 'Three Player Wins' +FourPlayerWins = 'Four Player Wins' +CourseZeroWins = GolfCourseNames[0] + ' Wins' +CourseOneWins = GolfCourseNames[1] + ' Wins' +CourseTwoWins = GolfCourseNames[2] + ' Wins' +GolfHistoryDescriptions = [CoursesCompleted, + CoursesUnderPar, + HoleInOneShots, + EagleOrBetterShots, + BirdieOrBetterShots, + ParOrBetterShots, + MultiPlayerCoursesCompleted, + CourseZeroWins, + CourseOneWins, + CourseTwoWins] +GolfTrophyDescriptions = [str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesCompleted][0]) + ' ' + CoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesCompleted][1]) + ' ' + CoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesCompleted][2]) + ' ' + CoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesUnderPar][0]) + ' ' + CoursesUnderPar, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesUnderPar][1]) + ' ' + CoursesUnderPar, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CoursesUnderPar][2]) + ' ' + CoursesUnderPar, + str(GolfGlobals.TrophyRequirements[GolfGlobals.HoleInOneShots][0]) + ' ' + HoleInOneShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.HoleInOneShots][1]) + ' ' + HoleInOneShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.HoleInOneShots][2]) + ' ' + HoleInOneShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.EagleOrBetterShots][0]) + ' ' + EagleOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.EagleOrBetterShots][1]) + ' ' + EagleOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.EagleOrBetterShots][2]) + ' ' + EagleOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.BirdieOrBetterShots][0]) + ' ' + BirdieOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.BirdieOrBetterShots][1]) + ' ' + BirdieOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.BirdieOrBetterShots][2]) + ' ' + BirdieOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.ParOrBetterShots][0]) + ' ' + ParOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.ParOrBetterShots][1]) + ' ' + ParOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.ParOrBetterShots][2]) + ' ' + ParOrBetterShots, + str(GolfGlobals.TrophyRequirements[GolfGlobals.MultiPlayerCoursesCompleted][0]) + ' ' + MultiPlayerCoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.MultiPlayerCoursesCompleted][1]) + ' ' + MultiPlayerCoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.MultiPlayerCoursesCompleted][2]) + ' ' + MultiPlayerCoursesCompleted, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseZeroWins][0]) + ' ' + CourseZeroWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseZeroWins][1]) + ' ' + CourseZeroWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseZeroWins][2]) + ' ' + CourseZeroWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseOneWins][0]) + ' ' + CourseOneWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseOneWins][1]) + ' ' + CourseOneWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseOneWins][2]) + ' ' + CourseOneWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseTwoWins][0]) + ' ' + CourseTwoWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseTwoWins][1]) + ' ' + CourseTwoWins, + str(GolfGlobals.TrophyRequirements[GolfGlobals.CourseTwoWins][2]) + ' ' + CourseTwoWins] +GolfCupDescriptions = [str(GolfGlobals.TrophiesPerCup) + ' Trophies won', str(GolfGlobals.TrophiesPerCup * 2) + ' Trophies won', str(GolfGlobals.TrophiesPerCup * 3) + ' Trophies won'] +GolfAvReceivesHoleBest = '%(name)s scored a new hole best at %(hole)s!' +GolfAvReceivesCourseBest = '%(name)s scored a new course best at %(course)s!' +GolfAvReceivesCup = '%(name)s receives the %(cup)s cup!! Laff point boost!' +GolfAvReceivesTrophy = '%(name)s receives the %(award)s trophy!!' +GolfRanking = 'Ranking: \n' +GolfPowerBarText = '%(power)s%%' +GolfChooseTeeInstructions = 'Press Left or Right to change tee spot.\nPress Control to select.' +GolfWarningMustSwing = 'Warning: You must press Control on your next swing.' +GolfAimInstructions = 'Press Left or Right to aim.\nPress and hold Control to swing.' +GolferExited = '%s has left the golf course.' +GolfPowerReminder = 'Hold Down Control Longer to\nHit the Ball Further' +GolfPar = 'Par' +GolfHole = 'Hole' +GolfTotal = 'Total' +GolfExitCourse = 'Exit Course' +GolfUnknownPlayer = '???' +GolfPageTitle = 'Golf' +GolfPageTitleCustomize = 'Golf Customizer' +GolfPageTitleRecords = 'Personal Best Records' +GolfPageTitleTrophy = 'Golfing Trophies' +GolfPageCustomizeTab = 'Customize' +GolfPageRecordsTab = 'Records' +GolfPageTrophyTab = 'Trophy' +GolfPageTickets = 'Tickets : ' +GolfPageConfirmDelete = 'Delete Accessory?' +GolfTrophyTextDisplay = 'Trophy %(number)s : %(desc)s' +GolfCupTextDisplay = 'Cup %(number)s : %(desc)s' +GolfCurrentHistory = 'Current %(historyDesc)s : %(num)s' +GolfTieBreakWinner = '%(name)s wins the random tie breaker!' +GolfSeconds = ' - %(time).2f seconds' +GolfTimeTieBreakWinner = '%(name)s wins the total aiming time tie breaker!!!' +RoamingTrialerWeekendStart = 'Tour Toontown is starting! Free players may now enter any neighborhood!' +RoamingTrialerWeekendOngoing = 'Welcome to Tour Toontown! Free players may now enter any neighborhood!' +RoamingTrialerWeekendEnd = "That's all for Tour Toontown." +MoreXpHolidayStart = 'Good news! Exclusive Test Toon double gag experience time has started.' +MoreXpHolidayOngoing = 'Welcome! Exclusive Test Toon double gag experience time is currently ongoing.' +MoreXpHolidayEnd = 'Exclusive Test Toon double gag experience time has ended. Thanks for helping us Test things!' +JellybeanDayHolidayStart = "It's Jellybean Day! Get Double Jellybean rewards at Parties!" +JellybeanDayHolidayEnd = "That's all for Jellybean Day. See you next year." +PartyRewardDoubledJellybean = 'Double Jellybeans!' +GrandPrixWeekendHolidayStart = "It's Grand Prix Weekend at Goofy Speedway! Free and paid players collect the most points in three consecutive races." +GrandPrixWeekendHolidayEnd = "That's all for Grand Prix Weekend. See you next year." +KartRace_DoubleTickets = 'Double Tickets' +SellbotNerfHolidayStart = 'Operation: Storm Sellbot is happening now! Battle the VP today!' +SellbotNerfHolidayEnd = 'Operation: Storm Sellbot has ended. Great work, Toons!' +JellybeanTrolleyHolidayStart = 'Double Bean Days for Trolley Games have begun!' +JellybeanTrolleyHolidayEnd = 'Double Bean Days for Trolley Games have ended!' +JellybeanFishingHolidayStart = 'Double Bean Days for Fishing have begun!' +JellybeanFishingHolidayEnd = 'Double Bean Days for Fishing have ended!' +JellybeanPartiesHolidayStart = "It's Jellybean Week! Get Double Jellybean rewards!" +JellybeanPartiesHolidayEnd = "That's all for Jellybean Week. See you next year." +JellybeanMonthHolidayStart = 'Celebrate Toontown with double beans, Cattlelog items and silly surprises!' +BankUpgradeHolidayStart = 'Something Toontastic happened to your Jellybean Bank!' +HalloweenPropsHolidayStart = "It's Halloween in Toontown!" +HalloweenPropsHolidayEnd = 'Halloween has ended. Boo!' +SpookyPropsHolidayStart = 'Silly Meter spins Toontown into spooky mode!' +BlackCatHolidayStart = 'Create a Black Cat - Today only!' +BlackCatHolidayEnd = 'Black Cat day has ended!' +SpookyBlackCatHolidayStart = 'Friday 13th means a Black Cat blast!' +TopToonsMarathonStart = "The Top Toons New Year's Day Marathon has begun!" +TopToonsMarathonEnd = "The Top Toons New Year's Day Marathon has ended." +WinterDecorationsStart = "It's Winter Holiday time in Toontown!" +WinterDecorationsEnd = 'Winter Holiday is over - Happy New Year!' +WackyWinterDecorationsStart = 'Brrr! Silly Meter goes from silly to chilly!' +WinterCarolingStart = 'Caroling has come to Toontown. Sing for your Snowman Head - see the Blog for details!' +ExpandedClosetsStart = 'Attention Toons: For a limited time, Members can purchase the new 50 item Closet from the Cattlelog for the low price of 50 jellybeans!' +KartingTicketsHolidayStart = 'Get double tickets from Practice races at Goofy Speedway today!' +IdesOfMarchStart = 'Toons go GREEN!' +LogoutForced = 'You have done something wrong\n and are being logged out automatically,\n additionally your account may be frozen.\n Try going on a walk outside, it is fun.' +CountryClubToonEnterElevator = '%s \nhas jumped in the golf kart.' +CountryClubBossConfrontedMsg = '%s is battling the Club President!' +ElevatorBlockedRoom = 'All challenges must be defeated first.' +MolesLeft = 'Moles Left: %d' +MolesInstruction = 'Mole Stomp!\nJump on the red moles!' +MolesFinished = 'Mole Stomp successful!' +MolesPityWin = 'Stomp Failed! But the moles left.' +MolesRestarted = 'Stomp Failed! Restarting...' +BustACogInstruction = 'Remove the cog ball!' +BustACogExit = 'Exit for Now' +BustACogHowto = 'How to Play' +BustACogFailure = 'Out of Time!' +BustACogSuccess = 'Success!' +GolfGreenGameScoreString = 'Puzzles Left: %s' +GolfGreenGamePlayerScore = 'Solved %s' +GolfGreenGameBonusGag = 'You won %s!' +GolfGreenGameGotHelp = '%s solved a Puzzle!' +GolfGreenGameDirections = 'Shoot balls using the the mouse\n\n\nMatching three of a color causes the balls to fall\n\n\nRemove all Cog balls from the board' +enterHedgeMaze = 'Race through the Hedge Maze\n for a laff bonus!' +toonFinishedHedgeMaze = '%s \n finished in %s place!' +hedgeMazePlaces = ['first', + 'second', + 'third', + 'Fourth'] +mazeLabel = 'Maze Race!' +BoardingPartyReadme = 'Boarding Group?' +BoardingGroupHide = 'Hide' +BoardingGroupShow = 'Show Boarding Group' +BoardingPartyInform = 'Create an elevator Boarding Group by clicking on another Toon and Inviting them.\nIn this area Boarding Groups cannot have more than %s Toons.' +BoardingPartyTitle = 'Boarding Group' +QuitBoardingPartyLeader = 'Disband' +QuitBoardingPartyNonLeader = 'Leave' +QuitBoardingPartyConfirm = 'Are you sure you want to quit this Boarding Group?' +BoardcodeMissing = 'Something went wrong; try again later.' +BoardcodeMinLaffLeader = 'Your group cannot board because you have less than %s laff points.' +BoardcodeMinLaffNonLeaderSingular = 'Your group cannot board because %s has less than %s laff points.' +BoardcodeMinLaffNonLeaderPlural = 'Your group cannot board because %s have less than %s laff points.' +BoardcodePromotionLeader = 'Your group cannot board because you do not have enough promotion merits.' +BoardcodePromotionNonLeaderSingular = 'Your group cannot board because %s does not have enough promotion merits.' +BoardcodePromotionNonLeaderPlural = 'Your group cannot board because %s do not have enough promotion merits.' +BoardcodeSpace = 'Your group cannot board because there is not enough space.' +BoardcodeBattleLeader = 'Your group cannot board because you are in battle.' +BoardcodeBattleNonLeaderSingular = 'Your group cannot board because %s is in battle.' +BoardcodeBattleNonLeaderPlural = 'Your group cannot board because %s are in battle.' +BoardingInviteMinLaffInviter = 'You need %s Laff Points before being a member of this Boarding Group.' +BoardingInviteMinLaffInvitee = '%s needs %s Laff Points before being a member of this Boarding Group.' +BoardingInvitePromotionInviter = 'You need to earn a promotion before being a member of this Boarding Group.' +BoardingInvitePromotionInvitee = '%s needs to earn a promotion before being a member of this Boarding Group.' +BoardingInviteNotPaidInvitee = '%s needs to be a paid Member to be a part of your Boarding Group.' +BoardingInviteeInDiffGroup = '%s is already in a different Boarding Group.' +BoardingInviteeInKickOutList = '%s had been removed by your leader. Only the leader can re-invite removed members.' +BoardingInviteePendingIvite = '%s has a pending invite; try again later.' +BoardingInviteeInElevator = '%s is currently busy; try again later.' +BoardingInviteGroupFull = 'Your Boarding Group is already full.' +BoardingAlreadyInGroup = 'You cannot accept this invitation because you are part of another Boarding Group.' +BoardingGroupAlreadyFull = 'You cannot accept this invitation because the group is already full.' +BoardingKickOutConfirm = 'Are you sure you want to remove %s?' +BoardingPendingInvite = 'You need to deal with the\n pending invitation first.' +BoardingCannotLeaveZone = 'You cannot leave this area because you are part of a Boarding Group.' +BoardingInviteeMessage = '%s would like you to join their Boarding Group.' +BoardingInvitingMessage = 'Inviting %s to your Boarding Group.' +BoardingInvitationRejected = '%s has rejected to join your Boarding Group.' +BoardingMessageKickedOut = 'You have been removed from the Boarding Group.' +BoardingMessageInvited = '%s has invited %s to the Boarding Group.' +BoardingMessageLeftGroup = '%s has left the Boarding Group.' +BoardingMessageGroupDissolved = 'Your Boarding Group was disbanded by the group leader.' +BoardingMessageGroupDisbandedGeneric = 'Your Boarding Group was disbanded.' +BoardingMessageInvitationFailed = '%s tried to invite you to their Boarding Group.' +BoardingMessageGroupFull = '%s tried to accept your invitation but your group was full.' +BoardingGo = 'GO' +BoardingCancelGo = 'Click Again to\nCancel Go' +And = 'and' +BoardingGoingTo = 'Going To:' +BoardingTimeWarning = 'Boarding the elevator in ' +BoardingMore = 'more' +BoardingGoShow = 'Going to\n%s in ' +BoardingGoPreShow = 'Confirming...' +BossbotBossName = 'C.E.O.' +BossbotRTWelcome = 'You toons will need different disguises.' +BossbotRTRemoveSuit = 'First take off your cog suits...' +BossbotRTFightWaiter = 'and then fight these waiters.' +BossbotRTWearWaiter = "Good Job! Now put on the waiters' clothes." +BossbotBossPreTwo1 = "What's taking so long? " +BossbotBossPreTwo2 = 'Get cracking and serve my banquet!' +BossbotRTServeFood1 = 'Hehe, serve the food I place on these conveyor belts.' +BossbotRTServeFood2 = 'If you serve a cog three times in a row it will explode.' +BossbotResistanceToonName = "Good ol' Gil Giggles" +BossbotPhase3Speech1 = "What's happening here?!" +BossbotPhase3Speech2 = 'These waiters are toons!' +BossbotPhase3Speech3 = 'Get them!!!' +BossbotPhase4Speech1 = 'Hrrmmpph. When I need a job done right...' +BossbotPhase4Speech2 = "I'll do it myself." +BossbotRTPhase4Speech1 = 'Good Job! Now squirt the C.E.O. with the water on the tables...' +BossbotRTPhase4Speech2 = 'or use golf balls to slow him down.' +BossbotPitcherLeave = 'Leave Bottle' +BossbotPitcherLeaving = 'Leaving Bottle' +BossbotPitcherAdvice = 'Use the left and right keys to rotate.\nHold down Ctrl increase power.\nRelease Ctrl to fire.' +BossbotGolfSpotLeave = 'Leave Golf Ball' +BossbotGolfSpotLeaving = 'Leaving Golf Ball' +BossbotGolfSpotAdvice = 'Use the left and right keys to rotate.\nCtrl to fire.' +BossbotRewardSpeech1 = "No! The Chairman won't like this." +BossbotRewardSpeech2 = 'Arrrggghhh!!!!' +BossbotRTCongratulations = "You did it! You've demoted the C.E.O.!\x07Here, take these pink slips the C.E.O. left behind.\x07With it you'll be able to fire Cogs in a battle." +BossbotRTLastPromotion = "\x07Wow, you've reached level %s on your Cog Suit!\x07Cogs don't get promoted higher than that.\x07You can't upgrade your Cog Suit anymore, but you can certainly keep working for the Resistance!" +BossbotRTHPBoost = "\x07You've done a lot of work for the Resistance.\x07The Toon Council has decided to give you another Laff point. Congratulations!" +BossbotRTMaxed = '\x07I see that you have a level %s Cog Suit. Very impressive!\x07On behalf of the Toon Council, thank you for coming back to defend more Toons!' +GolfAreaAttackTaunt = 'Fore!' +OvertimeAttackTaunts = ["It's time to reorganize.", "Now let's downsize."] +ElevatorBossBotBoss = 'C.E.O Battle' +ElevatorBossBotCourse0 = 'The Front Three' +ElevatorBossBotCourse1 = 'The Middle Six' +ElevatorBossBotCourse2 = 'The Back Nine' +ElevatorCashBotBoss = 'C.F.O Battle' +ElevatorCashBotMint0 = 'Coin Mint' +ElevatorCashBotMint1 = 'Dollar Mint' +ElevatorCashBotMint2 = 'Bullion Mint' +ElevatorSellBotBoss = 'Senior V.P Battle' +ElevatorSellBotFactory0 = 'Front Entrance' +ElevatorSellBotFactory1 = 'Side Entrance' +ElevatorLawBotBoss = 'Chief Justice Battle' +ElevatorLawBotCourse0 = 'Office A' +ElevatorLawBotCourse1 = 'Office B' +ElevatorLawBotCourse2 = 'Office C' +ElevatorLawBotCourse3 = 'Office D' +DaysToGo = 'Wait\n%s Days' +IceGameTitle = 'Ice Slide' +IceGameInstructions = 'Get as close to the center by the end of the second round. Use arrow keys to change direction and force. Press Ctrl to launch your toon. Hit barrels for extra points and avoid the TNT!' +IceGameInstructionsNoTnt = 'Get as close to the center by the end of the second round. Use arrow keys to change direction and force. Press Ctrl to launch your toon. Hit barrels for extra points.' +IceGameWaitingForPlayersToFinishMove = 'Waiting for other players...' +IceGameWaitingForAISync = 'Waiting for other players...' +IceGameInfo = 'Match %(curMatch)d/%(numMatch)d, Round %(curRound)d/%(numRound)d' +IceGameControlKeyWarning = 'Remember to press the Ctrl key!' +PicnicTableJoinButton = 'Join' +PicnicTableObserveButton = 'Observe' +PicnicTableCancelButton = 'Cancel' +PicnicTableTutorial = 'How To Play' +PicnicTableMenuTutorial = 'What game do you want to learn?' +PicnicTableMenuSelect = 'What game do you want to play?' +ChineseCheckersGetUpButton = 'Get Up' +ChineseCheckersStartButton = 'Start Game' +ChineseCheckersQuitButton = 'Quit Game' +ChineseCheckersIts = "It's " +ChineseCheckersYourTurn = 'Your Turn' +ChineseCheckersGreenTurn = "Green's Turn" +ChineseCheckersYellowTurn = "Yellow's Turn" +ChineseCheckersPurpleTurn = "Purple's Turn" +ChineseCheckersBlueTurn = "Blue's Turn" +ChineseCheckersPinkTurn = "Pink's Turn" +ChineseCheckersRedTurn = "Red's Turn" +ChineseCheckersColorG = 'You are Green' +ChineseCheckersColorY = 'You are Yellow' +ChineseCheckersColorP = 'You are Purple' +ChineseCheckersColorB = 'You are Blue' +ChineseCheckersColorPink = 'You are Pink' +ChineseCheckersColorR = 'You are Red' +ChineseCheckersColorO = 'You are Observing' +ChineseCheckersYouWon = 'You just won a game of Chinese Checkers!' +ChineseCheckers = 'Chinese Checkers.' +ChineseCheckersGameOf = ' has just won a game of ' +ChineseTutorialTitle1 = 'Objective' +ChineseTutorialTitle2 = 'How to Play' +ChineseTutorialPrev = 'Previous Page' +ChineseTutorialNext = 'Next Page' +ChineseTutorialDone = 'Done' +ChinesePage1 = 'The goal of Chinese Checkers is to be the first player to move all of your marbles from the bottom triangle across the board and into the triangle at the top. The first player to do so wins!' +ChinesePage2 = 'Players take turns moving any marble of their own color. A marble can move into an adjacent hole or it can hop over other marbles. Hops must go over a marble and end in an empty hole. It is possible to chain hops together for longer moves!' +CheckersPage1 = 'The goal of Checkers is to leave the opponent without any possible moves. To do this you can either capture all of his peices or block them in such that he has no available moves.' +CheckersPage2 = 'Players take turns moving any peice of their own color. A peice can move one square diagonal and forward. A peice can only move into a square that is not occupied by another peice. Kings follow the same rules but are allowed to move backwards.' +CheckersPage3 = 'To capture an opponents peice your peice must jump over it diagonally into the vacant square beyond it. If you have any jump moves during a turn, you must do one of them. You can chain jump moves together as long as it is with the same peice.' +CheckersPage4 = 'A peice becomes a king when it reaches the last row on the board. A peice that has just become a king cannot continue jumping until the next turn. Additionally, kings are allowed to move all directions and are allowed to change directions while jumping.' +CheckersGetUpButton = 'Get Up' +CheckersStartButton = 'Start Game' +CheckersQuitButton = 'Quit Game' +CheckersIts = "It's " +CheckersYourTurn = 'Your Turn' +CheckersWhiteTurn = "White's Turn" +CheckersBlackTurn = "Black's Turn" +CheckersColorWhite = 'You are White' +CheckersColorBlack = 'You are Black' +CheckersObserver = 'You are Observing' +RegularCheckers = 'Checkers.' +RegularCheckersGameOf = ' has just won a game of ' +RegularCheckersYouWon = 'You just won a game of Checkers!' +MailNotifyNewItems = "You've got mail!" +MailNewMailButton = 'Mail' +MailSimpleMail = 'Note' +MailFromTag = 'Note From: %s' +AwardNotifyNewItems = 'You have a new award in your mailbox!' +AwardNotifyOldItems = 'There are still awards waiting in your mailbox for you to pick up!' +InviteInvitation = 'the invitation' +InviteAcceptInvalidError = 'The invitation is no longer valid.' +InviteAcceptPartyInvalid = 'That party has been cancelled.' +InviteAcceptAllOk = 'The host has been informed of your reply.' +InviteRejectAllOk = 'The host has been informed that you declined the invitation.' +Months = {1: 'JANUARY', + 2: 'FEBRUARY', + 3: 'MARCH', + 4: 'APRIL', + 5: 'MAY', + 6: 'JUNE', + 7: 'JULY', + 8: 'AUGUST', + 9: 'SEPTEMBER', + 10: 'OCTOBER', + 11: 'NOVEMBER', + 12: 'DECEMBER'} +DayNames = ('Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday') +DayNamesAbbrev = ('MON', + 'TUE', + 'WED', + 'THU', + 'FRI', + 'SAT', + 'SUN') +HolidayNamesInCalendar = {1: ('Summer Fireworks', 'Celebrate Summer with a fireworks show every hour in each playground!'), + 2: ('New Year Fireworks', 'Happy New Year! Enjoy a fireworks show every hour in each playground!'), + 3: ('Bloodsucker Invasion', 'Help defend Toontown from the Bloodsucker invasion!'), + 4: ('Winter Holiday', 'Celebrate the Winter Holiday with Toontastic decorations, party and Cattlelog items, and more!'), + 5: ('Skelecog Invasion', 'Stop the Skelecogs from invading Toontown!'), + 6: ('Mr. Hollywood Invasion', 'Stop the Mr. Hollywood Cogs from invading Toontown!'), + 7: ('Fish Bingo', 'Fish Bingo Wednesday! Everyone at the pond works together to complete the card before time runs out.'), + 8: ('Toon Species Election', 'Vote on the new Toon species! Will it be Goat? Will it be Pig?'), + 9: ('Black Cat Day', 'Happy Halloween! Create a Toontastic Black Cat Toon - Today Only!'), + 13: ('Trick or Treat', 'Happy Halloween! Trick or treat throughout Toontown to get a nifty Halloween pumpkin head reward!'), + 14: ('Grand Prix', 'Grand Prix Monday at Goofy Speedway! To win, collect the most points in three consecutive races!'), + 16: ('Grand Prix Weekend', 'Free and Paid players compete in circuit races at Goofy Speedway!'), + 17: ('Trolley Tracks', 'Trolley Tracks Thursday! Board any Trolley with two or more Toons to play.'), + 19: ('Silly Saturdays', 'Saturdays are silly with Fish Bingo and Grand Prix throughout the day!'), + 24: ('Ides of March', 'Beware the Ides of March! Stop the Backstabber Cogs from invading Toontown!'), + 26: ('Halloween Decor', 'Celebrate Halloween as spooky trees and streetlights transform Toontown!'), + 28: ('Winter Invasion', 'The sellbots are on the loose spreading their cold sales tactics!'), + 29: ("April Toons' Week", "Celebrate April Toons' Week - a holiday built by Toons for Toons!"), + 33: ('Sellbot Surprise 1', 'Sellbot Surprise! Stop the Cold Caller Cogs from invading Toontown!'), + 34: ('Sellbot Surprise 2', 'Sellbot Surprise! Stop the Name Dropper Cogs from invading Toontown!'), + 35: ('Sellbot Surprise 3', 'Sellbot Surprise! Stop the Gladhander Cogs from invading Toontown!'), + 36: ('Sellbot Surprise 4', 'Sellbot Surprise! Stop the Mover & Shaker Cogs from invading Toontown!'), + 37: ('A Cashbot Conundrum 1', 'A Cashbot Conundrum. Stop the Short Change Cogs from invading Toontown!'), + 38: ('A Cashbot Conundrum 2', 'A Cashbot Conundrum. Stop the Penny Pincher Cogs from invading Toontown!'), + 39: ('A Cashbot Conundrum 3', 'A Cashbot Conundrum. Stop the Bean Counter Cogs from invading Toontown!'), + 40: ('A Cashbot Conundrum 4', 'A Cashbot Conundrum. Stop the Number Cruncher Cogs from invading Toontown!'), + 41: ('The Lawbot Gambit 1', 'The Lawbot Gambit. Stop the Bottomfeeder Cogs from invading Toontown!'), + 42: ('The Lawbot Gambit 2', 'The Lawbot Gambit. Stop the Double Talker Cogs from invading Toontown!'), + 43: ('The Lawbot Gambit 3', 'The Lawbot Gambit. Stop the Ambulance Chaser Cogs from invading Toontown!'), + 44: ('The Lawbot Gambit 4', 'The Lawbot Gambit. Stop the Backstabber Cogs from invading Toontown!'), + 45: ('The Trouble With Bossbots 1', 'The Trouble with Bossbots. Stop the Flunky Cogs from invading Toontown!'), + 46: ('The Trouble With Bossbots 2', 'The Trouble with Bossbots. Stop the Pencil Pusher Cogs from invading Toontown!'), + 47: ('The Trouble With Bossbots 3', 'The Trouble with Bossbots. Stop the Micromanager Cogs from invading Toontown!'), + 48: ('The Trouble With Bossbots 4', 'The Trouble with Bossbots. Stop the Downsizer Cogs from invading Toontown!'), + 49: ('Jellybean Day', 'Celebrate Jellybean Day with double Jellybean rewards at parties!'), + 53: ('Cold Caller Invasion', 'Stop the Cold Caller Cogs from invading Toontown!'), + 54: ('Bean Counter Invasion', 'Stop the Bean Counter Cogs from invading Toontown!'), + 55: ('Double Talker Invasion', 'Stop the Double Talker Cogs from invading Toontown!'), + 56: ('Downsizer Invasion', 'Stop the Downsizer Cogs from invading Toontown!'), + 57: ('Caroling', 'Sing for your Snowman Head! See the Blog for details!'), + 59: ("ValenToon's Day", "Celebrate ValenToon's Day from Feb 09 to Feb 16!"), + 72: ('Yes Men Invasion', 'Stop the Yes Men Cogs from invading Toontown!'), + 73: ('Tightwad Invasion', 'Stop the Tightwad Cogs from invading Toontown!'), + 74: ('Telemarketers Invasion', 'Stop the Telemarketer Cogs from invading Toontown!'), + 75: ('Head Hunter Invasion', 'Stop the Head Hunter Cogs from invading Toontown!'), + 76: ('Spin Doctor Invasion', 'Stop the Spin Doctor Cogs from invading Toontown!'), + 77: ('Moneybags Invasion', 'Stop the Moneybags from invading Toontown!'), + 78: ('Two-faces Invasion', 'Stop the Two-faces from invading Toontown!'), + 79: ('Mingler Invasion', 'Stop the Mingler Cogs from invading Toontown!'), + 80: ('Loan Shark Invasion', 'Stop the Loanshark Cogs from invading Toontown!'), + 81: ('Corporate Raider Invasion', 'Stop the Corporate Raider Cogs from invading Toontown!'), + 82: ('Robber Baron Invasion', 'Stop the Robber Baron Cogs from invading Toontown!'), + 83: ('Legal Eagle Invasion', 'Stop the Legal Eagle Cogs from invading Toontown!'), + 84: ('Big Wig Invasion', 'Stop the Big Wig Cogs from invading Toontown!'), + 85: ('Big Cheese Invasion', 'Stop the Big Cheese from invading Toontown!'), + 86: ('Down Sizer Invasion', 'Stop the Down Sizer Cogs from invading Toontown!'), + 87: ('Mover And Shaker Invasion', 'Stop the Mover and Shaker Cogs from invading Toontown!'), + 88: ('Double Talker Invasion', 'Stop the Double Talkers Cogs from invading Toontown!'), + 89: ('Penny Pincher Invasion', 'Stop the Penny Pinchers Cogs from invading Toontown!'), + 90: ('Name Dropper Invasion', 'Stop the Name Dropper Cogs from invading Toontown!'), + 91: ('Ambulance Chaser Invasion', 'Stop the Ambulance Chaser Cogs from invading Toontown!'), + 92: ('Micro Manager Invasion', 'Stop the Micro Manager Cogs from invading Toontown!'), + 93: ('Number Cruncher Invasion', 'Stop the Number Cruncher Cogs from invading Toontown!'), + 95: ('Victory Parties', 'Celebrate our historic triumph against the Cogs!'), + 96: ('Operation: Storm Sellbot', "Sellbot HQ is open to everyone. Let's go fight the VP!"), + 97: ('Double Bean Days - Trolley Games', ''), + 98: ('Double Bean Days - Fishing', ''), + 99: ('Jellybean Week', 'Celebrate Jellybean Week with double Jellybean rewards!'), + 101: ("Top Toons New Year's Day Marathon", "Chances to win every hour! See the What's New Blog for details!"), + 105: ('Toons go GREEN!', 'Toons make a green scene at Green Bean Jeans on Oak Street in Daisy Gardens!')} +UnknownHoliday = 'Unknown Holiday %d' +HolidayFormat = '%b %d ' +TimeZone = 'US/Pacific' +CogdoMemoGuiTitle = 'Memos:' +CogdoMemoNames = 'Barrel-Destruction Memos' +CogdoStomperName = 'Stomp-O-Matic' +BoardroomGameTitle = 'Boardroom Hijinks' +BoardroomGameInstructions = 'The COGS are having a meeting to decide what to do with stolen gags. Slide on through and grab as many gag-destruction memos as you can!' +CogdoCraneGameTitle = 'Vend-A-Stomper' +CogdoCraneGameInstructions = 'The COGS are using a coin-operated machine to destroy laff barrels. Use the cranes to pick up and throw money bags, in order to prevent barrel destruction!' +CogdoMazeGameTitle = 'Mover & Shaker\nField Office' +CogdoMazeGameInstructions = 'The big Mover & Shaker Cogs have the code to open the door. Defeat them with your water balloons in order to get it!' +CogdoMazeIntroMovieDialogue = (("This is the Toon Resistance! The Movers & Shakers\nhave our Jokes, and they've locked the exit!",), ('Grab water balloons at coolers, and throw them at Cogs!\nSmall Cogs drop Jokes, BIG COGS open the exit.',), ('The more Jokes you rescue, the bigger your Toon-Up\nat the end. Good luck!',)) +CogdoMazeGameDoorOpens = 'THE EXIT IS OPEN FOR 60 SECONDS!\nGET THERE FAST FOR A BIGGER TOON-UP' +CogdoMazeGameLocalToonFoundExit = "The exit will open when\nyou've busted all four BIG COGS!" +CogdoMazeGameWaitingForToons = 'Waiting for other Toons...' +CogdoMazeGameTimeOut = 'Oh no, time ran out! You lost your jokes.' +CogdoMazeGameTimeAlert = 'Hurry up! 60 seconds to go!' +CogdoMazeGameBossGuiTitle = 'BIG COGS:' +CogdoMazeFindHint = 'Find a Water Cooler' +CogdoMazeThrowHint = "Press 'Ctrl' to throw your water balloon" +CogdoMazeSquashHint = 'Falling objects pop your balloon' +CogdoMazeBossHint = 'Big Cogs take TWO hits to defeat' +CogdoMazeMinionHint = 'Smaller Cogs drop jokes' +CogdoFlyingGameTitle = 'Legal Eagle Offices' +CogdoFlyingGameInstructions = "Fly through the Legal Eagles' lair. Watch out for obstacles and cogs along the way, and don't forget to refuel your helicopter!" +CogdoFlyingIntroMovieDialogue = (("You won't ruffle our feathers, Toons! We're destroying barrels of your Laff, and you cannot stop us!", "A flock of Toons! We're crushing barrels of your Laff in our %s, and there's nothing you can do about it!" % CogdoStomperName, "You can't egg us on, Toons! We're powering our offices with your Laff, and you're powerless to stop us!"), ('This is the Toon Resistance! A little bird told me you can use propellers to fly around, grab Barrel Destruction Memos, and keep Laff from being destroyed! Good luck, Toons!', 'Attention Toons! Wing it with a propeller and collect Barrel Destruction Memos to keep our Laff from being stomped! Toon Resistance out!', 'Toon Resistance here! Cause a flap by finding propellers, flying to the Barrel Destruction Memos, and keeping our Laff from being smashed! Have fun!'), ("Squawk! I'm a Silver Sprocket Award winner, I don't need this!", 'Do your best, Toons! You will find us to be quite talon-ted!', "We'll teach you to obey the pecking order, Toons!")) +CogdoFlyingGameWaiting = 'Waiting for other Toons%s' +CogdoFlyingGameFuelLabel = 'Fuel' +CogdoFlyingGameLegalEagleTargeting = 'A Legal Eagle has noticed you!' +CogdoFlyingGameLegalEagleAttacking = 'Incoming Eagle!' +CogdoFlyingGamePickUpAPropeller = 'You need a propeller to fly!' +CogdoFlyingGamePressCtrlToFly = "Press 'Ctrl' to fly up!" +CogdoFlyingGameYouAreInvincible = 'Red Tape protects you!' +CogdoFlyingGameTimeIsRunningOut = 'Time is running out!' +CogdoFlyingGameMinimapIntro = 'This meter shows your progress!\nX marks the finish line.' +CogdoFlyingGameMemoIntro = 'Memos prevent Laff Barrels in\nthe Stomper Room from being destroyed!' +CogdoFlyingGameOutOfTime = 'Oh No! You ran out of time!' +CogdoFlyingGameYouMadeIt = 'You made it on time!' +CogdoFlyingGameYouMadeIt = 'Good work, you made it on time!' +CogdoFlyingGameTakingMemos = 'The legal eagles took all your memos!' +CogdoElevatorRewardLaff = 'Great job, Toons!\nYou get a Toon-Up from the jokes you saved!' +CogdoExecutiveSuiteTitle = 'Executive Suite' +CogdoExecutiveSuiteIntroMessage = "Oh no, they've got the shop keeper!\nDefeat the Cogs and free the captive." +CogdoExecutiveSuiteToonThankYou = 'Thanks for the rescue!\nIf you need help in a fight, use this SOS card to call my friend %s.' +CogdoExecutiveSuiteToonBye = 'Bye!' +SillySurgeTerms = {1: 'Amusing Ascent!', + 2: 'Silly Surge!', + 3: 'Ridiculous Rise!', + 4: 'Giggle Growth!', + 5: 'Funny Fueling!', + 6: 'Batty Boost!', + 7: 'Crazy Climb!', + 8: 'Jolly Jump!', + 9: 'Loony Lift!', + 10: 'Hilarity Hike!', + 11: 'Insanity Increase!', + 12: 'Cracked-Uptick!'} +InteractivePropTrackBonusTerms = {0: 'Super Toon-Up!', + 1: '', + 2: '', + 3: '', + 4: 'Super Throw!', + 5: 'Super Squirt!', + 6: ''} +PlayingCardUnknown = 'Card Name is unknown' diff --git a/toontown/toonbase/TTLocalizerEnglishProperty.py b/toontown/toonbase/TTLocalizerEnglishProperty.py new file mode 100644 index 0000000..cd70ec0 --- /dev/null +++ b/toontown/toonbase/TTLocalizerEnglishProperty.py @@ -0,0 +1,306 @@ +PBPTonscreenText = 0.2 +RPdirectFrame = (1.75, 1, 0.75) +RPtrackLabels = 0.05 +RPmeritBarLabels = 0.165 +RPskipScale = 0.2 +RPskipPos = (0, -.28) +RPmeritLabelPosX = 0.55 +RPmeritBarsPosX = 0.825 +BBbattleInputTimeout = 20.0 +FCPtextFrame = 0.08 +DHQInamePath = 0.9 +DHQInamePathPos = (-4, 0, 0) +DHQIscorePathPos = (-4.6, 0, 0) +DHQItrophyStarPos = (-6.6, 0, 0.3) +EexitButton = 0.8 +CCIPexitButton = 0.06 +CIPnameLabel = 1 +CIPwordwrapOffset = 0 +CIPtypeLabel = 0.075 +CIPbuyButton = (0.06, 0.05) +CSgiftTogglePos = (0.855, -0.13) +CSgiftToggle = 0.08 +CSbackCatalogButton = 0.065 +NametagReverse = False +TTCISCspeedChat = 0.055 +TTCISCtopLevelOverlap = 0.0 +TCMnormalButton = 0.06 +TCMscButtonPos = (-1.129, 0, 0.928) +TCMscButton = 0.06 +TCMwhisperFrame = 0.06 +TCMwhisperButton = 0.05 +TCMwhisperScButton = 0.05 +TCMunpaidChatWarningWordwrap = 18 +TCMunpaidChatWarning = 0.055 +TCMunpaidChatWarningPos = (0.0, 0.3) +TCMpayButton = 0.06 +TCMpayButtonPos = (0.0, 0.0, -0.13) +TCMopenChatWarning = 0.06 +TCMactivateChatGui = 0.05 +TCMsecretChatActivated = 0.06 +TCMdirectButtonTextPos = (0.0, 0.0, -0.28) +LCHQLfdText = 0.1 +SCHQLfdTypeText = 0.075 +SCHQLdgText = 0.1 +DMEEsignText = 2 +BCHQLsignText = 1.12 +DGGGquitButton = 0.045 +DGGGhowToButton = 0.045 +DGGGscoreLabel = 0.075 +FPvaluePos = (0, 0, -0.35) +FPvalue = 0.05 +FPinfo = 0.055 +FSGUIdirectFrame = 0.06 +FSGUIcancelButton = 0.06 +FSGUIokButton = 0.06 +GTenterPage2Wordwrap = 13.5 +GTenterPage4Wordwrap = 16.5 +BCGjpText = 0.04 +BCGjpTextWordwrap = 10.5 +BCGnextGame = 1.71 +FSGUIokButton = 0.06 +FSGUIcancelButton = 0.06 +FPnewEntry = 0.08 +FPnewRecord = 0.08 +GPgenus = 0.045 +FLPnewFriend = 0.045 +FLPsecrets = 0.045 +FLPsecretsPos = (0.152, 0.0, 0.14) +FLPtitle = 0.04 +FIbStop = 0.05 +FIdirectFrame = 0.06 +FIbCancelPos = (0.0, 0.0, -0.1) +FIbStopTextPos = (0.075, -0.015) +FIbStopPos = (-0.2, 0.0, 0.05) +FIbYesPos = (-0.15, 0.0, -0.1) +FIdirectFrameWordwrap = 14 +FIdirectFramePos = (0, 0.2) +DGHaimInstructions = 0.1 +DGHteeInstructions = 0.1 +GSBexitCourseBPos = (0.15, -.01) +GSBtitleLabel = 0.07 +EHpopupInfo = 0.08 +HtitleText = 0.16 +ACplayThisToon = 0.12 +ACmakeAToon = 0.12 +ACsubscribersOnly = 0.115 +ACdeleteWithPasswordFrame = 0.06 +ACstatusText = 1.0 +ACtitle = 0.15 +ACquitButton = 0.1 +AClogoutButton = 0.1 +ACquitButtonPos = (0, -0.035) +MASPscoreText = 0.1 +MASPnameText = 0.055 +MRPgameTitleText = 0.11 +MRgameTitleTextPos = (-0.046, 0.2, 0.092) +MRPplayButton = 0.055 +MRPinstructionsText = 0.07 +MRPinstructionsTextWordwrap = 26.5 +MRPinstructionsTextPos = (-0.115, 0.05, 0) +CRPgameTitleText = 0.088 +CRPgameTitleTextPos = (-0.046, 0.2, 0.13) +MPMpowerText = 0.07 +MPMtooSlow = 0.07 +MPMtooFast = 0.07 +MPMgaugeA = 0.35 +MPMgaugeTargetTop = 0.35 +MPMgaugeTargetBot = 0.35 +PstatusLabel = 0.08 +PBstatusLabel = 0.08 +DPGpointsFrame = 0.7 +DPGflipsFrame = 0.7 +DTGvoteButton = 0.07 +DTGuseLabel = 0.1 +DTGvotesPeriodLabel = 0.1 +DTGvotesToGoLabel = 0.1 +DTGupLabel = 0.125 +DTGdownLabel = 0.125 +DTGremainingVotesFrame = 0.7 +CStoonFrame = 0.0575 +NSmaxNameWidth = 8.0 +NSdirectScrolleList = 0.1 +NSmakeLabel = 0.1 +NSmakeCheckBox = 0.8 +NSnameEntry = 0.08 +NStypeANameButton = 0.06 +NStypeANameButtonPos = (0, -0.02) +NSnameResult = (0.09, 0.084, 0.084) +NSnameLabel = 0.1 +NStypeNotification = 0.08 +NScolorPrecede = True +MATenterGenderShop = 0.18 +MATenterBodyShop = 0.18 +MATenterColorShop = 0.18 +MATenterClothesShop = 0.16 +MATenterNameShop = 0.15 +MATclothesGUIshirt_scale = 0.06 +MATclothesGUIshirt_posL = 0.01 +MATclothesGUIshirt_posR = -0.014 +MATguiCancelButton = 0.08 +MATguiNextButton = 0.08 +SBshuffleBtn = 0.08 +IVwhenTextLabel = 0.06 +IVactivityTextLabel = 0.06 +PPelementDescription = 0.06 +PPelementTitleLabel = 0.07 +PPelementBuyButton = 0.055 +PPtitleScale = 0.1 +PPpbulicDescriptionLabel = 0.065 +PPprivateDescriptionLabel = 0.065 +PPpublicButton = 0.05 +PPprivateButton = 0.05 +PPcostLabel = 0.065 +PPpartyGroundsLabel = 1.0 +PPinstructionLabel = 0.07 +PPelementPriceNode = 0.065 +DPtimerTextLabel = 1.1 +DPtimerMinute = 1.1 +DPtimerColon = 1.1 +DPtimerSecond = 1.1 +DPtimerMinutePos = (-1.2, 0.0, 0.0) +DPtimerColonPos = (0.0, 0.0, 0.0) +DPtimerSecondPos = (1.2, 0.0, 0.0) +PPGpartyStartButton = 0.065 +PPGinstructionsLabel = 0.065 +PPGtoonsLabel = 0.06 +PPGactivitiesLabel = 0.06 +PPGminLeftLabel = 0.06 +JGcurrentlyPlayingLabel = 0.07 +JGsongNameLabel = 0.13 +JGaddSongButton = 0.1 +JGnumItemsVisible = 9 +JGlistItem = 1.0 +PAPfeedButton = 0.5 +PAPcallButton = 0.5 +PAPownerButton = 0.35 +PAPscratchButton = 0.5 +PAPstateLabel = 0.4 +PAPstateLabelPos = (0.7, 0, 3.5) +PAPstateLabelWordwrap = 7.5 +PDPtrickText = 0.17 +PDPlaff = 0.17 +PDPlaffPos = (0.0, -0.05) +PGUItextScale = 1 +PGUIchooserTitle = 0.1 +PGUIwordwrap = 14 +PGUIdescLabel = 0.9 +PGUIreturnConfirm = 0.07 +PGUIokButton = 0.6 +PGUIsubmitButton = 0.8 +PGUIokButtonPos = (-0.21, 1.05) +PGUIcancelButtonPos = (-3.3, 2.95) +PGUIcharLength = 1 +PTtitle = 0.13 +PTenterPage1Pos = (0.15, 0.13) +PTenterPage2Pos = (-0.27, 0.16) +PTenterPage3Pos = (0.15, 0.13) +QPauxText = 0.04 +QPtextScale = 0.045 +QPtextWordwrap = 15.6 +QPinfoZ = -0.0625 +DLBbuildTitleRow = 0.4 +DRenterWaiting = 0.2 +DRrollScale = 0.5 +KSGtextSizeBig = 0.088 +KSGtextSizeSmall = 0.055 +KSGaccDescriptionWordwrap = 11 +REPlargeLabel = 0.08 +REPsmallLabel = 0.04 +REPtextPosX = -0.6 +RGUIphotoFinish = 0.25 +RGUIplaceLabelNumPos = (-1.2, 0, -0.97) +RGUIplaceLabelStrPos = (-1.05, 0.0, -0.8) +DRAIwaitingForJoin = 90 +PimgLabel = 1.0 +GZSZLsignText = 1.5 +EPtitleLabel = 0.12 +EPhostTab = 0.07 +EPinvitedTab = 0.07 +EPcalendarTab = 0.07 +EPnewsTab = 0.07 +EPhostingCancelButton = 0.04 +EPhostingDateLabel = 0.05 +EPpartyGoButton = 0.045 +EPpublicPrivateLabel = 0.05 +EPpublicButton = 0.5 +EPprivateButton = 0.5 +EPinvitePartyGoButton = 0.045 +EPdecorationItemLabel = 0.055 +EPactivityItemLabel = 0.055 +EPcreateListAndLabel = 0.055 +FPtankTab = 0.07 +FPcollectionTab = 0.07 +FPtrophyTab = 0.07 +DSDintroText = 0.06 +DSDintroTextWordwrap = 25 +DSDwindowedButtonPos = (0.0961, 0, -0.221) +DSDfullscreenButtonPos = (0.097, 0, -0.311) +DSDembeddedButtonPos = (0.097, 0, -0.411) +DSDcancel = 0.06 +DSDcancelPos = (0, -0.02) +DPtab = 0.1 +DPdeptLabel = 0.17 +DPcogName = 0.093 +TPstartFrame = 0.12 +TPendFrame = 0.12 +SBpageTab = 0.75 +OPoptionsTab = 0.07 +OPCodesInstructionPanelTextPos = (0, -0.01) +OPCodesInstructionPanelTextWordWrap = 6 +OPCodesResultPanelTextPos = (0, 0.35) +OPCodesResultPanelTextScale = 0.06 +OPCodesResultPanelTextWordWrap = 9 +OPCodesInputTextScale = 0.8 +OPCodesSubmitTextScale = 0.07 +OPCodesSubmitTextPos = (0, -0.02) +MPsafeZoneButton = 0.055 +MPgoHomeButton = 0.055 +MPhoodLabel = 0.06 +MPhoodLabelWordwrap = 14 +KPkartTab = 0.07 +KPdeleteButton = 0.06 +KProtateButton = 0.035 +GPbasketTab = 0.07 +GPcollectionTab = 0.07 +GPtrophyTab = 0.07 +GPspecialsTab = 0.07 +GPrecordsTab = 0.07 +GPrecordsTabPos = (0.92, 0, 0.1) +GPtrophyTab = 0.07 +GPtrophyTabTextPos = (0.03, 0.0, 0.0) +GPtrophyTabPos = (0.92, 0, -0.3) +APBdialog = 0.06 +APBdirectLabelPosY = 0 +TAPwhisperButton = 0.06 +TAPsecretsButton = 0.045 +TAPgroupFrame = 0.05 +TAPgroupButton = 0.055 +TADPbCancel = 0.05 +TADPbCancelPos = (-0.865, 0.0, -0.78) +TADPtrackLabel = 0.066 +TADtrackLabelPosZ = 0.08 +GPdestFrame = 0.05 +GPdestScrollList = 0.05 +GPgoButton = 0.06 +INtrackNameLabels = 0.05 +INclickToAttack = 1.0 +INpassButton = 0.05 +INrunButton = 0.05 +INdetailNameLabel = 1.0 +INfireButton = 0.05 +NPCFimgLabel = 1.0 +PIPsecretsButton = 0.045 +PIPwisperButton = 0.06 +PIPdetailButton = 0.05 +TLStip = 0.18 +TPdialogWordwrap = 22 +TPdialog = 0.05 +TPpanel = 0.08 +TPpanelPos = (0.0, -0.7) +TPbrowserPosZ = -0.45 +TPbuttonTextList = 0.05 +TPhaveFun = 0.1 +TPjoinUs = 0.1 +TBSOSPSPenter = 0.1 +TexitButton = 0.8 diff --git a/toontown/toonbase/ToonBase.py b/toontown/toonbase/ToonBase.py new file mode 100644 index 0000000..f96b66e --- /dev/null +++ b/toontown/toonbase/ToonBase.py @@ -0,0 +1,401 @@ +from otp.otpbase import OTPBase +from otp.otpbase import OTPLauncherGlobals +from otp.otpbase import OTPGlobals +from direct.showbase.PythonUtil import * +import ToontownGlobals +from direct.directnotify import DirectNotifyGlobal +import ToontownLoader +from direct.gui import DirectGuiGlobals +from direct.gui.DirectGui import * +from pandac.PandaModules import * +import sys +import os +import math +from toontown.toonbase import ToontownAccess +from toontown.toonbase import TTLocalizer +from toontown.toonbase import ToontownBattleGlobals +from toontown.launcher import ToontownDownloadWatcher + +class ToonBase(OTPBase.OTPBase): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonBase') + + def __init__(self): + if not config.GetInt('ignore-user-options', 0): + Settings.readSettings() + mode = not Settings.getWindowedMode() + music = Settings.getMusic() + sfx = Settings.getSfx() + toonChatSounds = Settings.getToonChatSounds() + musicVol = Settings.getMusicVolume() + sfxVol = Settings.getSfxVolume() + resList = [(640, 480), + (800, 600), + (1024, 768), + (1280, 1024), + (1600, 1200)] + res = resList[Settings.getResolution()] + if mode == None: + mode = 1 + if res == None: + res = (800, 600) + loadPrcFileData('toonBase Settings Window Res', 'win-size %s %s' % (res[0], res[1])) + loadPrcFileData('toonBase Settings Window FullScreen', 'fullscreen %s' % mode) + loadPrcFileData('toonBase Settings Music Active', 'audio-music-active %s' % music) + loadPrcFileData('toonBase Settings Sound Active', 'audio-sfx-active %s' % sfx) + loadPrcFileData('toonBase Settings Music Volume', 'audio-master-music-volume %s' % musicVol) + loadPrcFileData('toonBase Settings Sfx Volume', 'audio-master-sfx-volume %s' % sfxVol) + loadPrcFileData('toonBase Settings Toon Chat Sounds', 'toon-chat-sounds %s' % toonChatSounds) + OTPBase.OTPBase.__init__(self) + if not self.isMainWindowOpen(): + try: + launcher.setPandaErrorCode(7) + except: + pass + + sys.exit(1) + self.disableShowbaseMouse() + base.debugRunningMultiplier /= OTPGlobals.ToonSpeedFactor + self.toonChatSounds = self.config.GetBool('toon-chat-sounds', 1) + self.placeBeforeObjects = config.GetBool('place-before-objects', 1) + self.endlessQuietZone = False + self.wantDynamicShadows = 0 + self.exitErrorCode = 0 + camera.setPosHpr(0, 0, 0, 0, 0, 0) + self.camLens.setFov(ToontownGlobals.DefaultCameraFov) + self.camLens.setNearFar(ToontownGlobals.DefaultCameraNear, ToontownGlobals.DefaultCameraFar) + self.musicManager.setVolume(0.65) + self.setBackgroundColor(ToontownGlobals.DefaultBackgroundColor) + tpm = TextPropertiesManager.getGlobalPtr() + candidateActive = TextProperties() + candidateActive.setTextColor(0, 0, 1, 1) + tpm.setProperties('candidate_active', candidateActive) + candidateInactive = TextProperties() + candidateInactive.setTextColor(0.3, 0.3, 0.7, 1) + tpm.setProperties('candidate_inactive', candidateInactive) + self.transitions.IrisModelName = 'phase_3/models/misc/iris' + self.transitions.FadeModelName = 'phase_3/models/misc/fade' + self.exitFunc = self.userExit + if __builtins__.has_key('launcher') and launcher: + launcher.setPandaErrorCode(11) + globalClock.setMaxDt(0.2) + if self.config.GetBool('want-particles', 1) == 1: + self.notify.debug('Enabling particles') + self.enableParticles() + self.accept(ToontownGlobals.ScreenshotHotkey, self.takeScreenShot) + self.accept('panda3d-render-error', self.panda3dRenderError) + oldLoader = self.loader + self.loader = ToontownLoader.ToontownLoader(self) + __builtins__['loader'] = self.loader + oldLoader.destroy() + self.accept('PandaPaused', self.disableAllAudio) + self.accept('PandaRestarted', self.enableAllAudio) + self.friendMode = self.config.GetBool('switchboard-friends', 0) + self.wantPets = self.config.GetBool('want-pets', 1) + self.wantBingo = self.config.GetBool('want-fish-bingo', 1) + self.wantKarts = self.config.GetBool('want-karts', 1) + self.wantNewSpecies = self.config.GetBool('want-new-species', 0) + self.inactivityTimeout = self.config.GetFloat('inactivity-timeout', ToontownGlobals.KeyboardTimeout) + if self.inactivityTimeout: + self.notify.debug('Enabling Panda timeout: %s' % self.inactivityTimeout) + self.mouseWatcherNode.setInactivityTimeout(self.inactivityTimeout) + self.randomMinigameAbort = self.config.GetBool('random-minigame-abort', 0) + self.randomMinigameDisconnect = self.config.GetBool('random-minigame-disconnect', 0) + self.randomMinigameNetworkPlugPull = self.config.GetBool('random-minigame-netplugpull', 0) + self.autoPlayAgain = self.config.GetBool('auto-play-again', 0) + self.skipMinigameReward = self.config.GetBool('skip-minigame-reward', 0) + self.wantMinigameDifficulty = self.config.GetBool('want-minigame-difficulty', 0) + self.minigameDifficulty = self.config.GetFloat('minigame-difficulty', -1.0) + if self.minigameDifficulty == -1.0: + del self.minigameDifficulty + self.minigameSafezoneId = self.config.GetInt('minigame-safezone-id', -1) + if self.minigameSafezoneId == -1: + del self.minigameSafezoneId + cogdoGameSafezoneId = self.config.GetInt('cogdo-game-safezone-id', -1) + cogdoGameDifficulty = self.config.GetFloat('cogdo-game-difficulty', -1) + if cogdoGameDifficulty != -1: + self.cogdoGameDifficulty = cogdoGameDifficulty + if cogdoGameSafezoneId != -1: + self.cogdoGameSafezoneId = cogdoGameSafezoneId + ToontownBattleGlobals.SkipMovie = self.config.GetBool('skip-battle-movies', 0) + self.creditCardUpFront = self.config.GetInt('credit-card-up-front', -1) + if self.creditCardUpFront == -1: + del self.creditCardUpFront + else: + self.creditCardUpFront = self.creditCardUpFront != 0 + self.housingEnabled = self.config.GetBool('want-housing', 1) + self.cannonsEnabled = self.config.GetBool('estate-cannons', 0) + self.fireworksEnabled = self.config.GetBool('estate-fireworks', 0) + self.dayNightEnabled = self.config.GetBool('estate-day-night', 0) + self.cloudPlatformsEnabled = self.config.GetBool('estate-clouds', 0) + self.greySpacing = self.config.GetBool('allow-greyspacing', 0) + self.goonsEnabled = self.config.GetBool('estate-goon', 0) + self.restrictTrialers = self.config.GetBool('restrict-trialers', 1) + self.roamingTrialers = self.config.GetBool('roaming-trialers', 1) + self.slowQuietZone = self.config.GetBool('slow-quiet-zone', 0) + self.slowQuietZoneDelay = self.config.GetFloat('slow-quiet-zone-delay', 5) + self.killInterestResponse = self.config.GetBool('kill-interest-response', 0) + tpMgr = TextPropertiesManager.getGlobalPtr() + WLDisplay = TextProperties() + WLDisplay.setSlant(0.3) + WLEnter = TextProperties() + WLEnter.setTextColor(1.0, 0.0, 0.0, 1) + tpMgr.setProperties('WLDisplay', WLDisplay) + tpMgr.setProperties('WLEnter', WLEnter) + del tpMgr + self.lastScreenShotTime = globalClock.getRealTime() + self.accept('InputState-forward', self.__walking) + self.canScreenShot = 1 + self.glitchCount = 0 + self.walking = 0 + self.resetMusic = self.loadMusic('phase_3/audio/bgm/MIDI_Events_16channels.mid') + self.oldX = max(1, base.win.getXSize()) + self.oldY = max(1, base.win.getYSize()) + self.aspectRatio = float(self.oldX) / self.oldY + return + + def windowEvent(self, win): + OTPBase.OTPBase.windowEvent(self, win) + if not config.GetInt('keep-aspect-ratio', 0): + return + x = max(1, win.getXSize()) + y = max(1, win.getYSize()) + maxX = base.pipe.getDisplayWidth() + maxY = base.pipe.getDisplayHeight() + cwp = win.getProperties() + originX = 0 + originY = 0 + if cwp.hasOrigin(): + originX = cwp.getXOrigin() + originY = cwp.getYOrigin() + if originX > maxX: + originX = originX - maxX + if originY > maxY: + oringY = originY - maxY + maxX -= originX + maxY -= originY + if math.fabs(x - self.oldX) > math.fabs(y - self.oldY): + newY = x / self.aspectRatio + newX = x + if newY > maxY: + newY = maxY + newX = self.aspectRatio * maxY + else: + newX = self.aspectRatio * y + newY = y + if newX > maxX: + newX = maxX + newY = maxX / self.aspectRatio + wp = WindowProperties() + wp.setSize(newX, newY) + base.win.requestProperties(wp) + base.cam.node().getLens().setFilmSize(newX, newY) + self.oldX = newX + self.oldY = newY + + def disableShowbaseMouse(self): + self.useDrive() + self.disableMouse() + if self.mouseInterface: + self.mouseInterface.reparentTo(self.dataUnused) + if base.mouse2cam: + self.mouse2cam.reparentTo(self.dataUnused) + + def __walking(self, pressed): + self.walking = pressed + + def takeScreenShot(self): + namePrefix = 'screenshot' + namePrefix = launcher.logPrefix + namePrefix + timedif = globalClock.getRealTime() - self.lastScreenShotTime + if self.glitchCount > 10 and self.walking: + return + if timedif < 1.0 and self.walking: + self.glitchCount += 1 + return + if not hasattr(self, 'localAvatar'): + self.screenshot(namePrefix=namePrefix) + self.lastScreenShotTime = globalClock.getRealTime() + return + coordOnScreen = self.config.GetBool('screenshot-coords', 0) + self.localAvatar.stopThisFrame = 1 + ctext = self.localAvatar.getAvPosStr() + self.screenshotStr = '' + messenger.send('takingScreenshot') + if coordOnScreen: + coordTextLabel = DirectLabel(pos=(-0.81, 0.001, -0.87), text=ctext, text_scale=0.05, text_fg=VBase4(1.0, 1.0, 1.0, 1.0), text_bg=(0, 0, 0, 0), text_shadow=(0, 0, 0, 1), relief=None) + coordTextLabel.setBin('gui-popup', 0) + strTextLabel = None + if len(self.screenshotStr): + strTextLabel = DirectLabel(pos=(0.0, 0.001, 0.9), text=self.screenshotStr, text_scale=0.05, text_fg=VBase4(1.0, 1.0, 1.0, 1.0), text_bg=(0, 0, 0, 0), text_shadow=(0, 0, 0, 1), relief=None) + strTextLabel.setBin('gui-popup', 0) + self.graphicsEngine.renderFrame() + self.screenshot(namePrefix=namePrefix, imageComment=ctext + ' ' + self.screenshotStr) + self.lastScreenShotTime = globalClock.getRealTime() + if coordOnScreen: + if strTextLabel is not None: + strTextLabel.destroy() + coordTextLabel.destroy() + return + + def addScreenshotString(self, str): + if len(self.screenshotStr): + self.screenshotStr += '\n' + self.screenshotStr += str + + def initNametagGlobals(self): + arrow = loader.loadModel('phase_3/models/props/arrow') + card = loader.loadModel('phase_3/models/props/panel') + speech3d = ChatBalloon(loader.loadModelNode('phase_3/models/props/chatbox')) + thought3d = ChatBalloon(loader.loadModelNode('phase_3/models/props/chatbox_thought_cutout')) + speech2d = ChatBalloon(loader.loadModelNode('phase_3/models/props/chatbox_noarrow')) + chatButtonGui = loader.loadModel('phase_3/models/gui/chat_button_gui') + NametagGlobals.setCamera(self.cam) + NametagGlobals.setArrowModel(arrow) + NametagGlobals.setNametagCard(card, VBase4(-0.5, 0.5, -0.5, 0.5)) + if self.mouseWatcherNode: + NametagGlobals.setMouseWatcher(self.mouseWatcherNode) + NametagGlobals.setSpeechBalloon3d(speech3d) + NametagGlobals.setThoughtBalloon3d(thought3d) + NametagGlobals.setSpeechBalloon2d(speech2d) + NametagGlobals.setThoughtBalloon2d(thought3d) + NametagGlobals.setPageButton(PGButton.SReady, chatButtonGui.find('**/Horiz_Arrow_UP')) + NametagGlobals.setPageButton(PGButton.SDepressed, chatButtonGui.find('**/Horiz_Arrow_DN')) + NametagGlobals.setPageButton(PGButton.SRollover, chatButtonGui.find('**/Horiz_Arrow_Rllvr')) + NametagGlobals.setQuitButton(PGButton.SReady, chatButtonGui.find('**/CloseBtn_UP')) + NametagGlobals.setQuitButton(PGButton.SDepressed, chatButtonGui.find('**/CloseBtn_DN')) + NametagGlobals.setQuitButton(PGButton.SRollover, chatButtonGui.find('**/CloseBtn_Rllvr')) + rolloverSound = DirectGuiGlobals.getDefaultRolloverSound() + if rolloverSound: + NametagGlobals.setRolloverSound(rolloverSound) + clickSound = DirectGuiGlobals.getDefaultClickSound() + if clickSound: + NametagGlobals.setClickSound(clickSound) + NametagGlobals.setToon(self.cam) + self.marginManager = MarginManager() + self.margins = self.aspect2d.attachNewNode(self.marginManager, DirectGuiGlobals.MIDGROUND_SORT_INDEX + 1) + mm = self.marginManager + self.leftCells = [mm.addGridCell(0, 1, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), mm.addGridCell(0, 2, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), mm.addGridCell(0, 3, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)] + self.bottomCells = [mm.addGridCell(0.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), + mm.addGridCell(1.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), + mm.addGridCell(2.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), + mm.addGridCell(3.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), + mm.addGridCell(4.5, 0, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)] + self.rightCells = [mm.addGridCell(5, 2, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop), mm.addGridCell(5, 1, base.a2dLeft, base.a2dRight, base.a2dBottom, base.a2dTop)] + + def setCellsAvailable(self, cell_list, available): + for cell in cell_list: + self.marginManager.setCellAvailable(cell, available) + + def cleanupDownloadWatcher(self): + self.downloadWatcher.cleanup() + self.downloadWatcher = None + return + + def startShow(self, cr, launcherServer = None): + self.cr = cr + base.graphicsEngine.renderFrame() + self.downloadWatcher = ToontownDownloadWatcher.ToontownDownloadWatcher(TTLocalizer.LauncherPhaseNames) + if launcher.isDownloadComplete(): + self.cleanupDownloadWatcher() + else: + self.acceptOnce('launcherAllPhasesComplete', self.cleanupDownloadWatcher) + gameServer = base.config.GetString('game-server', '') + if gameServer: + self.notify.info('Using game-server from Configrc: %s ' % gameServer) + elif launcherServer: + gameServer = launcherServer + self.notify.info('Using gameServer from launcher: %s ' % gameServer) + else: + gameServer = 'localhost' + serverPort = base.config.GetInt('server-port', 6667) + serverList = [] + for name in gameServer.split(';'): + url = URLSpec(name, 1) + url.setScheme('s') + if not url.hasPort(): + url.setPort(serverPort) + serverList.append(url) + + if len(serverList) == 1: + failover = base.config.GetString('server-failover', '') + serverURL = serverList[0] + for arg in failover.split(): + try: + port = int(arg) + url = URLSpec(serverURL) + url.setPort(port) + except: + url = URLSpec(arg, 1) + + if url != serverURL: + serverList.append(url) + + cr.loginFSM.request('connect', [serverList]) + self.ttAccess = ToontownAccess.ToontownAccess() + self.ttAccess.initModuleInfo() + + def removeGlitchMessage(self): + self.ignore('InputState-forward') + print 'ignoring InputState-forward' + + def exitShow(self, errorCode = None): + self.notify.info('Exiting Toontown: errorCode = %s' % errorCode) + if errorCode: + launcher.setPandaErrorCode(errorCode) + else: + launcher.setPandaErrorCode(0) + sys.exit() + + def setExitErrorCode(self, code): + self.exitErrorCode = code + if os.name == 'nt': + exitCode2exitPage = {OTPLauncherGlobals.ExitEnableChat: 'chat', + OTPLauncherGlobals.ExitSetParentPassword: 'setparentpassword', + OTPLauncherGlobals.ExitPurchase: 'purchase'} + if code in exitCode2exitPage: + launcher.setRegistry('EXIT_PAGE', exitCode2exitPage[code]) + + def getExitErrorCode(self): + return self.exitErrorCode + + def userExit(self): + try: + self.localAvatar.d_setAnimState('TeleportOut', 1) + except: + pass + + if hasattr(self, 'ttAccess'): + self.ttAccess.delete() + if self.cr.timeManager: + self.cr.timeManager.setDisconnectReason(ToontownGlobals.DisconnectCloseWindow) + base.cr._userLoggingOut = False + try: + localAvatar + except: + pass + else: + messenger.send('clientLogout') + self.cr.dumpAllSubShardObjects() + + self.cr.loginFSM.request('shutdown') + self.notify.warning('Could not request shutdown; exiting anyway.') + self.exitShow() + + def panda3dRenderError(self): + launcher.setPandaErrorCode(14) + if self.cr.timeManager: + self.cr.timeManager.setDisconnectReason(ToontownGlobals.DisconnectGraphicsError) + self.cr.sendDisconnect() + sys.exit() + + def getShardPopLimits(self): + if self.cr.productName == 'JP': + return (config.GetInt('shard-low-pop', ToontownGlobals.LOW_POP_JP), config.GetInt('shard-mid-pop', ToontownGlobals.MID_POP_JP), config.GetInt('shard-high-pop', ToontownGlobals.HIGH_POP_JP)) + elif self.cr.productName in ['BR', 'FR']: + return (config.GetInt('shard-low-pop', ToontownGlobals.LOW_POP_INTL), config.GetInt('shard-mid-pop', ToontownGlobals.MID_POP_INTL), config.GetInt('shard-high-pop', ToontownGlobals.HIGH_POP_INTL)) + else: + return (config.GetInt('shard-low-pop', ToontownGlobals.LOW_POP), config.GetInt('shard-mid-pop', ToontownGlobals.MID_POP), config.GetInt('shard-high-pop', ToontownGlobals.HIGH_POP)) + + def playMusic(self, music, looping = 0, interrupt = 1, volume = None, time = 0.0): + OTPBase.OTPBase.playMusic(self, self.resetMusic) + OTPBase.OTPBase.playMusic(self, music, looping, interrupt, volume, time) diff --git a/toontown/toonbase/ToonBaseGlobal.py b/toontown/toonbase/ToonBaseGlobal.py new file mode 100644 index 0000000..f593a0c --- /dev/null +++ b/toontown/toonbase/ToonBaseGlobal.py @@ -0,0 +1 @@ +from ToonBase import * diff --git a/toontown/toonbase/ToontownAccess.py b/toontown/toonbase/ToontownAccess.py new file mode 100644 index 0000000..5498800 --- /dev/null +++ b/toontown/toonbase/ToontownAccess.py @@ -0,0 +1,66 @@ +from pandac.PandaModules import listProcessModules +from direct.task import Task +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals + +class ToontownAccess: + + def __init__(self): + self.startupModules = [] + + def initModuleInfo(self): + self.startupModules = self.getModuleList() + taskMgr.doMethodLater(300, self.checkModuleInfo, 'moduleListTask') + + def delete(self): + taskMgr.remove('moduleListTask') + del self.startupModules + + def checkModuleInfo(self, task): + currentModuleList = self.getModuleList() + newModules = [] + for module in currentModuleList: + if module not in self.startupModules: + self.startupModules.insert(0, module) + newModules.insert(0, module) + + self.sendUpdate('setModuleInfo', [newModules]) + return task.again + + def getModuleList(self): + moduleString = listProcessModules() + moduleList = [] + if moduleString: + moduleList = moduleString.split(',') + return moduleList + + def sendUpdate(self, fieldName, args = [], sendToId = None): + if base.cr and hasattr(base, 'localAvatar'): + dg = base.localAvatar.dclass.clientFormatUpdate(fieldName, sendToId or base.localAvatar.doId, args) + base.cr.send(dg) + + def canAccess(self, zoneId = None): + if base.cr.isPaid(): + return True + allowed = False + allowedZones = [ToontownGlobals.ToontownCentral, + ToontownGlobals.MyEstate, + ToontownGlobals.GoofySpeedway, + ToontownGlobals.Tutorial] + specialZones = [ToontownGlobals.SellbotLobby] + if hasattr(base.cr, 'newsManager') and base.cr.newsManager: + holidayIds = base.cr.newsManager.getHolidayIdList() + if ToontownGlobals.SELLBOT_NERF_HOLIDAY in holidayIds: + specialZones.append(ToontownGlobals.SellbotHQ) + place = base.cr.playGame.getPlace() + if zoneId: + myHoodId = ZoneUtil.getCanonicalHoodId(zoneId) + else: + myHoodId = ZoneUtil.getCanonicalHoodId(place.zoneId) + if hasattr(place, 'id'): + myHoodId = place.id + if myHoodId in allowedZones: + allowed = True + elif zoneId and zoneId in specialZones: + allowed = True + return allowed diff --git a/toontown/toonbase/ToontownAccessAI.py b/toontown/toonbase/ToontownAccessAI.py new file mode 100644 index 0000000..1153661 --- /dev/null +++ b/toontown/toonbase/ToontownAccessAI.py @@ -0,0 +1,58 @@ +from otp.otpbase import OTPGlobals +from otp.ai import BanManagerAI +from toontown.toonbase import ToontownGlobals +from toontown.hood import ZoneUtil + +def canAccess(avatarId, zoneId, function = ''): + avatar = simbase.air.doId2do.get(avatarId) + if avatar and avatar.getGameAccess() != OTPGlobals.AccessFull and not openToAll(zoneId, avatar): + if cmp(function, 'DistributedBoardingPartyAI.checkBoard') == 0: + return False + simbase.air.writeServerEvent('suspicious', avatarId, 'User with rights: %s requesting enter for paid access content without proper rights in zone %s from %s' % (avatar.getGameAccess(), zoneId, function)) + if simbase.config.GetBool('want-ban-ispaid', True): + commentStr = 'User with rights: %s tried to gain access zone %s from function %s, an area they were not allowed to using TTInjector Hack' % (avatar.getGameAccess(), zoneId, function) + dislId = avatar.DISLid + simbase.air.banManager.ban(avatarId, dislId, commentStr) + return False + else: + return True + + +def openToAll(zoneId, avatar): + allowed = False + canonicalZoneId = ZoneUtil.getCanonicalHoodId(zoneId) + allowedZones = [ToontownGlobals.ToontownCentral, + ToontownGlobals.MyEstate, + ToontownGlobals.GoofySpeedway, + ToontownGlobals.Tutorial] + specialZones = [ToontownGlobals.SellbotLobby] + if ToontownGlobals.SELLBOT_NERF_HOLIDAY in simbase.air.holidayManager.currentHolidays: + specialZones.append(ToontownGlobals.SellbotHQ) + ownerId = simbase.air.estateMgr.getOwnerFromZone(zoneId) + if ownerId: + for zone in simbase.air.estateMgr.getEstateZones(ownerId): + specialZones.append(zone) + + if canonicalZoneId in allowedZones or avatar.isInEstate(): + allowed = True + elif zoneId in specialZones: + allowed = True + elif canonicalZoneId >= ToontownGlobals.DynamicZonesBegin and not avatar.getTutorialAck(): + zoneDict = simbase.air.tutorialManager.playerDict.get(avatar.doId) + if zoneDict: + allowed = True + return allowed + + +def canWearSuit(avatarId, zoneId): + canonicalZoneId = ZoneUtil.getCanonicalHoodId(zoneId) + allowedSuitZones = [ToontownGlobals.LawbotHQ, + ToontownGlobals.CashbotHQ, + ToontownGlobals.SellbotHQ, + ToontownGlobals.BossbotHQ] + if canonicalZoneId in allowedSuitZones: + return True + elif zoneId >= ToontownGlobals.DynamicZonesBegin: + return True + else: + return False diff --git a/toontown/toonbase/ToontownBattleGlobals.py b/toontown/toonbase/ToontownBattleGlobals.py new file mode 100644 index 0000000..7e1398f --- /dev/null +++ b/toontown/toonbase/ToontownBattleGlobals.py @@ -0,0 +1,845 @@ +from ToontownGlobals import * +import math +import TTLocalizer +BattleCamFaceOffFov = 30.0 +BattleCamFaceOffPos = Point3(0, -10, 4) +BattleCamDefaultPos = Point3(0, -8.6, 16.5) +BattleCamDefaultHpr = Vec3(0, -61, 0) +BattleCamDefaultFov = 80.0 +BattleCamMenuFov = 65.0 +BattleCamJoinPos = Point3(0, -12, 13) +BattleCamJoinHpr = Vec3(0, -45, 0) +SkipMovie = 0 +BaseHp = 15 +Tracks = TTLocalizer.BattleGlobalTracks +NPCTracks = TTLocalizer.BattleGlobalNPCTracks +TrackColors = ((211 / 255.0, 148 / 255.0, 255 / 255.0), + (249 / 255.0, 255 / 255.0, 93 / 255.0), + (79 / 255.0, 190 / 255.0, 76 / 255.0), + (93 / 255.0, 108 / 255.0, 239 / 255.0), + (255 / 255.0, 145 / 255.0, 66 / 255.0), + (255 / 255.0, 65 / 255.0, 199 / 255.0), + (67 / 255.0, 243 / 255.0, 255 / 255.0)) +HEAL_TRACK = 0 +TRAP_TRACK = 1 +LURE_TRACK = 2 +SOUND_TRACK = 3 +THROW_TRACK = 4 +SQUIRT_TRACK = 5 +DROP_TRACK = 6 +NPC_RESTOCK_GAGS = 7 +NPC_TOONS_HIT = 8 +NPC_COGS_MISS = 9 +MIN_TRACK_INDEX = 0 +MAX_TRACK_INDEX = 6 +MIN_LEVEL_INDEX = 0 +MAX_LEVEL_INDEX = 6 +MAX_UNPAID_LEVEL_INDEX = 4 +LAST_REGULAR_GAG_LEVEL = 5 +UBER_GAG_LEVEL_INDEX = 6 +NUM_GAG_TRACKS = 7 +PropTypeToTrackBonus = {AnimPropTypes.Hydrant: SQUIRT_TRACK, + AnimPropTypes.Mailbox: THROW_TRACK, + AnimPropTypes.Trashcan: HEAL_TRACK} +Levels = [[0, + 20, + 200, + 800, + 2000, + 6000, + 10000], + [0, + 20, + 100, + 800, + 2000, + 6000, + 10000], + [0, + 20, + 100, + 800, + 2000, + 6000, + 10000], + [0, + 40, + 200, + 1000, + 2500, + 7500, + 10000], + [0, + 10, + 50, + 400, + 2000, + 6000, + 10000], + [0, + 10, + 50, + 400, + 2000, + 6000, + 10000], + [0, + 20, + 100, + 500, + 2000, + 6000, + 10000]] +regMaxSkill = 10000 +UberSkill = 500 +MaxSkill = UberSkill + regMaxSkill +UnpaidMaxSkills = [Levels[0][1] - 1, + Levels[1][1] - 1, + Levels[2][1] - 1, + Levels[3][1] - 1, + Levels[4][4] - 1, + Levels[5][4] - 1, + Levels[6][1] - 1] +ExperienceCap = 200 + +def gagIsPaidOnly(track, level): + return Levels[track][level] > UnpaidMaxSkills[track] + + +def gagIsVelvetRoped(track, level): + if level > 0: + if track in [4, 5]: + if level > 3: + return True + else: + return True + return False + + +MaxToonAcc = 95 +StartingLevel = 0 +CarryLimits = (((10, + 0, + 0, + 0, + 0, + 0, + 0), + (10, + 5, + 0, + 0, + 0, + 0, + 0), + (15, + 10, + 5, + 0, + 0, + 0, + 0), + (20, + 15, + 10, + 5, + 0, + 0, + 0), + (25, + 20, + 15, + 10, + 3, + 0, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 1)), + ((5, + 0, + 0, + 0, + 0, + 0, + 0), + (7, + 3, + 0, + 0, + 0, + 0, + 0), + (10, + 7, + 3, + 0, + 0, + 0, + 0), + (15, + 10, + 7, + 3, + 0, + 0, + 0), + (15, + 15, + 10, + 5, + 3, + 0, + 0), + (20, + 15, + 15, + 10, + 5, + 2, + 0), + (20, + 15, + 15, + 10, + 5, + 2, + 1)), + ((10, + 0, + 0, + 0, + 0, + 0, + 0), + (10, + 5, + 0, + 0, + 0, + 0, + 0), + (15, + 10, + 5, + 0, + 0, + 0, + 0), + (20, + 15, + 10, + 5, + 0, + 0, + 0), + (25, + 20, + 15, + 10, + 3, + 0, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 1)), + ((10, + 0, + 0, + 0, + 0, + 0, + 0), + (10, + 5, + 0, + 0, + 0, + 0, + 0), + (15, + 10, + 5, + 0, + 0, + 0, + 0), + (20, + 15, + 10, + 5, + 0, + 0, + 0), + (25, + 20, + 15, + 10, + 3, + 0, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 1)), + ((10, + 0, + 0, + 0, + 0, + 0, + 0), + (10, + 5, + 0, + 0, + 0, + 0, + 0), + (15, + 10, + 5, + 0, + 0, + 0, + 0), + (20, + 15, + 10, + 5, + 0, + 0, + 0), + (25, + 20, + 15, + 10, + 3, + 0, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 1)), + ((10, + 0, + 0, + 0, + 0, + 0, + 0), + (10, + 5, + 0, + 0, + 0, + 0, + 0), + (15, + 10, + 5, + 0, + 0, + 0, + 0), + (20, + 15, + 10, + 5, + 0, + 0, + 0), + (25, + 20, + 15, + 10, + 3, + 0, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 1)), + ((10, + 0, + 0, + 0, + 0, + 0, + 0), + (10, + 5, + 0, + 0, + 0, + 0, + 0), + (15, + 10, + 5, + 0, + 0, + 0, + 0), + (20, + 15, + 10, + 5, + 0, + 0, + 0), + (25, + 20, + 15, + 10, + 3, + 0, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 0), + (30, + 25, + 20, + 15, + 7, + 3, + 1))) +MaxProps = ((15, 40), (30, 60), (75, 80)) +DLF_SKELECOG = 1 +DLF_FOREMAN = 2 +DLF_VP = 4 +DLF_CFO = 8 +DLF_SUPERVISOR = 16 +DLF_VIRTUAL = 32 +DLF_REVIVES = 64 +pieNames = ['tart', + 'fruitpie-slice', + 'creampie-slice', + 'fruitpie', + 'creampie', + 'birthday-cake', + 'wedding-cake', + 'lawbook'] +AvProps = (('feather', + 'bullhorn', + 'lipstick', + 'bamboocane', + 'pixiedust', + 'baton', + 'baton'), + ('banana', + 'rake', + 'marbles', + 'quicksand', + 'trapdoor', + 'tnt', + 'traintrack'), + ('1dollar', + 'smmagnet', + '5dollar', + 'bigmagnet', + '10dollar', + 'hypnogogs', + 'hypnogogs'), + ('bikehorn', + 'whistle', + 'bugle', + 'aoogah', + 'elephant', + 'foghorn', + 'singing'), + ('cupcake', + 'fruitpieslice', + 'creampieslice', + 'fruitpie', + 'creampie', + 'cake', + 'cake'), + ('flower', + 'waterglass', + 'waterballoon', + 'bottle', + 'firehose', + 'stormcloud', + 'stormcloud'), + ('flowerpot', + 'sandbag', + 'anvil', + 'weight', + 'safe', + 'piano', + 'piano')) +AvPropsNew = (('inventory_feather', + 'inventory_megaphone', + 'inventory_lipstick', + 'inventory_bamboo_cane', + 'inventory_pixiedust', + 'inventory_juggling_cubes', + 'inventory_ladder'), + ('inventory_bannana_peel', + 'inventory_rake', + 'inventory_marbles', + 'inventory_quicksand_icon', + 'inventory_trapdoor', + 'inventory_tnt', + 'inventory_traintracks'), + ('inventory_1dollarbill', + 'inventory_small_magnet', + 'inventory_5dollarbill', + 'inventory_big_magnet', + 'inventory_10dollarbill', + 'inventory_hypno_goggles', + 'inventory_screen'), + ('inventory_bikehorn', + 'inventory_whistle', + 'inventory_bugle', + 'inventory_aoogah', + 'inventory_elephant', + 'inventory_fog_horn', + 'inventory_opera_singer'), + ('inventory_tart', + 'inventory_fruit_pie_slice', + 'inventory_cream_pie_slice', + 'inventory_fruitpie', + 'inventory_creampie', + 'inventory_cake', + 'inventory_wedding'), + ('inventory_squirt_flower', + 'inventory_glass_of_water', + 'inventory_water_gun', + 'inventory_seltzer_bottle', + 'inventory_firehose', + 'inventory_storm_cloud', + 'inventory_geyser'), + ('inventory_flower_pot', + 'inventory_sandbag', + 'inventory_anvil', + 'inventory_weight', + 'inventory_safe_box', + 'inventory_piano', + 'inventory_ship')) +AvPropStrings = TTLocalizer.BattleGlobalAvPropStrings +AvPropStringsSingular = TTLocalizer.BattleGlobalAvPropStringsSingular +AvPropStringsPlural = TTLocalizer.BattleGlobalAvPropStringsPlural +AvPropAccuracy = ((70, + 70, + 70, + 70, + 70, + 70, + 100), + (0, + 0, + 0, + 0, + 0, + 0, + 0), + (50, + 50, + 60, + 60, + 70, + 70, + 90), + (95, + 95, + 95, + 95, + 95, + 95, + 95), + (75, + 75, + 75, + 75, + 75, + 75, + 75), + (95, + 95, + 95, + 95, + 95, + 95, + 95), + (50, + 50, + 50, + 50, + 50, + 50, + 50)) +AvLureBonusAccuracy = (60, + 60, + 70, + 70, + 80, + 80, + 100) +AvTrackAccStrings = TTLocalizer.BattleGlobalAvTrackAccStrings +AvPropDamage = ((((8, 10), (Levels[0][0], Levels[0][1])), + ((15, 18), (Levels[0][1], Levels[0][2])), + ((25, 30), (Levels[0][2], Levels[0][3])), + ((40, 45), (Levels[0][3], Levels[0][4])), + ((60, 70), (Levels[0][4], Levels[0][5])), + ((90, 120), (Levels[0][5], Levels[0][6])), + ((210, 210), (Levels[0][6], MaxSkill))), + (((10, 12), (Levels[1][0], Levels[1][1])), + ((18, 20), (Levels[1][1], Levels[1][2])), + ((30, 35), (Levels[1][2], Levels[1][3])), + ((45, 50), (Levels[1][3], Levels[1][4])), + ((60, 70), (Levels[1][4], Levels[1][5])), + ((90, 180), (Levels[1][5], Levels[1][6])), + ((195, 195), (Levels[1][6], MaxSkill))), + (((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0)), + ((0, 0), (0, 0))), + (((3, 4), (Levels[3][0], Levels[3][1])), + ((5, 7), (Levels[3][1], Levels[3][2])), + ((9, 11), (Levels[3][2], Levels[3][3])), + ((14, 16), (Levels[3][3], Levels[3][4])), + ((19, 21), (Levels[3][4], Levels[3][5])), + ((25, 50), (Levels[3][5], Levels[3][6])), + ((90, 90), (Levels[3][6], MaxSkill))), + (((4, 6), (Levels[4][0], Levels[4][1])), + ((8, 10), (Levels[4][1], Levels[4][2])), + ((14, 17), (Levels[4][2], Levels[4][3])), + ((24, 27), (Levels[4][3], Levels[4][4])), + ((36, 40), (Levels[4][4], Levels[4][5])), + ((48, 100), (Levels[4][5], Levels[4][6])), + ((120, 120), (Levels[4][6], MaxSkill))), + (((3, 4), (Levels[5][0], Levels[5][1])), + ((6, 8), (Levels[5][1], Levels[5][2])), + ((10, 12), (Levels[5][2], Levels[5][3])), + ((18, 21), (Levels[5][3], Levels[5][4])), + ((27, 30), (Levels[5][4], Levels[5][5])), + ((36, 80), (Levels[5][5], Levels[5][6])), + ((105, 105), (Levels[5][6], MaxSkill))), + (((10, 10), (Levels[6][0], Levels[6][1])), + ((18, 18), (Levels[6][1], Levels[6][2])), + ((30, 30), (Levels[6][2], Levels[6][3])), + ((45, 45), (Levels[6][3], Levels[6][4])), + ((60, 60), (Levels[6][4], Levels[6][5])), + ((85, 170), (Levels[6][5], Levels[6][6])), + ((180, 180), (Levels[6][6], MaxSkill)))) +ATK_SINGLE_TARGET = 0 +ATK_GROUP_TARGET = 1 +AvPropTargetCat = ((ATK_SINGLE_TARGET, + ATK_GROUP_TARGET, + ATK_SINGLE_TARGET, + ATK_GROUP_TARGET, + ATK_SINGLE_TARGET, + ATK_GROUP_TARGET, + ATK_GROUP_TARGET), + (ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET), + (ATK_GROUP_TARGET, + ATK_GROUP_TARGET, + ATK_GROUP_TARGET, + ATK_GROUP_TARGET, + ATK_GROUP_TARGET, + ATK_GROUP_TARGET, + ATK_GROUP_TARGET), + (ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_SINGLE_TARGET, + ATK_GROUP_TARGET)) +AvPropTarget = (0, + 3, + 0, + 2, + 3, + 3, + 3) + +def getAvPropDamage(attackTrack, attackLevel, exp, organicBonus = False, propBonus = False, propAndOrganicBonusStack = False): + minD = AvPropDamage[attackTrack][attackLevel][0][0] + maxD = AvPropDamage[attackTrack][attackLevel][0][1] + minE = AvPropDamage[attackTrack][attackLevel][1][0] + maxE = AvPropDamage[attackTrack][attackLevel][1][1] + expVal = min(exp, maxE) + expPerHp = float(maxE - minE + 1) / float(maxD - minD + 1) + damage = math.floor((expVal - minE) / expPerHp) + minD + if damage <= 0: + damage = minD + if propAndOrganicBonusStack: + originalDamage = damage + if organicBonus: + damage += getDamageBonus(originalDamage) + if propBonus: + damage += getDamageBonus(originalDamage) + elif organicBonus or propBonus: + damage += getDamageBonus(damage) + return damage + + +def getDamageBonus(normal): + bonus = int(normal * 0.1) + if bonus < 1 and normal > 0: + bonus = 1 + return bonus + + +def isGroup(track, level): + return AvPropTargetCat[AvPropTarget[track]][level] + + +def getCreditMultiplier(floorIndex): + return 1 + floorIndex * 0.5 + + +def getFactoryCreditMultiplier(factoryId): + return 2.0 + + +def getFactoryMeritMultiplier(factoryId): + return 4.0 + + +def getMintCreditMultiplier(mintId): + return {CashbotMintIntA: 2.0, + CashbotMintIntB: 2.5, + CashbotMintIntC: 3.0}.get(mintId, 1.0) + + +def getStageCreditMultiplier(floor): + return getCreditMultiplier(floor) + + +def getCountryClubCreditMultiplier(countryClubId): + return {BossbotCountryClubIntA: 2.0, + BossbotCountryClubIntB: 2.5, + BossbotCountryClubIntC: 3.0}.get(countryClubId, 1.0) + + +def getBossBattleCreditMultiplier(battleNumber): + return 1 + battleNumber + + +def getInvasionMultiplier(): + return 2.0 + + +def getMoreXpHolidayMultiplier(): + return 2.0 + + +def encodeUber(trackList): + bitField = 0 + for trackIndex in range(len(trackList)): + if trackList[trackIndex] > 0: + bitField += pow(2, trackIndex) + + return bitField + + +def decodeUber(flagMask): + if flagMask == 0: + return [] + maxPower = 16 + workNumber = flagMask + workPower = maxPower + trackList = [] + while workPower >= 0: + if workNumber >= pow(2, workPower): + workNumber -= pow(2, workPower) + trackList.insert(0, 1) + else: + trackList.insert(0, 0) + workPower -= 1 + + endList = len(trackList) + foundOne = 0 + while not foundOne: + if trackList[endList - 1] == 0: + trackList.pop(endList - 1) + endList -= 1 + else: + foundOne = 1 + + return trackList + + +def getUberFlag(flagMask, index): + decode = decodeUber(flagMask) + if index >= len(decode): + return 0 + else: + return decode[index] + + +def getUberFlagSafe(flagMask, index): + if flagMask == 'unknown' or flagMask < 0: + return -1 + else: + return getUberFlag(flagMask, index) diff --git a/toontown/toonbase/ToontownGlobals.py b/toontown/toonbase/ToontownGlobals.py new file mode 100644 index 0000000..d3c0ce8 --- /dev/null +++ b/toontown/toonbase/ToontownGlobals.py @@ -0,0 +1,1672 @@ +import TTLocalizer +from otp.otpbase.OTPGlobals import * +from direct.showbase.PythonUtil import Enum, invertDict +from pandac.PandaModules import BitMask32, Vec4 +MapHotkeyOn = 'alt' +MapHotkeyOff = 'alt-up' +MapHotkey = 'alt' +AccountDatabaseChannelId = 4008 +ToonDatabaseChannelId = 4021 +DoodleDatabaseChannelId = 4023 +DefaultDatabaseChannelId = AccountDatabaseChannelId +DatabaseIdFromClassName = {'Account': AccountDatabaseChannelId} +CogHQCameraFov = 60.0 +BossBattleCameraFov = 72.0 +MakeAToonCameraFov = 52.0 +CeilingBitmask = BitMask32(256) +FloorEventBitmask = BitMask32(16) +PieBitmask = BitMask32(256) +PetBitmask = BitMask32(8) +CatchGameBitmask = BitMask32(16) +CashbotBossObjectBitmask = BitMask32(16) +FurnitureSideBitmask = BitMask32(32) +FurnitureTopBitmask = BitMask32(64) +FurnitureDragBitmask = BitMask32(128) +PetLookatPetBitmask = BitMask32(256) +PetLookatNonPetBitmask = BitMask32(512) +BanquetTableBitmask = BitMask32(1024) +FullPies = 65535 +CogHQCameraFar = 900.0 +CogHQCameraNear = 1.0 +CashbotHQCameraFar = 2000.0 +CashbotHQCameraNear = 1.0 +LawbotHQCameraFar = 3000.0 +LawbotHQCameraNear = 1.0 +BossbotHQCameraFar = 3000.0 +BossbotHQCameraNear = 1.0 +SpeedwayCameraFar = 8000.0 +SpeedwayCameraNear = 1.0 +MaxMailboxContents = 30 +MaxHouseItems = 45 +MaxAccessories = 50 +ExtraDeletedItems = 5 +DeletedItemLifetime = 7 * 24 * 60 +CatalogNumWeeksPerSeries = 13 +CatalogNumWeeks = 78 +PetFloorCollPriority = 5 +PetPanelProximityPriority = 6 +P_NoTrunk = -28 +P_AlreadyOwnBiggerCloset = -27 +P_ItemAlreadyRented = -26 +P_OnAwardOrderListFull = -25 +P_AwardMailboxFull = -24 +P_ItemInPetTricks = -23 +P_ItemInMyPhrases = -22 +P_ItemOnAwardOrder = -21 +P_ItemInAwardMailbox = -20 +P_ItemAlreadyWorn = -19 +P_ItemInCloset = -18 +P_ItemOnGiftOrder = -17 +P_ItemOnOrder = -16 +P_ItemInMailbox = -15 +P_PartyNotFound = 14 +P_WillNotFit = -13 +P_NotAGift = -12 +P_OnOrderListFull = -11 +P_MailboxFull = -10 +P_NoPurchaseMethod = -9 +P_ReachedPurchaseLimit = -8 +P_NoRoomForItem = -7 +P_NotShopping = -6 +P_NotAtMailbox = -5 +P_NotInCatalog = -4 +P_NotEnoughMoney = -3 +P_InvalidIndex = -2 +P_UserCancelled = -1 +P_ItemAvailable = 1 +P_ItemOnOrder = 2 +P_ItemUnneeded = 3 +GIFT_user = 0 +GIFT_admin = 1 +GIFT_RAT = 2 +GIFT_mobile = 3 +GIFT_cogs = 4 +GIFT_partyrefund = 5 +FM_InvalidItem = -7 +FM_NondeletableItem = -6 +FM_InvalidIndex = -5 +FM_NotOwner = -4 +FM_NotDirector = -3 +FM_RoomFull = -2 +FM_HouseFull = -1 +FM_MovedItem = 1 +FM_SwappedItem = 2 +FM_DeletedItem = 3 +FM_RecoveredItem = 4 +SPDonaldsBoat = 3 +SPMinniesPiano = 4 +CEVirtual = 14 +MaxHpLimit = 137 +MaxCarryLimit = 80 +MaxQuestCarryLimit = 4 +MaxCogSuitLevel = 50 - 1 +CogSuitHPLevels = (15 - 1, + 20 - 1, + 30 - 1, + 40 - 1, + 50 - 1) +setInterfaceFont(TTLocalizer.InterfaceFont) +setSignFont(TTLocalizer.SignFont) +from toontown.toontowngui import TTDialog +setDialogClasses(TTDialog.TTDialog, TTDialog.TTGlobalDialog) +ToonFont = None +BuildingNametagFont = None +MinnieFont = None +SuitFont = None + +def getToonFont(): + global ToonFont + if ToonFont == None: + ToonFont = loader.loadFont(TTLocalizer.ToonFont, lineHeight=1.0) + return ToonFont + + +def getBuildingNametagFont(): + global BuildingNametagFont + if BuildingNametagFont == None: + BuildingNametagFont = loader.loadFont(TTLocalizer.BuildingNametagFont) + return BuildingNametagFont + + +def getMinnieFont(): + global MinnieFont + if MinnieFont == None: + MinnieFont = loader.loadFont(TTLocalizer.MinnieFont) + return MinnieFont + + +def getSuitFont(): + global SuitFont + if SuitFont == None: + SuitFont = loader.loadFont(TTLocalizer.SuitFont, pixelsPerUnit=40, spaceAdvance=0.25, lineHeight=1.0) + return SuitFont + + +DonaldsDock = 1000 +ToontownCentral = 2000 +TheBrrrgh = 3000 +MinniesMelodyland = 4000 +DaisyGardens = 5000 +OutdoorZone = 6000 +FunnyFarm = 7000 +GoofySpeedway = 8000 +DonaldsDreamland = 9000 +BarnacleBoulevard = 1100 +SeaweedStreet = 1200 +LighthouseLane = 1300 +SillyStreet = 2100 +LoopyLane = 2200 +PunchlinePlace = 2300 +WalrusWay = 3100 +SleetStreet = 3200 +PolarPlace = 3300 +AltoAvenue = 4100 +BaritoneBoulevard = 4200 +TenorTerrace = 4300 +ElmStreet = 5100 +MapleStreet = 5200 +OakStreet = 5300 +LullabyLane = 9100 +PajamaPlace = 9200 +ToonHall = 2513 +HoodHierarchy = {ToontownCentral: (SillyStreet, LoopyLane, PunchlinePlace), + DonaldsDock: (BarnacleBoulevard, SeaweedStreet, LighthouseLane), + TheBrrrgh: (WalrusWay, SleetStreet, PolarPlace), + MinniesMelodyland: (AltoAvenue, BaritoneBoulevard, TenorTerrace), + DaisyGardens: (ElmStreet, MapleStreet, OakStreet), + DonaldsDreamland: (LullabyLane, PajamaPlace), + GoofySpeedway: ()} +WelcomeValleyToken = 0 +BossbotHQ = 10000 +BossbotLobby = 10100 +BossbotCountryClubIntA = 10500 +BossbotCountryClubIntB = 10600 +BossbotCountryClubIntC = 10700 +SellbotHQ = 11000 +SellbotLobby = 11100 +SellbotFactoryExt = 11200 +SellbotFactoryInt = 11500 +CashbotHQ = 12000 +CashbotLobby = 12100 +CashbotMintIntA = 12500 +CashbotMintIntB = 12600 +CashbotMintIntC = 12700 +LawbotHQ = 13000 +LawbotLobby = 13100 +LawbotOfficeExt = 13200 +LawbotOfficeInt = 13300 +LawbotStageIntA = 13300 +LawbotStageIntB = 13400 +LawbotStageIntC = 13500 +LawbotStageIntD = 13600 +Tutorial = 15000 +MyEstate = 16000 +GolfZone = 17000 +PartyHood = 18000 +HoodsAlwaysVisited = [17000, 18000] +WelcomeValleyBegin = 22000 +WelcomeValleyEnd = 61000 +DynamicZonesBegin = 61000 +DynamicZonesEnd = 1 << 20 +cogDept2index = {'c': 0, + 'l': 1, + 'm': 2, + 's': 3} +cogIndex2dept = invertDict(cogDept2index) +HQToSafezone = {SellbotHQ: DaisyGardens, + CashbotHQ: DonaldsDreamland, + LawbotHQ: TheBrrrgh, + BossbotHQ: DonaldsDock} +CogDeptNames = [TTLocalizer.Bossbot, + TTLocalizer.Lawbot, + TTLocalizer.Cashbot, + TTLocalizer.Sellbot] + +def cogHQZoneId2deptIndex(zone): + if zone >= 13000 and zone <= 13999: + return 1 + elif zone >= 12000: + return 2 + elif zone >= 11000: + return 3 + else: + return 0 + + +def cogHQZoneId2dept(zone): + return cogIndex2dept[cogHQZoneId2deptIndex(zone)] + + +def dept2cogHQ(dept): + dept2hq = {'c': BossbotHQ, + 'l': LawbotHQ, + 'm': CashbotHQ, + 's': SellbotHQ} + return dept2hq[dept] + + +MockupFactoryId = 0 +MintNumFloors = {CashbotMintIntA: 20, + CashbotMintIntB: 20, + CashbotMintIntC: 20} +CashbotMintCogLevel = 10 +CashbotMintSkelecogLevel = 11 +CashbotMintBossLevel = 12 +MintNumBattles = {CashbotMintIntA: 4, + CashbotMintIntB: 6, + CashbotMintIntC: 8} +MintCogBuckRewards = {CashbotMintIntA: 8, + CashbotMintIntB: 14, + CashbotMintIntC: 20} +MintNumRooms = {CashbotMintIntA: 2 * (6,) + 5 * (7,) + 5 * (8,) + 5 * (9,) + 3 * (10,), + CashbotMintIntB: 3 * (8,) + 6 * (9,) + 6 * (10,) + 5 * (11,), + CashbotMintIntC: 4 * (10,) + 10 * (11,) + 6 * (12,)} +BossbotCountryClubCogLevel = 11 +BossbotCountryClubSkelecogLevel = 12 +BossbotCountryClubBossLevel = 12 +CountryClubNumRooms = {BossbotCountryClubIntA: (4,), + BossbotCountryClubIntB: 3 * (8,) + 6 * (9,) + 6 * (10,) + 5 * (11,), + BossbotCountryClubIntC: 4 * (10,) + 10 * (11,) + 6 * (12,)} +CountryClubNumBattles = {BossbotCountryClubIntA: 3, + BossbotCountryClubIntB: 2, + BossbotCountryClubIntC: 3} +CountryClubCogBuckRewards = {BossbotCountryClubIntA: 8, + BossbotCountryClubIntB: 14, + BossbotCountryClubIntC: 20} +LawbotStageCogLevel = 10 +LawbotStageSkelecogLevel = 11 +LawbotStageBossLevel = 12 +StageNumBattles = {LawbotStageIntA: 0, + LawbotStageIntB: 0, + LawbotStageIntC: 0, + LawbotStageIntD: 0} +StageNoticeRewards = {LawbotStageIntA: 75, + LawbotStageIntB: 150, + LawbotStageIntC: 225, + LawbotStageIntD: 300} +StageNumRooms = {LawbotStageIntA: 2 * (6,) + 5 * (7,) + 5 * (8,) + 5 * (9,) + 3 * (10,), + LawbotStageIntB: 3 * (8,) + 6 * (9,) + 6 * (10,) + 5 * (11,), + LawbotStageIntC: 4 * (10,) + 10 * (11,) + 6 * (12,), + LawbotStageIntD: 4 * (10,) + 10 * (11,) + 6 * (12,)} +FT_FullSuit = 'fullSuit' +FT_Leg = 'leg' +FT_Arm = 'arm' +FT_Torso = 'torso' +factoryId2factoryType = {MockupFactoryId: FT_FullSuit, + SellbotFactoryInt: FT_FullSuit, + LawbotOfficeInt: FT_FullSuit} +StreetNames = TTLocalizer.GlobalStreetNames +StreetBranchZones = StreetNames.keys() +Hoods = (DonaldsDock, + ToontownCentral, + TheBrrrgh, + MinniesMelodyland, + DaisyGardens, + OutdoorZone, + FunnyFarm, + GoofySpeedway, + DonaldsDreamland, + BossbotHQ, + SellbotHQ, + CashbotHQ, + LawbotHQ, + GolfZone) +HoodsForTeleportAll = (DonaldsDock, + ToontownCentral, + TheBrrrgh, + MinniesMelodyland, + DaisyGardens, + OutdoorZone, + GoofySpeedway, + DonaldsDreamland, + BossbotHQ, + SellbotHQ, + CashbotHQ, + LawbotHQ, + GolfZone) +NoPreviousGameId = 0 +RaceGameId = 1 +CannonGameId = 2 +TagGameId = 3 +PatternGameId = 4 +RingGameId = 5 +MazeGameId = 6 +TugOfWarGameId = 7 +CatchGameId = 8 +DivingGameId = 9 +TargetGameId = 10 +PairingGameId = 11 +VineGameId = 12 +IceGameId = 13 +CogThiefGameId = 14 +TwoDGameId = 15 +PhotoGameId = 16 +TravelGameId = 100 +MinigameNames = {'race': RaceGameId, + 'cannon': CannonGameId, + 'tag': TagGameId, + 'pattern': PatternGameId, + 'minnie': PatternGameId, + 'match': PatternGameId, + 'matching': PatternGameId, + 'ring': RingGameId, + 'maze': MazeGameId, + 'tug': TugOfWarGameId, + 'catch': CatchGameId, + 'diving': DivingGameId, + 'target': TargetGameId, + 'pairing': PairingGameId, + 'vine': VineGameId, + 'ice': IceGameId, + 'thief': CogThiefGameId, + '2d': TwoDGameId, + 'photo': PhotoGameId, + 'travel': TravelGameId} +MinigameTemplateId = -1 +MinigameIDs = (RaceGameId, + CannonGameId, + TagGameId, + PatternGameId, + RingGameId, + MazeGameId, + TugOfWarGameId, + CatchGameId, + DivingGameId, + TargetGameId, + PairingGameId, + VineGameId, + IceGameId, + CogThiefGameId, + TwoDGameId, + PhotoGameId, + TravelGameId) +MinigamePlayerMatrix = {1: (CannonGameId, + RingGameId, + MazeGameId, + TugOfWarGameId, + CatchGameId, + DivingGameId, + TargetGameId, + PairingGameId, + VineGameId, + CogThiefGameId, + TwoDGameId), + 2: (CannonGameId, + PatternGameId, + RingGameId, + TagGameId, + MazeGameId, + TugOfWarGameId, + CatchGameId, + DivingGameId, + TargetGameId, + PairingGameId, + VineGameId, + IceGameId, + CogThiefGameId, + TwoDGameId), + 3: (CannonGameId, + PatternGameId, + RingGameId, + TagGameId, + RaceGameId, + MazeGameId, + TugOfWarGameId, + CatchGameId, + DivingGameId, + TargetGameId, + PairingGameId, + VineGameId, + IceGameId, + CogThiefGameId, + TwoDGameId), + 4: (CannonGameId, + PatternGameId, + RingGameId, + TagGameId, + RaceGameId, + MazeGameId, + TugOfWarGameId, + CatchGameId, + DivingGameId, + TargetGameId, + PairingGameId, + VineGameId, + IceGameId, + CogThiefGameId, + TwoDGameId)} +MinigameReleaseDates = {IceGameId: (2008, 8, 5), + PhotoGameId: (2008, 8, 13), + TwoDGameId: (2008, 8, 20), + CogThiefGameId: (2008, 8, 27)} +KeyboardTimeout = 300 +phaseMap = {Tutorial: 4, + ToontownCentral: 4, + MyEstate: 5.5, + DonaldsDock: 6, + MinniesMelodyland: 6, + GoofySpeedway: 6, + TheBrrrgh: 8, + DaisyGardens: 8, + FunnyFarm: 8, + DonaldsDreamland: 8, + OutdoorZone: 8, + BossbotHQ: 12, + SellbotHQ: 9, + CashbotHQ: 10, + LawbotHQ: 11, + GolfZone: 8, + PartyHood: 13} +streetPhaseMap = {ToontownCentral: 5, + DonaldsDock: 6, + MinniesMelodyland: 6, + GoofySpeedway: 6, + TheBrrrgh: 8, + DaisyGardens: 8, + FunnyFarm: 8, + DonaldsDreamland: 8, + OutdoorZone: 8, + BossbotHQ: 12, + SellbotHQ: 9, + CashbotHQ: 10, + LawbotHQ: 11, + PartyHood: 13} +dnaMap = {Tutorial: 'toontown_central', + ToontownCentral: 'toontown_central', + DonaldsDock: 'donalds_dock', + MinniesMelodyland: 'minnies_melody_land', + GoofySpeedway: 'goofy_speedway', + TheBrrrgh: 'the_burrrgh', + DaisyGardens: 'daisys_garden', + FunnyFarm: 'not done yet', + DonaldsDreamland: 'donalds_dreamland', + OutdoorZone: 'outdoor_zone', + BossbotHQ: 'cog_hq_bossbot', + SellbotHQ: 'cog_hq_sellbot', + CashbotHQ: 'cog_hq_cashbot', + LawbotHQ: 'cog_hq_lawbot', + GolfZone: 'golf_zone'} +hoodNameMap = {DonaldsDock: TTLocalizer.DonaldsDock, + ToontownCentral: TTLocalizer.ToontownCentral, + TheBrrrgh: TTLocalizer.TheBrrrgh, + MinniesMelodyland: TTLocalizer.MinniesMelodyland, + DaisyGardens: TTLocalizer.DaisyGardens, + OutdoorZone: TTLocalizer.OutdoorZone, + FunnyFarm: TTLocalizer.FunnyFarm, + GoofySpeedway: TTLocalizer.GoofySpeedway, + DonaldsDreamland: TTLocalizer.DonaldsDreamland, + BossbotHQ: TTLocalizer.BossbotHQ, + SellbotHQ: TTLocalizer.SellbotHQ, + CashbotHQ: TTLocalizer.CashbotHQ, + LawbotHQ: TTLocalizer.LawbotHQ, + Tutorial: TTLocalizer.Tutorial, + MyEstate: TTLocalizer.MyEstate, + GolfZone: TTLocalizer.GolfZone, + PartyHood: TTLocalizer.PartyHood} +safeZoneCountMap = {MyEstate: 8, + Tutorial: 6, + ToontownCentral: 6, + DonaldsDock: 10, + MinniesMelodyland: 5, + GoofySpeedway: 500, + TheBrrrgh: 8, + DaisyGardens: 9, + FunnyFarm: 500, + DonaldsDreamland: 5, + OutdoorZone: 500, + GolfZone: 500, + PartyHood: 500} +townCountMap = {MyEstate: 8, + Tutorial: 40, + ToontownCentral: 37, + DonaldsDock: 40, + MinniesMelodyland: 40, + GoofySpeedway: 40, + TheBrrrgh: 40, + DaisyGardens: 40, + FunnyFarm: 40, + DonaldsDreamland: 40, + OutdoorZone: 40, + PartyHood: 20} +hoodCountMap = {MyEstate: 2, + Tutorial: 2, + ToontownCentral: 2, + DonaldsDock: 2, + MinniesMelodyland: 2, + GoofySpeedway: 2, + TheBrrrgh: 2, + DaisyGardens: 2, + FunnyFarm: 2, + DonaldsDreamland: 2, + OutdoorZone: 2, + BossbotHQ: 2, + SellbotHQ: 43, + CashbotHQ: 2, + LawbotHQ: 2, + GolfZone: 2, + PartyHood: 2} +TrophyStarLevels = (10, + 20, + 30, + 50, + 75, + 100) +TrophyStarColors = (Vec4(0.9, 0.6, 0.2, 1), + Vec4(0.9, 0.6, 0.2, 1), + Vec4(0.8, 0.8, 0.8, 1), + Vec4(0.8, 0.8, 0.8, 1), + Vec4(1, 1, 0, 1), + Vec4(1, 1, 0, 1)) +MickeySpeed = 5.0 +VampireMickeySpeed = 1.15 +MinnieSpeed = 3.2 +WitchMinnieSpeed = 1.8 +DonaldSpeed = 3.68 +FrankenDonaldSpeed = 0.9 +DaisySpeed = 2.3 +GoofySpeed = 5.2 +SuperGoofySpeed = 1.6 +PlutoSpeed = 5.5 +WesternPlutoSpeed = 3.2 +ChipSpeed = 3 +DaleSpeed = 3.5 +DaleOrbitDistance = 3 +SuitWalkSpeed = 4.8 +PieCodeBossCog = 1 +PieCodeNotBossCog = 2 +PieCodeToon = 3 +PieCodeBossInsides = 4 +PieCodeDefensePan = 5 +PieCodeProsecutionPan = 6 +PieCodeLawyer = 7 +PieCodeColors = {PieCodeBossCog: None, + PieCodeNotBossCog: (0.8, + 0.8, + 0.8, + 1), + PieCodeToon: None} +BossCogRollSpeed = 7.5 +BossCogTurnSpeed = 20 +BossCogTreadSpeed = 3.5 +BossCogDizzy = 0 +BossCogElectricFence = 1 +BossCogSwatLeft = 2 +BossCogSwatRight = 3 +BossCogAreaAttack = 4 +BossCogFrontAttack = 5 +BossCogRecoverDizzyAttack = 6 +BossCogDirectedAttack = 7 +BossCogStrafeAttack = 8 +BossCogNoAttack = 9 +BossCogGoonZap = 10 +BossCogSlowDirectedAttack = 11 +BossCogDizzyNow = 12 +BossCogGavelStomp = 13 +BossCogGavelHandle = 14 +BossCogLawyerAttack = 15 +BossCogMoveAttack = 16 +BossCogGolfAttack = 17 +BossCogGolfAreaAttack = 18 +BossCogGearDirectedAttack = 19 +BossCogOvertimeAttack = 20 +BossCogAttackTimes = {BossCogElectricFence: 0, + BossCogSwatLeft: 5.5, + BossCogSwatRight: 5.5, + BossCogAreaAttack: 4.21, + BossCogFrontAttack: 2.65, + BossCogRecoverDizzyAttack: 5.1, + BossCogDirectedAttack: 4.84, + BossCogNoAttack: 6, + BossCogSlowDirectedAttack: 7.84, + BossCogMoveAttack: 3, + BossCogGolfAttack: 6, + BossCogGolfAreaAttack: 7, + BossCogGearDirectedAttack: 4.84, + BossCogOvertimeAttack: 5} +BossCogDamageLevels = {BossCogElectricFence: 1, + BossCogSwatLeft: 5, + BossCogSwatRight: 5, + BossCogAreaAttack: 10, + BossCogFrontAttack: 3, + BossCogRecoverDizzyAttack: 3, + BossCogDirectedAttack: 3, + BossCogStrafeAttack: 2, + BossCogGoonZap: 5, + BossCogSlowDirectedAttack: 10, + BossCogGavelStomp: 20, + BossCogGavelHandle: 2, + BossCogLawyerAttack: 5, + BossCogMoveAttack: 20, + BossCogGolfAttack: 15, + BossCogGolfAreaAttack: 15, + BossCogGearDirectedAttack: 15, + BossCogOvertimeAttack: 10} +BossCogBattleAPosHpr = (0, + -25, + 0, + 0, + 0, + 0) +BossCogBattleBPosHpr = (0, + 25, + 0, + 180, + 0, + 0) +SellbotBossMaxDamage = 100 +SellbotBossMaxDamageNerfed = 100 +SellbotBossBattleOnePosHpr = (0, + -35, + 0, + -90, + 0, + 0) +SellbotBossBattleTwoPosHpr = (0, + 60, + 18, + -90, + 0, + 0) +SellbotBossBattleThreeHpr = (180, 0, 0) +SellbotBossBottomPos = (0, -110, -6.5) +SellbotBossDeathPos = (0, -175, -6.5) +SellbotBossDooberTurnPosA = (-20, -50, 0) +SellbotBossDooberTurnPosB = (20, -50, 0) +SellbotBossDooberTurnPosDown = (0, -50, 0) +SellbotBossDooberFlyPos = (0, -135, -6.5) +SellbotBossTopRampPosA = (-80, -35, 18) +SellbotBossTopRampTurnPosA = (-80, 10, 18) +SellbotBossP3PosA = (-50, 40, 18) +SellbotBossTopRampPosB = (80, -35, 18) +SellbotBossTopRampTurnPosB = (80, 10, 18) +SellbotBossP3PosB = (50, 60, 18) +CashbotBossMaxDamage = 500 +CashbotBossOffstagePosHpr = (120, + -195, + 0, + 0, + 0, + 0) +CashbotBossBattleOnePosHpr = (120, + -230, + 0, + 90, + 0, + 0) +CashbotRTBattleOneStartPosHpr = (94, + -220, + 0, + 110, + 0, + 0) +CashbotBossBattleThreePosHpr = (120, + -315, + 0, + 180, + 0, + 0) +CashbotToonsBattleThreeStartPosHpr = [(105, + -285, + 0, + 208, + 0, + 0), + (136, + -342, + 0, + 398, + 0, + 0), + (105, + -342, + 0, + 333, + 0, + 0), + (135, + -292, + 0, + 146, + 0, + 0), + (93, + -303, + 0, + 242, + 0, + 0), + (144, + -327, + 0, + 64, + 0, + 0), + (145, + -302, + 0, + 117, + 0, + 0), + (93, + -327, + 0, + -65, + 0, + 0)] +CashbotBossSafePosHprs = [(120, + -315, + 30, + 0, + 0, + 0), + (77.2, + -329.3, + 0, + -90, + 0, + 0), + (77.1, + -302.7, + 0, + -90, + 0, + 0), + (165.7, + -326.4, + 0, + 90, + 0, + 0), + (165.5, + -302.4, + 0, + 90, + 0, + 0), + (107.8, + -359.1, + 0, + 0, + 0, + 0), + (133.9, + -359.1, + 0, + 0, + 0, + 0), + (107.0, + -274.7, + 0, + 180, + 0, + 0), + (134.2, + -274.7, + 0, + 180, + 0, + 0)] +CashbotBossCranePosHprs = [(97.4, + -337.6, + 0, + -45, + 0, + 0), + (97.4, + -292.4, + 0, + -135, + 0, + 0), + (142.6, + -292.4, + 0, + 135, + 0, + 0), + (142.6, + -337.6, + 0, + 45, + 0, + 0)] +CashbotBossToMagnetTime = 0.2 +CashbotBossFromMagnetTime = 1 +CashbotBossSafeKnockImpact = 0.5 +CashbotBossSafeNewImpact = 0.0 +CashbotBossGoonImpact = 0.1 +CashbotBossKnockoutDamage = 15 +TTWakeWaterHeight = -4.79 +DDWakeWaterHeight = 1.669 +EstateWakeWaterHeight = -.3 +OZWakeWaterHeight = -0.5 +WakeRunDelta = 0.1 +WakeWalkDelta = 0.2 +NoItems = 0 +NewItems = 1 +OldItems = 2 +SuitInvasionBegin = 0 +SuitInvasionUpdate = 1 +SuitInvasionEnd = 2 +SuitInvasionBulletin = 3 +NO_HOLIDAY = 0 +JULY4_FIREWORKS = 1 +NEWYEARS_FIREWORKS = 2 +HALLOWEEN = 3 +WINTER_DECORATIONS = 4 +SKELECOG_INVASION = 5 +MR_HOLLYWOOD_INVASION = 6 +FISH_BINGO_NIGHT = 7 +ELECTION_PROMOTION = 8 +BLACK_CAT_DAY = 9 +RESISTANCE_EVENT = 10 +KART_RECORD_DAILY_RESET = 11 +KART_RECORD_WEEKLY_RESET = 12 +TRICK_OR_TREAT = 13 +CIRCUIT_RACING = 14 +POLAR_PLACE_EVENT = 15 +CIRCUIT_RACING_EVENT = 16 +TROLLEY_HOLIDAY = 17 +TROLLEY_WEEKEND = 18 +SILLY_SATURDAY_BINGO = 19 +SILLY_SATURDAY_CIRCUIT = 20 +SILLY_SATURDAY_TROLLEY = 21 +ROAMING_TRIALER_WEEKEND = 22 +BOSSCOG_INVASION = 23 +MARCH_INVASION = 24 +MORE_XP_HOLIDAY = 25 +HALLOWEEN_PROPS = 26 +HALLOWEEN_COSTUMES = 27 +DECEMBER_INVASION = 28 +APRIL_FOOLS_COSTUMES = 29 +CRASHED_LEADERBOARD = 30 +OCTOBER31_FIREWORKS = 31 +NOVEMBER19_FIREWORKS = 32 +SELLBOT_SURPRISE_1 = 33 +SELLBOT_SURPRISE_2 = 34 +SELLBOT_SURPRISE_3 = 35 +SELLBOT_SURPRISE_4 = 36 +CASHBOT_CONUNDRUM_1 = 37 +CASHBOT_CONUNDRUM_2 = 38 +CASHBOT_CONUNDRUM_3 = 39 +CASHBOT_CONUNDRUM_4 = 40 +LAWBOT_GAMBIT_1 = 41 +LAWBOT_GAMBIT_2 = 42 +LAWBOT_GAMBIT_3 = 43 +LAWBOT_GAMBIT_4 = 44 +TROUBLE_BOSSBOTS_1 = 45 +TROUBLE_BOSSBOTS_2 = 46 +TROUBLE_BOSSBOTS_3 = 47 +TROUBLE_BOSSBOTS_4 = 48 +JELLYBEAN_DAY = 49 +FEBRUARY14_FIREWORKS = 51 +JULY14_FIREWORKS = 52 +JUNE22_FIREWORKS = 53 +BIGWIG_INVASION = 54 +COLD_CALLER_INVASION = 53 +BEAN_COUNTER_INVASION = 54 +DOUBLE_TALKER_INVASION = 55 +DOWNSIZER_INVASION = 56 +WINTER_CAROLING = 57 +HYDRANT_ZERO_HOLIDAY = 58 +VALENTINES_DAY = 59 +SILLYMETER_HOLIDAY = 60 +MAILBOX_ZERO_HOLIDAY = 61 +TRASHCAN_ZERO_HOLIDAY = 62 +SILLY_SURGE_HOLIDAY = 63 +HYDRANTS_BUFF_BATTLES = 64 +MAILBOXES_BUFF_BATTLES = 65 +TRASHCANS_BUFF_BATTLES = 66 +SILLY_CHATTER_ONE = 67 +SILLY_CHATTER_TWO = 68 +SILLY_CHATTER_THREE = 69 +SILLY_CHATTER_FOUR = 70 +SILLY_TEST = 71 +YES_MAN_INVASION = 72 +TIGHTWAD_INVASION = 73 +TELEMARKETER_INVASION = 74 +HEADHUNTER_INVASION = 75 +SPINDOCTOR_INVASION = 76 +MONEYBAGS_INVASION = 77 +TWOFACES_INVASION = 78 +MINGLER_INVASION = 79 +LOANSHARK_INVASION = 80 +CORPORATE_RAIDER_INVASION = 81 +ROBBER_BARON_INVASION = 82 +LEGAL_EAGLE_INVASION = 83 +BIG_WIG_INVASION = 84 +BIG_CHEESE_INVASION = 85 +DOWN_SIZER_INVASION = 86 +MOVER_AND_SHAKER_INVASION = 87 +DOUBLETALKER_INVASION = 88 +PENNY_PINCHER_INVASION = 89 +NAME_DROPPER_INVASION = 90 +AMBULANCE_CHASER_INVASION = 91 +MICROMANAGER_INVASION = 92 +NUMBER_CRUNCHER_INVASION = 93 +SILLY_CHATTER_FIVE = 94 +VICTORY_PARTY_HOLIDAY = 95 +SELLBOT_NERF_HOLIDAY = 96 +JELLYBEAN_TROLLEY_HOLIDAY = 97 +JELLYBEAN_FISHING_HOLIDAY = 98 +JELLYBEAN_PARTIES_HOLIDAY = 99 +BANK_UPGRADE_HOLIDAY = 100 +TOP_TOONS_MARATHON = 101 +SELLBOT_INVASION = 102 +SELLBOT_FIELD_OFFICE = 103 +SELLBOT_INVASION_MOVER_AND_SHAKER = 104 +IDES_OF_MARCH = 105 +EXPANDED_CLOSETS = 106 +TAX_DAY_INVASION = 107 +KARTING_TICKETS_HOLIDAY = 109 +PRE_JULY_4_DOWNSIZER_INVASION = 110 +PRE_JULY_4_BIGWIG_INVASION = 111 +COMBO_FIREWORKS = 112 +JELLYBEAN_TROLLEY_HOLIDAY_MONTH = 113 +JELLYBEAN_FISHING_HOLIDAY_MONTH = 114 +JELLYBEAN_PARTIES_HOLIDAY_MONTH = 115 +SILLYMETER_EXT_HOLIDAY = 116 +SPOOKY_BLACK_CAT = 117 +SPOOKY_TRICK_OR_TREAT = 118 +SPOOKY_PROPS = 119 +SPOOKY_COSTUMES = 120 +WACKY_WINTER_DECORATIONS = 121 +WACKY_WINTER_CAROLING = 122 +TOT_REWARD_JELLYBEAN_AMOUNT = 100 +TOT_REWARD_END_OFFSET_AMOUNT = 0 +LawbotBossMaxDamage = 2700 +LawbotBossWinningTilt = 40 +LawbotBossInitialDamage = 1350 +LawbotBossBattleOnePosHpr = (-2.798, + -60, + 0, + 0, + 0, + 0) +LawbotBossBattleTwoPosHpr = (-2.798, + 89, + 19.145, + 0, + 0, + 0) +LawbotBossTopRampPosA = (-80, -35, 18) +LawbotBossTopRampTurnPosA = (-80, 10, 18) +LawbotBossP3PosA = (55, -9, 0) +LawbotBossTopRampPosB = (80, -35, 18) +LawbotBossTopRampTurnPosB = (80, 10, 18) +LawbotBossP3PosB = (55, -9, 0) +LawbotBossBattleThreePosHpr = LawbotBossBattleTwoPosHpr +LawbotBossBottomPos = (50, 39, 0) +LawbotBossDeathPos = (50, 40, 0) +LawbotBossGavelPosHprs = [(35, + 78.328, + 0, + -135, + 0, + 0), + (68.5, + 78.328, + 0, + 135, + 0, + 0), + (47, + -33, + 0, + 45, + 0, + 0), + (-50, + -39, + 0, + -45, + 0, + 0), + (-9, + -37, + 0, + 0, + 0, + 0), + (-9, + 49, + 0, + -180, + 0, + 0), + (32, + 0, + 0, + 45, + 0, + 0), + (33, + 56, + 0, + 135, + 0, + 0)] +LawbotBossGavelTimes = [(0.2, 0.9, 0.6), + (0.25, 1, 0.5), + (1.0, 6, 0.5), + (0.3, 3, 1), + (0.26, 0.9, 0.45), + (0.24, 1.1, 0.65), + (0.27, 1.2, 0.45), + (0.25, 0.95, 0.5)] +LawbotBossGavelHeadings = [(0, + -15, + 4, + -70 - 45, + 5, + 45), + (0, + -45, + -4, + -35, + -45, + -16, + 32), + (0, + -8, + 19, + -7, + 5, + 23), + (0, + -4, + 8, + -16, + 32, + -45, + 7, + 7, + -30, + 19, + -13, + 25), + (0, + -45, + -90, + 45, + 90), + (0, + -45, + -90, + 45, + 90), + (0, -45, 45), + (0, -45, 45)] +LawbotBossCogRelBattleAPosHpr = (-25, + -10, + 0, + 0, + 0, + 0) +LawbotBossCogRelBattleBPosHpr = (-25, + 10, + 0, + 0, + 0, + 0) +LawbotBossCogAbsBattleAPosHpr = (-5, + -2, + 0, + 0, + 0, + 0) +LawbotBossCogAbsBattleBPosHpr = (-5, + 0, + 0, + 0, + 0, + 0) +LawbotBossWitnessStandPosHpr = (54, + 100, + 0, + -90, + 0, + 0) +LawbotBossInjusticePosHpr = (-3, + 12, + 0, + 90, + 0, + 0) +LawbotBossInjusticeScale = (1.75, 1.75, 1.5) +LawbotBossDefensePanDamage = 1 +LawbotBossLawyerPosHprs = [(-57, + -24, + 0, + -90, + 0, + 0), + (-57, + -12, + 0, + -90, + 0, + 0), + (-57, + 0, + 0, + -90, + 0, + 0), + (-57, + 12, + 0, + -90, + 0, + 0), + (-57, + 24, + 0, + -90, + 0, + 0), + (-57, + 36, + 0, + -90, + 0, + 0), + (-57, + 48, + 0, + -90, + 0, + 0), + (-57, + 60, + 0, + -90, + 0, + 0), + (-3, + -37.3, + 0, + 0, + 0, + 0), + (-3, + 53, + 0, + -180, + 0, + 0)] +LawbotBossLawyerCycleTime = 6 +LawbotBossLawyerToPanTime = 2.5 +LawbotBossLawyerChanceToAttack = 50 +LawbotBossLawyerHeal = 2 +LawbotBossLawyerStunTime = 5 +LawbotBossDifficultySettings = [(38, + 4, + 8, + 1, + 0, + 0), + (36, + 5, + 8, + 1, + 0, + 0), + (34, + 5, + 8, + 1, + 0, + 0), + (32, + 6, + 8, + 2, + 0, + 0), + (30, + 6, + 8, + 2, + 0, + 0), + (28, + 7, + 8, + 3, + 0, + 0), + (26, + 7, + 9, + 3, + 1, + 1), + (24, + 8, + 9, + 4, + 1, + 1), + (22, + 8, + 10, + 4, + 1, + 0)] +LawbotBossCannonPosHprs = [(-40, + -12, + 0, + -90, + 0, + 0), + (-40, + 0, + 0, + -90, + 0, + 0), + (-40, + 12, + 0, + -90, + 0, + 0), + (-40, + 24, + 0, + -90, + 0, + 0), + (-40, + 36, + 0, + -90, + 0, + 0), + (-40, + 48, + 0, + -90, + 0, + 0), + (-40, + 60, + 0, + -90, + 0, + 0), + (-40, + 72, + 0, + -90, + 0, + 0)] +LawbotBossCannonPosA = (-80, -51.48, 0) +LawbotBossCannonPosB = (-80, 70.73, 0) +LawbotBossChairPosHprs = [(60, + 72, + 0, + -90, + 0, + 0), + (60, + 62, + 0, + -90, + 0, + 0), + (60, + 52, + 0, + -90, + 0, + 0), + (60, + 42, + 0, + -90, + 0, + 0), + (60, + 32, + 0, + -90, + 0, + 0), + (60, + 22, + 0, + -90, + 0, + 0), + (70, + 72, + 5, + -90, + 0, + 0), + (70, + 62, + 5, + -90, + 0, + 0), + (70, + 52, + 5, + -90, + 0, + 0), + (70, + 42, + 5, + -90, + 0, + 0), + (70, + 32, + 5, + -90, + 0, + 0), + (70, + 22, + 5, + -90, + 0, + 0)] +LawbotBossChairRow1PosB = (59.3, 48, 14.05) +LawbotBossChairRow1PosA = (59.3, -18.2, 14.05) +LawbotBossChairRow2PosB = (75.1, 48, 28.2) +LawbotBossChairRow2PosA = (75.1, -18.2, 28.2) +LawbotBossCannonBallMax = 12 +LawbotBossJuryBoxStartPos = (94, -8, 5) +LawbotBossJuryBoxRelativeEndPos = (30, 0, 12.645) +LawbotBossJuryBoxMoveTime = 70 +LawbotBossJurorsForBalancedScale = 8 +LawbotBossDamagePerJuror = 68 +LawbotBossCogJurorFlightTime = 10 +LawbotBossCogJurorDistance = 75 +LawbotBossBaseJurorNpcId = 2001 +LawbotBossWitnessEpiloguePosHpr = (-3, + 0, + 0, + 180, + 0, + 0) +LawbotBossChanceForTaunt = 25 +LawbotBossBonusWaitTime = 60 +LawbotBossBonusDuration = 20 +LawbotBossBonusToonup = 10 +LawbotBossBonusWeightMultiplier = 2 +LawbotBossChanceToDoAreaAttack = 11 +LOW_POP_JP = 0 +MID_POP_JP = 100 +HIGH_POP_JP = 200 +LOW_POP_INTL = 399 +MID_POP_INTL = 499 +HIGH_POP_INTL = -1 +LOW_POP = 399 +MID_POP = 599 +HIGH_POP = -1 +PinballCannonBumper = 0 +PinballCloudBumperLow = 1 +PinballCloudBumperMed = 2 +PinballCloudBumperHigh = 3 +PinballTarget = 4 +PinballRoof = 5 +PinballHouse = 6 +PinballFence = 7 +PinballBridge = 8 +PinballStatuary = 9 +PinballScoring = [(100, 1), + (150, 1), + (200, 1), + (250, 1), + (350, 1), + (100, 1), + (50, 1), + (25, 1), + (100, 1), + (10, 1)] +PinballCannonBumperInitialPos = (0, -20, 40) +RentalCop = 0 +RentalCannon = 1 +RentalGameTable = 2 +GlitchKillerZones = [13300, + 13400, + 13500, + 13600] +ColorPlayer = (0.3, + 0.7, + 0.3, + 1) +ColorAvatar = (0.3, + 0.3, + 0.7, + 1) +ColorPet = (0.6, + 0.4, + 0.2, + 1) +ColorFreeChat = (0.3, + 0.3, + 0.8, + 1) +ColorSpeedChat = (0.2, + 0.6, + 0.4, + 1) +ColorNoChat = (0.8, + 0.5, + 0.1, + 1) +FactoryLaffMinimums = [(0, 31), + (0, 66, 71), + (0, + 81, + 86, + 96), + (0, 101, 106)] +PICNIC_COUNTDOWN_TIME = 60 +BossbotRTIntroStartPosHpr = (0, + -64, + 0, + 180, + 0, + 0) +BossbotRTPreTwoPosHpr = (0, + -20, + 0, + 180, + 0, + 0) +BossbotRTEpiloguePosHpr = (0, + 90, + 0, + 180, + 0, + 0) +BossbotBossBattleOnePosHpr = (0, + 355, + 0, + 0, + 0, + 0) +BossbotBossPreTwoPosHpr = (0, + 20, + 0, + 0, + 0, + 0) +BossbotElevCamPosHpr = (0, + -100.544, + 7.18258, + 0, + 0, + 0) +BossbotFoodModelScale = 0.75 +BossbotNumFoodToExplode = 3 +BossbotBossServingDuration = 300 +BossbotPrepareBattleThreeDuration = 20 +WaiterBattleAPosHpr = (20, + -400, + 0, + 0, + 0, + 0) +WaiterBattleBPosHpr = (-20, + -400, + 0, + 0, + 0, + 0) +BossbotBossBattleThreePosHpr = (0, + 355, + 0, + 0, + 0, + 0) +DinerBattleAPosHpr = (20, + -240, + 0, + 0, + 0, + 0) +DinerBattleBPosHpr = (-20, + -240, + 0, + 0, + 0, + 0) +BossbotBossMaxDamage = 500 +BossbotMaxSpeedDamage = 90 +BossbotSpeedRecoverRate = 20 +BossbotBossDifficultySettings = [(8, + 4, + 11, + 3, + 30, + 25), + (9, + 5, + 12, + 6, + 28, + 26), + (10, + 6, + 11, + 7, + 26, + 27), + (8, + 8, + 12, + 8, + 24, + 28), + (13, + 5, + 12, + 9, + 22, + 29)] +BossbotRollSpeedMax = 22 +BossbotRollSpeedMin = 7.5 +BossbotTurnSpeedMax = 60 +BossbotTurnSpeedMin = 20 +BossbotTreadSpeedMax = 10.5 +BossbotTreadSpeedMin = 3.5 +CalendarFilterShowAll = 0 +CalendarFilterShowOnlyHolidays = 1 +CalendarFilterShowOnlyParties = 2 +TTC = 1 +DD = 2 +MM = 3 +GS = 4 +DG = 5 +BR = 6 +OZ = 7 +DL = 8 +DefaultWantNewsPageSetting = 1 +gmMagicWordList = ['restock', + 'restockUber', + 'autoRestock', + 'resistanceRestock', + 'restockSummons', + 'uberDrop', + 'rich', + 'maxBankMoney', + 'toonUp', + 'rod', + 'cogPageFull', + 'pinkSlips', + 'Tickets', + 'newSummons', + 'who', + 'who all'] +NewsPageScaleAdjust = 0.85 +AnimPropTypes = Enum(('Unknown', + 'Hydrant', + 'Mailbox', + 'Trashcan'), start=-1) +EmblemTypes = Enum(('Silver', 'Gold')) +NumEmblemTypes = 2 +DefaultMaxBankMoney = 12000 +DefaultBankItemId = 1350 +ToonAnimStates = set(['off', + 'neutral', + 'victory', + 'Happy', + 'Sad', + 'Catching', + 'CatchEating', + 'Sleep', + 'walk', + 'jumpSquat', + 'jump', + 'jumpAirborne', + 'jumpLand', + 'run', + 'swim', + 'swimhold', + 'dive', + 'cringe', + 'OpenBook', + 'ReadBook', + 'CloseBook', + 'TeleportOut', + 'Died', + 'TeleportedOut', + 'TeleportIn', + 'Emote', + 'SitStart', + 'Sit', + 'Push', + 'Squish', + 'FallDown', + 'GolfPuttLoop', + 'GolfRotateLeft', + 'GolfRotateRight', + 'GolfPuttSwing', + 'GolfGoodPutt', + 'GolfBadPutt', + 'Flattened', + 'CogThiefRunning', + 'ScientistJealous', + 'ScientistEmcee', + 'ScientistWork', + 'ScientistLessWork', + 'ScientistPlay']) +AV_FLAG_REASON_TOUCH = 1 +AV_FLAG_HISTORY_LEN = 500 +AV_TOUCH_CHECK_DELAY_AI = 3.0 +AV_TOUCH_CHECK_DELAY_CL = 1.0 +AV_TOUCH_CHECK_DIST = 2.0 +AV_TOUCH_CHECK_DIST_Z = 5.0 +AV_TOUCH_CHECK_TIMELIMIT_CL = 0.002 +AV_TOUCH_COUNT_LIMIT = 5 +AV_TOUCH_COUNT_TIME = 300 diff --git a/toontown/toonbase/ToontownIntervals.py b/toontown/toonbase/ToontownIntervals.py new file mode 100644 index 0000000..a903209 --- /dev/null +++ b/toontown/toonbase/ToontownIntervals.py @@ -0,0 +1,37 @@ +from direct.interval.MetaInterval import Sequence +from direct.interval.FunctionInterval import Wait, Func +PULSE_GUI_DURATION = 0.2 +PULSE_GUI_CHANGE = 0.333 + +def cleanup(name): + taskMgr.remove(name) + + +def start(ival): + cleanup(ival.getName()) + ival.start() + return ival + + +def loop(ival): + cleanup(ival.getName()) + ival.loop() + return ival + + +def getPulseLargerIval(np, name, duration = PULSE_GUI_DURATION, scale = 1): + return getPulseIval(np, name, 1 + PULSE_GUI_CHANGE, duration=duration, scale=scale) + + +def getPulseSmallerIval(np, name, duration = PULSE_GUI_DURATION, scale = 1): + return getPulseIval(np, name, 1 - PULSE_GUI_CHANGE, duration=duration, scale=scale) + + +def getPulseIval(np, name, change, duration = PULSE_GUI_CHANGE, scale = 1): + return Sequence(np.scaleInterval(duration, scale * change, blendType='easeOut'), np.scaleInterval(duration, scale, blendType='easeIn'), name=name, autoFinish=1) + + +def getPresentGuiIval(np, name, waitDuration = 0.5, moveDuration = 1.0, parent = aspect2d, startPos = (0, 0, 0)): + endPos = np.getPos() + np.setPos(parent, startPos[0], startPos[1], startPos[2]) + return Sequence(Func(np.show), getPulseLargerIval(np, '', scale=np.getScale()), Wait(waitDuration), np.posInterval(moveDuration, endPos, blendType='easeInOut'), name=name, autoFinish=1) diff --git a/toontown/toonbase/ToontownLoader.py b/toontown/toonbase/ToontownLoader.py new file mode 100644 index 0000000..b2970f7 --- /dev/null +++ b/toontown/toonbase/ToontownLoader.py @@ -0,0 +1,102 @@ +from pandac.PandaModules import * +from direct.directnotify.DirectNotifyGlobal import * +from direct.showbase import Loader +from toontown.toontowngui import ToontownLoadingScreen + +class ToontownLoader(Loader.Loader): + TickPeriod = 0.2 + + def __init__(self, base): + Loader.Loader.__init__(self, base) + self.inBulkBlock = None + self.blockName = None + self.loadingScreen = ToontownLoadingScreen.ToontownLoadingScreen() + return + + def destroy(self): + self.loadingScreen.destroy() + del self.loadingScreen + Loader.Loader.destroy(self) + + def beginBulkLoad(self, name, label, range, gui, tipCategory): + self._loadStartT = globalClock.getRealTime() + Loader.Loader.notify.info("starting bulk load of block '%s'" % name) + if self.inBulkBlock: + Loader.Loader.notify.warning("Tried to start a block ('%s'), but am already in a block ('%s')" % (name, self.blockName)) + return None + self.inBulkBlock = 1 + self._lastTickT = globalClock.getRealTime() + self.blockName = name + self.loadingScreen.begin(range, label, gui, tipCategory) + return None + + def endBulkLoad(self, name): + if not self.inBulkBlock: + Loader.Loader.notify.warning("Tried to end a block ('%s'), but not in one" % name) + return None + if name != self.blockName: + Loader.Loader.notify.warning("Tried to end a block ('%s'), other then the current one ('%s')" % (name, self.blockName)) + return None + self.inBulkBlock = None + expectedCount, loadedCount = self.loadingScreen.end() + now = globalClock.getRealTime() + Loader.Loader.notify.info("At end of block '%s', expected %s, loaded %s, duration=%s" % (self.blockName, + expectedCount, + loadedCount, + now - self._loadStartT)) + return + + def abortBulkLoad(self): + if self.inBulkBlock: + Loader.Loader.notify.info("Aborting block ('%s')" % self.blockName) + self.inBulkBlock = None + self.loadingScreen.abort() + return + + def tick(self): + if self.inBulkBlock: + now = globalClock.getRealTime() + if now - self._lastTickT > self.TickPeriod: + self._lastTickT += self.TickPeriod + self.loadingScreen.tick() + try: + base.cr.considerHeartbeat() + except: + pass + + def loadModel(self, *args, **kw): + ret = Loader.Loader.loadModel(self, *args, **kw) + self.tick() + return ret + + def loadFont(self, *args, **kw): + ret = Loader.Loader.loadFont(self, *args, **kw) + self.tick() + return ret + + def loadTexture(self, texturePath, alphaPath = None, okMissing = False): + ret = Loader.Loader.loadTexture(self, texturePath, alphaPath, okMissing=okMissing) + self.tick() + if alphaPath: + self.tick() + return ret + + def loadSfx(self, soundPath): + ret = Loader.Loader.loadSfx(self, soundPath) + self.tick() + return ret + + def loadMusic(self, soundPath): + ret = Loader.Loader.loadMusic(self, soundPath) + self.tick() + return ret + + def loadDNAFileAI(self, dnaStore, dnaFile): + ret = loadDNAFileAI(dnaStore, dnaFile, CSDefault) + self.tick() + return ret + + def loadDNAFile(self, dnaStore, dnaFile): + ret = loadDNAFile(dnaStore, dnaFile, CSDefault, 0) + self.tick() + return ret diff --git a/toontown/toonbase/ToontownStart.py b/toontown/toonbase/ToontownStart.py new file mode 100644 index 0000000..516198c --- /dev/null +++ b/toontown/toonbase/ToontownStart.py @@ -0,0 +1,114 @@ +import __builtin__ + +class game: + name = 'toontown' + process = 'client' + + +__builtin__.game = game() +import time +import os +import sys +import random +import __builtin__ +try: + launcher +except: + from toontown.launcher.ToontownDummyLauncher import ToontownDummyLauncher + launcher = ToontownDummyLauncher() + __builtin__.launcher = launcher + +launcher.setRegistry('EXIT_PAGE', 'normal') +pollingDelay = 0.5 +print 'ToontownStart: Polling for game2 to finish...' +while not launcher.getGame2Done(): + time.sleep(pollingDelay) + +print 'ToontownStart: Game2 is finished.' +print 'ToontownStart: Starting the game.' +from pandac.PandaModules import * +if launcher.isDummy(): + http = HTTPClient() +else: + http = launcher.http +tempLoader = PandaLoader() +backgroundNode = tempLoader.loadSync(Filename('phase_3/models/gui/loading-background')) +from direct.gui import DirectGuiGlobals +print 'ToontownStart: setting default font' +import ToontownGlobals +DirectGuiGlobals.setDefaultFontFunc(ToontownGlobals.getInterfaceFont) +launcher.setPandaErrorCode(7) +import ToonBase +ToonBase.ToonBase() +from pandac.PandaModules import * +if base.win == None: + print 'Unable to open window; aborting.' + sys.exit() +launcher.setPandaErrorCode(0) +launcher.setPandaWindowOpen() +ConfigVariableDouble('decompressor-step-time').setValue(0.01) +ConfigVariableDouble('extractor-step-time').setValue(0.01) +backgroundNodePath = aspect2d.attachNewNode(backgroundNode, 0) +backgroundNodePath.setPos(0.0, 0.0, 0.0) +backgroundNodePath.setScale(render2d, VBase3(1)) +backgroundNodePath.find('**/fg').setBin('fixed', 20) +backgroundNodePath.find('**/bg').setBin('fixed', 10) +base.graphicsEngine.renderFrame() +DirectGuiGlobals.setDefaultRolloverSound(base.loadSfx('phase_3/audio/sfx/GUI_rollover.mp3')) +DirectGuiGlobals.setDefaultClickSound(base.loadSfx('phase_3/audio/sfx/GUI_create_toon_fwd.mp3')) +DirectGuiGlobals.setDefaultDialogGeom(loader.loadModel('phase_3/models/gui/dialog_box_gui')) +import TTLocalizer +from otp.otpbase import OTPGlobals +OTPGlobals.setDefaultProductPrefix(TTLocalizer.ProductPrefix) +if base.musicManagerIsValid: + music = base.musicManager.getSound('phase_3/audio/bgm/tt_theme.mid') + if music: + music.setLoop(1) + music.setVolume(0.9) + music.play() + print 'ToontownStart: Loading default gui sounds' + DirectGuiGlobals.setDefaultRolloverSound(base.loadSfx('phase_3/audio/sfx/GUI_rollover.mp3')) + DirectGuiGlobals.setDefaultClickSound(base.loadSfx('phase_3/audio/sfx/GUI_create_toon_fwd.mp3')) +else: + music = None +import ToontownLoader +from direct.gui.DirectGui import * +serverVersion = base.config.GetString('server-version', 'no_version_set') +print 'ToontownStart: serverVersion: ', serverVersion +version = OnscreenText(serverVersion, pos=(-1.3, -0.975), scale=0.06, fg=Vec4(0, 0, 1, 0.6), align=TextNode.ALeft) +loader.beginBulkLoad('init', TTLocalizer.LoaderLabel, 138, 0, TTLocalizer.TIP_NONE) +from ToonBaseGlobal import * +from direct.showbase.MessengerGlobal import * +from toontown.distributed import ToontownClientRepository +cr = ToontownClientRepository.ToontownClientRepository(serverVersion, launcher) +cr.music = music +del music +base.initNametagGlobals() +base.cr = cr +loader.endBulkLoad('init') +from otp.friends import FriendManager +from otp.distributed.OtpDoGlobals import * +cr.generateGlobalObject(OTP_DO_ID_FRIEND_MANAGER, 'FriendManager') +if not launcher.isDummy(): + base.startShow(cr, launcher.getGameServer()) +else: + base.startShow(cr) +backgroundNodePath.reparentTo(hidden) +backgroundNodePath.removeNode() +del backgroundNodePath +del backgroundNode +del tempLoader +version.cleanup() +del version +base.loader = base.loader +__builtin__.loader = base.loader +autoRun = ConfigVariableBool('toontown-auto-run', 1) +if autoRun and launcher.isDummy() and (not Thread.isTrueThreads() or __name__ == '__main__'): + try: + run() + except SystemExit: + raise + except: + from direct.showbase import PythonUtil + print PythonUtil.describeException() + raise diff --git a/toontown/toonbase/ToontownTimer.py b/toontown/toonbase/ToontownTimer.py new file mode 100644 index 0000000..28e2e0f --- /dev/null +++ b/toontown/toonbase/ToontownTimer.py @@ -0,0 +1,15 @@ +from otp.otpbase.OTPTimer import OTPTimer +from pandac.PandaModules import * + +class ToontownTimer(OTPTimer): + + def __init__(self, useImage = True, highlightNearEnd = True): + OTPTimer.__init__(self, useImage, highlightNearEnd) + self.initialiseoptions(ToontownTimer) + + def getImage(self): + if ToontownTimer.ClockImage == None: + model = loader.loadModel('phase_3.5/models/gui/clock_gui') + ToontownTimer.ClockImage = model.find('**/alarm_clock') + model.removeNode() + return ToontownTimer.ClockImage diff --git a/toontown/toonbase/UserFunnel.py b/toontown/toonbase/UserFunnel.py new file mode 100644 index 0000000..d930029 --- /dev/null +++ b/toontown/toonbase/UserFunnel.py @@ -0,0 +1,707 @@ +import os, sys, socket, random +from urllib import quote_plus +from pandac.PandaModules import HTTPClient +from pandac.PandaModules import HTTPCookie +from pandac.PandaModules import URLSpec +from pandac.PandaModules import Ramfile +from pandac.PandaModules import Ostream +from pandac.PandaModules import HTTPDate +from pandac.PandaModules import DocumentSpec +from direct.task.Task import Task +from direct.directnotify.DirectNotifyGlobal import directNotify +notify = directNotify.newCategory('UserFunnel') + +class UserFunnel: + + def __init__(self): + self.hitboxAcct = 'DM53030620EW' + self.language = 'en-us' + self.cgRoot = 'ToonTown_Online' + self.cgBeta = 'Beta' + self.cgRelease = 'Release' + self.cgLocation = 'US' + self.campaignID = '' + self.cfCookieFile = 'cf.txt' + self.dynamicVRFunnel = 'http://download.toontown.com/' + self.hostDict = {0: 'Internal Disney PHP Collector Site', + 1: 'ehg-dig.hitbox.com/HG?', + 2: 'ehg-dig.hitbox.com/HG?', + 3: 'build64.online.disney.com:5020/index.php?'} + self.CurrentHost = '' + self.URLtoSend = '' + self.gameName = 'ToonTown' + self.browserName = 'Panda3D%20(' + self.gameName + ';%20' + sys.platform + ')' + self.HTTPUserHeader = [('User-agent', 'Panda3D')] + self.osMajorver = '' + self.osMinorver = '' + self.osRevver = '' + self.osBuild = '' + self.osType = '' + self.osComments = '' + self.msWinTypeDict = {0: 'Win32s on Windows 3.1', + 1: 'Windows 95/98/ME', + 2: 'Windows NT/2000/XP', + 3: 'Windows CE'} + self.milestoneDict = {0: 'New User', + 1: 'Create Account', + 2: 'View EULA', + 3: 'Accept EULA', + 4: 'Download Start', + 5: 'Download End', + 6: 'Installer Run', + 7: 'Launcher Start', + 8: 'Launcher Login', + 9: 'Client Opens', + 10: 'Create Pirate Loads', + 11: 'Create Pirate Exit', + 12: 'Cutscene One Start', + 13: 'Cutscene One Ends', + 14: 'Cutscene Two Start', + 15: 'Cutscene Thee Start', + 16: 'Cutscene Three Ends', + 17: 'Access Cannon', + 18: 'Cutscene Four Starts', + 19: 'Cutscene Four Ends', + 20: 'Dock - Start Game'} + self.macTypeDict = {2: 'Jaguar', + 1: 'Puma', + 3: 'Panther', + 4: 'Tiger', + 5: 'Lepard'} + self.milestone = '' + self.pandaHTTPClientVarWSS = [] + self.pandaHTTPClientVarCTG = [] + self.pandaHTTPClientVarDM = [] + self.checkForCFfile() + self.httpSession = HTTPClient() + self.whatOSver() + + def checkForCFfile(self): + if firstRun() == True: + pass + elif os.path.isfile(self.cfCookieFile) == False: + firstRun('write', True) + + def whatOSver(self): + if sys.platform == 'win32': + self.osMajorver = str(sys.getwindowsversion()[0]) + self.osMinorver = str(sys.getwindowsversion()[1]) + self.osBuild = str(sys.getwindowsversion()[2]) + self.osType = str(sys.getwindowsversion()[3]) + self.osComments = str(sys.getwindowsversion()[4]) + return + if sys.platform == 'darwin': + self.osMajorver = '10' + osxcmd = '/usr/sbin/system_profiler SPSoftwareDataType |/usr/bin/grep "System Version"' + infopipe = os.popen(osxcmd, 'r') + parseLine = infopipe.read() + infopipe.close() + del infopipe + notify.info('parseLine = %s' % str(parseLine)) + versionStringStart = parseLine.find('10.') + notify.info('versionStringStart = %s' % str(versionStringStart)) + testPlist = False + try: + self.osMinorver = parseLine[versionStringStart + 3] + self.osRevver = parseLine[versionStringStart + 5:versionStringStart + 7].strip(' ') + self.osBuild = parseLine[int(parseLine.find('(')) + 1:parseLine.find(')')] + except: + notify.info("couldn't parse the system_profiler output, using zeros") + self.osMinorver = '0' + self.osRevver = '0' + self.osBuild = '0000' + testPlist = True + + del versionStringStart + del parseLine + del osxcmd + if testPlist: + try: + import plistlib + pl = plistlib.readPlist('/System/Library/CoreServices/SystemVersion.plist') + notify.info('pl=%s' % str(pl)) + parseLine = pl['ProductVersion'] + numbers = parseLine.split('.') + notify.info('parseline =%s numbers =%s' % (parseLine, numbers)) + self.osMinorver = numbers[1] + self.osRevver = numbers[2] + self.osBuild = pl['ProductBuildVersion'] + except: + notify.info('tried plist but still got exception') + self.osMinorver = '0' + self.osRevver = '0' + self.osBuild = '0000' + + return + + def setmilestone(self, ms): + if firstRun() == False: + self.milestone = ms + else: + self.milestone = '%s_INITIAL' % ms + + def setgamename(self, gamename): + self.gameName = gamename + + def printosComments(self): + return self.osComments + + def setHost(self, hostID): + self.CurrentHost = hostID + + def getFunnelURL(self): + if patcherVer() == ['OFFLINE']: + return + if patcherVer() == []: + patcherHTTP = HTTPClient() + if checkParamFile() == None: + patcherDoc = patcherHTTP.getDocument(URLSpec('http://download.toontown.com/english/currentVersion/content/patcher.ver')) + vconGroup('w', self.cgRelease) + else: + patcherDoc = patcherHTTP.getDocument(URLSpec(checkParamFile())) + vconGroup('w', self.cgBeta) + rf = Ramfile() + patcherDoc.downloadToRam(rf) + self.patcherURL = rf.getData() + if self.patcherURL.find('FUNNEL_LOG') == -1: + patcherVer('w', 'OFFLINE') + return + self.patcherURL = self.patcherURL.split('\n') + del rf + del patcherDoc + del patcherHTTP + while self.patcherURL: + self.confLine = self.patcherURL.pop() + if self.confLine.find('FUNNEL_LOG=') != -1 and self.confLine.find('#FUNNEL_LOG=') == -1: + self.dynamicVRFunnel = self.confLine[11:].strip('\n') + patcherVer('w', self.confLine[11:].strip('\n')) + + else: + self.dynamicVRFunnel = patcherVer()[0] + return + + def isVarSet(self, varInQuestion): + try: + tempvar = type(varInQuestion) + return 1 + except NameError: + return 0 + + def buildURL(self): + if sys.platform == 'win32': + hitboxOSType = 'c3' + else: + hitboxOSType = 'c4' + if self.CurrentHost == 1: + self.URLtoSend = 'http://' + self.hostDict[self.CurrentHost] + 'hb=' + str(self.hitboxAcct) + '&n=' + str(self.milestone) + '&ln=' + self.language + '&gp=STARTGAME&fnl=TOONTOWN_FUNNEL&vcon=/' + self.cgRoot + '/' + self.cgLocation + '/' + str(vconGroup()) + '&c1=' + str(sys.platform) + '&' + str(hitboxOSType) + '=' + str(self.osMajorver) + '_' + str(self.osMinorver) + '_' + str(self.osRevver) + '_' + str(self.osBuild) + if self.CurrentHost == 2: + self.URLtoSend = 'http://' + self.hostDict[self.CurrentHost] + 'hb=' + str(self.hitboxAcct) + '&n=' + str(self.milestone) + '&ln=' + self.language + '&vcon=/' + self.cgRoot + '/' + self.cgLocation + '/' + str(vconGroup()) + '&c1=' + str(sys.platform) + '&' + str(hitboxOSType) + '=' + str(self.osMajorver) + '_' + str(self.osMinorver) + '_' + str(self.osRevver) + '_' + str(self.osBuild) + if self.CurrentHost == 0: + localMAC = str(getMAC()) + self.URLtoSend = str(self.dynamicVRFunnel) + '?funnel=' + str(self.milestone) + '&platform=' + str(sys.platform) + '&sysver=' + str(self.osMajorver) + '_' + str(self.osMinorver) + '_' + str(self.osRevver) + '_' + str(self.osBuild) + '&mac=' + localMAC + '&username=' + str(loggingSubID()) + '&id=' + str(loggingAvID()) + + def readInPandaCookie(self): + thefile = open(self.cfCookieFile, 'r') + thedata = thefile.read().split('\n') + thefile.close() + del thefile + if thedata[0].find('Netscape HTTP Cookie File') != -1: + return + thedata.pop() + try: + while thedata: + temp = thedata.pop() + temp = temp.split('\t') + domain = temp[0] + loc = temp[1] + variable = temp[2] + value = temp[3] + if variable == 'CTG': + self.pandaHTTPClientVarCTG = [domain, + loc, + variable, + value] + self.setTheHTTPCookie(self.pandaHTTPClientVarCTG) + if variable == self.hitboxAcct + 'V6': + self.pandaHTTPClientVarDM = [domain, + loc, + variable, + value] + self.setTheHTTPCookie(self.pandaHTTPClientVarDM) + if variable == 'WSS_GW': + self.pandaHTTPClientVarWSS = [domain, + loc, + variable, + value] + self.setTheHTTPCookie(self.pandaHTTPClientVarWSS) + + except IndexError: + print 'UserFunnel(Warning): Cookie Data file bad' + + del thedata + + def updateInstanceCookieValues(self): + a = self.httpSession.getCookie(HTTPCookie('WSS_GW', '/', '.hitbox.com')) + if a.getName(): + self.pandaHTTPClientVarWSS = ['.hitbox.com', + '/', + 'WSS_GW', + a.getValue()] + b = self.httpSession.getCookie(HTTPCookie('CTG', '/', '.hitbox.com')) + if b.getName(): + self.pandaHTTPClientVarCTG = ['.hitbox.com', + '/', + 'CTG', + b.getValue()] + c = self.httpSession.getCookie(HTTPCookie(self.hitboxAcct + 'V6', '/', 'ehg-dig.hitbox.com')) + if c.getName(): + self.pandaHTTPClientVarDM = ['ehg-dig.hitbox.com', + '/', + self.hitboxAcct + 'V6', + c.getValue()] + del a + del b + del c + + def setTheHTTPCookie(self, cookieParams): + c = HTTPCookie(cookieParams[2], cookieParams[1], cookieParams[0]) + c.setValue(cookieParams[3]) + self.httpSession.setCookie(c) + + def writeOutPandaCookie(self): + try: + thefile = open(self.cfCookieFile, 'w') + if len(self.pandaHTTPClientVarWSS) == 4: + thefile.write(self.pandaHTTPClientVarWSS[0] + '\t' + self.pandaHTTPClientVarWSS[1] + '\t' + self.pandaHTTPClientVarWSS[2] + '\t' + self.pandaHTTPClientVarWSS[3] + '\n') + if len(self.pandaHTTPClientVarCTG) == 4: + thefile.write(self.pandaHTTPClientVarCTG[0] + '\t' + self.pandaHTTPClientVarCTG[1] + '\t' + self.pandaHTTPClientVarCTG[2] + '\t' + self.pandaHTTPClientVarCTG[3] + '\n') + if len(self.pandaHTTPClientVarDM) == 4: + thefile.write(self.pandaHTTPClientVarDM[0] + '\t' + self.pandaHTTPClientVarDM[1] + '\t' + self.pandaHTTPClientVarDM[2] + '\t' + self.pandaHTTPClientVarDM[3] + '\n') + thefile.close() + except IOError: + return + + def prerun(self): + self.getFunnelURL() + self.buildURL() + if os.path.isfile(self.cfCookieFile) == True: + if self.CurrentHost == 1 or self.CurrentHost == 2: + self.readInPandaCookie() + + def run(self): + if self.CurrentHost == 0 and patcherVer() == ['OFFLINE']: + return + self.nonBlock = self.httpSession.makeChannel(False) + self.nonBlock.beginGetDocument(DocumentSpec(self.URLtoSend)) + instanceMarker = str(random.randint(1, 1000)) + instanceMarker = 'FunnelLoggingRequest-%s' % instanceMarker + self.startCheckingAsyncRequest(instanceMarker) + + def startCheckingAsyncRequest(self, name): + taskMgr.remove(name) + taskMgr.doMethodLater(0.5, self.pollFunnelTask, name) + + def stopCheckingFunnelTask(self, name): + taskMgr.remove('pollFunnelTask') + + def pollFunnelTask(self, task): + result = self.nonBlock.run() + if result == 0: + self.stopCheckingFunnelTask(task) + if self.CurrentHost == 1 or self.CurrentHost == 2: + self.updateInstanceCookieValues() + self.writeOutPandaCookie() + else: + return Task.again + + +def logSubmit(setHostID, setMileStone): + if __dev__: + return + trackItem = UserFunnel() + trackItem.setmilestone(quote_plus(setMileStone)) + trackItem.setHost(setHostID) + trackItem.prerun() + trackItem.run() + + +def getVRSFunnelURL(): + a = UserFunnel() + a.getFunnelURL() + + +class HitBoxCookie: + + def __init__(self): + self.ieCookieDir = os.getenv('USERPROFILE') + '\\Cookies' + self.pythonCookieFile = 'cf.txt' + self.hitboxCookieFile = None + self.ehgdigCookieFile = None + self.hitboxAcct = 'DM53030620EW' + self.ctg = None + self.wss_gw = None + self.dmAcct = None + self.pythonCookieHeader = ' # Netscape HTTP Cookie File\n # http://www.netscape.com/newsref/std/cookie_spec.html\n # This is a generated file! Do not edit.\n\n' + return + + def returnIECookieDir(self): + return self.ieCookieDir + + def findIECookieFiles(self): + try: + sdir = os.listdir(self.ieCookieDir) + except WindowsError: + print 'Dir does not exist, do nothing' + return + + while sdir: + temp = sdir.pop() + if temp.find('@hitbox[') != -1: + self.hitboxCookieFile = temp + if temp.find('@ehg-dig.hitbox[') != -1: + self.ehgdigCookieFile = temp + + if self.hitboxCookieFile != None and self.ehgdigCookieFile != None: + return 1 + if self.hitboxCookieFile == None and self.ehgdigCookieFile == None: + return 0 + else: + return -1 + return + + def openHitboxFile(self, filename, type = 'python'): + if type == 'ie': + fullfile = self.ieCookieDir + '\\' + filename + else: + fullfile = filename + cf = open(fullfile, 'r') + data = cf.read() + cf.close() + return data + + def splitIECookie(self, filestream): + return filestream.split('*\n') + + def sortIECookie(self, filestreamListElement): + return [filestreamListElement.split('\n')[2], filestreamListElement.split('\n')[0], filestreamListElement.split('\n')[1]] + + def sortPythonCookie(self, filestreamListElement): + return [filestreamListElement.split('\t')[0], filestreamListElement.split('\t')[5], filestreamListElement.split('\t')[6]] + + def writeIEHitBoxCookies(self): + if self.ctg == None or self.wss_gw == None or self.dmAcct == None: + return + if sys.platform != 'win32': + return + self.findIECookieFiles() + iecData = self.openHitboxFile(self.ehgdigCookieFile, 'ie') + iecData = iecData.split('*\n') + x = 0 + while x < len(iecData): + if iecData[x].find(self.hitboxAcct) != -1: + iecData.pop(x) + print 'Removed it from the list' + break + x += 1 + + iecWrite = open(self.ieCookieDir + '\\' + self.ehgdigCookieFile, 'w') + while iecData: + iecBuffer = iecData.pop() + '*\n' + iecBuffer = iecBuffer.strip('/') + if iecBuffer[0] == '.': + iecBuffer = iecBuffer[1:] + iecWrite.write(iecBuffer) + + tempDMBUFFER = self.dmAcct[0] + if tempDMBUFFER[0].find('.') == 0: + tempDMBUFFER = tempDMBUFFER[1:] + iecWrite.write(self.dmAcct[1] + '\n' + self.dmAcct[2] + '\n' + tempDMBUFFER + '/\n*\n') + iecWrite.close() + del iecData + del iecWrite + del iecBuffer + iecWrite = open(self.ieCookieDir + '\\' + self.hitboxCookieFile, 'w') + iecBuffer = self.ctg[0] + if iecBuffer[0] == '.': + iecBuffer = iecBuffer[1:] + if iecBuffer.find('/') == -1: + iecBuffer = iecBuffer + '/' + iecWrite.write(self.ctg[1] + '\n' + self.ctg[2] + '\n' + iecBuffer + '\n*\n') + iecWrite.write(self.wss_gw[1] + '\n' + self.wss_gw[2] + '\n' + iecBuffer + '\n*\n') + iecWrite.close() + return + + def OLDwritePythonHitBoxCookies(self, filename = 'cf.txt'): + if self.ctg == None or self.wss_gw == None or self.dmAcct == None: + return + outputfile = open(filename, 'w') + outputfile.write(self.pythonCookieHeader) + outputfile.write('.' + self.dmAcct[0].strip('/') + '\tTRUE\t/\tFALSE\t9999999999\t' + self.dmAcct[1] + '\t' + self.dmAcct[2] + '\n') + outputfile.write('.' + self.ctg[0].strip('/') + '\tTRUE\t/\tFALSE\t9999999999\t' + self.ctg[1] + '\t' + self.ctg[2] + '\n') + outputfile.write('.' + self.wss_gw[0].strip('/') + '\tTRUE\t/\tFALSE\t9999999999\t' + self.wss_gw[1] + '\t' + self.wss_gw[2] + '\n') + outputfile.close() + return + + def writePythonHitBoxCookies(self, filename = 'cf.txt'): + if self.ctg == None or self.wss_gw == None or self.dmAcct == None: + return + outputfile = open(filename, 'w') + outputfile.write('.' + self.dmAcct[0].strip('/') + '\t/\t' + self.dmAcct[1] + '\t' + self.dmAcct[2] + '\n') + outputfile.write('.' + self.ctg[0].strip('/') + '\t/\t' + self.ctg[1] + '\t' + self.ctg[2] + '\n') + outputfile.write('.' + self.wss_gw[0].strip('/') + '\t/\t' + self.wss_gw[1] + '\t' + self.wss_gw[2] + '\n') + outputfile.close() + return + + def loadPythonHitBoxCookies(self): + if os.path.isfile(self.pythonCookieFile) != 1: + return + pythonStandard = self.openHitboxFile(self.pythonCookieFile, 'python') + pythonStandard = pythonStandard.split('\n\n').pop(1) + pythonStandard = pythonStandard.split('\n') + for x in pythonStandard: + if x.find('\t' + self.hitboxAcct) != -1: + self.dmAcct = self.sortPythonCookie(x) + if x.find('\tCTG\t') != -1: + self.ctg = self.sortPythonCookie(x) + if x.find('\tWSS_GW\t') != -1: + self.wss_gw = self.sortPythonCookie(x) + + def loadIEHitBoxCookies(self): + if self.findIECookieFiles() != 1: + return + if sys.platform != 'win32': + return + hitboxStandard = self.openHitboxFile(self.hitboxCookieFile, 'ie') + hitboxDIG = self.openHitboxFile(self.ehgdigCookieFile, 'ie') + hitboxStandard = self.splitIECookie(hitboxStandard) + hitboxDIG = self.splitIECookie(hitboxDIG) + ctg = None + wss = None + for x in hitboxStandard: + if x.find('CTG\n') != -1: + ctg = x + if x.find('WSS_GW\n') != -1: + wss = x + + if ctg == None or wss == None: + return + DM = None + for x in hitboxDIG: + if x.find(self.hitboxAcct) != -1: + DM = x + + if DM == None: + return + self.ctg = self.sortIECookie(ctg) + self.wss_gw = self.sortIECookie(wss) + self.dm560804E8WD = self.sortIECookie(DM) + return + + +def convertHitBoxIEtoPython(): + if sys.platform != 'win32': + print 'Cookie Converter: Warning: System is not MS-Windows. I have not been setup to work with other systems yet. Sorry ' + sys.platform + ' user. The game client will create a cookie.' + return + if __dev__: + return + a = HitBoxCookie() + a.loadIEHitBoxCookies() + a.writePythonHitBoxCookies() + del a + + +def convertHitBoxPythontoIE(): + if sys.platform != 'win32': + print 'System is not MS-Windows. I have not been setup to work with other systems yet. Sorry ' + sys.platform + ' user.' + return + if os.path.isfile('cf.txt') == True: + return + a = HitBoxCookie() + a.loadPythonHitBoxCookies() + a.writeIEHitBoxCookies() + del a + + +def getreg(regVar): + if sys.platform != 'win32': + print "System is not MS-Windows. I haven't been setup yet to work with systems other than MS-Win using MS-Internet Explorer Cookies" + return '' + siteName = 'toontown.online.disney' + cookiedir = os.getenv('USERPROFILE') + '\\Cookies' + sdir = os.listdir(cookiedir) + wholeCookie = None + while sdir: + temp = sdir.pop() + if temp.find(siteName) != -1: + wholeCookie = temp + break + + if wholeCookie == None: + print 'Cookie not found for site name: ' + siteName + return '' + CompleteCookiePath = cookiedir + '\\' + wholeCookie + cf = open(CompleteCookiePath, 'r') + data = cf.read() + cf.close() + del cf + data = data.replace('%3D', '=') + data = data.replace('%26', '&') + regNameStart = data.find(regVar) + if regNameStart == -1: + return '' + regVarStart = data.find('=', regNameStart + 1) + regVarEnd = data.find('&', regNameStart + 1) + return data[regVarStart + 1:regVarEnd] + + +def getMAC(staticMAC = [None]): + if staticMAC[0] == None: + if sys.platform == 'win32': + correctSection = 0 + try: + ipconfdata = os.popen('/WINDOWS/SYSTEM32/ipconfig /all').readlines() + except: + staticMAC[0] = 'NO_MAC' + return staticMAC[0] + + for line in ipconfdata: + if line.find('Local Area Connection') >= 0: + correctSection = 1 + if line.find('Physical Address') >= 0 and correctSection == 1: + pa = line.split(':')[-1].strip() + correctSection = 0 + staticMAC[0] = pa + return pa + + if sys.platform == 'darwin': + macconfdata = os.popen('/usr/sbin/system_profiler SPNetworkDataType |/usr/bin/grep MAC').readlines() + result = '-1' + if macconfdata: + if macconfdata[0].find('MAC Address') != -1: + pa = macconfdata[0][macconfdata[0].find(':') + 2:macconfdata[0].find(':') + 22].strip('\n') + staticMAC[0] = pa.replace(':', '-') + result = staticMAC[0] + return result + if sys.platform != 'darwin' and sys.platform != 'win32': + print 'System is not running OSX or MS-Windows.' + return '-2' + else: + return staticMAC[0] + return + + +def firstRun(operation = 'read', newPlayer = None, newPlayerBool = [False]): + if operation != 'read': + if len(newPlayerBool) != 0: + newPlayerBool.pop() + newPlayerBool.append(newPlayer) + return newPlayerBool[0] + + +def patcherVer(operation = 'read', url = None, patchfile = []): + if operation != 'read': + if len(patchfile) != 0: + patchfile.pop() + patchfile.append(url) + return patchfile + + +def loggingAvID(operation = 'read', newId = None, localAvId = [None]): + if operation == 'write': + localAvId[0] = newId + else: + return localAvId[0] + + +def loggingSubID(operation = 'read', newId = None, localSubId = [None]): + if operation == 'write': + localSubId[0] = newId + else: + return localSubId[0] + + +def vconGroup(operation = 'read', group = None, staticStore = []): + if operation != 'read': + if len(staticStore) != 0: + staticStore.pop() + staticStore.append(group) + try: + return staticStore[0] + except IndexError: + return None + + return None + + +def printUnreachableLen(): + import gc + gc.set_debug(gc.DEBUG_SAVEALL) + gc.collect() + unreachableL = [] + for it in gc.garbage: + unreachableL.append(it) + + return len(str(unreachableL)) + + +def printUnreachableNum(): + import gc + gc.set_debug(gc.DEBUG_SAVEALL) + gc.collect() + return len(gc.garbage) + + +def reportMemoryLeaks(): + if printUnreachableNum() == 0: + return + import bz2, gc + gc.set_debug(gc.DEBUG_SAVEALL) + gc.collect() + uncompressedReport = '' + for s in gc.garbage: + try: + uncompressedReport += str(s) + '&' + except TypeError: + pass + + reportdata = bz2.compress(uncompressedReport, 9) + headers = {'Content-type': 'application/x-bzip2', + 'Accept': 'text/plain'} + try: + baseURL = patcherVer()[0].split('/lo')[0] + except IndexError: + print 'Base URL not available for leak submit' + return + + basePort = 80 + if baseURL.count(':') == 2: + basePort = baseURL[-4:] + baseURL = baseURL[:-5] + baseURL = baseURL[7:] + if basePort != 80: + finalURL = 'http://' + baseURL + ':' + str(basePort) + '/logging/memory_leak.php?leakcount=' + str(printUnreachableNum()) + else: + finalURL = 'http://' + baseURL + '/logging/memory_leak.php?leakcount=' + str(printUnreachableNum()) + reporthttp = HTTPClient() + reporthttp.postForm(URLSpec(finalURL), reportdata) + + +def checkParamFile(): + if os.path.exists('parameters.txt') == 1: + paramfile = open('parameters.txt', 'r') + contents = paramfile.read() + paramfile.close() + del paramfile + contents = contents.split('\n') + newURL = '' + while contents: + checkLine = contents.pop() + if checkLine.find('PATCHER_BASE_URL=') != -1 and checkLine[0] == 'P': + newURL = checkLine.split('=')[1] + newURL = newURL.replace(' ', '') + break + + if newURL == '': + return + else: + return newURL + 'patcher.ver' diff --git a/toontown/toonbase/__init__.py b/toontown/toonbase/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/toontowngui/NewsPageButtonManager.py b/toontown/toontowngui/NewsPageButtonManager.py new file mode 100644 index 0000000..50d74e0 --- /dev/null +++ b/toontown/toontowngui/NewsPageButtonManager.py @@ -0,0 +1,241 @@ +from pandac.PandaModules import VBase4, VBase3 +from direct.fsm import FSM +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectButton import DirectButton +from toontown.toonbase import ToontownGlobals +from direct.gui.DirectGui import * +from direct.interval.IntervalGlobal import * +from toontown.toonbase import TTLocalizer +from toontown.coghq import CogHQBossBattle + +class NewsPageButtonManager(FSM.FSM): + notify = DirectNotifyGlobal.directNotify.newCategory('NewsPageButtonManager') + + def __init__(self): + FSM.FSM.__init__(self, 'NewsPageButtonManager') + self.buttonsLoaded = False + self.clearGoingToNewsInfo() + self.__blinkIval = None + self.load() + return + + def load(self): + btnGui = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_newsBtnGui') + bookModel = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_shtickerBook') + self.openNewNewsUp = btnGui.find('**/tt_t_gui_ign_new') + self.openNewNewsUpBlink = btnGui.find('**/tt_t_gui_ign_newBlink') + self.openNewNewsHover = btnGui.find('**/tt_t_gui_ign_newHover') + self.openOldNewsUp = btnGui.find('**/tt_t_gui_ign_oldNews') + self.openOldNewsHover = btnGui.find('**/tt_t_gui_ign_oldHover') + self.closeNewsUp = bookModel.find('**/tt_t_gui_sbk_newsPage1') + self.closeNewsHover = bookModel.find('**/tt_t_gui_sbk_newsPage2') + btnGui.removeNode() + bookModel.removeNode() + oldScale = 0.5 + newScale = 0.9 + shtickerBookScale = 0.305 + newPos = VBase3(0.914, 0, 0.862) + shtickerBookPos = VBase3(1.175, 0, -0.83) + textScale = 0.06 + self.newIssueButton = DirectButton(relief=None, sortOrder=DGG.BACKGROUND_SORT_INDEX - 1, image=(self.openNewNewsUp, self.openNewNewsHover, self.openNewNewsHover), text=('', TTLocalizer.EventsPageNewsTabName, TTLocalizer.EventsPageNewsTabName), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=textScale, text_font=ToontownGlobals.getInterfaceFont(), pos=newPos, scale=newScale, command=self.__handleGotoNewsButton) + self.gotoPrevPageButton = DirectButton(relief=None, image=(self.closeNewsUp, self.closeNewsHover, self.closeNewsHover), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=textScale, text_font=ToontownGlobals.getInterfaceFont(), pos=shtickerBookPos, scale=shtickerBookScale, command=self.__handleGotoPrevPageButton) + self.goto3dWorldButton = DirectButton(relief=None, image=(self.closeNewsUp, self.closeNewsHover, self.closeNewsHover), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=textScale, text_font=ToontownGlobals.getInterfaceFont(), pos=shtickerBookPos, scale=shtickerBookScale, command=self.__handleGoto3dWorldButton) + self.hideNewIssueButton() + self.gotoPrevPageButton.hide() + self.goto3dWorldButton.hide() + self.accept('newIssueOut', self.handleNewIssueOut) + bounce1Pos = VBase3(newPos.getX(), newPos.getY(), newPos.getZ() + 0.022) + bounce2Pos = VBase3(newPos.getX(), newPos.getY(), newPos.getZ() + 0.015) + bounceIval = Sequence(LerpPosInterval(self.newIssueButton, 0.1, bounce1Pos, blendType='easeOut'), LerpPosInterval(self.newIssueButton, 0.1, newPos, blendType='easeIn'), LerpPosInterval(self.newIssueButton, 0.07, bounce2Pos, blendType='easeOut'), LerpPosInterval(self.newIssueButton, 0.07, newPos, blendType='easeIn')) + self.__blinkIval = Sequence(Func(self.__showOpenEyes), Wait(2), bounceIval, Wait(0.5), Func(self.__showClosedEyes), Wait(0.1), Func(self.__showOpenEyes), Wait(0.1), Func(self.__showClosedEyes), Wait(0.1)) + self.__blinkIval.loop() + self.__blinkIval.pause() + self.buttonsLoaded = True + return + + def __showOpenEyes(self): + self.newIssueButton['image'] = (self.openNewNewsUp, self.openNewNewsHover, self.openNewNewsHover) + + def __showClosedEyes(self): + self.newIssueButton['image'] = (self.openNewNewsUpBlink, self.openNewNewsHover, self.openNewNewsHover) + + def clearGoingToNewsInfo(self): + self.goingToNewsPageFrom3dWorld = False + self.setGoingToNewsPageFromStickerBook(False) + + def __handleGotoNewsButton(self): + currentState = base.localAvatar.animFSM.getCurrentState().getName() + if currentState == 'jumpAirborne': + return + from toontown.toon import LocalToon + if not LocalToon.WantNewsPage: + return + if base.cr and base.cr.playGame and base.cr.playGame.getPlace() and base.cr.playGame.getPlace().fsm: + fsm = base.cr.playGame.getPlace().fsm + curState = fsm.getCurrentState().getName() + if curState == 'walk': + if hasattr(localAvatar, 'newsPage'): + base.cr.centralLogger.writeClientEvent('news gotoNewsButton clicked') + localAvatar.book.setPage(localAvatar.newsPage) + fsm.request('stickerBook') + self.goingToNewsPageFrom3dWorld = True + elif curState == 'stickerBook': + if hasattr(localAvatar, 'newsPage'): + base.cr.centralLogger.writeClientEvent('news gotoNewsButton clicked') + fsm.request('stickerBook') + if hasattr(localAvatar, 'newsPage') and localAvatar.newsPage: + localAvatar.book.goToNewsPage(localAvatar.newsPage) + + def __handleGotoPrevPageButton(self): + self.clearGoingToNewsInfo() + localAvatar.book.setPageBeforeNews() + self.showAppropriateButton() + self.ignoreEscapeKeyPress() + + def __handleGoto3dWorldButton(self): + localAvatar.book.closeBook() + + def hideNewIssueButton(self): + if hasattr(self, 'newIssueButton') and self.newIssueButton: + self.newIssueButton.hide() + localAvatar.clarabelleNewsPageCollision(False) + + def __showNewIssueButton(self): + self.newIssueButton.show() + localAvatar.clarabelleNewsPageCollision(True) + + def hideAllButtons(self): + if not self.buttonsLoaded: + return + self.gotoPrevPageButton.hide() + self.goto3dWorldButton.hide() + self.hideNewIssueButton() + self.__blinkIval.pause() + + def isNewIssueButtonShown(self): + if localAvatar.getLastTimeReadNews() < base.cr.inGameNewsMgr.getLatestIssue(): + return True + return False + + def enterHidden(self): + self.hideAllButtons() + + def exitHidden(self): + pass + + def enterNormalWalk(self): + if not self.buttonsLoaded: + return + if localAvatar.getLastTimeReadNews() < base.cr.inGameNewsMgr.getLatestIssue(): + self.__showNewIssueButton() + self.__blinkIval.resume() + else: + self.hideNewIssueButton() + self.gotoPrevPageButton.hide() + self.goto3dWorldButton.hide() + + def exitNormalWalk(self): + if not self.buttonsLoaded: + return + self.hideAllButtons() + + def enterGotoWorld(self): + if not self.buttonsLoaded: + return + self.hideAllButtons() + self.goto3dWorldButton.show() + + def exitGotoWorld(self): + if not self.buttonsLoaded: + return + self.hideAllButtons() + localAvatar.book.setPageBeforeNews(enterPage=False) + self.clearGoingToNewsInfo() + + def enterPrevPage(self): + if not self.buttonsLoaded: + return + self.hideAllButtons() + self.gotoPrevPageButton.show() + + def exitPrevPage(self): + if not self.buttonsLoaded: + return + self.hideAllButtons() + + def showAppropriateButton(self): + self.notify.debugStateCall(self) + from toontown.toon import LocalToon + if not LocalToon.WantNewsPage: + return + if not self.buttonsLoaded: + return + if base.cr and base.cr.playGame and base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm') and base.cr.playGame.getPlace().fsm: + fsm = base.cr.playGame.getPlace().fsm + curState = fsm.getCurrentState().getName() + book = localAvatar.book + if curState == 'walk': + if localAvatar.tutorialAck and not localAvatar.isDisguised and not isinstance(base.cr.playGame.getPlace(), CogHQBossBattle.CogHQBossBattle): + self.request('NormalWalk') + else: + self.request('Hidden') + elif curState == 'stickerBook': + if self.goingToNewsPageFrom3dWorld: + if localAvatar.tutorialAck: + self.request('GotoWorld') + else: + self.request('Hidden') + elif self.goingToNewsPageFromStickerBook or hasattr(localAvatar, 'newsPage') and localAvatar.book.isOnPage(localAvatar.newsPage): + if localAvatar.tutorialAck: + self.request('PrevPage') + else: + self.request('Hidden') + elif localAvatar.tutorialAck: + self.request('NormalWalk') + else: + self.request('Hidden') + + def setGoingToNewsPageFromStickerBook(self, newVal): + self.goingToNewsPageFromStickerBook = newVal + + def enterOff(self): + self.ignoreAll() + if not self.buttonsLoaded: + return + if self.__blinkIval: + self.__blinkIval.finish() + self.__blinkIval = None + self.newIssueButton.destroy() + self.gotoPrevPageButton.destroy() + self.goto3dWorldButton.destroy() + del self.openNewNewsUp + del self.openNewNewsUpBlink + del self.openNewNewsHover + del self.openOldNewsUp + del self.openOldNewsHover + del self.closeNewsUp + del self.closeNewsHover + return + + def exitOff(self): + self.notify.warning('Should not get here. NewsPageButtonManager.exitOff') + + def simulateEscapeKeyPress(self): + if self.goingToNewsPageFrom3dWorld: + self.__handleGoto3dWorldButton() + if self.goingToNewsPageFromStickerBook: + self.__handleGotoPrevPageButton() + + def handleNewIssueOut(self): + if localAvatar.isReadingNews(): + pass + else: + self.showAppropriateButton() + + def acceptEscapeKeyPress(self): + self.accept(ToontownGlobals.StickerBookHotkey, self.simulateEscapeKeyPress) + self.accept(ToontownGlobals.OptionsPageHotkey, self.simulateEscapeKeyPress) + + def ignoreEscapeKeyPress(self): + self.ignore(ToontownGlobals.StickerBookHotkey) + self.ignore(ToontownGlobals.OptionsPageHotkey) diff --git a/toontown/toontowngui/TTDialog.py b/toontown/toontowngui/TTDialog.py new file mode 100644 index 0000000..b11af4c --- /dev/null +++ b/toontown/toontowngui/TTDialog.py @@ -0,0 +1,16 @@ +from otp.otpgui.OTPDialog import * + +class TTDialog(OTPDialog): + + def __init__(self, parent = None, style = NoButtons, **kw): + self.path = 'phase_3/models/gui/dialog_box_buttons_gui' + OTPDialog.__init__(self, parent, style, **kw) + self.initialiseoptions(TTDialog) + + +class TTGlobalDialog(GlobalDialog): + + def __init__(self, message = '', doneEvent = None, style = NoButtons, okButtonText = OTPLocalizer.DialogOK, cancelButtonText = OTPLocalizer.DialogCancel, **kw): + self.path = 'phase_3/models/gui/dialog_box_buttons_gui' + GlobalDialog.__init__(self, message, doneEvent, style, okButtonText, cancelButtonText, **kw) + self.initialiseoptions(TTGlobalDialog) diff --git a/toontown/toontowngui/TeaserPanel.py b/toontown/toontowngui/TeaserPanel.py new file mode 100644 index 0000000..4c71a4b --- /dev/null +++ b/toontown/toontowngui/TeaserPanel.py @@ -0,0 +1,208 @@ +from pandac.PandaModules import * +from direct.gui.DirectGui import * +from direct.gui import DirectGuiGlobals +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +import TTDialog +from toontown.toonbase import TTLocalizer +from direct.showbase import PythonUtil +from direct.showbase.DirectObject import DirectObject +from otp.login import LeaveToPayDialog +Pages = {'otherHoods': (TTLocalizer.TeaserOtherHoods,), + 'typeAName': (TTLocalizer.TeaserTypeAName,), + 'sixToons': (TTLocalizer.TeaserSixToons,), + 'otherGags': (TTLocalizer.TeaserOtherGags,), + 'clothing': (TTLocalizer.TeaserClothing,), + 'cogHQ': (TTLocalizer.TeaserCogHQ,), + 'secretChat': (TTLocalizer.TeaserSecretChat,), + 'quests': (TTLocalizer.TeaserQuests,), + 'emotions': (TTLocalizer.TeaserEmotions,), + 'minigames': (TTLocalizer.TeaserMinigames,), + 'karting': (TTLocalizer.TeaserKarting,), + 'kartingAccessories': (TTLocalizer.TeaserKartingAccessories,), + 'gardening': (TTLocalizer.TeaserGardening,), + 'tricks': (TTLocalizer.TeaserTricks,), + 'species': (TTLocalizer.TeaserSpecies,), + 'golf': (TTLocalizer.TeaserGolf,), + 'fishing': (TTLocalizer.TeaserFishing,), + 'parties': (TTLocalizer.TeaserParties,), + 'plantGags': (TTLocalizer.TeaserPlantGags,), + 'pickGags': (TTLocalizer.TeaserPickGags,), + 'restockGags': (TTLocalizer.TeaserRestockGags,), + 'getGags': (TTLocalizer.TeaserGetGags,), + 'useGags': (TTLocalizer.TeaserUseGags,)} +PageOrder = ['sixToons', + 'typeAName', + 'species', + 'otherHoods', + 'otherGags', + 'clothing', + 'parties', + 'tricks', + 'cogHQ', + 'secretChat', + 'quests', + 'emotions', + 'minigames', + 'karting', + 'kartingAccessories', + 'gardening', + 'golf', + 'fishing', + 'plantGags', + 'pickGags', + 'restockGags', + 'getGags', + 'useGags'] + +class TeaserPanel(DirectObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TeaserPanel') + + def __init__(self, pageName, doneFunc = None): + self.doneFunc = doneFunc + if not hasattr(self, 'browser'): + self.browser = FeatureBrowser() + self.browser.load() + self.browser.setPos(0, 0, TTLocalizer.TPbrowserPosZ) + self.browser.setScale(0.75) + self.browser.reparentTo(hidden) + self.upsellBackground = loader.loadModel('phase_3/models/gui/tt_m_gui_ups_panelBg') + self.leaveDialog = None + self.showPage(pageName) + self.ignore('exitingStoppedState') + self.accept('exitingStoppedState', self.cleanup) + return + + def __handleDone(self, choice = 0): + self.cleanup() + self.unload() + if choice == 1: + self.__handlePay() + else: + self.__handleContinue() + + def __handleContinue(self): + if self.doneFunc: + self.notify.debug('calling doneFunc') + self.doneFunc() + + def __handlePay(self): + if base.cr.isWebPlayToken() or __dev__: + if self.leaveDialog == None: + self.notify.debug('making LTP') + self.leaveDialog = LeaveToPayDialog.LeaveToPayDialog(0, doneFunc=self.doneFunc) + self.notify.debug('showing LTP') + self.leaveDialog.show() + else: + self.notify.error('You should not have a TeaserPanel without a PlayToken') + return + + def destroy(self): + self.cleanup() + + def cleanup(self): + if hasattr(self, 'browser'): + self.browser.reparentTo(hidden) + self.browser.ignoreAll() + if hasattr(self, 'dialog'): + base.transitions.noTransitions() + self.dialog.cleanup() + del self.dialog + if self.leaveDialog: + self.leaveDialog.destroy() + self.leaveDialog = None + self.ignoreAll() + return + + def unload(self): + if hasattr(self, 'browser'): + self.browser.destroy() + del self.browser + + def showPage(self, pageName): + if pageName not in PageOrder: + self.notify.error("unknown page '%s'" % pageName) + base.cr.centralLogger.writeClientEvent('velvetRope: %s' % pageName) + self.browser.scrollTo(PageOrder.index(pageName)) + self.cleanup() + self.dialog = TTDialog.TTDialog(parent=aspect2dp, text=TTLocalizer.TeaserTop, text_scale=TTLocalizer.TPdialog, text_align=TextNode.ACenter, text_wordwrap=TTLocalizer.TPdialogWordwrap, topPad=-0.15, midPad=1.25, sidePad=0.25, pad=(0.25, 0.25), command=self.__handleDone, fadeScreen=0.5, style=TTDialog.TwoChoice, buttonTextList=[TTLocalizer.TeaserSubscribe, TTLocalizer.TeaserContinue], button_text_scale=TTLocalizer.TPbuttonTextList, buttonPadSF=5.5, sortOrder=NO_FADE_SORT_INDEX, image=self.upsellBackground) + self.dialog.setPos(0, 0, 0.75) + self.browser.reparentTo(self.dialog) + base.transitions.fadeScreen(0.5) + if base.config.GetBool('want-teaser-scroll-keys', 0): + self.accept('arrow_right', self.showNextPage) + self.accept('arrow_left', self.showPrevPage) + self.accept('stoppedAsleep', self.__handleDone) + + def showNextPage(self): + self.notify.debug('show next') + self.browser.scrollBy(1) + + def showPrevPage(self): + self.notify.debug('show prev') + self.browser.scrollBy(-1) + + def showPay(self): + self.dialog.buttonList[0].show() + + def hidePay(self): + self.dialog.buttonList[0].hide() + + def removed(self): + if hasattr(self, 'dialog') and self.dialog: + return self.dialog.removed() + elif hasattr(self, 'leaveDialog') and self.leaveDialog: + return self.leaveDialog.removed() + else: + return 1 + + +class FeatureBrowser(DirectScrolledList): + + def __init__(self, parent = aspect2dp, **kw): + self.parent = parent + optiondefs = (('parent', self.parent, None), + ('relief', None, None), + ('numItemsVisible', 1, None), + ('items', [], None)) + self.defineoptions(kw, optiondefs) + DirectScrolledList.__init__(self, parent) + self.incButton.hide() + self.decButton.hide() + self.initialiseoptions(FeatureBrowser) + return + + def destroy(self): + DirectScrolledList.destroy(self) + + def load(self): + guiModel = loader.loadModel('phase_3/models/gui/tt_m_gui_ups_logo_noText') + leftLocator = guiModel.find('**/bubbleLeft_locator') + rightLocator = guiModel.find('**/bubbleRight_locator') + haveFunNode = TextNode('Have Fun') + haveFunNode.setText(TTLocalizer.TeaserHaveFun) + haveFunNode.setTextColor(0, 0, 0, 1) + haveFunNode.setWordwrap(6) + haveFunNode.setAlign(TextNode.ACenter) + haveFunNode.setFont(DirectGuiGlobals.getDefaultFont()) + haveFun = NodePath(haveFunNode) + haveFun.reparentTo(rightLocator) + haveFun.setScale(TTLocalizer.TPhaveFun) + JoinUsNode = TextNode('Join Us') + JoinUsNode.setText(TTLocalizer.TeaserJoinUs) + JoinUsNode.setTextColor(0, 0, 0, 1) + JoinUsNode.setWordwrap(6) + JoinUsNode.setAlign(TextNode.ACenter) + JoinUsNode.setFont(DirectGuiGlobals.getDefaultFont()) + JoinUs = NodePath(JoinUsNode) + JoinUs.reparentTo(leftLocator) + JoinUs.setPos(0, 0, -0.025) + JoinUs.setScale(TTLocalizer.TPjoinUs) + for page in PageOrder: + textInfo = Pages.get(page) + textInfo = textInfo[0] + TTLocalizer.TeaserDefault + panel = DirectFrame(parent=self, relief=None, image=guiModel, image_scale=(0.65, 0.65, 0.65), image_pos=(0, 0, 0.0), text_align=TextNode.ACenter, text=textInfo, text_scale=TTLocalizer.TPpanel, text_pos=TTLocalizer.TPpanelPos) + self.addItem(panel) + + guiModel.removeNode() + return diff --git a/toontown/toontowngui/ToonHeadDialog.py b/toontown/toontowngui/ToonHeadDialog.py new file mode 100644 index 0000000..f943e55 --- /dev/null +++ b/toontown/toontowngui/ToonHeadDialog.py @@ -0,0 +1,42 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.directnotify import DirectNotifyGlobal +import TTDialog +from toontown.toon import ToonHead + +class ToonHeadDialog(TTDialog.TTDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('ToonHeadDialog') + + def __init__(self, dna, **kw): + self.dna = dna + head = hidden.attachNewNode('head', 20) + self.headModel = ToonHead.ToonHead() + self.headModel.setupHead(self.dna, forGui=1) + self.headModel.fitAndCenterHead(1.0, forGui=1) + self.headModel.reparentTo(head) + self.headModel.setName('headModel') + self.headModel.startBlink() + optiondefs = (('dialogName', 'ToonHeadDialog', None), + ('style', TTDialog.NoButtons, None), + ('geom', head, None), + ('geom_scale', 0.35, None), + ('geom_pos', (-0.25, 0, 0), None), + ('text_wordwrap', 9, None), + ('fadeScreen', 0, None)) + self.defineoptions(kw, optiondefs) + TTDialog.TTDialog.__init__(self, style=self['style']) + self.initialiseoptions(ToonHeadDialog) + self.postInitialiseFuncList.append(self.replaceHead) + return + + def replaceHead(self): + head = self.stateNodePath[0].find('**/head') + headModelCopy = self.stateNodePath[0].find('**/headModel') + headModelCopy.removeNode() + self.headModel.reparentTo(head) + + def cleanup(self): + TTDialog.TTDialog.cleanup(self) + self.headModel.stopBlink() + self.headModel.stopLookAroundNow() + self.headModel.delete() diff --git a/toontown/toontowngui/ToontownLoadingBlocker.py b/toontown/toontowngui/ToontownLoadingBlocker.py new file mode 100644 index 0000000..12d7256 --- /dev/null +++ b/toontown/toontowngui/ToontownLoadingBlocker.py @@ -0,0 +1,192 @@ +from direct.directnotify import DirectNotifyGlobal +from direct.gui.DirectGui import * +from pandac.PandaModules import TextNode +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog +from otp.otpgui.OTPDialog import * +from direct.interval.LerpInterval import LerpPosInterval, LerpScaleInterval, LerpFunc +from direct.interval.IntervalGlobal import Sequence, Parallel, Func, Wait +from direct.task import Task +import random + +class ToontownLoadingBlocker(TTDialog.TTDialog): + notify = DirectNotifyGlobal.directNotify.newCategory('ToontownLoadingBlocker') + + def __init__(self, avList): + if not self.__shouldShowBlocker(avList): + return + TTDialog.TTDialog.__init__(self) + gui = loader.loadModel('phase_3/models/gui/tt_m_gui_pat_mainGui') + img = gui.find('**/tt_t_gui_pat_loadingPopup') + self['image'] = img + self['image_scale'] = (1, 0, 1) + self['image_pos'] = (0, 0, -0.4) + gui.removeNode() + self.loadingTextChangeTimer = 10.0 + self.loadingTextTimerVariant = 3.0 + self.loadingTextFreezeTime = 3.0 + self.toonTipChangeTimer = 20.0 + self.hideBlockerIval = None + self.canChangeLoadingText = True + self.__setupLoadingBar() + self.__createTitleText() + self.__createToonTip() + self.__createLoadingText() + self.__showBlocker() + self.accept('phaseComplete-4', self.__shrinkLoadingBar) + self.accept('launcherPercentPhaseComplete', self.__update) + return + + def destroy(self): + taskMgr.remove('changeLoadingTextTask') + taskMgr.remove('canChangeLoadingTextTask') + taskMgr.remove('changeToonTipTask') + self.ignore('phaseComplete-4') + self.ignore('launcherPercentPhaseComplete') + self.__cleanupHideBlockerIval() + self.title.destroy() + self.title = None + self.loadingText.destroy() + self.loadingText = None + self.loadingTextList = None + self.toonTipText.destroy() + self.toonTipText = None + self.bar.destroy() + self.bar = None + TTDialog.TTDialog.destroy(self) + return + + def __hideBlocker(self): + self.hide() + if self.__isValidDownloadBar(): + base.downloadWatcher.text.show() + + def __showBlocker(self): + self.show() + if self.__isValidDownloadBar(): + base.downloadWatcher.text.hide() + + def __setupLoadingBar(self): + self.bar = DirectWaitBar(parent=self, guiId='DownloadBlockerBar', pos=(0, 0, -0.3138), relief=DGG.SUNKEN, frameSize=(-0.6, + 0.6, + -0.1, + 0.1), borderWidth=(0.02, 0.02), scale=(0.8, 0.8, 0.5), range=100, sortOrder=5000, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(0.2, 0.7, 0.2, 0.5), text='0%', text_scale=(0.08, 0.128), text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.035)) + self.bar.setBin('gui-popup', 1) + if self.__isValidDownloadBar(): + base.downloadWatcher.bar.hide() + + def __resetLoadingBar(self): + self.bar.clearBin() + if self.__isValidDownloadBar(): + base.downloadWatcher.bar.show() + + def __isValidDownloadBar(self): + if hasattr(base, 'downloadWatcher') and base.downloadWatcher: + if hasattr(base.downloadWatcher, 'bar') and base.downloadWatcher.bar: + return True + return False + + def __createTitleText(self): + self.title = DirectLabel(parent=self, relief=None, guiId='BlockerTitle', pos=(0, 0, 0.38), text=TTLocalizer.BlockerTitle, text_font=ToontownGlobals.getSignFont(), text_fg=(1, 0.9, 0.1, 1), text_align=TextNode.ACenter, text_scale=0.1, textMayChange=1, sortOrder=50) + return + + def __createLoadingText(self): + self.loadingText = DirectLabel(parent=self, relief=None, guiId='BlockerLoadingText', pos=(0, 0, -0.2357), text='', text_fg=(1, 1, 1, 1), text_scale=0.055, textMayChange=1, text_align=TextNode.ACenter, sortOrder=50) + self.loadingTextList = TTLocalizer.BlockerLoadingTexts + self.__changeLoadingText() + taskMgr.doMethodLater(self.loadingTextChangeTimer, self.__changeLoadingTextTask, 'changeLoadingTextTask') + return + + def __changeLoadingText(self): + + def getLoadingText(): + listLen = len(self.loadingTextList) + if listLen > 0: + randomIndex = random.randrange(listLen) + randomLoadingText = self.loadingTextList.pop(randomIndex) + return randomLoadingText + else: + self.loadingTextList = TTLocalizer.BlockerLoadingTexts + + if self.canChangeLoadingText: + self.loadingText['text'] = getLoadingText() + self.canChangeLoadingText = False + taskMgr.doMethodLater(self.loadingTextFreezeTime, self.__canChangeLoadingTextTask, 'canChangeLoadingTextTask') + + def __changeLoadingTextTask(self, task): + self.__changeLoadingText() + randVariation = random.uniform(-self.loadingTextTimerVariant, self.loadingTextTimerVariant) + task.delayTime = self.loadingTextChangeTimer + randVariation + return task.again + + def __canChangeLoadingTextTask(self, task): + self.canChangeLoadingText = True + return task.done + + def __createToonTip(self): + self.toonTipText = DirectLabel(parent=self, relief=None, guiId='BlockerToonTip', pos=(0, 0, -0.4688), text='', text_fg=(1, 1, 1, 1), text_scale=0.05, textMayChange=1, text_align=TextNode.ACenter, text_wordwrap=32, sortOrder=50) + self.__changeToonTip() + taskMgr.doMethodLater(self.toonTipChangeTimer, self.__changeToonTipTask, 'changeToonTipTask') + return + + def __changeToonTip(self): + + def getTip(tipCategory): + return TTLocalizer.TipTitle + '\n' + random.choice(TTLocalizer.TipDict.get(tipCategory)) + + self.toonTipText['text'] = getTip(TTLocalizer.TIP_GENERAL) + + def __changeToonTipTask(self, task): + self.__changeToonTip() + return task.again + + def __shouldShowBlocker(self, avList): + + def hasPlayableToon(avList): + if len(avList) > 0: + if base.cr.isPaid(): + return True + else: + for av in avList: + if av.position == 1: + return True + + return False + + if hasPlayableToon(avList): + if not (base.launcher.getPhaseComplete(3.5) and base.launcher.getPhaseComplete(4)): + return True + return False + + def __shrinkLoadingBar(self): + if self.__isValidDownloadBar(): + ivalDuration = 0.5 + barPosIval = LerpPosInterval(self.bar, ivalDuration, (-0.81, 0, -0.96)) + barScaleIval = LerpScaleInterval(self.bar, ivalDuration, (0.25, 0.25, 0.25)) + + def posText(pos): + self.bar['text_pos'] = (0, pos) + + def scaleText(scale): + self.bar['text_scale'] = (scale, scale) + + textScaleIval = LerpFunc(scaleText, fromData=0.08, toData=0.16, duration=ivalDuration) + textPosIval = LerpFunc(posText, fromData=-0.035, toData=-0.05, duration=ivalDuration) + shrinkIval = Parallel(barPosIval, barScaleIval, textPosIval, textScaleIval, Func(self.loadingText.hide)) + self.hideBlockerIval = Sequence(shrinkIval, Wait(0.5), Func(self.__hideBlocker), Func(self.__resetLoadingBar), Func(self.destroy)) + self.hideBlockerIval.start() + + def __cleanupHideBlockerIval(self): + if self.hideBlockerIval: + self.hideBlockerIval.finish() + self.hideBlockerIval = None + return + + def __update(self, phase, percent, reqByteRate, actualByteRate): + if self.__isValidDownloadBar(): + percent = base.downloadWatcher.bar['value'] + self.bar['text'] = '%s %%' % percent + self.bar['value'] = percent + if percent == 0: + self.__changeLoadingText() diff --git a/toontown/toontowngui/ToontownLoadingScreen.py b/toontown/toontowngui/ToontownLoadingScreen.py new file mode 100644 index 0000000..827115c --- /dev/null +++ b/toontown/toontowngui/ToontownLoadingScreen.py @@ -0,0 +1,62 @@ +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import random + +class ToontownLoadingScreen: + + def __init__(self): + self.__expectedCount = 0 + self.__count = 0 + self.gui = loader.loadModel('phase_3/models/gui/progress-background') + self.banner = loader.loadModel('phase_3/models/gui/toon_council').find('**/scroll') + self.banner.reparentTo(self.gui) + self.banner.setScale(0.4, 0.4, 0.4) + self.tip = DirectLabel(guiId='ToontownLoadingScreenTip', parent=self.banner, relief=None, text='', text_scale=TTLocalizer.TLStip, textMayChange=1, pos=(-1.2, 0.0, 0.1), text_fg=(0.4, 0.3, 0.2, 1), text_wordwrap=13, text_align=TextNode.ALeft) + self.title = DirectLabel(guiId='ToontownLoadingScreenTitle', parent=self.gui, relief=None, pos=(-1.06, 0, -0.77), text='', textMayChange=1, text_scale=0.08, text_fg=(0, 0, 0.5, 1), text_align=TextNode.ALeft) + self.waitBar = DirectWaitBar(guiId='ToontownLoadingScreenWaitBar', parent=self.gui, frameSize=(-1.06, + 1.06, + -0.03, + 0.03), pos=(0, 0, -0.85), text='') + return + + def destroy(self): + self.tip.destroy() + self.title.destroy() + self.waitBar.destroy() + self.banner.removeNode() + self.gui.removeNode() + + def getTip(self, tipCategory): + return TTLocalizer.TipTitle + '\n' + random.choice(TTLocalizer.TipDict.get(tipCategory)) + + def begin(self, range, label, gui, tipCategory): + self.waitBar['range'] = range + self.title['text'] = label + self.tip['text'] = self.getTip(tipCategory) + self.__count = 0 + self.__expectedCount = range + if gui: + self.waitBar.reparentTo(self.gui) + self.title.reparentTo(self.gui) + self.gui.reparentTo(aspect2dp, NO_FADE_SORT_INDEX) + else: + self.waitBar.reparentTo(aspect2dp, NO_FADE_SORT_INDEX) + self.title.reparentTo(aspect2dp, NO_FADE_SORT_INDEX) + self.gui.reparentTo(hidden) + self.waitBar.update(self.__count) + + def end(self): + self.waitBar.finish() + self.waitBar.reparentTo(self.gui) + self.title.reparentTo(self.gui) + self.gui.reparentTo(hidden) + return (self.__expectedCount, self.__count) + + def abort(self): + self.gui.reparentTo(hidden) + + def tick(self): + self.__count = self.__count + 1 + self.waitBar.update(self.__count) diff --git a/toontown/toontowngui/__init__.py b/toontown/toontowngui/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/town/BRStreet.py b/toontown/town/BRStreet.py new file mode 100644 index 0000000..5043ff7 --- /dev/null +++ b/toontown/town/BRStreet.py @@ -0,0 +1,12 @@ +import Street + +class BRStreet(Street.Street): + + def __init__(self, loader, parentFSM, doneEvent): + Street.Street.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Street.Street.load(self) + + def unload(self): + Street.Street.unload(self) diff --git a/toontown/town/BRTownLoader.py b/toontown/town/BRTownLoader.py new file mode 100644 index 0000000..24ef145 --- /dev/null +++ b/toontown/town/BRTownLoader.py @@ -0,0 +1,22 @@ +import TownLoader +import BRStreet +from toontown.suit import Suit + +class BRTownLoader(TownLoader.TownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = BRStreet.BRStreet + self.musicFile = 'phase_8/audio/bgm/TB_SZ.mid' + self.activityMusicFile = 'phase_8/audio/bgm/TB_SZ_activity.mid' + self.townStorageDNAFile = 'phase_8/dna/storage_BR_town.dna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(3) + dnaFile = 'phase_8/dna/the_burrrgh_' + str(self.canonicalBranchZone) + '.dna' + self.createHood(dnaFile) + + def unload(self): + Suit.unloadSuits(3) + TownLoader.TownLoader.unload(self) diff --git a/toontown/town/DDStreet.py b/toontown/town/DDStreet.py new file mode 100644 index 0000000..66594c7 --- /dev/null +++ b/toontown/town/DDStreet.py @@ -0,0 +1,20 @@ +import Street + +class DDStreet(Street.Street): + + def __init__(self, loader, parentFSM, doneEvent): + Street.Street.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Street.Street.load(self) + + def unload(self): + Street.Street.unload(self) + + def enter(self, requestStatus): + self.loader.hood.setWhiteFog() + Street.Street.enter(self, requestStatus) + + def exit(self): + self.loader.hood.setNoFog() + Street.Street.exit(self) diff --git a/toontown/town/DDTownLoader.py b/toontown/town/DDTownLoader.py new file mode 100644 index 0000000..99e9f11 --- /dev/null +++ b/toontown/town/DDTownLoader.py @@ -0,0 +1,28 @@ +import TownLoader +import DDStreet +from toontown.suit import Suit + +class DDTownLoader(TownLoader.TownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = DDStreet.DDStreet + self.musicFile = 'phase_6/audio/bgm/DD_SZ.mid' + self.activityMusicFile = 'phase_6/audio/bgm/DD_SZ_activity.mid' + self.townStorageDNAFile = 'phase_6/dna/storage_DD_town.dna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(2) + dnaFile = 'phase_6/dna/donalds_dock_' + str(self.canonicalBranchZone) + '.dna' + self.createHood(dnaFile) + + def unload(self): + Suit.unloadSuits(2) + TownLoader.TownLoader.unload(self) + + def enter(self, requestStatus): + TownLoader.TownLoader.enter(self, requestStatus) + + def exit(self): + TownLoader.TownLoader.exit(self) diff --git a/toontown/town/DGStreet.py b/toontown/town/DGStreet.py new file mode 100644 index 0000000..729eff7 --- /dev/null +++ b/toontown/town/DGStreet.py @@ -0,0 +1,12 @@ +import Street + +class DGStreet(Street.Street): + + def __init__(self, loader, parentFSM, doneEvent): + Street.Street.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Street.Street.load(self) + + def unload(self): + Street.Street.unload(self) diff --git a/toontown/town/DGTownLoader.py b/toontown/town/DGTownLoader.py new file mode 100644 index 0000000..69bd83f --- /dev/null +++ b/toontown/town/DGTownLoader.py @@ -0,0 +1,22 @@ +import TownLoader +import DGStreet +from toontown.suit import Suit + +class DGTownLoader(TownLoader.TownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = DGStreet.DGStreet + self.musicFile = 'phase_8/audio/bgm/DG_SZ.mid' + self.activityMusicFile = 'phase_8/audio/bgm/DG_SZ.mid' + self.townStorageDNAFile = 'phase_8/dna/storage_DG_town.dna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(3) + dnaFile = 'phase_8/dna/daisys_garden_' + str(self.canonicalBranchZone) + '.dna' + self.createHood(dnaFile) + + def unload(self): + Suit.unloadSuits(3) + TownLoader.TownLoader.unload(self) diff --git a/toontown/town/DLStreet.py b/toontown/town/DLStreet.py new file mode 100644 index 0000000..782731d --- /dev/null +++ b/toontown/town/DLStreet.py @@ -0,0 +1,12 @@ +import Street + +class DLStreet(Street.Street): + + def __init__(self, loader, parentFSM, doneEvent): + Street.Street.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Street.Street.load(self) + + def unload(self): + Street.Street.unload(self) diff --git a/toontown/town/DLTownLoader.py b/toontown/town/DLTownLoader.py new file mode 100644 index 0000000..2cca7da --- /dev/null +++ b/toontown/town/DLTownLoader.py @@ -0,0 +1,22 @@ +import TownLoader +import DLStreet +from toontown.suit import Suit + +class DLTownLoader(TownLoader.TownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = DLStreet.DLStreet + self.musicFile = 'phase_8/audio/bgm/DL_SZ.mid' + self.activityMusicFile = 'phase_8/audio/bgm/DL_SZ_activity.mid' + self.townStorageDNAFile = 'phase_8/dna/storage_DL_town.dna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(3) + dnaFile = 'phase_8/dna/donalds_dreamland_' + str(self.canonicalBranchZone) + '.dna' + self.createHood(dnaFile) + + def unload(self): + Suit.unloadSuits(3) + TownLoader.TownLoader.unload(self) diff --git a/toontown/town/MMStreet.py b/toontown/town/MMStreet.py new file mode 100644 index 0000000..ca7a8f5 --- /dev/null +++ b/toontown/town/MMStreet.py @@ -0,0 +1,12 @@ +import Street + +class MMStreet(Street.Street): + + def __init__(self, loader, parentFSM, doneEvent): + Street.Street.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Street.Street.load(self) + + def unload(self): + Street.Street.unload(self) diff --git a/toontown/town/MMTownLoader.py b/toontown/town/MMTownLoader.py new file mode 100644 index 0000000..fb31ee4 --- /dev/null +++ b/toontown/town/MMTownLoader.py @@ -0,0 +1,22 @@ +import TownLoader +import MMStreet +from toontown.suit import Suit + +class MMTownLoader(TownLoader.TownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = MMStreet.MMStreet + self.musicFile = 'phase_6/audio/bgm/MM_SZ.mid' + self.activityMusicFile = 'phase_6/audio/bgm/MM_SZ_activity.mid' + self.townStorageDNAFile = 'phase_6/dna/storage_MM_town.dna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(2) + dnaFile = 'phase_6/dna/minnies_melody_land_' + str(self.canonicalBranchZone) + '.dna' + self.createHood(dnaFile) + + def unload(self): + Suit.unloadSuits(2) + TownLoader.TownLoader.unload(self) diff --git a/toontown/town/Street.py b/toontown/town/Street.py new file mode 100644 index 0000000..7ba7d30 --- /dev/null +++ b/toontown/town/Street.py @@ -0,0 +1,394 @@ +from pandac.PandaModules import * +from toontown.battle.BattleProps import * +from toontown.battle.BattleSounds import * +from toontown.distributed.ToontownMsgTypes import * +from direct.gui.DirectGui import cleanupDialog +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import Place +from toontown.battle import BattlePlace +from direct.showbase import DirectObject +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.task import Task +from otp.distributed.TelemetryLimiter import RotationLimitToH, TLGatherAllAvs +from toontown.battle import BattleParticles +from toontown.building import Elevator +from toontown.hood import ZoneUtil +from toontown.toonbase import ToontownGlobals +from toontown.toon.Toon import teleportDebug +from toontown.estate import HouseGlobals +from toontown.toonbase import TTLocalizer +from direct.interval.IntervalGlobal import * +visualizeZones = base.config.GetBool('visualize-zones', 0) + +class Street(BattlePlace.BattlePlace): + notify = DirectNotifyGlobal.directNotify.newCategory('Street') + + def __init__(self, loader, parentFSM, doneEvent): + BattlePlace.BattlePlace.__init__(self, loader, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Street', [State.State('start', self.enterStart, self.exitStart, ['walk', + 'tunnelIn', + 'doorIn', + 'teleportIn', + 'elevatorIn']), + State.State('walk', self.enterWalk, self.exitWalk, ['push', + 'sit', + 'stickerBook', + 'WaitForBattle', + 'battle', + 'DFA', + 'trialerFA', + 'doorOut', + 'elevator', + 'tunnelIn', + 'tunnelOut', + 'teleportOut', + 'quest', + 'stopped', + 'fishing', + 'purchase', + 'died']), + State.State('sit', self.enterSit, self.exitSit, ['walk']), + State.State('push', self.enterPush, self.exitPush, ['walk']), + State.State('stickerBook', self.enterStickerBook, self.exitStickerBook, ['walk', + 'push', + 'sit', + 'battle', + 'DFA', + 'trialerFA', + 'doorOut', + 'elevator', + 'tunnelIn', + 'tunnelOut', + 'WaitForBattle', + 'teleportOut', + 'quest', + 'stopped', + 'fishing', + 'purchase']), + State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']), + State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']), + State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']), + State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk']), + State.State('elevatorIn', self.enterElevatorIn, self.exitElevatorIn, ['walk']), + State.State('elevator', self.enterElevator, self.exitElevator, ['walk']), + 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', 'tunnelOut']), + State.State('DFAReject', self.enterDFAReject, self.exitDFAReject, ['walk']), + State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk', + 'teleportOut', + 'quietZone', + 'WaitForBattle', + 'battle']), + State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn', 'quietZone', 'WaitForBattle']), + State.State('died', self.enterDied, self.exitDied, ['quietZone']), + State.State('tunnelIn', self.enterTunnelIn, self.exitTunnelIn, ['walk']), + State.State('tunnelOut', self.enterTunnelOut, self.exitTunnelOut, ['final']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['teleportIn']), + State.State('quest', self.enterQuest, self.exitQuest, ['walk', 'stopped']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('stopped', self.enterStopped, self.exitStopped, ['walk']), + State.State('fishing', self.enterFishing, self.exitFishing, ['walk']), + State.State('purchase', self.enterPurchase, self.exitPurchase, ['walk']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.parentFSM = parentFSM + self.tunnelOriginList = [] + self.elevatorDoneEvent = 'elevatorDone' + self.halloweenLights = [] + + def enter(self, requestStatus, visibilityFlag = 1, arrowsOn = 1): + teleportDebug(requestStatus, 'Street.enter(%s)' % (requestStatus,)) + self._ttfToken = None + self.fsm.enterInitialState() + base.playMusic(self.loader.music, looping=1, volume=0.8) + self.loader.geom.reparentTo(render) + if visibilityFlag: + self.visibilityOn() + base.localAvatar.setGeom(self.loader.geom) + base.localAvatar.setOnLevelGround(1) + self._telemLimiter = TLGatherAllAvs('Street', RotationLimitToH) + NametagGlobals.setMasterArrowsOn(arrowsOn) + + def __lightDecorationOn__(): + geom = base.cr.playGame.getPlace().loader.geom + self.halloweenLights = geom.findAllMatches('**/*light*') + self.halloweenLights += geom.findAllMatches('**/*lamp*') + self.halloweenLights += geom.findAllMatches('**/prop_snow_tree*') + for light in self.halloweenLights: + light.setColorScaleOff(1) + + newsManager = base.cr.newsManager + if newsManager: + holidayIds = base.cr.newsManager.getDecorationHolidayId() + if (ToontownGlobals.HALLOWEEN_COSTUMES in holidayIds or ToontownGlobals.SPOOKY_COSTUMES in holidayIds) and self.loader.hood.spookySkyFile: + lightsOff = Sequence(LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(0.55, 0.55, 0.65, 1)), Func(self.loader.hood.startSpookySky)) + lightsOff.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + else: + self.loader.hood.startSky() + lightsOn = LerpColorScaleInterval(base.cr.playGame.hood.loader.geom, 0.1, Vec4(1, 1, 1, 1)) + lightsOn.start() + self.accept('doorDoneEvent', self.handleDoorDoneEvent) + self.accept('DistributedDoor_doorTrigger', self.handleDoorTrigger) + self.enterZone(requestStatus['zoneId']) + self.tunnelOriginList = base.cr.hoodMgr.addLinkTunnelHooks(self, self.loader.nodeList, self.zoneId) + self.fsm.request(requestStatus['how'], [requestStatus]) + self.replaceStreetSignTextures() + return + + def exit(self, visibilityFlag = 1): + if visibilityFlag: + self.visibilityOff() + self.loader.geom.reparentTo(hidden) + self._telemLimiter.destroy() + del self._telemLimiter + + def __lightDecorationOff__(): + for light in self.halloweenLights: + light.reparentTo(hidden) + + newsManager = base.cr.newsManager + NametagGlobals.setMasterArrowsOn(0) + self.loader.hood.stopSky() + self.loader.music.stop() + base.localAvatar.setGeom(render) + base.localAvatar.setOnLevelGround(0) + + def load(self): + BattlePlace.BattlePlace.load(self) + self.parentFSM.getStateNamed('street').addChild(self.fsm) + + def unload(self): + self.parentFSM.getStateNamed('street').removeChild(self.fsm) + del self.parentFSM + del self.fsm + self.enterZone(None) + cleanupDialog('globalDialog') + self.ignoreAll() + BattlePlace.BattlePlace.unload(self) + return + + def enterElevatorIn(self, requestStatus): + self._eiwbTask = taskMgr.add(Functor(self._elevInWaitBldgTask, requestStatus['bldgDoId']), uniqueName('elevInWaitBldg')) + + def _elevInWaitBldgTask(self, bldgDoId, task): + bldg = base.cr.doId2do.get(bldgDoId) + if bldg: + if bldg.elevatorNodePath is not None: + self._enterElevatorGotElevator() + return Task.done + return Task.cont + + def _enterElevatorGotElevator(self): + messenger.send('insideVictorElevator') + + def exitElevatorIn(self): + taskMgr.remove(self._eiwbTask) + + def enterElevator(self, distElevator): + base.localAvatar.cantLeaveGame = 1 + self.accept(self.elevatorDoneEvent, self.handleElevatorDone) + self.elevator = Elevator.Elevator(self.fsm.getStateNamed('elevator'), self.elevatorDoneEvent, distElevator) + self.elevator.load() + self.elevator.enter() + + def exitElevator(self): + base.localAvatar.cantLeaveGame = 0 + self.ignore(self.elevatorDoneEvent) + self.elevator.unload() + self.elevator.exit() + del self.elevator + + 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 in ('suitInterior', 'cogdoInterior'): + self.doneStatus = doneStatus + messenger.send(self.doneEvent) + else: + self.notify.error('Unknown mode: ' + where + ' in handleElevatorDone') + + def enterTunnelIn(self, requestStatus): + self.enterZone(requestStatus['zoneId']) + BattlePlace.BattlePlace.enterTunnelIn(self, requestStatus) + + def enterTeleportIn(self, requestStatus): + teleportDebug(requestStatus, 'Street.enterTeleportIn(%s)' % (requestStatus,)) + zoneId = requestStatus['zoneId'] + self._ttfToken = self.addSetZoneCompleteCallback(Functor(self._teleportToFriend, requestStatus)) + self.enterZone(zoneId) + BattlePlace.BattlePlace.enterTeleportIn(self, requestStatus) + + def _teleportToFriend(self, requestStatus): + avId = requestStatus['avId'] + hoodId = requestStatus['hoodId'] + zoneId = requestStatus['zoneId'] + if avId != -1: + if not base.cr.doId2do.has_key(avId): + teleportDebug(requestStatus, "couldn't find friend %s" % avId) + handle = base.cr.identifyFriend(avId) + requestStatus = {'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': hoodId, + 'shardId': None, + 'loader': 'safeZoneLoader', + 'where': 'playground', + 'avId': avId} + self.fsm.request('final') + self.__teleportOutDone(requestStatus) + return + + def exitTeleportIn(self): + self.removeSetZoneCompleteCallback(self._ttfToken) + self._ttfToken = None + BattlePlace.BattlePlace.exitTeleportIn(self) + return + + def enterTeleportOut(self, requestStatus): + if requestStatus.has_key('battle'): + self.__teleportOutDone(requestStatus) + else: + BattlePlace.BattlePlace.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 shardId == None: + if zoneId == self.zoneId: + self.fsm.request('teleportIn', [requestStatus]) + elif requestStatus['where'] == 'street' and ZoneUtil.getBranchZone(zoneId) == self.loader.branchZone: + self.fsm.request('quietZone', [requestStatus]) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + elif hoodId == ToontownGlobals.MyEstate: + self.getEstateZoneAndGoHome(requestStatus) + else: + self.doneStatus = requestStatus + messenger.send(self.doneEvent) + return + + def exitTeleportOut(self): + BattlePlace.BattlePlace.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 renameFloorPolys(self, nodeList): + for i in nodeList: + collNodePaths = i.findAllMatches('**/+CollisionNode') + numCollNodePaths = collNodePaths.getNumPaths() + visGroupName = i.node().getName() + for j in range(numCollNodePaths): + collNodePath = collNodePaths.getPath(j) + bitMask = collNodePath.node().getIntoCollideMask() + if bitMask.getBit(1): + collNodePath.node().setName(visGroupName) + + def hideAllVisibles(self): + for i in self.loader.nodeList: + i.stash() + + def showAllVisibles(self): + for i in self.loader.nodeList: + i.unstash() + + def visibilityOn(self): + self.hideAllVisibles() + self.accept('on-floor', self.enterZone) + + def visibilityOff(self): + self.ignore('on-floor') + self.showAllVisibles() + + def doEnterZone(self, newZoneId): + if self.zoneId != None: + for i in self.loader.nodeDict[self.zoneId]: + if newZoneId: + if i not in self.loader.nodeDict[newZoneId]: + self.loader.fadeOutDict[i].start() + self.loader.exitAnimatedProps(i) + else: + i.stash() + self.loader.exitAnimatedProps(i) + + if newZoneId != None: + for i in self.loader.nodeDict[newZoneId]: + if self.zoneId: + if i not in self.loader.nodeDict[self.zoneId]: + self.loader.fadeInDict[i].start() + self.loader.enterAnimatedProps(i) + else: + if self.loader.fadeOutDict[i].isPlaying(): + self.loader.fadeOutDict[i].finish() + if self.loader.fadeInDict[i].isPlaying(): + self.loader.fadeInDict[i].finish() + self.loader.enterAnimatedProps(i) + i.unstash() + + if newZoneId != self.zoneId: + if visualizeZones: + if self.zoneId != None: + self.loader.zoneDict[self.zoneId].clearColor() + if newZoneId != None: + self.loader.zoneDict[newZoneId].setColor(0, 0, 1, 1, 100) + if newZoneId != None: + base.cr.sendSetZoneMsg(newZoneId) + self.notify.debug('Entering Zone %d' % newZoneId) + self.zoneId = newZoneId + geom = base.cr.playGame.getPlace().loader.geom + self.halloweenLights = geom.findAllMatches('**/*light*') + self.halloweenLights += geom.findAllMatches('**/*lamp*') + self.halloweenLights += geom.findAllMatches('**/prop_snow_tree*') + for light in self.halloweenLights: + light.setColorScaleOff(1) + + return + + def replaceStreetSignTextures(self): + if not hasattr(base.cr, 'playGame'): + return + place = base.cr.playGame.getPlace() + if place is None: + return + geom = base.cr.playGame.getPlace().loader.geom + signs = geom.findAllMatches('**/*tunnelAheadSign*;+s') + if signs.getNumPaths() > 0: + streetSign = base.cr.streetSign + signTexturePath = streetSign.StreetSignBaseDir + '/' + streetSign.StreetSignFileName + loaderTexturePath = Filename(str(signTexturePath)) + alphaPath = 'phase_4/maps/tt_t_ara_gen_tunnelAheadSign_a.rgb' + inDreamland = False + if place.zoneId and ZoneUtil.getCanonicalHoodId(place.zoneId) == ToontownGlobals.DonaldsDreamland: + inDreamland = True + alphaPath = 'phase_4/maps/tt_t_ara_gen_tunnelAheadSign_a.rgb' + if Filename(signTexturePath).exists(): + signTexture = loader.loadTexture(loaderTexturePath, alphaPath) + for sign in signs: + if Filename(signTexturePath).exists(): + sign.setTexture(signTexture, 1) + if inDreamland: + sign.setColorScale(0.525, 0.525, 0.525, 1) + + return diff --git a/toontown/town/TTStreet.py b/toontown/town/TTStreet.py new file mode 100644 index 0000000..1df1bb5 --- /dev/null +++ b/toontown/town/TTStreet.py @@ -0,0 +1,15 @@ +import Street + +class TTStreet(Street.Street): + + def __init__(self, loader, parentFSM, doneEvent): + Street.Street.__init__(self, loader, parentFSM, doneEvent) + + def load(self): + Street.Street.load(self) + + def unload(self): + Street.Street.unload(self) + + def doRequestLeave(self, requestStatus): + self.fsm.request('trialerFA', [requestStatus]) diff --git a/toontown/town/TTTownLoader.py b/toontown/town/TTTownLoader.py new file mode 100644 index 0000000..9107d49 --- /dev/null +++ b/toontown/town/TTTownLoader.py @@ -0,0 +1,22 @@ +import TownLoader +import TTStreet +from toontown.suit import Suit + +class TTTownLoader(TownLoader.TownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TownLoader.TownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = TTStreet.TTStreet + self.musicFile = 'phase_3.5/audio/bgm/TC_SZ.mid' + self.activityMusicFile = 'phase_3.5/audio/bgm/TC_SZ_activity.mid' + self.townStorageDNAFile = 'phase_5/dna/storage_TT_town.dna' + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadSuits(1) + dnaFile = 'phase_5/dna/toontown_central_' + str(self.canonicalBranchZone) + '.dna' + self.createHood(dnaFile) + + def unload(self): + Suit.unloadSuits(1) + TownLoader.TownLoader.unload(self) diff --git a/toontown/town/TownBattle.py b/toontown/town/TownBattle.py new file mode 100644 index 0000000..d4bee0b --- /dev/null +++ b/toontown/town/TownBattle.py @@ -0,0 +1,686 @@ +from toontown.toonbase.ToontownBattleGlobals import * +import types +from direct.fsm import StateData +from direct.fsm import ClassicFSM, State +from direct.fsm import State +import TownBattleAttackPanel +import TownBattleWaitPanel +import TownBattleChooseAvatarPanel +import TownBattleSOSPanel +import TownBattleSOSPetSearchPanel +import TownBattleSOSPetInfoPanel +import TownBattleToonPanel +from toontown.toontowngui import TTDialog +from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleBase +from toontown.toonbase import ToontownTimer +from direct.showbase import PythonUtil +from toontown.toonbase import TTLocalizer +from toontown.pets import PetConstants +from direct.gui.DirectGui import DGG +from toontown.battle import FireCogPanel + +class TownBattle(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattle') + evenPos = (0.75, + 0.25, + -0.25, + -0.75) + oddPos = (0.5, 0, -0.5) + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.numCogs = 1 + self.creditLevel = None + self.luredIndices = [] + self.trappedIndices = [] + self.numToons = 1 + self.toons = [] + self.localNum = 0 + self.time = 0 + self.bldg = 0 + self.track = -1 + self.level = -1 + self.target = 0 + self.toonAttacks = [(-1, 0, 0), + (-1, 0, 0), + (-1, 0, 0), + (-1, 0, 0)] + self.fsm = ClassicFSM.ClassicFSM('TownBattle', [ + State.State('Off', + self.enterOff, + self.exitOff, + ['Attack']), + State.State('Attack', + self.enterAttack, + self.exitAttack, + ['ChooseCog', + 'ChooseToon', + 'AttackWait', + 'Run', + 'Fire', + 'SOS']), + State.State('ChooseCog', + self.enterChooseCog, + self.exitChooseCog, + ['AttackWait', + 'Attack']), + State.State('AttackWait', + self.enterAttackWait, + self.exitAttackWait, + ['ChooseCog', + 'ChooseToon', + 'Attack']), + State.State('ChooseToon', + self.enterChooseToon, + self.exitChooseToon, + ['AttackWait', + 'Attack']), + State.State('Run', + self.enterRun, + self.exitRun, + ['Attack']), + State.State('SOS', + self.enterSOS, + self.exitSOS, + ['Attack', + 'AttackWait', + 'SOSPetSearch', + 'SOSPetInfo']), + State.State('SOSPetSearch', + self.enterSOSPetSearch, + self.exitSOSPetSearch, + ['SOS', + 'SOSPetInfo']), + State.State('SOSPetInfo', + self.enterSOSPetInfo, + self.exitSOSPetInfo, + ['SOS', + 'AttackWait']), + State.State('Fire', + self.enterFire, + self.exitFire, + ['Attack', + 'AttackWait'])], + 'Off', 'Off') + self.runPanel = TTDialog.TTDialog(dialogName='TownBattleRunPanel', text=TTLocalizer.TownBattleRun, style=TTDialog.TwoChoice, command=self.__handleRunPanelDone) + self.runPanel.hide() + self.attackPanelDoneEvent = 'attack-panel-done' + self.attackPanel = TownBattleAttackPanel.TownBattleAttackPanel(self.attackPanelDoneEvent) + self.waitPanelDoneEvent = 'wait-panel-done' + self.waitPanel = TownBattleWaitPanel.TownBattleWaitPanel(self.waitPanelDoneEvent) + self.chooseCogPanelDoneEvent = 'choose-cog-panel-done' + self.chooseCogPanel = TownBattleChooseAvatarPanel.TownBattleChooseAvatarPanel(self.chooseCogPanelDoneEvent, 0) + self.chooseToonPanelDoneEvent = 'choose-toon-panel-done' + self.chooseToonPanel = TownBattleChooseAvatarPanel.TownBattleChooseAvatarPanel(self.chooseToonPanelDoneEvent, 1) + self.SOSPanelDoneEvent = 'SOS-panel-done' + self.SOSPanel = TownBattleSOSPanel.TownBattleSOSPanel(self.SOSPanelDoneEvent) + self.SOSPetSearchPanelDoneEvent = 'SOSPetSearch-panel-done' + self.SOSPetSearchPanel = TownBattleSOSPetSearchPanel.TownBattleSOSPetSearchPanel(self.SOSPetSearchPanelDoneEvent) + self.SOSPetInfoPanelDoneEvent = 'SOSPetInfo-panel-done' + self.SOSPetInfoPanel = TownBattleSOSPetInfoPanel.TownBattleSOSPetInfoPanel(self.SOSPetInfoPanelDoneEvent) + self.fireCogPanelDoneEvent = 'fire-cog-panel-done' + self.FireCogPanel = FireCogPanel.FireCogPanel(self.fireCogPanelDoneEvent) + self.cogFireCosts = [None, + None, + None, + None] + self.toonPanels = (TownBattleToonPanel.TownBattleToonPanel(0), + TownBattleToonPanel.TownBattleToonPanel(1), + TownBattleToonPanel.TownBattleToonPanel(2), + TownBattleToonPanel.TownBattleToonPanel(3)) + self.timer = ToontownTimer.ToontownTimer() + self.timer.setPos(1.182, 0, 0.842) + self.timer.setScale(0.4) + self.timer.hide() + return + + def cleanup(self): + self.ignore(self.attackPanelDoneEvent) + self.unload() + del self.fsm + self.runPanel.cleanup() + del self.runPanel + del self.attackPanel + del self.waitPanel + del self.chooseCogPanel + del self.chooseToonPanel + del self.SOSPanel + del self.FireCogPanel + del self.SOSPetSearchPanel + del self.SOSPetInfoPanel + for toonPanel in self.toonPanels: + toonPanel.cleanup() + + del self.toonPanels + self.timer.destroy() + del self.timer + del self.toons + + def enter(self, event, parentFSMState, bldg = 0, creditMultiplier = 1, tutorialFlag = 0): + self.parentFSMState = parentFSMState + self.parentFSMState.addChild(self.fsm) + if not self.isLoaded: + self.load() + print 'Battle Event %s' % event + self.battleEvent = event + self.fsm.enterInitialState() + base.localAvatar.laffMeter.start() + self.numToons = 1 + self.numCogs = 1 + self.toons = [base.localAvatar.doId] + self.toonPanels[0].setLaffMeter(base.localAvatar) + self.bldg = bldg + self.creditLevel = None + self.creditMultiplier = creditMultiplier + self.tutorialFlag = tutorialFlag + base.localAvatar.inventory.setBattleCreditMultiplier(self.creditMultiplier) + base.localAvatar.inventory.setActivateMode('battle', heal=0, bldg=bldg, tutorialFlag=tutorialFlag) + self.SOSPanel.bldg = bldg + return + + def exit(self): + base.localAvatar.laffMeter.stop() + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + base.localAvatar.inventory.setBattleCreditMultiplier(1) + + def load(self): + if self.isLoaded: + return + self.attackPanel.load() + self.waitPanel.load() + self.chooseCogPanel.load() + self.chooseToonPanel.load() + self.SOSPanel.load() + self.SOSPetSearchPanel.load() + self.SOSPetInfoPanel.load() + self.isLoaded = 1 + + def unload(self): + if not self.isLoaded: + return + self.attackPanel.unload() + self.waitPanel.unload() + self.chooseCogPanel.unload() + self.chooseToonPanel.unload() + self.FireCogPanel.unload() + self.SOSPanel.unload() + self.SOSPetSearchPanel.unload() + self.SOSPetInfoPanel.unload() + self.isLoaded = 0 + + def setState(self, state): + if hasattr(self, 'fsm'): + self.fsm.request(state) + + def updateTimer(self, time): + self.time = time + self.timer.setTime(time) + return None + + def __enterPanels(self, num, localNum): + self.notify.debug('enterPanels() num: %d localNum: %d' % (num, localNum)) + for toonPanel in self.toonPanels: + toonPanel.hide() + toonPanel.setPos(0, 0, -0.9) + + if num == 1: + self.toonPanels[0].setX(self.oddPos[1]) + self.toonPanels[0].show() + elif num == 2: + self.toonPanels[0].setX(self.evenPos[1]) + self.toonPanels[0].show() + self.toonPanels[1].setX(self.evenPos[2]) + self.toonPanels[1].show() + elif num == 3: + self.toonPanels[0].setX(self.oddPos[0]) + self.toonPanels[0].show() + self.toonPanels[1].setX(self.oddPos[1]) + self.toonPanels[1].show() + self.toonPanels[2].setX(self.oddPos[2]) + self.toonPanels[2].show() + elif num == 4: + self.toonPanels[0].setX(self.evenPos[0]) + self.toonPanels[0].show() + self.toonPanels[1].setX(self.evenPos[1]) + self.toonPanels[1].show() + self.toonPanels[2].setX(self.evenPos[2]) + self.toonPanels[2].show() + self.toonPanels[3].setX(self.evenPos[3]) + self.toonPanels[3].show() + else: + self.notify.error('Bad number of toons: %s' % num) + return None + + def updateChosenAttacks(self, battleIndices, tracks, levels, targets): + self.notify.debug('updateChosenAttacks bi=%s tracks=%s levels=%s targets=%s' % (battleIndices, + tracks, + levels, + targets)) + for i in range(4): + if battleIndices[i] == -1: + pass + else: + if tracks[i] == BattleBase.NO_ATTACK: + numTargets = 0 + target = -2 + elif tracks[i] == BattleBase.PASS_ATTACK: + numTargets = 0 + target = -2 + elif tracks[i] == BattleBase.SOS or tracks[i] == BattleBase.NPCSOS or tracks[i] == BattleBase.PETSOS: + numTargets = 0 + target = -2 + elif tracks[i] == HEAL_TRACK: + numTargets = self.numToons + if self.__isGroupHeal(levels[i]): + target = -2 + else: + target = targets[i] + else: + numTargets = self.numCogs + if self.__isGroupAttack(tracks[i], levels[i]): + target = -1 + else: + target = targets[i] + if target == -1: + numTargets = None + self.toonPanels[battleIndices[i]].setValues(battleIndices[i], tracks[i], levels[i], numTargets, target, self.localNum) + + return + + def chooseDefaultTarget(self): + if self.track > -1: + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.target + messenger.send(self.battleEvent, [response]) + return 1 + return 0 + + def updateLaffMeter(self, toonNum, hp): + self.toonPanels[toonNum].updateLaffMeter(hp) + + def enterOff(self): + if self.isLoaded: + for toonPanel in self.toonPanels: + toonPanel.hide() + + self.toonAttacks = [(-1, 0, 0), + (-1, 0, 0), + (-1, 0, 0), + (-1, 0, 0)] + self.target = 0 + if hasattr(self, 'timer'): + self.timer.hide() + return None + + def exitOff(self): + if self.isLoaded: + self.__enterPanels(self.numToons, self.localNum) + self.timer.show() + self.track = -1 + self.level = -1 + self.target = 0 + return None + + def enterAttack(self): + self.attackPanel.enter() + self.accept(self.attackPanelDoneEvent, self.__handleAttackPanelDone) + for toonPanel in self.toonPanels: + toonPanel.setValues(0, BattleBase.NO_ATTACK) + + return None + + def exitAttack(self): + self.ignore(self.attackPanelDoneEvent) + self.attackPanel.exit() + return None + + def __handleAttackPanelDone(self, doneStatus): + self.notify.debug('doneStatus: %s' % doneStatus) + mode = doneStatus['mode'] + if mode == 'Inventory': + self.track = doneStatus['track'] + self.level = doneStatus['level'] + self.toonPanels[self.localNum].setValues(self.localNum, self.track, self.level) + if self.track == HEAL_TRACK: + if self.__isGroupHeal(self.level): + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.target + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + elif self.numToons == 3 or self.numToons == 4: + self.fsm.request('ChooseToon') + elif self.numToons == 2: + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + if self.localNum == 0: + response['target'] = 1 + elif self.localNum == 1: + response['target'] = 0 + else: + self.notify.error('Bad localNum value: %s' % self.localNum) + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + else: + self.notify.error('Heal was chosen when number of toons is %s' % self.numToons) + elif self.__isCogChoiceNecessary(): + self.notify.debug('choice needed') + self.fsm.request('ChooseCog') + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = -1 + messenger.send(self.battleEvent, [response]) + else: + self.notify.debug('no choice needed') + self.fsm.request('AttackWait') + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = 0 + messenger.send(self.battleEvent, [response]) + elif mode == 'Run': + self.fsm.request('Run') + elif mode == 'SOS': + self.fsm.request('SOS') + elif mode == 'Fire': + self.fsm.request('Fire') + elif mode == 'Pass': + response = {} + response['mode'] = 'Pass' + response['id'] = -1 + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + else: + self.notify.warning('unknown mode: %s' % mode) + + def checkHealTrapLure(self): + self.notify.debug('numToons: %s, numCogs: %s, lured: %s, trapped: %s' % (self.numToons, + self.numCogs, + self.luredIndices, + self.trappedIndices)) + if len(PythonUtil.union(self.trappedIndices, self.luredIndices)) == self.numCogs: + canTrap = 0 + else: + canTrap = 1 + if len(self.luredIndices) == self.numCogs: + canLure = 0 + canTrap = 0 + else: + canLure = 1 + if self.numToons == 1: + canHeal = 0 + else: + canHeal = 1 + return (canHeal, canTrap, canLure) + + def adjustCogsAndToons(self, cogs, luredIndices, trappedIndices, toons): + numCogs = len(cogs) + self.notify.debug('adjustCogsAndToons() numCogs: %s self.numCogs: %s' % (numCogs, self.numCogs)) + self.notify.debug('adjustCogsAndToons() luredIndices: %s self.luredIndices: %s' % (luredIndices, self.luredIndices)) + self.notify.debug('adjustCogsAndToons() trappedIndices: %s self.trappedIndices: %s' % (trappedIndices, self.trappedIndices)) + toonIds = map(lambda toon: toon.doId, toons) + self.notify.debug('adjustCogsAndToons() toonIds: %s self.toons: %s' % (toonIds, self.toons)) + maxSuitLevel = 0 + cogFireCostIndex = 0 + for cog in cogs: + maxSuitLevel = max(maxSuitLevel, cog.getActualLevel()) + self.cogFireCosts[cogFireCostIndex] = 1 + cogFireCostIndex += 1 + + creditLevel = maxSuitLevel + if numCogs == self.numCogs and creditLevel == self.creditLevel and luredIndices == self.luredIndices and trappedIndices == self.trappedIndices and toonIds == self.toons: + resetActivateMode = 0 + else: + resetActivateMode = 1 + self.notify.debug('adjustCogsAndToons() resetActivateMode: %s' % resetActivateMode) + self.numCogs = numCogs + self.creditLevel = creditLevel + self.luredIndices = luredIndices + self.trappedIndices = trappedIndices + self.toons = toonIds + self.numToons = len(toons) + self.localNum = toons.index(base.localAvatar) + currStateName = self.fsm.getCurrentState().getName() + if resetActivateMode: + self.__enterPanels(self.numToons, self.localNum) + for i in range(len(toons)): + self.toonPanels[i].setLaffMeter(toons[i]) + + if currStateName == 'ChooseCog': + self.chooseCogPanel.adjustCogs(self.numCogs, self.luredIndices, self.trappedIndices, self.track) + elif currStateName == 'ChooseToon': + self.chooseToonPanel.adjustToons(self.numToons, self.localNum) + canHeal, canTrap, canLure = self.checkHealTrapLure() + base.localAvatar.inventory.setBattleCreditMultiplier(self.creditMultiplier) + base.localAvatar.inventory.setActivateMode('battle', heal=canHeal, trap=canTrap, lure=canLure, bldg=self.bldg, creditLevel=self.creditLevel, tutorialFlag=self.tutorialFlag) + + def enterChooseCog(self): + self.cog = 0 + self.chooseCogPanel.enter(self.numCogs, luredIndices=self.luredIndices, trappedIndices=self.trappedIndices, track=self.track) + self.accept(self.chooseCogPanelDoneEvent, self.__handleChooseCogPanelDone) + return None + + def exitChooseCog(self): + self.ignore(self.chooseCogPanelDoneEvent) + self.chooseCogPanel.exit() + return None + + def __handleChooseCogPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('Attack') + elif mode == 'Avatar': + self.cog = doneStatus['avatar'] + self.target = self.cog + self.fsm.request('AttackWait') + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.cog + messenger.send(self.battleEvent, [response]) + else: + self.notify.warning('unknown mode: %s' % mode) + + def enterAttackWait(self, chosenToon = -1): + self.accept(self.waitPanelDoneEvent, self.__handleAttackWaitBack) + self.waitPanel.enter(self.numToons) + + def exitAttackWait(self): + self.waitPanel.exit() + self.ignore(self.waitPanelDoneEvent) + + def __handleAttackWaitBack(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + if self.track == HEAL_TRACK: + self.fsm.request('Attack') + elif self.track == BattleBase.NO_ATTACK: + self.fsm.request('Attack') + elif self.__isCogChoiceNecessary(): + self.fsm.request('ChooseCog') + else: + self.fsm.request('Attack') + response = {} + response['mode'] = 'UnAttack' + messenger.send(self.battleEvent, [response]) + else: + self.notify.error('unknown mode: %s' % mode) + + def enterChooseToon(self): + self.toon = 0 + self.chooseToonPanel.enter(self.numToons, localNum=self.localNum) + self.accept(self.chooseToonPanelDoneEvent, self.__handleChooseToonPanelDone) + return None + + def exitChooseToon(self): + self.ignore(self.chooseToonPanelDoneEvent) + self.chooseToonPanel.exit() + return None + + def __handleChooseToonPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('Attack') + elif mode == 'Avatar': + self.toon = doneStatus['avatar'] + self.target = self.toon + self.fsm.request('AttackWait', [self.toon]) + response = {} + response['mode'] = 'Attack' + response['track'] = self.track + response['level'] = self.level + response['target'] = self.toon + messenger.send(self.battleEvent, [response]) + else: + self.notify.warning('unknown mode: %s' % mode) + + def enterRun(self): + self.runPanel.show() + + def exitRun(self): + self.runPanel.hide() + + def __handleRunPanelDone(self, doneStatus): + if doneStatus == DGG.DIALOG_OK: + response = {} + response['mode'] = 'Run' + messenger.send(self.battleEvent, [response]) + else: + self.fsm.request('Attack') + + def enterFire(self): + canHeal, canTrap, canLure = self.checkHealTrapLure() + self.FireCogPanel.enter(self.numCogs, luredIndices=self.luredIndices, trappedIndices=self.trappedIndices, track=self.track, fireCosts=self.cogFireCosts) + self.accept(self.fireCogPanelDoneEvent, self.__handleCogFireDone) + return None + + def exitFire(self): + self.ignore(self.fireCogPanelDoneEvent) + self.FireCogPanel.exit() + return None + + def __handleCogFireDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('Attack') + elif mode == 'Avatar': + self.cog = doneStatus['avatar'] + self.target = self.cog + self.fsm.request('AttackWait') + response = {} + response['mode'] = 'Fire' + response['target'] = self.cog + messenger.send(self.battleEvent, [response]) + else: + self.notify.warning('unknown mode: %s' % mode) + + def enterSOS(self): + canHeal, canTrap, canLure = self.checkHealTrapLure() + self.SOSPanel.enter(canLure, canTrap) + self.accept(self.SOSPanelDoneEvent, self.__handleSOSPanelDone) + return None + + def exitSOS(self): + self.ignore(self.SOSPanelDoneEvent) + self.SOSPanel.exit() + return None + + def __handleSOSPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Friend': + doId = doneStatus['friend'] + response = {} + response['mode'] = 'SOS' + response['id'] = doId + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + elif mode == 'Pet': + self.petId = doneStatus['petId'] + self.petName = doneStatus['petName'] + self.fsm.request('SOSPetSearch') + elif mode == 'NPCFriend': + doId = doneStatus['friend'] + response = {} + response['mode'] = 'NPCSOS' + response['id'] = doId + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + elif mode == 'Back': + self.fsm.request('Attack') + + def enterSOSPetSearch(self): + response = {} + response['mode'] = 'PETSOSINFO' + response['id'] = self.petId + self.SOSPetSearchPanel.enter(self.petId, self.petName) + self.proxyGenerateMessage = 'petProxy-%d-generated' % self.petId + self.accept(self.proxyGenerateMessage, self.__handleProxyGenerated) + self.accept(self.SOSPetSearchPanelDoneEvent, self.__handleSOSPetSearchPanelDone) + messenger.send(self.battleEvent, [response]) + return None + + def exitSOSPetSearch(self): + self.ignore(self.proxyGenerateMessage) + self.ignore(self.SOSPetSearchPanelDoneEvent) + self.SOSPetSearchPanel.exit() + return None + + def __handleSOSPetSearchPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'Back': + self.fsm.request('SOS') + else: + self.notify.error('invalid mode in handleSOSPetSearchPanelDone') + + def __handleProxyGenerated(self): + self.fsm.request('SOSPetInfo') + + def enterSOSPetInfo(self): + self.SOSPetInfoPanel.enter(self.petId) + self.accept(self.SOSPetInfoPanelDoneEvent, self.__handleSOSPetInfoPanelDone) + return None + + def exitSOSPetInfo(self): + self.ignore(self.SOSPetInfoPanelDoneEvent) + self.SOSPetInfoPanel.exit() + return None + + def __handleSOSPetInfoPanelDone(self, doneStatus): + mode = doneStatus['mode'] + if mode == 'OK': + response = {} + response['mode'] = 'PETSOS' + response['id'] = self.petId + response['trickId'] = doneStatus['trickId'] + messenger.send(self.battleEvent, [response]) + self.fsm.request('AttackWait') + bboard.post(PetConstants.OurPetsMoodChangedKey, True) + elif mode == 'Back': + self.fsm.request('SOS') + + def __isCogChoiceNecessary(self): + if self.numCogs > 1 and not self.__isGroupAttack(self.track, self.level): + return 1 + else: + return 0 + + def __isGroupAttack(self, trackNum, levelNum): + retval = BattleBase.attackAffectsGroup(trackNum, levelNum) + return retval + + def __isGroupHeal(self, levelNum): + retval = BattleBase.attackAffectsGroup(HEAL_TRACK, levelNum) + return retval diff --git a/toontown/town/TownBattleAttackPanel.py b/toontown/town/TownBattleAttackPanel.py new file mode 100644 index 0000000..789ff16 --- /dev/null +++ b/toontown/town/TownBattleAttackPanel.py @@ -0,0 +1,86 @@ +from pandac.PandaModules import * +from direct.directnotify import DirectNotifyGlobal +import string +from direct.fsm import StateData +from toontown.toontowngui.TeaserPanel import TeaserPanel +from toontown.toonbase.ToontownBattleGlobals import gagIsPaidOnly +AttackPanelHidden = 0 + +def hideAttackPanel(flag): + global AttackPanelHidden + AttackPanelHidden = flag + messenger.send('hide-attack-panel') + + +class TownBattleAttackPanel(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattleAttackPanel') + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + StateData.StateData.load(self) + + def unload(self): + StateData.StateData.unload(self) + + def enter(self): + StateData.StateData.enter(self) + self._teaserPanel = None + if not AttackPanelHidden: + base.localAvatar.inventory.show() + self.accept('inventory-selection', self.__handleInventory) + self.accept('inventory-run', self.__handleRun) + self.accept('inventory-sos', self.__handleSOS) + self.accept('inventory-pass', self.__handlePass) + self.accept('inventory-fire', self.__handleFire) + self.accept('hide-attack-panel', self.__handleHide) + return + + def exit(self): + StateData.StateData.exit(self) + self.ignore('inventory-selection') + self.ignore('inventory-run') + self.ignore('inventory-sos') + self.ignore('inventory-pass') + self.ignore('inventory-fire') + self.ignore('hide-attack-panel') + base.localAvatar.inventory.hide() + if self._teaserPanel: + self._teaserPanel.destroy() + del self._teaserPanel + + def __handleRun(self): + doneStatus = {'mode': 'Run'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleSOS(self): + doneStatus = {'mode': 'SOS'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handlePass(self): + doneStatus = {'mode': 'Pass'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleFire(self): + doneStatus = {'mode': 'Fire'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleInventory(self, track, level): + if not base.cr.isPaid() and gagIsPaidOnly(track, level): + self._teaserPanel = TeaserPanel(pageName='useGags') + return + if base.localAvatar.inventory.numItem(track, level) > 0: + doneStatus = {} + doneStatus['mode'] = 'Inventory' + doneStatus['track'] = track + doneStatus['level'] = level + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error("An item we don't have: track %s level %s was selected." % [track, level]) + + def __handleHide(self): + if AttackPanelHidden: + base.localAvatar.inventory.hide() + else: + base.localAvatar.inventory.show() diff --git a/toontown/town/TownBattleChooseAvatarPanel.py b/toontown/town/TownBattleChooseAvatarPanel.py new file mode 100644 index 0000000..f252902 --- /dev/null +++ b/toontown/town/TownBattleChooseAvatarPanel.py @@ -0,0 +1,114 @@ +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 TownBattleChooseAvatarPanel(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('ChooseAvatarPanel') + + def __init__(self, doneEvent, toon): + self.notify.debug('Init choose panel...') + StateData.StateData.__init__(self, doneEvent) + self.numAvatars = 0 + self.chosenAvatar = 0 + self.toon = toon + + 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), text='', text_fg=Vec4(0, 0, 0, 1), text_pos=(0, -0.025, 0), text_scale=0.08, pos=(-0.013, 0, 0.013)) + if self.toon: + self.textFrame['text'] = TTLocalizer.TownBattleChooseAvatarToonTitle + else: + self.textFrame['text'] = TTLocalizer.TownBattleChooseAvatarCogTitle + self.avatarButtons = [] + for i in range(4): + button = DirectButton(parent=self.frame, relief=None, image=(gui.find('**/PckMn_Arrow_Up'), gui.find('**/PckMn_Arrow_Dn'), gui.find('**/PckMn_Arrow_Rlvr')), command=self.__handleAvatar, extraArgs=[i]) + if self.toon: + button.setScale(1, 1, -1) + button.setPos(0, 0, -0.2) + else: + 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() + return + + def unload(self): + self.frame.destroy() + del self.frame + del self.statusFrame + del self.textFrame + del self.avatarButtons + del self.backButton + + def enter(self, numAvatars, localNum = None, luredIndices = None, trappedIndices = None, track = None): + 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) + + 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): + for i in range(4): + if numAvatars > i and i not in invalidTargets and i != localNum: + self.avatarButtons[i].show() + else: + self.avatarButtons[i].hide() + + 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/town/TownBattleSOSPanel.py b/toontown/town/TownBattleSOSPanel.py new file mode 100644 index 0000000..546042b --- /dev/null +++ b/toontown/town/TownBattleSOSPanel.py @@ -0,0 +1,222 @@ +from pandac.PandaModules import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from direct.showbase import DirectObject +from direct.directnotify import DirectNotifyGlobal +from direct.fsm import StateData +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +import types +from toontown.toon import NPCToons +from toontown.toon import NPCFriendPanel +from toontown.toonbase import ToontownBattleGlobals + +class TownBattleSOSPanel(DirectFrame, StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattleSOSPanel') + + def __init__(self, doneEvent): + DirectFrame.__init__(self, relief=None) + self.initialiseoptions(TownBattleSOSPanel) + StateData.StateData.__init__(self, doneEvent) + self.friends = {} + self.NPCFriends = {} + 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.bldg = 0 + self.chosenNPCToons = [] + return + + def load(self): + if self.isLoaded == 1: + return None + self.isLoaded = 1 + bgd = loader.loadModel('phase_3.5/models/gui/frame') + gui = loader.loadModel('phase_3.5/models/gui/frame4names') + scrollGui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') + backGui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self['image'] = bgd + self['image_pos'] = (0.0, 0.1, -0.08) + self.setScale(0.3) + self.title = DirectLabel(parent=self, relief=None, text=TTLocalizer.TownBattleSOSNoFriends, text_scale=0.4, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(0.0, 0.0, 1.5)) + self.NPCFriendPanel = NPCFriendPanel.NPCFriendPanel(parent=self, doneEvent=self.doneEvent) + self.NPCFriendPanel.setPos(-0.75, 0, -0.15) + self.NPCFriendPanel.setScale(0.325) + self.NPCFriendsLabel = DirectLabel(parent=self, relief=None, text=TTLocalizer.TownBattleSOSNPCFriends, text_scale=0.3, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(-0.75, 0.0, -2.0)) + self.scrollList = DirectScrolledList(parent=self, relief=None, image=gui.find('**/frame4names'), image_scale=(0.11, 1, 0.1), text=TTLocalizer.FriendsListPanelOnlineFriends, text_scale=0.04, text_pos=(-0.02, 0.275), text_fg=(0, 0, 0, 1), incButton_image=(scrollGui.find('**/FndsLst_ScrollUp'), + scrollGui.find('**/FndsLst_ScrollDN'), + scrollGui.find('**/FndsLst_ScrollUp_Rllvr'), + scrollGui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_pos=(0.0, 0.0, -0.3), incButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), incButton_scale=(1.0, 1.0, -1.0), decButton_image=(scrollGui.find('**/FndsLst_ScrollUp'), + scrollGui.find('**/FndsLst_ScrollDN'), + scrollGui.find('**/FndsLst_ScrollUp_Rllvr'), + scrollGui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_pos=(0.0, 0.0, 0.175), decButton_image3_color=Vec4(0.6, 0.6, 0.6, 0.6), itemFrame_pos=(-0.17, 0.0, 0.11), itemFrame_relief=None, numItemsVisible=9, items=[], pos=(2.4, 0.0, 0.025), scale=3.5) + clipper = PlaneNode('clipper') + clipper.setPlane(Plane(Vec3(-1, 0, 0), Point3(0.32, 0, 0))) + clipNP = self.scrollList.component('itemFrame').attachNewNode(clipper) + self.scrollList.component('itemFrame').setClipPlane(clipNP) + self.close = DirectButton(parent=self, relief=None, image=(backGui.find('**/PckMn_BackBtn'), backGui.find('**/PckMn_BackBtn_Dn'), backGui.find('**/PckMn_BackBtn_Rlvr')), pos=(2.3, 0.0, -1.65), scale=3, text=TTLocalizer.TownBattleSOSBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__close) + gui.removeNode() + scrollGui.removeNode() + backGui.removeNode() + bgd.removeNode() + self.hide() + return + + def unload(self): + if self.isLoaded == 0: + return None + self.isLoaded = 0 + self.exit() + del self.title + del self.scrollList + del self.close + del self.friends + del self.NPCFriends + DirectFrame.destroy(self) + return None + + def makeFriendButton(self, friendPair): + friendId, flags = friendPair + handle = base.cr.playerFriendsManager.identifyFriend(friendId) + if handle == None: + base.cr.fillUpFriendsMap() + return + friendName = handle.getName() + fg = Vec4(0.0, 0.0, 0.0, 1.0) + if handle.isPet(): + com = self.__chosePet + else: + com = self.__choseFriend + return DirectButton(relief=None, text=friendName, text_scale=0.04, text_align=TextNode.ALeft, text_fg=fg, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, command=com, extraArgs=[friendId, friendName]) + + def makeNPCFriendButton(self, NPCFriendId, numCalls): + if not TTLocalizer.NPCToonNames.has_key(NPCFriendId): + return None + friendName = TTLocalizer.NPCToonNames[NPCFriendId] + friendName += ' %d' % numCalls + fg = Vec4(0.0, 0.0, 0.0, 1.0) + return DirectButton(relief=None, text=friendName, text_scale=0.04, text_align=TextNode.ALeft, text_fg=fg, text1_bg=self.textDownColor, text2_bg=self.textRolloverColor, text3_fg=self.textDisabledColor, command=self.__choseNPCFriend, extraArgs=[NPCFriendId]) + + def enter(self, canLure = 1, canTrap = 1): + if self.isEntered == 1: + return None + self.isEntered = 1 + if self.isLoaded == 0: + self.load() + self.canLure = canLure + self.canTrap = canTrap + self.factoryToonIdList = None + messenger.send('SOSPanelEnter', [self]) + self.__updateScrollList() + self.__updateNPCFriendsPanel() + self.__updateTitleText() + self.show() + self.accept('friendOnline', self.__friendOnline) + self.accept('friendOffline', self.__friendOffline) + self.accept('friendsListChanged', self.__friendsListChanged) + self.accept('friendsMapComplete', self.__friendsListChanged) + return + + def exit(self): + if self.isEntered == 0: + return None + self.isEntered = 0 + self.hide() + self.ignore('friendOnline') + self.ignore('friendOffline') + self.ignore('friendsListChanged') + self.ignore('friendsMapComplete') + messenger.send(self.doneEvent) + return None + + def __close(self): + doneStatus = {} + doneStatus['mode'] = 'Back' + messenger.send(self.doneEvent, [doneStatus]) + + def __choseFriend(self, friendId, friendName): + doneStatus = {} + doneStatus['mode'] = 'Friend' + doneStatus['friend'] = friendId + messenger.send(self.doneEvent, [doneStatus]) + + def __chosePet(self, petId, petName): + doneStatus = {} + doneStatus['mode'] = 'Pet' + doneStatus['petId'] = petId + doneStatus['petName'] = petName + messenger.send(self.doneEvent, [doneStatus]) + + def __choseNPCFriend(self, friendId): + doneStatus = {} + doneStatus['mode'] = 'NPCFriend' + doneStatus['friend'] = friendId + self.chosenNPCToons.append(friendId) + messenger.send(self.doneEvent, [doneStatus]) + + def setFactoryToonIdList(self, toonIdList): + self.factoryToonIdList = toonIdList[:] + + def __updateScrollList(self): + newFriends = [] + battlePets = base.config.GetBool('want-pets-in-battle', 1) + if base.wantPets and battlePets == 1 and base.localAvatar.hasPet(): + newFriends.append((base.localAvatar.getPetId(), 0)) + if not self.bldg or self.factoryToonIdList is not None: + for friendPair in base.localAvatar.friendsList: + if base.cr.isFriendOnline(friendPair[0]): + if self.factoryToonIdList is None or friendPair[0] in self.factoryToonIdList: + newFriends.append(friendPair) + + if hasattr(base.cr, 'playerFriendsManager'): + for avatarId in base.cr.playerFriendsManager.getAllOnlinePlayerAvatars(): + if not base.cr.playerFriendsManager.askAvatarKnownElseWhere(avatarId): + newFriends.append((avatarId, 0)) + + for friendPair in self.friends.keys(): + if friendPair not in newFriends: + friendButton = self.friends[friendPair] + self.scrollList.removeItem(friendButton) + if not friendButton.isEmpty(): + friendButton.destroy() + del self.friends[friendPair] + + for friendPair in newFriends: + if not self.friends.has_key(friendPair): + friendButton = self.makeFriendButton(friendPair) + if friendButton: + self.scrollList.addItem(friendButton) + self.friends[friendPair] = friendButton + + return + + def __updateNPCFriendsPanel(self): + self.NPCFriends = {} + for friend, count in base.localAvatar.NPCFriendsDict.items(): + track = NPCToons.getNPCTrack(friend) + if track == ToontownBattleGlobals.LURE_TRACK and self.canLure == 0 or track == ToontownBattleGlobals.TRAP_TRACK and self.canTrap == 0: + self.NPCFriends[friend] = 0 + else: + self.NPCFriends[friend] = count + + self.NPCFriendPanel.update(self.NPCFriends, fCallable=1) + + def __updateTitleText(self): + isEmpty = (len(self.friends) == 0 and len(self.NPCFriends) == 0) + if isEmpty: + self.title['text'] = TTLocalizer.TownBattleSOSNoFriends + else: + self.title['text'] = TTLocalizer.TownBattleSOSWhichFriend + + def __friendOnline(self, doId, commonChatFlags, whitelistChatFlags): + self.__updateScrollList() + self.__updateTitleText() + + def __friendOffline(self, doId): + self.__updateScrollList() + self.__updateTitleText() + + def __friendsListChanged(self): + self.__updateScrollList() + self.__updateTitleText() diff --git a/toontown/town/TownBattleSOSPetInfoPanel.py b/toontown/town/TownBattleSOSPetInfoPanel.py new file mode 100644 index 0000000..25e6761 --- /dev/null +++ b/toontown/town/TownBattleSOSPetInfoPanel.py @@ -0,0 +1,160 @@ +from pandac.PandaModules import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer +from toontown.pets import Pet, PetTricks, PetDetailPanel +from toontown.speedchat import TTSCPetTrickMenu +from otp.speedchat import SpeedChatGlobals, SCSettings +from otp.otpbase import OTPLocalizer + +class TownBattleSOSPetInfoPanel(StateData.StateData): + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/PetControlPannel') + guiScale = 0.116 + guiPos = (0, 0, 0) + self.frame = DirectFrame(image=gui, scale=guiScale, pos=guiPos, relief=None) + self.frame.hide() + disabledImageColor = Vec4(0.6, 0.6, 0.6, 1) + text0Color = Vec4(1, 1, 1, 1) + text1Color = Vec4(0.5, 1, 0.5, 1) + text2Color = Vec4(1, 1, 0.5, 1) + text3Color = Vec4(0.6, 0.6, 0.6, 1) + self.closeButton = DirectButton(parent=self.frame, image=(gui.find('**/CancelButtonUp'), gui.find('**/CancelButtonDown'), gui.find('**/CancelButtonRollover')), relief=None, command=self.__handleClose) + self.feedButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonFeedUp'), + gui.find('**/ButtonFeedDown'), + gui.find('**/ButtonFeedRollover'), + gui.find('**/ButtonFeedUp')), geom=gui.find('**/PetControlFeedIcon'), image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelFeed, text_scale=0.5, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_pos=(-0.5, 2.8), text_align=TextNode.ALeft) + self.feedButton['state'] = DGG.DISABLED + self.callButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonGoToUp'), + gui.find('**/ButtonGoToDown'), + gui.find('**/ButtonGoToRollover'), + gui.find('**/ButtonGoToUp')), geom=gui.find('**/PetControlGoToIcon'), image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelCall, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=0.5, text_pos=(-0.5, 1.3), text_align=TextNode.ALeft) + self.callButton['state'] = DGG.DISABLED + self.scratchButton = DirectButton(parent=self.frame, image=(gui.find('**/ButtonScratchUp'), + gui.find('**/ButtonScratchDown'), + gui.find('**/ButtonScratchRollover'), + gui.find('**/ButtonScratchUp')), geom=gui.find('**/PetControlScratchIcon'), image3_color=disabledImageColor, relief=None, text=TTLocalizer.PetPanelScratch, text0_fg=text0Color, text1_fg=text1Color, text2_fg=text2Color, text3_fg=text3Color, text_scale=0.5, text_pos=(-0.5, 2.05), text_align=TextNode.ALeft) + self.scratchButton['state'] = DGG.DISABLED + self.callOwnerButton = DirectButton(parent=self.frame, image=(gui.find('**/PetControlToonButtonUp'), gui.find('**/PetControlToonButtonDown'), gui.find('**/PetControlToonButtonRollover')), geom=gui.find('**/PetControlToonIcon'), geom3_color=disabledImageColor, relief=None, image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelOwner, + TTLocalizer.PetPanelOwner, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.35, text_pos=(0.3, 1.1), text_align=TextNode.ACenter, command=self.__handleDetail) + self.callOwnerButton['state'] = DGG.DISABLED + self.detailButton = DirectButton(parent=self.frame, image=(gui.find('**/PetControlToonButtonUp1'), gui.find('**/PetControlToonButtonDown1'), gui.find('**/PetControlToonButtonRollover1')), geom=gui.find('**/PetBattleIcon'), geom3_color=disabledImageColor, relief=None, pos=(0, 0, 0), image3_color=disabledImageColor, text=('', + TTLocalizer.PetPanelDetail, + TTLocalizer.PetPanelDetail, + ''), text_fg=text2Color, text_shadow=(0, 0, 0, 1), text_scale=0.35, text_pos=(0.3, 1.1), text_align=TextNode.ACenter, command=self.__handleDetail) + self.detailButton['state'] = DGG.NORMAL + gui.removeNode() + self.nameLabel = None + self.trickMenu = TTSCPetTrickMenu.TTSCPetTrickMenu() + self.settings = SCSettings.SCSettings(eventPrefix='') + self.trickMenu.privSetSettingsRef(self.settings) + self.trickMenuEventName = self.trickMenu.getEventName(SpeedChatGlobals.SCStaticTextMsgEvent) + self.trickMenu.setScale(0.055) + self.trickMenu.setBin('gui-popup', 0) + self.trickMenu.finalizeAll() + localAvatar.chatMgr.chatInputSpeedChat.whisperAvatarId = None + self.petDetailPanel = None + return + + def unload(self): + self.frame.destroy() + del self.frame + self.frame = None + if hasattr(self, 'petView'): + self.petView.removeNode() + del self.petView + if hasattr(self, 'petModel'): + self.petModel.delete() + del self.petModel + del self.closeButton + del self.feedButton + del self.callButton + del self.scratchButton + del self.callOwnerButton + del self.detailButton + self.trickMenu.destroy() + del self.trickMenu + del self.petDetailPanel + return + + def enter(self, petProxyId): + self.petProxyId = petProxyId + if not base.cr.doId2do.has_key(petProxyId): + self.notify.warning('petProxyId %s not in doId2do!' % petProxyId) + return + self.petProxy = base.cr.doId2do[petProxyId] + self.__fillPetInfo(self.petProxy) + self.frame.show() + self.accept(self.trickMenuEventName, self.__handleTrickMenuEvent) + self.trickMenu.reparentTo(aspect2dp, DGG.FOREGROUND_SORT_INDEX) + localAvatar.chatMgr.chatInputSpeedChat.whisperAvatarId = None + self.detailButton['state'] = DGG.NORMAL + return + + def exit(self): + self.ignore(self.trickMenuEventName) + self.trickMenu.reparentTo(hidden) + self.petProxy = None + if self.petDetailPanel != None: + self.petDetailPanel.cleanup() + self.petDetailPanel = None + self.frame.hide() + return + + def __handleTrickMenuEvent(self, textId): + if PetTricks.ScId2trickId.has_key(textId): + trickId = PetTricks.ScId2trickId[textId] + doneStatus = {'mode': 'OK', + 'trickId': trickId} + messenger.send(self.doneEvent, [doneStatus]) + self.detailButton['state'] = DGG.NORMAL + + def __handleClose(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleCall(self): + doneStatus = {'mode': 'OK', + 'trickId': 0} + messenger.send(self.doneEvent, [doneStatus]) + + def __handleDetailDone(self): + if self.petDetailPanel != None: + self.petDetailPanel.cleanup() + self.petDetailPanel = None + self.detailButton['state'] = DGG.NORMAL + return + + def __handleDetail(self): + self.petDetailPanel = PetDetailPanel.PetDetailPanel(pet=self.petProxy, closeCallback=self.__handleDetailDone, parent=self.frame) + self.detailButton['state'] = DGG.DISABLED + + def __fillPetInfo(self, avatar): + self.notify.debug('__fillPetInfo(): doId=%s' % avatar.doId) + if self.nameLabel == None: + self.petView = self.frame.attachNewNode('petView') + self.petView.setPos(0, 0, 5.4) + self.petModel = Pet.Pet(forGui=1) + self.petModel.setDNA(avatar.getDNA()) + self.petModel.fitAndCenterHead(3.575, forGui=1) + self.petModel.reparentTo(self.petView) + self.petModel.enterNeutralHappy() + self.petModel.startBlink() + self.nameLabel = DirectLabel(parent=self.frame, pos=(0, 0, 5.2), relief=None, text=avatar.getName(), text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_pos=(0, 0), text_scale=0.4, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.stateLabel = DirectLabel(parent=self.frame, pos=(0.7, 0, 3.5), relief=None, text='', text_font=avatar.getFont(), text_fg=Vec4(0, 0, 0, 1), text_scale=0.4, text_wordwrap=7.5, text_shadow=(1, 1, 1, 1)) + self.__refreshPetInfo(avatar) + return + + def __refreshPetInfo(self, avatar): + self.notify.debug('__refreshPetInfo(): doId=%s' % avatar.doId) + avatar.updateOfflineMood() + mood = avatar.getDominantMood() + self.stateLabel['text'] = TTLocalizer.PetMoodAdjectives[mood] + self.nameLabel['text'] = avatar.getName() diff --git a/toontown/town/TownBattleSOSPetSearchPanel.py b/toontown/town/TownBattleSOSPetSearchPanel.py new file mode 100644 index 0000000..dfcb128 --- /dev/null +++ b/toontown/town/TownBattleSOSPetSearchPanel.py @@ -0,0 +1,38 @@ +from pandac.PandaModules import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer + +class TownBattleSOSPetSearchPanel(StateData.StateData): + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.frame = DirectFrame(relief=None, image=gui.find('**/Waiting4Others'), text_align=TextNode.ALeft, pos=(0, 0, 0), scale=0.65) + self.frame.hide() + 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.011), scale=1.05, text=TTLocalizer.TownBattleWaitBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__handleBack) + gui.removeNode() + return + + def unload(self): + self.frame.destroy() + del self.frame + del self.backButton + + def enter(self, petId, petName): + self.petId = petId + self.petName = petName + self.frame['text'] = TTLocalizer.TownBattleSOSPetSearchTitle % petName + self.frame['text_pos'] = (0, 0.01, 0) + self.frame['text_scale'] = TTLocalizer.TBSOSPSPenter + self.frame.show() + + def exit(self): + self.frame.hide() + + def __handleBack(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) diff --git a/toontown/town/TownBattleToonPanel.py b/toontown/town/TownBattleToonPanel.py new file mode 100644 index 0000000..26387bf --- /dev/null +++ b/toontown/town/TownBattleToonPanel.py @@ -0,0 +1,163 @@ +from pandac.PandaModules import * +from toontown.toonbase import ToontownGlobals +from toontown.toonbase.ToontownBattleGlobals import * +from direct.directnotify import DirectNotifyGlobal +import string +from toontown.toon import LaffMeter +from toontown.battle import BattleBase +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer + +class TownBattleToonPanel(DirectFrame): + notify = DirectNotifyGlobal.directNotify.newCategory('TownBattleToonPanel') + + def __init__(self, id): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + DirectFrame.__init__(self, relief=None, image=gui.find('**/ToonBtl_Status_BG'), image_color=Vec4(0.5, 0.9, 0.5, 0.7)) + self.setScale(0.8) + self.initialiseoptions(TownBattleToonPanel) + self.avatar = None + self.sosText = DirectLabel(parent=self, relief=None, pos=(0.1, 0, 0.015), text=TTLocalizer.TownBattleToonSOS, text_scale=0.06) + self.sosText.hide() + self.fireText = DirectLabel(parent=self, relief=None, pos=(0.1, 0, 0.015), text=TTLocalizer.TownBattleToonFire, text_scale=0.06) + self.fireText.hide() + self.undecidedText = DirectLabel(parent=self, relief=None, pos=(0.1, 0, 0.015), text=TTLocalizer.TownBattleUndecided, text_scale=0.1) + self.healthText = DirectLabel(parent=self, text='', pos=(-0.06, 0, -0.075), text_scale=0.055) + self.hpChangeEvent = None + self.gagNode = self.attachNewNode('gag') + self.gagNode.setPos(0.1, 0, 0.03) + self.hasGag = 0 + passGui = gui.find('**/tt_t_gui_bat_pass') + passGui.detachNode() + self.passNode = self.attachNewNode('pass') + self.passNode.setPos(0.1, 0, 0.05) + passGui.setScale(0.2) + passGui.reparentTo(self.passNode) + self.passNode.hide() + self.laffMeter = None + self.whichText = DirectLabel(parent=self, text='', pos=(0.1, 0, -0.08), text_scale=0.05) + self.hide() + gui.removeNode() + return + + def setLaffMeter(self, avatar): + self.notify.debug('setLaffMeter: new avatar %s' % avatar.doId) + if self.avatar == avatar: + messenger.send(self.avatar.uniqueName('hpChange'), [avatar.hp, avatar.maxHp, 1]) + return None + else: + if self.avatar: + self.cleanupLaffMeter() + self.avatar = avatar + self.laffMeter = LaffMeter.LaffMeter(avatar.style, avatar.hp, avatar.maxHp) + self.laffMeter.setAvatar(self.avatar) + self.laffMeter.reparentTo(self) + self.laffMeter.setPos(-0.06, 0, 0.05) + self.laffMeter.setScale(0.045) + self.laffMeter.start() + self.setHealthText(avatar.hp, avatar.maxHp) + self.hpChangeEvent = self.avatar.uniqueName('hpChange') + self.accept(self.hpChangeEvent, self.setHealthText) + return None + + def setHealthText(self, hp, maxHp, quietly = 0): + self.healthText['text'] = TTLocalizer.TownBattleHealthText % {'hitPoints': hp, + 'maxHit': maxHp} + + def show(self): + DirectFrame.show(self) + if self.laffMeter: + self.laffMeter.start() + + def hide(self): + DirectFrame.hide(self) + if self.laffMeter: + self.laffMeter.stop() + + def updateLaffMeter(self, hp): + if self.laffMeter: + self.laffMeter.adjustFace(hp, self.avatar.maxHp) + self.setHealthText(hp, maxHp) + + def setValues(self, index, track, level = None, numTargets = None, targetIndex = None, localNum = None): + self.notify.debug('Toon Panel setValues: index=%s track=%s level=%s numTargets=%s targetIndex=%s localNum=%s' % (index, + track, + level, + numTargets, + targetIndex, + localNum)) + self.undecidedText.hide() + self.sosText.hide() + self.fireText.hide() + self.gagNode.hide() + self.whichText.hide() + self.passNode.hide() + if self.hasGag: + self.gag.removeNode() + self.hasGag = 0 + if track == BattleBase.NO_ATTACK or track == BattleBase.UN_ATTACK: + self.undecidedText.show() + elif track == BattleBase.PASS_ATTACK: + self.passNode.show() + elif track == BattleBase.FIRE: + self.fireText.show() + self.whichText.show() + self.whichText['text'] = self.determineWhichText(numTargets, targetIndex, localNum, index) + elif track == BattleBase.SOS or track == BattleBase.NPCSOS or track == BattleBase.PETSOS: + self.sosText.show() + elif track >= MIN_TRACK_INDEX and track <= MAX_TRACK_INDEX: + self.undecidedText.hide() + self.passNode.hide() + self.gagNode.show() + invButton = base.localAvatar.inventory.buttonLookup(track, level) + self.gag = invButton.instanceUnderNode(self.gagNode, 'gag') + self.gag.setScale(0.8) + self.gag.setPos(0, 0, 0.02) + self.hasGag = 1 + if numTargets is not None and targetIndex is not None and localNum is not None: + self.whichText.show() + self.whichText['text'] = self.determineWhichText(numTargets, targetIndex, localNum, index) + else: + self.notify.error('Bad track value: %s' % track) + return + + def determineWhichText(self, numTargets, targetIndex, localNum, index): + returnStr = '' + targetList = range(numTargets) + targetList.reverse() + for i in targetList: + if targetIndex == -1: + returnStr += 'X' + elif targetIndex == -2: + if i == index: + returnStr += '-' + else: + returnStr += 'X' + elif targetIndex >= 0 and targetIndex <= 3: + if i == targetIndex: + returnStr += 'X' + else: + returnStr += '-' + else: + self.notify.error('Bad target index: %s' % targetIndex) + + return returnStr + + def cleanup(self): + self.ignoreAll() + self.cleanupLaffMeter() + if self.hasGag: + self.gag.removeNode() + del self.gag + self.gagNode.removeNode() + del self.gagNode + DirectFrame.destroy(self) + + def cleanupLaffMeter(self): + self.notify.debug('Cleaning up laffmeter!') + self.ignore(self.hpChangeEvent) + if self.laffMeter: + self.laffMeter.destroy() + self.laffMeter = None + return diff --git a/toontown/town/TownBattleWaitPanel.py b/toontown/town/TownBattleWaitPanel.py new file mode 100644 index 0000000..1dc0444 --- /dev/null +++ b/toontown/town/TownBattleWaitPanel.py @@ -0,0 +1,41 @@ +from pandac.PandaModules import * +from direct.fsm import StateData +from direct.gui.DirectGui import * +from pandac.PandaModules import * +from toontown.toonbase import TTLocalizer + +class TownBattleWaitPanel(StateData.StateData): + + def __init__(self, doneEvent): + StateData.StateData.__init__(self, doneEvent) + + def load(self): + gui = loader.loadModel('phase_3.5/models/gui/battle_gui') + self.frame = DirectFrame(relief=None, image=gui.find('**/Waiting4Others'), text_align=TextNode.ALeft, pos=(0, 0, 0), scale=0.65) + self.frame.hide() + 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.011), scale=1.05, text=TTLocalizer.TownBattleWaitBack, text_scale=0.05, text_pos=(0.01, -0.012), text_fg=Vec4(0, 0, 0.8, 1), command=self.__handleBack) + gui.removeNode() + return + + def unload(self): + self.frame.destroy() + del self.frame + del self.backButton + + def enter(self, numParticipants): + if numParticipants > 1: + self.frame['text'] = TTLocalizer.TownBattleWaitTitle + self.frame['text_pos'] = (0, 0.01, 0) + self.frame['text_scale'] = 0.1 + else: + self.frame['text'] = TTLocalizer.TownSoloBattleWaitTitle + self.frame['text_pos'] = (0, -0.05, 0) + self.frame['text_scale'] = 0.13 + self.frame.show() + + def exit(self): + self.frame.hide() + + def __handleBack(self): + doneStatus = {'mode': 'Back'} + messenger.send(self.doneEvent, [doneStatus]) diff --git a/toontown/town/TownLoader.py b/toontown/town/TownLoader.py new file mode 100644 index 0000000..5892d42 --- /dev/null +++ b/toontown/town/TownLoader.py @@ -0,0 +1,373 @@ +from pandac.PandaModules import * +from toontown.battle.BattleProps import * +from toontown.battle.BattleSounds import * +from toontown.distributed.ToontownMsgTypes import * +from toontown.toonbase.ToontownGlobals import * +from direct.gui.DirectGui import cleanupDialog +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 direct.task import Task +import TownBattle +from toontown.toon import Toon +from toontown.toon.Toon import teleportDebug +from toontown.battle import BattleParticles +from direct.fsm import StateData +from toontown.building import ToonInterior +from toontown.hood import QuietZoneState +from toontown.hood import ZoneUtil +from direct.interval.IntervalGlobal import * + +class TownLoader(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('TownLoader') + + def __init__(self, hood, parentFSMState, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.hood = hood + self.parentFSMState = parentFSMState + self.fsm = ClassicFSM.ClassicFSM('TownLoader', [State.State('start', self.enterStart, self.exitStart, ['quietZone', 'street', 'toonInterior']), + State.State('street', self.enterStreet, self.exitStreet, ['quietZone']), + State.State('toonInterior', self.enterToonInterior, self.exitToonInterior, ['quietZone']), + State.State('quietZone', self.enterQuietZone, self.exitQuietZone, ['street', 'toonInterior']), + State.State('final', self.enterFinal, self.exitFinal, ['start'])], 'start', 'final') + self.branchZone = None + self.canonicalBranchZone = None + self.placeDoneEvent = 'placeDone' + self.townBattleDoneEvent = 'town-battle-done' + return + + def loadBattleAnims(self): + Toon.loadBattleAnims() + + def unloadBattleAnims(self): + Toon.unloadBattleAnims() + + def load(self, zoneId): + self.zoneId = zoneId + self.parentFSMState.addChild(self.fsm) + self.loadBattleAnims() + self.branchZone = ZoneUtil.getBranchZone(zoneId) + self.canonicalBranchZone = ZoneUtil.getCanonicalBranchZone(zoneId) + self.music = base.loadMusic(self.musicFile) + self.activityMusic = base.loadMusic(self.activityMusicFile) + self.battleMusic = base.loadMusic('phase_3.5/audio/bgm/encntr_general_bg.mid') + self.townBattle = TownBattle.TownBattle(self.townBattleDoneEvent) + self.townBattle.load() + + def unload(self): + self.unloadBattleAnims() + globalPropPool.unloadProps() + globalBattleSoundCache.clear() + BattleParticles.unloadParticles() + self.parentFSMState.removeChild(self.fsm) + del self.parentFSMState + del self.fsm + del self.streetClass + self.landmarkBlocks.removeNode() + del self.landmarkBlocks + self.hood.dnaStore.resetSuitPoints() + self.hood.dnaStore.resetBattleCells() + del self.hood + del self.nodeDict + del self.zoneDict + del self.fadeInDict + del self.fadeOutDict + del self.nodeList + self.geom.removeNode() + del self.geom + self.townBattle.unload() + self.townBattle.cleanup() + del self.townBattle + del self.battleMusic + del self.music + del self.activityMusic + del self.holidayPropTransforms + self.deleteAnimatedProps() + cleanupDialog('globalDialog') + ModelPool.garbageCollect() + TexturePool.garbageCollect() + + def enter(self, requestStatus): + teleportDebug(requestStatus, 'TownLoader.enter(%s)' % requestStatus) + self.fsm.enterInitialState() + teleportDebug(requestStatus, 'setting state: %s' % requestStatus['where']) + self.setState(requestStatus['where'], requestStatus) + + def exit(self): + self.ignoreAll() + + def setState(self, stateName, requestStatus): + self.fsm.request(stateName, [requestStatus]) + + def enterStart(self): + pass + + def exitStart(self): + pass + + def enterStreet(self, requestStatus): + teleportDebug(requestStatus, 'enterStreet(%s)' % requestStatus) + self.acceptOnce(self.placeDoneEvent, self.streetDone) + self.place = self.streetClass(self, self.fsm, self.placeDoneEvent) + self.place.load() + base.cr.playGame.setPlace(self.place) + self.place.enter(requestStatus) + + def exitStreet(self): + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def streetDone(self): + self.requestStatus = self.place.doneStatus + status = self.place.doneStatus + if status['loader'] == 'townLoader' and ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enterToonInterior(self, requestStatus): + self.acceptOnce(self.placeDoneEvent, self.handleToonInteriorDone) + self.place = ToonInterior.ToonInterior(self, self.fsm.getStateNamed('toonInterior'), self.placeDoneEvent) + base.cr.playGame.setPlace(self.place) + self.place.load() + self.place.enter(requestStatus) + + def exitToonInterior(self): + self.ignore(self.placeDoneEvent) + self.place.exit() + self.place.unload() + self.place = None + base.cr.playGame.setPlace(self.place) + return + + def handleToonInteriorDone(self): + status = self.place.doneStatus + if ZoneUtil.getBranchZone(status['zoneId']) == self.branchZone and status['shardId'] == None: + self.fsm.request('quietZone', [status]) + else: + self.doneStatus = status + messenger.send(self.doneEvent) + return + + def enterQuietZone(self, requestStatus): + self.quietZoneDoneEvent = uniqueName('quietZoneDone') + self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone) + self.quietZoneStateData = QuietZoneState.QuietZoneState(self.quietZoneDoneEvent) + self.quietZoneStateData.load() + self.quietZoneStateData.enter(requestStatus) + + def exitQuietZone(self): + self.ignore(self.quietZoneDoneEvent) + del self.quietZoneDoneEvent + self.quietZoneStateData.exit() + self.quietZoneStateData.unload() + self.quietZoneStateData = None + return + + def handleQuietZoneDone(self): + status = self.quietZoneStateData.getRequestStatus() + self.fsm.request(status['where'], [status]) + + def enterFinal(self): + pass + + def exitFinal(self): + pass + + def createHood(self, dnaFile, loadStorage = 1): + if loadStorage: + loader.loadDNAFile(self.hood.dnaStore, 'phase_5/dna/storage_town.dna') + self.notify.debug('done loading %s' % 'phase_5/dna/storage_town.dna') + loader.loadDNAFile(self.hood.dnaStore, self.townStorageDNAFile) + self.notify.debug('done loading %s' % self.townStorageDNAFile) + node = loader.loadDNAFile(self.hood.dnaStore, dnaFile) + self.notify.debug('done loading %s' % dnaFile) + if node.getNumParents() == 1: + self.geom = NodePath(node.getParent(0)) + self.geom.reparentTo(hidden) + else: + self.geom = hidden.attachNewNode(node) + self.makeDictionaries(self.hood.dnaStore) + self.reparentLandmarkBlockNodes() + self.renameFloorPolys(self.nodeList) + self.createAnimatedProps(self.nodeList) + self.holidayPropTransforms = {} + npl = self.geom.findAllMatches('**/=DNARoot=holiday_prop') + for i in range(npl.getNumPaths()): + np = npl.getPath(i) + np.setTag('transformIndex', `i`) + self.holidayPropTransforms[i] = np.getNetTransform() + + self.notify.info('skipping self.geom.flattenMedium') + gsg = base.win.getGsg() + if gsg: + self.geom.prepareScene(gsg) + self.geom.setName('town_top_level') + + def reparentLandmarkBlockNodes(self): + bucket = self.landmarkBlocks = hidden.attachNewNode('landmarkBlocks') + npc = self.geom.findAllMatches('**/sb*:*_landmark_*_DNARoot') + for i in range(npc.getNumPaths()): + nodePath = npc.getPath(i) + nodePath.wrtReparentTo(bucket) + + npc = self.geom.findAllMatches('**/sb*:*animated_building*_DNARoot') + for i in range(npc.getNumPaths()): + nodePath = npc.getPath(i) + nodePath.wrtReparentTo(bucket) + + def makeDictionaries(self, dnaStore): + self.nodeDict = {} + self.zoneDict = {} + self.nodeList = [] + self.fadeInDict = {} + self.fadeOutDict = {} + a1 = Vec4(1, 1, 1, 1) + a0 = Vec4(1, 1, 1, 0) + numVisGroups = dnaStore.getNumDNAVisGroups() + for i in range(numVisGroups): + groupFullName = dnaStore.getDNAVisGroupName(i) + groupName = base.cr.hoodMgr.extractGroupName(groupFullName) + zoneId = int(groupName) + zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId) + groupNode = self.geom.find('**/' + groupFullName) + if groupNode.isEmpty(): + self.notify.error('Could not find visgroup') + else: + if ':' in groupName: + groupName = '%s%s' % (zoneId, groupName[groupName.index(':'):]) + else: + groupName = '%s' % zoneId + groupNode.setName(groupName) + self.nodeDict[zoneId] = [] + self.nodeList.append(groupNode) + self.zoneDict[zoneId] = groupNode + fadeDuration = 0.5 + self.fadeOutDict[groupNode] = Sequence(Func(groupNode.setTransparency, 1), LerpColorScaleInterval(groupNode, fadeDuration, a0, startColorScale=a1), Func(groupNode.clearColorScale), Func(groupNode.clearTransparency), Func(groupNode.stash), name='fadeZone-' + str(zoneId), autoPause=1) + self.fadeInDict[groupNode] = Sequence(Func(groupNode.unstash), Func(groupNode.setTransparency, 1), LerpColorScaleInterval(groupNode, fadeDuration, a1, startColorScale=a0), Func(groupNode.clearColorScale), Func(groupNode.clearTransparency), name='fadeZone-' + str(zoneId), autoPause=1) + + for i in range(numVisGroups): + groupFullName = dnaStore.getDNAVisGroupName(i) + zoneId = int(base.cr.hoodMgr.extractGroupName(groupFullName)) + zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId) + for j in range(dnaStore.getNumVisiblesInDNAVisGroup(i)): + visName = dnaStore.getVisibleName(i, j) + groupName = base.cr.hoodMgr.extractGroupName(visName) + nextZoneId = int(groupName) + nextZoneId = ZoneUtil.getTrueZoneId(nextZoneId, self.zoneId) + visNode = self.zoneDict[nextZoneId] + self.nodeDict[zoneId].append(visNode) + + self.hood.dnaStore.resetPlaceNodes() + self.hood.dnaStore.resetDNAGroups() + self.hood.dnaStore.resetDNAVisGroups() + self.hood.dnaStore.resetDNAVisGroupsAI() + + def renameFloorPolys(self, nodeList): + for i in nodeList: + collNodePaths = i.findAllMatches('**/+CollisionNode') + numCollNodePaths = collNodePaths.getNumPaths() + visGroupName = i.node().getName() + for j in range(numCollNodePaths): + collNodePath = collNodePaths.getPath(j) + bitMask = collNodePath.node().getIntoCollideMask() + if bitMask.getBit(1): + collNodePath.node().setName(visGroupName) + + def createAnimatedProps(self, nodeList): + self.animPropDict = {} + self.zoneIdToInteractivePropDict = {} + for i in nodeList: + animPropNodes = i.findAllMatches('**/animated_prop_*') + numAnimPropNodes = animPropNodes.getNumPaths() + for j in range(numAnimPropNodes): + animPropNode = animPropNodes.getPath(j) + if animPropNode.getName().startswith('animated_prop_generic'): + className = 'GenericAnimatedProp' + elif animPropNode.getName().startswith('animated_prop_'): + name = animPropNode.getName()[len('animated_prop_'):] + splits = name.split('_') + className = splits[0] + else: + className = animPropNode.getName()[14:-8] + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + animPropObj = classObj(animPropNode) + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(animPropObj) + + interactivePropNodes = i.findAllMatches('**/interactive_prop_*') + numInteractivePropNodes = interactivePropNodes.getNumPaths() + for j in range(numInteractivePropNodes): + interactivePropNode = interactivePropNodes.getPath(j) + className = 'InteractiveAnimatedProp' + if 'hydrant' in interactivePropNode.getName(): + className = 'HydrantInteractiveProp' + elif 'trashcan' in interactivePropNode.getName(): + className = 'TrashcanInteractiveProp' + elif 'mailbox' in interactivePropNode.getName(): + className = 'MailboxInteractiveProp' + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + interactivePropObj = classObj(interactivePropNode) + animPropList = self.animPropDict.get(i) + if animPropList is None: + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(interactivePropObj) + if interactivePropObj.getCellIndex() == 0: + zoneId = int(i.getName()) + if zoneId not in self.zoneIdToInteractivePropDict: + self.zoneIdToInteractivePropDict[zoneId] = interactivePropObj + else: + self.notify.error('already have interactive prop %s in zone %s' % (self.zoneIdToInteractivePropDict, zoneId)) + + animatedBuildingNodes = i.findAllMatches('**/*:animated_building_*;-h') + for np in animatedBuildingNodes: + if np.getName().startswith('sb'): + animatedBuildingNodes.removePath(np) + + numAnimatedBuildingNodes = animatedBuildingNodes.getNumPaths() + for j in range(numAnimatedBuildingNodes): + animatedBuildingNode = animatedBuildingNodes.getPath(j) + className = 'GenericAnimatedBuilding' + symbols = {} + base.cr.importModule(symbols, 'toontown.hood', [className]) + classObj = getattr(symbols[className], className) + animatedBuildingObj = classObj(animatedBuildingNode) + animPropList = self.animPropDict.get(i) + if animPropList is None: + animPropList = self.animPropDict.setdefault(i, []) + animPropList.append(animatedBuildingObj) + + return + + def deleteAnimatedProps(self): + for zoneNode, animPropList in self.animPropDict.items(): + for animProp in animPropList: + animProp.delete() + + del self.animPropDict + + def enterAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.enter() + + def exitAnimatedProps(self, zoneNode): + for animProp in self.animPropDict.get(zoneNode, ()): + animProp.exit() + + def getInteractiveProp(self, zoneId): + result = None + if zoneId in self.zoneIdToInteractivePropDict: + result = self.zoneIdToInteractivePropDict[zoneId] + return result diff --git a/toontown/town/TutorialStreet.py b/toontown/town/TutorialStreet.py new file mode 100644 index 0000000..1385f1d --- /dev/null +++ b/toontown/town/TutorialStreet.py @@ -0,0 +1,22 @@ +import TTStreet + +class TutorialStreet(TTStreet.TTStreet): + + def enter(self, requestStatus): + TTStreet.TTStreet.enter(self, requestStatus, visibilityFlag=0, arrowsOn=0) + + def exit(self): + TTStreet.TTStreet.exit(self, visibilityFlag=0) + + def enterTeleportIn(self, requestStatus): + TTStreet.TTStreet.enterTeleportIn(self, requestStatus) + + def enterTownBattle(self, event): + self.loader.townBattle.enter(event, self.fsm.getStateNamed('battle'), tutorialFlag=1) + + def handleEnterTunnel(self, requestStatus, collEntry): + messenger.send('stopTutorial') + TTStreet.TTStreet.handleEnterTunnel(self, requestStatus, collEntry) + + def exitDoorIn(self): + base.localAvatar.obscureMoveFurnitureButton(-1) diff --git a/toontown/town/TutorialTownLoader.py b/toontown/town/TutorialTownLoader.py new file mode 100644 index 0000000..fc8baec --- /dev/null +++ b/toontown/town/TutorialTownLoader.py @@ -0,0 +1,30 @@ +import TownLoader +import TTTownLoader +import TutorialStreet +from toontown.suit import Suit +from toontown.toon import Toon +from toontown.hood import ZoneUtil + +class TutorialTownLoader(TTTownLoader.TTTownLoader): + + def __init__(self, hood, parentFSM, doneEvent): + TTTownLoader.TTTownLoader.__init__(self, hood, parentFSM, doneEvent) + self.streetClass = TutorialStreet.TutorialStreet + + def load(self, zoneId): + TownLoader.TownLoader.load(self, zoneId) + Suit.loadTutorialSuit() + dnaFile = 'phase_3.5/dna/tutorial_street.dna' + self.createHood(dnaFile, loadStorage=0) + self.alterDictionaries() + + def loadBattleAnims(self): + Toon.loadTutorialBattleAnims() + + def unloadBattleAnims(self): + Toon.unloadTutorialBattleAnims() + + def alterDictionaries(self): + zoneId = ZoneUtil.tutorialDict['exteriors'][0] + self.nodeDict[zoneId] = self.nodeDict[20001] + del self.nodeDict[20001] diff --git a/toontown/town/__init__.py b/toontown/town/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/trolley/Trolley.py b/toontown/trolley/Trolley.py new file mode 100644 index 0000000..4a55934 --- /dev/null +++ b/toontown/trolley/Trolley.py @@ -0,0 +1,216 @@ +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.toontowngui import TTDialog +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from direct.directnotify import DirectNotifyGlobal + +class Trolley(StateData.StateData): + notify = DirectNotifyGlobal.directNotify.newCategory('Trolley') + + def __init__(self, safeZone, parentFSM, doneEvent): + StateData.StateData.__init__(self, doneEvent) + self.fsm = ClassicFSM.ClassicFSM('Trolley', [ + State.State('start', + self.enterStart, + self.exitStart, + ['requestBoard', + 'trolleyHFA', + 'trolleyTFA']), + State.State('trolleyHFA', + self.enterTrolleyHFA, + self.exitTrolleyHFA, + ['final']), + State.State('trolleyTFA', + self.enterTrolleyTFA, + self.exitTrolleyTFA, + ['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', + 'trolleyLeaving', + 'final']), + State.State('requestExit', + self.enterRequestExit, + self.exitRequestExit, + ['exiting', + 'trolleyLeaving']), + State.State('trolleyLeaving', + self.enterTrolleyLeaving, + self.exitTrolleyLeaving, + ['final']), + State.State('exiting', + self.enterExiting, + self.exitExiting, + ['final']), + State.State('final', + self.enterFinal, + self.exitFinal, + ['start'])], + 'start', 'final') + self.parentFSM = parentFSM + return None + + def load(self): + self.parentFSM.getStateNamed('trolley').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.parentFSM.getStateNamed('trolley').removeChild(self.fsm) + del self.fsm + del self.parentFSM + self.buttonModels.removeNode() + del self.buttonModels + del self.upButton + del self.downButton + del self.rolloverButton + + def enter(self): + self.fsm.enterInitialState() + if base.localAvatar.hp > 0: + messenger.send('enterTrolleyOK') + self.fsm.request('requestBoard') + else: + self.fsm.request('trolleyHFA') + return None + + def exit(self): + self.ignoreAll() + return None + + def enterStart(self): + return None + + def exitStart(self): + return None + + def enterTrolleyHFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyHFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyHFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def enterTrolleyTFA(self): + self.noTrolleyBox = TTDialog.TTGlobalDialog(message=TTLocalizer.TrolleyTFAMessage, doneEvent='noTrolleyAck', style=TTDialog.Acknowledge) + self.noTrolleyBox.show() + base.localAvatar.b_setAnimState('neutral', 1) + self.accept('noTrolleyAck', self.__handleNoTrolleyAck) + + def exitTrolleyTFA(self): + self.ignore('noTrolleyAck') + self.noTrolleyBox.cleanup() + del self.noTrolleyBox + + def __handleNoTrolleyAck(self): + ntbDoneStatus = self.noTrolleyBox.doneStatus + if ntbDoneStatus == 'ok': + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + else: + self.notify.error('Unrecognized doneStatus: ' + str(ntbDoneStatus)) + + def enterRequestBoard(self): + return None + + def handleRejectBoard(self): + doneStatus = {} + doneStatus['mode'] = 'reject' + messenger.send(self.doneEvent, [doneStatus]) + + def exitRequestBoard(self): + return None + + def enterBoarding(self, nodePath): + camera.wrtReparentTo(nodePath) + self.cameraBoardTrack = LerpPosHprInterval(camera, 1.5, Point3(-35, 0, 8), Point3(-90, 0, 0)) + self.cameraBoardTrack.start() + return None + + def exitBoarding(self): + self.ignore('boardedTrolley') + return None + + def enterBoarded(self): + if base.config.GetBool('want-qa-regression', 0): + self.notify.info('QA-REGRESSION: RIDETHETROLLEY: Ride the Trolley') + self.enableExitButton() + return None + + def exitBoarded(self): + self.cameraBoardTrack.finish() + self.disableExitButton() + return None + + def enableExitButton(self): + self.exitButton = DirectButton(relief=None, text=TTLocalizer.TrolleyHopOff, text_fg=(1, 1, 0.65, 1), text_pos=(0, -0.23), text_scale=TTLocalizer.TexitButton, image=(self.upButton, self.downButton, self.rolloverButton), image_color=(1, 0, 0, 1), image_scale=(20, 1, 11), pos=(0, 0, 0.8), scale=0.15, command=lambda self = self: self.fsm.request('requestExit')) + return + + def disableExitButton(self): + self.exitButton.destroy() + + def enterRequestExit(self): + messenger.send('trolleyExitButton') + return None + + def exitRequestExit(self): + return None + + def enterTrolleyLeaving(self): + camera.lerpPosHprXYZHPR(0, 18.55, 3.75, -180, 0, 0, 3, blendType='easeInOut', task='leavingCamera') + self.acceptOnce('playMinigame', self.handlePlayMinigame) + return None + + def handlePlayMinigame(self, zoneId, minigameId): + base.localAvatar.b_setParent(ToontownGlobals.SPHidden) + doneStatus = {} + doneStatus['mode'] = 'minigame' + doneStatus['zoneId'] = zoneId + doneStatus['minigameId'] = minigameId + messenger.send(self.doneEvent, [doneStatus]) + + def exitTrolleyLeaving(self): + self.ignore('playMinigame') + taskMgr.remove('leavingCamera') + return None + + def enterExiting(self): + return None + + def handleOffTrolley(self): + doneStatus = {} + doneStatus['mode'] = 'exit' + messenger.send(self.doneEvent, [doneStatus]) + return None + + def exitExiting(self): + return None + + def enterFinal(self): + return None + + def exitFinal(self): + return None diff --git a/toontown/trolley/__init__.py b/toontown/trolley/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/tutorial/DistributedBattleTutorial.py b/toontown/tutorial/DistributedBattleTutorial.py new file mode 100644 index 0000000..34a8c55 --- /dev/null +++ b/toontown/tutorial/DistributedBattleTutorial.py @@ -0,0 +1,11 @@ +from toontown.battle import DistributedBattle +from direct.directnotify import DirectNotifyGlobal + +class DistributedBattleTutorial(DistributedBattle.DistributedBattle): + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleTutorial') + + def startTimer(self, ts = 0): + self.townBattle.timer.hide() + + def playReward(self, ts): + self.movie.playTutorialReward(ts, self.uniqueName('reward'), self.handleRewardDone) diff --git a/toontown/tutorial/TutorialForceAcknowledge.py b/toontown/tutorial/TutorialForceAcknowledge.py new file mode 100644 index 0000000..d967106 --- /dev/null +++ b/toontown/tutorial/TutorialForceAcknowledge.py @@ -0,0 +1,25 @@ +from pandac.PandaModules import * +from toontown.toontowngui import TTDialog +from toontown.toonbase import TTLocalizer + +class TutorialForceAcknowledge: + + def __init__(self, doneEvent): + self.doneEvent = doneEvent + self.dialog = None + return + + def enter(self): + base.localAvatar.loop('neutral') + self.doneStatus = {'mode': 'incomplete'} + msg = TTLocalizer.TutorialForceAcknowledgeMessage + self.dialog = TTDialog.TTDialog(text=msg, command=self.handleOk, style=TTDialog.Acknowledge) + + def exit(self): + if self.dialog: + self.dialog.cleanup() + self.dialog = None + return + + def handleOk(self, value): + messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/tutorial/TutorialManager.py b/toontown/tutorial/TutorialManager.py new file mode 100644 index 0000000..fd9b928 --- /dev/null +++ b/toontown/tutorial/TutorialManager.py @@ -0,0 +1,53 @@ +from pandac.PandaModules import * +from direct.distributed import DistributedObject +from direct.directnotify import DirectNotifyGlobal +from toontown.hood import ZoneUtil + +class TutorialManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory('TutorialManager') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.DistributedObject.__init__(self, cr) + + def generate(self): + DistributedObject.DistributedObject.generate(self) + messenger.send('tmGenerate') + self.accept('requestTutorial', self.d_requestTutorial) + self.accept('requestSkipTutorial', self.d_requestSkipTutorial) + self.accept('rejectTutorial', self.d_rejectTutorial) + + def disable(self): + self.ignoreAll() + ZoneUtil.overrideOff() + DistributedObject.DistributedObject.disable(self) + + def d_requestTutorial(self): + self.sendUpdate('requestTutorial', []) + + def d_rejectTutorial(self): + self.sendUpdate('rejectTutorial', []) + + def d_requestSkipTutorial(self): + self.sendUpdate('requestSkipTutorial', []) + + def skipTutorialResponse(self, allOk): + messenger.send('skipTutorialAnswered', [allOk]) + + def enterTutorial(self, branchZone, streetZone, shopZone, hqZone): + base.localAvatar.cantLeaveGame = 1 + ZoneUtil.overrideOn(branch=branchZone, exteriorList=[streetZone], interiorList=[shopZone, hqZone]) + messenger.send('startTutorial', [shopZone]) + self.acceptOnce('stopTutorial', self.__handleStopTutorial) + self.acceptOnce('toonArrivedTutorial', self.d_toonArrived) + + def __handleStopTutorial(self): + base.localAvatar.cantLeaveGame = 0 + self.d_allDone() + ZoneUtil.overrideOff() + + def d_allDone(self): + self.sendUpdate('allDone', []) + + def d_toonArrived(self): + self.sendUpdate('toonArrived', []) diff --git a/toontown/tutorial/__init__.py b/toontown/tutorial/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/toontown/uberdog/DataStore.py b/toontown/uberdog/DataStore.py new file mode 100644 index 0000000..3f33689 --- /dev/null +++ b/toontown/uberdog/DataStore.py @@ -0,0 +1,179 @@ +from direct.directnotify import DirectNotifyGlobal +from pandac.PandaModules import ConfigVariableBool +from direct.task import Task +from string import maketrans +import cPickle +import os +import sys +import anydbm +import time + +class DataStore: + QueryTypes = [] + QueryTypes = dict(zip(QueryTypes, range(len(QueryTypes)))) + + @classmethod + def addQueryTypes(cls, typeStrings): + superTypes = zip(cls.QueryTypes.values(), cls.QueryTypes.keys()) + superTypes.sort() + newTypes = [ item[1] for item in superTypes ] + typeStrings + newTypes = dict(zip(newTypes, range(1 + len(newTypes)))) + return newTypes + + notify = DirectNotifyGlobal.directNotify.newCategory('DataStore') + wantAnyDbm = ConfigVariableBool('want-ds-anydbm', 1).getValue() + + def __init__(self, filepath, writePeriod = 300, writeCountTrigger = 100): + self.filepath = filepath + self.writePeriod = writePeriod + self.writeCountTrigger = writeCountTrigger + self.writeCount = 0 + self.data = None + self.className = self.__class__.__name__ + if self.wantAnyDbm: + self.filepath += '-anydbm' + self.notify.debug('anydbm default module used: %s ' % anydbm._defaultmod.__name__) + self.open() + return + + def readDataFromFile(self): + if self.wantAnyDbm: + try: + if os.path.exists(self.filepath): + self.data = anydbm.open(self.filepath, 'w') + self.notify.debug('Opening existing anydbm database at: %s.' % (self.filepath,)) + else: + self.data = anydbm.open(self.filepath, 'c') + self.notify.debug('Creating new anydbm database at: %s.' % (self.filepath,)) + except anydbm.error: + self.notify.warning('Cannot open anydbm database at: %s.' % (self.filepath,)) + + else: + try: + file = open(self.filepath + '.bu', 'r') + self.notify.debug('Opening backup pickle data file at %s.' % (self.filepath + '.bu',)) + if os.path.exists(self.filepath): + os.remove(self.filepath) + except IOError: + try: + file = open(self.filepath, 'r') + self.notify.debug('Opening old pickle data file at %s..' % (self.filepath,)) + except IOError: + file = None + self.notify.debug('New pickle data file will be written to %s.' % (self.filepath,)) + + if file: + data = cPickle.load(file) + file.close() + self.data = data + else: + self.data = {} + return + + def writeDataToFile(self): + if self.data is not None: + self.notify.debug('Data is now synced with disk at %s' % self.filepath) + if self.wantAnyDbm: + self.data.sync() + else: + try: + backuppath = self.filepath + '.bu' + if os.path.exists(self.filepath): + os.rename(self.filepath, backuppath) + outfile = open(self.filepath, 'w') + cPickle.dump(self.data, outfile) + outfile.close() + if os.path.exists(backuppath): + os.remove(backuppath) + except EnvironmentError: + self.notify.warning(str(sys.exc_info()[1])) + + else: + self.notify.warning('No data to write. Aborting sync.') + return + + def syncTask(self, task): + task.timeElapsed += globalClock.getDt() + if task.timeElapsed > self.writePeriod: + if self.writeCount: + self.writeDataToFile() + self.resetWriteCount() + task.timeElapsed = 0.0 + if self.writeCount > self.writeCountTrigger: + self.writeDataToFile() + self.resetWriteCount() + task.timeElapsed = 0.0 + return Task.cont + + def incrementWriteCount(self): + self.writeCount += 1 + + def resetWriteCount(self): + self.writeCount = 0 + + def close(self): + if self.data is not None: + self.writeDataToFile() + if self.wantAnyDbm: + self.data.close() + taskMgr.remove('%s-syncTask' % (self.className,)) + self.data = None + return + + def open(self): + self.close() + self.readDataFromFile() + self.resetWriteCount() + taskMgr.remove('%s-syncTask' % (self.className,)) + t = taskMgr.add(self.syncTask, '%s-syncTask' % (self.className,)) + t.timeElapsed = 0.0 + + def reset(self): + self.destroy() + self.open() + + def destroy(self): + self.close() + if self.wantAnyDbm: + lt = time.asctime(time.localtime()) + trans = maketrans(': ', '__') + t = lt.translate(trans) + head, tail = os.path.split(self.filepath) + newFileName = 'UDStoreBak' + t + if os.path.exists(self.filepath): + try: + os.rename(tail, newFileName) + uber.air.writeServerEvent('Uberdog data store Info', 0, 'Creating backup of file: %s saving as: %s' % (tail, newFileName)) + except: + uber.air.writeServerEvent('Uberdog data store Info', 0, 'Unable to create backup of file: %s ' % tail) + + else: + files = os.listdir(head) + for file in files: + if file.find(tail) > -1: + filename, ext = os.path.splitext(file) + try: + os.rename(file, newFileName + ext) + uber.air.writeServerEvent('Uberdog data store Info', 0, 'Creating backup of file: %s saving as: %s' % (file, newFileName + ext)) + except: + uber.air.writeServerEvent('Uberdog data store Info', 0, 'Unable to create backup of file: %s ' % newFileName + ext) + + else: + if os.path.exists(self.filepath + '.bu'): + os.remove(self.filepath + '.bu') + if os.path.exists(self.filepath): + os.remove(self.filepath) + + def query(self, query): + if self.data is not None: + qData = cPickle.loads(query) + results = self.handleQuery(qData) + qResults = cPickle.dumps(results) + else: + results = None + qResults = cPickle.dumps(results) + return qResults + + def handleQuery(self, query): + results = None + return results diff --git a/toontown/uberdog/DataStoreGlobals.py b/toontown/uberdog/DataStoreGlobals.py new file mode 100644 index 0000000..dddb413 --- /dev/null +++ b/toontown/uberdog/DataStoreGlobals.py @@ -0,0 +1,12 @@ +from toontown.uberdog.ScavengerHuntDataStore import * +from toontown.uberdog.DataStore import * +SH = 1 +GEN = 2 +TYPES = {SH: (ScavengerHuntDataStore,), + GEN: (DataStore,)} + +def getStoreClass(type): + storeClass = TYPES.get(type, None) + if storeClass: + return storeClass[0] + return diff --git a/toontown/uberdog/DistributedCpuInfoMgr.py b/toontown/uberdog/DistributedCpuInfoMgr.py new file mode 100644 index 0000000..2043edf --- /dev/null +++ b/toontown/uberdog/DistributedCpuInfoMgr.py @@ -0,0 +1,4 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal + +class DistributedCpuInfoMgr(DistributedObjectGlobal): + pass diff --git a/toontown/uberdog/DistributedDataStoreManager.py b/toontown/uberdog/DistributedDataStoreManager.py new file mode 100644 index 0000000..b21a6f0 --- /dev/null +++ b/toontown/uberdog/DistributedDataStoreManager.py @@ -0,0 +1,7 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from toontown.uberdog import DataStoreGlobals + +class DistributedDataStoreManager(DistributedObjectGlobal): + + def __init__(self): + pass diff --git a/toontown/uberdog/DistributedDeliveryManager.py b/toontown/uberdog/DistributedDeliveryManager.py new file mode 100644 index 0000000..819caf4 --- /dev/null +++ b/toontown/uberdog/DistributedDeliveryManager.py @@ -0,0 +1,25 @@ +from pandac.PandaModules import * +from direct.distributed.DistributedObject import DistributedObject +from toontown.catalog import CatalogItemList +from toontown.catalog import CatalogItem + +class DistributedDeliveryManager(DistributedObject): + neverDisable = 1 + + def sendHello(self, message): + self.sendUpdate('hello', [message]) + + def rejectHello(self, message): + print 'rejected', message + + def helloResponse(self, message): + print 'accepted', message + + def sendAck(self): + self.sendUpdate('requestAck', []) + + def returnAck(self): + messenger.send('DeliveryManagerAck') + + def test(self): + print 'Distributed Delviery Manager Stub Test' diff --git a/toontown/uberdog/DistributedInGameNewsMgr.py b/toontown/uberdog/DistributedInGameNewsMgr.py new file mode 100644 index 0000000..72f2b2e --- /dev/null +++ b/toontown/uberdog/DistributedInGameNewsMgr.py @@ -0,0 +1,40 @@ +import socket +import datetime +import os +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from direct.distributed.DistributedObject import DistributedObject +from toontown.toonbase import ToontownGlobals +from toontown.uberdog import InGameNewsResponses + +class DistributedInGameNewsMgr(DistributedObject): + notify = directNotify.newCategory('InGameNewsMgr') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + base.cr.inGameNewsMgr = self + + def delete(self): + DistributedObject.delete(self) + self.cr.inGameNewsMgr = None + return + + def disable(self): + self.notify.debug("i'm disabling InGameNewsMgr rightnow.") + DistributedObject.disable(self) + + def generate(self): + self.notify.debug('BASE: generate') + DistributedObject.generate(self) + + def setLatestIssueStr(self, issueStr): + self.latestIssueStr = issueStr + self.latestIssue = base.cr.toontownTimeManager.convertUtcStrToToontownTime(issueStr) + messenger.send('newIssueOut') + self.notify.info('latestIssue=%s' % self.latestIssue) + + def getLatestIssueStr(self): + pass + + def getLatestIssue(self): + return self.latestIssue diff --git a/toontown/uberdog/DistributedMailManager.py b/toontown/uberdog/DistributedMailManager.py new file mode 100644 index 0000000..59cbfde --- /dev/null +++ b/toontown/uberdog/DistributedMailManager.py @@ -0,0 +1,4 @@ +from direct.distributed.DistributedObject import DistributedObject + +class DistributedMailManager(DistributedObject): + neverDisable = 1 diff --git a/toontown/uberdog/DistributedPartyManager.py b/toontown/uberdog/DistributedPartyManager.py new file mode 100644 index 0000000..c6c1467 --- /dev/null +++ b/toontown/uberdog/DistributedPartyManager.py @@ -0,0 +1,202 @@ +from direct.distributed.DistributedObject import DistributedObject +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from pandac.PandaModules import CFSpeech, CFTimeout +from toontown.toonbase import ToontownGlobals +from toontown.toonbase import TTLocalizer +from toontown.toon import ToonDNA +from toontown.parties import PartyGlobals + +class DistributedPartyManager(DistributedObject): + neverDisable = 1 + notify = directNotify.newCategory('DistributedPartyManager') + PartyStatusChangedEvent = 'changePartyStatusResponseReceived' + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + base.cr.partyManager = self + self.allowUnreleased = False + self.partyPlannerStyle = None + self.partyPlannerName = None + self.showDoid = False + return + + def delete(self): + DistributedObject.delete(self) + self.cr.partyManager = None + return + + def disable(self): + self.notify.debug("i'm disabling DistributedPartyManager rightnow.") + self.ignore('deallocateZoneIdFromPlannedParty') + self.ignoreAll() + DistributedObject.disable(self) + + def generate(self): + self.notify.debug('BASE: generate') + DistributedObject.generate(self) + self.accept('deallocateZoneIdFromPlannedParty', self.deallocateZoneIdFromPlannedParty) + self.announceGenerateName = self.uniqueName('generate') + + def deallocateZoneIdFromPlannedParty(self, zoneId): + self.sendUpdate('freeZoneIdFromPlannedParty', [base.localAvatar.doId, zoneId]) + + def allowUnreleasedClient(self): + return self.allowUnreleased + + def setAllowUnreleaseClient(self, newValue): + self.allowUnreleased = newValue + + def toggleAllowUnreleasedClient(self): + self.allowUnreleased = not self.allowUnreleased + return self.allowUnreleased + + def sendAddParty(self, hostId, startTime, endTime, isPrivate, inviteTheme, activities, decorations, inviteeIds): + self.sendUpdate('addPartyRequest', [hostId, + startTime, + endTime, + isPrivate, + inviteTheme, + activities, + decorations, + inviteeIds]) + + def addPartyResponse(self, hostId, errorCode): + messenger.send('addPartyResponseReceived', [hostId, errorCode]) + if hasattr(base.localAvatar, 'creatingNewPartyWithMagicWord'): + if base.localAvatar.creatingNewPartyWithMagicWord: + base.localAvatar.creatingNewPartyWithMagicWord = False + if errorCode == PartyGlobals.AddPartyErrorCode.AllOk: + base.localAvatar.setChatAbsolute('New party entered into database successfully.', CFSpeech | CFTimeout) + else: + base.localAvatar.setChatAbsolute('New party creation failed : %s' % PartyGlobals.AddPartyErrorCode.getString(errorCode), CFSpeech | CFTimeout) + + def requestPartyZone(self, avId, zoneId, callback): + if zoneId < 0: + zoneId = 0 + self.acceptOnce('requestPartyZoneComplete', callback) + if hasattr(base.localAvatar, 'aboutToPlanParty'): + if base.localAvatar.aboutToPlanParty: + self.sendUpdate('getPartyZone', [avId, zoneId, True]) + return + self.sendUpdate('getPartyZone', [avId, zoneId, False]) + + def receivePartyZone(self, hostId, partyId, zoneId): + if partyId != 0 and zoneId != 0: + if base.localAvatar.doId == hostId: + for partyInfo in base.localAvatar.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status == PartyGlobals.PartyStatus.Started + + messenger.send('requestPartyZoneComplete', [hostId, partyId, zoneId]) + + def sendChangePrivateRequest(self, partyId, newPrivateStatus): + self.sendUpdate('changePrivateRequest', [partyId, newPrivateStatus]) + + def changePrivateResponse(self, partyId, newPrivateStatus, errorCode): + if errorCode == PartyGlobals.ChangePartyFieldErrorCode.AllOk: + self.notify.info('succesfully changed private field for the party') + for partyInfo in localAvatar.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.isPrivate = newPrivateStatus + + messenger.send('changePartyPrivateResponseReceived', [partyId, newPrivateStatus, errorCode]) + else: + messenger.send('changePartyPrivateResponseReceived', [partyId, newPrivateStatus, errorCode]) + self.notify.info('FAILED changing private field for the party') + + def sendChangePartyStatusRequest(self, partyId, newPartyStatus): + self.sendUpdate('changePartyStatusRequest', [partyId, newPartyStatus]) + + def changePartyStatusResponse(self, partyId, newPartyStatus, errorCode, beansRefunded): + self.notify.debug('changePartyStatusResponse : partyId=%s newPartyStatus=%s errorCode=%s' % (partyId, newPartyStatus, errorCode)) + for partyInfo in localAvatar.hostedParties: + if partyInfo.partyId == partyId: + partyInfo.status = newPartyStatus + + messenger.send(self.PartyStatusChangedEvent, [partyId, + newPartyStatus, + errorCode, + beansRefunded]) + + def setNeverStartedPartyRefunded(self, partyId, newStatus, refund): + partyInfo = None + for pInfo in localAvatar.hostedParties: + if pInfo.partyId == partyId: + partyInfo = pInfo + break + + if partyInfo: + partyInfo.status = newStatus + messenger.send(self.PartyStatusChangedEvent, [partyId, + newStatus, + 0, + refund]) + return + + def sendAvToPlayground(self, avId, retCode): + messenger.send(PartyGlobals.KICK_TO_PLAYGROUND_EVENT, [retCode]) + self.notify.debug('sendAvToPlayground: %d' % avId) + + def leaveParty(self): + if self.isDisabled(): + self.notify.warning('DistributedPartyManager disabled; unable to leave party.') + return + self.sendUpdate('exitParty', [localAvatar.zoneId]) + + def removeGuest(self, ownerId, avId): + self.notify.debug('removeGuest ownerId = %s, avId = %s' % (ownerId, avId)) + self.sendUpdate('removeGuest', [ownerId, avId]) + + def isToonAllowedAtParty(self, avId, partyId): + return PartyGlobals.GoToPartyStatus.AllowedToGo + + def getGoToPartyFailedMessage(self, reason): + return '' + + def sendAvatarToParty(self, hostId): + DistributedPartyManager.notify.debug('sendAvatarToParty hostId = %s' % hostId) + self.sendUpdate('requestShardIdZoneIdForHostId', [hostId]) + + def sendShardIdZoneIdToAvatar(self, shardId, zoneId): + DistributedPartyManager.notify.debug('sendShardIdZoneIdToAvatar shardId = %s zoneId = %s' % (shardId, zoneId)) + if shardId == 0 or zoneId == 0: + base.cr.playGame.getPlace().handleBookClose() + return + hoodId = ToontownGlobals.PartyHood + if shardId == base.localAvatar.defaultShard: + shardId = None + base.cr.playGame.getPlace().requestLeave({'loader': 'safeZoneLoader', + 'where': 'party', + 'how': 'teleportIn', + 'hoodId': hoodId, + 'zoneId': zoneId, + 'shardId': shardId, + 'avId': -1}) + return + + def setPartyPlannerStyle(self, dna): + self.partyPlannerStyle = dna + + def getPartyPlannerStyle(self): + if self.partyPlannerStyle: + return self.partyPlannerStyle + else: + dna = ToonDNA.ToonDNA() + dna.newToonRandom() + return dna + + def setPartyPlannerName(self, name): + self.partyPlannerName = name + + def getPartyPlannerName(self): + if self.partyPlannerName: + return self.partyPlannerName + else: + return TTLocalizer.PartyPlannerGenericName + + def toggleShowDoid(self): + self.showDoid = not self.showDoid + return self.showDoid + + def getShowDoid(self): + return self.showDoid diff --git a/toontown/uberdog/DistributedSecurityMgr.py b/toontown/uberdog/DistributedSecurityMgr.py new file mode 100644 index 0000000..98fbf89 --- /dev/null +++ b/toontown/uberdog/DistributedSecurityMgr.py @@ -0,0 +1,31 @@ +import socket +import datetime +import os +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from direct.distributed.DistributedObject import DistributedObject +from toontown.toonbase import ToontownGlobals + +class DistributedSecurityMgr(DistributedObject): + notify = directNotify.newCategory('SecurityMgr') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + base.cr.whitelistMgr = self + + def delete(self): + DistributedObject.delete(self) + self.cr.whitelistMgr = None + return + + def disable(self): + self.notify.debug("i'm disabling SecurityMgr right now.") + DistributedObject.disable(self) + + def generate(self): + self.notify.debug('BASE: generate') + DistributedObject.generate(self) + + def updateWhitelist(self): + messenger.send('updateWhitelist') + self.notify.info('Updating white list') diff --git a/toontown/uberdog/DistributedWhitelistMgr.py b/toontown/uberdog/DistributedWhitelistMgr.py new file mode 100644 index 0000000..e8672d2 --- /dev/null +++ b/toontown/uberdog/DistributedWhitelistMgr.py @@ -0,0 +1,31 @@ +import socket +import datetime +import os +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from direct.distributed.DistributedObject import DistributedObject +from toontown.toonbase import ToontownGlobals + +class DistributedWhitelistMgr(DistributedObject): + notify = directNotify.newCategory('WhitelistMgr') + neverDisable = 1 + + def __init__(self, cr): + DistributedObject.__init__(self, cr) + base.cr.whitelistMgr = self + + def delete(self): + DistributedObject.delete(self) + self.cr.whitelistMgr = None + return + + def disable(self): + self.notify.debug("i'm disabling WhitelistMgr right now.") + DistributedObject.disable(self) + + def generate(self): + self.notify.debug('BASE: generate') + DistributedObject.generate(self) + + def updateWhitelist(self): + messenger.send('updateWhitelist') + self.notify.info('Updating white list') diff --git a/toontown/uberdog/InGameNewsResponses.py b/toontown/uberdog/InGameNewsResponses.py new file mode 100644 index 0000000..c0f014b --- /dev/null +++ b/toontown/uberdog/InGameNewsResponses.py @@ -0,0 +1,2 @@ +setLatestIssueFailureXML = '\n\n false\n %s\n\n\r\n' +setLatestIssueSuccessXML = '\n\n true\n %s\n\n\r\n' diff --git a/toontown/uberdog/ScavengerHuntDataStore.py b/toontown/uberdog/ScavengerHuntDataStore.py new file mode 100644 index 0000000..33e548b --- /dev/null +++ b/toontown/uberdog/ScavengerHuntDataStore.py @@ -0,0 +1,52 @@ +from direct.directnotify import DirectNotifyGlobal +from toontown.uberdog.DataStore import * + +class ScavengerHuntDataStore(DataStore): + QueryTypes = DataStore.addQueryTypes(['GetGoals', 'AddGoal']) + notify = DirectNotifyGlobal.directNotify.newCategory('ScavengerHuntDataStore') + + def __init__(self, filepath): + DataStore.__init__(self, filepath) + + def handleQuery(self, query): + qId, qData = query + if qId == self.QueryTypes['GetGoals']: + avId, goal = qData + goals = self.__getGoalsForAvatarId(avId) + return (qId, (avId, goal, goals)) + elif qId == self.QueryTypes['AddGoal']: + avId, goal = qData + self.__addGoalToAvatarId(avId, goal) + return (qId, (avId,)) + return None + + def __addGoalToAvatarId(self, avId, goal): + if self.wantAnyDbm: + pAvId = cPickle.dumps(avId) + pGoal = cPickle.dumps(goal) + pData = self.data.get(pAvId, None) + if pData is not None: + data = cPickle.loads(pData) + else: + data = set() + data.add(goal) + pData = cPickle.dumps(data) + self.data[pAvId] = pData + else: + self.data.setdefault(avId, set()) + self.data[avId].add(goal) + self.incrementWriteCount() + return + + def __getGoalsForAvatarId(self, avId): + if self.wantAnyDbm: + pAvId = cPickle.dumps(avId) + pData = self.data.get(pAvId, None) + if pData is not None: + data = list(cPickle.loads(pData)) + else: + data = [] + return data + else: + return list(self.data.get(avId, [])) + return diff --git a/toontown/uberdog/TTSpeedchatRelay.py b/toontown/uberdog/TTSpeedchatRelay.py new file mode 100644 index 0000000..1bb965c --- /dev/null +++ b/toontown/uberdog/TTSpeedchatRelay.py @@ -0,0 +1,16 @@ +from direct.distributed.DistributedObjectGlobal import DistributedObjectGlobal +from direct.directnotify.DirectNotifyGlobal import directNotify +from otp.otpbase import OTPGlobals +from otp.uberdog.SpeedchatRelay import SpeedchatRelay +from otp.uberdog import SpeedchatRelayGlobals + +class TTSpeedchatRelay(SpeedchatRelay): + + def __init__(self, cr): + SpeedchatRelay.__init__(self, cr) + + def sendSpeedchatToonTask(self, receiverId, taskId, toNpcId, toonProgress, msgIndex): + self.sendSpeedchatToRelay(receiverId, SpeedchatRelayGlobals.TOONTOWN_QUEST, [taskId, + toNpcId, + toonProgress, + msgIndex]) diff --git a/toontown/uberdog/__init__.py b/toontown/uberdog/__init__.py new file mode 100644 index 0000000..e69de29